diff --git a/deps/mozjs/js/ductwork/debugger/IJSDebugger.idl b/deps/mozjs/js/ductwork/debugger/IJSDebugger.idl new file mode 100644 index 00000000000..228af9609e9 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/IJSDebugger.idl @@ -0,0 +1,52 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +/** + * Do not use this interface. Instead, write: + * Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + */ +[scriptable, uuid(2fc14cc6-4ed0-4bbf-a7dd-e535bf088eb5)] +interface IJSDebugger : nsISupports +{ + /** + * Define the global Debugger constructor. + */ + [implicit_jscontext] + void addClass(); +}; diff --git a/deps/mozjs/js/ductwork/debugger/JSDebugger.cpp b/deps/mozjs/js/ductwork/debugger/JSDebugger.cpp new file mode 100644 index 00000000000..1e131514210 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/JSDebugger.cpp @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "JSDebugger.h" +#include "nsIXPConnect.h" +#include "nsThreadUtils.h" +#include "jsapi.h" +#include "jsgc.h" +#include "jsfriendapi.h" +#include "jsdbgapi.h" +#include "mozilla/ModuleUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsMemory.h" + +#define JSDEBUGGER_CONTRACTID \ + "@mozilla.org/jsdebugger;1" + +#define JSDEBUGGER_CID \ +{ 0x0365cbd5, 0xd46e, 0x4e94, { 0xa3, 0x9f, 0x83, 0xb6, 0x3c, 0xd1, 0xa9, 0x63 } } + +namespace mozilla { +namespace jsdebugger { + +NS_GENERIC_FACTORY_CONSTRUCTOR(JSDebugger) + +NS_IMPL_ISUPPORTS1(JSDebugger, IJSDebugger) + +JSDebugger::JSDebugger() +{ +} + +JSDebugger::~JSDebugger() +{ +} + +NS_IMETHODIMP +JSDebugger::AddClass(JSContext *cx) +{ + nsresult rv; + nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + + JSObject* global = JS_GetGlobalForScopeChain(cx); + if (!global) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!JS_DefineDebuggerObject(cx, global)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} +} + +NS_DEFINE_NAMED_CID(JSDEBUGGER_CID); + +static const mozilla::Module::CIDEntry kJSDebuggerCIDs[] = { + { &kJSDEBUGGER_CID, false, NULL, mozilla::jsdebugger::JSDebuggerConstructor }, + { NULL } +}; + +static const mozilla::Module::ContractIDEntry kJSDebuggerContracts[] = { + { JSDEBUGGER_CONTRACTID, &kJSDEBUGGER_CID }, + { NULL } +}; + +static const mozilla::Module kJSDebuggerModule = { + mozilla::Module::kVersion, + kJSDebuggerCIDs, + kJSDebuggerContracts +}; + +NSMODULE_DEFN(jsdebugger) = &kJSDebuggerModule; diff --git a/deps/mozjs/js/ductwork/debugger/JSDebugger.h b/deps/mozjs/js/ductwork/debugger/JSDebugger.h new file mode 100644 index 00000000000..e7874f1f0ce --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/JSDebugger.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef COMPONENTS_JSDEBUGGER_H +#define COMPONENTS_JSDEBUGGER_H + +#include "IJSDebugger.h" + +namespace mozilla { +namespace jsdebugger { + +class JSDebugger : public IJSDebugger +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IJSDEBUGGER + + JSDebugger(); + +private: + ~JSDebugger(); +}; + +} +} + +#endif diff --git a/deps/mozjs/js/ductwork/debugger/Makefile.in b/deps/mozjs/js/ductwork/debugger/Makefile.in new file mode 100644 index 00000000000..f1715c66274 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/Makefile.in @@ -0,0 +1,75 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Dave Camp +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = js/ductwork/debugger + +include $(DEPTH)/config/autoconf.mk + +MODULE = jsdebugger +MODULE_NAME = jsdebugger +GRE_MODULE = 1 + +LIBRARY_NAME = jsdebugger +XPIDL_MODULE = jsdebugger +LIBXUL_LIBRARY = 1 +EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 + +CPPSRCS = \ + JSDebugger.cpp \ + $(NULL) + +EXTRA_DSO_LDOPTS += \ + $(MOZ_COMPONENT_LIBS) \ + $(MOZ_JS_LIBS) \ + $(NULL) + +EXTRA_JS_MODULES = \ + jsdebugger.jsm \ + $(NULL) + +XPIDLSRCS = \ + IJSDebugger.idl \ + $(NULL) + +XPCSHELL_TESTS = tests + +include $(topsrcdir)/config/rules.mk diff --git a/deps/mozjs/js/ductwork/debugger/jsdebugger.jsm b/deps/mozjs/js/ductwork/debugger/jsdebugger.jsm new file mode 100644 index 00000000000..fd74c589927 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/jsdebugger.jsm @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is js-ctypes. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let EXPORTED_SYMBOLS = [ "Debugger" ]; + +/* + * This is the js module for Debugger. Import it like so: + * Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + * + * This will create a 'Debugger' object, which provides an interface to debug + * JavaScript code running in other compartments in the same process, on the + * same thread. + * + * For documentation on the API, see: + * https://wiki.mozilla.org/Debugger + */ + +// Initialize the Debugger object. You do not need to do this yourself. +const init = Components.classes["@mozilla.org/jsdebugger;1"].createInstance(Components.interfaces.IJSDebugger); +init.addClass(); diff --git a/deps/mozjs/js/ductwork/debugger/tests/head_dbg.js b/deps/mozjs/js/ductwork/debugger/tests/head_dbg.js new file mode 100644 index 00000000000..0ffeed26bd7 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/tests/head_dbg.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +function testGlobal(aName) { + let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"] + .createInstance(Ci.nsIPrincipal); + + let sandbox = Cu.Sandbox(systemPrincipal); + Cu.evalInSandbox("this.__name = '" + aName + "'", sandbox); + return sandbox; +} diff --git a/deps/mozjs/js/ductwork/debugger/tests/test_nativewrappers.js b/deps/mozjs/js/ductwork/debugger/tests/test_nativewrappers.js new file mode 100644 index 00000000000..07b126ed532 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/tests/test_nativewrappers.js @@ -0,0 +1,29 @@ +function run_test() +{ + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + var g = testGlobal("test1"); + + var dbg = new Debugger(); + dbg.addDebuggee(g); + dbg.onDebuggerStatement = function(aFrame) { + let args = aFrame["arguments"]; + try { + args[0]; + do_check_true(true); + } catch(ex) { + do_check_true(false); + } + }; + + g.eval("function stopMe(arg) {debugger;}"); + + g2 = testGlobal("test2"); + g2.g = g; + g2.eval("(" + function createBadEvent() { + let parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); + let doc = parser.parseFromString("", "text/xml"); + g.stopMe(doc.createEvent("MouseEvent")); + } + ")()"); + + dbg.enabled = false; +} diff --git a/deps/mozjs/js/ductwork/debugger/tests/xpcshell.ini b/deps/mozjs/js/ductwork/debugger/tests/xpcshell.ini new file mode 100644 index 00000000000..64dabd238f4 --- /dev/null +++ b/deps/mozjs/js/ductwork/debugger/tests/xpcshell.ini @@ -0,0 +1,7 @@ +[DEFAULT] +head = head_dbg.js +tail = + +[test_nativewrappers.js] +# Bug 685068 +fail-if = os == "android" diff --git a/deps/mozjs/js/examples/jorendb.js b/deps/mozjs/js/examples/jorendb.js new file mode 100644 index 00000000000..cbf35bc1e8c --- /dev/null +++ b/deps/mozjs/js/examples/jorendb.js @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * jorendb - A toy command-line debugger for shell-js programs. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is jorendb toy JS debugger. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * jorendb is a simple command-line debugger for shell-js programs. It is + * intended as a demo of the Debugger object (as there are no shell js programs to + * speak of). + * + * To run it: $JS -d path/to/this/file/jorendb.js + * To run some JS code under it, try: + * (jorendb) print load("my-script-to-debug.js") + * Execution will stop at debugger statements and you'll get a jorendb prompt. + */ + +(function () { + var debuggerSource = "(" + function () { + // Debugger state. + var focusedFrame = null; + var topFrame = null; + var debuggeeValues = [null]; + var lastExc = null; + + // Convert a debuggee value v to a string. + function dvToString(v) { + return (typeof v !== 'object' || v === null) ? uneval(v) : "[object " + v.class + "]"; + } + + function showDebuggeeValue(dv) { + var dvrepr = dvToString(dv); + var i = debuggeeValues.length; + debuggeeValues[i] = dv; + print("$" + i + " = " + dvrepr); + } + + Object.defineProperty(Debugger.Frame.prototype, "num", { + configurable: true, + enumerable: false, + get: function () { + var i = 0; + for (var f = topFrame; f && f !== this; f = f.older) + i++; + return f === null ? undefined : i; + } + }); + + function framePosition(f) { + if (!f.script) + return f.type + " code"; + return (f.script.url || f.type + " code") + ":" + f.script.getOffsetLine(f.offset); + } + + function callDescription(f) { + return ((f.callee.name || '') + + "(" + f.arguments.map(dvToString).join(", ") + ")"); + } + + function showFrame(f, n) { + if (f === undefined || f === null) { + f = focusedFrame; + if (f === null) { + print("No stack."); + return; + } + } + if (n === undefined) { + n = f.num; + if (n === undefined) + throw new Error("Internal error: frame not on stack"); + } + + var me = '#' + n; + if (f.type === "call") + me += ' ' + callDescription(f); + me += ' ' + framePosition(f); + print(me); + } + + function saveExcursion(fn) { + var tf = topFrame, ff = focusedFrame; + try { + return fn(); + } finally { + topFrame = tf; + focusedFrame = ff; + } + } + + function quitCommand() { + dbg.enabled = false; + quit(0); + } + + function backtraceCommand() { + if (topFrame === null) + print("No stack."); + for (var i = 0, f = topFrame; f; i++, f = f.older) + showFrame(f, i); + } + + function printCommand(rest) { + if (focusedFrame === null) { + // This is super bogus, need a way to create an env wrapping the debuggeeGlobal + // and eval against that. + var nonwrappedValue; + try { + nonwrappedValue = debuggeeGlobal.eval(rest); + } catch (exc) { + print("Exception caught."); + nonwrappedValue = exc; + } + if (typeof nonwrappedValue !== "object" || nonwrappedValue === null) { + // primitive value, no sweat + print(" " + uneval(nonwrappedValue)); + } else { + // junk for now + print(" " + Object.prototype.toString.call(nonwrappedValue)); + } + } else { + // This is the real deal. + var cv = saveExcursion(function () { + return focusedFrame.eval(rest); + }); + if (cv === null) { + if (!dbg.enabled) + return [cv]; + print("Debuggee died."); + } else if ('return' in cv) { + if (!dbg.enabled) + return [undefined]; + showDebuggeeValue(cv.return); + } else { + if (!dbg.enabled) + return [cv]; + print("Exception caught. (To rethrow it, type 'throw'.)"); + lastExc = cv.throw; + showDebuggeeValue(lastExc); + } + } + } + + function detachCommand() { + dbg.enabled = false; + return [undefined]; + } + + function continueCommand() { + if (focusedFrame === null) { + print("No stack."); + return; + } + return [undefined]; + } + + function throwCommand(rest) { + var v; + if (focusedFrame !== topFrame) { + print("To throw, you must select the newest frame (use 'frame 0')."); + return; + } else if (focusedFrame === null) { + print("No stack."); + return; + } else if (rest === '') { + return [{throw: lastExc}]; + } else { + var cv = saveExcursion(function () { return focusedFrame.eval(rest); }); + if (cv === null) { + if (!dbg.enabled) + return [cv]; + print("Debuggee died while determining what to throw. Stopped."); + } else if ('return' in cv) { + return [{throw: cv.return}]; + } else { + if (!dbg.enabled) + return [cv]; + print("Exception determining what to throw. Stopped."); + showDebuggeeValue(cv.throw); + } + return; + } + } + + function frameCommand(rest) { + var n, f; + if (rest.match(/[0-9]+/)) { + n = +rest; + f = topFrame; + if (f === null) { + print("No stack."); + return; + } + for (var i = 0; i < n && f; i++) { + if (!f.older) { + print("There is no frame " + rest + "."); + return; + } + f.older.younger = f; + f = f.older; + } + focusedFrame = f; + showFrame(f, n); + } else if (rest !== '') { + if (topFrame === null) + print("No stack."); + else + showFrame(); + } else { + print("do what now?"); + } + } + + function debugmeCommand() { + var meta = newGlobal("new-compartment"); + meta.debuggeeGlobal = this; + meta.debuggerSource = debuggerSource; + meta.prompt = prompt.replace('(', '(meta-'); + meta.eval(debuggerSource); + } + + function upCommand() { + if (focusedFrame === null) + print("No stack."); + else if (focusedFrame.older === null) + print("Initial frame selected; you cannot go up."); + else { + focusedFrame.older.younger = focusedFrame; + focusedFrame = focusedFrame.older; + showFrame(); + } + } + + function downCommand() { + if (focusedFrame === null) + print("No stack."); + else if (!focusedFrame.younger) + print("Youngest frame selected; you cannot go down."); + else { + focusedFrame = focusedFrame.younger; + showFrame(); + } + } + + function forcereturnCommand(rest) { + var v; + var f = focusedFrame; + if (f !== topFrame) { + print("To forcereturn, you must select the newest frame (use 'frame 0')."); + } else if (f === null) { + print("Nothing on the stack."); + } else if (rest === '') { + return [{return: undefined}]; + } else { + var cv = saveExcursion(function () { return f.eval(rest); }); + if (cv === null) { + if (!dbg.enabled) + return [cv]; + print("Debuggee died while determining what to forcereturn. Stopped."); + } else if ('return' in cv) { + return [{return: cv.return}]; + } else { + if (!dbg.enabled) + return [cv]; + print("Error determining what to forcereturn. Stopped."); + showDebuggeeValue(cv.throw); + } + } + } + + // Build the table of commands. + var commands = {}; + var commandArray = [ + backtraceCommand, "bt", "where", + continueCommand, "c", + detachCommand, + debugmeCommand, + downCommand, "d", + forcereturnCommand, + frameCommand, "f", + printCommand, "p", + quitCommand, "q", + throwCommand, "t", + upCommand, "u" + ]; + var last = null; + for (var i = 0; i < commandArray.length; i++) { + var cmd = commandArray[i]; + if (typeof cmd === "string") + commands[cmd] = last; + else + last = commands[cmd.name.replace(/Command$/, '')] = cmd; + } + + // Break cmd into two parts: its first word and everything else. + function breakcmd(cmd) { + cmd = cmd.trimLeft(); + var m = /\s/.exec(cmd); + if (m === null) + return [cmd, '']; + return [cmd.slice(0, m.index), cmd.slice(m.index).trimLeft()]; + } + + function runcmd(cmd) { + var pieces = breakcmd(cmd); + if (pieces[0] === "") + return undefined; + + var first = pieces[0], rest = pieces[1]; + if (!commands.hasOwnProperty(first)) { + print("unrecognized command '" + first + "'"); + return undefined; + } + + var cmd = commands[first]; + if (cmd.length === 0 && rest !== '') { + print("this command cannot take an argument"); + return undefined; + } + + return cmd(rest); + } + + function repl() { + var cmd; + for (;;) { + print("\n" + prompt); + cmd = readline(); + if (cmd === null) + break; + + try { + var result = runcmd(cmd); + if (result === undefined) + ; // do nothing + else if (Array.isArray(result)) + return result[0]; + else + throw new Error("Internal error: result of runcmd wasn't array or undefined"); + } catch (exc) { + print("*** Internal error: exception in the debugger code."); + print(" " + exc); + var me = prompt.replace(/^\((.*)\)$/, function (a, b) { return b; }); + print("Debug " + me + "? (y/n)"); + if (readline().match(/^\s*y/i) !== null) + debugMe(); + else + print("ok, ignoring error"); + } + } + } + + var dbg = new Debugger(debuggeeGlobal); + dbg.onDebuggerStatement = function (frame) { + return saveExcursion(function () { + topFrame = focusedFrame = frame; + print("'debugger' statement hit."); + showFrame(); + return repl(); + }); + }; + dbg.onThrow = function (frame, exc) { + return saveExcursion(function () { + topFrame = focusedFrame = frame; + print("Unwinding due to exception. (Type 'c' to continue unwinding.)"); + showFrame(); + print("Exception value is:"); + showDebuggeeValue(exc); + return repl(); + }); + }; + repl(); + } + ")();" + + print("jorendb version -0.0"); + var g = newGlobal("new-compartment"); + g.debuggeeGlobal = this; + g.prompt = '(jorendb)'; + g.debuggerSource = debuggerSource; + g.eval(debuggerSource); +})(); diff --git a/deps/mozjs/js/ipc/ObjectWrapperChild.cpp b/deps/mozjs/js/ipc/ObjectWrapperChild.cpp index b81d6acefca..f826177585e 100644 --- a/deps/mozjs/js/ipc/ObjectWrapperChild.cpp +++ b/deps/mozjs/js/ipc/ObjectWrapperChild.cpp @@ -39,7 +39,6 @@ * ***** END LICENSE BLOCK ***** */ #include "base/basictypes.h" -#include "jscntxt.h" #include "mozilla/jsipc/ContextWrapperChild.h" #include "mozilla/jsipc/ObjectWrapperChild.h" @@ -75,7 +74,7 @@ namespace { JSOPTION_DONT_REPORT_UNCAUGHT))) { JS_GUARD_OBJECT_NOTIFIER_INIT; - mStack.Push(cx, PR_FALSE); + mStack.Push(cx, false); } ~AutoContextPusher() { @@ -415,11 +414,11 @@ static void CPOW_NewEnumerateState_Finalize(JSContext* cx, JSObject* state) { nsTArray* strIds = - static_cast*>(JS_GetPrivate(cx, state)); + static_cast*>(JS_GetPrivate(state)); if (strIds) { delete strIds; - JS_SetPrivate(cx, state, NULL); + JS_SetPrivate(state, NULL); } } @@ -453,10 +452,10 @@ ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */ for (JSObject* proto = mObj; proto; - proto = JS_GetPrototype(cx, proto)) + proto = JS_GetPrototype(proto)) { AutoIdArray ids(cx, JS_Enumerate(cx, proto)); - for (uint i = 0; i < ids.length(); ++i) + for (size_t i = 0; i < ids.length(); ++i) JS_DefinePropertyById(cx, state, ids[i], JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE | JSPROP_SHARED); } @@ -467,7 +466,7 @@ ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */ if (!ids) return false; strIds = new InfallibleTArray(ids.length()); - for (uint i = 0; i < ids.length(); ++i) + for (size_t i = 0; i < ids.length(); ++i) if (!jsid_to_nsString(cx, ids[i], strIds->AppendElement())) { delete strIds; return false; @@ -475,10 +474,10 @@ ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */ } *idp = strIds->Length(); - *status = (JS_SetPrivate(cx, state, strIds) && - JS_SetReservedSlot(cx, state, sNextIdIndexSlot, - JSVAL_ZERO) && - JSObject_to_JSVariant(cx, state, statep)); + JS_SetPrivate(state, strIds); + JS_SetReservedSlot(state, sNextIdIndexSlot, JSVAL_ZERO); + + *status = JSObject_to_JSVariant(cx, state, statep); return true; } @@ -488,7 +487,6 @@ ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state, OperationStatus* status, JSVariant* statep, nsString* idp) { JSObject* state; - jsval v; *statep = in_state; idp->Truncate(); @@ -501,11 +499,13 @@ ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state, return false; InfallibleTArray* strIds = - static_cast*>(JS_GetPrivate(cx, state)); + static_cast*>(JS_GetPrivate(state)); - if (!strIds || !JS_GetReservedSlot(cx, state, sNextIdIndexSlot, &v)) + if (!strIds) return false; + jsval v = JS_GetReservedSlot(state, sNextIdIndexSlot); + jsuint i = JSVAL_TO_INT(v); NS_ASSERTION(i >= 0, "Index of next jsid negative?"); NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?"); @@ -516,8 +516,8 @@ ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state, } *idp = strIds->ElementAt(i); - *status = JS_SetReservedSlot(cx, state, sNextIdIndexSlot, - INT_TO_JSVAL(i + 1)); + JS_SetReservedSlot(state, sNextIdIndexSlot, INT_TO_JSVAL(i + 1)); + *status = JS_TRUE; return true; } diff --git a/deps/mozjs/js/ipc/ObjectWrapperParent.cpp b/deps/mozjs/js/ipc/ObjectWrapperParent.cpp index f68156ea114..60db949109b 100644 --- a/deps/mozjs/js/ipc/ObjectWrapperParent.cpp +++ b/deps/mozjs/js/ipc/ObjectWrapperParent.cpp @@ -44,9 +44,8 @@ #include "mozilla/unused.h" #include "nsJSUtils.h" -#include "jsobj.h" -#include "jsfun.h" #include "jsutil.h" +#include "jsfriendapi.h" using namespace mozilla::jsipc; @@ -60,47 +59,38 @@ namespace { class AutoResolveFlag { - JSContext* mContext; JSObject* mObj; uintN mOldFlags; JS_DECL_USE_GUARD_OBJECT_NOTIFIER - static uintN GetFlags(JSContext* cx, JSObject* obj) { - jsval v; -#ifdef DEBUG - JSBool ok = -#endif - JS_GetReservedSlot(cx, obj, sFlagsSlot, &v); - NS_ASSERTION(ok, "Failed to get CPOW flags"); + static uintN GetFlags(JSObject* obj) { + jsval v = JS_GetReservedSlot(obj, sFlagsSlot); return JSVAL_TO_INT(v); } - static uintN SetFlags(JSContext* cx, JSObject* obj, uintN flags) { - uintN oldFlags = GetFlags(cx, obj); + static uintN SetFlags(JSObject* obj, uintN flags) { + uintN oldFlags = GetFlags(obj); if (oldFlags != flags) - JS_SetReservedSlot(cx, obj, sFlagsSlot, INT_TO_JSVAL(flags)); + JS_SetReservedSlot(obj, sFlagsSlot, INT_TO_JSVAL(flags)); return oldFlags; } public: - AutoResolveFlag(JSContext* cx, - JSObject* obj + AutoResolveFlag(JSObject* obj JS_GUARD_OBJECT_NOTIFIER_PARAM) - : mContext(cx) - , mObj(obj) - , mOldFlags(SetFlags(cx, obj, - GetFlags(cx, obj) | CPOW_FLAG_RESOLVING)) + : mObj(obj) + , mOldFlags(SetFlags(obj, GetFlags(obj) | CPOW_FLAG_RESOLVING)) { JS_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoResolveFlag() { - SetFlags(mContext, mObj, mOldFlags); + SetFlags(mObj, mOldFlags); } - static JSBool IsSet(JSContext* cx, JSObject* obj) { - return GetFlags(cx, obj) & CPOW_FLAG_RESOLVING; + static JSBool IsSet(JSObject* obj) { + return GetFlags(obj) & CPOW_FLAG_RESOLVING; } }; @@ -174,23 +164,23 @@ const js::Class ObjectWrapperParent::sCPOW_JSClass = { "CrossProcessObjectWrapper", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(sNumSlots), - JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_AddProperty), - JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_DelProperty), - JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_GetProperty), - JS_VALUEIFY(js::StrictPropertyOp, ObjectWrapperParent::CPOW_SetProperty), + ObjectWrapperParent::CPOW_AddProperty, + ObjectWrapperParent::CPOW_DelProperty, + ObjectWrapperParent::CPOW_GetProperty, + ObjectWrapperParent::CPOW_SetProperty, (JSEnumerateOp) ObjectWrapperParent::CPOW_NewEnumerate, (JSResolveOp) ObjectWrapperParent::CPOW_NewResolve, - JS_VALUEIFY(js::ConvertOp, ObjectWrapperParent::CPOW_Convert), + ObjectWrapperParent::CPOW_Convert, ObjectWrapperParent::CPOW_Finalize, nsnull, // reserved1 nsnull, // checkAccess - JS_VALUEIFY(js::CallOp, ObjectWrapperParent::CPOW_Call), - JS_VALUEIFY(js::CallOp, ObjectWrapperParent::CPOW_Construct), + ObjectWrapperParent::CPOW_Call, + ObjectWrapperParent::CPOW_Construct, nsnull, // xdrObject - JS_VALUEIFY(js::HasInstanceOp, ObjectWrapperParent::CPOW_HasInstance), + ObjectWrapperParent::CPOW_HasInstance, nsnull, // mark { - JS_VALUEIFY(js::EqualityOp, ObjectWrapperParent::CPOW_Equality), + ObjectWrapperParent::CPOW_Equality, nsnull, // outerObject nsnull, // innerObject nsnull, // iteratorObject @@ -202,7 +192,7 @@ void ObjectWrapperParent::ActorDestroy(ActorDestroyReason) { if (mObj) { - mObj->setPrivate(NULL); + JS_SetPrivate(mObj, NULL); mObj = NULL; } } @@ -217,25 +207,28 @@ ObjectWrapperParent::Manager() JSObject* ObjectWrapperParent::GetJSObject(JSContext* cx) const { - js::Class *clasp = const_cast(&ObjectWrapperParent::sCPOW_JSClass); - if (!mObj && (mObj = JS_NewObject(cx, js::Jsvalify(clasp), NULL, NULL))) { - JS_SetPrivate(cx, mObj, (void*)this); - JS_SetReservedSlot(cx, mObj, sFlagsSlot, JSVAL_ZERO); + if (!mObj) { + js::Class *clasp = const_cast(&ObjectWrapperParent::sCPOW_JSClass); + mObj = JS_NewObject(cx, js::Jsvalify(clasp), NULL, NULL); + if (mObj) { + JS_SetPrivate(mObj, (void*)this); + JS_SetReservedSlot(mObj, sFlagsSlot, JSVAL_ZERO); + } } return mObj; } static ObjectWrapperParent* -Unwrap(JSContext* cx, JSObject* obj) +Unwrap(JSObject* obj) { - while (obj->getClass() != &ObjectWrapperParent::sCPOW_JSClass) - if (!(obj = obj->getProto())) + while (js::GetObjectClass(obj) != &ObjectWrapperParent::sCPOW_JSClass) + if (!(obj = js::GetObjectProto(obj))) return NULL; ObjectWrapperParent* self = - static_cast(JS_GetPrivate(cx, obj)); + static_cast(JS_GetPrivate(obj)); - NS_ASSERTION(!self || self->GetJSObject(cx) == obj, + NS_ASSERTION(!self || self->GetJSObjectOrNull() == obj, "Wrapper and wrapped object disagree?"); return self; @@ -260,7 +253,7 @@ ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from, case JSTYPE_OBJECT: { PObjectWrapperParent* powp; - if (!JSObject_to_PObjectWrapperParent(cx, JSVAL_TO_OBJECT(from), &powp)) + if (!JSObject_to_PObjectWrapperParent(JSVAL_TO_OBJECT(from), &powp)) return with_error(cx, false, "Cannot pass parent-created object to child"); *to = powp; } @@ -323,14 +316,13 @@ ObjectWrapperParent::jsval_from_JSVariant(JSContext* cx, const JSVariant& from, /*static*/ bool ObjectWrapperParent:: -JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from, - PObjectWrapperParent** to) +JSObject_to_PObjectWrapperParent(JSObject* from, PObjectWrapperParent** to) { if (!from) { *to = NULL; return true; } - ObjectWrapperParent* owp = Unwrap(cx, from); + ObjectWrapperParent* owp = Unwrap(from); if (!owp) return false; *to = owp; @@ -402,11 +394,11 @@ ObjectWrapperParent::CPOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, CPOW_LOG(("Calling CPOW_AddProperty (%s)...", JSVAL_TO_CSTR(cx, id))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_AddProperty"); - if (AutoResolveFlag::IsSet(cx, obj)) + if (AutoResolveFlag::IsSet(obj)) return JS_TRUE; AutoCheckOperation aco(cx, self); @@ -429,7 +421,7 @@ ObjectWrapperParent::CPOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, CPOW_LOG(("Calling CPOW_GetProperty (%s)...", JSVAL_TO_CSTR(cx, id))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_GetProperty"); @@ -456,7 +448,7 @@ ObjectWrapperParent::CPOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, CPOW_LOG(("Calling CPOW_SetProperty (%s)...", JSVAL_TO_CSTR(cx, id))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_SetProperty"); @@ -485,7 +477,7 @@ ObjectWrapperParent::CPOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, CPOW_LOG(("Calling CPOW_DelProperty (%s)...", JSVAL_TO_CSTR(cx, id))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_DelProperty"); @@ -539,7 +531,7 @@ ObjectWrapperParent::NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp) jsid_from_nsString(cx, out_id, idp)) { JSObject* obj = GetJSObject(cx); - AutoResolveFlag arf(cx, obj); + AutoResolveFlag arf(obj); return JS_DefinePropertyById(cx, obj, *idp, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE); } @@ -566,7 +558,7 @@ ObjectWrapperParent::CPOW_NewEnumerate(JSContext *cx, JSObject *obj, { CPOW_LOG(("Calling CPOW_NewEnumerate...")); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewEnumerate"); @@ -592,7 +584,7 @@ ObjectWrapperParent::CPOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, CPOW_LOG(("Calling CPOW_NewResolve (%s)...", JSVAL_TO_CSTR(cx, id))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewResolve"); @@ -613,7 +605,7 @@ ObjectWrapperParent::CPOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, return JS_FALSE; if (*objp) { - AutoResolveFlag arf(cx, *objp); + AutoResolveFlag arf(*objp); JS_DefinePropertyById(cx, *objp, id, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE); } @@ -627,7 +619,7 @@ ObjectWrapperParent::CPOW_Convert(JSContext *cx, JSObject *obj, JSType type, CPOW_LOG(("Calling CPOW_Convert (to %s)...", JS_GetTypeName(cx, type))); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Convert"); @@ -641,7 +633,7 @@ ObjectWrapperParent::CPOW_Finalize(JSContext* cx, JSObject* obj) { CPOW_LOG(("Calling CPOW_Finalize...")); - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (self) { self->mObj = NULL; unused << ObjectWrapperParent::Send__delete__(self); @@ -658,13 +650,13 @@ ObjectWrapperParent::CPOW_Call(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; ObjectWrapperParent* function = - Unwrap(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); + Unwrap(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); if (!function) return with_error(cx, JS_FALSE, "Could not unwrap CPOW function"); AutoCheckOperation aco(cx, function); - ObjectWrapperParent* receiver = Unwrap(cx, thisobj); + ObjectWrapperParent* receiver = Unwrap(thisobj); if (!receiver) { // Substitute child global for parent global object. // TODO First make sure we're really replacing the global object? @@ -693,7 +685,7 @@ ObjectWrapperParent::CPOW_Construct(JSContext* cx, uintN argc, jsval* vp) { CPOW_LOG(("Calling CPOW_Construct...")); - ObjectWrapperParent* constructor = Unwrap(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); + ObjectWrapperParent* constructor = Unwrap(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); if (!constructor) return with_error(cx, JS_FALSE, "Could not unwrap CPOW constructor function"); @@ -721,7 +713,7 @@ ObjectWrapperParent::CPOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *bp = JS_FALSE; - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_HasInstance"); @@ -746,14 +738,14 @@ ObjectWrapperParent::CPOW_Equality(JSContext *cx, JSObject *obj, const jsval *v, *bp = JS_FALSE; - ObjectWrapperParent* self = Unwrap(cx, obj); + ObjectWrapperParent* self = Unwrap(obj); if (!self) return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Equality"); if (JSVAL_IS_PRIMITIVE(*v)) return JS_TRUE; - ObjectWrapperParent* other = Unwrap(cx, JSVAL_TO_OBJECT(*v)); + ObjectWrapperParent* other = Unwrap(JSVAL_TO_OBJECT(*v)); if (!other) return JS_TRUE; diff --git a/deps/mozjs/js/ipc/ObjectWrapperParent.h b/deps/mozjs/js/ipc/ObjectWrapperParent.h index bf48f9ae2ed..95388280ee6 100644 --- a/deps/mozjs/js/ipc/ObjectWrapperParent.h +++ b/deps/mozjs/js/ipc/ObjectWrapperParent.h @@ -43,7 +43,7 @@ #include "mozilla/jsipc/PObjectWrapperParent.h" #include "jsapi.h" -#include "jsvalue.h" +#include "jsclass.h" #include "nsAutoJSValHolder.h" namespace mozilla { @@ -69,6 +69,10 @@ class ObjectWrapperParent JSObject* GetJSObject(JSContext* cx) const; + JSObject* GetJSObjectOrNull() const { + return mObj; + } + jsval GetJSVal(JSContext* cx) const { return OBJECT_TO_JSVAL(GetJSObject(cx)); } @@ -133,8 +137,7 @@ class ObjectWrapperParent static bool jsval_from_JSVariant(JSContext* cx, const JSVariant& from, jsval* to); static bool - JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from, - PObjectWrapperParent** to); + JSObject_to_PObjectWrapperParent(JSObject* from, PObjectWrapperParent** to); static bool JSObject_from_PObjectWrapperParent(JSContext* cx, const PObjectWrapperParent* from, diff --git a/deps/mozjs/js/jetpack/Handle.h b/deps/mozjs/js/jetpack/Handle.h deleted file mode 100644 index c16e8e1d175..00000000000 --- a/deps/mozjs/js/jetpack/Handle.h +++ /dev/null @@ -1,373 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Newman (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_HandleParent_h -#define mozilla_jetpack_HandleParent_h - -#include "mozilla/jetpack/PHandleParent.h" -#include "mozilla/jetpack/PHandleChild.h" - -#include "jsapi.h" -#include "jsobj.h" -#include "jscntxt.h" - -#include "mozilla/unused.h" - -namespace mozilla { -namespace jetpack { - -/** - * BaseType should be one of PHandleParent or PHandleChild; see the - * HandleParent and HandleChild typedefs at the bottom of this file. - */ -template -class Handle - : public BaseType -{ - Handle(Handle* parent) - : mParent(parent) - , mObj(NULL) - , mCx(NULL) - , mRooted(false) - {} - - BaseType* AllocPHandle() { - return new Handle(this); - } - - bool DeallocPHandle(BaseType* actor) { - delete actor; - return true; - } - -public: - - Handle() - : mParent(NULL) - , mObj(NULL) - , mCx(NULL) - , mRooted(false) - {} - - ~Handle() { TearDown(); } - - static Handle* FromJSObject(JSContext* cx, JSObject* obj) { - // TODO Convert non-Handles to Handles somehow? - return Unwrap(cx, obj); - } - - static Handle* FromJSVal(JSContext* cx, jsval val) { - if (!JSVAL_IS_OBJECT(val)) - return NULL; - return Unwrap(cx, JSVAL_TO_OBJECT(val)); - } - - void Root() { - NS_ASSERTION(mObj && mCx, "Rooting with no object unexpected."); - if (mRooted) - return; - - if (!JS_AddNamedObjectRoot(mCx, &mObj, "Jetpack Handle")) { - NS_RUNTIMEABORT("Failed to add root."); - } - mRooted = true; - } - - void Unroot() { - NS_ASSERTION(mCx, "Unrooting with no JSContext unexpected."); - if (!mRooted) - return; - - JS_RemoveObjectRoot(mCx, &mObj); - mRooted = false; - } - - JSObject* ToJSObject(JSContext* cx) { - if (!mObj && !mCx) { - JSClass* clasp = const_cast(&sHandle_JSClass); - JSObject* obj = JS_NewObject(cx, clasp, NULL, NULL); - if (!obj) - return NULL; - js::AutoObjectRooter root(cx, obj); - - JSPropertySpec* ps = const_cast(sHandle_Properties); - JSFunctionSpec* fs = const_cast(sHandle_Functions); - - if (JS_SetPrivate(cx, obj, (void*)this) && - JS_DefineProperties(cx, obj, ps) && - JS_DefineFunctions(cx, obj, fs)) { - mObj = obj; - mCx = cx; - Root(); - } - } - return mObj; - } - -protected: - - void ActorDestroy(typename Handle::ActorDestroyReason why) { - TearDown(); - } - -private: - - static bool IsParent(const PHandleParent* handle) { return true; } - static bool IsParent(const PHandleChild* handle) { return false; } - - void TearDown() { - if (mCx) { - JSAutoRequest ar(mCx); - - if (mObj) { - mObj->setPrivate(NULL); - - js::AutoObjectRooter obj(mCx, mObj); - mObj = NULL; - - // If we can't enter the compartment, we won't run onInvalidate(). - JSAutoEnterCompartment ac; - if (ac.enter(mCx, obj.object())) { - JSBool hasOnInvalidate; - if (JS_HasProperty(mCx, obj.object(), "onInvalidate", - &hasOnInvalidate) && hasOnInvalidate) { - js::AutoValueRooter r(mCx); - JSBool ok = JS_CallFunctionName(mCx, obj.object(), "onInvalidate", 0, - NULL, r.jsval_addr()); - if (!ok) - JS_ReportPendingException(mCx); - } - } - - // By not nulling out mContext, we prevent ToJSObject from - // reviving an invalidated/destroyed handle. - } - - // Nulling out mObj effectively unroots the object, but we still - // need to remove the root, else the JS engine will complain at - // shutdown. - Unroot(); - } - } - - static const JSClass sHandle_JSClass; - static const JSPropertySpec sHandle_Properties[]; - static const JSFunctionSpec sHandle_Functions[]; - - Handle* const mParent; - - // Used to cache the JSObject returned by ToJSObject, which is - // otherwise a const method. - JSObject* mObj; - JSContext* mCx; - bool mRooted; - - static Handle* - Unwrap(JSContext* cx, JSObject* obj) { - while (obj && obj->getJSClass() != &sHandle_JSClass) - obj = obj->getProto(); - - if (!obj) - return NULL; - - Handle* self = static_cast(JS_GetPrivate(cx, obj)); - - NS_ASSERTION(!self || self->ToJSObject(cx) == obj, - "Wrapper and wrapped object disagree?"); - - return self; - } - - static JSBool - GetParent(JSContext* cx, JSObject* obj, jsid, jsval* vp) { - JS_SET_RVAL(cx, vp, JSVAL_NULL); - - Handle* self = Unwrap(cx, obj); - if (!self) - return JS_TRUE; - - Handle* parent = self->mParent; - if (!parent) - return JS_TRUE; - - JSObject* pobj = parent->ToJSObject(cx); - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(pobj)); - - return JS_TRUE; - } - - static JSBool - GetIsValid(JSContext* cx, JSObject* obj, jsid, jsval* vp) { - Handle* self = Unwrap(cx, obj); - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(!!self)); - return JS_TRUE; - } - - static JSBool - GetIsRooted(JSContext* cx, JSObject* obj, jsid, jsval* vp) { - Handle* self = Unwrap(cx, obj); - bool rooted = self ? self->mRooted : false; - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(rooted)); - return JS_TRUE; - } - - static JSBool - SetIsRooted(JSContext* cx, JSObject* obj, jsid, JSBool strict, jsval* vp) { - Handle* self = Unwrap(cx, obj); - JSBool v; - if (!JS_ValueToBoolean(cx, *vp, &v)) - return JS_FALSE; - - if (!self) { - if (v) { - JS_ReportError(cx, "Cannot root invalidated handle."); - return JS_FALSE; - } - return JS_TRUE; - } - - if (v) - self->Root(); - else - self->Unroot(); - - *vp = BOOLEAN_TO_JSVAL(v); - return JS_TRUE; - } - - static JSBool - Invalidate(JSContext* cx, uintN argc, jsval* vp) { - if (argc > 0) { - JS_ReportError(cx, "invalidate takes zero arguments"); - return JS_FALSE; - } - - Handle* self = Unwrap(cx, JS_THIS_OBJECT(cx, vp)); - if (self) - unused << BaseType::Send__delete__(self); - - JS_SET_RVAL(cx, vp, JSVAL_VOID); - - return JS_TRUE; - } - - static JSBool - CreateHandle(JSContext* cx, uintN argc, jsval* vp) { - if (argc > 0) { - JS_ReportError(cx, "createHandle takes zero arguments"); - return JS_FALSE; - } - - Handle* self = Unwrap(cx, JS_THIS_OBJECT(cx, vp)); - if (!self) { - JS_ReportError(cx, "Tried to create child from invalid handle"); - return JS_FALSE; - } - - BaseType* child = self->SendPHandleConstructor(); - if (!child) { - JS_ReportError(cx, "Failed to construct child"); - return JS_FALSE; - } - - JSObject* obj = static_cast(child)->ToJSObject(cx); - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); - - return JS_TRUE; - } - - static void - Finalize(JSContext* cx, JSObject* obj) { - Handle* self = Unwrap(cx, obj); - if (self) { - NS_ASSERTION(!self->mRooted, "Finalizing a rooted object?"); - self->mCx = NULL; - self->mObj = NULL; - unused << BaseType::Send__delete__(self); - } - } -}; - -template -const JSClass -Handle::sHandle_JSClass = { - "IPDL Handle", JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, Handle::Finalize, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#define HANDLE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_SHARED) - -template -const JSPropertySpec -Handle::sHandle_Properties[] = { - { "parent", 0, HANDLE_PROP_FLAGS | JSPROP_READONLY, GetParent, NULL }, - { "isValid", 0, HANDLE_PROP_FLAGS | JSPROP_READONLY, GetIsValid, NULL }, - { "isRooted", 0, HANDLE_PROP_FLAGS, GetIsRooted, SetIsRooted }, - { 0, 0, 0, NULL, NULL } -}; - -#undef HANDLE_PROP_FLAGS - -#define HANDLE_FUN_FLAGS (JSPROP_READONLY | \ - JSPROP_PERMANENT) - -template -const JSFunctionSpec -Handle::sHandle_Functions[] = { - JS_FN("invalidate", Invalidate, 0, HANDLE_FUN_FLAGS), - JS_FN("createHandle", CreateHandle, 0, HANDLE_FUN_FLAGS), - JS_FS_END -}; - -#undef HANDLE_FUN_FLAGS - -// The payoff for using templates is that these two implementations are -// guaranteed to be perfectly symmetric: -typedef Handle HandleParent; -typedef Handle HandleChild; - -} // namespace jetpack -} // namespace mozilla - -#endif diff --git a/deps/mozjs/js/jetpack/JetpackActorCommon.cpp b/deps/mozjs/js/jetpack/JetpackActorCommon.cpp deleted file mode 100644 index 8a9bb09d663..00000000000 --- a/deps/mozjs/js/jetpack/JetpackActorCommon.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Newman (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "base/basictypes.h" -#include "jscntxt.h" - -#include "jsapi.h" -#include "jshashtable.h" - -#include "mozilla/jetpack/JetpackActorCommon.h" -#include "mozilla/jetpack/PJetpack.h" -#include "mozilla/jetpack/PHandleParent.h" -#include "mozilla/jetpack/PHandleChild.h" -#include "mozilla/jetpack/Handle.h" - -#include "nsJSUtils.h" - -using namespace mozilla::jetpack; - -class JetpackActorCommon::OpaqueSeenType -{ -public: - typedef JSObject* KeyType; - typedef size_t IdType; - typedef js::HashMap< - KeyType, IdType, - js::DefaultHasher, - js::SystemAllocPolicy - > MapType; - - OpaqueSeenType() { - (void) map.init(1); - } - - bool ok() { return map.initialized(); } - - // Reserving 0 as an invalid ID means starting the valid IDs at 1. We - // could have reserved a dummy first element of rmap, but that would - // have wasted space. - static const IdType kInvalidId = 0; - - bool add(KeyType obj, IdType* id) { - MapType::AddPtr ap = map.lookupForAdd(obj); - if (!ap) { - if (!rmap.AppendElement(obj) || - !map.add(ap, obj, *id = rmap.Length())) - *id = kInvalidId; - return true; - } - *id = ap->value; - return false; - } - - KeyType reverseLookup(IdType id) { - return rmap.SafeElementAt(id - 1, NULL); - } - -private: - MapType map; - nsAutoTArray rmap; - -}; - -bool -JetpackActorCommon::jsval_to_PrimVariant(JSContext* cx, JSType type, jsval from, - PrimVariant* to) -{ - // A false return from this function just means the value couldn't be - // converted to a PrimVariant (i.e., it wasn't a primitive value). - - switch (type) { - case JSTYPE_VOID: - *to = void_t(); - return true; - - case JSTYPE_NULL: - *to = null_t(); - return true; - - case JSTYPE_FUNCTION: - return false; - - case JSTYPE_OBJECT: { - HandleParent* hp = HandleParent::FromJSVal(cx, from); - HandleChild* hc = HandleChild::FromJSVal(cx, from); - NS_ASSERTION(!hc || !hp, "Can't be both a parent and a child"); - if (hp) { - *to = hp; - return true; - } - if (hc) { - *to = hc; - return true; - } - return false; - } - - case JSTYPE_STRING: - { - nsDependentJSString depStr; - if (!depStr.init(cx, from)) - return false; - *to = depStr; - } - return true; - - case JSTYPE_NUMBER: - if (JSVAL_IS_INT(from)) - *to = JSVAL_TO_INT(from); - else if (JSVAL_IS_DOUBLE(from)) - *to = JSVAL_TO_DOUBLE(from); - else - return false; - return true; - - case JSTYPE_BOOLEAN: - *to = !!JSVAL_TO_BOOLEAN(from); - return true; - - case JSTYPE_XML: - return false; - - default: - return false; - } -} - -bool -JetpackActorCommon::jsval_to_CompVariant(JSContext* cx, JSType type, jsval from, - CompVariant* to, OpaqueSeenType* seen) -{ - if (type != JSTYPE_OBJECT) - return false; - - Maybe lost; - if (!seen) { - lost.construct(); - seen = lost.addr(); - if (!seen->ok()) - return false; - } - - OpaqueSeenType::KeyType obj = JSVAL_TO_OBJECT(from); - OpaqueSeenType::IdType id; - if (!seen->add(obj, &id)) { - if (OpaqueSeenType::kInvalidId == id) - return false; - *to = CompVariant(id); - return true; - } - - if (JS_IsArrayObject(cx, obj)) { - nsTArray elems; - jsuint len; - if (!JS_GetArrayLength(cx, obj, &len) || - !elems.SetCapacity(len)) - return false; - for (jsuint i = 0; i < len; ++i) { - jsval val; - Variant* vp = elems.AppendElement(); - if (!JS_GetElement(cx, obj, i, &val) || - !jsval_to_Variant(cx, val, vp, seen)) - *vp = void_t(); - } - InfallibleTArray outElems; - outElems.SwapElements(elems); - *to = outElems; - return true; - } - - js::AutoIdArray ida(cx, JS_Enumerate(cx, obj)); - if (!ida) - return false; - - nsTArray kvs; - for (size_t i = 0; i < ida.length(); ++i) { - jsval val; // reused for both key and value - if (!JS_IdToValue(cx, ida[i], &val)) - return false; - JSString* idStr = JS_ValueToString(cx, val); - if (!idStr) - return false; - if (!JS_GetPropertyById(cx, obj, ida[i], &val)) - return false; - KeyValue kv; - // Silently drop properties that can't be converted. - if (jsval_to_Variant(cx, val, &kv.value(), seen)) { - nsDependentJSString depStr; - if (!depStr.init(cx, idStr)) - return false; - kv.key() = depStr; - // If AppendElement fails, we lose this property, no big deal. - kvs.AppendElement(kv); - } - } - InfallibleTArray outKvs; - outKvs.SwapElements(kvs); - *to = outKvs; - - return true; -} - -bool -JetpackActorCommon::jsval_to_Variant(JSContext* cx, jsval from, Variant* to, - OpaqueSeenType* seen) -{ - JSType type = JS_TypeOfValue(cx, from); - if (JSVAL_IS_NULL(from)) - type = JSTYPE_NULL; - - PrimVariant pv; - if (jsval_to_PrimVariant(cx, type, from, &pv)) { - *to = pv; - return true; - } - - CompVariant cv; - if (jsval_to_CompVariant(cx, type, from, &cv, seen)) { - *to = cv; - return true; - } - - return false; -} - -bool -JetpackActorCommon::jsval_from_PrimVariant(JSContext* cx, - const PrimVariant& from, - jsval* to) -{ - switch (from.type()) { - case PrimVariant::Tvoid_t: - *to = JSVAL_VOID; - return true; - - case PrimVariant::Tnull_t: - *to = JSVAL_NULL; - return true; - - case PrimVariant::Tbool: - *to = from.get_bool() ? JSVAL_TRUE : JSVAL_FALSE; - return true; - - case PrimVariant::Tint: - *to = INT_TO_JSVAL(from.get_int()); - return true; - - case PrimVariant::Tdouble: - return !!JS_NewNumberValue(cx, from.get_double(), to); - - case PrimVariant::TnsString: { - const nsString& str = from.get_nsString(); - // TODO Use some sort of sharedstring/stringbuffer abstraction to - // exploit sharing opportunities more generally. - if (!str.Length()) { - *to = JS_GetEmptyStringValue(cx); - return true; - } - JSString* s = - JS_NewUCStringCopyN(cx, str.get(), str.Length()); - if (!s) - return false; - *to = STRING_TO_JSVAL(s); - return true; - } - - case PrimVariant::TPHandleParent: { - JSObject* hobj = - static_cast(from.get_PHandleParent())->ToJSObject(cx); - if (!hobj) - return false; - *to = OBJECT_TO_JSVAL(hobj); - return true; - } - - case PrimVariant::TPHandleChild: { - JSObject* hobj = - static_cast(from.get_PHandleChild())->ToJSObject(cx); - if (!hobj) - return false; - *to = OBJECT_TO_JSVAL(hobj); - return true; - } - - default: - return false; - } -} - -bool -JetpackActorCommon::jsval_from_CompVariant(JSContext* cx, - const CompVariant& from, - jsval* to, - OpaqueSeenType* seen) -{ - Maybe lost; - if (!seen) { - lost.construct(); - seen = lost.addr(); - if (!seen->ok()) - return false; - } - - JSObject* obj = NULL; - - switch (from.type()) { - case CompVariant::TArrayOfKeyValue: { - if (!(obj = JS_NewObject(cx, NULL, NULL, NULL))) - return false; - js::AutoObjectRooter root(cx, obj); - - OpaqueSeenType::IdType ignored; - if (!seen->add(obj, &ignored)) - return false; - - const nsTArray& kvs = from.get_ArrayOfKeyValue(); - for (PRUint32 i = 0; i < kvs.Length(); ++i) { - const KeyValue& kv = kvs.ElementAt(i); - js::AutoValueRooter toSet(cx); - if (!jsval_from_Variant(cx, kv.value(), toSet.jsval_addr(), seen) || - !JS_SetUCProperty(cx, obj, - kv.key().get(), - kv.key().Length(), - toSet.jsval_addr())) - return false; - } - - break; - } - - case CompVariant::TArrayOfVariant: { - const nsTArray& vs = from.get_ArrayOfVariant(); - nsAutoTArray jsvals; - jsval* elems = jsvals.AppendElements(vs.Length()); - if (!elems) - return false; - for (PRUint32 i = 0; i < vs.Length(); ++i) - elems[i] = JSVAL_VOID; - js::AutoArrayRooter root(cx, vs.Length(), elems); - - OpaqueSeenType::IdType ignored; - if (!seen->add(obj, &ignored)) - return false; - - for (PRUint32 i = 0; i < vs.Length(); ++i) - if (!jsval_from_Variant(cx, vs.ElementAt(i), elems + i, seen)) - return false; - - if (!(obj = JS_NewArrayObject(cx, vs.Length(), elems))) - return false; - - break; - } - - case CompVariant::Tsize_t: - if (!(obj = seen->reverseLookup(from.get_size_t()))) - return false; - break; - - default: - return false; - } - - *to = OBJECT_TO_JSVAL(obj); - return true; -} - -bool -JetpackActorCommon::jsval_from_Variant(JSContext* cx, const Variant& from, - jsval* to, OpaqueSeenType* seen) -{ - switch (from.type()) { - case Variant::TPrimVariant: - return jsval_from_PrimVariant(cx, from, to); - case Variant::TCompVariant: - return jsval_from_CompVariant(cx, from, to, seen); - default: - return false; - } -} - -bool -JetpackActorCommon::RecvMessage(JSContext* cx, - const nsString& messageName, - const InfallibleTArray& data, - InfallibleTArray* results) -{ - if (results) - results->Clear(); - - JSObject* implGlobal = JS_GetGlobalObject(cx); - - JSAutoEnterCompartment ac; - if (!ac.enter(cx, implGlobal)) - return false; - - RecList* list; - if (!mReceivers.Get(messageName, &list)) - return true; - - nsAutoTArray snapshot; - if (!list->copyTo(cx, snapshot)) - return false; - if (!snapshot.Length()) - return true; - - nsAutoTArray args; - PRUint32 argc = data.Length() + 1; - jsval* argv = args.AppendElements(argc); - if (!argv) - return false; - for (PRUint32 i = 0; i < argc; ++i) - argv[i] = JSVAL_VOID; - js::AutoArrayRooter argvRooter(cx, argc, argv); - - JSString* msgNameStr = - JS_NewUCStringCopyN(cx, - messageName.get(), - messageName.Length()); - if (!msgNameStr) - return false; - argv[0] = STRING_TO_JSVAL(msgNameStr); - - for (PRUint32 i = 0; i < data.Length(); ++i) - if (!jsval_from_Variant(cx, data.ElementAt(i), argv + i + 1)) - return false; - - js::AutoValueRooter rval(cx); - for (PRUint32 i = 0; i < snapshot.Length(); ++i) { - Variant* vp = results ? results->AppendElement() : NULL; - rval.set(JSVAL_VOID); - if (!JS_CallFunctionValue(cx, implGlobal, snapshot[i], argc, argv, - rval.jsval_addr())) { - (void) JS_ReportPendingException(cx); - if (vp) - *vp = void_t(); - } else if (vp && !jsval_to_Variant(cx, rval.jsval_value(), vp)) - *vp = void_t(); - } - - return true; -} - -JetpackActorCommon::RecList::~RecList() -{ - while (mHead) { - RecNode* old = mHead; - mHead = mHead->down; - delete old; - } -} - -void -JetpackActorCommon::RecList::add(jsval v) -{ - RecNode* node = mHead, *tail = NULL; - while (node) { - if (node->value() == v) - return; - node = (tail = node)->down; - } - node = new RecNode(mCx, v); - if (tail) - tail->down = node; - else - mHead = node; -} - -void -JetpackActorCommon::RecList::remove(jsval v) -{ - while (mHead && mHead->value() == v) { - RecNode* old = mHead; - mHead = mHead->down; - delete old; - } - if (!mHead) - return; - RecNode* prev = mHead, *node = prev->down; - while (node) { - if (node->value() == v) { - prev->down = node->down; - delete node; - } else - prev = node; - node = prev->down; - } -} - -bool -JetpackActorCommon::RecList::copyTo(JSContext *cx, nsTArray& dst) const -{ - dst.Clear(); - for (RecNode* node = mHead; node; node = node->down) { - jsval v = node->value(); - if (!JS_WrapValue(cx, &v)) - return false; - dst.AppendElement(v); - } - return true; -} - -nsresult -JetpackActorCommon::RegisterReceiver(JSContext* cx, - const nsString& messageName, - jsval receiver) -{ - if (JS_TypeOfValue(cx, receiver) != JSTYPE_FUNCTION) - return NS_ERROR_INVALID_ARG; - - RecList* list; - if (!mReceivers.Get(messageName, &list)) { - list = new RecList(cx); - if (!list || !mReceivers.Put(messageName, list)) { - delete list; - return NS_ERROR_OUT_OF_MEMORY; - } - } - - list->add(receiver); - - return NS_OK; -} - -void -JetpackActorCommon::UnregisterReceiver(const nsString& messageName, - jsval receiver) -{ - RecList* list; - if (!mReceivers.Get(messageName, &list)) - return; - list->remove(receiver); -} diff --git a/deps/mozjs/js/jetpack/JetpackActorCommon.h b/deps/mozjs/js/jetpack/JetpackActorCommon.h deleted file mode 100644 index 2c110f66e31..00000000000 --- a/deps/mozjs/js/jetpack/JetpackActorCommon.h +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Newman (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackActorCommon_h -#define mozilla_jetpack_JetpackActorCommon_h - -#include "nsClassHashtable.h" -#include "nsHashKeys.h" -#include "nsTArray.h" -#include "nsString.h" -#include "nsAutoJSValHolder.h" - -struct JSContext; - -namespace mozilla { -namespace jetpack { - -class KeyValue; -class PrimVariant; -class CompVariant; -class Variant; - -class JetpackActorCommon -{ -public: - - bool - RecvMessage(JSContext* cx, - const nsString& messageName, - const InfallibleTArray& data, - InfallibleTArray* results); - - nsresult - RegisterReceiver(JSContext* cx, - const nsString& messageName, - jsval receiver); - - void - UnregisterReceiver(const nsString& messageName, - jsval receiver); - - void - UnregisterReceivers(const nsString& messageName) { - mReceivers.Remove(messageName); - } - - void ClearReceivers() { - mReceivers.Clear(); - } - - class OpaqueSeenType; - static bool jsval_to_Variant(JSContext* cx, jsval from, Variant* to, - OpaqueSeenType* seen = NULL); - static bool jsval_from_Variant(JSContext* cx, const Variant& from, jsval* to, - OpaqueSeenType* seen = NULL); - -protected: - - JetpackActorCommon() { - mReceivers.Init(); - NS_ASSERTION(mReceivers.IsInitialized(), - "Failed to initialize message receiver hash set"); - } - -private: - - static bool jsval_to_PrimVariant(JSContext* cx, JSType type, jsval from, - PrimVariant* to); - static bool jsval_to_CompVariant(JSContext* cx, JSType type, jsval from, - CompVariant* to, OpaqueSeenType* seen); - - static bool jsval_from_PrimVariant(JSContext* cx, const PrimVariant& from, - jsval* to); - static bool jsval_from_CompVariant(JSContext* cx, const CompVariant& from, - jsval* to, OpaqueSeenType* seen); - - // Don't want to be memcpy'ing nsAutoJSValHolders around, so we need a - // linked list of receivers. - class RecList - { - JSContext* mCx; - class RecNode - { - nsAutoJSValHolder mHolder; - public: - RecNode* down; - RecNode(JSContext* cx, jsval v) : down(NULL) { - mHolder.Hold(cx); - mHolder = v; - } - jsval value() { return mHolder; } - }* mHead; - public: - RecList(JSContext* cx) : mCx(cx), mHead(NULL) {} - ~RecList(); - void add(jsval v); - void remove(jsval v); - bool copyTo(JSContext *cx, nsTArray& dst) const; - }; - - nsClassHashtable mReceivers; - -}; - -} // namespace jetpack -} // namespace mozilla - -#endif diff --git a/deps/mozjs/js/jetpack/JetpackChild.cpp b/deps/mozjs/js/jetpack/JetpackChild.cpp deleted file mode 100644 index d908b8f1e12..00000000000 --- a/deps/mozjs/js/jetpack/JetpackChild.cpp +++ /dev/null @@ -1,590 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "base/basictypes.h" -#include "jscntxt.h" -#include "nsXULAppAPI.h" -#include "nsNativeCharsetUtils.h" - -#include "mozilla/jetpack/JetpackChild.h" -#include "mozilla/jetpack/Handle.h" -#include "mozilla/IntentionalCrash.h" - -#include "jsarray.h" - -#include - -namespace mozilla { -namespace jetpack { - -JetpackChild::JetpackChild() -{ -} - -JetpackChild::~JetpackChild() -{ -} - -#define IMPL_METHOD_FLAGS (JSPROP_ENUMERATE | \ - JSPROP_READONLY | \ - JSPROP_PERMANENT) -const JSFunctionSpec -JetpackChild::sImplMethods[] = { - JS_FN("sendMessage", SendMessage, 3, IMPL_METHOD_FLAGS), - JS_FN("callMessage", CallMessage, 2, IMPL_METHOD_FLAGS), - JS_FN("registerReceiver", RegisterReceiver, 2, IMPL_METHOD_FLAGS), - JS_FN("unregisterReceiver", UnregisterReceiver, 2, IMPL_METHOD_FLAGS), - JS_FN("unregisterReceivers", UnregisterReceivers, 1, IMPL_METHOD_FLAGS), - JS_FN("createHandle", CreateHandle, 0, IMPL_METHOD_FLAGS), - JS_FN("createSandbox", CreateSandbox, 0, IMPL_METHOD_FLAGS), - JS_FN("evalInSandbox", EvalInSandbox, 2, IMPL_METHOD_FLAGS), - JS_FN("gc", GC, 0, IMPL_METHOD_FLAGS), -#ifdef JS_GC_ZEAL - JS_FN("gczeal", GCZeal, 1, IMPL_METHOD_FLAGS), -#endif - JS_FN("_noteIntentionalCrash", NoteIntentionalCrash, 0, - IMPL_METHOD_FLAGS), - JS_FS_END -}; - -#undef IMPL_METHOD_FLAGS - -const JSClass -JetpackChild::sGlobalClass = { - "JetpackChild::sGlobalClass", JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#ifdef BUILD_CTYPES -static char* -UnicodeToNative(JSContext *cx, const jschar *source, size_t slen) -{ - nsCAutoString native; - nsDependentString unicode(source, slen); - nsresult rv = NS_CopyUnicodeToNative(unicode, native); - if (NS_FAILED(rv)) { - JS_ReportError(cx, "could not convert string to native charset"); - return NULL; - } - - char* result = static_cast(JS_malloc(cx, native.Length() + 1)); - if (!result) - return NULL; - - memcpy(result, native.get(), native.Length() + 1); - return result; -} - -static JSCTypesCallbacks sCallbacks = { - UnicodeToNative -}; -#endif - -bool -JetpackChild::Init(base::ProcessHandle aParentProcessHandle, - MessageLoop* aIOLoop, - IPC::Channel* aChannel) -{ - if (!Open(aChannel, aParentProcessHandle, aIOLoop)) - return false; - - if (!(mRuntime = JS_NewRuntime(32L * 1024L * 1024L)) || - !(mCx = JS_NewContext(mRuntime, 8192))) - return false; - - JS_SetVersion(mCx, JSVERSION_LATEST); - JS_SetOptions(mCx, JS_GetOptions(mCx) | - JSOPTION_DONT_REPORT_UNCAUGHT | - JSOPTION_ATLINE | - JSOPTION_JIT); - JS_SetErrorReporter(mCx, ReportError); - - { - JSAutoRequest request(mCx); - JS_SetContextPrivate(mCx, this); - JSObject* implGlobal = - JS_NewCompartmentAndGlobalObject(mCx, const_cast(&sGlobalClass), NULL); - if (!implGlobal) - return false; - - JSAutoEnterCompartment ac; - if (!ac.enter(mCx, implGlobal)) - return false; - - jsval ctypes; - if (!JS_InitStandardClasses(mCx, implGlobal) || -#ifdef BUILD_CTYPES - !JS_InitCTypesClass(mCx, implGlobal) || - !JS_GetProperty(mCx, implGlobal, "ctypes", &ctypes) || - !JS_SetCTypesCallbacks(mCx, JSVAL_TO_OBJECT(ctypes), &sCallbacks) || -#endif - !JS_DefineFunctions(mCx, implGlobal, - const_cast(sImplMethods))) - return false; - } - - return true; -} - -void -JetpackChild::CleanUp() -{ - ClearReceivers(); - JS_DestroyContext(mCx); - JS_DestroyRuntime(mRuntime); - JS_ShutDown(); -} - -void -JetpackChild::ActorDestroy(ActorDestroyReason why) -{ - XRE_ShutdownChildProcess(); -} - -bool -JetpackChild::RecvSendMessage(const nsString& messageName, - const InfallibleTArray& data) -{ - JSAutoRequest request(mCx); - - JSObject *global = JS_GetGlobalObject(mCx); - JSAutoEnterCompartment ac; - if (!ac.enter(mCx, global)) - return false; - - return JetpackActorCommon::RecvMessage(mCx, messageName, data, NULL); -} - -bool -JetpackChild::RecvEvalScript(const nsString& code) -{ - JSAutoRequest request(mCx); - - JSObject *global = JS_GetGlobalObject(mCx); - JSAutoEnterCompartment ac; - if (!ac.enter(mCx, global)) - return false; - - jsval ignored; - (void) JS_EvaluateUCScript(mCx, global, code.get(), - code.Length(), "", 1, &ignored); - return true; -} - -PHandleChild* -JetpackChild::AllocPHandle() -{ - return new HandleChild(); -} - -bool -JetpackChild::DeallocPHandle(PHandleChild* actor) -{ - delete actor; - return true; -} - -JetpackChild* -JetpackChild::GetThis(JSContext* cx) -{ - JetpackChild* self = - static_cast(JS_GetContextPrivate(cx)); - JS_ASSERT(cx == self->mCx); - return self; -} - -struct MessageResult { - nsString msgName; - InfallibleTArray data; -}; - -static JSBool -MessageCommon(JSContext* cx, uintN argc, jsval* vp, - MessageResult* result) -{ - if (argc < 1) { - JS_ReportError(cx, "Message requires a name, at least"); - return JS_FALSE; - } - - jsval* argv = JS_ARGV(cx, vp); - - JSString* msgNameStr = JS_ValueToString(cx, argv[0]); - if (!msgNameStr) { - JS_ReportError(cx, "Could not convert value to string"); - return JS_FALSE; - } - - size_t length; - const jschar* chars = JS_GetStringCharsAndLength(cx, msgNameStr, &length); - if (!chars) - return JS_FALSE; - - result->msgName.Assign(chars, length); - - result->data.Clear(); - - if (!result->data.SetCapacity(argc)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - for (uintN i = 1; i < argc; ++i) { - Variant* vp = result->data.AppendElement(); - if (!JetpackActorCommon::jsval_to_Variant(cx, argv[i], vp)) { - JS_ReportError(cx, "Invalid message argument at position %d", i); - return JS_FALSE; - } - } - - return JS_TRUE; -} - -JSBool -JetpackChild::SendMessage(JSContext* cx, uintN argc, jsval* vp) -{ - MessageResult smr; - if (!MessageCommon(cx, argc, vp, &smr)) - return JS_FALSE; - - if (!GetThis(cx)->SendSendMessage(smr.msgName, smr.data)) { - JS_ReportError(cx, "Failed to sendMessage"); - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -JetpackChild::CallMessage(JSContext* cx, uintN argc, jsval* vp) -{ - MessageResult smr; - if (!MessageCommon(cx, argc, vp, &smr)) - return JS_FALSE; - - InfallibleTArray results; - if (!GetThis(cx)->CallCallMessage(smr.msgName, smr.data, &results)) { - JS_ReportError(cx, "Failed to callMessage"); - return JS_FALSE; - } - - nsAutoTArray jsvals; - jsval* rvals = jsvals.AppendElements(results.Length()); - if (!rvals) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - for (PRUint32 i = 0; i < results.Length(); ++i) - rvals[i] = JSVAL_VOID; - js::AutoArrayRooter root(cx, results.Length(), rvals); - - for (PRUint32 i = 0; i < results.Length(); ++i) - if (!jsval_from_Variant(cx, results.ElementAt(i), rvals + i)) { - JS_ReportError(cx, "Invalid result from handler %d", i); - return JS_FALSE; - } - - JSObject* arrObj = JS_NewArrayObject(cx, results.Length(), rvals); - if (!arrObj) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(arrObj)); - - return JS_TRUE; -} - -struct ReceiverResult -{ - nsString msgName; - jsval receiver; -}; - -static JSBool -ReceiverCommon(JSContext* cx, uintN argc, jsval* vp, - const char* methodName, uintN arity, - ReceiverResult* result) -{ - if (argc != arity) { - JS_ReportError(cx, "%s requires exactly %d arguments", methodName, arity); - return JS_FALSE; - } - - // Not currently possible, but think of the future. - if (arity < 1) - return JS_TRUE; - - jsval* argv = JS_ARGV(cx, vp); - - JSString* str = JS_ValueToString(cx, argv[0]); - if (!str) { - JS_ReportError(cx, "%s expects a stringifiable value as its first argument", - methodName); - return JS_FALSE; - } - - size_t length; - const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); - if (!chars) - return JS_FALSE; - - result->msgName.Assign(chars, length); - - if (arity < 2) - return JS_TRUE; - - if (JS_TypeOfValue(cx, argv[1]) != JSTYPE_FUNCTION) { - JS_ReportError(cx, "%s expects a function as its second argument", - methodName); - return JS_FALSE; - } - - // GC-safe because argv is rooted. - result->receiver = argv[1]; - - return JS_TRUE; -} - -JSBool -JetpackChild::RegisterReceiver(JSContext* cx, uintN argc, jsval* vp) -{ - ReceiverResult rr; - if (!ReceiverCommon(cx, argc, vp, "registerReceiver", 2, &rr)) - return JS_FALSE; - - JetpackActorCommon* actor = GetThis(cx); - nsresult rv = actor->RegisterReceiver(cx, rr.msgName, rr.receiver); - if (NS_FAILED(rv)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -JetpackChild::UnregisterReceiver(JSContext* cx, uintN argc, jsval* vp) -{ - ReceiverResult rr; - if (!ReceiverCommon(cx, argc, vp, "unregisterReceiver", 2, &rr)) - return JS_FALSE; - - JetpackActorCommon* actor = GetThis(cx); - actor->UnregisterReceiver(rr.msgName, rr.receiver); - return JS_TRUE; -} - -JSBool -JetpackChild::UnregisterReceivers(JSContext* cx, uintN argc, jsval* vp) -{ - ReceiverResult rr; - if (!ReceiverCommon(cx, argc, vp, "unregisterReceivers", 1, &rr)) - return JS_FALSE; - - JetpackActorCommon* actor = GetThis(cx); - actor->UnregisterReceivers(rr.msgName); - return JS_TRUE; -} - -JSBool -JetpackChild::CreateHandle(JSContext* cx, uintN argc, jsval* vp) -{ - if (argc > 0) { - JS_ReportError(cx, "createHandle takes zero arguments"); - return JS_FALSE; - } - - HandleChild* handle; - JSObject* hobj; - - PHandleChild* phc = GetThis(cx)->SendPHandleConstructor(); - if (!(handle = static_cast(phc)) || - !(hobj = handle->ToJSObject(cx))) { - JS_ReportError(cx, "Failed to construct Handle"); - return JS_FALSE; - } - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(hobj)); - - return JS_TRUE; -} - -JSBool -JetpackChild::CreateSandbox(JSContext* cx, uintN argc, jsval* vp) -{ - if (argc > 0) { - JS_ReportError(cx, "createSandbox takes zero arguments"); - return JS_FALSE; - } - - JSObject* obj = JS_NewCompartmentAndGlobalObject(cx, const_cast(&sGlobalClass), NULL); - if (!obj) - return JS_FALSE; - - jsval rval = OBJECT_TO_JSVAL(obj); - if (!JS_WrapValue(cx, &rval)) - return JS_FALSE; - - JSAutoEnterCompartment ac; - if (!ac.enter(cx, obj)) - return JS_FALSE; - - JS_SET_RVAL(cx, vp, rval); - return JS_InitStandardClasses(cx, obj); -} - -JSBool -JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp) -{ - if (argc != 2) { - JS_ReportError(cx, "evalInSandbox takes two arguments"); - return JS_FALSE; - } - - jsval* argv = JS_ARGV(cx, vp); - - JSObject* obj; - if (!JSVAL_IS_OBJECT(argv[0]) || - !(obj = JSVAL_TO_OBJECT(argv[0]))) { - JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); - JS_ASSERT(JS_FALSE); - return JS_FALSE; - } - - // Unwrap, and switch compartments - obj = obj->unwrap(); - - JSAutoEnterCompartment ac; - if (!ac.enter(cx, obj)) - return JS_FALSE; - - if (&sGlobalClass != JS_GetClass(cx, obj) || - obj == JS_GetGlobalObject(cx)) { - JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); - JS_ASSERT(JS_FALSE); - return JS_FALSE; - } - - if (!JS_WrapValue(cx, &argv[1])) - return JS_FALSE; - - JSString* str = JS_ValueToString(cx, argv[1]); - if (!str) - return JS_FALSE; - - size_t length; - const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); - if (!chars) - return JS_FALSE; - - js::AutoValueRooter ignored(cx); - return JS_EvaluateUCScript(cx, obj, chars, length, "", 1, ignored.jsval_addr()); -} - -bool JetpackChild::sReportingError; - -/* static */ void -JetpackChild::ReportError(JSContext* cx, const char* message, - JSErrorReport* report) -{ - if (sReportingError) { - NS_WARNING("Recursive error reported."); - return; - } - - sReportingError = true; - - js::AutoObjectRooter obj(cx, JS_NewObject(cx, NULL, NULL, NULL)); - - if (report && report->filename) { - jsval filename = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, report->filename)); - JS_SetProperty(cx, obj.object(), "fileName", &filename); - } - - if (report) { - jsval lineno = INT_TO_JSVAL(report->lineno); - JS_SetProperty(cx, obj.object(), "lineNumber", &lineno); - } - - jsval msgstr = JSVAL_NULL; - if (report && report->ucmessage) - msgstr = STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx, report->ucmessage)); - else - msgstr = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, message)); - JS_SetProperty(cx, obj.object(), "message", &msgstr); - - MessageResult smr; - Variant* vp = smr.data.AppendElement(); - JetpackActorCommon::jsval_to_Variant(cx, OBJECT_TO_JSVAL(obj.object()), vp); - GetThis(cx)->SendSendMessage(NS_LITERAL_STRING("core:exception"), smr.data); - - sReportingError = false; -} - -JSBool -JetpackChild::GC(JSContext* cx, uintN argc, jsval *vp) -{ - JS_GC(cx); - return JS_TRUE; -} - -#ifdef JS_GC_ZEAL -JSBool -JetpackChild::GCZeal(JSContext* cx, uintN argc, jsval *vp) -{ - jsval* argv = JS_ARGV(cx, vp); - - uint32 zeal; - if (!JS_ValueToECMAUint32(cx, argv[0], &zeal)) - return JS_FALSE; - - JS_SetGCZeal(cx, PRUint8(zeal)); - return JS_TRUE; -} -#endif - -JSBool -JetpackChild::NoteIntentionalCrash(JSContext* cx, uintN argc, jsval *vp) -{ - mozilla::NoteIntentionalCrash("jetpack"); - return JS_TRUE; -} - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/JetpackChild.h b/deps/mozjs/js/jetpack/JetpackChild.h deleted file mode 100644 index ab539c03640..00000000000 --- a/deps/mozjs/js/jetpack/JetpackChild.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackChild_h -#define mozilla_jetpack_JetpackChild_h - -#include "mozilla/jetpack/PJetpackChild.h" -#include "mozilla/jetpack/JetpackActorCommon.h" - -#include "nsTArray.h" - -namespace mozilla { -namespace jetpack { - -class PHandleChild; - -class JetpackChild - : public PJetpackChild - , private JetpackActorCommon -{ -public: - JetpackChild(); - ~JetpackChild(); - - static JetpackChild* current(); - - bool Init(base::ProcessHandle aParentProcessHandle, - MessageLoop* aIOLoop, - IPC::Channel* aChannel); - - void CleanUp(); - -protected: - NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); - - NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName, - const InfallibleTArray& data); - NS_OVERRIDE virtual bool RecvEvalScript(const nsString& script); - - NS_OVERRIDE virtual PHandleChild* AllocPHandle(); - NS_OVERRIDE virtual bool DeallocPHandle(PHandleChild* actor); - -private: - JSRuntime* mRuntime; - JSContext *mCx; - - static JetpackChild* GetThis(JSContext* cx); - - static const JSFunctionSpec sImplMethods[]; - static JSBool SendMessage(JSContext* cx, uintN argc, jsval *vp); - static JSBool CallMessage(JSContext* cx, uintN argc, jsval *vp); - static JSBool RegisterReceiver(JSContext* cx, uintN argc, jsval *vp); - static JSBool UnregisterReceiver(JSContext* cx, uintN argc, jsval *vp); - static JSBool UnregisterReceivers(JSContext* cx, uintN argc, jsval *vp); - static JSBool CreateHandle(JSContext* cx, uintN argc, jsval *vp); - static JSBool CreateSandbox(JSContext* cx, uintN argc, jsval *vp); - static JSBool EvalInSandbox(JSContext* cx, uintN argc, jsval *vp); - static JSBool GC(JSContext* cx, uintN argc, jsval *vp); -#ifdef JS_GC_ZEAL - static JSBool GCZeal(JSContext* cx, uintN argc, jsval *vp); -#endif - static JSBool NoteIntentionalCrash(JSContext* cx, uintN argc, jsval *vp); - - static void ReportError(JSContext* cx, const char* message, - JSErrorReport* report); - - static const JSClass sGlobalClass; - static bool sReportingError; - - DISALLOW_EVIL_CONSTRUCTORS(JetpackChild); -}; - -} // namespace jetpack -} // namespace mozilla - - -#endif // mozilla_jetpack_JetpackChild_h diff --git a/deps/mozjs/js/jetpack/JetpackParent.cpp b/deps/mozjs/js/jetpack/JetpackParent.cpp deleted file mode 100644 index 46d01d4484f..00000000000 --- a/deps/mozjs/js/jetpack/JetpackParent.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "mozilla/jetpack/JetpackParent.h" -#include "mozilla/jetpack/Handle.h" -#include "base/process_util.h" - -#include "nsIURI.h" -#include "nsNetUtil.h" -#include "nsIVariant.h" -#include "nsIXPConnect.h" -#include "nsIJSContextStack.h" - -#ifdef MOZ_CRASHREPORTER -#include "nsExceptionHandler.h" -#endif - -namespace mozilla { -namespace jetpack { - -JetpackParent::JetpackParent(JSContext* cx) - : mSubprocess(new JetpackProcessParent()) - , mContext(cx) - , mTaskFactory(this) -{ - mSubprocess->Launch(); - Open(mSubprocess->GetChannel(), - mSubprocess->GetChildProcessHandle()); -} - -JetpackParent::~JetpackParent() -{ - if (mSubprocess) - Destroy(); - - if (OtherProcess()) - base::CloseProcessHandle(OtherProcess()); -} - -NS_IMPL_ISUPPORTS1(JetpackParent, nsIJetpack) - -NS_IMETHODIMP -JetpackParent::SendMessage(const nsAString& aMessageName) -{ - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAXPCNativeCallContext* ncc = NULL; - rv = xpc->GetCurrentNativeCallContext(&ncc); - NS_ENSURE_SUCCESS(rv, rv); - - JSContext* cx; - rv = ncc->GetJSContext(&cx); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 argc; - rv = ncc->GetArgc(&argc); - NS_ENSURE_SUCCESS(rv, rv); - - jsval* argv; - rv = ncc->GetArgvPtr(&argv); - NS_ENSURE_SUCCESS(rv, rv); - - nsTArray data; - NS_ENSURE_TRUE(data.SetCapacity(argc), NS_ERROR_OUT_OF_MEMORY); - - JSAutoRequest request(cx); - - JSAutoEnterCompartment ac; - if (!ac.enter(cx, JS_GetGlobalObject(cx))) - return false; - - for (PRUint32 i = 1; i < argc; ++i) { - if (!JS_WrapValue(cx, &argv[i]) || - !jsval_to_Variant(cx, argv[i], data.AppendElement())) { - return NS_ERROR_INVALID_ARG; - } - } - - InfallibleTArray dataForSend; - dataForSend.SwapElements(data); - if (!SendSendMessage(nsString(aMessageName), dataForSend)) - return NS_ERROR_FAILURE; - - return NS_OK; -} - -NS_IMETHODIMP -JetpackParent::RegisterReceiver(const nsAString& aMessageName, - const jsval &aReceiver) -{ - return JetpackActorCommon::RegisterReceiver(mContext, - nsString(aMessageName), - aReceiver); -} - -NS_IMETHODIMP -JetpackParent::UnregisterReceiver(const nsAString& aMessageName, - const jsval &aReceiver) -{ - JetpackActorCommon::UnregisterReceiver(nsString(aMessageName), - aReceiver); - return NS_OK; -} - -NS_IMETHODIMP -JetpackParent::UnregisterReceivers(const nsAString& aMessageName) -{ - JetpackActorCommon::UnregisterReceivers(nsString(aMessageName)); - return NS_OK; -} - -NS_IMETHODIMP -JetpackParent::EvalScript(const nsAString& aScript) -{ - if (!SendEvalScript(nsString(aScript))) - return NS_ERROR_FAILURE; - - return NS_OK; -} - -class AutoCXPusher -{ -public: - AutoCXPusher(JSContext* cx) - : mCXStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1")) - { - NS_ASSERTION(mCXStack, "No JS context stack?"); - if (mCXStack) - mCXStack->Push(cx); - } - ~AutoCXPusher() - { - if (mCXStack) - mCXStack->Pop(NULL); - } - -private: - nsCOMPtr mCXStack; -}; - -// We have to delete the JetpackProcessParent on the I/O thread after event -// loop iteration in which JetpackParent::ActorDestroy runs is finished. -static void -DelayedDestroyProcess(JetpackProcessParent* o) -{ - XRE_GetIOMessageLoop() - ->PostTask(FROM_HERE, new DeleteTask(o)); -} - -void -JetpackParent::ActorDestroy(ActorDestroyReason why) -{ - switch (why) { - case AbnormalShutdown: { - nsAutoString dumpID; - -#ifdef MOZ_CRASHREPORTER - nsCOMPtr crashDump; - TakeMinidump(getter_AddRefs(crashDump)) && - CrashReporter::GetIDFromMinidump(crashDump, dumpID); -#endif - - MessageLoop::current()-> - PostTask(FROM_HERE, - mTaskFactory.NewRunnableMethod( - &JetpackParent::DispatchFailureMessage, - dumpID)); - break; - } - - case NormalShutdown: - break; - - default: - NS_ERROR("Unexpected actordestroy reason for toplevel actor."); - } - - MessageLoop::current()-> - PostTask(FROM_HERE, NewRunnableFunction(DelayedDestroyProcess, mSubprocess)); - mSubprocess = NULL; -} - -bool -JetpackParent::RecvSendMessage(const nsString& messageName, - const InfallibleTArray& data) -{ - AutoCXPusher cxp(mContext); - JSAutoRequest request(mContext); - - JSAutoEnterCompartment ac; - if (!ac.enter(mContext, JS_GetGlobalObject(mContext))) - return false; - - return JetpackActorCommon::RecvMessage(mContext, messageName, data, NULL); -} - -bool -JetpackParent::AnswerCallMessage(const nsString& messageName, - const InfallibleTArray& data, - InfallibleTArray* results) -{ - AutoCXPusher cxp(mContext); - JSAutoRequest request(mContext); - - JSAutoEnterCompartment ac; - if (!ac.enter(mContext, JS_GetGlobalObject(mContext))) - return false; - - return JetpackActorCommon::RecvMessage(mContext, messageName, data, results); -} - -NS_IMETHODIMP -JetpackParent::CreateHandle(nsIVariant** aResult) -{ - HandleParent* handle = - static_cast(SendPHandleConstructor()); - NS_ENSURE_TRUE(handle, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - JSAutoRequest request(mContext); - - JSAutoEnterCompartment ac; - if (!ac.enter(mContext, JS_GetGlobalObject(mContext))) - return false; - - JSObject* hobj = handle->ToJSObject(mContext); - if (!hobj) - return NS_ERROR_FAILURE; - - return xpc->JSToVariant(mContext, OBJECT_TO_JSVAL(hobj), aResult); -} - -NS_IMETHODIMP -JetpackParent::Destroy() -{ - if (mSubprocess) - Close(); - - NS_ASSERTION(!mSubprocess, "ActorDestroy should have been called."); - - ClearReceivers(); - - return NS_OK; -} - -PHandleParent* -JetpackParent::AllocPHandle() -{ - return new HandleParent(); -} - -bool -JetpackParent::DeallocPHandle(PHandleParent* actor) -{ - delete actor; - return true; -} - -void -JetpackParent::OnChannelConnected(int32 pid) -{ - ProcessHandle handle; - if (!base::OpenPrivilegedProcessHandle(pid, &handle)) - NS_RUNTIMEABORT("can't open handle to child process"); - - SetOtherProcess(handle); -} - -void -JetpackParent::DispatchFailureMessage(const nsString& aDumpID) -{ -#ifdef MOZ_CRASHREPORTER - CrashReporter::AnnotationTable notes; - notes.Init(); - notes.Put(NS_LITERAL_CSTRING("ProcessType"), NS_LITERAL_CSTRING("jetpack")); - // TODO: Additional per-process annotations. - CrashReporter::AppendExtraData(aDumpID, notes); -#endif - - InfallibleTArray keyvalues; - if (!aDumpID.IsEmpty()) { - KeyValue kv(NS_LITERAL_STRING("dumpID"), PrimVariant(aDumpID)); - keyvalues.AppendElement(kv); - } - - CompVariant object(keyvalues); - - InfallibleTArray arguments; - arguments.AppendElement(object); - - RecvSendMessage(NS_LITERAL_STRING("core:process-error"), arguments); -} - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/JetpackParent.h b/deps/mozjs/js/jetpack/JetpackParent.h deleted file mode 100644 index c39d6c526c2..00000000000 --- a/deps/mozjs/js/jetpack/JetpackParent.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Newman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackParent_h -#define mozilla_jetpack_JetpackParent_h - -#include "mozilla/jetpack/PJetpackParent.h" -#include "mozilla/jetpack/JetpackProcessParent.h" -#include "mozilla/jetpack/JetpackActorCommon.h" -#include "nsIJetpack.h" - -#include "nsTArray.h" - -struct JSContext; - -namespace mozilla { -namespace jetpack { - -class PHandleParent; - -class JetpackParent - : public PJetpackParent - , public nsIJetpack - , private JetpackActorCommon -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIJETPACK - - JetpackParent(JSContext* cx); - ~JetpackParent(); - - void OnChannelConnected(int32 pid); - -protected: - NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); - - NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName, - const InfallibleTArray& data); - NS_OVERRIDE virtual bool AnswerCallMessage(const nsString& messageName, - const InfallibleTArray& data, - InfallibleTArray* results); - - NS_OVERRIDE virtual PHandleParent* AllocPHandle(); - NS_OVERRIDE virtual bool DeallocPHandle(PHandleParent* actor); - -private: - JetpackProcessParent* mSubprocess; - JSContext* mContext; - ScopedRunnableMethodFactory mTaskFactory; - - void DispatchFailureMessage(const nsString& aDumpID); - - DISALLOW_EVIL_CONSTRUCTORS(JetpackParent); -}; - -} // namespace jetpack -} // namespace mozilla - -#endif // mozilla_jetpack_JetpackParent_h diff --git a/deps/mozjs/js/jetpack/JetpackProcessChild.cpp b/deps/mozjs/js/jetpack/JetpackProcessChild.cpp deleted file mode 100644 index c2b399fbdea..00000000000 --- a/deps/mozjs/js/jetpack/JetpackProcessChild.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "mozilla/ipc/IOThreadChild.h" - -#include "mozilla/jetpack/JetpackProcessChild.h" - -using mozilla::ipc::IOThreadChild; - -namespace mozilla { -namespace jetpack { - -bool -JetpackProcessChild::Init() -{ - mJetpack.Init(ParentHandle(), - IOThreadChild::message_loop(), - IOThreadChild::channel()); - return true; -} - -void -JetpackProcessChild::CleanUp() -{ - mJetpack.CleanUp(); -} - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/JetpackProcessChild.h b/deps/mozjs/js/jetpack/JetpackProcessChild.h deleted file mode 100644 index 3941663f01f..00000000000 --- a/deps/mozjs/js/jetpack/JetpackProcessChild.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackProcessChild_h -#define mozilla_jetpack_JetpackProcessChild_h - -#include "mozilla/ipc/ProcessChild.h" - -#include "mozilla/jetpack/JetpackChild.h" - -namespace mozilla { -namespace jetpack { - -// The JetpackProcessChild class represents the thread where jetpack code is run; -// the main() thread is the I/O thread of the jetpack process. - -class JetpackProcessChild : public mozilla::ipc::ProcessChild -{ - typedef mozilla::ipc::ProcessChild ProcessChild; - -public: - JetpackProcessChild(ProcessHandle aParentHandle) - : ProcessChild(aParentHandle) - { } - - virtual ~JetpackProcessChild() - { } - - NS_OVERRIDE virtual bool Init(); - NS_OVERRIDE virtual void CleanUp(); - - static JetpackProcessChild* current() { - return static_cast(ProcessChild::current()); - } - -private: - JetpackChild mJetpack; - - DISALLOW_EVIL_CONSTRUCTORS(JetpackProcessChild); -}; - -} -} - -#endif // mozilla_jetpack_JetpackProcessChild_h diff --git a/deps/mozjs/js/jetpack/JetpackProcessParent.cpp b/deps/mozjs/js/jetpack/JetpackProcessParent.cpp deleted file mode 100644 index def56c63494..00000000000 --- a/deps/mozjs/js/jetpack/JetpackProcessParent.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "mozilla/jetpack/JetpackProcessParent.h" - -#include "mozilla/ipc/BrowserProcessSubThread.h" - -namespace mozilla { -namespace jetpack { - -JetpackProcessParent::JetpackProcessParent() - : mozilla::ipc::GeckoChildProcessHost(GeckoProcessType_Jetpack) -{ -} - -JetpackProcessParent::~JetpackProcessParent() -{ -} - -void -JetpackProcessParent::Launch() -{ - AsyncLaunch(); -} - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/JetpackProcessParent.h b/deps/mozjs/js/jetpack/JetpackProcessParent.h deleted file mode 100644 index da62c23fe55..00000000000 --- a/deps/mozjs/js/jetpack/JetpackProcessParent.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackProcessParent_h -#define mozilla_jetpack_JetpackProcessParent_h - -#include "base/basictypes.h" - -#include "base/file_path.h" -#include "base/scoped_ptr.h" -#include "base/thread.h" -#include "base/waitable_event.h" -#include "chrome/common/child_process_host.h" - -#include "mozilla/ipc/GeckoChildProcessHost.h" - -namespace mozilla { -namespace jetpack { - -class JetpackProcessParent : mozilla::ipc::GeckoChildProcessHost -{ -public: - JetpackProcessParent(); - ~JetpackProcessParent(); - - /** - * Aynchronously launch the jetpack process. - */ - void Launch(); - - using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent; - using mozilla::ipc::GeckoChildProcessHost::GetChannel; - using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle; - -private: - DISALLOW_EVIL_CONSTRUCTORS(JetpackProcessParent); -}; - -} // namespace jetpack -} // namespace mozilla - -#endif // mozilla_jetpack_JetpackProcessParent_h diff --git a/deps/mozjs/js/jetpack/JetpackService.cpp b/deps/mozjs/js/jetpack/JetpackService.cpp deleted file mode 100644 index c9208986940..00000000000 --- a/deps/mozjs/js/jetpack/JetpackService.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "base/basictypes.h" -#include "mozilla/jetpack/JetpackService.h" - -#include "mozilla/jetpack/JetpackParent.h" -#include "nsIJetpack.h" - -#include "mozilla/ModuleUtils.h" - -#include "nsIXPConnect.h" - -namespace mozilla { -namespace jetpack { - -NS_IMPL_ISUPPORTS1(JetpackService, - nsIJetpackService) - -NS_IMETHODIMP -JetpackService::CreateJetpack(nsIJetpack** aResult) -{ - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAXPCNativeCallContext* ncc = NULL; - rv = xpc->GetCurrentNativeCallContext(&ncc); - NS_ENSURE_SUCCESS(rv, rv); - - JSContext* cx; - rv = ncc->GetJSContext(&cx); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr j = new JetpackParent(cx); - *aResult = j.forget().get(); - - return NS_OK; -} - -NS_GENERIC_FACTORY_CONSTRUCTOR(JetpackService) - -} // namespace jetpack -} // namespace mozilla - -#define JETPACKSERVICE_CID \ -{ 0x4cf18fcd, 0x4247, 0x4388, \ - { 0xb1, 0x88, 0xb0, 0x72, 0x2a, 0xc0, 0x52, 0x21 } } - -NS_DEFINE_NAMED_CID(JETPACKSERVICE_CID); - -static const mozilla::Module::CIDEntry kJetpackCIDs[] = { - { &kJETPACKSERVICE_CID, false, NULL, mozilla::jetpack::JetpackServiceConstructor }, - { NULL } -}; - -static const mozilla::Module::ContractIDEntry kJetpackContracts[] = { - { "@mozilla.org/jetpack/service;1", &kJETPACKSERVICE_CID }, - { NULL } -}; - -static const mozilla::Module kJetpackModule = { - mozilla::Module::kVersion, - kJetpackCIDs, - kJetpackContracts -}; - -NSMODULE_DEFN(jetpack) = &kJetpackModule; - diff --git a/deps/mozjs/js/jetpack/JetpackService.h b/deps/mozjs/js/jetpack/JetpackService.h deleted file mode 100644 index b30847a367f..00000000000 --- a/deps/mozjs/js/jetpack/JetpackService.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_jetpack_JetpackService_h -#define mozilla_jetpack_JetpackService_h - -#include "nsIJetpackService.h" - -namespace mozilla { -namespace jetpack { - -class JetpackService : public nsIJetpackService -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIJETPACKSERVICE -}; - -} // jetpack -} // mozilla - -#endif // mozilla_jetpack_JetpackService_h diff --git a/deps/mozjs/js/jetpack/Makefile.in b/deps/mozjs/js/jetpack/Makefile.in deleted file mode 100644 index 51ef103a4f5..00000000000 --- a/deps/mozjs/js/jetpack/Makefile.in +++ /dev/null @@ -1,89 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Firefox. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation . -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = jetpack -LIBRARY_NAME = jetpack_s -LIBXUL_LIBRARY = 1 -FORCE_STATIC_LIB = 1 -IS_COMPONENT = 1 -EXPORT_LIBRARY = 1 -MODULE_NAME = jetpack - -XPIDLSRCS = \ - nsIJetpackService.idl \ - nsIJetpack.idl \ - $(NULL) - -EXPORTS_NAMESPACES = mozilla/jetpack -EXPORTS_mozilla/jetpack = \ - JetpackProcessChild.h \ - JetpackProcessParent.h \ - JetpackParent.h \ - JetpackChild.h \ - JetpackService.h \ - JetpackActorCommon.h \ - Handle.h \ - $(NULL) - -CPPSRCS = \ - JetpackParent.cpp \ - JetpackChild.cpp \ - JetpackProcessChild.cpp \ - JetpackProcessParent.cpp \ - JetpackService.cpp \ - JetpackActorCommon.cpp \ - $(NULL) - -# For nsDependentJSString -LOCAL_INCLUDES += \ - -I$(topsrcdir)/dom/base \ - $(NULL) - -# Bug 629197: disabled jetpack service (and its tests). -# ifdef ENABLE_TESTS -# TOOL_DIRS += tests -# endif - -include $(topsrcdir)/config/config.mk -include $(topsrcdir)/ipc/chromium/chromium-config.mk -include $(topsrcdir)/config/rules.mk diff --git a/deps/mozjs/js/jetpack/PHandle.ipdl b/deps/mozjs/js/jetpack/PHandle.ipdl deleted file mode 100644 index e042ab09f96..00000000000 --- a/deps/mozjs/js/jetpack/PHandle.ipdl +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Newman (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -include protocol PJetpack; - -namespace mozilla { -namespace jetpack { - -async protocol PHandle -{ - manager PJetpack or PHandle; - manages PHandle; -both: - PHandle(); - __delete__(); -}; - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/PJetpack.ipdl b/deps/mozjs/js/jetpack/PJetpack.ipdl deleted file mode 100644 index c601f03b37e..00000000000 --- a/deps/mozjs/js/jetpack/PJetpack.ipdl +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -include protocol PHandle; - -using mozilla::void_t; -using mozilla::null_t; - -namespace mozilla { -namespace jetpack { - -struct KeyValue { - nsString key; - Variant value; -}; - -union PrimVariant { - void_t; - null_t; - bool; - int; - double; - nsString; - PHandle; -}; - -union CompVariant { - KeyValue[]; - Variant[]; - size_t; // reference -}; - -union Variant { - PrimVariant; - CompVariant; -}; - -rpc protocol PJetpack -{ - manages PHandle; -both: - async SendMessage(nsString messageName, - Variant[] data); - async PHandle(); - -child: - async EvalScript(nsString code); - -parent: - rpc CallMessage(nsString messageName, - Variant[] data) - returns (Variant[] results); - -}; - -} // namespace jetpack -} // namespace mozilla diff --git a/deps/mozjs/js/jetpack/ipdl.mk b/deps/mozjs/js/jetpack/ipdl.mk deleted file mode 100644 index 301136865fe..00000000000 --- a/deps/mozjs/js/jetpack/ipdl.mk +++ /dev/null @@ -1,4 +0,0 @@ -IPDLSRCS = \ - PJetpack.ipdl \ - PHandle.ipdl \ - $(NULL) diff --git a/deps/mozjs/js/jetpack/nsIJetpack.idl b/deps/mozjs/js/jetpack/nsIJetpack.idl deleted file mode 100644 index 4daf5c886d6..00000000000 --- a/deps/mozjs/js/jetpack/nsIJetpack.idl +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsISupports.idl" - -interface nsIVariant; - -[scriptable, uuid(61A5BE65-FAF2-4FD1-96DF-B13A7AD7D88D)] -interface nsIJetpack : nsISupports -{ - void sendMessage(in AString aMessageName - /* [optional] in jsval v1, - [optional] in jsval v2, - ... */); - - void registerReceiver(in AString aMessageName, - in jsval aReceiver); - void unregisterReceiver(in AString aMessageName, - in jsval aReceiver); - void unregisterReceivers(in AString aMessageName); - - void evalScript(in AString aScript); - - nsIVariant createHandle(); - - void destroy(); -}; diff --git a/deps/mozjs/js/jetpack/nsIJetpackService.idl b/deps/mozjs/js/jetpack/nsIJetpackService.idl deleted file mode 100644 index 96cc25503c4..00000000000 --- a/deps/mozjs/js/jetpack/nsIJetpackService.idl +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsISupports.idl" - -interface nsIJetpack; - -[scriptable, uuid(2e097e3e-225f-42a3-87a8-c5c22659fef0)] -interface nsIJetpackService : nsISupports -{ - nsIJetpack createJetpack(); -}; diff --git a/deps/mozjs/js/jetpack/tests/Makefile.in b/deps/mozjs/js/jetpack/tests/Makefile.in deleted file mode 100644 index 6794b6fb91f..00000000000 --- a/deps/mozjs/js/jetpack/tests/Makefile.in +++ /dev/null @@ -1,60 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Ben Newman (original author) -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = js/jetpack/tests - -include $(DEPTH)/config/autoconf.mk - -MODULE = test_jetpack - -XPCSHELL_TESTS = unit - -include $(topsrcdir)/config/config.mk -include $(topsrcdir)/config/rules.mk - -ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) # Disabled for bug 599475 -MOCHICHROME_FILES = \ - test_jetpack_crash.xul \ - $(NULL) - -libs:: $(MOCHICHROME_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) -endif diff --git a/deps/mozjs/js/jetpack/tests/test_jetpack_crash.xul b/deps/mozjs/js/jetpack/tests/test_jetpack_crash.xul deleted file mode 100644 index ae5e4794cd9..00000000000 --- a/deps/mozjs/js/jetpack/tests/test_jetpack_crash.xul +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - diff --git a/deps/mozjs/js/jetpack/tests/unit/handle_tests.js b/deps/mozjs/js/jetpack/tests/unit/handle_tests.js deleted file mode 100644 index 6f5904ec06b..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/handle_tests.js +++ /dev/null @@ -1,101 +0,0 @@ -function run_handle_tests() { - test_sanity(); - test_safe_iteration(); - test_local_invalidation(); - test_long_parent_chain(100); - test_invalid_creation(); -} - -function test_sanity() { - var parent = createHandle(), - child = parent.createHandle(), - grandchild = child.createHandle(); - - do_check_neq(child, parent); - do_check_eq(child.parent, parent); - do_check_eq(parent.parent, null); - do_check_eq(grandchild.parent.parent, parent); - - do_check_true(child.isValid); - do_check_true(parent.isValid); - - parent.invalidate(); -} - -function test_safe_iteration() { - var handle = createHandle(), - keys = []; - handle.foo = 42; - handle.self = handle; - for (var k in handle) - keys[keys.length] = k; - do_check_eq(keys.sort().join("~"), - "foo~self"); - handle.invalidate(); -} - -function test_local_invalidation() { - var parent = createHandle(), - child = parent.createHandle(); - - dump("test_local_invalidation\n"); - - child.invalidate(); - do_check_false(child.isValid); - do_check_true(parent.isValid); - - child = parent.createHandle(); - do_check_true(child.isValid); - - parent.invalidate(); - parent.invalidate(); - do_check_false(child.isValid); - do_check_false(parent.isValid); - - parent = createHandle(); - child = parent.createHandle(); - child = child.createHandle(); - - var uncle = parent.createHandle(), - sibling = child.parent.createHandle(); - - do_check_eq(child.parent.parent, parent); - do_check_true(child.parent.isValid); - - child.parent.invalidate(); - do_check_false(child.isValid); - do_check_true(parent.isValid); - - do_check_false(sibling.isValid); - do_check_true(uncle.isValid); - - parent.invalidate(); -} - -function test_long_parent_chain(len) { - const ancestor = createHandle(); - for (var handle = ancestor, i = 0; i < len; ++i) - handle = handle.createHandle(); - const child = handle; - - while (handle != ancestor) - handle = handle.parent; - - do_check_true(child.isValid); - ancestor.invalidate(); - do_check_false(child.isValid); -} - -function test_invalid_creation() { - var parent = createHandle(), - child = parent.createHandle(); - - parent.invalidate(); - - do_check_eq(child.parent, null); - - var threw = false; - try { child.createHandle(); } - catch (x) { threw = true; } - do_check_true(threw); -} diff --git a/deps/mozjs/js/jetpack/tests/unit/head_jetpack.js b/deps/mozjs/js/jetpack/tests/unit/head_jetpack.js deleted file mode 100644 index a90d90f975e..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/head_jetpack.js +++ /dev/null @@ -1,46 +0,0 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; - -function createJetpack(args) -{ - var jp = Components.classes["@mozilla.org/jetpack/service;1"]. - getService(Components.interfaces.nsIJetpackService). - createJetpack(); - - if (!args.skipRegisterCleanup) - do_register_cleanup(function() { - jp.destroy(); - }); - - if (!args.skipRegisterError) - jp.registerReceiver("core:exception", function(msgName, e) { - dump("Received exception from remote code: " + uneval(e) + "\n"); - do_check_true(false); - }); - - if (args.scriptFile) - jp.evalScript(read_file(args.scriptFile)); - - return jp; -} - -const PR_RDONLY = 0x1; - -function read_file(f) -{ - var fis = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); - fis.init(f, PR_RDONLY, 0444, Ci.nsIFileInputStream.CLOSE_ON_EOF); - - var lis = Cc["@mozilla.org/intl/converter-input-stream;1"] - .createInstance(Ci.nsIConverterInputStream); - lis.init(fis, "UTF-8", 1024, 0); - - var data = ""; - - var r = {}; - while (lis.readString(0x0FFFFFFF, r)) - data += r.value; - - return data; -} diff --git a/deps/mozjs/js/jetpack/tests/unit/impl.js b/deps/mozjs/js/jetpack/tests/unit/impl.js deleted file mode 100644 index 68dd707f781..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/impl.js +++ /dev/null @@ -1,87 +0,0 @@ -function echo() { - sendMessage.apply(this, arguments); -} - -registerReceiver("echo", echo); - -registerReceiver("callback", - function(msgName, data, handle) { - sendMessage("sendback", - callMessage("callback", data)[0], - handle); - }); - -registerReceiver("gimmeHandle", - function(msgName) { - sendMessage("recvHandle", "ok", createHandle()); - }); - -registerReceiver("kthx", - function(msgName, data, child) { - sendMessage("recvHandleAgain", data + data, child.parent); - }); - -registerReceiver("echo2", echo); - -registerReceiver("multireturn begin", - function() { - var results = callMessage("multireturn"); - sendMessage.apply(null, ["multireturn check"].concat(results)); - }); - -registerReceiver("testarray", - function(msgName, array) { - sendMessage("testarray", array.reverse()); - }); - -registerReceiver("test primitive types", echo); - -registerReceiver("drop methods", echo); - -registerReceiver("exception coping", echo); - -registerReceiver("duplicate receivers", echo); - -function ok(c, msg) -{ - sendMessage("test result", c, msg); -} - -registerReceiver("test sandbox", function() { - var addon = createSandbox(); - ok(typeof(addon) == "object", "typeof(addon)"); - ok("Date" in addon, "addon.Date exists"); - ok(addon.Date !== Date, "Date objects are different"); - - var fn = "var x; var c = 3; function doit() { x = 12; return 4; }"; - evalInSandbox(addon, fn); - - ok(addon.x === undefined, "x is undefined"); - ok(addon.c == 3, "c is 3"); - ok(addon.doit() == 4, "doit called successfully"); - ok(addon.x == 12, "x is now 12"); - - var fn2 = "let function barbar{}"; - try { - evalInSandbox(addon, fn2); - ok(false, "bad syntax should throw"); - } - catch(e) { - ok(true, "bad syntax should throw"); - } - - var fn3 = "throw new Error('just kidding')"; - try { - evalInSandbox(addon, fn3); - ok(false, "thrown error should be caught"); - } - catch(e) { - ok(true, "thrown error should be caught"); - } - - sendMessage("sandbox done"); -}); - -registerReceiver("throw", function(msgName) { - throw new Error("throwing on request"); -}); diff --git a/deps/mozjs/js/jetpack/tests/unit/impl_jetpack_ctypes.js b/deps/mozjs/js/jetpack/tests/unit/impl_jetpack_ctypes.js deleted file mode 100644 index 2e1692ed66c..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/impl_jetpack_ctypes.js +++ /dev/null @@ -1,7 +0,0 @@ -const CTYPES_TEST_LIB = ctypes.libraryName("jsctypes-test"); - -registerReceiver("testCTypes", function(name, libdir) { - var library = ctypes.open(libdir + '/' + CTYPES_TEST_LIB); - let test_void_t = library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t); - sendMessage("onCTypesTested", test_void_t() === undefined); -}); diff --git a/deps/mozjs/js/jetpack/tests/unit/impl_rooting.js b/deps/mozjs/js/jetpack/tests/unit/impl_rooting.js deleted file mode 100644 index eb23a41c4f0..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/impl_rooting.js +++ /dev/null @@ -1,5 +0,0 @@ -registerReceiver("ReceiveGCHandle", function(name, handle) { - handle.onInvalidate = function() { - sendMessage("onInvalidateReceived", handle.isValid); - }; -}); diff --git a/deps/mozjs/js/jetpack/tests/unit/test_jetpack.js b/deps/mozjs/js/jetpack/tests/unit/test_jetpack.js deleted file mode 100644 index 79072ef45dd..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/test_jetpack.js +++ /dev/null @@ -1,204 +0,0 @@ -var jetpack = null; - -load("handle_tests.js"); -function createHandle() { - return jetpack.createHandle(); -} - -function run_test() { - jetpack = createJetpack({ - skipRegisterError: true, - scriptFile: do_get_file("impl.js") - }); - run_handle_tests(); - - var circ1 = {}, - circ2 = {}, - circ3 = {}, - ok = false; - ((circ1.next = circ2).next = circ3).next = circ1; - try { - jetpack.sendMessage("ignored", circ3, circ1); - ok = true; - } catch (x) { - do_check_false(x); - } - do_check_true(ok); - - var echoHandle = jetpack.createHandle(); - echoHandle.payload = { weight: 10 }; - jetpack.registerReceiver("echo", - function(msgName, data, handle) { - do_check_eq(arguments.length, 3); - do_check_eq(msgName, "echo"); - do_check_eq(data, "echo this"); - do_check_true(handle.isValid); - do_check_eq(handle, echoHandle); - do_check_eq(handle.payload.weight, 10); - do_test_finished(); - }); - - jetpack.registerReceiver("callback", - function(msgName, data) { - do_check_eq(msgName, "callback"); - return "called back: " + data; - }); - - var callbackHandle = echoHandle.createHandle(); - jetpack.registerReceiver("sendback", - function(msgName, data, handle) { - do_check_eq(msgName, "sendback"); - do_check_eq(data, "called back: call me back"); - do_check_eq(handle, callbackHandle); - do_test_finished(); - }); - - var obj; - jetpack.registerReceiver("recvHandle", - function(msgName, data, handle) { - handle.mark = obj = {}; - jetpack.sendMessage("kthx", data + data, handle.createHandle()); - }); - jetpack.registerReceiver("recvHandleAgain", - function(msgName, data, handle) { - do_check_eq(data, "okokokok"); - do_check_eq(handle.mark, obj); - do_test_finished(); - }); - var obj1 = { - id: Math.random() + "" - }, obj2 = { - id: Math.random() + "", - obj: obj1 - }; - jetpack.registerReceiver("echo2", - function(msgName, a, b) { - do_check_neq(obj1, a); - do_check_neq(obj2, b); - do_check_eq(obj1.id, a.id); - do_check_eq(obj2.id, b.id); - do_check_eq(obj1.id, obj2.obj.id); - do_test_finished(); - }); - - jetpack.registerReceiver("multireturn", function() { return obj1 }); - jetpack.registerReceiver("multireturn", function() { return circ1 }); - jetpack.registerReceiver("multireturn", function() { return obj2 }); - jetpack.registerReceiver("multireturn check", - function(msgName, rval1, rval2, rval3) { - do_check_eq(rval1.id, obj1.id); - do_check_eq(rval2.next.next.next, rval2); - do_check_eq(rval3.id, obj2.id); - do_check_eq(rval3.obj.id, obj1.id); - do_test_finished(); - }); - - var testarray = [1, 1, 2, 3, 5, 8, 13]; - jetpack.registerReceiver("testarray", - function(msgName, reversed) { - for (var i = 0; i < testarray.length; ++i) - do_check_eq(testarray[i], - reversed[reversed.length - i - 1]); - do_test_finished(); - }); - - var undefined; - jetpack.registerReceiver("test primitive types", - function(msgName, - void_val, null_val, - bool_true, bool_false, - one, two, nine99, - one_quarter, - oyez_str) - { - do_check_true(void_val === undefined); - do_check_true(null_val === null); - do_check_true(bool_true === true); - do_check_true(bool_false === false); - do_check_eq(one, 1); - do_check_eq(two, 2); - do_check_eq(nine99, 999); - do_check_eq(one_quarter, 0.25); - do_check_eq(oyez_str, "oyez"); - - do_test_finished(); - }); - - var drop = { - nested: { - method: function() { return this.value }, - value: 42 - } - }; - jetpack.registerReceiver("drop methods", - function(msgName, echoed) { - do_check_true(!echoed.nested.method); - do_check_eq(echoed.nested.value, 42); - do_test_finished(); - }); - - var coped = "did not cope"; - jetpack.registerReceiver("exception coping", - function(msgName) { throw coped = "did cope" }); - jetpack.registerReceiver("exception coping", - function(msgName) { - do_check_eq(coped, "did cope"); - do_test_finished(); - }); - - var calls = ""; - function countCalls() { calls += "." } - jetpack.registerReceiver("duplicate receivers", countCalls); - jetpack.registerReceiver("duplicate receivers", countCalls); - jetpack.registerReceiver("duplicate receivers", - function() { do_check_eq(calls, ".") }); - jetpack.registerReceiver("duplicate receivers", countCalls); - jetpack.registerReceiver("duplicate receivers", - function() { - do_check_eq(calls, "."); - jetpack.unregisterReceivers("duplicate receivers"); - }); - jetpack.registerReceiver("duplicate receivers", - function() { do_test_finished() }); - - jetpack.registerReceiver("test result", function(name, c, msg) { - dump("TEST-INFO | test_jetpack.js | remote check '" + msg + "' result: " + c + "\n"); - do_check_true(c); - }); - jetpack.registerReceiver("sandbox done", do_test_finished); - - jetpack.registerReceiver("core:exception", - function(msgName, e) { - do_check_true(/throwing on request/.test(e.message)); - do_test_finished(); - }); - - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - - jetpack.sendMessage("echo", "echo this", echoHandle); - jetpack.sendMessage("callback", "call me back", callbackHandle); - jetpack.sendMessage("gimmeHandle"); - jetpack.sendMessage("echo2", obj1, obj2); - jetpack.sendMessage("multireturn begin"); - jetpack.sendMessage("testarray", testarray); - jetpack.sendMessage("test primitive types", - undefined, null, true, false, 1, 2, 999, 1/4, "oyez"); - jetpack.sendMessage("drop methods", drop); - jetpack.sendMessage("exception coping"); - - jetpack.sendMessage("duplicate receivers"); - - jetpack.sendMessage("test sandbox"); - jetpack.sendMessage("throw"); -} diff --git a/deps/mozjs/js/jetpack/tests/unit/test_jetpack_ctypes.js b/deps/mozjs/js/jetpack/tests/unit/test_jetpack_ctypes.js deleted file mode 100644 index 0c291b15cd9..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/test_jetpack_ctypes.js +++ /dev/null @@ -1,12 +0,0 @@ -function run_test() { - var jetpack = createJetpack({ - scriptFile: do_get_file("impl_jetpack_ctypes.js") - }); - jetpack.registerReceiver("onCTypesTested", function(name, ok) { - do_check_true(ok, "onCTypesTested"); - do_test_finished(); - }); - var jetpacktestdir = do_get_file('../../../../toolkit/components/ctypes/tests/unit').path; - jetpack.sendMessage("testCTypes", jetpacktestdir); - do_test_pending(); -} diff --git a/deps/mozjs/js/jetpack/tests/unit/test_jetpack_sandbox.js b/deps/mozjs/js/jetpack/tests/unit/test_jetpack_sandbox.js deleted file mode 100644 index 127b1e378ea..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/test_jetpack_sandbox.js +++ /dev/null @@ -1,19 +0,0 @@ -function run_test() { - var jetpack = createJetpack({ scriptFile: do_get_file("impl.js") }); - - var sandbox = Components.utils.Sandbox("about:blank"); - function registerReceiver(name, fn) { - jetpack.registerReceiver(name, fn); - } - sandbox.registerReceiver = registerReceiver; - sandbox.echoed = function(message, arg) { - do_check_eq(message, "echo"); - do_check_eq(arg, "testdata"); - jetpack.destroy(); - sandbox = null; - do_test_finished(); - }; - Components.utils.evalInSandbox("registerReceiver('echo', function(message, arg){ echoed(message, arg); });", sandbox); - jetpack.sendMessage("echo", "testdata"); - do_test_pending(); -} diff --git a/deps/mozjs/js/jetpack/tests/unit/test_rooting.js b/deps/mozjs/js/jetpack/tests/unit/test_rooting.js deleted file mode 100644 index 03d69887b25..00000000000 --- a/deps/mozjs/js/jetpack/tests/unit/test_rooting.js +++ /dev/null @@ -1,16 +0,0 @@ -function run_test() { - return; - var jetpack = createJetpack({ - scriptFile: do_get_file("impl_rooting.js") - }); - jetpack.registerReceiver("onInvalidateReceived", function(name, isValid) { - do_check_false(isValid, "onInvalidateReceived: isValid"); - do_test_finished(); - }); - var gchandle = jetpack.createHandle(); - jetpack.sendMessage("ReceiveGCHandle", gchandle); - gchandle.isRooted = false; - gchandle = null; - do_execute_soon(gc); - do_test_pending(); -} diff --git a/deps/mozjs/js/jsd/Makefile.in b/deps/mozjs/js/jsd/Makefile.in index fb2500a49f1..ec677f18e7c 100644 --- a/deps/mozjs/js/jsd/Makefile.in +++ b/deps/mozjs/js/jsd/Makefile.in @@ -42,23 +42,24 @@ DEPTH = ../.. topsrcdir = @top_srcdir@ VPATH = @srcdir@ srcdir = @srcdir@ +relativesrcdir = js/jsd include $(DEPTH)/config/autoconf.mk MODULE = jsdebug LIBRARY_NAME = jsd -FORCE_SHARED_LIB= 1 -ifeq ($(OS_ARCH)$(MOZ_ENABLE_LIBXUL),WINNT) -LIBRARY_NAME = jsd32$(VERSION_NUMBER) -endif +DIRS = idl +CPPSRCS = jsd_xpc.cpp +IS_COMPONENT = 1 +LIBXUL_LIBRARY = 1 -# REQUIRES = java js +MODULE_NAME = JavaScript_Debugger +EXPORT_LIBRARY = 1 +XPCSHELL_TESTS = test + +# REQUIRES = java js -EXTRA_DSO_LDOPTS += \ - $(MOZ_COMPONENT_LIBS) \ - $(MOZ_JS_LIBS) \ - $(NULL) EXPORTS = jsdebug.h ifdef JS_THREADSAFE @@ -79,21 +80,6 @@ CSRCS = \ jsd_val.c \ $(NULL) -ifdef JSD_STANDALONE -DIRS += jsdb -else -DIRS += idl -CPPSRCS = jsd_xpc.cpp -IS_COMPONENT = 1 -LIBXUL_LIBRARY = 1 - -ifdef MOZ_ENABLE_LIBXUL -FORCE_SHARED_LIB= -MODULE_NAME = JavaScript_Debugger -EXPORT_LIBRARY = 1 -endif -endif - ifdef ENABLE_TESTS TOOL_DIRS += test endif diff --git a/deps/mozjs/js/jsd/idl/jsdIDebuggerService.idl b/deps/mozjs/js/jsd/idl/jsdIDebuggerService.idl index a047bfcc40c..44be96cea50 100644 --- a/deps/mozjs/js/jsd/idl/jsdIDebuggerService.idl +++ b/deps/mozjs/js/jsd/idl/jsdIDebuggerService.idl @@ -1,6 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -79,7 +78,7 @@ interface jsdIActivationCallback; * Debugger service. It is not a good idea to have more than one active client * of the debugger service. */ -[scriptable, uuid(aa232c7f-855f-4488-a92c-6f89adc668cc)] +[scriptable, uuid(9be5b327-6818-464d-9695-f33885fd8377)] interface jsdIDebuggerService : nsISupports { /** Internal use only. */ @@ -249,7 +248,7 @@ interface jsdIDebuggerService : nsISupports /** * Recompile all active scripts in the runtime for debugMode. */ - [noscript] void recompileForDebugMode(in JSContext cx, in JSCompartment comp, in PRBool mode); + [noscript] void recompileForDebugMode(in JSContext cx, in JSCompartment comp, in boolean mode); /** * Turn the debugger off. This will invalidate all of your jsdIEphemeral @@ -274,7 +273,10 @@ interface jsdIDebuggerService : nsISupports */ unsigned long pause(); /** - * Undo a pause. + * Undo a pause. Once this is called, the debugger won't start + * getting execution callbacks until the stack is fully unwound so + * that no JS scripts are live. There is no way to query whether + * there are such scripts left to unwind at a given point in time. * * @return depth The number of remaining pending pause calls. */ @@ -372,13 +374,8 @@ interface jsdIDebuggerService : nsISupports * When called from another language this method returns an xpconnect * defined error code. */ - jsdIValue wrapValue(/*in jsvalue value*/); + jsdIValue wrapValue(in jsval value); - /** - * The same as above but to be called from C++. - */ - [noscript] jsdIValue wrapJSValue(in jsval value); - /* XXX these two routines are candidates for refactoring. The only problem * is that it is not clear where and how they should land. */ @@ -542,7 +539,7 @@ interface jsdIContextEnumerator : nsISupports /** * Set jsdIDebuggerService::scriptHook to an instance of one of these. */ -[scriptable, uuid(bb722893-0f63-45c5-b547-7a0947c7b6b6)] +[scriptable, uuid(d030d1a2-a58a-4f19-b9e3-96da4e2cdd09)] interface jsdIScriptHook : nsISupports { /** @@ -811,7 +808,7 @@ interface jsdIContext : jsdIEphemeral * interface. Once a jsdIStackFrame has been invalidated all method and * property accesses will throw a NS_ERROR_NOT_AVAILABLE exception. */ -[scriptable, uuid(0633ca73-105e-4e8e-bcc5-13405d61754a)] +[scriptable, uuid(7c95422c-7579-4a6f-8ef7-e5b391552ee5)] interface jsdIStackFrame : jsdIEphemeral { /** Internal use only. */ @@ -884,7 +881,7 @@ interface jsdIStackFrame : jsdIEphemeral * Script object. In JavaScript engine terms, there's a single script for each * function, and one for the top level script. */ -[scriptable, uuid(e7935220-7def-4c8e-832f-fbc948a97490)] +[scriptable, uuid(721724e0-7716-4bf4-b48f-92b78d056141)] interface jsdIScript : jsdIEphemeral { /** Internal use only. */ @@ -1030,6 +1027,17 @@ interface jsdIScript : jsdIEphemeral * The |pcmap| argument specifies which pc to source line map to use. */ boolean isLineExecutable(in unsigned long line, in unsigned long pcmap); + + /** + * Return a list of all executable lines in a script. + * |pcmap| specifies which pc to source line map to use. + * |startLine| and |maxLines| may be used to retrieve a chunk at a time. + */ + void getExecutableLines(in unsigned long pcmap, + in unsigned long startLine, in unsigned long maxLines, + [optional] out unsigned long count, + [array, size_is(count), retval] out unsigned long executableLines); + /** * Set a breakpoint at a PC in this script. */ @@ -1045,7 +1053,7 @@ interface jsdIScript : jsdIEphemeral /** * Call interrupt hook at least once per source line */ - void enableSingleStepInterrupts(in PRBool mode); + void enableSingleStepInterrupts(in boolean mode); }; /** @@ -1054,7 +1062,7 @@ interface jsdIScript : jsdIEphemeral * jsdIValue adds a root for the underlying JavaScript value, so don't keep it * if you don't need to. */ -[scriptable, uuid(fd1311f7-096c-44a3-847b-9d478c8176c3)] +[scriptable, uuid(1cd3535b-4ddb-4202-9053-e0ec88f5c82b)] interface jsdIValue : jsdIEphemeral { /** Internal use only. */ @@ -1182,7 +1190,7 @@ interface jsdIValue : jsdIEphemeral * When called from another language this method returns an xpconnect * defined error code. */ - void getWrappedValue(); + [implicit_jscontext] jsval getWrappedValue(); /** * If this is a function value, return its associated jsdIScript. diff --git a/deps/mozjs/js/jsd/jsd.h b/deps/mozjs/js/jsd/jsd.h index c1894d2a9ff..9078324b11c 100644 --- a/deps/mozjs/js/jsd/jsd.h +++ b/deps/mozjs/js/jsd/jsd.h @@ -136,7 +136,7 @@ struct JSDContext JSCList links; /* we are part of a JSCList */ JSBool inited; void* data; - uint32 flags; + uint32_t flags; JSD_ScriptHookProc scriptHook; void* scriptHookData; JSD_ExecutionHookProc interruptHook; @@ -168,7 +168,7 @@ struct JSDContext JSCList objectsList; JSHashTable* objectsTable; JSDProfileData* callingFunctionPData; - int64 lastReturnTime; + int64_t lastReturnTime; #ifdef JSD_THREADSAFE void* scriptsLock; void* sourceTextLock; @@ -187,12 +187,11 @@ struct JSDScript JSCList links; /* we are part of a JSCList */ JSDContext* jsdc; /* JSDContext for this jsdscript */ JSScript* script; /* script we are wrapping */ - JSFunction* function; /* back pointer to owning function (can be NULL) */ uintN lineBase; /* we cache this */ uintN lineExtent; /* we cache this */ JSCList hooks; /* JSCList of JSDExecHooks for this script */ char* url; - uint32 flags; + uint32_t flags; void* data; JSDProfileData *profileData; @@ -206,8 +205,8 @@ struct JSDScript struct JSDProfileData { JSDProfileData* caller; - int64 lastCallStart; - int64 runningTime; + int64_t lastCallStart; + int64_t runningTime; uintN callCount; uintN recurseDepth; uintN maxRecurseDepth; @@ -236,7 +235,7 @@ struct JSDExecHook { JSCList links; /* we are part of a JSCList */ JSDScript* jsdscript; - jsuword pc; + uintptr_t pc; JSD_ExecutionHookProc hook; void* callerdata; }; @@ -258,7 +257,7 @@ struct JSDStackFrameInfo JSCList links; /* we are part of a JSCList */ JSDThreadState* jsdthreadstate; JSDScript* jsdscript; - jsuword pc; + uintptr_t pc; JSStackFrame* fp; }; @@ -406,11 +405,11 @@ jsd_FindOrCreateJSDScript(JSDContext *jsdc, extern JSDProfileData* jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script); -extern uint32 +extern uint32_t jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script); extern void -jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32 flags); +jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags); extern uintN jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script); @@ -475,11 +474,16 @@ jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata); extern JSBool jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata); -extern jsuword +extern uintptr_t jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); extern uintN -jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); +jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); + +extern JSBool +jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + uintN startLine, uintN maxLines, + uintN* count, uintN** lines, uintptr_t** pcs); extern void jsd_NewScriptHookProc( @@ -501,14 +505,14 @@ jsd_DestroyScriptHookProc( extern JSBool jsd_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc, + uintptr_t pc, JSD_ExecutionHookProc hook, void* callerdata); extern JSBool jsd_ClearExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc); + uintptr_t pc); extern JSBool jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript); @@ -699,7 +703,7 @@ jsd_GetScriptForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe); -extern jsuword +extern uintptr_t jsd_GetPCForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe); @@ -965,7 +969,7 @@ jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval); extern JSBool jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval); -extern int32 +extern int32_t jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval); extern jsdouble @@ -1124,7 +1128,7 @@ extern JSDSourceText* jsdlw_ForceLoadSource(JSDContext* jsdc, JSDSourceText* jsdsrc); extern JSBool -jsdlw_UserCodeAtPC(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); +jsdlw_UserCodeAtPC(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); extern JSBool jsdlw_RawToProcessedLineNumber(JSDContext* jsdc, JSDScript* jsdscript, diff --git a/deps/mozjs/js/jsd/jsd_hook.c b/deps/mozjs/js/jsd/jsd_hook.c index 45c6be8e597..b407cbfe3ee 100644 --- a/deps/mozjs/js/jsd/jsd_hook.c +++ b/deps/mozjs/js/jsd/jsd_hook.c @@ -72,7 +72,7 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva return JSTRAP_CONTINUE; #ifdef LIVEWIRE - if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (jsuword)pc) ) + if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) ) return JSTRAP_CONTINUE; #endif @@ -124,10 +124,10 @@ jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, void* hookData; if( ! jsdc || ! jsdc->inited ) - return JSD_HOOK_RETURN_CONTINUE_THROW; + return JSTRAP_CONTINUE; if( JSD_IS_DANGEROUS_THREAD(jsdc) ) - return JSD_HOOK_RETURN_CONTINUE_THROW; + return JSTRAP_CONTINUE; /* local in case jsdc->throwHook gets cleared on another thread */ JSD_LOCK(); @@ -135,13 +135,13 @@ jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, hookData = jsdc->throwHookData; JSD_UNLOCK(); if (!hook) - return JSD_HOOK_RETURN_CONTINUE_THROW; + return JSTRAP_CONTINUE; JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, NULL); JSD_UNLOCK_SCRIPTS(jsdc); if( ! jsdscript ) - return JSD_HOOK_RETURN_CONTINUE_THROW; + return JSTRAP_CONTINUE; JS_GetPendingException(cx, rval); diff --git a/deps/mozjs/js/jsd/jsd_lock.c b/deps/mozjs/js/jsd/jsd_lock.c index ff5e6950f93..0d481d83745 100644 --- a/deps/mozjs/js/jsd/jsd_lock.c +++ b/deps/mozjs/js/jsd/jsd_lock.c @@ -66,7 +66,7 @@ struct JSDStaticLock PRLock* lock; int count; #ifdef DEBUG - uint16 sig; + uint16_t sig; #endif }; @@ -103,7 +103,7 @@ void ASSERT_VALID_LOCK(JSDStaticLock* lock) JS_ASSERT(lock); JS_ASSERT(lock->lock); JS_ASSERT(lock->count >= 0); - JS_ASSERT(lock->sig == (uint16) JSD_LOCK_SIG); + JS_ASSERT(lock->sig == (uint16_t) JSD_LOCK_SIG); } #else #define ASSERT_VALID_LOCK(x) ((void)0) @@ -124,7 +124,7 @@ jsd_CreateLock() } } #ifdef DEBUG - if(lock) lock->sig = (uint16) JSD_LOCK_SIG; + if(lock) lock->sig = (uint16_t) JSD_LOCK_SIG; #endif return lock; } diff --git a/deps/mozjs/js/jsd/jsd_scpt.c b/deps/mozjs/js/jsd/jsd_scpt.c index 237db062fc7..4bba0bbc320 100644 --- a/deps/mozjs/js/jsd/jsd_scpt.c +++ b/deps/mozjs/js/jsd/jsd_scpt.c @@ -91,8 +91,7 @@ HasFileExtention(const char* name, const char* ext) static JSDScript* _newJSDScript(JSDContext* jsdc, JSContext *cx, - JSScript *script, - JSFunction* function) + JSScript *script) { JSDScript* jsdscript; uintN lineno; @@ -114,8 +113,7 @@ _newJSDScript(JSDContext* jsdc, JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); jsdscript->jsdc = jsdc; - jsdscript->script = script; - jsdscript->function = function; + jsdscript->script = script; jsdscript->lineBase = lineno; jsdscript->lineExtent = (uintN)NOT_SET_YET; jsdscript->data = NULL; @@ -325,7 +323,7 @@ jsd_FindOrCreateJSDScript(JSDContext *jsdc, if (!fp) JS_FrameIterator(cx, &fp); if (fp) - jsdscript = _newJSDScript(jsdc, cx, script, JS_GetFrameFunction(cx, fp)); + jsdscript = _newJSDScript(jsdc, cx, script); return jsdscript; } @@ -339,14 +337,14 @@ jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script) return script->profileData; } -uint32 +uint32_t jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script) { return script->flags; } void -jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32 flags) +jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) { script->flags = flags; } @@ -442,7 +440,7 @@ jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) JSFunction * jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) { - return script->function; + return JS_GetScriptFunction(jsdc->dumbContext, script->script); } JSDScript* @@ -501,10 +499,11 @@ JSString* jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript) { JSString* str; + JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript); - if( ! jsdscript->function ) + if( ! fun ) return NULL; - str = JS_GetFunctionId(jsdscript->function); + str = JS_GetFunctionId(fun); /* For compatibility we return "anonymous", not an empty string here. */ return str ? str : JS_GetAnonymousString(jsdc->jsrt); @@ -524,14 +523,16 @@ jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) return jsdscript->lineExtent; } -jsuword +uintptr_t jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) { - jsuword pc; + uintptr_t pc; JSCrossCompartmentCall *call; + if( !jsdscript ) + return 0; #ifdef LIVEWIRE - if( jsdscript && jsdscript->lwscript ) + if( jsdscript->lwscript ) { uintN newline; jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline); @@ -543,13 +544,13 @@ jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script); if(!call) return 0; - pc = (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line ); + pc = (uintptr_t) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line ); JS_LeaveCrossCompartmentCall(call); return pc; } uintN -jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) +jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) { JSCrossCompartmentCall *call; uintN first = jsdscript->lineBase; @@ -580,6 +581,44 @@ jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) return line; } +JSBool +jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + uintN startLine, uintN maxLines, + uintN* count, uintN** retLines, uintptr_t** retPCs) +{ + JSCrossCompartmentCall *call; + uintN first = jsdscript->lineBase; + uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; + JSBool ok; + uintN *lines; + jsbytecode **pcs; + uintN i; + + if (last < startLine) + return JS_TRUE; + + call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script); + if (!call) + return JS_FALSE; + + ok = JS_GetLinePCs(jsdc->dumbContext, jsdscript->script, + startLine, maxLines, + count, retLines, &pcs); + + if (ok) { + if (retPCs) { + for (i = 0; i < *count; ++i) { + (*retPCs)[i] = (*pcs)[i]; + } + } + + JS_free(jsdc->dumbContext, pcs); + } + + JS_LeaveCrossCompartmentCall(call); + return ok; +} + JSBool jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata) { @@ -640,7 +679,7 @@ jsd_NewScriptHookProc( return; JSD_LOCK_SCRIPTS(jsdc); - jsdscript = _newJSDScript(jsdc, cx, script, fun); + jsdscript = _newJSDScript(jsdc, cx, script); JSD_UNLOCK_SCRIPTS(jsdc); if( ! jsdscript ) return; @@ -715,7 +754,7 @@ jsd_DestroyScriptHookProc( /***************************************************************************/ static JSDExecHook* -_findHook(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) +_findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) { JSDExecHook* jsdhook; JSCList* list = &jsdscript->hooks; @@ -782,7 +821,7 @@ jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, } JSD_ASSERT_VALID_EXEC_HOOK(jsdhook); - JS_ASSERT(!jsdhook->pc || jsdhook->pc == (jsuword)pc); + JS_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc); JS_ASSERT(jsdhook->jsdscript->script == script); JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc); @@ -800,7 +839,7 @@ jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, return JSTRAP_CONTINUE; #ifdef LIVEWIRE - if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (jsuword)pc) ) + if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) ) return JSTRAP_CONTINUE; #endif @@ -813,7 +852,7 @@ jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, JSBool jsd_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc, + uintptr_t pc, JSD_ExecutionHookProc hook, void* callerdata) { @@ -877,7 +916,7 @@ jsd_SetExecutionHook(JSDContext* jsdc, JSBool jsd_ClearExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc) + uintptr_t pc) { JSCrossCompartmentCall *call; JSDExecHook* jsdhook; diff --git a/deps/mozjs/js/jsd/jsd_stak.c b/deps/mozjs/js/jsd/jsd_stak.c index 6884adcc629..bb90856bd62 100644 --- a/deps/mozjs/js/jsd/jsd_stak.c +++ b/deps/mozjs/js/jsd/jsd_stak.c @@ -60,7 +60,7 @@ static JSDStackFrameInfo* _addNewFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSScript* script, - jsuword pc, + uintptr_t pc, JSStackFrame* fp) { JSDStackFrameInfo* jsdframe; @@ -125,7 +125,7 @@ jsd_NewThreadState(JSDContext* jsdc, JSContext *cx ) while( NULL != (fp = JS_FrameIterator(cx, &iter)) ) { JSScript* script = JS_GetFrameScript(cx, fp); - jsuword pc = (jsuword) JS_GetFramePC(cx, fp); + uintptr_t pc = (uintptr_t) JS_GetFramePC(cx, fp); jsval dummyThis; /* @@ -270,12 +270,12 @@ jsd_GetScriptForStackFrame(JSDContext* jsdc, return jsdscript; } -jsuword +uintptr_t jsd_GetPCForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe) { - jsuword pc = 0; + uintptr_t pc = 0; JSD_LOCK_THREADSTATES(jsdc); diff --git a/deps/mozjs/js/jsd/jsd_step.c b/deps/mozjs/js/jsd/jsd_step.c index 524a36a237c..8da3f477052 100644 --- a/deps/mozjs/js/jsd/jsd_step.c +++ b/deps/mozjs/js/jsd/jsd_step.c @@ -98,7 +98,7 @@ _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp, printf("%s this: ", JS_IsConstructorFrame(cx, fp) ? "constructing":""); if (JS_GetFrameThis(cx, fp, &thisVal)) - printf("0x%0llx", (JSUword) thisVal); + printf("0x%0llx", (uintptr_t) thisVal); else puts(""); } @@ -150,9 +150,9 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, { if (before) { - if (JSLL_IS_ZERO(pdata->lastCallStart)) + if (!pdata->lastCallStart) { - int64 now; + int64_t now; JSDProfileData *callerpdata; /* Get the time just the once, for consistency. */ @@ -162,17 +162,14 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, callerpdata = jsdc->callingFunctionPData; if (callerpdata) { - int64 ll_delta; + int64_t ll_delta; pdata->caller = callerpdata; /* We need to 'stop' the timer for the caller. * Use time since last return if appropriate. */ - if (JSLL_IS_ZERO(jsdc->lastReturnTime)) - { - JSLL_SUB(ll_delta, now, callerpdata->lastCallStart); - } else { - JSLL_SUB(ll_delta, now, jsdc->lastReturnTime); - } - JSLL_ADD(callerpdata->runningTime, callerpdata->runningTime, ll_delta); + ll_delta = jsdc->lastReturnTime + ? now - jsdc->lastReturnTime + : now - callerpdata->lastCallStart; + callerpdata->runningTime += ll_delta; } /* We're the new current function, and no return * has happened yet. */ @@ -188,13 +185,12 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, } /* make sure we're called for the return too. */ hookresult = JS_TRUE; - } else if (!pdata->recurseDepth && - !JSLL_IS_ZERO(pdata->lastCallStart)) { - int64 now, ll_delta; + } else if (!pdata->recurseDepth && pdata->lastCallStart) { + int64_t now, ll_delta; jsdouble delta; now = JS_Now(); - JSLL_SUB(ll_delta, now, pdata->lastCallStart); - JSLL_L2D(delta, ll_delta); + ll_delta = now - pdata->lastCallStart; + delta = ll_delta; delta /= 1000.0; pdata->totalExecutionTime += delta; /* minExecutionTime starts as 0, so we need to overwrite @@ -212,13 +208,13 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, * the running total by the time delta since the last * return, and use the running total instead of the * delta calculated above. */ - if (!JSLL_IS_ZERO(jsdc->lastReturnTime)) + if (jsdc->lastReturnTime) { /* Add last chunk to running time, and use total * running time as 'delta'. */ - JSLL_SUB(ll_delta, now, jsdc->lastReturnTime); - JSLL_ADD(pdata->runningTime, pdata->runningTime, ll_delta); - JSLL_L2D(delta, pdata->runningTime); + ll_delta = now - jsdc->lastReturnTime; + pdata->runningTime += ll_delta; + delta = pdata->runningTime; delta /= 1000.0; } diff --git a/deps/mozjs/js/jsd/jsd_val.c b/deps/mozjs/js/jsd/jsd_val.c index 2064e98dc3d..c51fa6a4c05 100644 --- a/deps/mozjs/js/jsd/jsd_val.c +++ b/deps/mozjs/js/jsd/jsd_val.c @@ -188,7 +188,7 @@ jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) return JSVAL_TO_BOOLEAN(val); } -int32 +int32_t jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) { jsval val = jsdval->val; @@ -228,7 +228,7 @@ jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) JS_BeginRequest(cx); /* Objects call JS_ValueToString in their own compartment. */ - scopeObj = JSVAL_IS_OBJECT(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; + scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; call = JS_EnterCrossCompartmentCall(cx, scopeObj); if(!call) { JS_EndRequest(cx); @@ -383,7 +383,7 @@ jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval) jsval val = jsdval->val; if (!JSVAL_IS_PRIMITIVE(val)) { cx = JSD_GetDefaultJSContext(jsdc); - obj = js_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val)); + obj = JS_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val)); if (!obj) { JS_ClearPendingException(cx); @@ -706,16 +706,7 @@ jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval) return NULL; if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; - JS_BeginRequest(jsdc->dumbContext); - call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj); - if(!call) { - JS_EndRequest(jsdc->dumbContext); - - return NULL; - } - proto = JS_GetPrototype(jsdc->dumbContext, obj); - JS_LeaveCrossCompartmentCall(call); - JS_EndRequest(jsdc->dumbContext); + proto = JS_GetPrototype(obj); if(!proto) return NULL; jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto)); @@ -747,7 +738,7 @@ jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval) return NULL; } - parent = JS_GetParent(jsdc->dumbContext,obj); + parent = JS_GetParentOrScopeChain(jsdc->dumbContext,obj); JS_LeaveCrossCompartmentCall(call); JS_EndRequest(jsdc->dumbContext); if(!parent) @@ -775,6 +766,9 @@ jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) return NULL; if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; + proto = JS_GetPrototype(obj); + if(!proto) + return NULL; JS_BeginRequest(jsdc->dumbContext); call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj); if(!call) { @@ -782,13 +776,6 @@ jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) return NULL; } - proto = JS_GetPrototype(jsdc->dumbContext,obj); - if(!proto) - { - JS_LeaveCrossCompartmentCall(call); - JS_EndRequest(jsdc->dumbContext); - return NULL; - } ctor = JS_GetConstructor(jsdc->dumbContext,proto); JS_LeaveCrossCompartmentCall(call); JS_EndRequest(jsdc->dumbContext); @@ -819,8 +806,7 @@ jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval) return NULL; } - if(JS_GET_CLASS(jsdc->dumbContext, obj)) - jsdval->className = JS_GET_CLASS(jsdc->dumbContext, obj)->name; + jsdval->className = JS_GetClass(obj)->name; JS_LeaveCrossCompartmentCall(call); JS_EndRequest(jsdc->dumbContext); } diff --git a/deps/mozjs/js/jsd/jsd_xpc.cpp b/deps/mozjs/js/jsd/jsd_xpc.cpp index ca0552ee6b6..bdd63e0f2e7 100644 --- a/deps/mozjs/js/jsd/jsd_xpc.cpp +++ b/deps/mozjs/js/jsd/jsd_xpc.cpp @@ -60,9 +60,6 @@ #include "nsIScriptContext.h" #include "nsIJSContextStack.h" -/* XXX private JS headers. */ -#include "jscompartment.h" - /* * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the * script hook. This was a hack to avoid some js engine problems that should @@ -257,7 +254,7 @@ jsds_FreeFilter (FilterRecord *rec) /* copies appropriate |filter| attributes into |rec|. * False return indicates failure, the contents of |rec| will not be changed. */ -PRBool +bool jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) { NS_ASSERTION (rec, "jsds_SyncFilter without rec"); @@ -267,7 +264,7 @@ jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) nsCOMPtr glob; nsresult rv = filter->GetGlobalObject(getter_AddRefs(glob)); if (NS_FAILED(rv)) - return PR_FALSE; + return false; if (glob) { nsCOMPtr nsiglob = do_QueryInterface(glob); if (nsiglob) @@ -277,17 +274,17 @@ jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) PRUint32 startLine; rv = filter->GetStartLine(&startLine); if (NS_FAILED(rv)) - return PR_FALSE; + return false; PRUint32 endLine; rv = filter->GetStartLine(&endLine); if (NS_FAILED(rv)) - return PR_FALSE; + return false; nsCAutoString urlPattern; rv = filter->GetUrlPattern (urlPattern); if (NS_FAILED(rv)) - return PR_FALSE; + return false; PRUint32 len = urlPattern.Length(); if (len) { @@ -334,7 +331,7 @@ jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) rec->urlPattern = urlPattern; - return PR_TRUE; + return true; } @@ -357,7 +354,7 @@ jsds_FindFilter (jsdIFilter *filter) } /* returns true if the hook should be executed. */ -PRBool +bool jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) { JSContext *cx = JSD_GetJSContext (jsdc, state); @@ -365,30 +362,30 @@ jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) if (!glob) { NS_WARNING("No global in threadstate"); - return PR_FALSE; + return false; } JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state); if (!frame) { NS_WARNING("No frame in threadstate"); - return PR_FALSE; + return false; } JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame); if (!script) - return PR_TRUE; + return true; - jsuword pc = JSD_GetPCForStackFrame (jsdc, state, frame); + uintptr_t pc = JSD_GetPCForStackFrame (jsdc, state, frame); - nsDependentCString url(JSD_GetScriptFilename (jsdc, script)); + nsCString url(JSD_GetScriptFilename (jsdc, script)); if (url.IsEmpty()) { NS_WARNING ("Script with no filename"); - return PR_FALSE; + return false; } if (!gFilters) - return PR_TRUE; + return true; PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc); PRUint32 len = 0; @@ -454,7 +451,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) (PR_NEXT_LINK(¤tFilter->links)); } while (currentFilter != gFilters); - return PR_TRUE; + return true; } @@ -474,7 +471,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx) if (jsds) { NS_ADDREF(jsds); jsds->GetScriptHook (getter_AddRefs(hook)); - jsds->Pause(nsnull); + jsds->DoPause(nsnull, true); } DeadScript *deadScripts = gDeadScripts; @@ -509,7 +506,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx) } if (jsds) { - jsds->UnPause(nsnull); + jsds->DoUnPause(nsnull, true); NS_RELEASE(jsds); } } @@ -545,7 +542,7 @@ static uintN jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, JSErrorReport *report, void *callerdata) { - static PRBool running = PR_FALSE; + static bool running = false; nsCOMPtr hook; gJsds->GetErrorHook(getter_AddRefs(hook)); @@ -555,7 +552,7 @@ jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, if (running) return JSD_ERROR_REPORTER_PASS_ALONG; - running = PR_TRUE; + running = true; nsCOMPtr val; if (JS_IsExceptionPending(cx)) { @@ -570,7 +567,7 @@ jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, PRUint32 pos; PRUint32 flags; PRUint32 errnum; - PRBool rval; + bool rval; if (report) { fileName.Assign(report->filename); line = report->lineno; @@ -586,11 +583,11 @@ jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, errnum = 0; } - gJsds->Pause(nsnull); + gJsds->DoPause(nsnull, true); hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval); - gJsds->UnPause(nsnull); + gJsds->DoUnPause(nsnull, true); - running = PR_FALSE; + running = false; if (!rval) return JSD_ERROR_REPORTER_DEBUG; @@ -629,9 +626,9 @@ jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, nsCOMPtr frame = getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame)); - gJsds->Pause(nsnull); + gJsds->DoPause(nsnull, true); hook->OnCall(frame, type); - gJsds->UnPause(nsnull); + gJsds->DoUnPause(nsnull, true); jsdStackFrame::InvalidateAll(); return JS_TRUE; @@ -691,13 +688,13 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, nsCOMPtr frame = getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame)); - gJsds->Pause(nsnull); + gJsds->DoPause(nsnull, true); jsdIValue *inout_rv = js_rv; NS_IF_ADDREF(inout_rv); hook->OnExecute (frame, type, &inout_rv, &hook_rv); js_rv = inout_rv; NS_IF_RELEASE(inout_rv); - gJsds->UnPause(nsnull); + gJsds->DoUnPause(nsnull, true); jsdStackFrame::InvalidateAll(); if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL || @@ -737,9 +734,9 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating, #ifdef CAUTIOUS_SCRIPTHOOK JS_UNKEEP_ATOMS(rt); #endif - gJsds->Pause(nsnull); + gJsds->DoPause(nsnull, true); hook->OnScriptCreated (script); - gJsds->UnPause(nsnull); + gJsds->DoUnPause(nsnull, true); #ifdef CAUTIOUS_SCRIPTHOOK JS_KEEP_ATOMS(rt); #endif @@ -766,9 +763,9 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating, JS_UNKEEP_ATOMS(rt); #endif - gJsds->Pause(nsnull); + gJsds->DoPause(nsnull, true); hook->OnScriptDestroyed (jsdis); - gJsds->UnPause(nsnull); + gJsds->DoUnPause(nsnull, true); #ifdef CAUTIOUS_SCRIPTHOOK JS_KEEP_ATOMS(rt); #endif @@ -888,7 +885,7 @@ NS_IMETHODIMP jsdProperty::Invalidate() { ASSERT_VALID_EPHEMERAL; - mValid = PR_FALSE; + mValid = false; jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry); JSD_DropProperty (mCx, mProperty); return NS_OK; @@ -916,7 +913,7 @@ jsdProperty::GetJSDProperty(JSDProperty **_rval) } NS_IMETHODIMP -jsdProperty::GetIsValid(PRBool *_rval) +jsdProperty::GetIsValid(bool *_rval) { *_rval = mValid; return NS_OK; @@ -983,7 +980,7 @@ AssignToJSString(nsACString *x, JSString *str) return NS_OK; } -jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), +jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(false), mTag(0), mCx(aCx), mScript(aScript), @@ -1012,17 +1009,15 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); JSD_UnlockScriptSubsystem(mCx); - mValid = PR_TRUE; + mValid = true; } } jsdScript::~jsdScript () { DEBUG_DESTROY ("jsdScript", gScriptCount); - if (mFileName) - delete mFileName; - if (mFunctionName) - delete mFunctionName; + delete mFileName; + delete mFunctionName; if (mPPLineMap) PR_Free(mPPLineMap); @@ -1047,7 +1042,6 @@ jsdScript::CreatePPLineMap() JSFunction *fun = JSD_GetJSFunction (mCx, mScript); JSScript *script; /* In JSD compartment */ PRUint32 baseLine; - JSObject *scriptObj = NULL; JSString *jsstr; size_t length; const jschar *chars; @@ -1098,17 +1092,12 @@ jsdScript::CreatePPLineMap() } JS::Anchor kungFuDeathGrip(jsstr); - scriptObj = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1); - if (!scriptObj) + script = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1); + if (!script) return nsnull; - 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 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 @@ -1214,7 +1203,7 @@ NS_IMETHODIMP jsdScript::Invalidate() { ASSERT_VALID_EPHEMERAL; - mValid = PR_FALSE; + mValid = false; /* release the addref we do in FromPtr */ jsdIScript *script = static_cast @@ -1246,7 +1235,7 @@ jsdScript::InvalidateAll () } NS_IMETHODIMP -jsdScript::GetIsValid(PRBool *_rval) +jsdScript::GetIsValid(bool *_rval) { *_rval = mValid; return NS_OK; @@ -1317,7 +1306,7 @@ jsdScript::GetParameterNames(PRUint32* count, PRUnichar*** paramNames) return NS_ERROR_OUT_OF_MEMORY; void *mark; - jsuword *names = JS_GetFunctionLocalNameArray(cx, fun, &mark); + uintptr_t *names = JS_GetFunctionLocalNameArray(cx, fun, &mark); if (!names) { NS_Free(ret); return NS_ERROR_OUT_OF_MEMORY; @@ -1518,7 +1507,7 @@ jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval) { ASSERT_VALID_EPHEMERAL; if (aPcmap == PCMAP_SOURCETEXT) { - jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine); + uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); *_rval = pc - mFirstPC; } else if (aPcmap == PCMAP_PRETTYPRINT) { *_rval = PPLineToPc(aLine); @@ -1530,7 +1519,7 @@ jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval) } NS_IMETHODIMP -jsdScript::EnableSingleStepInterrupts(PRBool enable) +jsdScript::EnableSingleStepInterrupts(bool enable) { ASSERT_VALID_EPHEMERAL; @@ -1542,16 +1531,68 @@ jsdScript::EnableSingleStepInterrupts(PRBool enable) } NS_IMETHODIMP -jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) +jsdScript::GetExecutableLines(PRUint32 aPcmap, PRUint32 aStartLine, PRUint32 aMaxLines, + PRUint32* aCount, PRUint32** aExecutableLines) +{ + ASSERT_VALID_EPHEMERAL; + if (aPcmap == PCMAP_SOURCETEXT) { + uintptr_t start = JSD_GetClosestPC(mCx, mScript, 0); + uintN lastLine = JSD_GetScriptBaseLineNumber(mCx, mScript) + + JSD_GetScriptLineExtent(mCx, mScript) - 1; + uintptr_t end = JSD_GetClosestPC(mCx, mScript, lastLine + 1); + + *aExecutableLines = static_cast(NS_Alloc((end - start + 1) * sizeof(PRUint32))); + if (!JSD_GetLinePCs(mCx, mScript, aStartLine, aMaxLines, aCount, aExecutableLines, NULL)) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; + } + + if (aPcmap == PCMAP_PRETTYPRINT) { + if (!mPPLineMap) { + if (!CreatePPLineMap()) + return NS_ERROR_OUT_OF_MEMORY; + } + + nsTArray lines; + PRUint32 i; + + for (i = 0; i < mPCMapSize; ++i) { + if (mPPLineMap[i].line >= aStartLine) + break; + } + + for (; i < mPCMapSize && lines.Length() < aMaxLines; ++i) { + lines.AppendElement(mPPLineMap[i].line); + } + + if (aCount) + *aCount = lines.Length(); + + *aExecutableLines = static_cast(NS_Alloc(lines.Length() * sizeof(PRUint32))); + if (!*aExecutableLines) + return NS_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < lines.Length(); ++i) + (*aExecutableLines)[i] = lines[i]; + + return NS_OK; + } + + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, bool *_rval) { ASSERT_VALID_EPHEMERAL; if (aPcmap == PCMAP_SOURCETEXT) { - jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine); + uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc)); } else if (aPcmap == PCMAP_PRETTYPRINT) { if (!mPPLineMap && !CreatePPLineMap()) return NS_ERROR_OUT_OF_MEMORY; - *_rval = PR_FALSE; + *_rval = false; for (PRUint32 i = 0; i < mPCMapSize; ++i) { if (mPPLineMap[i].line >= aLine) { *_rval = (mPPLineMap[i].line == aLine); @@ -1569,7 +1610,7 @@ NS_IMETHODIMP jsdScript::SetBreakpoint(PRUint32 aPC) { ASSERT_VALID_EPHEMERAL; - jsuword pc = mFirstPC + aPC; + uintptr_t pc = mFirstPC + aPC; JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, NULL); return NS_OK; } @@ -1578,7 +1619,7 @@ NS_IMETHODIMP jsdScript::ClearBreakpoint(PRUint32 aPC) { ASSERT_VALID_EPHEMERAL; - jsuword pc = mFirstPC + aPC; + uintptr_t pc = mFirstPC + aPC; JSD_ClearExecutionHook (mCx, mScript, pc); return NS_OK; } @@ -1623,7 +1664,7 @@ jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx) } jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx, - nsISupports *aISCx) : mValid(PR_TRUE), mTag(0), + nsISupports *aISCx) : mValid(true), mTag(0), mJSDCx(aJSDCx), mJSCx(aJSCx), mISCx(aISCx) { @@ -1644,7 +1685,7 @@ jsdContext::~jsdContext() } NS_IMETHODIMP -jsdContext::GetIsValid(PRBool *_rval) +jsdContext::GetIsValid(bool *_rval) { *_rval = mValid; return NS_OK; @@ -1654,7 +1695,7 @@ NS_IMETHODIMP jsdContext::Invalidate() { ASSERT_VALID_EPHEMERAL; - mValid = PR_FALSE; + mValid = false; jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry); return NS_OK; } @@ -1766,11 +1807,11 @@ jsdContext::GetGlobalObject (jsdIValue **_rval) } NS_IMETHODIMP -jsdContext::GetScriptsEnabled (PRBool *_rval) +jsdContext::GetScriptsEnabled (bool *_rval) { ASSERT_VALID_EPHEMERAL; if (!mISCx) { - *_rval = PR_TRUE; + *_rval = true; return NS_OK; } @@ -1784,7 +1825,7 @@ jsdContext::GetScriptsEnabled (PRBool *_rval) } NS_IMETHODIMP -jsdContext::SetScriptsEnabled (PRBool _rval) +jsdContext::SetScriptsEnabled (bool _rval) { ASSERT_VALID_EPHEMERAL; if (!mISCx) { @@ -1797,7 +1838,7 @@ jsdContext::SetScriptsEnabled (PRBool _rval) if (!context) return NS_ERROR_NO_INTERFACE; - context->SetScriptsEnabled(_rval, PR_TRUE); + context->SetScriptsEnabled(_rval, true); return NS_OK; } @@ -1860,7 +1901,7 @@ NS_IMETHODIMP jsdStackFrame::Invalidate() { ASSERT_VALID_EPHEMERAL; - mValid = PR_FALSE; + mValid = false; jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry); return NS_OK; } @@ -1897,7 +1938,7 @@ jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval) } NS_IMETHODIMP -jsdStackFrame::GetIsValid(PRBool *_rval) +jsdStackFrame::GetIsValid(bool *_rval) { *_rval = mValid; return NS_OK; @@ -1935,7 +1976,7 @@ jsdStackFrame::GetFunctionName(nsACString &_rval) } NS_IMETHODIMP -jsdStackFrame::GetIsDebugger(PRBool *_rval) +jsdStackFrame::GetIsDebugger(bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo); @@ -1943,7 +1984,7 @@ jsdStackFrame::GetIsDebugger(PRBool *_rval) } NS_IMETHODIMP -jsdStackFrame::GetIsConstructing(PRBool *_rval) +jsdStackFrame::GetIsConstructing(bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo); @@ -1968,9 +2009,9 @@ jsdStackFrame::GetPc(PRUint32 *_rval) mStackFrameInfo); if (!script) return NS_ERROR_FAILURE; - jsuword pcbase = JSD_GetClosestPC(mCx, script, 0); + uintptr_t pcbase = JSD_GetClosestPC(mCx, script, 0); - jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); + uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); if (pc) *_rval = pc - pcbase; else @@ -1985,7 +2026,7 @@ jsdStackFrame::GetLine(PRUint32 *_rval) JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, mStackFrameInfo); if (script) { - jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); + uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); *_rval = JSD_GetClosestLine (mCx, script, pc); } else { return NS_ERROR_FAILURE; @@ -2029,7 +2070,7 @@ jsdStackFrame::GetThisValue(jsdIValue **_rval) NS_IMETHODIMP jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName, - PRUint32 line, jsdIValue **result, PRBool *_rval) + PRUint32 line, jsdIValue **result, bool *_rval) { ASSERT_VALID_EPHEMERAL; @@ -2107,7 +2148,7 @@ jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue) return rv; } -jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE), +jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(true), mCx(aCx), mValue(aValue) { @@ -2125,7 +2166,7 @@ jsdValue::~jsdValue() } NS_IMETHODIMP -jsdValue::GetIsValid(PRBool *_rval) +jsdValue::GetIsValid(bool *_rval) { *_rval = mValid; return NS_OK; @@ -2135,7 +2176,7 @@ NS_IMETHODIMP jsdValue::Invalidate() { ASSERT_VALID_EPHEMERAL; - mValid = PR_FALSE; + mValid = false; jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry); JSD_DropValue (mCx, mValue); return NS_OK; @@ -2165,7 +2206,7 @@ jsdValue::GetJSDValue (JSDValue **_rval) } NS_IMETHODIMP -jsdValue::GetIsNative (PRBool *_rval) +jsdValue::GetIsNative (bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_IsValueNative (mCx, mValue); @@ -2173,7 +2214,7 @@ jsdValue::GetIsNative (PRBool *_rval) } NS_IMETHODIMP -jsdValue::GetIsNumber (PRBool *_rval) +jsdValue::GetIsNumber (bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_IsValueNumber (mCx, mValue); @@ -2181,7 +2222,7 @@ jsdValue::GetIsNumber (PRBool *_rval) } NS_IMETHODIMP -jsdValue::GetIsPrimitive (PRBool *_rval) +jsdValue::GetIsPrimitive (bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_IsValuePrimitive (mCx, mValue); @@ -2262,7 +2303,7 @@ jsdValue::GetJsFunctionName(nsACString &_rval) } NS_IMETHODIMP -jsdValue::GetBooleanValue(PRBool *_rval) +jsdValue::GetBooleanValue(bool *_rval) { ASSERT_VALID_EPHEMERAL; *_rval = JSD_GetValueBoolean (mCx, mValue); @@ -2396,34 +2437,13 @@ jsdValue::Refresh() } NS_IMETHODIMP -jsdValue::GetWrappedValue() +jsdValue::GetWrappedValue(JSContext* aCx, JS::Value* aRetval) { ASSERT_VALID_EPHEMERAL; - nsresult rv; - nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); - if (NS_FAILED(rv)) - return rv; - - nsAXPCNativeCallContext *cc = nsnull; - rv = xpc->GetCurrentNativeCallContext(&cc); - if (NS_FAILED(rv)) - return rv; - jsval *result; - rv = cc->GetRetValPtr(&result); - if (NS_FAILED(rv)) - return rv; - - if (result) - { - JSContext *cx; - rv = cc->GetJSContext(&cx); - if (NS_FAILED(rv)) - return rv; - *result = JSD_GetValueWrappedJSVal (mCx, mValue); - if (!JS_WrapValue(cx, result)) - return NS_ERROR_FAILURE; - cc->SetReturnValueWasSet(PR_TRUE); + *aRetval = JSD_GetValueWrappedJSVal(mCx, mValue); + if (!JS_WrapValue(aCx, aRetval)) { + return NS_ERROR_FAILURE; } return NS_OK; @@ -2488,7 +2508,7 @@ jsdService::GetImplementationMinor(PRUint32 *_rval) } NS_IMETHODIMP -jsdService::GetIsOn (PRBool *_rval) +jsdService::GetIsOn (bool *_rval) { *_rval = mOn; return NS_OK; @@ -2505,25 +2525,16 @@ jsdService::AsyncOn (jsdIActivationCallback *activationCallback) { nsresult rv; - /* get JS things from the CallContext */ nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); if (NS_FAILED(rv)) return rv; - nsAXPCNativeCallContext *cc = nsnull; - rv = xpc->GetCurrentNativeCallContext(&cc); - if (NS_FAILED(rv)) return rv; - - JSContext *cx; - rv = cc->GetJSContext (&cx); - if (NS_FAILED(rv)) return rv; - mActivationCallback = activationCallback; - return xpc->SetDebugModeWhenPossible(PR_TRUE); + return xpc->SetDebugModeWhenPossible(true, true); } NS_IMETHODIMP -jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, JSBool mode) { +jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, bool mode) { NS_ASSERTION(NS_IsMainThread(), "wrong thread"); /* XPConnect now does this work itself, so this IDL entry point is no longer used. */ return NS_ERROR_NOT_IMPLEMENTED; @@ -2555,7 +2566,7 @@ jsdService::DeactivateDebugger () mCx = nsnull; mRuntime = nsnull; - mOn = PR_FALSE; + mOn = false; return NS_OK; } @@ -2617,14 +2628,16 @@ jsdService::ActivateDebugger (JSRuntime *rt) JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); else JSD_ClearFunctionHook (mCx); - mOn = PR_TRUE; + mOn = true; #ifdef DEBUG printf ("+++ JavaScript debugging hooks installed.\n"); #endif - if (mActivationCallback) - return mActivationCallback->OnDebuggerActivated(); + nsCOMPtr activationCallback; + mActivationCallback.swap(activationCallback); + if (activationCallback) + return activationCallback->OnDebuggerActivated(); return NS_OK; } @@ -2663,7 +2676,7 @@ jsdService::Off (void) if (NS_FAILED(rv)) return rv; - xpc->SetDebugModeWhenPossible(PR_FALSE); + xpc->SetDebugModeWhenPossible(false, true); return NS_OK; } @@ -2678,6 +2691,12 @@ jsdService::GetPauseDepth(PRUint32 *_rval) NS_IMETHODIMP jsdService::Pause(PRUint32 *_rval) +{ + return DoPause(_rval, false); +} + +nsresult +jsdService::DoPause(PRUint32 *_rval, bool internalCall) { if (!mCx) return NS_ERROR_NOT_INITIALIZED; @@ -2691,6 +2710,15 @@ jsdService::Pause(PRUint32 *_rval) JSD_ClearTopLevelHook (mCx); JSD_ClearFunctionHook (mCx); JSD_DebuggerPause (mCx); + + nsresult rv; + nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) return rv; + + if (!internalCall) { + rv = xpc->SetDebugModeWhenPossible(false, false); + NS_ENSURE_SUCCESS(rv, rv); + } } if (_rval) @@ -2701,6 +2729,12 @@ jsdService::Pause(PRUint32 *_rval) NS_IMETHODIMP jsdService::UnPause(PRUint32 *_rval) +{ + return DoUnPause(_rval, false); +} + +nsresult +jsdService::DoUnPause(PRUint32 *_rval, bool internalCall) { if (!mCx) return NS_ERROR_NOT_INITIALIZED; @@ -2731,6 +2765,15 @@ jsdService::UnPause(PRUint32 *_rval) JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); else JSD_ClearFunctionHook (mCx); + + nsresult rv; + nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); + if (NS_FAILED(rv)) return rv; + + if (!internalCall) { + rv = xpc->SetDebugModeWhenPossible(true, false); + NS_ENSURE_SUCCESS(rv, rv); + } } if (_rval) @@ -2808,7 +2851,7 @@ jsdService::DumpHeap(const nsACString &fileName) rv = NS_ERROR_FAILURE; } else { JSContext *cx = JSD_GetDefaultJSContext (mCx); - if (!JS_DumpHeap(cx, file, NULL, 0, NULL, (size_t)-1, NULL)) + if (!JS_DumpHeap(cx, file, NULL, JSTRACE_OBJECT, NULL, (size_t)-1, NULL)) rv = NS_ERROR_FAILURE; if (file != stdout) fclose(file); @@ -3009,38 +3052,9 @@ jsdService::ClearAllBreakpoints (void) } NS_IMETHODIMP -jsdService::WrapValue(jsdIValue **_rval) +jsdService::WrapValue(const JS::Value &value, jsdIValue **_rval) { ASSERT_VALID_CONTEXT; - - nsresult rv; - nsCOMPtr xpc = do_GetService (nsIXPConnect::GetCID(), &rv); - if (NS_FAILED(rv)) - return rv; - - nsAXPCNativeCallContext *cc = nsnull; - rv = xpc->GetCurrentNativeCallContext (&cc); - if (NS_FAILED(rv)) - return rv; - - PRUint32 argc; - rv = cc->GetArgc (&argc); - if (NS_FAILED(rv)) - return rv; - if (argc < 1) - return NS_ERROR_INVALID_ARG; - - jsval *argv; - rv = cc->GetArgvPtr (&argv); - if (NS_FAILED(rv)) - return rv; - - return WrapJSValue(argv[0], _rval); -} - -NS_IMETHODIMP -jsdService::WrapJSValue(const jsval &value, jsdIValue** _rval) -{ JSDValue *jsdv = JSD_NewValue(mCx, value); if (!jsdv) return NS_ERROR_FAILURE; @@ -3067,9 +3081,9 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval) if (NS_SUCCEEDED(stack->Push(nsnull))) { if (callback) { - Pause(nsnull); + DoPause(nsnull, true); rv = callback->OnNest(); - UnPause(nsnull); + DoUnPause(nsnull, true); } while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) { @@ -3404,7 +3418,7 @@ jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic, if (NS_FAILED(rv)) return rv; - PRBool on; + bool on; rv = jsds->GetIsOn(&on); if (NS_FAILED(rv) || on) return rv; diff --git a/deps/mozjs/js/jsd/jsd_xpc.h b/deps/mozjs/js/jsd/jsd_xpc.h index ed759f48c84..28b66571286 100644 --- a/deps/mozjs/js/jsd/jsd_xpc.h +++ b/deps/mozjs/js/jsd/jsd_xpc.h @@ -124,7 +124,7 @@ class jsdProperty : public jsdIProperty jsdProperty(); /* no implementation */ jsdProperty(const jsdProperty&); /* no implementation */ - PRBool mValid; + bool mValid; LiveEphemeral mLiveListEntry; JSDContext *mCx; JSDProperty *mProperty; @@ -173,7 +173,7 @@ class jsdScript : public jsdIScript PRUint32 PPPcToLine(PRUint32 aPC); PRUint32 PPLineToPc(PRUint32 aLine); - PRBool mValid; + bool mValid; PRUint32 mTag; JSDContext *mCx; JSDScript *mScript; @@ -182,7 +182,7 @@ class jsdScript : public jsdIScript PRUint32 mBaseLineNumber, mLineExtent; PCMapEntry *mPPLineMap; PRUint32 mPCMapSize; - jsuword mFirstPC; + uintptr_t mFirstPC; }; PRUint32 jsdScript::LastTag = 0; @@ -205,7 +205,7 @@ class jsdContext : public jsdIContext jsdContext (); /* no implementation */ jsdContext (const jsdContext&); /* no implementation */ - PRBool mValid; + bool mValid; LiveEphemeral mLiveListEntry; PRUint32 mTag; JSDContext *mJSDCx; @@ -236,7 +236,7 @@ class jsdStackFrame : public jsdIStackFrame jsdStackFrame(); /* no implementation */ jsdStackFrame(const jsdStackFrame&); /* no implementation */ - PRBool mValid; + bool mValid; LiveEphemeral mLiveListEntry; JSDContext *mCx; JSDThreadState *mThreadState; @@ -261,7 +261,7 @@ class jsdValue : public jsdIValue jsdValue(); /* no implementation */ jsdValue (const jsdScript&); /* no implementation */ - PRBool mValid; + bool mValid; LiveEphemeral mLiveListEntry; JSDContext *mCx; JSDValue *mValue; @@ -277,7 +277,7 @@ class jsdService : public jsdIDebuggerService NS_DECL_ISUPPORTS NS_DECL_JSDIDEBUGGERSERVICE - jsdService() : mOn(PR_FALSE), mPauseLevel(0), + jsdService() : mOn(false), mPauseLevel(0), mNestedLoopLevel(0), mCx(0), mRuntime(0), mErrorHook(0), mBreakpointHook(0), mDebugHook(0), mDebuggerHook(0), mInterruptHook(0), mScriptHook(0), mThrowHook(0), @@ -289,10 +289,13 @@ class jsdService : public jsdIDebuggerService static jsdService *GetService (); - PRBool CheckInterruptHook() { return !!mInterruptHook; } + bool CheckInterruptHook() { return !!mInterruptHook; } + nsresult DoPause(PRUint32 *_rval, bool internalCall); + nsresult DoUnPause(PRUint32 *_rval, bool internalCall); + private: - PRBool mOn; + bool mOn; PRUint32 mPauseLevel; PRUint32 mNestedLoopLevel; JSDContext *mCx; diff --git a/deps/mozjs/js/jsd/jsdebug.c b/deps/mozjs/js/jsd/jsdebug.c index 6ae9c5801b0..576fd8becb2 100644 --- a/deps/mozjs/js/jsd/jsdebug.c +++ b/deps/mozjs/js/jsd/jsdebug.c @@ -133,9 +133,9 @@ JSD_ClearAllProfileData(JSDContext *jsdc) } JSD_PUBLIC_API(void) -JSD_SetContextFlags(JSDContext *jsdc, uint32 flags) +JSD_SetContextFlags(JSDContext *jsdc, uint32_t flags) { - uint32 oldFlags = jsdc->flags; + uint32_t oldFlags = jsdc->flags; JSD_ASSERT_VALID_CONTEXT(jsdc); jsdc->flags = flags; if (flags & JSD_COLLECT_PROFILE_DATA) { @@ -145,7 +145,7 @@ JSD_SetContextFlags(JSDContext *jsdc, uint32 flags) } } -JSD_PUBLIC_API(uint32) +JSD_PUBLIC_API(uint32_t) JSD_GetContextFlags(JSDContext *jsdc) { JSD_ASSERT_VALID_CONTEXT(jsdc); @@ -182,7 +182,7 @@ JSD_IterateScripts(JSDContext* jsdc, JSDScript **iterp) return jsd_IterateScripts(jsdc, iterp); } -JSD_PUBLIC_API(uint32) +JSD_PUBLIC_API(uint32_t) JSD_GetScriptFlags(JSDContext *jsdc, JSDScript *script) { JSD_ASSERT_VALID_CONTEXT(jsdc); @@ -190,7 +190,7 @@ JSD_GetScriptFlags(JSDContext *jsdc, JSDScript *script) } JSD_PUBLIC_API(void) -JSD_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32 flags) +JSD_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) { JSD_ASSERT_VALID_CONTEXT(jsdc); jsd_SetScriptFlags(jsdc, script, flags); @@ -340,7 +340,7 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) return jsd_GetScriptHook(jsdc, hook, callerdata); } -JSD_PUBLIC_API(jsuword) +JSD_PUBLIC_API(uintptr_t) JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) { JSD_ASSERT_VALID_CONTEXT(jsdc); @@ -349,13 +349,23 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) } JSD_PUBLIC_API(uintN) -JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) +JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) { JSD_ASSERT_VALID_CONTEXT(jsdc); JSD_ASSERT_VALID_SCRIPT(jsdscript); return jsd_GetClosestLine(jsdc, jsdscript, pc); } +JSD_PUBLIC_API(JSBool) +JSD_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + uintN startLine, uintN maxLines, + uintN* count, uintN** lines, uintptr_t** pcs) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetLinePCs(jsdc, jsdscript, startLine, maxLines, count, lines, pcs); +} + JSD_PUBLIC_API(void) JSD_ScriptCreated(JSDContext* jsdc, JSContext *cx, @@ -533,7 +543,7 @@ JSD_AddFullSourceText(JSDContext* jsdc, JSD_PUBLIC_API(JSBool) JSD_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc, + uintptr_t pc, JSD_ExecutionHookProc hook, void* callerdata) { @@ -545,7 +555,7 @@ JSD_SetExecutionHook(JSDContext* jsdc, JSD_PUBLIC_API(JSBool) JSD_ClearExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc) + uintptr_t pc) { JSD_ASSERT_VALID_CONTEXT(jsdc); JSD_ASSERT_VALID_SCRIPT(jsdscript); @@ -713,7 +723,7 @@ JSD_GetScriptForStackFrame(JSDContext* jsdc, return jsd_GetScriptForStackFrame(jsdc, jsdthreadstate, jsdframe); } -JSD_PUBLIC_API(jsuword) +JSD_PUBLIC_API(uintptr_t) JSD_GetPCForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe) @@ -1092,7 +1102,7 @@ JSD_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) return jsd_GetValueBoolean(jsdc, jsdval); } -JSD_PUBLIC_API(int32) +JSD_PUBLIC_API(int32_t) JSD_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) { JSD_ASSERT_VALID_CONTEXT(jsdc); diff --git a/deps/mozjs/js/jsd/jsdebug.h b/deps/mozjs/js/jsd/jsdebug.h index ce0a3dc4e7a..c619c309220 100644 --- a/deps/mozjs/js/jsd/jsdebug.h +++ b/deps/mozjs/js/jsd/jsdebug.h @@ -258,9 +258,9 @@ JSD_ClearAllProfileData(JSDContext* jsdc); extern JSD_PUBLIC_API(void) -JSD_SetContextFlags (JSDContext* jsdc, uint32 flags); +JSD_SetContextFlags (JSDContext* jsdc, uint32_t flags); -extern JSD_PUBLIC_API(uint32) +extern JSD_PUBLIC_API(uint32_t) JSD_GetContextFlags (JSDContext* jsdc); /* @@ -398,11 +398,11 @@ JSD_GetJSFunction(JSDContext* jsdc, JSDScript *script); */ #define JSD_SCRIPT_DEBUG_BIT 0x02 -extern JSD_PUBLIC_API(uint32) +extern JSD_PUBLIC_API(uint32_t) JSD_GetScriptFlags(JSDContext *jsdc, JSDScript* jsdscript); extern JSD_PUBLIC_API(void) -JSD_SetScriptFlags(JSDContext *jsdc, JSDScript* jsdscript, uint32 flags); +JSD_SetScriptFlags(JSDContext *jsdc, JSDScript* jsdscript, uint32_t flags); /* * Set the private data for this script, returns previous value @@ -485,7 +485,7 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) * code within the script (if any) after the given line. * This function can be used to set breakpoints -- see JSD_SetExecutionHook */ -extern JSD_PUBLIC_API(jsuword) +extern JSD_PUBLIC_API(uintptr_t) JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); /* @@ -494,7 +494,18 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); * the given pc. */ extern JSD_PUBLIC_API(uintN) -JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); +JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc); + +/* + * Get a list of lines and the corresponding earliest PC for each (see + * JSD_GetClosestPC). Lines with no PCs associated will not be returned. NULL + * may be passed for either lines or pcs to avoid filling anything in for that + * argument. + */ +extern JSD_PUBLIC_API(JSBool) +JSD_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, + uintN startLine, uintN maxLines, + uintN* count, uintN** lines, uintptr_t** pcs); /* these are only used in cases where scripts are created outside of JS*/ @@ -769,7 +780,7 @@ typedef JSBool extern JSD_PUBLIC_API(JSBool) JSD_SetExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc, + uintptr_t pc, JSD_ExecutionHookProc hook, void* callerdata); @@ -779,7 +790,7 @@ JSD_SetExecutionHook(JSDContext* jsdc, extern JSD_PUBLIC_API(JSBool) JSD_ClearExecutionHook(JSDContext* jsdc, JSDScript* jsdscript, - jsuword pc); + uintptr_t pc); /* * Clear all the pc specific hooks for this script @@ -928,7 +939,7 @@ JSD_GetScriptForStackFrame(JSDContext* jsdc, /* * Get the 'Program Counter' for the given call stack frame */ -extern JSD_PUBLIC_API(jsuword) +extern JSD_PUBLIC_API(uintptr_t) JSD_GetPCForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe); @@ -1272,10 +1283,10 @@ extern JSD_PUBLIC_API(JSBool) JSD_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval); /* -* Return int32 value (does NOT do conversion). +* Return int32_t value (does NOT do conversion). * *** new for version 1.1 **** */ -extern JSD_PUBLIC_API(int32) +extern JSD_PUBLIC_API(int32_t) JSD_GetValueInt(JSDContext* jsdc, JSDValue* jsdval); /* diff --git a/deps/mozjs/js/jsd/test/Makefile.in b/deps/mozjs/js/jsd/test/Makefile.in index 1142a0cec14..22c6e76eb81 100644 --- a/deps/mozjs/js/jsd/test/Makefile.in +++ b/deps/mozjs/js/jsd/test/Makefile.in @@ -49,6 +49,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = test_bug507448.html bug507448.js \ test_bug617870-callhooks.html test-bug617870-callhooks.js jsd-test.js \ + test_bug638178-execlines.html test-bug638178-execlines.js \ $(NULL) libs:: $(_TEST_FILES) diff --git a/deps/mozjs/js/jsd/test/test-bug638178-execlines.js b/deps/mozjs/js/jsd/test/test-bug638178-execlines.js new file mode 100644 index 00000000000..a35811dfe60 --- /dev/null +++ b/deps/mozjs/js/jsd/test/test-bug638178-execlines.js @@ -0,0 +1,95 @@ +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + +var jsdIScript = Components.interfaces.jsdIScript; + +function f1() { + var x; +} + +function f2() { + + + var x; var y; x = 1; +} + +function f3() { + + + var x; + + var y; var y2; y = 1; + var z; + +} + +var jsdIFilter = Components.interfaces.jsdIFilter; + +function testJSD(jsd) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + ok(jsd.isOn, "JSD needs to be running for this test."); + + jsd.functionHook = ({ + onCall: function(frame, type) { + //console.log("Got " + type); + console.log("Got " + frame.script.fileName); + } + }); + + console.log("Triggering functions"); + f1(); + f2(); + f3(); + console.log("Done with functions"); + + var linemap = {}; + var firsts = {}; + var rests = {}; + var startlines = {}; + jsd.enumerateScripts({ + enumerateScript: function(script) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (/execlines\.js$/.test(script.fileName)) { + console.log("script: " + script.fileName + " " + script.functionName); + var execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, 0, 10000); + console.log(execLines.toSource()); + linemap[script.functionName] = execLines; + startlines[script.functionName] = script.baseLineNumber; + + execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, 0, 1); + firsts[script.functionName] = execLines; + execLines = script.getExecutableLines(jsdIScript.PCMAP_SOURCETEXT, execLines[0]+1, 10000); + rests[script.functionName] = execLines; + } + } + }); + + var checklines = function (funcname, linemap, rellines) { + var base = startlines[funcname]; + var b = []; + for (var i = 0; i < rellines.length; ++i) { + b[i] = rellines[i] + base; + } + is(linemap[funcname].toSource(), b.toSource(), funcname + " lines"); + }; + + checklines('f1', linemap, [ 1 ]); + checklines('f2', linemap, [ 3 ]); + checklines('f3', linemap, [ 3, 5, 6 ]); + + checklines('f1', firsts, [ 1 ]); + checklines('f1', rests, []); + checklines('f3', firsts, [ 3 ]); + checklines('f3', rests, [ 5, 6 ]); + + jsd.functionHook = null; + + if (!jsdOnAtStart) { + // turn JSD off if it wasn't on when this test started + jsd.off(); + ok(!jsd.isOn, "JSD shouldn't be running at the end of this test."); + } + + SimpleTest.finish(); +} + +testJSD(jsd); diff --git a/deps/mozjs/js/jsd/test/test_bug507448.html b/deps/mozjs/js/jsd/test/test_bug507448.html index 54f87cc74c1..1df24fd87c0 100644 --- a/deps/mozjs/js/jsd/test/test_bug507448.html +++ b/deps/mozjs/js/jsd/test/test_bug507448.html @@ -5,7 +5,9 @@ --> Test for Bug 507448 - + + + diff --git a/deps/mozjs/js/jsd/test/test_bug617870-callhooks.html b/deps/mozjs/js/jsd/test/test_bug617870-callhooks.html index f0c4ee0abb3..e9f5b2e3023 100644 --- a/deps/mozjs/js/jsd/test/test_bug617870-callhooks.html +++ b/deps/mozjs/js/jsd/test/test_bug617870-callhooks.html @@ -3,7 +3,6 @@ JSD Test for Bug AUTOFILLED - diff --git a/deps/mozjs/js/jsd/test/test_bug638178-execlines.html b/deps/mozjs/js/jsd/test/test_bug638178-execlines.html new file mode 100644 index 00000000000..75cd1b09480 --- /dev/null +++ b/deps/mozjs/js/jsd/test/test_bug638178-execlines.html @@ -0,0 +1,46 @@ + + + + + JSD Test for Bug AUTOFILLED + + + + + + + + +

+ + + +
+
+ + + diff --git a/deps/mozjs/js/jsd/test/test_jsval_retval.js b/deps/mozjs/js/jsd/test/test_jsval_retval.js new file mode 100644 index 00000000000..78b44ac2d31 --- /dev/null +++ b/deps/mozjs/js/jsd/test/test_jsval_retval.js @@ -0,0 +1,39 @@ +// Bug 689101 - if the binary layout of jsval does not match between C and C++ +// code, then calls to functions returning jsval may get compiled differently +// than the callee, resulting in parameters being shifted over by one. +// +// An example is where on Windows, calling jsdValue.getWrappedValue() will +// return a random floating point number instead of an object. +// +// This test must be run with debugging already enabled + +function run_test() { + const Cc = Components.classes; + const Ci = Components.interfaces; + const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"]; + const jsdIDebuggerService = Ci.jsdIDebuggerService; + var jsd = DebuggerService.getService(jsdIDebuggerService); + + do_check_true(jsd.isOn); + + var n = 0; + function f() { + n++; + } + + jsd.enumerateScripts({ enumerateScript: function(script) { + script.setBreakpoint(0); + } }); + + jsd.breakpointHook = function(frame, type, dummy) { + var scope = frame.scope; + var parent = scope.jsParent; // Probably does not need to be called + var wrapped = scope.getWrappedValue(); + // Do not try to print 'wrapped'; it may be an internal Call object + // that will crash when you toString it. Different bug. + do_check_eq(typeof(wrapped), "object"); + return Ci.jsdIExecutionHook.RETURN_CONTINUE; + }; + + f(); +} diff --git a/deps/mozjs/js/jsd/test/xpcshell.ini b/deps/mozjs/js/jsd/test/xpcshell.ini new file mode 100644 index 00000000000..7c4f5af7024 --- /dev/null +++ b/deps/mozjs/js/jsd/test/xpcshell.ini @@ -0,0 +1,6 @@ +[DEFAULT] +head = +tail = + +[test_jsval_retval.js] +debug = 1 diff --git a/deps/mozjs/js/landbranch.pl b/deps/mozjs/js/landbranch.pl deleted file mode 100755 index 369b74d43e0..00000000000 --- a/deps/mozjs/js/landbranch.pl +++ /dev/null @@ -1,227 +0,0 @@ -#! /usr/local/bin/perl5 - -use File::Path; - -# The development branch is where primary development and checkins -# are done on a day-to-day basis. -$development_branch_prefix = "SpiderMonkey140"; - -# Space-separated list of CVS-controlled directories to tag/merge -$merge_dirs = - "mozilla/js/src " ; - -# When line below uncommented, don't recurse into subdirs -#$recurse_flag = '-l'; - -#---------------------------------------------------------------------------- - -# The merge branch is itself a branch off of the development branch -# at a point where the development branch is thought to be stable. -# (A branch is used rather than a static tag because, inevitably, -# the development branch is not quite as stable/buildable as was -# thought.) The contents of the merge branch will be copied to -# the trunk when merging takes place. - - -# The following tags are created automatically by this script: -# -# JS_STABLE_DROP -# -# A static tag on the development branch (or a branch off the -# development branch) that indicates the code that should be merged -# into the trunk. This is a "variable" tag in the sense that it is -# redefined after each merge. -# -# JS_LAST_STABLE_DROP -# -# A static tag that is a copy of what the JS_STABLE_DROP tag was in -# the previous merge cycle. This is a "variable" tag that is -# redefined after each merge. Changes in the branch can be merged -# to the trunk by using: -# -# cvs up -jJS_LAST_STABLE_DROP -jJS_STABLE_DROP -# -# JS_LANDING -# -# A static tag that identifies the code on the trunk after the merge -# from the branch to the trunk takes place. This is a "variable" -# tag that is redefined after each merge. Changes on the trunk -# since the last branch landing can be seen by using: -# -# cvs diff -rJS_LANDING -rHEAD -# -# JS_LANDING_mmddyyyy -# -# This is a tag on the trunk which may be used for archaeological -# purposes. This tag is made from the JS_LANDING tag. - - -$development_branch = $development_branch_prefix . "_BRANCH"; -$development_base = $development_branch_prefix . "_BASE"; - -sub help { -print <<"END"; -$0: A tool for merging stable snapshots of JavaScript from a CVS -development branch onto the trunk - -Landing a snapshot of the development branch consists of -the following steps: - - 1) Tag all/some files on the branch to identify files to be merged. - 2) Merge files from the branch into the trunk using a temporary - working directory. - 3) Resolve any conflicts that arise as a result of the merge. - 4) Commit merged changes to the trunk. - 5) Make changes to resolve (build) difficulties and re-commit. - Repeat as necessary. - 6) Backpropagate changes on the trunk to the development branch. - -This script will assist with steps #2, #4 and #6: - - $0 -merge JS_STABLE_10131998 - $0 -commit - $0 -backpatch - -END -} - -sub log { - local($msg) = @_; - print LOGFILE $msg if $logfile; -} - -# Similar to die built-in -sub die { - local($msg) = @_; - &log($msg); - chomp($msg); - if ($logfile) { - $msg .= "\nSee $logfile for details."; - } - die "$msg\n"; -} - -# Similar to system() built-in -sub system { - local(@args) = @_; - local($cmd) = join(" ", @args); - &log("Executing: $cmd\n"); - - if ($logfile) { - $cmd .= " >> $logfile 2>&1"; - close(LOGFILE); - } - - local($result) = 0xffff & system($cmd); - - if ($logfile) { - open(LOGFILE, ">>$logfile"); - } - - return unless ($result); - $msg = "Died while executing $cmd"; - - if ($result == 0xff00) { - &die("$msg\nWhile executExecution failed due to perl error: $!. "); - } else { - $result >>= 8; - &die("$msg\nExecution failed; exit status: $result. "); - } -} - -chomp($root_dir = `pwd`); - -# Default log file -$logfile = $root_dir . "/log"; - -while ($#ARGV >=0) { - $_ = shift; - - if (/-merge/) { - $do_tag = 1; - $do_checkout = 1; - $do_merge = 1; - $tag = shift; - } elsif (/-commit/ || /-ci/) { - $do_commit = 1; - } elsif (/-backpatch/) { - $do_backpatch = 1; - } elsif (/-log/) { - $logfile = shift; - } elsif (/-tag/) { # Debugging option - $do_tag = 1; - $tag = shift; - } elsif (/-co/) { # Debugging option - $do_checkout = 1; - } else { - print STDERR "Illegal option: $_\n" unless (/-h/); - &help(); - exit(1); - } -} - -die "You must set your CVSROOT environment variable" if !$ENV{"CVSROOT"}; - -if ($logfile) { - open(LOGFILE, ">$logfile") || die "Couldn't open log file \"$logfile\""; - print("Logging to file \"$logfile\".\n"); -} - -$trunk_dir = $root_dir . "/trunk"; - -if ($do_tag) { - if (!$tag) { - &die("Must specify tag on command-line\n"); - } - - print("Tagging tree with tag JS_STABLE_DROP.\n"); - &system("cvs rtag $recurse_flag -F -r $tag JS_STABLE_DROP $merge_dirs"); -} - -if ($do_checkout) { - - # Delete trunk subdir if it already exists - if (-d $trunk_dir) { - &log("Deleting directory $trunk_dir\n"); - rmtree ($trunk_dir, 0, 1); - } - &log("Creating directory $trunk_dir\n"); - mkdir($trunk_dir, 0777) || die "Couldn't create directory $trunk_dir"; - - # Check out on trunk - print("Checking out $merge_dirs.\n"); - chdir $trunk_dir; - &system("cvs co $recurse_flag -A $merge_dirs"); -} - -if ($do_merge) { - chdir $trunk_dir; - print("Merging from JS_STABLE_DROP into trunk\n"); - &system("cvs up -jJS_LAST_STABLE_DROP -jJS_STABLE_DROP"); -} - -if ($do_commit) { - &die("No merged tree found. Wrong directory ?") if (!chdir $trunk_dir); - - ($_,$_,$_,$day,$mon,$year,$_,$_) = localtime(time()); - if ($year < 30) { - $year = "20" . $year; - } else { - $year = "19" . $year; - } - - $mmddyyyy = sprintf("%02d%02d%s", $mon, $day, $year); - - print("Checking in code on trunk"); - &system("cvs ci -m 'Stable drop of JavaScript interpreter code from " . - "$development_branch'"); - - # Tag merged result - &system("cvs tag -F JS_LANDING"); - &system("cvs tag -F JS_LANDING_$mmddyyyy"); - - # Move JS_LAST_STABLE_DROP tag forward - &system("cvs tag -F -rJS_STABLE_DROP JS_LAST_STABLE_DROP"); -} - - diff --git a/deps/mozjs/js/public/HashTable.h b/deps/mozjs/js/public/HashTable.h new file mode 100644 index 00000000000..53a82ff6eb8 --- /dev/null +++ b/deps/mozjs/js/public/HashTable.h @@ -0,0 +1,1357 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * November 13, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Brendan Eich (Original Author) + * Chris Waterson + * L. David Baron , Mozilla Corporation + * Luke Wagner + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jshashtable_h_ +#define jshashtable_h_ + +#include "TemplateLib.h" +#include "Utility.h" + +namespace js { + +class TempAllocPolicy; + +/* Integral types for all hash functions. */ +typedef uint32_t HashNumber; + +/*****************************************************************************/ + +namespace detail { + +template +class HashTable; + +template +class HashTableEntry { + HashNumber keyHash; + + typedef typename tl::StripConst::result NonConstT; + + static const HashNumber sFreeKey = 0; + static const HashNumber sRemovedKey = 1; + static const HashNumber sCollisionBit = 1; + + template friend class HashTable; + + static bool isLiveHash(HashNumber hash) + { + return hash > sRemovedKey; + } + + public: + HashTableEntry() : keyHash(0), t() {} + HashTableEntry(MoveRef rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { } + void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; } + void operator=(MoveRef rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); } + + NonConstT t; + + bool isFree() const { return keyHash == sFreeKey; } + void setFree() { keyHash = sFreeKey; t = T(); } + bool isRemoved() const { return keyHash == sRemovedKey; } + void setRemoved() { keyHash = sRemovedKey; t = T(); } + bool isLive() const { return isLiveHash(keyHash); } + void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; } + + void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } + void setCollision(HashNumber collisionBit) { + JS_ASSERT(isLive()); keyHash |= collisionBit; + } + void unsetCollision() { JS_ASSERT(isLive()); keyHash &= ~sCollisionBit; } + bool hasCollision() const { JS_ASSERT(isLive()); return keyHash & sCollisionBit; } + bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } + HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; } +}; + +/* + * js::detail::HashTable is an implementation detail of the js::HashMap and + * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples, + * skip to the end of the detail namespace. + */ + +/* Reusable implementation of HashMap and HashSet. */ +template +class HashTable : private AllocPolicy +{ + typedef typename tl::StripConst::result NonConstT; + typedef typename HashPolicy::KeyType Key; + typedef typename HashPolicy::Lookup Lookup; + + public: + typedef HashTableEntry Entry; + + /* + * A nullable pointer to a hash table element. A Ptr |p| can be tested + * either explicitly |if (p.found()) p->...| or using boolean conversion + * |if (p) p->...|. Ptr objects must not be used after any mutating hash + * table operations unless |generation()| is tested. + */ + class Ptr + { + friend class HashTable; + typedef void (Ptr::* ConvertibleToBool)(); + void nonNull() {} + + Entry *entry; + + protected: + Ptr(Entry &entry) : entry(&entry) {} + + public: + /* Leaves Ptr uninitialized. */ + Ptr() { +#ifdef DEBUG + entry = (Entry *)0xbad; +#endif + } + + bool found() const { return entry->isLive(); } + operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; } + bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; } + bool operator!=(const Ptr &rhs) const { return !(*this == rhs); } + + T &operator*() const { return entry->t; } + T *operator->() const { return &entry->t; } + }; + + /* A Ptr that can be used to add a key after a failed lookup. */ + class AddPtr : public Ptr + { + friend class HashTable; + HashNumber keyHash; +#ifdef DEBUG + uint64_t mutationCount; + + AddPtr(Entry &entry, HashNumber hn, uint64_t mutationCount) + : Ptr(entry), keyHash(hn), mutationCount(mutationCount) {} +#else + AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {} +#endif + public: + /* Leaves AddPtr uninitialized. */ + AddPtr() {} + }; + + /* + * A collection of hash table entries. The collection is enumerated by + * calling |front()| followed by |popFront()| as long as |!empty()|. As + * with Ptr/AddPtr, Range objects must not be used after any mutating hash + * table operation unless the |generation()| is tested. + */ + class Range + { + protected: + friend class HashTable; + + Range(Entry *c, Entry *e) : cur(c), end(e) { + while (cur < end && !cur->isLive()) + ++cur; + } + + Entry *cur, *end; + + public: + Range() : cur(NULL), end(NULL) {} + + bool empty() const { + return cur == end; + } + + T &front() const { + JS_ASSERT(!empty()); + return cur->t; + } + + void popFront() { + JS_ASSERT(!empty()); + while (++cur < end && !cur->isLive()) + continue; + } + }; + + /* + * A Range whose lifetime delimits a mutating enumeration of a hash table. + * Since rehashing when elements were removed during enumeration would be + * bad, it is postponed until |endEnumeration()| is called. If + * |endEnumeration()| is not called before an Enum's constructor, it will + * be called automatically. Since |endEnumeration()| touches the hash + * table, the user must ensure that the hash table is still alive when this + * happens. + */ + class Enum : public Range + { + friend class HashTable; + + HashTable &table; + bool removed; + + /* Not copyable. */ + Enum(const Enum &); + void operator=(const Enum &); + + public: + template explicit + Enum(Map &map) : Range(map.all()), table(map.impl), removed(false) {} + + /* + * Removes the |front()| element from the table, leaving |front()| + * invalid until the next call to |popFront()|. For example: + * + * HashSet s; + * for (HashSet::Enum e(s); !e.empty(); e.popFront()) + * if (e.front() == 42) + * e.removeFront(); + */ + void removeFront() { + table.remove(*this->cur); + removed = true; + } + + /* Potentially rehashes the table. */ + ~Enum() { + if (removed) + table.checkUnderloaded(); + } + + /* Can be used to end the enumeration before the destructor. */ + void endEnumeration() { + if (removed) { + table.checkUnderloaded(); + removed = false; + } + } + }; + + private: + uint32_t hashShift; /* multiplicative hash shift */ + uint32_t entryCount; /* number of entries in table */ + uint32_t gen; /* entry storage generation number */ + uint32_t removedCount; /* removed entry sentinels in table */ + Entry *table; /* entry storage */ + + void setTableSizeLog2(unsigned sizeLog2) { + hashShift = sHashBits - sizeLog2; + } + +#ifdef DEBUG + mutable struct Stats { + uint32_t searches; /* total number of table searches */ + uint32_t steps; /* hash chain links traversed */ + uint32_t hits; /* searches that found key */ + uint32_t misses; /* searches that didn't find key */ + uint32_t addOverRemoved; /* adds that recycled a removed entry */ + uint32_t removes; /* calls to remove */ + uint32_t removeFrees; /* calls to remove that freed the entry */ + uint32_t grows; /* table expansions */ + uint32_t shrinks; /* table contractions */ + uint32_t compresses; /* table compressions */ + } stats; +# define METER(x) x +#else +# define METER(x) +#endif + +#ifdef DEBUG + friend class js::ReentrancyGuard; + mutable bool entered; + uint64_t mutationCount; +#endif + + /* The default initial capacity is 16, but you can ask for as small as 4. */ + static const unsigned sMinSizeLog2 = 2; + static const unsigned sMinSize = 1 << sMinSizeLog2; + static const unsigned sDefaultInitSizeLog2 = 4; + public: + static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2; + private: + static const unsigned sMaxInit = JS_BIT(23); + static const unsigned sMaxCapacity = JS_BIT(24); + static const unsigned sHashBits = tl::BitSize::result; + static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */ + static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */ + static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */ + static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */ + static const HashNumber sFreeKey = Entry::sFreeKey; + static const HashNumber sRemovedKey = Entry::sRemovedKey; + static const HashNumber sCollisionBit = Entry::sCollisionBit; + + static void staticAsserts() + { + /* Rely on compiler "constant overflow warnings". */ + JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity); + JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX); + JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX); + } + + static bool isLiveHash(HashNumber hash) + { + return Entry::isLiveHash(hash); + } + + static HashNumber prepareHash(const Lookup& l) + { + HashNumber keyHash = HashPolicy::hash(l); + + /* Improve keyHash distribution. */ + keyHash *= sGoldenRatio; + + /* Avoid reserved hash codes. */ + if (!isLiveHash(keyHash)) + keyHash -= (sRemovedKey + 1); + return keyHash & ~sCollisionBit; + } + + static Entry *createTable(AllocPolicy &alloc, uint32_t capacity) + { + Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); + if (!newTable) + return NULL; + for (Entry *e = newTable, *end = e + capacity; e < end; ++e) + new(e) Entry(); + return newTable; + } + + static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) + { + for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) + e->~Entry(); + alloc.free_(oldTable); + } + + public: + HashTable(AllocPolicy ap) + : AllocPolicy(ap), + hashShift(sHashBits), + entryCount(0), + gen(0), + removedCount(0), + table(NULL) +#ifdef DEBUG + , entered(false), + mutationCount(0) +#endif + {} + + bool init(uint32_t length) + { + /* Make sure that init isn't called twice. */ + JS_ASSERT(table == NULL); + + /* + * Correct for sMaxAlphaFrac such that the table will not resize + * when adding 'length' entries. + */ + if (length > sMaxInit) { + this->reportAllocOverflow(); + return false; + } + uint32_t capacity = (length * sInvMaxAlpha) >> 7; + + if (capacity < sMinSize) + capacity = sMinSize; + + /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */ + uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2; + while (roundUp < capacity) { + roundUp <<= 1; + ++roundUpLog2; + } + + capacity = roundUp; + JS_ASSERT(capacity <= sMaxCapacity); + + table = createTable(*this, capacity); + if (!table) + return false; + + setTableSizeLog2(roundUpLog2); + METER(memset(&stats, 0, sizeof(stats))); + return true; + } + + bool initialized() const + { + return !!table; + } + + ~HashTable() + { + if (table) + destroyTable(*this, table, capacity()); + } + + private: + static HashNumber hash1(HashNumber hash0, uint32_t shift) { + return hash0 >> shift; + } + + static HashNumber hash2(HashNumber hash0, uint32_t log2, uint32_t shift) { + return ((hash0 << log2) >> shift) | 1; + } + + bool overloaded() { + return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8); + } + + bool underloaded() { + uint32_t tableCapacity = capacity(); + return tableCapacity > sMinSize && + entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8); + } + + static bool match(Entry &e, const Lookup &l) { + return HashPolicy::match(HashPolicy::getKey(e.t), l); + } + + Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const + { + JS_ASSERT(isLiveHash(keyHash)); + JS_ASSERT(!(keyHash & sCollisionBit)); + JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit); + JS_ASSERT(table); + METER(stats.searches++); + + /* Compute the primary hash address. */ + HashNumber h1 = hash1(keyHash, hashShift); + Entry *entry = &table[h1]; + + /* Miss: return space for a new entry. */ + if (entry->isFree()) { + METER(stats.misses++); + return *entry; + } + + /* Hit: return entry. */ + if (entry->matchHash(keyHash) && match(*entry, l)) { + METER(stats.hits++); + return *entry; + } + + /* Collision: double hash. */ + unsigned sizeLog2 = sHashBits - hashShift; + HashNumber h2 = hash2(keyHash, sizeLog2, hashShift); + HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1; + + /* Save the first removed entry pointer so we can recycle later. */ + Entry *firstRemoved = NULL; + + while(true) { + if (JS_UNLIKELY(entry->isRemoved())) { + if (!firstRemoved) + firstRemoved = entry; + } else { + entry->setCollision(collisionBit); + } + + METER(stats.steps++); + h1 -= h2; + h1 &= sizeMask; + + entry = &table[h1]; + if (entry->isFree()) { + METER(stats.misses++); + return firstRemoved ? *firstRemoved : *entry; + } + + if (entry->matchHash(keyHash) && match(*entry, l)) { + METER(stats.hits++); + return *entry; + } + } + } + + /* + * This is a copy of lookup hardcoded to the assumptions: + * 1. the lookup is a lookupForAdd + * 2. the key, whose |keyHash| has been passed is not in the table, + * 3. no entries have been removed from the table. + * This specialized search avoids the need for recovering lookup values + * from entries, which allows more flexible Lookup/Key types. + */ + Entry &findFreeEntry(HashNumber keyHash) + { + METER(stats.searches++); + JS_ASSERT(!(keyHash & sCollisionBit)); + + /* N.B. the |keyHash| has already been distributed. */ + + /* Compute the primary hash address. */ + HashNumber h1 = hash1(keyHash, hashShift); + Entry *entry = &table[h1]; + + /* Miss: return space for a new entry. */ + if (entry->isFree()) { + METER(stats.misses++); + return *entry; + } + + /* Collision: double hash. */ + unsigned sizeLog2 = sHashBits - hashShift; + HashNumber h2 = hash2(keyHash, sizeLog2, hashShift); + HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1; + + while(true) { + JS_ASSERT(!entry->isRemoved()); + entry->setCollision(); + + METER(stats.steps++); + h1 -= h2; + h1 &= sizeMask; + + entry = &table[h1]; + if (entry->isFree()) { + METER(stats.misses++); + return *entry; + } + } + } + + bool changeTableSize(int deltaLog2) + { + /* Look, but don't touch, until we succeed in getting new entry store. */ + Entry *oldTable = table; + uint32_t oldCap = capacity(); + uint32_t newLog2 = sHashBits - hashShift + deltaLog2; + uint32_t newCapacity = JS_BIT(newLog2); + if (newCapacity > sMaxCapacity) { + this->reportAllocOverflow(); + return false; + } + + Entry *newTable = createTable(*this, newCapacity); + if (!newTable) + return false; + + /* We can't fail from here on, so update table parameters. */ + setTableSizeLog2(newLog2); + removedCount = 0; + gen++; + table = newTable; + + /* Copy only live entries, leaving removed ones behind. */ + for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { + if (src->isLive()) { + src->unsetCollision(); + findFreeEntry(src->getKeyHash()) = Move(*src); + } + } + + destroyTable(*this, oldTable, oldCap); + return true; + } + + void remove(Entry &e) + { + METER(stats.removes++); + if (e.hasCollision()) { + e.setRemoved(); + removedCount++; + } else { + METER(stats.removeFrees++); + e.setFree(); + } + entryCount--; +#ifdef DEBUG + mutationCount++; +#endif + } + + void checkUnderloaded() + { + if (underloaded()) { + METER(stats.shrinks++); + (void) changeTableSize(-1); + } + } + + public: + void clear() + { + if (tl::IsPodType::result) { + memset(table, 0, sizeof(*table) * capacity()); + } else { + uint32_t tableCapacity = capacity(); + for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) + *e = Move(Entry()); + } + removedCount = 0; + entryCount = 0; +#ifdef DEBUG + mutationCount++; +#endif + } + + void finish() + { + JS_ASSERT(!entered); + + if (!table) + return; + + destroyTable(*this, table, capacity()); + table = NULL; + gen++; + entryCount = 0; + removedCount = 0; +#ifdef DEBUG + mutationCount++; +#endif + } + + Range all() const { + return Range(table, table + capacity()); + } + + bool empty() const { + return !entryCount; + } + + uint32_t count() const { + return entryCount; + } + + uint32_t capacity() const { + return JS_BIT(sHashBits - hashShift); + } + + uint32_t generation() const { + return gen; + } + + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(table); + } + + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); + } + + Ptr lookup(const Lookup &l) const { + ReentrancyGuard g(*this); + HashNumber keyHash = prepareHash(l); + return Ptr(lookup(l, keyHash, 0)); + } + + AddPtr lookupForAdd(const Lookup &l) const { + ReentrancyGuard g(*this); + HashNumber keyHash = prepareHash(l); + Entry &entry = lookup(l, keyHash, sCollisionBit); +#ifdef DEBUG + return AddPtr(entry, keyHash, mutationCount); +#else + return AddPtr(entry, keyHash); +#endif + } + + bool add(AddPtr &p) + { + ReentrancyGuard g(*this); + JS_ASSERT(mutationCount == p.mutationCount); + JS_ASSERT(table); + JS_ASSERT(!p.found()); + JS_ASSERT(!(p.keyHash & sCollisionBit)); + + /* + * Changing an entry from removed to live does not affect whether we + * are overloaded and can be handled separately. + */ + if (p.entry->isRemoved()) { + METER(stats.addOverRemoved++); + removedCount--; + p.keyHash |= sCollisionBit; + } else { + /* If alpha is >= .75, grow or compress the table. */ + if (overloaded()) { + /* Compress if a quarter or more of all entries are removed. */ + int deltaLog2; + if (removedCount >= (capacity() >> 2)) { + METER(stats.compresses++); + deltaLog2 = 0; + } else { + METER(stats.grows++); + deltaLog2 = 1; + } + + if (!changeTableSize(deltaLog2)) + return false; + + /* Preserve the validity of |p.entry|. */ + p.entry = &findFreeEntry(p.keyHash); + } + } + + p.entry->setLive(p.keyHash); + entryCount++; +#ifdef DEBUG + mutationCount++; +#endif + return true; + } + + /* + * There is an important contract between the caller and callee for this + * function: if add() returns true, the caller must assign the T value + * which produced p before using the hashtable again. + */ + bool add(AddPtr &p, T** pentry) + { + if (!add(p)) + return false; + *pentry = &p.entry->t; + return true; + } + + bool add(AddPtr &p, const T &t) + { + if (!add(p)) + return false; + p.entry->t = t; + return true; + } + + bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t) + { +#ifdef DEBUG + p.mutationCount = mutationCount; +#endif + { + ReentrancyGuard g(*this); + p.entry = &lookup(l, p.keyHash, sCollisionBit); + } + return p.found() || add(p, t); + } + + void remove(Ptr p) + { + ReentrancyGuard g(*this); + JS_ASSERT(p.found()); + remove(*p.entry); + checkUnderloaded(); + } +#undef METER +}; + +} /* namespace detail */ + +/*****************************************************************************/ + +/* + * Hash policy + * + * A hash policy P for a hash table with key-type Key must provide: + * - a type |P::Lookup| to use to lookup table entries; + * - a static member function |P::hash| with signature + * + * static js::HashNumber hash(Lookup) + * + * to use to hash the lookup type; and + * - a static member function |P::match| with signature + * + * static bool match(Key, Lookup) + * + * to use to test equality of key and lookup values. + * + * Normally, Lookup = Key. In general, though, different values and types of + * values can be used to lookup and store. If a Lookup value |l| is != to the + * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: + * + * js::HashSet::AddPtr p = h.lookup(l); + * if (!p) { + * assert(P::match(k, l)); // must hold + * h.add(p, k); + * } + */ + +/* Default hashing policies. */ +template +struct DefaultHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + /* Hash if can implicitly cast to hash number type. */ + return l; + } + static bool match(const Key &k, const Lookup &l) { + /* Use builtin or overloaded operator==. */ + return k == l; + } +}; + +/* + * Pointer hashing policy that strips the lowest zeroBits when calculating the + * hash to improve key distribution. + */ +template +struct PointerHasher +{ + typedef Key Lookup; + static HashNumber hash(const Lookup &l) { + size_t word = reinterpret_cast(l) >> zeroBits; + JS_STATIC_ASSERT(sizeof(HashNumber) == 4); +#if JS_BYTES_PER_WORD == 4 + return HashNumber(word); +#else + JS_STATIC_ASSERT(sizeof word == 8); + return HashNumber((word >> 32) ^ word); +#endif + } + static bool match(const Key &k, const Lookup &l) { + return k == l; + } +}; + +template +struct TaggedPointerHasher +{ + typedef Key Lookup; + + static HashNumber hash(const Lookup &l) { + return PointerHasher::hash(l); + } + + static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1; + + static bool match(const Key &k, const Lookup &l) { + return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l); + } +}; + +/* + * Specialized hashing policy for pointer types. It assumes that the type is + * at least word-aligned. For types with smaller size use PointerHasher. + */ +template +struct DefaultHasher: PointerHasher::result> { }; + +/* Looking for a hasher for jsid? Try the DefaultHasher in jsatom.h. */ + +template +class HashMapEntry +{ + template friend class detail::HashTable; + template friend class detail::HashTableEntry; + void operator=(const HashMapEntry &rhs) { + const_cast(key) = rhs.key; + value = rhs.value; + } + + public: + HashMapEntry() : key(), value() {} + + template + HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {} + + HashMapEntry(MoveRef rhs) + : key(Move(rhs->key)), value(Move(rhs->value)) { } + void operator=(MoveRef rhs) { + const_cast(key) = Move(rhs->key); + value = Move(rhs->value); + } + + const Key key; + Value value; +}; + +namespace tl { + +template +struct IsPodType > { + static const bool result = IsPodType::result; +}; + +template +struct IsPodType > +{ + static const bool result = IsPodType::result && IsPodType::result; +}; + +} /* namespace tl */ + +/* + * JS-friendly, STL-like container providing a hash-based map from keys to + * values. In particular, HashMap calls constructors and destructors of all + * objects added so non-PODs may be used safely. + * + * Key/Value requirements: + * - default constructible, copyable, destructible, assignable + * HashPolicy requirements: + * - see "Hash policy" above (default js::DefaultHasher) + * AllocPolicy: + * - see "Allocation policies" in jsalloc.h + * + * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members + * called by HashMap must not call back into the same HashMap object. + * N.B: Due to the lack of exception handling, the user must call |init()|. + */ +template , + class AllocPolicy = TempAllocPolicy> +class HashMap +{ + public: + typedef typename HashPolicy::Lookup Lookup; + + typedef HashMapEntry Entry; + + private: + /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */ + struct MapHashPolicy : HashPolicy + { + typedef Key KeyType; + static const Key &getKey(Entry &e) { return e.key; } + }; + typedef detail::HashTable Impl; + + friend class Impl::Enum; + + /* Not implicitly copyable (expensive). May add explicit |clone| later. */ + HashMap(const HashMap &); + HashMap &operator=(const HashMap &); + + Impl impl; + + public: + /* + * HashMap construction is fallible (due to OOM); thus the user must call + * init after constructing a HashMap and check the return value. + */ + HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = Impl::sDefaultInitSize) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + /* + * Return whether the given lookup value is present in the map. E.g.: + * + * typedef HashMap HM; + * HM h; + * if (HM::Ptr p = h.lookup(3)) { + * const HM::Entry &e = *p; // p acts like a pointer to Entry + * assert(p->key == 3); // Entry contains the key + * char val = p->value; // and value + * } + * + * Also see the definition of Ptr in HashTable above (with T = Entry). + */ + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + /* Assuming |p.found()|, remove |*p|. */ + void remove(Ptr p) { impl.remove(p); } + + /* + * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using + * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: + * + * typedef HashMap HM; + * HM h; + * HM::AddPtr p = h.lookupForAdd(3); + * if (!p) { + * if (!h.add(p, 3, 'a')) + * return false; + * } + * const HM::Entry &e = *p; // p acts like a pointer to Entry + * assert(p->key == 3); // Entry contains the key + * char val = p->value; // and value + * + * Also see the definition of AddPtr in HashTable above (with T = Entry). + * + * N.B. The caller must ensure that no mutating hash table operations + * occur between a pair of |lookupForAdd| and |add| calls. To avoid + * looking up the key a second time, the caller may use the more efficient + * relookupOrAdd method. This method reuses part of the hashing computation + * to more efficiently insert the key if it has not been added. For + * example, a mutation-handling version of the previous example: + * + * HM::AddPtr p = h.lookupForAdd(3); + * if (!p) { + * call_that_may_mutate_h(); + * if (!h.relookupOrAdd(p, 3, 'a')) + * return false; + * } + * const HM::Entry &e = *p; + * assert(p->key == 3); + * char val = p->value; + */ + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { + return impl.lookupForAdd(l); + } + + template + bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) { + Entry *pentry; + if (!impl.add(p, &pentry)) + return false; + const_cast(pentry->key) = k; + pentry->value = v; + return true; + } + + bool add(AddPtr &p, const Key &k, MoveRef v) { + Entry *pentry; + if (!impl.add(p, &pentry)) + return false; + const_cast(pentry->key) = k; + pentry->value = v; + return true; + } + + bool add(AddPtr &p, const Key &k) { + Entry *pentry; + if (!impl.add(p, &pentry)) + return false; + const_cast(pentry->key) = k; + return true; + } + + template + bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) { + return impl.relookupOrAdd(p, k, Entry(k, v)); + } + + /* + * |all()| returns a Range containing |count()| elements. E.g.: + * + * typedef HashMap HM; + * HM h; + * for (HM::Range r = h.all(); !r.empty(); r.popFront()) + * char c = r.front().value; + * + * Also see the definition of Range in HashTable above (with T = Entry). + */ + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + size_t count() const { return impl.count(); } + size_t capacity() const { return impl.capacity(); } + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + /* + * Don't just call |impl.sizeOfExcludingThis()| because there's no + * guarantee that |impl| is the first field in HashMap. + */ + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + /* + * Typedef for the enumeration class. An Enum may be used to examine and + * remove table entries: + * + * typedef HashMap HM; + * HM s; + * for (HM::Enum e(s); !e.empty(); e.popFront()) + * if (e.front().value == 'l') + * e.removeFront(); + * + * Table resize may occur in Enum's destructor. Also see the definition of + * Enum in HashTable above (with T = Entry). + */ + typedef typename Impl::Enum Enum; + + /* + * Remove all entries. This does not shrink the table. For that consider + * using the finish() method. + */ + void clear() { impl.clear(); } + + /* + * Remove all the entries and release all internal buffers. The map must + * be initialized again before any use. + */ + void finish() { impl.finish(); } + + /* Does the table contain any entries? */ + bool empty() const { return impl.empty(); } + + /* + * If |generation()| is the same before and after a HashMap operation, + * pointers into the table remain valid. + */ + unsigned generation() const { return impl.generation(); } + + /* Shorthand operations: */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + /* Overwrite existing value with v. Return NULL on oom. */ + template + Entry *put(const KeyInput &k, const ValueInput &v) { + AddPtr p = lookupForAdd(k); + if (p) { + p->value = v; + return &*p; + } + return add(p, k, v) ? &*p : NULL; + } + + /* Like put, but assert that the given key is not already present. */ + bool putNew(const Key &k, const Value &v) { + AddPtr p = lookupForAdd(k); + JS_ASSERT(!p); + return add(p, k, v); + } + + /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */ + Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { + AddPtr p = lookupForAdd(k); + if (p) + return p; + (void)add(p, k, defaultValue); /* p is left false-y on oom. */ + return p; + } + + /* Remove if present. */ + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } +}; + +/* + * JS-friendly, STL-like container providing a hash-based set of values. In + * particular, HashSet calls constructors and destructors of all objects added + * so non-PODs may be used safely. + * + * T requirements: + * - default constructible, copyable, destructible, assignable + * HashPolicy requirements: + * - see "Hash policy" above (default js::DefaultHasher) + * AllocPolicy: + * - see "Allocation policies" in jsalloc.h + * + * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by + * HashSet must not call back into the same HashSet object. + * N.B: Due to the lack of exception handling, the user must call |init()|. + */ +template , class AllocPolicy = TempAllocPolicy> +class HashSet +{ + typedef typename HashPolicy::Lookup Lookup; + + /* Implement HashSet in terms of HashTable. */ + struct SetOps : HashPolicy { + typedef T KeyType; + static const KeyType &getKey(const T &t) { return t; } + }; + typedef detail::HashTable Impl; + + friend class Impl::Enum; + + /* Not implicitly copyable (expensive). May add explicit |clone| later. */ + HashSet(const HashSet &); + HashSet &operator=(const HashSet &); + + Impl impl; + + public: + /* + * HashSet construction is fallible (due to OOM); thus the user must call + * init after constructing a HashSet and check the return value. + */ + HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} + bool init(uint32_t len = Impl::sDefaultInitSize) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } + + /* + * Return whether the given lookup value is present in the map. E.g.: + * + * typedef HashSet HS; + * HS h; + * if (HS::Ptr p = h.lookup(3)) { + * assert(*p == 3); // p acts like a pointer to int + * } + * + * Also see the definition of Ptr in HashTable above. + */ + typedef typename Impl::Ptr Ptr; + Ptr lookup(const Lookup &l) const { return impl.lookup(l); } + + /* Assuming |p.found()|, remove |*p|. */ + void remove(Ptr p) { impl.remove(p); } + + /* + * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient + * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using + * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: + * + * typedef HashSet HS; + * HS h; + * HS::AddPtr p = h.lookupForAdd(3); + * if (!p) { + * if (!h.add(p, 3)) + * return false; + * } + * assert(*p == 3); // p acts like a pointer to int + * + * Also see the definition of AddPtr in HashTable above. + * + * N.B. The caller must ensure that no mutating hash table operations + * occur between a pair of |lookupForAdd| and |add| calls. To avoid + * looking up the key a second time, the caller may use the more efficient + * relookupOrAdd method. This method reuses part of the hashing computation + * to more efficiently insert the key if it has not been added. For + * example, a mutation-handling version of the previous example: + * + * HS::AddPtr p = h.lookupForAdd(3); + * if (!p) { + * call_that_may_mutate_h(); + * if (!h.relookupOrAdd(p, 3, 3)) + * return false; + * } + * assert(*p == 3); + * + * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the + * entry t, where the caller ensures match(l,t). + */ + typedef typename Impl::AddPtr AddPtr; + AddPtr lookupForAdd(const Lookup &l) const { + return impl.lookupForAdd(l); + } + + bool add(AddPtr &p, const T &t) { + return impl.add(p, t); + } + + bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { + return impl.relookupOrAdd(p, l, t); + } + + /* + * |all()| returns a Range containing |count()| elements: + * + * typedef HashSet HS; + * HS h; + * for (HS::Range r = h.all(); !r.empty(); r.popFront()) + * int i = r.front(); + * + * Also see the definition of Range in HashTable above. + */ + typedef typename Impl::Range Range; + Range all() const { return impl.all(); } + size_t count() const { return impl.count(); } + size_t capacity() const { return impl.capacity(); } + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return impl.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + /* + * Don't just call |impl.sizeOfExcludingThis()| because there's no + * guarantee that |impl| is the first field in HashSet. + */ + return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf); + } + + /* + * Typedef for the enumeration class. An Enum may be used to examine and + * remove table entries: + * + * typedef HashSet HS; + * HS s; + * for (HS::Enum e(s); !e.empty(); e.popFront()) + * if (e.front() == 42) + * e.removeFront(); + * + * Table resize may occur in Enum's destructor. Also see the definition of + * Enum in HashTable above. + */ + typedef typename Impl::Enum Enum; + + /* + * Remove all entries. This does not shrink the table. For that consider + * using the finish() method. + */ + void clear() { impl.clear(); } + + /* + * Remove all the entries and release all internal buffers. The set must + * be initialized again before any use. + */ + void finish() { impl.finish(); } + + /* Does the table contain any entries? */ + bool empty() const { return impl.empty(); } + + /* + * If |generation()| is the same before and after a HashSet operation, + * pointers into the table remain valid. + */ + unsigned generation() const { return impl.generation(); } + + /* Shorthand operations: */ + + bool has(const Lookup &l) const { + return impl.lookup(l) != NULL; + } + + /* Overwrite existing value with v. Return NULL on oom. */ + const T *put(const T &t) { + AddPtr p = lookupForAdd(t); + return p ? &*p : (add(p, t) ? &*p : NULL); + } + + /* Like put, but assert that the given key is not already present. */ + bool putNew(const T &t) { + AddPtr p = lookupForAdd(t); + JS_ASSERT(!p); + return add(p, t); + } + + bool putNew(const Lookup &l, const T &t) { + AddPtr p = lookupForAdd(l); + JS_ASSERT(!p); + return add(p, t); + } + + void remove(const Lookup &l) { + if (Ptr p = lookup(l)) + remove(p); + } +}; + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/public/LegacyIntTypes.h b/deps/mozjs/js/public/LegacyIntTypes.h new file mode 100644 index 00000000000..2e7d7aea486 --- /dev/null +++ b/deps/mozjs/js/public/LegacyIntTypes.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This section typedefs the old 'native' types to the new types. + * These redefinitions are provided solely to allow JSAPI users to more easily + * transition to types. They are not to be used in the JSAPI, and + * new JSAPI user code should not use them. This mapping file may eventually + * be removed from SpiderMonkey, so don't depend on it in the long run. + */ + +/* + * BEWARE: Comity with other implementers of these types is not guaranteed. + * Indeed, if you use this header and third-party code defining these + * types, *expect* to encounter either compile errors or link errors, + * depending how these types are used and on the order of inclusion. + * It is safest to use only the JSAPI -style types, + * customizing those types using MOZ_CUSTOM_STDINT_H if necessary. + */ +#ifndef PROTYPES_H +#define PROTYPES_H + +#include "mozilla/StdInt.h" + +#include "js-config.h" + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +/* + * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very + * common header file) defines the types int8, int16, int32, and int64. + * So we don't define these four types here to avoid conflicts in case + * the code also includes sys/types.h. + */ +#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) +#include +#else +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +#endif /* AIX && HAVE_SYS_INTTYPES_H */ + +typedef uint8_t JSUint8; +typedef uint16_t JSUint16; +typedef uint32_t JSUint32; +typedef uint64_t JSUint64; + +typedef int8_t JSInt8; +typedef int16_t JSInt16; +typedef int32_t JSInt32; +typedef int64_t JSInt64; + +#endif /* !defined(PROTYPES_H) */ diff --git a/deps/mozjs/js/public/MemoryMetrics.h b/deps/mozjs/js/public/MemoryMetrics.h new file mode 100644 index 00000000000..b83a487adb1 --- /dev/null +++ b/deps/mozjs/js/public/MemoryMetrics.h @@ -0,0 +1,207 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is about:memory glue. + * + * The Initial Developer of the Original Code is + * Ms2ger . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_MemoryMetrics_h +#define js_MemoryMetrics_h + +/* + * These declarations are not within jsapi.h because they are highly likely + * to change in the future. Depend on them at your own risk. + */ + +#include + +#include "jsalloc.h" +#include "jspubtd.h" + +#include "js/Utility.h" +#include "js/Vector.h" + +namespace JS { + +/* Data for tracking analysis/inference memory usage. */ +struct TypeInferenceSizes +{ + size_t scripts; + size_t objects; + size_t tables; + size_t temporary; +}; + +typedef void* (* GetNameCallback)(JSContext *cx, JSCompartment *c); +typedef void (* DestroyNameCallback)(void *string); + +struct CompartmentStats +{ + CompartmentStats() + { + memset(this, 0, sizeof(*this)); + } + + void init(void *name_, DestroyNameCallback destroyName) + { + name = name_; + destroyNameCb = destroyName; + } + + ~CompartmentStats() + { + destroyNameCb(name); + } + + // Pointer to an nsCString, which we can't use here. + void *name; + DestroyNameCallback destroyNameCb; + + size_t gcHeapArenaHeaders; + size_t gcHeapArenaPadding; + size_t gcHeapArenaUnused; + + size_t gcHeapObjectsNonFunction; + size_t gcHeapObjectsFunction; + size_t gcHeapStrings; + size_t gcHeapShapesTree; + size_t gcHeapShapesDict; + size_t gcHeapShapesBase; + size_t gcHeapScripts; + size_t gcHeapTypeObjects; + size_t gcHeapXML; + + size_t objectSlots; + size_t objectElements; + size_t objectMisc; + size_t stringChars; + size_t shapesExtraTreeTables; + size_t shapesExtraDictTables; + size_t shapesExtraTreeShapeKids; + size_t shapesCompartmentTables; + size_t scriptData; + +#ifdef JS_METHODJIT + size_t mjitCode; + size_t mjitData; +#endif + TypeInferenceSizes typeInferenceSizes; +}; + +struct RuntimeStats +{ + RuntimeStats(JSMallocSizeOfFun mallocSizeOf, GetNameCallback getNameCb, + DestroyNameCallback destroyNameCb) + : runtimeObject(0) + , runtimeAtomsTable(0) + , runtimeContexts(0) + , runtimeNormal(0) + , runtimeTemporary(0) + , runtimeRegexpCode(0) + , runtimeStackCommitted(0) + , gcHeapChunkTotal(0) + , gcHeapChunkCleanUnused(0) + , gcHeapChunkDirtyUnused(0) + , gcHeapChunkCleanDecommitted(0) + , gcHeapChunkDirtyDecommitted(0) + , gcHeapArenaUnused(0) + , gcHeapChunkAdmin(0) + , gcHeapUnusedPercentage(0) + , totalObjects(0) + , totalShapes(0) + , totalScripts(0) + , totalStrings(0) +#ifdef JS_METHODJIT + , totalMjit(0) +#endif + , totalTypeInference(0) + , totalAnalysisTemp(0) + , compartmentStatsVector() + , currCompartmentStats(NULL) + , mallocSizeOf(mallocSizeOf) + , getNameCb(getNameCb) + , destroyNameCb(destroyNameCb) + {} + + size_t runtimeObject; + size_t runtimeAtomsTable; + size_t runtimeContexts; + size_t runtimeNormal; + size_t runtimeTemporary; + size_t runtimeRegexpCode; + size_t runtimeStackCommitted; + size_t gcHeapChunkTotal; + size_t gcHeapChunkCleanUnused; + size_t gcHeapChunkDirtyUnused; + size_t gcHeapChunkCleanDecommitted; + size_t gcHeapChunkDirtyDecommitted; + size_t gcHeapArenaUnused; + size_t gcHeapChunkAdmin; + size_t gcHeapUnusedPercentage; + size_t totalObjects; + size_t totalShapes; + size_t totalScripts; + size_t totalStrings; +#ifdef JS_METHODJIT + size_t totalMjit; +#endif + size_t totalTypeInference; + size_t totalAnalysisTemp; + + js::Vector compartmentStatsVector; + CompartmentStats *currCompartmentStats; + + JSMallocSizeOfFun mallocSizeOf; + GetNameCallback getNameCb; + DestroyNameCallback destroyNameCb; +}; + +#ifdef JS_THREADSAFE + +extern JS_PUBLIC_API(bool) +CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats); + +extern JS_PUBLIC_API(bool) +GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount, + JSMallocSizeOfFun mallocSizeOf); + +#endif /* JS_THREADSAFE */ + +extern JS_PUBLIC_API(size_t) +SystemCompartmentCount(const JSRuntime *rt); + +extern JS_PUBLIC_API(size_t) +UserCompartmentCount(const JSRuntime *rt); + +} // namespace JS + +#endif // js_MemoryMetrics_h diff --git a/deps/mozjs/js/public/TemplateLib.h b/deps/mozjs/js/public/TemplateLib.h new file mode 100644 index 00000000000..9cdef99ba7a --- /dev/null +++ b/deps/mozjs/js/public/TemplateLib.h @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Luke Wagner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_template_lib_h__ +#define js_template_lib_h__ + +#include "jstypes.h" + +/* + * Library of reusable template meta-functions (that is, functions on types and + * compile-time values). Meta-functions are placed inside the 'tl' namespace to + * avoid conflict with non-meta functions that logically have the same name + * (e.g., js::tl::Min vs. js::Min). + */ + +namespace js { +namespace tl { + +/* Compute min/max/clamp. */ +template struct Min { + static const size_t result = i < j ? i : j; +}; +template struct Max { + static const size_t result = i > j ? i : j; +}; +template struct Clamp { + static const size_t result = i < min ? min : (i > max ? max : i); +}; + +/* Compute x^y. */ +template struct Pow { + static const size_t result = x * Pow::result; +}; +template struct Pow { + static const size_t result = 1; +}; + +/* Compute floor(log2(i)). */ +template struct FloorLog2 { + static const size_t result = 1 + FloorLog2::result; +}; +template <> struct FloorLog2<0> { /* Error */ }; +template <> struct FloorLog2<1> { static const size_t result = 0; }; + +/* Compute ceiling(log2(i)). */ +template struct CeilingLog2 { + static const size_t result = FloorLog2<2 * i - 1>::result; +}; + +/* Round up to the nearest power of 2. */ +template struct RoundUpPow2 { + static const size_t result = size_t(1) << CeilingLog2::result; +}; +template <> struct RoundUpPow2<0> { + static const size_t result = 1; +}; + +/* Compute the number of bits in the given unsigned type. */ +template struct BitSize { + static const size_t result = sizeof(T) * JS_BITS_PER_BYTE; +}; + +/* Allow Assertions by only including the 'result' typedef if 'true'. */ +template struct StaticAssert {}; +template <> struct StaticAssert { typedef int result; }; + +/* Boolean test for whether two types are the same. */ +template struct IsSameType { + static const bool result = false; +}; +template struct IsSameType { + static const bool result = true; +}; + +/* + * Produce an N-bit mask, where N <= BitSize::result. Handle the + * language-undefined edge case when N = BitSize::result. + */ +template struct NBitMask { + typedef typename StaticAssert::result>::result _; + static const size_t result = (size_t(1) << N) - 1; +}; +template <> struct NBitMask::result> { + static const size_t result = size_t(-1); +}; + +/* + * For the unsigned integral type size_t, compute a mask M for N such that + * for all X, !(X & M) implies X * N will not overflow (w.r.t size_t) + */ +template struct MulOverflowMask { + static const size_t result = + ~NBitMask::result - CeilingLog2::result>::result; +}; +template <> struct MulOverflowMask<0> { /* Error */ }; +template <> struct MulOverflowMask<1> { static const size_t result = 0; }; + +/* + * Generate a mask for T such that if (X & sUnsafeRangeSizeMask), an X-sized + * array of T's is big enough to cause a ptrdiff_t overflow when subtracting + * a pointer to the end of the array from the beginning. + */ +template struct UnsafeRangeSizeMask { + /* + * The '2' factor means the top bit is clear, sizeof(T) converts from + * units of elements to bytes. + */ + static const size_t result = MulOverflowMask<2 * sizeof(T)>::result; +}; + +/* Return T stripped of any const-ness. */ +template struct StripConst { typedef T result; }; +template struct StripConst { typedef T result; }; + +/* + * Traits class for identifying POD types. Until C++0x, there is no automatic + * way to detect PODs, so for the moment it is done manually. + */ +template struct IsPodType { static const bool result = false; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template struct IsPodType { static const bool result = true; }; + +template struct If { static const T result = v1; }; +template struct If { static const T result = v2; }; + +} /* namespace tl */ +} /* namespace js */ + +#endif /* js_template_lib_h__ */ diff --git a/deps/mozjs/js/public/Utility.h b/deps/mozjs/js/public/Utility.h new file mode 100644 index 00000000000..e5f72ce7c8c --- /dev/null +++ b/deps/mozjs/js/public/Utility.h @@ -0,0 +1,912 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_utility_h__ +#define js_utility_h__ + +#include "mozilla/Assertions.h" + +#include +#include + +#include "jstypes.h" + +#ifdef __cplusplus + +/* The public JS engine namespace. */ +namespace JS {} + +/* The mozilla-shared reusable template/utility namespace. */ +namespace mozilla {} + +/* The private JS engine namespace. */ +namespace js { + +/* The private namespace is a superset of the public/shared namespaces. */ +using namespace JS; +using namespace mozilla; + +} /* namespace js */ +#endif /* __cplusplus */ + +JS_BEGIN_EXTERN_C + +/* + * Pattern used to overwrite freed memory. If you are accessing an object with + * this pattern, you probably have a dangling pointer. + */ +#define JS_FREE_PATTERN 0xDA + +#define JS_ASSERT(expr) MOZ_ASSERT(expr) +#define JS_ASSERT_IF(cond, expr) MOZ_ASSERT_IF(cond, expr) +#define JS_NOT_REACHED(reason) MOZ_NOT_REACHED(reason) +#define JS_ALWAYS_TRUE(expr) MOZ_ALWAYS_TRUE(expr) +#define JS_ALWAYS_FALSE(expr) MOZ_ALWAYS_FALSE(expr) + +#ifdef DEBUG +# ifdef JS_THREADSAFE +# define JS_THREADSAFE_ASSERT(expr) JS_ASSERT(expr) +# else +# define JS_THREADSAFE_ASSERT(expr) ((void) 0) +# endif +#else +# define JS_THREADSAFE_ASSERT(expr) ((void) 0) +#endif + +#define JS_STATIC_ASSERT(cond) MOZ_STATIC_ASSERT(cond, "JS_STATIC_ASSERT") +#define JS_STATIC_ASSERT_IF(cond, expr) MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF") + +/* + * Abort the process in a non-graceful manner. This will cause a core file, + * call to the debugger or other moral equivalent as well as causing the + * entire process to stop. + */ +extern JS_PUBLIC_API(void) JS_Abort(void); + +/* + * Custom allocator support for SpiderMonkey + */ +#if defined JS_USE_CUSTOM_ALLOCATOR +# include "jscustomallocator.h" +#else +# ifdef DEBUG +/* + * In order to test OOM conditions, when the shell command-line option + * |-A NUM| is passed, we fail continuously after the NUM'th allocation. + */ +extern JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations; /* set from shell/js.cpp */ +extern JS_PUBLIC_DATA(uint32_t) OOM_counter; /* data race, who cares. */ +# define JS_OOM_POSSIBLY_FAIL() \ + do \ + { \ + if (OOM_counter++ >= OOM_maxAllocations) { \ + return NULL; \ + } \ + } while (0) + +# else +# define JS_OOM_POSSIBLY_FAIL() do {} while(0) +# endif + +/* + * SpiderMonkey code should not be calling these allocation functions directly. + * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. + * However, js_free() can be called directly. + */ +static JS_INLINE void* js_malloc(size_t bytes) +{ + JS_OOM_POSSIBLY_FAIL(); + return malloc(bytes); +} + +static JS_INLINE void* js_calloc(size_t bytes) +{ + JS_OOM_POSSIBLY_FAIL(); + return calloc(bytes, 1); +} + +static JS_INLINE void* js_realloc(void* p, size_t bytes) +{ + JS_OOM_POSSIBLY_FAIL(); + return realloc(p, bytes); +} + +static JS_INLINE void js_free(void* p) +{ + free(p); +} +#endif/* JS_USE_CUSTOM_ALLOCATOR */ + +/* + * Replace bit-scanning code sequences with CPU-specific instructions to + * speedup calculations of ceiling/floor log2. + * + * With GCC 3.4 or later we can use __builtin_clz for that, see bug 327129. + * + * SWS: Added MSVC intrinsic bitscan support. See bugs 349364 and 356856. + */ +#if defined(_WIN32) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) + +unsigned char _BitScanForward(unsigned long * Index, unsigned long Mask); +unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); +# pragma intrinsic(_BitScanForward,_BitScanReverse) + +__forceinline static int +__BitScanForward32(unsigned int val) +{ + unsigned long idx; + + _BitScanForward(&idx, (unsigned long)val); + return (int)idx; +} +__forceinline static int +__BitScanReverse32(unsigned int val) +{ + unsigned long idx; + + _BitScanReverse(&idx, (unsigned long)val); + return (int)(31-idx); +} +# define js_bitscan_ctz32(val) __BitScanForward32(val) +# define js_bitscan_clz32(val) __BitScanReverse32(val) +# define JS_HAS_BUILTIN_BITSCAN32 + +#if defined(_M_AMD64) || defined(_M_X64) +unsigned char _BitScanForward64(unsigned long * Index, unsigned __int64 Mask); +unsigned char _BitScanReverse64(unsigned long * Index, unsigned __int64 Mask); +# pragma intrinsic(_BitScanForward64,_BitScanReverse64) + +__forceinline static int +__BitScanForward64(unsigned __int64 val) +{ + unsigned long idx; + + _BitScanForward64(&idx, val); + return (int)idx; +} +__forceinline static int +__BitScanReverse64(unsigned __int64 val) +{ + unsigned long idx; + + _BitScanReverse64(&idx, val); + return (int)(63-idx); +} +# define js_bitscan_ctz64(val) __BitScanForward64(val) +# define js_bitscan_clz64(val) __BitScanReverse64(val) +# define JS_HAS_BUILTIN_BITSCAN64 +#endif +#elif (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +# define js_bitscan_ctz32(val) __builtin_ctz(val) +# define js_bitscan_clz32(val) __builtin_clz(val) +# define JS_HAS_BUILTIN_BITSCAN32 +# if (JS_BYTES_PER_WORD == 8) +# define js_bitscan_ctz64(val) __builtin_ctzll(val) +# define js_bitscan_clz64(val) __builtin_clzll(val) +# define JS_HAS_BUILTIN_BITSCAN64 +# endif + +#endif + +/* +** Macro version of JS_CeilingLog2: Compute the log of the least power of +** 2 greater than or equal to _n. The result is returned in _log2. +*/ +#ifdef JS_HAS_BUILTIN_BITSCAN32 +/* + * Use intrinsic function or count-leading-zeros to calculate ceil(log2(_n)). + * The macro checks for "n <= 1" and not "n != 0" as js_bitscan_clz32(0) is + * undefined. + */ +# define JS_CEILING_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + unsigned int j_ = (unsigned int)(_n); \ + (_log2) = (j_ <= 1 ? 0 : 32 - js_bitscan_clz32(j_ - 1)); \ + JS_END_MACRO +#else +# define JS_CEILING_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + uint32_t j_ = (uint32_t)(_n); \ + (_log2) = 0; \ + if ((j_) & ((j_)-1)) \ + (_log2) += 1; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + JS_END_MACRO +#endif + +/* +** Macro version of JS_FloorLog2: Compute the log of the greatest power of +** 2 less than or equal to _n. The result is returned in _log2. +** +** This is equivalent to finding the highest set bit in the word. +*/ +#ifdef JS_HAS_BUILTIN_BITSCAN32 +/* + * Use js_bitscan_clz32 or count-leading-zeros to calculate floor(log2(_n)). + * Since js_bitscan_clz32(0) is undefined, the macro set the loweset bit to 1 + * to ensure 0 result when _n == 0. + */ +# define JS_FLOOR_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + (_log2) = 31 - js_bitscan_clz32(((unsigned int)(_n)) | 1); \ + JS_END_MACRO +#else +# define JS_FLOOR_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + uint32_t j_ = (uint32_t)(_n); \ + (_log2) = 0; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + JS_END_MACRO +#endif + +/* + * Internal function. + * Compute the log of the least power of 2 greater than or equal to n. This is + * a version of JS_CeilingLog2 that operates on unsigned integers with + * CPU-dependant size. + */ +#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) + +/* + * Internal function. + * Compute the log of the greatest power of 2 less than or equal to n. + * This is a version of JS_FloorLog2 that operates on unsigned integers with + * CPU-dependant size and requires that n != 0. + */ +#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) + +#if JS_BYTES_PER_WORD == 4 +# ifdef JS_HAS_BUILTIN_BITSCAN32 +# define js_FloorLog2wImpl(n) \ + ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n))) +# else +JS_PUBLIC_API(size_t) js_FloorLog2wImpl(size_t n); +# endif +#elif JS_BYTES_PER_WORD == 8 +# ifdef JS_HAS_BUILTIN_BITSCAN64 +# define js_FloorLog2wImpl(n) \ + ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n))) +# else +JS_PUBLIC_API(size_t) js_FloorLog2wImpl(size_t n); +# endif +#else +# error "NOT SUPPORTED" +#endif + +JS_END_EXTERN_C + +#ifdef __cplusplus +#include + +/* + * User guide to memory management within SpiderMonkey: + * + * Quick tips: + * + * Allocation: + * - Prefer to allocate using JSContext: + * cx->{malloc_,realloc_,calloc_,new_,array_new} + * + * - If no JSContext is available, use a JSRuntime: + * rt->{malloc_,realloc_,calloc_,new_,array_new} + * + * - As a last resort, use unaccounted allocation ("OffTheBooks"): + * js::OffTheBooks::{malloc_,realloc_,calloc_,new_,array_new} + * + * Deallocation: + * - When the deallocation occurs on a slow path, use: + * Foreground::{free_,delete_,array_delete} + * + * - Otherwise deallocate on a background thread using a JSContext: + * cx->{free_,delete_,array_delete} + * + * - If no JSContext is available, use a JSRuntime: + * rt->{free_,delete_,array_delete} + * + * - As a last resort, use UnwantedForeground deallocation: + * js::UnwantedForeground::{free_,delete_,array_delete} + * + * General tips: + * + * - Mixing and matching these allocators is allowed (you may free memory + * allocated by any allocator, with any deallocator). + * + * - Never, ever use normal C/C++ memory management: + * malloc, free, new, new[], delete, operator new, etc. + * + * - Never, ever use low-level SpiderMonkey allocators: + * js_malloc(), js_free(), js_calloc(), js_realloc() + * Their use is reserved for the other memory managers. + * + * - Classes which have private constructors or destructors should have + * JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR added to their + * declaration. + * + * Details: + * + * Using vanilla new/new[] is unsafe in SpiderMonkey because they throw on + * failure instead of returning NULL, which is what SpiderMonkey expects. + * (Even overriding them is unsafe, as the system's C++ runtime library may + * throw, which we do not support. We also can't just use the 'nothrow' + * variant of new/new[], because we want to mediate *all* allocations + * within SpiderMonkey, to satisfy any embedders using + * JS_USE_CUSTOM_ALLOCATOR.) + * + * JSContexts and JSRuntimes keep track of memory allocated, and use this + * accounting to schedule GC. OffTheBooks does not. We'd like to remove + * OffTheBooks allocations as much as possible (bug 636558). + * + * On allocation failure, a JSContext correctly reports an error, which a + * JSRuntime and OffTheBooks does not. + * + * A JSContext deallocates in a background thread. A JSRuntime might + * deallocate in the background in the future, but does not now. Foreground + * deallocation is preferable on slow paths. UnwantedForeground deallocations + * occur where we have no JSContext or JSRuntime, and the deallocation is not + * on a slow path. We want to remove UnwantedForeground deallocations (bug + * 636561). + * + * JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR makes the allocation + * classes friends with your class, giving them access to private + * constructors and destructors. + * + * |make check| does a source level check on the number of uses OffTheBooks, + * UnwantedForeground, js_malloc, js_free etc, to prevent regressions. If you + * really must add one, update Makefile.in, and run |make check|. + * + * |make check| also statically prevents the use of vanilla new/new[]. + */ + +#define JS_NEW_BODY(allocator, t, parms) \ + void *memory = allocator(sizeof(t)); \ + return memory ? new(memory) t parms : NULL; + +/* + * Given a class which should provide new_() methods, add + * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This + * adds new_()s with up to 12 parameters. Add more versions of new_ below if + * you need more than 12 parameters. + * + * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS, + * or the build will break. + */ +#define JS_DECLARE_NEW_METHODS(ALLOCATOR, QUALIFIERS)\ + template \ + QUALIFIERS T *new_() {\ + JS_NEW_BODY(ALLOCATOR, T, ())\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11))\ + }\ +\ + template \ + QUALIFIERS T *new_(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12) {\ + JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12))\ + }\ + static const int JSMinAlignment = 8;\ + template \ + QUALIFIERS T *array_new(size_t n) {\ + /* The length is stored just before the vector memory. */\ + uint64_t numBytes64 = uint64_t(JSMinAlignment) + uint64_t(sizeof(T)) * uint64_t(n);\ + size_t numBytes = size_t(numBytes64);\ + if (numBytes64 != numBytes) {\ + JS_ASSERT(0); /* we want to know if this happens in debug builds */\ + return NULL;\ + }\ + void *memory = ALLOCATOR(numBytes);\ + if (!memory)\ + return NULL;\ + *(size_t *)memory = n;\ + memory = (void*)(uintptr_t(memory) + JSMinAlignment);\ + return new(memory) T[n];\ + }\ + + +#define JS_DECLARE_DELETE_METHODS(DEALLOCATOR, QUALIFIERS)\ + template \ + QUALIFIERS void delete_(T *p) {\ + if (p) {\ + p->~T();\ + DEALLOCATOR(p);\ + }\ + }\ +\ + template \ + QUALIFIERS void array_delete(T *p) {\ + if (p) {\ + void* p0 = (void *)(uintptr_t(p) - js::OffTheBooks::JSMinAlignment);\ + size_t n = *(size_t *)p0;\ + for (size_t i = 0; i < n; i++)\ + (p + i)->~T();\ + DEALLOCATOR(p0);\ + }\ + } + + +/* + * In general, all allocations should go through a JSContext or JSRuntime, so + * that the garbage collector knows how much memory has been allocated. In + * cases where it is difficult to use a JSContext or JSRuntime, OffTheBooks can + * be used, though this is undesirable. + */ +namespace js { + +class OffTheBooks { +public: + JS_DECLARE_NEW_METHODS(::js_malloc, JS_ALWAYS_INLINE static) + + static JS_INLINE void* malloc_(size_t bytes) { + return ::js_malloc(bytes); + } + + static JS_INLINE void* calloc_(size_t bytes) { + return ::js_calloc(bytes); + } + + static JS_INLINE void* realloc_(void* p, size_t bytes) { + return ::js_realloc(p, bytes); + } +}; + +/* + * We generally prefer deallocating using JSContext because it can happen in + * the background. On slow paths, we may prefer foreground allocation. + */ +class Foreground { +public: + /* See parentheses comment above. */ + static JS_ALWAYS_INLINE void free_(void* p) { + ::js_free(p); + } + + JS_DECLARE_DELETE_METHODS(::js_free, JS_ALWAYS_INLINE static) +}; + +class UnwantedForeground : public Foreground { +}; + +} /* namespace js */ + +/* + * Note lack of ; in JSRuntime below. This is intentional so "calling" this + * looks "normal". + */ +#define JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR \ + friend class js::OffTheBooks;\ + friend class js::Foreground;\ + friend class js::UnwantedForeground;\ + friend struct ::JSContext;\ + friend struct ::JSRuntime + +/* + * The following classes are designed to cause assertions to detect + * inadvertent use of guard objects as temporaries. In other words, + * when we have a guard object whose only purpose is its constructor and + * destructor (and is never otherwise referenced), the intended use + * might be: + * JSAutoTempValueRooter tvr(cx, 1, &val); + * but is is easy to accidentally write: + * JSAutoTempValueRooter(cx, 1, &val); + * which compiles just fine, but runs the destructor well before the + * intended time. + * + * They work by adding (#ifdef DEBUG) an additional parameter to the + * guard object's constructor, with a default value, so that users of + * the guard object's API do not need to do anything. The default value + * of this parameter is a temporary object. C++ (ISO/IEC 14882:1998), + * section 12.2 [class.temporary], clauses 4 and 5 seem to assume a + * guarantee that temporaries are destroyed in the reverse of their + * construction order, but I actually can't find a statement that that + * is true in the general case (beyond the two specific cases mentioned + * there). However, it seems to be true. + * + * These classes are intended to be used only via the macros immediately + * below them: + * JS_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member + * variable, and should be put where a declaration of a private + * member variable would be placed. + * JS_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the + * parameters to each constructor of the guard object; it declares + * (ifdef DEBUG) an additional parameter. + * JS_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each + * constructor. It uses the parameter declared by + * JS_GUARD_OBJECT_NOTIFIER_PARAM. + */ +#ifdef DEBUG +class JS_FRIEND_API(JSGuardObjectNotifier) +{ +private: + bool* mStatementDone; +public: + JSGuardObjectNotifier() : mStatementDone(NULL) {} + + ~JSGuardObjectNotifier() { + *mStatementDone = true; + } + + void setStatementDone(bool *aStatementDone) { + mStatementDone = aStatementDone; + } +}; + +class JS_FRIEND_API(JSGuardObjectNotificationReceiver) +{ +private: + bool mStatementDone; +public: + JSGuardObjectNotificationReceiver() : mStatementDone(false) {} + + ~JSGuardObjectNotificationReceiver() { + /* + * Assert that the guard object was not used as a temporary. + * (Note that this assert might also fire if Init is not called + * because the guard object's implementation is not using the + * above macros correctly.) + */ + JS_ASSERT(mStatementDone); + } + + void Init(const JSGuardObjectNotifier &aNotifier) { + /* + * aNotifier is passed as a const reference so that we can pass a + * temporary, but we really intend it as non-const + */ + const_cast(aNotifier). + setStatementDone(&mStatementDone); + } +}; + +#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER \ + JSGuardObjectNotificationReceiver _mCheckNotUsedAsTemporary; +#define JS_GUARD_OBJECT_NOTIFIER_PARAM \ + , const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier() +#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT \ + , const JSGuardObjectNotifier& _notifier +#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 \ + const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier() +#define JS_GUARD_OBJECT_NOTIFIER_INIT \ + JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO + +#else /* defined(DEBUG) */ + +#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER +#define JS_GUARD_OBJECT_NOTIFIER_PARAM +#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT +#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 +#define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO + +#endif /* !defined(DEBUG) */ + +namespace js { + +/* + * "Move" References + * + * Some types can be copied much more efficiently if we know the original's + * value need not be preserved --- that is, if we are doing a "move", not a + * "copy". For example, if we have: + * + * Vector u; + * Vector v(u); + * + * the constructor for v must apply a copy constructor to each element of u --- + * taking time linear in the length of u. However, if we know we will not need u + * any more once v has been initialized, then we could initialize v very + * efficiently simply by stealing u's dynamically allocated buffer and giving it + * to v --- a constant-time operation, regardless of the size of u. + * + * Moves often appear in container implementations. For example, when we append + * to a vector, we may need to resize its buffer. This entails moving each of + * its extant elements from the old, smaller buffer to the new, larger buffer. + * But once the elements have been migrated, we're just going to throw away the + * old buffer; we don't care if they still have their values. So if the vector's + * element type can implement "move" more efficiently than "copy", the vector + * resizing should by all means use a "move" operation. Hash tables also need to + * be resized. + * + * The details of the optimization, and whether it's worth applying, vary from + * one type to the next. And while some constructor calls are moves, many really + * are copies, and can't be optimized this way. So we need: + * + * 1) a way for a particular invocation of a copy constructor to say that it's + * really a move, and that the value of the original isn't important + * afterwards (althought it must still be safe to destroy); and + * + * 2) a way for a type (like Vector) to announce that it can be moved more + * efficiently than it can be copied, and provide an implementation of that + * move operation. + * + * The Move(T &) function takes a reference to a T, and returns an MoveRef + * referring to the same value; that's 1). An MoveRef is simply a reference + * to a T, annotated to say that a copy constructor applied to it may move that + * T, instead of copying it. Finally, a constructor that accepts an MoveRef + * should perform a more efficient move, instead of a copy, providing 2). + * + * So, where we might define a copy constructor for a class C like this: + * + * C(const C &rhs) { ... copy rhs to this ... } + * + * we would declare a move constructor like this: + * + * C(MoveRef rhs) { ... move rhs to this ... } + * + * And where we might perform a copy like this: + * + * C c2(c1); + * + * we would perform a move like this: + * + * C c2(Move(c1)) + * + * Note that MoveRef implicitly converts to T &, so you can pass an + * MoveRef to an ordinary copy constructor for a type that doesn't support a + * special move constructor, and you'll just get a copy. This means that + * templates can use Move whenever they know they won't use the original value + * any more, even if they're not sure whether the type at hand has a specialized + * move constructor. If it doesn't, the MoveRef will just convert to a T &, + * and the ordinary copy constructor will apply. + * + * A class with a move constructor can also provide a move assignment operator, + * which runs this's destructor, and then applies the move constructor to + * *this's memory. A typical definition: + * + * C &operator=(MoveRef rhs) { + * this->~C(); + * new(this) C(rhs); + * return *this; + * } + * + * With that in place, one can write move assignments like this: + * + * c2 = Move(c1); + * + * This destroys c1, moves c1's value to c2, and leaves c1 in an undefined but + * destructible state. + * + * This header file defines MoveRef and Move in the js namespace. It's up to + * individual containers to annotate moves as such, by calling Move; and it's up + * to individual types to define move constructors. + * + * One hint: if you're writing a move constructor where the type has members + * that should be moved themselves, it's much nicer to write this: + * + * C(MoveRef c) : x(c->x), y(c->y) { } + * + * than the equivalent: + * + * C(MoveRef c) { new(&x) X(c->x); new(&y) Y(c->y); } + * + * especially since GNU C++ fails to notice that this does indeed initialize x + * and y, which may matter if they're const. + */ +template +class MoveRef { + public: + typedef T Referent; + explicit MoveRef(T &t) : pointer(&t) { } + T &operator*() const { return *pointer; } + T *operator->() const { return pointer; } +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + /* + * If MoveRef is used in a rvalue position (which is expected), we can + * end up in a situation where, without this ifdef, we would try to pass + * a T& to a move constructor, which fails. It is not clear if the compiler + * should instead use the copy constructor, but for now this lets us build + * with clang. See bug 689066 and llvm.org/pr11003 for the details. + * Note: We can probably remove MoveRef completely once we are comfortable + * using c++11. + */ + operator T&& () const { return static_cast(*pointer); } +#else + operator T& () const { return *pointer; } +#endif + private: + T *pointer; +}; + +template +MoveRef Move(T &t) { return MoveRef(t); } + +template +MoveRef Move(const T &t) { return MoveRef(const_cast(t)); } + +/* Useful for implementing containers that assert non-reentrancy */ +class ReentrancyGuard +{ + /* ReentrancyGuard is not copyable. */ + ReentrancyGuard(const ReentrancyGuard &); + void operator=(const ReentrancyGuard &); + +#ifdef DEBUG + bool &entered; +#endif + public: + template +#ifdef DEBUG + ReentrancyGuard(T &obj) + : entered(obj.entered) +#else + ReentrancyGuard(T &/*obj*/) +#endif + { +#ifdef DEBUG + JS_ASSERT(!entered); + entered = true; +#endif + } + ~ReentrancyGuard() + { +#ifdef DEBUG + entered = false; +#endif + } +}; + +/* + * Round x up to the nearest power of 2. This function assumes that the most + * significant bit of x is not set, which would lead to overflow. + */ +JS_ALWAYS_INLINE size_t +RoundUpPow2(size_t x) +{ + return size_t(1) << JS_CEILING_LOG2W(x); +} + +} /* namespace js */ + +#endif /* defined(__cplusplus) */ + +/* + * This is SpiderMonkey's equivalent to |nsMallocSizeOfFun|. + */ +typedef size_t(*JSMallocSizeOfFun)(const void *p); + +/* sixgill annotation defines */ +#ifndef HAVE_STATIC_ANNOTATIONS +# define HAVE_STATIC_ANNOTATIONS +# ifdef XGILL_PLUGIN +# define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND))) +# define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND))) +# define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND))) +# define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND))) +# define STATIC_INVARIANT(COND) __attribute__((invariant(#COND))) +# define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND))) +# define STATIC_PASTE2(X,Y) X ## Y +# define STATIC_PASTE1(X,Y) STATIC_PASTE2(X,Y) +# define STATIC_ASSERT(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assert_static(#COND), unused)) \ + int STATIC_PASTE1(assert_static_, __COUNTER__); \ + JS_END_MACRO +# define STATIC_ASSUME(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assume_static(#COND), unused)) \ + int STATIC_PASTE1(assume_static_, __COUNTER__); \ + JS_END_MACRO +# define STATIC_ASSERT_RUNTIME(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assert_static_runtime(#COND), unused)) \ + int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \ + JS_END_MACRO +# else /* XGILL_PLUGIN */ +# define STATIC_PRECONDITION(COND) /* nothing */ +# define STATIC_PRECONDITION_ASSUME(COND) /* nothing */ +# define STATIC_POSTCONDITION(COND) /* nothing */ +# define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */ +# define STATIC_INVARIANT(COND) /* nothing */ +# define STATIC_INVARIANT_ASSUME(COND) /* nothing */ +# define STATIC_ASSERT(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO +# define STATIC_ASSUME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO +# define STATIC_ASSERT_RUNTIME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO +# endif /* XGILL_PLUGIN */ +# define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference()) +#endif /* HAVE_STATIC_ANNOTATIONS */ + +#endif /* js_utility_h__ */ diff --git a/deps/mozjs/js/public/Vector.h b/deps/mozjs/js/public/Vector.h new file mode 100644 index 00000000000..6f7694e7016 --- /dev/null +++ b/deps/mozjs/js/public/Vector.h @@ -0,0 +1,1005 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Luke Wagner + * Nicholas Nethercote + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsvector_h_ +#define jsvector_h_ + +#include "mozilla/Attributes.h" + +#include "TemplateLib.h" +#include "Utility.h" + +/* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4345) +#endif + +namespace js { + +class TempAllocPolicy; + +template +class Vector; + +/* + * This template class provides a default implementation for vector operations + * when the element type is not known to be a POD, as judged by IsPodType. + */ +template +struct VectorImpl +{ + /* Destroys constructed objects in the range [begin, end). */ + static inline void destroy(T *begin, T *end) { + for (T *p = begin; p != end; ++p) + p->~T(); + } + + /* Constructs objects in the uninitialized range [begin, end). */ + static inline void initialize(T *begin, T *end) { + for (T *p = begin; p != end; ++p) + new(p) T(); + } + + /* + * Copy-constructs objects in the uninitialized range + * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). + */ + template + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { + for (const U *p = srcbeg; p != srcend; ++p, ++dst) + new(dst) T(*p); + } + + /* + * Move-constructs objects in the uninitialized range + * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). + */ + template + static inline void moveConstruct(T *dst, const U *srcbeg, const U *srcend) { + for (const U *p = srcbeg; p != srcend; ++p, ++dst) + new(dst) T(Move(*p)); + } + + /* + * Copy-constructs objects in the uninitialized range [dst, dst+n) from the + * same object u. + */ + template + static inline void copyConstructN(T *dst, size_t n, const U &u) { + for (T *end = dst + n; dst != end; ++dst) + new(dst) T(u); + } + + /* + * Grows the given buffer to have capacity newcap, preserving the objects + * constructed in the range [begin, end) and updating v. Assumes that (1) + * newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will + * not overflow. + */ + static inline bool growTo(Vector &v, size_t newcap) { + JS_ASSERT(!v.usingInlineStorage()); + T *newbuf = reinterpret_cast(v.malloc_(newcap * sizeof(T))); + if (!newbuf) + return false; + for (T *dst = newbuf, *src = v.beginNoCheck(); src != v.endNoCheck(); ++dst, ++src) + new(dst) T(Move(*src)); + VectorImpl::destroy(v.beginNoCheck(), v.endNoCheck()); + v.free_(v.mBegin); + v.mBegin = newbuf; + /* v.mLength is unchanged. */ + v.mCapacity = newcap; + return true; + } +}; + +/* + * This partial template specialization provides a default implementation for + * vector operations when the element type is known to be a POD, as judged by + * IsPodType. + */ +template +struct VectorImpl +{ + static inline void destroy(T *, T *) {} + + static inline void initialize(T *begin, T *end) { + /* + * You would think that memset would be a big win (or even break even) + * when we know T is a POD. But currently it's not. This is probably + * because |append| tends to be given small ranges and memset requires + * a function call that doesn't get inlined. + * + * memset(begin, 0, sizeof(T) * (end-begin)); + */ + for (T *p = begin; p != end; ++p) + new(p) T(); + } + + template + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { + /* + * See above memset comment. Also, notice that copyConstruct is + * currently templated (T != U), so memcpy won't work without + * requiring T == U. + * + * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg)); + */ + for (const U *p = srcbeg; p != srcend; ++p, ++dst) + *dst = *p; + } + + template + static inline void moveConstruct(T *dst, const U *srcbeg, const U *srcend) { + copyConstruct(dst, srcbeg, srcend); + } + + static inline void copyConstructN(T *dst, size_t n, const T &t) { + for (T *p = dst, *end = dst + n; p != end; ++p) + *p = t; + } + + static inline bool growTo(Vector &v, size_t newcap) { + JS_ASSERT(!v.usingInlineStorage()); + size_t bytes = sizeof(T) * newcap; + size_t oldBytes = sizeof(T) * v.mCapacity; + T *newbuf = reinterpret_cast(v.realloc_(v.mBegin, oldBytes, bytes)); + if (!newbuf) + return false; + v.mBegin = newbuf; + /* v.mLength is unchanged. */ + v.mCapacity = newcap; + return true; + } +}; + +/* + * JS-friendly, STL-like container providing a short-lived, dynamic buffer. + * Vector calls the constructors/destructors of all elements stored in + * its internal buffer, so non-PODs may be safely used. Additionally, + * Vector will store the first N elements in-place before resorting to + * dynamic allocation. + * + * T requirements: + * - default and copy constructible, assignable, destructible + * - operations do not throw + * N requirements: + * - any value, however, N is clamped to min/max values + * AllocPolicy: + * - see "Allocation policies" in jsalloc.h (default js::TempAllocPolicy) + * + * N.B: Vector is not reentrant: T member functions called during Vector member + * functions must not call back into the same object. + */ +template +class Vector : private AllocPolicy +{ + /* utilities */ + + static const bool sElemIsPod = tl::IsPodType::result; + typedef VectorImpl Impl; + friend struct VectorImpl; + + bool calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap); + bool growStorageBy(size_t lengthInc); + bool growHeapStorageBy(size_t lengthInc); + bool convertToHeapStorage(size_t lengthInc); + + template inline bool growByImpl(size_t inc); + + /* magic constants */ + + static const int sMaxInlineBytes = 1024; + + /* compute constants */ + + /* + * Consider element size to be 1 for buffer sizing if there are + * 0 inline elements. This allows us to compile when the definition + * of the element type is not visible here. + * + * Explicit specialization is only allowed at namespace scope, so + * in order to keep everything here, we use a dummy template + * parameter with partial specialization. + */ + template + struct ElemSize { + static const size_t result = sizeof(T); + }; + template + struct ElemSize<0, Dummy> { + static const size_t result = 1; + }; + + static const size_t sInlineCapacity = + tl::Min::result>::result; + + /* Calculate inline buffer size; avoid 0-sized array. */ + static const size_t sInlineBytes = + tl::Max<1, sInlineCapacity * ElemSize::result>::result; + + /* member data */ + + /* + * Pointer to the buffer, be it inline or heap-allocated. Only [mBegin, + * mBegin + mLength) hold valid constructed T objects. The range [mBegin + + * mLength, mBegin + mCapacity) holds uninitialized memory. The range + * [mBegin + mLength, mBegin + mReserved) also holds uninitialized memory + * previously allocated by a call to reserve(). + */ + T *mBegin; + size_t mLength; /* Number of elements in the Vector. */ + size_t mCapacity; /* Max number of elements storable in the Vector without resizing. */ +#ifdef DEBUG + size_t mReserved; /* Max elements of reserved or used space in this vector. */ +#endif + + AlignedStorage storage; + +#ifdef DEBUG + friend class ReentrancyGuard; + bool entered; +#endif + + Vector(const Vector &) MOZ_DELETE; + Vector &operator=(const Vector &) MOZ_DELETE; + + /* private accessors */ + + bool usingInlineStorage() const { + return mBegin == (T *)storage.addr(); + } + + T *beginNoCheck() const { + return mBegin; + } + + T *endNoCheck() { + return mBegin + mLength; + } + + const T *endNoCheck() const { + return mBegin + mLength; + } + +#ifdef DEBUG + size_t reserved() const { + JS_ASSERT(mReserved <= mCapacity); + JS_ASSERT(mLength <= mReserved); + return mReserved; + } +#endif + + /* Append operations guaranteed to succeed due to pre-reserved space. */ + template void internalAppend(U t); + void internalAppendN(const T &t, size_t n); + template void internalAppend(const U *begin, size_t length); + template void internalAppend(const Vector &other); + + public: + static const size_t sMaxInlineStorage = N; + + typedef T ElementType; + + Vector(AllocPolicy = AllocPolicy()); + Vector(MoveRef); /* Move constructor. */ + Vector &operator=(MoveRef); /* Move assignment. */ + ~Vector(); + + /* accessors */ + + const AllocPolicy &allocPolicy() const { + return *this; + } + + AllocPolicy &allocPolicy() { + return *this; + } + + enum { InlineLength = N }; + + size_t length() const { + return mLength; + } + + bool empty() const { + return mLength == 0; + } + + size_t capacity() const { + return mCapacity; + } + + T *begin() { + JS_ASSERT(!entered); + return mBegin; + } + + const T *begin() const { + JS_ASSERT(!entered); + return mBegin; + } + + T *end() { + JS_ASSERT(!entered); + return mBegin + mLength; + } + + const T *end() const { + JS_ASSERT(!entered); + return mBegin + mLength; + } + + T &operator[](size_t i) { + JS_ASSERT(!entered && i < mLength); + return begin()[i]; + } + + const T &operator[](size_t i) const { + JS_ASSERT(!entered && i < mLength); + return begin()[i]; + } + + T &back() { + JS_ASSERT(!entered && !empty()); + return *(end() - 1); + } + + const T &back() const { + JS_ASSERT(!entered && !empty()); + return *(end() - 1); + } + + class Range { + friend class Vector; + T *cur, *end; + Range(T *cur, T *end) : cur(cur), end(end) {} + public: + Range() {} + bool empty() const { return cur == end; } + size_t remain() const { return end - cur; } + T &front() const { return *cur; } + void popFront() { JS_ASSERT(!empty()); ++cur; } + T popCopyFront() { JS_ASSERT(!empty()); return *cur++; } + }; + + Range all() { + return Range(begin(), end()); + } + + /* mutators */ + + /* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */ + bool reserve(size_t capacity); + + /* + * Destroy elements in the range [end() - incr, end()). Does not deallocate + * or unreserve storage for those elements. + */ + void shrinkBy(size_t incr); + + /* Grow the vector by incr elements. */ + bool growBy(size_t incr); + + /* Call shrinkBy or growBy based on whether newSize > length(). */ + bool resize(size_t newLength); + + /* Leave new elements as uninitialized memory. */ + bool growByUninitialized(size_t incr); + bool resizeUninitialized(size_t newLength); + + /* Shorthand for shrinkBy(length()). */ + void clear(); + + /* Clears and releases any heap-allocated storage. */ + void clearAndFree(); + + /* + * Potentially fallible append operations. + * + * The function templates that take an unspecified type U require a + * const T & or a MoveRef. The MoveRef variants move their + * operands into the vector, instead of copying them. If they fail, the + * operand is left unmoved. + */ + template bool append(U t); + bool appendN(const T &t, size_t n); + template bool append(const U *begin, const U *end); + template bool append(const U *begin, size_t length); + template bool append(const Vector &other); + + /* + * Guaranteed-infallible append operations for use upon vectors whose + * memory has been pre-reserved. + */ + void infallibleAppend(const T &t) { + internalAppend(t); + } + void infallibleAppendN(const T &t, size_t n) { + internalAppendN(t, n); + } + template void infallibleAppend(const U *begin, const U *end) { + internalAppend(begin, PointerRangeSize(begin, end)); + } + template void infallibleAppend(const U *begin, size_t length) { + internalAppend(begin, length); + } + template void infallibleAppend(const Vector &other) { + internalAppend(other); + } + + void popBack(); + + T popCopy(); + + /* + * Transfers ownership of the internal buffer used by Vector to the caller. + * After this call, the Vector is empty. Since the returned buffer may need + * to be allocated (if the elements are currently stored in-place), the + * call can fail, returning NULL. + * + * N.B. Although a T*, only the range [0, length()) is constructed. + */ + T *extractRawBuffer(); + + /* + * Transfer ownership of an array of objects into the Vector. + * N.B. This call assumes that there are no uninitialized elements in the + * passed array. + */ + void replaceRawBuffer(T *p, size_t length); + + /* + * Places |val| at position |p|, shifting existing elements + * from |p| onward one position higher. + */ + bool insert(T *p, const T &val); + + /* + * Removes the element |t|, which must fall in the bounds [begin, end), + * shifting existing elements from |t + 1| onward one position lower. + */ + void erase(T *t); +}; + +/* This does the re-entrancy check plus several other sanity checks. */ +#define REENTRANCY_GUARD_ET_AL \ + ReentrancyGuard g(*this); \ + JS_ASSERT_IF(usingInlineStorage(), mCapacity == sInlineCapacity); \ + JS_ASSERT(reserved() <= mCapacity); \ + JS_ASSERT(mLength <= reserved()); \ + JS_ASSERT(mLength <= mCapacity) + +/* Vector Implementation */ + +template +JS_ALWAYS_INLINE +Vector::Vector(AllocPolicy ap) + : AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0), + mCapacity(sInlineCapacity) +#ifdef DEBUG + , mReserved(0), entered(false) +#endif +{} + +/* Move constructor. */ +template +JS_ALWAYS_INLINE +Vector::Vector(MoveRef rhs) + : AllocPolicy(rhs) +{ + mLength = rhs->mLength; + mCapacity = rhs->mCapacity; +#ifdef DEBUG + mReserved = rhs->mReserved; +#endif + + if (rhs->usingInlineStorage()) { + /* We can't move the buffer over in this case, so copy elements. */ + mBegin = (T *)storage.addr(); + Impl::moveConstruct(mBegin, rhs->beginNoCheck(), rhs->endNoCheck()); + /* + * Leave rhs's mLength, mBegin, mCapacity, and mReserved as they are. + * The elements in its in-line storage still need to be destroyed. + */ + } else { + /* + * Take src's buffer, and turn src into an empty vector using + * in-line storage. + */ + mBegin = rhs->mBegin; + rhs->mBegin = (T *) rhs->storage.addr(); + rhs->mCapacity = sInlineCapacity; + rhs->mLength = 0; +#ifdef DEBUG + rhs->mReserved = 0; +#endif + } +} + +/* Move assignment. */ +template +JS_ALWAYS_INLINE +Vector & +Vector::operator=(MoveRef rhs) +{ + this->~Vector(); + new(this) Vector(rhs); + return *this; +} + +template +JS_ALWAYS_INLINE +Vector::~Vector() +{ + REENTRANCY_GUARD_ET_AL; + Impl::destroy(beginNoCheck(), endNoCheck()); + if (!usingInlineStorage()) + this->free_(beginNoCheck()); +} + +/* + * Calculate a new capacity that is at least lengthInc greater than + * curLength and check for overflow. + */ +template +STATIC_POSTCONDITION(!return || newCap >= curLength + lengthInc) +inline bool +Vector::calculateNewCapacity(size_t curLength, size_t lengthInc, + size_t &newCap) +{ + size_t newMinCap = curLength + lengthInc; + + /* + * Check for overflow in the above addition, below CEILING_LOG2, and later + * multiplication by sizeof(T). + */ + if (newMinCap < curLength || + newMinCap & tl::MulOverflowMask<2 * sizeof(T)>::result) { + this->reportAllocOverflow(); + return false; + } + + /* Round up to next power of 2. */ + newCap = RoundUpPow2(newMinCap); + + /* + * Do not allow a buffer large enough that the expression ((char *)end() - + * (char *)begin()) overflows ptrdiff_t. See Bug 510319. + */ + if (newCap & tl::UnsafeRangeSizeMask::result) { + this->reportAllocOverflow(); + return false; + } + return true; +} + +/* + * This function will grow the current heap capacity to have capacity + * (mLength + lengthInc) and fail on OOM or integer overflow. + */ +template +JS_ALWAYS_INLINE bool +Vector::growHeapStorageBy(size_t lengthInc) +{ + JS_ASSERT(!usingInlineStorage()); + size_t newCap; + return calculateNewCapacity(mLength, lengthInc, newCap) && + Impl::growTo(*this, newCap); +} + +/* + * This function will create a new heap buffer with capacity (mLength + + * lengthInc()), move all elements in the inline buffer to this new buffer, + * and fail on OOM or integer overflow. + */ +template +inline bool +Vector::convertToHeapStorage(size_t lengthInc) +{ + JS_ASSERT(usingInlineStorage()); + size_t newCap; + if (!calculateNewCapacity(mLength, lengthInc, newCap)) + return false; + + /* Allocate buffer. */ + T *newBuf = reinterpret_cast(this->malloc_(newCap * sizeof(T))); + if (!newBuf) + return false; + + /* Copy inline elements into heap buffer. */ + Impl::moveConstruct(newBuf, beginNoCheck(), endNoCheck()); + Impl::destroy(beginNoCheck(), endNoCheck()); + + /* Switch in heap buffer. */ + mBegin = newBuf; + /* mLength is unchanged. */ + mCapacity = newCap; + return true; +} + +template +JS_NEVER_INLINE bool +Vector::growStorageBy(size_t incr) +{ + JS_ASSERT(mLength + incr > mCapacity); + return usingInlineStorage() + ? convertToHeapStorage(incr) + : growHeapStorageBy(incr); +} + +template +inline bool +Vector::reserve(size_t request) +{ + REENTRANCY_GUARD_ET_AL; + if (request > mCapacity && !growStorageBy(request - mLength)) + return false; + +#ifdef DEBUG + if (request > mReserved) + mReserved = request; + JS_ASSERT(mLength <= mReserved); + JS_ASSERT(mReserved <= mCapacity); +#endif + return true; +} + +template +inline void +Vector::shrinkBy(size_t incr) +{ + REENTRANCY_GUARD_ET_AL; + JS_ASSERT(incr <= mLength); + Impl::destroy(endNoCheck() - incr, endNoCheck()); + mLength -= incr; +} + +template +template +JS_ALWAYS_INLINE bool +Vector::growByImpl(size_t incr) +{ + REENTRANCY_GUARD_ET_AL; + if (incr > mCapacity - mLength && !growStorageBy(incr)) + return false; + + JS_ASSERT(mLength + incr <= mCapacity); + T *newend = endNoCheck() + incr; + if (InitNewElems) + Impl::initialize(endNoCheck(), newend); + mLength += incr; +#ifdef DEBUG + if (mLength > mReserved) + mReserved = mLength; +#endif + return true; +} + +template +JS_ALWAYS_INLINE bool +Vector::growBy(size_t incr) +{ + return growByImpl(incr); +} + +template +JS_ALWAYS_INLINE bool +Vector::growByUninitialized(size_t incr) +{ + return growByImpl(incr); +} + +template +STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength) +inline bool +Vector::resize(size_t newLength) +{ + size_t curLength = mLength; + if (newLength > curLength) + return growBy(newLength - curLength); + shrinkBy(curLength - newLength); + return true; +} + +template +JS_ALWAYS_INLINE bool +Vector::resizeUninitialized(size_t newLength) +{ + size_t curLength = mLength; + if (newLength > curLength) + return growByUninitialized(newLength - curLength); + shrinkBy(curLength - newLength); + return true; +} + +template +inline void +Vector::clear() +{ + REENTRANCY_GUARD_ET_AL; + Impl::destroy(beginNoCheck(), endNoCheck()); + mLength = 0; +} + +template +inline void +Vector::clearAndFree() +{ + clear(); + + if (usingInlineStorage()) + return; + + this->free_(beginNoCheck()); + mBegin = (T *)storage.addr(); + mCapacity = sInlineCapacity; +#ifdef DEBUG + mReserved = 0; +#endif +} + +template +template +JS_ALWAYS_INLINE bool +Vector::append(U t) +{ + REENTRANCY_GUARD_ET_AL; + if (mLength == mCapacity && !growStorageBy(1)) + return false; + +#ifdef DEBUG + if (mLength + 1 > mReserved) + mReserved = mLength + 1; +#endif + internalAppend(t); + return true; +} + +template +template +JS_ALWAYS_INLINE void +Vector::internalAppend(U t) +{ + JS_ASSERT(mLength + 1 <= mReserved); + JS_ASSERT(mReserved <= mCapacity); + new(endNoCheck()) T(t); + ++mLength; +} + +template +JS_ALWAYS_INLINE bool +Vector::appendN(const T &t, size_t needed) +{ + REENTRANCY_GUARD_ET_AL; + if (mLength + needed > mCapacity && !growStorageBy(needed)) + return false; + +#ifdef DEBUG + if (mLength + needed > mReserved) + mReserved = mLength + needed; +#endif + internalAppendN(t, needed); + return true; +} + +template +JS_ALWAYS_INLINE void +Vector::internalAppendN(const T &t, size_t needed) +{ + JS_ASSERT(mLength + needed <= mReserved); + JS_ASSERT(mReserved <= mCapacity); + Impl::copyConstructN(endNoCheck(), needed, t); + mLength += needed; +} + +template +inline bool +Vector::insert(T *p, const T &val) +{ + JS_ASSERT(begin() <= p && p <= end()); + size_t pos = p - begin(); + JS_ASSERT(pos <= mLength); + size_t oldLength = mLength; + if (pos == oldLength) + return append(val); + { + T oldBack = back(); + if (!append(oldBack)) /* Dup the last element. */ + return false; + } + for (size_t i = oldLength; i > pos; --i) + (*this)[i] = (*this)[i - 1]; + (*this)[pos] = val; + return true; +} + +template +inline void +Vector::erase(T *it) +{ + JS_ASSERT(begin() <= it && it < end()); + while (it + 1 != end()) { + *it = *(it + 1); + ++it; + } + popBack(); +} + +template +template +JS_ALWAYS_INLINE bool +Vector::append(const U *insBegin, const U *insEnd) +{ + REENTRANCY_GUARD_ET_AL; + size_t needed = PointerRangeSize(insBegin, insEnd); + if (mLength + needed > mCapacity && !growStorageBy(needed)) + return false; + +#ifdef DEBUG + if (mLength + needed > mReserved) + mReserved = mLength + needed; +#endif + internalAppend(insBegin, needed); + return true; +} + +template +template +JS_ALWAYS_INLINE void +Vector::internalAppend(const U *insBegin, size_t length) +{ + JS_ASSERT(mLength + length <= mReserved); + JS_ASSERT(mReserved <= mCapacity); + Impl::copyConstruct(endNoCheck(), insBegin, insBegin + length); + mLength += length; +} + +template +template +inline bool +Vector::append(const Vector &other) +{ + return append(other.begin(), other.end()); +} + +template +template +inline void +Vector::internalAppend(const Vector &other) +{ + internalAppend(other.begin(), other.length()); +} + +template +template +JS_ALWAYS_INLINE bool +Vector::append(const U *insBegin, size_t length) +{ + return this->append(insBegin, insBegin + length); +} + +template +JS_ALWAYS_INLINE void +Vector::popBack() +{ + REENTRANCY_GUARD_ET_AL; + JS_ASSERT(!empty()); + --mLength; + endNoCheck()->~T(); +} + +template +JS_ALWAYS_INLINE T +Vector::popCopy() +{ + T ret = back(); + popBack(); + return ret; +} + +template +inline T * +Vector::extractRawBuffer() +{ + T *ret; + if (usingInlineStorage()) { + ret = reinterpret_cast(this->malloc_(mLength * sizeof(T))); + if (!ret) + return NULL; + Impl::copyConstruct(ret, beginNoCheck(), endNoCheck()); + Impl::destroy(beginNoCheck(), endNoCheck()); + /* mBegin, mCapacity are unchanged. */ + mLength = 0; + } else { + ret = mBegin; + mBegin = (T *)storage.addr(); + mLength = 0; + mCapacity = sInlineCapacity; +#ifdef DEBUG + mReserved = 0; +#endif + } + return ret; +} + +template +inline void +Vector::replaceRawBuffer(T *p, size_t length) +{ + REENTRANCY_GUARD_ET_AL; + + /* Destroy what we have. */ + Impl::destroy(beginNoCheck(), endNoCheck()); + if (!usingInlineStorage()) + this->free_(beginNoCheck()); + + /* Take in the new buffer. */ + if (length <= sInlineCapacity) { + /* + * We convert to inline storage if possible, even though p might + * otherwise be acceptable. Maybe this behaviour should be + * specifiable with an argument to this function. + */ + mBegin = (T *)storage.addr(); + mLength = length; + mCapacity = sInlineCapacity; + Impl::moveConstruct(mBegin, p, p + length); + Impl::destroy(p, p + length); + this->free_(p); + } else { + mBegin = p; + mLength = length; + mCapacity = length; + } +#ifdef DEBUG + mReserved = length; +#endif +} + +} /* namespace js */ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* jsvector_h_ */ diff --git a/deps/mozjs/js/src/.cproject b/deps/mozjs/js/src/.cproject new file mode 100644 index 00000000000..255d86c5846 --- /dev/null +++ b/deps/mozjs/js/src/.cproject @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deps/mozjs/js/src/.project b/deps/mozjs/js/src/.project new file mode 100644 index 00000000000..bafc48da59c --- /dev/null +++ b/deps/mozjs/js/src/.project @@ -0,0 +1,83 @@ + + + v8monkey + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + build-for-eclipse.log + + + org.eclipse.cdt.make.core.buildCommand + cat + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/v8monkey/_DBG.OBJ} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + false + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/deps/mozjs/js/src/.settings/language.settings.xml b/deps/mozjs/js/src/.settings/language.settings.xml new file mode 100644 index 00000000000..484daf8bbdf --- /dev/null +++ b/deps/mozjs/js/src/.settings/language.settings.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/deps/mozjs/js/src/ETWProvider.man b/deps/mozjs/js/src/ETWProvider.man new file mode 100644 index 00000000000..c9005beff8a --- /dev/null +++ b/deps/mozjs/js/src/ETWProvider.man @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deps/mozjs/js/src/GlobalObject.cpp b/deps/mozjs/js/src/GlobalObject.cpp deleted file mode 100644 index b137179b04b..00000000000 --- a/deps/mozjs/js/src/GlobalObject.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* -*- 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 ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey global object code. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jeff Walden (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "GlobalObject.h" - -#include "jscntxt.h" -#include "jsexn.h" -#include "json.h" - -#include "jsobjinlines.h" -#include "jsregexpinlines.h" - -using namespace js; - -JSObject * -js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) -{ - JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - - /* If cx has no global object, use obj so prototypes can be found. */ - if (!cx->globalObject) - JS_SetGlobalObject(cx, obj); - - /* Record Function and Object in cx->resolvingList. */ - JSAtom **classAtoms = cx->runtime->atomState.classAtoms; - 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. */ - JSObject *fun_proto; - if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto)) - return NULL; - if (!fun_proto) { - fun_proto = js_InitFunctionClass(cx, obj); - if (!fun_proto) - return NULL; - } else { - JSObject *ctor = JS_GetConstructor(cx, fun_proto); - if (!ctor) - return NULL; - if (!obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)), - ObjectValue(*ctor), 0, 0, 0)) { - return NULL; - } - } - - /* Initialize the object class next so Object.prototype works. */ - JSObject *obj_proto; - if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto)) - return NULL; - if (!obj_proto) - obj_proto = js_InitObjectClass(cx, obj); - if (!obj_proto) - return NULL; - - /* Function.prototype and the global object delegate to Object.prototype. */ - fun_proto->setProto(obj_proto); - if (!obj->getProto()) - obj->setProto(obj_proto); - - return fun_proto; -} - -namespace js { - -GlobalObject * -GlobalObject::create(JSContext *cx, Class *clasp) -{ - JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); - - JSObject *obj = NewNonFunction(cx, clasp, NULL, NULL); - if (!obj) - return NULL; - - GlobalObject *globalObj = obj->asGlobal(); - - globalObj->syncSpecialEquality(); - - /* Construct a regexp statics object for this global object. */ - JSObject *res = regexp_statics_construct(cx, globalObj); - if (!res) - return NULL; - globalObj->setSlot(REGEXP_STATICS, ObjectValue(*res)); - globalObj->setFlags(0); - return globalObj; -} - -bool -GlobalObject::initStandardClasses(JSContext *cx) -{ - /* Native objects get their reserved slots from birth. */ - JS_ASSERT(numSlots() >= JSSLOT_FREE(getClass())); - - JSAtomState &state = cx->runtime->atomState; - - /* Define a top-level property 'undefined' with the undefined value. */ - if (!defineProperty(cx, ATOM_TO_JSID(state.typeAtoms[JSTYPE_VOID]), UndefinedValue(), - PropertyStub, StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - if (!js_InitFunctionAndObjectClasses(cx, this)) - return false; - - /* Initialize the rest of the standard objects and functions. */ - return js_InitArrayClass(cx, this) && - js_InitBooleanClass(cx, this) && - js_InitExceptionClasses(cx, this) && - js_InitMathClass(cx, this) && - js_InitNumberClass(cx, this) && - js_InitJSONClass(cx, this) && - js_InitRegExpClass(cx, this) && - js_InitStringClass(cx, this) && - js_InitTypedArrayClasses(cx, this) && -#if JS_HAS_XML_SUPPORT - js_InitXMLClasses(cx, this) && -#endif -#if JS_HAS_GENERATORS - js_InitIteratorClasses(cx, this) && -#endif - js_InitDateClass(cx, this) && - js_InitProxyClass(cx, this); -} - -void -GlobalObject::clear(JSContext *cx) -{ - /* This can return false but that doesn't mean it failed. */ - unbrand(cx); - - for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++) - setSlot(key, UndefinedValue()); - - /* Clear regexp statics. */ - RegExpStatics::extractFrom(this)->clear(); - - /* Clear the CSP eval-is-allowed cache. */ - setSlot(EVAL_ALLOWED, UndefinedValue()); - - /* - * Mark global as cleared. If we try to execute any compile-and-go - * scripts from here on, we will throw. - */ - int32 flags = getSlot(FLAGS).toInt32(); - flags |= FLAGS_CLEARED; - setSlot(FLAGS, Int32Value(flags)); -} - -bool -GlobalObject::isEvalAllowed(JSContext *cx) -{ - Value &v = getSlotRef(EVAL_ALLOWED); - if (v.isUndefined()) { - JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); - - /* - * If there are callbacks, make sure that the CSP callback is installed - * and that it permits eval(), then cache the result. - */ - v.setBoolean((!callbacks || !callbacks->contentSecurityPolicyAllows) || - callbacks->contentSecurityPolicyAllows(cx)); - } - return !v.isFalse(); -} - -} // namespace js diff --git a/deps/mozjs/js/src/GlobalObject.h b/deps/mozjs/js/src/GlobalObject.h deleted file mode 100644 index 4108b0a65c0..00000000000 --- a/deps/mozjs/js/src/GlobalObject.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey global object code. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jeff Walden (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef GlobalObject_h___ -#define GlobalObject_h___ - -#include "jsfun.h" - -extern JSObject * -js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj); - -namespace js { - -/* - * Global object slots are reserved as follows: - * - * [0, JSProto_LIMIT) - * Stores the original value of the constructor for the corresponding - * JSProtoKey. - * [JSProto_LIMIT, 2 * JSProto_LIMIT) - * Stores the prototype, if any, for the constructor for the corresponding - * JSProtoKey offset from JSProto_LIMIT. - * [2 * JSProto_LIMIT, 3 * JSProto_LIMIT) - * Stores the current value of the global property named for the JSProtoKey - * for the corresponding JSProtoKey offset from 2 * JSProto_LIMIT. - * [3 * JSProto_LIMIT, RESERVED_SLOTS) - * Various one-off values: ES5 13.2.3's [[ThrowTypeError]], RegExp statics, - * the Namespace object for E4X's function::, the original eval for this - * global object (implementing |var eval = otherWindow.eval; eval(...)| as an - * indirect eval), a bit indicating whether this object has been cleared - * (see JS_ClearScope), and a cache for whether eval is allowed (per the - * global's Content Security Policy). - * - * The first two ranges are necessary to implement js::FindClassObject, - * js::FindClassPrototype, and spec language speaking in terms of "the original - * Array prototype object", or "as if by the expression new Array()" referring - * to the original Array constructor. The third range stores the (writable and - * even deletable) Object, Array, &c. properties (although a slot won't be used - * again if its property is deleted and readded). - */ -class GlobalObject : public ::JSObject { - /* - * Count of slots to store built-in constructors, prototypes, and initial - * visible properties for the constructors. - */ - static const uintN STANDARD_CLASS_SLOTS = JSProto_LIMIT * 3; - - /* One-off properties stored after slots for built-ins. */ - static const uintN THROWTYPEERROR = STANDARD_CLASS_SLOTS; - static const uintN REGEXP_STATICS = THROWTYPEERROR + 1; - static const uintN FUNCTION_NS = REGEXP_STATICS + 1; - static const uintN EVAL_ALLOWED = FUNCTION_NS + 1; - static const uintN EVAL = EVAL_ALLOWED + 1; - static const uintN FLAGS = EVAL + 1; - - /* Total reserved-slot count for global objects. */ - static const uintN RESERVED_SLOTS = FLAGS + 1; - - void staticAsserts() { - /* - * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, - * and we aren't going to expose GlobalObject, so just assert that the - * two values are synchronized. - */ - JS_STATIC_ASSERT(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS); - } - - static const int32 FLAGS_CLEARED = 0x1; - - void setFlags(int32 flags) { - setSlot(FLAGS, Int32Value(flags)); - } - - public: - static GlobalObject *create(JSContext *cx, Class *clasp); - - void setThrowTypeError(JSFunction *fun) { - Value &v = getSlotRef(THROWTYPEERROR); - // Our bootstrapping code is currently too convoluted to correctly and - // confidently assert this. - // JS_ASSERT(v.isUndefined()); - v.setObject(*fun); - } - - JSObject *getThrowTypeError() const { - return &getSlot(THROWTYPEERROR).toObject(); - } - - Value getRegExpStatics() const { - return getSlot(REGEXP_STATICS); - } - - void clear(JSContext *cx); - - bool isCleared() const { - return getSlot(FLAGS).toInt32() & FLAGS_CLEARED; - } - - bool isEvalAllowed(JSContext *cx); - - const Value &getOriginalEval() const { - return getSlot(EVAL); - } - - void setOriginalEval(JSObject *evalobj) { - Value &v = getSlotRef(EVAL); - // Our bootstrapping code is currently too convoluted to correctly and - // confidently assert this. - // JS_ASSERT(v.isUndefined()); - v.setObject(*evalobj); - } - - bool getFunctionNamespace(JSContext *cx, Value *vp); - - bool initStandardClasses(JSContext *cx); -}; - -} // namespace js - -js::GlobalObject * -JSObject::asGlobal() -{ - JS_ASSERT(isGlobal()); - return reinterpret_cast(this); -} - -#endif /* GlobalObject_h___ */ diff --git a/deps/mozjs/js/src/Makefile.in b/deps/mozjs/js/src/Makefile.in index 8602e446b19..600a94f376c 100644 --- a/deps/mozjs/js/src/Makefile.in +++ b/deps/mozjs/js/src/Makefile.in @@ -61,19 +61,10 @@ endif # FIXME: bug 530688 covers getting these working on Android ifneq ($(OS_ARCH),ANDROID) -ifdef ENABLE_TESTS -DIRS += jsapi-tests -endif +TEST_DIRS += jsapi-tests endif -ifdef ENABLE_TESTS -DIRS += tests -endif - -# XXX This should probably only happen if tests are enabled -ifneq ($(HOST_OS_ARCH),WINNT) -DIRS += build/unix -endif +TEST_DIRS += tests MODULE = js LIBRARY_NAME = mozjs @@ -82,36 +73,9 @@ GRE_MODULE = 1 LIBS = $(NSPR_LIBS) -ifdef GNU_CXX -ifdef INTEL_CXX -# icc gets special optimize flags -ifdef MOZ_PROFILE_GENERATE -MODULE_OPTIMIZE_FLAGS = -O0 -else -MODULE_OPTIMIZE_FLAGS = -O2 -ip -endif -else # not INTEL_CXX - -MODULE_OPTIMIZE_FLAGS = -O3 -fstrict-aliasing -fno-stack-protector - -# We normally want -fomit-frame-pointer, but we want an explicit -# -fno-omit-frame-pointer if we're using a sampling profiler. -ifndef MOZ_PROFILING -MODULE_OPTIMIZE_FLAGS += -fomit-frame-pointer -else -MODULE_OPTIMIZE_FLAGS += -fno-omit-frame-pointer -endif - -endif -else # not GNU_CXX -ifeq ($(OS_ARCH),SunOS) -MODULE_OPTIMIZE_FLAGS = -xO4 -endif ifeq ($(OS_ARCH),WINNT) -MODULE_OPTIMIZE_FLAGS = -O2 +NO_PROFILE_GUIDED_OPTIMIZE = 1 endif -endif - # JavaScript must be built shared, even for static builds, as it is used by # other modules which are always built shared. Failure to do so results in @@ -127,13 +91,19 @@ endif FORCE_STATIC_LIB = 1 DIST_INSTALL = 1 -VPATH = $(srcdir) +VPATH = \ + $(srcdir) \ + $(srcdir)/builtin \ + $(srcdir)/ds \ + $(srcdir)/frontend \ + $(srcdir)/gc \ + $(srcdir)/vm \ + $(NULL) CPPSRCS = \ jsalloc.cpp \ jsanalyze.cpp \ jsapi.cpp \ - jsarena.cpp \ jsarray.cpp \ jsatom.cpp \ jsbool.cpp \ @@ -144,20 +114,17 @@ CPPSRCS = \ jsdbgapi.cpp \ jsdhash.cpp \ jsdtoa.cpp \ - jsemit.cpp \ jsexn.cpp \ jsfriendapi.cpp \ jsfun.cpp \ jsgc.cpp \ jsgcmark.cpp \ - jsgcchunk.cpp \ jsgcstats.cpp \ - GlobalObject.cpp \ + jscrashreport.cpp \ jshash.cpp \ + jsinfer.cpp \ jsinterp.cpp \ - jsinvoke.cpp \ jsiter.cpp \ - jslock.cpp \ jslog2.cpp \ jsmath.cpp \ jsnativestack.cpp \ @@ -166,141 +133,134 @@ CPPSRCS = \ json.cpp \ jsonparser.cpp \ jsopcode.cpp \ - jsparse.cpp \ jsproxy.cpp \ jsprf.cpp \ jsprobes.cpp \ jspropertycache.cpp \ jspropertytree.cpp \ jsreflect.cpp \ - jsregexp.cpp \ - jsscan.cpp \ jsscope.cpp \ jsscript.cpp \ jsstr.cpp \ jstypedarray.cpp \ jsutil.cpp \ + jswatchpoint.cpp \ jsweakmap.cpp \ jswrapper.cpp \ jsxdrapi.cpp \ jsxml.cpp \ prmjtime.cpp \ sharkctl.cpp \ + ScopeObject.cpp \ + Debugger.cpp \ + GlobalObject.cpp \ Stack.cpp \ + String.cpp \ + BytecodeCompiler.cpp \ + BytecodeEmitter.cpp \ + FoldConstants.cpp \ + ParseMaps.cpp \ + ParseNode.cpp \ + Parser.cpp \ + SemanticAnalysis.cpp \ + TokenStream.cpp \ + LifoAlloc.cpp \ + MapObject.cpp \ + MemoryMetrics.cpp \ + RegExpObject.cpp \ + RegExpStatics.cpp \ + RegExp.cpp \ + Memory.cpp \ + Statistics.cpp \ + Unicode.cpp \ $(NULL) +# Changes to internal header files, used externally, massively slow down +# browser builds. Don't add new files here unless you know what you're +# doing! INSTALLED_HEADERS = \ js-config.h \ - jsautocfg.h \ - $(CURDIR)/jsautokw.h \ + jscpucfg.h \ js.msg \ jsalloc.h \ - jsanalyze.h \ jsapi.h \ - jsarray.h \ - jsarena.h \ jsatom.h \ - jsbit.h \ - jsbool.h \ + jsclass.h \ jsclist.h \ - jsclone.h \ - jscntxt.h \ jscompat.h \ - jsdate.h \ jsdbgapi.h \ jsdhash.h \ - jsdtoa.h \ - jsemit.h \ - jsfun.h \ jsfriendapi.h \ jsgc.h \ - jsgcmark.h \ jscell.h \ - jsgcchunk.h \ jsgcstats.h \ - jscompartment.h \ - GlobalObject.h \ jshash.h \ - jsinterp.h \ - jsinttypes.h \ - jsiter.h \ jslock.h \ - jslong.h \ - jsmath.h \ - jsnum.h \ - jsobj.h \ - jsobjinlines.h \ json.h \ - jsonparser.h \ - jsopcode.tbl \ - jsopcode.h \ - jsopcodeinlines.h \ - jsotypes.h \ - jsparse.h \ jsproxy.h \ jsprf.h \ - jsprobes.h \ - jspropertycache.h \ - jspropertycacheinlines.h \ - jspropertytree.h \ jsproto.tbl \ jsprvtd.h \ jspubtd.h \ - jsreflect.h \ - jsregexp.h \ - jsscan.h \ - jsscope.h \ - jsscript.h \ - jsscriptinlines.h \ - jsstaticcheck.h \ - jsstdint.h \ - jsstr.h \ - jstracer.h \ - jshotloop.h \ jstypedarray.h \ jstypes.h \ jsutil.h \ - jsvector.h \ - jstl.h \ - jshashtable.h \ jsversion.h \ - jsweakmap.h \ jswrapper.h \ jsxdrapi.h \ - jsxml.h \ jsval.h \ - jsvalue.h \ - prmjtime.h \ $(NULL) -############################################### -# BEGIN include sources for the vm subdirectory +###################################################### +# BEGIN exported headers that are only exported +# because of inclusion by an INSTALLED_HEADER # -VPATH += \ - $(srcdir)/vm \ - $(NULL) +EXPORTS_NAMESPACES += ds gc -EXPORTS_NAMESPACES = vm +EXPORTS_ds = \ + BitArray.h \ + $(NULL) -EXPORTS_vm = \ - Stack.h \ - StringObject.h \ +EXPORTS_gc = \ + Statistics.h \ + Barrier.h \ $(NULL) -############################################### -# BEGIN include sources for low-level code shared with Gecko +###################################################### +# BEGIN include exported headers from the JS engine +# +# Ultimately, after cleansing INSTALLED_HEADERS, +# these will be the ONLY headers exported by +# the js engine # VPATH += \ - $(srcdir)/../../mfbt \ + $(srcdir)/../public \ $(NULL) -EXPORTS_NAMESPACES += mozilla - -EXPORTS_mozilla = \ - Types.h \ - Util.h \ +EXPORTS_NAMESPACES += js + +# If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so +# that we ensure we don't over-expose our internal integer typedefs. Note that +# LegacyIntTypes.h below is deliberately exempted from this requirement. +EXPORTS_js = \ + HashTable.h \ + LegacyIntTypes.h \ + MemoryMetrics.h \ + TemplateLib.h \ + Utility.h \ + Vector.h \ $(NULL) +############################################### +# BEGIN include sources for low-level code shared with mfbt +# +VPATH += $(srcdir)/../../mfbt +include $(srcdir)/../../mfbt/exported_headers.mk + +############################################### +# BEGIN include sources for v8 API implementation +# + CPPSRCS += \ v8api/v8.cpp \ v8api/core.cpp \ @@ -330,51 +290,10 @@ EXPORTS_v8 = \ #ifdef ENABLE_TESTS DIRS += v8api/test #endif +# +# END include sources for v8 API implementation +############################################### -ifdef ENABLE_TRACEJIT -VPATH += \ - $(srcdir)/tracejit \ - $(srcdir)/nanojit \ - -INSTALLED_HEADERS += \ - jsbuiltins.h \ - Assembler.h \ - Allocator.h \ - CodeAlloc.h \ - Containers.h \ - LIR.h \ - LIRopcode.tbl \ - avmplus.h \ - Fragmento.h \ - Native.h \ - NativeCommon.h \ - Native$(NANOJIT_ARCH).h \ - njconfig.h \ - njcpudetect.h \ - RegAlloc.h \ - nanojit.h \ - VMPI.h \ - Writer.h \ - $(NULL) - -CPPSRCS += \ - jstracer.cpp \ - Assembler.cpp \ - Allocator.cpp \ - CodeAlloc.cpp \ - Containers.cpp \ - Fragmento.cpp \ - LIR.cpp \ - njconfig.cpp \ - RegAlloc.cpp \ - avmplus.cpp \ - Native$(NANOJIT_ARCH).cpp \ - jsbuiltins.cpp \ - VMPI.cpp \ - Writer.cpp \ - $(NULL) - -endif # ENABLE_TRACEJIT ifdef ENABLE_METHODJIT ############################################### @@ -387,7 +306,9 @@ CPPSRCS += MethodJIT.cpp \ Compiler.cpp \ FrameState.cpp \ FastArithmetic.cpp \ + FastBuiltins.cpp \ FastOps.cpp \ + LoopState.cpp \ StubCompiler.cpp \ MonoIC.cpp \ PolyIC.cpp \ @@ -396,7 +317,6 @@ CPPSRCS += MethodJIT.cpp \ Retcon.cpp \ TrampolineCompiler.cpp \ $(NULL) -# PICStubCompiler.cpp \ ifeq (86, $(findstring 86,$(TARGET_CPU))) ifeq (x86_64, $(TARGET_CPU)) @@ -425,6 +345,9 @@ endif ifeq (sparc, $(findstring sparc,$(TARGET_CPU))) ASFILES += TrampolineSparc.s endif +ifeq (mips, $(findstring mips,$(TARGET_CPU))) +CPPSRCS += TrampolineMIPS.cpp +endif # # END enclude sources for the method JIT ############################################# @@ -450,14 +373,16 @@ CPPSRCS += checks.cc \ # END enclude sources for V8 dtoa ############################################# -ifeq (,$(filter arm% %86 x86_64,$(TARGET_CPU))) +# For architectures without YARR JIT, PCRE is faster than the YARR +# interpreter (bug 684559). -VPATH += $(srcdir)/assembler \ - $(srcdir)/assembler/wtf \ - $(srcdir)/yarr/pcre \ +ifeq (,$(filter arm% sparc %86 x86_64 mips%,$(TARGET_CPU))) + +VPATH += $(srcdir)/yarr/pcre \ $(NULL) -CPPSRCS += pcre_compile.cpp \ +CPPSRCS += \ + pcre_compile.cpp \ pcre_exec.cpp \ pcre_tables.cpp \ pcre_xclass.cpp \ @@ -468,35 +393,48 @@ else ############################################### # BEGIN include sources for the Nitro assembler # + +ENABLE_YARR_JIT = 1 + VPATH += $(srcdir)/assembler \ $(srcdir)/assembler/wtf \ $(srcdir)/assembler/jit \ $(srcdir)/assembler/assembler \ $(srcdir)/methodjit \ $(srcdir)/yarr \ - $(srcdir)/yarr/yarr \ - $(srcdir)/yarr/pcre \ - $(srcdir)/yarr/wtf \ $(NONE) -CPPSRCS += Assertions.cpp \ - ExecutableAllocatorPosix.cpp \ - ExecutableAllocatorWin.cpp \ - ExecutableAllocatorOS2.cpp \ - ExecutableAllocator.cpp \ +CPPSRCS += ExecutableAllocator.cpp \ ARMAssembler.cpp \ - Logging.cpp \ MacroAssemblerARM.cpp \ MacroAssemblerX86Common.cpp \ - RegexCompiler.cpp \ - RegexJIT.cpp \ - pcre_compile.cpp \ - pcre_exec.cpp \ - pcre_tables.cpp \ - pcre_xclass.cpp \ - pcre_ucp_searchfuncs.cpp \ + PageBlock.cpp \ + YarrInterpreter.cpp \ + YarrJIT.cpp \ + YarrPattern.cpp \ + YarrSyntaxChecker.cpp \ $(NONE) +ifdef MOZ_DEBUG +CPPSRCS += Logging.cpp +endif + +ifneq (,$(filter-out OS2 WINNT,$(OS_ARCH))) +CPPSRCS += ExecutableAllocatorPosix.cpp \ + OSAllocatorPosix.cpp \ + $(NONE) +endif +ifeq ($(OS_ARCH),WINNT) +CPPSRCS += ExecutableAllocatorWin.cpp \ + OSAllocatorWin.cpp \ + $(NONE) +endif +ifeq ($(OS_ARCH),OS2) +CPPSRCS += ExecutableAllocatorOS2.cpp \ + OSAllocatorOS2.cpp \ + $(NONE) +endif + ifeq (86, $(findstring 86,$(TARGET_CPU))) ifeq (x86_64, $(TARGET_CPU)) #CPPSRCS += only_on_x86_64.cpp @@ -521,11 +459,20 @@ CPPSRCS += \ Library.cpp \ $(NULL) +ifdef MOZ_NATIVE_FFI +LOCAL_INCLUDES = $(MOZ_FFI_CFLAGS) +else +LOCAL_INCLUDES = -Ictypes/libffi/include +endif + LOCAL_INCLUDES += \ - -Ictypes/libffi/include \ -I. \ $(NULL) + +ifdef MOZ_NATIVE_FFI +EXTRA_DSO_LDOPTS += $(MOZ_FFI_LIBS) +else ifeq ($(OS_ARCH),OS2) SHARED_LIBRARY_LIBS += \ ctypes/libffi/.libs/ffi.a \ @@ -535,6 +482,7 @@ SHARED_LIBRARY_LIBS += \ ctypes/libffi/.libs/libffi.$(LIB_SUFFIX) \ $(NULL) endif +endif endif # JS_HAS_CTYPES @@ -557,10 +505,6 @@ else CPPSRCS += pm_stub.cpp endif -ifeq ($(OS_ARCH),WINNT) -INSTALLED_HEADERS += jscpucfg.h -endif - EXPORTS = $(INSTALLED_HEADERS) DASH_R = -r @@ -571,8 +515,24 @@ else SDK_LIBRARY = $(SHARED_LIBRARY) endif +# for compiler bug (http://support.microsoft.com/kb/982107) for MSVC x64 +ifdef _MSC_VER +ifneq (,$(filter 1400 1500,$(_MSC_VER))) +ifeq ($(OS_TEST),x86_64) +ASFILES += jswin64.asm +endif +endif +endif + include $(topsrcdir)/config/config.mk +ifeq (,$(MOZ_GLUE_PROGRAM_LDFLAGS)) +# When building standalone, we need to include mfbt sources, and to declare +# "exported" mfbt symbols on its behalf when we use its headers. +include $(srcdir)/../../mfbt/sources.mk +DEFINES += -DIMPL_MFBT +endif + EXTRA_DSO_LDOPTS += $(NSPR_LIBS) ifndef BUILD_OPT @@ -599,7 +559,6 @@ endif default:: ifneq (,$(CROSS_COMPILE)$(filter-out WINNT OS2,$(OS_ARCH))) -ifneq ($(OS_ARCH),SYMBIAN) # nsinstall doesn't get built until we enter config/ in the exports phase, # so we'll have to manually ensure it gets built here if we want to use # $(EXPORTS) @@ -609,11 +568,11 @@ $(PUBLIC) $(SDK_PUBLIC): config/nsinstall$(HOST_BIN_SUFFIX) config/nsinstall$(HOST_BIN_SUFFIX): $(srcdir)/config/nsinstall.c $(srcdir)/config/pathsub.c $(MAKE) -C config/ nsinstall$(HOST_BIN_SUFFIX) endif -endif include $(topsrcdir)/config/rules.mk ifdef JS_HAS_CTYPES +ifndef MOZ_NATIVE_FFI # Build libffi proper as part of the 'exports' target, so things get built # in the right order. export:: @@ -622,6 +581,7 @@ export:: distclean clean:: $(call SUBMAKE,$@,ctypes/libffi) endif +endif # Because the SpiderMonkey can be distributed and built independently # of the Mozilla source tree, it contains its own copies of many of @@ -671,7 +631,7 @@ endif # don't give different results. We skip the contents of objdirs using |find| # (it can't be done with %-expansion, because the files we want to skip aren't # in the vpath). -ALL_FILES=$(shell find $(srcdir) \( -name "*.cpp" -o -name "*.h" \) -not -path "*/dist/*") +ALL_FILES=$(shell find $(srcdir) \( -name "*.cpp" -o -name "*.h" \) ! -path "*/dist/*" ! -path "*/config/*") check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL_FILES)) # js_malloc and friends are only used by other memory managers, and should @@ -687,27 +647,22 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL # We desire these numbers to go down, not up. See "User guide to memory # management within SpiderMonkey" in jsutil.h. - $(srcdir)/config/check_source_count.py OffTheBooks:: 52 \ - "in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^ + $(srcdir)/config/check_source_count.py OffTheBooks:: 71 \ + "in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^ # This should go to zero, if possible. - $(srcdir)/config/check_source_count.py UnwantedForeground:: 34 \ + $(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \ "in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^ ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too. -check:: check-malloc-function-usage +#check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time. endif -JITFLAGS = ,m,j,mj,mjp,am,amj,amjp,amd +JITFLAGS = ,m,am,amd,n,mn,amn,amdn,mdn check-jit-test:: - $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \ - --no-slow --no-progress --tinderbox --jitflags=$(JITFLAGS) $(DIST)/bin/js$(BIN_SUFFIX) - -check-jit-test-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) check:: check-jit-test -check-valgrind:: check-jit-test-valgrind # jstests doesn't have a --jitflags option, so we need to loop, updating the # exit code (RC) after each invocation. @@ -740,9 +695,6 @@ check-jstests: ifeq ($(OS_ARCH),WINNT) ifdef ENABLE_METHODJIT -ifdef ENABLE_TRACEJIT -#check:: check-jstests -endif endif else ifndef HAVE_DTRACE @@ -758,31 +710,10 @@ distclean:: cat unallmakefiles | $(XARGS) rm -f $(RM) $(DIST_GARBAGE) -# our build system doesn't handle subdir srcs very gracefully today -export:: - $(MKDIR) -p nanojit - DEFINES += -DEXPORT_JS_API -# mfbt is always packed with us, so if we're building a shared object, -# we need to declare "exported" mfbt symbols on its behalf when we use -# its headers. -DEFINES += -DIMPL_MFBT - -# Some platforms that have stdint.h include it in system headers. So -# to reliably get limit macros defined, we'd always have to define the -# one below before including any header, but that's obscure and -# fragile, so we do it here. -DEFINES += -D__STDC_LIMIT_MACROS - INCLUDES += -I$(srcdir) -GARBAGE += jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg - -ifneq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) -TARGETS += jscpucfg$(HOST_BIN_SUFFIX) -endif - ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif @@ -792,14 +723,13 @@ DEFINES += -DJS_HAS_CTYPES DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" endif -ifdef JS_NO_THIN_LOCKS -DEFINES += -DJS_USE_ONLY_NSPR_LOCKS -endif - ifdef JS_VERSION DEFINES += -DJS_VERSION=$(JS_VERSION) endif +# We do not want to have obsolete NSPR functionality in threadsafe builds. +DEFINES += -DNO_NSPR_10_SUPPORT + ifneq ($(findstring -L,$(NSPR_LIBS)),) NSPR_STATIC_PATH = $(subst -L,,$(findstring -L,$(NSPR_LIBS))) else @@ -812,6 +742,11 @@ EXTRA_DSO_LDOPTS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib LIBS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib endif +ifdef MOZ_ETW +# This will get the ETW provider resources into the library mozjs.dll +RESFILE = ETWProvider.res +endif + # HP-UX does not require the extra linking of "-lm" ifeq (,$(filter HP-UX WINNT OS2,$(OS_ARCH))) EXTRA_LIBS += -lm @@ -829,17 +764,9 @@ endif # WINNT ifeq ($(OS_ARCH),FreeBSD) EXTRA_LIBS += -pthread endif -ifeq ($(OS_ARCH),IRIX) -ifdef USE_N32 -DASH_R += -n32 -endif -endif ifeq ($(OS_ARCH),Linux) EXTRA_LIBS += -ldl endif -ifeq ($(OS_ARCH),OSF1) -EXTRA_LIBS += -lc_r -endif # Silence warnings on AIX/HP-UX from non-GNU compilers ifndef GNU_CC ifeq ($(OS_ARCH),AIX) @@ -873,36 +800,6 @@ EXTRA_LIBS += -lposix4 -ldl -lnsl -lsocket endif endif -ifdef MOZ_MEMORY -ifeq ($(OS_ARCH),Darwin) -LDFLAGS += -ljemalloc -endif -endif - -ifdef SOLARIS_SUNPRO_CXX -ifeq ($(TARGET_CPU),sparc) -# Sun Studio SPARC doesn't work well with gcc inline asm, use lock_SunOS_sparc*.il -jslock.o: jslock.cpp Makefile.in lock_sparcv8plus.il lock_sparcv9.il - $(REPORT_BUILD) - @$(MAKE_DEPS_AUTO_CXX) -ifeq (sparcv9,$(findstring sparcv9,$(OS_TEST))) - $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv9.il $< -else - $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv8plus.il $< -endif # sparcv9 -endif # sparc -endif # SOLARIS_SUNPRO_CXX - -ifeq ($(OS_ARCH),IRIX) -ifndef GNU_CC -_COMPILE_CFLAGS = $(patsubst -O%,-O1,$(COMPILE_CFLAGS)) -jsapi.o jsxdrapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsreflect.o jsregexp.o jsparse.o jsopcode.o jsscript.o: %.o: %.cpp Makefile.in - $(REPORT_BUILD) - @$(MAKE_DEPS_AUTO_CXX) - $(CXX) -o $@ -c $(_COMPILE_CFLAGS) $< -endif -endif - # An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result. # This suppresses optimization for this single compilation unit. ifeq ($(OS_ARCH),AIX) @@ -916,45 +813,6 @@ jsdtoa.o: jsdtoa.cpp Makefile.in $(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< endif -export:: jsautocfg.h - -ifeq (,$(CROSS_COMPILE)$(GNU_CC)$(filter-out WINNT,$(OS_ARCH))) -jsautocfg.h: - $(TOUCH) $@ -else -jsautocfg.h: jscpucfg$(HOST_BIN_SUFFIX) - @$(RM) $@ jsautocfg.tmp - ./jscpucfg > jsautocfg.tmp - mv jsautocfg.tmp $@ -endif - -# jscpucfg is a strange target -# Needs to be built with the host compiler but needs to include -# the mdcpucfg for the target so it needs the appropriate target defines -ifdef HOST_NSPR_MDCPUCFG -HOST_CXX := $(HOST_CXX) -DMDCPUCFG=$(TARGET_NSPR_MDCPUCFG) -HOST_CXXFLAGS := $(patsubst -DXP_%,,$(HOST_CXXFLAGS)) -endif - -ifdef CROSS_COMPILE -# jscpucfg needs to know when it's supposed to produce a config for the target -JSCPUCFG_DEFINES = $(ACDEFINES) -endif - -ifeq ($(OS_ARCH),QNX) -ifneq ($(OS_TARGET),NTO) -# QNX's compiler apparently can't build a binary directly from a source file. -jscpucfg.o: jscpucfg.cpp Makefile.in - $(HOST_CXX) $(HOST_CXXFLAGS) -c $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) -o $@ $< - -jscpucfg: jscpucfg.o - $(HOST_CXX) $(HOST_CXXFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) -o $@ $< -endif -else -jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.cpp Makefile.in - $(HOST_CXX) $(HOST_CXXFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) $(HOST_OUTOPTION)$@ $< -endif - # Compute the linker flags that programs linking against SpiderMonkey should # pass to get SpiderMonkey and its dependencies, beyond just the -L and -l # for the SpiderMonkey library itself. @@ -1028,6 +886,22 @@ $(CURDIR)/jsautooplen.h: host_jsoplengen$(HOST_BIN_SUFFIX) # Force auto-header generation before compiling any source that may use them $(patsubst %.cc,%.$(OBJ_SUFFIX),$(CPPSRCS:%.cpp=%.$(OBJ_SUFFIX))): $(CURDIR)/jsautokw.h $(CURDIR)/jsautooplen.h +ifdef MOZ_ETW +ETWProvider.h ETWProvider.rc ETWProvider.mof: ETWProvider.man + $(MC) -um -mof $^ + +jsprobes.$(OBJ_SUFFIX): ETWProvider.h + +ETWProvider.res: ETWProvider.rc + $(RC) -r -i "$(SDKDIR)Include" $^ + +export:: ETWProvider.res + +install:: ETWProvider.mof ETWProvider.man + $(SYSINSTALL) $^ $(DESTDIR)$(bindir) + +endif + ifdef HAVE_DTRACE $(CURDIR)/javascript-trace.h: $(srcdir)/javascript-trace.d dtrace -h -s $(srcdir)/javascript-trace.d -o javascript-trace.h.in @@ -1040,41 +914,6 @@ $(CURDIR)/javascript-trace.h: $(srcdir)/javascript-trace.d $(addsuffix .$(OBJ_SUFFIX),jsprobes jsinterp jsobj): $(CURDIR)/javascript-trace.h endif -ifdef ENABLE_TRACEJIT -# Imacro compilation. -$(CURDIR)/imacros.c.out: $(srcdir)/imacro_asm.py $(srcdir)/imacros.jsasm jsopcode.tbl - $(PYTHON) $< $(srcdir)/imacros.jsasm $(CURDIR)/imacros.c.out -$(addsuffix .$(OBJ_SUFFIX),jstracer): $(CURDIR)/imacros.c.out - -# Code for importing the nanojit subtree from its repository. -NANOJIT_CENTRAL_REV=$(shell cat $(srcdir)/nanojit-import-rev) -NANOJIT_CENTRAL_REPO=http://hg.mozilla.org/projects/nanojit-central -NANOJIT_CENTRAL_LOCAL=$(CURDIR)/nanojit-central -CUR_REPO=$(srcdir)/../.. - -update-nanojit: - $(RM) -r $(NANOJIT_CENTRAL_LOCAL) import-splicemap import-revmap - hg clone $(NANOJIT_CENTRAL_REPO) $(NANOJIT_CENTRAL_LOCAL) - python $(srcdir)/find-child.py \ - --src=$(NANOJIT_CENTRAL_LOCAL) \ - --dst=$(CUR_REPO) \ - --start=$(NANOJIT_CENTRAL_REV) \ - --filemap=$(srcdir)/nanojit-import-filemap \ - >import-splicemap - hg convert --config convert.hg.saverev=True \ - --config convert.hg.startrev=`cut -d ' ' -f 1 import-splicemap` \ - --filemap=$(srcdir)/nanojit-import-filemap \ - --splicemap=import-splicemap \ - $(NANOJIT_CENTRAL_LOCAL) \ - $(CUR_REPO) \ - import-revmap - (cd $(srcdir) && hg up) - (cd $(NANOJIT_CENTRAL_LOCAL) && hg log -r tip --template "{node}\n") >$(srcdir)/nanojit-import-rev - (cd $(srcdir) && hg commit --message="Update nanojit-import-rev stamp." nanojit-import-rev) - -.PHONY: update-nanojit -endif - ############################################### # BEGIN kludges for the Nitro assembler # @@ -1082,7 +921,11 @@ endif # Needed to "configure" it correctly. Unfortunately these # flags wind up being applied to all code in js/src, not just # the code in js/src/assembler. -CXXFLAGS += -DUSE_SYSTEM_MALLOC=1 -DENABLE_ASSEMBLER=1 -DENABLE_JIT=1 +CXXFLAGS += -DUSE_SYSTEM_MALLOC=1 -DENABLE_ASSEMBLER=1 + +ifneq (,$(ENABLE_YARR_JIT)$(ENABLE_METHODJIT)) +CXXFLAGS += -DENABLE_JIT=1 +endif INCLUDES += -I$(srcdir)/assembler -I$(srcdir)/yarr @@ -1091,14 +934,19 @@ ifdef ENABLE_METHODJIT # sources a bit. TESTMAIN_OBJS = \ Assertions.$(OBJ_SUFFIX) \ - ExecutableAllocatorPosix.$(OBJ_SUFFIX) \ - ExecutableAllocatorWin.$(OBJ_SUFFIX) \ ExecutableAllocator.$(OBJ_SUFFIX) \ ARMAssembler.$(OBJ_SUFFIX) \ MacroAssemblerARM.$(OBJ_SUFFIX) \ TestMain.$(OBJ_SUFFIX) \ jsutil.$(OBJ_SUFFIX) \ jslog2.$(OBJ_SUFFIX) + +ifeq ($(OS_ARCH),WINNT) +TESTMAIN_OBJS += ExecutableAllocatorWin.$(OBJ_SUFFIX) +else +TESTMAIN_OBJS += ExecutableAllocatorPosix.$(OBJ_SUFFIX) +endif + TestMain$(HOST_BIN_SUFFIX): $(TESTMAIN_OBJS) $(CXX) -o TestMain$(HOST_BIN_SUFFIX) $(TESTMAIN_OBJS) endif diff --git a/deps/mozjs/js/src/Makefile.ref b/deps/mozjs/js/src/Makefile.ref deleted file mode 100644 index 2ff69cec606..00000000000 --- a/deps/mozjs/js/src/Makefile.ref +++ /dev/null @@ -1,468 +0,0 @@ -# -*- Mode: makefile -*- -# vim: ft=make -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Michael Ang -# Kevin Buhr -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# JSRef GNUmake makefile. -# -# Note: dependency rules are missing for some files (some -# .h, all .msg, etc.) Re-make clean if in doubt. -# - - -DEPTH = . - -include config.mk - -#NS_USE_NATIVE = 1 - -ifndef NANOJIT_ARCH -$(warning NANOJIT_ARCH not defined in config/$(OS_CONFIG).mk, JIT disabled) -else -ifdef DISABLE_JIT -$(warning disabling JIT per build specification) -else -ENABLE_JIT=1 -endif -endif - -ifdef ENABLE_JIT -DEFINES += -DJS_TRACER -DEFINES += -DFEATURE_NANOJIT -INCLUDES += -Inanojit -endif - -#ifndef BUILD_OPT -#DEFINES += -Ivprof -#endif - -# Look in OBJDIR to find jsautocfg.h, jsautokw.h, and js-config.h -INCLUDES += -I. -I$(OBJDIR) - -ifdef JS_THREADSAFE -DEFINES += -DJS_THREADSAFE -INCLUDES += -I$(DIST)/include/nspr -ifdef USE_MSVC -OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib -else -OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) -endif -endif - -ifdef JS_HAS_CTYPES -DEFINES += -DJS_HAS_CTYPES -INCLUDES += -I$(DIST)/include/nspr -ifdef USE_MSVC -OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib -else -OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) -endif -endif - -ifdef JS_NO_THIN_LOCKS -DEFINES += -DJS_USE_ONLY_NSPR_LOCKS -endif - -ifdef JS_GC_ZEAL -DEFINES += -DJS_GC_ZEAL -endif - -# -# XCFLAGS may be set in the environment or on the gmake command line -# -#CFLAGS += -DDEBUG -DDEBUG_brendan -DJS_ARENAMETER -DJS_HASHMETER -DJS_DUMP_PROPTREE_STATS -DJS_DUMP_SCOPE_METERS -DJS_SCOPE_DEPTH_METER -DJS_BASIC_STATS -CFLAGS += $(OS_CFLAGS) $(DEFINES) $(INCLUDES) $(XCFLAGS) - -LDFLAGS = $(XLDFLAGS) -LDFLAGS += $(OS_LDFLAGS) - -ifdef MOZ_SHARK -DEFINES += -DMOZ_SHARK -CFLAGS += -F/System/Library/PrivateFrameworks -LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD -endif -ifdef MOZ_CALLGRIND -DEFINES += -DMOZ_CALLGRIND -endif -ifdef MOZ_VTUNE -DEFINES += -DMOZ_VTUNE -CXXFLAGS += -IC:/Program\ Files/Intel/VTune/Analyzer/Include -OTHER_LIBS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib -endif - -ifndef NO_LIBM -LDFLAGS += -lm -endif - -# Prevent floating point errors caused by VC++ optimizations -ifeq ($(OS_ARCH),WINNT) -_MSC_VER = $(shell $(CXX) 2>&1 | sed -n 's/.*Compiler Version \([0-9]*\)\.\([0-9]*\).*/\1\2/p') -ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) -CFLAGS += -Op -else -CFLAGS += -fp:precise -endif -endif # WINNT - -# -# Server-related changes : -# -ifdef NES40 -DEFINES += -DNES40 -endif - -# -# Line editing support. -# Define JS_READLINE or JS_EDITLINE to enable line editing in the -# js command-line interpreter. -# -ifdef JS_READLINE -# For those platforms with the readline library installed. -DEFINES += -DEDITLINE -PROG_LIBS += -lreadline -ltermcap -else -ifdef JS_EDITLINE -# Use the editline library, built locally. -PREDIRS += editline -DEFINES += -DEDITLINE -PROG_LIBS += $(OBJDIR)/editline/libedit.a -endif -endif - -# For purify -PURE_CFLAGS = -DXP_UNIX $(OPTIMIZER) $(PURE_OS_CFLAGS) $(DEFINES) \ - $(INCLUDES) $(XCFLAGS) - -# -# JS file lists -# -JS_HFILES = \ - jsarray.h \ - jsatom.h \ - jsbool.h \ - jscntxt.h \ - jsdate.h \ - jsemit.h \ - jsexn.h \ - jsfun.h \ - jsgc.h \ - jsinterp.h \ - jsiter.h \ - jslibmath.h \ - jslock.h \ - jsmath.h \ - jsnum.h \ - jsobj.h \ - json.h \ - jsopcode.h \ - jsparse.h \ - jsarena.h \ - jsclist.h \ - jsdhash.h \ - jsdtoa.h \ - jshash.h \ - jslong.h \ - jstypes.h \ - jsprvtd.h \ - jspubtd.h \ - jsregexp.h \ - jsscan.h \ - jsscope.h \ - jsscript.h \ - jsstr.h \ - jsversion.h \ - jsxdrapi.h \ - jsxml.h \ - $(NULL) - -ifdef ENABLE_JIT -JS_HFILES += \ - jstracer.h \ - nanojit/Assembler.h \ - nanojit/LIR.h \ - nanojit/Native$(NANOJIT_ARCH).h \ - nanojit/avmplus.h \ - nanojit/vm_fops.h \ - nanojit/Fragmento.h \ - nanojit/Native.h \ - nanojit/RegAlloc.h \ - nanojit/nanojit.h \ - $(NULL) -endif - -ifndef BUILD_OPT -#JS_HFILES += \ -# vprof/vprof.h \ -# $(NULL) -endif - -API_HFILES = \ - jsapi.h \ - jsdbgapi.h \ - $(NULL) - -OTHER_HFILES = \ - jsbit.h \ - jscompat.h \ - jscpucfg.h \ - jsotypes.h \ - prmjtime.h \ - resource.h \ - jsopcode.tbl \ - jsproto.tbl \ - js.msg \ - jsshell.msg \ - jskeyword.tbl \ - $(NULL) - -ifndef PREBUILT_CPUCFG -OTHER_HFILES += $(OBJDIR)/jsautocfg.h -endif -OTHER_HFILES += $(OBJDIR)/jsautokw.h $(OBJDIR)/js-config.h - -HFILES = $(JS_HFILES) $(API_HFILES) $(OTHER_HFILES) - -JS_CPPFILES = \ - jsapi.cpp \ - jsarena.cpp \ - jsarray.cpp \ - jsatom.cpp \ - jsbool.cpp \ - jscntxt.cpp \ - jsdate.cpp \ - jsdbgapi.cpp \ - jsdhash.cpp \ - jsdtoa.cpp \ - jsemit.cpp \ - jsexn.cpp \ - jsfun.cpp \ - jsgc.cpp \ - jshash.cpp \ - jsinterp.cpp \ - jsinvoke.cpp \ - jsiter.cpp \ - jslock.cpp \ - jslog2.cpp \ - jslong.cpp \ - jsmath.cpp \ - jsnum.cpp \ - jsobj.cpp \ - json.cpp \ - jsopcode.cpp \ - jsparse.cpp \ - jsprf.cpp \ - jsregexp.cpp \ - jsscan.cpp \ - jsscope.cpp \ - jsscript.cpp \ - jsstr.cpp \ - jsutil.cpp \ - jsxdrapi.cpp \ - jsxml.cpp \ - prmjtime.cpp \ - $(NULL) - -ifdef ENABLE_JIT -JS_CPPFILES += \ - jsbuiltins.cpp \ - jstracer.cpp \ - nanojit/Assembler.cpp \ - nanojit/Fragmento.cpp \ - nanojit/LIR.cpp \ - nanojit/Native$(NANOJIT_ARCH).cpp \ - nanojit/RegAlloc.cpp \ - nanojit/avmplus.cpp \ - $(NULL) - -endif - -ifndef BUILD_OPT -#JS_CPPFILES += \ -# vprof/vprof.cpp \ -# $(NULL) -endif - -LIB_CPPFILES = $(JS_CPPFILES) -LIB_ASFILES := $(wildcard *_$(OS_ARCH).s) -PROG_CPPFILES = js.cpp - -ifdef USE_MSVC -LIBRARY = $(OBJDIR)/js32.lib -SHARED_LIBRARY = $(OBJDIR)/js32.dll -PROGRAM = $(OBJDIR)/js.exe -else -LIBRARY = $(OBJDIR)/libjs.a -SHARED_LIBRARY = $(OBJDIR)/libjs.$(SO_SUFFIX) -PROGRAM = $(OBJDIR)/js -endif - -include rules.mk - -MOZ_DEPTH = ../.. -include jsconfig.mk - -nsinstall-target: - cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR) - -# -# Automatic header generation -# - -AUTO_HEADERS = \ - $(OBJDIR)/jsautokw.h \ - $(OBJDIR)/jsautooplen.h \ - $(NULL) - -$(OBJDIR)/jsautokw.h: jskeyword.tbl - -$(OBJDIR)/jsautooplen.h: jsopcode.tbl - -GARBAGE += $(AUTO_HEADERS) -GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen$(HOST_BIN_SUFFIX)) - -ifdef USE_MSVC - -GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen.obj) - -$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(OPTIMIZER) $< - link.exe -out:"$(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX)" $(EXE_LINK_FLAGS) $(OBJDIR)/js$*gen.obj - $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@ -else - -GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen.d) -$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp - @$(MAKE_OBJDIR) - $(CXX) -o $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $(CFLAGS) $(OPTIMIZER) $(LDFLAGS) $< - $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@ - -endif - -# force creation of autoheaders before compiling any source that may use them -$(LIB_OBJS) : $(AUTO_HEADERS) - -# -# An installed header file describing configuration options that affect -# the API. -# - -# Avoid rebuilding unless js-config.h's contents actually change. The -# timestamp on js-config.h.stamp corresponds to the last time we -# checked that js-config.h was up to date. If the stamp changes but -# js-config.h does not, then make concludes that targets depending on -# js-config.h don't need to be rebuilt. The dummy '@true' rule here -# keeps make from concluding that js-config.h never changes. -$(OBJDIR)/js-config.h: $(OBJDIR)/js-config.h.stamp - @true - -js-config-switch=$(if $(value $($1)),-e 's/\#undef $1/\#define $1/') -$(OBJDIR)/js-config.h.stamp: js-config.h.in Makefile.ref - sed < $< > $(@:.stamp=.tmp) \ - $(call js-config-switch,JS_THREADSAFE) \ - $(call js-config-switch,JS_HAS_CTYPES) \ - $(call js-config-switch,JS_GC_ZEAL) \ - -e :dummy - if ! [ -f $(@:.stamp=) ] || ! cmp $(@:.stamp=.tmp) $(@:.stamp=); then \ - mv $(@:.stamp=.tmp) $(@:.stamp=); \ - fi - touch $@ - -GARBAGE += $(OBJDIR)/js-config.h $(OBJDIR)/js-config.h.stamp - -# Force creation of js-config.h before compiling any source that may use it. -$(LIB_OBJS) : $(OBJDIR)/js-config.h - -# -# JS shell executable -# - -ifdef USE_MSVC -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) - link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ -else -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) - $(CXX) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) $(OTHER_LIBS) \ - $(PROG_LIBS) -endif - -$(PROGRAM).pure: $(PROG_OBJS) $(LIBRARY) - purify $(PUREFLAGS) \ - $(CXX) -o $@ $(PURE_OS_CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) \ - $(OTHER_LIBS) $(PROG_LIBS) - -ifndef PREBUILT_CPUCFG -$(filter-out jscpucfg.h $(OBJDIR)/jsautocfg.h, $(HFILES)) $(CPPFILES): $(OBJDIR)/jsautocfg.h - -$(OBJDIR)/jsautocfg.h: $(OBJDIR)/jscpucfg - rm -f $@ - $(OBJDIR)/jscpucfg > $@ - -$(OBJDIR)/jscpucfg: $(OBJDIR)/jscpucfg.o - $(CXX) $(OS_LDFLAGS) -o $@ $(OBJDIR)/jscpucfg.o - -GARBAGE += $(OBJDIR)/jsautocfg.h $(OBJDIR)/jscpucfg \ - $(OBJDIR)/jscpucfg.o $(OBJDIR)/jscpucfg.d -endif - -# Automatic make dependencies files -DEPENDENCIES = $(CPPFILES:%.cpp=$(OBJDIR)/%.d) - -# -# Hardwire dependencies for some files -# -ifdef USE_MSVC -OBJ=obj -else -OBJ=o -endif - -$(OBJDIR)/jsinvoke.$(OBJ): jsinterp.h jsinterp.cpp -$(OBJDIR)/jsinvoke.obj : jsinterp.h jsinterp.cpp - --include $(DEPENDENCIES) - -TARNAME = jsref.tar -TARFILES = files `cat files` - -SUFFIXES: .i -%.i: %.cpp - $(CXX) -C -E $(CFLAGS) $< > $*.i diff --git a/deps/mozjs/js/src/MemoryMetrics.cpp b/deps/mozjs/js/src/MemoryMetrics.cpp new file mode 100644 index 00000000000..0a0366b60d8 --- /dev/null +++ b/deps/mozjs/js/src/MemoryMetrics.cpp @@ -0,0 +1,351 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is about:memory glue. + * + * The Initial Developer of the Original Code is + * Ms2ger . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "js/MemoryMetrics.h" + +#include "mozilla/Assertions.h" + +#include "jsapi.h" +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsgc.h" +#include "jsobj.h" +#include "jsscope.h" +#include "jsscript.h" + +#include "jsobjinlines.h" + +#ifdef JS_THREADSAFE + +namespace JS { + +using namespace js; + +static void +CompartmentStatsCallback(JSContext *cx, void *vdata, JSCompartment *compartment) +{ + // Append a new CompartmentStats to the vector. + RuntimeStats *rtStats = static_cast(vdata); + + // CollectRuntimeStats reserves enough space. + MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1)); + CompartmentStats &cStats = rtStats->compartmentStatsVector.back(); + cStats.init(rtStats->getNameCb(cx, compartment), rtStats->destroyNameCb); + rtStats->currCompartmentStats = &cStats; + + // Get the compartment-level numbers. +#ifdef JS_METHODJIT + cStats.mjitCode = compartment->sizeOfMjitCode(); +#endif + compartment->sizeOfTypeInferenceData(cx, &cStats.typeInferenceSizes, rtStats->mallocSizeOf); + cStats.shapesCompartmentTables = compartment->sizeOfShapeTable(rtStats->mallocSizeOf); +} + +static void +ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment) +{ +#ifdef JS_METHODJIT + size_t *n = static_cast(data); + *n += compartment->sizeOfMjitCode(); +#endif +} + +static void +ChunkCallback(JSContext *cx, void *vdata, gc::Chunk *chunk) +{ + // Nb: This function is only called for dirty chunks, which is why we + // increment gcHeapChunkDirtyDecommitted. + RuntimeStats *rtStats = static_cast(vdata); + for (size_t i = 0; i < gc::ArenasPerChunk; i++) + if (chunk->decommittedArenas.get(i)) + rtStats->gcHeapChunkDirtyDecommitted += gc::ArenaSize; +} + +static void +ArenaCallback(JSContext *cx, void *vdata, gc::Arena *arena, + JSGCTraceKind traceKind, size_t thingSize) +{ + RuntimeStats *rtStats = static_cast(vdata); + + rtStats->currCompartmentStats->gcHeapArenaHeaders += sizeof(gc::ArenaHeader); + size_t allocationSpace = arena->thingsSpan(thingSize); + rtStats->currCompartmentStats->gcHeapArenaPadding += + gc::ArenaSize - allocationSpace - sizeof(gc::ArenaHeader); + // We don't call the callback on unused things. So we compute the + // unused space like this: arenaUnused = maxArenaUnused - arenaUsed. + // We do this by setting arenaUnused to maxArenaUnused here, and then + // subtracting thingSize for every used cell, in CellCallback(). + rtStats->currCompartmentStats->gcHeapArenaUnused += allocationSpace; +} + +static void +CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, + size_t thingSize) +{ + RuntimeStats *rtStats = static_cast(vdata); + CompartmentStats *cStats = rtStats->currCompartmentStats; + switch (traceKind) { + case JSTRACE_OBJECT: + { + JSObject *obj = static_cast(thing); + if (obj->isFunction()) { + cStats->gcHeapObjectsFunction += thingSize; + } else { + cStats->gcHeapObjectsNonFunction += thingSize; + } + size_t slotsSize, elementsSize, miscSize; + obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, + &elementsSize, &miscSize); + cStats->objectSlots += slotsSize; + cStats->objectElements += elementsSize; + cStats->objectMisc += miscSize; + break; + } + case JSTRACE_STRING: + { + JSString *str = static_cast(thing); + cStats->gcHeapStrings += thingSize; + cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf); + break; + } + case JSTRACE_SHAPE: + { + Shape *shape = static_cast(thing); + size_t propTableSize, kidsSize; + shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize); + if (shape->inDictionary()) { + cStats->gcHeapShapesDict += thingSize; + cStats->shapesExtraDictTables += propTableSize; + JS_ASSERT(kidsSize == 0); + } else { + cStats->gcHeapShapesTree += thingSize; + cStats->shapesExtraTreeTables += propTableSize; + cStats->shapesExtraTreeShapeKids += kidsSize; + } + break; + } + case JSTRACE_BASE_SHAPE: + { + cStats->gcHeapShapesBase += thingSize; + break; + } + case JSTRACE_SCRIPT: + { + JSScript *script = static_cast(thing); + cStats->gcHeapScripts += thingSize; + cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf); +#ifdef JS_METHODJIT + cStats->mjitData += script->sizeOfJitScripts(rtStats->mallocSizeOf); +#endif + break; + } + case JSTRACE_TYPE_OBJECT: + { + types::TypeObject *obj = static_cast(thing); + cStats->gcHeapTypeObjects += thingSize; + obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf); + break; + } + case JSTRACE_XML: + { + cStats->gcHeapXML += thingSize; + break; + } + } + // Yes, this is a subtraction: see ArenaCallback() for details. + cStats->gcHeapArenaUnused -= thingSize; +} + +JS_PUBLIC_API(bool) +CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) +{ + JSContext *cx = JS_NewContext(rt, 0); + if (!cx) + return false; + + { + JSAutoRequest ar(cx); + + if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length())) + return false; + + rtStats->gcHeapChunkCleanDecommitted = + rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize; + rtStats->gcHeapChunkCleanUnused = + size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize - + rtStats->gcHeapChunkCleanDecommitted; + rtStats->gcHeapChunkTotal = + size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize; + + IterateCompartmentsArenasCells(cx, rtStats, CompartmentStatsCallback, + ArenaCallback, CellCallback); + IterateChunks(cx, rtStats, ChunkCallback); + + rtStats->runtimeObject = rtStats->mallocSizeOf(rt); + + rt->sizeOfExcludingThis(rtStats->mallocSizeOf, + &rtStats->runtimeNormal, + &rtStats->runtimeTemporary, + &rtStats->runtimeRegexpCode, + &rtStats->runtimeStackCommitted); + + // Nb: we use sizeOfExcludingThis() because atomState.atoms is within + // JSRuntime, and so counted when JSRuntime is counted. + rtStats->runtimeAtomsTable = + rt->atomState.atoms.sizeOfExcludingThis(rtStats->mallocSizeOf); + + JSContext *acx, *iter = NULL; + while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) + rtStats->runtimeContexts += acx->sizeOfIncludingThis(rtStats->mallocSizeOf); + } + + JS_DestroyContextNoGC(cx); + + // This is initialized to all bytes stored in used chunks, and then we + // subtract used space from it each time around the loop. + rtStats->gcHeapChunkDirtyUnused = rtStats->gcHeapChunkTotal - + rtStats->gcHeapChunkCleanUnused - + rtStats->gcHeapChunkCleanDecommitted - + rtStats->gcHeapChunkDirtyDecommitted; + + for (size_t index = 0; + index < rtStats->compartmentStatsVector.length(); + index++) { + CompartmentStats &cStats = rtStats->compartmentStatsVector[index]; + + size_t used = cStats.gcHeapArenaHeaders + + cStats.gcHeapArenaPadding + + cStats.gcHeapArenaUnused + + cStats.gcHeapObjectsNonFunction + + cStats.gcHeapObjectsFunction + + cStats.gcHeapStrings + + cStats.gcHeapShapesTree + + cStats.gcHeapShapesDict + + cStats.gcHeapShapesBase + + cStats.gcHeapScripts + + cStats.gcHeapTypeObjects + + cStats.gcHeapXML; + + rtStats->gcHeapChunkDirtyUnused -= used; + rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused; + rtStats->totalObjects += cStats.gcHeapObjectsNonFunction + + cStats.gcHeapObjectsFunction + + cStats.objectSlots + + cStats.objectElements + + cStats.objectMisc; + rtStats->totalShapes += cStats.gcHeapShapesTree + + cStats.gcHeapShapesDict + + cStats.gcHeapShapesBase + + cStats.shapesExtraTreeTables + + cStats.shapesExtraDictTables + + cStats.shapesCompartmentTables; + rtStats->totalScripts += cStats.gcHeapScripts + + cStats.scriptData; + rtStats->totalStrings += cStats.gcHeapStrings + + cStats.stringChars; +#ifdef JS_METHODJIT + rtStats->totalMjit += cStats.mjitCode + + cStats.mjitData; +#endif + rtStats->totalTypeInference += cStats.gcHeapTypeObjects + + cStats.typeInferenceSizes.objects + + cStats.typeInferenceSizes.scripts + + cStats.typeInferenceSizes.tables; + rtStats->totalAnalysisTemp += cStats.typeInferenceSizes.temporary; + } + + size_t numDirtyChunks = (rtStats->gcHeapChunkTotal - + rtStats->gcHeapChunkCleanUnused) / + gc::ChunkSize; + size_t perChunkAdmin = + sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk); + rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin; + rtStats->gcHeapChunkDirtyUnused -= rtStats->gcHeapChunkAdmin; + + // Why 10000x? 100x because it's a percentage, and another 100x + // because nsIMemoryReporter requires that for percentage amounts so + // they can be fractional. + rtStats->gcHeapUnusedPercentage = (rtStats->gcHeapChunkCleanUnused + + rtStats->gcHeapChunkDirtyUnused + + rtStats->gcHeapChunkCleanDecommitted + + rtStats->gcHeapChunkDirtyDecommitted + + rtStats->gcHeapArenaUnused) * 10000 / + rtStats->gcHeapChunkTotal; + + return true; +} + +JS_PUBLIC_API(bool) +GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount, + JSMallocSizeOfFun mallocSizeOf) +{ + JSContext *cx = JS_NewContext(rt, 0); + if (!cx) + return false; + + // explicit//gc-heap/* + *amount = int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * + gc::ChunkSize; + + { + JSAutoRequest ar(cx); + + // explicit//mjit-code + size_t n = 0; + IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback); + *amount += n; + + // explicit/runtime/regexp-code + // explicit/runtime/stack-committed + size_t regexpCode, stackCommitted; + rt->sizeOfExcludingThis(mallocSizeOf, + NULL, + NULL, + ®expCode, + &stackCommitted); + + *amount += regexpCode; + *amount += stackCommitted; + } + + JS_DestroyContextNoGC(cx); + + return true; +} + +} // namespace JS + +#endif // JS_THREADSAFE diff --git a/deps/mozjs/js/src/SpiderMonkey.rsp b/deps/mozjs/js/src/SpiderMonkey.rsp deleted file mode 100644 index eb93040c703..00000000000 --- a/deps/mozjs/js/src/SpiderMonkey.rsp +++ /dev/null @@ -1,3 +0,0 @@ -mozilla/js/src/* -mozilla/js/src/config/* -mozilla/js/src/macbuild/* diff --git a/deps/mozjs/js/src/aclocal.m4 b/deps/mozjs/js/src/aclocal.m4 index 548173055b3..b826fcbe471 100644 --- a/deps/mozjs/js/src/aclocal.m4 +++ b/deps/mozjs/js/src/aclocal.m4 @@ -8,7 +8,11 @@ builtin(include, build/autoconf/nspr.m4)dnl builtin(include, build/autoconf/altoptions.m4)dnl builtin(include, build/autoconf/moznbytetype.m4)dnl builtin(include, build/autoconf/mozprog.m4)dnl +builtin(include, build/autoconf/mozheader.m4)dnl +builtin(include, build/autoconf/mozcommonheader.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl +builtin(include, build/autoconf/gcc-pr49911.m4)dnl +builtin(include, build/autoconf/frameptr.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/deps/mozjs/js/src/analysis-tests/Makefile.in b/deps/mozjs/js/src/analysis-tests/Makefile.in deleted file mode 100644 index e70b0c3cbeb..00000000000 --- a/deps/mozjs/js/src/analysis-tests/Makefile.in +++ /dev/null @@ -1,114 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla 2. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation . -# -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Benjamin Smedberg (Author) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = .. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -# we will do compilations that create dependency files. -NEED_MDDEPDIR = 1 - -include $(DEPTH)/config/autoconf.mk - -REDGREEN_WARNING_TESTCASES = \ - green-callred.cpp \ - green-accessred.cpp \ - green-tored-badpath.cpp \ - misdeclared-red-funcptr.cpp \ - misdeclared-red-funcptr2.cpp \ - red-togreenptr.cpp \ - red-togreenptr-return.cpp \ - red-togreenptr-arg.cpp \ - red-togreenptr-field.cpp \ - red-togreenptr-initializer.cpp \ - red-togreenptr-initializer-struct.cpp \ - red-togreenptr-initializer-array.cpp \ - red-togreenptr-initializer-nested.cpp \ - red-togreenptr-initializer-cast.cpp \ - green-callredptr.cpp \ - $(NULL) - -REDGREEN_SUCCESS_TESTCASES = \ - red-callred.cpp \ - red-callgreen.cpp \ - red-accessred.cpp \ - green-tored.cpp \ - green-toredptr.cpp \ - $(NULL) - -STATIC_WARNING_TESTCASES = \ - $(REDGREEN_WARNING_TESTCASES) \ - $(NULL) - -STATIC_PASS_TESTCASES = \ - $(REDGREEN_SUCCESS_TESTCASES) \ - $(NULL) - -include $(topsrcdir)/config/rules.mk - -LOCAL_INCLUDES += -I$(srcdir)/.. -I.. - -check:: \ - $(STATIC_WARNING_TESTCASES:.cpp=.s-warn) \ - $(STATIC_PASS_TESTCASES:.cpp=.s-pass) \ - $(NULL) - -# We want to compile each file and invert the result to ensure that -# compilation failed. -%.s-warn: %.cpp $(GLOBAL_DEPS) $(DEHYDRA_SCRIPTS) - @printf "Compiling $($(*F).errlog 2>&1; then \ - printf "fail:\nerror: compilation of $($(*F).werrlog 2>&1; then \ - printf "ok.\n"; \ - else \ - printf "fail:\nerror: compilation of $($(*F).errlog 2>&1; then \ - printf "ok.\n"; \ - else \ - printf "fail:\nerror: compilation of $(redmember; -} diff --git a/deps/mozjs/js/src/analysis-tests/green-callred.cpp b/deps/mozjs/js/src/analysis-tests/green-callred.cpp deleted file mode 100644 index d9844030cce..00000000000 --- a/deps/mozjs/js/src/analysis-tests/green-callred.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "jstypes.h" - -#ifndef NS_STATIC_CHECKING -#error Running this without NS_STATIC_CHECKING is silly -#endif - -JS_REQUIRES_STACK void RedFunc(); - -void GreenFunc() -{ - RedFunc(); -} diff --git a/deps/mozjs/js/src/analysis-tests/green-callredptr.cpp b/deps/mozjs/js/src/analysis-tests/green-callredptr.cpp deleted file mode 100644 index 464e7f7e53d..00000000000 --- a/deps/mozjs/js/src/analysis-tests/green-callredptr.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "jstypes.h" - -typedef void (JS_REQUIRES_STACK *RedFuncPtr)(); - -void GreenFunc(RedFuncPtr f) -{ - f(); -} diff --git a/deps/mozjs/js/src/analysis-tests/green-tored-badpath.cpp b/deps/mozjs/js/src/analysis-tests/green-tored-badpath.cpp deleted file mode 100644 index 32d5a0eea4e..00000000000 --- a/deps/mozjs/js/src/analysis-tests/green-tored-badpath.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -JS_FORCES_STACK void TurnRedFunc(); - -void GreenPartlyRedFunc(int i) -{ - if (i) { - TurnRedFunc(); - } - - RedFunc(); -} diff --git a/deps/mozjs/js/src/analysis-tests/green-tored.cpp b/deps/mozjs/js/src/analysis-tests/green-tored.cpp deleted file mode 100644 index 78676c88db4..00000000000 --- a/deps/mozjs/js/src/analysis-tests/green-tored.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -JS_FORCES_STACK void TurnRedFunc(); - -void GreenToRedFunc() -{ - TurnRedFunc(); - - RedFunc(); -} diff --git a/deps/mozjs/js/src/analysis-tests/green-toredptr.cpp b/deps/mozjs/js/src/analysis-tests/green-toredptr.cpp deleted file mode 100644 index 78df373208b..00000000000 --- a/deps/mozjs/js/src/analysis-tests/green-toredptr.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "jstypes.h" - -void GreenFunc(); - -typedef void (JS_REQUIRES_STACK *RedFuncPtr)(); - -RedFuncPtr Test() -{ - // assigning a green function to a red function pointer is ok - RedFuncPtr p = GreenFunc; - return p; -} diff --git a/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr.cpp b/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr.cpp deleted file mode 100644 index 98053476908..00000000000 --- a/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "jstypes.h" - - -// JS_REQUIRES_STACK should come before the * -typedef void (* JS_REQUIRES_STACK RedFuncPtr)(); diff --git a/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr2.cpp b/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr2.cpp deleted file mode 100644 index 3ecc25e2b0b..00000000000 --- a/deps/mozjs/js/src/analysis-tests/misdeclared-red-funcptr2.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "jstypes.h" - - -// JS_REQUIRES_STACK should come before the * -typedef void (* RedFuncPtr)() JS_REQUIRES_STACK; diff --git a/deps/mozjs/js/src/analysis-tests/red-accessred.cpp b/deps/mozjs/js/src/analysis-tests/red-accessred.cpp deleted file mode 100644 index cf69fbf1af6..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-accessred.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "jstypes.h" - -struct Foo -{ - JS_REQUIRES_STACK void *redmember; -}; - -JS_REQUIRES_STACK void * RedFunc(Foo *f) -{ - return f->redmember; -} diff --git a/deps/mozjs/js/src/analysis-tests/red-callgreen.cpp b/deps/mozjs/js/src/analysis-tests/red-callgreen.cpp deleted file mode 100644 index 96270e836e1..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-callgreen.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "jstypes.h" - -void GreenFunc(); - -void JS_REQUIRES_STACK RedFunc() -{ - GreenFunc(); -} diff --git a/deps/mozjs/js/src/analysis-tests/red-callred.cpp b/deps/mozjs/js/src/analysis-tests/red-callred.cpp deleted file mode 100644 index 013bb128c18..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-callred.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc1(); - -JS_REQUIRES_STACK void RedFunc2() -{ - RedFunc1(); -} diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-arg.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-arg.cpp deleted file mode 100644 index b3bb17d74c5..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-arg.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "jstypes.h" - -typedef void (*GreenFuncPtr)(); -typedef void (JS_REQUIRES_STACK *RedFuncPtr)(); - -void TakesAsArgument(GreenFuncPtr g); - -void Test(RedFuncPtr p) -{ - TakesAsArgument(p); -} diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-field.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-field.cpp deleted file mode 100644 index f5c856b115e..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-field.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "jstypes.h" - -typedef void (*GreenFuncPtr)(); -typedef void (JS_REQUIRES_STACK *RedFuncPtr)(); - -struct Foo -{ - int i; - GreenFuncPtr p; -}; - -void Test(Foo *foo, RedFuncPtr p) -{ - foo->p = p; -} diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-array.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-array.cpp deleted file mode 100644 index 8e3ec186e7f..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-array.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "jstypes.h" - -void GreenFunc(); -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(); - -GreenFuncPtr fpa[2] = {GreenFunc, RedFunc}; diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-cast.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-cast.cpp deleted file mode 100644 index 8029605d650..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-cast.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(int); - -GreenFuncPtr gfpa = (GreenFuncPtr) RedFunc; diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-nested.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-nested.cpp deleted file mode 100644 index d4e4815dcf5..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-nested.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "jstypes.h" - -void GreenFunc(); -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(); - -struct S -{ - int i; - GreenFuncPtr p; -}; - -S sa[] = { - { 2, GreenFunc }, - { 1, RedFunc }, -}; - diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-struct.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-struct.cpp deleted file mode 100644 index c2d9a9e35f5..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer-struct.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(); - -struct GreenStruct -{ - GreenFuncPtr func; -}; - -GreenStruct gs = { RedFunc }; diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer.cpp deleted file mode 100644 index 063bb14d3ac..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-initializer.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(); - -GreenFuncPtr funcp = RedFunc; diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr-return.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr-return.cpp deleted file mode 100644 index ff91afd4795..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr-return.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "jstypes.h" - -typedef void (*GreenFuncPtr)(); -typedef void (JS_REQUIRES_STACK *RedFuncPtr)(); - -GreenFuncPtr Test(RedFuncPtr p) -{ - return p; -} diff --git a/deps/mozjs/js/src/analysis-tests/red-togreenptr.cpp b/deps/mozjs/js/src/analysis-tests/red-togreenptr.cpp deleted file mode 100644 index 65dfd4d9d03..00000000000 --- a/deps/mozjs/js/src/analysis-tests/red-togreenptr.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "jstypes.h" - -JS_REQUIRES_STACK void RedFunc(); - -typedef void (*GreenFuncPtr)(); - -GreenFuncPtr Test() -{ - return RedFunc; -} diff --git a/deps/mozjs/js/src/assembler/TestMain.cpp b/deps/mozjs/js/src/assembler/TestMain.cpp index 40f308597d3..dab36bb8122 100644 --- a/deps/mozjs/js/src/assembler/TestMain.cpp +++ b/deps/mozjs/js/src/assembler/TestMain.cpp @@ -110,7 +110,7 @@ void test1 ( void ) // constructor for LinkBuffer asks ep to allocate r-x memory, // then copies it there. - JSC::LinkBuffer patchBuffer(am, ep); + JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); // finalize JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); @@ -266,7 +266,7 @@ void test2 ( void ) // constructor for LinkBuffer asks ep to allocate r-x memory, // then copies it there. - JSC::LinkBuffer patchBuffer(am, ep); + JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); // finalize JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); @@ -453,7 +453,7 @@ void test3 ( void ) // constructor for LinkBuffer asks ep to allocate r-x memory, // then copies it there. - JSC::LinkBuffer patchBuffer(am, ep); + JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); // finalize JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); @@ -663,7 +663,7 @@ void test4 ( void ) // constructor for LinkBuffer asks ep to allocate r-x memory, // then copies it there. - JSC::LinkBuffer patchBuffer(am, ep); + JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); // now fix up any branches/calls //JSC::FunctionPtr target = JSC::FunctionPtr::FunctionPtr( &cube ); @@ -869,7 +869,7 @@ void test5 ( void ) // constructor for LinkBuffer asks ep to allocate r-x memory, // then copies it there. - JSC::LinkBuffer patchBuffer(am, ep); + JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); // now fix up any branches/calls JSC::FunctionPtr target = JSC::FunctionPtr::FunctionPtr( &cube ); diff --git a/deps/mozjs/js/src/assembler/assembler/ARMAssembler.cpp b/deps/mozjs/js/src/assembler/assembler/ARMAssembler.cpp index 57b92d3ff50..04584460820 100644 --- a/deps/mozjs/js/src/assembler/assembler/ARMAssembler.cpp +++ b/deps/mozjs/js/src/assembler/assembler/ARMAssembler.cpp @@ -92,6 +92,46 @@ ARMWord ARMAssembler::getOp2(ARMWord imm) return INVALID_IMM; } +ARMWord ARMAssembler::getOp2RegScale(RegisterID reg, ARMWord scale) +{ + // The field that this method constructs looks like this: + // [11:7] Shift immediate. + // [ 6:5] Shift type. Only LSL ("00") is used here. + // [ 4:4] 0. + // [ 3:0] The register to shift. + + ARMWord shift; // Shift field. This is log2(scale). + ARMWord lz; // Leading zeroes. + + // Calculate shift=log2(scale). +#if WTF_ARM_ARCH_AT_LEAST(5) + asm ( + " clz %[lz], %[scale]\n" + : [lz] "=r" (lz) + : [scale] "r" (scale) + : // No clobbers. + ); +#else + lz = 0; // Accumulate leading zeroes. + for (ARMWord s = 16; s > 0; s /= 2) { + ARMWord mask = 0xffffffff << (32-lz-s); + if ((scale & mask) == 0) { + lz += s; + } + } +#endif + if (lz >= 32) { + return INVALID_IMM; + } + shift = 31-lz; + // Check that scale was a power of 2. + if ((1<> 12) | getOp2RotLSL(12)); + } else { + sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | getOp2RotLSL(12)); + } + // Load using the lower bits of the offset. + mem_imm_off(isLoad, isSigned, size, posOffset, rt, + ARMRegisters::S0, (offset & 0xfff)); + } else { + // For even bigger offsets, load the entire offset into a register, then do an + // indexed load using the base register and the index register. + moveImm(offset, ARMRegisters::S0); + mem_reg_off(isLoad, isSigned, size, posOffset, rt, base, ARMRegisters::S0); + } +} void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset) { if (offset >= 0) { - if (offset <= 0xfff) + if (offset <= 0xfff) { + // LDR rd, [rb, +offset] dtr_u(isLoad, srcDst, base, offset); - else if (offset <= 0xfffff) { - add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); + } else if (offset <= 0xfffff) { + // Add upper bits of offset to the base, and store the result into the temp register. + add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | getOp2RotLSL(12)); + // Load using the lower bits of the register. dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); } else { + // For even bigger offsets, load the entire offset into a register, then do an + // indexed load using the base register and the index register. moveImm(offset, ARMRegisters::S0); dtr_ur(isLoad, srcDst, base, ARMRegisters::S0); } } else { - offset = -offset; - if (offset <= 0xfff) - dtr_d(isLoad, srcDst, base, offset); - else if (offset <= 0xfffff) { - sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); - dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); + // Negative offsets. + if (offset >= -0xfff) { + dtr_d(isLoad, srcDst, base, -offset); + } else if (offset >= -0xfffff) { + sub_r(ARMRegisters::S0, base, OP2_IMM | (-offset >> 12) | getOp2RotLSL(12)); + dtr_d(isLoad, srcDst, ARMRegisters::S0, (-offset & 0xfff)); } else { moveImm(offset, ARMRegisters::S0); - dtr_dr(isLoad, srcDst, base, ARMRegisters::S0); + dtr_ur(isLoad, srcDst, base, ARMRegisters::S0); } } } - -void ARMAssembler::dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset) +/* this is large, ugly and obsolete. dataTransferN is superior.*/ +void ARMAssembler::dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool isSigned) { if (offset >= 0) { - if (offset <= 0xfff) - dtrb_u(isLoad, srcDst, base, offset); - else if (offset <= 0xfffff) { - add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); - dtrb_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); + if (offset <= 0xfff) { + if (isSigned) + mem_imm_off(isLoad, true, 8, true, srcDst, base, offset); + else + dtrb_u(isLoad, srcDst, base, offset); + } else if (offset <= 0xfffff) { + add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | getOp2RotLSL(12)); + if (isSigned) + mem_imm_off(isLoad, true, 8, true, srcDst, ARMRegisters::S0, (offset & 0xfff)); + else + dtrb_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); } else { moveImm(offset, ARMRegisters::S0); - dtrb_ur(isLoad, srcDst, base, ARMRegisters::S0); + if (isSigned) + mem_reg_off(isLoad, true, 8, true, srcDst, base, ARMRegisters::S0); + else + dtrb_ur(isLoad, srcDst, base, ARMRegisters::S0); } } else { - offset = -offset; - if (offset <= 0xfff) - dtrb_d(isLoad, srcDst, base, offset); - else if (offset <= 0xfffff) { - sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); - dtrb_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff)); + if (offset >= -0xfff) { + if (isSigned) + mem_imm_off(isLoad, true, 8, false, srcDst, base, -offset); + else + dtrb_d(isLoad, srcDst, base, -offset); + } else if (offset >= -0xfffff) { + sub_r(ARMRegisters::S0, base, OP2_IMM | (-offset >> 12) | getOp2RotLSL(12)); + if (isSigned) + mem_imm_off(isLoad, true, 8, false, srcDst, ARMRegisters::S0, (-offset & 0xfff)); + else + dtrb_d(isLoad, srcDst, ARMRegisters::S0, (-offset & 0xfff)); + } else { moveImm(offset, ARMRegisters::S0); - dtrb_dr(isLoad, srcDst, base, ARMRegisters::S0); + if (isSigned) + mem_reg_off(isLoad, true, 8, true, srcDst, base, ARMRegisters::S0); + else + dtrb_ur(isLoad, srcDst, base, ARMRegisters::S0); + } } } +// rather X86-like, implements dest <- [base, index * shift + offset] void ARMAssembler::baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) { ARMWord op2; @@ -346,42 +448,158 @@ void ARMAssembler::baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterI dtr_ur(isLoad, srcDst, base, ARMRegisters::S0); } +void ARMAssembler::baseIndexTransferN(bool isLoad, bool isSigned, int size, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + ARMWord op2; + + ASSERT(scale >= 0 && scale <= 3); + op2 = lsl(index, scale); + + if (offset >= -0xfff && offset <= 0xfff) { + add_r(ARMRegisters::S0, base, op2); + bool posOffset = true; + if (offset < 0) { + posOffset = false; + offset = -offset; + } + mem_imm_off(isLoad, isSigned, size, posOffset, srcDst, ARMRegisters::S0, offset); + return; + } + ldr_un_imm(ARMRegisters::S0, offset); + add_r(ARMRegisters::S0, ARMRegisters::S0, op2); + mem_reg_off(isLoad, isSigned, size, true, srcDst, base, ARMRegisters::S0); +} + void ARMAssembler::doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset) { - if (offset & 0x3) { - if (offset <= 0x3ff && offset >= 0) { - fdtr_u(isLoad, srcDst, base, offset >> 2); + // VFP cannot directly access memory that is not four-byte-aligned, so + // special-case support will be required for such cases. However, we don't + // currently use any unaligned floating-point memory accesses and probably + // never will, so for now just assert that the offset is aligned. + // + // Note that we cannot assert that the base register is aligned, but in + // that case, an alignment fault will be raised at run-time. + ASSERT((offset & 0x3) == 0); + + // Try to use a single load/store instruction, or at least a simple address + // calculation. + if (offset >= 0) { + if (offset <= 0x3ff) { + fmem_imm_off(isLoad, true, true, srcDst, base, offset >> 2); return; } - if (offset <= 0x3ffff && offset >= 0) { - add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8)); - fdtr_u(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + if (offset <= 0x3ffff) { + add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | getOp2RotLSL(10)); + fmem_imm_off(isLoad, true, true, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); return; } - offset = -offset; - - if (offset <= 0x3ff && offset >= 0) { - fdtr_d(isLoad, srcDst, base, offset >> 2); + } else { + if (offset >= -0x3ff) { + fmem_imm_off(isLoad, true, false, srcDst, base, -offset >> 2); return; } - if (offset <= 0x3ffff && offset >= 0) { - sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8)); - fdtr_d(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + if (offset >= -0x3ffff) { + sub_r(ARMRegisters::S0, base, OP2_IMM | (-offset >> 10) | getOp2RotLSL(10)); + fmem_imm_off(isLoad, true, false, srcDst, ARMRegisters::S0, (-offset >> 2) & 0xff); return; } - offset = -offset; } - // TODO: This is broken in the case that offset is unaligned. VFP can never - // perform unaligned accesses, even from an unaligned register base. (NEON - // can, but VFP isn't NEON. It is not advisable to interleave a NEON load - // with VFP code, so the best solution here is probably to perform an - // unaligned integer load, then move the result into VFP using VMOV.) + // Slow case for long-range accesses. + ldr_un_imm(ARMRegisters::S0, offset); + add_r(ARMRegisters::S0, ARMRegisters::S0, base); + fmem_imm_off(isLoad, true, true, srcDst, ARMRegisters::S0, 0); +} + +void ARMAssembler::doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset, RegisterID index, int32_t scale) +{ + // This variant accesses memory at base+offset+(index*scale). VLDR and VSTR + // don't have such an addressing mode, so this access will require some + // arithmetic instructions. + + // This method does not support accesses that are not four-byte-aligned. ASSERT((offset & 0x3) == 0); + // Catch the trivial case, where scale is 0. + if (scale == 0) { + doubleTransfer(isLoad, srcDst, base, offset); + return; + } + + // Calculate the address, excluding the non-scaled offset. This is + // efficient for scale factors that are powers of two. + ARMWord op2_index = getOp2RegScale(index, scale); + if (op2_index == INVALID_IMM) { + // Use MUL to calculate scale factors that are not powers of two. + moveImm(scale, ARMRegisters::S0); + mul_r(ARMRegisters::S0, index, ARMRegisters::S0); + op2_index = ARMRegisters::S0; + } + + add_r(ARMRegisters::S0, base, op2_index); + doubleTransfer(isLoad, srcDst, ARMRegisters::S0, offset); +} + +void ARMAssembler::floatTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset) +{ + // Assert that the access is aligned, as in doubleTransfer. + ASSERT((offset & 0x3) == 0); + + // Try to use a single load/store instruction, or at least a simple address + // calculation. + if (offset >= 0) { + if (offset <= 0x3ff) { + fmem_imm_off(isLoad, false, true, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff) { + add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | getOp2RotLSL(10)); + fmem_imm_off(isLoad, false, true, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + } else { + if (offset >= -0x3ff) { + fmem_imm_off(isLoad, false, false, srcDst, base, -offset >> 2); + return; + } + if (offset >= -0x3ffff) { + sub_r(ARMRegisters::S0, base, OP2_IMM | (-offset >> 10) | getOp2RotLSL(10)); + fmem_imm_off(isLoad, false, false, srcDst, ARMRegisters::S0, (-offset >> 2) & 0xff); + return; + } + } + + // Slow case for long-range accesses. ldr_un_imm(ARMRegisters::S0, offset); add_r(ARMRegisters::S0, ARMRegisters::S0, base); - fdtr_u(isLoad, srcDst, ARMRegisters::S0, 0); + fmem_imm_off(isLoad, false, true, srcDst, ARMRegisters::S0, 0); +} + +void ARMAssembler::baseIndexFloatTransfer(bool isLoad, bool isDouble, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + ARMWord op2; + + ASSERT(scale >= 0 && scale <= 3); + op2 = lsl(index, scale); + // vldr/vstr have a more restricted range than your standard ldr. + // they get 8 bits that are implicitly shifted left by 2. + if (offset >= -(0xff<<2) && offset <= (0xff<<2)) { + add_r(ARMRegisters::S0, base, op2); + bool posOffset = true; + if (offset < 0) { + posOffset = false; + offset = -offset; + } + fmem_imm_off(isLoad, isDouble, posOffset, srcDst, ARMRegisters::S0, offset >> 2); + return; + } + + ldr_un_imm(ARMRegisters::S0, offset); + // vldr/vstr do not allow register-indexed operations, so we get to do this *manually*. + add_r(ARMRegisters::S0, ARMRegisters::S0, op2); + add_r(ARMRegisters::S0, ARMRegisters::S0, base); + + fmem_imm_off(isLoad, isDouble, true, srcDst, ARMRegisters::S0, 0); } // Fix up the offsets and literal-pool loads in buffer. The buffer should @@ -413,14 +631,14 @@ inline void ARMAssembler::fixUpOffsets(void * buffer) } } -void* ARMAssembler::executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp) +void* ARMAssembler::executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind) { // 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.executableAllocAndCopy(allocator, poolp); + void * data = m_buffer.executableAllocAndCopy(allocator, poolp, kind); if (data) fixUpOffsets(data); return data; diff --git a/deps/mozjs/js/src/assembler/assembler/ARMAssembler.h b/deps/mozjs/js/src/assembler/assembler/ARMAssembler.h index d45a1e41043..cd0d54077bc 100644 --- a/deps/mozjs/js/src/assembler/assembler/ARMAssembler.h +++ b/deps/mozjs/js/src/assembler/assembler/ARMAssembler.h @@ -127,6 +127,14 @@ namespace JSC { d31 } FPRegisterID; + inline FPRegisterID floatShadow(FPRegisterID s) + { + return (FPRegisterID)(s*2); + } + inline FPRegisterID doubleShadow(FPRegisterID d) + { + return (FPRegisterID)(d / 2); + } } // namespace ARMRegisters class ARMAssembler { @@ -190,6 +198,7 @@ namespace JSC { FCPYD = 0x0eb00b40, FADDD = 0x0e300b00, FNEGD = 0x0eb10b40, + FABSD = 0x0eb00bc0, FDIVD = 0x0e800b00, FSUBD = 0x0e300b40, FMULD = 0x0e200b00, @@ -198,20 +207,14 @@ namespace JSC { DTR = 0x05000000, LDRH = 0x00100090, STRH = 0x00000090, + DTRH = 0x00000090, STMDB = 0x09200000, LDMIA = 0x08b00000, - FDTR = 0x0d000b00, B = 0x0a000000, BL = 0x0b000000 #if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__) ,BX = 0x012fff10 #endif - ,FMSR = 0x0e000a10, - FMRS = 0x0e100a10, - FSITOD = 0x0eb80bc0, - FTOSID = 0x0ebd0b40, - FTOSIZD = 0x0ebd0bc0, - FMSTAT = 0x0ef1fa10 #if WTF_ARM_ARCH_VERSION >= 5 ,CLZ = 0x016f0f10, BKPT = 0xe1200070, @@ -234,7 +237,12 @@ namespace JSC { DT_WB = (1 << 21), // This flag is inlcuded in LDR and STR DT_PRE = (1 << 24), + // This flag makes switches the instruction between {ld,st}r{,s}h and {ld,st}rsb HDT_UH = (1 << 5), + // if this bit is on, we do a register offset, if it is off, we do an immediate offest. + HDT_IMM = (1 << 22), + // Differentiates half word load/store between signed and unsigned (also enables signed byte loads.) + HDT_S = (1 << 6), DT_LOAD = (1 << 20) }; @@ -318,6 +326,19 @@ namespace JSC { m_buffer.putInt(op | RN(rn) | RD(rd) | op2); } + // Work out the pre-shifted constant necessary to encode the specified + // logical shift left for op2 immediates. Only even shifts can be + // applied. + // + // Input validity is asserted in debug builds. + ARMWord getOp2RotLSL(int lsl) + { + ASSERT((lsl >= 0) && (lsl <= 24)); + ASSERT(!(lsl % 2)); + + return (-(lsl/2) & 0xf) << 8; + } + void and_r(int rd, int rn, ARMWord op2, Condition cc = AL) { spewInsWithOp2("and", cc, rd, rn, op2); @@ -523,76 +544,7 @@ namespace JSC { m_buffer.putInt(static_cast(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); } - void fcpyd_r(int dd, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov.f64", nameFpRegD(dd), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FCPYD, dd, dd, dm); - } - - void faddd_r(int dd, int dn, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FADDD, dd, dn, dm); - } - - void fnegd_r(int dd, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm)); - m_buffer.putInt(static_cast(cc) | FNEGD | DD(dd) | DM(dm)); - } - - void fdivd_r(int dd, int dn, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FDIVD, dd, dn, dm); - } - - void fsubd_r(int dd, int dn, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FSUBD, dd, dn, dm); - } - - void fmuld_r(int dd, int dn, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FMULD, dd, dn, dm); - } - - void fcmpd_r(int dd, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s\n", MAYBE_PAD, "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FCMPD, dd, 0, dm); - } - - void fsqrtd_r(int dd, int dm, Condition cc = AL) - { - js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, %s\n", MAYBE_PAD, "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm)); - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FSQRTD, dd, 0, dm); - } - + // pc relative loads (useful for loading from pools). void ldr_imm(int rd, ARMWord imm, Condition cc = AL) { char mnemonic[16]; @@ -611,6 +563,84 @@ namespace JSC { m_buffer.putIntWithConstantInt(static_cast(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm); } + void mem_imm_off(bool isLoad, bool isSigned, int size, bool posOffset, + int rd, int rb, ARMWord offset, Condition cc = AL) + { + ASSERT(size == 8 || size == 16 || size == 32); + char const * mnemonic_act = (isLoad) ? ("ld") : ("st"); + char const * mnemonic_sign = (isSigned) ? ("s") : (""); + + char const * mnemonic_size = NULL; + switch (size / 8) { + case 1: + mnemonic_size = "b"; + break; + case 2: + mnemonic_size = "h"; + break; + case 4: + mnemonic_size = ""; + break; + } + char const * off_sign = (posOffset) ? ("+") : ("-"); + js::JaegerSpew(js::JSpew_Insns, + IPFX "%sr%s%s %s, [%s, #%s%u]\n", + MAYBE_PAD, mnemonic_act, mnemonic_sign, mnemonic_size, + nameGpReg(rd), nameGpReg(rb), off_sign, offset); + if (size == 32 || (size == 8 && !isSigned)) { + /* All (the one) 32 bit ops and the unsigned 8 bit ops use the original encoding.*/ + emitInst(static_cast(cc) | DTR | + (isLoad ? DT_LOAD : 0) | + (size == 8 ? DT_BYTE : 0) | + (posOffset ? DT_UP : 0), rd, rb, offset); + } else { + /* All 16 bit ops and 8 bit unsigned use the newer encoding.*/ + emitInst(static_cast(cc) | DTRH | HDT_IMM | DT_PRE | + (isLoad ? DT_LOAD : 0) | + (size == 16 ? HDT_UH : 0) | + (isSigned ? HDT_S : 0) | + (posOffset ? DT_UP : 0), rd, rb, offset); + } + } + + void mem_reg_off(bool isLoad, bool isSigned, int size, bool posOffset, int rd, int rb, int rm, Condition cc = AL) + { + char const * mnemonic_act = (isLoad) ? ("ld") : ("st"); + char const * mnemonic_sign = (isSigned) ? ("s") : (""); + + char const * mnemonic_size = NULL; + switch (size / 8) { + case 1: + mnemonic_size = "b"; + break; + case 2: + mnemonic_size = "h"; + break; + case 4: + mnemonic_size = ""; + break; + } + char const * off_sign = (posOffset) ? ("+") : ("-"); + js::JaegerSpew(js::JSpew_Insns, + IPFX "%sr%s%s %s, [%s, #%s%s]\n", MAYBE_PAD, mnemonic_act, mnemonic_sign, mnemonic_size, + nameGpReg(rd), nameGpReg(rb), off_sign, nameGpReg(rm)); + if (size == 32 || (size == 8 && !isSigned)) { + /* All (the one) 32 bit ops and the signed 8 bit ops use the original encoding.*/ + emitInst(static_cast(cc) | DTR | + (isLoad ? DT_LOAD : 0) | + (size == 8 ? DT_BYTE : 0) | + (posOffset ? DT_UP : 0) | + OP2_OFSREG, rd, rb, rm); + } else { + /* All 16 bit ops and 8 bit unsigned use the newer encoding.*/ + emitInst(static_cast(cc) | DTRH | DT_PRE | + (isLoad ? DT_LOAD : 0) | + (size == 16 ? HDT_UH : 0) | + (isSigned ? HDT_S : 0) | + (posOffset ? DT_UP : 0), rd, rb, rm); + } + } + // Data transfers like this: // LDR rd, [rb, +offset] // STR rd, [rb, +offset] @@ -666,6 +696,17 @@ namespace JSC { emitInst(static_cast(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset); } + // Data transfers like this: + // LDRSB rd, [rb, +offset] + // STRSB rd, [rb, +offset] + void dtrsb_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL) + { + char const * mnemonic = (isLoad) ? ("ldrsb") : ("strb"); + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset); + emitInst(static_cast(cc) | DTRH | HDT_S | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset); + } + // Data transfers like this: // LDRB rd, [rb, +rm] // STRB rd, [rb, +rm] @@ -678,8 +719,8 @@ namespace JSC { } // Data transfers like this: - // LDRB rd, [rb, -offset] - // STRB rd, [rb, -offset] + // LDRB rd, [rb, #-offset] + // STRB rd, [rb, #-offset] void dtrb_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL) { char const * mnemonic = (isLoad) ? ("ldrb") : ("strb"); @@ -688,6 +729,18 @@ namespace JSC { emitInst(static_cast(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0), rd, rb, offset); } + // Data transfers like this: + // LDRSB rd, [rb, #-offset] + // STRSB rd, [rb, #-offset] + void dtrsb_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL) + { + ASSERT(isLoad); /*can only do signed byte loads, not stores*/ + char const * mnemonic = (isLoad) ? ("ldrsb") : ("strb"); + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset); + emitInst(static_cast(cc) | DTRH | HDT_S | (isLoad ? DT_LOAD : 0), rd, rb, offset); + } + // Data transfers like this: // LDRB rd, [rb, -rm] // STRB rd, [rb, -rm] @@ -720,29 +773,25 @@ namespace JSC { emitInst(static_cast(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, offset); } - void strh_r(int rb, int rm, int rd, Condition cc = AL) + void ldrsh_d(int rd, int rb, ARMWord offset, Condition cc = AL) { js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm)); - emitInst(static_cast(cc) | STRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm); - } + IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "ldrsh", nameGpReg(rd), nameGpReg(rb), offset); + emitInst(static_cast(cc) | LDRH | HDT_UH | HDT_S | DT_PRE, rd, rb, offset); + } - void fdtr_u(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL) + void ldrsh_u(int rd, int rb, ARMWord offset, Condition cc = AL) { - char const * ins = isLoad ? "vldr.f64" : "vstr.f64"; js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, ins, nameFpRegD(dd), nameGpReg(rn), offset); - ASSERT(offset <= 0xff); - emitInst(static_cast(cc) | FDTR | DT_UP | (isLoad ? DT_LOAD : 0), dd, rn, offset); + IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "ldrsh", nameGpReg(rd), nameGpReg(rb), offset); + emitInst(static_cast(cc) | LDRH | HDT_UH | HDT_S | DT_UP | DT_PRE, rd, rb, offset); } - void fdtr_d(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL) + void strh_r(int rb, int rm, int rd, Condition cc = AL) { - char const * ins = isLoad ? "vldr.f64" : "vstr.f64"; js::JaegerSpew(js::JSpew_Insns, - IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, ins, nameFpRegD(dd), nameGpReg(rn), offset); - ASSERT(offset <= 0xff); - emitInst(static_cast(cc) | FDTR | (isLoad ? DT_LOAD : 0), dd, rn, offset); + IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm)); + emitInst(static_cast(cc) | STRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm); } void push_r(int reg, Condition cc = AL) @@ -771,47 +820,7 @@ namespace JSC { dtr_u(true, reg, ARMRegisters::sp, 0, cc); } - void fmsr_r(int dd, int rn, Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FMSR, rn, dd, 0); - } - void fmrs_r(int rd, int dn, Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FMRS, rd, dn, 0); - } - - void fsitod_r(int dd, int dm, Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FSITOD, dd, 0, dm); - } - - void ftosid_r(int fd, int dm, Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FTOSID, fd, 0, dm); - } - - void ftosizd_r(int fd, int dm, Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - emitInst(static_cast(cc) | FTOSIZD, fd, 0, dm); - } - - void fmstat(Condition cc = AL) - { - // TODO: emitInst doesn't work for VFP instructions, though it - // seems to work for current usage. - m_buffer.putInt(static_cast(cc) | FMSTAT); - } #if WTF_ARM_ARCH_VERSION >= 5 void clz_r(int rd, int rm, Condition cc = AL) @@ -847,7 +856,7 @@ namespace JSC { JmpSrc blx(int rm, Condition cc = AL) { -#if WTF_ARM_ARCH_AT_LEAST(5) +#if WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= 5 int s = m_buffer.uncheckedSize(); js::JaegerSpew( js::JSpew_Insns, @@ -932,12 +941,10 @@ namespace JSC { return m_buffer.sizeOfConstantPool(); } -#ifdef DEBUG - void allowPoolFlush(bool allowFlush) + int flushCount() { - m_buffer.allowPoolFlush(allowFlush); + return m_buffer.flushCount(); } -#endif JmpDst label() { @@ -972,7 +979,7 @@ namespace JSC { return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); } - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp); + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind); void executableCopy(void* buffer); void fixUpOffsets(void* buffer); @@ -980,7 +987,7 @@ namespace JSC { static ARMWord* getLdrImmAddress(ARMWord* insn) { -#if WTF_ARM_ARCH_AT_LEAST(5) +#if WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= 5 // Check for call if ((*insn & 0x0f7f0000) != 0x051f0000) { // Must be BLX @@ -1037,7 +1044,7 @@ namespace JSC { static void repatchInt32(void* from, int32_t to) { js::JaegerSpew(js::JSpew_Insns, - ISPFX "##repatchInt32 ((%p)) holds ((%p))\n", + ISPFX "##repatchInt32 ((%p)) holds ((%#x))\n", from, to); patchPointerInternal(reinterpret_cast(from), reinterpret_cast(to)); @@ -1171,6 +1178,10 @@ namespace JSC { static ARMWord getOp2(ARMWord imm); + // Get an operand-2 field for immediate-shifted-registers in arithmetic + // instructions. + static ARMWord getOp2RegScale(RegisterID reg, ARMWord scale); + #if WTF_ARM_ARCH_VERSION >= 7 static ARMWord getImm16Op2(ARMWord imm) { @@ -1193,11 +1204,18 @@ namespace JSC { } // Memory load/store helpers + void dataTransferN(bool isLoad, bool isSigned, int size, RegisterID srcDst, RegisterID base, int32_t offset); void dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset); - void dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset); + void dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool isSigned); + void baseIndexTransferN(bool isLoad, bool isSigned, int size, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); void baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); void doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset); + void doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset, RegisterID index, int32_t scale); + + void floatTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset); + /**/ + void baseIndexFloatTransfer(bool isLoad, bool isDouble, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); // Constant pool hnadlers @@ -1208,7 +1226,7 @@ namespace JSC { return AL | B | (offset & BRANCH_MASK); } - private: + // pretty-printing functions static char const * nameGpReg(int reg) { ASSERT(reg <= 16); @@ -1238,6 +1256,22 @@ namespace JSC { }; return names[reg]; } + static char const * nameFpRegS(int reg) + { + ASSERT(reg <= 31); + ASSERT(reg >= 0); + static char const * names[] = { + "s0", "s1", "s2", "s3", + "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", + "s12", "s13", "s14", "s15", + "s16", "s17", "s18", "s19", + "s20", "s21", "s22", "s23", + "s24", "s25", "s26", "s27", + "s28", "s29", "s30", "s31" + }; + return names[reg]; + } static char const * nameCC(Condition cc) { @@ -1259,6 +1293,7 @@ namespace JSC { return names[ccIndex]; } + private: // Decodes operand 2 immediate values (for debug output and assertions). inline uint32_t decOp2Imm(uint32_t op2) { @@ -1400,6 +1435,25 @@ namespace JSC { return ((reg << 1) & 0x20) | (reg & 0xf); } + ARMWord SD(int reg) + { + ASSERT(reg <= ARMRegisters::d31); + // Endoded as bits [15:12,22]. + return ((reg << 11) | (reg << 22)) & 0x0040f000; + } + + ARMWord SM(int reg) + { + ASSERT(reg <= ARMRegisters::d31); + // Encoded as bits [5,3:0]. + return ((reg << 5) & 0x20) | ((reg >> 1) & 0xf); + } + ARMWord SN(int reg) + { + ASSERT(reg <= ARMRegisters::d31); + // Encoded as bits [19:16,7]. + return ((reg << 15) & 0xf0000) | ((reg & 1) << 7); + } static ARMWord getConditionalField(ARMWord i) { return i & 0xf0000000; @@ -1409,6 +1463,268 @@ namespace JSC { ARMBuffer m_buffer; Jumps m_jumps; + public: + // VFP instruction constants + enum { + VFP_DATA = 0x0E000A00, + VFP_EXT = 0x0C000A00, + VFP_XFER = 0x0E000A08, + VFP_DXFER = 0x0C400A00, + + VFP_DBL = (1<<8), + + /*integer conversions*/ + VFP_ICVT = 0x00B80040, + VFP_FPCVT = 0x00B700C0, + + VFP_DTR = 0x01000000, + VFP_MOV = 0x00000010, + + FMSR = 0x0e000a10, + FMRS = 0x0e100a10, + FSITOD = 0x0eb80bc0, + FUITOD = 0x0eb80b40, + FTOSID = 0x0ebd0b40, + FTOSIZD = 0x0ebd0bc0, + FMSTAT = 0x0ef1fa10, + FDTR = 0x0d000b00 + + }; + enum RegType { + SIntReg32, + UIntReg32, + FloatReg32, + FloatReg64 + }; + + const char * nameType(RegType t) + { + const char * const name[4] = + {"S32", "U32", "F32", "F64"}; + return name[t]; + } + + const char * nameTypedReg(RegType t, int reg) + { + switch(t) { + case SIntReg32: + case UIntReg32: + return nameGpReg(reg); + case FloatReg32: + return nameFpRegS(reg); + case FloatReg64: + return nameFpRegD(reg); + } + return ""; + } + + bool isFloatType(RegType rt) + { + if (rt == FloatReg32 || rt == FloatReg64) + return true; + return false; + } + + bool isIntType(RegType rt) + { + if (rt == FloatReg32 || rt == FloatReg64) + return false; + return true; + } + + // ******************************************************************** + // * VFP Code: + //********************************************************************* + /* this is horrible. There needs to be some sane way of distinguishing D from S from R*/ + void emitVFPInst(ARMWord op, ARMWord rd, ARMWord rn, ARMWord op2) + { + m_buffer.putInt(op | rn | rd | op2); + } + + // NOTE: offset is the actual value that is going to be encoded. It is the offset in words, NOT in bytes. + void fmem_imm_off(bool isLoad, bool isDouble, bool isUp, int dest, int rn, ARMWord offset, Condition cc = AL) + { + char const * ins = isLoad ? "vldr.f" : "vstr.f"; + js::JaegerSpew(js::JSpew_Insns, + IPFX "%s%d %s, [%s, #%s%u]\n", MAYBE_PAD, + ins, (isDouble ? 64 : 32), (isDouble ? nameFpRegD(dest) : nameFpRegS(dest)), + nameGpReg(rn), (isUp ? "+" : "-"), offset); + ASSERT(offset <= 0xff); + emitVFPInst(static_cast(cc) | + VFP_EXT | VFP_DTR | + (isDouble ? VFP_DBL : 0) | + (isUp ? DT_UP : 0) | + (isLoad ? DT_LOAD : 0), isDouble ? DD(dest) : SD(dest), RN(rn), offset); + + } + + // WARNING: even for an int -> float conversion, all registers used + // are VFP registers. + void vcvt(RegType srcType, RegType dstType, int src, int dest, Condition cc = AL) + { + ASSERT(srcType != dstType); + ASSERT(isFloatType(srcType) || isFloatType(dstType)); + + js::JaegerSpew(js::JSpew_Insns, + IPFX "vcvt.%s.%-15s, %s,%s\n", MAYBE_PAD, + nameType(dstType), nameType(srcType), + nameTypedReg(dstType,dest), nameTypedReg(srcType,src)); + + if (isFloatType(srcType) && isFloatType (dstType)) { + // doing a float -> float conversion + bool dblToFloat = srcType == FloatReg64; + emitVFPInst(static_cast(cc) | VFP_DATA | VFP_FPCVT | + (dblToFloat ? VFP_DBL : 0), + dblToFloat ? SD(dest) : DD(dest), + dblToFloat ? DM(src) : SM(src), 0); + } else { + JS_NOT_REACHED("Other conversions did not seem useful on 2011/08/04"); + } + } + + // does r2:r1 -> dn, dn -> r2:r1, r2:r1 -> s2:s1, etc. + void vmov64 (bool fromFP, bool isDbl, int r1, int r2, int rFP, Condition cc = AL) + { + if (fromFP) { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov", + nameGpReg(r1), nameGpReg(r2), nameFpRegD(rFP)); + } else { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov", + nameFpRegD(rFP), nameGpReg(r1), nameGpReg(r2)); + } + emitVFPInst(static_cast(cc) | VFP_DXFER | VFP_MOV | + (fromFP ? DT_LOAD : 0) | + (isDbl ? VFP_DBL : 0), RD(r1), RN(r2), isDbl ? DM(rFP) : SM(rFP)); + } + + void fcpyd_r(int dd, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s\n", MAYBE_PAD, "vmov.f64", + nameFpRegD(dd), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FCPYD, DD(dd), DM(dm), 0); + } + + void faddd_r(int dd, int dn, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FADDD, DD(dd), DN(dn), DM(dm)); + } + + void fnegd_r(int dd, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm)); + m_buffer.putInt(static_cast(cc) | FNEGD | DD(dd) | DM(dm)); + } + + void fdivd_r(int dd, int dn, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FDIVD, DD(dd), DN(dn), DM(dm)); + } + + void fsubd_r(int dd, int dn, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FSUBD, DD(dd), DN(dn), DM(dm)); + } + + void fabsd_r(int dd, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s\n", MAYBE_PAD, "fabsd", nameFpRegD(dd), nameFpRegD(dm)); + m_buffer.putInt(static_cast(cc) | FABSD | DD(dd) | DM(dm)); + } + + void fmuld_r(int dd, int dn, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FMULD, DD(dd), DN(dn), DM(dm)); + } + + void fcmpd_r(int dd, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s\n", MAYBE_PAD, "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FCMPD, DD(dd), 0, DM(dm)); + } + + void fsqrtd_r(int dd, int dm, Condition cc = AL) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "%-15s %s, %s\n", MAYBE_PAD, "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm)); + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FSQRTD, DD(dd), 0, DM(dm)); + } + + void fmsr_r(int dd, int rn, Condition cc = AL) + { + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FMSR, RD(rn), SN(dd), 0); + } + + void fmrs_r(int rd, int dn, Condition cc = AL) + { + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FMRS, RD(rd), SN(dn), 0); + } + + // dear god :( + // integer registers ar encoded the same as single registers + void fsitod_r(int dd, int dm, Condition cc = AL) + { + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FSITOD, DD(dd), 0, SM(dm)); + } + + void fuitod_r(int dd, int dm, Condition cc = AL) + { + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + emitVFPInst(static_cast(cc) | FUITOD, DD(dd), 0, SM(dm)); + } + + void ftosid_r(int fd, int dm, Condition cc = AL) + { + // TODO: I don't actually know what the encoding is i'm guessing SD and DM. + emitVFPInst(static_cast(cc) | FTOSID, SD(fd), 0, DM(dm)); + } + + void ftosizd_r(int fd, int dm, Condition cc = AL) + { + // TODO: I don't actually know what the encoding is i'm guessing SD and DM. + emitVFPInst(static_cast(cc) | FTOSIZD, SD(fd), 0, DM(dm)); + } + + void fmstat(Condition cc = AL) + { + // TODO: emitInst doesn't work for VFP instructions, though it + // seems to work for current usage. + m_buffer.putInt(static_cast(cc) | FMSTAT); + } }; } // namespace JSC diff --git a/deps/mozjs/js/src/assembler/assembler/ARMv7Assembler.h b/deps/mozjs/js/src/assembler/assembler/ARMv7Assembler.h index e1478ba2c91..54c866f2139 100644 --- a/deps/mozjs/js/src/assembler/assembler/ARMv7Assembler.h +++ b/deps/mozjs/js/src/assembler/assembler/ARMv7Assembler.h @@ -1558,9 +1558,9 @@ class ARMv7Assembler { return m_formatter.size(); } - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) { - void* copy = m_formatter.executableAllocAndCopy(allocator, poolp); + void* copy = m_formatter.executableAllocAndCopy(allocator, poolp, kind); unsigned jumpCount = m_jumpsToLink.size(); for (unsigned i = 0; i < jumpCount; ++i) { @@ -1909,8 +1909,8 @@ class ARMv7Assembler { 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* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) { - return m_buffer.executableAllocAndCopy(allocator, poolp); + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) { + return m_buffer.executableAllocAndCopy(allocator, poolp, kind); } bool oom() const { return m_buffer.oom(); } diff --git a/deps/mozjs/js/src/assembler/assembler/AbstractMacroAssembler.h b/deps/mozjs/js/src/assembler/assembler/AbstractMacroAssembler.h index 260380acd30..42276896a11 100644 --- a/deps/mozjs/js/src/assembler/assembler/AbstractMacroAssembler.h +++ b/deps/mozjs/js/src/assembler/assembler/AbstractMacroAssembler.h @@ -33,7 +33,6 @@ #include "assembler/wtf/Platform.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" #include "assembler/assembler/CodeLocation.h" -#include "jsstdint.h" #if ENABLE_ASSEMBLER @@ -166,13 +165,13 @@ class AbstractMacroAssembler { void* m_ptr; }; - // ImmPtr: + // TrustedImmPtr: // // A pointer sized immediate operand to an instruction - this is wrapped // in a class requiring explicit construction in order to differentiate // from pointers used as absolute addresses to memory operations - struct ImmPtr { - explicit ImmPtr(const void* value) + struct TrustedImmPtr { + explicit TrustedImmPtr(const void* value) : m_value(value) { } @@ -185,14 +184,21 @@ class AbstractMacroAssembler { const void* m_value; }; - // Imm32: + struct ImmPtr : public TrustedImmPtr { + explicit ImmPtr(const void* value) + : TrustedImmPtr(value) + { + } + }; + + // TrustedImm32: // // A 32bit immediate operand to an instruction - this is wrapped in a // class requiring explicit construction in order to prevent RegisterIDs // (which are implemented as an enum) from accidentally being passed as // immediate values. - struct Imm32 { - explicit Imm32(int32_t value) + struct TrustedImm32 { + explicit TrustedImm32(int32_t value) : m_value(value) #if WTF_CPU_ARM || WTF_CPU_MIPS , m_isPointer(false) @@ -201,7 +207,7 @@ class AbstractMacroAssembler { } #if !WTF_CPU_X86_64 - explicit Imm32(ImmPtr ptr) + explicit TrustedImm32(TrustedImmPtr ptr) : m_value(ptr.asIntptr()) #if WTF_CPU_ARM || WTF_CPU_MIPS , m_isPointer(true) @@ -223,13 +229,27 @@ class AbstractMacroAssembler { #endif }; + + struct Imm32 : public TrustedImm32 { + explicit Imm32(int32_t value) + : TrustedImm32(value) + { + } +#if !WTF_CPU_X86_64 + explicit Imm32(TrustedImmPtr ptr) + : TrustedImm32(ptr) + { + } +#endif + }; + struct ImmDouble { union { struct { #if WTF_CPU_BIG_ENDIAN || WTF_CPU_MIDDLE_ENDIAN - uint32 msb, lsb; + uint32_t msb, lsb; #else - uint32 lsb, msb; + uint32_t lsb, msb; #endif } s; uint64_t u64; @@ -241,7 +261,6 @@ class AbstractMacroAssembler { } }; - // Section 2: MacroAssembler code buffer handles // // The following types are used to reference items in the code buffer @@ -273,7 +292,7 @@ class AbstractMacroAssembler { bool isUsed() const { return m_label.isUsed(); } void used() { m_label.used(); } - bool isValid() const { return m_label.isValid(); } + bool isSet() const { return m_label.isValid(); } private: JmpDst m_label; }; @@ -296,6 +315,8 @@ class AbstractMacroAssembler { { } + bool isSet() const { return m_label.isValid(); } + private: JmpDst m_label; }; @@ -411,6 +432,20 @@ class AbstractMacroAssembler { public: typedef js::Vector JumpVector; + JumpList() {} + + JumpList(const JumpList &other) + { + m_jumps.append(other.m_jumps); + } + + JumpList &operator=(const JumpList &other) + { + m_jumps.clear(); + m_jumps.append(other.m_jumps); + return *this; + } + void link(AbstractMacroAssembler* masm) { size_t size = m_jumps.length(); @@ -432,17 +467,22 @@ class AbstractMacroAssembler { m_jumps.append(jump); } - void append(JumpList& other) + void append(const JumpList& other) { m_jumps.append(other.m_jumps.begin(), other.m_jumps.length()); } + void clear() + { + m_jumps.clear(); + } + bool empty() { return !m_jumps.length(); } - const JumpVector& jumps() { return m_jumps; } + const JumpVector& jumps() const { return m_jumps; } private: JumpVector m_jumps; @@ -523,6 +563,11 @@ class AbstractMacroAssembler { return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); } + ptrdiff_t differenceBetween(DataLabelPtr from, Label to) + { + return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); + } + ptrdiff_t differenceBetween(DataLabelPtr from, Jump to) { return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp); diff --git a/deps/mozjs/js/src/assembler/assembler/AssemblerBuffer.h b/deps/mozjs/js/src/assembler/assembler/AssemblerBuffer.h index e94d89bf08e..daeec22823d 100644 --- a/deps/mozjs/js/src/assembler/assembler/AssemblerBuffer.h +++ b/deps/mozjs/js/src/assembler/assembler/AssemblerBuffer.h @@ -37,7 +37,6 @@ #include #include "assembler/jit/ExecutableAllocator.h" #include "assembler/wtf/Assertions.h" -#include "jsstdint.h" namespace JSC { @@ -137,14 +136,14 @@ namespace JSC { * The user must check for a NULL return value, which means * no code was generated, or there was an OOM. */ - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) { if (m_oom || m_size == 0) { *poolp = NULL; return 0; } - void* result = allocator->alloc(m_size, poolp); + void* result = allocator->alloc(m_size, poolp, kind); if (!result) { *poolp = NULL; return 0; @@ -191,7 +190,11 @@ namespace JSC { void grow(int extraCapacity = 0) { - int newCapacity = m_capacity + m_capacity / 2 + extraCapacity; + /* + * If |extraCapacity| is zero (as it almost always is) this is an + * allocator-friendly doubling growth strategy. + */ + int newCapacity = m_capacity + m_capacity + extraCapacity; char* newBuffer; if (m_buffer == m_inlineBuffer) { diff --git a/deps/mozjs/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h b/deps/mozjs/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h index ea9d6a0f258..0d87bbeb09e 100644 --- a/deps/mozjs/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h +++ b/deps/mozjs/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h @@ -106,9 +106,7 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { , m_numConsts(0) , m_maxDistance(maxPoolSize) , m_lastConstDelta(0) -#ifdef DEBUG - , m_allowFlush(true) -#endif + , m_flushCount(0) { m_pool = static_cast(malloc(maxPoolSize)); m_mask = static_cast(malloc(maxPoolSize / sizeof(uint32_t))); @@ -194,10 +192,10 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { return AssemblerBuffer::size(); } - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) { flushConstantPool(false); - return AssemblerBuffer::executableAllocAndCopy(allocator, poolp); + return AssemblerBuffer::executableAllocAndCopy(allocator, poolp, kind); } void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) @@ -241,19 +239,16 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { return m_numConsts; } -#ifdef DEBUG - // Guard constant pool flushes to ensure that they don't occur during - // regions where offsets into the code have to be maintained (such as PICs). - void allowPoolFlush(bool allowFlush) + int flushCount() { - m_allowFlush = allowFlush; + return m_flushCount; } -#endif private: void correctDeltas(int insnSize) { m_maxDistance -= insnSize; + ASSERT(m_maxDistance >= 0); m_lastConstDelta -= insnSize; if (m_lastConstDelta < 0) m_lastConstDelta = 0; @@ -264,6 +259,7 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { correctDeltas(insnSize); m_maxDistance -= m_lastConstDelta; + ASSERT(m_maxDistance >= 0); m_lastConstDelta = constSize; } @@ -271,9 +267,9 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { { js::JaegerSpew(js::JSpew_Insns, " -- FLUSHING CONSTANT POOL WITH %d CONSTANTS --\n", m_numConsts); - ASSERT(m_allowFlush); if (m_numConsts == 0) return; + m_flushCount++; int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); if (alignPool) @@ -304,12 +300,16 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { m_loadOffsets.clear(); m_numConsts = 0; m_maxDistance = maxPoolSize; + ASSERT(m_maxDistance >= 0); + } void flushIfNoSpaceFor(int nextInsnSize) { - if (m_numConsts == 0) + if (m_numConsts == 0) { + m_maxDistance = maxPoolSize; return; + } int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) flushConstantPool(); @@ -317,8 +317,10 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) { - if (m_numConsts == 0) + if (m_numConsts == 0) { + m_maxDistance = maxPoolSize; return; + } if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) flushConstantPool(); @@ -331,10 +333,7 @@ class AssemblerBufferWithConstantPool: public AssemblerBuffer { int m_numConsts; int m_maxDistance; int m_lastConstDelta; - -#ifdef DEBUG - bool m_allowFlush; -#endif + int m_flushCount; }; } // namespace JSC diff --git a/deps/mozjs/js/src/assembler/assembler/LinkBuffer.h b/deps/mozjs/js/src/assembler/assembler/LinkBuffer.h index eac984c8d46..35913c87aaf 100644 --- a/deps/mozjs/js/src/assembler/assembler/LinkBuffer.h +++ b/deps/mozjs/js/src/assembler/assembler/LinkBuffer.h @@ -64,8 +64,9 @@ class LinkBuffer { public: // 'ok' should be checked after this constructor is called; it's false if OOM occurred. LinkBuffer(MacroAssembler* masm, ExecutableAllocator* executableAllocator, - ExecutablePool** poolp, bool* ok) + ExecutablePool** poolp, bool* ok, CodeKind codeKind) { + m_codeKind = codeKind; m_code = executableAllocAndCopy(*masm, executableAllocator, poolp); m_executablePool = *poolp; m_size = masm->m_assembler.size(); // must come after call to executableAllocAndCopy()! @@ -75,20 +76,22 @@ class LinkBuffer { *ok = !!m_code; } - LinkBuffer() + LinkBuffer(CodeKind kind) : m_executablePool(NULL) , m_code(NULL) , m_size(0) + , m_codeKind(kind) #ifndef NDEBUG , m_completed(false) #endif { } - LinkBuffer(uint8* ncode, size_t size) + LinkBuffer(uint8_t* ncode, size_t size, CodeKind kind) : m_executablePool(NULL) , m_code(ncode) , m_size(size) + , m_codeKind(kind) #ifndef NDEBUG , m_completed(false) #endif @@ -200,7 +203,7 @@ class LinkBuffer { void *executableAllocAndCopy(MacroAssembler &masm, ExecutableAllocator *allocator, ExecutablePool **poolp) { - return masm.m_assembler.executableAllocAndCopy(allocator, poolp); + return masm.m_assembler.executableAllocAndCopy(allocator, poolp, m_codeKind); } void performFinalization() @@ -217,6 +220,7 @@ class LinkBuffer { ExecutablePool* m_executablePool; void* m_code; size_t m_size; + CodeKind m_codeKind; #ifndef NDEBUG bool m_completed; #endif diff --git a/deps/mozjs/js/src/assembler/assembler/MIPSAssembler.h b/deps/mozjs/js/src/assembler/assembler/MIPSAssembler.h new file mode 100644 index 00000000000..b73952c3205 --- /dev/null +++ b/deps/mozjs/js/src/assembler/assembler/MIPSAssembler.h @@ -0,0 +1,1089 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MIPSAssembler_h +#define MIPSAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AssemblerBuffer.h" +#include "assembler/wtf/Assertions.h" +#include "assembler/wtf/SegmentedVector.h" + +#include "methodjit/Logging.h" +#define IPFX " %s" +#define ISPFX " " +#ifdef JS_METHODJIT_SPEW +# define MAYBE_PAD (isOOLPath ? "> " : "") +# define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os)) +# define FIXME_INSN_PRINTING \ + do { \ + js::JaegerSpew(js::JSpew_Insns, \ + ISPFX "FIXME insn printing %s:%d\n", \ + __FILE__, __LINE__); \ + } while (0) +#else +# define MAYBE_PAD "" +# define FIXME_INSN_PRINTING ((void) 0) +# define PRETTY_PRINT_OFFSET(os) "", 0 +#endif + +namespace JSC { + +typedef uint32_t MIPSWord; + +namespace MIPSRegisters { +typedef enum { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31 +} RegisterID; + +typedef enum { + f0 = 0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31 +} FPRegisterID; + +} // namespace MIPSRegisters + +class MIPSAssembler { +public: + typedef MIPSRegisters::RegisterID RegisterID; + typedef MIPSRegisters::FPRegisterID FPRegisterID; + typedef SegmentedVector Jumps; + unsigned char *buffer() const { return m_buffer.buffer(); } + bool oom() const { return m_buffer.oom(); } + +#ifdef JS_METHODJIT_SPEW + bool isOOLPath; + MIPSAssembler() : isOOLPath(false) { } +#else + MIPSAssembler() { } +#endif + + // MIPS instruction opcode field position + enum { + OP_SH_RD = 11, + OP_SH_RT = 16, + OP_SH_RS = 21, + OP_SH_SHAMT = 6, + OP_SH_CODE = 16, + OP_SH_FD = 6, + OP_SH_FS = 11, + OP_SH_FT = 16 + }; + + class JmpSrc { + friend class MIPSAssembler; + public: + JmpSrc() + : m_offset(-1) + { + } + + private: + JmpSrc(int offset) + : m_offset(offset) + { + } + + int m_offset; + }; + + class JmpDst { + friend class MIPSAssembler; + public: + JmpDst() + : m_offset(-1) + , m_used(false) + { + } + + bool isUsed() const { return m_used; } + void used() { m_used = true; } + bool isValid() const { return m_offset != -1; } + private: + JmpDst(int offset) + : m_offset(offset) + , m_used(false) + { + ASSERT(m_offset == offset); + } + + int m_offset : 31; + int m_used : 1; + }; + + void emitInst(MIPSWord op) + { + void* oldBase = m_buffer.data(); + + m_buffer.putInt(op); + + void* newBase = m_buffer.data(); + if (oldBase != newBase) + relocateJumps(oldBase, newBase); + } + + void nop() + { + emitInst(0x00000000); + } + + /* Need to insert one load data delay nop for mips1. */ + void loadDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + /* Need to insert one coprocessor access delay nop for mips1. */ + void copDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + void movz(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000000a | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void move(RegisterID rd, RegisterID rs) + { + /* addu */ + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); + } + + /* Set an immediate value to a register. This may generate 1 or 2 + instructions. */ + void li(RegisterID dest, int imm) + { + if (imm >= -32768 && imm <= 32767) + addiu(dest, MIPSRegisters::zero, imm); + else if (imm >= 0 && imm < 65536) + ori(dest, MIPSRegisters::zero, imm); + else { + lui(dest, imm >> 16); + if (imm & 0xffff) + ori(dest, dest, imm); + } + } + + void lui(RegisterID rt, int imm) + { + emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void addiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void addu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void subu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void mult(RegisterID rs, RegisterID rt) + { + emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void div(RegisterID rs, RegisterID rt) + { + emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mfhi(RegisterID rd) + { + emitInst(0x00000010 | (rd << OP_SH_RD)); + } + + void mflo(RegisterID rd) + { + emitInst(0x00000012 | (rd << OP_SH_RD)); + } + + void mul(RegisterID rd, RegisterID rs, RegisterID rt) + { +#if WTF_MIPS_ISA_AT_LEAST(32) + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); +#else + mult(rs, rt); + mflo(rd); +#endif + } + + void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void andi(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void nor(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void ori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void xori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void slt(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void sll(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void sllv(RegisterID rd, RegisterID rt, int rs) + { + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void sra(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srav(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void srl(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srlv(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void lb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lbu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwl(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwr(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lhu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void sb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void sh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void sw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void jr(RegisterID rs) + { + emitInst(0x00000008 | (rs << OP_SH_RS)); + } + + void jalr(RegisterID rs) + { + emitInst(0x0000f809 | (rs << OP_SH_RS)); + } + + void jal() + { + emitInst(0x0c000000); + } + + void bkpt() + { + int value = 512; /* BRK_BUG */ + emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); + } + + void bgez(RegisterID rs, int imm) + { + emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void bltz(RegisterID rs, int imm) + { + emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void beq(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bne(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bc1t() + { + emitInst(0x45010000); + } + + void bc1f() + { + emitInst(0x45000000); + } + + JmpSrc newJmpSrc() + { + return JmpSrc(m_buffer.size()); + } + + void appendJump() + { + m_jumps.append(m_buffer.size()); + } + + void movd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200006 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void negd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200007 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void absd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200005 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void lwc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + copDelayNop(); + } + + void ldc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void swc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void sdc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void mtc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mthc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mfc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void sqrtd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void truncwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtdw(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtds(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtsd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void ceqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cngtd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cnged(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cueqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void culed(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cultd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + // General helpers + + JmpDst label() + { + return JmpDst(m_buffer.size()); + } + + JmpDst align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, JmpSrc jump) + { + ASSERT(jump.m_offset != -1); + void* b = reinterpret_cast((reinterpret_cast(code)) + jump.m_offset); + return b; + } + + static void* getRelocatedAddress(void* code, JmpDst label) + { + void* b = reinterpret_cast((reinterpret_cast(code)) + label.m_offset); + return b; + } + + static int getDifferenceBetweenLabels(JmpDst from, JmpDst to) + { + return to.m_offset - from.m_offset; + } + + static int getDifferenceBetweenLabels(JmpDst from, JmpSrc to) + { + return to.m_offset - from.m_offset; + } + + static int getDifferenceBetweenLabels(JmpSrc from, JmpDst to) + { + return to.m_offset - from.m_offset; + } + + // Assembler admin methods: + + size_t size() const + { + return m_buffer.size(); + } + + void executableCopy(void* buffer) + { + memcpy(buffer, m_buffer.data(), m_buffer.size()); + relocateJumps(m_buffer.data(), buffer); + } + + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) + { + void *result = m_buffer.executableAllocAndCopy(allocator, poolp, kind); + if (!result) + return 0; + + relocateJumps(m_buffer.data(), result); + return result; + } + + static unsigned getCallReturnOffset(JmpSrc call) + { + // The return address is after a call and a delay slot instruction + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(JmpSrc from, JmpDst to) + { + ASSERT(to.m_offset != -1); + ASSERT(from.m_offset != -1); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); + MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, toPos); + } + + static void linkJump(void* code, JmpSrc from, void* to) + { + ASSERT(from.m_offset != -1); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, to); + } + + static bool canRelinkJump(void* from, void* to) + { + return true; + } + + static void linkCall(void* code, JmpSrc from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkCallInternal(insn, to); + } + + static void linkPointer(void* code, JmpDst from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + } + + static void relinkJump(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + + ASSERT(!(*(insn - 1)) && !(*(insn - 5))); + insn = insn - 6; + int flushSize = linkWithOffset(insn, to); + + ExecutableAllocator::cacheFlush(insn, flushSize); + } + + static void relinkCall(void* from, void* to) + { + void* start; + int size = linkCallInternal(from, to); + if (size == sizeof(MIPSWord)) + start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); + else + start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); + + ExecutableAllocator::cacheFlush(start, size); + } + + static void repatchInt32(void* from, int32_t to) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (to & 0xffff); + insn--; + ExecutableAllocator::cacheFlush(insn, 2 * sizeof(MIPSWord)); + } + + static void repatchPointer(void* from, void* to) + { + repatchInt32(from, reinterpret_cast(to)); + } + + static void repatchLoadPtrToLEA(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn + 3; + ASSERT((*insn & 0xfc000000) == 0x8c000000); // lw + /* lw -> addiu */ + *insn = 0x24000000 | (*insn & 0x03ffffff); + + ExecutableAllocator::cacheFlush(insn, sizeof(MIPSWord)); + } + + static void repatchLEAToLoadPtr(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn + 3; + if ((*insn & 0xfc000000) == 0x8c000000) + return; // Valid lw instruction + + ASSERT((*insn & 0xfc000000) == 0x24000000); // addiu + /* addiu -> lw */ + *insn = 0x8c000000 | (*insn & 0x03ffffff); + + ExecutableAllocator::cacheFlush(insn, sizeof(MIPSWord)); + } + +private: + + /* Update each jump in the buffer of newBase. */ + void relocateJumps(void* oldBase, void* newBase) + { + // Check each jump + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + int pos = *iter; + MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); + insn = insn + 2; + // Need to make sure we have 5 valid instructions after pos + if ((unsigned int)pos >= m_buffer.size() - 5 * sizeof(MIPSWord)) + continue; + + if ((*insn & 0xfc000000) == 0x08000000) { // j + int offset = *insn & 0x03ffffff; + int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; + int topFourBits = (oldInsnAddress + 4) >> 28; + int oldTargetAddress = (topFourBits << 28) | (offset << 2); + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + int newInsnAddress = (int)insn; + if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) + *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); + else { + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + } + } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui + int high = (*insn & 0xffff) << 16; + int low = *(insn + 1) & 0xffff; + int oldTargetAddress = high | low; + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + } + } + } + + static int linkWithOffset(MIPSWord* insn, void* to) + { + ASSERT((*insn & 0xfc000000) == 0x10000000 // beq + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast(to) + - reinterpret_cast(insn) - 4) >> 2; + + if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { + /* + Convert the sequence: + beq $2, $3, target + nop + b 1f + nop + nop + nop + 1: + + to the new sequence if possible: + bne $2, $3, 1f + nop + j target + nop + nop + nop + 1: + + OR to the new sequence: + bne $2, $3, 1f + nop + lui $25, target >> 16 + ori $25, $25, target & 0xffff + jr $25 + nop + 1: + + Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. + */ + + if (*(insn + 2) == 0x10000003) { + if ((*insn & 0xfc000000) == 0x10000000) // beq + *insn = (*insn & 0x03ff0000) | 0x14000005; // bne + else if ((*insn & 0xfc000000) == 0x14000000) // bne + *insn = (*insn & 0x03ff0000) | 0x10000005; // beq + else if ((*insn & 0xffff0000) == 0x45010000) // bc1t + *insn = 0x45000005; // bc1f + else if ((*insn & 0xffff0000) == 0x45000000) // bc1f + *insn = 0x45010005; // bc1t + else + ASSERT(0); + } + + insn = insn + 2; + if ((reinterpret_cast(insn) + 4) >> 28 + == reinterpret_cast(to) >> 28) { + *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + *(insn + 1) = 0; + return 4 * sizeof(MIPSWord); + } + + intptr_t newTargetAddress = reinterpret_cast(to); + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + return 5 * sizeof(MIPSWord); + } + + *insn = (*insn & 0xffff0000) | (diff & 0xffff); + return sizeof(MIPSWord); + } + + static int linkCallInternal(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn - 4; + + if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal + if ((reinterpret_cast(from) - 4) >> 28 + == reinterpret_cast(to) >> 28) { + *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + return sizeof(MIPSWord); + } + + /* lui $25, (to >> 16) & 0xffff */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori $25, $25, to & 0xffff */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); + /* jalr $25 */ + *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); + return 3 * sizeof(MIPSWord); + } + + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori + + /* lui */ + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori */ + *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + return 2 * sizeof(MIPSWord); + } + + AssemblerBuffer m_buffer; + Jumps m_jumps; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MIPSAssembler_h diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssembler.h b/deps/mozjs/js/src/assembler/assembler/MacroAssembler.h index 73bda22a8ee..8bf9c0b0a80 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssembler.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssembler.h @@ -95,12 +95,12 @@ class MacroAssembler : public MacroAssemblerBase { storePtr(src, Address(stackPointerRegister, (index * sizeof(void*)))); } - void poke(Imm32 value, int index = 0) + void poke(TrustedImm32 value, int index = 0) { store32(value, Address(stackPointerRegister, (index * sizeof(void*)))); } - void poke(ImmPtr imm, int index = 0) + void poke(TrustedImmPtr imm, int index = 0) { storePtr(imm, Address(stackPointerRegister, (index * sizeof(void*)))); } @@ -117,7 +117,7 @@ class MacroAssembler : public MacroAssemblerBase { branch32(cond, op1, op2).linkTo(target, this); } - void branch32(Condition cond, RegisterID op1, Imm32 imm, Label target) + void branch32(Condition cond, RegisterID op1, TrustedImm32 imm, Label target) { branch32(cond, op1, imm).linkTo(target, this); } @@ -177,11 +177,6 @@ class MacroAssembler : public MacroAssemblerBase { and32(src, dest); } - void andPtr(Address address, RegisterID srcDest) - { - and32(address, srcDest); - } - void andPtr(Imm32 imm, RegisterID srcDest) { and32(imm, srcDest); @@ -192,6 +187,11 @@ class MacroAssembler : public MacroAssemblerBase { and32(Imm32(ptr), srcDest); } + void negPtr(RegisterID srcDest) + { + neg32(srcDest); + } + void notPtr(RegisterID srcDest) { not32(srcDest); @@ -212,11 +212,6 @@ class MacroAssembler : public MacroAssemblerBase { or32(imm, dest); } - void orPtr(Address address, RegisterID srcDest) - { - or32(address, srcDest); - } - void subPtr(RegisterID src, RegisterID dest) { sub32(src, dest); @@ -278,27 +273,22 @@ class MacroAssembler : public MacroAssemblerBase { store32(src, address); } - void storePtr(RegisterID src, BaseIndex address) - { - store32(src, address); - } - void storePtr(RegisterID src, void* address) { store32(src, address); } - void storePtr(ImmPtr imm, ImplicitAddress address) + void storePtr(TrustedImmPtr imm, ImplicitAddress address) { store32(Imm32(imm), address); } - void storePtr(ImmPtr imm, BaseIndex address) + void storePtr(TrustedImmPtr imm, BaseIndex address) { store32(Imm32(imm), address); } - void storePtr(ImmPtr imm, void* address) + void storePtr(TrustedImmPtr imm, void* address) { store32(Imm32(imm), address); } @@ -344,7 +334,7 @@ class MacroAssembler : public MacroAssemblerBase { return branch32(cond, left, Imm32(right)); } - Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right) + Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right, RegisterID scratch) { return branch32(cond, left, Imm32(right)); } diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.cpp b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.cpp index 14b4166b7ea..02c4f20a04b 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.cpp +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.cpp @@ -34,7 +34,7 @@ #include "MacroAssemblerARM.h" -#if WTF_PLATFORM_LINUX || WTF_PLATFORM_ANDROID +#if WTF_OS_LINUX || WTF_OS_ANDROID #include #include #include @@ -70,6 +70,22 @@ static bool isVFPPresent() } #endif +#if defined(__GNUC__) && defined(__VFP_FP__) + return true; +#endif + +#ifdef WTF_OS_ANDROID + FILE *fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + return false; + + char buf[1024]; + fread(buf, sizeof(char), sizeof(buf), fp); + fclose(fp); + if (strstr(buf, "vfp")) + return true; +#endif + return false; } diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.h index 7413411f450..4fcc3c6197a 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARM.h @@ -43,8 +43,7 @@ namespace JSC { class MacroAssemblerARM : public AbstractMacroAssembler { static const int DoubleConditionMask = 0x0f; - static const int DoubleConditionBitSpecial = 0x10; - COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); + static const int DoubleConditionBitSpecial = 0x8; public: enum Condition { Equal = ARMAssembler::EQ, @@ -91,14 +90,14 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.adds_r(dest, dest, src); } - void add32(Imm32 imm, Address address) + void add32(TrustedImm32 imm, Address address) { load32(address, ARMRegisters::S1); add32(imm, ARMRegisters::S1); store32(ARMRegisters::S1, address); } - void add32(Imm32 imm, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID dest) { m_assembler.adds_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } @@ -173,7 +172,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.orrs_r(dest, dest, src); } - void or32(Imm32 imm, RegisterID dest) + void or32(TrustedImm32 imm, RegisterID dest) { m_assembler.orrs_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } @@ -211,12 +210,12 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.subs_r(dest, dest, src); } - void sub32(Imm32 imm, RegisterID dest) + void sub32(TrustedImm32 imm, RegisterID dest) { m_assembler.subs_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } - void sub32(Imm32 imm, Address address) + void sub32(TrustedImm32 imm, Address address) { load32(address, ARMRegisters::S1); sub32(imm, ARMRegisters::S1); @@ -240,7 +239,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.eors_r(dest, dest, src); } - void xor32(Imm32 imm, RegisterID dest) + void xor32(TrustedImm32 imm, RegisterID dest) { m_assembler.eors_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } @@ -251,9 +250,52 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.eors_r(dest, dest, ARMRegisters::S1); } + void load8SignExtend(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransferN(true, true, 8, dest, address.base, address.offset); + } + + void load8ZeroExtend(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransferN(true, false, 8, dest, address.base, address.offset); + } + + void load8SignExtend(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransferN(true, true, 8, dest, + address.base, address.index, address.scale, address.offset); + } + + void load8ZeroExtend(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransferN(true, false, 8, dest, + address.base, address.index, address.scale, address.offset); + } + + /* this is *identical* to the zero extending case*/ void load8(ImplicitAddress address, RegisterID dest) { - m_assembler.dataTransfer8(true, dest, address.base, address.offset); + load8ZeroExtend(address, dest); + } + + void load16SignExtend(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransferN(true, true, 16, dest, address.base, address.offset); + } + + void load16ZeroExtend(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransferN(true, false, 16, dest, address.base, address.offset); + } + void load16SignExtend(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransferN(true, true, 16, dest, + address.base, address.index, address.scale, address.offset); + } + void load16ZeroExtend(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransferN(true, false, 16, dest, + address.base, address.index, address.scale, address.offset); } void load32(ImplicitAddress address, RegisterID dest) @@ -380,7 +422,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.baseIndexTransfer32(false, src, address.base, address.index, static_cast(address.scale), address.offset); } - void store32(Imm32 imm, BaseIndex address) + void store32(TrustedImm32 imm, BaseIndex address) { if (imm.m_isPointer) m_assembler.ldr_un_imm(ARMRegisters::S1, imm.m_value); @@ -389,7 +431,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { store32(ARMRegisters::S1, address); } - void store32(Imm32 imm, ImplicitAddress address) + void store32(TrustedImm32 imm, ImplicitAddress address) { if (imm.m_isPointer) m_assembler.ldr_un_imm(ARMRegisters::S1, imm.m_value); @@ -404,7 +446,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.dtr_u(false, src, ARMRegisters::S0, 0); } - void store32(Imm32 imm, void* address) + void store32(TrustedImm32 imm, void* address) { m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast(address)); if (imm.m_isPointer) @@ -414,6 +456,92 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0); } + void store16(RegisterID src, ImplicitAddress address) + { + m_assembler.dataTransferN(false, false, 16, src, address.base, address.offset); + } + void store16(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferN(false, false, 16, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store16(TrustedImm32 imm, BaseIndex address) + { + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 16 bits of a pointer?"); + else + move(imm, ARMRegisters::S1); + store16(ARMRegisters::S1, address); + } + void store16(TrustedImm32 imm, ImplicitAddress address) + { + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 16 bits of a pointer?"); + else + move(imm, ARMRegisters::S1); + store16(ARMRegisters::S1, address); + } + + void store16(RegisterID src, void* address) + { + m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.mem_imm_off(false, false, 16, true, src, ARMRegisters::S0, 0); + } + + void store16(TrustedImm32 imm, void* address) + { + m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast(address)); + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 16 bits of a pointer?"); + else + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.mem_imm_off(false, false, 16, true, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void store8(RegisterID src, ImplicitAddress address) + { + m_assembler.dataTransferN(false, false, 16, src, address.base, address.offset); + } + + void store8(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferN(false, false, 8, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store8(TrustedImm32 imm, BaseIndex address) + { + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 8 bits of a pointer?"); + else + move(imm, ARMRegisters::S1); + store8(ARMRegisters::S1, address); + } + + void store8(TrustedImm32 imm, ImplicitAddress address) + { + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 16 bits of a pointer?"); + else + move(imm, ARMRegisters::S1); + store8(ARMRegisters::S1, address); + } + + void store8(RegisterID src, void* address) + { + m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.mem_imm_off(false, false, 8, true, src, ARMRegisters::S0, 0); + } + + void store8(TrustedImm32 imm, void* address) + { + m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast(address)); + if (imm.m_isPointer) + JS_ASSERT("What are you trying to do with 16 bits of a pointer?"); + else + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.mem_imm_off(false, false, 8, true, ARMRegisters::S1, ARMRegisters::S0, 0); + } + void pop(RegisterID dest) { m_assembler.pop_r(dest); @@ -436,7 +564,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { push(ARMRegisters::S0); } - void move(Imm32 imm, RegisterID dest) + void move(TrustedImm32 imm, RegisterID dest) { if (imm.m_isPointer) m_assembler.ldr_un_imm(dest, imm.m_value); @@ -449,7 +577,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.mov_r(dest, src); } - void move(ImmPtr imm, RegisterID dest) + void move(TrustedImmPtr imm, RegisterID dest) { move(Imm32(imm), dest); } @@ -485,14 +613,36 @@ class MacroAssemblerARM : public AbstractMacroAssembler { return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); } - Jump branch32(Condition cond, RegisterID left, Imm32 right, int useConstantPool = 0) + Jump branch32(Condition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) { ASSERT(left != ARMRegisters::S0); if (right.m_isPointer) { m_assembler.ldr_un_imm(ARMRegisters::S0, right.m_value); m_assembler.cmp_r(left, ARMRegisters::S0); - } else - m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } else { + // This is a rather cute (if not confusing) pattern. + // unfortunately, it is not quite conducive to switching from + // cmp to cmn, so I'm doing so manually. + // m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + + // try to shoehorn the immediate into the compare instruction + ARMWord arg = m_assembler.getOp2(right.m_value); + if (arg != m_assembler.INVALID_IMM) { + m_assembler.cmp_r(left, arg); + } else { + // if it does not fit, try to shoehorn a negative in, and use a negated compare + // p.s. why couldn't arm just include the sign bit in the imm, rather than the inst. + arg = m_assembler.getOp2(-right.m_value); + if (arg != m_assembler.INVALID_IMM) { + m_assembler.cmn_r(left, arg); + } else { + // If we get here, we *need* to use a temp register and any way of loading a value + // will enable us to load a negative easily, so there is no reason to switch from + // cmp to cmn. + m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } + } + } return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); } @@ -500,21 +650,21 @@ class MacroAssemblerARM : public AbstractMacroAssembler { // number of instructions emitted is constant, regardless of the argument // values. For ARM, this is identical to branch32WithPatch, except that it // does not generate a DataLabel32. - Jump branch32FixedLength(Condition cond, RegisterID left, Imm32 right) + Jump branch32FixedLength(Condition cond, RegisterID left, TrustedImm32 right) { m_assembler.ldr_un_imm(ARMRegisters::S1, right.m_value); return branch32(cond, left, ARMRegisters::S1, true); } // As branch32_force32, but allow the value ('right') to be patched. - Jump branch32WithPatch(Condition cond, RegisterID left, Imm32 right, DataLabel32 &dataLabel) + Jump branch32WithPatch(Condition cond, RegisterID left, TrustedImm32 right, DataLabel32 &dataLabel) { ASSERT(left != ARMRegisters::S1); dataLabel = moveWithPatch(right, ARMRegisters::S1); return branch32(cond, left, ARMRegisters::S1, true); } - Jump branch32WithPatch(Condition cond, Address left, Imm32 right, DataLabel32 &dataLabel) + Jump branch32WithPatch(Condition cond, Address left, TrustedImm32 right, DataLabel32 &dataLabel) { ASSERT(left.base != ARMRegisters::S1); load32(left, ARMRegisters::S1); @@ -524,6 +674,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { Jump branch32(Condition cond, RegisterID left, Address right) { + /*If the load only takes a single instruction, then we could just do a load into*/ load32(right, ARMRegisters::S1); return branch32(cond, left, ARMRegisters::S1); } @@ -534,19 +685,19 @@ class MacroAssemblerARM : public AbstractMacroAssembler { return branch32(cond, ARMRegisters::S1, right); } - Jump branch32(Condition cond, Address left, Imm32 right) + Jump branch32(Condition cond, Address left, TrustedImm32 right) { load32(left, ARMRegisters::S1); return branch32(cond, ARMRegisters::S1, right); } - Jump branch32(Condition cond, BaseIndex left, Imm32 right) + Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right) { load32(left, ARMRegisters::S1); return branch32(cond, ARMRegisters::S1, right); } - Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right) + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right) { load32WithUnalignedHalfWords(left, ARMRegisters::S1); return branch32(cond, ARMRegisters::S1, right); @@ -828,7 +979,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { setTest32(cond, address, mask, dest); } - void add32(Imm32 imm, RegisterID src, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { m_assembler.add_r(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } @@ -850,7 +1001,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { move(ARMRegisters::S1, dest); } - void add32(Imm32 imm, AbsoluteAddress address) + void add32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.ldr_un_imm(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0); @@ -859,7 +1010,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0); } - void sub32(Imm32 imm, AbsoluteAddress address) + void sub32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.ldr_un_imm(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0); @@ -880,7 +1031,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { return branch32(cond, ARMRegisters::S1, right); } - Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right) + Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right) { load32(left.m_ptr, ARMRegisters::S1); return branch32(cond, ARMRegisters::S1, right); @@ -908,14 +1059,14 @@ class MacroAssemblerARM : public AbstractMacroAssembler { return Call::fromTailJump(oldJump); } - DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest) + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) { DataLabelPtr dataLabel(this); m_assembler.ldr_un_imm(dest, reinterpret_cast(initialValue.m_value)); return dataLabel; } - DataLabel32 moveWithPatch(Imm32 initialValue, RegisterID dest) + DataLabel32 moveWithPatch(TrustedImm32 initialValue, RegisterID dest) { DataLabel32 dataLabel(this); m_assembler.ldr_un_imm(dest, initialValue.m_value); @@ -937,7 +1088,7 @@ class MacroAssemblerARM : public AbstractMacroAssembler { return jump; } - DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); store32(ARMRegisters::S1, address); @@ -972,21 +1123,112 @@ class MacroAssemblerARM : public AbstractMacroAssembler { void loadDouble(ImplicitAddress address, FPRegisterID dest) { + // Load a double from base+offset. m_assembler.doubleTransfer(true, dest, address.base, address.offset); } + void loadDouble(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexFloatTransfer(true, true, dest, + address.base, address.index, + address.scale, address.offset); + } + DataLabelPtr loadDouble(const void* address, FPRegisterID dest) { DataLabelPtr label = moveWithPatch(ImmPtr(address), ARMRegisters::S0); - m_assembler.fdtr_u(true, dest, ARMRegisters::S0, 0); + m_assembler.doubleTransfer(true, dest, ARMRegisters::S0, 0); return label; } + void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg) { + m_assembler.vmov64(false, true, lo, hi, fpReg); + } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + // as long as this is a sane mapping, (*2) should just work + dest = (FPRegisterID) (dest * 2); + ASSERT((address.offset & 0x3) == 0); + m_assembler.floatTransfer(true, dest, address.base, address.offset); + m_assembler.vcvt(m_assembler.FloatReg32, m_assembler.FloatReg64, (FPRegisterID)(dest*2), dest); + } + void loadFloat(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexFloatTransfer(true, false, (FPRegisterID)(dest*2), + address.base, address.index, + address.scale, address.offset); + m_assembler.vcvt(m_assembler.FloatReg32, m_assembler.FloatReg64, (FPRegisterID)(dest*2), dest); + } + + DataLabelPtr loadFloat(const void* address, FPRegisterID dest) + { + DataLabelPtr label = moveWithPatch(ImmPtr(address), ARMRegisters::S0); + m_assembler.fmem_imm_off(true, false, true, (FPRegisterID)(dest*2), ARMRegisters::S0, 0); + m_assembler.vcvt(m_assembler.FloatReg32, m_assembler.FloatReg64, (FPRegisterID)(dest*2), dest); + return label; + } + void storeDouble(FPRegisterID src, ImplicitAddress address) { + // Store a double at base+offset. m_assembler.doubleTransfer(false, src, address.base, address.offset); } + void storeDouble(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexFloatTransfer(false, true, src, + address.base, address.index, + address.scale, address.offset); + } + + void storeDouble(ImmDouble imm, Address address) + { + store32(Imm32(imm.u.s.lsb), address); + store32(Imm32(imm.u.s.msb), Address(address.base, address.offset + 4)); + } + + void storeDouble(ImmDouble imm, BaseIndex address) + { + store32(Imm32(imm.u.s.lsb), address); + store32(Imm32(imm.u.s.msb), + BaseIndex(address.base, address.index, address.scale, address.offset + 4)); + } + void fastStoreDouble(FPRegisterID fpReg, RegisterID lo, RegisterID hi) { + m_assembler.vmov64(true, true, lo, hi, fpReg); + } + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + m_assembler.floatTransfer(false, src, address.base, address.offset); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexFloatTransfer(false, false, src, + address.base, address.index, + address.scale, address.offset); + } + void storeFloat(ImmDouble imm, Address address) + { + union { + float f; + uint32_t u32; + } u; + u.f = imm.u.d; + store32(Imm32(u.u32), address); + } + + void storeFloat(ImmDouble imm, BaseIndex address) + { + union { + float f; + uint32_t u32; + } u; + u.f = imm.u.d; + store32(Imm32(u.u32), address); + } + void addDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.faddd_r(dest, dest, src); @@ -1037,6 +1279,11 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.fnegd_r(dest, src); } + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.fabsd_r(dest, src); + } + void sqrtDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.fsqrtd_r(dest, src); @@ -1044,8 +1291,14 @@ class MacroAssemblerARM : public AbstractMacroAssembler { void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { - m_assembler.fmsr_r(dest, src); - m_assembler.fsitod_r(dest, dest); + m_assembler.fmsr_r(floatShadow(dest), src); + m_assembler.fsitod_r(dest, floatShadow(dest)); + } + + void convertUInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.fmsr_r(floatShadow(dest), src); + m_assembler.fuitod_r(dest, floatShadow(dest)); } void convertInt32ToDouble(Address src, FPRegisterID dest) @@ -1064,6 +1317,11 @@ class MacroAssemblerARM : public AbstractMacroAssembler { convertInt32ToDouble(ARMRegisters::S1, dest); } + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vcvt(m_assembler.FloatReg64, m_assembler.FloatReg32, src, dest); + } + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) { m_assembler.fcmpd_r(left, right); @@ -1078,11 +1336,11 @@ class MacroAssemblerARM : public AbstractMacroAssembler { // May also branch for some values that are representable in 32 bits Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) { - m_assembler.ftosizd_r(ARMRegisters::SD0, src); + m_assembler.ftosizd_r(floatShadow(ARMRegisters::SD0), src); // If FTOSIZD (VCVT.S32.F64) can't fit the result into a 32-bit // integer, it saturates at INT_MAX or INT_MIN. Testing this is // probably quicker than testing FPSCR for exception. - m_assembler.fmrs_r(dest, ARMRegisters::SD0); + m_assembler.fmrs_r(dest, floatShadow(ARMRegisters::SD0)); m_assembler.cmn_r(dest, ARMAssembler::getOp2(-0x7fffffff)); m_assembler.cmp_r(dest, ARMAssembler::getOp2(0x80000000), ARMCondition(NonZero)); return Jump(m_assembler.jmp(ARMCondition(Zero))); @@ -1094,11 +1352,11 @@ class MacroAssemblerARM : public AbstractMacroAssembler { // (specifically, in this case, 0). void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) { - m_assembler.ftosid_r(ARMRegisters::SD0, src); - m_assembler.fmrs_r(dest, ARMRegisters::SD0); + m_assembler.ftosid_r(floatShadow(ARMRegisters::SD0), src); + m_assembler.fmrs_r(dest, floatShadow(ARMRegisters::SD0)); // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.fsitod_r(ARMRegisters::SD0, ARMRegisters::SD0); + m_assembler.fsitod_r(ARMRegisters::SD0, floatShadow(ARMRegisters::SD0)); failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 @@ -1121,12 +1379,10 @@ class MacroAssemblerARM : public AbstractMacroAssembler { m_assembler.forceFlushConstantPool(); } -#ifdef DEBUG - void allowPoolFlush(bool allowFlush) + int flushCount() { - m_assembler.allowPoolFlush(allowFlush); + return m_assembler.flushCount(); } -#endif protected: ARMAssembler::Condition ARMCondition(Condition cond) diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARMv7.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARMv7.h index 5492f8246a0..2bdb6e8fdb5 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARMv7.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerARMv7.h @@ -52,7 +52,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { struct ArmAddress { enum AddressType { HasOffset, - HasIndex, + HasIndex } type; RegisterID base; union { @@ -113,7 +113,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, - DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, + DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE }; static const RegisterID stackPointerRegister = ARMRegisters::sp; @@ -131,12 +131,12 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.add(dest, dest, src); } - void add32(Imm32 imm, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID dest) { add32(imm, dest, dest); } - void add32(Imm32 imm, RegisterID src, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); if (armImm.isValid()) @@ -147,7 +147,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { } } - void add32(Imm32 imm, Address address) + void add32(TrustedImm32 imm, Address address) { load32(address, dataTempRegister); @@ -170,7 +170,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { add32(dataTempRegister, dest); } - void add32(Imm32 imm, AbsoluteAddress address) + void add32(TrustedImm32 imm, AbsoluteAddress address) { load32(address.m_ptr, dataTempRegister); @@ -239,7 +239,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.orr(dest, dest, src); } - void or32(Imm32 imm, RegisterID dest) + void or32(TrustedImm32 imm, RegisterID dest) { ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); if (armImm.isValid()) @@ -285,7 +285,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.sub(dest, dest, src); } - void sub32(Imm32 imm, RegisterID dest) + void sub32(TrustedImm32 imm, RegisterID dest) { ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); if (armImm.isValid()) @@ -296,7 +296,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { } } - void sub32(Imm32 imm, Address address) + void sub32(TrustedImm32 imm, Address address) { load32(address, dataTempRegister); @@ -319,7 +319,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { sub32(dataTempRegister, dest); } - void sub32(Imm32 imm, AbsoluteAddress address) + void sub32(TrustedImm32 imm, AbsoluteAddress address) { load32(address.m_ptr, dataTempRegister); @@ -341,7 +341,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.eor(dest, dest, src); } - void xor32(Imm32 imm, RegisterID dest) + void xor32(TrustedImm32 imm, RegisterID dest) { ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); if (armImm.isValid()) @@ -486,7 +486,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { store32(src, setupArmAddress(address)); } - void store32(Imm32 imm, ImplicitAddress address) + void store32(TrustedImm32 imm, ImplicitAddress address) { move(imm, dataTempRegister); store32(dataTempRegister, setupArmAddress(address)); @@ -498,7 +498,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); } - void store32(Imm32 imm, void* address) + void store32(TrustedImm32 imm, void* address) { move(imm, dataTempRegister); store32(dataTempRegister, address); @@ -667,7 +667,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { // // Move values in registers. - void move(Imm32 imm, RegisterID dest) + void move(TrustedImm32 imm, RegisterID dest) { uint32_t value = imm.m_value; @@ -693,7 +693,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.mov(dest, src); } - void move(ImmPtr imm, RegisterID dest) + void move(TrustedImmPtr imm, RegisterID dest) { move(Imm32(imm), dest); } @@ -780,7 +780,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { return Jump(makeBranch(cond)); } - Jump branch32(Condition cond, RegisterID left, Imm32 right) + Jump branch32(Condition cond, RegisterID left, TrustedImm32 right) { compare32(left, right); return Jump(makeBranch(cond)); @@ -798,21 +798,21 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { return branch32(cond, dataTempRegister, right); } - Jump branch32(Condition cond, Address left, Imm32 right) + Jump branch32(Condition cond, Address left, TrustedImm32 right) { // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ load32(left, addressTempRegister); return branch32(cond, addressTempRegister, right); } - Jump branch32(Condition cond, BaseIndex left, Imm32 right) + Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right) { // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ load32(left, addressTempRegister); return branch32(cond, addressTempRegister, right); } - Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right) + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right) { // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ load32WithUnalignedHalfWords(left, addressTempRegister); @@ -825,7 +825,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { return branch32(cond, dataTempRegister, right); } - Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right) + Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right) { // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ load32(left.m_ptr, addressTempRegister); @@ -1065,13 +1065,13 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); } - DataLabel32 moveWithPatch(Imm32 imm, RegisterID dst) + DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) { moveFixedWidthEncoding(imm, dst); return DataLabel32(this); } - DataLabelPtr moveWithPatch(ImmPtr imm, RegisterID dst) + DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) { moveFixedWidthEncoding(Imm32(imm), dst); return DataLabelPtr(this); @@ -1090,7 +1090,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { return branch32(cond, addressTempRegister, dataTempRegister); } - DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); store32(dataTempRegister, address); @@ -1179,7 +1179,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler { return addressTempRegister; } - void moveFixedWidthEncoding(Imm32 imm, RegisterID dst) + void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) { uint32_t value = imm.m_value; m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h index 841fa964712..7103c3847f4 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h @@ -164,8 +164,8 @@ class MacroAssemblerCodePtr { ptrdiff_t operator -(const MacroAssemblerCodePtr &other) const { JS_ASSERT(m_value); - return reinterpret_cast(m_value) - - reinterpret_cast(other.m_value); + return reinterpret_cast(m_value) - + reinterpret_cast(other.m_value); } private: @@ -180,7 +180,8 @@ class MacroAssemblerCodePtr { class MacroAssemblerCodeRef { public: MacroAssemblerCodeRef() - : m_size(0) + : m_executablePool(NULL), + m_size(0) { } @@ -191,6 +192,20 @@ class MacroAssemblerCodeRef { { } + // Release the code memory in this code ref. + void release() + { + if (!m_executablePool) + return; + +#if defined DEBUG && (defined WTF_CPU_X86 || defined WTF_CPU_X86_64) + void *addr = m_code.executableAddress(); + memset(addr, 0xcc, m_size); +#endif + m_executablePool->release(); + m_executablePool = NULL; + } + MacroAssemblerCodePtr m_code; ExecutablePool* m_executablePool; size_t m_size; diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h new file mode 100644 index 00000000000..385606e1724 --- /dev/null +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h @@ -0,0 +1,2790 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerMIPS_h +#define MacroAssemblerMIPS_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AbstractMacroAssembler.h" +#include "MIPSAssembler.h" + +namespace JSC { + +class MacroAssemblerMIPS : public AbstractMacroAssembler { +public: + typedef MIPSRegisters::FPRegisterID FPRegisterID; + + MacroAssemblerMIPS() + : m_fixedWidth(false) + { + } + + static const Scale ScalePtr = TimesFour; + static const unsigned int TotalRegisters = 24; // Count from $0 to $23 + // to leave the bitmask for + // 8 FP registers. + + // For storing immediate number + static const RegisterID immTempRegister = MIPSRegisters::t0; + // For storing data loaded from the memory + static const RegisterID dataTempRegister = MIPSRegisters::t1; + // For storing address base + static const RegisterID addrTempRegister = MIPSRegisters::t2; + // For storing compare result + static const RegisterID cmpTempRegister = MIPSRegisters::t3; + // For storing data loaded from the memory + static const RegisterID dataTemp2Register = MIPSRegisters::t4; + + // FP temp register + static const FPRegisterID fpTempRegister = MIPSRegisters::f16; + + enum Condition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Overflow, + Signed, + Zero, + NonZero + }; + + enum DoubleCondition { + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + static const RegisterID stackPointerRegister = MIPSRegisters::sp; + static const RegisterID returnAddressRegister = MIPSRegisters::ra; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an Imm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void lea(Address address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + addiu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.addiu(dest, addrTempRegister, address.offset); + } + } + + void lea(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + addiu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.addiu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + addiu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.addiu(dest, addrTempRegister, address.offset); + } + } + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addu(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_isPointer && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, imm.m_value); + } else { + /* + li immTemp, imm + addu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.addu(dest, src, immTempRegister); + } + } + + void add32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (!imm.m_isPointer + && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(RegisterID src, Address dest) + { + if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + addu dataTemp, dataTemp, src + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, dest.base, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, dest.base, dest.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + addu dataTemp, dataTemp, src + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); + m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); + } + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + addu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(ImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + if (!imm.m_isPointer && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + // If src is (0x7FF7FFFF<<1), we will convert it to (0x7FF80000<<1). + void setShiftedCanonicalNaN(RegisterID src, RegisterID dest) + { + /* + li dataTemp, (0x7FF80000<<1) + li immTemp, (0x7FF7FFFF<<1) + xor immTemp, immTemp, src + move dest, src + movz dest, dataTemp, immTemp + */ + move(TrustedImm32(0x7FF80000 << 1), dataTempRegister); + move(TrustedImm32(0x7FF7FFFF << 1), immTempRegister); + xor32(src, immTempRegister); + move(src, dest); + m_assembler.movz(dest, dataTempRegister, immTempRegister); + } + + void and32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.andInsn(dest, dest, dataTempRegister); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andInsn(dest, dest, src); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (!imm.m_isPointer && imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) + m_assembler.andi(dest, dest, imm.m_value); + else { + /* + li immTemp, imm + and dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.andInsn(dest, dest, immTempRegister); + } + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sll(dest, dest, imm.m_value); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.sllv(dest, dest, shiftAmount); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.mul(dest, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (!imm.m_isPointer && imm.m_value == 1 && !m_fixedWidth) + move(src, dest); + else { + /* + li dataTemp, imm + mul dest, src, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.mul(dest, src, dataTempRegister); + } + } + + void neg32(RegisterID srcDest) + { + m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); + } + + void not32(RegisterID srcDest) + { + m_assembler.nor(srcDest, srcDest, MIPSRegisters::zero); + } + + void or32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orInsn(dest, dest, src); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + return; + + if (!imm.m_isPointer && imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) { + m_assembler.ori(dest, dest, imm.m_value); + return; + } + + /* + li dataTemp, imm + or dest, dest, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srav(dest, dest, shiftAmount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, dest, imm.m_value); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srlv(dest, dest, shiftAmount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.srl(dest, dest, imm.m_value); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subu(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, dest, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, dest, immTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (!imm.m_isPointer + && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (!imm.m_isPointer + && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + subu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(ImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + + if (!imm.m_isPointer && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + } else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void xor32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.xorInsn(dest, dest, dataTempRegister); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorInsn(dest, dest, src); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + /* + li immTemp, imm + xor dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.xorInsn(dest, dest, immTempRegister); + } + + void load16SignExtend(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lh(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lh dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lh(dest, addrTempRegister, address.offset); + } + } + + void load16ZeroExtend(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + void load16SignExtend(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lh dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lh(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lh dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lh(dest, addrTempRegister, address.offset); + } + } + + void load16ZeroExtend(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + void load8SignExtend(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lb(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lb dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lb(dest, addrTempRegister, address.offset); + } + } + + void load8ZeroExtend(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8SignExtend(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lb dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lb(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lb dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lb(dest, addrTempRegister, address.offset); + } + } + + void load8ZeroExtend(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lbu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lbu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void negDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.negd(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.absd(dest, src); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtd(dst, src); + } + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an Imm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + /* Need to use zero-extened load byte for load8. */ + void load8(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load32(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lw(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lw dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lw dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32764 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + (Big-Endian) + lwl dest, address.offset(addrTemp) + lwr dest, address.offset+3(addrTemp) + (Little-Endian) + lwl dest, address.offset+3(addrTemp) + lwr dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, address.offset); + m_assembler.lwr(dest, addrTempRegister, address.offset + 3); +#else + m_assembler.lwl(dest, addrTempRegister, address.offset + 3); + m_assembler.lwr(dest, addrTempRegister, address.offset); + +#endif + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, address.offset >> 16 + ori immTemp, immTemp, address.offset & 0xffff + addu addrTemp, addrTemp, immTemp + (Big-Endian) + lw dest, 0(addrTemp) + lw dest, 3(addrTemp) + (Little-Endian) + lw dest, 3(addrTemp) + lw dest, 0(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, address.offset >> 16); + m_assembler.ori(immTempRegister, immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, 0); + m_assembler.lwr(dest, addrTempRegister, 3); +#else + m_assembler.lwl(dest, addrTempRegister, 3); + m_assembler.lwr(dest, addrTempRegister, 0); +#endif + } + } + + void load32(void* address, RegisterID dest) + { + /* + li addrTemp, address + lw dest, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.lw(dest, addrTempRegister, 0); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void load64WithPatch(Address address, RegisterID tag, RegisterID payload, int tag_offset, int payload_offset) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw tag, tag_offset(addrTemp) + lw payload, payload_offset(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(tag, addrTempRegister, tag_offset); + m_assembler.lw(payload, addrTempRegister, payload_offset); + m_fixedWidth = false; + } + + void store64WithPatch(Address address, RegisterID tag, RegisterID payload, int tag_offset, int payload_offset) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw tag, tag_offset(addrTemp) + sw payload, payload_offset(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_fixedWidth = false; + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(tag, addrTempRegister, tag_offset); + m_assembler.sw(payload, addrTempRegister, payload_offset); + } + + void store64WithPatch(Address address, Imm32 tag, RegisterID payload, int tag_offset, int payload_offset) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + li immTemp, tag + sw immTemp, tag_offset(addrTemp) + sw payload, payload_offset(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_fixedWidth = false; + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + move(tag, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, tag_offset); + m_assembler.sw(payload, addrTempRegister, payload_offset); + } + + void store64WithPatch(Address address, Imm32 tag, Imm32 payload, int tag_offset, int payload_offset) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + li immTemp, tag + sw immTemp, tag_offset(addrTemp) + li immTemp, payload + sw immTemp, payload_offset(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_fixedWidth = false; + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + move(tag, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, tag_offset); + move(payload, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, payload_offset); + } + + Label loadPtrWithPatchToLEA(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return label; + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw src, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void store8(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sb(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sb src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sb(src, addrTempRegister, address.offset); + } + } + + void store8(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sb src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sb(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sb src, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.sb(src, addrTempRegister, address.offset); + } + } + + void store8(TrustedImm32 imm, BaseIndex address) + { + if (!imm.m_isPointer && !imm.m_value) + store8(MIPSRegisters::zero, address); + else { + move(imm, immTempRegister); + store8(immTempRegister, address); + } + } + + void store8(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_isPointer && !imm.m_value) + m_assembler.sb(MIPSRegisters::zero, address.base, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sb(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sb immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + m_assembler.sb(MIPSRegisters::zero, addrTempRegister, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sb(immTempRegister, addrTempRegister, + address.offset); + } + } + } + + void store8(RegisterID src, void* address) + { + /* + li addrTemp, address + sb src, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.sb(src, addrTempRegister, 0); + } + + void store8(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sb src, 0(addrTemp) + */ + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) { + move(ImmPtr(address), addrTempRegister); + m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(ImmPtr(address), addrTempRegister); + m_assembler.sb(immTempRegister, addrTempRegister, 0); + } + } + + void store16(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sh(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sh src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sh(src, addrTempRegister, address.offset); + } + } + + void store16(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sh src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sh(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sh src, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.sh(src, addrTempRegister, address.offset); + } + } + + void store16(TrustedImm32 imm, BaseIndex address) + { + if (!imm.m_isPointer && !imm.m_value) + store16(MIPSRegisters::zero, address); + else { + move(imm, immTempRegister); + store16(immTempRegister, address); + } + } + + void store16(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_isPointer && !imm.m_value) + m_assembler.sh(MIPSRegisters::zero, address.base, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sh(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sh immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + m_assembler.sh(MIPSRegisters::zero, addrTempRegister, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sh(immTempRegister, addrTempRegister, + address.offset); + } + } + } + + void store16(RegisterID src, void* address) + { + /* + li addrTemp, address + sh src, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.sh(src, addrTempRegister, 0); + } + + void store16(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sh src, 0(addrTemp) + */ + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) { + move(ImmPtr(address), addrTempRegister); + m_assembler.sh(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(ImmPtr(address), addrTempRegister); + m_assembler.sh(immTempRegister, addrTempRegister, 0); + } + } + + void store32(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sw(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sw src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sw src, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + if (!imm.m_isPointer && !imm.m_value) + store32(MIPSRegisters::zero, address); + else { + move(imm, immTempRegister); + store32(immTempRegister, address); + } + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_isPointer && !imm.m_value) + m_assembler.sw(MIPSRegisters::zero, address.base, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, + address.offset); + } + } + } + + void store32(RegisterID src, void* address) + { + /* + li addrTemp, address + sw src, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(src, addrTempRegister, 0); + } + + void store32(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sw src, 0(addrTemp) + */ + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) { + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, 0); + } + } + + // Floating-point operations: + + bool supportsFloatingPoint() const + { +#if WTF_MIPS_DOUBLE_FLOAT + return true; +#else + return false; +#endif + } + + bool supportsFloatingPointTruncate() const + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + bool supportsFloatingPointSqrt() const + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.lw(dest, MIPSRegisters::sp, 0); + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); + } + + void push(RegisterID src) + { + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); + m_assembler.sw(src, MIPSRegisters::sp, 0); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, immTempRegister); + push(immTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_isPointer || m_fixedWidth) { + m_assembler.lui(dest, imm.m_value >> 16); + m_assembler.ori(dest, dest, imm.m_value); + } else + m_assembler.li(dest, imm.m_value); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + m_assembler.move(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(Imm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, immTempRegister); + move(reg2, reg1); + move(immTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, Imm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + + Jump branch8(Condition cond, Address left, TrustedImm32 right) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, RegisterID left, RegisterID right) + { + if (cond == Equal || cond == Zero) + return branchEqual(left, right); + if (cond == NotEqual || cond == NonZero) + return branchNotEqual(left, right); + if (cond == Above) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == AboveOrEqual) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Below) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == BelowOrEqual) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThan) { + m_assembler.slt(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThanOrEqual) { + m_assembler.slt(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThan) { + m_assembler.slt(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThanOrEqual) { + m_assembler.slt(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Overflow) { + /* + xor cmpTemp, left, right + bgez No_overflow, cmpTemp # same sign bit -> no overflow + nop + subu cmpTemp, left, right + xor cmpTemp, cmpTemp, left + bgez No_overflow, cmpTemp # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.xorInsn(cmpTempRegister, left, right); + m_assembler.bgez(cmpTempRegister, 11); + m_assembler.nop(); + m_assembler.subu(cmpTempRegister, left, right); + m_assembler.xorInsn(cmpTempRegister, cmpTempRegister, left); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + m_assembler.subu(cmpTempRegister, left, right); + // Check if the result is negative. + m_assembler.slt(cmpTempRegister, cmpTempRegister, + MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + ASSERT(0); + + return Jump(); + } + + Jump branch32(Condition cond, RegisterID left, TrustedImm32 right) + { + move(right, immTempRegister); + return branch32(cond, left, immTempRegister); + } + + Jump branch32(Condition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(Condition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(Condition cond, Address left, TrustedImm32 right) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, dataTempRegister); + // Be careful that the previous load32() uses immTempRegister. + // So, we need to put move() after load32(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, dataTempRegister); + // Be careful that the previous load32WithUnalignedHalfWords() + // uses immTempRegister. + // So, we need to put move() after load32WithUnalignedHalfWords(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch16(Condition cond, BaseIndex left, RegisterID right) + { + load16(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch16(Condition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFF0000)); + load16(left, dataTempRegister); + // Be careful that the previous load16() uses immTempRegister. + // So, we need to put move() after load16(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.andInsn(cmpTempRegister, reg, mask); + if (cond == Zero) + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + + Jump branchTest32(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + return branchEqual(reg, MIPSRegisters::zero); + return branchNotEqual(reg, MIPSRegisters::zero); + } + move(mask, immTempRegister); + return branchTest32(cond, reg, immTempRegister); + } + + Jump branchTest32(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest32(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + load8(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump jump() + { + return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); + } + + void jump(RegisterID target) + { + m_assembler.jr(target); + m_assembler.nop(); + } + + void jump(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + } + + void jump(BaseIndex address) + { + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + } + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bltz cmpTemp, No_overflow # diff sign bit -> no overflow + addu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign big -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bltz(cmpTempRegister, 10); + m_assembler.addu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + add32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + add32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + add32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchAdd32(Condition cond, Imm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchAdd32(Condition cond, Address src, RegisterID dest) + { + load32(src, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchMul32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + mult src, dest + mfhi dataTemp + mflo dest + sra addrTemp, dest, 31 + beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.mult(src, dest); + m_assembler.mfhi(dataTempRegister); + m_assembler.mflo(dest); + m_assembler.sra(addrTempRegister, dest, 31); + m_assembler.beq(dataTempRegister, addrTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + mul32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + mul32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + mul32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchMul32(cond, immTempRegister, dest); + } + + Jump branchSub32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bgez cmpTemp, No_overflow # same sign bit -> no overflow + subu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bgez(cmpTempRegister, 10); + m_assembler.subu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + sub32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + sub32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + sub32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchSub32(Condition cond, Imm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchSub32(Condition cond, Address src, RegisterID dest) + { + load32(src, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchSub32(Condition cond, Imm32 imm, Address dest) + { + // Save the original value at dest to dataTemp2Register + load32(dest, dataTemp2Register); + + // Calculate the result and save it to dest + sub32(imm, dest); + + return branchSub32(cond, imm, dataTemp2Register); + } + + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Signed) { + or32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + or32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + or32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.bkpt(); + } + + Call nearCall() + { + /* We need two words for relaxation. */ + m_assembler.nop(); + m_assembler.nop(); + m_assembler.jal(); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::LinkableNear); + } + + Call call() + { + m_assembler.lui(MIPSRegisters::t9, 0); + m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::Linkable); + } + + Call call(RegisterID target) + { + m_assembler.jalr(target); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::None); + } + + Call call(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.newJmpSrc(), Call::None); + } + + void ret() + { + m_assembler.jr(MIPSRegisters::ra); + m_assembler.nop(); + } + + void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + set32(cond, left, right, dest); + } + + void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + move(right, immTempRegister); + set32(cond, left, immTempRegister, dest); + } + + void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + if (cond == Equal || cond == Zero) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltiu(dest, dest, 1); + } else if (cond == NotEqual || cond == NonZero) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltu(dest, MIPSRegisters::zero, dest); + } else if (cond == Above) + m_assembler.sltu(dest, right, left); + else if (cond == AboveOrEqual) { + m_assembler.sltu(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == Below) + m_assembler.sltu(dest, left, right); + else if (cond == BelowOrEqual) { + m_assembler.sltu(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == GreaterThan) + m_assembler.slt(dest, right, left); + else if (cond == GreaterThanOrEqual) { + m_assembler.slt(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == LessThan) + m_assembler.slt(dest, left, right); + else if (cond == LessThanOrEqual) { + m_assembler.slt(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == Overflow) { + /* + xor cmpTemp, left, right + bgez Done, cmpTemp # same sign bit -> no overflow + move dest, 0 + subu cmpTemp, left, right + xor cmpTemp, cmpTemp, left # diff sign bit -> overflow + slt dest, cmpTemp, 0 + Done: + */ + m_assembler.xorInsn(cmpTempRegister, left, right); + m_assembler.bgez(cmpTempRegister, 4); + m_assembler.move(dest, MIPSRegisters::zero); + m_assembler.subu(cmpTempRegister, left, right); + m_assembler.xorInsn(cmpTempRegister, cmpTempRegister, left); + m_assembler.slt(dest, cmpTempRegister, MIPSRegisters::zero); + } else if (cond == Signed) { + m_assembler.subu(dest, left, right); + // Check if the result is negative. + m_assembler.slt(dest, dest, MIPSRegisters::zero); + } + } + + void set32(Condition cond, Address left, Imm32 right, RegisterID dest) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + set32(cond, dataTempRegister, immTempRegister, dest); + } + + void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + move(right, immTempRegister); + set32(cond, left, immTempRegister, dest); + } + + void set32(Condition cond, Address left, RegisterID right, RegisterID dest) + { + load32(left, immTempRegister); + set32(cond, immTempRegister, right, dest); + } + + void set32(Condition cond, RegisterID left, Address right, RegisterID dest) + { + load32(right, immTempRegister); + set32(cond, left, immTempRegister, dest); + } + + void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load8(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load32(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) + { + m_fixedWidth = true; + DataLabel32 label(this); + move(imm, dest); + m_fixedWidth = false; + return label; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_fixedWidth = true; + DataLabelPtr label(this); + move(initialValue, dest); + m_fixedWidth = false; + return label; + } + + Jump branch32WithPatch(Condition cond, RegisterID left, TrustedImm32 right, DataLabel32& dataLabel) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(right, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branch32FixedLength(Condition cond, RegisterID left, TrustedImm32 right) + { + m_fixedWidth = true; + move(right, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branch32WithPatch(Condition cond, Address left, TrustedImm32 right, DataLabel32& dataLabel) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(right, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0)) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(Condition cond, Address left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0)) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + m_fixedWidth = true; + DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(ImmPtr(0), address); + } + + Call tailRecursiveCall() + { + // Like a normal call, but don't update the returned address register + m_fixedWidth = true; + move(Imm32(0), MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.newJmpSrc(), Call::Linkable); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.movd(dest, src); + } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.lwc1(dest, address.base, address.offset); + m_assembler.cvtds(dest, dest); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lwc1 dest, (offset & 0xffff)(addrTemp) + cvt.d.s dest, dest + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.cvtds(dest, dest); + } + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + cvt.d.s dest, dest + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.cvtds(dest, dest); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(addrTemp) + cvt.d.s dest, dest + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.cvtds(dest, dest); + } + } + + DataLabelPtr loadFloat(const void* address, FPRegisterID dest) + { + DataLabelPtr label = moveWithPatch(ImmPtr(address), addrTempRegister); + /* + lwc1 dest, 0(addrTemp) + cvt.d.s dest, dest + */ + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.cvtds(dest, dest); + return label; + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.ldc1(dest, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + ldc1 dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32763 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + lwc1 dest+1, address.offset+4(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } else { + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + li immTemp, address.offset + addu addrTemp, addrTemp, immTemp + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.li(immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + ldc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + ldc1 dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + DataLabelPtr loadDouble(const void* address, FPRegisterID dest) + { + DataLabelPtr label = moveWithPatch(ImmPtr(address), addrTempRegister); +#if WTF_MIPS_ISA(1) + /* + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + /* + ldc1 dest, 0(addrTemp) + */ + m_assembler.ldc1(dest, addrTempRegister, 0); +#endif + return label; + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + lea(address, addrTempRegister); + m_assembler.swc1(src, addrTempRegister, 0); + } + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.swc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + swc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + } + } + + void storeFloat(ImmDouble imm, Address address) + { + union { + float f; + uint32_t u32; + } u; + u.f = imm.u.d; + store32(Imm32(u.u32), address); + } + + void storeFloat(ImmDouble imm, BaseIndex address) + { + union { + float f; + uint32_t u32; + } u; + u.f = imm.u.d; + store32(Imm32(u.u32), address); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + lea(address, addrTempRegister); +#if WTF_MIPS_ISA(1) + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + m_assembler.sdc1(src, addrTempRegister, 0); +#endif + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + swc1 dest, 0(addrTemp) + swc1 dest+1, 4(addrTemp) + */ + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sdc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sdc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(ImmDouble imm, Address address) + { +#if CPU(BIG_ENDIAN) + store32(Imm32(imm.u.s.msb), address); + store32(Imm32(imm.u.s.lsb), Address(address.base, address.offset + 4)); +#else + store32(Imm32(imm.u.s.lsb), address); + store32(Imm32(imm.u.s.msb), Address(address.base, address.offset + 4)); +#endif + } + + void storeDouble(ImmDouble imm, BaseIndex address) + { +#if CPU(BIG_ENDIAN) + store32(Imm32(imm.u.s.msb), address); + store32(Imm32(imm.u.s.lsb), + BaseIndex(address.base, address.index, address.scale, address.offset + 4)); +#else + store32(Imm32(imm.u.s.lsb), address); + store32(Imm32(imm.u.s.msb), + BaseIndex(address.base, address.index, address.scale, address.offset + 4)); +#endif + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.addd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.subd(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.subd(dest, dest, fpTempRegister); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.muld(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.muld(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.divd(dest, dest, src); + } + + void convertUInt32ToDouble(RegisterID src, FPRegisterID dest) + { + /* + mtc1 src, fpTemp + bgez src, OK // Test the sign bit + cvt.d.w dest, fpTemp + + // Store 0x41F0000000000000 to the fpTemp. + // Then, add fpTemp to dest to compenstate the negative value. + mtc1 zero, fpTemp + lui dataTemp, 0x41F0 + mtc1 dataTemp, fpTemp + 1 + add.d dest, dest, fpTemp + OK: + */ + m_assembler.mtc1(src, fpTempRegister); + m_assembler.bgez(src, 5); + m_assembler.cvtdw(dest, fpTempRegister); + + m_assembler.mtc1(MIPSRegisters::zero, fpTempRegister); + m_assembler.lui(dataTempRegister, 0x41F0); +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mthc1(dataTempRegister, fpTempRegister); +#else + m_assembler.mtc1(dataTempRegister, FPRegisterID(fpTempRegister + 1)); +#endif + m_assembler.addd(dest, dest, fpTempRegister); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void insertRelaxationWords() + { + /* We need four words for relaxation. */ + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; + m_assembler.nop(); + m_assembler.nop(); + m_assembler.nop(); + } + + Jump branchTrue() + { + m_assembler.appendJump(); + m_assembler.bc1t(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchFalse() + { + m_assembler.appendJump(); + m_assembler.bc1f(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.beq(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchNotEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.bne(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + void fastStoreDouble(FPRegisterID src, RegisterID lo, RegisterID hi) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mfc1(lo, src); + m_assembler.mfhc1(hi, src); +#else + m_assembler.mfc1(lo, src); + m_assembler.mfc1(hi, FPRegisterID(src + 1)); +#endif + } + + void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(lo, fpReg); + m_assembler.mthc1(hi, fpReg); +#else + m_assembler.mtc1(lo, fpReg); + m_assembler.mtc1(hi, FPRegisterID(fpReg + 1)); +#endif + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dest) + { + m_assembler.cvtsd(dest, src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.ceqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqual) { + m_assembler.cueqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThan) { + m_assembler.cngtd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.cnged(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThan) { + m_assembler.cltd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqual) { + m_assembler.cled(left, right); + return branchTrue(); + } + if (cond == DoubleEqualOrUnordered) { + m_assembler.cueqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqualOrUnordered) { + m_assembler.ceqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrUnordered) { + m_assembler.coled(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + m_assembler.coltd(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThanOrUnordered) { + m_assembler.cultd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqualOrUnordered) { + m_assembler.culed(left, right); + return branchTrue(); + } + ASSERT(0); + + return Jump(); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MAX 0x7fffffff). + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.truncwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + return branch32(Equal, dest, Imm32(0x7fffffff)); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.cvtwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); + } + + void zeroDouble(FPRegisterID dest) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, dest); + m_assembler.mthc1(MIPSRegisters::zero, dest); +#else + m_assembler.mtc1(MIPSRegisters::zero, dest); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(dest + 1)); +#endif + } + +private: + // If m_fixedWidth is true, we will generate a fixed number of instructions. + // Otherwise, we can emit any number of instructions. + bool m_fixedWidth; + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + MIPSAssembler::linkCall(code, call.m_jmp, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MacroAssemblerMIPS_h diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerSparc.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerSparc.h index 3bdd2d871b1..63853102feb 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerSparc.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerSparc.h @@ -42,7 +42,7 @@ #ifndef MacroAssemblerSparc_h #define MacroAssemblerSparc_h -#include +#include #if ENABLE_ASSEMBLER && WTF_CPU_SPARC @@ -90,21 +90,21 @@ namespace JSC { static const RegisterID stackPointerRegister = SparcRegisters::sp; static const Scale ScalePtr = TimesFour; - static const unsigned int TotalRegisters = 32; + static const unsigned int TotalRegisters = 24; void add32(RegisterID src, RegisterID dest) { m_assembler.addcc_r(dest, src, dest); } - void add32(Imm32 imm, Address address) + void add32(TrustedImm32 imm, Address address) { load32(address, SparcRegisters::g2); add32(imm, SparcRegisters::g2); store32(SparcRegisters::g2, address); } - void add32(Imm32 imm, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.addcc_imm(dest, imm.m_value, dest); @@ -126,7 +126,7 @@ namespace JSC { m_assembler.andcc_r(dest, SparcRegisters::g2, dest); } - void add32(Imm32 imm, RegisterID src, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.addcc_imm(src, imm.m_value, dest); @@ -194,7 +194,7 @@ namespace JSC { m_assembler.orcc_r(dest, src, dest); } - void or32(Imm32 imm, RegisterID dest) + void or32(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.orcc_imm(dest, imm.m_value, dest); @@ -240,7 +240,7 @@ namespace JSC { m_assembler.subcc_r(dest, src, dest); } - void sub32(Imm32 imm, RegisterID dest) + void sub32(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.subcc_imm(dest, imm.m_value, dest); @@ -250,7 +250,7 @@ namespace JSC { } } - void sub32(Imm32 imm, Address address) + void sub32(TrustedImm32 imm, Address address) { load32(address, SparcRegisters::g2); sub32(imm, SparcRegisters::g2); @@ -268,7 +268,7 @@ namespace JSC { m_assembler.xorcc_r(src, dest, dest); } - void xor32(Imm32 imm, RegisterID dest) + void xor32(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.xorcc_imm(dest, imm.m_value, dest); @@ -548,7 +548,7 @@ namespace JSC { m_assembler.stw_r(src, address.base, SparcRegisters::g2); } - void store32(Imm32 imm, BaseIndex address) + void store32(TrustedImm32 imm, BaseIndex address) { m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2); add32(Imm32(address.offset), SparcRegisters::g2); @@ -556,7 +556,7 @@ namespace JSC { m_assembler.stw_r(SparcRegisters::g3, SparcRegisters::g2, address.base); } - void store32(Imm32 imm, ImplicitAddress address) + void store32(TrustedImm32 imm, ImplicitAddress address) { m_assembler.move_nocheck(imm.m_value, SparcRegisters::g2); store32(SparcRegisters::g2, address); @@ -568,7 +568,7 @@ namespace JSC { m_assembler.stw_r(src, SparcRegisters::g0, SparcRegisters::g3); } - void store32(Imm32 imm, void* address) + void store32(TrustedImm32 imm, void* address) { move(imm, SparcRegisters::g2); store32(SparcRegisters::g2, address); @@ -598,7 +598,7 @@ namespace JSC { push(SparcRegisters::g2); } - void move(Imm32 imm, RegisterID dest) + void move(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isimm13(imm.m_value)) m_assembler.or_imm(SparcRegisters::g0, imm.m_value, dest); @@ -611,7 +611,7 @@ namespace JSC { m_assembler.or_r(src, SparcRegisters::g0, dest); } - void move(ImmPtr imm, RegisterID dest) + void move(TrustedImmPtr imm, RegisterID dest) { move(Imm32(imm), dest); } @@ -641,20 +641,20 @@ namespace JSC { return branch32(cond, SparcRegisters::g2, right); } - Jump branch32_force32(Condition cond, RegisterID left, Imm32 right) + Jump branch32_force32(Condition cond, RegisterID left, TrustedImm32 right) { m_assembler.move_nocheck(right.m_value, SparcRegisters::g3); m_assembler.subcc_r(left, SparcRegisters::g3, SparcRegisters::g0); return Jump(m_assembler.branch(SparcCondition(cond))); } - Jump branch32FixedLength(Condition cond, RegisterID left, Imm32 right) + Jump branch32FixedLength(Condition cond, RegisterID left, TrustedImm32 right) { m_assembler.move_nocheck(right.m_value, SparcRegisters::g2); return branch32(cond, left, SparcRegisters::g2); } - Jump branch32WithPatch(Condition cond, RegisterID left, Imm32 right, DataLabel32 &dataLabel) + Jump branch32WithPatch(Condition cond, RegisterID left, TrustedImm32 right, DataLabel32 &dataLabel) { // Always use move_nocheck, since the value is to be patched. dataLabel = DataLabel32(this); @@ -669,7 +669,7 @@ namespace JSC { return Jump(m_assembler.branch(SparcCondition(cond))); } - Jump branch32(Condition cond, RegisterID left, Imm32 right) + Jump branch32(Condition cond, RegisterID left, TrustedImm32 right) { if (m_assembler.isimm13(right.m_value)) m_assembler.subcc_imm(left, right.m_value, SparcRegisters::g0); @@ -692,20 +692,20 @@ namespace JSC { return branch32(cond, SparcRegisters::g2, right); } - Jump branch32(Condition cond, Address left, Imm32 right) + Jump branch32(Condition cond, Address left, TrustedImm32 right) { load32(left, SparcRegisters::g2); return branch32(cond, SparcRegisters::g2, right); } - Jump branch32(Condition cond, BaseIndex left, Imm32 right) + Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right) { load32(left, SparcRegisters::g2); return branch32(cond, SparcRegisters::g2, right); } - Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right) + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right) { load32WithUnalignedHalfWords(left, SparcRegisters::g4); return branch32(cond, SparcRegisters::g4, right); @@ -1052,7 +1052,7 @@ namespace JSC { store32(SparcRegisters::g2, address.m_ptr); } - void sub32(Imm32 imm, AbsoluteAddress address) + void sub32(TrustedImm32 imm, AbsoluteAddress address) { load32(address.m_ptr, SparcRegisters::g2); sub32(imm, SparcRegisters::g2); @@ -1071,7 +1071,7 @@ namespace JSC { return branch32(cond, SparcRegisters::g2, right); } - Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right) + Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right) { load32(left.m_ptr, SparcRegisters::g2); return branch32(cond, SparcRegisters::g2, right); @@ -1099,7 +1099,7 @@ namespace JSC { return Call::fromTailJump(oldJump); } - DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest) + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) { DataLabelPtr dataLabel(this); Imm32 imm = Imm32(initialValue); @@ -1107,7 +1107,7 @@ namespace JSC { return dataLabel; } - DataLabel32 moveWithPatch(Imm32 initialValue, RegisterID dest) + DataLabel32 moveWithPatch(TrustedImm32 initialValue, RegisterID dest) { DataLabel32 dataLabel(this); m_assembler.move_nocheck(initialValue.m_value, dest); @@ -1129,7 +1129,7 @@ namespace JSC { return jump; } - DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr dataLabel = moveWithPatch(initialValue, SparcRegisters::g2); store32(SparcRegisters::g2, address); @@ -1235,7 +1235,7 @@ namespace JSC { { union { float f; - uint32 u32; + uint32_t u32; } u; u.f = imm.u.d; store32(Imm32(u.u32), address); @@ -1245,7 +1245,7 @@ namespace JSC { { union { float f; - uint32 u32; + uint32_t u32; } u; u.f = imm.u.d; store32(Imm32(u.u32), address); @@ -1330,6 +1330,11 @@ namespace JSC { m_assembler.fmuld_r(SparcRegisters::f30, dest, dest); } + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.fabsd_r(src, dest); + } + void sqrtDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.fsqrtd_r(src, dest); @@ -1448,7 +1453,7 @@ namespace JSC { void zeroDouble(FPRegisterID srcDest) { - m_assembler.fsubd_r(srcDest, srcDest, srcDest); + fastLoadDouble(SparcRegisters::g0, SparcRegisters::g0, srcDest); } protected: diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86.h index ee61b895a8f..c6ab40f587f 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86.h @@ -60,7 +60,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { using MacroAssemblerX86Common::storeDouble; using MacroAssemblerX86Common::convertInt32ToDouble; - void add32(Imm32 imm, RegisterID src, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { m_assembler.leal_mr(imm.m_value, src, dest); } @@ -90,12 +90,12 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { m_assembler.andl_im(imm.m_value, address.m_ptr); } - void or32(Imm32 imm, AbsoluteAddress address) + void or32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.orl_im(imm.m_value, address.m_ptr); } - void sub32(Imm32 imm, AbsoluteAddress address) + void sub32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.subl_im(imm.m_value, address.m_ptr); } @@ -148,7 +148,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { addDouble(Address(srcDest), dest); } - void store32(Imm32 imm, void* address) + void store32(TrustedImm32 imm, void* address) { m_assembler.movl_i32m(imm.m_value, address); } @@ -164,7 +164,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right) + Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right) { m_assembler.cmpl_im(right.m_value, left.m_ptr); return Jump(m_assembler.jCC(x86Condition(cond))); @@ -186,7 +186,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { } - DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest) + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) { m_assembler.movl_i32r(initialValue.asIntptr(), dest); return DataLabelPtr(this); @@ -206,7 +206,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Common { return Jump(m_assembler.jCC(x86Condition(cond))); } - DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); return DataLabelPtr(this); diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86Common.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86Common.h index d70ba3a45de..d3e84cc5782 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86Common.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86Common.h @@ -91,9 +91,11 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { DoubleLessThanOrUnordered = X86Assembler::ConditionB, DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE }; - COMPILE_ASSERT( - !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), - DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); + static void staticAsserts() { + COMPILE_ASSERT( + !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), + DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); + } static const RegisterID stackPointerRegister = X86Registers::esp; @@ -116,12 +118,12 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.addl_rr(src, dest); } - void add32(Imm32 imm, Address address) + void add32(TrustedImm32 imm, Address address) { m_assembler.addl_im(imm.m_value, address.offset, address.base); } - void add32(Imm32 imm, RegisterID dest) + void add32(TrustedImm32 imm, RegisterID dest) { m_assembler.addl_ir(imm.m_value, dest); } @@ -203,6 +205,12 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.imull_i32r(src, imm.m_value, dest); } + void idiv(RegisterID reg) + { + m_assembler.cdq(); + m_assembler.idivl_r(reg); + } + void neg32(RegisterID srcDest) { m_assembler.negl_r(srcDest); @@ -228,7 +236,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.orl_rr(src, dest); } - void or32(Imm32 imm, RegisterID dest) + void or32(TrustedImm32 imm, RegisterID dest) { m_assembler.orl_ir(imm.m_value, dest); } @@ -243,7 +251,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.orl_mr(src.offset, src.base, dest); } - void or32(Imm32 imm, Address address) + void or32(TrustedImm32 imm, Address address) { m_assembler.orl_im(imm.m_value, address.offset, address.base); } @@ -307,12 +315,12 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.subl_rr(src, dest); } - void sub32(Imm32 imm, RegisterID dest) + void sub32(TrustedImm32 imm, RegisterID dest) { m_assembler.subl_ir(imm.m_value, dest); } - void sub32(Imm32 imm, Address address) + void sub32(TrustedImm32 imm, Address address) { m_assembler.subl_im(imm.m_value, address.offset, address.base); } @@ -333,12 +341,12 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.xorl_rr(src, dest); } - void xor32(Imm32 imm, Address dest) + void xor32(TrustedImm32 imm, Address dest) { m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); } - void xor32(Imm32 imm, RegisterID dest) + void xor32(TrustedImm32 imm, RegisterID dest) { m_assembler.xorl_ir(imm.m_value, dest); } @@ -462,7 +470,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); } - void store32(Imm32 imm, BaseIndex address) + void store32(TrustedImm32 imm, BaseIndex address) { m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); } @@ -477,7 +485,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); } - void store32(Imm32 imm, ImplicitAddress address) + void store32(TrustedImm32 imm, ImplicitAddress address) { m_assembler.movl_i32m(imm.m_value, address.offset, address.base); } @@ -539,7 +547,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { { union { float f; - uint32 u32; + uint32_t u32; } u; u.f = imm.u.d; store32(Imm32(u.u32), address); @@ -549,7 +557,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { { union { float f; - uint32 u32; + uint32_t u32; } u; u.f = imm.u.d; store32(Imm32(u.u32), address); @@ -633,6 +641,21 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.xorpd_rr(src, dest); } + void andDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.andpd_rr(src, dest); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + /* Compile abs(x) as x & -x. */ + zeroDouble(dest); + subDouble(src, dest); + andDouble(src, dest); + } + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -690,6 +713,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) { ASSERT(isSSE2Present()); + ASSERT(src != fpTemp); m_assembler.cvttsd2si_rr(src, dest); // If the result is zero, it might have been -0.0, and the double comparison won't catch this! @@ -742,7 +766,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { // // Move values in registers. - void move(Imm32 imm, RegisterID dest) + void move(TrustedImm32 imm, RegisterID dest) { // Note: on 64-bit the Imm32 value is zero extended into the register, it // may be useful to have a separate version that sign extends the value? @@ -761,7 +785,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.movq_rr(src, dest); } - void move(ImmPtr imm, RegisterID dest) + void move(TrustedImmPtr imm, RegisterID dest) { m_assembler.movq_i64r(imm.asIntptr(), dest); } @@ -792,7 +816,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { m_assembler.movl_rr(src, dest); } - void move(ImmPtr imm, RegisterID dest) + void move(TrustedImmPtr imm, RegisterID dest) { m_assembler.movl_i32r(imm.asIntptr(), dest); } @@ -846,7 +870,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32(Condition cond, RegisterID left, Imm32 right) + Jump branch32(Condition cond, RegisterID left, TrustedImm32 right) { if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) m_assembler.testl_rr(left, left); @@ -858,14 +882,14 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { // Branch based on a 32-bit comparison, forcing the size of the // immediate operand to 32 bits in the native code stream to ensure that // the length of code emitted by this instruction is consistent. - Jump branch32FixedLength(Condition cond, RegisterID left, Imm32 right) + Jump branch32FixedLength(Condition cond, RegisterID left, TrustedImm32 right) { m_assembler.cmpl_ir_force32(right.m_value, left); return Jump(m_assembler.jCC(x86Condition(cond))); } // Branch and record a label after the comparison. - Jump branch32WithPatch(Condition cond, RegisterID left, Imm32 right, DataLabel32 &dataLabel) + Jump branch32WithPatch(Condition cond, RegisterID left, TrustedImm32 right, DataLabel32 &dataLabel) { // Always use cmpl, since the value is to be patched. m_assembler.cmpl_ir_force32(right.m_value, left); @@ -873,7 +897,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32WithPatch(Condition cond, Address left, Imm32 right, DataLabel32 &dataLabel) + Jump branch32WithPatch(Condition cond, Address left, TrustedImm32 right, DataLabel32 &dataLabel) { m_assembler.cmpl_im_force32(right.m_value, left.offset, left.base); dataLabel = DataLabel32(this); @@ -892,19 +916,19 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32(Condition cond, Address left, Imm32 right) + Jump branch32(Condition cond, Address left, TrustedImm32 right) { m_assembler.cmpl_im(right.m_value, left.offset, left.base); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32(Condition cond, BaseIndex left, Imm32 right) + Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right) { m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right) + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right) { return branch32(cond, left, right); } @@ -1313,7 +1337,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { : "%eax", "%ecx", "%edx" ); #endif -#elif WTF_COMPILER_SUNPRO +#elif WTF_COMPILER_SUNCC #if WTF_CPU_X86_64 asm ( "movl $0x1, %%eax;" @@ -1363,7 +1387,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { } #if WTF_CPU_X86 -#if WTF_PLATFORM_MAC +#if WTF_OS_MAC_OS_X // All X86 Macs are guaranteed to support at least SSE2 static bool isSSEPresent() @@ -1376,7 +1400,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler { return true; } -#else // PLATFORM(MAC) +#else // OS(MAC_OS_X) static bool isSSEPresent() { diff --git a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86_64.h b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86_64.h index 7dadc6bcaf2..628c7fdb8a6 100644 --- a/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86_64.h +++ b/deps/mozjs/js/src/assembler/assembler/MacroAssemblerX86_64.h @@ -38,6 +38,8 @@ #define REPTACH_OFFSET_CALL_R11 3 +#include "mozilla/Util.h" + namespace JSC { class MacroAssemblerX86_64 : public MacroAssemblerX86Common { @@ -60,7 +62,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { using MacroAssemblerX86Common::storeDouble; using MacroAssemblerX86Common::convertInt32ToDouble; - void add32(Imm32 imm, AbsoluteAddress address) + void add32(TrustedImm32 imm, AbsoluteAddress address) { move(ImmPtr(address.m_ptr), scratchRegister); add32(imm, Address(scratchRegister)); @@ -72,13 +74,13 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { and32(imm, Address(scratchRegister)); } - void or32(Imm32 imm, AbsoluteAddress address) + void or32(TrustedImm32 imm, AbsoluteAddress address) { move(ImmPtr(address.m_ptr), scratchRegister); or32(imm, Address(scratchRegister)); } - void sub32(Imm32 imm, AbsoluteAddress address) + void sub32(TrustedImm32 imm, AbsoluteAddress address) { move(ImmPtr(address.m_ptr), scratchRegister); sub32(imm, Address(scratchRegister)); @@ -114,7 +116,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { m_assembler.cvtsq2sd_rr(srcDest, dest); } - void store32(Imm32 imm, void* address) + void store32(TrustedImm32 imm, void* address) { move(X86Registers::eax, scratchRegister); move(imm, X86Registers::eax); @@ -124,7 +126,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { Call call() { - DataLabelPtr label = moveWithPatch(ImmPtr(0), scratchRegister); + js::DebugOnly label = moveWithPatch(ImmPtr(0), scratchRegister); Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); ASSERT(differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); return result; @@ -132,7 +134,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { Call tailRecursiveCall() { - DataLabelPtr label = moveWithPatch(ImmPtr(0), scratchRegister); + js::DebugOnly label = moveWithPatch(ImmPtr(0), scratchRegister); Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); ASSERT(differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); return Call::fromTailJump(newJump); @@ -141,7 +143,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { Call makeTailRecursiveCall(Jump oldJump) { oldJump.link(this); - DataLabelPtr label = moveWithPatch(ImmPtr(0), scratchRegister); + js::DebugOnly label = moveWithPatch(ImmPtr(0), scratchRegister); Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); ASSERT(differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); return Call::fromTailJump(newJump); @@ -218,6 +220,11 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { } } + void negPtr(RegisterID srcDest) + { + m_assembler.negq_r(srcDest); + } + void notPtr(RegisterID srcDest) { m_assembler.notq_r(srcDest); @@ -311,7 +318,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { m_assembler.movq_rm(src, address.offset, address.base); } - void storePtr(ImmPtr imm, BaseIndex address) + void storePtr(TrustedImmPtr imm, BaseIndex address) { intptr_t value = intptr_t(imm.m_value); @@ -341,7 +348,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { } } - void storePtr(ImmPtr imm, ImplicitAddress address) + void storePtr(TrustedImmPtr imm, ImplicitAddress address) { intptr_t value = intptr_t(imm.m_value); @@ -424,6 +431,12 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { return branchPtr(cond, Address(scratchRegister), right); } + Jump branchPtr(Condition cond, AbsoluteAddress left, ImmPtr right, RegisterID scratch) + { + move(ImmPtr(left.m_ptr), scratch); + return branchPtr(cond, Address(scratch), right); + } + Jump branchPtr(Condition cond, Address left, RegisterID right) { m_assembler.cmpq_rm(right, left.offset, left.base); @@ -487,7 +500,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { return Jump(m_assembler.jCC(x86Condition(cond))); } - DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest) + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) { m_assembler.movq_i64r(initialValue.asIntptr(), dest); return DataLabelPtr(this); @@ -505,7 +518,7 @@ class MacroAssemblerX86_64 : public MacroAssemblerX86Common { return branchPtr(cond, left, scratchRegister); } - DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); storePtr(scratchRegister, address); diff --git a/deps/mozjs/js/src/assembler/assembler/RepatchBuffer.h b/deps/mozjs/js/src/assembler/assembler/RepatchBuffer.h index 8d28da73586..003479211b4 100644 --- a/deps/mozjs/js/src/assembler/assembler/RepatchBuffer.h +++ b/deps/mozjs/js/src/assembler/assembler/RepatchBuffer.h @@ -110,9 +110,9 @@ class RepatchBuffer { MacroAssembler::repatchInt32(dataLabel32, value); } - void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) + void repatch(CodeLocationDataLabelPtr dataLabelPtr, const void* value) { - MacroAssembler::repatchPointer(dataLabelPtr, value); + MacroAssembler::repatchPointer(dataLabelPtr, (void*) value); } void repatchLoadPtrToLEA(CodeLocationInstruction instruction) diff --git a/deps/mozjs/js/src/assembler/assembler/SparcAssembler.h b/deps/mozjs/js/src/assembler/assembler/SparcAssembler.h index 2e68817ba1d..b508898bc1d 100644 --- a/deps/mozjs/js/src/assembler/assembler/SparcAssembler.h +++ b/deps/mozjs/js/src/assembler/assembler/SparcAssembler.h @@ -42,7 +42,7 @@ #ifndef SparcAssembler_h #define SparcAssembler_h -#include +#include // Some debug code uses s(n)printf for instruction logging. #include @@ -50,7 +50,7 @@ #if ENABLE_ASSEMBLER && WTF_CPU_SPARC #include "AssemblerBufferWithConstantPool.h" -#include +#include #include "methodjit/Logging.h" #define IPFX " %s" @@ -889,6 +889,14 @@ namespace JSC { format_3_8(2, rd, 0x34, 0, 0x2a, rs2); } + void fabsd_r(int rs2, int rd) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "fabsd %s, %s\n", MAYBE_PAD, + nameFpReg(rs2), nameFpReg(rd)); + format_3_8(2, rd, 0x34, 0, 0x0a, rs2); + } + void fnegd_r(int rs2, int rd) { js::JaegerSpew(js::JSpew_Insns, @@ -1037,9 +1045,9 @@ namespace JSC { return reinterpret_cast(reinterpret_cast(code) + destination.m_offset); } - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp) + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind) { - return m_buffer.executableAllocAndCopy(allocator, poolp); + return m_buffer.executableAllocAndCopy(allocator, poolp, kind); } void* executableCopy(void* buffer) @@ -1108,19 +1116,24 @@ namespace JSC { static void relinkCall(void* from, void* to) { js::JaegerSpew(js::JSpew_Insns, - ISPFX "##linkCall ((from=%p)) ((to=%p))\n", + ISPFX "##relinkCall ((from=%p)) ((to=%p))\n", from, to); - int disp = ((int)to - (int)from)/4; - *(uint32_t *)((int)from) &= 0x40000000; - *(uint32_t *)((int)from) |= disp & 0x3fffffff; - ExecutableAllocator::cacheFlush(from, 4); + void * where= (void *)((intptr_t)from - 20); + patchPointerInternal(where, (int)to); + ExecutableAllocator::cacheFlush(where, 8); } static void linkCall(void* code, JmpSrc where, void* to) { void *from = (void *)((intptr_t)code + where.m_offset); - relinkCall(from, to); + js::JaegerSpew(js::JSpew_Insns, + ISPFX "##linkCall ((from=%p)) ((to=%p))\n", + from, to); + int disp = ((int)to - (int)from)/4; + *(uint32_t *)((int)from) &= 0x40000000; + *(uint32_t *)((int)from) |= disp & 0x3fffffff; + ExecutableAllocator::cacheFlush(from, 4); } static void linkPointer(void* code, JmpDst where, void* value) diff --git a/deps/mozjs/js/src/assembler/assembler/X86Assembler.h b/deps/mozjs/js/src/assembler/assembler/X86Assembler.h index fb12f78f478..1ed088e0633 100644 --- a/deps/mozjs/js/src/assembler/assembler/X86Assembler.h +++ b/deps/mozjs/js/src/assembler/assembler/X86Assembler.h @@ -35,9 +35,8 @@ #if ENABLE_ASSEMBLER && (WTF_CPU_X86 || WTF_CPU_X86_64) #include "AssemblerBuffer.h" -#include "jsstdint.h" #include "assembler/wtf/Assertions.h" -#include "jsvector.h" +#include "js/Vector.h" #include "methodjit/Logging.h" #define IPFX " %s" @@ -226,6 +225,7 @@ class X86Assembler { OP_MOV_OvEAX = 0xA3, OP_MOV_EAXIv = 0xB8, OP_GROUP2_EvIb = 0xC1, + OP_RET_Iz = 0xC2, OP_RET = 0xC3, OP_GROUP11_EvIb = 0xC6, OP_GROUP11_EvIz = 0xC7, @@ -257,6 +257,7 @@ class X86Assembler { OP2_SUBSD_VsdWsd = 0x5C, OP2_DIVSD_VsdWsd = 0x5E, OP2_SQRTSD_VsdWsd = 0x51, + OP2_ANDPD_VpdWpd = 0x54, OP2_XORPD_VpdWpd = 0x57, OP2_MOVD_VdEd = 0x6E, OP2_PSRLDQ_Vd = 0x73, @@ -690,6 +691,13 @@ class X86Assembler { } #if WTF_CPU_X86_64 + void negq_r(RegisterID dst) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "negq %s\n", MAYBE_PAD, nameIReg(8,dst)); + m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } + void orq_rr(RegisterID src, RegisterID dst) { js::JaegerSpew(js::JSpew_Insns, @@ -981,7 +989,9 @@ class X86Assembler { void imull_i32r(RegisterID src, int32_t value, RegisterID dst) { - FIXME_INSN_PRINTING; + js::JaegerSpew(js::JSpew_Insns, + IPFX "imull %d, %s, %s\n", + MAYBE_PAD, value, nameIReg(4, src), nameIReg(4, dst)); m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); m_formatter.immediate32(value); } @@ -1563,8 +1573,8 @@ class X86Assembler { void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { js::JaegerSpew(js::JSpew_Insns, - IPFX "movq %s0x%x(%s), %s\n", MAYBE_PAD, - PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst)); + IPFX "movq %d(%s,%s,%d), %s\n", MAYBE_PAD, + offset, nameIReg(base), nameIReg(index), scale, nameIReg(8,dst)); m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); } @@ -2250,6 +2260,15 @@ class X86Assembler { m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); } + void andpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "andpd %s, %s\n", MAYBE_PAD, + nameFPReg(src), nameFPReg(dst)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_ANDPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) { js::JaegerSpew(js::JSpew_Insns, @@ -2276,13 +2295,22 @@ class X86Assembler { js::JaegerSpew(js::JSpew_Insns, IPFX "int3\n", MAYBE_PAD); m_formatter.oneByteOp(OP_INT3); } - + void ret() { js::JaegerSpew(js::JSpew_Insns, IPFX "ret\n", MAYBE_PAD); m_formatter.oneByteOp(OP_RET); } + void ret(int imm) + { + js::JaegerSpew(js::JSpew_Insns, + IPFX "ret %d\n", MAYBE_PAD, + imm); + m_formatter.oneByteOp(OP_RET_Iz); + m_formatter.immediate16(imm); + } + void predictNotTaken() { FIXME_INSN_PRINTING; @@ -2474,9 +2502,9 @@ class X86Assembler { return dst.m_offset - src.m_offset; } - void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp) + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind) { - return m_formatter.executableAllocAndCopy(allocator, poolp); + return m_formatter.executableAllocAndCopy(allocator, poolp, kind); } void executableCopy(void* buffer) @@ -2826,8 +2854,8 @@ class X86Assembler { 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* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) { - return m_buffer.executableAllocAndCopy(allocator, poolp); + void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) { + return m_buffer.executableAllocAndCopy(allocator, poolp, kind); } private: @@ -2918,10 +2946,11 @@ class X86Assembler { { // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. #if WTF_CPU_X86_64 - if ((base == hasSib) || (base == hasSib2)) { + if ((base == hasSib) || (base == hasSib2)) #else - if (base == hasSib) { + if (base == hasSib) #endif + { if (!offset) // No need to check if the base is noBase, since we know it is hasSib! putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); else if (CAN_SIGN_EXTEND_8_32(offset)) { @@ -2952,10 +2981,11 @@ class X86Assembler { { // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. #if WTF_CPU_X86_64 - if ((base == hasSib) || (base == hasSib2)) { + if ((base == hasSib) || (base == hasSib2)) #else - if (base == hasSib) { + if (base == hasSib) #endif + { putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); m_buffer.putIntUnchecked(offset); } else { diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.cpp b/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.cpp index 14c747915ff..f01cfb33a75 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.cpp +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.cpp @@ -27,6 +27,8 @@ #if ENABLE_ASSEMBLER +#include "prmjtime.h" + namespace JSC { size_t ExecutableAllocator::pageSize = 0; @@ -37,15 +39,19 @@ ExecutablePool::~ExecutablePool() m_allocator->releasePoolPages(this); } -size_t -ExecutableAllocator::getCodeSize() const +void +ExecutableAllocator::sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const { - size_t n = 0; + *method = 0; + *regexp = 0; + *unused = 0; + for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) { ExecutablePool* pool = r.front(); - n += pool->m_allocation.size; + *method += pool->m_mjitCodeMethod; + *regexp += pool->m_mjitCodeRegexp; + *unused += pool->m_allocation.size - pool->m_mjitCodeMethod - pool->m_mjitCodeRegexp; } - return n; } } diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.h b/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.h index 02b4f8a6d48..25cf4913aea 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.h +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocator.h @@ -28,13 +28,14 @@ #include // for ptrdiff_t #include -#include "assembler/wtf/Assertions.h" +#include "jsalloc.h" #include "jsapi.h" -#include "jshashtable.h" #include "jsprvtd.h" -#include "jsvector.h" -#include "jslock.h" + +#include "assembler/wtf/Assertions.h" +#include "js/HashTable.h" +#include "js/Vector.h" #if WTF_CPU_SPARC #ifdef linux // bugzilla 502369 @@ -52,16 +53,16 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len); #endif #endif -#if WTF_PLATFORM_IPHONE +#if WTF_OS_IOS #include #include #endif -#if WTF_PLATFORM_SYMBIAN +#if WTF_OS_SYMBIAN #include #endif -#if WTF_CPU_MIPS && WTF_PLATFORM_LINUX +#if WTF_CPU_MIPS && WTF_OS_LINUX #include #endif @@ -81,6 +82,8 @@ namespace JSC { class ExecutableAllocator; + enum CodeKind { METHOD_CODE, REGEXP_CODE }; + // These are reference-counted. A new one starts with a count of 1. class ExecutablePool { @@ -90,7 +93,7 @@ namespace JSC { struct Allocation { char* pages; size_t size; -#if WTF_PLATFORM_SYMBIAN +#if WTF_OS_SYMBIAN RChunk* chunk; #endif }; @@ -102,6 +105,10 @@ namespace JSC { // Reference count for automatic reclamation. unsigned m_refCount; + + // Number of bytes currently used for Method and Regexp JIT code. + size_t m_mjitCodeMethod; + size_t m_mjitCodeRegexp; public: // Flag for downstream use, whether to try to release references to this pool. @@ -114,7 +121,8 @@ namespace JSC { void release(bool willDestroy = false) { JS_ASSERT(m_refCount != 0); - JS_ASSERT_IF(willDestroy, m_refCount = 1); + // XXX: disabled, see bug 654820. + //JS_ASSERT_IF(willDestroy, m_refCount == 1); if (--m_refCount == 0) { js::UnwantedForeground::delete_(this); } @@ -132,16 +140,22 @@ namespace JSC { ExecutablePool(ExecutableAllocator* allocator, Allocation a) : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), - m_refCount(1), m_destroy(false), m_gcNumber(0) + m_refCount(1), m_mjitCodeMethod(0), m_mjitCodeRegexp(0), m_destroy(false), m_gcNumber(0) { } ~ExecutablePool(); - void* alloc(size_t n) + void* alloc(size_t n, CodeKind kind) { JS_ASSERT(n <= available()); void *result = m_freePtr; m_freePtr += n; + + if ( kind == REGEXP_CODE ) + m_mjitCodeRegexp += n; + else + m_mjitCodeMethod += n; + return result; } @@ -151,11 +165,23 @@ namespace JSC { } }; +enum AllocationBehavior +{ + AllocationCanRandomize, + AllocationDeterministic +}; + class ExecutableAllocator { + typedef void (*DestroyCallback)(void* addr, size_t size); enum ProtectionSetting { Writable, Executable }; + DestroyCallback destroyCallback; + + void initSeed(); public: - ExecutableAllocator() + explicit ExecutableAllocator(AllocationBehavior allocBehavior) + : destroyCallback(NULL), + allocBehavior(allocBehavior) { if (!pageSize) { pageSize = determinePageSize(); @@ -170,6 +196,10 @@ class ExecutableAllocator { largeAllocSize = pageSize * 16; } +#if WTF_OS_WINDOWS + initSeed(); +#endif + JS_ASSERT(m_smallPools.empty()); } @@ -177,13 +207,14 @@ class ExecutableAllocator { { for (size_t i = 0; i < m_smallPools.length(); i++) m_smallPools[i]->release(/* willDestroy = */true); - JS_ASSERT(m_pools.empty()); // if this asserts we have a pool leak + // XXX: temporarily disabled because it fails; see bug 654820. + //JS_ASSERT(m_pools.empty()); // if this asserts we have a pool leak } // 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) + void* alloc(size_t n, ExecutablePool** poolp, CodeKind type) { // Round 'n' up to a multiple of word size; if all allocations are of // word sized quantities, then all subsequent allocations will be @@ -200,22 +231,35 @@ class ExecutableAllocator { // This alloc is infallible because poolForSize() just obtained // (found, or created if necessary) a pool that had enough space. - void *result = (*poolp)->alloc(n); + void *result = (*poolp)->alloc(n, type); JS_ASSERT(result); return result; } void releasePoolPages(ExecutablePool *pool) { JS_ASSERT(pool->m_allocation.pages); + if (destroyCallback) + destroyCallback(pool->m_allocation.pages, pool->m_allocation.size); systemRelease(pool->m_allocation); m_pools.remove(m_pools.lookup(pool)); // this asserts if |pool| is not in m_pools } - size_t getCodeSize() const; + void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const; + + void setDestroyCallback(DestroyCallback destroyCallback) { + this->destroyCallback = destroyCallback; + } + + void setRandomize(bool enabled) { + allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic; + } private: static size_t pageSize; static size_t largeAllocSize; +#if WTF_OS_WINDOWS + static int64_t rngSeed; +#endif static const size_t OVERSIZE_ALLOCATION = size_t(-1); @@ -238,8 +282,9 @@ class ExecutableAllocator { } // On OOM, this will return an Allocation where pages is NULL. - static ExecutablePool::Allocation systemAlloc(size_t n); + ExecutablePool::Allocation systemAlloc(size_t n); static void systemRelease(const ExecutablePool::Allocation& alloc); + void *computeRandomAllocationAddress(); ExecutablePool* createPool(size_t n) { @@ -267,6 +312,7 @@ class ExecutableAllocator { return pool; } +public: ExecutablePool* poolForSize(size_t n) { #ifndef DEBUG_STRESS_JSC_ALLOCATOR @@ -325,7 +371,6 @@ class ExecutableAllocator { return pool; } -public: #if ENABLE_ASSEMBLER_WX_EXCLUSIVE static void makeWritable(void* start, size_t size) { @@ -356,7 +401,7 @@ class ExecutableAllocator { // // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in // mips_expand_synci_loop that may execute synci one more time. - // "start" points to the fisrt byte of the cache line. + // "start" points to the first byte of the cache line. // "end" points to the last byte of the line before the last cache line. // Because size is always a multiple of 4, this is safe to set // "end" to the last byte. @@ -372,13 +417,13 @@ class ExecutableAllocator { _flush_cache(reinterpret_cast(code), size, BCACHE); #endif } -#elif WTF_CPU_ARM_THUMB2 && WTF_PLATFORM_IPHONE +#elif WTF_CPU_ARM && WTF_OS_IOS static void cacheFlush(void* code, size_t size) { sys_dcache_flush(code, size); sys_icache_invalidate(code, size); } -#elif WTF_CPU_ARM_THUMB2 && WTF_PLATFORM_LINUX +#elif WTF_CPU_ARM_THUMB2 && WTF_IOS static void cacheFlush(void* code, size_t size) { asm volatile ( @@ -394,14 +439,14 @@ class ExecutableAllocator { : "r" (code), "r" (reinterpret_cast(code) + size) : "r0", "r1", "r2"); } -#elif WTF_PLATFORM_SYMBIAN +#elif WTF_OS_SYMBIAN static void cacheFlush(void* code, size_t size) { User::IMB_Range(code, static_cast(code) + size); } -#elif WTF_CPU_ARM_TRADITIONAL && WTF_PLATFORM_LINUX && WTF_COMPILER_RVCT +#elif WTF_CPU_ARM_TRADITIONAL && WTF_OS_LINUX && WTF_COMPILER_RVCT static __asm void cacheFlush(void* code, size_t size); -#elif WTF_CPU_ARM_TRADITIONAL && (WTF_PLATFORM_LINUX || WTF_PLATFORM_ANDROID) && WTF_COMPILER_GCC +#elif WTF_CPU_ARM_TRADITIONAL && (WTF_OS_LINUX || WTF_OS_ANDROID) && WTF_COMPILER_GCC static void cacheFlush(void* code, size_t size) { asm volatile ( @@ -443,6 +488,7 @@ class ExecutableAllocator { typedef js::HashSet, js::SystemAllocPolicy> ExecPoolHashSet; ExecPoolHashSet m_pools; // All pools, just for stats purposes. + AllocationBehavior allocBehavior; static size_t determinePageSize(); }; diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorOS2.cpp b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorOS2.cpp index ef9e27d92b4..b0879c8ac69 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorOS2.cpp +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorOS2.cpp @@ -26,7 +26,7 @@ #include "ExecutableAllocator.h" -#if ENABLE_ASSEMBLER && WTF_PLATFORM_OS2 +#if ENABLE_ASSEMBLER && WTF_OS_OS2 #define INCL_DOS #include @@ -44,7 +44,7 @@ ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n) if (DosAllocMem(&allocation, n, OBJ_ANY|PAG_COMMIT|PAG_READ|PAG_WRITE) && DosAllocMem(&allocation, n, PAG_COMMIT|PAG_READ|PAG_WRITE)) CRASH(); - ExecutablePool::Allocation alloc = {reinterpret_cast(allocation), n}; + ExecutablePool::Allocation alloc = { reinterpret_cast(allocation), n }; return alloc; } diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorPosix.cpp b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorPosix.cpp index 50efd932e02..e334626ccc2 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorPosix.cpp +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorPosix.cpp @@ -25,7 +25,7 @@ #include "ExecutableAllocator.h" -#if ENABLE_ASSEMBLER && WTF_PLATFORM_UNIX && !WTF_PLATFORM_SYMBIAN +#if ENABLE_ASSEMBLER && WTF_OS_UNIX && !WTF_OS_SYMBIAN #include #include @@ -74,7 +74,7 @@ void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSe } #endif -#if WTF_CPU_ARM_TRADITIONAL && WTF_PLATFORM_LINUX && WTF_COMPILER_RVCT +#if WTF_CPU_ARM_TRADITIONAL && WTF_OS_LINUX && WTF_COMPILER_RVCT __asm void ExecutableAllocator::cacheFlush(void* code, size_t size) { ARM diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorSymbian.cpp b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorSymbian.cpp index c66fa80fff1..f51c0d50787 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorSymbian.cpp +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorSymbian.cpp @@ -22,7 +22,7 @@ #include "ExecutableAllocator.h" -#if ENABLE_ASSEMBLER && WTF_PLATFORM_SYMBIAN +#if ENABLE_ASSEMBLER && WTF_OS_SYMBIAN #include #include diff --git a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorWin.cpp b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorWin.cpp index f5775608f36..457ee5de146 100644 --- a/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorWin.cpp +++ b/deps/mozjs/js/src/assembler/jit/ExecutableAllocatorWin.cpp @@ -20,18 +20,28 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "ExecutableAllocator.h" -#if ENABLE_ASSEMBLER && WTF_PLATFORM_WIN_OS +#if ENABLE_ASSEMBLER && WTF_OS_WINDOWS #include "jswin.h" +#include "prmjtime.h" + +extern void random_setSeed(int64_t *, int64_t); +extern uint64_t random_next(int64_t *, int); namespace JSC { +int64_t ExecutableAllocator::rngSeed; + +void ExecutableAllocator::initSeed() +{ + random_setSeed(&rngSeed, (PRMJ_Now() / 1000) ^ int64_t(this)); +} + size_t ExecutableAllocator::determinePageSize() { SYSTEM_INFO system_info; @@ -39,16 +49,72 @@ size_t ExecutableAllocator::determinePageSize() return system_info.dwPageSize; } +void *ExecutableAllocator::computeRandomAllocationAddress() +{ + /* + * Inspiration is V8's OS::Allocate in platform-win32.cc. + * + * VirtualAlloc takes 64K chunks out of the virtual address space, so we + * keep 16b alignment. + * + * x86: V8 comments say that keeping addresses in the [64MiB, 1GiB) range + * tries to avoid system default DLL mapping space. In the end, we get 13 + * bits of randomness in our selection. + * x64: [2GiB, 4TiB), with 25 bits of randomness. + */ + static const uintN chunkBits = 16; +#if WTF_CPU_X86_64 + static const uintptr_t base = 0x0000000080000000; + static const uintptr_t mask = 0x000003ffffff0000; +#elif WTF_CPU_X86 + static const uintptr_t base = 0x04000000; + static const uintptr_t mask = 0x3fff0000; +#else +# error "Unsupported architecture" +#endif + uint64_t rand = random_next(&rngSeed, 32) << chunkBits; + return (void *) (base | rand & mask); +} + +static bool +RandomizeIsBrokenImpl() +{ + OSVERSIONINFO osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + // Version number mapping is available at: + // http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx + // We disable everything before Vista, for now. + return osvi.dwMajorVersion <= 5; +} + +static bool +RandomizeIsBroken() +{ + static int result = RandomizeIsBrokenImpl(); + return !!result; +} + ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n) { - void *allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - ExecutablePool::Allocation alloc = {reinterpret_cast(allocation), n}; + void *allocation = NULL; + if (allocBehavior == AllocationCanRandomize && !RandomizeIsBroken()) { + void *randomAddress = computeRandomAllocationAddress(); + allocation = VirtualAlloc(randomAddress, n, MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + } + if (!allocation) + allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + ExecutablePool::Allocation alloc = { reinterpret_cast(allocation), n }; return alloc; } void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc) -{ - VirtualFree(alloc.pages, 0, MEM_RELEASE); +{ + VirtualFree(alloc.pages, 0, MEM_RELEASE); } #if ENABLE_ASSEMBLER_WX_EXCLUSIVE diff --git a/deps/mozjs/js/src/assembler/wtf/Assertions.h b/deps/mozjs/js/src/assembler/wtf/Assertions.h index 6c9da1e2e8f..537737a5b82 100644 --- a/deps/mozjs/js/src/assembler/wtf/Assertions.h +++ b/deps/mozjs/js/src/assembler/wtf/Assertions.h @@ -26,6 +26,18 @@ #ifndef WTF_Assertions_h #define WTF_Assertions_h +#include "Platform.h" +#include "mozilla/Assertions.h" + +#define ASSERT(assertion) MOZ_ASSERT(assertion) +#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) +#define ASSERT_NOT_REACHED() MOZ_NOT_REACHED("") +#define CRASH() MOZ_Crash() +#define COMPILE_ASSERT(exp, name) MOZ_STATIC_ASSERT(exp, #name) + +#endif + +#if 0 /* no namespaces because this file has to be includable from C and Objective-C diff --git a/deps/mozjs/js/src/assembler/wtf/Platform.h b/deps/mozjs/js/src/assembler/wtf/Platform.h index 68713cd4c81..6570f2d9e1e 100644 --- a/deps/mozjs/js/src/assembler/wtf/Platform.h +++ b/deps/mozjs/js/src/assembler/wtf/Platform.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,206 +28,267 @@ #ifndef WTF_Platform_h #define WTF_Platform_h -/* Either XX(YY) --> WTF_XX_YY or XX(YY) --> XX_YY, depending on XX - - PLATFORM(YY) --> WTF_PLATFORM_YY - COMPILER(YY) --> WTF_COMPILER_YY - CPU(YY) --> WTF_CPU_YY - OS(YY) --> WTF_OS_YY - USE(YY) --> WTF_USE_YY - - HAVE(YY) --> HAVE_YY - ENABLE(YY) --> ENABLE_YY -*/ - /* ==== PLATFORM handles OS, operating environment, graphics API, and CPU. This macro will be phased out in favor of platform adaptation macros, policy decision macros, and top-level port definitions. ==== */ -//#define PLATFORM(WTF_FEATURE) (defined(WTF_PLATFORM_##WTF_FEATURE) && WTF_PLATFORM_##WTF_FEATURE) +#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) /* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ /* COMPILER() - the compiler being used to build the project */ -//#define COMPILER(WTF_FEATURE) (defined(WTF_COMPILER_##WTF_FEATURE) && WTF_COMPILER_##WTF_FEATURE) +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) /* CPU() - the target CPU architecture */ -//#define CPU(WTF_FEATURE) (defined(WTF_CPU_##WTF_FEATURE) && WTF_CPU_##WTF_FEATURE) +#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) /* HAVE() - specific system features (headers, functions or similar) that are present or not */ -//#define HAVE(WTF_FEATURE) (defined(HAVE_##WTF_FEATURE) && HAVE_##WTF_FEATURE) +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) /* OS() - underlying operating system; only to be used for mandated low-level services like virtual memory, not to choose a GUI toolkit */ -//#define OS(WTF_FEATURE) (defined(WTF_OS_##WTF_FEATURE) && WTF_OS_##WTF_FEATURE) +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) /* ==== Policy decision macros: these define policy choices for a particular port. ==== */ /* USE() - use a particular third-party library or optional OS service */ -//#define USE(WTF_FEATURE) (defined(WTF_USE_##WTF_FEATURE) && WTF_USE_##WTF_FEATURE) +#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) /* ENABLE() - turn on a specific feature of WebKit */ -//#define ENABLE(WTF_FEATURE) (defined(ENABLE_##WTF_FEATURE) && ENABLE_##WTF_FEATURE) +#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) /* ==== COMPILER() - the compiler being used to build the project ==== */ -/* COMPILER(MSVC) Microsoft Visual C++ */ -/* COMPILER(MSVC7) Microsoft Visual C++ v7 or lower*/ +/* WTF_COMPILER_MSVC Microsoft Visual C++ */ +/* WTF_COMPILER_MSVC7_OR_LOWER Microsoft Visual C++ 2003 or lower*/ +/* WTF_COMPILER_MSVC9_OR_LOWER Microsoft Visual C++ 2008 or lower*/ #if defined(_MSC_VER) #define WTF_COMPILER_MSVC 1 #if _MSC_VER < 1400 -#define WTF_COMPILER_MSVC7 1 +#define WTF_COMPILER_MSVC7_OR_LOWER 1 +#elif _MSC_VER < 1600 +#define WTF_COMPILER_MSVC9_OR_LOWER 1 #endif #endif -/* COMPILER(RVCT) - ARM RealView Compilation Tools */ +/* WTF_COMPILER_RVCT - ARM RealView Compilation Tools */ +/* WTF_COMPILER_RVCT4_OR_GREATER - ARM RealView Compilation Tools 4.0 or greater */ #if defined(__CC_ARM) || defined(__ARMCC__) #define WTF_COMPILER_RVCT 1 +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) +#else +/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 #endif -/* COMPILER(GCC) - GNU Compiler Collection */ +/* WTF_COMPILER_GCC - GNU Compiler Collection */ /* --gnu option of the RVCT compiler also defines __GNUC__ */ #if defined(__GNUC__) && !WTF_COMPILER_RVCT #define WTF_COMPILER_GCC 1 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 #endif -/* COMPILER(MINGW) - MinGW GCC */ -#if defined(MINGW) || defined(__MINGW32__) +/* WTF_COMPILER_MINGW - MinGW GCC */ +/* WTF_COMPILER_MINGW64 - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ +#if defined(__MINGW32__) #define WTF_COMPILER_MINGW 1 -#endif +#include <_mingw.h> /* private MinGW header */ + #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ + #define WTF_COMPILER_MINGW64 1 + #endif /* __MINGW64_VERSION_MAJOR */ +#endif /* __MINGW32__ */ -/* COMPILER(WINSCW) - CodeWarrior for Symbian emulator */ +/* WTF_COMPILER_WINSCW - CodeWarrior for Symbian emulator */ #if defined(__WINSCW__) #define WTF_COMPILER_WINSCW 1 +/* cross-compiling, it is not really windows */ +#undef WIN32 +#undef _WIN32 #endif -/* COMPILER(SUNPRO) - Sun Studio for Solaris */ -#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) -#define WTF_COMPILER_SUNPRO 1 +/* WTF_COMPILER_INTEL - Intel C++ Compiler */ +#if defined(__INTEL_COMPILER) +#define WTF_COMPILER_INTEL 1 #endif +/* WTF_COMPILER_SUNCC */ +#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) +#define WTF_COMPILER_SUNCC 1 +#endif /* ==== CPU() - the target CPU architecture ==== */ -/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ - +/* This also defines WTF_CPU_BIG_ENDIAN or WTF_CPU_MIDDLE_ENDIAN or neither, as appropriate. */ -/* CPU(ALPHA) - DEC Alpha */ +/* WTF_CPU_ALPHA - DEC Alpha */ #if defined(__alpha__) #define WTF_CPU_ALPHA 1 #endif -/* CPU(IA64) - Itanium / IA-64 */ +/* WTF_CPU_IA64 - Itanium / IA-64 */ #if defined(__ia64__) #define WTF_CPU_IA64 1 +/* 32-bit mode on Itanium */ +#if !defined(__LP64__) +#define WTF_CPU_IA64_32 1 +#endif #endif -/* CPU(PPC) - PowerPC 32-bit */ +/* WTF_CPU_MIPS - MIPS 32-bit */ +/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ +#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ + && defined(_ABIO32) +#define WTF_CPU_MIPS 1 +#if defined(__MIPSEB__) +#define WTF_CPU_BIG_ENDIAN 1 +#endif +#define WTF_MIPS_PIC (defined __PIC__) +#define WTF_MIPS_ARCH __mips +#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) +#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) +#define WTF_MIPS_ARCH_REV __mips_isa_rev +#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) +#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) +#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) +/* MIPS requires allocators to use aligned memory */ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif /* MIPS */ + +/* WTF_CPU_PPC - PowerPC 32-bit */ #if defined(__ppc__) \ - || defined(__PPC__) \ - || defined(__powerpc__) \ - || defined(__powerpc) \ - || defined(__POWERPC__) \ - || defined(_M_PPC) \ - || defined(__PPC) + || defined(__PPC__) \ + || defined(__powerpc__) \ + || defined(__powerpc) \ + || defined(__POWERPC__) \ + || defined(_M_PPC) \ + || defined(__PPC) #define WTF_CPU_PPC 1 #define WTF_CPU_BIG_ENDIAN 1 #endif -/* CPU(PPC64) - PowerPC 64-bit */ +/* WTF_CPU_PPC64 - PowerPC 64-bit */ #if defined(__ppc64__) \ - || defined(__PPC64__) + || defined(__PPC64__) #define WTF_CPU_PPC64 1 #define WTF_CPU_BIG_ENDIAN 1 #endif -/* CPU(SH4) - SuperH SH-4 */ +/* WTF_CPU_SH4 - SuperH SH-4 */ #if defined(__SH4__) #define WTF_CPU_SH4 1 #endif -/* CPU(SPARC32) - SPARC 32-bit */ +/* WTF_CPU_SPARC32 - SPARC 32-bit */ #if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) #define WTF_CPU_SPARC32 1 #define WTF_CPU_BIG_ENDIAN 1 #endif -/* CPU(SPARC64) - SPARC 64-bit */ +/* WTF_CPU_SPARC64 - SPARC 64-bit */ #if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) #define WTF_CPU_SPARC64 1 #define WTF_CPU_BIG_ENDIAN 1 #endif -/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ +/* WTF_CPU_SPARC - any SPARC, true for WTF_CPU_SPARC32 and WTF_CPU_SPARC64 */ #if WTF_CPU_SPARC32 || WTF_CPU_SPARC64 #define WTF_CPU_SPARC 1 #endif -/* CPU(X86) - i386 / x86 32-bit */ +/* WTF_CPU_S390X - S390 64-bit */ +#if defined(__s390x__) +#define WTF_CPU_S390X 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* WTF_CPU_S390 - S390 32-bit */ +#if defined(__s390__) +#define WTF_CPU_S390 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* WTF_CPU_X86 - i386 / x86 32-bit */ #if defined(__i386__) \ - || defined(i386) \ - || defined(_M_IX86) \ - || defined(_X86_) \ - || defined(__THW_INTEL) + || defined(i386) \ + || defined(_M_IX86) \ + || defined(_X86_) \ + || defined(__THW_INTEL) #define WTF_CPU_X86 1 #endif -/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ +/* WTF_CPU_X86_64 - AMD64 / Intel64 / x86_64 64-bit */ #if defined(__x86_64__) \ - || defined(_M_X64) + || defined(_M_X64) #define WTF_CPU_X86_64 1 #endif -/* CPU(ARM) - ARM, any version*/ +/* WTF_CPU_ARM - ARM, any version*/ #if defined(arm) \ - || defined(__arm__) + || defined(__arm__) \ + || defined(ARM) \ + || defined(_ARM_) #define WTF_CPU_ARM 1 -#if defined(__ARMEB__) +#if defined(__ARMEB__) || (WTF_COMPILER_RVCT && defined(__BIG_ENDIAN)) #define WTF_CPU_BIG_ENDIAN 1 #elif !defined(__ARM_EABI__) \ - && !defined(__EABI__) \ - && !defined(__VFP_FP__) \ - && !defined(ANDROID) + && !defined(__EABI__) \ + && !defined(__VFP_FP__) \ + && !defined(_WIN32_WCE) \ + && !defined(ANDROID) #define WTF_CPU_MIDDLE_ENDIAN 1 #endif -#define WTF_ARM_ARCH_AT_LEAST(N) (WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= N) +#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) /* Set WTF_ARM_ARCH_VERSION */ #if defined(__ARM_ARCH_4__) \ - || defined(__ARM_ARCH_4T__) \ - || defined(__MARM_ARMV4__) \ - || defined(_ARMV4I_) + || defined(__ARM_ARCH_4T__) \ + || defined(__MARM_ARMV4__) \ + || defined(_ARMV4I_) #define WTF_ARM_ARCH_VERSION 4 #elif defined(__ARM_ARCH_5__) \ - || defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5E__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) \ - || defined(__MARM_ARMV5__) + || defined(__ARM_ARCH_5T__) \ + || defined(__MARM_ARMV5__) #define WTF_ARM_ARCH_VERSION 5 +#elif defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_ARM_ARCH_VERSION 5 +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 + #elif defined(__ARM_ARCH_6__) \ - || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6T2__) \ - || defined(__ARMV6__) + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARMV6__) #define WTF_ARM_ARCH_VERSION 6 #elif defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) + || defined(__ARM_ARCH_7R__) #define WTF_ARM_ARCH_VERSION 7 /* RVCT sets _TARGET_ARCH_ARM */ #elif defined(__TARGET_ARCH_ARM) #define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM +#if defined(__TARGET_ARCH_5E) \ + || defined(__TARGET_ARCH_5TE) \ + || defined(__TARGET_ARCH_5TEJ) +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif + #else #define WTF_ARM_ARCH_VERSION 0 @@ -237,22 +299,22 @@ #define WTF_THUMB_ARCH_VERSION 1 #elif defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) #define WTF_THUMB_ARCH_VERSION 2 #elif defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6M__) + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) #define WTF_THUMB_ARCH_VERSION 3 #elif defined(__ARM_ARCH_6T2__) \ - || defined(__ARM_ARCH_7__) \ - || defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7M__) + || defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) #define WTF_THUMB_ARCH_VERSION 4 /* RVCT sets __TARGET_ARCH_THUMB */ @@ -264,23 +326,23 @@ #endif -/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ +/* WTF_CPU_ARMV5_OR_LOWER - ARM instruction set v5 or earlier */ /* On ARMv5 and below the natural alignment is required. And there are some other differences for v5 or earlier. */ -#if !defined(ARMV5_OR_LOWER) /* && !CPU_ARM_ARCH_AT_LEAST(6) */ +#if !defined(ARMV5_OR_LOWER) && WTF_CPU_ARM && !(WTF_ARM_ARCH_VERSION >= 6) #define WTF_CPU_ARMV5_OR_LOWER 1 #endif -/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ -/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ +/* WTF_CPU_ARM_TRADITIONAL - Thumb2 is not available, only traditional ARM (v4 or greater) */ +/* WTF_CPU_ARM_THUMB2 - Thumb2 instruction set is available */ /* Only one of these will be defined. */ #if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) # if defined(thumb2) || defined(__thumb2__) \ - || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) + || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) # define WTF_CPU_ARM_TRADITIONAL 1 # define WTF_CPU_ARM_THUMB2 0 -# elif WTF_ARM_ARCH_AT_LEAST(4) +# elif WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= 4 # define WTF_CPU_ARM_TRADITIONAL 1 # define WTF_CPU_ARM_THUMB2 0 # else @@ -288,19 +350,36 @@ # endif #elif WTF_CPU_ARM_TRADITIONAL && WTF_CPU_ARM_THUMB2 /* Sanity Check */ # error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" -#endif // !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) +#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ + +#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) +#define WTF_CPU_ARM_NEON 1 +#endif #endif /* ARM */ +#if WTF_CPU_ARM || WTF_CPU_MIPS +#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 +#endif + +/* ==== OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit ==== */ +/* WTF_OS_ANDROID - Android */ +#ifdef ANDROID +#define WTF_OS_ANDROID 1 +#endif -/* Operating systems - low-level dependencies */ +/* WTF_OS_AIX - AIX */ +#ifdef _AIX +#define WTF_OS_AIX 1 +#endif -/* PLATFORM(DARWIN) */ -/* Operating system level dependencies for Mac OS X / Darwin that should */ -/* be used regardless of operating environment */ +/* WTF_OS_DARWIN - Any Darwin-based OS, including Mac OS X and iPhone OS */ #ifdef __APPLE__ -#define WTF_PLATFORM_DARWIN 1 +#define WTF_OS_DARWIN 1 + +/* FIXME: BUILDING_ON_.., and TARGETING... macros should be folded into the OS() system */ #include #if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 #define BUILDING_ON_TIGER 1 @@ -317,98 +396,103 @@ #define TARGETING_SNOW_LEOPARD 1 #endif #include + #endif -/* PLATFORM(WIN_OS) */ -/* Operating system level dependencies for Windows that should be used */ -/* regardless of operating environment */ -#if defined(WIN32) || defined(_WIN32) -#define WTF_PLATFORM_WIN_OS 1 +/* WTF_OS_IOS - iOS */ +/* WTF_OS_MAC_OS_X - Mac OS X (not including iOS) */ +#if WTF_OS_DARWIN && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ + || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ + || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) +#define WTF_OS_IOS 1 +#elif WTF_OS_DARWIN && defined(TARGET_OS_MAC) && TARGET_OS_MAC +#define WTF_OS_MAC_OS_X 1 +#endif + + +/* WTF_OS_FREEBSD - FreeBSD */ +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define WTF_OS_FREEBSD 1 #endif -/* PLATFORM(LINUX) */ -/* Operating system level dependencies for Linux-like systems that */ -/* should be used regardless of operating environment */ -#ifdef __linux__ -#define WTF_PLATFORM_LINUX 1 +/* WTF_OS_HAIKU - Haiku */ +#ifdef __HAIKU__ +#define WTF_OS_HAIKU 1 #endif -/* PLATFORM(FREEBSD) */ -/* Operating system level dependencies for FreeBSD-like systems that */ -/* should be used regardless of operating environment */ -#ifdef __FreeBSD__ -#define WTF_PLATFORM_FREEBSD 1 +/* WTF_OS_LINUX - Linux */ +#if defined(__linux__) && !defined(ANDROID) +#define WTF_OS_LINUX 1 #endif -/* PLATFORM(OPENBSD) */ -/* Operating system level dependencies for OpenBSD systems that */ -/* should be used regardless of operating environment */ +/* WTF_OS_NETBSD - NetBSD */ +#if defined(__NetBSD__) +#define WTF_OS_NETBSD 1 +#endif + +/* WTF_OS_OPENBSD - OpenBSD */ #ifdef __OpenBSD__ -#define WTF_PLATFORM_OPENBSD 1 +#define WTF_OS_OPENBSD 1 +#endif + +/* WTF_OS_QNX - QNX */ +#if defined(__QNXNTO__) +#define WTF_OS_QNX 1 #endif -/* PLATFORM(SOLARIS) */ -/* Operating system level dependencies for Solaris that should be used */ -/* regardless of operating environment */ +/* WTF_OS_SOLARIS - Solaris */ #if defined(sun) || defined(__sun) -#define WTF_PLATFORM_SOLARIS 1 +#define WTF_OS_SOLARIS 1 #endif -/* PLATFORM(OS2) */ -/* Operating system level dependencies for OS/2 that should be used */ -/* regardless of operating environment */ -#if defined(OS2) || defined(__OS2__) -#define WTF_PLATFORM_OS2 1 +/* WTF_OS_WINCE - Windows CE; note that for this platform WTF_OS_WINDOWS is also defined */ +#if defined(_WIN32_WCE) +#define WTF_OS_WINCE 1 #endif -#if defined (__SYMBIAN32__) -/* we are cross-compiling, it is not really windows */ -#undef WTF_PLATFORM_WIN_OS -#undef WTF_PLATFORM_WIN -#define WTF_PLATFORM_SYMBIAN 1 +/* WTF_OS_WINDOWS - Any version of Windows */ +#if defined(WIN32) || defined(_WIN32) +#define WTF_OS_WINDOWS 1 #endif +/* WTF_OS_SYMBIAN - Symbian */ +#if defined (__SYMBIAN32__) +#define WTF_OS_SYMBIAN 1 +#endif -/* PLATFORM(NETBSD) */ -/* Operating system level dependencies for NetBSD that should be used */ -/* regardless of operating environment */ -#if defined(__NetBSD__) -#define WTF_PLATFORM_NETBSD 1 +/* WTF_OS_UNIX - Any Unix-like system */ +#if WTF_OS_AIX \ + || WTF_OS_ANDROID \ + || WTF_OS_DARWIN \ + || WTF_OS_FREEBSD \ + || WTF_OS_HAIKU \ + || WTF_OS_LINUX \ + || WTF_OS_NETBSD \ + || WTF_OS_OPENBSD \ + || WTF_OS_QNX \ + || WTF_OS_SOLARIS \ + || WTF_OS_SYMBIAN \ + || defined(unix) \ + || defined(__unix) \ + || defined(__unix__) +#define WTF_OS_UNIX 1 #endif -/* PLATFORM(QNX) */ -/* Operating system level dependencies for QNX that should be used */ -/* regardless of operating environment */ -#if defined(__QNXNTO__) -#define WTF_PLATFORM_QNX 1 -#endif - -/* PLATFORM(UNIX) */ -/* Operating system level dependencies for Unix-like systems that */ -/* should be used regardless of operating environment */ -#if WTF_PLATFORM_DARWIN \ - || WTF_PLATFORM_FREEBSD \ - || WTF_PLATFORM_SYMBIAN \ - || WTF_PLATFORM_NETBSD \ - || defined(unix) \ - || defined(__unix) \ - || defined(__unix__) \ - || defined(_AIX) \ - || defined(__HAIKU__) \ - || defined(__QNXNTO__) \ - || defined(ANDROID) -#define WTF_PLATFORM_UNIX 1 +/* WTF_OS_OS2 - OS/2 */ +#if defined (__OS2__) +#define WTF_OS_OS2 1 #endif /* Operating environments */ -/* PLATFORM(CHROMIUM) */ -/* PLATFORM(QT) */ -/* PLATFORM(WX) */ -/* PLATFORM(GTK) */ -/* PLATFORM(HAIKU) */ -/* PLATFORM(MAC) */ -/* PLATFORM(WIN) */ +/* FIXME: these are all mixes of OS, operating environment and policy choices. */ +/* WTF_PLATFORM_CHROMIUM */ +/* WTF_PLATFORM_QT */ +/* WTF_PLATFORM_WX */ +/* WTF_PLATFORM_GTK */ +/* WTF_PLATFORM_HAIKU */ +/* WTF_PLATFORM_MAC */ +/* WTF_PLATFORM_WIN */ #if defined(BUILDING_CHROMIUM__) #define WTF_PLATFORM_CHROMIUM 1 #elif defined(BUILDING_QT__) @@ -419,142 +503,226 @@ #define WTF_PLATFORM_GTK 1 #elif defined(BUILDING_HAIKU__) #define WTF_PLATFORM_HAIKU 1 -#elif WTF_PLATFORM_DARWIN +#elif defined(BUILDING_BREWMP__) +#define WTF_PLATFORM_BREWMP 1 +#if defined(AEE_SIMULATOR) +#define WTF_PLATFORM_BREWMP_SIMULATOR 1 +#else +#define WTF_PLATFORM_BREWMP_SIMULATOR 0 +#endif +#undef WTF_OS_WINDOWS +#undef WTF_PLATFORM_WIN +#elif WTF_OS_DARWIN #define WTF_PLATFORM_MAC 1 -#elif WTF_PLATFORM_WIN_OS +#elif WTF_OS_WINDOWS #define WTF_PLATFORM_WIN 1 #endif -/* PLATFORM(IPHONE) */ +/* WTF_PLATFORM_IOS */ +/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ #if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) -#define WTF_PLATFORM_IPHONE 1 +#define WTF_PLATFORM_IOS 1 #endif -/* PLATFORM(IPHONE_SIMULATOR) */ +/* WTF_PLATFORM_IOS_SIMULATOR */ #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR -#define WTF_PLATFORM_IPHONE 1 -#define WTF_PLATFORM_IPHONE_SIMULATOR 1 +#define WTF_PLATFORM_IOS 1 +#define WTF_PLATFORM_IOS_SIMULATOR 1 #else -#define WTF_PLATFORM_IPHONE_SIMULATOR 0 +#define WTF_PLATFORM_IOS_SIMULATOR 0 #endif -#if !defined(WTF_PLATFORM_IPHONE) -#define WTF_PLATFORM_IPHONE 0 +#if !defined(WTF_PLATFORM_IOS) +#define WTF_PLATFORM_IOS 0 #endif -/* PLATFORM(ANDROID) */ +/* WTF_PLATFORM_ANDROID */ +/* FIXME: this is sometimes used as an OS() switch, and other times to drive + policy choices */ #if defined(ANDROID) #define WTF_PLATFORM_ANDROID 1 #endif /* Graphics engines */ -/* PLATFORM(CG) and PLATFORM(CI) */ -#if WTF_PLATFORM_MAC || WTF_PLATFORM_IPHONE -#define WTF_PLATFORM_CG 1 +/* WTF_USE_CG and WTF_PLATFORM_CI */ +#if WTF_PLATFORM_MAC || WTF_PLATFORM_IOS +#define WTF_USE_CG 1 #endif -#if WTF_PLATFORM_MAC && !WTF_PLATFORM_IPHONE -#define WTF_PLATFORM_CI 1 +#if WTF_PLATFORM_MAC || WTF_PLATFORM_IOS || (WTF_PLATFORM_WIN && WTF_USE_CG) +#define WTF_USE_CA 1 #endif -/* PLATFORM(SKIA) for Win/Linux, CG/CI for Mac */ +/* WTF_USE_SKIA for Win/Linux, CG for Mac */ #if WTF_PLATFORM_CHROMIUM -#if WTF_PLATFORM_DARWIN -#define WTF_PLATFORM_CG 1 -#define WTF_PLATFORM_CI 1 +#if WTF_OS_DARWIN +#define WTF_USE_CG 1 #define WTF_USE_ATSUI 1 #define WTF_USE_CORE_TEXT 1 +#define WTF_USE_ICCJPEG 1 #else -#define WTF_PLATFORM_SKIA 1 +#define WTF_USE_SKIA 1 +#define WTF_USE_CHROMIUM_NET 1 #endif #endif +#if WTF_PLATFORM_BREWMP +#define WTF_USE_SKIA 1 +#endif + #if WTF_PLATFORM_GTK -#define WTF_PLATFORM_CAIRO 1 +#define WTF_USE_CAIRO 1 +#endif + + +#if WTF_OS_WINCE +#include +#define WTF_USE_MERSENNE_TWISTER_19937 1 #endif +#if WTF_PLATFORM_QT && WTF_OS_UNIX && !WTF_OS_SYMBIAN && !WTF_OS_DARWIN +#define WTF_USE_PTHREAD_BASED_QT 1 +#endif -#if (WTF_PLATFORM_IPHONE || WTF_PLATFORM_MAC || WTF_PLATFORM_WIN || WTF_PLATFORM_OS2 || (WTF_PLATFORM_QT && WTF_PLATFORM_DARWIN && !ENABLE_SINGLE_THREADED)) && !defined(ENABLE_JSC_MULTIPLE_THREADS) +#if (WTF_PLATFORM_GTK || WTF_PLATFORM_IOS || WTF_PLATFORM_MAC || WTF_PLATFORM_WIN || (WTF_PLATFORM_QT && (WTF_OS_DARWIN || WTF_USE_PTHREAD_BASED_QT) && !ENABLE_SINGLE_THREADED)) && !defined(ENABLE_JSC_MULTIPLE_THREADS) #define ENABLE_JSC_MULTIPLE_THREADS 1 #endif +#if ENABLE_JSC_MULTIPLE_THREADS +#define ENABLE_WTF_MULTIPLE_THREADS 1 +#endif + /* On Windows, use QueryPerformanceCounter by default */ -#if WTF_PLATFORM_WIN_OS +#if WTF_OS_WINDOWS #define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 #endif +#if WTF_OS_WINCE && !WTF_PLATFORM_QT +#define NOMINMAX /* Windows min and max conflict with standard macros */ +#define NOSHLWAPI /* shlwapi.h not available on WinCe */ + +/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ +#define __usp10__ /* disable "usp10.h" */ + +#define _INC_ASSERT /* disable "assert.h" */ +#define assert(x) + +#endif /* WTF_OS_WINCE && !WTF_PLATFORM_QT */ + #if WTF_PLATFORM_QT #define WTF_USE_QT4_UNICODE 1 +#elif WTF_OS_WINCE +#define WTF_USE_WINCE_UNICODE 1 +#elif WTF_PLATFORM_BREWMP +#define WTF_USE_BREWMP_UNICODE 1 #elif WTF_PLATFORM_GTK /* The GTK+ Unicode backend is configurable */ #else #define WTF_USE_ICU_UNICODE 1 #endif -#if WTF_PLATFORM_MAC && !WTF_PLATFORM_IPHONE -#define WTF_PLATFORM_CF 1 -#define WTF_USE_PTHREADS 1 -#define HAVE_PTHREAD_RWLOCK 1 +#if WTF_PLATFORM_MAC && !WTF_PLATFORM_IOS #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER) && WTF_CPU_X86_64 #define WTF_USE_PLUGIN_HOST_PROCESS 1 #endif -#if !defined(ENABLE_MAC_JAVA_BRIDGE) -#define ENABLE_MAC_JAVA_BRIDGE 1 +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#define ENABLE_GESTURE_EVENTS 1 +#define ENABLE_RUBBER_BANDING 1 +#define WTF_USE_WK_SCROLLBAR_PAINTER 1 +#endif +#if !defined(ENABLE_JAVA_BRIDGE) +#define ENABLE_JAVA_BRIDGE 1 #endif #if !defined(ENABLE_DASHBOARD_SUPPORT) #define ENABLE_DASHBOARD_SUPPORT 1 #endif +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 #define HAVE_READLINE 1 #define HAVE_RUNLOOP_TIMER 1 -#endif /* PLATFORM(MAC) && !PLATFORM(IPHONE) */ +#define ENABLE_FULLSCREEN_API 1 +#define ENABLE_SMOOTH_SCROLLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#endif /* WTF_PLATFORM_MAC && !WTF_PLATFORM_IOS */ -#if WTF_PLATFORM_CHROMIUM && WTF_PLATFORM_DARWIN -#define WTF_PLATFORM_CF 1 +#if WTF_PLATFORM_CHROMIUM && WTF_OS_DARWIN +#define WTF_USE_CF 1 #define WTF_USE_PTHREADS 1 #define HAVE_PTHREAD_RWLOCK 1 #endif -#if WTF_PLATFORM_QT && WTF_PLATFORM_DARWIN -#define WTF_PLATFORM_CF 1 +#if WTF_PLATFORM_BREWMP +#define ENABLE_SINGLE_THREADED 1 +#endif + +#if WTF_PLATFORM_QT && WTF_OS_DARWIN +#define WTF_USE_CF 1 +#endif + +#if WTF_OS_DARWIN && !defined(BUILDING_ON_TIGER) && !WTF_PLATFORM_GTK && !WTF_PLATFORM_QT +#define ENABLE_PURGEABLE_MEMORY 1 #endif -#if WTF_PLATFORM_IPHONE +#if WTF_PLATFORM_IOS #define ENABLE_CONTEXT_MENUS 0 #define ENABLE_DRAG_SUPPORT 0 +#define ENABLE_DATA_TRANSFER_ITEMS 0 #define ENABLE_FTPDIR 1 #define ENABLE_GEOLOCATION 1 #define ENABLE_ICONDATABASE 0 #define ENABLE_INSPECTOR 0 -#define ENABLE_MAC_JAVA_BRIDGE 0 +#define ENABLE_JAVA_BRIDGE 0 #define ENABLE_NETSCAPE_PLUGIN_API 0 #define ENABLE_ORIENTATION_EVENTS 1 #define ENABLE_REPAINT_THROTTLING 1 #define HAVE_READLINE 1 -#define WTF_PLATFORM_CF 1 +#define WTF_USE_CF 1 #define WTF_USE_PTHREADS 1 #define HAVE_PTHREAD_RWLOCK 1 +#define ENABLE_WEB_ARCHIVE 1 #endif #if WTF_PLATFORM_ANDROID #define WTF_USE_PTHREADS 1 -#define WTF_PLATFORM_SGL 1 #define USE_SYSTEM_MALLOC 1 -#define ENABLE_MAC_JAVA_BRIDGE 1 +#define ENABLE_JAVA_BRIDGE 1 #define LOG_DISABLED 1 -// Prevents Webkit from drawing the caret in textfields and textareas -// This prevents unnecessary invals. +/* Prevents Webkit from drawing the caret in textfields and textareas + This prevents unnecessary invals. */ #define ENABLE_TEXT_CARET 1 #define ENABLE_JAVASCRIPT_DEBUGGER 0 #endif -#if WTF_PLATFORM_WIN -#define WTF_USE_WININET 1 +#if WTF_PLATFORM_WIN && !WTF_OS_WINCE +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 0 +#endif + +#if WTF_PLATFORM_WIN && !WTF_OS_WINCE && !WTF_PLATFORM_CHROMIUM && !defined(WIN_CAIRO) +#define WTF_USE_CFNETWORK 1 +#endif + +#if WTF_USE_CFNETWORK || WTF_PLATFORM_MAC +#define WTF_USE_CFURLCACHE 1 +#define WTF_USE_CFURLSTORAGESESSIONS 1 +#endif + +#if WTF_PLATFORM_WIN && !WTF_OS_WINCE && !WTF_PLATFORM_CHROMIUM && !WTF_PLATFORM_QT +#define ENABLE_WEB_ARCHIVE 1 #endif #if WTF_PLATFORM_WX #define ENABLE_ASSEMBLER 1 -#if WTF_PLATFORM_DARWIN -#define WTF_PLATFORM_CF 1 +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if WTF_OS_DARWIN +#define WTF_USE_CF 1 +#ifndef BUILDING_ON_TIGER +#define WTF_USE_CORE_TEXT 1 +#define ENABLE_WEB_ARCHIVE 1 +#else +#define WTF_USE_ATSUI 1 +#endif #endif #endif @@ -574,25 +742,39 @@ #define ENABLE_NETSCAPE_PLUGIN_API 0 #endif +#if WTF_PLATFORM_BREWMP +#define USE_SYSTEM_MALLOC 1 +#endif + +#if WTF_PLATFORM_BREWMP_SIMULATOR +#define ENABLE_JIT 0 +#endif + #if !defined(HAVE_ACCESSIBILITY) -#if WTF_PLATFORM_IPHONE || WTF_PLATFORM_MAC || WTF_PLATFORM_WIN || WTF_PLATFORM_GTK || WTF_PLATFORM_CHROMIUM +#if WTF_PLATFORM_IOS || WTF_PLATFORM_MAC || WTF_PLATFORM_WIN || WTF_PLATFORM_GTK || WTF_PLATFORM_CHROMIUM #define HAVE_ACCESSIBILITY 1 #endif #endif /* !defined(HAVE_ACCESSIBILITY) */ -#if WTF_PLATFORM_UNIX && !WTF_PLATFORM_SYMBIAN +#if WTF_OS_UNIX && !WTF_OS_SYMBIAN #define HAVE_SIGNAL_H 1 #endif -#if !WTF_PLATFORM_WIN_OS && !WTF_PLATFORM_SOLARIS && !WTF_PLATFORM_QNX \ - && !WTF_PLATFORM_SYMBIAN && !WTF_PLATFORM_HAIKU && !WTF_COMPILER_RVCT \ - && !WTF_PLATFORM_ANDROID && !WTF_PLATFORM_OS2 +#if !defined(HAVE_STRNSTR) +#if WTF_OS_DARWIN || WTF_OS_FREEBSD +#define HAVE_STRNSTR 1 +#endif +#endif + +#if !WTF_OS_WINDOWS && !WTF_OS_SOLARIS && !WTF_OS_QNX \ + && !WTF_OS_SYMBIAN && !WTF_OS_HAIKU && !WTF_OS_RVCT \ + && !WTF_OS_ANDROID && !WTF_PLATFORM_BREWMP #define HAVE_TM_GMTOFF 1 #define HAVE_TM_ZONE 1 #define HAVE_TIMEGM 1 -#endif +#endif -#if WTF_PLATFORM_DARWIN +#if WTF_OS_DARWIN #define HAVE_ERRNO_H 1 #define HAVE_LANGINFO_H 1 @@ -603,23 +785,37 @@ #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TIMEB_H 1 +#define WTF_USE_ACCELERATE 1 + +#if !defined(TARGETING_TIGER) && !defined(TARGETING_LEOPARD) + +#define HAVE_DISPATCH_H 1 +#define HAVE_HOSTED_CORE_ANIMATION 1 -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !WTF_PLATFORM_IPHONE && !WTF_PLATFORM_QT +#if !WTF_PLATFORM_IOS #define HAVE_MADV_FREE_REUSE 1 #define HAVE_MADV_FREE 1 #define HAVE_PTHREAD_SETNAME_NP 1 #endif -#if WTF_PLATFORM_IPHONE +#endif + +#if WTF_PLATFORM_IOS #define HAVE_MADV_FREE 1 #endif -#elif WTF_PLATFORM_WIN_OS +#elif WTF_OS_WINDOWS +#if WTF_OS_WINCE +#define HAVE_ERRNO_H 0 +#else #define HAVE_SYS_TIMEB_H 1 +#define HAVE_ALIGNED_MALLOC 1 +#define HAVE_ISDEBUGGERPRESENT 1 +#endif #define HAVE_VIRTUALALLOC 1 -#elif WTF_PLATFORM_SYMBIAN +#elif WTF_OS_SYMBIAN #define HAVE_ERRNO_H 1 #define HAVE_MMAP 0 @@ -632,7 +828,11 @@ #define HAVE_SYS_PARAM_H 1 #endif -#elif WTF_PLATFORM_QNX +#elif WTF_PLATFORM_BREWMP + +#define HAVE_ERRNO_H 1 + +#elif WTF_OS_QNX #define HAVE_ERRNO_H 1 #define HAVE_MMAP 1 @@ -641,7 +841,7 @@ #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 -#elif WTF_PLATFORM_ANDROID +#elif WTF_OS_ANDROID #define HAVE_ERRNO_H 1 #define HAVE_LANGINFO_H 0 @@ -651,15 +851,17 @@ #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 -#elif WTF_PLATFORM_OS2 +#elif WTF_OS_OS2 -#define HAVE_MMAP 1 -#define ENABLE_ASSEMBLER 1 +#define USE_SYSTEM_MALLOC 1 #define HAVE_ERRNO_H 1 -#define HAVE_STRINGS_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 0 +#define HAVE_POSIX_MEMALIGN 1 +#define HAVE_SBRK 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TIMEB_H 1 +#define HAVE_STRINGS_H 1 #else @@ -667,7 +869,7 @@ #define HAVE_ERRNO_H 1 /* As long as Haiku doesn't have a complete support of locale this will be disabled. */ -#if !WTF_PLATFORM_HAIKU +#if !WTF_OS_HAIKU #define HAVE_LANGINFO_H 1 #endif #define HAVE_MMAP 1 @@ -680,6 +882,14 @@ /* ENABLE macro defaults */ +#if WTF_PLATFORM_QT +/* We must not customize the global operator new and delete for the Qt port. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if !WTF_OS_UNIX || WTF_OS_SYMBIAN +#define USE_SYSTEM_MALLOC 1 +#endif +#endif + /* fastMalloc match validation allows for runtime verification that new is matched by delete, fastMalloc is matched by fastFree, etc. */ #if !defined(ENABLE_FAST_MALLOC_MATCH_VALIDATION) @@ -710,6 +920,10 @@ #define ENABLE_DRAG_SUPPORT 1 #endif +#if !defined(ENABLE_DATA_TRANSFER_ITEMS) +#define ENABLE_DATA_TRANSFER_ITEMS 0 +#endif + #if !defined(ENABLE_DASHBOARD_SUPPORT) #define ENABLE_DASHBOARD_SUPPORT 0 #endif @@ -718,14 +932,22 @@ #define ENABLE_INSPECTOR 1 #endif -#if !defined(ENABLE_MAC_JAVA_BRIDGE) -#define ENABLE_MAC_JAVA_BRIDGE 0 +#if !defined(ENABLE_JAVA_BRIDGE) +#define ENABLE_JAVA_BRIDGE 0 #endif #if !defined(ENABLE_NETSCAPE_PLUGIN_API) #define ENABLE_NETSCAPE_PLUGIN_API 1 #endif +#if !defined(ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE) +#define ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE 0 +#endif + +#if !defined(ENABLE_PURGEABLE_MEMORY) +#define ENABLE_PURGEABLE_MEMORY 0 +#endif + #if !defined(WTF_USE_PLUGIN_HOST_PROCESS) #define WTF_USE_PLUGIN_HOST_PROCESS 0 #endif @@ -738,6 +960,11 @@ #define ENABLE_OPCODE_STATS 0 #endif +#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) +#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 +#endif + +#define ENABLE_DEBUG_WITH_BREAKPOINT 0 #define ENABLE_SAMPLING_COUNTERS 0 #define ENABLE_SAMPLING_FLAGS 0 #define ENABLE_OPCODE_SAMPLING 0 @@ -753,10 +980,18 @@ #define ENABLE_GEOLOCATION 0 #endif +#if !defined(ENABLE_GESTURE_RECOGNIZER) +#define ENABLE_GESTURE_RECOGNIZER 0 +#endif + #if !defined(ENABLE_NOTIFICATIONS) #define ENABLE_NOTIFICATIONS 0 #endif +#if WTF_PLATFORM_IOS +#define ENABLE_TEXT_CARET 0 +#endif + #if !defined(ENABLE_TEXT_CARET) #define ENABLE_TEXT_CARET 1 #endif @@ -765,80 +1000,83 @@ #define ENABLE_ON_FIRST_TEXTAREA_FOCUS_SELECT_ALL 0 #endif -#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32) && !defined(WTF_USE_JSVALUE32_64) -#if (WTF_CPU_X86_64 && (WTF_PLATFORM_UNIX || WTF_PLATFORM_WIN_OS)) || WTF_CPU_IA64 || WTF_CPU_ALPHA +#if !defined(ENABLE_FULLSCREEN_API) +#define ENABLE_FULLSCREEN_API 0 +#endif + +#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) +#if (WTF_CPU_X86_64 && (WTF_OS_UNIX || WTF_OS_WINDOWS)) \ + || (WTF_CPU_IA64 && !WTF_CPU_IA64_32) \ + || WTF_CPU_ALPHA \ + || WTF_CPU_SPARC64 \ + || WTF_CPU_S390X \ + || WTF_CPU_PPC64 #define WTF_USE_JSVALUE64 1 -#elif WTF_CPU_ARM || WTF_CPU_PPC64 -#define WTF_USE_JSVALUE32 1 -#elif WTF_PLATFORM_WIN_OS && WTF_COMPILER_MINGW -/* Using JSVALUE32_64 causes padding/alignement issues for JITStubArg -on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ -#define WTF_USE_JSVALUE32 1 #else #define WTF_USE_JSVALUE32_64 1 #endif -#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32) && !defined(WTF_USE_JSVALUE32_64) */ +#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ #if !defined(ENABLE_REPAINT_THROTTLING) #define ENABLE_REPAINT_THROTTLING 0 #endif -#if !defined(ENABLE_JIT) - -/* The JIT is tested & working on x86_64 Mac */ -#if WTF_CPU_X86_64 && WTF_PLATFORM_MAC - #define ENABLE_JIT 1 -/* The JIT is tested & working on x86 Mac */ -#elif WTF_CPU_X86 && WTF_PLATFORM_MAC - #define ENABLE_JIT 1 - #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 -#elif WTF_CPU_ARM_THUMB2 && WTF_PLATFORM_IPHONE - #define ENABLE_JIT 1 -/* The JIT is tested & working on x86 OS/2 */ -#elif WTF_CPU_X86 && WTF_PLATFORM_OS2 - #define ENABLE_JIT 1 -/* The JIT is tested & working on x86 Windows */ -#elif WTF_CPU_X86 && WTF_PLATFORM_WIN - #define ENABLE_JIT 1 -#elif WTF_CPU_SPARC - #define ENABLE_JIT 1 +/* Disable the JIT on versions of GCC prior to 4.1 */ +#if !defined(ENABLE_JIT) && WTF_COMPILER_GCC && !GCC_VERSION_AT_LEAST(4, 1, 0) +#define ENABLE_JIT 0 #endif -#if WTF_PLATFORM_QT -#if WTF_CPU_X86_64 && WTF_PLATFORM_DARWIN - #define ENABLE_JIT 1 -#elif WTF_CPU_X86 && WTF_PLATFORM_DARWIN - #define ENABLE_JIT 1 - #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 -#elif WTF_CPU_X86 && WTF_PLATFORM_WIN_OS && WTF_COMPILER_MINGW && GCC_VERSION >= 40100 - #define ENABLE_JIT 1 - #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 -#elif WTF_CPU_X86 && WTF_PLATFORM_WIN_OS && WTF_COMPILER_MSVC - #define ENABLE_JIT 1 - #define WTF_USE_JIT_STUB_ARGUMENT_REGISTER 1 -#elif WTF_CPU_X86 && WTF_PLATFORM_LINUX && GCC_VERSION >= 40100 - #define ENABLE_JIT 1 - #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 -#elif WTF_CPU_ARM_TRADITIONAL && WTF_PLATFORM_LINUX - #define ENABLE_JIT 1 -#endif -#endif /* PLATFORM(QT) */ - -#endif /* !defined(ENABLE_JIT) */ +/* The JIT is enabled by default on all x86, x64-64, ARM platforms. */ +#if !defined(ENABLE_JIT) \ + && (WTF_CPU_X86 || WTF_CPU_X86_64 || WTF_CPU_ARM || WTF_CPU_SPARC32 || WTF_CPU_MIPS) \ + && (WTF_OS_DARWIN || !WTF_COMPILER_GCC || GCC_VERSION_AT_LEAST(4, 1, 0)) \ + && !WTF_OS_WINCE +#define ENABLE_JIT 1 +#endif -#if ENABLE_JIT -#ifndef ENABLE_JIT_OPTIMIZE_CALL -#define ENABLE_JIT_OPTIMIZE_CALL 1 +/* Currently only implemented for JSVALUE64, only tested on WTF_PLATFORM_MAC */ +#if ENABLE_JIT && WTF_USE_JSVALUE64 && WTF_PLATFORM_MAC +#define ENABLE_DFG_JIT 1 +/* Enabled with restrictions to circumvent known performance regressions. */ +#define ENABLE_DFG_JIT_RESTRICTIONS 1 #endif -#ifndef ENABLE_JIT_OPTIMIZE_NATIVE_CALL -#define ENABLE_JIT_OPTIMIZE_NATIVE_CALL 1 + +/* Ensure that either the JIT or the interpreter has been enabled. */ +#if !defined(ENABLE_INTERPRETER) && !ENABLE_JIT +#define ENABLE_INTERPRETER 1 #endif -#ifndef ENABLE_JIT_OPTIMIZE_PROPERTY_ACCESS -#define ENABLE_JIT_OPTIMIZE_PROPERTY_ACCESS 1 +#if !(ENABLE_JIT || ENABLE_INTERPRETER) +#error You have to have at least one execution model enabled to build JSC #endif -#ifndef ENABLE_JIT_OPTIMIZE_METHOD_CALLS -#define ENABLE_JIT_OPTIMIZE_METHOD_CALLS 1 + +#if WTF_CPU_SH4 && WTF_PLATFORM_QT +#define ENABLE_JIT 1 +#define ENABLE_YARR 1 +#define ENABLE_YARR_JIT 1 +#define WTF_USE_JIT_STUB_ARGUMENT_REGISTER 1 +#define ENABLE_ASSEMBLER 1 #endif + +/* Configure the JIT */ +#if ENABLE_JIT + #if WTF_CPU_ARM + #if !defined(ENABLE_JIT_USE_SOFT_MODULO) && WTF_CPU_ARM && WTF_ARM_ARCH_VERSION >= 5 + #define ENABLE_JIT_USE_SOFT_MODULO 1 + #endif + #endif + + #ifndef ENABLE_JIT_OPTIMIZE_CALL + #define ENABLE_JIT_OPTIMIZE_CALL 1 + #endif + #ifndef ENABLE_JIT_OPTIMIZE_NATIVE_CALL + #define ENABLE_JIT_OPTIMIZE_NATIVE_CALL 1 + #endif + #ifndef ENABLE_JIT_OPTIMIZE_PROPERTY_ACCESS + #define ENABLE_JIT_OPTIMIZE_PROPERTY_ACCESS 1 + #endif + #ifndef ENABLE_JIT_OPTIMIZE_METHOD_CALLS + #define ENABLE_JIT_OPTIMIZE_METHOD_CALLS 1 + #endif #endif #if WTF_CPU_X86 && WTF_COMPILER_MSVC @@ -849,80 +1087,90 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define JSC_HOST_CALL #endif -#if WTF_COMPILER_GCC && !ENABLE_JIT +/* Configure the interpreter */ +#if WTF_COMPILER_GCC || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) #define HAVE_COMPUTED_GOTO 1 #endif - -#if ENABLE_JIT && defined(COVERAGE) - #define WTF_USE_INTERPRETER 0 -#else - #define WTF_USE_INTERPRETER 1 +#if HAVE_COMPUTED_GOTO && ENABLE_INTERPRETER +#define ENABLE_COMPUTED_GOTO_INTERPRETER 1 #endif -/* Yet Another Regex Runtime. */ -#if !defined(ENABLE_YARR_JIT) +/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ +#define ENABLE_REGEXP_TRACING 0 -/* YARR supports x86 & x86-64, and has been tested on Mac and Windows. */ -#if (WTF_CPU_X86 \ - || WTF_CPU_X86_64 \ - || WTF_CPU_SPARC \ - || WTF_CPU_ARM_TRADITIONAL \ - || WTF_CPU_ARM_THUMB2 \ - || WTF_CPU_X86) -#define ENABLE_YARR_JIT 1 -#else +/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ +#if WTF_PLATFORM_CHROMIUM #define ENABLE_YARR_JIT 0 -#endif -#endif /* !defined(ENABLE_YARR_JIT) */ +#elif ENABLE_JIT && !defined(ENABLE_YARR_JIT) +#define ENABLE_YARR_JIT 1 + +/* Setting this flag compares JIT results with interpreter results. */ +#define ENABLE_YARR_JIT_DEBUG 0 +#endif -#if (ENABLE_JIT || ENABLE_YARR_JIT) +#if ENABLE_JIT || ENABLE_YARR_JIT #define ENABLE_ASSEMBLER 1 #endif /* Setting this flag prevents the assembler from using RWX memory; this may improve security but currectly comes at a significant performance cost. */ -#if WTF_PLATFORM_IPHONE -#define ENABLE_ASSEMBLER_WX_EXCLUSIVE 1 -#else +#if WTF_PLATFORM_IOS +//XXX: this doesn't currently compile in the spidermonkey build #define ENABLE_ASSEMBLER_WX_EXCLUSIVE 0 #endif -#if !defined(ENABLE_PAN_SCROLLING) && WTF_PLATFORM_WIN_OS +/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. + On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ +#if ENABLE_ASSEMBLER +#if WTF_CPU_X86_64 +#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 +#else +#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 +#endif +#endif + +#if !defined(ENABLE_PAN_SCROLLING) && WTF_OS_WINDOWS #define ENABLE_PAN_SCROLLING 1 #endif -/* Use the QXmlStreamReader implementation for XMLTokenizer */ +#if !defined(ENABLE_SMOOTH_SCROLLING) +#define ENABLE_SMOOTH_SCROLLING 0 +#endif + +#if !defined(ENABLE_WEB_ARCHIVE) +#define ENABLE_WEB_ARCHIVE 0 +#endif + +/* Use the QXmlStreamReader implementation for XMLDocumentParser */ /* Use the QXmlQuery implementation for XSLTProcessor */ #if WTF_PLATFORM_QT #define WTF_USE_QXMLSTREAM 1 #define WTF_USE_QXMLQUERY 1 #endif -#if !WTF_PLATFORM_QT -#define WTF_USE_FONT_FAST_PATH 1 +#if WTF_PLATFORM_MAC +/* Complex text framework */ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +#define WTF_USE_ATSUI 0 +#define WTF_USE_CORE_TEXT 1 +#else +#define WTF_USE_ATSUI 1 +#define WTF_USE_CORE_TEXT 0 +#endif #endif /* Accelerated compositing */ -#if WTF_PLATFORM_MAC -#if !defined(BUILDING_ON_TIGER) +#if (WTF_PLATFORM_MAC && !defined(BUILDING_ON_TIGER)) || WTF_PLATFORM_IOS || WTF_PLATFORM_QT || (WTF_PLATFORM_WIN && !WTF_OS_WINCE &&!defined(WIN_CAIRO)) #define WTF_USE_ACCELERATED_COMPOSITING 1 #endif -#endif -#if WTF_PLATFORM_IPHONE -#define WTF_USE_ACCELERATED_COMPOSITING 1 +#if (WTF_PLATFORM_MAC && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) || WTF_PLATFORM_IOS +#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 #endif -/* FIXME: Defining ENABLE_3D_RENDERING here isn't really right, but it's always used with - with WTF_USE_ACCELERATED_COMPOSITING, and it allows the feature to be turned on and - off in one place. */ -//#if WTF_PLATFORM_WIN -//#include "QuartzCorePresent.h" -//#if QUARTZCORE_PRESENT -//#define WTF_USE_ACCELERATED_COMPOSITING 1 -//#define ENABLE_3D_RENDERING 1 -//#endif -//#endif +#if WTF_PLATFORM_MAC && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#define WTF_USE_AVFOUNDATION 1 +#endif #if WTF_COMPILER_GCC #define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) @@ -930,7 +1178,7 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define WARN_UNUSED_RETURN #endif -#if !ENABLE_NETSCAPE_PLUGIN_API || (ENABLE_NETSCAPE_PLUGIN_API && ((WTF_PLATFORM_UNIX && (WTF_PLATFORM_QT || WTF_PLATFORM_WX)) || WTF_PLATFORM_GTK)) +#if !ENABLE_NETSCAPE_PLUGIN_API || (ENABLE_NETSCAPE_PLUGIN_API && ((WTF_OS_UNIX && (WTF_PLATFORM_QT || WTF_PLATFORM_WX)) || WTF_PLATFORM_GTK)) #define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 #endif @@ -939,4 +1187,46 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define ENABLE_JSC_ZOMBIES 0 +/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ +#if WTF_PLATFORM_MAC || WTF_PLATFORM_WIN || WTF_PLATFORM_QT +#define WTF_USE_PLATFORM_STRATEGIES 1 +#endif + +#if WTF_PLATFORM_WIN +#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 +#endif + +/* Geolocation request policy. pre-emptive policy is to acquire user permission before acquiring location. + Client based implementations will have option to choose between pre-emptive and nonpre-emptive permission policy. + pre-emptive permission policy is enabled by default for all client-based implementations. */ +#if ENABLE_CLIENT_BASED_GEOLOCATION +#define WTF_USE_PREEMPT_GEOLOCATION_PERMISSION 1 +#endif + +#if WTF_CPU_ARM_THUMB2 +#define ENABLE_BRANCH_COMPACTION 1 +#endif + +#if !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) +#define ENABLE_THREADING_OPENMP 1 +#endif + +#if !defined(ENABLE_PARALLEL_JOBS) && !ENABLE_SINGLE_THREADED && (ENABLE_THREADING_GENERIC || ENABLE_THREADING_LIBDISPATCH || ENABLE_THREADING_OPENMP) +#define ENABLE_PARALLEL_JOBS 1 +#endif + +#if ENABLE_GLIB_SUPPORT +#include "GTypedefs.h" +#endif + +/* FIXME: This define won't be needed once #27551 is fully landed. However, + since most ports try to support sub-project independence, adding new headers + to WTF causes many ports to break, and so this way we can address the build + breakages one port at a time. */ +#define WTF_USE_EXPORT_MACROS 0 + +#if WTF_PLATFORM_QT || WTF_PLATFORM_GTK +#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 +#endif + #endif /* WTF_Platform_h */ diff --git a/deps/mozjs/js/src/assembler/wtf/SegmentedVector.h b/deps/mozjs/js/src/assembler/wtf/SegmentedVector.h index 0aed45e18b8..f679a21fe13 100644 --- a/deps/mozjs/js/src/assembler/wtf/SegmentedVector.h +++ b/deps/mozjs/js/src/assembler/wtf/SegmentedVector.h @@ -30,7 +30,7 @@ #define SegmentedVector_h #include "jsprvtd.h" -#include "jsvector.h" +#include "js/Vector.h" namespace WTF { diff --git a/deps/mozjs/js/src/bench.sh b/deps/mozjs/js/src/bench.sh deleted file mode 100755 index c5b1ec5ad03..00000000000 --- a/deps/mozjs/js/src/bench.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -X="var d = Date.now();"; -for i in t/*.js; do X="$X load(\"$i\");"; done -X="$X print(Date.now() - d);" -echo $X | $1 -j diff --git a/deps/mozjs/js/src/build/autoconf/config.sub b/deps/mozjs/js/src/build/autoconf/config.sub index 72438295912..1c035c93116 100755 --- a/deps/mozjs/js/src/build/autoconf/config.sub +++ b/deps/mozjs/js/src/build/autoconf/config.sub @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-12-04' +timestamp='2011-01-03' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -1432,6 +1432,9 @@ case $os in -dicos*) os=-dicos ;; + -android*) + os=-android + ;; -none) ;; *) @@ -1686,6 +1689,9 @@ case $basic_machine in -vos*) vendor=stratus ;; + *-android*|*-linuxandroid*) + vendor=linux- + ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; diff --git a/deps/mozjs/js/src/build/autoconf/frameptr.m4 b/deps/mozjs/js/src/build/autoconf/frameptr.m4 new file mode 100644 index 00000000000..77a6d71aedc --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/frameptr.m4 @@ -0,0 +1,25 @@ +dnl Set MOZ_FRAMEPTR_FLAGS to the flags that should be used for enabling or +dnl disabling frame pointers in this architecture based on the configure +dnl options + +AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [ + if test "$GNU_CC"; then + MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer" + MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer" + else + case "$target" in + *-mingw*) + MOZ_ENABLE_FRAME_PTR="-Oy-" + MOZ_DISABLE_FRAME_PTR="-Oy" + ;; + esac + fi + + # if we are debugging or profiling, we want a frame pointer. + if test -z "$MOZ_OPTIMIZE" -o \ + -n "$MOZ_PROFILING" -o -n "$MOZ_DEBUG"; then + MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR" + else + MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR" + fi +]) diff --git a/deps/mozjs/js/src/build/autoconf/gcc-pr49911.m4 b/deps/mozjs/js/src/build/autoconf/gcc-pr49911.m4 new file mode 100644 index 00000000000..3aded690dad --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/gcc-pr49911.m4 @@ -0,0 +1,67 @@ +dnl Check if the compiler is gcc and has PR49911. If so +dnl disable vrp. + +AC_DEFUN([MOZ_GCC_PR49911], +[ +if test "$GNU_CC"; then + +AC_MSG_CHECKING(for gcc PR49911) +ac_have_gcc_pr49911="no" +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +_SAVE_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-O2" +AC_TRY_RUN([ +extern "C" void abort(void); +typedef enum { +eax, ecx, edx, ebx, esp, ebp, +esi, edi } +RegisterID; +union StateRemat { + RegisterID reg_; + int offset_; +}; +static StateRemat FromRegister(RegisterID reg) { + StateRemat sr; + sr.reg_ = reg; + return sr; +} +static StateRemat FromAddress3(int address) { + StateRemat sr; + sr.offset_ = address; + if (address < 46 && address >= 0) { + abort(); + } + return sr; +} +struct FrameState { + StateRemat dataRematInfo2(bool y, int z) { + if (y) return FromRegister(RegisterID(1)); + return FromAddress3(z); + } +}; +FrameState frame; +StateRemat x; +__attribute__((noinline)) void jsop_setelem(bool y, int z) { + x = frame.dataRematInfo2(y, z); +} +int main(void) { + jsop_setelem(0, 47); +} +], true, + ac_have_gcc_pr49911="yes", + true) +CXXFLAGS="$_SAVE_CXXFLAGS" + +AC_LANG_RESTORE + +if test "$ac_have_gcc_pr49911" = "yes"; then + AC_MSG_RESULT(yes) + CFLAGS="$CFLAGS -fno-tree-vrp" + CXXFLAGS="$CXXFLAGS -fno-tree-vrp" +else + AC_MSG_RESULT(no) +fi +fi +]) diff --git a/deps/mozjs/js/src/build/autoconf/lto.m4 b/deps/mozjs/js/src/build/autoconf/lto.m4 index d7a9802166a..46648fa23bb 100644 --- a/deps/mozjs/js/src/build/autoconf/lto.m4 +++ b/deps/mozjs/js/src/build/autoconf/lto.m4 @@ -1,6 +1,6 @@ dnl check if the build is using lto. This is really primitive and only detects llvm based dnl compilers right now. -AC_DEFUN(MOZ_DOING_LTO, +AC_DEFUN([MOZ_DOING_LTO], [ cat > conftest.c < +# Joey Armstrong # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), @@ -35,29 +37,81 @@ # # ***** END LICENSE BLOCK ***** -# make-makefiles - Quickly create Makefiles for subdirectories. -# Also, creates any needed subdirectories. -# -# usage: make-makefiles [ -t -p -d ] [ | /Makefile ] ... +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +use Getopt::Long; + +use Benchmark; +use Cwd; +use File::Basename; +use File::Copy; +use File::Path qw{mkpath}; + +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(2.0); + +##--------------------## +##---] INCLUDES [---## +##--------------------## + +############################################################## +# pymake: special case path handling for windows cmd shell. +# if invoked by cmd.exe and msys-perl is in play +# $0 may contain a drive letter +# modules use-or-expect msys/unix paths +# adjust $0 => C:/foo => /c/foo so string tests and +# manipulation can by applied properly. +############################################################## +sub BEGIN +{ + if ($^O eq 'msys' && $ENV{PATH} =~ m!\w:/!) + { + $0 =~ s!^(\w):!/$1!; + } + eval 'use FindBin'; + die $@ if ($@); +} -# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). +use lib $FindBin::Bin; +use makemakefile; + +##-------------------## +##---] GLOBALS [---## +##-------------------## +my %argv; + +my $t0 = Benchmark->new(); +sub END +{ + if ($argv{bench}) + { + my $t1 = Benchmark->new(); + my $delta = timediff($t1, $t0); + print STDERR timestr($delta), "\n"; + } +} -#$debug = 1; +##----------------## +##---] MAIN [---## +##----------------## +umask 0; -if ($^O eq 'msys') { - $pwdcmd = 'pwd -W'; -} -else { - $pwdcmd = 'pwd'; -} +my $debug = $argv{debug} || 0; + +my $pwdcmd = ($^O eq 'msys') ? 'pwd -W' : 'pwd'; # Determine various tree path variables # -($topsrcdir, $ptopsrcdir, $depth, @makefiles) = parse_arguments(@ARGV); +my ($topsrcdir, $ptopsrcdir, $depth, @makefiles) = parse_arguments(@ARGV); -$object_fullpath = `$pwdcmd`; +my $object_fullpath = `$pwdcmd`; # Cwd::getcwd() chdir $depth; -$object_root = `$pwdcmd`; +my $object_root = `$pwdcmd`; # Cwd::getcwd() chomp $object_fullpath; chomp $object_root; @@ -65,24 +119,23 @@ chomp $object_root; # 'make-makefile' was called. For example, if make-makefile was # called from "mozilla/gfx/src", then $source_subdir would be # "gfx/src/". -$source_subdir = "$object_fullpath/"; +my $source_subdir = "$object_fullpath/"; my $quoted_object_root = quotemeta($object_root); $source_subdir =~ s|^$quoted_object_root/||; # Prefix makefiles with $source_subdir so that paths # will be relative to the top of the object tree. # -for $makefile (@makefiles) { +my $makefile; +for $makefile (@makefiles) { # dead code ? $makefile = "$source_subdir$makefile"; } -create_directories(@makefiles); - # Find the path to the source directory based on how 'make-makefile' # was invoked. The path is either relative to the object directory # or an absolute path. -$given_srcdir = find_srcdir($topsrcdir, $depth); -$pgiven_srcdir = find_srcdir($ptopsrcdir, $depth); +my $given_srcdir = find_srcdir($topsrcdir, $depth); +my $pgiven_srcdir = find_srcdir($ptopsrcdir, $depth); if ($debug) { warn "object_fullpath = $object_fullpath\n"; @@ -92,18 +145,21 @@ if ($debug) { warn "given_srcdir = $given_srcdir\n"; } -@unhandled = update_makefiles($given_srcdir, $pgiven_srcdir, @makefiles); +my @errors; +my @unhandled = update_makefiles_legacy($given_srcdir, $pgiven_srcdir, @makefiles); +push(@errors, $@) if ($@); run_config_status(@unhandled); +push(@errors, $@) if ($@ && $argv{'no-warnings'}); + +exit scalar(@errors); # end of Main ############################################################ -sub dirname { - return $_[0] =~ /(.*)\/.*/ ? "$1" : '.'; -} - +########################################################################### # find_depth: Pull the value of DEPTH out of a Makefile (or Makefile.in) +########################################################################### sub find_depth { my $depth = ''; open(MAKEFILE, "<$_[0]") || die "Unable to open $_[0]: $!\n"; @@ -116,39 +172,115 @@ sub find_depth { return $depth; } +########################################################################### +## Intent: Parse command line arguments and assign values +########################################################################### sub parse_arguments { my @args = @_; - my $depth = ''; - my $topsrcdir = ''; - my $ptopsrcdir; my @makefiles = (); - while (1) { - if ($args[0] eq '-d') { - $depth = $args[1]; - shift @args; - shift @args; - } elsif ($args[0] eq '-t') { - $topsrcdir = $args[1]; - shift @args; - shift @args; - } elsif ($args[0] eq '-p') { - $ptopsrcdir = $args[1]; - shift @args; - shift @args; - } else { - last; - } + my @arglist = qw(badtokens! bench + chdir=s + debug + depth|d=s + enhanced + obj=s top|t=s ptop|p=s + src=s dst=s + ); + unless(GetOptions(\%argv, @arglist)) + { + my $script = join('/', $FindBin::RealBin, $FindBin::Script); + system("perldoc $script $depth, obj=>$obj, top=>$top}); + if ($@) + { + push(@errors, $@); + } + elsif ($rc eq 'badtokens') + { + push(@unhandled, $mf); + } + } + + run_config_status(@unhandled); + push(@errors, $@) if ($@ && $argv{'no-warnings'}); + exit scalar(@errors); } - if ($depth eq '') { + + + my $depth = $argv{depth} || ''; + if (! $depth) + { + foreach my $fyl (@args) + { + if (my $tmp = find_depth($fyl)) + { + $depth = $tmp; + last; + } + } + } + + if (! $depth) { # Use $(DEPTH) in the Makefile or Makefile.in to determine the depth if (-e "Makefile.in") { $depth = find_depth("Makefile.in"); @@ -166,34 +298,21 @@ sub parse_arguments { # Build the list of makefiles to generate # @makefiles = (); - my $makefile; - foreach $makefile (@args) { - $makefile =~ s/\.in$//; - $makefile =~ s/\/$//; - $makefile =~ /Makefile$/ - or $makefile =~ /^\.\// - or $makefile .= "/Makefile"; + while (@args) + { + next unless my $makefile = shift @args; + $makefile =~ s/\.in$//; + $makefile =~ s/\/$//; + $makefile =~ /Makefile$/ + or $makefile =~ /^\.\// + or $makefile .= "/Makefile"; push @makefiles, "$makefile"; } - @makefiles = "Makefile" unless @args; + @makefiles = "Makefile" unless @makefiles; return ($topsrcdir, $ptopsrcdir, $depth, @makefiles); } - -# Create all the directories at once. -# This can be much faster than calling mkdir() for each one. -sub create_directories { - my @makefiles = @_; - my @dirs = (); - my $ac_file; - foreach $ac_file (@makefiles) { - push @dirs, dirname($ac_file); - } - # Call mkdir with the directories sorted by subdir count (how many /'s) - system "mkdir -p ". join(' ', map("\"$_\"", @dirs)) if @dirs; -} - # Find the top of the source directory # (Assuming that the executable is in $top_srcdir/build/autoconf) sub find_srcdir { @@ -214,111 +333,146 @@ sub find_srcdir { return $ac_given_srcdir; } -# Output the makefiles. -# -sub update_makefiles { - my ($ac_given_srcdir, $pac_given_srcdir, @makefiles) = @_; - my @unhandled=(); - - my $ac_file; - foreach $ac_file (@makefiles) { - my $ac_file_in = "$ac_given_srcdir/${ac_file}.in"; - my $ac_dir = dirname($ac_file); - my $ac_dots = ''; - my $ac_dir_suffix = ''; - my $srcdir = '.'; - my $top_srcdir = '.'; - - # Determine $srcdir and $top_srcdir - # - if ($ac_dir ne '.') { - $ac_dir_suffix = "/$ac_dir"; - $ac_dir_suffix =~ s%^/\./%/%; - $ac_dots = $ac_dir_suffix; - # Remove .. components from the provided dir suffix, and - # also the forward path components they were reversing. - my $backtracks = $ac_dots =~ s%\.\.(/|$)%%g; - while ($backtracks--) { - $ac_dots =~ s%/[^/]*%%; - } - $ac_dots =~ s%/[^/]*%../%g; - } - if ($ac_given_srcdir eq '.') { - if ($ac_dots ne '') { - $top_srcdir = $ac_dots; - $top_srcdir =~ s%/$%%; - } - } elsif ($pac_given_srcdir =~ m%^/% or $pac_given_srcdir =~ m%^.:/%) { - $srcdir = "$pac_given_srcdir$ac_dir_suffix"; - $top_srcdir = "$pac_given_srcdir"; - } else { - if ($debug) { - print "ac_dots = $ac_dots\n"; - print "ac_dir_suffix = $ac_dir_suffix\n"; - } - $srcdir = "$ac_dots$ac_given_srcdir$ac_dir_suffix"; - $top_srcdir = "$ac_dots$ac_given_srcdir"; - } +1; +########################################################################### +## perldoc +########################################################################### +__END__ - if ($debug) { - print "ac_dir = $ac_dir\n"; - print "ac_file = $ac_file\n"; - print "ac_file_in = $ac_file_in\n"; - print "srcdir = $srcdir\n"; - print "top_srcdir = $top_srcdir\n"; - print "cwd = " . `$pwdcmd` . "\n"; - } +=head1 NAME - # Copy the file and make substitutions. - # @srcdir@ -> value of $srcdir - # @top_srcdir@ -> value of $top_srcdir - # - if (-e $ac_file) { - next if -M _ < -M $ac_file_in; # Next if Makefile is up-to-date. - warn "updating $ac_file\n"; - } else { - warn "creating $ac_file\n"; - } +make-makefile - Generate a Makefile from a F template - open INFILE, "<$ac_file_in" or do { - warn "$0: Cannot read $ac_file_in: No such file or directory\n"; - next; - }; - open OUTFILE, ">$ac_file" or do { - warn "$0: Unable to create $ac_file\n"; - next; - }; - - while () { - #if (/\@[_a-zA-Z]*\@.*\@[_a-zA-Z]*\@/) { - # #warn "Two defines on a line:$ac_file:$.:$_"; - # push @unhandled, $ac_file; - # last; - #} - - s/\@srcdir\@/$srcdir/g; - s/\@top_srcdir\@/$top_srcdir/g; - - if (/\@[_a-zA-Z]*\@/) { - #warn "Unknown variable:$ac_file:$.:$_"; - push @unhandled, $ac_file; - last; - } - print OUTFILE; - } - close INFILE; - close OUTFILE; - } - return @unhandled; -} +=head1 SYNOPSIS -sub run_config_status { - my @unhandled = @_; +make-makefile [--top t] [--obj o] [--depth d] foo/bar/Makefile.in tans/fans/Makefile foo/bar - # Run config.status with any unhandled files. - # - if (@unhandled) { - $ENV{CONFIG_FILES}= join ' ', @unhandled; - system "./config.status"; - } -} +=head1 DESCRIPTION + +Given options and makefile path arguments determine path to the template +F beneath a source directory and path to generated F +beneath $MOZ_OBJDIR. DEPTH from destination directory to the 'root' will +also be determined. F will be read in, template strings of the +gorm @token@ will be replaced with derived values and a generated makefile +will be written out as F. + +Makefile DEPTH= can be determined in a few different ways: + o The string C may be embedded within F. + o Search parent directories for F and use it to assign the child. + + +=head2 Option List + +=over 4 + +=item --chdir + +Move to this directory before doing anything else + +=item -d, --depth + +Explicitly specify the relative path from directory containing Makefile.in +to the top sandbox directory. memory/makefile, DEPTH=../.., js/src/config, DEPTH=.. + +=item --enhanced + +Use alternate/simplified path construction when options --top and --obj are +passed. This feature will be used by container makefiles to support makefile +generation while cd'd into the sandbox top directory. + +=item -t, --top + +Path the root of a development sandbox. + +=item --obj + +Path to object directory where generated makefile will be written ($MOZ_OBJDIR). + +=item --ptop + +Print top source dir + +=back + + +=head2 Options List DEBUG + +=over 4 + +=item --bench + +Enable script benchmarking, report elapsed runtime. + +=item --debug + +Enable script debug mode. + +=back + + +=head2 Options List --NO- + +=over 4 + +=item --no-badtokens (wip) + +Handle unexpanded @token@ makefile tokens as an error condition. +Do not rely on system(config.status) to externally supply values. + +=item --no-excludes + +Ignore file entries on the exclusion list, generate everything. + +=item --no-warnings + +Warnings are handled as an error condition. + +=back + + +=head2 Examples + +=over 4 + +=item * make-makefile -t /mozilla/nightly -d . memory/mozalloc + +cd $MOZ_OBJDIR; +--top and --depth are explicitly set for generting memory/mozalloc/Makefile. + +=item * make-makefile -t /mozilla/nightly -d ../../../.. html5lib_tree_construction/Makefile + +cd $MOZ_OBJDIR/parser/htmlparser/tests/mochitest + +--top and --depth are explicitly set for generting a makefile from within +a subdirectory of $MOZ_OBJDIR + +=item * make-makefile --top /mozilla/nightly --obj /mozilla/nightly/obj memory/mozalloc + +With --top and --obj explicitly set generate $MOZ_OBJDIR/memory/mozalloc/Makefile +while sitting in the sandbox root. + +=back + + +=head2 Work In Progress + +=over 4 + +=item --no-badtokens + +Fail on unexpanded @foo@ makefile tokens. Any tokens that can be expanded +directly by make-makefile will avoid config.status shell overhead. + +=item Depth from delta(--obj, --top) + +If DEPTH= has not been embedded within a makefile the value could +be set directly if --top and --obj are specified and the paths overlap. + +=back + + +=head1 SEE ALSO + +L + +=cut diff --git a/deps/mozjs/js/src/build/autoconf/make-makefile.excl b/deps/mozjs/js/src/build/autoconf/make-makefile.excl new file mode 100644 index 00000000000..25ab32dd267 --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/make-makefile.excl @@ -0,0 +1,5 @@ +########################################################################### +## Intent: Exclusion list for container make builds +########################################################################### + +# EOF diff --git a/deps/mozjs/js/src/build/autoconf/makemakefile.pm b/deps/mozjs/js/src/build/autoconf/makemakefile.pm new file mode 100644 index 00000000000..6eafbce4fca --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/makemakefile.pm @@ -0,0 +1,745 @@ +package makemakefile; + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999-2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Lamm +# Joey Armstrong +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +# use feature 'state'; 5.10+ not available everywhere + +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(2.0); +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(dirname_legacy + getConfig getDepth getRelPath getObjDir getTopDir mkdirr + getExclusions + run_config_status + updateMakefiles + update_makefiles_legacy + ); + +##--------------------## +##---] INCLUDES [---## +##--------------------## +use Cwd; +use Cwd qw{abs_path}; +use FindBin; +use File::Basename; +use File::Copy; + +##-------------------## +##---] GLOBALS [---## +##-------------------## +umask 0; +my $cwd = Cwd::abs_path('.'); +my %argv; + + +########################################################################### +## Intent: Helper function, retrieve contents of a file with error checking +## ----------------------------------------------------------------------- +## Args: +## scalar path to input file +## Returns: +## array contents of the given file +## $@ set on error +########################################################################### +sub cat +{ + my $fyl = shift || ''; + $@ = ''; + my @data; + + local *FYL; + if (!open(FYL, $fyl)) + { + $@ = "open($fyl) failed: $!"; + } + else + { + @data = ; + close(FYL); + } + return @data; +} # cat + +########################################################################### +## Intent: Return directory path for a given argument +## ----------------------------------------------------------------------- +## ----------------------------------------------------------------------- +## Todo: +## o Check if function can be replaced by File::Basename::dirname() +########################################################################### +sub dirname_legacy +{ + my $str = (@_ && defined($_[0])) ? shift : ''; + return $str =~ /(.*)\/.*/ ? "$1" : '.'; +} + +########################################################################### +## Intent: Given a list of makefile paths recursively create all +## directories between file and the root +## ----------------------------------------------------------------------- +## Args: +## array A list of makefiles +## fargs Function arguments +## mode Filesystem mode used for directory creation +## Returns: +## $@ Set on error +## 0 on success +## ----------------------------------------------------------------------- +## Note: +## Reporting directory creation can be enabled by the --verbose +## command line argument. +########################################################################### +sub mkdirr +{ + my %fargs = (@_ && ref($_[$#_])) ? %{ (pop) } : (); + my $mode = $fargs{mode} || 0755; + my $verbose = $main::argv{verbose} || 0; + $@ = '' unless ($fargs{recursive}); + $fargs{recursive} = 1; + + my @errors; + push(@errors, $@) if ($@); + foreach my $path (@_) + { + (my $dir = $path) =~ s%/?Makefile[^/]*$%%o; + next unless (length($dir)); + next if (-e $dir); + mkdirr( dirname($dir), \%fargs); + eval{ File::Path::mkpath($dir, $verbose, 0755); }; + push(@errors, $@) if ($@); + } + $@ = join("\n", @errors); + return $@ ? 0 : 1; +} # mkdirr + +########################################################################### +## Intent: Read in configure values and return a hash of key/value pairs +## ----------------------------------------------------------------------- +## Args: +## fargs Function arguments +## reset clear value storage and repopulate +## Returns: +## hash configure data to use for makefile substitutions +## ----------------------------------------------------------------------- +## Todo: wrapper for reading config* and run_config_status +########################################################################### +my %_CONFIG_; # todo: state %config; w/5.10 +sub getConfig +{ + my %fargs = (@_ && ref($_[$#_]) eq 'HASH') ? %{ (pop) } : (); + if ($fargs{reset}) + { + %_CONFIG_ = (); + shift; + } + + #my $ac_file_in = "$ac_given_srcdir/${ac_file}.in"; + #my $ac_dir = dirname_legacy($ac_file); + #my $ac_dots = ''; + #my $ac_dir_suffix = ''; + #my $srcdir = '.'; + #my $top_srcdir = '.'; + unless (%_CONFIG_) + { + while (@_) + { + my ($k, $v) = splice(@_, 0, 2); + $_CONFIG_{$k} = $v; + } + } + + return %_CONFIG_; +} # getConfig + +########################################################################### +## Intent: Determine path depth between leaf and root directory. +## o DEPTH= may be set by makefile content +## o DEPTH= may be set by Makefile in a parent +## o Manually determine by relpath form leaf to sandbox top +## ----------------------------------------------------------------------- +## Args: +## scalar Path to makefile or directory to determine DEPTH for +## Returns: +## scalar Relative path from leaf to root directory +## ----------------------------------------------------------------------- +########################################################################### +sub getDepth($) +{ + my $fyl = shift || ''; + + my @path = split(m%/%o, $fyl); + pop(@path) if ('Makefile' eq substr($path[$#path], 0, 8)); + my $depth; + my @depth; + + my $top = getTopDir(); + my @top = split(m%/%o, $top); + my @pathNoTop = @path; + splice(@pathNoTop, 0, scalar(@top)); + + SEARCH: + while (@path) + { + ## Search for a file containing DEPTH=../.. + foreach my $fyl ( qw{Makefile.in Makefile} ) + { + my $path = join('/', @path, $fyl); + local *FYL; + if (!open(FYL, $path)) {} # NOP + elsif (my @tmp = map{ /^\s*DEPTH\s*=\s*([\.\/]+)/o ? $1 : () } ) + { + $depth = join('/', @depth, shift @tmp); + last SEARCH; + } + close(FYL); + } + pop @path; + pop @pathNoTop; + + if (0 == scalar(@pathNoTop)) + { + $depth = join('/', @depth); + last; + } + + ## Construct path manually + push(@depth, '..'); + } + return $depth; +} # getDepth + +########################################################################### +## Intent: Read in the exclusion file +########################################################################### +sub getExclusions +{ + my $file = shift || ''; + + return () if ($main::argv{'no-exclusions'}); + + my %exclude; + if ($file) + { + my @data = cat($file); + foreach (@data) + { + next unless ($_); + next if (/^\s*\#/o); + next unless (m%/%); + chomp; + $exclude{$_}++; + } + } + return %exclude; +} # getExclusions + +########################################################################### +## Intent: Given the path to a makefile beneath either src or obj +## derive the relative path prefix between makefile and root. +########################################################################### +sub getRelPath +{ + my $path0 = shift; + my $abspath; + + # Determine type and orientation + my $name = basename($path0); + my $haveMF = ($name eq 'Makefile.in') ? 1 + : ($name eq 'Makefile') ? -1 + : 0 + ; + + #################################################### + ## Prep work: form a relative path with ../ removed + #################################################### + my $top = getTopDir(); + my $obj = getObjDir(); + ## If the same Makefile will be created alongside Makefile.in + my $topQM = quotemeta($top); + my $objQM = quotemeta($obj); + + if ('..' eq substr($path0, 0, 2)) + { + my @cwd = split(m%/%, $cwd); + my @pth = split(m%/%, $path0); + while (@pth && $pth[0] eq '..') + { + pop(@cwd); + shift @pth; + } + $path0 = join('/', @cwd, @pth); + $abspath = $path0; + } + + if ('/' eq substr($path0, 0, 1)) + { + $path0 =~ s%^$objQM\/?%%; + $path0 =~ s%^$topQM\/?%%; + } + + ####################################################################### + ## Build a list of directories to search. Input source will be one + ## of path to Makefile.in, path to Makefile, directory, file within + ## a directory or relative path from cwd. + ####################################################################### + my @subdirs; + my $path = (0 == $haveMF) ? $path0 : dirname($path0); + push(@subdirs, $path); # containing directory + push(@subdirs, dirname($path)) if (0 == $haveMF && -f $path); # Arg is file within a directory + push(@subdirs, $cwd); # relative to pwd + + # obj - path to generated makefile + # top - path to Makefile.in source template + my @prefixes = ('/' ne substr($path0, 0, 1)) + ? (&getTopDir, &getObjDir) + : () + ; + + ON_SAFARI: + for my $prefix (@prefixes) + { + next unless ($prefix); # no command line not passed + foreach my $subdir (@subdirs) + { + foreach my $mf ('Makefile.in', 'Makefile') + { + my $path = join('/', $prefix, $subdir, $mf); + if (-e $path) + { + $name = $mf; + $haveMF = ($mf eq 'Makefile.in') ? 1 : -1; + $abspath = $path; + last ON_SAFARI; + } + } + } + } + + ####################################################################### + ## Generated makefile does not yet exist or path is invalid. + ## Should this conditon be handled to detect non-existent Makefile.in: + ## Makefile.am => Makefile.in => Makefile but Makefile.in + ####################################################################### + if (!$abspath && -1 == $haveMF && $obj) + { + $abspath = ('/' eq substr($path0, 0, 1)) + ? $path0 + : join('/', $obj, $path0) + ; + } + + ######################################################## + ## If --top and/or --obj specified extract relative path + ######################################################## + my $relpath; + if (! $abspath) + { + # Error, fall through + } + elsif (1 == $haveMF) # Makefile.in + { + ## err w/o --top + (my $tmp = $abspath) =~ s%^$topQM/?%%; + $relpath = dirname($tmp) unless ($tmp eq $abspath); + } + elsif (-1 == $haveMF) # Makefile + { + ## err w/o --obj + (my $tmp = $abspath) =~ s%^$objQM/?%%; + $relpath = dirname($tmp) unless ($tmp eq $abspath); + } + + $relpath ||= ''; + $relpath =~ s%/./%/%og; # filter ./ + + $@ = ($relpath) ? '' : "ERROR($path0): Unable to locate sources"; + return $relpath || ''; +} # getRelPath + +########################################################################### +## Intent: Determine sandbox root from script startup directory +## ----------------------------------------------------------------------- +## Args: +## _set_ optional, if passed use the given value as path +## _reset_ clear cached directory path to reassign +## Returns: +## scalar - absolute path to the sandbox root directory +## ----------------------------------------------------------------------- +########################################################################### +my $gtd_dir; +sub getTopDir +{ + if (@_) # testing override + { + $gtd_dir = abs_path($_[1] || '.') if ($_[0] eq '_set_'); + $gtd_dir = '' if ($_[0] eq '_reset_'); + } + + unless ($gtd_dir) + { + ## Set by command line + if ($main::argv{top}) + { + $gtd_dir = $main::argv{top}; + } + else + { + my $path = abs_path($FindBin::RealBin); + my @path = split(m%/%o, $path); + ## --2 memory/mozalloc/Makefile.in + ## --3 was this for FindBin::Script ? + splice(@path, -2); + $gtd_dir = join('/', @path); + } + } + return $gtd_dir; +} # getTopDir + +########################################################################### +## Intent: Determine path to MOZ_OBJDIR/object directory +## ----------------------------------------------------------------------- +## Args: +## _set_ optional testing arg, if passed re-compute cached value +## Returns: +## scalar - absolute path to the sandbox object directory +## ----------------------------------------------------------------------- +########################################################################### +my $god_dir; +sub getObjDir +{ + if (@_) # testing override + { + if ($_[0] eq '_reset_') + { + $god_dir = ''; + shift; + } + elsif ($_[0] eq '_set_') + { + shift; + my $path = $_[0] || '.'; + $god_dir = abs_path($path); + shift; + } + } + + ## extract $obj from given path + unless ($god_dir) + { + if ($main::argv{obj}) + { + $god_dir = $main::argv{obj}; + } + elsif (@_ && 'Makefile' eq substr($_, -8)) + { + $god_dir = abs_path(shift); + } + else # assume we are sitting in moz_objdir + { + $god_dir = abs_path('.'); + } + } + + return $god_dir; +} # getObjDir + +########################################################################### +## Intent: Generate Makefile from a given Makefile.in template +## ----------------------------------------------------------------------- +## Args: +## scalar Relative path to a directory containing a makefile +## fargs Hash ref of function arguments. +## obj Absolute path to MOZ_OBJ/a destination directory +## top Absolute path to the sandbox root +## Returns: +## $@ Set on error +## scalar +## 1 True if the makefile was updated +## 0 Otherwise +## badtokens - If the makefile contains unexpandable @token@ strings +## ----------------------------------------------------------------------- +########################################################################### +sub updateMakefiles +{ + my %fargs = (@_ && ref($_[$#_])) ? %{ (pop) } : (); + local $_; + $@ = ''; + + my $top = $fargs{top}; + my $obj = $fargs{obj}; + + my $relpath = shift || ''; + my $src = join('/', $top, $relpath, 'Makefile.in'); + my $depth = getDepth($src); + + my @src = cat($src); + return 0 if ($@); + + my $dst = join('/', $obj, $relpath, 'Makefile'); + my @dst = cat($dst); + $@ = ''; + + my $dstD = dirname($dst); + mkdirr($dstD); + return 0 if ($@); + + my %data = + ( getConfig(), + depth => $depth, + srcdir => join('/', $top, $relpath), + top_srcdir => $top, + ); + + my $line = 0; + my @data; + while (scalar @src) + { + $line++; + $_ = shift(@src); + + ## Expand embedded @foo@ + while (/\@[^\@\s\$]+\@/go) + { + my $end = pos($_); + my $val = $&; + my $len = length($val); + $val =~ s/^\@\s*//o; + $val =~ s/\s*\@$//o; + + ## Identify expansions to see if we can avoid shell overhead + if (!defined $data{$val} && !$argv{'no-badtokens'}) + { + if (1) # warnings + { + print STDERR "WARNING: token $val not defined\n"; + print STDERR " line $line, src: $src\n"; + } + return 'badtokens'; + } + + # Insert $(error txt) makefile macros for invalid tokens + my $val1 = defined($data{$val}) + ? $data{$val} + : "\$(error $FindBin::Script: variable ${val} is undefined)" + ; + substr($_, ($end-$len), $len, $val1); + } + push(@data, $_); + } + + if (("@data" eq "@dst") && scalar(@data)) + { + print "Skipping up2date makefile: $dst\n" if ($argv{verbose}); + } + else + { + my $action = (scalar @dst) ? 'Updating' : 'Creating'; + print "$action makefile: $dst\n"; + + my $tmp = join('.', $dst, "tmp_$$"); + if (!open(FYL, "> $tmp")) + { + $@ = "open($tmp) failed: $!"; + } + else + { + print FYL @data; + close(FYL); + + ## Install the new makefile + File::Copy::move($tmp, $dst) + || ($@ = "move($tmp, $dst) failed: $!"); + } + } + + return $@ ? 0 : 1; +} # updateMakefiles + +# Output the makefiles. +# +sub update_makefiles_legacy { + my ($ac_given_srcdir, $pac_given_srcdir, @makefiles) = @_; + my $debug = $main::argv{debug} || 0; + my $pwdcmd = ($^O eq 'msys') ? 'pwd -W' : 'pwd'; + my @unhandled=(); + + my @warn; + + my $ac_file; + foreach $ac_file (@makefiles) { + my $ac_file_in = "$ac_given_srcdir/${ac_file}.in"; + my $ac_dir = dirname_legacy($ac_file); + my $ac_dots = ''; + my $ac_dir_suffix = ''; + my $srcdir = '.'; + my $top_srcdir = '.'; + + # Determine $srcdir and $top_srcdir + # + if ($ac_dir ne '.') { + $ac_dir_suffix = "/$ac_dir"; + $ac_dir_suffix =~ s%^/\./%/%; + $ac_dots = $ac_dir_suffix; + # Remove .. components from the provided dir suffix, and + # also the forward path components they were reversing. + my $backtracks = $ac_dots =~ s%\.\.(/|$)%%g; + while ($backtracks--) { + $ac_dots =~ s%/[^/]*%%; + } + $ac_dots =~ s%/[^/]*%../%g; + } + if ($ac_given_srcdir eq '.') { + if ($ac_dots ne '') { + $top_srcdir = $ac_dots; + $top_srcdir =~ s%/$%%; + } + } elsif ($pac_given_srcdir =~ m%^/% or $pac_given_srcdir =~ m%^.:/%) { + $srcdir = "$pac_given_srcdir$ac_dir_suffix"; + $top_srcdir = "$pac_given_srcdir"; + } else { + if ($debug) { + print "ac_dots = $ac_dots\n"; + print "ac_dir_suffix = $ac_dir_suffix\n"; + } + $srcdir = "$ac_dots$ac_given_srcdir$ac_dir_suffix"; + $top_srcdir = "$ac_dots$ac_given_srcdir"; + } + + if ($debug) { + print "ac_dir = $ac_dir\n"; + print "ac_file = $ac_file\n"; + print "ac_file_in = $ac_file_in\n"; + print "srcdir = $srcdir\n"; + print "top_srcdir = $top_srcdir\n"; + print "cwd = " . `$pwdcmd` . "\n"; + } + + # Copy the file and make substitutions. + # @srcdir@ -> value of $srcdir + # @top_srcdir@ -> value of $top_srcdir + # + if (-e $ac_file) { + next if -M _ < -M $ac_file_in; # Next if Makefile is up-to-date. + warn "updating $ac_file\n"; + } else { + warn "creating $ac_file\n"; + } + + mkdirr(dirname($ac_file)); + + open INFILE, "<$ac_file_in" or do { + warn "$0: Cannot read $ac_file_in: No such file or directory\n"; + next; + }; + open OUTFILE, ">$ac_file" or do { + warn "$0: Unable to create $ac_file\n"; + next; + }; + + while () { + s/\@srcdir\@/$srcdir/g; + s/\@top_srcdir\@/$top_srcdir/g; + + if (/\@[_a-zA-Z]*\@/) { + #warn "Unknown variable:$ac_file:$.:$_"; + push @unhandled, $ac_file; + last; + } + print OUTFILE; + } + close INFILE; + close OUTFILE; + } + return @unhandled; +} # update_makefiles_legacy + +########################################################################### +## Intent: Invoke config.status for unknown makefiles to create +## directory hierarchy for the tree. +## ----------------------------------------------------------------------- +## Args: +## array an optional list of makefiles to process +## Returns: +## 0 on success +## $# set on error +## ----------------------------------------------------------------------- +## Note: Is this function needed anymore ? Undefined tokens should fail +## at time of expansion rather than having to source config.status. +## Also config.status could be parsed to define values and avoide the +## shell overhead altogether. +########################################################################### +sub run_config_status { + my @unhandled = @_; + + # Run config.status with any unhandled files. + # + my @errors; + if (@unhandled) { + local $ENV{CONFIG_FILES}= join ' ', @unhandled; + + my $conf = 'config.status'; + if (! -e $conf) # legacy behavior, warn rather than err + { + my $cwd = cwd(); + my $err = "$FindBin::Script ERROR: Config file $conf does not exist, cwd=$cwd"; + push(@errors, $err); + } + elsif (0 != system("./config.status")) + { + my $cwd = cwd(); + push(@errors, "config.status failed \$?=$?, \$!=$!, cwd: $cwd"); + } + } + $@ = join("\n", @errors); + + ## Legacy behavior: config.status problems are not fatal {yet}. + ## Display warning since caller will not be calling die. + warn $@ if ($@ && $argv{'no-warnings'}); + return $@ ? 1 : 0; +} + +1; diff --git a/deps/mozjs/js/src/build/autoconf/mozcommonheader.m4 b/deps/mozjs/js/src/build/autoconf/mozcommonheader.m4 new file mode 100644 index 00000000000..cc910377858 --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/mozcommonheader.m4 @@ -0,0 +1,41 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is the +dnl Mozilla Foundation +dnl +dnl Portions created by the Initial Developer are Copyright (C) 2009 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +AC_DEFUN(MOZ_CHECK_COMMON_HEADERS, + MOZ_CHECK_HEADERS(sys/byteorder.h compat.h getopt.h sys/bitypes.h \ + memory.h unistd.h gnu/libc-version.h nl_types.h malloc.h \ + X11/XKBlib.h io.h cpuid.h) +) diff --git a/deps/mozjs/js/src/build/autoconf/mozheader.m4 b/deps/mozjs/js/src/build/autoconf/mozheader.m4 new file mode 100644 index 00000000000..50b3189944c --- /dev/null +++ b/deps/mozjs/js/src/build/autoconf/mozheader.m4 @@ -0,0 +1,66 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is the +dnl Mozilla Foundation +dnl +dnl Portions created by the Initial Developer are Copyright (C) 2009 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl Neil Rashbrook +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +dnl MOZ_CHECK_HEADER(HEADER-FILE, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADER], +[ dnl Do the transliteration at runtime so arg 1 can be a shell variable. + ac_safe=`echo "$1" | sed 'y%./+-%__p_%'` + AC_MSG_CHECKING([for $1]) + AC_CACHE_VAL(ac_cv_header_$ac_safe, + [ AC_TRY_COMPILE([$4 +#include <$1>], , + eval "ac_cv_header_$ac_safe=yes", + eval "ac_cv_header_$ac_safe=no") ]) + if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , , [$3]) + fi +]) + +dnl MOZ_CHECK_HEADERS(HEADER-FILE... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADERS], +[ for ac_hdr in $1 + do + MOZ_CHECK_HEADER($ac_hdr, + [ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + AC_DEFINE_UNQUOTED($ac_tr_hdr) $2], $3, [$4]) + done +]) diff --git a/deps/mozjs/js/src/build/autoconf/moznbytetype.m4 b/deps/mozjs/js/src/build/autoconf/moznbytetype.m4 index 430eae573a8..b1266f1822a 100644 --- a/deps/mozjs/js/src/build/autoconf/moznbytetype.m4 +++ b/deps/mozjs/js/src/build/autoconf/moznbytetype.m4 @@ -49,7 +49,7 @@ dnl dnl for example: dnl MOZ_N_BYTE_TYPE([JS_INT32_T], [4], [int long 'long long' short]) dnl -AC_DEFUN(MOZ_N_BYTE_TYPE, +AC_DEFUN([MOZ_N_BYTE_TYPE], [ dnl The simplest approach would simply be to run a program that says dnl printf ("%d\n", sizeof ($type)); @@ -83,7 +83,7 @@ dnl found. dnl dnl for example: dnl MOZ_SIZE_OF_TYPE([JS_BYTES_PER_WORD], [void*], [4 8]) -AC_DEFUN(MOZ_SIZE_OF_TYPE, +AC_DEFUN([MOZ_SIZE_OF_TYPE], [ AC_CACHE_CHECK([for the size of $2], moz_cv_size_of_$1, [ moz_cv_size_of_$1= @@ -113,7 +113,7 @@ dnl found. dnl dnl for example: dnl MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) -AC_DEFUN(MOZ_ALIGN_OF_TYPE, +AC_DEFUN([MOZ_ALIGN_OF_TYPE], [ AC_CACHE_CHECK([for the alignment of $2], moz_cv_align_of_$1, [ moz_cv_align_of_$1= diff --git a/deps/mozjs/js/src/build/autoconf/mozprog.m4 b/deps/mozjs/js/src/build/autoconf/mozprog.m4 index f22b4c68a83..eca23b749af 100644 --- a/deps/mozjs/js/src/build/autoconf/mozprog.m4 +++ b/deps/mozjs/js/src/build/autoconf/mozprog.m4 @@ -36,14 +36,14 @@ dnl the terms of any one of the MPL, the GPL or the LGPL. dnl dnl ***** END LICENSE BLOCK ***** -AC_DEFUN(MOZ_PROG_CHECKMSYS, +AC_DEFUN([MOZ_PROG_CHECKMSYS], [AC_REQUIRE([AC_INIT_BINSH])dnl if test `uname -s | grep -c MINGW 2>/dev/null` != "0"; then msyshost=1 fi ]) -AC_DEFUN(MOZ_PATH_PROG, +AC_DEFUN([MOZ_PATH_PROG], [ AC_PATH_PROG($1,$2,$3,$4) if test "$msyshost"; then case "[$]$1" in @@ -59,7 +59,7 @@ AC_DEFUN(MOZ_PATH_PROG, fi ]) -AC_DEFUN(MOZ_PATH_PROGS, +AC_DEFUN([MOZ_PATH_PROGS], [ AC_PATH_PROGS($1,$2,$3,$4) if test "$msyshost"; then case "[$]$1" in diff --git a/deps/mozjs/js/src/build/hcc b/deps/mozjs/js/src/build/hcc deleted file mode 100644 index adadbe48926..00000000000 --- a/deps/mozjs/js/src/build/hcc +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/sh -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Fix brain-damaged compilers that don't understand -o and -c together -# -CC=`echo $1 | sed -e "s|'||g" -e 's|"||g'` -shift -DASH_C=0 -DASH_O=0 -DUMMY="XxxXxxX" -GET_OBJECT=0 -OBJ="${DUMMY}" -OBJECT="${DUMMY}" - -for i in $* -do - [ "${CHECK_O}" = yes ] && { - case $i in - ./*/*.o) OBJECT="$i" - OPTS="${OPTS} -o" - DASH_O=1 - ;; - ./*.o) OBJECT="`basename $i`" - i="" - DASH_O=1 - ;; - *.o) if [ $i = `basename $i` ] - then - OBJECT="$i" - i="" - else - OPTS="${OPTS} -o" - fi - DASH_O=1 - ;; - *) OPTS="${OPTS} -o $i" - DASH_O=1 - i="" - ;; - esac - CHECK_O=no - } - case $i in - -c) DASH_C=1 - OPTS="${OPTS} -c" - ;; - -o) CHECK_O=yes - ;; - *.c) C_SRC=$i - OPTS="${OPTS} $i" -# cc always creates the .o from the .c name - OBJ=`basename $C_SRC .c`.o - ;; - *.s) S_SRC=$i - OPTS="${OPTS} $i" -# or the .o from the .s name - OBJ=`basename $S_SRC .s`.o - ;; - *.o) OBJECT=$i - OPTS="${OPTS} $i" - ;; - *) OPTS="${OPTS} $i" - ;; - esac -done - -${CC} ${OPTS} || exit $? - -# if there was no -c and -o we're done -[ $DASH_C = 1 -a $DASH_O = 1 ] || exit 0 - -# if $OBJ and $OBJECT are the same we're done -[ $OBJ = $OBJECT ] && exit 0 - -[ -f $OBJ ] && mv -f $OBJ $OBJECT diff --git a/deps/mozjs/js/src/build/hcpp b/deps/mozjs/js/src/build/hcpp deleted file mode 100644 index ea55fa44a1b..00000000000 --- a/deps/mozjs/js/src/build/hcpp +++ /dev/null @@ -1,155 +0,0 @@ -#!/bin/sh -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Wrapper for brain-damaged compilers that don't understand -o and -c together. -# -CXX=`echo $1 | sed -e "s|'||g" -e 's|"||g'` -shift -DUMMY="XxxXxxX" -DASH_C=0 -DASH_O=0 -GET_OBJECT=0 -C_SRC="${DUMMY}" -CC_SRC="${DUMMY}" -CPP_SRC="${DUMMY}" -S_SRC="${DUMMY}" -OBJECT="${DUMMY}" -NEW_i="${DUMMY}" -PLATFORM=`uname -s` - -for i in $* -do - [ ${GET_OBJECT} -eq 1 ] && { - case $i in - ./*/*.o) OBJECT="$i" - OPTS="${OPTS} -o" - DASH_O=1 - ;; - ./*.o) OBJECT="`basename $i`" - i="" - DASH_O=1 - ;; - *.o) if [ $i = `basename $i` ] - then - i="" - else - OPTS="${OPTS} -o" - DASH_O=1 - fi - ;; - *) OPTS="${OPTS} -o $i" - DASH_O=1 - i="" - ;; - esac - GET_OBJECT=0 - } - case $i in - -c) - DASH_C=1 - OPTS="${OPTS} -c" - ;; - -o) - GET_OBJECT=1 - ;; - *.c) - C_SRC="$i" - OPTS="${OPTS} $i" -# cc always creates the .o from the .c name - OBJ=`basename ${C_SRC} .c`.o - ;; - +.*) - OPTS="${OPTS} $i" - ;; - *.cpp) - CPP_SRC="$i" - if [ "${PLATFORM}" = "SCO_SV" ]; then - OPTS="${OPTS} +.cpp $i" - elif [ "${PLATFORM}" = "IRIX" ]; then - NEW_i=`basename ${CPP_SRC} .cpp`.C - rm -f ${NEW_i} - cp $i ${NEW_i} - OPTS="${OPTS} ${NEW_i}" - else - OPTS="${OPTS} $i" - fi -# cc always creates the .o from the .cpp name - OBJ=`basename ${CPP_SRC} .cpp`.o - ;; - *.cc) - CC_SRC="$i" - OPTS="${OPTS} $i" -# cc always creates the .o from the .cc name - OBJ=`basename ${CC_SRC} .cc`.o - ;; - *.s) - S_SRC="$i" - OPTS="${OPTS} $i" -# cc always creates the .o from the .s name - OBJ=`basename ${S_SRC} .s`.o - ;; - *.o) OBJECT=$i - OPTS="${OPTS} $i" - ;; - *) OPTS="${OPTS} $i" - ;; - esac -done - -${CXX} ${OPTS} || exit $? -rm -f ${NEW_i} - -# Really only needed for NSPR now. -if [ "${PLATFORM}" = "IRIX" -a "$OBJ" != "$OBJECT" ]; then - OBJ=$OBJECT -fi - -# LAME!!! -if [ -f -O ]; then - mv -f -- -O ${OBJECT} -fi - -# if there was no -c and -o we're done -[ ${DASH_C} -eq 1 -a ${DASH_O} -eq 1 ] || exit 0 - -# if $OBJ and $OBJECT are the same we're done -[ $OBJ = $OBJECT ] && exit 0 - -[ -f $OBJ ] && mv -f $OBJ $OBJECT - diff --git a/deps/mozjs/js/src/build/unix/Makefile.in b/deps/mozjs/js/src/build/unix/Makefile.in deleted file mode 100644 index 0bf3e36180d..00000000000 --- a/deps/mozjs/js/src/build/unix/Makefile.in +++ /dev/null @@ -1,49 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -include $(topsrcdir)/config/rules.mk - -libs:: $(srcdir)/run-mozilla.sh - $(INSTALL) $< $(DIST)/bin diff --git a/deps/mozjs/js/src/build/unix/run-mozilla.sh b/deps/mozjs/js/src/build/unix/run-mozilla.sh deleted file mode 100755 index 8372064c308..00000000000 --- a/deps/mozjs/js/src/build/unix/run-mozilla.sh +++ /dev/null @@ -1,400 +0,0 @@ -#!/bin/sh -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** -cmdname=`basename "$0"` -MOZ_DIST_BIN=`dirname "$0"` -MOZ_DEFAULT_NAME="./${cmdname}-bin" -MOZ_APPRUNNER_NAME="./mozilla-bin" -MOZ_PROGRAM="" - -exitcode=1 -# -## -## Functions -## -########################################################################## -moz_usage() -{ -echo "Usage: ${cmdname} [options] [program]" -echo "" -echo " options:" -echo "" -echo " -g Run in debugger." -echo " --debug" -echo "" -echo " -d debugger Debugger to use." -echo " --debugger debugger" -echo "" -echo " -a debugger_args Arguments passed to [debugger]." -echo " --debugger-args debugger_args" -echo "" -echo " Examples:" -echo "" -echo " Run the mozilla-bin binary" -echo "" -echo " ${cmdname} mozilla-bin" -echo "" -echo " Debug the mozilla-bin binary in gdb" -echo "" -echo " ${cmdname} -g mozilla-bin -d gdb" -echo "" -echo " Run mozilla-bin under valgrind with arguments" -echo "" -echo " ${cmdname} -g -d valgrind -a '--tool=memcheck --leak-check=full' mozilla-bin" -echo "" - return 0 -} -########################################################################## -moz_bail() -{ - message=$1 - echo - echo "$cmdname: $message" - echo - exit 1 -} -########################################################################## -moz_test_binary() -{ - binary=$1 - if [ -f "$binary" ] - then - if [ -x "$binary" ] - then - return 1 - fi - fi - return 0 -} -########################################################################## -moz_get_debugger() -{ - debuggers="ddd gdb dbx bdb native-gdb" - debugger="notfound" - done="no" - for d in $debuggers - do - moz_test_binary /bin/which - if [ $? -eq 1 ] - then - dpath=`which ${d}` - else - dpath=`LC_MESSAGES=C type ${d} | awk '{print $3;}' | sed -e 's/\.$//'` - fi - if [ -x "$dpath" ] - then - debugger=$dpath - break - fi - done - echo $debugger - return 0 -} -########################################################################## -moz_run_program() -{ - prog=$MOZ_PROGRAM - ## - ## Make sure the program is executable - ## - if [ ! -x "$prog" ] - then - moz_bail "Cannot execute $prog." - fi - ## - ## Run the program - ## - exec "$prog" ${1+"$@"} - exitcode=$? -} -########################################################################## -moz_debug_program() -{ - prog=$MOZ_PROGRAM - ## - ## Make sure the program is executable - ## - if [ ! -x "$prog" ] - then - moz_bail "Cannot execute $prog." - fi - if [ -n "$moz_debugger" ] - then - moz_test_binary /bin/which - if [ $? -eq 1 ] - then - debugger=`which $moz_debugger` - else - debugger=`LC_MESSAGES=C type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'` - fi - else - debugger=`moz_get_debugger` - fi - if [ -x "$debugger" ] - then -# If you are not using ddd, gdb and know of a way to convey the arguments -# over to the prog then add that here- Gagan Saksena 03/15/00 - case `basename $debugger` in - native-gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} - exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} - exitcode=$? - ;; - gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} - exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} - exitcode=$? - ;; - ddd) echo "$debugger $moz_debugger_args --gdb -- --args $prog" ${1+"$@"} - exec "$debugger" $moz_debugger_args --gdb -- --args "$prog" ${1+"$@"} - exitcode=$? - ;; - *) echo "$debugger $moz_debugger_args $prog ${1+"$@"}" - exec $debugger $moz_debugger_args "$prog" ${1+"$@"} - exitcode=$? - ;; - esac - else - moz_bail "Could not find a debugger on your system." - fi -} -########################################################################## -## -## Command line arg defaults -## -moz_debug=0 -moz_debugger="" -moz_debugger_args="" -# -## -## Parse the command line -## -while [ $# -gt 0 ] -do - case $1 in - -g | --debug) - moz_debug=1 - shift - ;; - -d | --debugger) - moz_debugger=$2; - if [ "${moz_debugger}" != "" ]; then - shift 2 - else - echo "-d requires an argument" - exit 1 - fi - ;; - -a | --debugger-args) - moz_debugger_args=$2; - if [ "${moz_debugger_args}" != "" ]; then - shift 2 - else - echo "-a requires an argument" - exit 1 - fi - ;; - *) - break; - ;; - esac -done -# -## -## Program name given in $1 -## -if [ $# -gt 0 ] -then - MOZ_PROGRAM=$1 - shift -fi -## -## Program not given, try to guess a default -## -if [ -z "$MOZ_PROGRAM" ] -then - ## - ## Try this script's name with '-bin' appended - ## - if [ -x "$MOZ_DEFAULT_NAME" ] - then - MOZ_PROGRAM=$MOZ_DEFAULT_NAME - ## - ## Try mozilla-bin - ## - elif [ -x "$MOZ_APPRUNNER_NAME" ] - then - MOZ_PROGRAM=$MOZ_APPRUNNER_NAME - fi -fi -# -# -## -## Make sure the program is executable -## -if [ ! -x "$MOZ_PROGRAM" ] -then - moz_bail "Cannot execute $MOZ_PROGRAM." -fi -# -## -## Set MOZILLA_FIVE_HOME -## -MOZILLA_FIVE_HOME=$MOZ_DIST_BIN - -if [ -z "$MRE_HOME" ]; then - MRE_HOME=$MOZILLA_FIVE_HOME -fi -## -## Set LD_LIBRARY_PATH -## -## On Solaris we use $ORIGIN (set in RUNPATH) instead of LD_LIBRARY_PATH -## to locate shared libraries. -## -## When a shared library is a symbolic link, $ORIGIN will be replaced with -## the real path (i.e., what the symbolic link points to) by the runtime -## linker. For example, if dist/bin/libxul.so is a symbolic link to -## toolkit/library/libxul.so, $ORIGIN will be "toolkit/library" instead of "dist/bin". -## So the runtime linker will use "toolkit/library" NOT "dist/bin" to locate the -## other shared libraries that libxul.so depends on. This only happens -## when a user (developer) tries to start firefox, thunderbird, or seamonkey -## under dist/bin. To solve the problem, we should rely on LD_LIBRARY_PATH -## to locate shared libraries. -## -## Note: -## We test $MOZ_DIST_BIN/*.so. If any of them is a symbolic link, -## we need to set LD_LIBRARY_PATH. -########################################################################## -moz_should_set_ld_library_path() -{ - [ `uname -s` != "SunOS" ] && return 0 - for sharedlib in $MOZ_DIST_BIN/*.so - do - [ -h $sharedlib ] && return 0 - done - return 1 -} -if moz_should_set_ld_library_path -then - LD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"} -fi - -if [ -n "$LD_LIBRARYN32_PATH" ] -then - LD_LIBRARYN32_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN32_PATH:+":$LD_LIBRARYN32_PATH"} -fi -if [ -n "$LD_LIBRARYN64_PATH" ] -then - LD_LIBRARYN64_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN64_PATH:+":$LD_LIBRARYN64_PATH"} -fi -if [ -n "$LD_LIBRARY_PATH_64" ]; then - LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"} -fi -# -# -## Set SHLIB_PATH for HPUX -SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"} -# -## Set LIBPATH for AIX -LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"} -# -## Set DYLD_LIBRARY_PATH for Mac OS X (Darwin) -DYLD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${DYLD_LIBRARY_PATH:+":$DYLD_LIBRARY_PATH"} -# -## Set LIBRARY_PATH for BeOS -LIBRARY_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/components:${MRE_HOME}${LIBRARY_PATH:+":$LIBRARY_PATH"} -# -## Set ADDON_PATH for BeOS -ADDON_PATH=${MOZ_DIST_BIN}${ADDON_PATH:+":$ADDON_PATH"} -# -## Solaris Xserver(Xsun) tuning - use shared memory transport if available -if [ "$XSUNTRANSPORT" = "" ] -then - XSUNTRANSPORT="shmem" - XSUNSMESIZE="512" - export XSUNTRANSPORT XSUNSMESIZE -fi - -# Disable Gnome crash dialog -GNOME_DISABLE_CRASH_DIALOG=1 -export GNOME_DISABLE_CRASH_DIALOG - -if [ "$moz_debug" -eq 1 ] -then - echo "MOZILLA_FIVE_HOME=$MOZILLA_FIVE_HOME" - echo " LD_LIBRARY_PATH=$LD_LIBRARY_PATH" - if [ -n "$LD_LIBRARYN32_PATH" ] - then - echo "LD_LIBRARYN32_PATH=$LD_LIBRARYN32_PATH" - fi - if [ -n "$LD_LIBRARYN64_PATH" ] - then - echo "LD_LIBRARYN64_PATH=$LD_LIBRARYN64_PATH" - fi - if [ -n "$LD_LIBRARY_PATH_64" ]; then - echo "LD_LIBRARY_PATH_64=$LD_LIBRARY_PATH_64" - fi - if [ -n "$DISPLAY" ]; then - echo "DISPLAY=$DISPLAY" - fi - if [ -n "$FONTCONFIG_PATH" ]; then - echo "FONTCONFIG_PATH=$FONTCONFIG_PATH" - fi - if [ -n "$MOZILLA_POSTSCRIPT_PRINTER_LIST" ]; then - echo "MOZILLA_POSTSCRIPT_PRINTER_LIST=$MOZILLA_POSTSCRIPT_PRINTER_LIST" - fi - echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH" - echo " LIBRARY_PATH=$LIBRARY_PATH" - echo " SHLIB_PATH=$SHLIB_PATH" - echo " LIBPATH=$LIBPATH" - echo " ADDON_PATH=$ADDON_PATH" - echo " MOZ_PROGRAM=$MOZ_PROGRAM" - echo " MOZ_TOOLKIT=$MOZ_TOOLKIT" - echo " moz_debug=$moz_debug" - echo " moz_debugger=$moz_debugger" - echo "moz_debugger_args=$moz_debugger_args" -fi -# -export MOZILLA_FIVE_HOME LD_LIBRARY_PATH -export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH - -if [ $moz_debug -eq 1 ] -then - moz_debug_program ${1+"$@"} -else - moz_run_program ${1+"$@"} -fi - -exit $exitcode diff --git a/deps/mozjs/js/src/build/unix/uniq.pl b/deps/mozjs/js/src/build/unix/uniq.pl index 1b40bda1c85..1cb8cc0d479 100644 --- a/deps/mozjs/js/src/build/unix/uniq.pl +++ b/deps/mozjs/js/src/build/unix/uniq.pl @@ -22,6 +22,7 @@ # # Contributor(s): # Christopher Seawood +# Joey Armstrong # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), @@ -37,27 +38,88 @@ # # ***** END LICENSE BLOCK ***** -use Getopt::Std; +##----------------------------## +##---] CORE/CPAN INCLUDES [---## +##----------------------------## +use strict; +use warnings; +use Getopt::Long; -getopts('rs'); -$regexp = 1 if (defined($opt_r)); -$sort = 1 if (defined($opt_s)); +##-------------------## +##---] EXPORTS [---## +##-------------------## +our $VERSION = qw(1.1); -undef @out; -if ($sort) { - @in = sort @ARGV; -} else { - @in = @ARGV; +##-------------------## +##---] GLOBALS [---## +##-------------------## +my %argv; +my $modver = $Getopt::Long::VERSION || 0; +my $isOldGetopt = ($modver eq '2.25') ? 1 : 0; + +########################################################################### +## Intent: Script init function +########################################################################### +sub init +{ + if ($isOldGetopt) + { + # mozilla.build/mingw perl in need of an upgrade + # emulate Getopt::Long switch|short:init + foreach (qw(debug regex sort)) + { + if (defined($argv{$_})) + { + $argv{$_} ||= 1; + } + } + } +} # init + +##----------------## +##---] MAIN [---## +##----------------## +my @args = ($isOldGetopt) + ? qw(debug|d regex|r sort|s) + : qw(debug|d:1 regex|r:1 sort|s:1) + ; + +unless(GetOptions(\%argv, @args)) +{ + print "Usage: $0\n"; + print " --sort Sort list elements early\n"; + print " --regex Exclude subdirs by pattern\n"; } -foreach $d (@in) { - if ($regexp) { - $found = 0; - foreach $dir (@out) { - $found++, last if ($d =~ m/^$dir\// || $d eq $dir); + +init(); +my $debug = $argv{debug} || 0; + +my %seen; +my @out; +my @in = ($argv{sort}) ? sort @ARGV : @ARGV; + +foreach my $d (@in) +{ + next if ($seen{$d}++); + + print " arg is $d\n" if ($debug); + + if ($argv{regex}) + { + my $found = 0; + foreach my $dir (@out) + { + my $dirM = quotemeta($dir); + $found++, last if ($d eq $dir || $d =~ m!^${dirM}\/!); } + print "Adding $d\n" if ($debug && !$found); push @out, $d if (!$found); } else { - push @out, $d if (!grep(/^$d$/, @out)); + print "Adding: $d\n" if ($debug); + push(@out, $d); } } + print "@out\n" + +# EOF diff --git a/deps/mozjs/js/src/builtin/MapObject.cpp b/deps/mozjs/js/src/builtin/MapObject.cpp new file mode 100644 index 00000000000..c7cdd46e3b8 --- /dev/null +++ b/deps/mozjs/js/src/builtin/MapObject.cpp @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "builtin/MapObject.h" + +#include "jscntxt.h" +#include "jsgcmark.h" +#include "jsobj.h" + +#include "vm/GlobalObject.h" +#include "vm/Stack.h" + +#include "jsobjinlines.h" + +using namespace js; + +static JSObject * +InitClass(JSContext *cx, GlobalObject *global, Class *clasp, JSProtoKey key, Native construct, + JSFunctionSpec *methods) +{ + JSObject *proto = global->createBlankPrototype(cx, clasp); + if (!proto) + return NULL; + proto->setPrivate(NULL); + + JSAtom *atom = cx->runtime->atomState.classAtoms[key]; + JSFunction *ctor = global->createConstructor(cx, construct, clasp, atom, 0); + if (!ctor || + !LinkConstructorAndPrototype(cx, ctor, proto) || + !DefinePropertiesAndBrand(cx, proto, NULL, methods) || + !DefineConstructorAndPrototype(cx, global, key, ctor, proto)) + { + return NULL; + } + return proto; +} + + +/*** HashableValue *******************************************************************************/ + +bool +HashableValue::setValue(JSContext *cx, const Value &v) +{ + if (v.isString() && v.toString()->isRope()) { + /* Flatten this rope so that equals() is infallible. */ + JSString *str = v.toString()->ensureLinear(cx); + if (!str) + return false; + value = StringValue(str); + } else if (v.isDouble()) { + jsdouble d = v.toDouble(); + int32_t i; + if (JSDOUBLE_IS_INT32(d, &i)) { + /* Normalize int32-valued doubles to int32 for faster hashing and testing. */ + value = Int32Value(i); + } else if (JSDOUBLE_IS_NaN(d)) { + /* NaNs with different bits must hash and test identically. */ + value = DoubleValue(js_NaN); + } else { + value = v; + } + } else { + value = v; + } + + JS_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() || + value.isNumber() || value.isString() || value.isObject()); + return true; +} + +HashNumber +HashableValue::hash() const +{ + /* + * HashableValue::setValue normalizes values so that the SameValue relation + * on HashableValues is the same as the == relationship on + * value.data.asBits, except for strings. + */ + if (value.isString()) { + JSLinearString &s = value.toString()->asLinear(); + return HashChars(s.chars(), s.length()); + } + + /* Having dispensed with strings, we can just hash asBits. */ + uint64_t u = value.asRawBits(); + return HashNumber((u >> 3) ^ (u >> (32 + 3)) ^ (u << (32 - 3))); +} + +bool +HashableValue::equals(const HashableValue &other) const +{ + /* Two HashableValues are equal if they have equal bits or they're equal strings. */ + bool b = (value.asRawBits() == other.value.asRawBits()) || + (value.isString() && + other.value.isString() && + EqualStrings(&value.toString()->asLinear(), + &other.value.toString()->asLinear())); + +#ifdef DEBUG + bool same; + JS_ASSERT(SameValue(NULL, value, other.value, &same)); + JS_ASSERT(same == b); +#endif + return b; +} + + +/*** Map *****************************************************************************************/ + +Class MapObject::class_ = { + "Map", + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_CACHED_PROTO(JSProto_Map), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + finalize, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + mark +}; + +JSFunctionSpec MapObject::methods[] = { + JS_FN("get", get, 1, 0), + JS_FN("has", has, 1, 0), + JS_FN("set", set, 2, 0), + JS_FN("delete", delete_, 1, 0), + JS_FS_END +}; + +JSObject * +MapObject::initClass(JSContext *cx, JSObject *obj) +{ + return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Map, construct, methods); +} + +void +MapObject::mark(JSTracer *trc, JSObject *obj) +{ + MapObject *mapobj = static_cast(obj); + if (ValueMap *map = mapobj->getData()) { + for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) { + gc::MarkValue(trc, r.front().key, "key"); + gc::MarkValue(trc, r.front().value, "value"); + } + } +} + +void +MapObject::finalize(JSContext *cx, JSObject *obj) +{ + MapObject *mapobj = static_cast(obj); + if (ValueMap *map = mapobj->getData()) + cx->delete_(map); +} + +JSBool +MapObject::construct(JSContext *cx, uintN argc, Value *vp) +{ + JSObject *obj = NewBuiltinClassInstance(cx, &class_); + if (!obj) + return false; + + ValueMap *map = cx->new_(cx->runtime); + if (!map || !map->init()) + return false; + obj->setPrivate(map); + + CallArgsFromVp(argc, vp).rval().setObject(*obj); + return true; +} + +#define UNPACK_THIS(T, native, cx, argc, vp, args, data) \ + CallArgs args = CallArgsFromVp(argc, vp); \ + if (!args.thisv().isObject() || \ + !args.thisv().toObject().hasClass(&T::class_)) \ + { \ + return js::HandleNonGenericMethodClassMismatch(cx, args, native, \ + &T::class_); \ + } \ + if (!args.thisv().toObject().getPrivate()) { \ + ReportIncompatibleMethod(cx, args, &T::class_); \ + return false; \ + } \ + T::Data &data = *static_cast(args.thisv().toObject()).getData(); \ + (void) data + +#define THIS_MAP(native, cx, argc, vp, args, map) \ + UNPACK_THIS(MapObject, native, cx, argc, vp, args, map) + +#define ARG0_KEY(cx, args, key) \ + HashableValue key; \ + if (args.length() > 0 && !key.setValue(cx, args[0])) \ + return false + +JSBool +MapObject::get(JSContext *cx, uintN argc, Value *vp) +{ + THIS_MAP(get, cx, argc, vp, args, map); + ARG0_KEY(cx, args, key); + + if (ValueMap::Ptr p = map.lookup(key)) + args.rval() = p->value; + else + args.rval().setUndefined(); + return true; +} + +JSBool +MapObject::has(JSContext *cx, uintN argc, Value *vp) +{ + THIS_MAP(has, cx, argc, vp, args, map); + ARG0_KEY(cx, args, key); + args.rval().setBoolean(map.lookup(key)); + return true; +} + +JSBool +MapObject::set(JSContext *cx, uintN argc, Value *vp) +{ + THIS_MAP(set, cx, argc, vp, args, map); + ARG0_KEY(cx, args, key); + map.put(key, args.length() > 1 ? args[1] : UndefinedValue()); + args.rval().setUndefined(); + return true; +} + +JSBool +MapObject::delete_(JSContext *cx, uintN argc, Value *vp) +{ + THIS_MAP(delete_, cx, argc, vp, args, map); + ARG0_KEY(cx, args, key); + ValueMap::Ptr p = map.lookup(key); + bool found = p.found(); + if (found) + map.remove(p); + args.rval().setBoolean(found); + return true; +} + +JSObject * +js_InitMapClass(JSContext *cx, JSObject *obj) +{ + return MapObject::initClass(cx, obj); +} + + +/*** Set *****************************************************************************************/ + +Class SetObject::class_ = { + "Set", + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_CACHED_PROTO(JSProto_Set), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + finalize, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + mark +}; + +JSFunctionSpec SetObject::methods[] = { + JS_FN("has", has, 1, 0), + JS_FN("add", add, 1, 0), + JS_FN("delete", delete_, 1, 0), + JS_FS_END +}; + +JSObject * +SetObject::initClass(JSContext *cx, JSObject *obj) +{ + return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Set, construct, methods); +} + +void +SetObject::mark(JSTracer *trc, JSObject *obj) +{ + SetObject *setobj = static_cast(obj); + if (ValueSet *set = setobj->getData()) { + for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) + gc::MarkValue(trc, r.front(), "key"); + } +} + +void +SetObject::finalize(JSContext *cx, JSObject *obj) +{ + SetObject *setobj = static_cast(obj); + if (ValueSet *set = setobj->getData()) + cx->delete_(set); +} + +JSBool +SetObject::construct(JSContext *cx, uintN argc, Value *vp) +{ + JSObject *obj = NewBuiltinClassInstance(cx, &class_); + if (!obj) + return false; + + ValueSet *set = cx->new_(cx->runtime); + if (!set || !set->init()) + return false; + obj->setPrivate(set); + + CallArgsFromVp(argc, vp).rval().setObject(*obj); + return true; +} + +#define THIS_SET(native, cx, argc, vp, args, set) \ + UNPACK_THIS(SetObject, native, cx, argc, vp, args, set) + +JSBool +SetObject::has(JSContext *cx, uintN argc, Value *vp) +{ + THIS_SET(has, cx, argc, vp, args, set); + ARG0_KEY(cx, args, key); + args.rval().setBoolean(set.has(key)); + return true; +} + +JSBool +SetObject::add(JSContext *cx, uintN argc, Value *vp) +{ + THIS_SET(add, cx, argc, vp, args, set); + ARG0_KEY(cx, args, key); + if (!set.put(key)) + return false; + args.rval().setUndefined(); + return true; +} + +JSBool +SetObject::delete_(JSContext *cx, uintN argc, Value *vp) +{ + THIS_SET(delete_, cx, argc, vp, args, set); + ARG0_KEY(cx, args, key); + ValueSet::Ptr p = set.lookup(key); + bool found = p.found(); + if (found) + set.remove(p); + args.rval().setBoolean(found); + return true; +} + +JSObject * +js_InitSetClass(JSContext *cx, JSObject *obj) +{ + return SetObject::initClass(cx, obj); +} diff --git a/deps/mozjs/js/src/builtin/MapObject.h b/deps/mozjs/js/src/builtin/MapObject.h new file mode 100644 index 00000000000..becb5849d02 --- /dev/null +++ b/deps/mozjs/js/src/builtin/MapObject.h @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef MapObject_h__ +#define MapObject_h__ + +#include "jsapi.h" +#include "jscntxt.h" +#include "jsobj.h" + +#include "js/HashTable.h" + +namespace js { + +/* + * Comparing two ropes for equality can fail. The js::HashTable template + * requires infallible hash() and match() operations. Therefore we require + * all values to be converted to hashable form before being used as a key + * in a Map or Set object. + * + * All values except ropes are hashable as-is. + */ +class HashableValue { + HeapValue value; + + public: + struct Hasher { + typedef HashableValue Lookup; + static HashNumber hash(const Lookup &v) { return v.hash(); } + static bool match(const HashableValue &k, const Lookup &l) { return k.equals(l); } + }; + + HashableValue() : value(UndefinedValue()) {} + + operator const HeapValue &() const { return value; } + bool setValue(JSContext *cx, const Value &v); + HashNumber hash() const; + bool equals(const HashableValue &other) const; +}; + +typedef HashMap ValueMap; +typedef HashSet ValueSet; + +class MapObject : public JSObject { + public: + static JSObject *initClass(JSContext *cx, JSObject *obj); + static Class class_; + private: + typedef ValueMap Data; + static JSFunctionSpec methods[]; + ValueMap *getData() { return static_cast(getPrivate()); } + static void mark(JSTracer *trc, JSObject *obj); + static void finalize(JSContext *cx, JSObject *obj); + static JSBool construct(JSContext *cx, uintN argc, Value *vp); + static JSBool get(JSContext *cx, uintN argc, Value *vp); + static JSBool has(JSContext *cx, uintN argc, Value *vp); + static JSBool set(JSContext *cx, uintN argc, Value *vp); + static JSBool delete_(JSContext *cx, uintN argc, Value *vp); +}; + +class SetObject : public JSObject { + public: + static JSObject *initClass(JSContext *cx, JSObject *obj); + static Class class_; + private: + typedef ValueSet Data; + static JSFunctionSpec methods[]; + ValueSet *getData() { return static_cast(getPrivate()); } + static void mark(JSTracer *trc, JSObject *obj); + static void finalize(JSContext *cx, JSObject *obj); + static JSBool construct(JSContext *cx, uintN argc, Value *vp); + static JSBool has(JSContext *cx, uintN argc, Value *vp); + static JSBool add(JSContext *cx, uintN argc, Value *vp); + static JSBool delete_(JSContext *cx, uintN argc, Value *vp); +}; + +} /* namespace js */ + +extern JSObject * +js_InitMapClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitSetClass(JSContext *cx, JSObject *obj); + +#endif /* MapObject_h__ */ diff --git a/deps/mozjs/js/src/builtin/RegExp.cpp b/deps/mozjs/js/src/builtin/RegExp.cpp new file mode 100644 index 00000000000..1bef6632ea3 --- /dev/null +++ b/deps/mozjs/js/src/builtin/RegExp.cpp @@ -0,0 +1,655 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jscntxt.h" + +#include "builtin/RegExp.h" + +#include "vm/RegExpObject-inl.h" +#include "vm/RegExpStatics-inl.h" + +using namespace js; +using namespace js::types; + +class RegExpMatchBuilder +{ + JSContext * const cx; + JSObject * const array; + + bool setProperty(JSAtom *name, Value v) { + return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v, + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); + } + + public: + RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {} + + bool append(uint32_t index, Value v) { + JS_ASSERT(!array->getOps()->getElement); + return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE); + } + + bool setIndex(int index) { + return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index)); + } + + bool setInput(JSString *str) { + JS_ASSERT(str); + return setProperty(cx->runtime->atomState.inputAtom, StringValue(str)); + } +}; + +static bool +CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length, + MatchPairs *matchPairs, Value *rval) +{ + /* + * Create the (slow) result array for a match. + * + * Array contents: + * 0: matched string + * 1..pairCount-1: paren matches + * input: input string + * index: start index for the match + */ + JSObject *array = NewSlowEmptyArray(cx); + if (!array) + return false; + + if (!input) { + input = js_NewStringCopyN(cx, chars, length); + if (!input) + return false; + } + + RegExpMatchBuilder builder(cx, array); + + for (size_t i = 0; i < matchPairs->pairCount(); ++i) { + MatchPair pair = matchPairs->pair(i); + + JSString *captured; + if (pair.isUndefined()) { + JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ + if (!builder.append(i, UndefinedValue())) + return false; + } else { + captured = js_NewDependentString(cx, input, pair.start, pair.length()); + if (!captured || !builder.append(i, StringValue(captured))) + return false; + } + } + + if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input)) + return false; + + *rval = ObjectValue(*array); + return true; +} + +template +bool +ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T &re, JSLinearString *input, + const jschar *chars, size_t length, + size_t *lastIndex, RegExpExecType type, Value *rval) +{ + LifoAllocScope allocScope(&cx->tempLifoAlloc()); + MatchPairs *matchPairs = NULL; + RegExpRunStatus status = re.execute(cx, chars, length, lastIndex, &matchPairs); + + switch (status) { + case RegExpRunStatus_Error: + return false; + case RegExpRunStatus_Success_NotFound: + *rval = NullValue(); + return true; + default: + JS_ASSERT(status == RegExpRunStatus_Success); + JS_ASSERT(matchPairs); + } + + if (res) + res->updateFromMatchPairs(cx, input, matchPairs); + + *lastIndex = matchPairs->pair(0).limit; + + if (type == RegExpTest) { + *rval = BooleanValue(true); + return true; + } + + return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval); +} + +bool +js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared, JSLinearString *input, + const jschar *chars, size_t length, + size_t *lastIndex, RegExpExecType type, Value *rval) +{ + return ExecuteRegExpImpl(cx, res, shared, input, chars, length, lastIndex, type, rval); +} + +bool +js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, JSLinearString *input, + const jschar *chars, size_t length, + size_t *lastIndex, RegExpExecType type, Value *rval) +{ + return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval); +} + +/* Note: returns the original if no escaping need be performed. */ +static JSAtom * +EscapeNakedForwardSlashes(JSContext *cx, JSAtom *unescaped) +{ + size_t oldLen = unescaped->length(); + const jschar *oldChars = unescaped->chars(); + + JS::Anchor anchor(unescaped); + + /* We may never need to use |sb|. Start using it lazily. */ + StringBuffer sb(cx); + + for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { + if (*it == '/' && (it == oldChars || it[-1] != '\\')) { + /* There's a forward slash that needs escaping. */ + if (sb.empty()) { + /* This is the first one we've seen, copy everything up to this point. */ + if (!sb.reserve(oldLen + 1)) + return NULL; + sb.infallibleAppend(oldChars, size_t(it - oldChars)); + } + if (!sb.append('\\')) + return NULL; + } + + if (!sb.empty() && !sb.append(*it)) + return NULL; + } + + return sb.empty() ? unescaped : sb.finishAtom(); +} + +/* + * Compile a new |RegExpShared| for the |RegExpObject|. + * + * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as + * arguments: + * + * RegExp, undefined => flags := pattern.flags + * RegExp, _ => throw TypeError + * _ => pattern := ToString(pattern) if defined(pattern) else '' + * flags := ToString(flags) if defined(flags) else '' + */ +static bool +CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) +{ + if (args.length() == 0) { + RegExpStatics *res = cx->regExpStatics(); + RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags()); + if (!reobj) + return false; + args.rval() = ObjectValue(*reobj); + return true; + } + + Value sourceValue = args[0]; + + /* + * If we get passed in an object whose internal [[Class]] property is + * "RegExp", return a new object with the same source/flags. + */ + if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) { + /* + * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only + * use generic (proxyable) operations on sourceObj that do not assume + * sourceObj.isRegExp(). + */ + JSObject &sourceObj = sourceValue.toObject(); + + if (args.length() >= 2 && !args[1].isUndefined()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); + return false; + } + + /* + * Only extract the 'flags' out of sourceObj; do not reuse the + * RegExpShared since it may be from a different compartment. + */ + RegExpFlag flags; + { + RegExpShared *shared = RegExpToShared(cx, sourceObj); + if (!shared) + return false; + + flags = shared->getFlags(); + } + + /* + * 'toSource' is a permanent read-only property, so this is equivalent + * to executing RegExpObject::getSource on the unwrapped object. + */ + Value v; + if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v)) + return false; + + RegExpObject *reobj = builder.build(&v.toString()->asAtom(), flags); + if (!reobj) + return false; + + args.rval() = ObjectValue(*reobj); + return true; + } + + JSAtom *source; + if (sourceValue.isUndefined()) { + source = cx->runtime->emptyString; + } else { + /* Coerce to string and compile. */ + JSString *str = ToString(cx, sourceValue); + if (!str) + return false; + + source = js_AtomizeString(cx, str); + if (!source) + return false; + } + + RegExpFlag flags = RegExpFlag(0); + if (args.length() > 1 && !args[1].isUndefined()) { + JSString *flagStr = ToString(cx, args[1]); + if (!flagStr) + return false; + args[1].setString(flagStr); + if (!ParseRegExpFlags(cx, flagStr, &flags)) + return false; + } + + JSAtom *escapedSourceStr = EscapeNakedForwardSlashes(cx, source); + if (!escapedSourceStr) + return false; + + if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr)) + return false; + + RegExpStatics *res = cx->regExpStatics(); + RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); + if (!reobj) + return NULL; + + args.rval() = ObjectValue(*reobj); + return true; +} + +static JSBool +regexp_compile(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok); + if (!obj) + return ok; + + RegExpObjectBuilder builder(cx, &obj->asRegExp()); + return CompileRegExpObject(cx, builder, args); +} + +static JSBool +regexp_construct(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (!IsConstructing(args)) { + /* + * If first arg is regexp and no flags are given, just return the arg. + * Otherwise, delegate to the standard constructor. + * See ECMAv5 15.10.3.1. + */ + if (args.length() >= 1 && IsObjectWithClass(args[0], ESClass_RegExp, cx) && + (args.length() == 1 || args[1].isUndefined())) + { + args.rval() = args[0]; + return true; + } + } + + RegExpObjectBuilder builder(cx); + return CompileRegExpObject(cx, builder, args); +} + +static JSBool +regexp_toString(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, regexp_toString, &RegExpClass, &ok); + if (!obj) + return ok; + + JSString *str = obj->asRegExp().toString(cx); + if (!str) + return false; + + *vp = StringValue(str); + return true; +} + +static JSFunctionSpec regexp_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, regexp_toString, 0,0), +#endif + JS_FN(js_toString_str, regexp_toString, 0,0), + JS_FN("compile", regexp_compile, 2,0), + JS_FN("exec", regexp_exec, 1,0), + JS_FN("test", regexp_test, 1,0), + JS_FS_END +}; + +/* + * RegExp static properties. + * + * RegExp class static properties and their Perl counterparts: + * + * RegExp.input $_ + * RegExp.multiline $* + * RegExp.lastMatch $& + * RegExp.lastParen $+ + * RegExp.leftContext $` + * RegExp.rightContext $' + */ + +#define DEFINE_STATIC_GETTER(name, code) \ + static JSBool \ + name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ + { \ + RegExpStatics *res = cx->regExpStatics(); \ + code; \ + } + +DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, vp)) +DEFINE_STATIC_GETTER(static_multiline_getter, *vp = BOOLEAN_TO_JSVAL(res->multiline()); + return true) +DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, vp)) +DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, vp)) +DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, vp)) +DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, vp)) + +DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, vp)) +DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, vp)) +DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, vp)) +DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, vp)) +DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, vp)) +DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, vp)) +DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, vp)) +DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, vp)) +DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, vp)) + +#define DEFINE_STATIC_SETTER(name, code) \ + static JSBool \ + name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \ + { \ + RegExpStatics *res = cx->regExpStatics(); \ + code; \ + return true; \ + } + +DEFINE_STATIC_SETTER(static_input_setter, + if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) + return false; + res->setPendingInput(JSVAL_TO_STRING(*vp))) +DEFINE_STATIC_SETTER(static_multiline_setter, + if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) + return false; + res->setMultiline(cx, !!JSVAL_TO_BOOLEAN(*vp))) + +const uint8_t REGEXP_STATIC_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE; +const uint8_t RO_REGEXP_STATIC_PROP_ATTRS = REGEXP_STATIC_PROP_ATTRS | JSPROP_READONLY; + +const uint8_t HIDDEN_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED; +const uint8_t RO_HIDDEN_PROP_ATTRS = HIDDEN_PROP_ATTRS | JSPROP_READONLY; + +static JSPropertySpec regexp_static_props[] = { + {"input", 0, REGEXP_STATIC_PROP_ATTRS, static_input_getter, static_input_setter}, + {"multiline", 0, REGEXP_STATIC_PROP_ATTRS, static_multiline_getter, + static_multiline_setter}, + {"lastMatch", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_lastMatch_getter, NULL}, + {"lastParen", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_lastParen_getter, NULL}, + {"leftContext", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_leftContext_getter, NULL}, + {"rightContext", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_rightContext_getter, NULL}, + {"$1", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren1_getter, NULL}, + {"$2", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren2_getter, NULL}, + {"$3", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren3_getter, NULL}, + {"$4", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren4_getter, NULL}, + {"$5", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren5_getter, NULL}, + {"$6", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren6_getter, NULL}, + {"$7", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren7_getter, NULL}, + {"$8", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren8_getter, NULL}, + {"$9", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren9_getter, NULL}, + + {"$_", 0, HIDDEN_PROP_ATTRS, static_input_getter, static_input_setter}, + {"$*", 0, HIDDEN_PROP_ATTRS, static_multiline_getter, static_multiline_setter}, + {"$&", 0, RO_HIDDEN_PROP_ATTRS, static_lastMatch_getter, NULL}, + {"$+", 0, RO_HIDDEN_PROP_ATTRS, static_lastParen_getter, NULL}, + {"$`", 0, RO_HIDDEN_PROP_ATTRS, static_leftContext_getter, NULL}, + {"$'", 0, RO_HIDDEN_PROP_ATTRS, static_rightContext_getter, NULL}, + {0,0,0,0,0} +}; + +JSObject * +js_InitRegExpClass(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *proto = global->createBlankPrototype(cx, &RegExpClass); + if (!proto) + return NULL; + proto->setPrivate(NULL); + + RegExpObject *reproto = &proto->asRegExp(); + RegExpObjectBuilder builder(cx, reproto); + if (!builder.build(cx->runtime->emptyString, RegExpFlag(0))) + return NULL; + + if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods)) + return NULL; + + JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass, + CLASS_ATOM(cx, RegExp), 2); + if (!ctor) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, proto)) + return NULL; + + /* Add static properties to the RegExp constructor. */ + if (!JS_DefineProperties(cx, ctor, regexp_static_props)) + return NULL; + + /* Capture normal data properties pregenerated for RegExp objects. */ + TypeObject *type = proto->getNewType(cx); + if (!type) + return NULL; + AddTypeProperty(cx, type, "source", Type::StringType()); + AddTypeProperty(cx, type, "global", Type::BooleanType()); + AddTypeProperty(cx, type, "ignoreCase", Type::BooleanType()); + AddTypeProperty(cx, type, "multiline", Type::BooleanType()); + AddTypeProperty(cx, type, "sticky", Type::BooleanType()); + AddTypeProperty(cx, type, "lastIndex", Type::Int32Type()); + + if (!DefineConstructorAndPrototype(cx, global, JSProto_RegExp, ctor, proto)) + return NULL; + + return proto; +} + + +static const jschar GreedyStarChars[] = {'.', '*'}; + +static inline bool +StartsWithGreedyStar(JSAtom *source) +{ + return false; + +#if 0 + if (source->length() < 3) + return false; + + const jschar *chars = source->chars(); + return chars[0] == GreedyStarChars[0] && + chars[1] == GreedyStarChars[1] && + chars[2] != '?'; +#endif +} + +static inline RegExpShared * +GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags) +{ + if (RegExpShared *hit = cx->compartment->regExps.lookupHack(cx, source, flags)) + return hit; + + JSAtom *hackedSource = js_AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars), + source->length() - ArrayLength(GreedyStarChars)); + if (!hackedSource) + return NULL; + + return cx->compartment->regExps.getHack(cx, source, hackedSource, flags); +} + +/* + * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). + * + * RegExp.prototype.test doesn't need to create a results array, and we use + * |execType| to perform this optimization. + */ +static bool +ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok); + if (!obj) + return ok; + + RegExpObject &reobj = obj->asRegExp(); + + RegExpShared *shared; + if (StartsWithGreedyStar(reobj.getSource())) + shared = GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags()); + else + shared = reobj.getShared(cx); + + if (!shared) + return false; + + RegExpShared::Guard re(*shared); + RegExpStatics *res = cx->regExpStatics(); + + /* Step 2. */ + JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue()); + if (!input) + return false; + + /* Step 3. */ + JSLinearString *linearInput = input->ensureLinear(cx); + if (!linearInput) + return false; + const jschar *chars = linearInput->chars(); + size_t length = input->length(); + + /* Step 4. */ + const Value &lastIndex = reobj.getLastIndex(); + + /* Step 5. */ + jsdouble i; + if (!ToInteger(cx, lastIndex, &i)) + return false; + + /* Steps 6-7 (with sticky extension). */ + if (!re->global() && !re->sticky()) + i = 0; + + /* Step 9a. */ + if (i < 0 || i > length) { + reobj.zeroLastIndex(); + args.rval() = NullValue(); + return true; + } + + /* Steps 8-21. */ + RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec; + size_t lastIndexInt(i); + if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType, + &args.rval())) { + return false; + } + + /* Step 11 (with sticky extension). */ + if (re->global() || (!args.rval().isNull() && re->sticky())) { + if (args.rval().isNull()) + reobj.zeroLastIndex(); + else + reobj.setLastIndex(lastIndexInt); + } + + return true; +} + +/* ES5 15.10.6.2. */ +JSBool +js::regexp_exec(JSContext *cx, uintN argc, Value *vp) +{ + return ExecuteRegExp(cx, regexp_exec, argc, vp); +} + +/* ES5 15.10.6.3. */ +JSBool +js::regexp_test(JSContext *cx, uintN argc, Value *vp) +{ + if (!ExecuteRegExp(cx, regexp_test, argc, vp)) + return false; + if (!vp->isTrue()) + vp->setBoolean(false); + return true; +} diff --git a/deps/mozjs/js/src/builtin/RegExp.h b/deps/mozjs/js/src/builtin/RegExp.h new file mode 100644 index 00000000000..9a54b1b6f61 --- /dev/null +++ b/deps/mozjs/js/src/builtin/RegExp.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef RegExp_h___ +#define RegExp_h___ + +#include "jsprvtd.h" + +JSObject * +js_InitRegExpClass(JSContext *cx, JSObject *obj); + +/* + * The following builtin natives are extern'd for pointer comparison in + * other parts of the engine. + */ + +namespace js { + +/* + * |res| may be null if the |RegExpStatics| are not to be updated. + * |input| may be null if there is no |JSString| corresponding to + * |chars| and |length|. + */ +bool +ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, + JSLinearString *input, const jschar *chars, size_t length, + size_t *lastIndex, RegExpExecType type, Value *rval); + +bool +ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared, + JSLinearString *input, const jschar *chars, size_t length, + size_t *lastIndex, RegExpExecType type, Value *rval); + +extern JSBool +regexp_exec(JSContext *cx, uintN argc, Value *vp); + +extern JSBool +regexp_test(JSContext *cx, uintN argc, Value *vp); + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/config.mk b/deps/mozjs/js/src/config.mk deleted file mode 100644 index 01fb023f9ac..00000000000 --- a/deps/mozjs/js/src/config.mk +++ /dev/null @@ -1,186 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998-1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -ifdef JS_DIST -DIST = $(JS_DIST) -else -DIST = $(DEPTH)/../../dist -endif - -# Set os+release dependent make variables -OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/)) - -# Attempt to differentiate between SunOS 5.4 and x86 5.4 -OS_CPUARCH := $(shell uname -m) -ifeq ($(OS_CPUARCH),i86pc) -OS_RELEASE := $(shell uname -r)_$(OS_CPUARCH) -else -ifeq ($(OS_ARCH),AIX) -OS_RELEASE := $(shell uname -v).$(shell uname -r) -else -OS_RELEASE := $(shell uname -r) -endif -endif -ifeq ($(OS_ARCH),IRIX64) -OS_ARCH := IRIX -endif - -# Handle output from win32 unames other than Netscape's version -ifeq (,$(filter-out Windows_95 Windows_98, $(OS_ARCH))) - OS_ARCH := WIN95 -endif -ifeq ($(OS_ARCH),WIN95) - OS_ARCH := WINNT - OS_RELEASE := 4.0 -endif -ifeq ($(OS_ARCH), Windows_NT) - OS_ARCH := WINNT - OS_MINOR_RELEASE := $(shell uname -v) - ifeq ($(OS_MINOR_RELEASE),00) - OS_MINOR_RELEASE = 0 - endif - OS_RELEASE := $(OS_RELEASE).$(OS_MINOR_RELEASE) -endif -ifeq (MINGW32_NT,$(findstring MINGW32_NT,$(OS_ARCH))) - OS_RELEASE := $(patsubst MINGW32_NT-%,%,$(OS_ARCH)) - OS_ARCH := WINNT -endif - -# Virtually all Linux versions are identical. -# Any distinctions are handled in linux.h -ifeq ($(OS_ARCH),Linux) -OS_CONFIG := Linux_All -else -ifeq ($(OS_ARCH),dgux) -OS_CONFIG := dgux -else -ifeq ($(OS_ARCH),Darwin) -OS_CONFIG := Darwin -else -ifeq ($(OS_ARCH),Darwin64) -OS_CONFIG := Darwin64 -else -OS_CONFIG := $(OS_ARCH)$(OS_OBJTYPE)$(OS_RELEASE) -endif -endif -endif -endif - -ASFLAGS = -DEFINES = - -ifeq ($(OS_ARCH), WINNT) -INSTALL = nsinstall -CP = cp -else -INSTALL = $(DIST)/bin/nsinstall -CP = cp -endif - -ifdef BUILD_OPT -ifdef USE_MSVC -OPTIMIZER = -O2 -GL -INTERP_OPTIMIZER = -O2 -GL -BUILTINS_OPTIMIZER = -O2 -GL -LDFLAGS += -LTCG -else -OPTIMIZER = -Os -fno-exceptions -fno-rtti -fstrict-aliasing -Wstrict-aliasing=3 -BUILTINS_OPTIMIZER = -O9 -fno-exceptions -fno-rtti -fstrict-aliasing -INTERP_OPTIMIZER = -O3 -fno-exceptions -fno-rtti -fstrict-aliasing -endif -DEFINES += -UDEBUG -DNDEBUG -UDEBUG_$(USER) -OBJDIR_TAG = _OPT -else -ifdef USE_MSVC -OPTIMIZER = -Zi -INTERP_OPTIMIZER = -Zi -BUILTINS_OPTIMIZER = $(INTERP_OPTIMIZER) -else -OPTIMIZER = -g3 -fstrict-aliasing -fno-exceptions -fno-rtti -Wstrict-aliasing=3 -INTERP_OPTIMIZER = -g3 -fstrict-aliasing -fno-exceptions -fno-rtti -BUILTINS_OPTIMIZER = $(INTERP_OPTIMIZER) -endif -DEFINES += -DDEBUG -DDEBUG_$(USER) -OBJDIR_TAG = _DBG -endif - -SO_SUFFIX = so - -NS_USE_NATIVE = 1 - -include $(DEPTH)/ref-config/$(OS_CONFIG).mk - -ifndef OBJ_SUFFIX -ifdef USE_MSVC -OBJ_SUFFIX = obj -else -OBJ_SUFFIX = o -endif -endif - -ifndef HOST_BIN_SUFFIX -ifeq ($(OS_ARCH),WINNT) -HOST_BIN_SUFFIX = .exe -else -HOST_BIN_SUFFIX = -endif -endif - -# Name of the binary code directories -ifdef OBJROOT -# prepend $(DEPTH) to the root unless it is an absolute path -OBJDIR = $(if $(filter /%,$(OBJROOT)),$(OBJROOT),$(DEPTH)/$(OBJROOT)) -else -ifeq ($(DEPTH),.) -OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).$(if $(BUILD_IDG),OBJD,OBJ) -else -OBJDIR = $(DEPTH)/$(OS_CONFIG)$(OBJDIR_TAG).$(if $(BUILD_IDG),OBJD,OBJ) -endif -endif - -VPATH = $(OBJDIR) - -LCJAR = js15lc30.jar - -# Library name -LIBDIR := lib -ifeq ($(CPU_ARCH), x86_64) -LIBDIR := lib64 -endif - diff --git a/deps/mozjs/js/src/config/Expression.py b/deps/mozjs/js/src/config/Expression.py new file mode 100644 index 00000000000..56a58db65fa --- /dev/null +++ b/deps/mozjs/js/src/config/Expression.py @@ -0,0 +1,209 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla build system. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +""" +Parses and evaluates simple statements for Preprocessor: + +Expression currently supports the following grammar, whitespace is ignored: + +expression : + unary ( ( '==' | '!=' ) unary ) ? ; +unary : + '!'? value ; +value : + [0-9]+ # integer + | \w+ # string identifier or value; +""" + +import re + +class Expression: + def __init__(self, expression_string): + """ + Create a new expression with this string. + The expression will already be parsed into an Abstract Syntax Tree. + """ + self.content = expression_string + self.offset = 0 + self.__ignore_whitespace() + self.e = self.__get_equality() + if self.content: + raise Expression.ParseError, self + + def __get_equality(self): + """ + Production: unary ( ( '==' | '!=' ) unary ) ? + """ + if not len(self.content): + return None + rv = Expression.__AST("equality") + # unary + rv.append(self.__get_unary()) + self.__ignore_whitespace() + if not re.match('[=!]=', self.content): + # no equality needed, short cut to our prime unary + return rv[0] + # append operator + rv.append(Expression.__ASTLeaf('op', self.content[:2])) + self.__strip(2) + self.__ignore_whitespace() + rv.append(self.__get_unary()) + self.__ignore_whitespace() + return rv + + def __get_unary(self): + """ + Production: '!'? value + """ + # eat whitespace right away, too + not_ws = re.match('!\s*', self.content) + if not not_ws: + return self.__get_value() + rv = Expression.__AST('not') + self.__strip(not_ws.end()) + rv.append(self.__get_value()) + self.__ignore_whitespace() + return rv + + def __get_value(self): + """ + Production: ( [0-9]+ | \w+) + Note that the order is important, and the expression is kind-of + ambiguous as \w includes 0-9. One could make it unambiguous by + removing 0-9 from the first char of a string literal. + """ + rv = None + word_len = re.match('[0-9]*', self.content).end() + if word_len: + value = int(self.content[:word_len]) + rv = Expression.__ASTLeaf('int', value) + else: + word_len = re.match('\w*', self.content).end() + if word_len: + rv = Expression.__ASTLeaf('string', self.content[:word_len]) + else: + raise Expression.ParseError, self + self.__strip(word_len) + self.__ignore_whitespace() + return rv + + def __ignore_whitespace(self): + ws_len = re.match('\s*', self.content).end() + self.__strip(ws_len) + return + + def __strip(self, length): + """ + Remove a given amount of chars from the input and update + the offset. + """ + self.content = self.content[length:] + self.offset += length + + def evaluate(self, context): + """ + Evaluate the expression with the given context + """ + + # Helper function to evaluate __get_equality results + def eval_equality(tok): + left = opmap[tok[0].type](tok[0]) + right = opmap[tok[2].type](tok[2]) + rv = left == right + if tok[1].value == '!=': + rv = not rv + return rv + # Mapping from token types to evaluator functions + # Apart from (non-)equality, all these can be simple lambda forms. + opmap = { + 'equality': eval_equality, + 'not': lambda tok: not opmap[tok[0].type](tok[0]), + 'string': lambda tok: context[tok.value], + 'int': lambda tok: tok.value} + + return opmap[self.e.type](self.e); + + class __AST(list): + """ + Internal class implementing Abstract Syntax Tree nodes + """ + def __init__(self, type): + self.type = type + super(self.__class__, self).__init__(self) + + class __ASTLeaf: + """ + Internal class implementing Abstract Syntax Tree leafs + """ + def __init__(self, type, value): + self.value = value + self.type = type + def __str__(self): + return self.value.__str__() + def __repr__(self): + return self.value.__repr__() + + class ParseError(StandardError): + """ + Error raised when parsing fails. + It has two members, offset and content, which give the offset of the + error and the offending content. + """ + def __init__(self, expression): + self.offset = expression.offset + self.content = expression.content[:3] + def __str__(self): + return 'Unexpected content at offset %i, "%s"'%(self.offset, self.content) + +class Context(dict): + """ + This class holds variable values by subclassing dict, and while it + truthfully reports True and False on + + name in context + + it returns the variable name itself on + + context["name"] + + to reflect the ambiguity between string literals and preprocessor + variables. + """ + def __getitem__(self, key): + if key in self: + return super(self.__class__, self).__getitem__(key) + return key diff --git a/deps/mozjs/js/src/config/Makefile.in b/deps/mozjs/js/src/config/Makefile.in index dfed04b23e3..1c36b803ff0 100644 --- a/deps/mozjs/js/src/config/Makefile.in +++ b/deps/mozjs/js/src/config/Makefile.in @@ -56,9 +56,7 @@ else HOST_CSRCS = nsinstall.c pathsub.c endif -PLSRCS = nfspwd.pl - -TARGETS = $(HOST_PROGRAM) $(PLSRCS:.pl=) $(SIMPLE_PROGRAMS) +TARGETS = $(HOST_PROGRAM) $(SIMPLE_PROGRAMS) # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have # a recursive rule for finding nsinstall and the Perl scripts. @@ -93,8 +91,7 @@ endif ifdef WRAP_SYSTEM_INCLUDES export:: if test ! -d system_wrappers_js; then mkdir system_wrappers_js; fi - $(PERL) $(srcdir)/preprocessor.pl $(DEFINES) $(ACDEFINES) \ - -DBUILD_STATIC_LIBS=$(BUILD_STATIC_LIBS) \ + $(PYTHON) $(srcdir)/Preprocessor.py $(DEFINES) $(ACDEFINES) \ $(srcdir)/system-headers | $(PERL) $(srcdir)/make-system-wrappers.pl system_wrappers_js $(INSTALL) system_wrappers_js $(DIST) diff --git a/deps/mozjs/js/src/config/Preprocessor.py b/deps/mozjs/js/src/config/Preprocessor.py new file mode 100644 index 00000000000..a2721179aa5 --- /dev/null +++ b/deps/mozjs/js/src/config/Preprocessor.py @@ -0,0 +1,477 @@ +""" +This is a very primitive line based preprocessor, for times when using +a C preprocessor isn't an option. +""" + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla build system. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import sys +import os +import os.path +import re +from optparse import OptionParser + +# hack around win32 mangling our line endings +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65443 +if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + os.linesep = '\n' + +import Expression + +__all__ = ['Preprocessor', 'preprocess'] + + +class Preprocessor: + """ + Class for preprocessing text files. + """ + class Error(RuntimeError): + def __init__(self, cpp, MSG, context): + self.file = cpp.context['FILE'] + self.line = cpp.context['LINE'] + self.key = MSG + RuntimeError.__init__(self, (self.file, self.line, self.key, context)) + def __init__(self): + self.context = Expression.Context() + for k,v in {'FILE': '', + 'LINE': 0, + 'DIRECTORY': os.path.abspath('.')}.iteritems(): + self.context[k] = v + self.disableLevel = 0 + # ifStates can be + # 0: hadTrue + # 1: wantsTrue + # 2: #else found + self.ifStates = [] + self.checkLineNumbers = False + self.writtenLines = 0 + self.filters = [] + self.cmds = {} + for cmd, level in {'define': 0, + 'undef': 0, + 'if': sys.maxint, + 'ifdef': sys.maxint, + 'ifndef': sys.maxint, + 'else': 1, + 'elif': 1, + 'elifdef': 1, + 'elifndef': 1, + 'endif': sys.maxint, + 'expand': 0, + 'literal': 0, + 'filter': 0, + 'unfilter': 0, + 'include': 0, + 'includesubst': 0, + 'error': 0}.iteritems(): + self.cmds[cmd] = (level, getattr(self, 'do_' + cmd)) + self.out = sys.stdout + self.setMarker('#') + self.LE = '\n' + self.varsubst = re.compile('@(?P\w+)@', re.U) + + def setLineEndings(self, aLE): + """ + Set the line endings to be used for output. + """ + self.LE = {'cr': '\x0D', 'lf': '\x0A', 'crlf': '\x0D\x0A'}[aLE] + + def setMarker(self, aMarker): + """ + Set the marker to be used for processing directives. + Used for handling CSS files, with pp.setMarker('%'), for example. + """ + self.marker = aMarker + self.instruction = re.compile('%s(?P[a-z]+)(?:\s(?P.*))?$'%aMarker, re.U) + self.comment = re.compile(aMarker, re.U) + + def clone(self): + """ + Create a clone of the current processor, including line ending + settings, marker, variable definitions, output stream. + """ + rv = Preprocessor() + rv.context.update(self.context) + rv.setMarker(self.marker) + rv.LE = self.LE + rv.out = self.out + return rv + + def write(self, aLine): + """ + Internal method for handling output. + """ + if self.checkLineNumbers: + self.writtenLines += 1 + ln = self.context['LINE'] + if self.writtenLines != ln: + self.out.write('//@line %(line)d "%(file)s"%(le)s'%{'line': ln, + 'file': self.context['FILE'], + 'le': self.LE}) + self.writtenLines = ln + for f in self.filters: + aLine = f[1](aLine) + # ensure our line ending. Only need to handle \n, as we're reading + # with universal line ending support, at least for files. + aLine = re.sub('\n', self.LE, aLine) + self.out.write(aLine) + + def handleCommandLine(self, args, defaultToStdin = False): + """ + Parse a commandline into this parser. + Uses OptionParser internally, no args mean sys.argv[1:]. + """ + p = self.getCommandLineParser() + (options, args) = p.parse_args(args=args) + includes = options.I + if defaultToStdin and len(args) == 0: + args = [sys.stdin] + includes.extend(args) + for f in includes: + self.do_include(f) + pass + + def getCommandLineParser(self, unescapeDefines = False): + escapedValue = re.compile('".*"$') + numberValue = re.compile('\d+$') + def handleE(option, opt, value, parser): + for k,v in os.environ.iteritems(): + self.context[k] = v + def handleD(option, opt, value, parser): + vals = value.split('=', 1) + if len(vals) == 1: + vals.append(1) + elif unescapeDefines and escapedValue.match(vals[1]): + # strip escaped string values + vals[1] = vals[1][1:-1] + elif numberValue.match(vals[1]): + vals[1] = int(vals[1]) + self.context[vals[0]] = vals[1] + def handleU(option, opt, value, parser): + del self.context[value] + def handleF(option, opt, value, parser): + self.do_filter(value) + def handleLE(option, opt, value, parser): + self.setLineEndings(value) + def handleMarker(option, opt, value, parser): + self.setMarker(value) + p = OptionParser() + p.add_option('-I', action='append', type="string", default = [], + metavar="FILENAME", help='Include file') + p.add_option('-E', action='callback', callback=handleE, + help='Import the environment into the defined variables') + p.add_option('-D', action='callback', callback=handleD, type="string", + metavar="VAR[=VAL]", help='Define a variable') + p.add_option('-U', action='callback', callback=handleU, type="string", + metavar="VAR", help='Undefine a variable') + p.add_option('-F', action='callback', callback=handleF, type="string", + metavar="FILTER", help='Enable the specified filter') + p.add_option('--line-endings', action='callback', callback=handleLE, + type="string", metavar="[cr|lr|crlf]", + help='Use the specified line endings [Default: OS dependent]') + p.add_option('--marker', action='callback', callback=handleMarker, + type="string", + help='Use the specified marker instead of #') + return p + + def handleLine(self, aLine): + """ + Handle a single line of input (internal). + """ + m = self.instruction.match(aLine) + if m: + args = None + cmd = m.group('cmd') + try: + args = m.group('args') + except IndexError: + pass + if cmd not in self.cmds: + raise Preprocessor.Error(self, 'INVALID_CMD', aLine) + level, cmd = self.cmds[cmd] + if (level >= self.disableLevel): + cmd(args) + elif self.disableLevel == 0 and not self.comment.match(aLine): + self.write(aLine) + pass + + # Instruction handlers + # These are named do_'instruction name' and take one argument + + # Variables + def do_define(self, args): + m = re.match('(?P\w+)(?:\s(?P.*))?', args, re.U) + if not m: + raise Preprocessor.Error(self, 'SYNTAX_DEF', args) + val = 1 + if m.group('value'): + val = m.group('value') + try: + val = int(val) + except: + pass + self.context[m.group('name')] = val + def do_undef(self, args): + m = re.match('(?P\w+)$', args, re.U) + if not m: + raise Preprocessor.Error(self, 'SYNTAX_DEF', args) + if args in self.context: + del self.context[args] + # Logic + def ensure_not_else(self): + if len(self.ifStates) == 0 or self.ifStates[-1] == 2: + sys.stderr.write('WARNING: bad nesting of #else\n') + def do_if(self, args, replace=False): + if self.disableLevel and not replace: + self.disableLevel += 1 + return + val = None + try: + e = Expression.Expression(args) + val = e.evaluate(self.context) + except Exception: + # XXX do real error reporting + raise Preprocessor.Error(self, 'SYNTAX_ERR', args) + if type(val) == str: + # we're looking for a number value, strings are false + val = False + if not val: + self.disableLevel = 1 + if replace: + if val: + self.disableLevel = 0 + self.ifStates[-1] = self.disableLevel + else: + self.ifStates.append(self.disableLevel) + pass + def do_ifdef(self, args, replace=False): + if self.disableLevel and not replace: + self.disableLevel += 1 + return + if re.match('\W', args, re.U): + raise Preprocessor.Error(self, 'INVALID_VAR', args) + if args not in self.context: + self.disableLevel = 1 + if replace: + if args in self.context: + self.disableLevel = 0 + self.ifStates[-1] = self.disableLevel + else: + self.ifStates.append(self.disableLevel) + pass + def do_ifndef(self, args, replace=False): + if self.disableLevel and not replace: + self.disableLevel += 1 + return + if re.match('\W', args, re.U): + raise Preprocessor.Error(self, 'INVALID_VAR', args) + if args in self.context: + self.disableLevel = 1 + if replace: + if args not in self.context: + self.disableLevel = 0 + self.ifStates[-1] = self.disableLevel + else: + self.ifStates.append(self.disableLevel) + pass + def do_else(self, args, ifState = 2): + self.ensure_not_else() + hadTrue = self.ifStates[-1] == 0 + self.ifStates[-1] = ifState # in-else + if hadTrue: + self.disableLevel = 1 + return + self.disableLevel = 0 + def do_elif(self, args): + if self.disableLevel == 1: + if self.ifStates[-1] == 1: + self.do_if(args, replace=True) + else: + self.do_else(None, self.ifStates[-1]) + def do_elifdef(self, args): + if self.disableLevel == 1: + if self.ifStates[-1] == 1: + self.do_ifdef(args, replace=True) + else: + self.do_else(None, self.ifStates[-1]) + def do_elifndef(self, args): + if self.disableLevel == 1: + if self.ifStates[-1] == 1: + self.do_ifndef(args, replace=True) + else: + self.do_else(None, self.ifStates[-1]) + def do_endif(self, args): + if self.disableLevel > 0: + self.disableLevel -= 1 + if self.disableLevel == 0: + self.ifStates.pop() + # output processing + def do_expand(self, args): + lst = re.split('__(\w+)__', args, re.U) + do_replace = False + def vsubst(v): + if v in self.context: + return str(self.context[v]) + return '' + for i in range(1, len(lst), 2): + lst[i] = vsubst(lst[i]) + lst.append('\n') # add back the newline + self.write(reduce(lambda x, y: x+y, lst, '')) + def do_literal(self, args): + self.write(args + self.LE) + def do_filter(self, args): + filters = [f for f in args.split(' ') if hasattr(self, 'filter_' + f)] + if len(filters) == 0: + return + current = dict(self.filters) + for f in filters: + current[f] = getattr(self, 'filter_' + f) + filterNames = current.keys() + filterNames.sort() + self.filters = [(fn, current[fn]) for fn in filterNames] + return + def do_unfilter(self, args): + filters = args.split(' ') + current = dict(self.filters) + for f in filters: + if f in current: + del current[f] + filterNames = current.keys() + filterNames.sort() + self.filters = [(fn, current[fn]) for fn in filterNames] + return + # Filters + # + # emptyLines + # Strips blank lines from the output. + def filter_emptyLines(self, aLine): + if aLine == '\n': + return '' + return aLine + # slashslash + # Strips everything after // + def filter_slashslash(self, aLine): + [aLine, rest] = aLine.split('//', 1) + if rest: + aLine += '\n' + return aLine + # spaces + # Collapses sequences of spaces into a single space + def filter_spaces(self, aLine): + return re.sub(' +', ' ', aLine).strip(' ') + # substition + # helper to be used by both substition and attemptSubstitution + def filter_substitution(self, aLine, fatal=True): + def repl(matchobj): + varname = matchobj.group('VAR') + if varname in self.context: + return str(self.context[varname]) + if fatal: + raise Preprocessor.Error(self, 'UNDEFINED_VAR', varname) + return '' + return self.varsubst.sub(repl, aLine) + def filter_attemptSubstitution(self, aLine): + return self.filter_substitution(aLine, fatal=False) + # File ops + def do_include(self, args): + """ + Preprocess a given file. + args can either be a file name, or a file-like object. + Files should be opened, and will be closed after processing. + """ + isName = type(args) == str or type(args) == unicode + oldWrittenLines = self.writtenLines + oldCheckLineNumbers = self.checkLineNumbers + self.checkLineNumbers = False + if isName: + try: + args = str(args) + if not os.path.isabs(args): + args = os.path.join(self.context['DIRECTORY'], args) + args = open(args, 'rU') + except: + raise Preprocessor.Error(self, 'FILE_NOT_FOUND', str(args)) + self.checkLineNumbers = bool(re.search('\.(js|java)(?:\.in)?$', args.name)) + oldFile = self.context['FILE'] + oldLine = self.context['LINE'] + oldDir = self.context['DIRECTORY'] + if args.isatty(): + # we're stdin, use '-' and '' for file and dir + self.context['FILE'] = '-' + self.context['DIRECTORY'] = '' + else: + abspath = os.path.abspath(args.name) + self.context['FILE'] = abspath + self.context['DIRECTORY'] = os.path.dirname(abspath) + self.context['LINE'] = 0 + self.writtenLines = 0 + for l in args: + self.context['LINE'] += 1 + self.handleLine(l) + args.close() + self.context['FILE'] = oldFile + self.checkLineNumbers = oldCheckLineNumbers + self.writtenLines = oldWrittenLines + self.context['LINE'] = oldLine + self.context['DIRECTORY'] = oldDir + def do_includesubst(self, args): + args = self.filter_substitution(args) + self.do_include(args) + def do_error(self, args): + raise Preprocessor.Error(self, 'Error: ', str(args)) + +def main(): + pp = Preprocessor() + pp.handleCommandLine(None, True) + return + +def preprocess(includes=[sys.stdin], defines={}, + output = sys.stdout, + line_endings='\n', marker='#'): + pp = Preprocessor() + pp.context.update(defines) + pp.setLineEndings(line_endings) + pp.setMarker(marker) + pp.out = output + for f in includes: + pp.do_include(f) + +if __name__ == "__main__": + main() diff --git a/deps/mozjs/js/src/config/autoconf.mk.in b/deps/mozjs/js/src/config/autoconf.mk.in index 9cfbe9e9676..23b776cc903 100644 --- a/deps/mozjs/js/src/config/autoconf.mk.in +++ b/deps/mozjs/js/src/config/autoconf.mk.in @@ -43,7 +43,6 @@ USE_AUTOCONF = 1 MOZILLA_CLIENT = 1 target = @target@ ac_configure_args = @ac_configure_args@ -BUILD_MODULES = @BUILD_MODULES@ MOZILLA_VERSION = @MOZILLA_VERSION@ MOZ_BUILD_APP = @MOZ_BUILD_APP@ @@ -90,13 +89,13 @@ MOZ_JPROF = @MOZ_JPROF@ MOZ_SHARK = @MOZ_SHARK@ MOZ_CALLGRIND = @MOZ_CALLGRIND@ MOZ_VTUNE = @MOZ_VTUNE@ +MOZ_ETW = @MOZ_ETW@ JS_HAS_CTYPES = @JS_HAS_CTYPES@ DEHYDRA_PATH = @DEHYDRA_PATH@ NS_TRACE_MALLOC = @NS_TRACE_MALLOC@ INCREMENTAL_LINKER = @INCREMENTAL_LINKER@ MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@ -BUILD_STATIC_LIBS = @BUILD_STATIC_LIBS@ ENABLE_TESTS = @ENABLE_TESTS@ TAR=@TAR@ @@ -115,7 +114,6 @@ MOZ_FIX_LINK_PATHS=@MOZ_FIX_LINK_PATHS@ XPCOM_FROZEN_LDOPTS=@XPCOM_FROZEN_LDOPTS@ XPCOM_LIBS=@XPCOM_LIBS@ -MOZ_TIMELINE=@MOZ_TIMELINE@ ENABLE_STRIP = @ENABLE_STRIP@ PKG_SKIP_STRIP = @PKG_SKIP_STRIP@ @@ -145,14 +143,14 @@ WARNINGS_AS_ERRORS = @WARNINGS_AS_ERRORS@ FAIL_ON_WARNINGS = @FAIL_ON_WARNINGS@ MOZ_OPTIMIZE = @MOZ_OPTIMIZE@ +MOZ_FRAMEPTR_FLAGS = @MOZ_FRAMEPTR_FLAGS@ MOZ_OPTIMIZE_FLAGS = @MOZ_OPTIMIZE_FLAGS@ +MOZ_PGO_OPTIMIZE_FLAGS = @MOZ_PGO_OPTIMIZE_FLAGS@ MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@ MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@ MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@ -MOZ_EXCEPTIONS_FLAGS_ON = @_MOZ_EXCEPTIONS_FLAGS_ON@ -MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE = @MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE@ PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@ PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@ PROFILE_USE_CFLAGS = @PROFILE_USE_CFLAGS@ @@ -170,6 +168,7 @@ AS_DASH_C_FLAG = @AS_DASH_C_FLAG@ LD = @LD@ RC = @RC@ RCFLAGS = @RCFLAGS@ +MC = @MC@ WINDRES = @WINDRES@ IMPLIB = @IMPLIB@ FILTER = @FILTER@ @@ -178,7 +177,10 @@ _MSC_VER = @_MSC_VER@ DLL_PREFIX = @DLL_PREFIX@ LIB_PREFIX = @LIB_PREFIX@ -OBJ_SUFFIX = @OBJ_SUFFIX@ +# We do magic with OBJ_SUFFIX in config.mk, the following ensures we don't +# manually use it before config.mk inclusion +OBJ_SUFFIX = $(error config/config.mk needs to be included before using OBJ_SUFFIX) +_OBJ_SUFFIX = @OBJ_SUFFIX@ LIB_SUFFIX = @LIB_SUFFIX@ DLL_SUFFIX = @DLL_SUFFIX@ BIN_SUFFIX = @BIN_SUFFIX@ @@ -188,13 +190,9 @@ LIBS_DESC_SUFFIX = @LIBS_DESC_SUFFIX@ USE_N32 = @USE_N32@ HAVE_64BIT_OS = @HAVE_64BIT_OS@ -# Temp hack. It is not my intention to leave this crap in here for ever. -# Im talking to fur right now to solve the problem without introducing -# NS_USE_NATIVE to the build system -ramiro. -NS_USE_NATIVE = @NS_USE_NATIVE@ - CC = @CC@ CXX = @CXX@ +CPP = @CPP@ CC_VERSION = @CC_VERSION@ CXX_VERSION = @CXX_VERSION@ @@ -203,7 +201,6 @@ GNU_AS = @GNU_AS@ GNU_LD = @GNU_LD@ GNU_CC = @GNU_CC@ GNU_CXX = @GNU_CXX@ -HAVE_GCC3_ABI = @HAVE_GCC3_ABI@ INTEL_CC = @INTEL_CC@ INTEL_CXX = @INTEL_CXX@ @@ -236,8 +233,6 @@ AUTOCONF = @AUTOCONF@ PERL = @PERL@ PYTHON = @PYTHON@ RANLIB = @RANLIB@ -UNZIP = @UNZIP@ -ZIP = @ZIP@ XARGS = @XARGS@ STRIP = @STRIP@ DOXYGEN = @DOXYGEN@ @@ -249,6 +244,10 @@ NSPR_CONFIG = @NSPR_CONFIG@ NSPR_CFLAGS = @NSPR_CFLAGS@ NSPR_LIBS = @NSPR_LIBS@ +MOZ_NATIVE_FFI = @MOZ_NATIVE_FFI@ +MOZ_FFI_LIBS = @MOZ_FFI_LIBS@ +MOZ_FFI_CFLAGS = @MOZ_FFI_CFLAGS@ + USE_DEPENDENT_LIBS = @USE_DEPENDENT_LIBS@ JS_NATIVE_EDITLINE = @JS_NATIVE_EDITLINE@ @@ -260,8 +259,7 @@ EDITLINE_LIBS = @EDITLINE_LIBS@ # to normal behavior. Makefile's that create shared libraries out of # archives use these flags to force in all of the .o files in the # archives into the shared library. -WRAP_MALLOC_LIB = @WRAP_MALLOC_LIB@ -WRAP_MALLOC_CFLAGS = @WRAP_MALLOC_CFLAGS@ +WRAP_LDFLAGS = @WRAP_LDFLAGS@ DSO_CFLAGS = @DSO_CFLAGS@ DSO_PIC_CFLAGS = @DSO_PIC_CFLAGS@ MKSHLIB = @MKSHLIB@ @@ -288,9 +286,6 @@ CXX_WRAPPER = @CXX_WRAPPER@ MOZ_DEMANGLE_SYMBOLS = @MOZ_DEMANGLE_SYMBOLS@ -# XXX - these need to be cleaned up and have real checks added -cls -CM_BLDTYPE=dbg -AWT_11=1 OS_TARGET=@OS_TARGET@ OS_ARCH=@OS_ARCH@ OS_RELEASE=@OS_RELEASE@ @@ -316,7 +311,8 @@ MOZ_BROWSE_INFO = @MOZ_BROWSE_INFO@ MOZ_TOOLS_DIR = @MOZ_TOOLS_DIR@ MOZ_QUANTIFY = @MOZ_QUANTIFY@ MSMANIFEST_TOOL = @MSMANIFEST_TOOL@ -MOZ_MEMORY_LDFLAGS = @MOZ_MEMORY_LDFLAGS@ +MOZ_GLUE_LDFLAGS = @MOZ_GLUE_LDFLAGS@ +MOZ_GLUE_PROGRAM_LDFLAGS = @MOZ_GLUE_PROGRAM_LDFLAGS@ # Codesighs tools option, enables win32 mapfiles. MOZ_MAPINFO = @MOZ_MAPINFO@ @@ -326,17 +322,14 @@ QEMU_CANT_RUN_JS_SHELL = @QEMU_CANT_RUN_JS_SHELL@ MACOS_SDK_DIR = @MACOS_SDK_DIR@ NEXT_ROOT = @NEXT_ROOT@ GCC_VERSION = @GCC_VERSION@ -XCODEBUILD_VERSION= @XCODEBUILD_VERSION@ -HAS_XCODE_2_1 = @HAS_XCODE_2_1@ UNIVERSAL_BINARY= @UNIVERSAL_BINARY@ +MOZ_CAN_RUN_PROGRAMS = @MOZ_CAN_RUN_PROGRAMS@ HAVE_DTRACE= @HAVE_DTRACE@ VISIBILITY_FLAGS = @VISIBILITY_FLAGS@ WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@ -ENABLE_TRACEJIT = @ENABLE_TRACEJIT@ ENABLE_METHODJIT = @ENABLE_METHODJIT@ -NANOJIT_ARCH = @NANOJIT_ARCH@ HAVE_ARM_SIMD= @HAVE_ARM_SIMD@ JS_SHARED_LIBRARY = @JS_SHARED_LIBRARY@ diff --git a/deps/mozjs/js/src/config/check-sync-exceptions b/deps/mozjs/js/src/config/check-sync-exceptions index 47fa2f585d4..05e83273758 100644 --- a/deps/mozjs/js/src/config/check-sync-exceptions +++ b/deps/mozjs/js/src/config/check-sync-exceptions @@ -22,3 +22,4 @@ system_wrappers_js # Ignore "compiled" python files *.pyc +*.pyo diff --git a/deps/mozjs/js/src/config/check_source_count.py b/deps/mozjs/js/src/config/check_source_count.py index 4a3cab07ab6..bdc48659f91 100755 --- a/deps/mozjs/js/src/config/check_source_count.py +++ b/deps/mozjs/js/src/config/check_source_count.py @@ -33,9 +33,9 @@ else: print "TEST-UNEXPECTED-FAIL | check_source_count.py %s | " % (search_string), if count < expected_count: - print "There are less occurences of /%s/ than expected. This may mean that you have removed some, but forgotten to account for it %s." % (search_string, error_location) + print "There are fewer occurrences of /%s/ than expected. This may mean that you have removed some, but forgotten to account for it %s." % (search_string, error_location) else: - print "There are more occurences of /%s/ than expected. We're trying to prevent an increase in the number of %s's, using %s if possible. If it in unavoidable, you should update the expected count %s." % (search_string, search_string, replacement, error_location) + print "There are more occurrences of /%s/ than expected. We're trying to prevent an increase in the number of %s's, using %s if possible. If it in unavoidable, you should update the expected count %s." % (search_string, search_string, replacement, error_location) print "Expected: %d; found: %d" % (expected_count, count) for k in sorted(details): diff --git a/deps/mozjs/js/src/config/config.mk b/deps/mozjs/js/src/config/config.mk index 0f816b3e4a3..02460d7bd7a 100644 --- a/deps/mozjs/js/src/config/config.mk +++ b/deps/mozjs/js/src/config/config.mk @@ -45,9 +45,9 @@ # # Define an include-at-most-once flag -#ifdef INCLUDED_CONFIG_MK -#$(error Don't include config.mk twice!) -#endif +ifdef INCLUDED_CONFIG_MK +$(error Don't include config.mk twice!) +endif INCLUDED_CONFIG_MK = 1 EXIT_ON_ERROR = set -e; # Shell loops continue past errors without this. @@ -88,6 +88,9 @@ space :=$(nullstr) # EOL core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1))))))) +# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there. +LIBXUL_DIST ?= $(DIST) + # FINAL_TARGET specifies the location into which we copy end-user-shipped # build products (typelibs, components, chrome). # @@ -126,7 +129,7 @@ endif # but save the version to allow multiple versions of the same base # platform to be built in the same tree. # -ifneq (,$(filter FreeBSD HP-UX IRIX Linux NetBSD OpenBSD OSF1 SunOS,$(OS_ARCH))) +ifneq (,$(filter FreeBSD HP-UX Linux NetBSD OpenBSD OSF1 SunOS,$(OS_ARCH))) OS_RELEASE := $(basename $(OS_RELEASE)) # Allow the user to ignore the OS_VERSION, which is usually irrelevant. @@ -146,42 +149,51 @@ FINAL_LINK_COMP_NAMES = $(DEPTH)/config/final-link-comp-names MOZ_UNICHARUTIL_LIBS = $(LIBXUL_DIST)/lib/$(LIB_PREFIX)unicharutil_s.$(LIB_SUFFIX) MOZ_WIDGET_SUPPORT_LIBS = $(DIST)/lib/$(LIB_PREFIX)widgetsupport_s.$(LIB_SUFFIX) -ifdef MOZ_MEMORY -ifneq ($(OS_ARCH),WINNT) -JEMALLOC_LIBS = $(MKSHLIB_FORCE_ALL) $(call EXPAND_MOZLIBNAME,jemalloc) $(MKSHLIB_UNFORCE_ALL) -# If we are linking jemalloc into a program, we want the jemalloc symbols -# to be exported -ifneq (,$(SIMPLE_PROGRAMS)$(PROGRAM)) -JEMALLOC_LIBS += $(MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS) -endif -endif -endif - CC := $(CC_WRAPPER) $(CC) CXX := $(CXX_WRAPPER) $(CXX) MKDIR ?= mkdir SLEEP ?= sleep TOUCH ?= touch +ifndef .PYMAKE +PYTHON_PATH = $(PYTHON) $(topsrcdir)/config/pythonpath.py +else +PYCOMMANDPATH += $(topsrcdir)/config +PYTHON_PATH = %pythonpath main +endif + # determine debug-related options +_DEBUG_ASFLAGS := _DEBUG_CFLAGS := _DEBUG_LDFLAGS := ifdef MOZ_DEBUG - _DEBUG_CFLAGS += $(MOZ_DEBUG_ENABLE_DEFS) $(MOZ_DEBUG_FLAGS) - _DEBUG_LDFLAGS += $(MOZ_DEBUG_LDFLAGS) + _DEBUG_CFLAGS += $(MOZ_DEBUG_ENABLE_DEFS) XULPPFLAGS += $(MOZ_DEBUG_ENABLE_DEFS) else _DEBUG_CFLAGS += $(MOZ_DEBUG_DISABLE_DEFS) XULPPFLAGS += $(MOZ_DEBUG_DISABLE_DEFS) - ifdef MOZ_DEBUG_SYMBOLS - _DEBUG_CFLAGS += $(MOZ_DEBUG_FLAGS) - _DEBUG_LDFLAGS += $(MOZ_DEBUG_LDFLAGS) +endif + +ifneq (,$(MOZ_DEBUG)$(MOZ_DEBUG_SYMBOLS)) + ifeq ($(AS),yasm) + ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) + _DEBUG_ASFLAGS += -g cv8 + else + ifneq ($(OS_ARCH),Darwin) + _DEBUG_ASFLAGS += -g dwarf2 + endif + endif + else + _DEBUG_ASFLAGS += $(MOZ_DEBUG_FLAGS) endif + _DEBUG_CFLAGS += $(MOZ_DEBUG_FLAGS) + _DEBUG_LDFLAGS += $(MOZ_DEBUG_LDFLAGS) endif MOZALLOC_LIB = $(call EXPAND_LIBNAME_PATH,mozalloc,$(DIST)/lib) +ASFLAGS += $(_DEBUG_ASFLAGS) OS_CFLAGS += $(_DEBUG_CFLAGS) OS_CXXFLAGS += $(_DEBUG_CFLAGS) OS_LDFLAGS += $(_DEBUG_LDFLAGS) @@ -195,20 +207,12 @@ OS_CXXFLAGS += -FR endif else # ! MOZ_DEBUG -# We don't build a static CRT when building a custom CRT, -# it appears to be broken. So don't link to jemalloc if -# the Makefile wants static CRT linking. -ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_) -# Disable default CRT libs and add the right lib path for the linker -OS_LDFLAGS += $(MOZ_MEMORY_LDFLAGS) -endif - # MOZ_DEBUG_SYMBOLS generates debug symbols in separate PDB files. # Used for generating an optimized build with debugging symbols. # Used in the Windows nightlies to generate symbols for crash reporting. ifdef MOZ_DEBUG_SYMBOLS -OS_CXXFLAGS += -Zi -UDEBUG -DNDEBUG -OS_CFLAGS += -Zi -UDEBUG -DNDEBUG +OS_CXXFLAGS += -UDEBUG -DNDEBUG +OS_CFLAGS += -UDEBUG -DNDEBUG ifdef HAVE_64BIT_OS OS_LDFLAGS += -DEBUG -OPT:REF,ICF else @@ -241,39 +245,31 @@ endif endif # NS_TRACE_MALLOC endif # MOZ_DEBUG + +# We don't build a static CRT when building a custom CRT, +# it appears to be broken. So don't link to jemalloc if +# the Makefile wants static CRT linking. +ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_1) +# Disable default CRT libs and add the right lib path for the linker +MOZ_GLUE_LDFLAGS= +endif + endif # WINNT && !GNU_CC +ifdef MOZ_GLUE_PROGRAM_LDFLAGS +DEFINES += -DMOZ_GLUE_IN_PROGRAM +else +MOZ_GLUE_PROGRAM_LDFLAGS=$(MOZ_GLUE_LDFLAGS) +endif + # # Build using PIC by default -# Do not use PIC if not building a shared lib (see exceptions below) # - -ifndef BUILD_STATIC_LIBS _ENABLE_PIC=1 -endif -ifneq (,$(FORCE_SHARED_LIB)$(FORCE_USE_PIC)) -_ENABLE_PIC=1 -endif - -# If module is going to be merged into the nsStaticModule, -# make sure that the entry points are translated and -# the module is built static. - -ifdef IS_COMPONENT -ifdef EXPORT_LIBRARY -ifneq (,$(BUILD_STATIC_LIBS)) -ifdef MODULE_NAME -DEFINES += -DXPCOM_TRANSLATE_NSGM_ENTRY_POINT=1 -FORCE_STATIC_LIB=1 -endif -endif -endif -endif -# Determine if module being compiled is destined +# Determine if module being compiled is destined # to be merged into libxul -ifdef MOZ_ENABLE_LIBXUL ifdef LIBXUL_LIBRARY ifdef IS_COMPONENT ifdef MODULE_NAME @@ -283,17 +279,14 @@ $(error Component makefile does not specify MODULE_NAME.) endif endif FORCE_STATIC_LIB=1 -_ENABLE_PIC=1 SHORT_LIBNAME= endif -endif # If we are building this component into an extension/xulapp, it cannot be # statically linked. In the future we may want to add a xulapp meta-component # build option. ifdef XPI_NAME -_ENABLE_PIC=1 ifdef IS_COMPONENT EXPORT_LIBRARY= FORCE_STATIC_LIB= @@ -301,19 +294,6 @@ FORCE_SHARED_LIB=1 endif endif -# -# Disable PIC if necessary -# - -ifndef _ENABLE_PIC -DSO_CFLAGS= -ifeq ($(OS_ARCH)_$(HAVE_GCC3_ABI),Darwin_1) -DSO_PIC_CFLAGS=-mdynamic-no-pic -else -DSO_PIC_CFLAGS= -endif -endif - ifndef SHARED_LIBRARY_NAME ifdef LIBRARY_NAME SHARED_LIBRARY_NAME=$(LIBRARY_NAME) @@ -326,11 +306,6 @@ STATIC_LIBRARY_NAME=$(LIBRARY_NAME) endif endif -# This comes from configure -ifdef MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE -NO_PROFILE_GUIDED_OPTIMIZE = 1 -endif - # No sense in profiling tools ifdef INTERNAL_TOOLS NO_PROFILE_GUIDED_OPTIMIZE = 1 @@ -376,7 +351,6 @@ endif # Force XPCOM/widget/gfx methods to be _declspec(dllexport) when we're # building libxul libraries -ifdef MOZ_ENABLE_LIBXUL ifdef LIBXUL_LIBRARY DEFINES += \ -D_IMPL_NS_COM \ @@ -393,20 +367,6 @@ ifndef JS_SHARED_LIBRARY DEFINES += -DSTATIC_EXPORTABLE_JS_API endif endif -endif - -# Force _all_ exported methods to be |_declspec(dllexport)| when we're -# building them into the executable. - -ifeq (,$(filter-out WINNT OS2, $(OS_ARCH))) -ifdef BUILD_STATIC_LIBS -DEFINES += \ - -D_IMPL_NS_GFX \ - -D_IMPL_NS_MSG_BASE \ - -D_IMPL_NS_WIDGET \ - $(NULL) -endif -endif # Flags passed to JarMaker.py MAKE_JARS_FLAGS = \ @@ -442,7 +402,6 @@ MY_RULES := $(DEPTH)/config/myrules.mk # Default command macros; can be overridden in .mk. # CCC = $(CXX) -NFSPWD = $(CONFIG_TOOLS)/nfspwd PURIFY = purify $(PURIFYOPTIONS) QUANTIFY = quantify $(QUANTIFYOPTIONS) ifdef CROSS_COMPILE @@ -464,7 +423,7 @@ INCLUDES = \ -I$(DIST)/include -I$(DIST)/include/nsprpub \ $(if $(LIBXUL_SDK),-I$(LIBXUL_SDK)/include -I$(LIBXUL_SDK)/include/nsprpub) \ $(OS_INCLUDES) \ - $(NULL) + $(NULL) include $(topsrcdir)/config/static-checking-config.mk @@ -481,8 +440,13 @@ ifdef MODULE_OPTIMIZE_FLAGS CFLAGS += $(MODULE_OPTIMIZE_FLAGS) CXXFLAGS += $(MODULE_OPTIMIZE_FLAGS) else +ifneq (,$(if $(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE),$(MOZ_PGO_OPTIMIZE_FLAGS))) +CFLAGS += $(MOZ_PGO_OPTIMIZE_FLAGS) +CXXFLAGS += $(MOZ_PGO_OPTIMIZE_FLAGS) +else CFLAGS += $(MOZ_OPTIMIZE_FLAGS) CXXFLAGS += $(MOZ_OPTIMIZE_FLAGS) +endif # neq (,$(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE)) endif # MODULE_OPTIMIZE_FLAGS else CFLAGS += $(MOZ_OPTIMIZE_FLAGS) @@ -507,6 +471,9 @@ endif # MOZ_OPTIMIZE == 1 endif # MOZ_OPTIMIZE endif # CROSS_COMPILE +CFLAGS += $(MOZ_FRAMEPTR_FLAGS) +CXXFLAGS += $(MOZ_FRAMEPTR_FLAGS) + # Check for FAIL_ON_WARNINGS & FAIL_ON_WARNINGS_DEBUG (Shorthand for Makefiles # to request that we use the 'warnings as errors' compile flags) @@ -518,13 +485,6 @@ FAIL_ON_WARNINGS_DEBUG= FAIL_ON_WARNINGS= endif # WINNT && (MOS_PROFILE_GENERATE ^ MOZ_PROFILE_USE) -# Also clear FAIL_ON_WARNINGS[_DEBUG] for Android builds, since -# they have some platform-specific warnings we haven't fixed yet. -ifeq ($(OS_TARGET),Android) -FAIL_ON_WARNINGS_DEBUG= -FAIL_ON_WARNINGS= -endif # Android - # Now, check for debug version of flag; it turns on normal flag in debug builds. ifdef FAIL_ON_WARNINGS_DEBUG ifdef MOZ_DEBUG @@ -557,17 +517,22 @@ RTL_FLAGS=-MD # Dynamically linked, multithreaded RTL ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)) ifndef MOZ_NO_DEBUG_RTL RTL_FLAGS=-MDd # Dynamically linked, multithreaded MSVC4.0 debug RTL -endif +endif endif # MOZ_DEBUG || NS_TRACE_MALLOC endif # USE_STATIC_LIBS endif # WINNT && !GNU_CC ifeq ($(OS_ARCH),Darwin) -# Darwin doesn't cross-compile, so just set both types of flags here. +# Compiling ObjC requires an Apple compiler anyway, so it's ok to set +# host CMFLAGS here. HOST_CMFLAGS += -fobjc-exceptions HOST_CMMFLAGS += -fobjc-exceptions OS_COMPILE_CMFLAGS += -fobjc-exceptions OS_COMPILE_CMMFLAGS += -fobjc-exceptions +ifeq ($(MOZ_WIDGET_TOOLKIT),uikit) +OS_COMPILE_CMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch +OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch +endif endif COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) @@ -616,10 +581,6 @@ else ELF_DYNSTR_GC = : endif -ifeq ($(MOZ_WIDGET_TOOLKIT),qt) -OS_LIBS += $(MOZ_QT_LIBS) -endif - ifndef CROSS_COMPILE ifdef USE_ELF_DYNSTR_GC ifdef MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS @@ -649,13 +610,6 @@ ifeq (2,$(MOZ_OPTIMIZE)) PBBUILD_SETTINGS += GCC_MODEL_TUNING= OPTIMIZATION_CFLAGS="$(MOZ_OPTIMIZE_FLAGS)" endif # MOZ_OPTIMIZE=2 endif # MOZ_OPTIMIZE -ifeq (1,$(HAS_XCODE_2_1)) -# Xcode 2.1 puts its build products in a directory corresponding to the -# selected build style/configuration. -XCODE_PRODUCT_DIR = build/$(BUILDSTYLE) -else -XCODE_PRODUCT_DIR = build -endif # HAS_XCODE_2_1=1 endif # OS_ARCH=Darwin @@ -741,27 +695,24 @@ endif # NSINSTALL_BIN ifeq (,$(CROSS_COMPILE)$(filter-out WINNT OS2, $(OS_ARCH))) INSTALL = $(NSINSTALL) else -ifeq ($(NSDISTMODE),copy) -# copy files, but preserve source mtime -INSTALL = $(NSINSTALL) -t -else -ifeq ($(NSDISTMODE),absolute_symlink) -# install using absolute symbolic links -ifeq ($(OS_ARCH),Darwin) -INSTALL = $(NSINSTALL) -L $(PWD) -else -INSTALL = $(NSINSTALL) -L `$(NFSPWD)` -endif # Darwin -else -# install using relative symbolic links -INSTALL = $(NSINSTALL) -R -endif # absolute_symlink -endif # copy + +# This isn't laid out as conditional directives so that NSDISTMODE can be +# target-specific. +INSTALL = $(if $(filter copy, $(NSDISTMODE)), $(NSINSTALL) -t, $(if $(filter absolute_symlink, $(NSDISTMODE)), $(NSINSTALL) -L $(PWD), $(NSINSTALL) -R)) + endif # WINNT/OS2 # Use nsinstall in copy mode to install files on the system SYSINSTALL = $(NSINSTALL) -t +# Directory nsinstall. Windows and OS/2 nsinstall can't recursively copy +# directories. +ifneq (,$(filter WINNT os2-emx,$(HOST_OS_ARCH))) +DIR_INSTALL = $(PYTHON) $(topsrcdir)/config/nsinstall.py +else +DIR_INSTALL = $(INSTALL) +endif # WINNT/OS2 + # # Localization build automation # @@ -804,10 +755,10 @@ endif MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f))) ifeq (OS2,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)" +RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(LIBXUL_DIST)" else ifneq (WINNT,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh +RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh endif # ! WINNT endif # ! OS2 @@ -837,9 +788,45 @@ EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR) EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC) EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC) -EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD) -EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist -- $(MKSHLIB) +EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD) +EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB) ifdef STDCXX_COMPAT -CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && echo "Error: We don't want these libstdc++ symbol versions to be used" && exit 1 || exit 0 +CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0 +endif + +# autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including +# this file +OBJ_SUFFIX := $(_OBJ_SUFFIX) + +# PGO builds with GCC build objects with instrumentation in a first pass, +# then objects optimized, without instrumentation, in a second pass. If +# we overwrite the ojects from the first pass with those from the second, +# we end up not getting instrumentation data for better optimization on +# incremental builds. As a consequence, we use a different object suffix +# for the first pass. +ifndef NO_PROFILE_GUIDED_OPTIMIZE +ifdef MOZ_PROFILE_GENERATE +ifdef GNU_CC +OBJ_SUFFIX := i_o +endif +endif +endif + +# EXPAND_LIBNAME - $(call EXPAND_LIBNAME,foo) +# expands to $(LIB_PREFIX)foo.$(LIB_SUFFIX) or -lfoo, depending on linker +# arguments syntax. Should only be used for system libraries + +# EXPAND_LIBNAME_PATH - $(call EXPAND_LIBNAME_PATH,foo,dir) +# expands to dir/$(LIB_PREFIX)foo.$(LIB_SUFFIX) + +# EXPAND_MOZLIBNAME - $(call EXPAND_MOZLIBNAME,foo) +# expands to $(DIST)/lib/$(LIB_PREFIX)foo.$(LIB_SUFFIX) + +ifdef GNU_CC +EXPAND_LIBNAME = $(addprefix -l,$(1)) +else +EXPAND_LIBNAME = $(foreach lib,$(1),$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) endif +EXPAND_LIBNAME_PATH = $(foreach lib,$(1),$(2)/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) +EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) diff --git a/deps/mozjs/js/src/config/expandlibs.py b/deps/mozjs/js/src/config/expandlibs.py index 62d93ebfc1b..ca3eac8ccff 100644 --- a/deps/mozjs/js/src/config/expandlibs.py +++ b/deps/mozjs/js/src/config/expandlibs.py @@ -86,6 +86,11 @@ def splitpath(path): return relpath return path +def isObject(path): + '''Returns whether the given path points to an object file, that is, + ends with OBJ_SUFFIX or .i_o''' + return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o'] + class LibDescriptor(dict): KEYS = ['OBJS', 'LIBS'] diff --git a/deps/mozjs/js/src/config/expandlibs_exec.py b/deps/mozjs/js/src/config/expandlibs_exec.py index 20cbac679ef..b6671cfdef7 100644 --- a/deps/mozjs/js/src/config/expandlibs_exec.py +++ b/deps/mozjs/js/src/config/expandlibs_exec.py @@ -48,11 +48,15 @@ EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list) or 'linkerscript' for GNU ld linker scripts. See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details. + +With the --reorder argument, followed by a file name, it will reorder the +object files from the command line according to the order given in the file. +Implies --extract. ''' from __future__ import with_statement import sys import os -from expandlibs import ExpandArgs, relativize +from expandlibs import ExpandArgs, relativize, isObject import expandlibs_config as conf from optparse import OptionParser import subprocess @@ -93,7 +97,7 @@ def _extract(self, args): subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp) objs = [] for root, dirs, files in os.walk(tmp): - objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] == conf.OBJ_SUFFIX] + objs += [relativize(os.path.join(root, f)) for f in files if isObject(f)] newlist += objs else: newlist += [arg] @@ -105,7 +109,7 @@ def makelist(self): '''Replaces object file names with a temporary list file, using a list format depending on the EXPAND_LIBS_LIST_STYLE variable ''' - objs = [o for o in self if os.path.splitext(o)[1] == conf.OBJ_SUFFIX] + objs = [o for o in self if isObject(o)] if not len(objs): return fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir) if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript": @@ -125,6 +129,22 @@ def makelist(self): newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs] self[0:] = newlist + def reorder(self, order_list): + '''Given a list of file names without OBJ_SUFFIX, rearrange self + so that the object file names it contains are ordered according to + that list. + ''' + objs = [o for o in self if isObject(o)] + if not objs: return + idx = self.index(objs[0]) + # Keep everything before the first object, then the ordered objects, + # then any other objects, then any non-objects after the first object + objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs]) + self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \ + [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \ + [x for x in self[idx:] if not isObject(x)] + + def main(): parser = OptionParser() parser.add_option("--extract", action="store_true", dest="extract", @@ -133,12 +153,17 @@ def main(): help="use a list file for objects when executing a command") parser.add_option("--verbose", action="store_true", dest="verbose", help="display executed command and temporary files content") + parser.add_option("--reorder", dest="reorder", + help="reorder the objects according to the given list", metavar="FILE") (options, args) = parser.parse_args() with ExpandArgsMore(args) as args: - if options.extract: + if options.extract or options.reorder: args.extract() + if options.reorder: + with open(options.reorder) as file: + args.reorder([l.strip() for l in file.readlines()]) if options.uselist: args.makelist() diff --git a/deps/mozjs/js/src/config/expandlibs_gen.py b/deps/mozjs/js/src/config/expandlibs_gen.py index beaaf2e74b7..f6d26d3c3d1 100644 --- a/deps/mozjs/js/src/config/expandlibs_gen.py +++ b/deps/mozjs/js/src/config/expandlibs_gen.py @@ -41,12 +41,12 @@ import sys import os import expandlibs_config as conf -from expandlibs import LibDescriptor +from expandlibs import LibDescriptor, isObject def generate(args): desc = LibDescriptor() for arg in args: - if os.path.splitext(arg)[1] == conf.OBJ_SUFFIX: + if isObject(arg): desc['OBJS'].append(os.path.abspath(arg)) elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX and \ (os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX)): diff --git a/deps/mozjs/js/src/config/fastcwd.pl b/deps/mozjs/js/src/config/fastcwd.pl deleted file mode 100644 index c327ccfdf2b..00000000000 --- a/deps/mozjs/js/src/config/fastcwd.pl +++ /dev/null @@ -1,66 +0,0 @@ -#!perl5 -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -sub fastcwd { - local($odev, $oino, $cdev, $cino, $tdev, $tino); - local(@path, $path); - local(*DIR); - - ($cdev, $cino) = stat('.'); - for (;;) { - ($odev, $oino) = ($cdev, $cino); - chdir('..'); - ($cdev, $cino) = stat('.'); - last if $odev == $cdev && $oino == $cino; - opendir(DIR, '.'); - for (;;) { - $_ = readdir(DIR); - next if $_ eq '.'; - next if $_ eq '..'; - - last unless $_; - ($tdev, $tino) = lstat($_); - last unless $tdev != $odev || $tino != $oino; - } - closedir(DIR); - unshift(@path, $_); - } - chdir($path = '/' . join('/', @path)); - $path; -} -1; diff --git a/deps/mozjs/js/src/config/makefiles/target_export.mk b/deps/mozjs/js/src/config/makefiles/target_export.mk new file mode 100644 index 00000000000..318feb8f949 --- /dev/null +++ b/deps/mozjs/js/src/config/makefiles/target_export.mk @@ -0,0 +1,68 @@ +# -*- makefile -*- +# vim:set ts=8 sw=8 sts=8 noet: +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chase Phillips +# Benjamin Smedberg +# Jeff Walden +# Joey Armstrong +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +PARALLEL_DIRS_export = $(addsuffix _export,$(PARALLEL_DIRS)) + +.PHONY: export $(PARALLEL_DIRS_export) + +############### +## TIER targets +############### +export_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,export,$(dir))) + +################# +## Common targets +################# +ifdef PARALLEL_DIRS +export:: $(PARALLEL_DIRS_export) + +$(PARALLEL_DIRS_export): %_export: %/Makefile + +@$(call SUBMAKE,export,$*) +endif + +export:: $(SUBMAKEFILES) $(MAKE_DIRS) $(if $(XPIDLSRCS),$(IDL_DIR)) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) diff --git a/deps/mozjs/js/src/config/makefiles/target_libs.mk b/deps/mozjs/js/src/config/makefiles/target_libs.mk new file mode 100644 index 00000000000..707fdd179d8 --- /dev/null +++ b/deps/mozjs/js/src/config/makefiles/target_libs.mk @@ -0,0 +1,141 @@ +# -*- makefile -*- +# vim:set ts=8 sw=8 sts=8 noet: +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chase Phillips +# Benjamin Smedberg +# Jeff Walden +# Joey Armstrong +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +PARALLEL_DIRS_libs = $(addsuffix _libs,$(PARALLEL_DIRS)) + +.PHONY: libs $(PARALLEL_DIRS_libs) + +############### +## TIER targets +############### +libs_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,libs,$(dir))) + +################# +## Common targets +################# +ifdef PARALLEL_DIRS +libs:: $(PARALLEL_DIRS_libs) + +$(PARALLEL_DIRS_libs): %_libs: %/Makefile + +@$(call SUBMAKE,libs,$*) +endif + + +#################### +## +#################### +ifdef EXPORT_LIBRARY +ifeq ($(EXPORT_LIBRARY),1) +ifdef IS_COMPONENT +EXPORT_LIBRARY = $(DEPTH)/staticlib/components +else +EXPORT_LIBRARY = $(DEPTH)/staticlib +endif +else +# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there +GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib)) +endif +endif # EXPORT_LIBRARY + +libs:: $(SUBMAKEFILES) $(MAKE_DIRS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) $(HOST_PROGRAM) $(PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(SIMPLE_PROGRAMS) $(JAVA_LIBRARY) +ifndef NO_DIST_INSTALL +ifdef LIBRARY +ifdef EXPORT_LIBRARY # Stage libs that will be linked into a static build + $(INSTALL) $(IFLAGS1) $(LIBRARY) $(EXPORT_LIBRARY) +endif # EXPORT_LIBRARY +ifdef DIST_INSTALL +ifdef IS_COMPONENT + $(error Shipping static component libs makes no sense.) +else + $(INSTALL) $(IFLAGS1) $(LIBRARY) $(DIST)/lib +endif +endif # DIST_INSTALL +endif # LIBRARY +ifdef SHARED_LIBRARY +ifdef IS_COMPONENT + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components + $(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY) +ifndef NO_COMPONENTS_MANIFEST + @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest "manifest components/components.manifest" + @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/components/components.manifest "binary-component $(SHARED_LIBRARY)" +endif +else # ! IS_COMPONENT +ifneq (,$(filter OS2 WINNT,$(OS_ARCH))) +ifndef NO_INSTALL_IMPORT_LIBRARY + $(INSTALL) $(IFLAGS2) $(IMPORT_LIBRARY) $(DIST)/lib +endif +else + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(DIST)/lib +endif + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET) +endif # IS_COMPONENT +endif # SHARED_LIBRARY +ifdef PROGRAM + $(INSTALL) $(IFLAGS2) $(PROGRAM) $(FINAL_TARGET) +endif +ifdef SIMPLE_PROGRAMS + $(INSTALL) $(IFLAGS2) $(SIMPLE_PROGRAMS) $(FINAL_TARGET) +endif +ifdef HOST_PROGRAM + $(INSTALL) $(IFLAGS2) $(HOST_PROGRAM) $(DIST)/host/bin +endif +ifdef HOST_SIMPLE_PROGRAMS + $(INSTALL) $(IFLAGS2) $(HOST_SIMPLE_PROGRAMS) $(DIST)/host/bin +endif +ifdef HOST_LIBRARY + $(INSTALL) $(IFLAGS1) $(HOST_LIBRARY) $(DIST)/host/lib +endif +ifdef JAVA_LIBRARY +ifdef IS_COMPONENT + $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)/components +else + $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET) +endif +endif # JAVA_LIBRARY +endif # !NO_DIST_INSTALL + $(LOOP_OVER_DIRS) + +# EOF diff --git a/deps/mozjs/js/src/config/makefiles/target_tools.mk b/deps/mozjs/js/src/config/makefiles/target_tools.mk new file mode 100644 index 00000000000..1147d910d0d --- /dev/null +++ b/deps/mozjs/js/src/config/makefiles/target_tools.mk @@ -0,0 +1,72 @@ +# -*- makefile -*- +# vim:set ts=8 sw=8 sts=8 noet: +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chase Phillips +# Benjamin Smedberg +# Jeff Walden +# Joey Armstrong +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +PARALLEL_DIRS_tools = $(addsuffix _tools,$(PARALLEL_DIRS)) + +.PHONY: tools $(PARALLEL_DIRS_tools) + +############### +## TIER targets +############### +tools_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir))) + +################# +## Common targets +################# +ifdef PARALLEL_DIRS +tools:: $(PARALLEL_DIRS_tools) + +$(PARALLEL_DIRS_tools): %_tools: %/Makefile + +@$(call SUBMAKE,tools,$*) +endif + +tools:: $(SUBMAKEFILES) $(MAKE_DIRS) + $(LOOP_OVER_DIRS) +ifneq (,$(strip $(TOOL_DIRS))) + $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,libs,$(dir))) +endif + +# EOF diff --git a/deps/mozjs/js/src/config/milestone.txt b/deps/mozjs/js/src/config/milestone.txt index 87783c6532f..f780391e9db 100644 --- a/deps/mozjs/js/src/config/milestone.txt +++ b/deps/mozjs/js/src/config/milestone.txt @@ -10,4 +10,4 @@ # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -6.0a1 +13.0a1 diff --git a/deps/mozjs/js/src/config/nfspwd.pl b/deps/mozjs/js/src/config/nfspwd.pl deleted file mode 100644 index 2f0e4fb8047..00000000000 --- a/deps/mozjs/js/src/config/nfspwd.pl +++ /dev/null @@ -1,50 +0,0 @@ -#! perl -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -require "fastcwd.pl"; - -$_ = &fastcwd; -if (m@^/[uh]/@o || s@^/tmp_mnt/@/@o) { - print("$_\n"); -} elsif ((($user, $rest) = m@^/usr/people/(\w+)/(.*)@o) - && readlink("/u/$user") eq "/usr/people/$user") { - print("/u/$user/$rest\n"); -} else { - chop($host = `hostname`); - print("/h/$host$_\n"); -} diff --git a/deps/mozjs/js/src/config/nsinstall_win.c b/deps/mozjs/js/src/config/nsinstall_win.c index d9b723b2c7c..1c1623f82fa 100644 --- a/deps/mozjs/js/src/config/nsinstall_win.c +++ b/deps/mozjs/js/src/config/nsinstall_win.c @@ -41,6 +41,38 @@ static BOOL sh_DoCopy(wchar_t *srcFileName, DWORD srcFileAttributes, #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define STR_LEN(a) (ARRAY_LEN(a) - 1) +#ifdef __MINGW32__ + +/* MingW currently does not implement a wide version of the + startup routines. Workaround is to implement something like + it ourselves. */ + +#include + +int wmain(int argc, WCHAR **argv); + +int main(int argc, char **argv) +{ + int result; + wchar_t *commandLine = GetCommandLineW(); + int argcw = 0; + wchar_t **_argvw = CommandLineToArgvW( commandLine, &argcw ); + wchar_t *argvw[argcw + 1]; + int i; + if (!_argvw) + return 127; + /* CommandLineToArgvW doesn't output the ending NULL so + we have to manually add it on */ + for ( i = 0; i < argcw; i++ ) + argvw[i] = _argvw[i]; + argvw[argcw] = NULL; + + result = wmain(argcw, argvw); + LocalFree(_argvw); + return result; +} +#endif /* __MINGW32__ */ + /* changes all forward slashes in token to backslashes */ void changeForwardSlashesToBackSlashes ( wchar_t *arg ) { diff --git a/deps/mozjs/js/src/config/pathsub.c b/deps/mozjs/js/src/config/pathsub.c index 8384e5fb982..d8629b79340 100644 --- a/deps/mozjs/js/src/config/pathsub.c +++ b/deps/mozjs/js/src/config/pathsub.c @@ -128,7 +128,7 @@ struct dirent *readdir(DIR *d) #endif char * -ino2name(ino_t ino, char *dir) +ino2name(ino_t ino) { DIR *dp; struct dirent *ep; @@ -230,7 +230,7 @@ reversepath(char *inpath, char *name, int len, char *outpath) if (strcmp(buf, "..") == 0) { if (stat(".", &sb) < 0) fail("cannot stat current directory"); - name = ino2name(sb.st_ino, ".."); + name = ino2name(sb.st_ino); len = strlen(name); cp -= len + 1; strcpy(cp, name); diff --git a/deps/mozjs/js/src/config/pathsub.h b/deps/mozjs/js/src/config/pathsub.h index ab1a7fee2bf..8fe11a9fa63 100644 --- a/deps/mozjs/js/src/config/pathsub.h +++ b/deps/mozjs/js/src/config/pathsub.h @@ -59,7 +59,7 @@ extern char *program; extern void fail(char *format, ...); extern char *getcomponent(char *path, char *name); -extern char *ino2name(ino_t ino, char *dir); +extern char *ino2name(ino_t ino); extern void *xmalloc(size_t size); extern char *xstrdup(char *s); extern char *xbasename(char *path); diff --git a/deps/mozjs/js/src/config/preprocessor.pl b/deps/mozjs/js/src/config/preprocessor.pl deleted file mode 100644 index 3da6654dd72..00000000000 --- a/deps/mozjs/js/src/config/preprocessor.pl +++ /dev/null @@ -1,671 +0,0 @@ -#!/usr/bin/perl -w -# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*- -# -# Preprocessor -# Version 1.1 -# -# Copyright (c) 2002, 2003, 2004 by Ian Hickson -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -# Thanks to bryner and bsmedberg for suggestions. -# Thanks to jon rekai for a patch to not require File::Spec 0.8. - -use strict; - -# takes as arguments the files to process -# defaults to stdin -# output to stdout - -my $stack = new stack; -my $marker = '#'; - -# command line arguments -my @includes; -while ($_ = $ARGV[0], defined($_) && /^-./) { - shift; - last if /^--$/os; - if (/^-D(.*)$/os) { - for ($1) { - if (/^([\w\.]+)=(.*)$/os) { - $stack->define($1, $2); - } elsif (/^([\w\.]+)$/os) { - $stack->define($1, 1); - } else { - die "$0: invalid argument to -D: $_\n"; - } - } - } elsif (/^-F(.*)$/os) { - for ($1) { - if (/^(\w+)$/os) { - $stack->filter($1, 1); - } else { - die "$0: invalid argument to -F: $_\n"; - } - } - } elsif (/^-I(.*)$/os) { - push(@includes, $1); - } elsif (/^-E$/os) { - foreach (keys %ENV) { - # define all variables that have valid names - $stack->define($_, $ENV{$_}) unless m/\W/; - } - } elsif (/^-d$/os) { - $stack->{'dependencies'} = 1; - } elsif (/^--line-endings=crlf$/os) { - $stack->{'lineEndings'} = "\x0D\x0A"; - } elsif (/^--line-endings=cr$/os) { - $stack->{'lineEndings'} = "\x0D"; - } elsif (/^--line-endings=lf$/os) { - $stack->{'lineEndings'} = "\x0A"; - } elsif (/^--line-endings=(.+)$/os) { - die "$0: unrecognised line ending: $1\n"; - } elsif (/^--marker=(.)$/os) { - $marker = $1; - } else { - die "$0: invalid argument: $_\n"; - } -} -unshift(@ARGV, '-') unless @ARGV; -unshift(@ARGV, @includes); - -# do the work -foreach (@ARGV) { include($stack, $_); } -exit(0); - -######################################################################## - -package main; -use File::Spec; -use File::Spec::Unix; # on all platforms, because the #include syntax is unix-based - -# Note: Ideally we would use File::Spec 0.8. When this becomes -# possible, add "0.8" to the first "use" line above, then replace -# occurrences of "::_0_8::" with "->" below. And remove the code for -# File::Spec 0.8 much lower down the file. - -sub include { - my($stack, $filename) = @_; - my $directory = $stack->{'variables'}->{'DIRECTORY'}; - if ($filename ne '-') { - $filename = File::Spec::_0_8::rel2abs($filename, $directory); - # splitpath expects forward-slash paths on windows, so we have to - # change the slashes if using Activestate Perl. - $filename =~ s?\\?/?g if "$^O" eq "MSWin32"; - my($volume, $path) = File::Spec::_0_8::splitpath($filename); - $directory = File::Spec::_0_8::catpath($volume, $path, ''); - } - local $stack->{'variables'}->{'DIRECTORY'} = $directory; - local $stack->{'variables'}->{'FILE'} = $filename; - local $stack->{'variables'}->{'LINE'} = 0; - local *FILE; - open(FILE, $filename) or die "Couldn't open $filename: $!\n"; - my $lineout = 0; - while () { - # on cygwin, line endings are screwed up, so normalise them. - s/[\x0D\x0A]+$/\n/os if ($^O eq 'msys' || $^O eq 'cygwin' || "$^O" eq "MSWin32"); - $stack->newline; - if (/^\Q$marker\E([a-z]+)\n?$/os) { # argumentless processing instruction - process($stack, $1); - } elsif (/^\Q$marker\E([a-z]+)\s(.*?)\n?$/os) { # processing instruction with arguments - process($stack, $1, $2); - } elsif (/^\Q$marker\E/os) { # comment - # ignore it - } elsif ($stack->enabled) { - next if $stack->{'dependencies'}; - - # set the current line number in JavaScript if necessary - my $linein = $stack->{'variables'}->{'LINE'}; - if (++$lineout != $linein) { - if ($filename =~ /\.js(|\.in)$/o) { - $stack->print("//\@line $linein \"$filename\"\n") - } - $lineout = $linein; - } - - # print it, including any newlines - $stack->print(filtered($stack, $_)); - } - } - close(FILE); -} - -sub process { - my($stack, $instruction, @arguments) = @_; - my $method = 'preprocessor'->can($instruction); - if (not defined($method)) { - fatal($stack, 'unknown instruction', $instruction); - } - eval { &$method($stack, @arguments) }; - if ($@) { - fatal($stack, "error evaluating $instruction:", $@); - } -} - -sub filtered { - my($stack, $text) = @_; - foreach my $filter (sort keys %{$stack->{'filters'}}) { - next unless $stack->{'filters'}->{$filter}; - my $method = 'filter'->can($filter); - if (not defined($method)) { - fatal($stack, 'unknown filter', $filter); - } - $text = eval { &$method($stack, $text) }; - if ($@) { - fatal($stack, "error using $filter:", $@); - } - } - return $text; -} - -sub fatal { - my $stack = shift; - my $filename = $stack->{'variables'}->{'FILE'}; - local $" = ' '; - print STDERR "$0:$filename:$.: @_\n"; - exit(1); -} - - -######################################################################## - -package stack; - -# condition evaluated just prior to this context was false -use constant COND_FALSE => 0; - -# condition evaluated just prior to this context was true -use constant COND_TRUE => 1; - -# some prior condition at this level already evaluated to true (or a -# parent condition evaluated to false or must be ignored), so we're -# ignoring all remaining conditions at current level (and nested -# conditions, too) -use constant COND_COMPLETED => 2; - -sub new { - return bless { - 'variables' => { - # %ENV, - 'LINE' => 0, # the line number in the source file - 'DIRECTORY' => '', # current directory - 'FILE' => '', # source filename - '1' => 1, # for convenience (the constant '1' is thus true) - }, - 'filters' => { - # filters - }, - 'values' => [], # the value of the last condition evaluated at the nth level - 'lastConditionState' => [], # whether the condition in the nth-level context was true, false, or not applicable - 'conditionState' => COND_TRUE, - 'dependencies' => 0, # whether we are showing dependencies - 'lineEndings' => "\n", # default to platform conventions - }; -} - -sub newline { - my $self = shift; - $self->{'variables'}->{'LINE'}++; -} - -sub define { - my $self = shift; - my($variable, $value) = @_; - die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; - $self->{'variables'}->{$variable} = $value; -} - -sub defined { - my $self = shift; - my($variable) = @_; - die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; - return defined($self->{'variables'}->{$variable}); -} - -sub undefine { - my $self = shift; - my($variable) = @_; - die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; - delete($self->{'variables'}->{$variable}); -} - -sub get { - my $self = shift; - my($variable, $required) = @_; - die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; - my $value = $self->{'variables'}->{$variable}; - if (defined($value)) { - return $value; - } else { - die "variable '$variable' is not defined\n" if $required; - return ''; - } -} - -sub replace { - my $self = shift; - my ($value) = @_; - - ${$self->{'values'}}[-1] = $value; - $self->{'conditionState'} = $self->{'conditionState'} != COND_FALSE - ? COND_COMPLETED - : $value ? COND_TRUE : COND_FALSE; -} - -sub push { - my $self = shift; - my($value) = @_; - - push(@{$self->{'values'}}, $value); - my $lastCondition = $self->{'conditionState'}; - push(@{$self->{'lastConditionState'}}, $lastCondition); - $self->{'conditionState'} = $lastCondition != COND_TRUE - ? COND_COMPLETED - : $value ? COND_TRUE : COND_FALSE; -} - -sub pop { - my $self = shift; - $self->{'conditionState'} = pop(@{$self->{'lastConditionState'}}); - return pop(@{$self->{'values'}}); -} - -sub enabled { - my $self = shift; - return $self->{'conditionState'} == COND_TRUE; -} - -sub disabled { - my $self = shift; - return $self->{'conditionState'} != COND_TRUE; -} - -sub filter { - my $self = shift; - my($filter, $value) = @_; - die "not a valid filter name: '$filter'\n" if $filter =~ m/\W/; - $self->{'filters'}->{$filter} = $value; -} - -sub expand { - my $self = shift; - my($line) = @_; - $line =~ s/__(\w+)__/$self->get($1)/gose; - return $line; -} - -sub print { - my $self = shift; - return if $self->{'dependencies'}; - foreach my $line (@_) { - if (chomp $line) { - CORE::print("$line$self->{'lineEndings'}"); - } else { - CORE::print($line); - } - } -} - -sub visit { - my $self = shift; - my($filename) = @_; - my $directory = $stack->{'variables'}->{'DIRECTORY'}; - $filename = File::Spec::_0_8::abs2rel(File::Spec::_0_8::rel2abs($filename, $directory)); - CORE::print("$filename\n"); -} - -######################################################################## - -package preprocessor; - -sub define { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - my $argument = shift; - for ($argument) { - /^(\w+)\s(.*)$/os && do { - return $stack->define($1, $2); - }; - /^(\w+)$/os && do { - return $stack->define($1, 1); - }; - die "invalid argument: '$_'\n"; - } -} - -sub undef { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - $stack->undefine(@_); -} - -sub ifdef { - my $stack = shift; - my $variable = shift; - my $replace = defined(shift); - die "argument expected\n" unless defined($variable); - if ($replace) { - $stack->replace($stack->defined($variable)); - } else { - $stack->push($stack->defined($variable)); - } -} - -sub ifndef { - my $stack = shift; - my $variable = shift; - my $replace = defined(shift); - die "argument expected\n" unless defined($variable); - if ($replace) { - $stack->replace(not $stack->defined($variable)); - } else { - $stack->push(not $stack->defined($variable)); - } -} - -sub if { - my $stack = shift; - die "argument expected\n" unless @_; - my $argument = shift; - my $replace = defined(shift); - for ($argument) { - /^(\w+)==(.*)$/os && do { - # equality - if ($replace) { - return $stack->replace($stack->get($1) eq $2); - } else { - return $stack->push($stack->get($1) eq $2); - } - }; - /^(\w+)!=(.*)$/os && do { - # inequality - if ($replace) { - return $stack->replace($stack->get($1) ne $2); - } else { - return $stack->push($stack->get($1) ne $2); - } - }; - /^(\w+)$/os && do { - # true value - if ($replace) { - return $stack->replace($stack->get($1)); - } else { - return $stack->push($stack->get($1)); - } - }; - /^!(\w+)$/os && do { - # false value - if ($replace) { - return $stack->replace(not $stack->get($1)); - } else { - return $stack->push(not $stack->get($1)); - } - }; - die "invalid argument: '$_'\n"; - } -} - -sub else { - my $stack = shift; - die "argument unexpected\n" if @_; - $stack->replace(1); -} - -sub elif { - my $stack = shift; - die "argument expected\n" unless @_; - &if($stack, @_, 1); -} - -sub elifdef { - my $stack = shift; - die "argument expected\n" unless @_; - &ifdef($stack, @_, 1); -} - -sub elifndef { - my $stack = shift; - die "argument expected\n" unless @_; - &ifndef($stack, @_, 1); -} - -sub endif { - my $stack = shift; - die "argument unexpected\n" if @_; - $stack->pop; -} - -sub error { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - my $line = $stack->expand(@_); - die "$line\n"; -} - -sub expand { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - my $line = $stack->expand(@_); - $stack->print("$line\n"); -} - -sub literal { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - my $line = shift; - $stack->print("$line\n"); -} - -sub include { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - my $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath(@_)); - if ($stack->{'dependencies'}) { - $stack->visit($filename); - } else { - main::include($stack, $filename); - } -} - -sub includesubst { - my ($stack, $filename) = @_; - return if $stack->disabled; - die "argument expected\n" unless $filename; - $filename =~ s/@(\w+)@/$stack->get($1, 1)/gose; - $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath($filename)); - if ($stack->{'dependencies'}) { - $stack->visit($filename); - } else { - main::include($stack, $filename); - } -} - -sub filter { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - foreach (split(/\s/os, shift)) { - $stack->filter($_, 1); - } -} - -sub unfilter { - my $stack = shift; - return if $stack->disabled; - die "argument expected\n" unless @_; - foreach (split(/\s/os, shift)) { - $stack->filter($_, 0); - } -} - - -######################################################################## - -package filter; - -sub emptyLines { - my($stack, $text) = @_; - $text = "" if $text eq "\n"; - return $text; -} - -sub spaces { - my($stack, $text) = @_; - $text =~ s/ +/ /gos; # middle spaces - $text =~ s/^ //gos; # start spaces - $text =~ s/ (\n?)$/$1/gos; # end spaces - return $text; -} - -sub slashslash { - my($stack, $text) = @_; - $text =~ s|//.*?(\n?)$|$1|gos; - return $text; -} - -sub substitution { - my($stack, $text) = @_; - $text =~ s/@(\w+)@/$stack->get($1, 1)/gose; - return $text; -} - -sub attemptSubstitution { - my($stack, $text) = @_; - $text =~ s/@(\w+)@/$stack->get($1, 0)/gose; - return $text; -} - -######################################################################## - -######################################################################## -# This code is from File::Spec::Unix 0.8. -# It is not considered a part of the preprocessor.pl source file -# This code is licensed under the same license as File::Spec itself. - -package File::Spec::_0_8; - -use Cwd; - -sub rel2abs { - my ($path, $base) = @_; - if ( ! File::Spec->file_name_is_absolute( $path ) ) { - if ( !defined( $base ) || $base eq '' ) { - $base = cwd() ; - } elsif ( ! File::Spec->file_name_is_absolute( $base ) ) { - $base = rel2abs( $base ); - } else { - $base = File::Spec->canonpath( $base ); - } - $path = File::Spec->catdir( $base, $path ); - } - return File::Spec->canonpath( $path ); -} - -sub splitdir { - return split m|/|, $_[1], -1; # Preserve trailing fields -} - -sub splitpath { - my ($path, $nofile) = @_; - - my ($volume,$directory,$file) = ('','',''); - - if ( $nofile ) { - $directory = $path; - } - else { - $path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs; - $directory = $1; - $file = $2; - } - - return ($volume,$directory,$file); -} - -sub catpath { - my ($volume,$directory,$file) = @_; - - if ( $directory ne '' && - $file ne '' && - substr( $directory, -1 ) ne '/' && - substr( $file, 0, 1 ) ne '/' - ) { - $directory .= "/$file" ; - } - else { - $directory .= $file ; - } - - return $directory ; -} - -sub abs2rel { - my($path,$base) = @_; - - # Clean up $path - if ( ! File::Spec->file_name_is_absolute( $path ) ) { - $path = rel2abs( $path ) ; - } - else { - $path = File::Spec->canonpath( $path ) ; - } - - # Figure out the effective $base and clean it up. - if ( !defined( $base ) || $base eq '' ) { - $base = cwd(); - } - elsif ( ! File::Spec->file_name_is_absolute( $base ) ) { - $base = rel2abs( $base ) ; - } - else { - $base = File::Spec->canonpath( $base ) ; - } - - # Now, remove all leading components that are the same - my @pathchunks = File::Spec::_0_8::splitdir( $path); - my @basechunks = File::Spec::_0_8::splitdir( $base); - - while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) { - shift @pathchunks ; - shift @basechunks ; - } - - $path = CORE::join( '/', @pathchunks ); - $base = CORE::join( '/', @basechunks ); - - # $base now contains the directories the resulting relative path - # must ascend out of before it can descend to $path_directory. So, - # replace all names with $parentDir - $base =~ s|[^/]+|..|g ; - - # Glue the two together, using a separator if necessary, and preventing an - # empty result. - if ( $path ne '' && $base ne '' ) { - $path = "$base/$path" ; - } else { - $path = "$base$path" ; - } - - return File::Spec->canonpath( $path ) ; -} - -# End code from File::Spec::Unix 0.8. -######################################################################## diff --git a/deps/mozjs/js/src/config/pythonpath.py b/deps/mozjs/js/src/config/pythonpath.py index cc47db42fd9..7457ec5a12c 100644 --- a/deps/mozjs/js/src/config/pythonpath.py +++ b/deps/mozjs/js/src/config/pythonpath.py @@ -2,39 +2,51 @@ Run a python script, adding extra directories to the python path. """ -import sys, os - -def usage(): - print >>sys.stderr, "pythonpath.py -I directory script.py [args...]" - sys.exit(150) -paths = [] +def main(args): + def usage(): + print >>sys.stderr, "pythonpath.py -I directory script.py [args...]" + sys.exit(150) -while True: - try: - arg = sys.argv[1] - except IndexError: - usage() + paths = [] - if arg == '-I': - del sys.argv[1] + while True: try: - path = sys.argv.pop(1) + arg = args[0] except IndexError: usage() - paths.append(path) - continue + if arg == '-I': + args.pop(0) + try: + path = args.pop(0) + except IndexError: + usage() + + paths.append(os.path.abspath(path)) + continue + + if arg.startswith('-I'): + paths.append(os.path.abspath(args.pop(0)[2:])) + continue + + break - if arg.startswith('-I'): - path = sys.argv.pop(1)[2:] - paths.append(path) - continue + script = args[0] - break + sys.path[0:0] = [os.path.abspath(os.path.dirname(script))] + paths + sys.argv = args + sys.argc = len(args) -sys.argv.pop(0) -script = sys.argv[0] + frozenglobals['__name__'] = '__main__' + frozenglobals['__file__'] = script + + execfile(script, frozenglobals) + +# Freeze scope here ... why this makes things work I have no idea ... +frozenglobals = globals() + +import sys, os -sys.path[0:0] = [os.path.dirname(script)] + paths -execfile(script, {'__name__': '__main__', '__file__': script}) +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/deps/mozjs/js/src/config/rules.mk b/deps/mozjs/js/src/config/rules.mk index 2dec19c48cc..991052d1fe9 100644 --- a/deps/mozjs/js/src/config/rules.mk +++ b/deps/mozjs/js/src/config/rules.mk @@ -1,3 +1,4 @@ +# -*- makefile -*- # vim:set ts=8 sw=8 sts=8 noet: # # ***** BEGIN LICENSE BLOCK ***** @@ -24,6 +25,7 @@ # Chase Phillips # Benjamin Smedberg # Jeff Walden +# Joey Armstrong # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), @@ -90,24 +92,6 @@ ifdef LIBXUL_SDK VPATH += $(LIBXUL_SDK)/lib endif -# EXPAND_LIBNAME - $(call EXPAND_LIBNAME,foo) -# expands to $(LIB_PREFIX)foo.$(LIB_SUFFIX) or -lfoo, depending on linker -# arguments syntax. Should only be used for system libraries - -# EXPAND_LIBNAME_PATH - $(call EXPAND_LIBNAME_PATH,foo,dir) -# expands to dir/$(LIB_PREFIX)foo.$(LIB_SUFFIX) - -# EXPAND_MOZLIBNAME - $(call EXPAND_MOZLIBNAME,foo) -# expands to $(DIST)/lib/$(LIB_PREFIX)foo.$(LIB_SUFFIX) - -ifdef GNU_CC -EXPAND_LIBNAME = $(addprefix -l,$(1)) -else -EXPAND_LIBNAME = $(foreach lib,$(1),$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) -endif -EXPAND_LIBNAME_PATH = $(foreach lib,$(1),$(2)/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) -EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) - ifdef EXTRA_DSO_LIBS EXTRA_DSO_LIBS := $(call EXPAND_MOZLIBNAME,$(EXTRA_DSO_LIBS)) endif @@ -120,21 +104,26 @@ testxpcobjdir = $(DEPTH)/_tests/xpcshell ifdef ENABLE_TESTS +# Add test directories to the regular directories list. TEST_DIRS should +# arguably have the same status as TOOL_DIRS and other *_DIRS variables. It is +# coded this way until Makefiles stop using the "ifdef ENABLE_TESTS; DIRS +=" +# convention. +# +# The current developer workflow expects tests to be updated when processing +# the default target. If we ever change this implementation, the behavior +# should be preserved or the change should be widely communicated. A +# consequence of not processing test dir targets during the default target is +# that changes to tests may not be updated and code could assume to pass +# locally against non-current test code. +DIRS += $(TEST_DIRS) + ifdef XPCSHELL_TESTS ifndef relativesrcdir $(error Must define relativesrcdir when defining XPCSHELL_TESTS.) endif -# Test file installation -ifneq (,$(filter WINNT os2-emx,$(HOST_OS_ARCH))) -# Windows and OS/2 nsinstall can't recursively copy directories, so use nsinstall.py -TEST_INSTALLER = $(PYTHON) $(topsrcdir)/config/nsinstall.py -else -TEST_INSTALLER = $(INSTALL) -endif - define _INSTALL_TESTS -$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir) +$(DIR_INSTALL) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir) endef # do not remove the blank line! @@ -142,9 +131,10 @@ SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-inter libs:: $(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS)) - $(PYTHON) $(MOZILLA_DIR)/config/buildlist.py \ - $(testxpcobjdir)/all-test-dirs.list \ - $(addprefix $(relativesrcdir)/,$(XPCSHELL_TESTS)) + $(PYTHON) $(MOZILLA_DIR)/build/xpccheck.py \ + $(topsrcdir) \ + $(topsrcdir)/testing/xpcshell/xpcshell.ini \ + $(addprefix $(MOZILLA_DIR)/$(relativesrcdir)/,$(XPCSHELL_TESTS)) testxpcsrcdir = $(topsrcdir)/testing/xpcshell @@ -155,8 +145,23 @@ xpcshell-tests: -I$(topsrcdir)/build \ $(testxpcsrcdir)/runxpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ + --build-info-json=$(DEPTH)/mozinfo.json \ + $(EXTRA_TEST_ARGS) \ + $(LIBXUL_DIST)/bin/xpcshell \ + $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) + +xpcshell-tests-remote: DM_TRANS?=adb +xpcshell-tests-remote: + $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/build \ + -I$(topsrcdir)/build/mobile \ + $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ + --symbols-path=$(DIST)/crashreporter-symbols \ + --build-info-json=$(DEPTH)/mozinfo.json \ $(EXTRA_TEST_ARGS) \ - $(DIST)/bin/xpcshell \ + --dm_trans=$(DM_TRANS) \ + --deviceIP=${TEST_DEVICE} \ + --objdir=$(DEPTH) \ $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) # Execute a single test, specified in $(SOLO_FILE), but don't automatically @@ -167,10 +172,11 @@ check-interactive: -I$(topsrcdir)/build \ $(testxpcsrcdir)/runxpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ + --build-info-json=$(DEPTH)/mozinfo.json \ --test-path=$(SOLO_FILE) \ --profile-name=$(MOZ_APP_NAME) \ --interactive \ - $(DIST)/bin/xpcshell \ + $(LIBXUL_DIST)/bin/xpcshell \ $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) # Execute a single test, specified in $(SOLO_FILE) @@ -179,13 +185,31 @@ check-one: -I$(topsrcdir)/build \ $(testxpcsrcdir)/runxpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ + --build-info-json=$(DEPTH)/mozinfo.json \ --test-path=$(SOLO_FILE) \ --profile-name=$(MOZ_APP_NAME) \ --verbose \ $(EXTRA_TEST_ARGS) \ - $(DIST)/bin/xpcshell \ + $(LIBXUL_DIST)/bin/xpcshell \ $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) +check-one-remote: DM_TRANS?=adb +check-one-remote: + $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/build \ + -I$(topsrcdir)/build/mobile \ + $(testxpcsrcdir)/remotexpcshelltests.py \ + --symbols-path=$(DIST)/crashreporter-symbols \ + --build-info-json=$(DEPTH)/mozinfo.json \ + --test-path=$(SOLO_FILE) \ + --profile-name=$(MOZ_APP_NAME) \ + --verbose \ + $(EXTRA_TEST_ARGS) \ + --dm_trans=$(DM_TRANS) \ + --deviceIP=${TEST_DEVICE} \ + --objdir=$(DEPTH) \ + --noSetup \ + $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) endif # XPCSHELL_TESTS ifdef CPP_UNIT_TESTS @@ -196,7 +220,7 @@ ifdef CPP_UNIT_TESTS CPPSRCS += $(CPP_UNIT_TESTS) SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX)) INCLUDES += -I$(DIST)/include/testing -LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) +LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS) # ...and run them the usual way check:: @@ -215,18 +239,18 @@ endif # ENABLE_TESTS # # Library rules # -# If BUILD_STATIC_LIBS or FORCE_STATIC_LIB is set, build a static library. +# If FORCE_STATIC_LIB is set, build a static library. # Otherwise, build a shared library. # ifndef LIBRARY ifdef STATIC_LIBRARY_NAME -_LIBRARY := $(LIB_PREFIX)$(STATIC_LIBRARY_NAME).$(LIB_SUFFIX) +REAL_LIBRARY := $(LIB_PREFIX)$(STATIC_LIBRARY_NAME).$(LIB_SUFFIX) # Only build actual library if it is installed in DIST/lib or SDK ifeq (,$(SDK_LIBRARY)$(DIST_INSTALL)$(NO_EXPAND_LIBS)) -LIBRARY := $(_LIBRARY).$(LIBS_DESC_SUFFIX) +LIBRARY := $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX) else -LIBRARY := $(_LIBRARY) $(_LIBRARY).$(LIBS_DESC_SUFFIX) +LIBRARY := $(REAL_LIBRARY) $(REAL_LIBRARY).$(LIBS_DESC_SUFFIX) endif endif # STATIC_LIBRARY_NAME endif # LIBRARY @@ -238,7 +262,7 @@ endif endif ifdef LIBRARY -ifneq (_1,$(FORCE_SHARED_LIB)_$(BUILD_STATIC_LIBS)) +ifdef FORCE_SHARED_LIB ifdef MKSHLIB ifdef LIB_IS_C_ONLY @@ -265,24 +289,12 @@ ifeq ($(OS_ARCH),OS2) DEF_FILE := $(SHARED_LIBRARY:.dll=.def) endif -ifdef MOZ_ENABLE_LIBXUL EMBED_MANIFEST_AT=2 -endif endif # MKSHLIB -endif # FORCE_SHARED_LIB && !BUILD_STATIC_LIBS +endif # FORCE_SHARED_LIB endif # LIBRARY -ifeq (,$(BUILD_STATIC_LIBS)$(FORCE_STATIC_LIB)) -LIBRARY := $(NULL) -endif - -ifeq (_1,$(FORCE_SHARED_LIB)_$(BUILD_STATIC_LIBS)) -SHARED_LIBRARY := $(NULL) -DEF_FILE := $(NULL) -IMPORT_LIBRARY := $(NULL) -endif - ifdef FORCE_STATIC_LIB ifndef FORCE_SHARED_LIB SHARED_LIBRARY := $(NULL) @@ -293,7 +305,7 @@ endif ifdef FORCE_SHARED_LIB ifndef FORCE_STATIC_LIB -LIBRARY := $(NULL) +LIBRARY := $(NULL) endif endif @@ -343,11 +355,15 @@ ifdef MAPFILE OS_LDFLAGS += -MAP:$(MAPFILE) endif +else #!GNU_CC + +ifdef DEFFILE +OS_LDFLAGS += $(call normalizepath,$(DEFFILE)) +EXTRA_DEPS += $(DEFFILE) +endif + endif # !GNU_CC -ifdef ENABLE_CXX_EXCEPTIONS -CXXFLAGS += $(MOZ_EXCEPTIONS_FLAGS_ON) -DMOZ_CPP_EXCEPTIONS=1 -endif # ENABLE_CXX_EXCEPTIONS endif # WINNT ifeq ($(SOLARIS_SUNPRO_CXX),1) @@ -398,7 +414,7 @@ ifndef MOZ_AUTO_DEPS ifneq (,$(OBJS)$(XPIDLSRCS)$(SIMPLE_PROGRAMS)) MDDEPFILES = $(addprefix $(MDDEPDIR)/,$(OBJS:.$(OBJ_SUFFIX)=.pp)) ifndef NO_GEN_XPT -MDDEPFILES += $(addprefix $(MDDEPDIR)/,$(XPIDLSRCS:.idl=.xpt)) +MDDEPFILES += $(addprefix $(MDDEPDIR)/,$(XPIDLSRCS:.idl=.h.pp) $(XPIDLSRCS:.idl=.xpt.pp)) endif endif endif @@ -406,7 +422,7 @@ endif ALL_TRASH = \ $(GARBAGE) $(TARGETS) $(OBJS) $(PROGOBJS) LOGS TAGS a.out \ $(filter-out $(ASFILES),$(OBJS:.$(OBJ_SUFFIX)=.s)) $(OBJS:.$(OBJ_SUFFIX)=.ii) \ - $(OBJS:.$(OBJ_SUFFIX)=.i) \ + $(OBJS:.$(OBJ_SUFFIX)=.i) $(OBJS:.$(OBJ_SUFFIX)=.i_o) \ $(HOST_PROGOBJS) $(HOST_OBJS) $(IMPORT_LIBRARY) $(DEF_FILE)\ $(EXE_DEF_FILE) so_locations _gen _stubs $(wildcard *.res) $(wildcard *.RES) \ $(wildcard *.pdb) $(CODFILE) $(MAPFILE) $(IMPORT_LIBRARY) \ @@ -420,6 +436,7 @@ ALL_TRASH_DIRS = \ ifdef QTDIR GARBAGE += $(MOCSRCS) +GARBAGE += $(RCCSRCS) endif ifdef SIMPLE_PROGRAMS @@ -449,7 +466,7 @@ endif define SUBMAKE # $(call SUBMAKE,target,directory) +@$(UPDATE_TITLE) -+@$(MAKE) $(if $(2),-C $(2)) $(1) ++$(MAKE) $(if $(2),-C $(2)) $(1) endef # The extra line is important here! don't delete it @@ -474,15 +491,6 @@ LOOP_OVER_TOOL_DIRS = \ $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,$@,$(dir))) endif -ifdef PARALLEL_DIRS -# create a bunch of fake targets for order-only processing -PARALLEL_DIRS_export = $(addsuffix _export,$(PARALLEL_DIRS)) -PARALLEL_DIRS_libs = $(addsuffix _libs,$(PARALLEL_DIRS)) -PARALLEL_DIRS_tools = $(addsuffix _tools,$(PARALLEL_DIRS)) - -.PHONY: $(PARALLEL_DIRS_export) $(PARALLEL_DIRS_libs) $(PARALLEL_DIRS_tools) -endif - # # Now we can differentiate between objects used to build a library, and # objects used to build an executable in the same directory. @@ -511,7 +519,7 @@ TAG_PROGRAM = xargs etags -a # # Turn on C++ linking if we have any .cpp or .mm files -# (moved this from config.mk so that config.mk can be included +# (moved this from config.mk so that config.mk can be included # before the CPPSRCS are defined) # ifneq ($(CPPSRCS)$(CMMSRCS),) @@ -522,7 +530,7 @@ HOST_CPP_PROG_LINK = 1 endif # -# This will strip out symbols that the component should not be +# This will strip out symbols that the component should not be # exporting from the .dynsym section. # ifdef IS_COMPONENT @@ -530,7 +538,7 @@ EXTRA_DSO_LDOPTS += $(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS) endif # IS_COMPONENT # -# Enforce the requirement that MODULE_NAME must be set +# Enforce the requirement that MODULE_NAME must be set # for components in static builds # ifdef IS_COMPONENT @@ -620,18 +628,18 @@ ifeq ($(OS_ARCH),OSF1) ifdef IS_COMPONENT ifeq ($(GNU_CC)$(GNU_CXX),) EXTRA_DSO_LDOPTS += -B symbolic -endif -endif +endif +endif endif # # Linux: add -Bsymbolic flag for components -# +# ifeq ($(OS_ARCH),Linux) ifdef IS_COMPONENT EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic endif -endif +endif # # GNU doesn't have path length limitation @@ -666,6 +674,12 @@ else OUTOPTION = -o # eol endif # WINNT && !GNU_CC +ifneq (,$(filter ml%,$(AS))) +ASOUTOPTION = -Fo# eol +else +ASOUTOPTION = -o # eol +endif + ifeq (,$(CROSS_COMPILE)) HOST_OUTOPTION = $(OUTOPTION) else @@ -696,7 +710,7 @@ endif $(MAKE) tools # Do depend as well -alldep:: +alldep:: $(MAKE) export $(MAKE) depend $(MAKE) libs @@ -715,21 +729,6 @@ endif MAKE_TIER_SUBMAKEFILES = +$(if $(tier_$*_dirs),$(MAKE) $(addsuffix /Makefile,$(tier_$*_dirs))) -export_tier_%: - @$(ECHO) "$@" - @$(MAKE_TIER_SUBMAKEFILES) - $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,export,$(dir))) - -libs_tier_%: - @$(ECHO) "$@" - @$(MAKE_TIER_SUBMAKEFILES) - $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,libs,$(dir))) - -tools_tier_%: - @$(ECHO) "$@" - @$(MAKE_TIER_SUBMAKEFILES) - $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir))) - $(foreach tier,$(TIERS),tier_$(tier)):: @$(ECHO) "$@: $($@_staticdirs) $($@_dirs)" $(foreach dir,$($@_staticdirs),$(call SUBMAKE,,$(dir))) @@ -753,29 +752,8 @@ ifneq (,$(DIRS)$(TOOL_DIRS)$(PARALLEL_DIRS)) $(LOOP_OVER_TOOL_DIRS) endif -ifdef PARALLEL_DIRS -export:: $(PARALLEL_DIRS_export) - -$(PARALLEL_DIRS_export): %_export: %/Makefile - +@$(call SUBMAKE,export,$*) -endif - -export:: $(SUBMAKEFILES) $(MAKE_DIRS) $(if $(XPIDLSRCS),$(IDL_DIR)) - $(LOOP_OVER_DIRS) - $(LOOP_OVER_TOOL_DIRS) - -ifdef PARALLEL_DIRS -tools:: $(PARALLEL_DIRS_tools) - -$(PARALLEL_DIRS_tools): %_tools: %/Makefile - +@$(call SUBMAKE,tools,$*) -endif - -tools:: $(SUBMAKEFILES) $(MAKE_DIRS) - $(LOOP_OVER_DIRS) -ifneq (,$(strip $(TOOL_DIRS))) - $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,libs,$(dir))) -endif +include $(topsrcdir)/config/makefiles/target_export.mk +include $(topsrcdir)/config/makefiles/target_tools.mk # # Rule to create list of libraries for final link @@ -784,12 +762,6 @@ export:: ifdef LIBRARY_NAME ifdef EXPORT_LIBRARY ifdef IS_COMPONENT -ifdef BUILD_STATIC_LIBS - @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_LINK_COMPS) $(STATIC_LIBRARY_NAME) -ifdef MODULE_NAME - @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_LINK_COMP_NAMES) $(MODULE_NAME) -endif -endif # BUILD_STATIC_LIBS else # !IS_COMPONENT $(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_LINK_LIBS) $(STATIC_LIBRARY_NAME) endif # IS_COMPONENT @@ -802,7 +774,7 @@ endif # Create dependencies on static (and shared EXTRA_DSO_LIBS) libraries DO_EXPAND_LIBS = $(foreach f,$(1),$(if $(filter %.$(LIB_SUFFIX),$(f)),$(if $(wildcard $(f).$(LIBS_DESC_SUFFIX)),$(f).$(LIBS_DESC_SUFFIX),$(if $(wildcard $(f)),$(f))))) -LIBS_DEPS = $(call DO_EXPAND_LIBS,$(filter %.$(LIB_SUFFIX),$(LIBS))) +LIBS_DEPS = $(call DO_EXPAND_LIBS,$(filter %.$(LIB_SUFFIX),$(LIBS) $(if $(PROGRAM)$(SIMPLE_PROGRAMS),$(MOZ_GLUE_PROGRAM_LDFLAGS)))) SHARED_LIBRARY_LIBS_DEPS = $(call DO_EXPAND_LIBS,$(SHARED_LIBRARY_LIBS)) HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(HOST_LIBS)) DSO_LDOPTS_DEPS = $(call DO_EXPAND_LIBS,$(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFIX), $(EXTRA_DSO_LDOPTS))) @@ -811,84 +783,9 @@ DSO_LDOPTS_DEPS = $(call DO_EXPAND_LIBS,$(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFI GLOBAL_DEPS += Makefile Makefile.in $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk ############################################## -ifdef PARALLEL_DIRS -libs:: $(PARALLEL_DIRS_libs) - -$(PARALLEL_DIRS_libs): %_libs: %/Makefile - +@$(call SUBMAKE,libs,$*) -endif - -ifdef EXPORT_LIBRARY -ifeq ($(EXPORT_LIBRARY),1) -ifdef IS_COMPONENT -EXPORT_LIBRARY = $(DEPTH)/staticlib/components -else -EXPORT_LIBRARY = $(DEPTH)/staticlib -endif -else -# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there -GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib)) -endif -endif # EXPORT_LIBRARY - -libs:: $(SUBMAKEFILES) $(MAKE_DIRS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) $(HOST_PROGRAM) $(PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(SIMPLE_PROGRAMS) $(JAVA_LIBRARY) -ifndef NO_DIST_INSTALL -ifdef LIBRARY -ifdef EXPORT_LIBRARY # Stage libs that will be linked into a static build - $(INSTALL) $(IFLAGS1) $(LIBRARY) $(EXPORT_LIBRARY) -endif # EXPORT_LIBRARY -ifdef DIST_INSTALL -ifdef IS_COMPONENT - $(error Shipping static component libs makes no sense.) -else - $(INSTALL) $(IFLAGS1) $(LIBRARY) $(DIST)/lib -endif -endif # DIST_INSTALL -endif # LIBRARY -ifdef SHARED_LIBRARY -ifdef IS_COMPONENT - $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components - $(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY) -ifndef NO_COMPONENTS_MANIFEST - @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest "manifest components/components.manifest" - @$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/components/components.manifest "binary-component $(SHARED_LIBRARY)" -endif -else # ! IS_COMPONENT -ifneq (,$(filter OS2 WINNT,$(OS_ARCH))) - $(INSTALL) $(IFLAGS2) $(IMPORT_LIBRARY) $(DIST)/lib -else - $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(DIST)/lib -endif - $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET) -endif # IS_COMPONENT -endif # SHARED_LIBRARY -ifdef PROGRAM - $(INSTALL) $(IFLAGS2) $(PROGRAM) $(FINAL_TARGET) -endif -ifdef SIMPLE_PROGRAMS - $(INSTALL) $(IFLAGS2) $(SIMPLE_PROGRAMS) $(FINAL_TARGET) -endif -ifdef HOST_PROGRAM - $(INSTALL) $(IFLAGS2) $(HOST_PROGRAM) $(DIST)/host/bin -endif -ifdef HOST_SIMPLE_PROGRAMS - $(INSTALL) $(IFLAGS2) $(HOST_SIMPLE_PROGRAMS) $(DIST)/host/bin -endif -ifdef HOST_LIBRARY - $(INSTALL) $(IFLAGS1) $(HOST_LIBRARY) $(DIST)/host/lib -endif -ifdef JAVA_LIBRARY -ifdef IS_COMPONENT - $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)/components -else - $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET) -endif -endif # JAVA_LIBRARY -endif # !NO_DIST_INSTALL - $(LOOP_OVER_DIRS) +include $(topsrcdir)/config/makefiles/target_libs.mk ############################################## - ifndef NO_PROFILE_GUIDED_OPTIMIZE ifdef MOZ_PROFILE_USE ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_) @@ -911,19 +808,42 @@ ifneq (,$(SHARED_LIBRARY)$(PROGRAM)) export:: ifdef PROGRAM $(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \ - $(PROGRAM:$(BIN_SUFFIX)=) $(DIST)/$(MOZ_APP_NAME) + $(PROGRAM:$(BIN_SUFFIX)=) $(DIST)/bin endif ifdef SHARED_LIBRARY $(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \ - $(SHARED_LIBRARY_NAME) $(DIST)/$(MOZ_APP_NAME) + $(SHARED_LIBRARY_NAME) $(DIST)/bin endif endif # SHARED_LIBRARY || PROGRAM endif # WINNT_ -endif # MOZ_PROFILE_GENERATE || MOZ_PROFILE_USE +endif # MOZ_PROFILE_USE +ifdef MOZ_PROFILE_GENERATE +# Clean up profiling data during PROFILE_GENERATE phase +export:: +ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_) + $(foreach pgd,$(wildcard *.pgd),pgomgr -clear $(pgd);) +else +ifdef GNU_CC + -$(RM) *.gcda +endif +endif +endif + +ifneq (,$(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE)) +ifdef GNU_CC +# Force rebuilding libraries and programs in both passes because each +# pass uses different object files. +$(PROGRAM) $(SHARED_LIBRARY) $(LIBRARY): FORCE +endif +endif + endif # NO_PROFILE_GUIDED_OPTIMIZE ############################################## +stdc++compat.$(OBJ_SUFFIX): CXXFLAGS+=-DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION) +host_stdc++compat.$(OBJ_SUFFIX): CXXFLAGS+=-DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_HOST_VERSION) + checkout: $(MAKE) -C $(topsrcdir) -f client.mk checkout @@ -934,7 +854,7 @@ clean clobber realclean clobber_all:: $(SUBMAKEFILES) distclean:: $(SUBMAKEFILES) $(foreach dir,$(PARALLEL_DIRS) $(DIRS) $(STATIC_DIRS) $(TOOL_DIRS),-$(call SUBMAKE,$@,$(dir))) - -$(RM) -r $(ALL_TRASH_DIRS) + -$(RM) -r $(ALL_TRASH_DIRS) -$(RM) $(ALL_TRASH) \ Makefile .HSancillary \ $(wildcard *.$(OBJ_SUFFIX)) $(wildcard *.ho) $(wildcard host_*.o*) \ @@ -955,7 +875,7 @@ alltags: $(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS) @$(RM) $@.manifest ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) - $(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) + $(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) ifdef MSMANIFEST_TOOL @if test -f $@.manifest; then \ if test -f "$(srcdir)/$@.manifest"; then \ @@ -977,10 +897,10 @@ ifdef MOZ_PROFILE_GENERATE endif else # !WINNT || GNU_CC ifeq ($(CPP_PROG_LINK),1) - $(EXPAND_CCC) -o $@ $(CXXFLAGS) $(WRAP_MALLOC_CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(WRAP_MALLOC_LIB) $(EXE_DEF_FILE) + $(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) @$(call CHECK_STDCXX,$@) else # ! CPP_PROG_LINK - $(EXPAND_CC) -o $@ $(CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) + $(EXPAND_CC) -o $@ $(CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(WRAP_LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) endif # CPP_PROG_LINK endif # WINNT && !GNU_CC @@ -1026,7 +946,7 @@ endif # $(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) - $(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) + $(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) ifdef MSMANIFEST_TOOL @if test -f $@.manifest; then \ mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ @@ -1035,10 +955,10 @@ ifdef MSMANIFEST_TOOL endif # MSVC with manifest tool else ifeq ($(CPP_PROG_LINK),1) - $(EXPAND_CCC) $(WRAP_MALLOC_CFLAGS) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(WRAP_MALLOC_LIB) $(BIN_FLAGS) + $(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(WRAP_LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) @$(call CHECK_STDCXX,$@) else - $(EXPAND_CC) $(WRAP_MALLOC_CFLAGS) $(CFLAGS) $(OUTOPTION)$@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(WRAP_MALLOC_LIB) $(BIN_FLAGS) + $(EXPAND_CC) $(CFLAGS) $(OUTOPTION)$@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(WRAP_LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) endif # CPP_PROG_LINK endif # WINNT && !GNU_CC @@ -1062,7 +982,7 @@ endif # # Purify target. Solaris/sparc only to start. -# Purify does not recognize "egcs" or "c++" so we go with +# Purify does not recognize "egcs" or "c++" so we go with # "gcc" and "g++" for now. # pure: $(PROGRAM) @@ -1087,6 +1007,7 @@ endif ifdef DTRACE_PROBE_OBJ EXTRA_DEPS += $(DTRACE_PROBE_OBJ) +OBJS += $(DTRACE_PROBE_OBJ) endif $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) @@ -1095,6 +1016,8 @@ $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEP $(RANLIB) $@ $(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) +# When we only build a library descriptor, blow out any existing library + $(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY) $(EXPORT_LIBRARY:%=%/$(REAL_LIBRARY))) $(EXPAND_LIBS_GEN) $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS) > $@ ifeq ($(OS_ARCH),WINNT) @@ -1127,8 +1050,9 @@ ifdef HAVE_DTRACE ifndef XP_MACOSX ifdef DTRACE_PROBE_OBJ ifndef DTRACE_LIB_DEPENDENT -$(DTRACE_PROBE_OBJ): $(OBJS) - dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS) +NON_DTRACE_OBJS := $(filter-out $(DTRACE_PROBE_OBJ),$(OBJS)) +$(DTRACE_PROBE_OBJ): $(NON_DTRACE_OBJS) + dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(NON_DTRACE_OBJS) endif endif endif @@ -1147,10 +1071,10 @@ ifdef DTRACE_LIB_DEPENDENT ifndef XP_MACOSX dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(shell $(EXPAND_LIBS) $(MOZILLA_PROBE_LIBS)) endif - $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) + $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(MOZ_GLUE_LDFLAGS) $(WRAP_LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) @$(RM) $(DTRACE_PROBE_OBJ) else # ! DTRACE_LIB_DEPENDENT - $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) + $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(MOZ_GLUE_LDFLAGS) $(WRAP_LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) endif # DTRACE_LIB_DEPENDENT @$(call CHECK_STDCXX,$@) @@ -1246,20 +1170,22 @@ host_%.$(OBJ_SUFFIX): %.mm $(GLOBAL_DEPS) $(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $(_VPATH_SRCS) # DEFINES and ACDEFINES are needed here to enable conditional compilation of Q_OBJECTs: -# 'moc' only knows about #defines it gets on the command line (-D...), not in +# 'moc' only knows about #defines it gets on the command line (-D...), not in # included headers like mozilla-config.h moc_%.cpp: %.h $(GLOBAL_DEPS) - $(MOC) $(DEFINES) $(ACDEFINES) $< $(OUTOPTION)$@ + $(ELOG) $(MOC) $(DEFINES) $(ACDEFINES) $< $(OUTOPTION)$@ moc_%.cc: %.cc $(GLOBAL_DEPS) - $(REPORT_BUILD) $(ELOG) $(MOC) $(DEFINES) $(ACDEFINES) $(_VPATH_SRCS:.cc=.h) $(OUTOPTION)$@ +qrc_%.cpp: %.qrc $(GLOBAL_DEPS) + $(ELOG) $(RCC) -name $* $< $(OUTOPTION)$@ + ifdef ASFILES # The AS_DASH_C_FLAG is needed cause not all assemblers (Solaris) accept # a '-c' flag. %.$(OBJ_SUFFIX): %.$(ASM_SUFFIX) $(GLOBAL_DEPS) - $(AS) -o $@ $(ASFLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS) + $(AS) $(ASOUTOPTION)$@ $(ASFLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS) endif %.$(OBJ_SUFFIX): %.S $(GLOBAL_DEPS) @@ -1427,6 +1353,9 @@ $(topsrcdir)/configure: $(topsrcdir)/configure.in (cd $(topsrcdir) && $(AUTOCONF)) && (cd $(DEPTH) && ./config.status --recheck) endif +$(DEPTH)/config/autoconf.mk: $(topsrcdir)/config/autoconf.mk.in + cd $(DEPTH) && CONFIG_HEADERS= CONFIG_FILES=config/autoconf.mk ./config.status + ############################################################################### # Bunch of things that extend the 'export' rule (in order): ############################################################################### @@ -1480,9 +1409,10 @@ ifndef NO_DIST_INSTALL $(FINAL_TARGET)/$(PREF_DIR): $(NSINSTALL) -D $@ -libs:: $(FINAL_TARGET)/$(PREF_DIR) $(PREF_JS_EXPORTS) +libs:: $(FINAL_TARGET)/$(PREF_DIR) +libs:: $(PREF_JS_EXPORTS) $(EXIT_ON_ERROR) \ - for i in $(PREF_JS_EXPORTS); do \ + for i in $^; do \ dest=$(FINAL_TARGET)/$(PREF_DIR)/`basename $$i`; \ $(RM) -f $$dest; \ $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \ @@ -1502,7 +1432,7 @@ export:: $(AUTOCFG_JS_EXPORTS) $(FINAL_TARGET)/defaults/autoconfig $(INSTALL) $(IFLAGS1) $^ endif -endif +endif ################################################################################ # Export the elements of $(XPIDLSRCS) # generating .h and .xpt files and moving them to the appropriate places. @@ -1524,11 +1454,8 @@ export:: FORCE @echo; sleep 2; false endif -$(IDL_DIR):: - $(NSINSTALL) -D $@ - # generate .h files from into $(XPIDL_GEN_DIR), then export to $(DIST)/include; -# warn against overriding existing .h file. +# warn against overriding existing .h file. $(XPIDL_GEN_DIR)/.done: $(MKDIR) -p $(XPIDL_GEN_DIR) @$(TOUCH) $@ @@ -1536,18 +1463,31 @@ $(XPIDL_GEN_DIR)/.done: # don't depend on $(XPIDL_GEN_DIR), because the modification date changes # with any addition to the directory, regenerating all .h files -> everything. -$(XPIDL_GEN_DIR)/%.h: %.idl $(XPIDL_COMPILE) $(XPIDL_GEN_DIR)/.done +XPIDL_DEPS = \ + $(topsrcdir)/xpcom/idl-parser/header.py \ + $(topsrcdir)/xpcom/idl-parser/typelib.py \ + $(topsrcdir)/xpcom/idl-parser/xpidl.py \ + $(NULL) + +$(XPIDL_GEN_DIR)/%.h: %.idl $(XPIDL_DEPS) $(XPIDL_GEN_DIR)/.done $(REPORT_BUILD) - $(ELOG) $(XPIDL_COMPILE) -m header -w $(XPIDL_FLAGS) -o $(XPIDL_GEN_DIR)/$* $(_VPATH_SRCS) + $(PYTHON_PATH) \ + -I$(topsrcdir)/other-licenses/ply \ + -I$(topsrcdir)/xpcom/idl-parser \ + $(topsrcdir)/xpcom/idl-parser/header.py --cachedir=$(DEPTH)/xpcom/idl-parser $(XPIDL_FLAGS) $(_VPATH_SRCS) -d $(MDDEPDIR)/$(@F).pp -o $@ @if test -n "$(findstring $*.h, $(EXPORTS))"; \ then echo "*** WARNING: file $*.h generated from $*.idl overrides $(srcdir)/$*.h"; else true; fi ifndef NO_GEN_XPT # generate intermediate .xpt files into $(XPIDL_GEN_DIR), then link # into $(XPIDL_MODULE).xpt and export it to $(FINAL_TARGET)/components. -$(XPIDL_GEN_DIR)/%.xpt: %.idl $(XPIDL_COMPILE) $(XPIDL_GEN_DIR)/.done +$(XPIDL_GEN_DIR)/%.xpt: %.idl $(XPIDL_DEPS) $(XPIDL_GEN_DIR)/.done $(REPORT_BUILD) - $(ELOG) $(XPIDL_COMPILE) -m typelib -w $(XPIDL_FLAGS) -e $@ -d $(MDDEPDIR)/$*.pp $(_VPATH_SRCS) + $(PYTHON_PATH) \ + -I$(topsrcdir)/other-licenses/ply \ + -I$(topsrcdir)/xpcom/idl-parser \ + -I$(topsrcdir)/xpcom/typelib/xpt/tools \ + $(topsrcdir)/xpcom/idl-parser/typelib.py --cachedir=$(DEPTH)/xpcom/idl-parser $(XPIDL_FLAGS) $(_VPATH_SRCS) -d $(MDDEPDIR)/$(@F).pp -o $@ # no need to link together if XPIDLSRCS contains only XPIDL_MODULE ifneq ($(XPIDL_MODULE).idl,$(strip $(XPIDLSRCS))) @@ -1577,21 +1517,15 @@ export:: $(XPIDLSRCS) $(IDL_DIR) $(INSTALL) $(IFLAGS1) $^ export:: $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.h, $(XPIDLSRCS)) $(DIST)/include - $(INSTALL) $(IFLAGS1) $^ + $(INSTALL) $(IFLAGS1) $^ endif # NO_DIST_INSTALL endif # XPIDLSRCS -# # General rules for exporting idl files. -# -# WORK-AROUND ONLY, for mozilla/tools/module-deps/bootstrap.pl build. -# Bug to fix idl dependency problems w/o this extra build pass is -# http://bugzilla.mozilla.org/show_bug.cgi?id=145777 -# -$(IDL_DIR):: +$(IDL_DIR): $(NSINSTALL) -D $@ export-idl:: $(SUBMAKEFILES) $(MAKE_DIRS) @@ -1707,9 +1641,9 @@ chrome:: $(FINAL_TARGET)/chrome: $(NSINSTALL) -D $@ -libs realchrome:: $(CHROME_DEPS) $(FINAL_TARGET)/chrome ifneq (,$(wildcard $(JAR_MANIFEST))) ifndef NO_DIST_INSTALL +libs realchrome:: $(CHROME_DEPS) $(FINAL_TARGET)/chrome $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ $(QUIET) -j $(FINAL_TARGET)/chrome \ $(MAKE_JARS_FLAGS) $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \ @@ -1721,26 +1655,27 @@ ifneq ($(DIST_FILES),) $(DIST)/bin: $(NSINSTALL) -D $@ -libs:: $(DIST_FILES) $(DIST)/bin +libs:: $(DIST)/bin +libs:: $(DIST_FILES) @$(EXIT_ON_ERROR) \ - for f in $(DIST_FILES); do \ + for f in $^; do \ dest=$(FINAL_TARGET)/`basename $$f`; \ $(RM) -f $$dest; \ $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \ - $(srcdir)/$$f > $$dest; \ + $$f > $$dest; \ done endif ifneq ($(DIST_CHROME_FILES),) libs:: $(DIST_CHROME_FILES) @$(EXIT_ON_ERROR) \ - for f in $(DIST_CHROME_FILES); do \ + for f in $^; do \ dest=$(FINAL_TARGET)/chrome/`basename $$f`; \ $(RM) -f $$dest; \ $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \ - $(srcdir)/$$f > $$dest; \ + $$f > $$dest; \ done endif @@ -1947,7 +1882,7 @@ FORCE: .DELETE_ON_ERROR: # Properly set LIBPATTERNS for the platform -.LIBPATTERNS = $(if $(IMPORT_LIB_SUFFIX),$(LIB_PREFIX)%.$(IMPORT_LIB_SUFFIX)) $(LIB_PREFIX)%.$(LIB_SUFFIX) $(DLL_PREFIX)%$(DLL_SUFFIX) +.LIBPATTERNS = $(if $(IMPORT_LIB_SUFFIX),$(LIB_PREFIX)%.$(IMPORT_LIB_SUFFIX)) $(LIB_PREFIX)%.$(LIB_SUFFIX) $(DLL_PREFIX)%$(DLL_SUFFIX) tags: TAGS @@ -2018,6 +1953,7 @@ showbuild: @echo "MKSHLIB = $(MKSHLIB)" @echo "MKCSHLIB = $(MKCSHLIB)" @echo "RC = $(RC)" + @echo "MC = $(MC)" @echo "CFLAGS = $(CFLAGS)" @echo "OS_CFLAGS = $(OS_CFLAGS)" @echo "COMPILE_CFLAGS = $(COMPILE_CFLAGS)" @@ -2057,7 +1993,6 @@ showhost: @echo "HOST_LIBRARY = $(HOST_LIBRARY)" showbuildmods:: - @echo "Build Modules = $(BUILD_MODULES)" @echo "Module dirs = $(BUILD_MODULE_DIRS)" documentation: diff --git a/deps/mozjs/js/src/config/system-headers b/deps/mozjs/js/src/config/system-headers index 12a88bf75aa..5bddb364965 100644 --- a/deps/mozjs/js/src/config/system-headers +++ b/deps/mozjs/js/src/config/system-headers @@ -23,6 +23,7 @@ alloc.h alsa/asoundlib.h alsa/pcm.h alsa/mixer.h +android/log.h ansi_parms.h a.out.h app/Cursor.h @@ -40,7 +41,6 @@ asm/signal.h ASRegistry.h assert.h atk/atk.h -atlbase.h atlcom.h atlconv.h atlctl.cpp @@ -70,9 +70,6 @@ bstring.h builtin.h Button.h byteswap.h -#if MOZ_ENABLE_LIBXUL!=1 -#define WRAP_CAIRO_HEADERS -#endif #if MOZ_TREE_CAIRO!=1 #define WRAP_CAIRO_HEADERS #endif @@ -116,11 +113,6 @@ fusion/protocol.h fusion/fusion.h fusion/arena.h fusion/object.h -directfbgl.h -directfb_version.h -directfb.h -directfb_util.h -directfb_keynames.h dgiff.h direct/util.h direct/memcpy.h @@ -207,7 +199,6 @@ ctime ctype.h curl/curl.h curl/easy.h -curl/types.h curses.h cxxabi.h DateTimeUtils.h @@ -277,6 +268,11 @@ freetype/ftoutln.h freetype/ttnameid.h freetype/tttables.h freetype/t1tables.h +freetype/ftlcdfil.h +freetype/ftsizes.h +freetype/ftadvanc.h +freetype/ftbitmap.h +freetype/ftxf86.h fribidi/fribidi.h FSp_fopen.h fstream @@ -313,7 +309,6 @@ hlink.h ia64/sys/inline.h Icons.h iconv.h -IDL.h ieeefp.h ifaddrs.h image.h @@ -339,6 +334,7 @@ jar.h JavaControl.h JavaEmbedding/JavaControl.h JavaVM/jni.h +jemalloc.h JManager.h JNIEnvTests.h jni.h @@ -409,7 +405,6 @@ libgnomevfs/gnome-vfs-mime-handlers.h libgnomevfs/gnome-vfs-mime-utils.h libgnomevfs/gnome-vfs-ops.h libgnomevfs/gnome-vfs-standard-callbacks.h -libIDL/IDL.h lib$routines.h libnotify/notify.h limits @@ -536,6 +531,7 @@ nl_types.h NodeInfo.h nss.h nssilock.h +nsswitch.h objbase.h objidl.h Objsafe.h @@ -569,14 +565,6 @@ pcfs/pc_dir.h Pgenerr.h PGenErr.h Ph.h -photon/Pg.h -photon/PhProto.h -photon/PhRender.h -photon/PpProto.h -photon/PtProgress.h -photon/PtServer.h -photon/PtWebClient.h -photon/PxImage.h pk11func.h pk11pub.h pkcs11t.h @@ -741,6 +729,7 @@ sys/pstat.h sys/ptrace.h sys/queue.h sys/quota.h +sys/reboot.h sys/reg.h sys/regset.h sys/resource.h @@ -763,6 +752,7 @@ sys/sysctl.h sys/sysinfo.h sys/sysmp.h sys/syssgi.h +sys/system_properties.h sys/systeminfo.h sys/timeb.h sys/time.h @@ -994,15 +984,6 @@ png.h #if MOZ_NATIVE_ZLIB==1 zlib.h #endif -#if MOZ_ENABLE_LIBXUL!=1 -#if BUILD_STATIC_LIBS!=1 -#define WRAP_LCMS_HEADERS -#endif -#endif -#ifdef WRAP_LCMS_HEADERS -icc34.h -lcms.h -#endif #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION libsn/sn.h libsn/sn-common.h @@ -1041,6 +1022,14 @@ proxy.h #if MOZ_PLATFORM_MAEMO==6 contentaction/contentaction.h #endif +#ifdef MOZ_ENABLE_CONTENTMANAGER +SelectSingleContentItemPage.h +SelectMultipleContentItemsPage.h +QtSparql/qsparqlconnection.h +QtSparql/qsparqlquery.h +QtSparql/qsparqlresult.h +#endif + #if MOZ_TREE_PIXMAN!=1 pixman.h #endif @@ -1051,3 +1040,15 @@ shareuiinterface.h vpx/vpx_decoder.h vpx/vp8dx.h #endif +#ifdef XP_WIN +vpx/vpx_decoder.h +vpx/vp8dx.h +sydneyaudio/sydney_audio.h +vorbis/codec.h +theora/theoradec.h +tremor/ivorbiscodec.h +ogg/ogg.h +ogg/os_types.h +nestegg/nestegg.h +cubeb/cubeb.h +#endif diff --git a/deps/mozjs/js/src/configure.in b/deps/mozjs/js/src/configure.in index e01c0243ca7..9da84897ffa 100644 --- a/deps/mozjs/js/src/configure.in +++ b/deps/mozjs/js/src/configure.in @@ -112,7 +112,7 @@ dnl ======================================================== PERL_VERSION=5.006 PYTHON_VERSION=2.5 WINDRES_VERSION=2.14.90 -W32API_VERSION=3.8 +W32API_VERSION=3.14 MSMANIFEST_TOOL= @@ -173,7 +173,7 @@ MOZ_ARG_WITH_STRING(dist-dir, TOP_DIST=dist) AC_SUBST(TOP_DIST) -dnl Default to MSVC for win32 and gcc-4.2 for darwin +dnl Default to MSVC for win32 dnl ============================================================== if test -z "$CROSS_COMPILE"; then case "$target" in @@ -196,8 +196,9 @@ case "$target" in if test -z "$MIDL"; then MIDL=midl; fi ;; *-darwin*) - if test -z "$CC"; then CC=gcc-4.2; fi - if test -z "$CXX"; then CXX=g++-4.2; fi + # prefer gcc-4.2 to default cc on older xcode + MOZ_PATH_PROGS(CC, $CC gcc-4.2 gcc) + MOZ_PATH_PROGS(CXX, $CXX g++-4.2 g++) ;; esac fi @@ -255,7 +256,88 @@ MOZ_ARG_WITH_STRING(android-sdk, location where the Android SDK can be found (base directory, e.g. .../android/platforms/android-6)], android_sdk=$withval) -if test "$target" = "arm-android-eabi" ; then +MOZ_ARG_WITH_STRING(android-platform, +[ --with-android-platform=DIR + location of platform dir, default NDK/build/platforms/android-5/arch-arm], + android_platform=$withval) + +MOZ_ARG_ENABLE_BOOL(android-libstdcxx, +[ --enable-android-libstdcxx + use GNU libstdc++ instead of STLPort for NDK >= 5], + MOZ_ANDROID_LIBSTDCXX=1, + MOZ_ANDROID_LIBSTDCXX= ) + +case "$target" in +arm-linux*-android*|*-linuxandroid*) + android_tool_prefix="arm-linux-androideabi" + ;; +arm-android-eabi) + android_tool_prefix="arm-eabi" + ;; +i?86-*android*) + android_tool_prefix="i686-android-linux" + ;; +*) + android_tool_prefix="$target_os" + ;; +esac + +MOZ_ARG_WITH_STRING(gonk, +[ --with-gonk=DIR + location of gonk dir], + gonkdir=$withval) + +if test -n "$gonkdir" ; then + kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"` + gonk_toolchain="$gonkdir"/prebuilt/$kernel_name-x86/toolchain/arm-eabi-4.4.3 + + dnl set up compilers + AS="$gonk_toolchain"/bin/"$android_tool_prefix"-as + CC="$gonk_toolchain"/bin/"$android_tool_prefix"-gcc + CXX="$gonk_toolchain"/bin/"$android_tool_prefix"-g++ + CPP="$gonk_toolchain"/bin/"$android_tool_prefix"-cpp + LD="$gonk_toolchain"/bin/"$android_tool_prefix"-ld + AR="$gonk_toolchain"/bin/"$android_tool_prefix"-ar + RANLIB="$gonk_toolchain"/bin/"$android_tool_prefix"-ranlib + STRIP="$gonk_toolchain"/bin/"$android_tool_prefix"-strip + + STLPORT_CPPFLAGS="-I$gonkdir/external/stlport/stlport" + STLPORT_LIBS="-lstlport" + + CPPFLAGS="-DANDROID -I$gonkdir/bionic/libc/include/ -I$gonkdir/bionic/libc/kernel/common -I$gonkdir/bionic/libc/arch-arm/include -I$gonkdir/bionic/libc/kernel/arch-arm -I$gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/system/core/include -I$gonkdir/bionic $STLPORT_CPPFLAGS $CPPFLAGS" + CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS" + CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions $CXXFLAGS" + LIBS="$LIBS $STLPORT_LIBS" + + dnl Add -llog by default, since we use it all over the place. + LDFLAGS="-mandroid -L$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib -Wl,-rpath-link=$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib --sysroot=$gonkdir/out/target/product/$GONK_PRODUCT/obj/ -llog $LDFLAGS" + + dnl prevent cross compile section from using these flags as host flags + if test -z "$HOST_CPPFLAGS" ; then + HOST_CPPFLAGS=" " + fi + if test -z "$HOST_CFLAGS" ; then + HOST_CFLAGS=" " + fi + if test -z "$HOST_CXXFLAGS" ; then + HOST_CXXFLAGS=" " + fi + if test -z "$HOST_LDFLAGS" ; then + HOST_LDFLAGS=" " + fi + + # save these for libffi's subconfigure, + # which doesn't know how to figure this stuff out on its own + ANDROID_CFLAGS="$CFLAGS" + ANDROID_CPPFLAGS="$CPPFLAGS" + ANDROID_LDFLAGS="$LDFLAGS" + + AC_DEFINE(ANDROID) + AC_DEFINE(GONK) + CROSS_COMPILE=1 +else +case "$target" in +*-android*|*-linuxandroid*) if test -z "$android_ndk" ; then AC_MSG_ERROR([You must specify --with-android-ndk=/path/to/ndk when targeting Android.]) fi @@ -267,25 +349,75 @@ if test "$target" = "arm-android-eabi" ; then android_platform_tools="$android_sdk"/../../platform-tools if test ! -d "$android_platform_tools" ; then android_platform_tools="$android_sdk"/tools # SDK Tools < r8 + else + if ! test -e "$android_sdk"/source.properties ; then + AC_MSG_ERROR([The path in --with-android-sdk isn't valid (source.properties hasn't been found).]) + fi + + # Minimum Android SDK API Level we require. + android_min_api_level=13 + + # Get the api level from "$android_sdk"/source.properties. + android_api_level=`$AWK -F = '$1 == "AndroidVersion.ApiLevel" {print $2}' "$android_sdk"/source.properties` + + if test -z "$android_api_level" ; then + AC_MSG_ERROR([Unexpected error: no AndroidVersion.ApiLevel field has been found in source.properties.]) + fi + + if ! test "$android_api_level" -eq "$android_api_level" ; then + AC_MSG_ERROR([Unexpected error: the found android api value isn't a number! (found $android_api_level)]) + fi + + if test $android_api_level -lt $android_min_api_level ; then + AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($android_min_api_level or higher required).]) + fi fi if test -z "$android_toolchain" ; then - android_toolchain="$android_ndk"/build/prebuilt/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/arm-eabi-4.4.0 + AC_MSG_CHECKING([for android toolchain directory]) + + kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"` + + android_toolchain="$android_ndk"/build/prebuilt/$kernel_name-x86/arm-eabi-4.4.0 + + # With newer NDK, the toolchain path has changed. + if ! test -d "$android_toolchain" ; then + android_toolchain="$android_ndk"/toolchains/arm-$kernel_name-androideabi-4.4.3/prebuilt/$kernel_name-x86 + fi + + if test -d "$android_toolchain" ; then + AC_MSG_RESULT([$android_toolchain]) + else + AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.]) + fi fi if test -z "$android_platform" ; then - android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu" + AC_MSG_CHECKING([for android platform directory]) + + android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu" + + # With newer NDK, the platform path has changed. + if ! test -d "$android_platform" ; then + android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_cpu" + fi + + if test -d "$android_platform" ; then + AC_MSG_RESULT([$android_platform]) + else + AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.]) + fi fi dnl set up compilers - AS="$android_toolchain"/bin/arm-eabi-as - CC="$android_toolchain"/bin/arm-eabi-gcc - CXX="$android_toolchain"/bin/arm-eabi-g++ - CPP="$android_toolchain"/bin/arm-eabi-cpp - LD="$android_toolchain"/bin/arm-eabi-ld - AR="$android_toolchain"/bin/arm-eabi-ar - RANLIB="$android_toolchain"/bin/arm-eabi-ranlib - STRIP="$android_toolchain"/bin/arm-eabi-strip + AS="$android_toolchain"/bin/"$android_tool_prefix"-as + CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc + CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++ + CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp + LD="$android_toolchain"/bin/"$android_tool_prefix"-ld + AR="$android_toolchain"/bin/"$android_tool_prefix"-ar + RANLIB="$android_toolchain"/bin/"$android_tool_prefix"-ranlib + STRIP="$android_toolchain"/bin/"$android_tool_prefix"-strip CPPFLAGS="-I$android_platform/usr/include $CPPFLAGS" CFLAGS="-mandroid -I$android_platform/usr/include -fno-short-enums -fno-exceptions $CFLAGS" @@ -312,23 +444,20 @@ if test "$target" = "arm-android-eabi" ; then fi ANDROID_NDK="${android_ndk}" - ANDROID_TOOLCHAIN="{android_toolchain}" - ANDROID_PLATFORM="{android_platform}" + ANDROID_TOOLCHAIN="${android_toolchain}" + ANDROID_PLATFORM="${android_platform}" ANDROID_SDK="${android_sdk}" ANDROID_PLATFORM_TOOLS="${android_platform_tools}" ANDROID_VERSION="${android_version}" - # save these for libffi's subconfigure, - # which doesn't know how to figure this stuff out on its own - ANDROID_CFLAGS="$CFLAGS" - ANDROID_CPPFLAGS="$CPPFLAGS" - ANDROID_LDFLAGS="$LDFLAGS" - AC_DEFINE(ANDROID) AC_DEFINE_UNQUOTED(ANDROID_VERSION, $android_version) AC_SUBST(ANDROID_VERSION) CROSS_COMPILE=1 MOZ_CHROME_FILE_FORMAT=omni + ;; +esac + fi AC_SUBST(ANDROID_NDK) @@ -397,7 +526,7 @@ if test "$target" != "$host"; then LDFLAGS="$HOST_LDFLAGS" AC_MSG_CHECKING([whether the host c compiler ($HOST_CC $HOST_CFLAGS $HOST_LDFLAGS) works]) - AC_TRY_COMPILE([], [return(0);], + AC_TRY_COMPILE([], [return(0);], [ac_cv_prog_hostcc_works=1 AC_MSG_RESULT([yes])], AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CC cannot create executables.]) ) @@ -405,62 +534,14 @@ if test "$target" != "$host"; then CFLAGS="$HOST_CXXFLAGS" AC_MSG_CHECKING([whether the host c++ compiler ($HOST_CXX $HOST_CXXFLAGS $HOST_LDFLAGS) works]) - AC_TRY_COMPILE([], [return(0);], + AC_TRY_COMPILE([], [return(0);], [ac_cv_prog_hostcxx_works=1 AC_MSG_RESULT([yes])], AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CXX cannot create executables.]) ) - + CC=$_SAVE_CC CFLAGS=$_SAVE_CFLAGS LDFLAGS=$_SAVE_LDFLAGS - case "$build:$target" in - powerpc-apple-darwin8*:i?86-apple-darwin*) - dnl The Darwin cross compiler doesn't necessarily point itself at a - dnl root that has libraries for the proper architecture, it defaults - dnl to the system root. The libraries in the system root on current - dnl versions of PPC OS X 10.4 aren't fat, so these target compiler - dnl checks will fail. Fake a working SDK in that case. - _SAVE_CFLAGS=$CFLAGS - _SAVE_CXXFLAGS=$CXXLAGS - CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk $CFLAGS" - CXXFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk $CXXFLAGS" - ;; - esac - - case "$target" in - *symbian*) - AC_ARG_WITH(symbian-sdk, - [ --with-symbian-sdk=SYMBIAN_SDK_DIR - The path to the Symbian SDK], - SYMBIAN_SDK_DIR=$withval) - - OS_EXE_CFLAGS="$OS_EXE_CFLAGS -D__EXE__" - CFLAGS="-MD -nostdinc" - SYMBIAN_SYS_INCLUDE="-I$SYMBIAN_SDK_DIR/Epoc32/include -I$SYMBIAN_SDK_DIR/Epoc32/include/variant -I$SYMBIAN_SDK_DIR/Epoc32/include/stdapis" - - case "$target" in - *-symbianelf) - OS_TARGET=GCCE - CC=arm-none-symbianelf-gcc.exe - CXX=arm-none-symbianelf-g++.exe - LD=arm-none-symbianelf-ld.exe - AR=arm-none-symbianelf-ar.exe - CPP=arm-none-symbianelf-cpp.exe - CFLAGS="$CFLAGS -c -Wall -Wno-unknown-pragmas -fexceptions -march=armv5t -mapcs -pipe -msoft-float" - CXXFLAGS="$CFLAGS -Wno-ctor-dtor-privacy" - GCCE_INCLUDE="-include $SYMBIAN_SDK_DIR/EPOC32/INCLUDE/GCCE/GCCE.h -D__PRODUCT_INCLUDE__=$SYMBIAN_SDK_DIR/Epoc32/include/variant/Symbian_OS_v9.2.hrh" - CFLAGS="$CFLAGS ${GCCE_INCLUDE} -x c" - CXXFLAGS="$CXXFLAGS ${GCCE_INCLUDE} -x c++" - CPPFLAGS="$CPPFLAGS ${SYMBIAN_SYS_INCLUDE}" - ;; - *-symbianwinscw) - dnl TODO: add emulator build code - OS_TARGET=WINSCW - ;; - esac - ;; - esac - AC_CHECK_PROGS(CC, $CC "${target_alias}-gcc" "${target}-gcc", :) unset ac_cv_prog_CC AC_PROG_CC @@ -468,16 +549,6 @@ if test "$target" != "$host"; then unset ac_cv_prog_CXX AC_PROG_CXX - case "$build:$target" in - powerpc-apple-darwin8*:i?86-apple-darwin*) - dnl Revert the changes made above. From this point on, the target - dnl compiler will never be used without applying the SDK to CFLAGS - dnl (see --with-macos-sdk below). - CFLAGS=$_SAVE_CFLAGS - CXXFLAGS=$_SAVE_CXXFLAGS - ;; - esac - AC_CHECK_PROGS(RANLIB, $RANLIB "${target_alias}-ranlib" "${target}-ranlib", :) AC_CHECK_PROGS(AR, $AR "${target_alias}-ar" "${target}-ar", :) MOZ_PATH_PROGS(AS, $AS "${target_alias}-as" "${target}-as", :) @@ -566,6 +637,20 @@ if test "$GXX" = yes; then fi fi +CLANG_CC= +CLANG_CXX= +if test "$GCC" = yes; then + if test "`$CC -v 2>&1 | grep -c 'clang version'`" != "0"; then + CLANG_CC=1 + fi +fi + +if test "$GXX" = yes; then + if test "`$CXX -v 2>&1 | grep -c 'clang version'`" != "0"; then + CLANG_CXX=1 + fi +fi + dnl Special win32 checks dnl ======================================================== WINVER=502 @@ -575,18 +660,16 @@ WINSDK_TARGETVER=601 MOZ_ARG_WITH_STRING(windows-version, [ --with-windows-version=WINSDK_TARGETVER Highest Windows version to target using this SDK - 502: Windows Server 2003 - 600: Windows Vista 601: Windows 7], WINSDK_TARGETVER=$withval) case "$WINSDK_TARGETVER" in -502|600|601) +601) MOZ_WINSDK_TARGETVER=0${WINSDK_TARGETVER}0000 ;; *) - AC_MSG_ERROR([Invalid value for --with-windows-version ($WINSDK_TARGETVER), must be 502, 600 or 601]); + AC_MSG_ERROR([Invalid value for --with-windows-version ($WINSDK_TARGETVER), must be 601]); ;; esac @@ -615,11 +698,11 @@ case "$target" in [ unsigned *test = new unsigned(42); ],, AC_MSG_ERROR([\$(CXX) test failed. You must have MS VC++ in your path to build.]) ) AC_LANG_RESTORE - + changequote(,) _MSVC_VER_FILTER='s|.* ([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p' changequote([,]) - + # Determine compiler version CC_VERSION=`"${CC}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'` @@ -644,17 +727,18 @@ case "$target" in fi _CC_SUITE=8 - CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" AC_DEFINE(_CRT_SECURE_NO_DEPRECATE) AC_DEFINE(_CRT_NONSTDC_NO_DEPRECATE) elif test "$_CC_MAJOR_VERSION" = "15"; then _CC_SUITE=9 - CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) elif test "$_CC_MAJOR_VERSION" = "16"; then _CC_SUITE=10 - CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" + AC_DEFINE(_CRT_SECURE_NO_WARNINGS) + AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) + elif test "$_CC_MAJOR_VERSION" = "17"; then + _CC_SUITE=11 AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) else @@ -663,8 +747,6 @@ case "$target" in _MOZ_RTTI_FLAGS_ON='-GR' _MOZ_RTTI_FLAGS_OFF='-GR-' - _MOZ_EXCEPTIONS_FLAGS_ON='-EHsc' - _MOZ_EXCEPTIONS_FLAGS_OFF='' dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool', dnl not something else like "magnetic tape manipulation utility". @@ -696,7 +778,7 @@ case "$target" in # Identify which version of the SDK we're building with # Windows Server 2008 and newer SDKs have WinSDKVer.h, get the version # from there - AC_CHECK_HEADERS([winsdkver.h]) + MOZ_CHECK_HEADERS([winsdkver.h]) if test "$ac_cv_header_winsdkver_h" = "yes"; then # Get the highest _WIN32_WINNT and NTDDI versions supported # Take the higher of the two @@ -721,15 +803,8 @@ EOF ]) MOZ_WINSDK_MAXVER=${ac_cv_winsdk_maxver} else - # The Vista SDK is the only one to have sdkddkver.h but not - # WinSDKVer.h - AC_CHECK_HEADERS([sdkddkver.h]) - if test "$ac_cv_header_sdkddkver_h" = "yes"; then - MOZ_WINSDK_MAXVER=0x06000000 - else - # Assume the Server 2003 Platform SDK - MOZ_WINSDK_MAXVER=0x05020000 - fi + # Any SDK which doesn't have WinSDKVer.h is too old. + AC_MSG_ERROR([Your SDK does not have WinSDKVer.h. It is probably too old. Please upgrade to a newer SDK or try running the Windows SDK Configuration Tool and selecting a newer SDK. See https://developer.mozilla.org/En/Windows_SDK_versions for more details on fixing this.]) fi unset _MSVC_VER_FILTER @@ -835,8 +910,7 @@ EOF AC_MSG_ERROR([windres version $WINDRES_VERSION or higher is required to build.]) fi - # Server 2003 is the highest version supported - MOZ_WINSDK_MAXVER=0x05020000 + MOZ_WINSDK_MAXVER=0x06010000 fi # !GNU_CC AC_DEFINE_UNQUOTED(WINVER,0x$WINVER) @@ -862,18 +936,6 @@ EOF ;; esac -dnl Test breaks icc on OS/2 && MSVC -if test "$CC" != "icc" -a -z "$_WIN32_MSVC"; then - AC_PROG_CC_C_O - if grep "NO_MINUS_C_MINUS_O 1" ./confdefs.h >/dev/null; then - USING_HCC=1 - _OLDCC=$CC - _OLDCXX=$CXX - CC="${srcdir}/build/hcc '$CC'" - CXX="${srcdir}/build/hcpp '$CXX'" - fi -fi - AC_PROG_CPP AC_PROG_CXXCPP @@ -959,7 +1021,7 @@ if test "$_perl_res" != 0; then AC_MSG_RESULT([no]) AC_MSG_ERROR([Cannot find Config.pm or \$Config{archlib}. A full perl installation is required.]) else - AC_MSG_RESULT([yes]) + AC_MSG_RESULT([yes]) fi MOZ_PATH_PROGS(PYTHON, $PYTHON python2.7 python2.6 python2.5 python) @@ -974,11 +1036,6 @@ AC_SUBST(NSINSTALL_BIN) MOZ_PATH_PROG(DOXYGEN, doxygen, :) MOZ_PATH_PROG(AUTOCONF, autoconf, :) -MOZ_PATH_PROG(UNZIP, unzip, :) -MOZ_PATH_PROGS(ZIP, zip) -if test -z "$ZIP" -o "$ZIP" = ":"; then - AC_MSG_ERROR([zip not found in \$PATH]) -fi MOZ_PATH_PROG(SYSTEM_MAKEDEPEND, makedepend) MOZ_PATH_PROG(XARGS, xargs) if test -z "$XARGS" -o "$XARGS" = ":"; then @@ -1019,17 +1076,6 @@ from building Mozilla. Upgrade to Xcode 2.1 or later.]) dnl /usr/bin/g(cc|++)-$GCC_VERSION. MOZ_PATH_PROGS(PBBUILD, pbbuild xcodebuild pbxbuild) - case "$PBBUILD" in - *xcodebuild*) - changequote(,) - XCODEBUILD_VERSION=`$PBBUILD -version 2>/dev/null | xargs | sed -e 's/.*DevToolsCore-\([0-9]*\).*/\1/'` - changequote([,]) - if test -n "$XCODEBUILD_VERSION" && test "$XCODEBUILD_VERSION" -ge 620 ; then - HAS_XCODE_2_1=1; - fi - ;; - esac - dnl sdp was formerly in /Developer/Tools. As of Mac OS X 10.4 (Darwin 8), dnl it has moved into /usr/bin. MOZ_PATH_PROG(SDP, sdp, :, [$PATH:/usr/bin:/Developer/Tools]) @@ -1037,12 +1083,12 @@ from building Mozilla. Upgrade to Xcode 2.1 or later.]) esac AC_SUBST(GCC_VERSION) -AC_SUBST(XCODEBUILD_VERSION) -AC_SUBST(HAS_XCODE_2_1) dnl The universal machinery sets UNIVERSAL_BINARY to inform packager.mk -dnl that a universal binary is being produced. +dnl that a universal binary is being produced and MOZ_CAN_RUN_PROGRAMS +dnl when we can run target binaries. AC_SUBST(UNIVERSAL_BINARY) +AC_SUBST(MOZ_CAN_RUN_PROGRAMS) dnl ======================================================== dnl Check for MacOS deployment target version @@ -1053,19 +1099,16 @@ MOZ_ARG_ENABLE_STRING(macos-target, Set the minimum MacOS version needed at runtime], [_MACOSX_DEPLOYMENT_TARGET=$enableval]) +if test "$target_cpu" != "arm"; then case "$target" in *-darwin*) if test -n "$_MACOSX_DEPLOYMENT_TARGET" ; then dnl Use the specified value export MACOSX_DEPLOYMENT_TARGET=$_MACOSX_DEPLOYMENT_TARGET - AC_DEFINE_UNQUOTED(__ENVIRONMENT_MAC_OS_X_VERION_MIN_REQUIRED__,$_MACOSX_DEPLOYMENT_TARGET) else dnl No value specified on the command line or in the environment, dnl use architecture minimum. case "${target_cpu}" in - ppc*) - export MACOSX_DEPLOYMENT_TARGET=10.5 - ;; i*86) export MACOSX_DEPLOYMENT_TARGET=10.5 ;; @@ -1076,6 +1119,7 @@ case "$target" in fi ;; esac +fi AC_SUBST(MACOSX_DEPLOYMENT_TARGET) @@ -1111,7 +1155,7 @@ tools are selected during the Xcode/Developer Tools installation.]) CFLAGS="$CFLAGS -isysroot ${MACOS_SDK_DIR}" CXXFLAGS="$CXXFLAGS -isysroot ${MACOS_SDK_DIR}" - dnl CPP/CXXCPP needs to be set for AC_CHECK_HEADER. + dnl CPP/CXXCPP needs to be set for MOZ_CHECK_HEADER. CPP="$CPP -isysroot ${MACOS_SDK_DIR}" CXXCPP="$CXXCPP -isysroot ${MACOS_SDK_DIR}" @@ -1161,6 +1205,11 @@ if test "$GMAKE" = ":"; then fi AC_SUBST(GMAKE) +# MAKE will be set by client.mk, but still need this for standalone js builds +if test -z "$MAKE"; then + MAKE=$GMAKE +fi + if test "$COMPILE_ENVIRONMENT"; then AC_PATH_XTRA @@ -1208,11 +1257,11 @@ if test -n "$CROSS_COMPILE"; then kfreebsd*-gnu) OS_ARCH=GNU_kFreeBSD OS_TARGET=GNU_kFreeBSD ;; gnu*) OS_ARCH=GNU ;; solaris*) OS_ARCH=SunOS OS_RELEASE=5 ;; - mingw*) OS_ARCH=WINNT ;; + mingw*) OS_ARCH=WINNT OS_TARGET=WINNT ;; darwin*) OS_ARCH=Darwin OS_TARGET=Darwin ;; esac case "${target}" in - arm-android-eabi) OS_ARCH=Linux OS_TARGET=Android ;; + *-android*|*-linuxandroid*) OS_ARCH=Linux OS_TARGET=Android ;; esac else OS_TARGET=`uname -s` @@ -1224,8 +1273,6 @@ fi # but that breaks when you have a 64 bit kernel with a 32 bit userland. OS_TEST="${target_cpu}" -_COMPILER_PREFIX= - HOST_OS_ARCH=`echo $host_os | sed -e 's|/|_|g'` ####################################################################### @@ -1278,41 +1325,9 @@ solaris*) BSD_386) HOST_OS_ARCH=BSD ;; -dgux) - HOST_OS_ARCH=DGUX - ;; -IRIX64) - HOST_OS_ARCH=IRIX - ;; -UNIX_SV) - if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then - HOST_OS_ARCH=NCR - else - HOST_OS_ARCH=UNIXWARE - fi - ;; -ncr) - HOST_OS_ARCH=NCR - ;; -UNIX_SYSTEM_V) - HOST_OS_ARCH=NEC - ;; -OSF1) - ;; OS_2) HOST_OS_ARCH=OS2 ;; -QNX) - ;; -SCO_SV) - HOST_OS_ARCH=SCOOS - ;; -SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) - HOST_OS_ARCH=SINIX - ;; -UnixWare) - HOST_OS_ARCH=UNIXWARE - ;; esac case "$OS_ARCH" in @@ -1359,71 +1374,18 @@ AIX) BSD_386) OS_ARCH=BSD ;; -dgux) - OS_ARCH=DGUX - ;; -IRIX64) - OS_ARCH=IRIX - ;; -UNIX_SV) - if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then - OS_ARCH=NCR - else - OS_ARCH=UNIXWARE - OS_RELEASE=`uname -v` - fi - ;; -ncr) - OS_ARCH=NCR - ;; -UNIX_SYSTEM_V) - OS_ARCH=NEC - ;; -OSF1) - case `uname -v` in - 148) - OS_RELEASE=V3.2C - ;; - 564) - OS_RELEASE=V4.0B - ;; - 878) - OS_RELEASE=V4.0D - ;; - esac - ;; OS_2) OS_ARCH=OS2 OS_TARGET=OS2 OS_RELEASE=`uname -v` ;; -QNX) - if test "$OS_TARGET" != "NTO"; then - changequote(,) - OS_RELEASE=`uname -v | sed 's/^\([0-9]\)\([0-9]*\)$/\1.\2/'` - changequote([,]) - fi - OS_TEST=x86 - ;; -SCO_SV) - OS_ARCH=SCOOS - OS_RELEASE=5.0 - ;; -SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) - OS_ARCH=SINIX - OS_TEST=`uname -p` - ;; -UnixWare) - OS_ARCH=UNIXWARE - OS_RELEASE=`uname -v` - ;; Darwin) case "${target_cpu}" in powerpc*) OS_TEST=ppc ;; i*86*) - OS_TEST=i386 + OS_TEST=i386 ;; x86_64) OS_TEST=x86_64 @@ -1437,12 +1399,6 @@ Darwin) ;; esac -if test "$OS_ARCH" = "NCR"; then - changequote(,) - OS_RELEASE=`awk '{print $3}' /etc/.relid | sed 's/^\([0-9]\)\(.\)\(..\)\(.*\)$/\2.\3/'` - changequote([,]) -fi - # Only set CPU_ARCH if we recognize the value of OS_TEST case "$OS_TEST" in @@ -1504,12 +1460,313 @@ case "$OS_TEST" in INTEL_ARCHITECTURE=1 esac +dnl ======================================================== +dnl = ARM toolchain tweaks +dnl ======================================================== + +dnl Defaults +case "${CPU_ARCH}-${OS_TARGET}" in +arm-Android) + MOZ_THUMB=yes + MOZ_ARCH=armv7-a + MOZ_FPU=vfp + MOZ_FLOAT_ABI=softfp + ;; +arm-Darwin) + MOZ_THUMB=yes + ;; +arm-*) + if test -n "$MOZ_PLATFORM_MAEMO"; then + MOZ_THUMB=no + MOZ_ARCH=armv7-a + MOZ_FLOAT_ABI=softfp + fi + if test "$MOZ_PLATFORM_MAEMO" = 6; then + MOZ_THUMB=yes + fi + ;; +esac + +dnl Kept for compatibility with some buildbot mozconfig +MOZ_ARG_DISABLE_BOOL(thumb2, [], MOZ_THUMB=no, MOZ_THUMB=yes) + +MOZ_ARG_WITH_STRING(thumb, +[ --with-thumb[[=yes|no|toolchain-default]]] +[ Use Thumb instruction set (-mthumb)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb is not supported on non-GNU toolchain-defaults]) + fi + MOZ_THUMB=$withval) + +MOZ_ARG_WITH_STRING(thumb-interwork, +[ --with-thumb-interwork[[=yes|no|toolchain-default]] + Use Thumb/ARM instuctions interwork (-mthumb-interwork)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb-interwork is not supported on non-GNU toolchain-defaults]) + fi + MOZ_THUMB_INTERWORK=$withval) + +MOZ_ARG_WITH_STRING(arch, +[ --with-arch=[[type|toolchain-default]] + Use specific CPU features (-march=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-arch is not supported on non-GNU toolchain-defaults]) + fi + MOZ_ARCH=$withval) + +MOZ_ARG_WITH_STRING(fpu, +[ --with-fpu=[[type|toolchain-default]] + Use specific FPU type (-mfpu=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-fpu is not supported on non-GNU toolchain-defaults]) + fi + MOZ_FPU=$withval) + +MOZ_ARG_WITH_STRING(float-abi, +[ --with-float-abi=[[type|toolchain-default]] + Use specific arm float ABI (-mfloat-abi=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-float-abi is not supported on non-GNU toolchain-defaults]) + fi + MOZ_FLOAT_ABI=$withval) + +MOZ_ARG_WITH_STRING(soft-float, +[ --with-soft-float[[=yes|no|toolchain-default]] + Use soft float library (-msoft-float)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-soft-float is not supported on non-GNU toolchain-defaults]) + fi + MOZ_SOFT_FLOAT=$withval) + +case "$MOZ_ARCH" in +toolchain-default|"") + arch_flag="" + ;; +*) + arch_flag="-march=$MOZ_ARCH" + ;; +esac + +case "$MOZ_THUMB" in +yes) + MOZ_THUMB2=1 + thumb_flag="-mthumb" + ;; +no) + MOZ_THUMB2= + thumb_flag="-marm" + ;; +*) + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$arch_flag" + AC_TRY_COMPILE([],[return sizeof(__thumb2__);], + MOZ_THUMB2=1, + MOZ_THUMB2=) + CFLAGS="$_SAVE_CFLAGS" + thumb_flag="" + ;; +esac + +if test "$MOZ_THUMB2" = 1; then + AC_DEFINE(MOZ_THUMB2) +fi + +case "$MOZ_THUMB_INTERWORK" in +yes) + thumb_interwork_flag="-mthumb-interwork" + ;; +no) + thumb_interwork_flag="-mno-thumb-interwork" + ;; +*) # toolchain-default + thumb_interwork_flag="" + ;; +esac + +case "$MOZ_FPU" in +toolchain-default|"") + fpu_flag="" + ;; +*) + fpu_flag="-mfpu=$MOZ_FPU" + ;; +esac + +case "$MOZ_FLOAT_ABI" in +toolchain-default|"") + float_abi_flag="" + ;; +*) + float_abi_flag="-mfloat-abi=$MOZ_FLOAT_ABI" + ;; +esac + +case "$MOZ_SOFT_FLOAT" in +yes) + soft_float_flag="-msoft-float" + ;; +no) + soft_float_flag="-mno-soft-float" + ;; +*) # toolchain-default + soft_float_flag="" + ;; +esac + +dnl Use echo to avoid accumulating space characters +all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag` +if test -n "$all_flags"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$all_flags" + AC_MSG_CHECKING(whether the chosen combination of compiler flags ($all_flags) works) + AC_TRY_COMPILE([],[return 0;], + AC_MSG_RESULT([yes]), + AC_MSG_ERROR([no])) + + CFLAGS="$_SAVE_CFLAGS $all_flags" + CXXFLAGS="$CXXFLAGS $all_flags" + ASFLAGS="$ASFLAGS $all_flags" + if test -n "$thumb_flag"; then + LDFLAGS="$LDFLAGS $thumb_flag" + fi +fi + +AC_SUBST(MOZ_THUMB2) + +if test "$CPU_ARCH" = "arm"; then + AC_MSG_CHECKING(for ARM SIMD support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm("uqadd8 r1, r1, r2");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_SIMD) + HAVE_ARM_SIMD=1 + fi + + AC_MSG_CHECKING(for ARM NEON support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm(".fpu neon\n vadd.i8 d0, d0, d0");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_NEON) + HAVE_ARM_NEON=1 + fi +fi # CPU_ARCH = arm + +AC_SUBST(HAVE_ARM_SIMD) +AC_SUBST(HAVE_ARM_NEON) + +dnl ================================================================= +dnl Set up and test static assertion macros used to avoid AC_TRY_RUN, +dnl which is bad when cross compiling. +dnl ================================================================= +if test "$COMPILE_ENVIRONMENT"; then +configure_static_assert_macros=' +#define CONFIGURE_STATIC_ASSERT(condition) CONFIGURE_STATIC_ASSERT_IMPL(condition, __LINE__) +#define CONFIGURE_STATIC_ASSERT_IMPL(condition, line) CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) +#define CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) typedef int static_assert_line_##line[(condition) ? 1 : -1] +' + +dnl test that the macros actually work: +AC_MSG_CHECKING(that static assertion macros used in autoconf tests work) +AC_CACHE_VAL(ac_cv_static_assertion_macros_work, + [AC_LANG_SAVE + AC_LANG_C + ac_cv_static_assertion_macros_work="yes" + AC_TRY_COMPILE([$configure_static_assert_macros], + [CONFIGURE_STATIC_ASSERT(1)], + , + ac_cv_static_assertion_macros_work="no") + AC_TRY_COMPILE([$configure_static_assert_macros], + [CONFIGURE_STATIC_ASSERT(0)], + ac_cv_static_assertion_macros_work="no", + ) + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([$configure_static_assert_macros], + [CONFIGURE_STATIC_ASSERT(1)], + , + ac_cv_static_assertion_macros_work="no") + AC_TRY_COMPILE([$configure_static_assert_macros], + [CONFIGURE_STATIC_ASSERT(0)], + ac_cv_static_assertion_macros_work="no", + ) + AC_LANG_RESTORE + ]) +AC_MSG_RESULT("$ac_cv_static_assertion_macros_work") +if test "$ac_cv_static_assertion_macros_work" = "no"; then + AC_MSG_ERROR([Compiler cannot compile macros used in autoconf tests.]) +fi +fi # COMPILE_ENVIRONMENT + +dnl ======================================================== +dnl Android libstdc++, placed here so it can use MOZ_ARCH +dnl computed above. +dnl ======================================================== + +if test "$OS_TARGET" = "Android"; then + case "${CPU_ARCH}-${MOZ_ARCH}" in + arm-armv7*) + ANDROID_CPU_ARCH=armeabi-v7a + ;; + arm-*) + ANDROID_CPU_ARCH=armeabi + ;; + x86-*) + ANDROID_CPU_ARCH=x86 + ;; + esac + + if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then + if test ! -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a" ; then + AC_MSG_ERROR([Cannot find path to libstdc++ (NDK version >= 5?)]) + fi + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include -D_GLIBCXX_PERMIT_BACKWARD_HASH" + STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH" + STLPORT_LIBS="-lstdc++" + elif test -e "$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport" + STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/" + STLPORT_LIBS="-lstlport_static" + elif test -e "$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then + STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport" + STLPORT_LDFLAGS="-L$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH" + STLPORT_LIBS="-lstlport_static" + elif test "$target" != "arm-android-eabi"; then + dnl fail if we're not building with NDKr4 + AC_MSG_ERROR([Couldn't find path to stlport in the android ndk]) + fi + CPPFLAGS="$CPPFLAGS $STLPORT_CPPFLAGS" + LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS" + LIBS="$LIBS $STLPORT_LIBS" + + # save these for libffi's subconfigure, + # which doesn't know how to figure this stuff out on its own + ANDROID_CFLAGS="$CFLAGS" + ANDROID_CPPFLAGS="$CPPFLAGS" + ANDROID_LDFLAGS="$LDFLAGS" +fi + +dnl ======================================================== +dnl Suppress Clang Argument Warnings +dnl ======================================================== +if test -n "$CLANG_CC"; then + _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}" + CPPFLAGS="-Qunused-arguments ${CPPFLAGS}" +fi +if test -n "$CLANG_CXX"; then + _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}" +fi + dnl ======================================================== dnl GNU specific defaults dnl ======================================================== if test "$GNU_CC"; then - # FIXME: Let us build with strict aliasing. bug 414641. - CFLAGS="$CFLAGS -fno-strict-aliasing" MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' DSO_LDOPTS='-shared' @@ -1517,14 +1774,12 @@ if test "$GNU_CC"; then # Don't allow undefined symbols in libraries DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs" fi - WARNINGS_AS_ERRORS='-Werror' + WARNINGS_AS_ERRORS='-Werror -Wno-error=uninitialized' DSO_CFLAGS='' DSO_PIC_CFLAGS='-fPIC' ASFLAGS="$ASFLAGS -fPIC" - _MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti - _MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti - _MOZ_EXCEPTIONS_FLAGS_ON='-fexceptions' - _MOZ_EXCEPTIONS_FLAGS_OFF='-fno-exceptions' + _MOZ_RTTI_FLAGS_ON=-frtti + _MOZ_RTTI_FLAGS_OFF=-fno-rtti # Turn on GNU specific features # -Wall - turn on all warnings @@ -1534,8 +1789,8 @@ if test "$GNU_CC"; then # -Wbad-function-cast - warns when casting a function to a new return type # -Wshadow - removed because it generates more noise than help --pete _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith" - if test -z "$INTEL_CC"; then - # Don't use -Wcast-align with ICC + if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then + # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in # And don't use it on hppa, ia64, sparc, arm, since it's noisy there hppa | ia64 | sparc | arm) @@ -1580,12 +1835,10 @@ else fi if test "$GNU_CXX"; then - # FIXME: Let us build with strict aliasing. bug 414641. - CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" # Turn on GNU specific features _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" - if test -z "$INTEL_CC"; then - # Don't use -Wcast-align with ICC + if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then + # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in # And don't use it on hppa, ia64, sparc, arm, since it's noisy there hppa | ia64 | sparc | arm) @@ -1599,13 +1852,44 @@ if test "$GNU_CXX"; then _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/js-confdefs.h' _USE_CPP_INCLUDE_FLAG=1 + # Recent clang and gcc support C++11 deleted functions without warnings if + # compiling with -std=c++0x or -std=gnu++0x (or c++11 or gnu++11 in very new + # versions). We can't use -std=c++0x yet, so gcc's support must remain + # unused. But clang's warning can be disabled, so when compiling with clang + # we use it to opt out of the warning, enabling (macro-encapsulated) use of + # deleted function syntax. + if test "$CLANG_CXX"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-c++0x-extensions" + fi + + AC_CACHE_CHECK(whether the compiler supports -Wno-extended-offsetof, + ac_has_wno_extended_offsetof, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -Wno-extended-offsetof" + AC_TRY_COMPILE([$configure_static_assert_macros + #ifndef __has_warning + #define __has_warning(x) 0 + #endif], + [CONFIGURE_STATIC_ASSERT(__has_warning("-Wextended-offsetof"))], + ac_has_wno_extended_offsetof="yes", + ac_has_wno_extended_offsetof="no") + CXXFLAGS="$_SAVE_CXXFLAGS" + AC_LANG_RESTORE + ]) + if test "$ac_has_wno_extended_offsetof" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-extended-offsetof" + fi + AC_CACHE_CHECK(whether the compiler supports -Wno-invalid-offsetof, ac_has_wno_invalid_offsetof, [ AC_LANG_SAVE AC_LANG_CPLUSPLUS _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + CXXFLAGS="$CXXFLAGS -Wno-invalid-offsetof" AC_TRY_COMPILE([], [return(0);], ac_has_wno_invalid_offsetof="yes", @@ -1614,7 +1898,7 @@ if test "$GNU_CXX"; then AC_LANG_RESTORE ]) if test "$ac_has_wno_invalid_offsetof" = "yes"; then - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-invalid-offsetof" fi AC_CACHE_CHECK(whether the compiler supports -Wno-variadic-macros, @@ -1623,7 +1907,7 @@ if test "$GNU_CXX"; then AC_LANG_SAVE AC_LANG_CPLUSPLUS _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-variadic-macros" + CXXFLAGS="$CXXFLAGS -Wno-variadic-macros" AC_TRY_COMPILE([], [return(0);], ac_has_wno_variadic_macros="yes", @@ -1632,7 +1916,7 @@ if test "$GNU_CXX"; then AC_LANG_RESTORE ]) if test "$ac_has_wno_variadic_macros" = "yes"; then - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-variadic-macros" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-variadic-macros" fi AC_CACHE_CHECK(whether the compiler supports -Werror=return-type, @@ -1697,48 +1981,6 @@ LOOP_INPUT fi # GNU_CC fi # COMPILE_ENVIRONMENT -dnl ================================================================= -dnl Set up and test static assertion macros used to avoid AC_TRY_RUN, -dnl which is bad when cross compiling. -dnl ================================================================= -if test "$COMPILE_ENVIRONMENT"; then -configure_static_assert_macros=' -#define CONFIGURE_STATIC_ASSERT(condition) CONFIGURE_STATIC_ASSERT_IMPL(condition, __LINE__) -#define CONFIGURE_STATIC_ASSERT_IMPL(condition, line) CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) -#define CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) typedef int static_assert_line_##line[(condition) ? 1 : -1] -' - -dnl test that the macros actually work: -AC_MSG_CHECKING(that static assertion macros used in autoconf tests work) -AC_CACHE_VAL(ac_cv_static_assertion_macros_work, - [AC_LANG_SAVE - AC_LANG_C - ac_cv_static_assertion_macros_work="yes" - AC_TRY_COMPILE([$configure_static_assert_macros], - [CONFIGURE_STATIC_ASSERT(1)], - , - ac_cv_static_assertion_macros_work="no") - AC_TRY_COMPILE([$configure_static_assert_macros], - [CONFIGURE_STATIC_ASSERT(0)], - ac_cv_static_assertion_macros_work="no", - ) - AC_LANG_CPLUSPLUS - AC_TRY_COMPILE([$configure_static_assert_macros], - [CONFIGURE_STATIC_ASSERT(1)], - , - ac_cv_static_assertion_macros_work="no") - AC_TRY_COMPILE([$configure_static_assert_macros], - [CONFIGURE_STATIC_ASSERT(0)], - ac_cv_static_assertion_macros_work="no", - ) - AC_LANG_RESTORE - ]) -AC_MSG_RESULT("$ac_cv_static_assertion_macros_work") -if test "$ac_cv_static_assertion_macros_work" = "no"; then - AC_MSG_ERROR([Compiler cannot compile macros used in autoconf tests.]) -fi -fi # COMPILE_ENVIRONMENT - dnl ======================================================== dnl Checking for 64-bit OS dnl ======================================================== @@ -1828,12 +2070,6 @@ case "$host" in MOZ_FIX_LINK_PATHS= ;; -*-osf*) - HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" - HOST_NSPR_MDCPUCFG='\"md/_osf1.cfg\"' - HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" - ;; - *) HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" @@ -1852,6 +2088,17 @@ if test "$_python_res" != 0; then fi AC_MSG_RESULT([yes]) +dnl Check for using a custom implementation +dnl ======================================================== +AC_MSG_CHECKING(for custom implementation) +if test "$MOZ_CUSTOM_STDINT_H"; then + AC_DEFINE_UNQUOTED(MOZ_CUSTOM_STDINT_H, "$MOZ_CUSTOM_STDINT_H") + AC_SUBST(MOZ_CUSTOM_STDINT_H) + AC_MSG_RESULT(using $MOZ_CUSTOM_STDINT_H) +else + AC_MSG_RESULT(none specified) +fi + MOZ_DOING_LTO(lto_is_enabled) dnl ======================================================== @@ -1864,7 +2111,7 @@ case "$target" in if test ! "$GNU_CC"; then if test ! "$HAVE_64BIT_OS"; then # Compiling with Visual Age C++ object model compat is the - # default. To compile with object model ibm, add + # default. To compile with object model ibm, add # AIX_OBJMODEL=ibm to .mozconfig. if test "$AIX_OBJMODEL" = "ibm"; then CXXFLAGS="$CXXFLAGS -qobjmodel=ibm" @@ -1892,10 +2139,10 @@ case "$target" in #endif], _BAD_COMPILER=,_BAD_COMPILER=1) if test -n "$_BAD_COMPILER"; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) AC_MSG_ERROR([IBM XLC/C++ 9.0.0.7 or higher is required to build.]) else - AC_MSG_RESULT([yes]) + AC_MSG_RESULT([yes]) fi AC_LANG_RESTORE TARGET_COMPILER_ABI="ibmc" @@ -1909,7 +2156,7 @@ case "$target" in ;; esac if test "$COMPILE_ENVIRONMENT"; then - AC_CHECK_HEADERS(sys/inttypes.h) + MOZ_CHECK_HEADERS(sys/inttypes.h) fi AC_DEFINE(JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES) AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) @@ -1934,29 +2181,34 @@ case "$target" in esac ;; -*-darwin*) +*-darwin*) MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS="-O3 -fomit-frame-pointer" - else - MOZ_OPTIMIZE_FLAGS="-O3 -fno-omit-frame-pointer" - fi + MOZ_OPTIMIZE_FLAGS="-O3 -fno-stack-protector" _PEDANTIC= - CFLAGS="$CFLAGS -fpascal-strings -fno-common" - CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common" + CFLAGS="$CFLAGS -fno-common" + CXXFLAGS="$CXXFLAGS -fno-common" DLL_SUFFIX=".dylib" DSO_LDOPTS='' STRIP="$STRIP -x -S" _PLATFORM_DEFAULT_TOOLKIT='cairo-cocoa' TARGET_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' - LDFLAGS="$LDFLAGS -framework Cocoa -lobjc" + LDFLAGS="$LDFLAGS -lobjc" LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL' # The ExceptionHandling framework is needed for Objective-C exception # logging code in nsObjCExceptions.h. Currently we only use that in debug # builds. - MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling" + _SAVE_LDFLAGS=$LDFLAGS + AC_MSG_CHECKING([for -framework ExceptionHandling]) + LDFLAGS="$LDFLAGS -framework ExceptionHandling" + AC_TRY_LINK(,[return 0;], + ac_cv_have_framework_exceptionhandling="yes", + ac_cv_have_framework_exceptionhandling="no") + AC_MSG_RESULT([$ac_cv_have_framework_exceptionhandling]) + if test "$ac_cv_have_framework_exceptionhandling" = "yes"; then + MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling"; + fi + LDFLAGS=$_SAVE_LDFLAGS if test "x$lto_is_enabled" = "xyes"; then echo "Skipping -dead_strip because lto is enabled." @@ -1976,7 +2228,7 @@ case "$target" in else AC_MSG_RESULT([no]) fi - + LDFLAGS=$_SAVE_LDFLAGS fi MOZ_FIX_LINK_PATHS='-Wl,-executable_path,$(LIBXUL_DIST)/bin' @@ -1990,7 +2242,7 @@ case "$target" in if test ! "$GNU_CC"; then DSO_LDOPTS="-Bshareable $DSO_LDOPTS" fi - ;; + ;; ia64*-hpux*) DLL_SUFFIX=".so" @@ -2030,64 +2282,16 @@ ia64*-hpux*) AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) ;; -*-irix5*) - AC_DEFINE(IRIX) - DSO_LDOPTS='-elf -shared' - - if test "$GNU_CC"; then - MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - MKSHLIB_FORCE_ALL='-Wl,-all' - MKSHLIB_UNFORCE_ALL='-Wl,-none' - CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" - else - MKSHLIB='$(LD) $(DSO_LDOPTS) -o $@' - MKCSHLIB='$(LD) $(DSO_LDOPTS) -o $@' - MKSHLIB_FORCE_ALL='-all' - MKSHLIB_UNFORCE_ALL='-none' - fi - ;; +*-android*|*-linuxandroid*) + AC_DEFINE(NO_PW_GECOS) + no_x=yes + _PLATFORM_DEFAULT_TOOLKIT=cairo-android + TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' -*-irix6*) - AC_DEFINE(IRIX) - dnl the irix specific xptcinvoke code is written against the n32 ABI so we *must* - dnl compile and link using -n32 - USE_N32=1 - TARGET_COMPILER_ABI=n32 - DSO_LDOPTS='-elf -shared' - MKSHLIB='$(CCC) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - _MOZ_EXCEPTIONS_FLAGS_OFF="-LANG:exceptions=OFF" - _MOZ_EXCEPTIONS_FLAGS_ON="-LANG:exceptions=ON" - if test "$GNU_CC"; then - MKSHLIB_FORCE_ALL='-Wl,-all' - MKSHLIB_UNFORCE_ALL='-Wl,-none' - _WARNINGS_CFLAGS="-Wall" - _WARNINGS_CXXFLAGS="-Wall" - CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" - else - MKSHLIB_FORCE_ALL='-all' - MKSHLIB_UNFORCE_ALL='-none' - AR_LIST="$AR t" - AR_EXTRACT="$AR x" - AR_DELETE="$AR d" - AR='$(CXX) -ar' - AR_FLAGS='-o $@' - CFLAGS="$CFLAGS -woff 3262 -G 4" - CXXFLAGS="$CXXFLAGS -woff 3262 -G 4" - if test -n "$USE_N32"; then - ASFLAGS="$ASFLAGS -n32" - CFLAGS="$CFLAGS -n32" - CXXFLAGS="$CXXFLAGS -n32" - LDFLAGS="$LDFLAGS -n32" - fi - AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) - AC_MSG_WARN([Clearing MOZ_FIX_LINK_PATHS for OSF/1 as fix for bug 333545 (till the reference bug 332075 is fixed.]) - MOZ_FIX_LINK_PATHS= - fi - if test -z "$GNU_CXX"; then - MIPSPRO_CXX=1 - fi + MOZ_GFX_OPTIMIZE_MOBILE=1 + MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks -fno-reorder-functions" + # The Maemo builders don't know about this flag + MOZ_ARM_VFP_FLAGS="-mfpu=vfp" ;; *-*linux*) @@ -2105,12 +2309,8 @@ ia64*-hpux*) # -Os is broken on gcc 4.1.x 4.2.x, 4.5.x we need to tweak it to get good results. MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50" esac - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS="-O3 -fomit-frame-pointer" - else - MOZ_OPTIMIZE_FLAGS="-O3 -fno-omit-frame-pointer" - fi + MOZ_PGO_OPTIMIZE_FLAGS="-O3" + MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks $MOZ_OPTIMIZE_SIZE_TWEAK" MOZ_DEBUG_FLAGS="-g" fi @@ -2125,42 +2325,19 @@ ia64*-hpux*) MOZ_DEBUG_FLAGS="-g" # We want inlining ;; esac - ;; -*-symbian*) - - AC_DEFINE(XP_UNIX) - AC_DEFINE(SYMBIAN) - AC_DEFINE(__arm__) - AC_DEFINE(__SYMBIAN32__) - AC_DEFINE(_UNICODE) - AC_DEFINE(NDEBUG) - AC_DEFINE(__SUPPORT_CPP_EXCEPTIONS__) - AC_DEFINE(MOZ_STDERR_TO_STDOUT) - AC_DEFINE(HAVE_FCNTL_FILE_LOCKING) - AC_DEFINE(HAVE_SOCKLEN_T) - AC_DEFINE(__GCCE__) - - CPU_ARCH=ARM - OS_RELEASE=9.2 - OS_ARCH=SYMBIAN - USE_PTHREADS=1 - LIB_SUFFIX=lib - DLL_SUFFIX=dll - MKSHLIB= - DSO_LDOPTS= - DSO_CFLAGS= - VISIBILITY_FLAGS= - TARGET_NSPR_MDCPUCFG='\"md/_symbian.cfg\"' - RANLIB='echo no ranlib ' -;; + if test -z "$MC"; then + MC=mc.exe + fi + ;; *-mingw*) DSO_CFLAGS= DSO_PIC_CFLAGS= DLL_SUFFIX=.dll RC=rc.exe - # certain versions of cygwin's makedepend barf on the + MC=mc.exe + # certain versions of cygwin's makedepend barf on the # #include vs -I./dist/include/string issue so don't use it SYSTEM_MAKEDEPEND= if test -n "$GNU_CC"; then @@ -2199,8 +2376,6 @@ ia64*-hpux*) STRIP='echo not_strip' PKG_SKIP_STRIP=1 XARGS=xargs - ZIP=zip - UNZIP=unzip DOXYGEN=: GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb' ASM_SUFFIX=asm @@ -2221,16 +2396,14 @@ ia64*-hpux*) CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" # MSVC warnings C4244 and C4800 are ubiquitous, useless, and annoying. CXXFLAGS="$CXXFLAGS -wd4244 -wd4800" + # make 'foo == bar;' error out + CFLAGS="$CFLAGS -we4553" + CXXFLAGS="$CXXFLAGS -we4553" LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib" MOZ_DEBUG_FLAGS='-Zi' MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' WARNINGS_AS_ERRORS='-WX' - # If we're building with --enable-profiling, we need -Oy-, which forces a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS='-O1' - else - MOZ_OPTIMIZE_FLAGS='-O1 -Oy-' - fi + MOZ_OPTIMIZE_FLAGS="-O2" MOZ_JS_LIBS='$(libdir)/mozjs.lib' MOZ_FIX_LINK_PATHS= DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib $(LIBXUL_DIST)/lib/mozalloc.lib' @@ -2297,7 +2470,7 @@ ia64*-hpux*) AC_MSG_ERROR([\$MOZ_TOOLS\\bin must be in your path.]) fi ;; - esac + esac case "$host_os" in cygwin*|msvc*|mks*) @@ -2316,11 +2489,11 @@ ia64*-hpux*) fi if test -n "$GNU_CC"; then - CFLAGS="$CFLAGS -mstackrealign" - CXXFLAGS="$CXXFLAGS -mstackrealign" + CFLAGS="$CFLAGS -mstackrealign -fno-keep-inline-dllexport" + CXXFLAGS="$CXXFLAGS -mstackrealign -fno-keep-inline-dllexport" fi - AC_CHECK_HEADERS(mmintrin.h) + MOZ_CHECK_HEADERS(mmintrin.h) AC_DEFINE(_X86_) ;; x86_64-*) @@ -2360,32 +2533,6 @@ ia64*-hpux*) MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,lib$(LIBRARY_NAME)$(DLL_SUFFIX) -o $@' ;; -*-nto*) - AC_DEFINE(NTO) - AC_DEFINE(_QNX_SOURCE) - AC_DEFINE(_i386) - OS_TARGET=NTO - WARNINGS_AS_ERRORS='' - MOZ_OPTIMIZE_FLAGS="-O" - MOZ_DEBUG_FLAGS="-gstabs" - USE_PTHREADS=1 - _PEDANTIC= - LIBS="$LIBS -lsocket -lstdc++" - _DEFINES_CFLAGS='-include $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506' - _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/js-confdefs.h -D_POSIX_C_SOURCE=199506' - if test "$with_x" != "yes" - then - _PLATFORM_DEFAULT_TOOLKIT="photon" - TK_CFLAGS='-I/usr/include/photon' - TK_LIBS='-lph' - fi - case "${target_cpu}" in - ppc*) - AC_DEFINE(HAVE_VA_LIST_AS_ARRAY) - ;; - esac - ;; - *-openbsd*) DLL_SUFFIX=".so.1.0" DSO_CFLAGS='' @@ -2428,6 +2575,7 @@ ia64*-hpux*) TARGET_MD_ARCH=os2 _PLATFORM_DEFAULT_TOOLKIT="cairo-os2" RC=rc.exe + MC=mc.exe RCFLAGS='-n' MOZ_USER_DIR="Mozilla" @@ -2458,57 +2606,7 @@ ia64*-hpux*) fi ;; -alpha*-*-osf*) - if test "$GNU_CC"; then - MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' - MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' - - else - MOZ_DEBUG_FLAGS='-g' - ASFLAGS='-I$(topsrcdir)/xpcom/reflect/xptcall/public -g' - CFLAGS="$CFLAGS -ieee" - CXXFLAGS="$CXXFLAGS "'-noexceptions -ieee -ptr $(DIST)/cxx_repository' - DSO_LDOPTS='-shared -msym -expect_unresolved \* -update_registry $(DIST)/so_locations' - DSO_CFLAGS= - DSO_PIC_CFLAGS= - MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' - MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' - MKSHLIB_FORCE_ALL='-all' - MKSHLIB_UNFORCE_ALL='-none' - dnl Might fix the libxpcom.so breakage on this platform as well.... - AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) - AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) - fi - if test -z "$GNU_CXX"; then - COMPAQ_CXX=1 - fi - AC_DEFINE(NEED_USLEEP_PROTOTYPE) - ;; - -*-qnx*) - DIRENT_INO=d_stat.st_ino - dnl Solves the problems the QNX compiler has with nsCOMPtr.h. - AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) - AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) - dnl Explicit set STDC_HEADERS to workaround QNX 6.0's failing of std test - AC_DEFINE(STDC_HEADERS) - if test "$no_x" = "yes"; then - _PLATFORM_DEFAULT_TOOLKIT='photon' - TK_CFLAGS='-I/usr/nto/include/photon' - TK_LIBS='-lphoton -lphrender' - fi - ;; - -*-sco*) - AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) - AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) - CXXFLAGS="$CXXFLAGS -I/usr/include/CC" - if test ! "$GNU_CC"; then - DSO_LDOPTS='-G' - fi - ;; - -*-solaris*) +*-solaris*) AC_DEFINE(SOLARIS) TARGET_NSPR_MDCPUCFG='\"md/_solaris.cfg\"' if test -z "$CROSS_COMPILE" && pkginfo -q SUNWpr && pkginfo -q SUNWprd; then @@ -2523,16 +2621,13 @@ alpha*-*-osf*) if test "$SOLARIS_SUNPRO_CC"; then LDFLAGS="$LDFLAGS -z ignore -R '\$\$ORIGIN:\$\$ORIGIN/..' -z lazyload -z combreloc -z muldefs" LIBS="-lCrun -lCstd -lc $LIBS" - NS_USE_NATIVE=1 AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__" - CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions -norunpath -D__FUNCTION__=__func__ -template=no%extdef" - _MOZ_EXCEPTIONS_FLAGS_ON='-features=except' - _MOZ_EXCEPTIONS_FLAGS_OFF='-features=no%except' + CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions,no%except -norunpath -D__FUNCTION__=__func__ -template=no%extdef" LDFLAGS="-xildoff $LDFLAGS" if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then _SAVE_LDFLAGS=$LDFLAGS - LDFLAGS="-M /usr/lib/ld/map.noexstk $LDFLAGS" + LDFLAGS="-M /usr/lib/ld/map.noexstk $LDFLAGS" AC_TRY_LINK([#include ], [printf("Hello World\n");], , @@ -2599,7 +2694,7 @@ alpha*-*-osf*) fi ;; -*-sunos*) +*-sunos*) DSO_LDOPTS='-Bdynamic' MKSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' MKCSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' @@ -2612,31 +2707,10 @@ alpha*-*-osf*) esac ;; -*-sysv4.2uw7*) - NSPR_LIBS="-lnspr$NSPR_VERSION -lplc$NSPR_VERSION -lplds$NSPR_VERSION -L/usr/ccs/lib -lcrt" - ;; - *-os2*) HOST_NSPR_MDCPUCFG='\"md/_os2.cfg\"' ;; -*-android*) - AC_DEFINE(NO_PW_GECOS) - no_x=yes - _PLATFORM_DEFAULT_TOOLKIT=cairo-android - TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' - - MOZ_GFX_OPTIMIZE_MOBILE=1 - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions -fomit-frame-pointer" - else - MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions -fno-omit-frame-pointer" - fi - # The Maemo builders don't know about this flag - MOZ_ARM_VFP_FLAGS="-mfpu=vfp" - ;; - esac dnl Only one oddball right now (QNX), but this gives us flexibility @@ -2689,9 +2763,6 @@ case "$target" in fi fi ;; - *-nto*) - MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' - ;; *-darwin*) MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,-exported_symbols_list -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-export-list' ;; @@ -2711,43 +2782,46 @@ dnl Configure JIT support case "$target" in i?86-*) - ENABLE_TRACEJIT=1 - NANOJIT_ARCH=i386 ENABLE_METHODJIT=1 ENABLE_MONOIC=1 ENABLE_POLYIC=1 - ENABLE_POLYIC_TYPED_ARRAY=1 + ENABLE_METHODJIT_TYPED_ARRAY=1 AC_DEFINE(JS_CPU_X86) AC_DEFINE(JS_NUNBOX32) ;; x86_64*-*) - ENABLE_TRACEJIT=1 - NANOJIT_ARCH=X64 ENABLE_METHODJIT=1 ENABLE_MONOIC=1 ENABLE_POLYIC=1 - ENABLE_POLYIC_TYPED_ARRAY=1 + ENABLE_METHODJIT_TYPED_ARRAY=1 AC_DEFINE(JS_CPU_X64) AC_DEFINE(JS_PUNBOX64) ;; arm*-*) - ENABLE_TRACEJIT=1 - NANOJIT_ARCH=ARM ENABLE_METHODJIT=1 ENABLE_MONOIC=1 ENABLE_POLYIC=1 + ENABLE_POLYIC_TYPED_ARRAY=1 AC_DEFINE(JS_CPU_ARM) AC_DEFINE(JS_NUNBOX32) ;; sparc*-*) - ENABLE_TRACEJIT=1 - NANOJIT_ARCH=Sparc + if test ! "$HAVE_64BIT_OS" ; then ENABLE_METHODJIT=1 ENABLE_MONOIC=1 ENABLE_POLYIC=1 - ENABLE_POLYIC_TYPED_ARRAY=1 + ENABLE_METHODJIT_TYPED_ARRAY=1 AC_DEFINE(JS_CPU_SPARC) AC_DEFINE(JS_NUNBOX32) + fi + ;; +mips*-*) + ENABLE_METHODJIT=1 + ENABLE_MONOIC=1 + ENABLE_POLYIC=1 + ENABLE_METHODJIT_TYPED_ARRAY=1 + AC_DEFINE(JS_CPU_MIPS) + AC_DEFINE(JS_NUNBOX32) ;; esac @@ -2763,10 +2837,6 @@ MOZ_ARG_DISABLE_BOOL(polyic, [ --disable-polyic Disable use of PICs by JIT compiler], ENABLE_POLYIC= ) -MOZ_ARG_DISABLE_BOOL(tracejit, -[ --disable-tracejit Disable tracing JIT support], - ENABLE_TRACEJIT=) - MOZ_ARG_ENABLE_BOOL(methodjit-spew, [ --enable-methodjit-spew Enable method JIT spew support], ENABLE_METHODJIT_SPEW=1, @@ -2786,70 +2856,14 @@ if test "$ENABLE_POLYIC"; then AC_DEFINE(JS_POLYIC) fi -if test "$ENABLE_POLYIC_TYPED_ARRAY"; then - AC_DEFINE(JS_POLYIC_TYPED_ARRAY) +if test "$ENABLE_METHODJIT_TYPED_ARRAY"; then + AC_DEFINE(JS_METHODJIT_TYPED_ARRAY) fi if test "$ENABLE_METHODJIT_SPEW"; then AC_DEFINE(JS_METHODJIT_SPEW) fi -if test "$ENABLE_TRACEJIT"; then - -AC_DEFINE(FEATURE_NANOJIT) -AC_DEFINE(JS_TRACER) - -case "$target" in -i?86-*) - AC_DEFINE(AVMPLUS_IA32) - ;; -x86_64*-*) - AC_DEFINE(AVMPLUS_AMD64) - AC_DEFINE(AVMPLUS_64BIT) - ;; -arm*-*) - AC_DEFINE(AVMPLUS_ARM) - ;; -sparc-*) - AC_DEFINE(AVMPLUS_SPARC) - ;; -esac - -case "$target" in -*-linux*|*-android-eabi) - AC_DEFINE(AVMPLUS_UNIX) - AC_DEFINE(AVMPLUS_LINUX) - ;; -*-darwin*) - AC_DEFINE(AVMPLUS_UNIX) - ;; -*-solaris*) - AC_DEFINE(AVMPLUS_UNIX) - ;; -*-freebsd*|*-kfreebsd*) - AC_DEFINE(AVMPLUS_UNIX) - ;; -*-openbsd*) - AC_DEFINE(AVMPLUS_UNIX) - ;; -*-gnu*) - AC_DEFINE(AVMPLUS_UNIX) - ;; -*-mingw*) - AC_DEFINE(AVMPLUS_WIN32) - ;; -*-os2*) - AC_DEFINE(AVMPLUS_OS2) - ;; -*) - AC_MSG_ERROR([Unrecognized nanojit platform. Use --disable-tracejit to build without tracing JIT support.]) -esac - -fi # ENABLE_TRACEJIT - -AC_SUBST(ENABLE_TRACEJIT) -AC_SUBST(NANOJIT_ARCH) - if test -z "$SKIP_COMPILER_CHECKS"; then dnl Checks for typedefs, structures, and compiler characteristics. dnl ======================================================== @@ -2896,33 +2910,11 @@ AC_CACHE_VAL(ac_cv_siginfo_t, [siginfo_t* info;], [ac_cv_siginfo_t=true], [ac_cv_siginfo_t=false])]) -if test "$ac_cv_siginfo_t" = true ; then - AC_DEFINE(HAVE_SIGINFO_T) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi - -dnl Find exact-width integer types, or figure them out -dnl ourselves. -dnl ======================================================== -dnl Once this is working, we can delete the code for int16_t, -dnl etc. below. - -AC_CHECK_HEADER(stdint.h) -if test "$ac_cv_header_stdint_h" = yes; then - AC_DEFINE(JS_HAVE_STDINT_H) +if test "$ac_cv_siginfo_t" = true ; then + AC_DEFINE(HAVE_SIGINFO_T) + AC_MSG_RESULT(yes) else - dnl We'll figure them out for ourselves. List more likely types - dnl earlier. If we ever really encounter a size for which none of - dnl the listed types are appropriate, we'll get a configure-time - dnl error; just add the right answer. - MOZ_N_BYTE_TYPE(JS_INT8_TYPE, 1, [char]) - MOZ_N_BYTE_TYPE(JS_INT16_TYPE, 2, [short int long]) - MOZ_N_BYTE_TYPE(JS_INT32_TYPE, 4, [int long 'long long' short]) - MOZ_N_BYTE_TYPE(JS_INT64_TYPE, 8, [int long 'long long']) - MOZ_N_BYTE_TYPE(JS_INTPTR_TYPE, sizeof (void *), - [int long 'long long' short]) + AC_MSG_RESULT(no) fi MOZ_SIZE_OF_TYPE(JS_BYTES_PER_WORD, void*, 4 8) @@ -2937,60 +2929,23 @@ fi MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) MOZ_SIZE_OF_TYPE(JS_BYTES_PER_DOUBLE, double, 6 8 10 12 14) -dnl Check for int16_t, int32_t, int64_t, int64, uint, uint_t, and uint16_t. -dnl ======================================================== -AC_MSG_CHECKING(for int16_t) -AC_CACHE_VAL(ac_cv_int16_t, - [AC_TRY_COMPILE([#include - #include ], - [int16_t foo = 0;], - [ac_cv_int16_t=true], - [ac_cv_int16_t=false])]) -if test "$ac_cv_int16_t" = true ; then - AC_DEFINE(HAVE_INT16_T) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi -AC_MSG_CHECKING(for int32_t) -AC_CACHE_VAL(ac_cv_int32_t, - [AC_TRY_COMPILE([#include - #include ], - [int32_t foo = 0;], - [ac_cv_int32_t=true], - [ac_cv_int32_t=false])]) -if test "$ac_cv_int32_t" = true ; then - AC_DEFINE(HAVE_INT32_T) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) +MOZ_CHECK_HEADERS(endian.h) +if test "$ac_cv_header_endian_h" = yes; then + AC_DEFINE(JS_HAVE_ENDIAN_H) fi -AC_MSG_CHECKING(for int64_t) -AC_CACHE_VAL(ac_cv_int64_t, - [AC_TRY_COMPILE([#include - #include ], - [int64_t foo = 0;], - [ac_cv_int64_t=true], - [ac_cv_int64_t=false])]) -if test "$ac_cv_int64_t" = true ; then - AC_DEFINE(HAVE_INT64_T) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) + +MOZ_CHECK_HEADERS([machine/endian.h],[],[],[#include ]) +if test "$ac_cv_header_machine_endian_h" = yes; then + AC_DEFINE(JS_HAVE_MACHINE_ENDIAN_H) fi -AC_MSG_CHECKING(for int64) -AC_CACHE_VAL(ac_cv_int64, - [AC_TRY_COMPILE([#include - #include ], - [int64 foo = 0;], - [ac_cv_int64=true], - [ac_cv_int64=false])]) -if test "$ac_cv_int64" = true ; then - AC_DEFINE(HAVE_INT64) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) + +MOZ_CHECK_HEADERS(sys/isa_defs.h) +if test "$ac_cv_header_sys_isa_defs_h" = yes; then + AC_DEFINE(JS_HAVE_SYS_ISA_DEFS_H) fi + +dnl Check for uint and uint_t. +dnl ======================================================== AC_MSG_CHECKING(for uint) AC_CACHE_VAL(ac_cv_uint, [AC_TRY_COMPILE([#include @@ -3017,19 +2972,6 @@ if test "$ac_cv_uint_t" = true ; then else AC_MSG_RESULT(no) fi -AC_MSG_CHECKING(for uint16_t) -AC_CACHE_VAL(ac_cv_uint16_t, - [AC_TRY_COMPILE([#include - #include ], - [uint16_t foo = 0;], - [ac_cv_uint16_t=true], - [ac_cv_uint16_t=false])]) -if test "$ac_cv_uint16_t" = true ; then - AC_DEFINE(HAVE_UINT16_T) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi dnl On the gcc trunk (as of 2001-02-09) _GNU_SOURCE, and thus __USE_GNU, dnl are defined when compiling C++ but not C. Since the result of this @@ -3039,7 +2981,7 @@ AC_LANG_CPLUSPLUS AC_MSG_CHECKING(for uname.domainname) AC_CACHE_VAL(ac_cv_have_uname_domainname_field, [AC_TRY_COMPILE([#include ], - [ struct utsname *res; char *domain; + [ struct utsname *res; char *domain; (void)uname(res); if (res != 0) { domain = res->domainname; } ], [ac_cv_have_uname_domainname_field=true], [ac_cv_have_uname_domainname_field=false])]) @@ -3054,7 +2996,7 @@ fi AC_MSG_CHECKING(for uname.__domainname) AC_CACHE_VAL(ac_cv_have_uname_us_domainname_field, [AC_TRY_COMPILE([#include ], - [ struct utsname *res; char *domain; + [ struct utsname *res; char *domain; (void)uname(res); if (res != 0) { domain = res->__domainname; } ], [ac_cv_have_uname_us_domainname_field=true], [ac_cv_have_uname_us_domainname_field=false])]) @@ -3190,6 +3132,8 @@ fi # Sun Studio on Solaris AC_SUBST(WRAP_SYSTEM_INCLUDES) AC_SUBST(VISIBILITY_FLAGS) +MOZ_GCC_PR49911 + dnl Check for __force_align_arg_pointer__ for SSE2 on gcc dnl ======================================================== if test "$GNU_CC"; then @@ -3219,30 +3163,25 @@ freebsd*) CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}" ;; esac -AC_CHECK_HEADERS(sys/byteorder.h compat.h getopt.h) -AC_CHECK_HEADERS(sys/bitypes.h memory.h unistd.h) -AC_CHECK_HEADERS(gnu/libc-version.h nl_types.h) -AC_CHECK_HEADERS(malloc.h) -AC_CHECK_HEADERS(X11/XKBlib.h) -AC_CHECK_HEADERS(io.h) +MOZ_CHECK_COMMON_HEADERS dnl These are all the places some variant of statfs can be hiding. -AC_CHECK_HEADERS(sys/statvfs.h sys/statfs.h sys/vfs.h sys/mount.h) +MOZ_CHECK_HEADERS(sys/statvfs.h sys/statfs.h sys/vfs.h sys/mount.h) dnl Quota support -AC_CHECK_HEADERS(sys/quota.h) -AC_CHECK_HEADERS(linux/quota.h) +MOZ_CHECK_HEADERS(sys/quota.h) +MOZ_CHECK_HEADERS(linux/quota.h) dnl Try for MMX support dnl NB - later gcc versions require -mmmx for this header to be successfully dnl included (or another option which implies it, such as -march=pentium-mmx) -AC_CHECK_HEADERS(mmintrin.h) +MOZ_CHECK_HEADERS(mmintrin.h) dnl Check whether the compiler supports the new-style C++ standard dnl library headers (i.e. ) or needs the old "new.h" AC_LANG_CPLUSPLUS NEW_H=new.h -AC_CHECK_HEADER(new, [NEW_H=new]) +MOZ_CHECK_HEADER(new, [NEW_H=new]) AC_DEFINE_UNQUOTED(NEW_H, <$NEW_H>) AC_LANG_C @@ -3250,7 +3189,7 @@ AC_ARG_ENABLE(dtrace, [ --enable-dtrace build with dtrace support if available (default=no)], [enable_dtrace="yes"],) if test "x$enable_dtrace" = "xyes"; then - AC_CHECK_HEADER(sys/sdt.h, HAVE_DTRACE=1) + MOZ_CHECK_HEADER(sys/sdt.h, HAVE_DTRACE=1) if test -n "$HAVE_DTRACE"; then AC_DEFINE(INCLUDE_MOZILLA_DTRACE) else @@ -3263,12 +3202,12 @@ case $target in *-aix4.3*|*-aix5*) ;; *) - AC_CHECK_HEADERS(sys/cdefs.h) + MOZ_CHECK_HEADERS(sys/cdefs.h) ;; esac dnl Performance measurement headers. -AC_CHECK_HEADER(linux/perf_event.h, +MOZ_CHECK_HEADER(linux/perf_event.h, [AC_CACHE_CHECK(for perf_event_open system call,ac_cv_perf_event_open, [AC_TRY_COMPILE([#include ],[return sizeof(__NR_perf_event_open);], ac_cv_perf_event_open=yes, @@ -3302,8 +3241,8 @@ case $target in *-os2*) ;; *) - AC_SEARCH_LIBS(dlopen, dl, - AC_CHECK_HEADER(dlfcn.h, + AC_SEARCH_LIBS(dlopen, dl, + MOZ_CHECK_HEADER(dlfcn.h, AC_DEFINE(HAVE_DLOPEN))) ;; esac @@ -3333,37 +3272,6 @@ case $target in AC_CHECK_LIB(socket, socket) esac -if test "$CPU_ARCH" = "arm"; then - AC_MSG_CHECKING(for ARM SIMD support in compiler) - AC_TRY_COMPILE([], - [asm("uqadd8 r1, r1, r2");], - result="yes", result="no") - AC_MSG_RESULT("$result") - if test "$result" = "yes"; then - AC_DEFINE(HAVE_ARM_SIMD) - HAVE_ARM_SIMD=1 - fi - - AC_MSG_CHECKING(for ARM NEON support in compiler) - _SAVE_CFLAGS="$CFLAGS" - if test "$GNU_CC"; then - # gcc needs -mfpu=neon to recognize NEON instructions - CFLAGS="$CFLAGS -mfpu=neon -mfloat-abi=softfp" - fi - AC_TRY_COMPILE([], - [asm("vadd.i8 d0, d0, d0");], - result="yes", result="no") - AC_MSG_RESULT("$result") - if test "$result" = "yes"; then - AC_DEFINE(HAVE_ARM_NEON) - HAVE_ARM_NEON=1 - fi - CFLAGS="$_SAVE_CFLAGS" -fi - -AC_SUBST(HAVE_ARM_SIMD) -AC_SUBST(HAVE_ARM_NEON) - dnl ======================================================== dnl = pthread support dnl = Start by checking whether the system support pthreads @@ -3389,7 +3297,7 @@ darwin*) esac dnl ======================================================== -dnl Check the command line for --with-pthreads +dnl Check the command line for --with-pthreads dnl ======================================================== MOZ_ARG_WITH_BOOL(pthreads, [ --with-pthreads Force use of system pthread library with NSPR ], @@ -3471,83 +3379,28 @@ then ;; *-*-linux*|*-*-kfreebsd*-gnu|*-*-gnu*) - AC_DEFINE(_REENTRANT) - ;; - - *-*-nto*) - AC_DEFINE(_REENTRANT) + AC_DEFINE(_REENTRANT) ;; *-aix4.3*|*-aix5*) - AC_DEFINE(_REENTRANT) + AC_DEFINE(_REENTRANT) ;; *-hpux11.*) - AC_DEFINE(_REENTRANT) - ;; - - alpha*-*-osf*) AC_DEFINE(_REENTRANT) ;; - *-*-solaris*) + *-*-solaris*) AC_DEFINE(_REENTRANT) if test "$SOLARIS_SUNPRO_CC"; then - CFLAGS="$CFLAGS -mt" - CXXFLAGS="$CXXFLAGS -mt" + CFLAGS="$CFLAGS -mt" + CXXFLAGS="$CXXFLAGS -mt" fi ;; esac LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}" fi -dnl ======================================================== -dnl See if mmap sees writes -dnl For cross compiling, just define it as no, which is a safe default -dnl ======================================================== -AC_MSG_CHECKING(whether mmap() sees write()s) - -changequote(,) -mmap_test_prog=' - #include - #include - #include - #include - #include - #include - - char fname[] = "conftest.file"; - char zbuff[1024]; /* Fractional page is probably worst case */ - - int main() { - char *map; - int fd; - int i; - unlink(fname); - fd = open(fname, O_RDWR | O_CREAT, 0660); - if(fd<0) return 1; - unlink(fname); - write(fd, zbuff, sizeof(zbuff)); - lseek(fd, 0, SEEK_SET); - map = (char*)mmap(0, sizeof(zbuff), PROT_READ, MAP_SHARED, fd, 0); - if(map==(char*)-1) return 2; - for(i=0; fname[i]; i++) { - int rc = write(fd, &fname[i], 1); - if(map[i]!=fname[i]) return 4; - } - return 0; - } -' -changequote([,]) - -AC_TRY_RUN($mmap_test_prog , result="yes", result="no", result="yes") - -AC_MSG_RESULT("$result") - -if test "$result" = "no"; then - AC_DEFINE(MMAP_MISSES_WRITES) -fi - dnl Checks for library functions. dnl ======================================================== @@ -3651,7 +3504,7 @@ AC_CACHE_CHECK( ], [const char *glibc_version = gnu_get_libc_version();], [ac_cv_func_gnu_get_libc_version=yes], - [ac_cv_func_gnu_get_libc_version=no] + [ac_cv_func_gnu_get_libc_version=no] )] ) @@ -3663,7 +3516,7 @@ case $target_os in darwin*|mingw*|os2*) ;; *) - + AC_CHECK_LIB(c, iconv, [_ICONV_LIBS="$_ICONV_LIBS"], AC_CHECK_LIB(iconv, iconv, [_ICONV_LIBS="$_ICONV_LIBS -liconv"], AC_CHECK_LIB(iconv, libiconv, [_ICONV_LIBS="$_ICONV_LIBS -liconv"]))) @@ -3682,7 +3535,7 @@ AC_CACHE_CHECK( iconv_close(h); ], [ac_cv_func_iconv=yes], - [ac_cv_func_iconv=no] + [ac_cv_func_iconv=no] )] ) if test "$ac_cv_func_iconv" = "yes"; then @@ -3704,7 +3557,7 @@ if test "$ac_cv_func_iconv" = "yes"; then iconv_close(h); ], [ac_cv_func_const_iconv=yes], - [ac_cv_func_const_iconv=no] + [ac_cv_func_const_iconv=no] )] ) if test "$ac_cv_func_const_iconv" = "yes"; then @@ -3792,63 +3645,6 @@ if test "x$ac_cv_va_val_copy" = "xno"; then fi AC_MSG_RESULT($ac_cv_va_val_copy) -dnl Check for dll-challenged libc's. -dnl This check is apparently only needed for Linux. -case "$target" in - *-linux*) - dnl =================================================================== - _curdir=`pwd` - export _curdir - rm -rf conftest* _conftest - mkdir _conftest - cat >> conftest.C <<\EOF -#include -#include -#include -#ifdef _dl_loaded -void __dump_link_map(void) { - struct link_map *map = _dl_loaded; - while (NULL != map) {printf("0x%08x %s\n", map->l_addr, map->l_name); map = map->l_next;} -} -int main() { - dlopen("./conftest1.so",RTLD_LAZY); - dlopen("./../_conftest/conftest1.so",RTLD_LAZY); - dlopen("CURDIR/_conftest/conftest1.so",RTLD_LAZY); - dlopen("CURDIR/_conftest/../_conftest/conftest1.so",RTLD_LAZY); - __dump_link_map(); -} -#else -/* _dl_loaded isn't defined, so this should be either a libc5 (glibc1) system, or a glibc2 system that doesn't have the multiple load bug (i.e., RH6.0).*/ -int main() { printf("./conftest1.so\n"); } -#endif -EOF - - $PERL -p -i -e "s/CURDIR/\$ENV{_curdir}/g;" conftest.C - - cat >> conftest1.C <<\EOF -#include -void foo(void) {printf("foo in dll called\n");} -EOF - ${CXX-g++} -fPIC -c -g conftest1.C - ${CXX-g++} -shared -Wl,-h -Wl,conftest1.so -o conftest1.so conftest1.o - ${CXX-g++} -g conftest.C -o conftest -ldl - cp -f conftest1.so conftest _conftest - cd _conftest - if test `./conftest | grep conftest1.so | wc -l` -gt 1 - then - echo - echo "*** Your libc has a bug that can result in loading the same dynamic" - echo "*** library multiple times. This bug is known to be fixed in glibc-2.0.7-32" - echo "*** or later. However, if you choose not to upgrade, the only effect" - echo "*** will be excessive memory usage at runtime." - echo - fi - cd ${_curdir} - rm -rf conftest* _conftest - dnl =================================================================== - ;; -esac - dnl =================================================================== dnl ======================================================== dnl Put your C++ language/feature checks below @@ -3856,7 +3652,6 @@ dnl ======================================================== AC_LANG_CPLUSPLUS ARM_ABI_PREFIX= -HAVE_GCC3_ABI= if test "$GNU_CC"; then if test "$CPU_ARCH" = "arm" ; then AC_CACHE_CHECK(for ARM EABI, @@ -3879,61 +3674,7 @@ if test "$GNU_CC"; then fi fi - AC_CACHE_CHECK(for gcc 3.0 ABI, - ac_cv_gcc_three_abi, - [AC_TRY_COMPILE([], - [ -#if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */ - return 0; -#else -#error Not gcc3. -#endif - ], - ac_cv_gcc_three_abi="yes", - ac_cv_gcc_three_abi="no")]) - if test "$ac_cv_gcc_three_abi" = "yes"; then - TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-${ARM_ABI_PREFIX}gcc3}" - HAVE_GCC3_ABI=1 - else - TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-${ARM_ABI_PREFIX}gcc2}" - fi -fi -AC_SUBST(HAVE_GCC3_ABI) - - -AC_CACHE_CHECK(for C++ \"explicit\" keyword, - ac_cv_cpp_explicit, - [AC_TRY_COMPILE(class X { - public: explicit X(int i) : i_(i) {} - private: int i_; - };, - X x(3);, - ac_cv_cpp_explicit=yes, - ac_cv_cpp_explicit=no)]) -if test "$ac_cv_cpp_explicit" = yes ; then - AC_DEFINE(HAVE_CPP_EXPLICIT) -fi - -AC_CACHE_CHECK(for C++ \"typename\" keyword, - ac_cv_cpp_typename, - [AC_TRY_COMPILE(class param { - public: - typedef unsigned long num_type; - }; - - template class tplt { - public: - typedef typename T::num_type t_num_type; - t_num_type foo(typename T::num_type num) { - return num; - } - };, - tplt A; - A.foo(0);, - ac_cv_cpp_typename=yes, - ac_cv_cpp_typename=no)]) -if test "$ac_cv_cpp_typename" = yes ; then - AC_DEFINE(HAVE_CPP_TYPENAME) + TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-${ARM_ABI_PREFIX}gcc3}" fi dnl Check for support of modern template specialization syntax @@ -3948,11 +3689,10 @@ AC_CACHE_CHECK(for modern C++ template specialization syntax support, X y_x;, ac_cv_cpp_modern_specialize_template_syntax=yes, ac_cv_cpp_modern_specialize_template_syntax=no)]) -if test "$ac_cv_cpp_modern_specialize_template_syntax" = yes ; then - AC_DEFINE(HAVE_CPP_MODERN_SPECIALIZE_TEMPLATE_SYNTAX) +if test "$ac_cv_cpp_modern_specialize_template_syntax" = no ; then + AC_MSG_ERROR([The C++ compiler does not support template specialization]) fi - dnl Some compilers support only full specialization, and some don't. AC_CACHE_CHECK(whether partial template specialization works, ac_cv_cpp_partial_specialization, @@ -3965,113 +3705,10 @@ if test "$ac_cv_cpp_partial_specialization" = yes ; then AC_DEFINE(HAVE_CPP_PARTIAL_SPECIALIZATION) fi -dnl Some compilers have limited support for operators with templates; -dnl specifically, it is necessary to define derived operators when a base -dnl class's operator declaration should suffice. -AC_CACHE_CHECK(whether operators must be re-defined for templates derived from templates, - ac_cv_need_derived_template_operators, - [AC_TRY_COMPILE([template class Base { }; - template - Base operator+(const Base& lhs, const Base& rhs) { return lhs; } - template class Derived : public Base { };], - [Derived a, b; - Base c = a + b; - return 0;], - ac_cv_need_derived_template_operators=no, - ac_cv_need_derived_template_operators=yes)]) -if test "$ac_cv_need_derived_template_operators" = yes ; then - AC_DEFINE(NEED_CPP_DERIVED_TEMPLATE_OPERATORS) -fi - - -dnl Some compilers have trouble detecting that a template class -dnl that derives from another template is actually an instance -dnl of the base class. This test checks for that. -AC_CACHE_CHECK(whether we need to cast a derived template to pass as its base class, - ac_cv_need_cpp_template_cast_to_base, - [AC_TRY_COMPILE([template class Base { }; - template class Derived : public Base { }; - template int foo(const Base&) { return 0; }], - [Derived bar; return foo(bar);], - ac_cv_need_cpp_template_cast_to_base=no, - ac_cv_need_cpp_template_cast_to_base=yes)]) -if test "$ac_cv_need_cpp_template_cast_to_base" = yes ; then - AC_DEFINE(NEED_CPP_TEMPLATE_CAST_TO_BASE) -fi - -dnl Some compilers have trouble resolving the ambiguity between two -dnl functions whose arguments differ only by cv-qualifications. -AC_CACHE_CHECK(whether the compiler can resolve const ambiguities for templates, - ac_cv_can_resolve_const_ambiguity, - [AC_TRY_COMPILE([ - template class ptrClass { - public: T* ptr; - }; - - template T* a(ptrClass *arg) { - return arg->ptr; - } - - template - const T* a(const ptrClass *arg) { - return arg->ptr; - } - ], - [ ptrClass i; - a(&i); ], - ac_cv_can_resolve_const_ambiguity=yes, - ac_cv_can_resolve_const_ambiguity=no)]) -if test "$ac_cv_can_resolve_const_ambiguity" = no ; then - AC_DEFINE(CANT_RESOLVE_CPP_CONST_AMBIGUITY) -fi - -dnl -dnl We don't do exceptions on unix. The only reason this used to be here -dnl is that mozilla/xpcom/tests/TestCOMPtr.cpp has a test which uses -dnl exceptions. But, we turn exceptions off by default and this test breaks. -dnl So im commenting this out until someone writes some artificial -dnl intelligence to detect not only if the compiler has exceptions, but if -dnl they are enabled as well. -dnl -dnl AC_CACHE_CHECK(for C++ \"exceptions\", -dnl ac_cv_cpp_exceptions, -dnl [AC_TRY_COMPILE(class X { public: X() {} }; -dnl static void F() { throw X(); }, -dnl try { F(); } catch(X & e) { }, -dnl ac_cv_cpp_exceptions=yes, -dnl ac_cv_cpp_exceptions=no)]) -dnl if test $ac_cv_cpp_exceptions = yes ; then -dnl AC_DEFINE(HAVE_CPP_EXCEPTIONS) -dnl fi - -dnl Some compilers have marginal |using| support; for example, gcc-2.7.2.3 -dnl supports it well enough to allow us to use it to change access, but not -dnl to resolve ambiguity. The next two tests determine how well the |using| -dnl keyword is supported. -dnl -dnl Check to see if we can change access with |using|. Test both a -dnl legal and an illegal example. -AC_CACHE_CHECK(whether the C++ \"using\" keyword can change access, - ac_cv_cpp_access_changing_using2, - [AC_TRY_COMPILE( - class A { protected: int foo() { return 0; } }; - class B : public A { public: using A::foo; };, - B b; return b.foo();, - [AC_TRY_COMPILE( - class A { public: int foo() { return 1; } }; - class B : public A { private: using A::foo; };, - B b; return b.foo();, - ac_cv_cpp_access_changing_using2=no, - ac_cv_cpp_access_changing_using2=yes)], - ac_cv_cpp_access_changing_using2=no)]) -if test "$ac_cv_cpp_access_changing_using2" = yes ; then - AC_DEFINE(HAVE_CPP_ACCESS_CHANGING_USING) -fi - dnl Check to see if we can resolve ambiguity with |using|. AC_CACHE_CHECK(whether the C++ \"using\" keyword resolves ambiguity, ac_cv_cpp_ambiguity_resolving_using, - [AC_TRY_COMPILE(class X { + [AC_TRY_COMPILE(class X { public: int go(const X&) {return 3;} int jo(const X&) {return 3;} }; @@ -4088,48 +3725,6 @@ if test "$ac_cv_cpp_ambiguity_resolving_using" = yes ; then AC_DEFINE(HAVE_CPP_AMBIGUITY_RESOLVING_USING) fi -dnl Check to see if the |std| namespace is supported. If so, we'll want -dnl to qualify any standard library calls with "std::" to ensure that -dnl those functions can be resolved. -AC_CACHE_CHECK(for \"std::\" namespace, - ac_cv_cpp_namespace_std, - [AC_TRY_COMPILE([#include ], - [return std::min(0, 1);], - ac_cv_cpp_namespace_std=yes, - ac_cv_cpp_namespace_std=no)]) -if test "$ac_cv_cpp_namespace_std" = yes ; then - AC_DEFINE(HAVE_CPP_NAMESPACE_STD) -fi - -dnl Older compilers are overly ambitious with respect to using the standard -dnl template library's |operator!=()| when |operator==()| is defined. In -dnl which case, defining |operator!=()| in addition to |operator==()| causes -dnl ambiguity at compile-time. This test checks for that case. -AC_CACHE_CHECK(whether standard template operator!=() is ambiguous, - ac_cv_cpp_unambiguous_std_notequal, - [AC_TRY_COMPILE([#include - struct T1 {}; - int operator==(const T1&, const T1&) { return 0; } - int operator!=(const T1&, const T1&) { return 0; }], - [T1 a,b; return a != b;], - ac_cv_cpp_unambiguous_std_notequal=unambiguous, - ac_cv_cpp_unambiguous_std_notequal=ambiguous)]) -if test "$ac_cv_cpp_unambiguous_std_notequal" = unambiguous ; then - AC_DEFINE(HAVE_CPP_UNAMBIGUOUS_STD_NOTEQUAL) -fi - - -AC_CACHE_CHECK(for C++ reinterpret_cast, - ac_cv_cpp_reinterpret_cast, - [AC_TRY_COMPILE(struct X { int i; }; - struct Y { int i; };, - X x; X*const z = &x;Y*y = reinterpret_cast(z);, - ac_cv_cpp_reinterpret_cast=yes, - ac_cv_cpp_reinterpret_cast=no)]) -if test "$ac_cv_cpp_reinterpret_cast" = yes ; then - AC_DEFINE(HAVE_CPP_NEW_CASTS) -fi - dnl See if a dynamic_cast to void* gives the most derived object. AC_CACHE_CHECK(for C++ dynamic_cast to void*, ac_cv_cpp_dynamic_cast_void_ptr, @@ -4188,8 +3783,11 @@ if test "$ac_cv_trouble_comparing_to_zero" = yes ; then fi # try harder, when checking for __thread support, see bug 521750 comment #33 and below +# We pass MOZ_OPTIMIZE_LDFLAGS to the linker because if dead_strip is +# enabled, the linker in xcode 4.1 will crash. Without this it would crash when +# linking XUL. _SAVE_LDFLAGS=$LDFLAGS -LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS $DSO_LDOPTS" +LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS $DSO_LDOPTS $MOZ_OPTIMIZE_LDFLAGS" AC_CACHE_CHECK(for __thread keyword for TLS variables, ac_cv_thread_keyword, [AC_TRY_LINK([__thread bool tlsIsMainThread = false;], @@ -4204,7 +3802,7 @@ if test "$ac_cv_thread_keyword" = yes; then mips*-*) : ;; - *-android*) + *-android*|*-linuxandroid*) : ;; *) @@ -4216,11 +3814,11 @@ fi dnl Check for the existence of various allocation headers/functions MALLOC_H= -AC_CHECK_HEADER(malloc.h, [MALLOC_H=malloc.h]) +MOZ_CHECK_HEADER(malloc.h, [MALLOC_H=malloc.h]) if test "$MALLOC_H" = ""; then - AC_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h]) + MOZ_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h]) if test "$MALLOC_H" = ""; then - AC_CHECK_HEADER(sys/malloc.h, [MALLOC_H=sys/malloc.h]) + MOZ_CHECK_HEADER(sys/malloc.h, [MALLOC_H=sys/malloc.h]) fi fi if test "$MALLOC_H" != ""; then @@ -4234,32 +3832,25 @@ dnl See if compiler supports some gcc-style attributes AC_CACHE_CHECK(for __attribute__((always_inline)), ac_cv_attribute_always_inline, - [AC_TRY_COMPILE([], - [inline void f(void) __attribute__((always_inline));], + [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));], + [], ac_cv_attribute_always_inline=yes, ac_cv_attribute_always_inline=no)]) AC_CACHE_CHECK(for __attribute__((malloc)), ac_cv_attribute_malloc, - [AC_TRY_COMPILE([], - [void* f(int) __attribute__((malloc));], + [AC_TRY_COMPILE([void* f(int) __attribute__((malloc));], + [], ac_cv_attribute_malloc=yes, ac_cv_attribute_malloc=no)]) AC_CACHE_CHECK(for __attribute__((warn_unused_result)), ac_cv_attribute_warn_unused, - [AC_TRY_COMPILE([], - [int f(void) __attribute__((warn_unused_result));], + [AC_TRY_COMPILE([int f(void) __attribute__((warn_unused_result));], + [], ac_cv_attribute_warn_unused=yes, ac_cv_attribute_warn_unused=no)]) -AC_CACHE_CHECK(for __attribute__((noreturn)), - ac_cv_attribute_noreturn, - [AC_TRY_COMPILE([], - [void f(void) __attribute__((noreturn));], - ac_cv_attribute_noreturn=yes, - ac_cv_attribute_noreturn=no)]) - dnl End of C++ language/feature checks AC_LANG_C @@ -4283,8 +3874,7 @@ if test "$ac_cv_i18n_lc_messages" = yes; then fi AC_HAVE_FUNCS(localeconv) - -fi # SKIP_COMPILER_CHECKS +fi # ! SKIP_COMPILER_CHECKS TARGET_XPCOM_ABI= if test -n "${CPU_ARCH}" -a -n "${TARGET_COMPILER_ABI}"; then @@ -4316,12 +3906,6 @@ else AC_DEFINE(NS_WARN_UNUSED_RESULT,) fi -if test "$ac_cv_attribute_noreturn" = yes ; then - AC_DEFINE(NS_NORETURN, [__attribute__((noreturn))]) -else - AC_DEFINE(NS_NORETURN,) -fi - dnl We can't run TRY_COMPILE tests on Windows, so hard-code some dnl features that Windows actually does support. @@ -4392,6 +3976,25 @@ if test -n "$MOZ_NATIVE_NSPR"; then CFLAGS=$_SAVE_CFLAGS fi +dnl ======================================================== +dnl system libffi Support +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(system-ffi, +[ --enable-system-ffi Use system libffi (located with pkgconfig)], + MOZ_NATIVE_FFI=1 ) + +if test -n "$MOZ_NATIVE_FFI"; then + # Vanilla libffi 3.0.9 needs a few patches from upcoming version 3.0.10 + # for non-GCC compilers. + if test -z "$GNU_CC"; then + PKG_CHECK_MODULES(MOZ_FFI, libffi > 3.0.9) + else + PKG_CHECK_MODULES(MOZ_FFI, libffi >= 3.0.9) + fi +fi + +AC_SUBST(MOZ_NATIVE_FFI) + dnl ======================================================== dnl = dnl = Application @@ -4400,16 +4003,13 @@ dnl ======================================================== MOZ_ARG_HEADER(Application) -BUILD_STATIC_LIBS= ENABLE_TESTS=1 -MOZ_THUMB2= USE_ARM_KUSER= case "${target}" in - arm-android-eabi) + arm*-android*|arm*-linuxandroid*) USE_ARM_KUSER=1 - MOZ_THUMB2=1 ;; esac @@ -4427,7 +4027,7 @@ fi dnl ======================================================== dnl = dnl = Components & Features -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Components and Features) @@ -4455,85 +4055,10 @@ dnl = dnl ======================================================== MOZ_ARG_HEADER(Individual module options) -dnl Setup default CPU arch for arm target -case "$target_cpu" in - arm*) - MOZ_ARM_ARCH=armv7 - ;; -esac -dnl ======================================================== -dnl = Enable building the Thumb2 instruction set -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(thumb2, - [ --enable-thumb2 Enable Thumb2 instruction set (implies ARMv7)], - MOZ_THUMB2=1, - MOZ_THUMB2=) -if test -n "$MOZ_THUMB2"; then - MOZ_ARM_ARCH=armv7 -fi - -dnl ======================================================== -dnl = Enable building for ARM specific CPU features -dnl ======================================================== -MOZ_ARG_WITH_STRING(cpu-arch, -[ --with-cpu-arch=arch Use specific arm architecture CPU features, default armv7], - MOZ_ARM_ARCH=$withval) - -if test -n "$MOZ_THUMB2"; then - case "$target_cpu" in - arm*) - if test "$MOZ_ARM_ARCH" != "armv7"; then - AC_MSG_ERROR([--enable-thumb2 is not compatible with cpu-arch=$MOZ_ARM_ARCH]) - fi - if test "$GNU_CC"; then - AC_DEFINE(MOZ_THUMB2) - AC_DEFINE(MOZ_ARM_ARCH) - CFLAGS="$CFLAGS -march=armv7-a -mthumb -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - CXXFLAGS="$CXXFLAGS -march=armv7-a -mthumb -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - ASFLAGS="$ASFLAGS -march=armv7-a -mthumb -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - else - AC_MSG_ERROR([--enable-thumb2 is not supported for non-GNU toolchains]) - fi - ;; - *) - AC_MSG_ERROR([--enable-thumb2 is not supported for non-ARM CPU architectures]) - ;; - esac -elif test "$MOZ_ARM_ARCH" = "armv7"; then - case "$target_cpu" in - arm*) - if test "$GNU_CC"; then - AC_DEFINE(MOZ_ARM_ARCH) - CFLAGS="$CFLAGS -march=armv7-a -marm -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - CXXFLAGS="$CXXFLAGS -march=armv7-a -marm -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - ASFLAGS="$ASFLAGS -march=armv7-a -marm -mfloat-abi=softfp $MOZ_ARM_VFP_FLAGS" - else - AC_MSG_ERROR([--with-cpu-arch=armv7 is not supported for non-GNU toolchains]) - fi - ;; - *) - AC_MSG_ERROR([--with-cpu-arch=armv7 is not supported for non-ARM CPU architectures]) - ;; - esac -else - case "$target_cpu" in - arm*) - if test "$GNU_CC"; then - CFLAGS="$CFLAGS -march=armv5te -mthumb-interwork -msoft-float" - CXXFLAGS="$CXXFLAGS -march=armv5te -mthumb-interwork -msoft-float" - ASFLAGS="$ASFLAGS -march=armv5te -mthumb-interwork -msoft-float" - fi - ;; - esac -fi - -AC_SUBST(MOZ_THUMB2) -AC_SUBST(MOZ_ARM_ARCH) - dnl ======================================================== dnl = dnl = Debugging Options -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Debugging and Optimizations) @@ -4574,7 +4099,7 @@ if test -n "$MOZ_DEBUG"; then AC_MSG_CHECKING([for valid debug flags]) _SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS" - AC_TRY_COMPILE([#include ], + AC_TRY_COMPILE([#include ], [printf("Hello World\n");], _results=yes, _results=no) @@ -4605,12 +4130,14 @@ else MOZ_OPTIMIZE= fi ], MOZ_OPTIMIZE=1) +MOZ_SET_FRAMEPTR_FLAGS + if test "$COMPILE_ENVIRONMENT"; then if test -n "$MOZ_OPTIMIZE"; then AC_MSG_CHECKING([for valid optimization flags]) _SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $MOZ_OPTIMIZE_FLAGS" - AC_TRY_COMPILE([#include ], + AC_TRY_COMPILE([#include ], [printf("Hello World\n");], _results=yes, _results=no) @@ -4623,9 +4150,11 @@ fi fi # COMPILE_ENVIRONMENT AC_SUBST(MOZ_OPTIMIZE) +AC_SUBST(MOZ_FRAMEPTR_FLAGS) AC_SUBST(MOZ_OPTIMIZE_FLAGS) AC_SUBST(MOZ_OPTIMIZE_LDFLAGS) AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK) +AC_SUBST(MOZ_PGO_OPTIMIZE_FLAGS) dnl ======================================================== dnl = Enable generation of debug symbols @@ -4653,14 +4182,14 @@ if test -n "$MOZ_DEBUG" -o -n "$MOZ_DEBUG_SYMBOLS"; then fi dnl ======================================================== -dnl = Disable any treating of compile warnings as errors +dnl = Enable any treating of compile warnings as errors dnl ======================================================== MOZ_ARG_DISABLE_BOOL(warnings-as-errors, -[ --disable-warnings-as-errors - Disable treating of warnings as errors], - MOZ_DISABLE_WARNINGS_AS_ERRORS=1, - MOZ_DISABLE_WARNINGS_AS_ERRORS= ) -if test "$MOZ_DISABLE_WARNINGS_AS_ERRORS"; then +[ --enable-warnings-as-errors + Enable treating of warnings as errors], + MOZ_ENABLE_WARNINGS_AS_ERRORS=1, + MOZ_ENABLE_WARNINGS_AS_ERRORS=) +if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then WARNINGS_AS_ERRORS='' fi @@ -4703,7 +4232,7 @@ fi if test "$MOZ_MEMORY"; then - dnl Don't try to run compiler tests on Windows + dnl Don't try to run compiler tests on Windows if test "$OS_ARCH" = "WINNT"; then if test -z "$HAVE_64BIT_OS"; then AC_DEFINE_UNQUOTED([MOZ_MEMORY_SIZEOF_PTR_2POW], 2) @@ -4739,6 +4268,10 @@ if test "$MOZ_MEMORY"; then *-*freebsd*) AC_DEFINE(MOZ_MEMORY_BSD) ;; + *-android*|*-linuxandroid*) + AC_DEFINE(MOZ_MEMORY_LINUX) + AC_DEFINE(MOZ_MEMORY_ANDROID) + ;; *-*linux*) AC_DEFINE(MOZ_MEMORY_LINUX) ;; @@ -4750,11 +4283,7 @@ if test "$MOZ_MEMORY"; then ;; *-mingw*) AC_DEFINE(MOZ_MEMORY_WINDOWS) - # the interesting bits will get passed down in MOZ_MEMORY_LDFLAGS - ;; - *-android*) - AC_DEFINE(MOZ_MEMORY_LINUX) - AC_DEFINE(MOZ_MEMORY_ANDROID) + # the interesting bits will get passed down in MOZ_GLUE_LDFLAGS ;; *) AC_MSG_ERROR([--enable-jemalloc not supported on ${target}]) @@ -4769,7 +4298,8 @@ if test "$MOZ_MEMORY"; then fi fi AC_SUBST(MOZ_MEMORY) -AC_SUBST(MOZ_MEMORY_LDFLAGS) +AC_SUBST(MOZ_GLUE_LDFLAGS) +AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS) dnl ======================================================== dnl = Use malloc wrapper lib @@ -4781,8 +4311,13 @@ MOZ_ARG_ENABLE_BOOL(wrap-malloc, if test -n "$_WRAP_MALLOC"; then if test "$GNU_CC"; then - WRAP_MALLOC_CFLAGS="${LDFLAGS} -Wl,--wrap -Wl,malloc -Wl,--wrap -Wl,calloc -Wl,--wrap -Wl,valloc -Wl,--wrap -Wl,free -Wl,--wrap -Wl,realloc -Wl,--wrap -Wl,memalign -Wl,--wrap -Wl,__builtin_new -Wl,--wrap -Wl,__builtin_vec_new -Wl,--wrap -Wl,__builtin_delete -Wl,--wrap -Wl,__builtin_vec_delete -Wl,--wrap -Wl,PR_Free -Wl,--wrap -Wl,PR_Malloc -Wl,--wrap -Wl,PR_Calloc -Wl,--wrap -Wl,PR_Realloc -Wl,--wrap -Wl,strdup -Wl,--wrap -Wl,strndup -Wl,--wrap -Wl,posix_memalign" - MKSHLIB='$(CXX) $(DSO_LDOPTS) $(WRAP_MALLOC_CFLAGS) $(WRAP_MALLOC_LIB) -o $@' + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=malloc,--wrap=calloc,--wrap=valloc,--wrap=free,--wrap=realloc,--wrap=memalign" + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=__builtin_new,--wrap=__builtin_vec_new,--wrap=__builtin_delete,--wrap=__builtin_vec_delete" + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=PR_Free,--wrap=PR_Malloc,--wrap=PR_Calloc,--wrap=PR_Realloc" + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=strdup,--wrap=strndup" + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=posix_memalign,--wrap=malloc_usable_size" + else + AC_MSG_ERROR([--enable-wrap-malloc is not supported for non-GNU toolchains]) fi fi @@ -4791,7 +4326,7 @@ dnl = Location of malloc wrapper lib dnl ======================================================== MOZ_ARG_WITH_STRING(wrap-malloc, [ --with-wrap-malloc=DIR Location of malloc wrapper library], - WRAP_MALLOC_LIB=$withval) + WRAP_LDFLAGS="${WRAP_LDFLAGS} $withval") dnl ======================================================== dnl = Use JS Call tracing @@ -4805,28 +4340,36 @@ if test -n "$MOZ_TRACE_JSCALLS"; then fi dnl ======================================================== -dnl = Use TraceVis +dnl = Use incremental GC dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(tracevis, -[ --enable-tracevis Enable TraceVis tracing tool (default=no)], - MOZ_TRACEVIS=1, - MOZ_TRACEVIS= ) -if test -n "$MOZ_TRACEVIS"; then - AC_DEFINE(MOZ_TRACEVIS) - if test -z "$ENABLE_TRACEJIT"; then - AC_MSG_ERROR([--enable-tracevis is incompatible with --disable-tracejit]) - fi +JSGC_INCREMENTAL=1 +MOZ_ARG_DISABLE_BOOL(gcincremental, +[ --disable-gcincremental Disable incremental GC], + JSGC_INCREMENTAL= ) +if test -n "$JSGC_INCREMENTAL"; then + AC_DEFINE(JSGC_INCREMENTAL) +fi + +dnl ======================================================== +dnl = Use generational GC +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(gcgenerational, +[ --enable-gcgenerational Enable generational GC], + JSGC_GENERATIONAL=1, + JSGC_GENERATIONAL= ) +if test -n "$JSGC_GENERATIONAL"; then + AC_DEFINE(JSGC_GENERATIONAL) fi dnl ======================================================== -dnl = Use GCTimer +dnl = Perform moving GC stack rooting analysis dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(gctimer, -[ --enable-gctimer Enable GC timer (default=no)], - MOZ_GCTIMER=1, - MOZ_GCTIMER= ) -if test -n "$MOZ_GCTIMER"; then - AC_DEFINE(MOZ_GCTIMER) +MOZ_ARG_ENABLE_BOOL(gcrootanalysis, +[ --enable-root-analysis Enable moving GC stack root analysis], + JSGC_ROOT_ANALYSIS=1, + JSGC_ROOT_ANALYSIS= ) +if test -n "$JSGC_ROOT_ANALYSIS"; then + AC_DEFINE(JSGC_ROOT_ANALYSIS) fi dnl ======================================================== @@ -4837,11 +4380,13 @@ MOZ_ARG_ENABLE_BOOL(valgrind, MOZ_VALGRIND=1, MOZ_VALGRIND= ) if test -n "$MOZ_VALGRIND"; then - AC_CHECK_HEADER([valgrind/valgrind.h], [], + MOZ_CHECK_HEADER([valgrind/valgrind.h], [], AC_MSG_ERROR( [--enable-valgrind specified but Valgrind is not installed])) AC_DEFINE(MOZ_VALGRIND) + MOZ_VALGRIND=1 fi +AC_SUBST(MOZ_VALGRIND) dnl ======================================================== dnl jprof @@ -4871,10 +4416,11 @@ dnl ======================================================== dnl callgrind dnl ======================================================== MOZ_ARG_ENABLE_BOOL(callgrind, -[ --enable-callgrind Enable callgrind profiling], +[ --enable-callgrind Enable callgrind profiling. Implies --enable-profiling.], MOZ_CALLGRIND=1, MOZ_CALLGRIND= ) if test -n "$MOZ_CALLGRIND"; then + MOZ_PROFILING=1 AC_DEFINE(MOZ_CALLGRIND) fi @@ -4890,6 +4436,24 @@ if test -n "$MOZ_VTUNE"; then AC_DEFINE(MOZ_VTUNE) fi +dnl ======================================================== +dnl ETW - Event Tracing for Windows +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(ETW, +[ --enable-ETW Enable ETW (Event Tracing for Windows) event reporting], + MOZ_ETW=1, + MOZ_ETW= ) +if test -n "$MOZ_ETW"; then + AC_DEFINE(MOZ_ETW) + WINVER=600 +fi + +if test -n "$MOZ_ETW"; then + if test -z "$MOZ_WINSDK_TARGETVER"; then + AC_MSG_ERROR([--enable-ETW is only valid on Windows]) + fi +fi + dnl ======================================================== dnl Profiling dnl ======================================================== @@ -4908,6 +4472,30 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + +dnl ======================================================== +dnl Enable changes that make the shell more deterministic +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(more-deterministic, +[ --enable-more-deterministic + Enable changes that make the shell more deterministic], + JS_MORE_DETERMINISTIC=1, + JS_MORE_DETERMINISTIC= ) +if test -n "$JS_MORE_DETERMINISTIC"; then + AC_DEFINE(JS_MORE_DETERMINISTIC) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== @@ -4970,25 +4558,12 @@ MOZ_ARG_ENABLE_BOOL(install-strip, PKG_SKIP_STRIP=1) dnl ======================================================== -dnl = +dnl = dnl = Profiling and Instrumenting -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Profiling and Instrumenting) -dnl ======================================================== -dnl = Enable timeline service, which provides lightweight -dnl = instrumentation of mozilla for performance measurement. -dnl = Timeline is off by default. -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(timeline, -[ --enable-timeline Enable timeline services ], - MOZ_TIMELINE=1, - MOZ_TIMELINE= ) -if test -n "$MOZ_TIMELINE"; then - AC_DEFINE(MOZ_TIMELINE) -fi - dnl ======================================================== dnl = Support for Quantify (Windows) dnl ======================================================== @@ -5009,7 +4584,7 @@ fi # Demangle only for debug or trace-malloc builds MOZ_DEMANGLE_SYMBOLS= -if test "$HAVE_DEMANGLE" -a "$HAVE_GCC3_ABI" && test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC"; then +if test "$HAVE_DEMANGLE" && test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC"; then MOZ_DEMANGLE_SYMBOLS=1 AC_DEFINE(MOZ_DEMANGLE_SYMBOLS) fi @@ -5018,14 +4593,31 @@ AC_SUBST(MOZ_DEMANGLE_SYMBOLS) dnl ======================================================== dnl = Support for gcc stack unwinding (from gcc 3.3) dnl ======================================================== -if test "$HAVE_GCC3_ABI" -a -z "$SKIP_LIBRARY_CHECKS"; then - AC_CHECK_HEADER(unwind.h, AC_CHECK_FUNCS(_Unwind_Backtrace)) +if test -z "$SKIP_LIBRARY_CHECKS"; then + MOZ_CHECK_HEADER(unwind.h, AC_CHECK_FUNCS(_Unwind_Backtrace)) fi +dnl ======================================================== +dnl JIT observers +dnl ======================================================== + +MOZ_ARG_WITH_STRING(jitreport-granularity, +[ --jitreport-granularity=N + Default granularity at which to report JIT code + to external tools + 0 - no info + 1 - code ranges for whole functions only + 2 - per-line information + 3 - per-op information], + JITREPORT_GRANULARITY=$withval, + JITREPORT_GRANULARITY=3) + +AC_DEFINE_UNQUOTED(JS_DEFAULT_JITREPORT_GRANULARITY, $JITREPORT_GRANULARITY) + dnl ======================================================== dnl = dnl = Misc. Options -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Misc. Options) @@ -5041,61 +4633,29 @@ if test -z "$SKIP_COMPILER_CHECKS"; then dnl ======================================================== dnl = dnl = Compiler Options -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Compiler Options) dnl ======================================================== dnl Check for gcc -pipe support dnl ======================================================== -AC_MSG_CHECKING([for gcc -pipe support]) -if test -n "$GNU_CC" -a -n "$GNU_CXX" -a -n "$GNU_AS"; then - echo '#include ' > dummy-hello.c - echo 'int main() { printf("Hello World\n"); exit(0); }' >> dummy-hello.c - ${CC} -S dummy-hello.c -o dummy-hello.s 2>&5 - cat dummy-hello.s 2> /dev/null | ${AS_BIN} -o dummy-hello.S - 2>&5 - if test $? = 0; then - _res_as_stdin="yes" - else - _res_as_stdin="no" - fi - if test "$_res_as_stdin" = "yes"; then - _SAVE_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS -pipe" - AC_TRY_COMPILE( [ #include ], - [printf("Hello World\n");], - [_res_gcc_pipe="yes"], - [_res_gcc_pipe="no"] ) - CFLAGS=$_SAVE_CFLAGS - fi - if test "$_res_as_stdin" = "yes" -a "$_res_gcc_pipe" = "yes"; then - _res="yes"; - CFLAGS="$CFLAGS -pipe" - CXXFLAGS="$CXXFLAGS -pipe" - else - _res="no" - fi - rm -f dummy-hello.c dummy-hello.s dummy-hello.S dummy-hello a.out - AC_MSG_RESULT([$_res]) +AC_MSG_CHECKING([for -pipe support]) +if test -n "$GNU_CC" -a -n "$GNU_CXX"; then + dnl Any gcc that supports firefox supports -pipe. + CFLAGS="$CFLAGS -pipe" + CXXFLAGS="$CXXFLAGS -pipe" + AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi dnl ======================================================== -dnl Profile guided optimization +dnl Profile guided optimization (gcc checks) dnl ======================================================== dnl Test for profiling options dnl Under gcc 3.4+, use -fprofile-generate/-fprofile-use -dnl Provide a switch to disable PGO even when called via profiledbuild. -MOZ_ARG_DISABLE_BOOL(profile-guided-optimization, -[ --disable-profile-guided-optimization - Don't build with PGO even if called via make profiledbuild], -MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE=1, -MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE=) - -AC_SUBST(MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE) - _SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fprofile-generate -fprofile-correction" @@ -5143,7 +4703,7 @@ MOZ_ARG_DISABLE_BOOL(pedantic, _PEDANTIC= ) if test "$_PEDANTIC"; then _SAVE_CXXFLAGS=$CXXFLAGS - CXXFLAGS="$CXXFLAGS ${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic ${_COMPILER_PREFIX}-Wno-long-long" + CXXFLAGS="$CXXFLAGS -pedantic ${_WARNINGS_CXXFLAGS} -Wno-long-long" AC_MSG_CHECKING([whether C++ compiler has -pedantic long long bug]) AC_TRY_COMPILE([$configure_static_assert_macros], [CONFIGURE_STATIC_ASSERT(sizeof(long long) == 8)], @@ -5153,8 +4713,8 @@ if test "$_PEDANTIC"; then case "$result" in no) - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} ${_COMPILER_PREFIX}-pedantic ${_COMPILER_PREFIX}-Wno-long-long" - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic ${_COMPILER_PREFIX}-Wno-long-long" + _WARNINGS_CFLAGS="-pedantic ${_WARNINGS_CFLAGS} -Wno-long-long" + _WARNINGS_CXXFLAGS="-pedantic ${_WARNINGS_CXXFLAGS} -Wno-long-long" ;; yes) AC_MSG_ERROR([Your compiler appears to have a known bug where long long is miscompiled when using -pedantic. Reconfigure using --disable-pedantic. ]) @@ -5162,36 +4722,6 @@ if test "$_PEDANTIC"; then esac fi -dnl ======================================================== -dnl Test for correct temporary object destruction order -dnl ======================================================== -dnl We want to make sure the compiler follows the C++ spec here as -dnl xpcom and the string classes depend on it (bug 235381). -AC_MSG_CHECKING([for correct temporary object destruction order]) -AC_TRY_RUN([ class A { - public: A(int& x) : mValue(x) {} - ~A() { mValue--; } - operator char**() { return 0; } - private: int& mValue; - }; - void func(char **arg) {} - int m=2; - void test() { - func(A(m)); - if (m==1) m = 0; - } - int main() { - test(); - return(m); - } - ], - result="yes", result="no", result="maybe") -AC_MSG_RESULT([$result]) - -if test "$result" = "no"; then - AC_MSG_ERROR([Your compiler does not follow the C++ specification for temporary object destruction order.]) -fi - dnl ======================================================== dnl Autoconf test for gcc 2.7.2.x (and maybe others?) so that we don't dnl provide non-const forms of the operator== for comparing nsCOMPtrs to @@ -5208,13 +4738,13 @@ AC_CACHE_CHECK(for correct overload resolution with const and templates, public: T* myPtr; }; - + template int operator==(const Pointer& rhs, U* lhs) { return rhs.myPtr == lhs; } - + template int operator==(const Pointer& rhs, const U* lhs) { @@ -5246,8 +4776,7 @@ AC_CACHE_CHECK(for tm_zone tm_gmtoff in struct tm, if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then AC_DEFINE(HAVE_TM_ZONE_TM_GMTOFF) fi - -fi # SKIP_COMPILER_CHECKS +fi # ! SKIP_COMPILER_CHECKS dnl ======================================================== dnl C++ rtti @@ -5266,31 +4795,7 @@ fi AC_SUBST(_MOZ_RTTI_FLAGS_ON) -dnl ======================================================== -dnl C++ exceptions (g++/VC/irix6/Sun only - for now) -dnl Should be smarter and check that the compiler does indeed have exceptions -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(cpp-exceptions, -[ --enable-cpp-exceptions Enable C++ exceptions ], -[ _MOZ_CPP_EXCEPTIONS=1 ], -[ _MOZ_CPP_EXCEPTIONS= ]) - -if test "$_MOZ_CPP_EXCEPTIONS"; then - _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_ON - AC_DEFINE(MOZ_CPP_EXCEPTIONS) -else - _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_OFF -fi - -AC_SUBST(_MOZ_EXCEPTIONS_FLAGS_ON) - -# Irix & OSF native compilers do not like exception declarations -# when exceptions are disabled -if test -n "$MIPSPRO_CXX" -o -n "$COMPAQ_CXX" -o -n "$VACPP"; then - AC_DEFINE(CPP_THROW_NEW, []) -else - AC_DEFINE(CPP_THROW_NEW, [throw()]) -fi +AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C dnl ======================================================== @@ -5375,7 +4880,7 @@ else else echo '#include ' > dummy-hello.c changequote(,) - CL_INCLUDES_PREFIX=`"${CC}" -showIncludes -c -Fonul dummy-hello.c 2>&1 | sed -ne 's/^\([^:]*:[^:]*:\).*stdio.h$/\1/p'` + CL_INCLUDES_PREFIX=`${CC} -showIncludes -c -Fonul dummy-hello.c 2>&1 | sed -ne 's/^\([^:]*:[^:]*:\).*stdio.h$/\1/p'` changequote([,]) if test -z "$CL_INCLUDES_PREFIX"; then AC_MSG_ERROR([Cannot find cl -showIncludes prefix.]) @@ -5398,18 +4903,6 @@ AC_SUBST(CC_WRAPPER) AC_SUBST(CXX_WRAPPER) -dnl ======================================================== -dnl = -dnl = Static Build Options -dnl = -dnl ======================================================== -MOZ_ARG_HEADER(Static build options) - -MOZ_ARG_ENABLE_BOOL(static, -[ --enable-static Enable building of internal static libs], - BUILD_STATIC_LIBS=1, - BUILD_STATIC_LIBS=) - dnl ======================================================== dnl = Link js shell to system readline dnl ======================================================== @@ -5426,10 +4919,6 @@ case "$target" in *-mingw*) NO_EDITLINE=1 ;; -*-symbian*) - NO_EDITLINE=1 - JS_DISABLE_SHELL=1 - ;; *) ;; esac @@ -5455,7 +4944,7 @@ AC_SUBST(EDITLINE_LIBS) dnl ======================================================== dnl = dnl = Standalone module options -dnl = +dnl = dnl ======================================================== MOZ_ARG_HEADER(Standalone module options (Not for building Mozilla)) @@ -5505,11 +4994,11 @@ AC_SUBST(AS_DASH_C_FLAG) AC_SUBST(LD) AC_SUBST(RC) AC_SUBST(RCFLAGS) +AC_SUBST(MC) AC_SUBST(WINDRES) AC_SUBST(IMPLIB) AC_SUBST(FILTER) AC_SUBST(BIN_FLAGS) -AC_SUBST(NS_USE_NATIVE) AC_SUBST(MOZ_JS_LIBS) AC_SUBST(MOZ_PSM) AC_SUBST(MOZ_DEBUG) @@ -5524,11 +5013,11 @@ AC_SUBST(MOZ_JPROF) AC_SUBST(MOZ_SHARK) AC_SUBST(MOZ_CALLGRIND) AC_SUBST(MOZ_VTUNE) +AC_SUBST(MOZ_ETW) AC_SUBST(MOZ_PROFILING) AC_SUBST(MOZ_QUANTIFY) AC_SUBST(LIBICONV) -AC_SUBST(BUILD_STATIC_LIBS) AC_SUBST(ENABLE_TESTS) AC_SUBST(ENABLE_STRIP) @@ -5546,13 +5035,11 @@ AC_SUBST(MOZ_OS2_TOOLS) AC_SUBST(MOZ_POST_DSO_LIB_COMMAND) AC_SUBST(MOZ_POST_PROGRAM_COMMAND) -AC_SUBST(MOZ_TIMELINE) AC_SUBST(MOZ_APP_NAME) AC_SUBST(MOZ_APP_DISPLAYNAME) AC_SUBST(MOZ_APP_UA_NAME) AC_SUBST(MOZ_APP_VERSION) -AC_SUBST(MOZ_UA_FIREFOX_VERSION) AC_SUBST(FIREFOX_VERSION) AC_SUBST(MOZ_PKG_SPECIAL) @@ -5572,7 +5059,6 @@ CFLAGS=`echo \ CXXFLAGS=`echo \ $_MOZ_RTTI_FLAGS \ - $_MOZ_EXCEPTIONS_FLAGS \ $_WARNINGS_CXXFLAGS \ $CXXFLAGS` @@ -5628,8 +5114,7 @@ AC_SUBST(OS_TEST) AC_SUBST(CPU_ARCH) AC_SUBST(INTEL_ARCHITECTURE) -AC_SUBST(WRAP_MALLOC_CFLAGS) -AC_SUBST(WRAP_MALLOC_LIB) +AC_SUBST(WRAP_LDFLAGS) AC_SUBST(MKSHLIB) AC_SUBST(MKCSHLIB) AC_SUBST(MKSHLIB_FORCE_ALL) @@ -5651,15 +5136,6 @@ AC_SUBST(CC_VERSION) AC_SUBST(CXX_VERSION) AC_SUBST(MSMANIFEST_TOOL) -if test "$USING_HCC"; then - CC='${topsrcdir}/build/hcc' - CC="$CC '$_OLDCC'" - CXX='${topsrcdir}/build/hcpp' - CXX="$CXX '$_OLDCXX'" - AC_SUBST(CC) - AC_SUBST(CXX) -fi - AC_MSG_CHECKING([for posix_fallocate]) AC_TRY_LINK([#define _XOPEN_SOURCE 600 #include ], @@ -5694,11 +5170,11 @@ if test "$MOZ_X11"; then fprintf(stderr, ": can't open %s\n", XDisplayName(NULL)); exit(1); } - ], [], + ], [], [ AC_MSG_ERROR([Could not compile basic X program.]) ]) CFLAGS="$_SAVE_CFLAGS" - if test ! -z "$MISSING_X"; then + if test -n "$MISSING_X"; then AC_MSG_ERROR([ Could not find the following X libraries: $MISSING_X ]); fi @@ -5781,11 +5257,20 @@ if test -n "$_NON_GLOBAL_ACDEFINES"; then done fi _EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)" - + sort confdefs.h | egrep -v "$_EGREP_PATTERN" >> $_CONFIG_TMP + if test "$?" != 0; then + AC_MSG_ERROR([Error outputting config definitions]) + fi + cat >> $_CONFIG_TMP <<\EOF +/* The c99 defining the limit macros (UINT32_MAX for example), says: + * C++ implementations should define these macros only when __STDC_LIMIT_MACROS + * is defined before is included. */ +#define __STDC_LIMIT_MACROS + #endif /* _JS_CONFDEFS_H_ */ EOF @@ -5805,6 +5290,9 @@ dnl Probably shouldn't call this manually but we always want the output of DEFS rm -f confdefs.h.save mv confdefs.h confdefs.h.save egrep -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h +if test "$?" != 0; then + AC_MSG_ERROR([Error outputting confdefs.h]) +fi AC_OUTPUT_MAKE_DEFS() MOZ_DEFINES=$DEFS AC_SUBST(MOZ_DEFINES) @@ -5814,23 +5302,32 @@ mv confdefs.h.save confdefs.h MAKEFILES=" Makefile shell/Makefile - lirasm/Makefile - jsapi-tests/Makefile - tests/Makefile config/Makefile config/autoconf.mk config/expandlibs_config.py - config/mkdepend/Makefile v8api/test/Makefile " -if test -n "$JS_NATIVE_EDITLINE"; then - MAKEFILES="$MAKEFILES -editline/Makefile -" +if test "$JS_NATIVE_EDITLINE"; then + MAKEFILES="$MAKEFILES + editline/Makefile + " +fi + +if test ! "$COMPILER_DEPEND" -a ! "$MOZ_NATIVE_MAKEDEPEND"; then + MAKEFILES="$MAKEFILES + config/mkdepend/Makefile + " +fi + +if test "$ENABLE_TESTS"; then + MAKEFILES="$MAKEFILES + jsapi-tests/Makefile + tests/Makefile + " fi -dnl +dnl dnl Run a perl script to quickly create the makefiles. dnl If it succeeds, it outputs a shell command to set CONFIG_FILES dnl for the files it cannot handle correctly. This way, config.status @@ -5859,11 +5356,11 @@ fi # Produce the js-config script at configure time; see the comments for # 'js-config' in Makefile.in. -AC_MSG_RESULT(invoking make to create js-config script) -$GMAKE js-config +AC_MSG_RESULT(invoking $MAKE to create js-config script) +$MAKE js-config # Build jsctypes if it's enabled. -if test "$JS_HAS_CTYPES"; then +if test "$JS_HAS_CTYPES" -a -z "$MOZ_NATIVE_FFI"; then # Run the libffi 'configure' script. ac_configure_args="--disable-shared --enable-static --disable-raw-api" if test "$MOZ_DEBUG"; then @@ -5874,14 +5371,10 @@ if test "$JS_HAS_CTYPES"; then fi if test "$CROSS_COMPILE"; then case "$target" in - *-android*) - CFLAGS="$ANDROID_CFLAGS" - CPPFLAGS="$ANDROID_CPPFLAGS" - LDFLAGS="$ANDROID_LDFLAGS" - + *-android*|*-linuxandroid*) export AS CC CXX CPP LD AR RANLIB STRIP CPPFLAGS CFLAGS LDFLAGS - ac_configure_args="$ac_configure_args --build=$build --host=${target_cpu}-${target_os} HOST_CC=\"$HOST_CC\"" + ac_configure_args="$ac_configure_args --build=$build --host=$target HOST_CC=\"$HOST_CC\"" ;; *) ac_configure_args="$ac_configure_args --build=$build --host=$target HOST_CC=\"$HOST_CC\" CC=\"$CC\"" @@ -5921,12 +5414,6 @@ if test "$JS_HAS_CTYPES"; then if test "$OS_ARCH" = "OS2"; then ac_configure_args="$ac_configure_args CFLAGS=-Zomf AR=emxomfar" fi - if test -n "$MOZ_THUMB2"; then - ac_configure_args="$ac_configure_args --enable-thumb2" - fi - if test -n "$MOZ_ARM_ARCH"; then - ac_configure_args="$ac_configure_args --with-cpu-arch=$MOZ_ARM_ARCH" - fi # Use a separate cache file for libffi, since it does things differently # from our configure. diff --git a/deps/mozjs/js/src/ctypes/CTypes.cpp b/deps/mozjs/js/src/ctypes/CTypes.cpp index 5cf8356247c..4ef88ee463e 100644 --- a/deps/mozjs/js/src/ctypes/CTypes.cpp +++ b/deps/mozjs/js/src/ctypes/CTypes.cpp @@ -39,6 +39,8 @@ #include "CTypes.h" #include "Library.h" #include "jsnum.h" +#include "jscompartment.h" +#include "jsobjinlines.h" #include #include @@ -59,19 +61,6 @@ using namespace std; namespace js { namespace ctypes { -/******************************************************************************* -** Helper classes -*******************************************************************************/ - -class ScopedContextThread -{ -public: - ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); } - ~ScopedContextThread() { JS_ClearContextThread(mCx); } -private: - JSContext* mCx; -}; - /******************************************************************************* ** JSAPI function prototypes *******************************************************************************/ @@ -110,6 +99,11 @@ namespace PointerType { static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp); static JSBool IsNull(JSContext* cx, uintN argc, jsval* vp); + static JSBool Increment(JSContext* cx, uintN argc, jsval* vp); + static JSBool Decrement(JSContext* cx, uintN argc, jsval* vp); + // The following is not an instance function, since we don't want to expose arbitrary + // pointer arithmetic at this moment. + static JSBool OffsetBy(JSContext* cx, intN offset, jsval* vp); } namespace ArrayType { @@ -142,7 +136,7 @@ namespace StructType { namespace FunctionType { static JSBool Create(JSContext* cx, uintN argc, jsval* vp); static JSBool ConstructData(JSContext* cx, JSObject* typeObj, - JSObject* dataObj, JSObject* fnObj, JSObject* thisObj); + JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal); static JSBool Call(JSContext* cx, uintN argc, jsval* vp); @@ -178,10 +172,10 @@ namespace CData { // Int64Base provides functions common to Int64 and UInt64. namespace Int64Base { - JSObject* Construct(JSContext* cx, JSObject* proto, JSUint64 data, + JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data, bool isUnsigned); - JSUint64 GetInt(JSContext* cx, JSObject* obj); + uint64_t GetInt(JSObject* obj); JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp, bool isUnsigned); @@ -335,6 +329,8 @@ static JSPropertySpec sPointerProps[] = { static JSFunctionSpec sPointerInstanceFunctions[] = { JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS), + JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS), + JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), JS_FS_END }; @@ -456,6 +452,7 @@ static JSFunctionSpec sUInt64Functions[] = { static JSFunctionSpec sModuleFunctions[] = { JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), + JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS), JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS), JS_FS_END }; @@ -481,16 +478,14 @@ Align(size_t val, size_t align) } static ABICode -GetABICode(JSContext* cx, JSObject* obj) +GetABICode(JSObject* obj) { // make sure we have an object representing a CABI class, // and extract the enumerated class type from the reserved slot. - if (JS_GET_CLASS(cx, obj) != &sCABIClass) + if (JS_GetClass(obj) != &sCABIClass) return INVALID_ABI; - jsval result; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ABICODE, &result)); - + jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE); return ABICode(JSVAL_TO_INT(result)); } @@ -538,7 +533,7 @@ InitCTypeClass(JSContext* cx, JSObject* parent) return NULL; JSObject* ctor = JS_GetFunctionObject(fun); - JSObject* fnproto = JS_GetPrototype(cx, ctor); + JSObject* fnproto = JS_GetPrototype(ctor); JS_ASSERT(ctor); JS_ASSERT(fnproto); @@ -618,8 +613,7 @@ DefineABIConstant(JSContext* cx, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); if (!obj) return false; - if (!JS_SetReservedSlot(cx, obj, SLOT_ABICODE, INT_TO_JSVAL(code))) - return false; + JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code)); return JS_FreezeObject(cx, obj); } @@ -638,7 +632,7 @@ InitTypeConstructor(JSContext* cx, JSObject*& typeProto, JSObject*& dataProto) { - JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call, + JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call, spec.nargs, spec.flags); if (!fun) return false; @@ -669,8 +663,7 @@ InitTypeConstructor(JSContext* cx, // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of // the type constructor, for faster lookup. - if (!JS_SetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto))) - return false; + js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); // Create an object to serve as the common ancestor for all CData objects // created from the given type constructor. This has ctypes.CData.prototype @@ -690,6 +683,9 @@ InitTypeConstructor(JSContext* cx, if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) return false; + // Link the type prototype to the data prototype. + JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); + if (!JS_FreezeObject(cx, obj) || //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! !JS_FreezeObject(cx, typeProto)) @@ -718,32 +714,32 @@ InitInt64Class(JSContext* cx, if (!JS_FreezeObject(cx, ctor)) return NULL; - // Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join' - // function. - jsval join; - ASSERT_OK(JS_GetProperty(cx, ctor, "join", &join)); - if (!JS_SetReservedSlot(cx, JSVAL_TO_OBJECT(join), SLOT_FN_INT64PROTO, - OBJECT_TO_JSVAL(prototype))) + // Redefine the 'join' function as an extended native and stash + // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. + JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); + JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; + JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, + 2, CTYPESFN_FLAGS); + if (!fun) return NULL; + js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, + OBJECT_TO_JSVAL(prototype)); + if (!JS_FreezeObject(cx, prototype)) return NULL; return prototype; } -static JSBool -AttachProtos(JSContext* cx, JSObject* proto, JSObject** protos) +static void +AttachProtos(JSObject* proto, JSObject** protos) { // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' // to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot - // of [[Class]] "CTypeProto".) - for (JSUint32 i = 0; i <= SLOT_UINT64PROTO; ++i) { - if (!JS_SetReservedSlot(cx, proto, i, OBJECT_TO_JSVAL(protos[i]))) - return false; - } - - return true; + // of [[Class]] "CTypeProto" that we fill in this automated manner.) + for (uint32_t i = 0; i <= SLOT_UINT64PROTO; ++i) + JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); } JSBool @@ -781,6 +777,10 @@ InitTypeClasses(JSContext* cx, JSObject* parent) if (!CDataProto) return false; + // Link CTypeProto to CDataProto. + JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, + OBJECT_TO_JSVAL(CDataProto)); + // Create and attach the special class constructors: ctypes.PointerType, // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. // Each of these constructors 'c' has, respectively: @@ -857,12 +857,11 @@ InitTypeClasses(JSContext* cx, JSObject* parent) // Attach the prototypes just created to each of ctypes.CType.prototype, // and the special type constructors, so we can access them when constructing // instances of those types. - if (!AttachProtos(cx, CTypeProto, protos) || - !AttachProtos(cx, protos[SLOT_POINTERPROTO], protos) || - !AttachProtos(cx, protos[SLOT_ARRAYPROTO], protos) || - !AttachProtos(cx, protos[SLOT_STRUCTPROTO], protos) || - !AttachProtos(cx, protos[SLOT_FUNCTIONPROTO], protos)) - return false; + AttachProtos(CTypeProto, protos); + AttachProtos(protos[SLOT_POINTERPROTO], protos); + AttachProtos(protos[SLOT_ARRAYPROTO], protos); + AttachProtos(protos[SLOT_STRUCTPROTO], protos); + AttachProtos(protos[SLOT_FUNCTIONPROTO], protos); // Attach objects representing ABI constants. if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) || @@ -914,19 +913,18 @@ InitTypeClasses(JSContext* cx, JSObject* parent) } bool -IsCTypesGlobal(JSContext* cx, JSObject* obj) +IsCTypesGlobal(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sCTypesGlobalClass; + return JS_GetClass(obj) == &sCTypesGlobalClass; } // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'. JSCTypesCallbacks* -GetCallbacks(JSContext* cx, JSObject* obj) +GetCallbacks(JSObject* obj) { - JS_ASSERT(IsCTypesGlobal(cx, obj)); + JS_ASSERT(IsCTypesGlobal(obj)); - jsval result; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_CALLBACKS, &result)); + jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS); if (JSVAL_IS_VOID(result)) return NULL; @@ -959,17 +957,15 @@ JS_InitCTypesClass(JSContext* cx, JSObject* global) return JS_FreezeObject(cx, ctypes); } -JS_PUBLIC_API(JSBool) -JS_SetCTypesCallbacks(JSContext* cx, - JSObject* ctypesObj, +JS_PUBLIC_API(void) +JS_SetCTypesCallbacks(JSObject* ctypesObj, JSCTypesCallbacks* callbacks) { JS_ASSERT(callbacks); - JS_ASSERT(IsCTypesGlobal(cx, ctypesObj)); + JS_ASSERT(IsCTypesGlobal(ctypesObj)); // Set the callbacks on a reserved slot. - return JS_SetReservedSlot(cx, ctypesObj, SLOT_CALLBACKS, - PRIVATE_TO_JSVAL(callbacks)); + JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks)); } JS_END_EXTERN_C @@ -1008,11 +1004,34 @@ struct ConvertImpl { // MSVC can't perform double to unsigned __int64 conversion when the // double is greater than 2^63 - 1. Help it along a little. template<> -struct ConvertImpl { - static JS_ALWAYS_INLINE JSUint64 Convert(jsdouble d) { +struct ConvertImpl { + static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) { return d > 0x7fffffffffffffffui64 ? - JSUint64(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : - JSUint64(d); + uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : + uint64_t(d); + } +}; +#endif + +// C++ doesn't guarantee that exact values are the only ones that will +// round-trip. In fact, on some platforms, including SPARC, there are pairs of +// values, a uint64_t and a double, such that neither value is exactly +// representable in the other type, but they cast to each other. +#ifdef SPARC +// Simulate x86 overflow behavior +template<> +struct ConvertImpl { + static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) { + return d >= 0xffffffffffffffff ? + 0x8000000000000000 : uint64_t(d); + } +}; + +template<> +struct ConvertImpl { + static JS_ALWAYS_INLINE int64_t Convert(jsdouble d) { + return d >= 0x7fffffffffffffff ? + 0x8000000000000000 : int64_t(d); } }; #endif @@ -1169,13 +1188,13 @@ jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) } if (!JSVAL_IS_PRIMITIVE(val)) { JSObject* obj = JSVAL_TO_OBJECT(val); - if (CData::IsCData(cx, obj)) { - JSObject* typeObj = CData::GetCType(cx, obj); - void* data = CData::GetData(cx, obj); + if (CData::IsCData(obj)) { + JSObject* typeObj = CData::GetCType(obj); + void* data = CData::GetData(obj); // Check whether the source type is always representable, with exact // precision, by the target type. If it is, convert the value. - switch (CType::GetTypeCode(cx, typeObj)) { + switch (CType::GetTypeCode(typeObj)) { #define DEFINE_INT_TYPE(name, fromType, ffiType) \ case TYPE_##name: \ if (!IsAlwaysExact()) \ @@ -1203,15 +1222,15 @@ jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) } } - if (Int64::IsInt64(cx, obj)) { + if (Int64::IsInt64(obj)) { // Make sure the integer fits in IntegerType. - JSInt64 i = Int64Base::GetInt(cx, obj); + int64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } - if (UInt64::IsUInt64(cx, obj)) { + if (UInt64::IsUInt64(obj)) { // Make sure the integer fits in IntegerType. - JSUint64 i = Int64Base::GetInt(cx, obj); + uint64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } @@ -1250,13 +1269,13 @@ jsvalToFloat(JSContext *cx, jsval val, FloatType* result) } if (!JSVAL_IS_PRIMITIVE(val)) { JSObject* obj = JSVAL_TO_OBJECT(val); - if (CData::IsCData(cx, obj)) { - JSObject* typeObj = CData::GetCType(cx, obj); - void* data = CData::GetData(cx, obj); + if (CData::IsCData(obj)) { + JSObject* typeObj = CData::GetCType(obj); + void* data = CData::GetData(obj); // Check whether the source type is always representable, with exact // precision, by the target type. If it is, convert the value. - switch (CType::GetTypeCode(cx, typeObj)) { + switch (CType::GetTypeCode(typeObj)) { #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \ case TYPE_##name: \ if (!IsAlwaysExact()) \ @@ -1375,15 +1394,15 @@ jsvalToBigInteger(JSContext* cx, // Allow conversion from an Int64 or UInt64 object directly. JSObject* obj = JSVAL_TO_OBJECT(val); - if (UInt64::IsUInt64(cx, obj)) { + if (UInt64::IsUInt64(obj)) { // Make sure the integer fits in IntegerType. - JSUint64 i = Int64Base::GetInt(cx, obj); + uint64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } - if (Int64::IsInt64(cx, obj)) { + if (Int64::IsInt64(obj)) { // Make sure the integer fits in IntegerType. - JSInt64 i = Int64Base::GetInt(cx, obj); + int64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } } @@ -1431,15 +1450,15 @@ jsidToBigInteger(JSContext* cx, // Allow conversion from an Int64 or UInt64 object directly. JSObject* obj = JSID_TO_OBJECT(val); - if (UInt64::IsUInt64(cx, obj)) { + if (UInt64::IsUInt64(obj)) { // Make sure the integer fits in IntegerType. - JSUint64 i = Int64Base::GetInt(cx, obj); + uint64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } - if (Int64::IsInt64(cx, obj)) { + if (Int64::IsInt64(obj)) { // Make sure the integer fits in IntegerType. - JSInt64 i = Int64Base::GetInt(cx, obj); + int64_t i = Int64Base::GetInt(obj); return ConvertExact(i, result); } } @@ -1474,7 +1493,7 @@ SizeTojsval(JSContext* cx, size_t size, jsval* result) // Forcefully convert val to IntegerType when explicitly requested. template static bool -jsvalToIntegerExplicit(JSContext* cx, jsval val, IntegerType* result) +jsvalToIntegerExplicit(jsval val, IntegerType* result) { JS_STATIC_ASSERT(numeric_limits::is_exact); @@ -1487,13 +1506,13 @@ jsvalToIntegerExplicit(JSContext* cx, jsval val, IntegerType* result) if (!JSVAL_IS_PRIMITIVE(val)) { // Convert Int64 and UInt64 values by C-style cast. JSObject* obj = JSVAL_TO_OBJECT(val); - if (Int64::IsInt64(cx, obj)) { - JSInt64 i = Int64Base::GetInt(cx, obj); + if (Int64::IsInt64(obj)) { + int64_t i = Int64Base::GetInt(obj); *result = IntegerType(i); return true; } - if (UInt64::IsUInt64(cx, obj)) { - JSUint64 i = Int64Base::GetInt(cx, obj); + if (UInt64::IsUInt64(obj)) { + uint64_t i = Int64Base::GetInt(obj); *result = IntegerType(i); return true; } @@ -1531,23 +1550,23 @@ jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) } if (!JSVAL_IS_PRIMITIVE(val)) { JSObject* obj = JSVAL_TO_OBJECT(val); - if (Int64::IsInt64(cx, obj)) { - JSInt64 i = Int64Base::GetInt(cx, obj); + if (Int64::IsInt64(obj)) { + int64_t i = Int64Base::GetInt(obj); intptr_t p = intptr_t(i); // Make sure the integer fits in the alotted precision. - if (JSInt64(p) != i) + if (int64_t(p) != i) return false; *result = uintptr_t(p); return true; } - if (UInt64::IsUInt64(cx, obj)) { - JSUint64 i = Int64Base::GetInt(cx, obj); + if (UInt64::IsUInt64(obj)) { + uint64_t i = Int64Base::GetInt(obj); // Make sure the integer fits in the alotted precision. *result = uintptr_t(i); - return JSUint64(*result) == i; + return uint64_t(*result) == i; } } return false; @@ -1614,11 +1633,11 @@ ConvertToJS(JSContext* cx, bool ownResult, jsval* result) { - JS_ASSERT(!parentObj || CData::IsCData(cx, parentObj)); + JS_ASSERT(!parentObj || CData::IsCData(parentObj)); JS_ASSERT(!parentObj || !ownResult); JS_ASSERT(!wantPrimitive || !ownResult); - TypeCode typeCode = CType::GetTypeCode(cx, typeObj); + TypeCode typeCode = CType::GetTypeCode(typeObj); switch (typeCode) { case TYPE_void_t: @@ -1639,16 +1658,16 @@ ConvertToJS(JSContext* cx, #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ case TYPE_##name: { \ /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \ - JSUint64 value; \ + uint64_t value; \ JSObject* proto; \ if (!numeric_limits::is_signed) { \ value = *static_cast(data); \ /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \ - proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \ + proto = CType::GetProtoFromType(typeObj, SLOT_UINT64PROTO); \ } else { \ - value = JSInt64(*static_cast(data)); \ + value = int64_t(*static_cast(data)); \ /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \ - proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \ + proto = CType::GetProtoFromType(typeObj, SLOT_INT64PROTO); \ } \ \ JSObject* obj = Int64Base::Construct(cx, proto, value, \ @@ -1724,26 +1743,26 @@ ImplicitConvert(JSContext* cx, bool isArgument, bool* freePointer) { - JS_ASSERT(CType::IsSizeDefined(cx, targetType)); + JS_ASSERT(CType::IsSizeDefined(targetType)); // First, check if val is a CData object of type targetType. JSObject* sourceData = NULL; JSObject* sourceType = NULL; if (!JSVAL_IS_PRIMITIVE(val) && - CData::IsCData(cx, JSVAL_TO_OBJECT(val))) { + CData::IsCData(JSVAL_TO_OBJECT(val))) { sourceData = JSVAL_TO_OBJECT(val); - sourceType = CData::GetCType(cx, sourceData); + sourceType = CData::GetCType(sourceData); // If the types are equal, copy the buffer contained within the CData. // (Note that the buffers may overlap partially or completely.) - if (CType::TypesEqual(cx, sourceType, targetType)) { - size_t size = CType::GetSize(cx, sourceType); - memmove(buffer, CData::GetData(cx, sourceData), size); + if (CType::TypesEqual(sourceType, targetType)) { + size_t size = CType::GetSize(sourceType); + memmove(buffer, CData::GetData(sourceData), size); return true; } } - TypeCode targetCode = CType::GetTypeCode(cx, targetType); + TypeCode targetCode = CType::GetTypeCode(targetType); switch (targetCode) { case TYPE_bool: { @@ -1801,12 +1820,12 @@ ImplicitConvert(JSContext* cx, break; } - JSObject* baseType = PointerType::GetBaseType(cx, targetType); + JSObject* baseType = PointerType::GetBaseType(targetType); if (sourceData) { // First, determine if the targetType is ctypes.void_t.ptr. - TypeCode sourceCode = CType::GetTypeCode(cx, sourceType); - void* sourceBuffer = CData::GetData(cx, sourceData); - bool voidptrTarget = CType::GetTypeCode(cx, baseType) == TYPE_void_t; + TypeCode sourceCode = CType::GetTypeCode(sourceType); + void* sourceBuffer = CData::GetData(sourceData); + bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t; if (sourceCode == TYPE_pointer && voidptrTarget) { // Autoconvert if targetType is ctypes.voidptr_t. @@ -1816,8 +1835,8 @@ ImplicitConvert(JSContext* cx, if (sourceCode == TYPE_array) { // Autoconvert an array to a ctypes.void_t.ptr or to // sourceType.elementType.ptr, just like C. - JSObject* elementType = ArrayType::GetBaseType(cx, sourceType); - if (voidptrTarget || CType::TypesEqual(cx, baseType, elementType)) { + JSObject* elementType = ArrayType::GetBaseType(sourceType); + if (voidptrTarget || CType::TypesEqual(baseType, elementType)) { *static_cast(buffer) = sourceBuffer; break; } @@ -1833,13 +1852,13 @@ ImplicitConvert(JSContext* cx, if (!sourceChars) return false; - switch (CType::GetTypeCode(cx, baseType)) { + switch (CType::GetTypeCode(baseType)) { case TYPE_char: case TYPE_signed_char: case TYPE_unsigned_char: { // Convert from UTF-16 to UTF-8. size_t nbytes = - js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); + GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); if (nbytes == (size_t) -1) return false; @@ -1850,7 +1869,7 @@ ImplicitConvert(JSContext* cx, return false; } - ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, + ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, *charBuffer, &nbytes)); (*charBuffer)[nbytes] = 0; *freePointer = true; @@ -1880,8 +1899,8 @@ ImplicitConvert(JSContext* cx, return TypeError(cx, "pointer", val); } case TYPE_array: { - JSObject* baseType = ArrayType::GetBaseType(cx, targetType); - size_t targetLength = ArrayType::GetLength(cx, targetType); + JSObject* baseType = ArrayType::GetBaseType(targetType); + size_t targetLength = ArrayType::GetLength(targetType); if (JSVAL_IS_STRING(val)) { JSString* sourceString = JSVAL_TO_STRING(val); @@ -1890,13 +1909,13 @@ ImplicitConvert(JSContext* cx, if (!sourceChars) return false; - switch (CType::GetTypeCode(cx, baseType)) { + switch (CType::GetTypeCode(baseType)) { case TYPE_char: case TYPE_signed_char: case TYPE_unsigned_char: { // Convert from UTF-16 to UTF-8. size_t nbytes = - js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); + GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); if (nbytes == (size_t) -1) return false; @@ -1906,7 +1925,7 @@ ImplicitConvert(JSContext* cx, } char* charBuffer = static_cast(buffer); - ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, + ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, charBuffer, &nbytes)); if (targetLength > nbytes) @@ -1944,7 +1963,7 @@ ImplicitConvert(JSContext* cx, } // Convert into an intermediate, in case of failure. - size_t elementSize = CType::GetSize(cx, baseType); + size_t elementSize = CType::GetSize(baseType); size_t arraySize = elementSize * targetLength; AutoPtr::Array intermediate(cx->array_new(arraySize)); if (!intermediate) { @@ -1982,7 +2001,7 @@ ImplicitConvert(JSContext* cx, js::AutoObjectRooter iterroot(cx, iter); // Convert into an intermediate, in case of failure. - size_t structSize = CType::GetSize(cx, targetType); + size_t structSize = CType::GetSize(targetType); AutoPtr::Array intermediate(cx->array_new(structSize)); if (!intermediate) { JS_ReportAllocationOverflow(cx); @@ -2019,7 +2038,7 @@ ImplicitConvert(JSContext* cx, ++i; } - const FieldInfoHash* fields = StructType::GetFieldInfo(cx, targetType); + const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); if (i != fields->count()) { JS_ReportError(cx, "missing fields"); return false; @@ -2061,7 +2080,7 @@ ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer) // can throw a different one as required. JS_ClearPendingException(cx); - TypeCode type = CType::GetTypeCode(cx, targetType); + TypeCode type = CType::GetTypeCode(targetType); switch (type) { case TYPE_bool: { @@ -2076,7 +2095,7 @@ ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer) /* Convert numeric values with a C-style cast, and */ \ /* allow conversion from a base-10 or base-16 string. */ \ type result; \ - if (!jsvalToIntegerExplicit(cx, val, &result) && \ + if (!jsvalToIntegerExplicit(val, &result) && \ (!JSVAL_IS_STRING(val) || \ !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \ return TypeError(cx, #name, val); \ @@ -2127,15 +2146,15 @@ BuildTypeName(JSContext* cx, JSObject* typeObj) // pointers on the left and arrays on the right. An excellent description // of the rules for building C type declarations can be found at: // http://unixwiz.net/techtips/reading-cdecl.html - TypeCode prevGrouping = CType::GetTypeCode(cx, typeObj), currentGrouping; + TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping; while (1) { - currentGrouping = CType::GetTypeCode(cx, typeObj); + currentGrouping = CType::GetTypeCode(typeObj); switch (currentGrouping) { case TYPE_pointer: { // Pointer types go on the left. PrependString(result, "*"); - typeObj = PointerType::GetBaseType(cx, typeObj); + typeObj = PointerType::GetBaseType(typeObj); prevGrouping = currentGrouping; continue; } @@ -2149,28 +2168,37 @@ BuildTypeName(JSContext* cx, JSObject* typeObj) // Array types go on the right. AppendString(result, "["); size_t length; - if (ArrayType::GetSafeLength(cx, typeObj, &length)) + if (ArrayType::GetSafeLength(typeObj, &length)) IntegerToString(length, 10, result); AppendString(result, "]"); - typeObj = ArrayType::GetBaseType(cx, typeObj); + typeObj = ArrayType::GetBaseType(typeObj); prevGrouping = currentGrouping; continue; } case TYPE_function: { - FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); // Add in the calling convention, if it's not cdecl. - ABICode abi = GetABICode(cx, fninfo->mABI); + // There's no trailing or leading space needed here, as none of the + // modifiers can produce a string beginning with an identifier --- + // except for TYPE_function itself, which is fine because functions + // can't return functions. + ABICode abi = GetABICode(fninfo->mABI); if (abi == ABI_STDCALL) - PrependString(result, "__stdcall "); + PrependString(result, "__stdcall"); else if (abi == ABI_WINAPI) - PrependString(result, "WINAPI "); + PrependString(result, "WINAPI"); - // Wrap the entire expression so far with parens. - PrependString(result, "("); - AppendString(result, ")"); + // Function application binds more tightly than dereferencing, so + // wrap pointer types in parens. Functions can't return functions + // (only pointers to them), and arrays can't hold functions + // (similarly), so we don't need to address those cases. + if (prevGrouping == TYPE_pointer) { + PrependString(result, "("); + AppendString(result, ")"); + } // Argument list goes on the right. AppendString(result, "("); @@ -2198,6 +2226,13 @@ BuildTypeName(JSContext* cx, JSObject* typeObj) break; } + // If prepending the base type name directly would splice two + // identifiers, insert a space. + if (('a' <= result[0] && result[0] <= 'z') || + ('A' <= result[0] && result[0] <= 'Z') || + (result[0] == '_')) + PrependString(result, " "); + // Stick the base type and derived type parts together. JSString* baseName = CType::GetName(cx, typeObj); PrependString(result, baseName); @@ -2218,7 +2253,7 @@ BuildTypeSource(JSContext* cx, AutoString& result) { // Walk the types, building up the toSource() string. - switch (CType::GetTypeCode(cx, typeObj)) { + switch (CType::GetTypeCode(typeObj)) { case TYPE_void_t: #define DEFINE_TYPE(name, type, ffiType) \ case TYPE_##name: @@ -2230,10 +2265,10 @@ BuildTypeSource(JSContext* cx, break; } case TYPE_pointer: { - JSObject* baseType = PointerType::GetBaseType(cx, typeObj); + JSObject* baseType = PointerType::GetBaseType(typeObj); // Specialcase ctypes.voidptr_t. - if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) { + if (CType::GetTypeCode(baseType) == TYPE_void_t) { AppendString(result, "ctypes.voidptr_t"); break; } @@ -2244,11 +2279,11 @@ BuildTypeSource(JSContext* cx, break; } case TYPE_function: { - FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); AppendString(result, "ctypes.FunctionType("); - switch (GetABICode(cx, fninfo->mABI)) { + switch (GetABICode(fninfo->mABI)) { case ABI_DEFAULT: AppendString(result, "ctypes.default_abi, "); break; @@ -2287,12 +2322,12 @@ BuildTypeSource(JSContext* cx, // Recursively build the source string, and append '.array(n)', // where n is the array length, or the empty string if the array length // is undefined. - JSObject* baseType = ArrayType::GetBaseType(cx, typeObj); + JSObject* baseType = ArrayType::GetBaseType(typeObj); BuildTypeSource(cx, baseType, makeShort, result); AppendString(result, ".array("); size_t length; - if (ArrayType::GetSafeLength(cx, typeObj, &length)) + if (ArrayType::GetSafeLength(typeObj, &length)) IntegerToString(length, 10, result); AppendString(result, ")"); @@ -2314,14 +2349,14 @@ BuildTypeSource(JSContext* cx, AppendString(result, "\""); // If it's an opaque struct, we're done. - if (!CType::IsSizeDefined(cx, typeObj)) { + if (!CType::IsSizeDefined(typeObj)) { AppendString(result, ")"); break; } AppendString(result, ", ["); - const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj); + const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); size_t length = fields->count(); Array fieldsArray; if (!fieldsArray.resize(length)) @@ -2364,7 +2399,7 @@ BuildDataSource(JSContext* cx, bool isImplicit, AutoString& result) { - TypeCode type = CType::GetTypeCode(cx, typeObj); + TypeCode type = CType::GetTypeCode(typeObj); switch (type) { case TYPE_bool: if (*static_cast(data)) @@ -2445,11 +2480,11 @@ BuildDataSource(JSContext* cx, case TYPE_array: { // Serialize each element of the array recursively. Each element must // be able to ImplicitConvert successfully. - JSObject* baseType = ArrayType::GetBaseType(cx, typeObj); + JSObject* baseType = ArrayType::GetBaseType(typeObj); AppendString(result, "["); - size_t length = ArrayType::GetLength(cx, typeObj); - size_t elementSize = CType::GetSize(cx, baseType); + size_t length = ArrayType::GetLength(typeObj); + size_t elementSize = CType::GetSize(baseType); for (size_t i = 0; i < length; ++i) { char* element = static_cast(data) + elementSize * i; if (!BuildDataSource(cx, baseType, element, true, result)) @@ -2471,7 +2506,7 @@ BuildDataSource(JSContext* cx, // Serialize each field of the struct recursively. Each field must // be able to ImplicitConvert successfully. - const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj); + const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); size_t length = fields->count(); Array fieldsArray; if (!fieldsArray.resize(length)) @@ -2535,7 +2570,7 @@ CType::ConstructData(JSContext* cx, { // get the callee object... JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - if (!CType::IsCType(cx, obj)) { + if (!CType::IsCType(obj)) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } @@ -2544,7 +2579,7 @@ CType::ConstructData(JSContext* cx, // An instance 'd' of a CData object of type 't' has: // * [[Class]] "CData" // * __proto__ === t.prototype - switch (GetTypeCode(cx, obj)) { + switch (GetTypeCode(obj)) { case TYPE_void_t: JS_ReportError(cx, "cannot construct from void_t"); return JS_FALSE; @@ -2579,7 +2614,7 @@ CType::ConstructBasic(JSContext* cx, return JS_FALSE; if (argc == 1) { - if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result))) + if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result))) return JS_FALSE; } @@ -2597,7 +2632,7 @@ CType::Create(JSContext* cx, jsval align, ffi_type* ffiType) { - JSObject* parent = JS_GetParent(cx, typeProto); + JSObject* parent = JS_GetParent(typeProto); JS_ASSERT(parent); // Create a CType object with the properties and slots common to all CTypes. @@ -2622,12 +2657,13 @@ CType::Create(JSContext* cx, js::AutoObjectRooter root(cx, typeObj); // Set up the reserved slots. - if (!JS_SetReservedSlot(cx, typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)) || - (ffiType && !JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType))) || - (name && !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name))) || - !JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, size) || - !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, align)) - return NULL; + JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)); + if (ffiType) + JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)); + if (name) + JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name)); + JS_SetReservedSlot(typeObj, SLOT_SIZE, size); + JS_SetReservedSlot(typeObj, SLOT_ALIGN, align); if (dataProto) { // Set up the 'prototype' and 'prototype.constructor' properties. @@ -2641,9 +2677,9 @@ CType::Create(JSContext* cx, return NULL; // Set the 'prototype' object. - if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! - !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype))) - return NULL; + //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212! + // return NULL; + JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); } if (!JS_FreezeObject(cx, typeObj)) @@ -2651,8 +2687,8 @@ CType::Create(JSContext* cx, // Assert a sanity check on size and alignment: size % alignment should always // be zero. - JS_ASSERT_IF(IsSizeDefined(cx, typeObj), - GetSize(cx, typeObj) % GetAlignment(cx, typeObj) == 0); + JS_ASSERT_IF(IsSizeDefined(typeObj), + GetSize(typeObj) % GetAlignment(typeObj) == 0); return typeObj; } @@ -2692,15 +2728,15 @@ void CType::Finalize(JSContext* cx, JSObject* obj) { // Make sure our TypeCode slot is legit. If it's not, bail. - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_TYPECODE, &slot) || JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE); + if (JSVAL_IS_VOID(slot)) return; // The contents of our slots depends on what kind of type we are. switch (TypeCode(JSVAL_TO_INT(slot))) { case TYPE_function: { // Free the FunctionInfo. - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot)); + slot = JS_GetReservedSlot(obj, SLOT_FNINFO); if (!JSVAL_IS_VOID(slot)) cx->delete_(static_cast(JSVAL_TO_PRIVATE(slot))); break; @@ -2708,7 +2744,7 @@ CType::Finalize(JSContext* cx, JSObject* obj) case TYPE_struct: { // Free the FieldInfoHash table. - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot)); + slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); if (!JSVAL_IS_VOID(slot)) { void* info = JSVAL_TO_PRIVATE(slot); cx->delete_(static_cast(info)); @@ -2718,7 +2754,7 @@ CType::Finalize(JSContext* cx, JSObject* obj) // Fall through. case TYPE_array: { // Free the ffi_type info. - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot)); + slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); if (!JSVAL_IS_VOID(slot)) { ffi_type* ffiType = static_cast(JSVAL_TO_PRIVATE(slot)); cx->array_delete(ffiType->elements); @@ -2741,29 +2777,26 @@ CType::FinalizeProtoClass(JSContext* cx, JSObject* obj) // for use with FunctionType closures. And if we're here, in this finalizer, // we're guaranteed to not need it anymore. Note that this slot will only // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype. - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSURECX); + if (JSVAL_IS_VOID(slot)) return; JSContext* closureCx = static_cast(JSVAL_TO_PRIVATE(slot)); - JS_SetContextThread(closureCx); JS_DestroyContextNoGC(closureCx); } void CType::Trace(JSTracer* trc, JSObject* obj) { - JSContext* cx = trc->context; - // Make sure our TypeCode slot is legit. If it's not, bail. - jsval slot = js::Jsvalify(obj->getSlot(SLOT_TYPECODE)); + jsval slot = obj->getSlot(SLOT_TYPECODE); if (JSVAL_IS_VOID(slot)) return; // The contents of our slots depends on what kind of type we are. switch (TypeCode(JSVAL_TO_INT(slot))) { case TYPE_struct: { - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot)); + slot = obj->getReservedSlot(SLOT_FIELDINFO); if (JSVAL_IS_VOID(slot)) return; @@ -2778,7 +2811,7 @@ CType::Trace(JSTracer* trc, JSObject* obj) } case TYPE_function: { // Check if we have a FunctionInfo. - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot)); + slot = obj->getReservedSlot(SLOT_FNINFO); if (JSVAL_IS_VOID(slot)) return; @@ -2800,33 +2833,38 @@ CType::Trace(JSTracer* trc, JSObject* obj) } bool -CType::IsCType(JSContext* cx, JSObject* obj) +CType::IsCType(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sCTypeClass; + return JS_GetClass(obj) == &sCTypeClass; +} + +bool +CType::IsCTypeProto(JSObject* obj) +{ + return JS_GetClass(obj) == &sCTypeProtoClass; } TypeCode -CType::GetTypeCode(JSContext* cx, JSObject* typeObj) +CType::GetTypeCode(JSObject* typeObj) { - JS_ASSERT(IsCType(cx, typeObj)); + JS_ASSERT(IsCType(typeObj)); - jsval result; - ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_TYPECODE, &result)); + jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE); return TypeCode(JSVAL_TO_INT(result)); } bool -CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2) +CType::TypesEqual(JSObject* t1, JSObject* t2) { - JS_ASSERT(IsCType(cx, t1) && IsCType(cx, t2)); + JS_ASSERT(IsCType(t1) && IsCType(t2)); // Fast path: check for object equality. if (t1 == t2) return true; // First, perform shallow comparison. - TypeCode c1 = GetTypeCode(cx, t1); - TypeCode c2 = GetTypeCode(cx, t2); + TypeCode c1 = GetTypeCode(t1); + TypeCode c2 = GetTypeCode(t2); if (c1 != c2) return false; @@ -2834,19 +2872,19 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2) switch (c1) { case TYPE_pointer: { // Compare base types. - JSObject* b1 = PointerType::GetBaseType(cx, t1); - JSObject* b2 = PointerType::GetBaseType(cx, t2); - return TypesEqual(cx, b1, b2); + JSObject* b1 = PointerType::GetBaseType(t1); + JSObject* b2 = PointerType::GetBaseType(t2); + return TypesEqual(b1, b2); } case TYPE_function: { - FunctionInfo* f1 = FunctionType::GetFunctionInfo(cx, t1); - FunctionInfo* f2 = FunctionType::GetFunctionInfo(cx, t2); + FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1); + FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2); // Compare abi, return type, and argument types. if (f1->mABI != f2->mABI) return false; - if (!TypesEqual(cx, f1->mReturnType, f2->mReturnType)) + if (!TypesEqual(f1->mReturnType, f2->mReturnType)) return false; if (f1->mArgTypes.length() != f2->mArgTypes.length()) @@ -2856,7 +2894,7 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2) return false; for (size_t i = 0; i < f1->mArgTypes.length(); ++i) { - if (!TypesEqual(cx, f1->mArgTypes[i], f2->mArgTypes[i])) + if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) return false; } @@ -2866,14 +2904,14 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2) // Compare length, then base types. // An undefined length array matches other undefined length arrays. size_t s1 = 0, s2 = 0; - bool d1 = ArrayType::GetSafeLength(cx, t1, &s1); - bool d2 = ArrayType::GetSafeLength(cx, t2, &s2); + bool d1 = ArrayType::GetSafeLength(t1, &s1); + bool d2 = ArrayType::GetSafeLength(t2, &s2); if (d1 != d2 || (d1 && s1 != s2)) return false; - JSObject* b1 = ArrayType::GetBaseType(cx, t1); - JSObject* b2 = ArrayType::GetBaseType(cx, t2); - return TypesEqual(cx, b1, b2); + JSObject* b1 = ArrayType::GetBaseType(t1); + JSObject* b2 = ArrayType::GetBaseType(t2); + return TypesEqual(b1, b2); } case TYPE_struct: // Require exact type object equality. @@ -2885,12 +2923,11 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2) } bool -CType::GetSafeSize(JSContext* cx, JSObject* obj, size_t* result) +CType::GetSafeSize(JSObject* obj, size_t* result) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval size; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size)); + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID // (for arrays of undefined length), and must always fit in a size_t. @@ -2908,12 +2945,11 @@ CType::GetSafeSize(JSContext* cx, JSObject* obj, size_t* result) } size_t -CType::GetSize(JSContext* cx, JSObject* obj) +CType::GetSize(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval size; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size)); + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); JS_ASSERT(!JSVAL_IS_VOID(size)); @@ -2926,12 +2962,11 @@ CType::GetSize(JSContext* cx, JSObject* obj) } bool -CType::IsSizeDefined(JSContext* cx, JSObject* obj) +CType::IsSizeDefined(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval size; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size)); + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID // (for arrays of undefined length), and must always fit in a size_t. @@ -2940,29 +2975,27 @@ CType::IsSizeDefined(JSContext* cx, JSObject* obj) } size_t -CType::GetAlignment(JSContext* cx, JSObject* obj) +CType::GetAlignment(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ALIGN, &slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN); return static_cast(JSVAL_TO_INT(slot)); } ffi_type* CType::GetFFIType(JSContext* cx, JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); if (!JSVAL_IS_VOID(slot)) { return static_cast(JSVAL_TO_PRIVATE(slot)); } AutoPtr result; - switch (CType::GetTypeCode(cx, obj)) { + switch (CType::GetTypeCode(obj)) { case TYPE_array: result = ArrayType::BuildFFIType(cx, obj); break; @@ -2975,26 +3008,24 @@ CType::GetFFIType(JSContext* cx, JSObject* obj) JS_NOT_REACHED("simple types must have an ffi_type"); } - if (!result || - !JS_SetReservedSlot(cx, obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get()))) + if (!result) return NULL; - + JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())); return result.forget(); } JSString* CType::GetName(JSContext* cx, JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval string; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_NAME, &string)); + jsval string = JS_GetReservedSlot(obj, SLOT_NAME); if (JSVAL_IS_VOID(string)) { // Build the type name lazily. JSString* name = BuildTypeName(cx, obj); - if (!name || !JS_SetReservedSlot(cx, obj, SLOT_NAME, STRING_TO_JSVAL(name))) + if (!name) return NULL; - + JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name)); return name; } @@ -3002,47 +3033,46 @@ CType::GetName(JSContext* cx, JSObject* obj) } JSObject* -CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot) +CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) { // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot // on the type constructor. - jsval protoslot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, &protoslot)); + jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO); JSObject* proto = JSVAL_TO_OBJECT(protoslot); JS_ASSERT(proto); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass); + JS_ASSERT(CType::IsCTypeProto(proto)); // Get the desired prototype. - jsval result; - ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result)); + jsval result = JS_GetReservedSlot(proto, slot); return JSVAL_TO_OBJECT(result); } JSObject* -CType::GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot) +CType::GetProtoFromType(JSObject* obj, CTypeProtoSlot slot) { - JS_ASSERT(IsCType(cx, obj)); + JS_ASSERT(IsCType(obj)); // Get the prototype of the type object. - JSObject* proto = JS_GetPrototype(cx, obj); + JSObject* proto = JS_GetPrototype(obj); JS_ASSERT(proto); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass); + JS_ASSERT(CType::IsCTypeProto(proto)); // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype. - jsval result; - ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result)); + jsval result = JS_GetReservedSlot(proto, slot); return JSVAL_TO_OBJECT(result); } JSBool CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj)) { - JS_ReportError(cx, "not a CType"); + if (!(CType::IsCType(obj) || CType::IsCTypeProto(obj))) { + JS_ReportError(cx, "not a CType or CTypeProto"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, vp)); + unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO + : (unsigned) SLOT_PROTO; + *vp = JS_GetReservedSlot(obj, slot); JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp)); return JS_TRUE; } @@ -3050,7 +3080,7 @@ CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) JSBool CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj)) { + if (!CType::IsCType(obj)) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } @@ -3066,12 +3096,12 @@ CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) JSBool CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj)) { + if (!CType::IsCType(obj)) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_SIZE); JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp)); return JS_TRUE; } @@ -3079,7 +3109,7 @@ CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) JSBool CType::PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj)) { + if (!CType::IsCType(obj)) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } @@ -3096,7 +3126,7 @@ JSBool CType::CreateArray(JSContext* cx, uintN argc, jsval* vp) { JSObject* baseType = JS_THIS_OBJECT(cx, vp); - if (!baseType || !CType::IsCType(cx, baseType)) { + if (!baseType || !CType::IsCType(baseType)) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } @@ -3127,19 +3157,26 @@ JSBool CType::ToString(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CType::IsCType(cx, obj)) { + if (!obj || !(CType::IsCType(obj) || CType::IsCTypeProto(obj))) { JS_ReportError(cx, "not a CType"); return JS_FALSE; } - AutoString type; - AppendString(type, "type "); - AppendString(type, GetName(cx, obj)); - - JSString* result = NewUCString(cx, type); + // Create the appropriate string depending on whether we're sCTypeClass or + // sCTypeProtoClass. + JSString* result; + if (CType::IsCType(obj)) { + AutoString type; + AppendString(type, "type "); + AppendString(type, GetName(cx, obj)); + result = NewUCString(cx, type); + } + else { + result = JS_NewStringCopyZ(cx, "[CType proto object]"); + } if (!result) return JS_FALSE; - + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result)); return JS_TRUE; } @@ -3148,17 +3185,26 @@ JSBool CType::ToSource(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CType::IsCType(cx, obj)) { + if (!obj || + !(CType::IsCType(obj) || CType::IsCTypeProto(obj))) + { JS_ReportError(cx, "not a CType"); return JS_FALSE; } - AutoString source; - BuildTypeSource(cx, obj, false, source); - JSString* result = NewUCString(cx, source); + // Create the appropriate string depending on whether we're sCTypeClass or + // sCTypeProtoClass. + JSString* result; + if (CType::IsCType(obj)) { + AutoString source; + BuildTypeSource(cx, obj, false, source); + result = NewUCString(cx, source); + } else { + result = JS_NewStringCopyZ(cx, "[CType proto object]"); + } if (!result) return JS_FALSE; - + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result)); return JS_TRUE; } @@ -3166,20 +3212,19 @@ CType::ToSource(JSContext* cx, uintN argc, jsval* vp) JSBool CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp) { - JS_ASSERT(CType::IsCType(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, &slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO); JSObject* prototype = JSVAL_TO_OBJECT(slot); JS_ASSERT(prototype); - JS_ASSERT(JS_GET_CLASS(cx, prototype) == &sCDataProtoClass); + JS_ASSERT(CData::IsCDataProto(prototype)); *bp = JS_FALSE; if (JSVAL_IS_PRIMITIVE(*v)) return JS_TRUE; JSObject* proto = JSVAL_TO_OBJECT(*v); - while ((proto = JS_GetPrototype(cx, proto))) { + while ((proto = JS_GetPrototype(proto))) { if (proto == prototype) { *bp = JS_TRUE; break; @@ -3202,7 +3247,7 @@ PointerType::Create(JSContext* cx, uintN argc, jsval* vp) } jsval arg = JS_ARGV(cx, vp)[0]; - if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) { + if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(JSVAL_TO_OBJECT(arg))) { JS_ReportError(cx, "first argument must be a CType"); return JS_FALSE; } @@ -3219,8 +3264,7 @@ JSObject* PointerType::CreateInternal(JSContext* cx, JSObject* baseType) { // check if we have a cached PointerType on our base CType. - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot)); + jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR); if (!JSVAL_IS_VOID(slot)) return JSVAL_TO_OBJECT(slot); @@ -3228,8 +3272,8 @@ PointerType::CreateInternal(JSContext* cx, JSObject* baseType) // of this type. JSObject* typeProto; JSObject* dataProto; - typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO); - dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO); + typeProto = CType::GetProtoFromType(baseType, SLOT_POINTERPROTO); + dataProto = CType::GetProtoFromType(baseType, SLOT_POINTERDATAPROTO); // Create a new CType object with the common properties and slots. JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer, @@ -3238,15 +3282,12 @@ PointerType::CreateInternal(JSContext* cx, JSObject* baseType) &ffi_type_pointer); if (!typeObj) return NULL; - js::AutoObjectRooter root(cx, typeObj); // Set the target type. (This will be 'null' for an opaque pointer type.) - if (!JS_SetReservedSlot(cx, typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType))) - return NULL; + JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)); // Finally, cache our newly-created PointerType on our pointed-to CType. - if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj))) - return NULL; + JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)); return typeObj; } @@ -3257,13 +3298,13 @@ PointerType::ConstructData(JSContext* cx, uintN argc, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) { JS_ReportError(cx, "not a PointerType"); return JS_FALSE; } - if (argc > 2) { - JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments"); + if (argc > 3) { + JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments"); return JS_FALSE; } @@ -3274,49 +3315,71 @@ PointerType::ConstructData(JSContext* cx, // Set return value early, must not observe *vp after JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); - if (argc == 0) { - // Construct a null pointer. + // There are 3 things that we might be creating here: + // 1 - A null pointer (no arguments) + // 2 - An initialized pointer (1 argument) + // 3 - A closure (1-3 arguments) + // + // The API doesn't give us a perfect way to distinguish 2 and 3, but the + // heuristics we use should be fine. + + // + // Case 1 - Null pointer + // + if (argc == 0) return JS_TRUE; - } + // Analyze the arguments a bit to decide what to do next. jsval* argv = JS_ARGV(cx, vp); - if (argc >= 1) { - JSObject* baseObj = PointerType::GetBaseType(cx, obj); - if (CType::GetTypeCode(cx, baseObj) == TYPE_function && - JSVAL_IS_OBJECT(argv[0]) && - JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) { - // Construct a FunctionType.ptr from a JS function, and allow an - // optional 'this' argument. - JSObject* thisObj = NULL; - if (argc == 2) { - if (JSVAL_IS_OBJECT(argv[1])) { - thisObj = JSVAL_TO_OBJECT(argv[1]); - } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { - return JS_FALSE; - } - } - - JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); - return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); + JSObject* baseObj = PointerType::GetBaseType(obj); + bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function && + JSVAL_IS_OBJECT(argv[0]) && + JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0])); + + // + // Case 2 - Initialized pointer + // + if (!looksLikeClosure) { + if (argc != 1) { + JS_ReportError(cx, "first argument must be a function"); + return JS_FALSE; } + return ExplicitConvert(cx, argv[0], obj, CData::GetData(result)); + } - if (argc == 2) { - JS_ReportError(cx, "first argument must be a function"); + // + // Case 3 - Closure + // + + // The second argument is an optional 'this' parameter with which to invoke + // the given js function. Callers may leave this blank, or pass null if they + // wish to pass the third argument. + JSObject* thisObj = NULL; + if (argc >= 2) { + if (JSVAL_IS_OBJECT(argv[1])) { + thisObj = JSVAL_TO_OBJECT(argv[1]); + } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { return JS_FALSE; } } - // Construct from a raw pointer value. - return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)); + // The third argument is an optional error sentinel that js-ctypes will return + // if an exception is raised while executing the closure. The type must match + // the return type of the callback. + jsval errVal = JSVAL_VOID; + if (argc == 3) + errVal = argv[2]; + + JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); + return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal); } JSObject* -PointerType::GetBaseType(JSContext* cx, JSObject* obj) +PointerType::GetBaseType(JSObject* obj) { - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_pointer); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer); - jsval type; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type)); + jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T); JS_ASSERT(!JSVAL_IS_NULL(type)); return JSVAL_TO_OBJECT(type); } @@ -3327,12 +3390,12 @@ PointerType::TargetTypeGetter(JSContext* cx, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) { JS_ReportError(cx, "not a PointerType"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_TARGET_T); JS_ASSERT(JSVAL_IS_OBJECT(*vp)); return JS_TRUE; } @@ -3341,49 +3404,95 @@ JSBool PointerType::IsNull(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CData::IsCData(cx, obj)) { + if (!obj || !CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Get pointer type and base type. - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { JS_ReportError(cx, "not a PointerType"); return JS_FALSE; } - void* data = *static_cast(CData::GetData(cx, obj)); + void* data = *static_cast(CData::GetData(obj)); jsval result = BOOLEAN_TO_JSVAL(data == NULL); JS_SET_RVAL(cx, vp, result); return JS_TRUE; } +JSBool +PointerType::OffsetBy(JSContext* cx, intN offset, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !CData::IsCData(obj)) { + JS_ReportError(cx, "not a CData"); + return JS_FALSE; + } + + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { + JS_ReportError(cx, "not a PointerType"); + return JS_FALSE; + } + + JSObject* baseType = PointerType::GetBaseType(typeObj); + if (!CType::IsSizeDefined(baseType)) { + JS_ReportError(cx, "cannot modify pointer of undefined size"); + return JS_FALSE; + } + + size_t elementSize = CType::GetSize(baseType); + char* data = static_cast(*static_cast(CData::GetData(obj))); + void* address = data + offset * elementSize; + + // Create a PointerType CData object containing the new address. + JSObject* result = CData::Create(cx, typeObj, NULL, &address, true); + if (!result) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); + return JS_TRUE; +} + +JSBool +PointerType::Increment(JSContext* cx, uintN argc, jsval* vp) +{ + return OffsetBy(cx, 1, vp); +} + +JSBool +PointerType::Decrement(JSContext* cx, uintN argc, jsval* vp) +{ + return OffsetBy(cx, -1, vp); +} + JSBool PointerType::ContentsGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Get pointer type and base type. - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { JS_ReportError(cx, "not a PointerType"); return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, typeObj); - if (!CType::IsSizeDefined(cx, baseType)) { + JSObject* baseType = GetBaseType(typeObj); + if (!CType::IsSizeDefined(baseType)) { JS_ReportError(cx, "cannot get contents of undefined size"); return JS_FALSE; } - void* data = *static_cast(CData::GetData(cx, obj)); + void* data = *static_cast(CData::GetData(obj)); if (data == NULL) { JS_ReportError(cx, "cannot read contents of null pointer"); return JS_FALSE; @@ -3404,25 +3513,25 @@ PointerType::ContentsSetter(JSContext* cx, JSBool strict, jsval* vp) { - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Get pointer type and base type. - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { JS_ReportError(cx, "not a PointerType"); return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, typeObj); - if (!CType::IsSizeDefined(cx, baseType)) { + JSObject* baseType = GetBaseType(typeObj); + if (!CType::IsSizeDefined(baseType)) { JS_ReportError(cx, "cannot set contents of undefined size"); return JS_FALSE; } - void* data = *static_cast(CData::GetData(cx, obj)); + void* data = *static_cast(CData::GetData(obj)); if (data == NULL) { JS_ReportError(cx, "cannot write contents to null pointer"); return JS_FALSE; @@ -3446,7 +3555,7 @@ ArrayType::Create(JSContext* cx, uintN argc, jsval* vp) jsval* argv = JS_ARGV(cx, vp); if (JSVAL_IS_PRIMITIVE(argv[0]) || - !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[0]))) { + !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "first argument must be a CType"); return JS_FALSE; } @@ -3475,14 +3584,14 @@ ArrayType::CreateInternal(JSContext* cx, { // Get ctypes.ArrayType.prototype and the common prototype for CData objects // of this type, from ctypes.CType.prototype. - JSObject* typeProto = CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO); - JSObject* dataProto = CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO); + JSObject* typeProto = CType::GetProtoFromType(baseType, SLOT_ARRAYPROTO); + JSObject* dataProto = CType::GetProtoFromType(baseType, SLOT_ARRAYDATAPROTO); // Determine the size of the array from the base type, if possible. // The size of the base type must be defined. // If our length is undefined, both our size and length will be undefined. size_t baseSize; - if (!CType::GetSafeSize(cx, baseType, &baseSize)) { + if (!CType::GetSafeSize(baseType, &baseSize)) { JS_ReportError(cx, "base size must be defined"); return NULL; } @@ -3501,22 +3610,19 @@ ArrayType::CreateInternal(JSContext* cx, return NULL; } - size_t align = CType::GetAlignment(cx, baseType); + size_t align = CType::GetAlignment(baseType); // Create a new CType object with the common properties and slots. JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL, sizeVal, INT_TO_JSVAL(align), NULL); if (!typeObj) return NULL; - js::AutoObjectRooter root(cx, typeObj); // Set the element type. - if (!JS_SetReservedSlot(cx, typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType))) - return NULL; + JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)); // Set the length. - if (!JS_SetReservedSlot(cx, typeObj, SLOT_LENGTH, lengthVal)) - return NULL; + JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal); return typeObj; } @@ -3527,7 +3633,7 @@ ArrayType::ConstructData(JSContext* cx, uintN argc, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { JS_ReportError(cx, "not an ArrayType"); return JS_FALSE; } @@ -3538,7 +3644,7 @@ ArrayType::ConstructData(JSContext* cx, // Check if we're an array of undefined length. If we are, allow construction // with a length argument, or with an actual JS array. - if (CType::IsSizeDefined(cx, obj)) { + if (CType::IsSizeDefined(obj)) { if (argc > 1) { JS_ReportError(cx, "constructor takes zero or one argument"); return JS_FALSE; @@ -3550,7 +3656,7 @@ ArrayType::ConstructData(JSContext* cx, return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, obj); + JSObject* baseType = GetBaseType(obj); jsval* argv = JS_ARGV(cx, vp); size_t length; @@ -3578,12 +3684,12 @@ ArrayType::ConstructData(JSContext* cx, if (!sourceChars) return false; - switch (CType::GetTypeCode(cx, baseType)) { + switch (CType::GetTypeCode(baseType)) { case TYPE_char: case TYPE_signed_char: case TYPE_unsigned_char: { // Determine the UTF-8 length. - length = js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); + length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); if (length == (size_t) -1) return false; @@ -3618,7 +3724,7 @@ ArrayType::ConstructData(JSContext* cx, JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); if (convertObject) { - if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result))) + if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result))) return JS_FALSE; } @@ -3626,25 +3732,23 @@ ArrayType::ConstructData(JSContext* cx, } JSObject* -ArrayType::GetBaseType(JSContext* cx, JSObject* obj) +ArrayType::GetBaseType(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); - jsval type; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type)); + jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T); JS_ASSERT(!JSVAL_IS_NULL(type)); return JSVAL_TO_OBJECT(type); } bool -ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result) +ArrayType::GetSafeLength(JSObject* obj, size_t* result) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); - jsval length; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length)); + jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID // (for arrays of undefined length), and must always fit in a size_t. @@ -3662,13 +3766,12 @@ ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result) } size_t -ArrayType::GetLength(JSContext* cx, JSObject* obj) +ArrayType::GetLength(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); - jsval length; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length)); + jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); JS_ASSERT(!JSVAL_IS_VOID(length)); @@ -3683,16 +3786,16 @@ ArrayType::GetLength(JSContext* cx, JSObject* obj) ffi_type* ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array); - JS_ASSERT(CType::IsSizeDefined(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); + JS_ASSERT(CType::IsSizeDefined(obj)); - JSObject* baseType = ArrayType::GetBaseType(cx, obj); + JSObject* baseType = ArrayType::GetBaseType(obj); ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType); if (!ffiBaseType) return NULL; - size_t length = ArrayType::GetLength(cx, obj); + size_t length = ArrayType::GetLength(obj); // Create an ffi_type to represent the array. This is necessary for the case // where the array is part of a struct. Since libffi has no intrinsic @@ -3708,8 +3811,8 @@ ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) } ffiType->type = FFI_TYPE_STRUCT; - ffiType->size = CType::GetSize(cx, obj); - ffiType->alignment = CType::GetAlignment(cx, obj); + ffiType->size = CType::GetSize(obj); + ffiType->alignment = CType::GetAlignment(obj); ffiType->elements = cx->array_new(length + 1); if (!ffiType->elements) { JS_ReportAllocationOverflow(cx); @@ -3726,12 +3829,12 @@ ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) JSBool ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { JS_ReportError(cx, "not an ArrayType"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_ELEMENT_T); JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); return JS_TRUE; } @@ -3741,15 +3844,15 @@ ArrayType::LengthGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { // This getter exists for both CTypes and CDatas of the ArrayType persuasion. // If we're dealing with a CData, get the CType from it. - if (CData::IsCData(cx, obj)) - obj = CData::GetCType(cx, obj); + if (CData::IsCData(obj)) + obj = CData::GetCType(obj); - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { JS_ReportError(cx, "not an ArrayType"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_LENGTH); JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp)); return JS_TRUE; } @@ -3758,20 +3861,20 @@ JSBool ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { // This should never happen, but we'll check to be safe. - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Bail early if we're not an ArrayType. (This setter is present for all // CData, regardless of CType.) - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_array) + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_array) return JS_TRUE; // Convert the index to a size_t and bounds-check it. size_t index; - size_t length = GetLength(cx, typeObj); + size_t length = GetLength(typeObj); bool ok = jsidToSize(cx, idval, true, &index); if (!ok && JSID_IS_STRING(idval)) { // String either isn't a number, or doesn't fit in size_t. @@ -3783,9 +3886,9 @@ ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, typeObj); - size_t elementSize = CType::GetSize(cx, baseType); - char* data = static_cast(CData::GetData(cx, obj)) + elementSize * index; + JSObject* baseType = GetBaseType(typeObj); + size_t elementSize = CType::GetSize(baseType); + char* data = static_cast(CData::GetData(obj)) + elementSize * index; return ConvertToJS(cx, baseType, obj, data, false, false, vp); } @@ -3793,20 +3896,20 @@ JSBool ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp) { // This should never happen, but we'll check to be safe. - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Bail early if we're not an ArrayType. (This setter is present for all // CData, regardless of CType.) - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_array) + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_array) return JS_TRUE; // Convert the index to a size_t and bounds-check it. size_t index; - size_t length = GetLength(cx, typeObj); + size_t length = GetLength(typeObj); bool ok = jsidToSize(cx, idval, true, &index); if (!ok && JSID_IS_STRING(idval)) { // String either isn't a number, or doesn't fit in size_t. @@ -3818,9 +3921,9 @@ ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, typeObj); - size_t elementSize = CType::GetSize(cx, baseType); - char* data = static_cast(CData::GetData(cx, obj)) + elementSize * index; + JSObject* baseType = GetBaseType(typeObj); + size_t elementSize = CType::GetSize(baseType); + char* data = static_cast(CData::GetData(obj)) + elementSize * index; return ImplicitConvert(cx, *vp, baseType, data, false, NULL); } @@ -3828,13 +3931,13 @@ JSBool ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CData::IsCData(cx, obj)) { + if (!obj || !CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_array) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_array) { JS_ReportError(cx, "not an ArrayType"); return JS_FALSE; } @@ -3844,7 +3947,7 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; } - JSObject* baseType = GetBaseType(cx, typeObj); + JSObject* baseType = GetBaseType(typeObj); JSObject* pointerType = PointerType::CreateInternal(cx, baseType); if (!pointerType) return JS_FALSE; @@ -3859,7 +3962,7 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) // Convert the index to a size_t and bounds-check it. size_t index; - size_t length = GetLength(cx, typeObj); + size_t length = GetLength(typeObj); if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) || index >= length) { JS_ReportError(cx, "invalid index"); @@ -3867,9 +3970,9 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) } // Manually set the pointer inside the object, so we skip the conversion step. - void** data = static_cast(CData::GetData(cx, result)); - size_t elementSize = CType::GetSize(cx, baseType); - *data = static_cast(CData::GetData(cx, obj)) + elementSize * index; + void** data = static_cast(CData::GetData(result)); + size_t elementSize = CType::GetSize(baseType); + *data = static_cast(CData::GetData(obj)) + elementSize * index; return JS_TRUE; } @@ -3920,7 +4023,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) return NULL; if (propVal.value().isPrimitive() || - !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal.jsval_value()))) { + !CType::IsCType(JSVAL_TO_OBJECT(propVal.jsval_value()))) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); return NULL; } @@ -3930,7 +4033,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) // choke on a zero-size struct, so we disallow them.) *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value()); size_t size; - if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) { + if (!CType::GetSafeSize(*typeObj, &size) || size == 0) { JS_ReportError(cx, "struct field types must have defined and nonzero size"); return NULL; } @@ -3979,7 +4082,7 @@ StructType::Create(JSContext* cx, uintN argc, jsval* vp) // Get ctypes.StructType.prototype from the ctypes.StructType constructor. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - JSObject* typeProto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTPROTO); + JSObject* typeProto = CType::GetProtoFromCtor(callee, SLOT_STRUCTPROTO); // Create a simple StructType with no defined fields. The result will be // non-instantiable as CData, will have no 'prototype' property, and will @@ -4014,8 +4117,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj // Get the common prototype for CData objects of this type from // ctypes.CType.prototype. - JSObject* dataProto = - CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO); + JSObject* dataProto = CType::GetProtoFromType(typeObj, SLOT_STRUCTDATAPROTO); // Set up the 'prototype' and 'prototype.constructor' properties. // The prototype will reflect the struct fields as properties on CData objects @@ -4077,8 +4179,8 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT)) return JS_FALSE; - size_t fieldSize = CType::GetSize(cx, fieldType); - size_t fieldAlign = CType::GetAlignment(cx, fieldType); + size_t fieldSize = CType::GetSize(fieldType); + size_t fieldAlign = CType::GetAlignment(fieldType); size_t fieldOffset = Align(structSize, fieldAlign); // Check for overflow. Since we hold invariant that fieldSize % fieldAlign // be zero, we can safely check fieldOffset + fieldSize without first @@ -4115,32 +4217,28 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj if (!SizeTojsval(cx, structSize, &sizeVal)) return JS_FALSE; - if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO, - PRIVATE_TO_JSVAL(fields.get()))) - return JS_FALSE; - fields.forget(); - - if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) || - !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) || - //!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! - !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype))) - return JS_FALSE; + JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget())); + JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal); + JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)); + //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212! + // return false; + JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); return JS_TRUE; } ffi_type* StructType::BuildFFIType(JSContext* cx, JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); - JS_ASSERT(CType::IsSizeDefined(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); + JS_ASSERT(CType::IsSizeDefined(obj)); - const FieldInfoHash* fields = GetFieldInfo(cx, obj); + const FieldInfoHash* fields = GetFieldInfo(obj); size_t len = fields->count(); - size_t structSize = CType::GetSize(cx, obj); - size_t structAlign = CType::GetAlignment(cx, obj); + size_t structSize = CType::GetSize(obj); + size_t structAlign = CType::GetAlignment(obj); AutoPtr ffiType(cx->new_()); if (!ffiType) { @@ -4210,13 +4308,13 @@ StructType::Define(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); if (!obj || - !CType::IsCType(cx, obj) || - CType::GetTypeCode(cx, obj) != TYPE_struct) { + !CType::IsCType(obj) || + CType::GetTypeCode(obj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } - if (CType::IsSizeDefined(cx, obj)) { + if (CType::IsSizeDefined(obj)) { JS_ReportError(cx, "StructType has already been defined"); return JS_FALSE; } @@ -4242,12 +4340,12 @@ StructType::ConstructData(JSContext* cx, uintN argc, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } - if (!CType::IsSizeDefined(cx, obj)) { + if (!CType::IsSizeDefined(obj)) { JS_ReportError(cx, "cannot construct an opaque StructType"); return JS_FALSE; } @@ -4261,8 +4359,8 @@ StructType::ConstructData(JSContext* cx, if (argc == 0) return JS_TRUE; - char* buffer = static_cast(CData::GetData(cx, result)); - const FieldInfoHash* fields = GetFieldInfo(cx, obj); + char* buffer = static_cast(CData::GetData(result)); + const FieldInfoHash* fields = GetFieldInfo(obj); jsval* argv = JS_ARGV(cx, vp); if (argc == 1) { @@ -4314,13 +4412,12 @@ StructType::ConstructData(JSContext* cx, } const FieldInfoHash* -StructType::GetFieldInfo(JSContext* cx, JSObject* obj) +StructType::GetFieldInfo(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); return static_cast(JSVAL_TO_PRIVATE(slot)); @@ -4329,10 +4426,10 @@ StructType::GetFieldInfo(JSContext* cx, JSObject* obj) const FieldInfo* StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); - FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name); + FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name); if (ptr) return &ptr->value; @@ -4347,11 +4444,11 @@ StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) JSObject* StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); - JS_ASSERT(CType::IsSizeDefined(cx, obj)); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); + JS_ASSERT(CType::IsSizeDefined(obj)); - const FieldInfoHash* fields = GetFieldInfo(cx, obj); + const FieldInfoHash* fields = GetFieldInfo(obj); size_t len = fields->count(); // Prepare a new array for the 'fields' property of the StructType. @@ -4382,14 +4479,14 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) JSBool StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDS, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_FIELDS); - if (!CType::IsSizeDefined(cx, obj)) { + if (!CType::IsSizeDefined(obj)) { JS_ASSERT(JSVAL_IS_VOID(*vp)); return JS_TRUE; } @@ -4397,9 +4494,9 @@ StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* v if (JSVAL_IS_VOID(*vp)) { // Build the 'fields' array lazily. JSObject* fields = BuildFieldsArray(cx, obj); - if (!fields || - !JS_SetReservedSlot(cx, obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields))) + if (!fields) return JS_FALSE; + JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)); *vp = OBJECT_TO_JSVAL(fields); } @@ -4412,13 +4509,13 @@ StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* v JSBool StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } @@ -4427,20 +4524,20 @@ StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) if (!field) return JS_FALSE; - char* data = static_cast(CData::GetData(cx, obj)) + field->mOffset; + char* data = static_cast(CData::GetData(obj)) + field->mOffset; return ConvertToJS(cx, field->mType, obj, data, false, false, vp); } JSBool StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp) { - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } @@ -4449,7 +4546,7 @@ StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, if (!field) return JS_FALSE; - char* data = static_cast(CData::GetData(cx, obj)) + field->mOffset; + char* data = static_cast(CData::GetData(obj)) + field->mOffset; return ImplicitConvert(cx, *vp, field->mType, data, false, NULL); } @@ -4457,13 +4554,13 @@ JSBool StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CData::IsCData(cx, obj)) { + if (!obj || !CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_struct) { JS_ReportError(cx, "not a StructType"); return JS_FALSE; } @@ -4495,8 +4592,8 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp) JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); // Manually set the pointer inside the object, so we skip the conversion step. - void** data = static_cast(CData::GetData(cx, result)); - *data = static_cast(CData::GetData(cx, obj)) + field->mOffset; + void** data = static_cast(CData::GetData(result)); + *data = static_cast(CData::GetData(obj)) + field->mOffset; return JS_TRUE; } @@ -4517,7 +4614,7 @@ struct AutoValue bool SizeToType(JSContext* cx, JSObject* type) { // Allocate a minimum of sizeof(ffi_arg) to handle small integers. - size_t size = Align(CType::GetSize(cx, type), sizeof(ffi_arg)); + size_t size = Align(CType::GetSize(type), sizeof(ffi_arg)); mData = cx->array_new(size); if (mData) memset(mData, 0, size); @@ -4533,7 +4630,7 @@ GetABI(JSContext* cx, jsval abiType, ffi_abi* result) if (JSVAL_IS_PRIMITIVE(abiType)) return false; - ABICode abi = GetABICode(cx, JSVAL_TO_OBJECT(abiType)); + ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType)); // determine the ABI from the subset of those available on the // given platform. ABI_DEFAULT specifies the default @@ -4547,6 +4644,11 @@ GetABI(JSContext* cx, jsval abiType, ffi_abi* result) #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) *result = FFI_STDCALL; return true; +#elif (defined(_WIN64)) + // We'd like the same code to work across Win32 and Win64, so stdcall_api + // and winapi_abi become aliases to the lone Win64 ABI. + *result = FFI_WIN64; + return true; #endif case INVALID_ABI: break; @@ -4558,18 +4660,18 @@ static JSObject* PrepareType(JSContext* cx, jsval type) { if (JSVAL_IS_PRIMITIVE(type) || - !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) { + !CType::IsCType(JSVAL_TO_OBJECT(type))) { JS_ReportError(cx, "not a ctypes type"); return NULL; } JSObject* result = JSVAL_TO_OBJECT(type); - TypeCode typeCode = CType::GetTypeCode(cx, result); + TypeCode typeCode = CType::GetTypeCode(result); if (typeCode == TYPE_array) { // convert array argument types to pointers, just like C. // ImplicitConvert will do the same, when passing an array as data. - JSObject* baseType = ArrayType::GetBaseType(cx, result); + JSObject* baseType = ArrayType::GetBaseType(result); result = PointerType::CreateInternal(cx, baseType); if (!result) return NULL; @@ -4580,13 +4682,13 @@ PrepareType(JSContext* cx, jsval type) return NULL; } - if (!CType::IsSizeDefined(cx, result)) { + if (!CType::IsSizeDefined(result)) { JS_ReportError(cx, "Argument type must have defined size"); return NULL; } // libffi cannot pass types of zero size by value. - JS_ASSERT(CType::GetSize(cx, result) != 0); + JS_ASSERT(CType::GetSize(result) != 0); return result; } @@ -4595,13 +4697,13 @@ static JSObject* PrepareReturnType(JSContext* cx, jsval type) { if (JSVAL_IS_PRIMITIVE(type) || - !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) { + !CType::IsCType(JSVAL_TO_OBJECT(type))) { JS_ReportError(cx, "not a ctypes type"); return NULL; } JSObject* result = JSVAL_TO_OBJECT(type); - TypeCode typeCode = CType::GetTypeCode(cx, result); + TypeCode typeCode = CType::GetTypeCode(result); // Arrays and functions can never be return types. if (typeCode == TYPE_array || typeCode == TYPE_function) { @@ -4609,13 +4711,13 @@ PrepareReturnType(JSContext* cx, jsval type) return NULL; } - if (typeCode != TYPE_void_t && !CType::IsSizeDefined(cx, result)) { + if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) { JS_ReportError(cx, "Return type must have defined size"); return NULL; } // libffi cannot pass types of zero size by value. - JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(cx, result) != 0); + JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0); return result; } @@ -4676,14 +4778,13 @@ PrepareCIF(JSContext* cx, } void -FunctionType::BuildSymbolName(JSContext* cx, - JSString* name, +FunctionType::BuildSymbolName(JSString* name, JSObject* typeObj, AutoCString& result) { - FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = GetFunctionInfo(typeObj); - switch (GetABICode(cx, fninfo->mABI)) { + switch (GetABICode(fninfo->mABI)) { case ABI_DEFAULT: case ABI_WINAPI: // For cdecl or WINAPI functions, no mangling is necessary. @@ -4691,6 +4792,7 @@ FunctionType::BuildSymbolName(JSContext* cx, break; case ABI_STDCALL: { +#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) // On WIN32, stdcall functions look like: // _foo@40 // where 'foo' is the function name, and '40' is the aligned size of the @@ -4703,10 +4805,15 @@ FunctionType::BuildSymbolName(JSContext* cx, size_t size = 0; for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { JSObject* argType = fninfo->mArgTypes[i]; - size += Align(CType::GetSize(cx, argType), sizeof(ffi_arg)); + size += Align(CType::GetSize(argType), sizeof(ffi_arg)); } IntegerToString(size, 10, result); +#elif defined(_WIN64) + // On Win64, stdcall is an alias to the default ABI for compatibility, so no + // mangling is done. + AppendString(result, name); +#endif break; } @@ -4750,10 +4857,10 @@ NewFunctionInfo(JSContext* cx, fninfo->mIsVariadic = false; - for (JSUint32 i = 0; i < argLength; ++i) { + for (uint32_t i = 0; i < argLength; ++i) { bool isEllipsis; if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) - return false; + return NULL; if (isEllipsis) { fninfo->mIsVariadic = true; if (i < 1) { @@ -4766,7 +4873,7 @@ NewFunctionInfo(JSContext* cx, "variadic function declaration"); return NULL; } - if (GetABICode(cx, fninfo->mABI) != ABI_DEFAULT) { + if (GetABICode(fninfo->mABI) != ABI_DEFAULT) { JS_ReportError(cx, "Variadic functions must use the __cdecl calling " "convention"); return NULL; @@ -4858,9 +4965,9 @@ FunctionType::CreateInternal(JSContext* cx, // Get ctypes.FunctionType.prototype and the common prototype for CData objects // of this type, from ctypes.CType.prototype. - JSObject* typeProto = CType::GetProtoFromType(cx, fninfo->mReturnType, + JSObject* typeProto = CType::GetProtoFromType(fninfo->mReturnType, SLOT_FUNCTIONPROTO); - JSObject* dataProto = CType::GetProtoFromType(cx, fninfo->mReturnType, + JSObject* dataProto = CType::GetProtoFromType(fninfo->mReturnType, SLOT_FUNCTIONDATAPROTO); // Create a new CType object with the common properties and slots. @@ -4871,10 +4978,7 @@ FunctionType::CreateInternal(JSContext* cx, js::AutoObjectRooter root(cx, typeObj); // Stash the FunctionInfo in a reserved slot. - if (!JS_SetReservedSlot(cx, typeObj, SLOT_FNINFO, - PRIVATE_TO_JSVAL(fninfo.get()))) - return NULL; - fninfo.forget(); + JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget())); return typeObj; } @@ -4887,32 +4991,31 @@ FunctionType::ConstructData(JSContext* cx, JSObject* typeObj, JSObject* dataObj, JSObject* fnObj, - JSObject* thisObj) + JSObject* thisObj, + jsval errVal) { - JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function); + JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function); - PRFuncPtr* data = static_cast(CData::GetData(cx, dataObj)); + PRFuncPtr* data = static_cast(CData::GetData(dataObj)); - FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); if (fninfo->mIsVariadic) { JS_ReportError(cx, "Can't declare a variadic callback function"); return JS_FALSE; } - if (GetABICode(cx, fninfo->mABI) == ABI_WINAPI) { + if (GetABICode(fninfo->mABI) == ABI_WINAPI) { JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, " "use ctypes.stdcall_abi instead"); return JS_FALSE; } - JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data); + JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data); if (!closureObj) return JS_FALSE; js::AutoObjectRooter root(cx, closureObj); // Set the closure object as the referent of the new CData object. - if (!JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT, - OBJECT_TO_JSVAL(closureObj))) - return JS_FALSE; + JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj)); // Seal the CData object, to prevent modification of the function pointer. // This permanently associates this object with the closure, and avoids @@ -4961,25 +5064,25 @@ FunctionType::Call(JSContext* cx, { // get the callee object... JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - if (!CData::IsCData(cx, obj)) { + if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return false; } - JSObject* typeObj = CData::GetCType(cx, obj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) { + JSObject* typeObj = CData::GetCType(obj); + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { JS_ReportError(cx, "not a FunctionType.ptr"); return false; } - typeObj = PointerType::GetBaseType(cx, typeObj); - if (CType::GetTypeCode(cx, typeObj) != TYPE_function) { + typeObj = PointerType::GetBaseType(typeObj); + if (CType::GetTypeCode(typeObj) != TYPE_function) { JS_ReportError(cx, "not a FunctionType.ptr"); return false; } - FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj); - JSUint32 argcFixed = fninfo->mArgTypes.length(); + FunctionInfo* fninfo = GetFunctionInfo(typeObj); + uint32_t argcFixed = fninfo->mArgTypes.length(); if ((!fninfo->mIsVariadic && argc != argcFixed) || (fninfo->mIsVariadic && argc < argcFixed)) { @@ -4988,10 +5091,9 @@ FunctionType::Call(JSContext* cx, } // Check if we have a Library object. If we do, make sure it's open. - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_REFERENT, &slot)); - if (!JSVAL_IS_VOID(slot) && Library::IsLibrary(cx, JSVAL_TO_OBJECT(slot))) { - PRLibrary* library = Library::GetLibrary(cx, JSVAL_TO_OBJECT(slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT); + if (!JSVAL_IS_VOID(slot) && Library::IsLibrary(JSVAL_TO_OBJECT(slot))) { + PRLibrary* library = Library::GetLibrary(JSVAL_TO_OBJECT(slot)); if (!library) { JS_ReportError(cx, "library is not open"); return false; @@ -5020,16 +5122,16 @@ FunctionType::Call(JSContext* cx, JSObject* obj; // Could reuse obj instead of declaring a second JSObject* type; // JSObject*, but readability would suffer. - for (JSUint32 i = argcFixed; i < argc; ++i) { + for (uint32_t i = argcFixed; i < argc; ++i) { if (JSVAL_IS_PRIMITIVE(argv[i]) || - !CData::IsCData(cx, obj = JSVAL_TO_OBJECT(argv[i]))) { + !CData::IsCData(obj = JSVAL_TO_OBJECT(argv[i]))) { // Since we know nothing about the CTypes of the ... arguments, // they absolutely must be CData objects already. JS_ReportError(cx, "argument %d of type %s is not a CData object", i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i]))); return false; } - if (!(type = CData::GetCType(cx, obj)) || + if (!(type = CData::GetCType(obj)) || !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) || // Relying on ImplicitConvert only for the limited purpose of // converting one CType to another (e.g., T[] to T*). @@ -5045,14 +5147,14 @@ FunctionType::Call(JSContext* cx, // initialize a pointer to an appropriate location, for storing the result AutoValue returnValue; - TypeCode typeCode = CType::GetTypeCode(cx, fninfo->mReturnType); + TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); if (typeCode != TYPE_void_t && !returnValue.SizeToType(cx, fninfo->mReturnType)) { JS_ReportAllocationOverflow(cx); return false; } - uintptr_t fn = *reinterpret_cast(CData::GetData(cx, obj)); + uintptr_t fn = *reinterpret_cast(CData::GetData(obj)); // suspend the request before we call into the function, since the call // may block or otherwise take a long time to return. @@ -5087,13 +5189,12 @@ FunctionType::Call(JSContext* cx, } FunctionInfo* -FunctionType::GetFunctionInfo(JSContext* cx, JSObject* obj) +FunctionType::GetFunctionInfo(JSObject* obj) { - JS_ASSERT(CType::IsCType(cx, obj)); - JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_function); + JS_ASSERT(CType::IsCType(obj)); + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO); JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); return static_cast(JSVAL_TO_PRIVATE(slot)); @@ -5102,7 +5203,7 @@ FunctionType::GetFunctionInfo(JSContext* cx, JSObject* obj) static JSBool CheckFunctionType(JSContext* cx, JSObject* obj) { - if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) { + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_function) { JS_ReportError(cx, "not a FunctionType"); return JS_FALSE; } @@ -5116,11 +5217,11 @@ FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp return JS_FALSE; // Check if we have a cached argTypes array. - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ARGS_T, vp)); + *vp = JS_GetReservedSlot(obj, SLOT_ARGS_T); if (!JSVAL_IS_VOID(*vp)) return JS_TRUE; - FunctionInfo* fninfo = GetFunctionInfo(cx, obj); + FunctionInfo* fninfo = GetFunctionInfo(obj); size_t len = fninfo->mArgTypes.length(); // Prepare a new array. @@ -5136,9 +5237,9 @@ FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp return JS_FALSE; // Seal and cache it. - if (!JS_FreezeObject(cx, argTypes) || - !JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes))) + if (!JS_FreezeObject(cx, argTypes)) return JS_FALSE; + JS_SetReservedSlot(obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)); *vp = OBJECT_TO_JSVAL(argTypes); return JS_TRUE; @@ -5151,7 +5252,7 @@ FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* return JS_FALSE; // Get the returnType object from the FunctionInfo. - *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mReturnType); + *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mReturnType); return JS_TRUE; } @@ -5162,7 +5263,7 @@ FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; // Get the abi object from the FunctionInfo. - *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mABI); + *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mABI); return JS_TRUE; } @@ -5172,7 +5273,7 @@ FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* if (!CheckFunctionType(cx, obj)) return JS_FALSE; - *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(cx, obj)->mIsVariadic); + *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(obj)->mIsVariadic); return JS_TRUE; } @@ -5185,6 +5286,7 @@ CClosure::Create(JSContext* cx, JSObject* typeObj, JSObject* fnObj, JSObject* thisObj, + jsval errVal, PRFuncPtr* fnptr) { JS_ASSERT(fnObj); @@ -5195,11 +5297,11 @@ CClosure::Create(JSContext* cx, js::AutoObjectRooter root(cx, result); // Get the FunctionInfo from the FunctionType. - FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); JS_ASSERT(!fninfo->mIsVariadic); - JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI); + JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI); - AutoPtr cinfo(cx->new_()); + AutoPtr cinfo(cx->new_(JS_GetRuntime(cx))); if (!cinfo) { JS_ReportOutOfMemory(cx); return NULL; @@ -5207,13 +5309,12 @@ CClosure::Create(JSContext* cx, // Get the prototype of the FunctionType object, of class CTypeProto, // which stores our JSContext for use with the closure. - JSObject* proto = JS_GetPrototype(cx, typeObj); + JSObject* proto = JS_GetPrototype(typeObj); JS_ASSERT(proto); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass); + JS_ASSERT(CType::IsCTypeProto(proto)); // Get a JSContext for use with the closure. - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, proto, SLOT_CLOSURECX, &slot)); + jsval slot = JS_GetReservedSlot(proto, SLOT_CLOSURECX); if (!JSVAL_IS_VOID(slot)) { // Use the existing JSContext. cinfo->cx = static_cast(JSVAL_TO_PRIVATE(slot)); @@ -5228,20 +5329,40 @@ CClosure::Create(JSContext* cx, return NULL; } - if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX, - PRIVATE_TO_JSVAL(cinfo->cx))) { - JS_DestroyContextNoGC(cinfo->cx); + JS_SetReservedSlot(proto, SLOT_CLOSURECX, PRIVATE_TO_JSVAL(cinfo->cx)); + } + + // Prepare the error sentinel value. It's important to do this now, because + // we might be unable to convert the value to the proper type. If so, we want + // the caller to know about it _now_, rather than some uncertain time in the + // future when the error sentinel is actually needed. + if (!JSVAL_IS_VOID(errVal)) { + + // Make sure the callback returns something. + if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) { + JS_ReportError(cx, "A void callback can't pass an error sentinel"); return NULL; } - JS_ClearContextThread(cinfo->cx); - } + // With the exception of void, the FunctionType constructor ensures that + // the return type has a defined size. + JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType)); -#ifdef DEBUG - // We want *this* context's thread here so use cx instead of cinfo->cx. - cinfo->cxThread = JS_GetContextThread(cx); -#endif + // Allocate a buffer for the return value. + size_t rvSize = CType::GetSize(fninfo->mReturnType); + cinfo->errResult = cx->malloc_(rvSize); + if (!cinfo->errResult) + return NULL; + // Do the value conversion. This might fail, in which case we throw. + if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult, + false, NULL)) + return NULL; + } else { + cinfo->errResult = NULL; + } + + // Copy the important bits of context into cinfo. cinfo->closureObj = result; cinfo->typeObj = typeObj; cinfo->thisObj = thisObj; @@ -5259,18 +5380,12 @@ CClosure::Create(JSContext* cx, ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF, CClosure::ClosureStub, cinfo.get(), code); if (status != FFI_OK) { - ffi_closure_free(cinfo->closure); JS_ReportError(cx, "couldn't create closure - libffi error"); return NULL; } // Stash the ClosureInfo struct on our new object. - if (!JS_SetReservedSlot(cx, result, SLOT_CLOSUREINFO, - PRIVATE_TO_JSVAL(cinfo.get()))) { - ffi_closure_free(cinfo->closure); - return NULL; - } - cinfo.forget(); + JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget())); // Casting between void* and a function pointer is forbidden in C and C++. // Do it via an integral type. @@ -5281,12 +5396,9 @@ CClosure::Create(JSContext* cx, void CClosure::Trace(JSTracer* trc, JSObject* obj) { - JSContext* cx = trc->context; - // Make sure our ClosureInfo slot is legit. If it's not, bail. - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) || - JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); + if (JSVAL_IS_VOID(slot)) return; ClosureInfo* cinfo = static_cast(JSVAL_TO_PRIVATE(slot)); @@ -5303,15 +5415,11 @@ void CClosure::Finalize(JSContext* cx, JSObject* obj) { // Make sure our ClosureInfo slot is legit. If it's not, bail. - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) || - JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); + if (JSVAL_IS_VOID(slot)) return; ClosureInfo* cinfo = static_cast(JSVAL_TO_PRIVATE(slot)); - if (cinfo->closure) - ffi_closure_free(cinfo->closure); - cx->delete_(cinfo); } @@ -5330,10 +5438,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) JSObject* thisObj = cinfo->thisObj; JSObject* jsfnObj = cinfo->jsfnObj; - ScopedContextThread scopedThread(cx); - - // Assert that we're on the thread we were created from. - JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx)); + JS_AbortIfWrongThread(JS_GetRuntime(cx)); JSAutoRequest ar(cx); @@ -5342,16 +5447,17 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) return; // Assert that our CIFs agree. - FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj); + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); JS_ASSERT(cif == &fninfo->mCIF); - TypeCode typeCode = CType::GetTypeCode(cx, fninfo->mReturnType); + TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); // Initialize the result to zero, in case something fails. Small integer types // are promoted to a word-sized ffi_arg, so we must be careful to zero the // whole word. + size_t rvSize = 0; if (cif->rtype != &ffi_type_void) { - size_t size = cif->rtype->size; + rvSize = cif->rtype->size; switch (typeCode) { #define DEFINE_INT_TYPE(name, type, ffiType) \ case TYPE_##name: @@ -5360,12 +5466,12 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) #include "typedefs.h" - size = Align(size, sizeof(ffi_arg)); + rvSize = Align(rvSize, sizeof(ffi_arg)); break; default: break; } - memset(result, 0, size); + memset(result, 0, rvSize); } // Get a death grip on 'closureObj'. @@ -5379,7 +5485,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) } js::AutoArrayRooter roots(cx, argv.length(), argv.begin()); - for (JSUint32 i = 0; i < cif->nargs; ++i) { + for (uint32_t i = 0; i < cif->nargs; ++i) { // Convert each argument, and have any CData objects created depend on // the existing buffers. if (!ConvertToJS(cx, fninfo->mArgTypes[i], NULL, args[i], false, false, @@ -5390,17 +5496,51 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) // Call the JS function. 'thisObj' may be NULL, in which case the JS engine // will find an appropriate object to use. jsval rval; - if (!JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), cif->nargs, - argv.begin(), &rval)) - return; + JSBool success = JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), + cif->nargs, argv.begin(), &rval); // Convert the result. Note that we pass 'isArgument = false', such that // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char // type, which would require an allocation that we can't track. The JS // function must perform this conversion itself and return a PointerType // CData; thusly, the burden of freeing the data is left to the user. - if (!ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, NULL)) - return; + if (success && cif->rtype != &ffi_type_void) + success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, + NULL); + + if (!success) { + // Something failed. The callee may have thrown, or it may not have + // returned a value that ImplicitConvert() was happy with. Depending on how + // prudent the consumer has been, we may or may not have a recovery plan. + + // In any case, a JS exception cannot be passed to C code, so report the + // exception if any and clear it from the cx. + if (JS_IsExceptionPending(cx)) + JS_ReportPendingException(cx); + + if (cinfo->errResult) { + // Good case: we have a sentinel that we can return. Copy it in place of + // the actual return value, and then proceed. + + // The buffer we're returning might be larger than the size of the return + // type, due to libffi alignment issues (see above). But it should never + // be smaller. + size_t copySize = CType::GetSize(fninfo->mReturnType); + JS_ASSERT(copySize <= rvSize); + memcpy(result, cinfo->errResult, copySize); + } else { + // Bad case: not much we can do here. The rv is already zeroed out, so we + // just report (another) error and hope for the best. JS_ReportError will + // actually throw an exception here, so then we have to report it. Again. + // Ugh. + JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " + "was not specified."); + if (JS_IsExceptionPending(cx)) + JS_ReportPendingException(cx); + + return; + } + } // Small integer types must be returned as a word-sized ffi_arg. Coerce it // back into the size libffi expects. @@ -5455,18 +5595,17 @@ CData::Create(JSContext* cx, bool ownResult) { JS_ASSERT(typeObj); - JS_ASSERT(CType::IsCType(cx, typeObj)); - JS_ASSERT(CType::IsSizeDefined(cx, typeObj)); + JS_ASSERT(CType::IsCType(typeObj)); + JS_ASSERT(CType::IsSizeDefined(typeObj)); JS_ASSERT(ownResult || source); - JS_ASSERT_IF(refObj && CData::IsCData(cx, refObj), !ownResult); + JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult); // Get the 'prototype' property from the type. - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_PROTO, &slot)); + jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO); JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot)); JSObject* proto = JSVAL_TO_OBJECT(slot); - JSObject* parent = JS_GetParent(cx, typeObj); + JSObject* parent = JS_GetParent(typeObj); JS_ASSERT(parent); JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, parent); @@ -5475,17 +5614,14 @@ CData::Create(JSContext* cx, js::AutoObjectRooter root(cx, dataObj); // set the CData's associated type - if (!JS_SetReservedSlot(cx, dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj))) - return NULL; + JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)); // Stash the referent object, if any, for GC safety. - if (refObj && - !JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj))) - return NULL; + if (refObj) + JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)); // Set our ownership flag. - if (!JS_SetReservedSlot(cx, dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult))) - return NULL; + JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)); // attach the buffer. since it might not be 2-byte aligned, we need to // allocate an aligned space for it and store it there. :( @@ -5500,7 +5636,7 @@ CData::Create(JSContext* cx, data = static_cast(source); } else { // Initialize our own buffer. - size_t size = CType::GetSize(cx, typeObj); + size_t size = CType::GetSize(typeObj); data = cx->array_new(size); if (!data) { // Report a catchable allocation error. @@ -5516,12 +5652,7 @@ CData::Create(JSContext* cx, } *buffer = data; - if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) { - if (ownResult) - Foreground::array_delete(data); - Foreground::delete_(buffer); - return NULL; - } + JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer)); return dataObj; } @@ -5530,13 +5661,14 @@ void CData::Finalize(JSContext* cx, JSObject* obj) { // Delete our buffer, and the data it contains if we own it. - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_OWNS, &slot) || JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS); + if (JSVAL_IS_VOID(slot)) return; JSBool owns = JSVAL_TO_BOOLEAN(slot); - if (!JS_GetReservedSlot(cx, obj, SLOT_DATA, &slot) || JSVAL_IS_VOID(slot)) + slot = JS_GetReservedSlot(obj, SLOT_DATA); + if (JSVAL_IS_VOID(slot)) return; char** buffer = static_cast(JSVAL_TO_PRIVATE(slot)); @@ -5546,24 +5678,22 @@ CData::Finalize(JSContext* cx, JSObject* obj) } JSObject* -CData::GetCType(JSContext* cx, JSObject* dataObj) +CData::GetCType(JSObject* dataObj) { - JS_ASSERT(CData::IsCData(cx, dataObj)); + JS_ASSERT(CData::IsCData(dataObj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_CTYPE, &slot)); + jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE); JSObject* typeObj = JSVAL_TO_OBJECT(slot); - JS_ASSERT(CType::IsCType(cx, typeObj)); + JS_ASSERT(CType::IsCType(typeObj)); return typeObj; } void* -CData::GetData(JSContext* cx, JSObject* dataObj) +CData::GetData(JSObject* dataObj) { - JS_ASSERT(CData::IsCData(cx, dataObj)); + JS_ASSERT(CData::IsCData(dataObj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_DATA, &slot)); + jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA); void** buffer = static_cast(JSVAL_TO_PRIVATE(slot)); JS_ASSERT(buffer); @@ -5572,21 +5702,27 @@ CData::GetData(JSContext* cx, JSObject* dataObj) } bool -CData::IsCData(JSContext* cx, JSObject* obj) +CData::IsCData(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sCDataClass; + return JS_GetClass(obj) == &sCDataClass; +} + +bool +CData::IsCDataProto(JSObject* obj) +{ + return JS_GetClass(obj) == &sCDataProtoClass; } JSBool CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) { - if (!IsCData(cx, obj)) { + if (!IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } // Convert the value to a primitive; do not create a new CData object. - if (!ConvertToJS(cx, GetCType(cx, obj), NULL, GetData(cx, obj), true, false, vp)) + if (!ConvertToJS(cx, GetCType(obj), NULL, GetData(obj), true, false, vp)) return JS_FALSE; return JS_TRUE; @@ -5595,12 +5731,12 @@ CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) JSBool CData::ValueSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp) { - if (!IsCData(cx, obj)) { + if (!IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - return ImplicitConvert(cx, *vp, GetCType(cx, obj), GetData(cx, obj), false, NULL); + return ImplicitConvert(cx, *vp, GetCType(obj), GetData(obj), false, NULL); } JSBool @@ -5612,12 +5748,12 @@ CData::Address(JSContext* cx, uintN argc, jsval* vp) } JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !IsCData(cx, obj)) { + if (!obj || !IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); + JSObject* typeObj = CData::GetCType(obj); JSObject* pointerType = PointerType::CreateInternal(cx, typeObj); if (!pointerType) return JS_FALSE; @@ -5631,8 +5767,8 @@ CData::Address(JSContext* cx, uintN argc, jsval* vp) JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); // Manually set the pointer inside the object, so we skip the conversion step. - void** data = static_cast(GetData(cx, result)); - *data = GetData(cx, obj); + void** data = static_cast(GetData(result)); + *data = GetData(obj); return JS_TRUE; } @@ -5646,23 +5782,23 @@ CData::Cast(JSContext* cx, uintN argc, jsval* vp) jsval* argv = JS_ARGV(cx, vp); if (JSVAL_IS_PRIMITIVE(argv[0]) || - !CData::IsCData(cx, JSVAL_TO_OBJECT(argv[0]))) { + !CData::IsCData(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "first argument must be a CData"); return JS_FALSE; } JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]); - JSObject* sourceType = CData::GetCType(cx, sourceData); + JSObject* sourceType = CData::GetCType(sourceData); if (JSVAL_IS_PRIMITIVE(argv[1]) || - !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1]))) { + !CType::IsCType(JSVAL_TO_OBJECT(argv[1]))) { JS_ReportError(cx, "second argument must be a CType"); return JS_FALSE; } JSObject* targetType = JSVAL_TO_OBJECT(argv[1]); size_t targetSize; - if (!CType::GetSafeSize(cx, targetType, &targetSize) || - targetSize > CType::GetSize(cx, sourceType)) { + if (!CType::GetSafeSize(targetType, &targetSize) || + targetSize > CType::GetSize(sourceType)) { JS_ReportError(cx, "target CType has undefined or larger size than source CType"); return JS_FALSE; @@ -5670,7 +5806,7 @@ CData::Cast(JSContext* cx, uintN argc, jsval* vp) // Construct a new CData object with a type of 'targetType' and a referent // of 'sourceData'. - void* data = CData::GetData(cx, sourceData); + void* data = CData::GetData(sourceData); JSObject* result = CData::Create(cx, targetType, sourceData, data, false); if (!result) return JS_FALSE; @@ -5679,6 +5815,38 @@ CData::Cast(JSContext* cx, uintN argc, jsval* vp) return JS_TRUE; } +JSBool +CData::GetRuntime(JSContext* cx, uintN argc, jsval* vp) +{ + if (argc != 1) { + JS_ReportError(cx, "getRuntime takes one argument"); + return JS_FALSE; + } + + jsval* argv = JS_ARGV(cx, vp); + if (JSVAL_IS_PRIMITIVE(argv[0]) || + !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) { + JS_ReportError(cx, "first argument must be a CType"); + return JS_FALSE; + } + + JSObject* targetType = JSVAL_TO_OBJECT(argv[0]); + size_t targetSize; + if (!CType::GetSafeSize(targetType, &targetSize) || + targetSize != sizeof(void*)) { + JS_ReportError(cx, "target CType has non-pointer size"); + return JS_FALSE; + } + + void* data = static_cast(cx->runtime); + JSObject* result = CData::Create(cx, targetType, NULL, &data, true); + if (!result) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); + return JS_TRUE; +} + JSBool CData::ReadString(JSContext* cx, uintN argc, jsval* vp) { @@ -5688,7 +5856,7 @@ CData::ReadString(JSContext* cx, uintN argc, jsval* vp) } JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !IsCData(cx, obj)) { + if (!obj || !IsCData(obj)) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } @@ -5696,23 +5864,23 @@ CData::ReadString(JSContext* cx, uintN argc, jsval* vp) // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit // character or integer type. JSObject* baseType; - JSObject* typeObj = GetCType(cx, obj); - TypeCode typeCode = CType::GetTypeCode(cx, typeObj); + JSObject* typeObj = GetCType(obj); + TypeCode typeCode = CType::GetTypeCode(typeObj); void* data; size_t maxLength = -1; switch (typeCode) { case TYPE_pointer: - baseType = PointerType::GetBaseType(cx, typeObj); - data = *static_cast(GetData(cx, obj)); + baseType = PointerType::GetBaseType(typeObj); + data = *static_cast(GetData(obj)); if (data == NULL) { JS_ReportError(cx, "cannot read contents of null pointer"); return JS_FALSE; } break; case TYPE_array: - baseType = ArrayType::GetBaseType(cx, typeObj); - data = GetData(cx, obj); - maxLength = ArrayType::GetLength(cx, typeObj); + baseType = ArrayType::GetBaseType(typeObj); + data = GetData(obj); + maxLength = ArrayType::GetLength(typeObj); break; default: JS_ReportError(cx, "not a PointerType or ArrayType"); @@ -5722,7 +5890,7 @@ CData::ReadString(JSContext* cx, uintN argc, jsval* vp) // Convert the string buffer, taking care to determine the correct string // length in the case of arrays (which may contain embedded nulls). JSString* result; - switch (CType::GetTypeCode(cx, baseType)) { + switch (CType::GetTypeCode(baseType)) { case TYPE_int8_t: case TYPE_uint8_t: case TYPE_char: @@ -5733,7 +5901,7 @@ CData::ReadString(JSContext* cx, uintN argc, jsval* vp) // Determine the length. size_t dstlen; - if (!js_InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen)) + if (!InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen)) return JS_FALSE; jschar* dst = @@ -5741,7 +5909,7 @@ CData::ReadString(JSContext* cx, uintN argc, jsval* vp) if (!dst) return JS_FALSE; - ASSERT_OK(js_InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen)); + ASSERT_OK(InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen)); dst[dstlen] = 0; result = JS_NewUCString(cx, dst, dstlen); @@ -5779,29 +5947,35 @@ CData::ToSource(JSContext* cx, uintN argc, jsval* vp) } JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !CData::IsCData(cx, obj)) { + if (!obj || + !(CData::IsCData(obj) || CData::IsCDataProto(obj))) { JS_ReportError(cx, "not a CData"); return JS_FALSE; } - JSObject* typeObj = CData::GetCType(cx, obj); - void* data = CData::GetData(cx, obj); - - // Walk the types, building up the toSource() string. - // First, we build up the type expression: - // 't.ptr' for pointers; - // 't.array([n])' for arrays; - // 'n' for structs, where n = t.name, the struct's name. (We assume this is - // bound to a variable in the current scope.) - AutoString source; - BuildTypeSource(cx, typeObj, true, source); - AppendString(source, "("); - if (!BuildDataSource(cx, typeObj, data, false, source)) - return JS_FALSE; + JSString* result; + if (CData::IsCData(obj)) { + JSObject* typeObj = CData::GetCType(obj); + void* data = CData::GetData(obj); + + // Walk the types, building up the toSource() string. + // First, we build up the type expression: + // 't.ptr' for pointers; + // 't.array([n])' for arrays; + // 'n' for structs, where n = t.name, the struct's name. (We assume this is + // bound to a variable in the current scope.) + AutoString source; + BuildTypeSource(cx, typeObj, true, source); + AppendString(source, "("); + if (!BuildDataSource(cx, typeObj, data, false, source)) + return JS_FALSE; - AppendString(source, ")"); + AppendString(source, ")"); - JSString* result = NewUCString(cx, source); + result = NewUCString(cx, source); + } + else + result = JS_NewStringCopyZ(cx, "[CData proto object]"); if (!result) return JS_FALSE; @@ -5816,26 +5990,23 @@ CData::ToSource(JSContext* cx, uintN argc, jsval* vp) JSObject* Int64Base::Construct(JSContext* cx, JSObject* proto, - JSUint64 data, + uint64_t data, bool isUnsigned) { JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class; - JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(cx, proto)); + JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(proto)); if (!result) return NULL; js::AutoObjectRooter root(cx, result); // attach the Int64's data - JSUint64* buffer = cx->new_(data); + uint64_t* buffer = cx->new_(data); if (!buffer) { JS_ReportOutOfMemory(cx); return NULL; } - if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) { - Foreground::delete_(buffer); - return NULL; - } + JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer)); if (!JS_FreezeObject(cx, result)) return NULL; @@ -5846,20 +6017,19 @@ Int64Base::Construct(JSContext* cx, void Int64Base::Finalize(JSContext* cx, JSObject* obj) { - jsval slot; - if (!JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot) || JSVAL_IS_VOID(slot)) + jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); + if (JSVAL_IS_VOID(slot)) return; - cx->delete_(static_cast(JSVAL_TO_PRIVATE(slot))); + cx->delete_(static_cast(JSVAL_TO_PRIVATE(slot))); } -JSUint64 -Int64Base::GetInt(JSContext* cx, JSObject* obj) { - JS_ASSERT(Int64::IsInt64(cx, obj) || UInt64::IsUInt64(cx, obj)); +uint64_t +Int64Base::GetInt(JSObject* obj) { + JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot)); - return *static_cast(JSVAL_TO_PRIVATE(slot)); + jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); + return *static_cast(JSVAL_TO_PRIVATE(slot)); } JSBool @@ -5887,9 +6057,9 @@ Int64Base::ToString(JSContext* cx, AutoString intString; if (isUnsigned) { - IntegerToString(GetInt(cx, obj), radix, intString); + IntegerToString(GetInt(obj), radix, intString); } else { - IntegerToString(static_cast(GetInt(cx, obj)), radix, intString); + IntegerToString(static_cast(GetInt(obj)), radix, intString); } JSString *result = NewUCString(cx, intString); @@ -5916,10 +6086,10 @@ Int64Base::ToSource(JSContext* cx, AutoString source; if (isUnsigned) { AppendString(source, "ctypes.UInt64(\""); - IntegerToString(GetInt(cx, obj), 10, source); + IntegerToString(GetInt(obj), 10, source); } else { AppendString(source, "ctypes.Int64(\""); - IntegerToString(static_cast(GetInt(cx, obj)), 10, source); + IntegerToString(static_cast(GetInt(obj)), 10, source); } AppendString(source, "\")"); @@ -5943,7 +6113,7 @@ Int64::Construct(JSContext* cx, } jsval* argv = JS_ARGV(cx, vp); - JSInt64 i = 0; + int64_t i = 0; if (!jsvalToBigInteger(cx, argv[0], true, &i)) return TypeError(cx, "int64", argv[0]); @@ -5952,7 +6122,7 @@ Int64::Construct(JSContext* cx, ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), "prototype", &slot)); JSObject* proto = JSVAL_TO_OBJECT(slot); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64ProtoClass); + JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); JSObject* result = Int64Base::Construct(cx, proto, i, false); if (!result) @@ -5963,16 +6133,16 @@ Int64::Construct(JSContext* cx, } bool -Int64::IsInt64(JSContext* cx, JSObject* obj) +Int64::IsInt64(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sInt64Class; + return JS_GetClass(obj) == &sInt64Class; } JSBool Int64::ToString(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !Int64::IsInt64(cx, obj)) { + if (!obj || !Int64::IsInt64(obj)) { JS_ReportError(cx, "not an Int64"); return JS_FALSE; } @@ -5984,7 +6154,7 @@ JSBool Int64::ToSource(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !Int64::IsInt64(cx, obj)) { + if (!obj || !Int64::IsInt64(obj)) { JS_ReportError(cx, "not an Int64"); return JS_FALSE; } @@ -5999,8 +6169,8 @@ Int64::Compare(JSContext* cx, uintN argc, jsval* vp) if (argc != 2 || JSVAL_IS_PRIMITIVE(argv[0]) || JSVAL_IS_PRIMITIVE(argv[1]) || - !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0])) || - !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[1]))) { + !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0])) || + !Int64::IsInt64(JSVAL_TO_OBJECT(argv[1]))) { JS_ReportError(cx, "compare takes two Int64 arguments"); return JS_FALSE; } @@ -6008,8 +6178,8 @@ Int64::Compare(JSContext* cx, uintN argc, jsval* vp) JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]); JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]); - JSInt64 i1 = Int64Base::GetInt(cx, obj1); - JSInt64 i2 = Int64Base::GetInt(cx, obj2); + int64_t i1 = Int64Base::GetInt(obj1); + int64_t i2 = Int64Base::GetInt(obj2); if (i1 == i2) JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0)); @@ -6021,7 +6191,7 @@ Int64::Compare(JSContext* cx, uintN argc, jsval* vp) return JS_TRUE; } -#define LO_MASK ((JSUint64(1) << 32) - 1) +#define LO_MASK ((uint64_t(1) << 32) - 1) #define INT64_LO(i) ((i) & LO_MASK) #define INT64_HI(i) ((i) >> 32) @@ -6030,14 +6200,14 @@ Int64::Lo(JSContext* cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) || - !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0]))) { + !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "lo takes one Int64 argument"); return JS_FALSE; } JSObject* obj = JSVAL_TO_OBJECT(argv[0]); - JSInt64 u = Int64Base::GetInt(cx, obj); - jsdouble d = JSUint32(INT64_LO(u)); + int64_t u = Int64Base::GetInt(obj); + jsdouble d = uint32_t(INT64_LO(u)); jsval result; if (!JS_NewNumberValue(cx, d, &result)) @@ -6052,14 +6222,14 @@ Int64::Hi(JSContext* cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) || - !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0]))) { + !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "hi takes one Int64 argument"); return JS_FALSE; } JSObject* obj = JSVAL_TO_OBJECT(argv[0]); - JSInt64 u = Int64Base::GetInt(cx, obj); - jsdouble d = JSInt32(INT64_HI(u)); + int64_t u = Int64Base::GetInt(obj); + jsdouble d = int32_t(INT64_HI(u)); jsval result; if (!JS_NewNumberValue(cx, d, &result)) @@ -6078,22 +6248,21 @@ Int64::Join(JSContext* cx, uintN argc, jsval* vp) } jsval* argv = JS_ARGV(cx, vp); - JSInt32 hi; - JSUint32 lo; + int32_t hi; + uint32_t lo; if (!jsvalToInteger(cx, argv[0], &hi)) return TypeError(cx, "int32", argv[0]); if (!jsvalToInteger(cx, argv[1], &lo)) return TypeError(cx, "uint32", argv[1]); - JSInt64 i = (JSInt64(hi) << 32) + JSInt64(lo); + int64_t i = (int64_t(hi) << 32) + int64_t(lo); // Get Int64.prototype from the function's reserved slot. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot)); + jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); JSObject* proto = JSVAL_TO_OBJECT(slot); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64ProtoClass); + JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); JSObject* result = Int64Base::Construct(cx, proto, i, false); if (!result) @@ -6115,7 +6284,7 @@ UInt64::Construct(JSContext* cx, } jsval* argv = JS_ARGV(cx, vp); - JSUint64 u = 0; + uint64_t u = 0; if (!jsvalToBigInteger(cx, argv[0], true, &u)) return TypeError(cx, "uint64", argv[0]); @@ -6124,7 +6293,7 @@ UInt64::Construct(JSContext* cx, ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), "prototype", &slot)); JSObject* proto = JSVAL_TO_OBJECT(slot); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64ProtoClass); + JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); JSObject* result = Int64Base::Construct(cx, proto, u, true); if (!result) @@ -6135,16 +6304,16 @@ UInt64::Construct(JSContext* cx, } bool -UInt64::IsUInt64(JSContext* cx, JSObject* obj) +UInt64::IsUInt64(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sUInt64Class; + return JS_GetClass(obj) == &sUInt64Class; } JSBool UInt64::ToString(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !UInt64::IsUInt64(cx, obj)) { + if (!obj || !UInt64::IsUInt64(obj)) { JS_ReportError(cx, "not a UInt64"); return JS_FALSE; } @@ -6156,7 +6325,7 @@ JSBool UInt64::ToSource(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !UInt64::IsUInt64(cx, obj)) { + if (!obj || !UInt64::IsUInt64(obj)) { JS_ReportError(cx, "not a UInt64"); return JS_FALSE; } @@ -6171,8 +6340,8 @@ UInt64::Compare(JSContext* cx, uintN argc, jsval* vp) if (argc != 2 || JSVAL_IS_PRIMITIVE(argv[0]) || JSVAL_IS_PRIMITIVE(argv[1]) || - !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0])) || - !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[1]))) { + !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0])) || + !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[1]))) { JS_ReportError(cx, "compare takes two UInt64 arguments"); return JS_FALSE; } @@ -6180,8 +6349,8 @@ UInt64::Compare(JSContext* cx, uintN argc, jsval* vp) JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]); JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]); - JSUint64 u1 = Int64Base::GetInt(cx, obj1); - JSUint64 u2 = Int64Base::GetInt(cx, obj2); + uint64_t u1 = Int64Base::GetInt(obj1); + uint64_t u2 = Int64Base::GetInt(obj2); if (u1 == u2) JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0)); @@ -6198,14 +6367,14 @@ UInt64::Lo(JSContext* cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) || - !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0]))) { + !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "lo takes one UInt64 argument"); return JS_FALSE; } JSObject* obj = JSVAL_TO_OBJECT(argv[0]); - JSUint64 u = Int64Base::GetInt(cx, obj); - jsdouble d = JSUint32(INT64_LO(u)); + uint64_t u = Int64Base::GetInt(obj); + jsdouble d = uint32_t(INT64_LO(u)); jsval result; if (!JS_NewNumberValue(cx, d, &result)) @@ -6220,14 +6389,14 @@ UInt64::Hi(JSContext* cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) || - !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0]))) { + !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "hi takes one UInt64 argument"); return JS_FALSE; } JSObject* obj = JSVAL_TO_OBJECT(argv[0]); - JSUint64 u = Int64Base::GetInt(cx, obj); - jsdouble d = JSUint32(INT64_HI(u)); + uint64_t u = Int64Base::GetInt(obj); + jsdouble d = uint32_t(INT64_HI(u)); jsval result; if (!JS_NewNumberValue(cx, d, &result)) @@ -6246,22 +6415,21 @@ UInt64::Join(JSContext* cx, uintN argc, jsval* vp) } jsval* argv = JS_ARGV(cx, vp); - JSUint32 hi; - JSUint32 lo; + uint32_t hi; + uint32_t lo; if (!jsvalToInteger(cx, argv[0], &hi)) return TypeError(cx, "uint32_t", argv[0]); if (!jsvalToInteger(cx, argv[1], &lo)) return TypeError(cx, "uint32_t", argv[1]); - JSUint64 u = (JSUint64(hi) << 32) + JSUint64(lo); + uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo); // Get UInt64.prototype from the function's reserved slot. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - jsval slot; - ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot)); + jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); JSObject* proto = JSVAL_TO_OBJECT(slot); - JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64ProtoClass); + JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); JSObject* result = Int64Base::Construct(cx, proto, u, true); if (!result) diff --git a/deps/mozjs/js/src/ctypes/CTypes.h b/deps/mozjs/js/src/ctypes/CTypes.h index 78effaea463..9ec0fdb7175 100644 --- a/deps/mozjs/js/src/ctypes/CTypes.h +++ b/deps/mozjs/js/src/ctypes/CTypes.h @@ -41,10 +41,11 @@ #include "jscntxt.h" #include "jsapi.h" -#include "jshashtable.h" #include "prlink.h" #include "ffi.h" +#include "js/HashTable.h" + namespace js { namespace ctypes { @@ -266,10 +267,10 @@ struct FieldHashPolicy typedef JSFlatString* Key; typedef Key Lookup; - static uint32 hash(const Lookup &l) { + static uint32_t hash(const Lookup &l) { const jschar* s = l->chars(); size_t n = l->length(); - uint32 hash = 0; + uint32_t hash = 0; for (; n > 0; s++, n--) hash = hash * 33 + *s; return hash; @@ -322,19 +323,34 @@ struct FunctionInfo struct ClosureInfo { JSContext* cx; // JSContext to use + JSRuntime* rt; // Used in the destructor, where cx might have already + // been GCed. JSObject* closureObj; // CClosure object JSObject* typeObj; // FunctionType describing the C function JSObject* thisObj; // 'this' object to use for the JS function call JSObject* jsfnObj; // JS function + void* errResult; // Result that will be returned if the closure throws ffi_closure* closure; // The C closure itself -#ifdef DEBUG - jsword cxThread; // The thread on which the context may be used -#endif + + // Anything conditionally freed in the destructor should be initialized to + // NULL here. + ClosureInfo(JSRuntime* runtime) + : rt(runtime) + , errResult(NULL) + , closure(NULL) + {} + + ~ClosureInfo() { + if (closure) + ffi_closure_free(closure); + if (errResult) + rt->free_(errResult); + }; }; -bool IsCTypesGlobal(JSContext* cx, JSObject* obj); +bool IsCTypesGlobal(JSObject* obj); -JSCTypesCallbacks* GetCallbacks(JSContext* cx, JSObject* obj); +JSCTypesCallbacks* GetCallbacks(JSObject* obj); JSBool InitTypeClasses(JSContext* cx, JSObject* parent); @@ -373,7 +389,8 @@ enum CTypeProtoSlot { SLOT_FUNCTIONDATAPROTO = 8, // common ancestor of all CData objects of FunctionType SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object - SLOT_CLOSURECX = 11, // JSContext for use with FunctionType closures + SLOT_OURDATAPROTO = 11, // the data prototype corresponding to this object + SLOT_CLOSURECX = 12, // JSContext for use with FunctionType closures CTYPEPROTO_SLOTS }; @@ -437,40 +454,41 @@ namespace CType { JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type, jsval size, jsval align, ffi_type* ffiType); - bool IsCType(JSContext* cx, JSObject* obj); - TypeCode GetTypeCode(JSContext* cx, JSObject* typeObj); - bool TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2); - size_t GetSize(JSContext* cx, JSObject* obj); - bool GetSafeSize(JSContext* cx, JSObject* obj, size_t* result); - bool IsSizeDefined(JSContext* cx, JSObject* obj); - size_t GetAlignment(JSContext* cx, JSObject* obj); + bool IsCType(JSObject* obj); + bool IsCTypeProto(JSObject* obj); + TypeCode GetTypeCode(JSObject* typeObj); + bool TypesEqual(JSObject* t1, JSObject* t2); + size_t GetSize(JSObject* obj); + bool GetSafeSize(JSObject* obj, size_t* result); + bool IsSizeDefined(JSObject* obj); + size_t GetAlignment(JSObject* obj); ffi_type* GetFFIType(JSContext* cx, JSObject* obj); JSString* GetName(JSContext* cx, JSObject* obj); - JSObject* GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot); - JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot); - JSCTypesCallbacks* GetCallbacksFromType(JSContext* cx, JSObject* obj); + JSObject* GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot); + JSObject* GetProtoFromType(JSObject* obj, CTypeProtoSlot slot); + JSCTypesCallbacks* GetCallbacksFromType(JSObject* obj); } namespace PointerType { JSObject* CreateInternal(JSContext* cx, JSObject* baseType); - JSObject* GetBaseType(JSContext* cx, JSObject* obj); + JSObject* GetBaseType(JSObject* obj); } namespace ArrayType { JSObject* CreateInternal(JSContext* cx, JSObject* baseType, size_t length, bool lengthDefined); - JSObject* GetBaseType(JSContext* cx, JSObject* obj); - size_t GetLength(JSContext* cx, JSObject* obj); - bool GetSafeLength(JSContext* cx, JSObject* obj, size_t* result); + JSObject* GetBaseType(JSObject* obj); + size_t GetLength(JSObject* obj); + bool GetSafeLength(JSObject* obj, size_t* result); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); } namespace StructType { JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); - const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj); + const FieldInfoHash* GetFieldInfo(JSObject* obj); const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name); JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); @@ -483,35 +501,37 @@ namespace FunctionType { JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj, JSObject* refObj, PRFuncPtr fnptr, JSObject* result); - FunctionInfo* GetFunctionInfo(JSContext* cx, JSObject* obj); - JSObject* GetLibrary(JSContext* cx, JSObject* obj); - void BuildSymbolName(JSContext* cx, JSString* name, JSObject* typeObj, + FunctionInfo* GetFunctionInfo(JSObject* obj); + void BuildSymbolName(JSString* name, JSObject* typeObj, AutoCString& result); } namespace CClosure { JSObject* Create(JSContext* cx, JSObject* typeObj, JSObject* fnObj, - JSObject* thisObj, PRFuncPtr* fnptr); + JSObject* thisObj, jsval errVal, PRFuncPtr* fnptr); } namespace CData { JSObject* Create(JSContext* cx, JSObject* typeObj, JSObject* refObj, void* data, bool ownResult); - JSObject* GetCType(JSContext* cx, JSObject* dataObj); - void* GetData(JSContext* cx, JSObject* dataObj); - bool IsCData(JSContext* cx, JSObject* obj); + JSObject* GetCType(JSObject* dataObj); + void* GetData(JSObject* dataObj); + bool IsCData(JSObject* obj); + bool IsCDataProto(JSObject* obj); // Attached by JSAPI as the function 'ctypes.cast' JSBool Cast(JSContext* cx, uintN argc, jsval* vp); + // Attached by JSAPI as the function 'ctypes.getRuntime' + JSBool GetRuntime(JSContext* cx, uintN argc, jsval* vp); } namespace Int64 { - bool IsInt64(JSContext* cx, JSObject* obj); + bool IsInt64(JSObject* obj); } namespace UInt64 { - bool IsUInt64(JSContext* cx, JSObject* obj); + bool IsUInt64(JSObject* obj); } } diff --git a/deps/mozjs/js/src/ctypes/Library.cpp b/deps/mozjs/js/src/ctypes/Library.cpp index 517d6760e36..33cb6d3a2a9 100644 --- a/deps/mozjs/js/src/ctypes/Library.cpp +++ b/deps/mozjs/js/src/ctypes/Library.cpp @@ -39,6 +39,7 @@ * ***** END LICENSE BLOCK ***** */ #include "jscntxt.h" +#include "jsstr.h" #include "Library.h" #include "CTypes.h" #include "prlink.h" @@ -120,8 +121,7 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) js::AutoObjectRooter root(cx, libraryObj); // initialize the library - if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL))) - return NULL; + JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)); // attach API functions if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions)) @@ -159,7 +159,7 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) // Fallback: assume the platform native charset is UTF-8. This is true // for Mac OS X, Android, and probably Linux. size_t nbytes = - js_GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length()); + GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length()); if (nbytes == (size_t) -1) return NULL; @@ -167,7 +167,7 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) if (!pathBytes) return NULL; - ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, pathStr->chars(), + ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(), pathStr->length(), pathBytes, &nbytes)); pathBytes[nbytes] = 0; } @@ -186,26 +186,23 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) } // stash the library - if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, - PRIVATE_TO_JSVAL(library))) - return NULL; + JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library)); return libraryObj; } bool -Library::IsLibrary(JSContext* cx, JSObject* obj) +Library::IsLibrary(JSObject* obj) { - return JS_GET_CLASS(cx, obj) == &sLibraryClass; + return JS_GetClass(obj) == &sLibraryClass; } PRLibrary* -Library::GetLibrary(JSContext* cx, JSObject* obj) +Library::GetLibrary(JSObject* obj) { - JS_ASSERT(IsLibrary(cx, obj)); + JS_ASSERT(IsLibrary(obj)); - jsval slot; - JS_GetReservedSlot(cx, obj, SLOT_LIBRARY, &slot); + jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY); return static_cast(JSVAL_TO_PRIVATE(slot)); } @@ -213,7 +210,7 @@ void Library::Finalize(JSContext* cx, JSObject* obj) { // unload the library - PRLibrary* library = GetLibrary(cx, obj); + PRLibrary* library = GetLibrary(obj); if (library) PR_UnloadLibrary(library); } @@ -222,7 +219,7 @@ JSBool Library::Open(JSContext* cx, uintN argc, jsval *vp) { JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp); - if (!ctypesObj || !IsCTypesGlobal(cx, ctypesObj)) { + if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) { JS_ReportError(cx, "not a ctypes object"); return JS_FALSE; } @@ -232,7 +229,7 @@ Library::Open(JSContext* cx, uintN argc, jsval *vp) return JS_FALSE; } - JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(cx, ctypesObj)); + JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(ctypesObj)); if (!library) return JS_FALSE; @@ -244,7 +241,7 @@ JSBool Library::Close(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !IsLibrary(cx, obj)) { + if (!obj || !IsLibrary(obj)) { JS_ReportError(cx, "not a library"); return JS_FALSE; } @@ -256,7 +253,7 @@ Library::Close(JSContext* cx, uintN argc, jsval* vp) // delete our internal objects Finalize(cx, obj); - JS_SetReservedSlot(cx, obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)); + JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; @@ -266,12 +263,12 @@ JSBool Library::Declare(JSContext* cx, uintN argc, jsval* vp) { JSObject* obj = JS_THIS_OBJECT(cx, vp); - if (!obj || !IsLibrary(cx, obj)) { + if (!obj || !IsLibrary(obj)) { JS_ReportError(cx, "not a library"); return JS_FALSE; } - PRLibrary* library = GetLibrary(cx, obj); + PRLibrary* library = GetLibrary(obj); if (!library) { JS_ReportError(cx, "library not open"); return JS_FALSE; @@ -320,16 +317,16 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp) } else { // Case 2). if (JSVAL_IS_PRIMITIVE(argv[1]) || - !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1])) || - !CType::IsSizeDefined(cx, JSVAL_TO_OBJECT(argv[1]))) { + !CType::IsCType(JSVAL_TO_OBJECT(argv[1])) || + !CType::IsSizeDefined(JSVAL_TO_OBJECT(argv[1]))) { JS_ReportError(cx, "second argument must be a type of defined size"); return JS_FALSE; } typeObj = JSVAL_TO_OBJECT(argv[1]); - if (CType::GetTypeCode(cx, typeObj) == TYPE_pointer) { - fnObj = PointerType::GetBaseType(cx, typeObj); - isFunction = fnObj && CType::GetTypeCode(cx, fnObj) == TYPE_function; + if (CType::GetTypeCode(typeObj) == TYPE_pointer) { + fnObj = PointerType::GetBaseType(typeObj); + isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function; } } @@ -339,7 +336,7 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp) AutoCString symbol; if (isFunction) { // Build the symbol, with mangling if necessary. - FunctionType::BuildSymbolName(cx, nameStr, fnObj, symbol); + FunctionType::BuildSymbolName(nameStr, fnObj, symbol); AppendString(symbol, "\0"); // Look up the function symbol. diff --git a/deps/mozjs/js/src/ctypes/Library.h b/deps/mozjs/js/src/ctypes/Library.h index 0293fd229ac..6ecb0c91ee0 100644 --- a/deps/mozjs/js/src/ctypes/Library.h +++ b/deps/mozjs/js/src/ctypes/Library.h @@ -58,8 +58,8 @@ namespace Library JSObject* Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks); - bool IsLibrary(JSContext* cx, JSObject* obj); - PRLibrary* GetLibrary(JSContext* cx, JSObject* obj); + bool IsLibrary(JSObject* obj); + PRLibrary* GetLibrary(JSObject* obj); JSBool Open(JSContext* cx, uintN argc, jsval* vp); } diff --git a/deps/mozjs/js/src/ctypes/libffi.patch b/deps/mozjs/js/src/ctypes/libffi.patch deleted file mode 100644 index 12344eba191..00000000000 --- a/deps/mozjs/js/src/ctypes/libffi.patch +++ /dev/null @@ -1,1148 +0,0 @@ -Patch libffi to fix bug 550602, bug 538216, bug545634, bug 594611, bug 605421 -and bug 631928. - -diff --git a/js/src/ctypes/libffi/Makefile.in b/js/src/ctypes/libffi/Makefile.in ---- a/js/src/ctypes/libffi/Makefile.in -+++ b/js/src/ctypes/libffi/Makefile.in -@@ -199,17 +199,17 @@ LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - CCLD = $(CC) - LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ - SOURCES = $(libffi_la_SOURCES) $(nodist_libffi_la_SOURCES) \ - $(libffi_convenience_la_SOURCES) \ - $(nodist_libffi_convenience_la_SOURCES) - DIST_SOURCES = $(libffi_la_SOURCES) $(libffi_convenience_la_SOURCES) --INFO_DEPS = $(srcdir)/doc/libffi.info -+INFO_DEPS = - am__TEXINFO_TEX_DIR = $(srcdir) - DVIS = doc/libffi.dvi - PDFS = doc/libffi.pdf - PSS = doc/libffi.ps - HTMLS = doc/libffi.html - TEXINFOS = doc/libffi.texi - TEXI2DVI = texi2dvi - TEXI2PDF = $(TEXI2DVI) --pdf --batch -@@ -986,57 +986,57 @@ distclean-compile: - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/ffi.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/ffi64.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/freebsd.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/sysv.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/unix64.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/win32.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/win64.Plo@am__quote@ - --.S.o: -+%.o: %.S - @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ - @am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ - @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCCAS_FALSE@ $(CPPASCOMPILE) -c -o $@ $< - --.S.obj: -+%.obj: %.S - @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ - @am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ - @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCCAS_FALSE@ $(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - --.S.lo: -+%.lo: %.S - @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ - @am__fastdepCCAS_TRUE@ $(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ - @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCCAS_FALSE@ $(LTCPPASCOMPILE) -c -o $@ $< - --.c.o: -+%.o: %.c - @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ - @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ - @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po - @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< - --.c.obj: -+%.obj: %.c - @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ - @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ - @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po - @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - --.c.lo: -+%.lo: %.c - @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ - @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ - @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo - @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ - @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< - - mostlyclean-libtool: -@@ -1129,17 +1129,17 @@ $(srcdir)/doc/stamp-vti: doc/libffi.texi $(top_srcdir)/configure - -@rm -f vti.tmp - @cp $(srcdir)/doc/version.texi $@ - - mostlyclean-vti: - -rm -f vti.tmp - - maintainer-clean-vti: - @MAINTAINER_MODE_TRUE@ -rm -f $(srcdir)/doc/stamp-vti $(srcdir)/doc/version.texi --.dvi.ps: -+%.ps: %.dvi - TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ - $(DVIPS) -o $@ $< - - uninstall-dvi-am: - @$(NORMAL_UNINSTALL) - @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ -diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure ---- a/js/src/ctypes/libffi/configure -+++ b/js/src/ctypes/libffi/configure -@@ -8903,17 +8903,17 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_from_new_cmds='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' -- fix_srcfile_path='`cygpath -w "$srcfile"`' -+ fix_srcfile_path='' - enable_shared_with_static_runtimes=yes - ;; - - darwin* | rhapsody*) - - - archive_cmds_need_lc=no - hardcode_direct=no -@@ -12270,20 +12270,20 @@ fi - - if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler supports pc related relocs" >&5 - $as_echo_n "checking assembler supports pc related relocs... " >&6; } - if test "${libffi_cv_as_x86_pcrel+set}" = set; then : - $as_echo_n "(cached) " >&6 - else - -- libffi_cv_as_x86_pcrel=yes -+ libffi_cv_as_x86_pcrel=no - echo '.text; foo: nop; .data; .long foo-.; .text' > conftest.s -- if $CC $CFLAGS -c conftest.s 2>&1 | $EGREP -i 'illegal|warning' > /dev/null; then -- libffi_cv_as_x86_pcrel=no -+ if $CC $CFLAGS -c conftest.s > /dev/null; then -+ libffi_cv_as_x86_pcrel=yes - fi - - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libffi_cv_as_x86_pcrel" >&5 - $as_echo "$libffi_cv_as_x86_pcrel" >&6; } - if test "x$libffi_cv_as_x86_pcrel" = xyes; then - - $as_echo "#define HAVE_AS_X86_PCREL 1" >>confdefs.h -diff --git a/js/src/ctypes/libffi/src/x86/ffi64.c b/js/src/ctypes/libffi/src/x86/ffi64.c ---- a/js/src/ctypes/libffi/src/x86/ffi64.c -+++ b/js/src/ctypes/libffi/src/x86/ffi64.c -@@ -373,29 +373,29 @@ ffi_prep_cif_machdep (ffi_cif *cif) - || gprcount + ngpr > MAX_GPR_REGS - || ssecount + nsse > MAX_SSE_REGS) - { - long align = cif->arg_types[i]->alignment; - - if (align < 8) - align = 8; - -- bytes = ALIGN(bytes, align); -+ bytes = ALIGN (bytes, align); - bytes += cif->arg_types[i]->size; - } - else - { - gprcount += ngpr; - ssecount += nsse; - } - } - if (ssecount) - flags |= 1 << 11; - cif->flags = flags; -- cif->bytes = bytes; -+ cif->bytes = ALIGN (bytes, 8); - - return FFI_OK; - } - - void - ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) - { - enum x86_64_reg_class classes[MAX_CLASSES]; -diff --git a/js/src/ctypes/libffi/src/arm/ffi.c b/js/src/ctypes/libffi/src/arm/ffi.c ---- a/js/src/ctypes/libffi/src/arm/ffi.c -+++ b/js/src/ctypes/libffi/src/arm/ffi.c -@@ -24,22 +24,30 @@ - DEALINGS IN THE SOFTWARE. - ----------------------------------------------------------------------- */ - - #include - #include - - #include - --/* ffi_prep_args is called by the assembly routine once stack space -- has been allocated for the function's arguments */ -+/* Forward declares. */ -+static int vfp_type_p (ffi_type *); -+static void layout_vfp_args (ffi_cif *); - --void ffi_prep_args(char *stack, extended_cif *ecif) -+/* ffi_prep_args is called by the assembly routine once stack space -+ has been allocated for the function's arguments -+ -+ The vfp_space parameter is the load area for VFP regs, the return -+ value is cif->vfp_used (word bitset of VFP regs used for passing -+ arguments). These are only used for the VFP hard-float ABI. -+*/ -+int ffi_prep_args(char *stack, extended_cif *ecif, float *vfp_space) - { -- register unsigned int i; -+ register unsigned int i, vi = 0; - register void **p_argv; - register char *argp; - register ffi_type **p_arg; - - argp = stack; - - if ( ecif->cif->flags == FFI_TYPE_STRUCT ) { - *(void **) argp = ecif->rvalue; -@@ -49,16 +57,31 @@ void ffi_prep_args(char *stack, extended_cif *ecif) - p_argv = ecif->avalue; - - for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; - (i != 0); - i--, p_arg++) - { - size_t z; - -+ /* Allocated in VFP registers. */ -+ if (ecif->cif->abi == FFI_VFP -+ && vi < ecif->cif->vfp_nargs && vfp_type_p (*p_arg)) -+ { -+ float* vfp_slot = vfp_space + ecif->cif->vfp_args[vi++]; -+ if ((*p_arg)->type == FFI_TYPE_FLOAT) -+ *((float*)vfp_slot) = *((float*)*p_argv); -+ else if ((*p_arg)->type == FFI_TYPE_DOUBLE) -+ *((double*)vfp_slot) = *((double*)*p_argv); -+ else -+ memcpy(vfp_slot, *p_argv, (*p_arg)->size); -+ p_argv++; -+ continue; -+ } -+ - /* Align if necessary */ - if (((*p_arg)->alignment - 1) & (unsigned) argp) { - argp = (char *) ALIGN(argp, (*p_arg)->alignment); - } - - if ((*p_arg)->type == FFI_TYPE_STRUCT) - argp = (char *) ALIGN(argp, 4); - -@@ -98,23 +121,25 @@ void ffi_prep_args(char *stack, extended_cif *ecif) - } - else - { - memcpy(argp, *p_argv, z); - } - p_argv++; - argp += z; - } -- -- return; -+ -+ /* Indicate the VFP registers used. */ -+ return ecif->cif->vfp_used; - } - - /* Perform machine dependent cif processing */ - ffi_status ffi_prep_cif_machdep(ffi_cif *cif) - { -+ int type_code; - /* Round the stack up to a multiple of 8 bytes. This isn't needed - everywhere, but it is on some platforms, and it doesn't harm anything - when it isn't needed. */ - cif->bytes = (cif->bytes + 7) & ~7; - - /* Set the return type flag */ - switch (cif->rtype->type) - { -@@ -125,137 +150,176 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) - break; - - case FFI_TYPE_SINT64: - case FFI_TYPE_UINT64: - cif->flags = (unsigned) FFI_TYPE_SINT64; - break; - - case FFI_TYPE_STRUCT: -- if (cif->rtype->size <= 4) -+ if (cif->abi == FFI_VFP -+ && (type_code = vfp_type_p (cif->rtype)) != 0) -+ { -+ /* A Composite Type passed in VFP registers, either -+ FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */ -+ cif->flags = (unsigned) type_code; -+ } -+ else if (cif->rtype->size <= 4) - /* A Composite Type not larger than 4 bytes is returned in r0. */ - cif->flags = (unsigned)FFI_TYPE_INT; - else - /* A Composite Type larger than 4 bytes, or whose size cannot - be determined statically ... is stored in memory at an - address passed [in r0]. */ - cif->flags = (unsigned)FFI_TYPE_STRUCT; - break; - - default: - cif->flags = FFI_TYPE_INT; - break; - } - -+ /* Map out the register placements of VFP register args. -+ The VFP hard-float calling conventions are slightly more sophisticated than -+ the base calling conventions, so we do it here instead of in ffi_prep_args(). */ -+ if (cif->abi == FFI_VFP) -+ layout_vfp_args (cif); -+ - return FFI_OK; - } - --extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, -- unsigned, unsigned, unsigned *, void (*fn)(void)); -+/* Prototypes for assembly functions, in sysv.S */ -+extern void ffi_call_SYSV (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *); -+extern void ffi_call_VFP (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *); - - void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) - { - extended_cif ecif; - - int small_struct = (cif->flags == FFI_TYPE_INT - && cif->rtype->type == FFI_TYPE_STRUCT); -+ int vfp_struct = (cif->flags == FFI_TYPE_STRUCT_VFP_FLOAT -+ || cif->flags == FFI_TYPE_STRUCT_VFP_DOUBLE); - - ecif.cif = cif; - ecif.avalue = avalue; - - unsigned int temp; - - /* If the return value is a struct and we don't have a return */ - /* value address then we need to make one */ - - if ((rvalue == NULL) && - (cif->flags == FFI_TYPE_STRUCT)) - { - ecif.rvalue = alloca(cif->rtype->size); - } - else if (small_struct) - ecif.rvalue = &temp; -+ else if (vfp_struct) -+ { -+ /* Largest case is double x 4. */ -+ ecif.rvalue = alloca(32); -+ } - else - ecif.rvalue = rvalue; - - switch (cif->abi) - { - case FFI_SYSV: -- ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, -- fn); -+ ffi_call_SYSV (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue); -+ break; - -+ case FFI_VFP: -+ ffi_call_VFP (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue); - break; -+ - default: - FFI_ASSERT(0); - break; - } - if (small_struct) - memcpy (rvalue, &temp, cif->rtype->size); -+ else if (vfp_struct) -+ memcpy (rvalue, ecif.rvalue, cif->rtype->size); - } - - /** private members **/ - - static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, -- void** args, ffi_cif* cif); -+ void** args, ffi_cif* cif, float *vfp_stack); - - void ffi_closure_SYSV (ffi_closure *); - -+void ffi_closure_VFP (ffi_closure *); -+ - /* This function is jumped to by the trampoline */ - - unsigned int --ffi_closure_SYSV_inner (closure, respp, args) -+ffi_closure_SYSV_inner (closure, respp, args, vfp_args) - ffi_closure *closure; - void **respp; - void *args; -+ void *vfp_args; - { - // our various things... - ffi_cif *cif; - void **arg_area; - - cif = closure->cif; - arg_area = (void**) alloca (cif->nargs * sizeof (void*)); - - /* this call will initialize ARG_AREA, such that each - * element in that array points to the corresponding - * value on the stack; and if the function returns - * a structure, it will re-set RESP to point to the - * structure return address. */ - -- ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); -+ ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args); - - (closure->fun) (cif, *respp, arg_area, closure->user_data); - - return cif->flags; - } - - /*@-exportheader@*/ - static void - ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, -- void **avalue, ffi_cif *cif) -+ void **avalue, ffi_cif *cif, -+ /* Used only under VFP hard-float ABI. */ -+ float *vfp_stack) - /*@=exportheader@*/ - { -- register unsigned int i; -+ register unsigned int i, vi = 0; - register void **p_argv; - register char *argp; - register ffi_type **p_arg; - - argp = stack; - - if ( cif->flags == FFI_TYPE_STRUCT ) { - *rvalue = *(void **) argp; - argp += 4; - } - - p_argv = avalue; - - for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) - { - size_t z; -- -- size_t alignment = (*p_arg)->alignment; -+ size_t alignment; -+ -+ if (cif->abi == FFI_VFP -+ && vi < cif->vfp_nargs && vfp_type_p (*p_arg)) -+ { -+ *p_argv++ = (void*)(vfp_stack + cif->vfp_args[vi++]); -+ continue; -+ } -+ -+ alignment = (*p_arg)->alignment; - if (alignment < 4) - alignment = 4; - /* Align if necessary */ - if ((alignment - 1) & (unsigned) argp) { - argp = (char *) ALIGN(argp, alignment); - } - - z = (*p_arg)->size; -@@ -290,20 +354,147 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, - - ffi_status - ffi_prep_closure_loc (ffi_closure* closure, - ffi_cif* cif, - void (*fun)(ffi_cif*,void*,void**,void*), - void *user_data, - void *codeloc) - { -- FFI_ASSERT (cif->abi == FFI_SYSV); -+ void (*closure_func)(ffi_closure*) = NULL; - -+ if (cif->abi == FFI_SYSV) -+ closure_func = &ffi_closure_SYSV; -+ else if (cif->abi == FFI_VFP) -+ closure_func = &ffi_closure_VFP; -+ else -+ FFI_ASSERT (0); -+ - FFI_INIT_TRAMPOLINE (&closure->tramp[0], \ -- &ffi_closure_SYSV, \ -+ closure_func, \ - codeloc); - - closure->cif = cif; - closure->user_data = user_data; - closure->fun = fun; - - return FFI_OK; - } -+ -+/* Below are routines for VFP hard-float support. */ -+ -+static int rec_vfp_type_p (ffi_type *t, int *elt, int *elnum) -+{ -+ switch (t->type) -+ { -+ case FFI_TYPE_FLOAT: -+ case FFI_TYPE_DOUBLE: -+ *elt = (int) t->type; -+ *elnum = 1; -+ return 1; -+ -+ case FFI_TYPE_STRUCT_VFP_FLOAT: -+ *elt = FFI_TYPE_FLOAT; -+ *elnum = t->size / sizeof (float); -+ return 1; -+ -+ case FFI_TYPE_STRUCT_VFP_DOUBLE: -+ *elt = FFI_TYPE_DOUBLE; -+ *elnum = t->size / sizeof (double); -+ return 1; -+ -+ case FFI_TYPE_STRUCT:; -+ { -+ int base_elt = 0, total_elnum = 0; -+ ffi_type **el = t->elements; -+ while (*el) -+ { -+ int el_elt = 0, el_elnum = 0; -+ if (! rec_vfp_type_p (*el, &el_elt, &el_elnum) -+ || (base_elt && base_elt != el_elt) -+ || total_elnum + el_elnum > 4) -+ return 0; -+ base_elt = el_elt; -+ total_elnum += el_elnum; -+ el++; -+ } -+ *elnum = total_elnum; -+ *elt = base_elt; -+ return 1; -+ } -+ default: ; -+ } -+ return 0; -+} -+ -+static int vfp_type_p (ffi_type *t) -+{ -+ int elt, elnum; -+ if (rec_vfp_type_p (t, &elt, &elnum)) -+ { -+ if (t->type == FFI_TYPE_STRUCT) -+ { -+ if (elnum == 1) -+ t->type = elt; -+ else -+ t->type = (elt == FFI_TYPE_FLOAT -+ ? FFI_TYPE_STRUCT_VFP_FLOAT -+ : FFI_TYPE_STRUCT_VFP_DOUBLE); -+ } -+ return (int) t->type; -+ } -+ return 0; -+} -+ -+static void place_vfp_arg (ffi_cif *cif, ffi_type *t) -+{ -+ int reg = cif->vfp_reg_free; -+ int nregs = t->size / sizeof (float); -+ int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT -+ || t->type == FFI_TYPE_FLOAT) ? 1 : 2); -+ /* Align register number. */ -+ if ((reg & 1) && align == 2) -+ reg++; -+ while (reg + nregs <= 16) -+ { -+ int s, new_used = 0; -+ for (s = reg; s < reg + nregs; s++) -+ { -+ new_used |= (1 << s); -+ if (cif->vfp_used & (1 << s)) -+ { -+ reg += align; -+ goto next_reg; -+ } -+ } -+ /* Found regs to allocate. */ -+ cif->vfp_used |= new_used; -+ cif->vfp_args[cif->vfp_nargs++] = reg; -+ -+ /* Update vfp_reg_free. */ -+ if (cif->vfp_used & (1 << cif->vfp_reg_free)) -+ { -+ reg += nregs; -+ while (cif->vfp_used & (1 << reg)) -+ reg += 1; -+ cif->vfp_reg_free = reg; -+ } -+ return; -+ next_reg: ; -+ } -+} -+ -+static void layout_vfp_args (ffi_cif *cif) -+{ -+ int i; -+ /* Init VFP fields */ -+ cif->vfp_used = 0; -+ cif->vfp_nargs = 0; -+ cif->vfp_reg_free = 0; -+ memset (cif->vfp_args, -1, 16); /* Init to -1. */ -+ -+ for (i = 0; i < cif->nargs; i++) -+ { -+ ffi_type *t = cif->arg_types[i]; -+ if (vfp_type_p (t)) -+ place_vfp_arg (cif, t); -+ } -+} -diff --git a/js/src/ctypes/libffi/src/arm/ffitarget.h b/js/src/ctypes/libffi/src/arm/ffitarget.h ---- a/js/src/ctypes/libffi/src/arm/ffitarget.h -+++ b/js/src/ctypes/libffi/src/arm/ffitarget.h -@@ -29,21 +29,35 @@ - - #ifndef LIBFFI_ASM - typedef unsigned long ffi_arg; - typedef signed long ffi_sarg; - - typedef enum ffi_abi { - FFI_FIRST_ABI = 0, - FFI_SYSV, -+ FFI_VFP, - FFI_LAST_ABI, -+#ifdef __ARM_PCS_VFP -+ FFI_DEFAULT_ABI = FFI_VFP -+#else - FFI_DEFAULT_ABI = FFI_SYSV -+#endif - } ffi_abi; - #endif - -+#define FFI_EXTRA_CIF_FIELDS \ -+ int vfp_used; \ -+ short vfp_reg_free, vfp_nargs; \ -+ signed char vfp_args[16] \ -+ -+/* Internally used. */ -+#define FFI_TYPE_STRUCT_VFP_FLOAT (FFI_TYPE_LAST + 1) -+#define FFI_TYPE_STRUCT_VFP_DOUBLE (FFI_TYPE_LAST + 2) -+ - /* ---- Definitions for closures ----------------------------------------- */ - - #define FFI_CLOSURES 1 - #define FFI_TRAMPOLINE_SIZE 20 - #define FFI_NATIVE_RAW_API 0 - - #endif - -diff --git a/js/src/ctypes/libffi/src/arm/sysv.S b/js/src/ctypes/libffi/src/arm/sysv.S ---- a/js/src/ctypes/libffi/src/arm/sysv.S -+++ b/js/src/ctypes/libffi/src/arm/sysv.S -@@ -137,54 +137,52 @@ _L__\name: - ldr\cond pc, [sp], #4 - .else - ldm\cond\dirn sp!, {\regs, pc} - .endif - #endif - .endm - - -- @ r0: ffi_prep_args -+ @ r0: fn - @ r1: &ecif - @ r2: cif->bytes - @ r3: fig->flags - @ sp+0: ecif.rvalue -- @ sp+4: fn - - @ This assumes we are using gas. - ARM_FUNC_START ffi_call_SYSV - @ Save registers - stmfd sp!, {r0-r3, fp, lr} - UNWIND .save {r0-r3, fp, lr} - mov fp, sp - - UNWIND .setfp fp, sp - - @ Make room for all of the new args. - sub sp, fp, r2 - - @ Place all of the ffi_prep_args in position -- mov ip, r0 - mov r0, sp - @ r1 already set - - @ Call ffi_prep_args(stack, &ecif) -- call_reg(ip) -+ bl ffi_prep_args - - @ move first 4 parameters in registers - ldmia sp, {r0-r3} - - @ and adjust stack -- ldr ip, [fp, #8] -- cmp ip, #16 -- movhs ip, #16 -- add sp, sp, ip -+ sub lr, fp, sp @ cif->bytes == fp - sp -+ ldr ip, [fp] @ load fn() in advance -+ cmp lr, #16 -+ movhs lr, #16 -+ add sp, sp, lr - - @ call (fn) (...) -- ldr ip, [fp, #28] - call_reg(ip) - - @ Remove the space we pushed for the args - mov sp, fp - - @ Load r2 with the pointer to storage for the return value - ldr r2, [sp, #24] - -@@ -225,16 +223,111 @@ ARM_FUNC_START ffi_call_SYSV - - LSYM(Lepilogue): - RETLDM "r0-r3,fp" - - .ffi_call_SYSV_end: - UNWIND .fnend - .size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV) - -+ -+ @ r0: fn -+ @ r1: &ecif -+ @ r2: cif->bytes -+ @ r3: fig->flags -+ @ sp+0: ecif.rvalue -+ -+ARM_FUNC_START ffi_call_VFP -+ @ Save registers -+ stmfd sp!, {r0-r3, fp, lr} -+ UNWIND .save {r0-r3, fp, lr} -+ mov fp, sp -+ UNWIND .setfp fp, sp -+ -+ @ Make room for all of the new args. -+ sub sp, sp, r2 -+ -+ @ Make room for loading VFP args -+ sub sp, sp, #64 -+ -+ @ Place all of the ffi_prep_args in position -+ mov r0, sp -+ @ r1 already set -+ sub r2, fp, #64 @ VFP scratch space -+ -+ @ Call ffi_prep_args(stack, &ecif, vfp_space) -+ bl ffi_prep_args -+ -+ @ Load VFP register args if needed -+ cmp r0, #0 -+ beq LSYM(Lbase_args) -+ -+ @ Load only d0 if possible -+ cmp r0, #3 -+ sub ip, fp, #64 -+ flddle d0, [ip] -+ fldmiadgt ip, {d0-d7} -+ -+LSYM(Lbase_args): -+ @ move first 4 parameters in registers -+ ldmia sp, {r0-r3} -+ -+ @ and adjust stack -+ sub lr, ip, sp @ cif->bytes == (fp - 64) - sp -+ ldr ip, [fp] @ load fn() in advance -+ cmp lr, #16 -+ movhs lr, #16 -+ add sp, sp, lr -+ -+ @ call (fn) (...) -+ call_reg(ip) -+ -+ @ Remove the space we pushed for the args -+ mov sp, fp -+ -+ @ Load r2 with the pointer to storage for -+ @ the return value -+ ldr r2, [sp, #24] -+ -+ @ Load r3 with the return type code -+ ldr r3, [sp, #12] -+ -+ @ If the return value pointer is NULL, -+ @ assume no return value. -+ cmp r2, #0 -+ beq LSYM(Lepilogue_vfp) -+ -+ cmp r3, #FFI_TYPE_INT -+ streq r0, [r2] -+ beq LSYM(Lepilogue_vfp) -+ -+ cmp r3, #FFI_TYPE_SINT64 -+ stmeqia r2, {r0, r1} -+ beq LSYM(Lepilogue_vfp) -+ -+ cmp r3, #FFI_TYPE_FLOAT -+ fstseq s0, [r2] -+ beq LSYM(Lepilogue_vfp) -+ -+ cmp r3, #FFI_TYPE_DOUBLE -+ fstdeq d0, [r2] -+ beq LSYM(Lepilogue_vfp) -+ -+ cmp r3, #FFI_TYPE_STRUCT_VFP_FLOAT -+ cmpne r3, #FFI_TYPE_STRUCT_VFP_DOUBLE -+ fstmiadeq r2, {d0-d3} -+ -+LSYM(Lepilogue_vfp): -+ RETLDM "r0-r3,fp" -+ -+.ffi_call_VFP_end: -+ UNWIND .fnend -+ .size CNAME(ffi_call_VFP),.ffi_call_VFP_end-CNAME(ffi_call_VFP) -+ -+ - /* - unsigned int FFI_HIDDEN - ffi_closure_SYSV_inner (closure, respp, args) - ffi_closure *closure; - void **respp; - void *args; - */ - -@@ -297,11 +390,73 @@ ARM_FUNC_START ffi_closure_SYSV - ldfd f0, [sp] - b .Lclosure_epilogue - #endif - - .ffi_closure_SYSV_end: - UNWIND .fnend - .size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV) - -+ -+ARM_FUNC_START ffi_closure_VFP -+ fstmfdd sp!, {d0-d7} -+ @ r0-r3, then d0-d7 -+ UNWIND .pad #80 -+ add ip, sp, #80 -+ stmfd sp!, {ip, lr} -+ UNWIND .save {r0, lr} -+ add r2, sp, #72 -+ add r3, sp, #8 -+ .pad #72 -+ sub sp, sp, #72 -+ str sp, [sp, #64] -+ add r1, sp, #64 -+ bl ffi_closure_SYSV_inner -+ -+ cmp r0, #FFI_TYPE_INT -+ beq .Lretint_vfp -+ -+ cmp r0, #FFI_TYPE_FLOAT -+ beq .Lretfloat_vfp -+ -+ cmp r0, #FFI_TYPE_DOUBLE -+ cmpne r0, #FFI_TYPE_LONGDOUBLE -+ beq .Lretdouble_vfp -+ -+ cmp r0, #FFI_TYPE_SINT64 -+ beq .Lretlonglong_vfp -+ -+ cmp r0, #FFI_TYPE_STRUCT_VFP_FLOAT -+ beq .Lretfloat_struct_vfp -+ -+ cmp r0, #FFI_TYPE_STRUCT_VFP_DOUBLE -+ beq .Lretdouble_struct_vfp -+ -+.Lclosure_epilogue_vfp: -+ add sp, sp, #72 -+ ldmfd sp, {sp, pc} -+ -+.Lretfloat_vfp: -+ flds s0, [sp] -+ b .Lclosure_epilogue_vfp -+.Lretdouble_vfp: -+ fldd d0, [sp] -+ b .Lclosure_epilogue_vfp -+.Lretint_vfp: -+ ldr r0, [sp] -+ b .Lclosure_epilogue_vfp -+.Lretlonglong_vfp: -+ ldmia sp, {r0, r1} -+ b .Lclosure_epilogue_vfp -+.Lretfloat_struct_vfp: -+ fldmiad sp, {d0-d1} -+ b .Lclosure_epilogue_vfp -+.Lretdouble_struct_vfp: -+ fldmiad sp, {d0-d3} -+ b .Lclosure_epilogue_vfp -+ -+.ffi_closure_VFP_end: -+ UNWIND .fnend -+ .size CNAME(ffi_closure_VFP),.ffi_closure_VFP_end-CNAME(ffi_closure_VFP) -+ - #if defined __ELF__ && defined __linux__ - .section .note.GNU-stack,"",%progbits - #endif -diff --git a/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp b/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp ---- a/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp -+++ b/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp -@@ -261,16 +261,66 @@ proc dg-xfail-if { args } { - set args [lreplace $args 0 0] - set selector "target [join [lindex $args 1]]" - if { [dg-process-target $selector] == "S" } { - global compiler_conditional_xfail_data - set compiler_conditional_xfail_data $args - } - } - -+proc check-flags { args } { -+ -+ # The args are within another list; pull them out. -+ set args [lindex $args 0] -+ -+ # The next two arguments are optional. If they were not specified, -+ # use the defaults. -+ if { [llength $args] == 2 } { -+ lappend $args [list "*"] -+ } -+ if { [llength $args] == 3 } { -+ lappend $args [list ""] -+ } -+ -+ # If the option strings are the defaults, or the same as the -+ # defaults, there is no need to call check_conditional_xfail to -+ # compare them to the actual options. -+ if { [string compare [lindex $args 2] "*"] == 0 -+ && [string compare [lindex $args 3] "" ] == 0 } { -+ set result 1 -+ } else { -+ # The target list might be an effective-target keyword, so replace -+ # the original list with "*-*-*", since we already know it matches. -+ set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]] -+ } -+ -+ return $result -+} -+ -+proc dg-skip-if { args } { -+ # Verify the number of arguments. The last two are optional. -+ set args [lreplace $args 0 0] -+ if { [llength $args] < 2 || [llength $args] > 4 } { -+ error "dg-skip-if 2: need 2, 3, or 4 arguments" -+ } -+ -+ # Don't bother if we're already skipping the test. -+ upvar dg-do-what dg-do-what -+ if { [lindex ${dg-do-what} 1] == "N" } { -+ return -+ } -+ -+ set selector [list target [lindex $args 1]] -+ if { [dg-process-target $selector] == "S" } { -+ if [check-flags $args] { -+ upvar dg-do-what dg-do-what -+ set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"] -+ } -+ } -+} - - # We need to make sure that additional_files and additional_sources - # are both cleared out after every test. It is not enough to clear - # them out *before* the next test run because gcc-target-compile gets - # run directly from some .exp files (outside of any test). (Those - # uses should eventually be eliminated.) - - # Because the DG framework doesn't provide a hook that is run at the -diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c b/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c ---- a/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c -+++ b/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c -@@ -1,16 +1,18 @@ - /* Area: ffi_call, closure_call - Purpose: Test doubles passed in variable argument lists. - Limitations: none. - PR: none. - Originator: Blake Chaffin 6/6/2007 */ - - /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */ - /* { dg-output "" { xfail avr32*-*-* } } */ -+/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */ -+ - #include "ffitest.h" - - static void - cls_double_va_fn(ffi_cif* cif __UNUSED__, void* resp, - void** args, void* userdata __UNUSED__) - { - char* format = *(char**)args[0]; - double doubleValue = *(double*)args[1]; -diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c b/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c ---- a/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c -+++ b/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c -@@ -1,16 +1,18 @@ - /* Area: ffi_call, closure_call - Purpose: Test long doubles passed in variable argument lists. - Limitations: none. - PR: none. - Originator: Blake Chaffin 6/6/2007 */ - - /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */ - /* { dg-output "" { xfail avr32*-*-* x86_64-*-mingw* } } */ -+/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */ -+ - #include "ffitest.h" - - static void - cls_longdouble_va_fn(ffi_cif* cif __UNUSED__, void* resp, - void** args, void* userdata __UNUSED__) - { - char* format = *(char**)args[0]; - long double ldValue = *(long double*)args[1]; -diff --git a/js/src/ctypes/libffi/include/ffi.h.in b/js/src/ctypes/libffi/include/ffi.h.in ---- a/js/src/ctypes/libffi/include/ffi.h.in -+++ b/js/src/ctypes/libffi/include/ffi.h.in -@@ -72,25 +72,37 @@ extern "C" { - #endif - - #include - #include - - /* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example). - But we can find it either under the correct ANSI name, or under GNU - C's internal name. */ -+ -+#define FFI_64_BIT_MAX 9223372036854775807 -+ - #ifdef LONG_LONG_MAX - # define FFI_LONG_LONG_MAX LONG_LONG_MAX - #else - # ifdef LLONG_MAX - # define FFI_LONG_LONG_MAX LLONG_MAX - # else - # ifdef __GNUC__ - # define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ - # endif -+# ifdef _AIX -+# ifndef __PPC64__ -+# if defined (__IBMC__) || defined (__IBMCPP__) -+# define FFI_LONG_LONG_MAX LONGLONG_MAX -+# endif -+# endif /* __PPC64__ */ -+# undef FFI_64_BIT_MAX -+# define FFI_64_BIT_MAX 9223372036854775807LL -+# endif - # endif - #endif - - /* The closure code assumes that this works on pointers, i.e. a size_t */ - /* can hold a pointer. */ - - typedef struct _ffi_type - { -@@ -127,27 +139,27 @@ typedef struct _ffi_type - #elif INT_MAX == 9223372036854775807 - # define ffi_type_uint ffi_type_uint64 - # define ffi_type_sint ffi_type_sint64 - #else - #error "int size not supported" - #endif - - #if LONG_MAX == 2147483647 --# if FFI_LONG_LONG_MAX != 9223372036854775807 -+# if FFI_LONG_LONG_MAX != FFI_64_BIT_MAX - #error "no 64-bit data type supported" - # endif --#elif LONG_MAX != 9223372036854775807 -+#elif LONG_MAX != FFI_64_BIT_MAX - #error "long size not supported" - #endif - - #if LONG_MAX == 2147483647 - # define ffi_type_ulong ffi_type_uint32 - # define ffi_type_slong ffi_type_sint32 --#elif LONG_MAX == 9223372036854775807 -+#elif LONG_MAX == FFI_64_BIT_MAX - # define ffi_type_ulong ffi_type_uint64 - # define ffi_type_slong ffi_type_sint64 - #else - #error "long size not supported" - #endif - - /* These are defined in types.c */ - extern ffi_type ffi_type_void; -@@ -190,17 +202,17 @@ typedef struct { - #endif - } ffi_cif; - - /* ---- Definitions for the raw API -------------------------------------- */ - - #ifndef FFI_SIZEOF_ARG - # if LONG_MAX == 2147483647 - # define FFI_SIZEOF_ARG 4 --# elif LONG_MAX == 9223372036854775807 -+# elif LONG_MAX == FFI_64_BIT_MAX - # define FFI_SIZEOF_ARG 8 - # endif - #endif - - #ifndef FFI_SIZEOF_JAVA_RAW - # define FFI_SIZEOF_JAVA_RAW FFI_SIZEOF_ARG - #endif - -diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac ---- a/js/src/ctypes/libffi/configure.ac -+++ b/js/src/ctypes/libffi/configure.ac -@@ -272,20 +272,20 @@ if test x$TARGET = xSPARC; then - AC_DEFINE(HAVE_AS_REGISTER_PSEUDO_OP, 1, - [Define if your assembler supports .register.]) - fi - fi - - if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64; then - AC_CACHE_CHECK([assembler supports pc related relocs], - libffi_cv_as_x86_pcrel, [ -- libffi_cv_as_x86_pcrel=yes -+ libffi_cv_as_x86_pcrel=no - echo '.text; foo: nop; .data; .long foo-.; .text' > conftest.s -- if $CC $CFLAGS -c conftest.s 2>&1 | $EGREP -i 'illegal|warning' > /dev/null; then -- libffi_cv_as_x86_pcrel=no -+ if $CC $CFLAGS -c conftest.s > /dev/null; then -+ libffi_cv_as_x86_pcrel=yes - fi - ]) - if test "x$libffi_cv_as_x86_pcrel" = xyes; then - AC_DEFINE(HAVE_AS_X86_PCREL, 1, - [Define if your assembler supports PC relative relocs.]) - fi - - AC_CACHE_CHECK([assembler .ascii pseudo-op support], diff --git a/deps/mozjs/js/src/ctypes/libffi/aclocal.m4 b/deps/mozjs/js/src/ctypes/libffi/aclocal.m4 index d5eb6a612fd..41012e3f319 100644 --- a/deps/mozjs/js/src/ctypes/libffi/aclocal.m4 +++ b/deps/mozjs/js/src/ctypes/libffi/aclocal.m4 @@ -7364,6 +7364,811 @@ _LT_EOF esac ]) +# ltdl.m4 - Configure ltdl for the target system. -*-Autoconf-*- +# +# Copyright (C) 1999-2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Thomas Tanner, 1999 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 17 LTDL_INIT + +# LT_CONFIG_LTDL_DIR(DIRECTORY, [LTDL-MODE]) +# ------------------------------------------ +# DIRECTORY contains the libltdl sources. It is okay to call this +# function multiple times, as long as the same DIRECTORY is always given. +AC_DEFUN([LT_CONFIG_LTDL_DIR], +[AC_BEFORE([$0], [LTDL_INIT]) +_$0($*) +])# LT_CONFIG_LTDL_DIR + +# We break this out into a separate macro, so that we can call it safely +# internally without being caught accidentally by the sed scan in libtoolize. +m4_defun([_LT_CONFIG_LTDL_DIR], +[dnl remove trailing slashes +m4_pushdef([_ARG_DIR], m4_bpatsubst([$1], [/*$])) +m4_case(_LTDL_DIR, + [], [dnl only set lt_ltdl_dir if _ARG_DIR is not simply `.' + m4_if(_ARG_DIR, [.], + [], + [m4_define([_LTDL_DIR], _ARG_DIR) + _LT_SHELL_INIT([lt_ltdl_dir=']_ARG_DIR['])])], + [m4_if(_ARG_DIR, _LTDL_DIR, + [], + [m4_fatal([multiple libltdl directories: `]_LTDL_DIR[', `]_ARG_DIR['])])]) +m4_popdef([_ARG_DIR]) +])# _LT_CONFIG_LTDL_DIR + +# Initialise: +m4_define([_LTDL_DIR], []) + + +# _LT_BUILD_PREFIX +# ---------------- +# If Autoconf is new enough, expand to `${top_build_prefix}', otherwise +# to `${top_builddir}/'. +m4_define([_LT_BUILD_PREFIX], +[m4_ifdef([AC_AUTOCONF_VERSION], + [m4_if(m4_version_compare(m4_defn([AC_AUTOCONF_VERSION]), [2.62]), + [-1], [m4_ifdef([_AC_HAVE_TOP_BUILD_PREFIX], + [${top_build_prefix}], + [${top_builddir}/])], + [${top_build_prefix}])], + [${top_builddir}/])[]dnl +]) + + +# LTDL_CONVENIENCE +# ---------------- +# sets LIBLTDL to the link flags for the libltdl convenience library and +# LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-convenience to the configure arguments. Note that +# AC_CONFIG_SUBDIRS is not called here. LIBLTDL will be prefixed with +# '${top_build_prefix}' if available, otherwise with '${top_builddir}/', +# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single +# quotes!). If your package is not flat and you're not using automake, +# define top_build_prefix, top_builddir, and top_srcdir appropriately +# in your Makefiles. +AC_DEFUN([LTDL_CONVENIENCE], +[AC_BEFORE([$0], [LTDL_INIT])dnl +dnl Although the argument is deprecated and no longer documented, +dnl LTDL_CONVENIENCE used to take a DIRECTORY orgument, if we have one +dnl here make sure it is the same as any other declaration of libltdl's +dnl location! This also ensures lt_ltdl_dir is set when configure.ac is +dnl not yet using an explicit LT_CONFIG_LTDL_DIR. +m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl +_$0() +])# LTDL_CONVENIENCE + +# AC_LIBLTDL_CONVENIENCE accepted a directory argument in older libtools, +# now we have LT_CONFIG_LTDL_DIR: +AU_DEFUN([AC_LIBLTDL_CONVENIENCE], +[_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) +_LTDL_CONVENIENCE]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBLTDL_CONVENIENCE], []) + + +# _LTDL_CONVENIENCE +# ----------------- +# Code shared by LTDL_CONVENIENCE and LTDL_INIT([convenience]). +m4_defun([_LTDL_CONVENIENCE], +[case $enable_ltdl_convenience in + no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; + "") enable_ltdl_convenience=yes + ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; +esac +LIBLTDL='_LT_BUILD_PREFIX'"${lt_ltdl_dir+$lt_ltdl_dir/}libltdlc.la" +LTDLDEPS=$LIBLTDL +LTDLINCL='-I${top_srcdir}'"${lt_ltdl_dir+/$lt_ltdl_dir}" + +AC_SUBST([LIBLTDL]) +AC_SUBST([LTDLDEPS]) +AC_SUBST([LTDLINCL]) + +# For backwards non-gettext consistent compatibility... +INCLTDL="$LTDLINCL" +AC_SUBST([INCLTDL]) +])# _LTDL_CONVENIENCE + + +# LTDL_INSTALLABLE +# ---------------- +# sets LIBLTDL to the link flags for the libltdl installable library +# and LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-install to the configure arguments. Note that +# AC_CONFIG_SUBDIRS is not called from here. If an installed libltdl +# is not found, LIBLTDL will be prefixed with '${top_build_prefix}' if +# available, otherwise with '${top_builddir}/', and LTDLINCL will be +# prefixed with '${top_srcdir}/' (note the single quotes!). If your +# package is not flat and you're not using automake, define top_build_prefix, +# top_builddir, and top_srcdir appropriately in your Makefiles. +# In the future, this macro may have to be called after LT_INIT. +AC_DEFUN([LTDL_INSTALLABLE], +[AC_BEFORE([$0], [LTDL_INIT])dnl +dnl Although the argument is deprecated and no longer documented, +dnl LTDL_INSTALLABLE used to take a DIRECTORY orgument, if we have one +dnl here make sure it is the same as any other declaration of libltdl's +dnl location! This also ensures lt_ltdl_dir is set when configure.ac is +dnl not yet using an explicit LT_CONFIG_LTDL_DIR. +m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl +_$0() +])# LTDL_INSTALLABLE + +# AC_LIBLTDL_INSTALLABLE accepted a directory argument in older libtools, +# now we have LT_CONFIG_LTDL_DIR: +AU_DEFUN([AC_LIBLTDL_INSTALLABLE], +[_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) +_LTDL_INSTALLABLE]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBLTDL_INSTALLABLE], []) + + +# _LTDL_INSTALLABLE +# ----------------- +# Code shared by LTDL_INSTALLABLE and LTDL_INIT([installable]). +m4_defun([_LTDL_INSTALLABLE], +[if test -f $prefix/lib/libltdl.la; then + lt_save_LDFLAGS="$LDFLAGS" + LDFLAGS="-L$prefix/lib $LDFLAGS" + AC_CHECK_LIB([ltdl], [lt_dlinit], [lt_lib_ltdl=yes]) + LDFLAGS="$lt_save_LDFLAGS" + if test x"${lt_lib_ltdl-no}" = xyes; then + if test x"$enable_ltdl_install" != xyes; then + # Don't overwrite $prefix/lib/libltdl.la without --enable-ltdl-install + AC_MSG_WARN([not overwriting libltdl at $prefix, force with `--enable-ltdl-install']) + enable_ltdl_install=no + fi + elif test x"$enable_ltdl_install" = xno; then + AC_MSG_WARN([libltdl not installed, but installation disabled]) + fi +fi + +# If configure.ac declared an installable ltdl, and the user didn't override +# with --disable-ltdl-install, we will install the shipped libltdl. +case $enable_ltdl_install in + no) ac_configure_args="$ac_configure_args --enable-ltdl-install=no" + LIBLTDL="-lltdl" + LTDLDEPS= + LTDLINCL= + ;; + *) enable_ltdl_install=yes + ac_configure_args="$ac_configure_args --enable-ltdl-install" + LIBLTDL='_LT_BUILD_PREFIX'"${lt_ltdl_dir+$lt_ltdl_dir/}libltdl.la" + LTDLDEPS=$LIBLTDL + LTDLINCL='-I${top_srcdir}'"${lt_ltdl_dir+/$lt_ltdl_dir}" + ;; +esac + +AC_SUBST([LIBLTDL]) +AC_SUBST([LTDLDEPS]) +AC_SUBST([LTDLINCL]) + +# For backwards non-gettext consistent compatibility... +INCLTDL="$LTDLINCL" +AC_SUBST([INCLTDL]) +])# LTDL_INSTALLABLE + + +# _LTDL_MODE_DISPATCH +# ------------------- +m4_define([_LTDL_MODE_DISPATCH], +[dnl If _LTDL_DIR is `.', then we are configuring libltdl itself: +m4_if(_LTDL_DIR, [], + [], + dnl if _LTDL_MODE was not set already, the default value is `subproject': + [m4_case(m4_default(_LTDL_MODE, [subproject]), + [subproject], [AC_CONFIG_SUBDIRS(_LTDL_DIR) + _LT_SHELL_INIT([lt_dlopen_dir="$lt_ltdl_dir"])], + [nonrecursive], [_LT_SHELL_INIT([lt_dlopen_dir="$lt_ltdl_dir"; lt_libobj_prefix="$lt_ltdl_dir/"])], + [recursive], [], + [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])])dnl +dnl Be careful not to expand twice: +m4_define([$0], []) +])# _LTDL_MODE_DISPATCH + + +# _LT_LIBOBJ(MODULE_NAME) +# ----------------------- +# Like AC_LIBOBJ, except that MODULE_NAME goes into _LT_LIBOBJS instead +# of into LIBOBJS. +AC_DEFUN([_LT_LIBOBJ], [ + m4_pattern_allow([^_LT_LIBOBJS$]) + _LT_LIBOBJS="$_LT_LIBOBJS $1.$ac_objext" +])# _LT_LIBOBJS + + +# LTDL_INIT([OPTIONS]) +# -------------------- +# Clients of libltdl can use this macro to allow the installer to +# choose between a shipped copy of the ltdl sources or a preinstalled +# version of the library. If the shipped ltdl sources are not in a +# subdirectory named libltdl, the directory name must be given by +# LT_CONFIG_LTDL_DIR. +AC_DEFUN([LTDL_INIT], +[dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +dnl We need to keep our own list of libobjs separate from our parent project, +dnl and the easiest way to do that is redefine the AC_LIBOBJs macro while +dnl we look for our own LIBOBJs. +m4_pushdef([AC_LIBOBJ], m4_defn([_LT_LIBOBJ])) +m4_pushdef([AC_LIBSOURCES]) + +dnl If not otherwise defined, default to the 1.5.x compatible subproject mode: +m4_if(_LTDL_MODE, [], + [m4_define([_LTDL_MODE], m4_default([$2], [subproject])) + m4_if([-1], [m4_bregexp(_LTDL_MODE, [\(subproject\|\(non\)?recursive\)])], + [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])]) + +AC_ARG_WITH([included_ltdl], + [AS_HELP_STRING([--with-included-ltdl], + [use the GNU ltdl sources included here])]) + +if test "x$with_included_ltdl" != xyes; then + # We are not being forced to use the included libltdl sources, so + # decide whether there is a useful installed version we can use. + AC_CHECK_HEADER([ltdl.h], + [AC_CHECK_DECL([lt_dlinterface_register], + [AC_CHECK_LIB([ltdl], [lt_dladvise_preload], + [with_included_ltdl=no], + [with_included_ltdl=yes])], + [with_included_ltdl=yes], + [AC_INCLUDES_DEFAULT + #include ])], + [with_included_ltdl=yes], + [AC_INCLUDES_DEFAULT] + ) +fi + +dnl If neither LT_CONFIG_LTDL_DIR, LTDL_CONVENIENCE nor LTDL_INSTALLABLE +dnl was called yet, then for old times' sake, we assume libltdl is in an +dnl eponymous directory: +AC_PROVIDE_IFELSE([LT_CONFIG_LTDL_DIR], [], [_LT_CONFIG_LTDL_DIR([libltdl])]) + +AC_ARG_WITH([ltdl_include], + [AS_HELP_STRING([--with-ltdl-include=DIR], + [use the ltdl headers installed in DIR])]) + +if test -n "$with_ltdl_include"; then + if test -f "$with_ltdl_include/ltdl.h"; then : + else + AC_MSG_ERROR([invalid ltdl include directory: `$with_ltdl_include']) + fi +else + with_ltdl_include=no +fi + +AC_ARG_WITH([ltdl_lib], + [AS_HELP_STRING([--with-ltdl-lib=DIR], + [use the libltdl.la installed in DIR])]) + +if test -n "$with_ltdl_lib"; then + if test -f "$with_ltdl_lib/libltdl.la"; then : + else + AC_MSG_ERROR([invalid ltdl library directory: `$with_ltdl_lib']) + fi +else + with_ltdl_lib=no +fi + +case ,$with_included_ltdl,$with_ltdl_include,$with_ltdl_lib, in + ,yes,no,no,) + m4_case(m4_default(_LTDL_TYPE, [convenience]), + [convenience], [_LTDL_CONVENIENCE], + [installable], [_LTDL_INSTALLABLE], + [m4_fatal([unknown libltdl build type: ]_LTDL_TYPE)]) + ;; + ,no,no,no,) + # If the included ltdl is not to be used, then use the + # preinstalled libltdl we found. + AC_DEFINE([HAVE_LTDL], [1], + [Define this if a modern libltdl is already installed]) + LIBLTDL=-lltdl + LTDLDEPS= + LTDLINCL= + ;; + ,no*,no,*) + AC_MSG_ERROR([`--with-ltdl-include' and `--with-ltdl-lib' options must be used together]) + ;; + *) with_included_ltdl=no + LIBLTDL="-L$with_ltdl_lib -lltdl" + LTDLDEPS= + LTDLINCL="-I$with_ltdl_include" + ;; +esac +INCLTDL="$LTDLINCL" + +# Report our decision... +AC_MSG_CHECKING([where to find libltdl headers]) +AC_MSG_RESULT([$LTDLINCL]) +AC_MSG_CHECKING([where to find libltdl library]) +AC_MSG_RESULT([$LIBLTDL]) + +_LTDL_SETUP + +dnl restore autoconf definition. +m4_popdef([AC_LIBOBJ]) +m4_popdef([AC_LIBSOURCES]) + +AC_CONFIG_COMMANDS_PRE([ + _ltdl_libobjs= + _ltdl_ltlibobjs= + if test -n "$_LT_LIBOBJS"; then + # Remove the extension. + _lt_sed_drop_objext='s/\.o$//;s/\.obj$//' + for i in `for i in $_LT_LIBOBJS; do echo "$i"; done | sed "$_lt_sed_drop_objext" | sort -u`; do + _ltdl_libobjs="$_ltdl_libobjs $lt_libobj_prefix$i.$ac_objext" + _ltdl_ltlibobjs="$_ltdl_ltlibobjs $lt_libobj_prefix$i.lo" + done + fi + AC_SUBST([ltdl_LIBOBJS], [$_ltdl_libobjs]) + AC_SUBST([ltdl_LTLIBOBJS], [$_ltdl_ltlibobjs]) +]) + +# Only expand once: +m4_define([LTDL_INIT]) +])# LTDL_INIT + +# Old names: +AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)]) +AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)]) +AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIB_LTDL], []) +dnl AC_DEFUN([AC_WITH_LTDL], []) +dnl AC_DEFUN([LT_WITH_LTDL], []) + + +# _LTDL_SETUP +# ----------- +# Perform all the checks necessary for compilation of the ltdl objects +# -- including compiler checks and header checks. This is a public +# interface mainly for the benefit of libltdl's own configure.ac, most +# other users should call LTDL_INIT instead. +AC_DEFUN([_LTDL_SETUP], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_SYS_MODULE_EXT])dnl +AC_REQUIRE([LT_SYS_MODULE_PATH])dnl +AC_REQUIRE([LT_SYS_DLSEARCH_PATH])dnl +AC_REQUIRE([LT_LIB_DLLOAD])dnl +AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl +AC_REQUIRE([LT_FUNC_DLSYM_USCORE])dnl +AC_REQUIRE([LT_SYS_DLOPEN_DEPLIBS])dnl +AC_REQUIRE([gl_FUNC_ARGZ])dnl + +m4_require([_LT_CHECK_OBJDIR])dnl +m4_require([_LT_HEADER_DLFCN])dnl +m4_require([_LT_CHECK_DLPREOPEN])dnl +m4_require([_LT_DECL_SED])dnl + +dnl Don't require this, or it will be expanded earlier than the code +dnl that sets the variables it relies on: +_LT_ENABLE_INSTALL + +dnl _LTDL_MODE specific code must be called at least once: +_LTDL_MODE_DISPATCH + +# In order that ltdl.c can compile, find out the first AC_CONFIG_HEADERS +# the user used. This is so that ltdl.h can pick up the parent projects +# config.h file, The first file in AC_CONFIG_HEADERS must contain the +# definitions required by ltdl.c. +# FIXME: Remove use of undocumented AC_LIST_HEADERS (2.59 compatibility). +AC_CONFIG_COMMANDS_PRE([dnl +m4_pattern_allow([^LT_CONFIG_H$])dnl +m4_ifset([AH_HEADER], + [LT_CONFIG_H=AH_HEADER], + [m4_ifset([AC_LIST_HEADERS], + [LT_CONFIG_H=`echo "AC_LIST_HEADERS" | $SED 's,^[[ ]]*,,;s,[[ :]].*$,,'`], + [])])]) +AC_SUBST([LT_CONFIG_H]) + +AC_CHECK_HEADERS([unistd.h dl.h sys/dl.h dld.h mach-o/dyld.h dirent.h], + [], [], [AC_INCLUDES_DEFAULT]) + +AC_CHECK_FUNCS([closedir opendir readdir], [], [AC_LIBOBJ([lt__dirent])]) +AC_CHECK_FUNCS([strlcat strlcpy], [], [AC_LIBOBJ([lt__strl])]) + +AC_DEFINE_UNQUOTED([LT_LIBEXT],["$libext"],[The archive extension]) + +name=ltdl +LTDLOPEN=`eval "\\$ECHO \"$libname_spec\""` +AC_SUBST([LTDLOPEN]) +])# _LTDL_SETUP + + +# _LT_ENABLE_INSTALL +# ------------------ +m4_define([_LT_ENABLE_INSTALL], +[AC_ARG_ENABLE([ltdl-install], + [AS_HELP_STRING([--enable-ltdl-install], [install libltdl])]) + +case ,${enable_ltdl_install},${enable_ltdl_convenience} in + *yes*) ;; + *) enable_ltdl_convenience=yes ;; +esac + +m4_ifdef([AM_CONDITIONAL], +[AM_CONDITIONAL(INSTALL_LTDL, test x"${enable_ltdl_install-no}" != xno) + AM_CONDITIONAL(CONVENIENCE_LTDL, test x"${enable_ltdl_convenience-no}" != xno)]) +])# _LT_ENABLE_INSTALL + + +# LT_SYS_DLOPEN_DEPLIBS +# --------------------- +AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_CACHE_CHECK([whether deplibs are loaded by dlopen], + [lt_cv_sys_dlopen_deplibs], + [# PORTME does your system automatically load deplibs for dlopen? + # or its logical equivalent (e.g. shl_load for HP-UX < 11) + # For now, we just catch OSes we know something about -- in the + # future, we'll try test this programmatically. + lt_cv_sys_dlopen_deplibs=unknown + case $host_os in + aix3*|aix4.1.*|aix4.2.*) + # Unknown whether this is true for these versions of AIX, but + # we want this `case' here to explicitly catch those versions. + lt_cv_sys_dlopen_deplibs=unknown + ;; + aix[[4-9]]*) + lt_cv_sys_dlopen_deplibs=yes + ;; + amigaos*) + case $host_cpu in + powerpc) + lt_cv_sys_dlopen_deplibs=no + ;; + esac + ;; + darwin*) + # Assuming the user has installed a libdl from somewhere, this is true + # If you are looking for one http://www.opendarwin.org/projects/dlcompat + lt_cv_sys_dlopen_deplibs=yes + ;; + freebsd* | dragonfly*) + lt_cv_sys_dlopen_deplibs=yes + ;; + gnu* | linux* | k*bsd*-gnu) + # GNU and its variants, using gnu ld.so (Glibc) + lt_cv_sys_dlopen_deplibs=yes + ;; + hpux10*|hpux11*) + lt_cv_sys_dlopen_deplibs=yes + ;; + interix*) + lt_cv_sys_dlopen_deplibs=yes + ;; + irix[[12345]]*|irix6.[[01]]*) + # Catch all versions of IRIX before 6.2, and indicate that we don't + # know how it worked for any of those versions. + lt_cv_sys_dlopen_deplibs=unknown + ;; + irix*) + # The case above catches anything before 6.2, and it's known that + # at 6.2 and later dlopen does load deplibs. + lt_cv_sys_dlopen_deplibs=yes + ;; + netbsd*) + lt_cv_sys_dlopen_deplibs=yes + ;; + openbsd*) + lt_cv_sys_dlopen_deplibs=yes + ;; + osf[[1234]]*) + # dlopen did load deplibs (at least at 4.x), but until the 5.x series, + # it did *not* use an RPATH in a shared library to find objects the + # library depends on, so we explicitly say `no'. + lt_cv_sys_dlopen_deplibs=no + ;; + osf5.0|osf5.0a|osf5.1) + # dlopen *does* load deplibs and with the right loader patch applied + # it even uses RPATH in a shared library to search for shared objects + # that the library depends on, but there's no easy way to know if that + # patch is installed. Since this is the case, all we can really + # say is unknown -- it depends on the patch being installed. If + # it is, this changes to `yes'. Without it, it would be `no'. + lt_cv_sys_dlopen_deplibs=unknown + ;; + osf*) + # the two cases above should catch all versions of osf <= 5.1. Read + # the comments above for what we know about them. + # At > 5.1, deplibs are loaded *and* any RPATH in a shared library + # is used to find them so we can finally say `yes'. + lt_cv_sys_dlopen_deplibs=yes + ;; + qnx*) + lt_cv_sys_dlopen_deplibs=yes + ;; + solaris*) + lt_cv_sys_dlopen_deplibs=yes + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + libltdl_cv_sys_dlopen_deplibs=yes + ;; + esac + ]) +if test "$lt_cv_sys_dlopen_deplibs" != yes; then + AC_DEFINE([LTDL_DLOPEN_DEPLIBS], [1], + [Define if the OS needs help to load dependent libraries for dlopen().]) +fi +])# LT_SYS_DLOPEN_DEPLIBS + +# Old name: +AU_ALIAS([AC_LTDL_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], []) + + +# LT_SYS_MODULE_EXT +# ----------------- +AC_DEFUN([LT_SYS_MODULE_EXT], +[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +AC_CACHE_CHECK([which extension is used for runtime loadable modules], + [libltdl_cv_shlibext], +[ +module=yes +eval libltdl_cv_shlibext=$shrext_cmds + ]) +if test -n "$libltdl_cv_shlibext"; then + m4_pattern_allow([LT_MODULE_EXT])dnl + AC_DEFINE_UNQUOTED([LT_MODULE_EXT], ["$libltdl_cv_shlibext"], + [Define to the extension used for runtime loadable modules, say, ".so".]) +fi +])# LT_SYS_MODULE_EXT + +# Old name: +AU_ALIAS([AC_LTDL_SHLIBEXT], [LT_SYS_MODULE_EXT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_SHLIBEXT], []) + + +# LT_SYS_MODULE_PATH +# ------------------ +AC_DEFUN([LT_SYS_MODULE_PATH], +[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +AC_CACHE_CHECK([which variable specifies run-time module search path], + [lt_cv_module_path_var], [lt_cv_module_path_var="$shlibpath_var"]) +if test -n "$lt_cv_module_path_var"; then + m4_pattern_allow([LT_MODULE_PATH_VAR])dnl + AC_DEFINE_UNQUOTED([LT_MODULE_PATH_VAR], ["$lt_cv_module_path_var"], + [Define to the name of the environment variable that determines the run-time module search path.]) +fi +])# LT_SYS_MODULE_PATH + +# Old name: +AU_ALIAS([AC_LTDL_SHLIBPATH], [LT_SYS_MODULE_PATH]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_SHLIBPATH], []) + + +# LT_SYS_DLSEARCH_PATH +# -------------------- +AC_DEFUN([LT_SYS_DLSEARCH_PATH], +[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +AC_CACHE_CHECK([for the default library search path], + [lt_cv_sys_dlsearch_path], + [lt_cv_sys_dlsearch_path="$sys_lib_dlsearch_path_spec"]) +if test -n "$lt_cv_sys_dlsearch_path"; then + sys_dlsearch_path= + for dir in $lt_cv_sys_dlsearch_path; do + if test -z "$sys_dlsearch_path"; then + sys_dlsearch_path="$dir" + else + sys_dlsearch_path="$sys_dlsearch_path$PATH_SEPARATOR$dir" + fi + done + m4_pattern_allow([LT_DLSEARCH_PATH])dnl + AC_DEFINE_UNQUOTED([LT_DLSEARCH_PATH], ["$sys_dlsearch_path"], + [Define to the system default library search path.]) +fi +])# LT_SYS_DLSEARCH_PATH + +# Old name: +AU_ALIAS([AC_LTDL_SYSSEARCHPATH], [LT_SYS_DLSEARCH_PATH]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_SYSSEARCHPATH], []) + + +# _LT_CHECK_DLPREOPEN +# ------------------- +m4_defun([_LT_CHECK_DLPREOPEN], +[m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +AC_CACHE_CHECK([whether libtool supports -dlopen/-dlpreopen], + [libltdl_cv_preloaded_symbols], + [if test -n "$lt_cv_sys_global_symbol_pipe"; then + libltdl_cv_preloaded_symbols=yes + else + libltdl_cv_preloaded_symbols=no + fi + ]) +if test x"$libltdl_cv_preloaded_symbols" = xyes; then + AC_DEFINE([HAVE_PRELOADED_SYMBOLS], [1], + [Define if libtool can extract symbol lists from object files.]) +fi +])# _LT_CHECK_DLPREOPEN + + +# LT_LIB_DLLOAD +# ------------- +AC_DEFUN([LT_LIB_DLLOAD], +[m4_pattern_allow([^LT_DLLOADERS$]) +LT_DLLOADERS= +AC_SUBST([LT_DLLOADERS]) + +AC_LANG_PUSH([C]) + +LIBADD_DLOPEN= +AC_SEARCH_LIBS([dlopen], [dl], + [AC_DEFINE([HAVE_LIBDL], [1], + [Define if you have the libdl library or equivalent.]) + if test "$ac_cv_search_dlopen" != "none required" ; then + LIBADD_DLOPEN="-ldl" + fi + libltdl_cv_lib_dl_dlopen="yes" + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#if HAVE_DLFCN_H +# include +#endif + ]], [[dlopen(0, 0);]])], + [AC_DEFINE([HAVE_LIBDL], [1], + [Define if you have the libdl library or equivalent.]) + libltdl_cv_func_dlopen="yes" + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"], + [AC_CHECK_LIB([svld], [dlopen], + [AC_DEFINE([HAVE_LIBDL], [1], + [Define if you have the libdl library or equivalent.]) + LIBADD_DLOPEN="-lsvld" libltdl_cv_func_dlopen="yes" + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"])])]) +if test x"$libltdl_cv_func_dlopen" = xyes || test x"$libltdl_cv_lib_dl_dlopen" = xyes +then + lt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBADD_DLOPEN" + AC_CHECK_FUNCS([dlerror]) + LIBS="$lt_save_LIBS" +fi +AC_SUBST([LIBADD_DLOPEN]) + +LIBADD_SHL_LOAD= +AC_CHECK_FUNC([shl_load], + [AC_DEFINE([HAVE_SHL_LOAD], [1], + [Define if you have the shl_load function.]) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"], + [AC_CHECK_LIB([dld], [shl_load], + [AC_DEFINE([HAVE_SHL_LOAD], [1], + [Define if you have the shl_load function.]) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la" + LIBADD_SHL_LOAD="-ldld"])]) +AC_SUBST([LIBADD_SHL_LOAD]) + +case $host_os in +darwin[[1567]].*) +# We only want this for pre-Mac OS X 10.4. + AC_CHECK_FUNC([_dyld_func_lookup], + [AC_DEFINE([HAVE_DYLD], [1], + [Define if you have the _dyld_func_lookup function.]) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dyld.la"]) + ;; +beos*) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}load_add_on.la" + ;; +cygwin* | mingw* | os2* | pw32*) + AC_CHECK_DECLS([cygwin_conv_path], [], [], [[#include ]]) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}loadlibrary.la" + ;; +esac + +AC_CHECK_LIB([dld], [dld_link], + [AC_DEFINE([HAVE_DLD], [1], + [Define if you have the GNU dld library.]) + LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dld_link.la"]) +AC_SUBST([LIBADD_DLD_LINK]) + +m4_pattern_allow([^LT_DLPREOPEN$]) +LT_DLPREOPEN= +if test -n "$LT_DLLOADERS" +then + for lt_loader in $LT_DLLOADERS; do + LT_DLPREOPEN="$LT_DLPREOPEN-dlpreopen $lt_loader " + done + AC_DEFINE([HAVE_LIBDLLOADER], [1], + [Define if libdlloader will be built on this platform]) +fi +AC_SUBST([LT_DLPREOPEN]) + +dnl This isn't used anymore, but set it for backwards compatibility +LIBADD_DL="$LIBADD_DLOPEN $LIBADD_SHL_LOAD" +AC_SUBST([LIBADD_DL]) + +AC_LANG_POP +])# LT_LIB_DLLOAD + +# Old name: +AU_ALIAS([AC_LTDL_DLLIB], [LT_LIB_DLLOAD]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_DLLIB], []) + + +# LT_SYS_SYMBOL_USCORE +# -------------------- +# does the compiler prefix global symbols with an underscore? +AC_DEFUN([LT_SYS_SYMBOL_USCORE], +[m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +AC_CACHE_CHECK([for _ prefix in compiled symbols], + [lt_cv_sys_symbol_underscore], + [lt_cv_sys_symbol_underscore=no + cat > conftest.$ac_ext <<_LT_EOF +void nm_test_func(){} +int main(){nm_test_func;return 0;} +_LT_EOF + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + ac_nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) && test -s "$ac_nlist"; then + # See whether the symbols have a leading underscore. + if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then + lt_cv_sys_symbol_underscore=yes + else + if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then + : + else + echo "configure: cannot find nm_test_func in $ac_nlist" >&AS_MESSAGE_LOG_FD + fi + fi + else + echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.c >&AS_MESSAGE_LOG_FD + fi + rm -rf conftest* + ]) + sys_symbol_underscore=$lt_cv_sys_symbol_underscore + AC_SUBST([sys_symbol_underscore]) +])# LT_SYS_SYMBOL_USCORE + +# Old name: +AU_ALIAS([AC_LTDL_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_SYMBOL_USCORE], []) + + +# LT_FUNC_DLSYM_USCORE +# -------------------- +AC_DEFUN([LT_FUNC_DLSYM_USCORE], +[AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl +if test x"$lt_cv_sys_symbol_underscore" = xyes; then + if test x"$libltdl_cv_func_dlopen" = xyes || + test x"$libltdl_cv_lib_dl_dlopen" = xyes ; then + AC_CACHE_CHECK([whether we have to add an underscore for dlsym], + [libltdl_cv_need_uscore], + [libltdl_cv_need_uscore=unknown + save_LIBS="$LIBS" + LIBS="$LIBS $LIBADD_DLOPEN" + _LT_TRY_DLOPEN_SELF( + [libltdl_cv_need_uscore=no], [libltdl_cv_need_uscore=yes], + [], [libltdl_cv_need_uscore=cross]) + LIBS="$save_LIBS" + ]) + fi +fi + +if test x"$libltdl_cv_need_uscore" = xyes; then + AC_DEFINE([NEED_USCORE], [1], + [Define if dlsym() requires a leading underscore in symbol names.]) +fi +])# LT_FUNC_DLSYM_USCORE + +# Old name: +AU_ALIAS([AC_LTDL_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LTDL_DLSYM_USCORE], []) + # Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. diff --git a/deps/mozjs/js/src/ctypes/libffi/config.sub b/deps/mozjs/js/src/ctypes/libffi/config.sub index 17c91458a8a..1c035c93116 100755 --- a/deps/mozjs/js/src/ctypes/libffi/config.sub +++ b/deps/mozjs/js/src/ctypes/libffi/config.sub @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-11-07' +timestamp='2011-01-03' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -126,7 +126,7 @@ case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) + storm-chaos* | os2-emx* | rtmk-nova* | wince-winmo*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; @@ -287,7 +287,6 @@ case $basic_machine in | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ - | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ @@ -295,14 +294,13 @@ case $basic_machine in | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ - | ubicom32 \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | picochip) + m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none @@ -373,7 +371,7 @@ case $basic_machine in | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ - | romp-* | rs6000-* | rx-* \ + | romp-* | rs6000-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ @@ -382,7 +380,6 @@ case $basic_machine in | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ | tron-* \ - | ubicom32-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ @@ -1299,7 +1296,7 @@ case $os in | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -winmo*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1341,6 +1338,9 @@ case $os in -os400*) os=-os400 ;; + -wince-winmo*) + os=-wince-winmo + ;; -wince*) os=-wince ;; @@ -1432,6 +1432,9 @@ case $os in -dicos*) os=-dicos ;; + -android*) + os=-android + ;; -none) ;; *) @@ -1686,6 +1689,9 @@ case $basic_machine in -vos*) vendor=stratus ;; + *-android*|*-linuxandroid*) + vendor=linux- + ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; diff --git a/deps/mozjs/js/src/ctypes/libffi/configure.ac b/deps/mozjs/js/src/ctypes/libffi/configure.ac index e85cff10cdc..d149b2e8efe 100644 --- a/deps/mozjs/js/src/ctypes/libffi/configure.ac +++ b/deps/mozjs/js/src/ctypes/libffi/configure.ac @@ -315,8 +315,16 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64 fi fi +if test x$TARGET = xX86_WIN64; then + LT_SYS_SYMBOL_USCORE + if test "x$sys_symbol_underscore" = xyes; then + AC_DEFINE(SYMBOL_UNDERSCORE, 1, [Define if symbols are underscored.]) + fi +fi + case "$target" in - *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + # Darwin 10 (OSX 10.6) and beyond allocate non-executable pages + *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, [Cannot use malloc on this target, so, we revert to alternative means]) diff --git a/deps/mozjs/js/src/ctypes/libffi/src/arm/sysv.S b/deps/mozjs/js/src/ctypes/libffi/src/arm/sysv.S index 7bce72712bb..9b99fca69db 100644 --- a/deps/mozjs/js/src/ctypes/libffi/src/arm/sysv.S +++ b/deps/mozjs/js/src/ctypes/libffi/src/arm/sysv.S @@ -229,6 +229,10 @@ LSYM(Lepilogue): .size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV) +/* Below are VFP hard-float ABI call and closure implementations. + Add VFP FPU directive here. */ + .fpu vfp + @ r0: fn @ r1: &ecif @ r2: cif->bytes diff --git a/deps/mozjs/js/src/ctypes/libffi/src/x86/win64.S b/deps/mozjs/js/src/ctypes/libffi/src/x86/win64.S index 6e9181867de..fcdb270faf5 100644 --- a/deps/mozjs/js/src/ctypes/libffi/src/x86/win64.S +++ b/deps/mozjs/js/src/ctypes/libffi/src/x86/win64.S @@ -232,10 +232,18 @@ ret_void$: ffi_call_win64 ENDP _TEXT ENDS END -#else + +#else + +#ifdef SYMBOL_UNDERSCORE +#define SYMBOL_NAME(name) _##name +#else +#define SYMBOL_NAME(name) name +#endif + .text -.extern _ffi_closure_win64_inner +.extern SYMBOL_NAME(ffi_closure_win64_inner) # ffi_closure_win64 will be called with these registers set: # rax points to 'closure' @@ -246,8 +254,8 @@ END # call ffi_closure_win64_inner for the actual work, then return the result. # .balign 16 - .globl _ffi_closure_win64 -_ffi_closure_win64: + .globl SYMBOL_NAME(ffi_closure_win64) +SYMBOL_NAME(ffi_closure_win64): # copy register arguments onto stack test $1,%r11 jne .Lfirst_is_float @@ -287,7 +295,7 @@ _ffi_closure_win64: mov %rax, %rcx # context is first parameter mov %rsp, %rdx # stack is second parameter add $48, %rdx # point to start of arguments - mov $_ffi_closure_win64_inner, %rax + mov $SYMBOL_NAME(ffi_closure_win64_inner), %rax callq *%rax # call the real closure function add $40, %rsp movq %rax, %xmm0 # If the closure returned a float, @@ -296,8 +304,8 @@ _ffi_closure_win64: .ffi_closure_win64_end: .balign 16 - .globl _ffi_call_win64 -_ffi_call_win64: + .globl SYMBOL_NAME(ffi_call_win64) +SYMBOL_NAME(ffi_call_win64): # copy registers onto stack mov %r9,32(%rsp) mov %r8,24(%rsp) diff --git a/deps/mozjs/js/src/ctypes/patches-libffi/00-base.patch b/deps/mozjs/js/src/ctypes/patches-libffi/00-base.patch new file mode 100644 index 00000000000..bde81a21460 --- /dev/null +++ b/deps/mozjs/js/src/ctypes/patches-libffi/00-base.patch @@ -0,0 +1,1328 @@ +Patch libffi to fix bug 550602, bug 538216, bug545634, bug 594611, bug 605421 +and bug 631928. + +diff --git a/js/src/ctypes/libffi/Makefile.in b/js/src/ctypes/libffi/Makefile.in +--- a/js/src/ctypes/libffi/Makefile.in ++++ b/js/src/ctypes/libffi/Makefile.in +@@ -199,17 +199,17 @@ LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + CCLD = $(CC) + LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ + SOURCES = $(libffi_la_SOURCES) $(nodist_libffi_la_SOURCES) \ + $(libffi_convenience_la_SOURCES) \ + $(nodist_libffi_convenience_la_SOURCES) + DIST_SOURCES = $(libffi_la_SOURCES) $(libffi_convenience_la_SOURCES) +-INFO_DEPS = $(srcdir)/doc/libffi.info ++INFO_DEPS = + am__TEXINFO_TEX_DIR = $(srcdir) + DVIS = doc/libffi.dvi + PDFS = doc/libffi.pdf + PSS = doc/libffi.ps + HTMLS = doc/libffi.html + TEXINFOS = doc/libffi.texi + TEXI2DVI = texi2dvi + TEXI2PDF = $(TEXI2DVI) --pdf --batch +@@ -986,57 +986,57 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/ffi.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/ffi64.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/freebsd.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/sysv.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/unix64.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/win32.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@src/x86/$(DEPDIR)/win64.Plo@am__quote@ + +-.S.o: ++%.o: %.S + @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + @am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCCAS_FALSE@ $(CPPASCOMPILE) -c -o $@ $< + +-.S.obj: ++%.obj: %.S + @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ + @am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ + @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCCAS_FALSE@ $(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +-.S.lo: ++%.lo: %.S + @am__fastdepCCAS_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ + @am__fastdepCCAS_TRUE@ $(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + @am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCCAS_FALSE@ $(LTCPPASCOMPILE) -c -o $@ $< + +-.c.o: ++%.o: %.c + @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +-.c.obj: ++%.obj: %.c + @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ + @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ + @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +-.c.lo: ++%.lo: %.c + @am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ + @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + + mostlyclean-libtool: +@@ -1129,17 +1129,17 @@ $(srcdir)/doc/stamp-vti: doc/libffi.texi $(top_srcdir)/configure + -@rm -f vti.tmp + @cp $(srcdir)/doc/version.texi $@ + + mostlyclean-vti: + -rm -f vti.tmp + + maintainer-clean-vti: + @MAINTAINER_MODE_TRUE@ -rm -f $(srcdir)/doc/stamp-vti $(srcdir)/doc/version.texi +-.dvi.ps: ++%.ps: %.dvi + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + $(DVIPS) -o $@ $< + + uninstall-dvi-am: + @$(NORMAL_UNINSTALL) + @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ +diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure +--- a/js/src/ctypes/libffi/configure ++++ b/js/src/ctypes/libffi/configure +@@ -8903,17 +8903,17 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' +- fix_srcfile_path='`cygpath -w "$srcfile"`' ++ fix_srcfile_path='' + enable_shared_with_static_runtimes=yes + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no +@@ -12270,20 +12270,20 @@ fi + + if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler supports pc related relocs" >&5 + $as_echo_n "checking assembler supports pc related relocs... " >&6; } + if test "${libffi_cv_as_x86_pcrel+set}" = set; then : + $as_echo_n "(cached) " >&6 + else + +- libffi_cv_as_x86_pcrel=yes ++ libffi_cv_as_x86_pcrel=no + echo '.text; foo: nop; .data; .long foo-.; .text' > conftest.s +- if $CC $CFLAGS -c conftest.s 2>&1 | $EGREP -i 'illegal|warning' > /dev/null; then +- libffi_cv_as_x86_pcrel=no ++ if $CC $CFLAGS -c conftest.s > /dev/null; then ++ libffi_cv_as_x86_pcrel=yes + fi + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libffi_cv_as_x86_pcrel" >&5 + $as_echo "$libffi_cv_as_x86_pcrel" >&6; } + if test "x$libffi_cv_as_x86_pcrel" = xyes; then + + $as_echo "#define HAVE_AS_X86_PCREL 1" >>confdefs.h +diff --git a/js/src/ctypes/libffi/src/x86/ffi64.c b/js/src/ctypes/libffi/src/x86/ffi64.c +--- a/js/src/ctypes/libffi/src/x86/ffi64.c ++++ b/js/src/ctypes/libffi/src/x86/ffi64.c +@@ -373,29 +373,29 @@ ffi_prep_cif_machdep (ffi_cif *cif) + || gprcount + ngpr > MAX_GPR_REGS + || ssecount + nsse > MAX_SSE_REGS) + { + long align = cif->arg_types[i]->alignment; + + if (align < 8) + align = 8; + +- bytes = ALIGN(bytes, align); ++ bytes = ALIGN (bytes, align); + bytes += cif->arg_types[i]->size; + } + else + { + gprcount += ngpr; + ssecount += nsse; + } + } + if (ssecount) + flags |= 1 << 11; + cif->flags = flags; +- cif->bytes = bytes; ++ cif->bytes = ALIGN (bytes, 8); + + return FFI_OK; + } + + void + ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) + { + enum x86_64_reg_class classes[MAX_CLASSES]; +diff --git a/js/src/ctypes/libffi/src/arm/ffi.c b/js/src/ctypes/libffi/src/arm/ffi.c +--- a/js/src/ctypes/libffi/src/arm/ffi.c ++++ b/js/src/ctypes/libffi/src/arm/ffi.c +@@ -24,22 +24,30 @@ + DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + + #include + #include + + #include + +-/* ffi_prep_args is called by the assembly routine once stack space +- has been allocated for the function's arguments */ ++/* Forward declares. */ ++static int vfp_type_p (ffi_type *); ++static void layout_vfp_args (ffi_cif *); + +-void ffi_prep_args(char *stack, extended_cif *ecif) ++/* ffi_prep_args is called by the assembly routine once stack space ++ has been allocated for the function's arguments ++ ++ The vfp_space parameter is the load area for VFP regs, the return ++ value is cif->vfp_used (word bitset of VFP regs used for passing ++ arguments). These are only used for the VFP hard-float ABI. ++*/ ++int ffi_prep_args(char *stack, extended_cif *ecif, float *vfp_space) + { +- register unsigned int i; ++ register unsigned int i, vi = 0; + register void **p_argv; + register char *argp; + register ffi_type **p_arg; + + argp = stack; + + if ( ecif->cif->flags == FFI_TYPE_STRUCT ) { + *(void **) argp = ecif->rvalue; +@@ -49,16 +57,31 @@ void ffi_prep_args(char *stack, extended_cif *ecif) + p_argv = ecif->avalue; + + for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; + (i != 0); + i--, p_arg++) + { + size_t z; + ++ /* Allocated in VFP registers. */ ++ if (ecif->cif->abi == FFI_VFP ++ && vi < ecif->cif->vfp_nargs && vfp_type_p (*p_arg)) ++ { ++ float* vfp_slot = vfp_space + ecif->cif->vfp_args[vi++]; ++ if ((*p_arg)->type == FFI_TYPE_FLOAT) ++ *((float*)vfp_slot) = *((float*)*p_argv); ++ else if ((*p_arg)->type == FFI_TYPE_DOUBLE) ++ *((double*)vfp_slot) = *((double*)*p_argv); ++ else ++ memcpy(vfp_slot, *p_argv, (*p_arg)->size); ++ p_argv++; ++ continue; ++ } ++ + /* Align if necessary */ + if (((*p_arg)->alignment - 1) & (unsigned) argp) { + argp = (char *) ALIGN(argp, (*p_arg)->alignment); + } + + if ((*p_arg)->type == FFI_TYPE_STRUCT) + argp = (char *) ALIGN(argp, 4); + +@@ -98,23 +121,25 @@ void ffi_prep_args(char *stack, extended_cif *ecif) + } + else + { + memcpy(argp, *p_argv, z); + } + p_argv++; + argp += z; + } +- +- return; ++ ++ /* Indicate the VFP registers used. */ ++ return ecif->cif->vfp_used; + } + + /* Perform machine dependent cif processing */ + ffi_status ffi_prep_cif_machdep(ffi_cif *cif) + { ++ int type_code; + /* Round the stack up to a multiple of 8 bytes. This isn't needed + everywhere, but it is on some platforms, and it doesn't harm anything + when it isn't needed. */ + cif->bytes = (cif->bytes + 7) & ~7; + + /* Set the return type flag */ + switch (cif->rtype->type) + { +@@ -125,137 +150,176 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + cif->flags = (unsigned) FFI_TYPE_SINT64; + break; + + case FFI_TYPE_STRUCT: +- if (cif->rtype->size <= 4) ++ if (cif->abi == FFI_VFP ++ && (type_code = vfp_type_p (cif->rtype)) != 0) ++ { ++ /* A Composite Type passed in VFP registers, either ++ FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */ ++ cif->flags = (unsigned) type_code; ++ } ++ else if (cif->rtype->size <= 4) + /* A Composite Type not larger than 4 bytes is returned in r0. */ + cif->flags = (unsigned)FFI_TYPE_INT; + else + /* A Composite Type larger than 4 bytes, or whose size cannot + be determined statically ... is stored in memory at an + address passed [in r0]. */ + cif->flags = (unsigned)FFI_TYPE_STRUCT; + break; + + default: + cif->flags = FFI_TYPE_INT; + break; + } + ++ /* Map out the register placements of VFP register args. ++ The VFP hard-float calling conventions are slightly more sophisticated than ++ the base calling conventions, so we do it here instead of in ffi_prep_args(). */ ++ if (cif->abi == FFI_VFP) ++ layout_vfp_args (cif); ++ + return FFI_OK; + } + +-extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, +- unsigned, unsigned, unsigned *, void (*fn)(void)); ++/* Prototypes for assembly functions, in sysv.S */ ++extern void ffi_call_SYSV (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *); ++extern void ffi_call_VFP (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *); + + void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) + { + extended_cif ecif; + + int small_struct = (cif->flags == FFI_TYPE_INT + && cif->rtype->type == FFI_TYPE_STRUCT); ++ int vfp_struct = (cif->flags == FFI_TYPE_STRUCT_VFP_FLOAT ++ || cif->flags == FFI_TYPE_STRUCT_VFP_DOUBLE); + + ecif.cif = cif; + ecif.avalue = avalue; + + unsigned int temp; + + /* If the return value is a struct and we don't have a return */ + /* value address then we need to make one */ + + if ((rvalue == NULL) && + (cif->flags == FFI_TYPE_STRUCT)) + { + ecif.rvalue = alloca(cif->rtype->size); + } + else if (small_struct) + ecif.rvalue = &temp; ++ else if (vfp_struct) ++ { ++ /* Largest case is double x 4. */ ++ ecif.rvalue = alloca(32); ++ } + else + ecif.rvalue = rvalue; + + switch (cif->abi) + { + case FFI_SYSV: +- ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, +- fn); ++ ffi_call_SYSV (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue); ++ break; + ++ case FFI_VFP: ++ ffi_call_VFP (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue); + break; ++ + default: + FFI_ASSERT(0); + break; + } + if (small_struct) + memcpy (rvalue, &temp, cif->rtype->size); ++ else if (vfp_struct) ++ memcpy (rvalue, ecif.rvalue, cif->rtype->size); + } + + /** private members **/ + + static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, +- void** args, ffi_cif* cif); ++ void** args, ffi_cif* cif, float *vfp_stack); + + void ffi_closure_SYSV (ffi_closure *); + ++void ffi_closure_VFP (ffi_closure *); ++ + /* This function is jumped to by the trampoline */ + + unsigned int +-ffi_closure_SYSV_inner (closure, respp, args) ++ffi_closure_SYSV_inner (closure, respp, args, vfp_args) + ffi_closure *closure; + void **respp; + void *args; ++ void *vfp_args; + { + // our various things... + ffi_cif *cif; + void **arg_area; + + cif = closure->cif; + arg_area = (void**) alloca (cif->nargs * sizeof (void*)); + + /* this call will initialize ARG_AREA, such that each + * element in that array points to the corresponding + * value on the stack; and if the function returns + * a structure, it will re-set RESP to point to the + * structure return address. */ + +- ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); ++ ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args); + + (closure->fun) (cif, *respp, arg_area, closure->user_data); + + return cif->flags; + } + + /*@-exportheader@*/ + static void + ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, +- void **avalue, ffi_cif *cif) ++ void **avalue, ffi_cif *cif, ++ /* Used only under VFP hard-float ABI. */ ++ float *vfp_stack) + /*@=exportheader@*/ + { +- register unsigned int i; ++ register unsigned int i, vi = 0; + register void **p_argv; + register char *argp; + register ffi_type **p_arg; + + argp = stack; + + if ( cif->flags == FFI_TYPE_STRUCT ) { + *rvalue = *(void **) argp; + argp += 4; + } + + p_argv = avalue; + + for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) + { + size_t z; +- +- size_t alignment = (*p_arg)->alignment; ++ size_t alignment; ++ ++ if (cif->abi == FFI_VFP ++ && vi < cif->vfp_nargs && vfp_type_p (*p_arg)) ++ { ++ *p_argv++ = (void*)(vfp_stack + cif->vfp_args[vi++]); ++ continue; ++ } ++ ++ alignment = (*p_arg)->alignment; + if (alignment < 4) + alignment = 4; + /* Align if necessary */ + if ((alignment - 1) & (unsigned) argp) { + argp = (char *) ALIGN(argp, alignment); + } + + z = (*p_arg)->size; +@@ -290,20 +354,147 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, + + ffi_status + ffi_prep_closure_loc (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void *codeloc) + { +- FFI_ASSERT (cif->abi == FFI_SYSV); ++ void (*closure_func)(ffi_closure*) = NULL; + ++ if (cif->abi == FFI_SYSV) ++ closure_func = &ffi_closure_SYSV; ++ else if (cif->abi == FFI_VFP) ++ closure_func = &ffi_closure_VFP; ++ else ++ FFI_ASSERT (0); ++ + FFI_INIT_TRAMPOLINE (&closure->tramp[0], \ +- &ffi_closure_SYSV, \ ++ closure_func, \ + codeloc); + + closure->cif = cif; + closure->user_data = user_data; + closure->fun = fun; + + return FFI_OK; + } ++ ++/* Below are routines for VFP hard-float support. */ ++ ++static int rec_vfp_type_p (ffi_type *t, int *elt, int *elnum) ++{ ++ switch (t->type) ++ { ++ case FFI_TYPE_FLOAT: ++ case FFI_TYPE_DOUBLE: ++ *elt = (int) t->type; ++ *elnum = 1; ++ return 1; ++ ++ case FFI_TYPE_STRUCT_VFP_FLOAT: ++ *elt = FFI_TYPE_FLOAT; ++ *elnum = t->size / sizeof (float); ++ return 1; ++ ++ case FFI_TYPE_STRUCT_VFP_DOUBLE: ++ *elt = FFI_TYPE_DOUBLE; ++ *elnum = t->size / sizeof (double); ++ return 1; ++ ++ case FFI_TYPE_STRUCT:; ++ { ++ int base_elt = 0, total_elnum = 0; ++ ffi_type **el = t->elements; ++ while (*el) ++ { ++ int el_elt = 0, el_elnum = 0; ++ if (! rec_vfp_type_p (*el, &el_elt, &el_elnum) ++ || (base_elt && base_elt != el_elt) ++ || total_elnum + el_elnum > 4) ++ return 0; ++ base_elt = el_elt; ++ total_elnum += el_elnum; ++ el++; ++ } ++ *elnum = total_elnum; ++ *elt = base_elt; ++ return 1; ++ } ++ default: ; ++ } ++ return 0; ++} ++ ++static int vfp_type_p (ffi_type *t) ++{ ++ int elt, elnum; ++ if (rec_vfp_type_p (t, &elt, &elnum)) ++ { ++ if (t->type == FFI_TYPE_STRUCT) ++ { ++ if (elnum == 1) ++ t->type = elt; ++ else ++ t->type = (elt == FFI_TYPE_FLOAT ++ ? FFI_TYPE_STRUCT_VFP_FLOAT ++ : FFI_TYPE_STRUCT_VFP_DOUBLE); ++ } ++ return (int) t->type; ++ } ++ return 0; ++} ++ ++static void place_vfp_arg (ffi_cif *cif, ffi_type *t) ++{ ++ int reg = cif->vfp_reg_free; ++ int nregs = t->size / sizeof (float); ++ int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT ++ || t->type == FFI_TYPE_FLOAT) ? 1 : 2); ++ /* Align register number. */ ++ if ((reg & 1) && align == 2) ++ reg++; ++ while (reg + nregs <= 16) ++ { ++ int s, new_used = 0; ++ for (s = reg; s < reg + nregs; s++) ++ { ++ new_used |= (1 << s); ++ if (cif->vfp_used & (1 << s)) ++ { ++ reg += align; ++ goto next_reg; ++ } ++ } ++ /* Found regs to allocate. */ ++ cif->vfp_used |= new_used; ++ cif->vfp_args[cif->vfp_nargs++] = reg; ++ ++ /* Update vfp_reg_free. */ ++ if (cif->vfp_used & (1 << cif->vfp_reg_free)) ++ { ++ reg += nregs; ++ while (cif->vfp_used & (1 << reg)) ++ reg += 1; ++ cif->vfp_reg_free = reg; ++ } ++ return; ++ next_reg: ; ++ } ++} ++ ++static void layout_vfp_args (ffi_cif *cif) ++{ ++ int i; ++ /* Init VFP fields */ ++ cif->vfp_used = 0; ++ cif->vfp_nargs = 0; ++ cif->vfp_reg_free = 0; ++ memset (cif->vfp_args, -1, 16); /* Init to -1. */ ++ ++ for (i = 0; i < cif->nargs; i++) ++ { ++ ffi_type *t = cif->arg_types[i]; ++ if (vfp_type_p (t)) ++ place_vfp_arg (cif, t); ++ } ++} +diff --git a/js/src/ctypes/libffi/src/arm/ffitarget.h b/js/src/ctypes/libffi/src/arm/ffitarget.h +--- a/js/src/ctypes/libffi/src/arm/ffitarget.h ++++ b/js/src/ctypes/libffi/src/arm/ffitarget.h +@@ -29,21 +29,35 @@ + + #ifndef LIBFFI_ASM + typedef unsigned long ffi_arg; + typedef signed long ffi_sarg; + + typedef enum ffi_abi { + FFI_FIRST_ABI = 0, + FFI_SYSV, ++ FFI_VFP, + FFI_LAST_ABI, ++#ifdef __ARM_PCS_VFP ++ FFI_DEFAULT_ABI = FFI_VFP ++#else + FFI_DEFAULT_ABI = FFI_SYSV ++#endif + } ffi_abi; + #endif + ++#define FFI_EXTRA_CIF_FIELDS \ ++ int vfp_used; \ ++ short vfp_reg_free, vfp_nargs; \ ++ signed char vfp_args[16] \ ++ ++/* Internally used. */ ++#define FFI_TYPE_STRUCT_VFP_FLOAT (FFI_TYPE_LAST + 1) ++#define FFI_TYPE_STRUCT_VFP_DOUBLE (FFI_TYPE_LAST + 2) ++ + /* ---- Definitions for closures ----------------------------------------- */ + + #define FFI_CLOSURES 1 + #define FFI_TRAMPOLINE_SIZE 20 + #define FFI_NATIVE_RAW_API 0 + + #endif + +diff --git a/js/src/ctypes/libffi/src/arm/sysv.S b/js/src/ctypes/libffi/src/arm/sysv.S +--- a/js/src/ctypes/libffi/src/arm/sysv.S ++++ b/js/src/ctypes/libffi/src/arm/sysv.S +@@ -137,54 +137,52 @@ _L__\name: + ldr\cond pc, [sp], #4 + .else + ldm\cond\dirn sp!, {\regs, pc} + .endif + #endif + .endm + + +- @ r0: ffi_prep_args ++ @ r0: fn + @ r1: &ecif + @ r2: cif->bytes + @ r3: fig->flags + @ sp+0: ecif.rvalue +- @ sp+4: fn + + @ This assumes we are using gas. + ARM_FUNC_START ffi_call_SYSV + @ Save registers + stmfd sp!, {r0-r3, fp, lr} + UNWIND .save {r0-r3, fp, lr} + mov fp, sp + + UNWIND .setfp fp, sp + + @ Make room for all of the new args. + sub sp, fp, r2 + + @ Place all of the ffi_prep_args in position +- mov ip, r0 + mov r0, sp + @ r1 already set + + @ Call ffi_prep_args(stack, &ecif) +- call_reg(ip) ++ bl ffi_prep_args + + @ move first 4 parameters in registers + ldmia sp, {r0-r3} + + @ and adjust stack +- ldr ip, [fp, #8] +- cmp ip, #16 +- movhs ip, #16 +- add sp, sp, ip ++ sub lr, fp, sp @ cif->bytes == fp - sp ++ ldr ip, [fp] @ load fn() in advance ++ cmp lr, #16 ++ movhs lr, #16 ++ add sp, sp, lr + + @ call (fn) (...) +- ldr ip, [fp, #28] + call_reg(ip) + + @ Remove the space we pushed for the args + mov sp, fp + + @ Load r2 with the pointer to storage for the return value + ldr r2, [sp, #24] + +@@ -225,16 +223,111 @@ ARM_FUNC_START ffi_call_SYSV + + LSYM(Lepilogue): + RETLDM "r0-r3,fp" + + .ffi_call_SYSV_end: + UNWIND .fnend + .size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV) + ++ ++ @ r0: fn ++ @ r1: &ecif ++ @ r2: cif->bytes ++ @ r3: fig->flags ++ @ sp+0: ecif.rvalue ++ ++ARM_FUNC_START ffi_call_VFP ++ @ Save registers ++ stmfd sp!, {r0-r3, fp, lr} ++ UNWIND .save {r0-r3, fp, lr} ++ mov fp, sp ++ UNWIND .setfp fp, sp ++ ++ @ Make room for all of the new args. ++ sub sp, sp, r2 ++ ++ @ Make room for loading VFP args ++ sub sp, sp, #64 ++ ++ @ Place all of the ffi_prep_args in position ++ mov r0, sp ++ @ r1 already set ++ sub r2, fp, #64 @ VFP scratch space ++ ++ @ Call ffi_prep_args(stack, &ecif, vfp_space) ++ bl ffi_prep_args ++ ++ @ Load VFP register args if needed ++ cmp r0, #0 ++ beq LSYM(Lbase_args) ++ ++ @ Load only d0 if possible ++ cmp r0, #3 ++ sub ip, fp, #64 ++ flddle d0, [ip] ++ fldmiadgt ip, {d0-d7} ++ ++LSYM(Lbase_args): ++ @ move first 4 parameters in registers ++ ldmia sp, {r0-r3} ++ ++ @ and adjust stack ++ sub lr, ip, sp @ cif->bytes == (fp - 64) - sp ++ ldr ip, [fp] @ load fn() in advance ++ cmp lr, #16 ++ movhs lr, #16 ++ add sp, sp, lr ++ ++ @ call (fn) (...) ++ call_reg(ip) ++ ++ @ Remove the space we pushed for the args ++ mov sp, fp ++ ++ @ Load r2 with the pointer to storage for ++ @ the return value ++ ldr r2, [sp, #24] ++ ++ @ Load r3 with the return type code ++ ldr r3, [sp, #12] ++ ++ @ If the return value pointer is NULL, ++ @ assume no return value. ++ cmp r2, #0 ++ beq LSYM(Lepilogue_vfp) ++ ++ cmp r3, #FFI_TYPE_INT ++ streq r0, [r2] ++ beq LSYM(Lepilogue_vfp) ++ ++ cmp r3, #FFI_TYPE_SINT64 ++ stmeqia r2, {r0, r1} ++ beq LSYM(Lepilogue_vfp) ++ ++ cmp r3, #FFI_TYPE_FLOAT ++ fstseq s0, [r2] ++ beq LSYM(Lepilogue_vfp) ++ ++ cmp r3, #FFI_TYPE_DOUBLE ++ fstdeq d0, [r2] ++ beq LSYM(Lepilogue_vfp) ++ ++ cmp r3, #FFI_TYPE_STRUCT_VFP_FLOAT ++ cmpne r3, #FFI_TYPE_STRUCT_VFP_DOUBLE ++ fstmiadeq r2, {d0-d3} ++ ++LSYM(Lepilogue_vfp): ++ RETLDM "r0-r3,fp" ++ ++.ffi_call_VFP_end: ++ UNWIND .fnend ++ .size CNAME(ffi_call_VFP),.ffi_call_VFP_end-CNAME(ffi_call_VFP) ++ ++ + /* + unsigned int FFI_HIDDEN + ffi_closure_SYSV_inner (closure, respp, args) + ffi_closure *closure; + void **respp; + void *args; + */ + +@@ -297,11 +390,73 @@ ARM_FUNC_START ffi_closure_SYSV + ldfd f0, [sp] + b .Lclosure_epilogue + #endif + + .ffi_closure_SYSV_end: + UNWIND .fnend + .size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV) + ++ ++ARM_FUNC_START ffi_closure_VFP ++ fstmfdd sp!, {d0-d7} ++ @ r0-r3, then d0-d7 ++ UNWIND .pad #80 ++ add ip, sp, #80 ++ stmfd sp!, {ip, lr} ++ UNWIND .save {r0, lr} ++ add r2, sp, #72 ++ add r3, sp, #8 ++ .pad #72 ++ sub sp, sp, #72 ++ str sp, [sp, #64] ++ add r1, sp, #64 ++ bl ffi_closure_SYSV_inner ++ ++ cmp r0, #FFI_TYPE_INT ++ beq .Lretint_vfp ++ ++ cmp r0, #FFI_TYPE_FLOAT ++ beq .Lretfloat_vfp ++ ++ cmp r0, #FFI_TYPE_DOUBLE ++ cmpne r0, #FFI_TYPE_LONGDOUBLE ++ beq .Lretdouble_vfp ++ ++ cmp r0, #FFI_TYPE_SINT64 ++ beq .Lretlonglong_vfp ++ ++ cmp r0, #FFI_TYPE_STRUCT_VFP_FLOAT ++ beq .Lretfloat_struct_vfp ++ ++ cmp r0, #FFI_TYPE_STRUCT_VFP_DOUBLE ++ beq .Lretdouble_struct_vfp ++ ++.Lclosure_epilogue_vfp: ++ add sp, sp, #72 ++ ldmfd sp, {sp, pc} ++ ++.Lretfloat_vfp: ++ flds s0, [sp] ++ b .Lclosure_epilogue_vfp ++.Lretdouble_vfp: ++ fldd d0, [sp] ++ b .Lclosure_epilogue_vfp ++.Lretint_vfp: ++ ldr r0, [sp] ++ b .Lclosure_epilogue_vfp ++.Lretlonglong_vfp: ++ ldmia sp, {r0, r1} ++ b .Lclosure_epilogue_vfp ++.Lretfloat_struct_vfp: ++ fldmiad sp, {d0-d1} ++ b .Lclosure_epilogue_vfp ++.Lretdouble_struct_vfp: ++ fldmiad sp, {d0-d3} ++ b .Lclosure_epilogue_vfp ++ ++.ffi_closure_VFP_end: ++ UNWIND .fnend ++ .size CNAME(ffi_closure_VFP),.ffi_closure_VFP_end-CNAME(ffi_closure_VFP) ++ + #if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",%progbits + #endif +diff --git a/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp b/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp +--- a/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp ++++ b/js/src/ctypes/libffi/testsuite/lib/libffi-dg.exp +@@ -261,16 +261,66 @@ proc dg-xfail-if { args } { + set args [lreplace $args 0 0] + set selector "target [join [lindex $args 1]]" + if { [dg-process-target $selector] == "S" } { + global compiler_conditional_xfail_data + set compiler_conditional_xfail_data $args + } + } + ++proc check-flags { args } { ++ ++ # The args are within another list; pull them out. ++ set args [lindex $args 0] ++ ++ # The next two arguments are optional. If they were not specified, ++ # use the defaults. ++ if { [llength $args] == 2 } { ++ lappend $args [list "*"] ++ } ++ if { [llength $args] == 3 } { ++ lappend $args [list ""] ++ } ++ ++ # If the option strings are the defaults, or the same as the ++ # defaults, there is no need to call check_conditional_xfail to ++ # compare them to the actual options. ++ if { [string compare [lindex $args 2] "*"] == 0 ++ && [string compare [lindex $args 3] "" ] == 0 } { ++ set result 1 ++ } else { ++ # The target list might be an effective-target keyword, so replace ++ # the original list with "*-*-*", since we already know it matches. ++ set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]] ++ } ++ ++ return $result ++} ++ ++proc dg-skip-if { args } { ++ # Verify the number of arguments. The last two are optional. ++ set args [lreplace $args 0 0] ++ if { [llength $args] < 2 || [llength $args] > 4 } { ++ error "dg-skip-if 2: need 2, 3, or 4 arguments" ++ } ++ ++ # Don't bother if we're already skipping the test. ++ upvar dg-do-what dg-do-what ++ if { [lindex ${dg-do-what} 1] == "N" } { ++ return ++ } ++ ++ set selector [list target [lindex $args 1]] ++ if { [dg-process-target $selector] == "S" } { ++ if [check-flags $args] { ++ upvar dg-do-what dg-do-what ++ set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"] ++ } ++ } ++} + + # We need to make sure that additional_files and additional_sources + # are both cleared out after every test. It is not enough to clear + # them out *before* the next test run because gcc-target-compile gets + # run directly from some .exp files (outside of any test). (Those + # uses should eventually be eliminated.) + + # Because the DG framework doesn't provide a hook that is run at the +diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c b/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c +--- a/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c ++++ b/js/src/ctypes/libffi/testsuite/libffi.call/cls_double_va.c +@@ -1,16 +1,18 @@ + /* Area: ffi_call, closure_call + Purpose: Test doubles passed in variable argument lists. + Limitations: none. + PR: none. + Originator: Blake Chaffin 6/6/2007 */ + + /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */ + /* { dg-output "" { xfail avr32*-*-* } } */ ++/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */ ++ + #include "ffitest.h" + + static void + cls_double_va_fn(ffi_cif* cif __UNUSED__, void* resp, + void** args, void* userdata __UNUSED__) + { + char* format = *(char**)args[0]; + double doubleValue = *(double*)args[1]; +diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c b/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c +--- a/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c ++++ b/js/src/ctypes/libffi/testsuite/libffi.call/cls_longdouble_va.c +@@ -1,16 +1,18 @@ + /* Area: ffi_call, closure_call + Purpose: Test long doubles passed in variable argument lists. + Limitations: none. + PR: none. + Originator: Blake Chaffin 6/6/2007 */ + + /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */ + /* { dg-output "" { xfail avr32*-*-* x86_64-*-mingw* } } */ ++/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */ ++ + #include "ffitest.h" + + static void + cls_longdouble_va_fn(ffi_cif* cif __UNUSED__, void* resp, + void** args, void* userdata __UNUSED__) + { + char* format = *(char**)args[0]; + long double ldValue = *(long double*)args[1]; +diff --git a/js/src/ctypes/libffi/include/ffi.h.in b/js/src/ctypes/libffi/include/ffi.h.in +--- a/js/src/ctypes/libffi/include/ffi.h.in ++++ b/js/src/ctypes/libffi/include/ffi.h.in +@@ -72,25 +72,37 @@ extern "C" { + #endif + + #include + #include + + /* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example). + But we can find it either under the correct ANSI name, or under GNU + C's internal name. */ ++ ++#define FFI_64_BIT_MAX 9223372036854775807 ++ + #ifdef LONG_LONG_MAX + # define FFI_LONG_LONG_MAX LONG_LONG_MAX + #else + # ifdef LLONG_MAX + # define FFI_LONG_LONG_MAX LLONG_MAX + # else + # ifdef __GNUC__ + # define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ + # endif ++# ifdef _AIX ++# ifndef __PPC64__ ++# if defined (__IBMC__) || defined (__IBMCPP__) ++# define FFI_LONG_LONG_MAX LONGLONG_MAX ++# endif ++# endif /* __PPC64__ */ ++# undef FFI_64_BIT_MAX ++# define FFI_64_BIT_MAX 9223372036854775807LL ++# endif + # endif + #endif + + /* The closure code assumes that this works on pointers, i.e. a size_t */ + /* can hold a pointer. */ + + typedef struct _ffi_type + { +@@ -127,27 +139,27 @@ typedef struct _ffi_type + #elif INT_MAX == 9223372036854775807 + # define ffi_type_uint ffi_type_uint64 + # define ffi_type_sint ffi_type_sint64 + #else + #error "int size not supported" + #endif + + #if LONG_MAX == 2147483647 +-# if FFI_LONG_LONG_MAX != 9223372036854775807 ++# if FFI_LONG_LONG_MAX != FFI_64_BIT_MAX + #error "no 64-bit data type supported" + # endif +-#elif LONG_MAX != 9223372036854775807 ++#elif LONG_MAX != FFI_64_BIT_MAX + #error "long size not supported" + #endif + + #if LONG_MAX == 2147483647 + # define ffi_type_ulong ffi_type_uint32 + # define ffi_type_slong ffi_type_sint32 +-#elif LONG_MAX == 9223372036854775807 ++#elif LONG_MAX == FFI_64_BIT_MAX + # define ffi_type_ulong ffi_type_uint64 + # define ffi_type_slong ffi_type_sint64 + #else + #error "long size not supported" + #endif + + /* These are defined in types.c */ + extern ffi_type ffi_type_void; +@@ -190,17 +202,17 @@ typedef struct { + #endif + } ffi_cif; + + /* ---- Definitions for the raw API -------------------------------------- */ + + #ifndef FFI_SIZEOF_ARG + # if LONG_MAX == 2147483647 + # define FFI_SIZEOF_ARG 4 +-# elif LONG_MAX == 9223372036854775807 ++# elif LONG_MAX == FFI_64_BIT_MAX + # define FFI_SIZEOF_ARG 8 + # endif + #endif + + #ifndef FFI_SIZEOF_JAVA_RAW + # define FFI_SIZEOF_JAVA_RAW FFI_SIZEOF_ARG + #endif + +diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac +--- a/js/src/ctypes/libffi/configure.ac ++++ b/js/src/ctypes/libffi/configure.ac +@@ -272,20 +272,20 @@ if test x$TARGET = xSPARC; then + AC_DEFINE(HAVE_AS_REGISTER_PSEUDO_OP, 1, + [Define if your assembler supports .register.]) + fi + fi + + if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64; then + AC_CACHE_CHECK([assembler supports pc related relocs], + libffi_cv_as_x86_pcrel, [ +- libffi_cv_as_x86_pcrel=yes ++ libffi_cv_as_x86_pcrel=no + echo '.text; foo: nop; .data; .long foo-.; .text' > conftest.s +- if $CC $CFLAGS -c conftest.s 2>&1 | $EGREP -i 'illegal|warning' > /dev/null; then +- libffi_cv_as_x86_pcrel=no ++ if $CC $CFLAGS -c conftest.s > /dev/null; then ++ libffi_cv_as_x86_pcrel=yes + fi + ]) + if test "x$libffi_cv_as_x86_pcrel" = xyes; then + AC_DEFINE(HAVE_AS_X86_PCREL, 1, + [Define if your assembler supports PC relative relocs.]) + fi + + AC_CACHE_CHECK([assembler .ascii pseudo-op support], +diff --git a/js/src/ctypes/libffi/config.sub b/js/src/ctypes/libffi/config.sub +--- a/js/src/ctypes/libffi/config.sub ++++ b/js/src/ctypes/libffi/config.sub +@@ -1,15 +1,15 @@ + #! /bin/sh + # Configuration validation subroutine script. + # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + # Free Software Foundation, Inc. + +-timestamp='2009-11-07' ++timestamp='2011-01-03' + + # This file is (in principle) common to ALL GNU software. + # The presence of a machine in this file suggests that SOME GNU software + # can handle that machine. It does not imply ALL GNU software can. + # + # This file is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by + # the Free Software Foundation; either version 2 of the License, or +@@ -121,17 +121,17 @@ esac + + # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). + # Here we must recognize all the valid KERNEL-OS combinations. + maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` + case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ +- storm-chaos* | os2-emx* | rtmk-nova*) ++ storm-chaos* | os2-emx* | rtmk-nova* | wince-winmo*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi +@@ -282,32 +282,30 @@ case $basic_machine in + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ +- | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ +- | ubicom32 \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; +- m6811 | m68hc11 | m6812 | m68hc12 | picochip) ++ m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown +@@ -368,26 +366,25 @@ case $basic_machine in + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ +- | romp-* | rs6000-* | rx-* \ ++ | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tron-* \ +- | ubicom32-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. +@@ -1294,17 +1291,17 @@ case $os in + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ +- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) ++ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -winmo*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os +@@ -1336,16 +1333,19 @@ case $os in + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; ++ -wince-winmo*) ++ os=-wince-winmo ++ ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf +@@ -1427,16 +1427,19 @@ case $os in + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; ++ -android*) ++ os=-android ++ ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +@@ -1681,16 +1684,19 @@ case $basic_machine in + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; ++ *-android*|*-linuxandroid*) ++ vendor=linux- ++ ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; + esac + + echo $basic_machine$os + exit + \ No newline at end of file diff --git a/deps/mozjs/js/src/ctypes/patches-libffi/01-bug-670719.patch b/deps/mozjs/js/src/ctypes/patches-libffi/01-bug-670719.patch new file mode 100644 index 00000000000..fa152e3e94d --- /dev/null +++ b/deps/mozjs/js/src/ctypes/patches-libffi/01-bug-670719.patch @@ -0,0 +1,28 @@ +# HG changeset patch +# Parent e357f3f732a0f3e98f8bd4661de03c9042c5c330 +# User Landry Breuil +treat powerpc-*-openbsd* as powerpc-*-freebsd* + + +diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure +--- a/js/src/ctypes/libffi/configure ++++ b/js/src/ctypes/libffi/configure +@@ -11272,17 +11272,17 @@ case "$host" in + TARGET=POWERPC; TARGETDIR=powerpc + ;; + powerpc-*-darwin*) + TARGET=POWERPC_DARWIN; TARGETDIR=powerpc + ;; + powerpc-*-aix* | rs6000-*-aix*) + TARGET=POWERPC_AIX; TARGETDIR=powerpc + ;; +- powerpc-*-freebsd*) ++ powerpc-*-freebsd* | powerpc-*-openbsd*) + TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc + ;; + powerpc*-*-rtems*) + TARGET=POWERPC; TARGETDIR=powerpc + ;; + + s390-*-* | s390x-*-*) + TARGET=S390; TARGETDIR=s390 diff --git a/deps/mozjs/js/src/ctypes/patches-libffi/02-bug-682180.patch b/deps/mozjs/js/src/ctypes/patches-libffi/02-bug-682180.patch new file mode 100644 index 00000000000..310836387cc --- /dev/null +++ b/deps/mozjs/js/src/ctypes/patches-libffi/02-bug-682180.patch @@ -0,0 +1,27 @@ +diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure +index 2c08e1b..37e3055 100755 +--- a/js/src/ctypes/libffi/configure ++++ b/js/src/ctypes/libffi/configure +@@ -12362,7 +12362,7 @@ $as_echo "#define HAVE_AS_STRING_PSEUDO_OP 1" >>confdefs.h + fi + + case "$target" in +- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) ++ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + + $as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h + +diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac +index e85cff1..1db02ce 100644 +--- a/js/src/ctypes/libffi/configure.ac ++++ b/js/src/ctypes/libffi/configure.ac +@@ -316,7 +316,8 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64 + fi + + case "$target" in +- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) ++ # Darwin 10 (OSX 10.6) and beyond allocate non-executable pages ++ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, + [Cannot use malloc on this target, so, we revert to + alternative means]) diff --git a/deps/mozjs/js/src/ctypes/patches-libffi/03-bug-712594.patch b/deps/mozjs/js/src/ctypes/patches-libffi/03-bug-712594.patch new file mode 100644 index 00000000000..1d62ad77449 --- /dev/null +++ b/deps/mozjs/js/src/ctypes/patches-libffi/03-bug-712594.patch @@ -0,0 +1,1072 @@ +commit 5b9cd52784339a42e417174a55e310e214d435f9 +Author: Anthony Green +Date: Mon Nov 22 15:19:57 2010 -0500 + + win64-underscore patch + +diff --git a/aclocal.m4 b/aclocal.m4 +index 0ef4b09..4079f82 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -7364,6 +7364,811 @@ _LT_EOF + esac + ]) + ++# ltdl.m4 - Configure ltdl for the target system. -*-Autoconf-*- ++# ++# Copyright (C) 1999-2006, 2007, 2008 Free Software Foundation, Inc. ++# Written by Thomas Tanner, 1999 ++# ++# This file is free software; the Free Software Foundation gives ++# unlimited permission to copy and/or distribute it, with or without ++# modifications, as long as this notice is preserved. ++ ++# serial 17 LTDL_INIT ++ ++# LT_CONFIG_LTDL_DIR(DIRECTORY, [LTDL-MODE]) ++# ------------------------------------------ ++# DIRECTORY contains the libltdl sources. It is okay to call this ++# function multiple times, as long as the same DIRECTORY is always given. ++AC_DEFUN([LT_CONFIG_LTDL_DIR], ++[AC_BEFORE([$0], [LTDL_INIT]) ++_$0($*) ++])# LT_CONFIG_LTDL_DIR ++ ++# We break this out into a separate macro, so that we can call it safely ++# internally without being caught accidentally by the sed scan in libtoolize. ++m4_defun([_LT_CONFIG_LTDL_DIR], ++[dnl remove trailing slashes ++m4_pushdef([_ARG_DIR], m4_bpatsubst([$1], [/*$])) ++m4_case(_LTDL_DIR, ++ [], [dnl only set lt_ltdl_dir if _ARG_DIR is not simply `.' ++ m4_if(_ARG_DIR, [.], ++ [], ++ [m4_define([_LTDL_DIR], _ARG_DIR) ++ _LT_SHELL_INIT([lt_ltdl_dir=']_ARG_DIR['])])], ++ [m4_if(_ARG_DIR, _LTDL_DIR, ++ [], ++ [m4_fatal([multiple libltdl directories: `]_LTDL_DIR[', `]_ARG_DIR['])])]) ++m4_popdef([_ARG_DIR]) ++])# _LT_CONFIG_LTDL_DIR ++ ++# Initialise: ++m4_define([_LTDL_DIR], []) ++ ++ ++# _LT_BUILD_PREFIX ++# ---------------- ++# If Autoconf is new enough, expand to `${top_build_prefix}', otherwise ++# to `${top_builddir}/'. ++m4_define([_LT_BUILD_PREFIX], ++[m4_ifdef([AC_AUTOCONF_VERSION], ++ [m4_if(m4_version_compare(m4_defn([AC_AUTOCONF_VERSION]), [2.62]), ++ [-1], [m4_ifdef([_AC_HAVE_TOP_BUILD_PREFIX], ++ [${top_build_prefix}], ++ [${top_builddir}/])], ++ [${top_build_prefix}])], ++ [${top_builddir}/])[]dnl ++]) ++ ++ ++# LTDL_CONVENIENCE ++# ---------------- ++# sets LIBLTDL to the link flags for the libltdl convenience library and ++# LTDLINCL to the include flags for the libltdl header and adds ++# --enable-ltdl-convenience to the configure arguments. Note that ++# AC_CONFIG_SUBDIRS is not called here. LIBLTDL will be prefixed with ++# '${top_build_prefix}' if available, otherwise with '${top_builddir}/', ++# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single ++# quotes!). If your package is not flat and you're not using automake, ++# define top_build_prefix, top_builddir, and top_srcdir appropriately ++# in your Makefiles. ++AC_DEFUN([LTDL_CONVENIENCE], ++[AC_BEFORE([$0], [LTDL_INIT])dnl ++dnl Although the argument is deprecated and no longer documented, ++dnl LTDL_CONVENIENCE used to take a DIRECTORY orgument, if we have one ++dnl here make sure it is the same as any other declaration of libltdl's ++dnl location! This also ensures lt_ltdl_dir is set when configure.ac is ++dnl not yet using an explicit LT_CONFIG_LTDL_DIR. ++m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl ++_$0() ++])# LTDL_CONVENIENCE ++ ++# AC_LIBLTDL_CONVENIENCE accepted a directory argument in older libtools, ++# now we have LT_CONFIG_LTDL_DIR: ++AU_DEFUN([AC_LIBLTDL_CONVENIENCE], ++[_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) ++_LTDL_CONVENIENCE]) ++ ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LIBLTDL_CONVENIENCE], []) ++ ++ ++# _LTDL_CONVENIENCE ++# ----------------- ++# Code shared by LTDL_CONVENIENCE and LTDL_INIT([convenience]). ++m4_defun([_LTDL_CONVENIENCE], ++[case $enable_ltdl_convenience in ++ no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; ++ "") enable_ltdl_convenience=yes ++ ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; ++esac ++LIBLTDL='_LT_BUILD_PREFIX'"${lt_ltdl_dir+$lt_ltdl_dir/}libltdlc.la" ++LTDLDEPS=$LIBLTDL ++LTDLINCL='-I${top_srcdir}'"${lt_ltdl_dir+/$lt_ltdl_dir}" ++ ++AC_SUBST([LIBLTDL]) ++AC_SUBST([LTDLDEPS]) ++AC_SUBST([LTDLINCL]) ++ ++# For backwards non-gettext consistent compatibility... ++INCLTDL="$LTDLINCL" ++AC_SUBST([INCLTDL]) ++])# _LTDL_CONVENIENCE ++ ++ ++# LTDL_INSTALLABLE ++# ---------------- ++# sets LIBLTDL to the link flags for the libltdl installable library ++# and LTDLINCL to the include flags for the libltdl header and adds ++# --enable-ltdl-install to the configure arguments. Note that ++# AC_CONFIG_SUBDIRS is not called from here. If an installed libltdl ++# is not found, LIBLTDL will be prefixed with '${top_build_prefix}' if ++# available, otherwise with '${top_builddir}/', and LTDLINCL will be ++# prefixed with '${top_srcdir}/' (note the single quotes!). If your ++# package is not flat and you're not using automake, define top_build_prefix, ++# top_builddir, and top_srcdir appropriately in your Makefiles. ++# In the future, this macro may have to be called after LT_INIT. ++AC_DEFUN([LTDL_INSTALLABLE], ++[AC_BEFORE([$0], [LTDL_INIT])dnl ++dnl Although the argument is deprecated and no longer documented, ++dnl LTDL_INSTALLABLE used to take a DIRECTORY orgument, if we have one ++dnl here make sure it is the same as any other declaration of libltdl's ++dnl location! This also ensures lt_ltdl_dir is set when configure.ac is ++dnl not yet using an explicit LT_CONFIG_LTDL_DIR. ++m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl ++_$0() ++])# LTDL_INSTALLABLE ++ ++# AC_LIBLTDL_INSTALLABLE accepted a directory argument in older libtools, ++# now we have LT_CONFIG_LTDL_DIR: ++AU_DEFUN([AC_LIBLTDL_INSTALLABLE], ++[_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) ++_LTDL_INSTALLABLE]) ++ ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LIBLTDL_INSTALLABLE], []) ++ ++ ++# _LTDL_INSTALLABLE ++# ----------------- ++# Code shared by LTDL_INSTALLABLE and LTDL_INIT([installable]). ++m4_defun([_LTDL_INSTALLABLE], ++[if test -f $prefix/lib/libltdl.la; then ++ lt_save_LDFLAGS="$LDFLAGS" ++ LDFLAGS="-L$prefix/lib $LDFLAGS" ++ AC_CHECK_LIB([ltdl], [lt_dlinit], [lt_lib_ltdl=yes]) ++ LDFLAGS="$lt_save_LDFLAGS" ++ if test x"${lt_lib_ltdl-no}" = xyes; then ++ if test x"$enable_ltdl_install" != xyes; then ++ # Don't overwrite $prefix/lib/libltdl.la without --enable-ltdl-install ++ AC_MSG_WARN([not overwriting libltdl at $prefix, force with `--enable-ltdl-install']) ++ enable_ltdl_install=no ++ fi ++ elif test x"$enable_ltdl_install" = xno; then ++ AC_MSG_WARN([libltdl not installed, but installation disabled]) ++ fi ++fi ++ ++# If configure.ac declared an installable ltdl, and the user didn't override ++# with --disable-ltdl-install, we will install the shipped libltdl. ++case $enable_ltdl_install in ++ no) ac_configure_args="$ac_configure_args --enable-ltdl-install=no" ++ LIBLTDL="-lltdl" ++ LTDLDEPS= ++ LTDLINCL= ++ ;; ++ *) enable_ltdl_install=yes ++ ac_configure_args="$ac_configure_args --enable-ltdl-install" ++ LIBLTDL='_LT_BUILD_PREFIX'"${lt_ltdl_dir+$lt_ltdl_dir/}libltdl.la" ++ LTDLDEPS=$LIBLTDL ++ LTDLINCL='-I${top_srcdir}'"${lt_ltdl_dir+/$lt_ltdl_dir}" ++ ;; ++esac ++ ++AC_SUBST([LIBLTDL]) ++AC_SUBST([LTDLDEPS]) ++AC_SUBST([LTDLINCL]) ++ ++# For backwards non-gettext consistent compatibility... ++INCLTDL="$LTDLINCL" ++AC_SUBST([INCLTDL]) ++])# LTDL_INSTALLABLE ++ ++ ++# _LTDL_MODE_DISPATCH ++# ------------------- ++m4_define([_LTDL_MODE_DISPATCH], ++[dnl If _LTDL_DIR is `.', then we are configuring libltdl itself: ++m4_if(_LTDL_DIR, [], ++ [], ++ dnl if _LTDL_MODE was not set already, the default value is `subproject': ++ [m4_case(m4_default(_LTDL_MODE, [subproject]), ++ [subproject], [AC_CONFIG_SUBDIRS(_LTDL_DIR) ++ _LT_SHELL_INIT([lt_dlopen_dir="$lt_ltdl_dir"])], ++ [nonrecursive], [_LT_SHELL_INIT([lt_dlopen_dir="$lt_ltdl_dir"; lt_libobj_prefix="$lt_ltdl_dir/"])], ++ [recursive], [], ++ [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])])dnl ++dnl Be careful not to expand twice: ++m4_define([$0], []) ++])# _LTDL_MODE_DISPATCH ++ ++ ++# _LT_LIBOBJ(MODULE_NAME) ++# ----------------------- ++# Like AC_LIBOBJ, except that MODULE_NAME goes into _LT_LIBOBJS instead ++# of into LIBOBJS. ++AC_DEFUN([_LT_LIBOBJ], [ ++ m4_pattern_allow([^_LT_LIBOBJS$]) ++ _LT_LIBOBJS="$_LT_LIBOBJS $1.$ac_objext" ++])# _LT_LIBOBJS ++ ++ ++# LTDL_INIT([OPTIONS]) ++# -------------------- ++# Clients of libltdl can use this macro to allow the installer to ++# choose between a shipped copy of the ltdl sources or a preinstalled ++# version of the library. If the shipped ltdl sources are not in a ++# subdirectory named libltdl, the directory name must be given by ++# LT_CONFIG_LTDL_DIR. ++AC_DEFUN([LTDL_INIT], ++[dnl Parse OPTIONS ++_LT_SET_OPTIONS([$0], [$1]) ++ ++dnl We need to keep our own list of libobjs separate from our parent project, ++dnl and the easiest way to do that is redefine the AC_LIBOBJs macro while ++dnl we look for our own LIBOBJs. ++m4_pushdef([AC_LIBOBJ], m4_defn([_LT_LIBOBJ])) ++m4_pushdef([AC_LIBSOURCES]) ++ ++dnl If not otherwise defined, default to the 1.5.x compatible subproject mode: ++m4_if(_LTDL_MODE, [], ++ [m4_define([_LTDL_MODE], m4_default([$2], [subproject])) ++ m4_if([-1], [m4_bregexp(_LTDL_MODE, [\(subproject\|\(non\)?recursive\)])], ++ [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])]) ++ ++AC_ARG_WITH([included_ltdl], ++ [AS_HELP_STRING([--with-included-ltdl], ++ [use the GNU ltdl sources included here])]) ++ ++if test "x$with_included_ltdl" != xyes; then ++ # We are not being forced to use the included libltdl sources, so ++ # decide whether there is a useful installed version we can use. ++ AC_CHECK_HEADER([ltdl.h], ++ [AC_CHECK_DECL([lt_dlinterface_register], ++ [AC_CHECK_LIB([ltdl], [lt_dladvise_preload], ++ [with_included_ltdl=no], ++ [with_included_ltdl=yes])], ++ [with_included_ltdl=yes], ++ [AC_INCLUDES_DEFAULT ++ #include ])], ++ [with_included_ltdl=yes], ++ [AC_INCLUDES_DEFAULT] ++ ) ++fi ++ ++dnl If neither LT_CONFIG_LTDL_DIR, LTDL_CONVENIENCE nor LTDL_INSTALLABLE ++dnl was called yet, then for old times' sake, we assume libltdl is in an ++dnl eponymous directory: ++AC_PROVIDE_IFELSE([LT_CONFIG_LTDL_DIR], [], [_LT_CONFIG_LTDL_DIR([libltdl])]) ++ ++AC_ARG_WITH([ltdl_include], ++ [AS_HELP_STRING([--with-ltdl-include=DIR], ++ [use the ltdl headers installed in DIR])]) ++ ++if test -n "$with_ltdl_include"; then ++ if test -f "$with_ltdl_include/ltdl.h"; then : ++ else ++ AC_MSG_ERROR([invalid ltdl include directory: `$with_ltdl_include']) ++ fi ++else ++ with_ltdl_include=no ++fi ++ ++AC_ARG_WITH([ltdl_lib], ++ [AS_HELP_STRING([--with-ltdl-lib=DIR], ++ [use the libltdl.la installed in DIR])]) ++ ++if test -n "$with_ltdl_lib"; then ++ if test -f "$with_ltdl_lib/libltdl.la"; then : ++ else ++ AC_MSG_ERROR([invalid ltdl library directory: `$with_ltdl_lib']) ++ fi ++else ++ with_ltdl_lib=no ++fi ++ ++case ,$with_included_ltdl,$with_ltdl_include,$with_ltdl_lib, in ++ ,yes,no,no,) ++ m4_case(m4_default(_LTDL_TYPE, [convenience]), ++ [convenience], [_LTDL_CONVENIENCE], ++ [installable], [_LTDL_INSTALLABLE], ++ [m4_fatal([unknown libltdl build type: ]_LTDL_TYPE)]) ++ ;; ++ ,no,no,no,) ++ # If the included ltdl is not to be used, then use the ++ # preinstalled libltdl we found. ++ AC_DEFINE([HAVE_LTDL], [1], ++ [Define this if a modern libltdl is already installed]) ++ LIBLTDL=-lltdl ++ LTDLDEPS= ++ LTDLINCL= ++ ;; ++ ,no*,no,*) ++ AC_MSG_ERROR([`--with-ltdl-include' and `--with-ltdl-lib' options must be used together]) ++ ;; ++ *) with_included_ltdl=no ++ LIBLTDL="-L$with_ltdl_lib -lltdl" ++ LTDLDEPS= ++ LTDLINCL="-I$with_ltdl_include" ++ ;; ++esac ++INCLTDL="$LTDLINCL" ++ ++# Report our decision... ++AC_MSG_CHECKING([where to find libltdl headers]) ++AC_MSG_RESULT([$LTDLINCL]) ++AC_MSG_CHECKING([where to find libltdl library]) ++AC_MSG_RESULT([$LIBLTDL]) ++ ++_LTDL_SETUP ++ ++dnl restore autoconf definition. ++m4_popdef([AC_LIBOBJ]) ++m4_popdef([AC_LIBSOURCES]) ++ ++AC_CONFIG_COMMANDS_PRE([ ++ _ltdl_libobjs= ++ _ltdl_ltlibobjs= ++ if test -n "$_LT_LIBOBJS"; then ++ # Remove the extension. ++ _lt_sed_drop_objext='s/\.o$//;s/\.obj$//' ++ for i in `for i in $_LT_LIBOBJS; do echo "$i"; done | sed "$_lt_sed_drop_objext" | sort -u`; do ++ _ltdl_libobjs="$_ltdl_libobjs $lt_libobj_prefix$i.$ac_objext" ++ _ltdl_ltlibobjs="$_ltdl_ltlibobjs $lt_libobj_prefix$i.lo" ++ done ++ fi ++ AC_SUBST([ltdl_LIBOBJS], [$_ltdl_libobjs]) ++ AC_SUBST([ltdl_LTLIBOBJS], [$_ltdl_ltlibobjs]) ++]) ++ ++# Only expand once: ++m4_define([LTDL_INIT]) ++])# LTDL_INIT ++ ++# Old names: ++AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)]) ++AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)]) ++AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LIB_LTDL], []) ++dnl AC_DEFUN([AC_WITH_LTDL], []) ++dnl AC_DEFUN([LT_WITH_LTDL], []) ++ ++ ++# _LTDL_SETUP ++# ----------- ++# Perform all the checks necessary for compilation of the ltdl objects ++# -- including compiler checks and header checks. This is a public ++# interface mainly for the benefit of libltdl's own configure.ac, most ++# other users should call LTDL_INIT instead. ++AC_DEFUN([_LTDL_SETUP], ++[AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([LT_SYS_MODULE_EXT])dnl ++AC_REQUIRE([LT_SYS_MODULE_PATH])dnl ++AC_REQUIRE([LT_SYS_DLSEARCH_PATH])dnl ++AC_REQUIRE([LT_LIB_DLLOAD])dnl ++AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl ++AC_REQUIRE([LT_FUNC_DLSYM_USCORE])dnl ++AC_REQUIRE([LT_SYS_DLOPEN_DEPLIBS])dnl ++AC_REQUIRE([gl_FUNC_ARGZ])dnl ++ ++m4_require([_LT_CHECK_OBJDIR])dnl ++m4_require([_LT_HEADER_DLFCN])dnl ++m4_require([_LT_CHECK_DLPREOPEN])dnl ++m4_require([_LT_DECL_SED])dnl ++ ++dnl Don't require this, or it will be expanded earlier than the code ++dnl that sets the variables it relies on: ++_LT_ENABLE_INSTALL ++ ++dnl _LTDL_MODE specific code must be called at least once: ++_LTDL_MODE_DISPATCH ++ ++# In order that ltdl.c can compile, find out the first AC_CONFIG_HEADERS ++# the user used. This is so that ltdl.h can pick up the parent projects ++# config.h file, The first file in AC_CONFIG_HEADERS must contain the ++# definitions required by ltdl.c. ++# FIXME: Remove use of undocumented AC_LIST_HEADERS (2.59 compatibility). ++AC_CONFIG_COMMANDS_PRE([dnl ++m4_pattern_allow([^LT_CONFIG_H$])dnl ++m4_ifset([AH_HEADER], ++ [LT_CONFIG_H=AH_HEADER], ++ [m4_ifset([AC_LIST_HEADERS], ++ [LT_CONFIG_H=`echo "AC_LIST_HEADERS" | $SED 's,^[[ ]]*,,;s,[[ :]].*$,,'`], ++ [])])]) ++AC_SUBST([LT_CONFIG_H]) ++ ++AC_CHECK_HEADERS([unistd.h dl.h sys/dl.h dld.h mach-o/dyld.h dirent.h], ++ [], [], [AC_INCLUDES_DEFAULT]) ++ ++AC_CHECK_FUNCS([closedir opendir readdir], [], [AC_LIBOBJ([lt__dirent])]) ++AC_CHECK_FUNCS([strlcat strlcpy], [], [AC_LIBOBJ([lt__strl])]) ++ ++AC_DEFINE_UNQUOTED([LT_LIBEXT],["$libext"],[The archive extension]) ++ ++name=ltdl ++LTDLOPEN=`eval "\\$ECHO \"$libname_spec\""` ++AC_SUBST([LTDLOPEN]) ++])# _LTDL_SETUP ++ ++ ++# _LT_ENABLE_INSTALL ++# ------------------ ++m4_define([_LT_ENABLE_INSTALL], ++[AC_ARG_ENABLE([ltdl-install], ++ [AS_HELP_STRING([--enable-ltdl-install], [install libltdl])]) ++ ++case ,${enable_ltdl_install},${enable_ltdl_convenience} in ++ *yes*) ;; ++ *) enable_ltdl_convenience=yes ;; ++esac ++ ++m4_ifdef([AM_CONDITIONAL], ++[AM_CONDITIONAL(INSTALL_LTDL, test x"${enable_ltdl_install-no}" != xno) ++ AM_CONDITIONAL(CONVENIENCE_LTDL, test x"${enable_ltdl_convenience-no}" != xno)]) ++])# _LT_ENABLE_INSTALL ++ ++ ++# LT_SYS_DLOPEN_DEPLIBS ++# --------------------- ++AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], ++[AC_REQUIRE([AC_CANONICAL_HOST])dnl ++AC_CACHE_CHECK([whether deplibs are loaded by dlopen], ++ [lt_cv_sys_dlopen_deplibs], ++ [# PORTME does your system automatically load deplibs for dlopen? ++ # or its logical equivalent (e.g. shl_load for HP-UX < 11) ++ # For now, we just catch OSes we know something about -- in the ++ # future, we'll try test this programmatically. ++ lt_cv_sys_dlopen_deplibs=unknown ++ case $host_os in ++ aix3*|aix4.1.*|aix4.2.*) ++ # Unknown whether this is true for these versions of AIX, but ++ # we want this `case' here to explicitly catch those versions. ++ lt_cv_sys_dlopen_deplibs=unknown ++ ;; ++ aix[[4-9]]*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ amigaos*) ++ case $host_cpu in ++ powerpc) ++ lt_cv_sys_dlopen_deplibs=no ++ ;; ++ esac ++ ;; ++ darwin*) ++ # Assuming the user has installed a libdl from somewhere, this is true ++ # If you are looking for one http://www.opendarwin.org/projects/dlcompat ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ freebsd* | dragonfly*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ gnu* | linux* | k*bsd*-gnu) ++ # GNU and its variants, using gnu ld.so (Glibc) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ hpux10*|hpux11*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ interix*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ irix[[12345]]*|irix6.[[01]]*) ++ # Catch all versions of IRIX before 6.2, and indicate that we don't ++ # know how it worked for any of those versions. ++ lt_cv_sys_dlopen_deplibs=unknown ++ ;; ++ irix*) ++ # The case above catches anything before 6.2, and it's known that ++ # at 6.2 and later dlopen does load deplibs. ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ netbsd*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ openbsd*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ osf[[1234]]*) ++ # dlopen did load deplibs (at least at 4.x), but until the 5.x series, ++ # it did *not* use an RPATH in a shared library to find objects the ++ # library depends on, so we explicitly say `no'. ++ lt_cv_sys_dlopen_deplibs=no ++ ;; ++ osf5.0|osf5.0a|osf5.1) ++ # dlopen *does* load deplibs and with the right loader patch applied ++ # it even uses RPATH in a shared library to search for shared objects ++ # that the library depends on, but there's no easy way to know if that ++ # patch is installed. Since this is the case, all we can really ++ # say is unknown -- it depends on the patch being installed. If ++ # it is, this changes to `yes'. Without it, it would be `no'. ++ lt_cv_sys_dlopen_deplibs=unknown ++ ;; ++ osf*) ++ # the two cases above should catch all versions of osf <= 5.1. Read ++ # the comments above for what we know about them. ++ # At > 5.1, deplibs are loaded *and* any RPATH in a shared library ++ # is used to find them so we can finally say `yes'. ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ qnx*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ solaris*) ++ lt_cv_sys_dlopen_deplibs=yes ++ ;; ++ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) ++ libltdl_cv_sys_dlopen_deplibs=yes ++ ;; ++ esac ++ ]) ++if test "$lt_cv_sys_dlopen_deplibs" != yes; then ++ AC_DEFINE([LTDL_DLOPEN_DEPLIBS], [1], ++ [Define if the OS needs help to load dependent libraries for dlopen().]) ++fi ++])# LT_SYS_DLOPEN_DEPLIBS ++ ++# Old name: ++AU_ALIAS([AC_LTDL_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], []) ++ ++ ++# LT_SYS_MODULE_EXT ++# ----------------- ++AC_DEFUN([LT_SYS_MODULE_EXT], ++[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl ++AC_CACHE_CHECK([which extension is used for runtime loadable modules], ++ [libltdl_cv_shlibext], ++[ ++module=yes ++eval libltdl_cv_shlibext=$shrext_cmds ++ ]) ++if test -n "$libltdl_cv_shlibext"; then ++ m4_pattern_allow([LT_MODULE_EXT])dnl ++ AC_DEFINE_UNQUOTED([LT_MODULE_EXT], ["$libltdl_cv_shlibext"], ++ [Define to the extension used for runtime loadable modules, say, ".so".]) ++fi ++])# LT_SYS_MODULE_EXT ++ ++# Old name: ++AU_ALIAS([AC_LTDL_SHLIBEXT], [LT_SYS_MODULE_EXT]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_SHLIBEXT], []) ++ ++ ++# LT_SYS_MODULE_PATH ++# ------------------ ++AC_DEFUN([LT_SYS_MODULE_PATH], ++[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl ++AC_CACHE_CHECK([which variable specifies run-time module search path], ++ [lt_cv_module_path_var], [lt_cv_module_path_var="$shlibpath_var"]) ++if test -n "$lt_cv_module_path_var"; then ++ m4_pattern_allow([LT_MODULE_PATH_VAR])dnl ++ AC_DEFINE_UNQUOTED([LT_MODULE_PATH_VAR], ["$lt_cv_module_path_var"], ++ [Define to the name of the environment variable that determines the run-time module search path.]) ++fi ++])# LT_SYS_MODULE_PATH ++ ++# Old name: ++AU_ALIAS([AC_LTDL_SHLIBPATH], [LT_SYS_MODULE_PATH]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_SHLIBPATH], []) ++ ++ ++# LT_SYS_DLSEARCH_PATH ++# -------------------- ++AC_DEFUN([LT_SYS_DLSEARCH_PATH], ++[m4_require([_LT_SYS_DYNAMIC_LINKER])dnl ++AC_CACHE_CHECK([for the default library search path], ++ [lt_cv_sys_dlsearch_path], ++ [lt_cv_sys_dlsearch_path="$sys_lib_dlsearch_path_spec"]) ++if test -n "$lt_cv_sys_dlsearch_path"; then ++ sys_dlsearch_path= ++ for dir in $lt_cv_sys_dlsearch_path; do ++ if test -z "$sys_dlsearch_path"; then ++ sys_dlsearch_path="$dir" ++ else ++ sys_dlsearch_path="$sys_dlsearch_path$PATH_SEPARATOR$dir" ++ fi ++ done ++ m4_pattern_allow([LT_DLSEARCH_PATH])dnl ++ AC_DEFINE_UNQUOTED([LT_DLSEARCH_PATH], ["$sys_dlsearch_path"], ++ [Define to the system default library search path.]) ++fi ++])# LT_SYS_DLSEARCH_PATH ++ ++# Old name: ++AU_ALIAS([AC_LTDL_SYSSEARCHPATH], [LT_SYS_DLSEARCH_PATH]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_SYSSEARCHPATH], []) ++ ++ ++# _LT_CHECK_DLPREOPEN ++# ------------------- ++m4_defun([_LT_CHECK_DLPREOPEN], ++[m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl ++AC_CACHE_CHECK([whether libtool supports -dlopen/-dlpreopen], ++ [libltdl_cv_preloaded_symbols], ++ [if test -n "$lt_cv_sys_global_symbol_pipe"; then ++ libltdl_cv_preloaded_symbols=yes ++ else ++ libltdl_cv_preloaded_symbols=no ++ fi ++ ]) ++if test x"$libltdl_cv_preloaded_symbols" = xyes; then ++ AC_DEFINE([HAVE_PRELOADED_SYMBOLS], [1], ++ [Define if libtool can extract symbol lists from object files.]) ++fi ++])# _LT_CHECK_DLPREOPEN ++ ++ ++# LT_LIB_DLLOAD ++# ------------- ++AC_DEFUN([LT_LIB_DLLOAD], ++[m4_pattern_allow([^LT_DLLOADERS$]) ++LT_DLLOADERS= ++AC_SUBST([LT_DLLOADERS]) ++ ++AC_LANG_PUSH([C]) ++ ++LIBADD_DLOPEN= ++AC_SEARCH_LIBS([dlopen], [dl], ++ [AC_DEFINE([HAVE_LIBDL], [1], ++ [Define if you have the libdl library or equivalent.]) ++ if test "$ac_cv_search_dlopen" != "none required" ; then ++ LIBADD_DLOPEN="-ldl" ++ fi ++ libltdl_cv_lib_dl_dlopen="yes" ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"], ++ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#if HAVE_DLFCN_H ++# include ++#endif ++ ]], [[dlopen(0, 0);]])], ++ [AC_DEFINE([HAVE_LIBDL], [1], ++ [Define if you have the libdl library or equivalent.]) ++ libltdl_cv_func_dlopen="yes" ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"], ++ [AC_CHECK_LIB([svld], [dlopen], ++ [AC_DEFINE([HAVE_LIBDL], [1], ++ [Define if you have the libdl library or equivalent.]) ++ LIBADD_DLOPEN="-lsvld" libltdl_cv_func_dlopen="yes" ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"])])]) ++if test x"$libltdl_cv_func_dlopen" = xyes || test x"$libltdl_cv_lib_dl_dlopen" = xyes ++then ++ lt_save_LIBS="$LIBS" ++ LIBS="$LIBS $LIBADD_DLOPEN" ++ AC_CHECK_FUNCS([dlerror]) ++ LIBS="$lt_save_LIBS" ++fi ++AC_SUBST([LIBADD_DLOPEN]) ++ ++LIBADD_SHL_LOAD= ++AC_CHECK_FUNC([shl_load], ++ [AC_DEFINE([HAVE_SHL_LOAD], [1], ++ [Define if you have the shl_load function.]) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"], ++ [AC_CHECK_LIB([dld], [shl_load], ++ [AC_DEFINE([HAVE_SHL_LOAD], [1], ++ [Define if you have the shl_load function.]) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la" ++ LIBADD_SHL_LOAD="-ldld"])]) ++AC_SUBST([LIBADD_SHL_LOAD]) ++ ++case $host_os in ++darwin[[1567]].*) ++# We only want this for pre-Mac OS X 10.4. ++ AC_CHECK_FUNC([_dyld_func_lookup], ++ [AC_DEFINE([HAVE_DYLD], [1], ++ [Define if you have the _dyld_func_lookup function.]) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dyld.la"]) ++ ;; ++beos*) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}load_add_on.la" ++ ;; ++cygwin* | mingw* | os2* | pw32*) ++ AC_CHECK_DECLS([cygwin_conv_path], [], [], [[#include ]]) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}loadlibrary.la" ++ ;; ++esac ++ ++AC_CHECK_LIB([dld], [dld_link], ++ [AC_DEFINE([HAVE_DLD], [1], ++ [Define if you have the GNU dld library.]) ++ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dld_link.la"]) ++AC_SUBST([LIBADD_DLD_LINK]) ++ ++m4_pattern_allow([^LT_DLPREOPEN$]) ++LT_DLPREOPEN= ++if test -n "$LT_DLLOADERS" ++then ++ for lt_loader in $LT_DLLOADERS; do ++ LT_DLPREOPEN="$LT_DLPREOPEN-dlpreopen $lt_loader " ++ done ++ AC_DEFINE([HAVE_LIBDLLOADER], [1], ++ [Define if libdlloader will be built on this platform]) ++fi ++AC_SUBST([LT_DLPREOPEN]) ++ ++dnl This isn't used anymore, but set it for backwards compatibility ++LIBADD_DL="$LIBADD_DLOPEN $LIBADD_SHL_LOAD" ++AC_SUBST([LIBADD_DL]) ++ ++AC_LANG_POP ++])# LT_LIB_DLLOAD ++ ++# Old name: ++AU_ALIAS([AC_LTDL_DLLIB], [LT_LIB_DLLOAD]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_DLLIB], []) ++ ++ ++# LT_SYS_SYMBOL_USCORE ++# -------------------- ++# does the compiler prefix global symbols with an underscore? ++AC_DEFUN([LT_SYS_SYMBOL_USCORE], ++[m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl ++AC_CACHE_CHECK([for _ prefix in compiled symbols], ++ [lt_cv_sys_symbol_underscore], ++ [lt_cv_sys_symbol_underscore=no ++ cat > conftest.$ac_ext <<_LT_EOF ++void nm_test_func(){} ++int main(){nm_test_func;return 0;} ++_LT_EOF ++ if AC_TRY_EVAL(ac_compile); then ++ # Now try to grab the symbols. ++ ac_nlist=conftest.nm ++ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) && test -s "$ac_nlist"; then ++ # See whether the symbols have a leading underscore. ++ if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then ++ lt_cv_sys_symbol_underscore=yes ++ else ++ if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then ++ : ++ else ++ echo "configure: cannot find nm_test_func in $ac_nlist" >&AS_MESSAGE_LOG_FD ++ fi ++ fi ++ else ++ echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD ++ fi ++ else ++ echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD ++ cat conftest.c >&AS_MESSAGE_LOG_FD ++ fi ++ rm -rf conftest* ++ ]) ++ sys_symbol_underscore=$lt_cv_sys_symbol_underscore ++ AC_SUBST([sys_symbol_underscore]) ++])# LT_SYS_SYMBOL_USCORE ++ ++# Old name: ++AU_ALIAS([AC_LTDL_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_SYMBOL_USCORE], []) ++ ++ ++# LT_FUNC_DLSYM_USCORE ++# -------------------- ++AC_DEFUN([LT_FUNC_DLSYM_USCORE], ++[AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl ++if test x"$lt_cv_sys_symbol_underscore" = xyes; then ++ if test x"$libltdl_cv_func_dlopen" = xyes || ++ test x"$libltdl_cv_lib_dl_dlopen" = xyes ; then ++ AC_CACHE_CHECK([whether we have to add an underscore for dlsym], ++ [libltdl_cv_need_uscore], ++ [libltdl_cv_need_uscore=unknown ++ save_LIBS="$LIBS" ++ LIBS="$LIBS $LIBADD_DLOPEN" ++ _LT_TRY_DLOPEN_SELF( ++ [libltdl_cv_need_uscore=no], [libltdl_cv_need_uscore=yes], ++ [], [libltdl_cv_need_uscore=cross]) ++ LIBS="$save_LIBS" ++ ]) ++ fi ++fi ++ ++if test x"$libltdl_cv_need_uscore" = xyes; then ++ AC_DEFINE([NEED_USCORE], [1], ++ [Define if dlsym() requires a leading underscore in symbol names.]) ++fi ++])# LT_FUNC_DLSYM_USCORE ++ ++# Old name: ++AU_ALIAS([AC_LTDL_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE]) ++dnl aclocal-1.4 backwards compatibility: ++dnl AC_DEFUN([AC_LTDL_DLSYM_USCORE], []) ++ + # Helper functions for option handling. -*- Autoconf -*- + # + # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +diff --git a/configure b/configure +index da1cb4d..e086c65 100755 +--- a/configure ++++ b/configure +@@ -795,6 +795,7 @@ FFI_DEBUG_FALSE + FFI_DEBUG_TRUE + TARGETDIR + TARGET ++sys_symbol_underscore + HAVE_LONG_DOUBLE + ALLOCA + PA64_HPUX_FALSE +@@ -4782,13 +4783,13 @@ if test "${lt_cv_nm_interface+set}" = set; then + else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext +- (eval echo "\"\$as_me:4785: $ac_compile\"" >&5) ++ (eval echo "\"\$as_me:4786: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 +- (eval echo "\"\$as_me:4788: $NM \\\"conftest.$ac_objext\\\"\"" >&5) ++ (eval echo "\"\$as_me:4789: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 +- (eval echo "\"\$as_me:4791: output\"" >&5) ++ (eval echo "\"\$as_me:4792: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" +@@ -5994,7 +5995,7 @@ ia64-*-hpux*) + ;; + *-*-irix6*) + # Find out which ABI we are using. +- echo '#line 5997 "configure"' > conftest.$ac_ext ++ echo '#line 5998 "configure"' > conftest.$ac_ext + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? +@@ -7847,11 +7848,11 @@ else + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` +- (eval echo "\"\$as_me:7850: $lt_compile\"" >&5) ++ (eval echo "\"\$as_me:7851: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 +- echo "$as_me:7854: \$? = $ac_status" >&5 ++ echo "$as_me:7855: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. +@@ -8186,11 +8187,11 @@ else + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` +- (eval echo "\"\$as_me:8189: $lt_compile\"" >&5) ++ (eval echo "\"\$as_me:8190: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 +- echo "$as_me:8193: \$? = $ac_status" >&5 ++ echo "$as_me:8194: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. +@@ -8291,11 +8292,11 @@ else + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` +- (eval echo "\"\$as_me:8294: $lt_compile\"" >&5) ++ (eval echo "\"\$as_me:8295: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 +- echo "$as_me:8298: \$? = $ac_status" >&5 ++ echo "$as_me:8299: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized +@@ -8346,11 +8347,11 @@ else + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` +- (eval echo "\"\$as_me:8349: $lt_compile\"" >&5) ++ (eval echo "\"\$as_me:8350: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 +- echo "$as_me:8353: \$? = $ac_status" >&5 ++ echo "$as_me:8354: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized +@@ -11149,7 +11150,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11152 "configure" ++#line 11153 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -11245,7 +11246,7 @@ else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +-#line 11248 "configure" ++#line 11249 "configure" + #include "confdefs.h" + + #if HAVE_DLFCN_H +@@ -14647,6 +14648,63 @@ _ACEOF + fi + fi + ++if test x$TARGET = xX86_WIN64; then ++ { $as_echo "$as_me:$LINENO: checking for _ prefix in compiled symbols" >&5 ++$as_echo_n "checking for _ prefix in compiled symbols... " >&6; } ++if test "${lt_cv_sys_symbol_underscore+set}" = set; then ++ $as_echo_n "(cached) " >&6 ++else ++ lt_cv_sys_symbol_underscore=no ++ cat > conftest.$ac_ext <<_LT_EOF ++void nm_test_func(){} ++int main(){nm_test_func;return 0;} ++_LT_EOF ++ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 ++ (eval $ac_compile) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; then ++ # Now try to grab the symbols. ++ ac_nlist=conftest.nm ++ if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist\"") >&5 ++ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && test -s "$ac_nlist"; then ++ # See whether the symbols have a leading underscore. ++ if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then ++ lt_cv_sys_symbol_underscore=yes ++ else ++ if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then ++ : ++ else ++ echo "configure: cannot find nm_test_func in $ac_nlist" >&5 ++ fi ++ fi ++ else ++ echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&5 ++ fi ++ else ++ echo "configure: failed program was:" >&5 ++ cat conftest.c >&5 ++ fi ++ rm -rf conftest* ++ ++fi ++{ $as_echo "$as_me:$LINENO: result: $lt_cv_sys_symbol_underscore" >&5 ++$as_echo "$lt_cv_sys_symbol_underscore" >&6; } ++ sys_symbol_underscore=$lt_cv_sys_symbol_underscore ++ ++ ++ if test "x$sys_symbol_underscore" = xyes; then ++ ++cat >>confdefs.h <<\_ACEOF ++#define SYMBOL_UNDERSCORE 1 ++_ACEOF ++ ++ fi ++fi ++ + case "$target" in + *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + +diff --git a/configure.ac b/configure.ac +index a94bd26..c7cead8 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -314,6 +314,13 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64 + fi + fi + ++if test x$TARGET = xX86_WIN64; then ++ LT_SYS_SYMBOL_USCORE ++ if test "x$sys_symbol_underscore" = xyes; then ++ AC_DEFINE(SYMBOL_UNDERSCORE, 1, [Define if symbols are underscored.]) ++ fi ++fi ++ + case "$target" in + *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, +diff --git a/src/x86/win64.S b/src/x86/win64.S +index 6e91818..fcdb270 100644 +--- a/src/x86/win64.S ++++ b/src/x86/win64.S +@@ -232,10 +232,18 @@ ret_void$: + ffi_call_win64 ENDP + _TEXT ENDS + END +-#else ++ ++#else ++ ++#ifdef SYMBOL_UNDERSCORE ++#define SYMBOL_NAME(name) _##name ++#else ++#define SYMBOL_NAME(name) name ++#endif ++ + .text + +-.extern _ffi_closure_win64_inner ++.extern SYMBOL_NAME(ffi_closure_win64_inner) + + # ffi_closure_win64 will be called with these registers set: + # rax points to 'closure' +@@ -246,8 +254,8 @@ END + # call ffi_closure_win64_inner for the actual work, then return the result. + # + .balign 16 +- .globl _ffi_closure_win64 +-_ffi_closure_win64: ++ .globl SYMBOL_NAME(ffi_closure_win64) ++SYMBOL_NAME(ffi_closure_win64): + # copy register arguments onto stack + test $1,%r11 + jne .Lfirst_is_float +@@ -287,7 +295,7 @@ _ffi_closure_win64: + mov %rax, %rcx # context is first parameter + mov %rsp, %rdx # stack is second parameter + add $48, %rdx # point to start of arguments +- mov $_ffi_closure_win64_inner, %rax ++ mov $SYMBOL_NAME(ffi_closure_win64_inner), %rax + callq *%rax # call the real closure function + add $40, %rsp + movq %rax, %xmm0 # If the closure returned a float, +@@ -296,8 +304,8 @@ _ffi_closure_win64: + .ffi_closure_win64_end: + + .balign 16 +- .globl _ffi_call_win64 +-_ffi_call_win64: ++ .globl SYMBOL_NAME(ffi_call_win64) ++SYMBOL_NAME(ffi_call_win64): + # copy registers onto stack + mov %r9,32(%rsp) + mov %r8,24(%rsp) diff --git a/deps/mozjs/js/src/ctypes/typedefs.h b/deps/mozjs/js/src/ctypes/typedefs.h index 7e27b8aa5f4..9391375b457 100644 --- a/deps/mozjs/js/src/ctypes/typedefs.h +++ b/deps/mozjs/js/src/ctypes/typedefs.h @@ -102,18 +102,18 @@ // The meat. DEFINE_BOOL_TYPE (bool, bool, CTYPES_FFI_BOOL) -DEFINE_INT_TYPE (int8_t, JSInt8, ffi_type_sint8) -DEFINE_INT_TYPE (int16_t, JSInt16, ffi_type_sint16) -DEFINE_INT_TYPE (int32_t, JSInt32, ffi_type_sint32) -DEFINE_INT_TYPE (uint8_t, JSUint8, ffi_type_uint8) -DEFINE_INT_TYPE (uint16_t, JSUint16, ffi_type_uint16) -DEFINE_INT_TYPE (uint32_t, JSUint32, ffi_type_uint32) +DEFINE_INT_TYPE (int8_t, int8_t, ffi_type_sint8) +DEFINE_INT_TYPE (int16_t, int16_t, ffi_type_sint16) +DEFINE_INT_TYPE (int32_t, int32_t, ffi_type_sint32) +DEFINE_INT_TYPE (uint8_t, uint8_t, ffi_type_uint8) +DEFINE_INT_TYPE (uint16_t, uint16_t, ffi_type_uint16) +DEFINE_INT_TYPE (uint32_t, uint32_t, ffi_type_uint32) DEFINE_INT_TYPE (short, short, ffi_type_sint16) DEFINE_INT_TYPE (unsigned_short, unsigned short, ffi_type_uint16) DEFINE_INT_TYPE (int, int, ffi_type_sint32) DEFINE_INT_TYPE (unsigned_int, unsigned int, ffi_type_uint32) -DEFINE_WRAPPED_INT_TYPE(int64_t, JSInt64, ffi_type_sint64) -DEFINE_WRAPPED_INT_TYPE(uint64_t, JSUint64, ffi_type_uint64) +DEFINE_WRAPPED_INT_TYPE(int64_t, int64_t, ffi_type_sint64) +DEFINE_WRAPPED_INT_TYPE(uint64_t, uint64_t, ffi_type_uint64) DEFINE_WRAPPED_INT_TYPE(long, long, CTYPES_FFI_LONG) DEFINE_WRAPPED_INT_TYPE(unsigned_long, unsigned long, CTYPES_FFI_ULONG) DEFINE_WRAPPED_INT_TYPE(long_long, long long, ffi_type_sint64) diff --git a/deps/mozjs/js/src/ds/BitArray.h b/deps/mozjs/js/src/ds/BitArray.h new file mode 100644 index 00000000000..91165f919d1 --- /dev/null +++ b/deps/mozjs/js/src/ds/BitArray.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Terrence Cole + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef BitArray_h__ +#define BitArray_h__ + +#include "jstypes.h" + +#include "js/TemplateLib.h" + +namespace js { + +template +class BitArray { + private: + uintptr_t map[nbits / JS_BITS_PER_WORD + (nbits % JS_BITS_PER_WORD == 0 ? 0 : 1)]; + + public: + void clear(bool value) { + if (value) + memset(map, 0xFF, sizeof(map)); + else + memset(map, 0, sizeof(map)); + } + + inline bool get(size_t offset) const { + uintptr_t index, mask; + getMarkWordAndMask(offset, &index, &mask); + return map[index] & mask; + } + + inline void set(size_t offset) { + uintptr_t index, mask; + getMarkWordAndMask(offset, &index, &mask); + map[index] |= mask; + } + + inline void unset(size_t offset) { + uintptr_t index, mask; + getMarkWordAndMask(offset, &index, &mask); + map[index] &= ~mask; + } + + private: + inline void getMarkWordAndMask(size_t offset, + uintptr_t *indexp, uintptr_t *maskp) const { + *indexp = offset >> tl::FloorLog2::result; + *maskp = uintptr_t(1) << (offset & (JS_BITS_PER_WORD - 1)); + } +}; + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/ds/InlineMap.h b/deps/mozjs/js/src/ds/InlineMap.h new file mode 100644 index 00000000000..54b4810d613 --- /dev/null +++ b/deps/mozjs/js/src/ds/InlineMap.h @@ -0,0 +1,410 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef InlineMap_h__ +#define InlineMap_h__ + +#include "js/HashTable.h" + +namespace js { + +/* + * A type can only be used as an InlineMap key if zero is an invalid key value + * (and thus may be used as a tombstone value by InlineMap). + */ +template struct ZeroIsReserved { static const bool result = false; }; +template struct ZeroIsReserved { static const bool result = true; }; + +template +class InlineMap +{ + public: + typedef HashMap, TempAllocPolicy> WordMap; + + struct InlineElem + { + K key; + V value; + }; + + private: + typedef typename WordMap::Ptr WordMapPtr; + typedef typename WordMap::AddPtr WordMapAddPtr; + typedef typename WordMap::Range WordMapRange; + + size_t inlNext; + size_t inlCount; + InlineElem inl[InlineElems]; + WordMap map; + + void checkStaticInvariants() { + JS_STATIC_ASSERT(ZeroIsReserved::result); + } + + bool usingMap() const { + return inlNext > InlineElems; + } + + bool switchToMap() { + JS_ASSERT(inlNext == InlineElems); + + if (map.initialized()) { + map.clear(); + } else { + if (!map.init(count())) + return false; + JS_ASSERT(map.initialized()); + } + + for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) { + if (it->key && !map.putNew(it->key, it->value)) + return false; + } + + inlNext = InlineElems + 1; + JS_ASSERT(map.count() == inlCount); + JS_ASSERT(usingMap()); + return true; + } + + JS_NEVER_INLINE + bool switchAndAdd(const K &key, const V &value) { + if (!switchToMap()) + return false; + + return map.putNew(key, value); + } + + public: + explicit InlineMap(JSContext *cx) + : inlNext(0), inlCount(0), map(cx) { + checkStaticInvariants(); /* Force the template to instantiate the static invariants. */ + } + + class Entry + { + friend class InlineMap; + const K &key_; + const V &value_; + + Entry(const K &key, const V &value) : key_(key), value_(value) {} + + public: + const K &key() { return key_; } + const V &value() { return value_; } + }; /* class Entry */ + + class Ptr + { + friend class InlineMap; + + WordMapPtr mapPtr; + InlineElem *inlPtr; + bool isInlinePtr; + + typedef Ptr ******* ConvertibleToBool; + + explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {} + explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {} + void operator==(const Ptr &other); + + public: + /* Leaves Ptr uninitialized. */ + Ptr() { +#ifdef DEBUG + inlPtr = (InlineElem *) 0xbad; + isInlinePtr = true; +#endif + } + + /* Default copy constructor works for this structure. */ + + bool found() const { + return isInlinePtr ? bool(inlPtr) : mapPtr.found(); + } + + operator ConvertibleToBool() const { + return ConvertibleToBool(found()); + } + + K &key() { + JS_ASSERT(found()); + return isInlinePtr ? inlPtr->key : mapPtr->key; + } + + V &value() { + JS_ASSERT(found()); + return isInlinePtr ? inlPtr->value : mapPtr->value; + } + }; /* class Ptr */ + + class AddPtr + { + friend class InlineMap; + + WordMapAddPtr mapAddPtr; + InlineElem *inlAddPtr; + bool isInlinePtr; + /* Indicates whether inlAddPtr is a found result or an add pointer. */ + bool inlPtrFound; + + AddPtr(InlineElem *ptr, bool found) + : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found) + {} + + AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {} + + void operator==(const AddPtr &other); + + typedef AddPtr ******* ConvertibleToBool; + + public: + AddPtr() {} + + bool found() const { + return isInlinePtr ? inlPtrFound : mapAddPtr.found(); + } + + operator ConvertibleToBool() const { + return found() ? ConvertibleToBool(1) : ConvertibleToBool(0); + } + + V &value() { + JS_ASSERT(found()); + if (isInlinePtr) + return inlAddPtr->value; + return mapAddPtr->value; + } + }; /* class AddPtr */ + + size_t count() { + return usingMap() ? map.count() : inlCount; + } + + bool empty() const { + return usingMap() ? map.empty() : !inlCount; + } + + void clear() { + inlNext = 0; + inlCount = 0; + } + + bool isMap() const { + return usingMap(); + } + + const WordMap &asMap() const { + JS_ASSERT(isMap()); + return map; + } + + const InlineElem *asInline() const { + JS_ASSERT(!isMap()); + return inl; + } + + const InlineElem *inlineEnd() const { + JS_ASSERT(!isMap()); + return inl + inlNext; + } + + JS_ALWAYS_INLINE + Ptr lookup(const K &key) { + if (usingMap()) + return Ptr(map.lookup(key)); + + for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) { + if (it->key == key) + return Ptr(it); + } + + return Ptr(NULL); + } + + JS_ALWAYS_INLINE + AddPtr lookupForAdd(const K &key) { + if (usingMap()) + return AddPtr(map.lookupForAdd(key)); + + for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) { + if (it->key == key) + return AddPtr(it, true); + } + + /* + * The add pointer that's returned here may indicate the limit entry of + * the linear space, in which case the |add| operation will initialize + * the map if necessary and add the entry there. + */ + return AddPtr(inl + inlNext, false); + } + + JS_ALWAYS_INLINE + bool add(AddPtr &p, const K &key, const V &value) { + JS_ASSERT(!p); + + if (p.isInlinePtr) { + InlineElem *addPtr = p.inlAddPtr; + JS_ASSERT(addPtr == inl + inlNext); + + /* Switching to map mode before we add this pointer. */ + if (addPtr == inl + InlineElems) + return switchAndAdd(key, value); + + JS_ASSERT(!p.found()); + JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr)); + p.inlAddPtr->key = key; + p.inlAddPtr->value = value; + ++inlCount; + ++inlNext; + return true; + } + + return map.add(p.mapAddPtr, key, value); + } + + JS_ALWAYS_INLINE + bool put(const K &key, const V &value) { + AddPtr p = lookupForAdd(key); + if (p) { + p.value() = value; + return true; + } + return add(p, key, value); + } + + void remove(Ptr p) { + JS_ASSERT(p); + if (p.isInlinePtr) { + JS_ASSERT(inlCount > 0); + JS_ASSERT(p.inlPtr->key != NULL); + p.inlPtr->key = NULL; + --inlCount; + return; + } + JS_ASSERT(map.initialized() && usingMap()); + map.remove(p.mapPtr); + } + + void remove(const K &key) { + if (Ptr p = lookup(key)) + remove(p); + } + + class Range + { + friend class InlineMap; + + WordMapRange mapRange; + InlineElem *cur; + InlineElem *end; + bool isInline; + + explicit Range(WordMapRange r) + : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */ + isInline(false) { + mapRange = r; + JS_ASSERT(!isInlineRange()); + } + + Range(const InlineElem *begin, const InlineElem *end_) + : cur(const_cast(begin)), + end(const_cast(end_)), + isInline(true) { + advancePastNulls(cur); + JS_ASSERT(isInlineRange()); + } + + bool checkInlineRangeInvariants() const { + JS_ASSERT(uintptr_t(cur) <= uintptr_t(end)); + JS_ASSERT_IF(cur != end, cur->key != NULL); + return true; + } + + bool isInlineRange() const { + JS_ASSERT_IF(isInline, checkInlineRangeInvariants()); + return isInline; + } + + void advancePastNulls(InlineElem *begin) { + InlineElem *newCur = begin; + while (newCur < end && NULL == newCur->key) + ++newCur; + JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end)); + cur = newCur; + } + + void bumpCurPtr() { + JS_ASSERT(isInlineRange()); + advancePastNulls(cur + 1); + } + + void operator==(const Range &other); + + public: + bool empty() const { + return isInlineRange() ? cur == end : mapRange.empty(); + } + + Entry front() { + JS_ASSERT(!empty()); + if (isInlineRange()) + return Entry(cur->key, cur->value); + return Entry(mapRange.front().key, mapRange.front().value); + } + + void popFront() { + JS_ASSERT(!empty()); + if (isInlineRange()) + bumpCurPtr(); + else + mapRange.popFront(); + } + }; /* class Range */ + + Range all() const { + return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext); + } +}; /* class InlineMap */ + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/ds/LifoAlloc.cpp b/deps/mozjs/js/src/ds/LifoAlloc.cpp new file mode 100644 index 00000000000..a6f43df0b77 --- /dev/null +++ b/deps/mozjs/js/src/ds/LifoAlloc.cpp @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "LifoAlloc.h" + +#include + +using namespace js; + +namespace js { +namespace detail { + +BumpChunk * +BumpChunk::new_(size_t chunkSize) +{ + JS_ASSERT(RoundUpPow2(chunkSize) == chunkSize); + void *mem = js_malloc(chunkSize); + if (!mem) + return NULL; + BumpChunk *result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk)); + + /* + * We assume that the alignment of sAlign is less than that of + * the underlying memory allocator -- creating a new BumpChunk should + * always satisfy the sAlign alignment constraint. + */ + JS_ASSERT(AlignPtr(result->bump) == result->bump); + return result; +} + +void +BumpChunk::delete_(BumpChunk *chunk) +{ +#ifdef DEBUG + memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize); +#endif + js_free(chunk); +} + +bool +BumpChunk::canAlloc(size_t n) +{ + char *aligned = AlignPtr(bump); + char *bumped = aligned + n; + return bumped <= limit && bumped > headerBase(); +} + +} /* namespace detail */ +} /* namespace js */ + +void +LifoAlloc::freeAll() +{ + while (first) { + BumpChunk *victim = first; + first = first->next(); + BumpChunk::delete_(victim); + } + first = latest = NULL; +} + +void +LifoAlloc::freeUnused() +{ + /* Don't free anything if we have outstanding marks. */ + if (markCount || !first) + return; + + JS_ASSERT(first && latest); + + /* Rewind through any unused chunks. */ + if (!latest->used()) { + BumpChunk *lastUsed = NULL; + for (BumpChunk *it = first; it != latest; it = it->next()) { + if (it->used()) + lastUsed = it; + } + if (!lastUsed) { + freeAll(); + return; + } + latest = lastUsed; + } + + /* Free all chunks after |latest|. */ + BumpChunk *it = latest->next(); + while (it) { + BumpChunk *victim = it; + it = it->next(); + BumpChunk::delete_(victim); + } + + latest->setNext(NULL); +} + +LifoAlloc::BumpChunk * +LifoAlloc::getOrCreateChunk(size_t n) +{ + if (first) { + /* Look for existing, unused BumpChunks to satisfy the request. */ + while (latest->next()) { + latest = latest->next(); + latest->resetBump(); /* This was an unused BumpChunk on the chain. */ + if (latest->canAlloc(n)) + return latest; + } + } + + size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk); + size_t chunkSize; + if (n > defaultChunkFreeSpace) { + size_t allocSizeWithHeader = n + sizeof(BumpChunk); + + /* Guard for overflow. */ + if (allocSizeWithHeader < n || + (allocSizeWithHeader & (size_t(1) << (tl::BitSize::result - 1)))) { + return NULL; + } + + chunkSize = RoundUpPow2(allocSizeWithHeader); + } else { + chunkSize = defaultChunkSize_; + } + + /* If we get here, we couldn't find an existing BumpChunk to fill the request. */ + BumpChunk *newChunk = BumpChunk::new_(chunkSize); + if (!newChunk) + return NULL; + if (!first) { + latest = first = newChunk; + } else { + JS_ASSERT(latest && !latest->next()); + latest->setNext(newChunk); + latest = newChunk; + } + return newChunk; +} diff --git a/deps/mozjs/js/src/ds/LifoAlloc.h b/deps/mozjs/js/src/ds/LifoAlloc.h new file mode 100644 index 00000000000..4d269fba483 --- /dev/null +++ b/deps/mozjs/js/src/ds/LifoAlloc.h @@ -0,0 +1,354 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef LifoAlloc_h__ +#define LifoAlloc_h__ + +#include "mozilla/Attributes.h" + +/* + * This data structure supports stacky LIFO allocation (mark/release and + * LifoAllocScope). It does not maintain one contiguous segment; instead, it + * maintains a bunch of linked memory segments. In order to prevent malloc/free + * thrashing, unused segments are deallocated when garbage collection occurs. + */ + +#include "jsutil.h" + +#include "js/TemplateLib.h" + +namespace js { + +namespace detail { + +static const size_t LIFO_ALLOC_ALIGN = 8; + +JS_ALWAYS_INLINE +char * +AlignPtr(void *orig) +{ + typedef tl::StaticAssert< + tl::FloorLog2::result == tl::CeilingLog2::result + >::result _; + + char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1)); + JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0); + return result; +} + +/* Header for a chunk of memory wrangled by the LifoAlloc. */ +class BumpChunk +{ + char *bump; /* start of the available data */ + char *limit; /* end of the data */ + BumpChunk *next_; /* the next BumpChunk */ + size_t bumpSpaceSize; /* size of the data area */ + + char *headerBase() { return reinterpret_cast(this); } + char *bumpBase() const { return limit - bumpSpaceSize; } + + BumpChunk *thisDuringConstruction() { return this; } + + explicit BumpChunk(size_t bumpSpaceSize) + : bump(reinterpret_cast(thisDuringConstruction()) + sizeof(BumpChunk)), + limit(bump + bumpSpaceSize), + next_(NULL), bumpSpaceSize(bumpSpaceSize) + { + JS_ASSERT(bump == AlignPtr(bump)); + } + + void setBump(void *ptr) { + JS_ASSERT(bumpBase() <= ptr); + JS_ASSERT(ptr <= limit); + DebugOnly prevBump = bump; + bump = static_cast(ptr); +#ifdef DEBUG + JS_ASSERT(contains(prevBump)); + + /* Clobber the now-free space. */ + if (prevBump > bump) + memset(bump, 0xcd, prevBump - bump); +#endif + } + + public: + BumpChunk *next() const { return next_; } + void setNext(BumpChunk *succ) { next_ = succ; } + + size_t used() const { return bump - bumpBase(); } + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) { + return mallocSizeOf(this); + } + + void resetBump() { + setBump(headerBase() + sizeof(BumpChunk)); + } + + void *mark() const { return bump; } + + void release(void *mark) { + JS_ASSERT(contains(mark)); + JS_ASSERT(mark <= bump); + setBump(mark); + } + + bool contains(void *mark) const { + return bumpBase() <= mark && mark <= limit; + } + + bool canAlloc(size_t n); + + /* Try to perform an allocation of size |n|, return null if not possible. */ + JS_ALWAYS_INLINE + void *tryAlloc(size_t n) { + char *aligned = AlignPtr(bump); + char *newBump = aligned + n; + + if (newBump > limit) + return NULL; + + /* Check for overflow. */ + if (JS_UNLIKELY(newBump < bump)) + return NULL; + + JS_ASSERT(canAlloc(n)); /* Ensure consistency between "can" and "try". */ + setBump(newBump); + return aligned; + } + + void *allocInfallible(size_t n) { + void *result = tryAlloc(n); + JS_ASSERT(result); + return result; + } + + static BumpChunk *new_(size_t chunkSize); + static void delete_(BumpChunk *chunk); +}; + +} /* namespace detail */ + +/* + * LIFO bump allocator: used for phase-oriented and fast LIFO allocations. + * + * Note: |latest| is not necessary "last". We leave BumpChunks latent in the + * chain after they've been released to avoid thrashing before a GC. + */ +class LifoAlloc +{ + typedef detail::BumpChunk BumpChunk; + + BumpChunk *first; + BumpChunk *latest; + size_t markCount; + size_t defaultChunkSize_; + + void operator=(const LifoAlloc &) MOZ_DELETE; + LifoAlloc(const LifoAlloc &) MOZ_DELETE; + + /* + * Return a BumpChunk that can perform an allocation of at least size |n| + * and add it to the chain appropriately. + * + * Side effect: if retval is non-null, |first| and |latest| are initialized + * appropriately. + */ + BumpChunk *getOrCreateChunk(size_t n); + + void reset(size_t defaultChunkSize) { + JS_ASSERT(RoundUpPow2(defaultChunkSize) == defaultChunkSize); + first = latest = NULL; + defaultChunkSize_ = defaultChunkSize; + markCount = 0; + } + + public: + explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); } + + /* Steal allocated chunks from |other|. */ + void steal(LifoAlloc *other) { + JS_ASSERT(!other->markCount); + PodCopy((char *) this, (char *) other, sizeof(*this)); + other->reset(defaultChunkSize_); + } + + ~LifoAlloc() { freeAll(); } + + size_t defaultChunkSize() const { return defaultChunkSize_; } + + /* Frees all held memory. */ + void freeAll(); + + /* Should be called on GC in order to release any held chunks. */ + void freeUnused(); + + JS_ALWAYS_INLINE + void *alloc(size_t n) { + void *result; + if (latest && (result = latest->tryAlloc(n))) + return result; + + if (!getOrCreateChunk(n)) + return NULL; + + return latest->allocInfallible(n); + } + + template + T *newArray(size_t count) { + void *mem = alloc(sizeof(T) * count); + if (!mem) + return NULL; + JS_STATIC_ASSERT(tl::IsPodType::result); + return (T *) mem; + } + + /* + * Create an array with uninitialized elements of type |T|. + * The caller is responsible for initialization. + */ + template + T *newArrayUninitialized(size_t count) { + return static_cast(alloc(sizeof(T) * count)); + } + + void *mark() { + markCount++; + + return latest ? latest->mark() : NULL; + } + + void release(void *mark) { + markCount--; + + if (!mark) { + latest = first; + if (latest) + latest->resetBump(); + return; + } + + /* + * Find the chunk that contains |mark|, and make sure we don't pass + * |latest| along the way -- we should be making the chain of active + * chunks shorter, not longer! + */ + BumpChunk *container = first; + while (true) { + if (container->contains(mark)) + break; + JS_ASSERT(container != latest); + container = container->next(); + } + latest = container; + latest->release(mark); + } + + /* Get the total "used" (occupied bytes) count for the arena chunks. */ + size_t used() const { + size_t accum = 0; + BumpChunk *it = first; + while (it) { + accum += it->used(); + it = it->next(); + } + return accum; + } + + /* Get the total size of the arena chunks (including unused space). */ + size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const { + size_t accum = 0; + BumpChunk *it = first; + while (it) { + accum += it->sizeOfIncludingThis(mallocSizeOf); + it = it->next(); + } + return accum; + } + + /* Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself. */ + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); + } + + /* Doesn't perform construction; useful for lazily-initialized POD types. */ + template + JS_ALWAYS_INLINE + T *newPod() { + return static_cast(alloc(sizeof(T))); + } + + JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE) +}; + +class LifoAllocScope +{ + LifoAlloc *lifoAlloc; + void *mark; + bool shouldRelease; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + + public: + explicit LifoAllocScope(LifoAlloc *lifoAlloc + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : lifoAlloc(lifoAlloc), shouldRelease(true) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + mark = lifoAlloc->mark(); + } + + ~LifoAllocScope() { + if (shouldRelease) + lifoAlloc->release(mark); + } + + LifoAlloc &alloc() { + return *lifoAlloc; + } + + void releaseEarly() { + JS_ASSERT(shouldRelease); + lifoAlloc->release(mark); + shouldRelease = false; + } +}; + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/ds/Sort.h b/deps/mozjs/js/src/ds/Sort.h new file mode 100644 index 00000000000..2e25716a7a8 --- /dev/null +++ b/deps/mozjs/js/src/ds/Sort.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef Sort_h__ +#define Sort_h__ + +#include "jstypes.h" + +namespace js { + +namespace detail { + +template +JS_ALWAYS_INLINE void +CopyNonEmptyArray(T *dst, const T *src, size_t nelems) +{ + JS_ASSERT(nelems != 0); + const T *end = src + nelems; + do { + *dst++ = *src++; + } while (src != end); +} + +/* Helper function for MergeSort. */ +template +JS_ALWAYS_INLINE bool +MergeArrayRuns(T *dst, const T *src, size_t run1, size_t run2, Comparator c) +{ + JS_ASSERT(run1 >= 1); + JS_ASSERT(run2 >= 1); + + /* Copy runs already in sorted order. */ + const T *b = src + run1; + bool lessOrEqual; + if (!c(b[-1], b[0], &lessOrEqual)) + return false; + + if (!lessOrEqual) { + /* Runs are not already sorted, merge them. */ + for (const T *a = src;;) { + if (!c(*a, *b, &lessOrEqual)) + return false; + if (lessOrEqual) { + *dst++ = *a++; + if (!--run1) { + src = b; + break; + } + } else { + *dst++ = *b++; + if (!--run2) { + src = a; + break; + } + } + } + } + CopyNonEmptyArray(dst, src, run1 + run2); + return true; +} + +} /* namespace detail */ + +/* + * Sort the array using the merge sort algorithm. The scratch should point to + * a temporary storage that can hold nelems elements. + * + * The comparator must provide the () operator with the following signature: + * + * bool operator()(const T& a, const T& a, bool *lessOrEqualp); + * + * It should return true on success and set *lessOrEqualp to the result of + * a <= b operation. If it returns false, the sort terminates immediately with + * the false result. In this case the content of the array and scratch is + * arbitrary. + */ +template +bool +MergeSort(T *array, size_t nelems, T *scratch, Comparator c) +{ + const size_t INS_SORT_LIMIT = 3; + + if (nelems <= 1) + return true; + + /* + * Apply insertion sort to small chunks to reduce the number of merge + * passes needed. + */ + for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) { + size_t hi = lo + INS_SORT_LIMIT; + if (hi >= nelems) + hi = nelems; + for (size_t i = lo + 1; i != hi; i++) { + for (size_t j = i; ;) { + bool lessOrEqual; + if (!c(array[j - 1], array[j], &lessOrEqual)) + return false; + if (lessOrEqual) + break; + T tmp = array[j - 1]; + array[j - 1] = array[j]; + array[j] = tmp; + if (--j == lo) + break; + } + } + } + + T *vec1 = array; + T *vec2 = scratch; + for (size_t run = INS_SORT_LIMIT; run < nelems; run *= 2) { + for (size_t lo = 0; lo < nelems; lo += 2 * run) { + size_t hi = lo + run; + if (hi >= nelems) { + detail::CopyNonEmptyArray(vec2 + lo, vec1 + lo, nelems - lo); + break; + } + size_t run2 = (run <= nelems - hi) ? run : nelems - hi; + if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c)) + return false; + } + T *swap = vec1; + vec1 = vec2; + vec2 = swap; + } + if (vec1 == scratch) + detail::CopyNonEmptyArray(array, scratch, nelems); + return true; +} + +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/editline/editline.c b/deps/mozjs/js/src/editline/editline.c index cc3a71881db..92af9f91865 100644 --- a/deps/mozjs/js/src/editline/editline.c +++ b/deps/mozjs/js/src/editline/editline.c @@ -1002,6 +1002,7 @@ void rl_reset_terminal(p) char *p; { + (void)p; } void diff --git a/deps/mozjs/js/src/editline/editline.h b/deps/mozjs/js/src/editline/editline.h index 41f8f3d3337..237802fc90f 100644 --- a/deps/mozjs/js/src/editline/editline.h +++ b/deps/mozjs/js/src/editline/editline.h @@ -115,7 +115,7 @@ extern unsigned rl_kill; extern unsigned rl_quit; extern char *rl_complete(); extern int rl_list_possib(); -extern void rl_ttyset(); +extern void rl_ttyset(int); extern void rl_add_slash(); #if !defined(HAVE_STDLIB) diff --git a/deps/mozjs/js/src/find-child.py b/deps/mozjs/js/src/find-child.py deleted file mode 100644 index 49807674462..00000000000 --- a/deps/mozjs/js/src/find-child.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python -# -# The output of this script is a splicemap. -# -# A splicemap is used by the mercurial 'convert' extension to direct its -# splicing operation. -# -# The script assumes you already have a destination repository -# containing a conversion somewhere in its history, but that you've -# possibly mixed some new commits on top of that conversion. It outputs -# a splicemap that picks up the conversion where it left off (with the -# first descendant, in the source repo, of the src rev given by --start, -# if or rather the first descendant that would be included by the -# conversion's filemap) and connects them to the current tip of the -# destination repo. -# - -from mercurial import ui, hg -from hgext.convert.filemap import filemapper -from optparse import OptionParser - -import sys - -parser = OptionParser() - -parser.add_option("-s", "--src", dest="src", - help="source repository", metavar="REPO") - -parser.add_option("-d", "--dst", dest="dst", - help="destination repository", metavar="REPO") - -parser.add_option("-t", "--start", dest="start", - help="starting revid in source repository", metavar="REV") - -parser.add_option("-f", "--filemap", dest="filemap", - help="filemap used in conversion", metavar="PATH") - -(options, args) = parser.parse_args() - -if not (options.src and options.dst and options.start): - parser.print_help() - exit(1) - -u = ui.ui() - -src_repo = hg.repository(u, options.src) -dst_repo = hg.repository(u, options.dst) - -fm = None -if options.filemap: - fm = filemapper(u, options.filemap) - -last_converted_src = src_repo[options.start] - -dst_tip = dst_repo.changectx(dst_repo.changelog.tip()).hex() -revs = last_converted_src.children() - -while len(revs) != 0: - tmp = revs - revs = [] - for child in tmp: - for f in child.files(): - if (not fm) or fm(f): - u.write("%s %s\n" % (child.hex(), dst_tip)) - exit(0); - revs.extend(child.children()) - -sys.stderr.write("No candidate child found in source repository\n") -exit(1) diff --git a/deps/mozjs/js/src/frontend/BytecodeCompiler.cpp b/deps/mozjs/js/src/frontend/BytecodeCompiler.cpp new file mode 100644 index 00000000000..83510cfc46c --- /dev/null +++ b/deps/mozjs/js/src/frontend/BytecodeCompiler.cpp @@ -0,0 +1,425 @@ +/* -*- 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 ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "frontend/BytecodeCompiler.h" + +#include "jsprobes.h" + +#include "frontend/BytecodeEmitter.h" +#include "frontend/FoldConstants.h" +#include "frontend/SemanticAnalysis.h" +#include "vm/GlobalObject.h" + +#include "jsinferinlines.h" + +using namespace js; +using namespace js::frontend; + +bool +DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script) +{ + JSObject *globalObj = globalScope.globalObj; + + /* Define and update global properties. */ + for (size_t i = 0; i < globalScope.defs.length(); i++) { + GlobalScope::GlobalDef &def = globalScope.defs[i]; + + /* Names that could be resolved ahead of time can be skipped. */ + if (!def.atom) + continue; + + jsid id = ATOM_TO_JSID(def.atom); + Value rval; + + if (def.funbox) { + JSFunction *fun = def.funbox->function(); + + /* + * No need to check for redeclarations or anything, global + * optimizations only take place if the property is not defined. + */ + rval.setObject(*fun); + types::AddTypePropertyId(cx, globalObj, id, rval); + } else { + rval.setUndefined(); + } + + /* + * Don't update the type information when defining the property for the + * global object, per the consistency rules for type properties. If the + * property is only undefined before it is ever written, we can check + * the global directly during compilation and avoid having to emit type + * checks every time it is accessed in the script. + */ + const Shape *shape = + DefineNativeProperty(cx, globalObj, id, rval, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE); + if (!shape) + return false; + def.knownSlot = shape->slot(); + } + + Vector worklist(cx); + if (!worklist.append(script)) + return false; + + /* + * Recursively walk through all scripts we just compiled. For each script, + * go through all global uses. Each global use indexes into globalScope->defs. + * Use this information to repoint each use to the correct slot in the global + * object. + */ + while (worklist.length()) { + JSScript *outer = worklist.back(); + worklist.popBack(); + + if (JSScript::isValidOffset(outer->objectsOffset)) { + JSObjectArray *arr = outer->objects(); + + /* + * If this is an eval script, don't treat the saved caller function + * stored in the first object slot as an inner function. + */ + size_t start = outer->savedCallerFun ? 1 : 0; + + for (size_t i = start; i < arr->length; i++) { + JSObject *obj = arr->vector[i]; + if (!obj->isFunction()) + continue; + JSFunction *fun = obj->toFunction(); + JS_ASSERT(fun->isInterpreted()); + JSScript *inner = fun->script(); + if (outer->function() && outer->function()->isHeavyweight()) { + outer->isOuterFunction = true; + inner->isInnerFunction = true; + } + if (!JSScript::isValidOffset(inner->globalsOffset) && + !JSScript::isValidOffset(inner->objectsOffset)) { + continue; + } + if (!worklist.append(inner)) + return false; + } + } + + if (!JSScript::isValidOffset(outer->globalsOffset)) + continue; + + GlobalSlotArray *globalUses = outer->globals(); + uint32_t nGlobalUses = globalUses->length; + for (uint32_t i = 0; i < nGlobalUses; i++) { + uint32_t index = globalUses->vector[i].slot; + JS_ASSERT(index < globalScope.defs.length()); + globalUses->vector[i].slot = globalScope.defs[index].knownSlot; + } + } + + return true; +} + +JSScript * +frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, + JSPrincipals *principals, JSPrincipals *originPrincipals, + uint32_t tcflags, + const jschar *chars, size_t length, + const char *filename, uintN lineno, JSVersion version, + JSString *source /* = NULL */, + uintN staticLevel /* = 0 */) +{ + TokenKind tt; + ParseNode *pn; + JSScript *script; + bool inDirectivePrologue; + + JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_COMPILE_FOR_EVAL + | TCF_NEED_SCRIPT_GLOBAL))); + + /* + * The scripted callerFrame can only be given for compile-and-go scripts + * and non-zero static level requires callerFrame. + */ + JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); + JS_ASSERT_IF(staticLevel != 0, callerFrame); + + Parser parser(cx, principals, originPrincipals, callerFrame); + if (!parser.init(chars, length, filename, lineno, version)) + return NULL; + + TokenStream &tokenStream = parser.tokenStream; + + BytecodeEmitter bce(&parser, tokenStream.getLineno()); + if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT)) + return NULL; + + Probes::compileScriptBegin(cx, filename, lineno); + MUST_FLOW_THROUGH("out"); + + // We can specialize a bit for the given scope chain if that scope chain is the global object. + JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() + ? &scopeChain->global() + : NULL; + + JS_ASSERT_IF(globalObj, globalObj->isNative()); + JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); + + /* Null script early in case of error, to reduce our code footprint. */ + script = NULL; + + GlobalScope globalScope(cx, globalObj, &bce); + bce.flags |= tcflags; + bce.setScopeChain(scopeChain); + bce.globalScope = &globalScope; + if (!SetStaticLevel(&bce, staticLevel)) + goto out; + + /* If this is a direct call to eval, inherit the caller's strictness. */ + if (callerFrame && + callerFrame->isScriptFrame() && + callerFrame->script()->strictModeCode) { + bce.flags |= TCF_STRICT_MODE_CODE; + tokenStream.setStrictMode(); + } + +#ifdef DEBUG + bool savedCallerFun; + savedCallerFun = false; +#endif + if (tcflags & TCF_COMPILE_N_GO) { + if (source) { + /* + * Save eval program source in script->atoms[0] for the + * eval cache (see EvalCacheLookup in jsobj.cpp). + */ + JSAtom *atom = js_AtomizeString(cx, source); + jsatomid _; + if (!atom || !bce.makeAtomIndex(atom, &_)) + goto out; + } + + if (callerFrame && callerFrame->isFunctionFrame()) { + /* + * An eval script in a caller frame needs to have its enclosing + * function captured in case it refers to an upvar, and someone + * wishes to decompile it while it's running. + */ + ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); + if (!funbox) + goto out; + funbox->emitLink = bce.objectList.lastbox; + bce.objectList.lastbox = funbox; + bce.objectList.length++; +#ifdef DEBUG + savedCallerFun = true; +#endif + } + } + + /* + * Inline this->statements to emit as we go to save AST space. We must + * generate our script-body blockid since we aren't calling Statements. + */ + uint32_t bodyid; + if (!GenerateBlockId(&bce, bodyid)) + goto out; + bce.bodyid = bodyid; + +#if JS_HAS_XML_SUPPORT + pn = NULL; + bool onlyXML; + onlyXML = true; +#endif + + inDirectivePrologue = true; + tokenStream.setOctalCharacterEscape(false); + for (;;) { + tt = tokenStream.peekToken(TSF_OPERAND); + if (tt <= TOK_EOF) { + if (tt == TOK_EOF) + break; + JS_ASSERT(tt == TOK_ERROR); + goto out; + } + + pn = parser.statement(); + if (!pn) + goto out; + JS_ASSERT(!bce.blockNode); + + if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) + goto out; + + if (!FoldConstants(cx, pn, &bce)) + goto out; + + if (!AnalyzeFunctions(&bce)) + goto out; + bce.functionList = NULL; + + if (!EmitTree(cx, &bce, pn)) + goto out; + +#if JS_HAS_XML_SUPPORT + if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) + onlyXML = false; +#endif + bce.freeTree(pn); + } + +#if JS_HAS_XML_SUPPORT + /* + * Prevent XML data theft via + * + * It does not cope with malformed comment hiding hacks where --> is hidden + * by C-style comments, or on a dirty line. Such cases are already broken. + */ + TSF_IN_HTML_COMMENT = 0x2000 +}; + +class TokenStream +{ + /* Unicode separators that are treated as line terminators, in addition to \n, \r */ + enum { + LINE_SEPARATOR = 0x2028, + PARA_SEPARATOR = 0x2029 + }; + + static const size_t ntokens = 4; /* 1 current + 2 lookahead, rounded + to power of 2 to avoid divmod by 3 */ + static const uintN ntokensMask = ntokens - 1; + + public: + typedef Vector CharBuffer; + + /* + * To construct a TokenStream, first call the constructor, which is + * infallible, then call |init|, which can fail. To destroy a TokenStream, + * first call |close| then call the destructor. If |init| fails, do not call + * |close|. + * + * This class uses JSContext.tempLifoAlloc to allocate internal buffers. The + * caller should JS_ARENA_MARK before calling |init| and JS_ARENA_RELEASE + * after calling |close|. + */ + TokenStream(JSContext *, JSPrincipals *principals, JSPrincipals *originPrincipals); + + /* + * Create a new token stream from an input buffer. + * Return false on memory-allocation failure. + */ + bool init(const jschar *base, size_t length, const char *filename, uintN lineno, + JSVersion version); + ~TokenStream(); + + /* Accessors. */ + JSContext *getContext() const { return cx; } + bool onCurrentLine(const TokenPos &pos) const { return lineno == pos.end.lineno; } + const Token ¤tToken() const { return tokens[cursor]; } + bool isCurrentTokenType(TokenKind type) const { + return currentToken().type == type; + } + bool isCurrentTokenType(TokenKind type1, TokenKind type2) const { + TokenKind type = currentToken().type; + return type == type1 || type == type2; + } + const CharBuffer &getTokenbuf() const { return tokenbuf; } + const char *getFilename() const { return filename; } + uintN getLineno() const { return lineno; } + /* Note that the version and hasXML can get out of sync via setXML. */ + JSVersion versionNumber() const { return VersionNumber(version); } + JSVersion versionWithFlags() const { return version; } + bool hasXML() const { return xml || VersionShouldParseXML(versionNumber()); } + void setXML(bool enabled) { xml = enabled; } + + bool isCurrentTokenEquality() const { + return TokenKindIsEquality(currentToken().type); + } + + bool isCurrentTokenRelational() const { + return TokenKindIsRelational(currentToken().type); + } + + bool isCurrentTokenShift() const { + return TokenKindIsShift(currentToken().type); + } + + bool isCurrentTokenAssignment() const { + return TokenKindIsAssignment(currentToken().type); + } + + /* Flag methods. */ + void setStrictMode(bool enabled = true) { setFlag(enabled, TSF_STRICT_MODE_CODE); } + void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); } + void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); } + void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); } + void setOctalCharacterEscape(bool enabled = true) { setFlag(enabled, TSF_OCTAL_CHAR); } + + bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); } + bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); } + bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); } + bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); } + bool isEOF() const { return !!(flags & TSF_EOF); } + bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; } + + bool reportCompileErrorNumberVA(ParseNode *pn, uintN flags, uintN errorNumber, va_list ap); + + private: + static JSAtom *atomize(JSContext *cx, CharBuffer &cb); + bool putIdentInTokenbuf(const jschar *identStart); + + /* + * Enables flags in the associated tokenstream for the object lifetime. + * Useful for lexically-scoped flag toggles. + */ + class Flagger { + TokenStream * const parent; + uintN flags; + public: + Flagger(TokenStream *parent, uintN withFlags) : parent(parent), flags(withFlags) { + parent->flags |= flags; + } + + ~Flagger() { parent->flags &= ~flags; } + }; + friend class Flagger; + + void setFlag(bool enabled, TokenStreamFlags flag) { + if (enabled) + flags |= flag; + else + flags &= ~flag; + } + + public: + /* + * Get the next token from the stream, make it the current token, and + * return its kind. + */ + TokenKind getToken() { + /* Check for a pushed-back token resulting from mismatching lookahead. */ + if (lookahead != 0) { + JS_ASSERT(!(flags & TSF_XMLTEXTMODE)); + lookahead--; + cursor = (cursor + 1) & ntokensMask; + TokenKind tt = currentToken().type; + JS_ASSERT(tt != TOK_EOL); + return tt; + } + + return getTokenInternal(); + } + + /* Similar, but also sets flags. */ + TokenKind getToken(uintN withFlags) { + Flagger flagger(this, withFlags); + return getToken(); + } + + /* + * Push the last scanned token back into the stream. + */ + void ungetToken() { + JS_ASSERT(lookahead < ntokensMask); + lookahead++; + cursor = (cursor - 1) & ntokensMask; + } + + TokenKind peekToken() { + if (lookahead != 0) { + JS_ASSERT(lookahead == 1); + return tokens[(cursor + lookahead) & ntokensMask].type; + } + TokenKind tt = getTokenInternal(); + ungetToken(); + return tt; + } + + TokenKind peekToken(uintN withFlags) { + Flagger flagger(this, withFlags); + return peekToken(); + } + + TokenKind peekTokenSameLine(uintN withFlags = 0) { + if (!onCurrentLine(currentToken().pos)) + return TOK_EOL; + + if (lookahead != 0) { + JS_ASSERT(lookahead == 1); + return tokens[(cursor + lookahead) & ntokensMask].type; + } + + /* + * This is the only place TOK_EOL is produced. No token with TOK_EOL + * is created, just a TOK_EOL TokenKind is returned. + */ + flags &= ~TSF_EOL; + TokenKind tt = getToken(withFlags); + if (flags & TSF_EOL) { + tt = TOK_EOL; + flags &= ~TSF_EOL; + } + ungetToken(); + return tt; + } + + /* + * Get the next token from the stream if its kind is |tt|. + */ + bool matchToken(TokenKind tt) { + if (getToken() == tt) + return true; + ungetToken(); + return false; + } + + bool matchToken(TokenKind tt, uintN withFlags) { + Flagger flagger(this, withFlags); + return matchToken(tt); + } + + void consumeKnownToken(TokenKind tt) { + JS_ALWAYS_TRUE(matchToken(tt)); + } + + /* + * Give up responsibility for managing the sourceMap filename's memory. + */ + const jschar *releaseSourceMap() { + const jschar* sm = sourceMap; + sourceMap = NULL; + return sm; + } + + /* + * If the name at s[0:length] is not a keyword in this version, return + * true with *ttp and *topp unchanged. + * + * If it is a reserved word in this version and strictness mode, and thus + * can't be present in correct code, report a SyntaxError and return false. + * + * If it is a keyword, like "if", the behavior depends on ttp/topp. If ttp + * and topp are null, report a SyntaxError ("if is a reserved identifier") + * and return false. If ttp and topp are non-null, return true with the + * keyword's TokenKind in *ttp and its JSOp in *topp. + * + * ttp and topp must be either both null or both non-null. + */ + bool checkForKeyword(const jschar *s, size_t length, TokenKind *ttp, JSOp *topp); + + private: + /* + * This is the low-level interface to the JS source code buffer. It just + * gets raw chars, basically. TokenStreams functions are layered on top + * and do some extra stuff like converting all EOL sequences to '\n', + * tracking the line number, and setting the TSF_EOF flag. (The "raw" in + * "raw chars" refers to the lack of EOL sequence normalization.) + */ + class TokenBuf { + public: + TokenBuf() : base(NULL), limit(NULL), ptr(NULL) { } + + void init(const jschar *buf, size_t length) { + base = ptr = buf; + limit = base + length; + } + + bool hasRawChars() const { + return ptr < limit; + } + + bool atStart() const { + return ptr == base; + } + + jschar getRawChar() { + return *ptr++; /* this will NULL-crash if poisoned */ + } + + jschar peekRawChar() const { + return *ptr; /* this will NULL-crash if poisoned */ + } + + bool matchRawChar(jschar c) { + if (*ptr == c) { /* this will NULL-crash if poisoned */ + ptr++; + return true; + } + return false; + } + + bool matchRawCharBackwards(jschar c) { + JS_ASSERT(ptr); /* make sure haven't been poisoned */ + if (*(ptr - 1) == c) { + ptr--; + return true; + } + return false; + } + + void ungetRawChar() { + JS_ASSERT(ptr); /* make sure haven't been poisoned */ + ptr--; + } + + const jschar *addressOfNextRawChar() { + JS_ASSERT(ptr); /* make sure haven't been poisoned */ + return ptr; + } + + /* Use this with caution! */ + void setAddressOfNextRawChar(const jschar *a) { + JS_ASSERT(a); + ptr = a; + } + +#ifdef DEBUG + /* + * Poison the TokenBuf so it cannot be accessed again. There's one + * exception to this rule -- see findEOL() -- which is why + * ptrWhenPoisoned exists. + */ + void poison() { + ptrWhenPoisoned = ptr; + ptr = NULL; + } +#endif + + static bool isRawEOLChar(int32_t c) { + return (c == '\n' || c == '\r' || c == LINE_SEPARATOR || c == PARA_SEPARATOR); + } + + const jschar *findEOL(); + + private: + const jschar *base; /* base of buffer */ + const jschar *limit; /* limit for quick bounds check */ + const jschar *ptr; /* next char to get */ + const jschar *ptrWhenPoisoned; /* |ptr| when poison() was called */ + }; + + TokenKind getTokenInternal(); /* doesn't check for pushback or error flag. */ + + int32_t getChar(); + int32_t getCharIgnoreEOL(); + void ungetChar(int32_t c); + void ungetCharIgnoreEOL(int32_t c); + Token *newToken(ptrdiff_t adjust); + bool peekUnicodeEscape(int32_t *c); + bool matchUnicodeEscapeIdStart(int32_t *c); + bool matchUnicodeEscapeIdent(int32_t *c); + bool peekChars(intN n, jschar *cp); + bool getAtLine(); + bool getAtSourceMappingURL(); + + bool getXMLEntity(); + bool getXMLTextOrTag(TokenKind *ttp, Token **tpp); + bool getXMLMarkup(TokenKind *ttp, Token **tpp); + + bool matchChar(int32_t expect) { + int32_t c = getChar(); + if (c == expect) + return true; + ungetChar(c); + return false; + } + + void consumeKnownChar(int32_t expect) { + mozilla::DebugOnly c = getChar(); + JS_ASSERT(c == expect); + } + + int32_t peekChar() { + int32_t c = getChar(); + ungetChar(c); + return c; + } + + void skipChars(intN n) { + while (--n >= 0) + getChar(); + } + + void updateLineInfoForEOL(); + void updateFlagsForEOL(); + + Token tokens[ntokens];/* circular token buffer */ + uintN cursor; /* index of last parsed token */ + uintN lookahead; /* count of lookahead tokens */ + uintN lineno; /* current line number */ + uintN flags; /* flags -- see above */ + const jschar *linebase; /* start of current line; points into userbuf */ + const jschar *prevLinebase; /* start of previous line; NULL if on the first line */ + TokenBuf userbuf; /* user input buffer */ + const char *filename; /* input filename or null */ + jschar *sourceMap; /* source map's filename or null */ + void *listenerTSData;/* listener data for this TokenStream */ + CharBuffer tokenbuf; /* current token string buffer */ + int8_t oneCharTokens[128]; /* table of one-char tokens */ + JSPackedBool maybeEOL[256]; /* probabilistic EOL lookup table */ + JSPackedBool maybeStrSpecial[256];/* speeds up string scanning */ + JSVersion version; /* (i.e. to identify keywords) */ + bool xml; /* see JSOPTION_XML */ + JSContext *const cx; + JSPrincipals *const originPrincipals; +}; + +struct KeywordInfo { + const char *chars; /* C string with keyword text */ + TokenKind tokentype; + JSOp op; /* JSOp */ + JSVersion version; /* JSVersion */ +}; + +/* + * Returns a KeywordInfo for the specified characters, or NULL if the string is + * not a keyword. + */ +const KeywordInfo * +FindKeyword(const jschar *s, size_t length); + +/* + * Check that str forms a valid JS identifier name. The function does not + * check if str is a JS keyword. + */ +JSBool +IsIdentifier(JSLinearString *str); + +/* + * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error + * message have const jschar* type, not const char*. + */ +#define JSREPORT_UC 0x100 + +/* + * Report a compile-time error by its number. Return true for a warning, false + * for an error. When pn is not null, use it to report error's location. + * Otherwise use ts, which must not be null. + */ +bool +ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, ParseNode *pn, uintN flags, + uintN errorNumber, ...); + +/* + * Report a condition that should elicit a warning with JSOPTION_STRICT, + * or an error if ts or tc is handling strict mode code. This function + * defers to ReportCompileErrorNumber to do the real work. Either tc + * or ts may be NULL, if there is no tree context or token stream state + * whose strictness should affect the report. + * + * One could have ReportCompileErrorNumber recognize the + * JSREPORT_STRICT_MODE_ERROR flag instead of having a separate function + * like this one. However, the strict mode code flag we need to test is + * in the TreeContext structure for that code; we would have to change + * the ~120 ReportCompileErrorNumber calls to pass the additional + * argument, even though many of those sites would never use it. Using + * ts's TSF_STRICT_MODE_CODE flag instead of tc's would be brittle: at some + * points ts's flags don't correspond to those of the tc relevant to the + * error. + */ +bool +ReportStrictModeError(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode *pn, + uintN errorNumber, ...); + +} /* namespace js */ + +extern JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file); + +#ifdef DEBUG +extern const char * +TokenKindToString(js::TokenKind tt); +#endif + +#endif /* TokenStream_h__ */ diff --git a/deps/mozjs/js/src/gc/Barrier-inl.h b/deps/mozjs/js/src/gc/Barrier-inl.h new file mode 100644 index 00000000000..b7310137494 --- /dev/null +++ b/deps/mozjs/js/src/gc/Barrier-inl.h @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey global object code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsgcmark.h" + +#include "gc/Barrier.h" + +#ifndef jsgc_barrier_inl_h___ +#define jsgc_barrier_inl_h___ + +namespace js { + +static JS_ALWAYS_INLINE void +ClearValueRange(JSCompartment *comp, HeapValue *vec, uintN len, bool useHoles) +{ + if (useHoles) { + for (uintN i = 0; i < len; i++) + vec[i].set(comp, MagicValue(JS_ARRAY_HOLE)); + } else { + for (uintN i = 0; i < len; i++) + vec[i].set(comp, UndefinedValue()); + } +} + +static JS_ALWAYS_INLINE void +InitValueRange(HeapValue *vec, uintN len, bool useHoles) +{ + if (useHoles) { + for (uintN i = 0; i < len; i++) + vec[i].init(MagicValue(JS_ARRAY_HOLE)); + } else { + for (uintN i = 0; i < len; i++) + vec[i].init(UndefinedValue()); + } +} + +static JS_ALWAYS_INLINE void +DestroyValueRange(HeapValue *vec, uintN len) +{ + for (uintN i = 0; i < len; i++) + vec[i].~HeapValue(); +} + +inline +HeapValue::HeapValue(const Value &v) + : value(v) +{ + JS_ASSERT(!IsPoisonedValue(v)); + post(); +} + +inline +HeapValue::HeapValue(const HeapValue &v) + : value(v.value) +{ + JS_ASSERT(!IsPoisonedValue(v.value)); + post(); +} + +inline +HeapValue::~HeapValue() +{ + pre(); +} + +inline void +HeapValue::init(const Value &v) +{ + JS_ASSERT(!IsPoisonedValue(v)); + value = v; + post(); +} + +inline void +HeapValue::init(JSCompartment *comp, const Value &v) +{ + value = v; + post(comp); +} + +inline void +HeapValue::writeBarrierPre(const Value &value) +{ +#ifdef JSGC_INCREMENTAL + if (value.isMarkable()) { + js::gc::Cell *cell = (js::gc::Cell *)value.toGCThing(); + writeBarrierPre(cell->compartment(), value); + } +#endif +} + +inline void +HeapValue::writeBarrierPost(const Value &value, void *addr) +{ +} + +inline void +HeapValue::writeBarrierPre(JSCompartment *comp, const Value &value) +{ +#ifdef JSGC_INCREMENTAL + if (comp->needsBarrier()) + js::gc::MarkValueUnbarriered(comp->barrierTracer(), value, "write barrier"); +#endif +} + +inline void +HeapValue::writeBarrierPost(JSCompartment *comp, const Value &value, void *addr) +{ +} + +inline void +HeapValue::pre() +{ + writeBarrierPre(value); +} + +inline void +HeapValue::post() +{ +} + +inline void +HeapValue::pre(JSCompartment *comp) +{ + writeBarrierPre(comp, value); +} + +inline void +HeapValue::post(JSCompartment *comp) +{ +} + +inline HeapValue & +HeapValue::operator=(const Value &v) +{ + pre(); + JS_ASSERT(!IsPoisonedValue(v)); + value = v; + post(); + return *this; +} + +inline HeapValue & +HeapValue::operator=(const HeapValue &v) +{ + pre(); + JS_ASSERT(!IsPoisonedValue(v.value)); + value = v.value; + post(); + return *this; +} + +inline void +HeapValue::set(JSCompartment *comp, const Value &v) +{ +#ifdef DEBUG + if (value.isMarkable()) { + js::gc::Cell *cell = (js::gc::Cell *)value.toGCThing(); + JS_ASSERT(cell->compartment() == comp || + cell->compartment() == comp->rt->atomsCompartment); + } +#endif + + pre(comp); + JS_ASSERT(!IsPoisonedValue(v)); + value = v; + post(comp); +} + +inline +HeapId::HeapId(jsid id) + : value(id) +{ + JS_ASSERT(!IsPoisonedId(id)); + post(); +} + +inline +HeapId::~HeapId() +{ + pre(); +} + +inline void +HeapId::init(jsid id) +{ + JS_ASSERT(!IsPoisonedId(id)); + value = id; + post(); +} + +inline void +HeapId::pre() +{ +#ifdef JSGC_INCREMENTAL + if (JS_UNLIKELY(JSID_IS_OBJECT(value))) { + JSObject *obj = JSID_TO_OBJECT(value); + JSCompartment *comp = obj->compartment(); + if (comp->needsBarrier()) + js::gc::MarkObjectUnbarriered(comp->barrierTracer(), obj, "write barrier"); + } +#endif +} + +inline void +HeapId::post() +{ +} + +inline HeapId & +HeapId::operator=(jsid id) +{ + pre(); + JS_ASSERT(!IsPoisonedId(id)); + value = id; + post(); + return *this; +} + +inline HeapId & +HeapId::operator=(const HeapId &v) +{ + pre(); + JS_ASSERT(!IsPoisonedId(v.value)); + value = v.value; + post(); + return *this; +} + +} /* namespace js */ + +#endif /* jsgc_barrier_inl_h___ */ diff --git a/deps/mozjs/js/src/gc/Barrier.h b/deps/mozjs/js/src/gc/Barrier.h new file mode 100644 index 00000000000..78d5fa03f56 --- /dev/null +++ b/deps/mozjs/js/src/gc/Barrier.h @@ -0,0 +1,460 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey global object code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsgc_barrier_h___ +#define jsgc_barrier_h___ + +#include "jsapi.h" +#include "jscell.h" + +#include "js/HashTable.h" + +/* + * A write barrier is a mechanism used by incremental or generation GCs to + * ensure that every value that needs to be marked is marked. In general, the + * write barrier should be invoked whenever a write can cause the set of things + * traced through by the GC to change. This includes: + * - writes to object properties + * - writes to array slots + * - writes to fields like JSObject::shape_ that we trace through + * - writes to fields in private data, like JSGenerator::obj + * - writes to non-markable fields like JSObject::private that point to + * markable data + * The last category is the trickiest. Even though the private pointers does not + * point to a GC thing, changing the private pointer may change the set of + * objects that are traced by the GC. Therefore it needs a write barrier. + * + * Every barriered write should have the following form: + * + * obj->field = value; // do the actual write + * + * The pre-barrier is used for incremental GC and the post-barrier is for + * generational GC. + * + * PRE-BARRIER + * + * To understand the pre-barrier, let's consider how incremental GC works. The + * GC itself is divided into "slices". Between each slice, JS code is allowed to + * run. Each slice should be short so that the user doesn't notice the + * interruptions. In our GC, the structure of the slices is as follows: + * + * 1. ... JS work, which leads to a request to do GC ... + * 2. [first GC slice, which performs all root marking and possibly more marking] + * 3. ... more JS work is allowed to run ... + * 4. [GC mark slice, which runs entirely in drainMarkStack] + * 5. ... more JS work ... + * 6. [GC mark slice, which runs entirely in drainMarkStack] + * 7. ... more JS work ... + * 8. [GC marking finishes; sweeping done non-incrementally; GC is done] + * 9. ... JS continues uninterrupted now that GC is finishes ... + * + * Of course, there may be a different number of slices depending on how much + * marking is to be done. + * + * The danger inherent in this scheme is that the JS code in steps 3, 5, and 7 + * might change the heap in a way that causes the GC to collect an object that + * is actually reachable. The write barrier prevents this from happening. We use + * a variant of incremental GC called "snapshot at the beginning." This approach + * guarantees the invariant that if an object is reachable in step 2, then we + * will mark it eventually. The name comes from the idea that we take a + * theoretical "snapshot" of all reachable objects in step 2; all objects in + * that snapshot should eventually be marked. (Note that the write barrier + * verifier code takes an actual snapshot.) + * + * The basic correctness invariant of a snapshot-at-the-beginning collector is + * that any object reachable at the end of the GC (step 9) must either: + * (1) have been reachable at the beginning (step 2) and thus in the snapshot + * (2) or must have been newly allocated, in steps 3, 5, or 7. + * To deal with case (2), any objects allocated during an incremental GC are + * automatically marked black. + * + * This strategy is actually somewhat conservative: if an object becomes + * unreachable between steps 2 and 8, it would be safe to collect it. We won't, + * mainly for simplicity. (Also, note that the snapshot is entirely + * theoretical. We don't actually do anything special in step 2 that we wouldn't + * do in a non-incremental GC. + * + * It's the pre-barrier's job to maintain the snapshot invariant. Consider the + * write "obj->field = value". Let the prior value of obj->field be + * value0. Since it's possible that value0 may have been what obj->field + * contained in step 2, when the snapshot was taken, the barrier marks + * value0. Note that it only does this if we're in the middle of an incremental + * GC. Since this is rare, the cost of the write barrier is usually just an + * extra branch. + * + * In practice, we implement the pre-barrier differently based on the type of + * value0. E.g., see JSObject::writeBarrierPre, which is used if obj->field is + * a JSObject*. It takes value0 as a parameter. + * + * POST-BARRIER + * + * These are not yet implemented. Once we get generational GC, they will allow + * us to keep track of pointers from non-nursery space into the nursery. + * + * IMPLEMENTATION DETAILS + * + * Since it would be awkward to change every write to memory into a function + * call, this file contains a bunch of C++ classes and templates that use + * operator overloading to take care of barriers automatically. In many cases, + * all that's necessary to make some field be barriered is to replace + * Type *field; + * with + * HeapPtr field; + * There are also special classes HeapValue and HeapId, which barrier js::Value + * and jsid, respectively. + * + * One additional note: not all object writes need to be barriered. Writes to + * newly allocated objects do not need a barrier as long as the GC is not + * allowed to run in between the allocation and the write. In these cases, we + * use the "obj->field.init(value)" method instead of "obj->field = value". + * We use the init naming idiom in many places to signify that a field is being + * assigned for the first time, and that no GCs have taken place between the + * object allocation and the assignment. + */ + +namespace js { + +/* + * Ideally, we would like to make the argument to functions like MarkShape be a + * HeapPtr. That would ensure that we don't forget to + * barrier any fields that we mark through. However, that would prohibit us from + * passing in a derived class like HeapPtr. + * + * To overcome the problem, we make the argument to MarkShape be a + * MarkablePtr. And we allow conversions from HeapPtr + * to MarkablePtr as long as T can be converted to U. + */ +template +class MarkablePtr +{ + public: + T *value; + + explicit MarkablePtr(T *value) : value(value) {} +}; + +template +class HeapPtr +{ + union { + T *value; + Unioned other; + }; + + public: + HeapPtr() : value(NULL) {} + explicit HeapPtr(T *v) : value(v) { post(); } + explicit HeapPtr(const HeapPtr &v) : value(v.value) { post(); } + + ~HeapPtr() { pre(); } + + /* Use this to install a ptr into a newly allocated object. */ + void init(T *v) { + JS_ASSERT(!IsPoisonedPtr(v)); + value = v; + post(); + } + + /* Use to set the pointer to NULL. */ + void clear() { + pre(); + value = NULL; + } + + /* Use this if the automatic coercion to T* isn't working. */ + T *get() const { return value; } + + /* + * Use these if you want to change the value without invoking the barrier. + * Obviously this is dangerous unless you know the barrier is not needed. + */ + T **unsafeGet() { return &value; } + void unsafeSet(T *v) { value = v; } + + Unioned *unsafeGetUnioned() { return &other; } + + HeapPtr &operator=(T *v) { + pre(); + JS_ASSERT(!IsPoisonedPtr(v)); + value = v; + post(); + return *this; + } + + HeapPtr &operator=(const HeapPtr &v) { + pre(); + JS_ASSERT(!IsPoisonedPtr(v.value)); + value = v.value; + post(); + return *this; + } + + T &operator*() const { return *value; } + T *operator->() const { return value; } + + operator T*() const { return value; } + + /* + * This coerces to MarkablePtr as long as T can coerce to U. See the + * comment for MarkablePtr above. + */ + template + operator MarkablePtr() const { return MarkablePtr(value); } + + private: + void pre() { T::writeBarrierPre(value); } + void post() { T::writeBarrierPost(value, (void *)&value); } + + /* Make this friend so it can access pre() and post(). */ + template + friend inline void + BarrieredSetPair(JSCompartment *comp, + HeapPtr &v1, T1 *val1, + HeapPtr &v2, T2 *val2); +}; + +/* + * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two + * barriers with only one branch to check if we're in an incremental GC. + */ +template +static inline void +BarrieredSetPair(JSCompartment *comp, + HeapPtr &v1, T1 *val1, + HeapPtr &v2, T2 *val2) +{ + if (T1::needWriteBarrierPre(comp)) { + v1.pre(); + v2.pre(); + } + v1.unsafeSet(val1); + v2.unsafeSet(val2); + v1.post(); + v2.post(); +} + +typedef HeapPtr HeapPtrObject; +typedef HeapPtr HeapPtrFunction; +typedef HeapPtr HeapPtrString; +typedef HeapPtr HeapPtrScript; +typedef HeapPtr HeapPtrShape; +typedef HeapPtr HeapPtrBaseShape; +typedef HeapPtr HeapPtrTypeObject; +typedef HeapPtr HeapPtrXML; + +/* Useful for hashtables with a HeapPtr as key. */ +template +struct HeapPtrHasher +{ + typedef HeapPtr Key; + typedef T *Lookup; + + static HashNumber hash(Lookup obj) { return DefaultHasher::hash(obj); } + static bool match(const Key &k, Lookup l) { return k.get() == l; } +}; + +/* Specialized hashing policy for HeapPtrs. */ +template +struct DefaultHasher< HeapPtr >: HeapPtrHasher { }; + +class HeapValue +{ + Value value; + + public: + explicit HeapValue() : value(UndefinedValue()) {} + explicit inline HeapValue(const Value &v); + explicit inline HeapValue(const HeapValue &v); + + inline ~HeapValue(); + + inline void init(const Value &v); + inline void init(JSCompartment *comp, const Value &v); + + inline HeapValue &operator=(const Value &v); + inline HeapValue &operator=(const HeapValue &v); + + /* + * This is a faster version of operator=. Normally, operator= has to + * determine the compartment of the value before it can decide whether to do + * the barrier. If you already know the compartment, it's faster to pass it + * in. + */ + inline void set(JSCompartment *comp, const Value &v); + + const Value &get() const { return value; } + operator const Value &() const { return value; } + + bool isUndefined() const { return value.isUndefined(); } + bool isNull() const { return value.isNull(); } + bool isBoolean() const { return value.isBoolean(); } + bool isTrue() const { return value.isTrue(); } + bool isFalse() const { return value.isFalse(); } + bool isNumber() const { return value.isNumber(); } + bool isInt32() const { return value.isInt32(); } + bool isString() const { return value.isString(); } + bool isObject() const { return value.isObject(); } + bool isMagic(JSWhyMagic why) const { return value.isMagic(why); } + bool isGCThing() const { return value.isGCThing(); } + bool isMarkable() const { return value.isMarkable(); } + + bool toBoolean() const { return value.toBoolean(); } + double toNumber() const { return value.toNumber(); } + int32_t toInt32() const { return value.toInt32(); } + double toDouble() const { return value.toDouble(); } + JSString *toString() const { return value.toString(); } + JSObject &toObject() const { return value.toObject(); } + JSObject *toObjectOrNull() const { return value.toObjectOrNull(); } + void *toGCThing() const { return value.toGCThing(); } + + JSGCTraceKind gcKind() const { return value.gcKind(); } + + uint64_t asRawBits() const { return value.asRawBits(); } + +#ifdef DEBUG + JSWhyMagic whyMagic() const { return value.whyMagic(); } +#endif + + static inline void writeBarrierPre(const Value &v); + static inline void writeBarrierPost(const Value &v, void *addr); + + static inline void writeBarrierPre(JSCompartment *comp, const Value &v); + static inline void writeBarrierPost(JSCompartment *comp, const Value &v, void *addr); + + private: + inline void pre(); + inline void post(); + + inline void pre(JSCompartment *comp); + inline void post(JSCompartment *comp); +}; + +static inline const Value * +Valueify(const HeapValue *array) +{ + JS_ASSERT(sizeof(HeapValue) == sizeof(Value)); + return (const Value *)array; +} + +class HeapValueArray +{ + HeapValue *array; + + public: + HeapValueArray(HeapValue *array) : array(array) {} + + operator const Value *() const { return Valueify(array); } + operator HeapValue *() const { return array; } + + HeapValueArray operator +(int offset) const { return HeapValueArray(array + offset); } + HeapValueArray operator +(uint32_t offset) const { return HeapValueArray(array + offset); } +}; + +class HeapId +{ + jsid value; + + public: + explicit HeapId() : value(JSID_VOID) {} + explicit inline HeapId(jsid id); + + inline ~HeapId(); + + inline void init(jsid id); + + inline HeapId &operator=(jsid id); + inline HeapId &operator=(const HeapId &v); + + bool operator==(jsid id) const { return value == id; } + bool operator!=(jsid id) const { return value != id; } + + jsid get() const { return value; } + operator jsid() const { return value; } + + private: + inline void pre(); + inline void post(); + + HeapId(const HeapId &v); +}; + +/* + * Incremental GC requires that weak pointers have read barriers. This is mostly + * an issue for empty shapes stored in JSCompartment. The problem happens when, + * during an incremental GC, some JS code stores one of the compartment's empty + * shapes into an object already marked black. Normally, this would not be a + * problem, because the empty shape would have been part of the initial snapshot + * when the GC started. However, since this is a weak pointer, it isn't. So we + * may collect the empty shape even though a live object points to it. To fix + * this, we mark these empty shapes black whenever they get read out. + */ +template +class ReadBarriered +{ + T *value; + + public: + ReadBarriered() : value(NULL) {} + ReadBarriered(T *value) : value(value) {} + + T *get() const { + if (!value) + return NULL; + T::readBarrier(value); + return value; + } + + operator T*() const { return get(); } + + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + + T *unsafeGet() { return value; } + + void set(T *v) { value = v; } + + operator bool() { return !!value; } + + template + operator MarkablePtr() const { return MarkablePtr(value); } +}; + +} + +#endif /* jsgc_barrier_h___ */ diff --git a/deps/mozjs/js/src/gc/Memory.cpp b/deps/mozjs/js/src/gc/Memory.cpp new file mode 100644 index 00000000000..835320037ce --- /dev/null +++ b/deps/mozjs/js/src/gc/Memory.cpp @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jstypes.h" + +#include "js/Utility.h" +#include "gc/Memory.h" + +namespace js { +namespace gc { + +#if defined(XP_WIN) +#include "jswin.h" + +static size_t AllocationGranularity = 0; + +void +InitMemorySubsystem() +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + JS_OPT_ASSERT(sysinfo.dwPageSize == PageSize); + AllocationGranularity = sysinfo.dwAllocationGranularity; +} + +void * +MapAlignedPages(size_t size, size_t alignment) +{ + JS_ASSERT(size >= alignment); + JS_ASSERT(size % alignment == 0); + JS_ASSERT(size % PageSize == 0); + JS_ASSERT(alignment % AllocationGranularity == 0); + + /* Special case: If we want allocation alignment, no further work is needed. */ + if (alignment == AllocationGranularity) { + return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + } + + /* + * Windows requires that there be a 1:1 mapping between VM allocation + * and deallocation operations. Therefore, take care here to acquire the + * final result via one mapping operation. This means unmapping any + * preliminary result that is not correctly aligned. + */ + void *p = NULL; + while (!p) { + /* + * Over-allocate in order to map a memory region that is + * definitely large enough then deallocate and allocate again the + * correct sizee, within the over-sized mapping. + * + * Since we're going to unmap the whole thing anyway, the first + * mapping doesn't have to commit pages. + */ + p = VirtualAlloc(NULL, size * 2, MEM_RESERVE, PAGE_READWRITE); + if (!p) + return NULL; + void *chunkStart = (void *)(uintptr_t(p) + (alignment - (uintptr_t(p) % alignment))); + UnmapPages(p, size * 2); + p = VirtualAlloc(chunkStart, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + + /* Failure here indicates a race with another thread, so try again. */ + } + + JS_ASSERT(uintptr_t(p) % alignment == 0); + return p; +} + +void +UnmapPages(void *p, size_t size) +{ + JS_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE)); +} + +bool +MarkPagesUnused(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + LPVOID p2 = VirtualAlloc(p, size, MEM_RESET, PAGE_READWRITE); + return p2 == p; +} + +bool +MarkPagesInUse(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +#elif defined(XP_OS2) + +#define INCL_DOSMEMMGR +#include + +#define JS_GC_HAS_MAP_ALIGN 1 +#define OS2_MAX_RECURSIONS 16 + +void +InitMemorySubsystem() +{ +} + +void +UnmapPages(void *addr, size_t size) +{ + if (!DosFreeMem(addr)) + return; + + /* + * If DosFreeMem() failed, 'addr' is probably part of an "expensive" + * allocation, so calculate the base address and try again. + */ + unsigned long cb = 2 * size; + unsigned long flags; + if (DosQueryMem(addr, &cb, &flags) || cb < size) + return; + + uintptr_t base = reinterpret_cast(addr) - ((2 * size) - cb); + DosFreeMem(reinterpret_cast(base)); + + return; +} + +static void * +MapAlignedPagesRecursively(size_t size, size_t alignment, int& recursions) +{ + if (++recursions >= OS2_MAX_RECURSIONS) + return NULL; + + void *tmp; + if (DosAllocMem(&tmp, size, + OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { + JS_ALWAYS_TRUE(DosAllocMem(&tmp, size, + PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); + } + size_t offset = reinterpret_cast(tmp) & (alignment - 1); + if (!offset) + return tmp; + + /* + * If there are 'filler' bytes of free space above 'tmp', free 'tmp', + * then reallocate it as a 'filler'-sized block; assuming we're not + * in a race with another thread, the next recursion should succeed. + */ + size_t filler = size + alignment - offset; + unsigned long cb = filler; + unsigned long flags = 0; + unsigned long rc = DosQueryMem(&(static_cast(tmp))[size], + &cb, &flags); + if (!rc && (flags & PAG_FREE) && cb >= filler) { + UnmapPages(tmp, 0); + if (DosAllocMem(&tmp, filler, + OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { + JS_ALWAYS_TRUE(DosAllocMem(&tmp, filler, + PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); + } + } + + void *p = MapAlignedPagesRecursively(size, alignment, recursions); + UnmapPages(tmp, 0); + + return p; +} + +void * +MapAlignedPages(size_t size, size_t alignment) +{ + JS_ASSERT(size >= alignment); + JS_ASSERT(size % alignment == 0); + JS_ASSERT(size % PageSize == 0); + JS_ASSERT(alignment % PageSize == 0); + + int recursions = -1; + + /* + * Make up to OS2_MAX_RECURSIONS attempts to get an aligned block + * of the right size by recursively allocating blocks of unaligned + * free memory until only an aligned allocation is possible. + */ + void *p = MapAlignedPagesRecursively(size, alignment, recursions); + if (p) + return p; + + /* + * If memory is heavily fragmented, the recursive strategy may fail; + * instead, use the "expensive" strategy: allocate twice as much + * as requested and return an aligned address within this block. + */ + if (DosAllocMem(&p, 2 * size, + OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { + JS_ALWAYS_TRUE(DosAllocMem(&p, 2 * size, + PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); + } + + uintptr_t addr = reinterpret_cast(p); + addr = (addr + (alignment - 1)) & ~(alignment - 1); + + return reinterpret_cast(addr); +} + +bool +MarkPagesUnused(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +bool +MarkPagesInUse(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +#elif defined(SOLARIS) + +#include +#include + +#ifndef MAP_NOSYNC +# define MAP_NOSYNC 0 +#endif + +void +InitMemorySubsystem() +{ +} + +void * +MapAlignedPages(size_t size, size_t alignment) +{ + JS_ASSERT(size >= alignment); + JS_ASSERT(size % alignment == 0); + JS_ASSERT(size % PageSize == 0); + JS_ASSERT(alignment % PageSize == 0); + + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC; + + void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0); + if (p == MAP_FAILED) + return NULL; + return p; +} + +void +UnmapPages(void *p, size_t size) +{ + JS_ALWAYS_TRUE(0 == munmap((caddr_t)p, size)); +} + +bool +MarkPagesUnused(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +bool +MarkPagesInUse(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +#elif defined(XP_UNIX) || defined(XP_MACOSX) || defined(DARWIN) + +#include +#include + +void +InitMemorySubsystem() +{ + JS_OPT_ASSERT(size_t(sysconf(_SC_PAGESIZE)) == PageSize); +} + +void * +MapAlignedPages(size_t size, size_t alignment) +{ + JS_ASSERT(size >= alignment); + JS_ASSERT(size % alignment == 0); + JS_ASSERT(size % PageSize == 0); + JS_ASSERT(alignment % PageSize == 0); + + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANON; + + /* Special case: If we want page alignment, no further work is needed. */ + if (alignment == PageSize) { + return mmap(NULL, size, prot, flags, -1, 0); + } + + /* Overallocate and unmap the region's edges. */ + size_t reqSize = JS_MIN(size + 2 * alignment, 2 * size); + void *region = mmap(NULL, reqSize, prot, flags, -1, 0); + if (region == MAP_FAILED) + return NULL; + + uintptr_t regionEnd = uintptr_t(region) + reqSize; + uintptr_t offset = uintptr_t(region) % alignment; + JS_ASSERT(offset < reqSize - size); + + void *front = (void *)(uintptr_t(region) + (alignment - offset)); + void *end = (void *)(uintptr_t(front) + size); + if (front != region) + JS_ALWAYS_TRUE(0 == munmap(region, alignment - offset)); + if (uintptr_t(end) != regionEnd) + JS_ALWAYS_TRUE(0 == munmap(end, regionEnd - uintptr_t(end))); + + JS_ASSERT(uintptr_t(front) % alignment == 0); + return front; +} + +void +UnmapPages(void *p, size_t size) +{ + JS_ALWAYS_TRUE(0 == munmap(p, size)); +} + +bool +MarkPagesUnused(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + int result = madvise(p, size, MADV_DONTNEED); + return result != -1; +} + +bool +MarkPagesInUse(void *p, size_t size) +{ + JS_ASSERT(uintptr_t(p) % PageSize == 0); + return true; +} + +#else +#error "Memory mapping functions are not defined for your OS." +#endif + +} /* namespace gc */ +} /* namespace js */ diff --git a/deps/mozjs/js/src/gc/Memory.h b/deps/mozjs/js/src/gc/Memory.h new file mode 100644 index 00000000000..30caeba4c70 --- /dev/null +++ b/deps/mozjs/js/src/gc/Memory.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jsgc_memory_h___ +#define jsgc_memory_h___ + +#include +#include "jsgc.h" + +namespace js { +namespace gc { + +/* + * Sanity check that our compiled configuration matches the currently running + * instance and initialize any runtime data needed for allocation. + */ +void InitMemorySubsystem(); + +void *MapAlignedPages(size_t size, size_t alignment); +void UnmapPages(void *p, size_t size); + +bool MarkPagesUnused(void *p, size_t size); +bool MarkPagesInUse(void *p, size_t size); + +} /* namespace gc */ +} /* namespace js */ + +#endif /* jsgc_memory_h___ */ diff --git a/deps/mozjs/js/src/gc/Root.h b/deps/mozjs/js/src/gc/Root.h new file mode 100644 index 00000000000..79fab09c96e --- /dev/null +++ b/deps/mozjs/js/src/gc/Root.h @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey global object code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsgc_root_h__ +#define jsgc_root_h__ + +#include "jsapi.h" +#include "jsprvtd.h" + +namespace js { + +/* + * Moving GC Stack Rooting + * + * A moving GC may change the physical location of GC allocated things, even + * when they are rooted, updating all pointers to the thing to refer to its new + * location. The GC must therefore know about all live pointers to a thing, + * not just one of them, in order to behave correctly. + * + * The classes below are used to root stack locations whose value may be held + * live across a call that can trigger GC (i.e. a call which might allocate any + * GC things). For a code fragment such as: + * + * Foo(); + * ... = obj->lastProperty(); + * + * If Foo() can trigger a GC, the stack location of obj must be rooted to + * ensure that the GC does not move the JSObject referred to by obj without + * updating obj's location itself. This rooting must happen regardless of + * whether there are other roots which ensure that the object itself will not + * be collected. + * + * If Foo() cannot trigger a GC, and the same holds for all other calls made + * between obj's definitions and its last uses, then no rooting is required. + * + * Several classes are available for rooting stack locations. All are templated + * on the type T of the value being rooted, for which RootMethods must + * have an instantiation. + * + * - Root roots an existing stack allocated variable or other location of + * type T. This is typically used either when a variable only needs to be + * rooted on certain rare paths, or when a function takes a bare GC thing + * pointer as an argument and needs to root it. In the latter case a + * Handle is generally preferred, see below. + * + * - RootedVar declares a variable of type T, whose value is always rooted. + * + * - Handle is a const reference to a Root or RootedVar. Handles are + * coerced automatically from such a Root or RootedVar. Functions which + * take GC things or values as arguments and need to root those arguments + * should generally replace those arguments with handles and avoid any + * explicit rooting. This has two benefits. First, when several such + * functions call each other then redundant rooting of multiple copies of the + * GC thing can be avoided. Second, if the caller does not pass a rooted + * value a compile error will be generated, which is quicker and easier to + * fix than when relying on a separate rooting analysis. + */ + +template <> struct RootMethods +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +template <> struct RootMethods +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +template <> struct RootMethods +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + +template <> struct RootMethods +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + +template +struct RootMethods +{ + static T *initial() { return NULL; } + static ThingRootKind kind() { return T::rootKind(); } + static bool poisoned(T *v) { return IsPoisonedPtr(v); } +}; + +/* + * Root a stack location holding a GC thing. This takes a stack pointer + * and ensures that throughout its lifetime the referenced variable + * will remain pinned against a moving GC. + * + * It is important to ensure that the location referenced by a Root is + * initialized, as otherwise the GC may try to use the the uninitialized value. + * It is generally preferable to use either RootedVar for local variables, or + * Handle for arguments. + */ +template +class Root +{ + public: + Root(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { +#ifdef JSGC_ROOT_ANALYSIS + ThingRootKind kind = RootMethods::kind(); + this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); + this->prev = *stack; + *stack = this; +#endif + + JS_ASSERT(!RootMethods::poisoned(*ptr)); + + this->ptr = ptr; + + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~Root() + { +#ifdef JSGC_ROOT_ANALYSIS + JS_ASSERT(*stack == this); + *stack = prev; +#endif + } + +#ifdef JSGC_ROOT_ANALYSIS + Root *previous() { return prev; } +#endif + + const T *address() const { return ptr; } + + private: + +#ifdef JSGC_ROOT_ANALYSIS + Root **stack, *prev; +#endif + const T *ptr; + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +template template +inline +Handle::Handle(const Root &root) +{ + testAssign(); + ptr = reinterpret_cast(root.address()); +} + +typedef Root RootObject; +typedef Root RootFunction; +typedef Root RootShape; +typedef Root RootBaseShape; +typedef Root RootTypeObject; +typedef Root RootString; +typedef Root RootAtom; +typedef Root RootId; +typedef Root RootValue; + +/* Mark a stack location as a root for a rooting analysis. */ +class CheckRoot +{ +#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) + + CheckRoot **stack, *prev; + const uint8_t *ptr; + + public: + template + CheckRoot(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { + this->stack = &cx->checkGCRooters; + this->prev = *stack; + *stack = this; + this->ptr = static_cast(ptr); + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~CheckRoot() + { + JS_ASSERT(*stack == this); + *stack = prev; + } + + CheckRoot *previous() { return prev; } + + bool contains(const uint8_t *v, size_t len) { + return ptr >= v && ptr < v + len; + } + +#else /* DEBUG && JSGC_ROOT_ANALYSIS */ + + public: + template + CheckRoot(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + +#endif /* DEBUG && JSGC_ROOT_ANALYSIS */ + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* Make a local variable which stays rooted throughout its lifetime. */ +template +class RootedVar +{ + public: + RootedVar(JSContext *cx) + : ptr(RootMethods::initial()), root(cx, &ptr) + {} + + RootedVar(JSContext *cx, T initial) + : ptr(initial), root(cx, &ptr) + {} + + operator T () { return ptr; } + T operator ->() { return ptr; } + T * address() { return &ptr; } + const T * address() const { return &ptr; } + T raw() { return ptr; } + + T & operator =(T value) + { + JS_ASSERT(!RootMethods::poisoned(value)); + ptr = value; + return ptr; + } + + private: + T ptr; + Root root; +}; + +template template +inline +Handle::Handle(const RootedVar &root) +{ + ptr = reinterpret_cast(root.address()); +} + +typedef RootedVar RootedVarObject; +typedef RootedVar RootedVarFunction; +typedef RootedVar RootedVarShape; +typedef RootedVar RootedVarBaseShape; +typedef RootedVar RootedVarTypeObject; +typedef RootedVar RootedVarString; +typedef RootedVar RootedVarAtom; +typedef RootedVar RootedVarId; +typedef RootedVar RootedVarValue; + +} /* namespace js */ +#endif /* jsgc_root_h___ */ diff --git a/deps/mozjs/js/src/gc/Statistics.cpp b/deps/mozjs/js/src/gc/Statistics.cpp new file mode 100644 index 00000000000..ba17a972246 --- /dev/null +++ b/deps/mozjs/js/src/gc/Statistics.cpp @@ -0,0 +1,337 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include + +#include "jscntxt.h" +#include "jscrashformat.h" +#include "jscrashreport.h" +#include "jsprf.h" +#include "jsprobes.h" +#include "jsutil.h" +#include "prmjtime.h" + +#include "gc/Statistics.h" + +namespace js { +namespace gcstats { + +static const char * +ExplainReason(gcreason::Reason reason) +{ + switch (reason) { +#define SWITCH_REASON(name) \ + case gcreason::name: \ + return #name; + GCREASONS(SWITCH_REASON) + + default: + JS_NOT_REACHED("bad GC reason"); + return "?"; +#undef SWITCH_REASON + } +} + +Statistics::ColumnInfo::ColumnInfo(const char *title, double t, double total) + : title(title) +{ + JS_snprintf(str, sizeof(str), "%.1f", t); + JS_snprintf(totalStr, sizeof(totalStr), "%.1f", total); + width = 6; +} + +Statistics::ColumnInfo::ColumnInfo(const char *title, double t) + : title(title) +{ + JS_snprintf(str, sizeof(str), "%.1f", t); + strcpy(totalStr, "n/a"); + width = 6; +} + +Statistics::ColumnInfo::ColumnInfo(const char *title, unsigned int data) + : title(title) +{ + JS_snprintf(str, sizeof(str), "%d", data); + strcpy(totalStr, "n/a"); + width = 4; +} + +Statistics::ColumnInfo::ColumnInfo(const char *title, const char *data) + : title(title) +{ + JS_ASSERT(strlen(data) < sizeof(str)); + strcpy(str, data); + strcpy(totalStr, "n/a "); + width = 0; +} + +static const int NUM_COLUMNS = 17; + +void +Statistics::makeTable(ColumnInfo *cols) +{ + int i = 0; + + cols[i++] = ColumnInfo("Type", compartment ? "Comp" : "Glob"); + + cols[i++] = ColumnInfo("Total", t(PHASE_GC), total(PHASE_GC)); + cols[i++] = ColumnInfo("Wait", beginDelay(PHASE_MARK, PHASE_GC)); + cols[i++] = ColumnInfo("Mark", t(PHASE_MARK), total(PHASE_MARK)); + cols[i++] = ColumnInfo("Sweep", t(PHASE_SWEEP), total(PHASE_SWEEP)); + cols[i++] = ColumnInfo("FinObj", t(PHASE_SWEEP_OBJECT), total(PHASE_SWEEP_OBJECT)); + cols[i++] = ColumnInfo("FinStr", t(PHASE_SWEEP_STRING), total(PHASE_SWEEP_STRING)); + cols[i++] = ColumnInfo("FinScr", t(PHASE_SWEEP_SCRIPT), total(PHASE_SWEEP_SCRIPT)); + cols[i++] = ColumnInfo("FinShp", t(PHASE_SWEEP_SHAPE), total(PHASE_SWEEP_SHAPE)); + cols[i++] = ColumnInfo("DisCod", t(PHASE_DISCARD_CODE), total(PHASE_DISCARD_CODE)); + cols[i++] = ColumnInfo("DisAnl", t(PHASE_DISCARD_ANALYSIS), total(PHASE_DISCARD_ANALYSIS)); + cols[i++] = ColumnInfo("XPCnct", t(PHASE_XPCONNECT), total(PHASE_XPCONNECT)); + cols[i++] = ColumnInfo("Destry", t(PHASE_DESTROY), total(PHASE_DESTROY)); + cols[i++] = ColumnInfo("End", endDelay(PHASE_GC, PHASE_DESTROY)); + + cols[i++] = ColumnInfo("+Chu", counts[STAT_NEW_CHUNK]); + cols[i++] = ColumnInfo("-Chu", counts[STAT_DESTROY_CHUNK]); + + cols[i++] = ColumnInfo("Reason", ExplainReason(triggerReason)); + + JS_ASSERT(i == NUM_COLUMNS); +} + +Statistics::Statistics(JSRuntime *rt) + : runtime(rt), + triggerReason(gcreason::NO_REASON) +{ + PodArrayZero(counts); + PodArrayZero(totals); + + startupTime = PRMJ_Now(); + + char *env = getenv("MOZ_GCTIMER"); + if (!env || strcmp(env, "none") == 0) { + fp = NULL; + return; + } + + if (strcmp(env, "stdout") == 0) { + fullFormat = false; + fp = stdout; + } else if (strcmp(env, "stderr") == 0) { + fullFormat = false; + fp = stderr; + } else { + fullFormat = true; + + fp = fopen(env, "a"); + JS_ASSERT(fp); + + fprintf(fp, " AppTime"); + + ColumnInfo cols[NUM_COLUMNS]; + makeTable(cols); + for (int i = 0; i < NUM_COLUMNS; i++) + fprintf(fp, ", %*s", cols[i].width, cols[i].title); + fprintf(fp, "\n"); + } +} + +Statistics::~Statistics() +{ + if (fp) { + if (fullFormat) { + fprintf(fp, "------>TOTAL"); + + ColumnInfo cols[NUM_COLUMNS]; + makeTable(cols); + for (int i = 0; i < NUM_COLUMNS && cols[i].totalStr[0]; i++) + fprintf(fp, ", %*s", cols[i].width, cols[i].totalStr); + fprintf(fp, "\n"); + } + + if (fp != stdout && fp != stderr) + fclose(fp); + } +} + +struct GCCrashData +{ + int isRegen; + int isCompartment; +}; + +void +Statistics::beginGC(JSCompartment *comp, gcreason::Reason reason) +{ + compartment = comp; + + PodArrayZero(phaseStarts); + PodArrayZero(phaseEnds); + PodArrayZero(phaseTimes); + + triggerReason = reason; + + beginPhase(PHASE_GC); + Probes::GCStart(); + + GCCrashData crashData; + crashData.isCompartment = !!compartment; + crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); +} + +double +Statistics::t(Phase phase) +{ + return double(phaseTimes[phase]) / PRMJ_USEC_PER_MSEC; +} + +double +Statistics::total(Phase phase) +{ + return double(totals[phase]) / PRMJ_USEC_PER_MSEC; +} + +double +Statistics::beginDelay(Phase phase1, Phase phase2) +{ + return double(phaseStarts[phase1] - phaseStarts[phase2]) / PRMJ_USEC_PER_MSEC; +} + +double +Statistics::endDelay(Phase phase1, Phase phase2) +{ + return double(phaseEnds[phase1] - phaseEnds[phase2]) / PRMJ_USEC_PER_MSEC; +} + +void +Statistics::statsToString(char *buffer, size_t size) +{ + JS_ASSERT(size); + buffer[0] = 0x00; + + ColumnInfo cols[NUM_COLUMNS]; + makeTable(cols); + + size_t pos = 0; + for (int i = 0; i < NUM_COLUMNS; i++) { + int len = strlen(cols[i].title) + 1 + strlen(cols[i].str); + if (i > 0) + len += 2; + if (pos + len >= size) + break; + if (i > 0) + strcat(buffer, ", "); + strcat(buffer, cols[i].title); + strcat(buffer, ":"); + strcat(buffer, cols[i].str); + pos += len; + } +} + +void +Statistics::printStats() +{ + if (fullFormat) { + fprintf(fp, "%12.0f", double(phaseStarts[PHASE_GC] - startupTime) / PRMJ_USEC_PER_MSEC); + + ColumnInfo cols[NUM_COLUMNS]; + makeTable(cols); + for (int i = 0; i < NUM_COLUMNS; i++) + fprintf(fp, ", %*s", cols[i].width, cols[i].str); + fprintf(fp, "\n"); + } else { + fprintf(fp, "%f %f %f\n", + t(PHASE_GC), t(PHASE_MARK), t(PHASE_SWEEP)); + } + fflush(fp); +} + +void +Statistics::endGC() +{ + Probes::GCEnd(); + endPhase(PHASE_GC); + crash::SnapshotGCStack(); + + for (int i = 0; i < PHASE_LIMIT; i++) + totals[i] += phaseTimes[i]; + + if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { + (*cb)(JS_TELEMETRY_GC_REASON, triggerReason); + (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0); + (*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC)); + (*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK)); + (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP)); + } + + if (JSGCFinishedCallback cb = runtime->gcFinishedCallback) { + char buffer[1024]; + statsToString(buffer, sizeof(buffer)); + (*cb)(runtime, compartment, buffer); + } + + if (fp) + printStats(); + + PodArrayZero(counts); +} + +void +Statistics::beginPhase(Phase phase) +{ + phaseStarts[phase] = PRMJ_Now(); + + if (phase == gcstats::PHASE_MARK) + Probes::GCStartMarkPhase(); + else if (phase == gcstats::PHASE_SWEEP) + Probes::GCStartSweepPhase(); +} + +void +Statistics::endPhase(Phase phase) +{ + phaseEnds[phase] = PRMJ_Now(); + phaseTimes[phase] += phaseEnds[phase] - phaseStarts[phase]; + + if (phase == gcstats::PHASE_MARK) + Probes::GCEndMarkPhase(); + else if (phase == gcstats::PHASE_SWEEP) + Probes::GCEndSweepPhase(); +} + +} /* namespace gcstats */ +} /* namespace js */ diff --git a/deps/mozjs/js/src/gc/Statistics.h b/deps/mozjs/js/src/gc/Statistics.h new file mode 100644 index 00000000000..497efaca607 --- /dev/null +++ b/deps/mozjs/js/src/gc/Statistics.h @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsgc_statistics_h___ +#define jsgc_statistics_h___ + +#include + +#include "jsfriendapi.h" +#include "jspubtd.h" +#include "jsutil.h" + +struct JSCompartment; + +namespace js { +namespace gcstats { + +enum Phase { + PHASE_GC, + PHASE_MARK, + PHASE_SWEEP, + PHASE_SWEEP_OBJECT, + PHASE_SWEEP_STRING, + PHASE_SWEEP_SCRIPT, + PHASE_SWEEP_SHAPE, + PHASE_DISCARD_CODE, + PHASE_DISCARD_ANALYSIS, + PHASE_XPCONNECT, + PHASE_DESTROY, + + PHASE_LIMIT +}; + +enum Stat { + STAT_NEW_CHUNK, + STAT_DESTROY_CHUNK, + + STAT_LIMIT +}; + +struct Statistics { + Statistics(JSRuntime *rt); + ~Statistics(); + + void beginGC(JSCompartment *comp, gcreason::Reason reason); + void endGC(); + + void beginPhase(Phase phase); + void endPhase(Phase phase); + + void count(Stat s) { + JS_ASSERT(s < STAT_LIMIT); + counts[s]++; + } + + private: + JSRuntime *runtime; + + uint64_t startupTime; + + FILE *fp; + bool fullFormat; + + gcreason::Reason triggerReason; + JSCompartment *compartment; + + uint64_t phaseStarts[PHASE_LIMIT]; + uint64_t phaseEnds[PHASE_LIMIT]; + uint64_t phaseTimes[PHASE_LIMIT]; + uint64_t totals[PHASE_LIMIT]; + unsigned int counts[STAT_LIMIT]; + + double t(Phase phase); + double total(Phase phase); + double beginDelay(Phase phase1, Phase phase2); + double endDelay(Phase phase1, Phase phase2); + void printStats(); + void statsToString(char *buffer, size_t size); + + struct ColumnInfo { + const char *title; + char str[32]; + char totalStr[32]; + int width; + + ColumnInfo() {} + ColumnInfo(const char *title, double t, double total); + ColumnInfo(const char *title, double t); + ColumnInfo(const char *title, unsigned int data); + ColumnInfo(const char *title, const char *data); + }; + + void makeTable(ColumnInfo *cols); +}; + +struct AutoGC { + AutoGC(Statistics &stats, JSCompartment *comp, gcreason::Reason reason + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginGC(comp, reason); } + ~AutoGC() { stats.endGC(); } + + Statistics &stats; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +struct AutoPhase { + AutoPhase(Statistics &stats, Phase phase JS_GUARD_OBJECT_NOTIFIER_PARAM) + : stats(stats), phase(phase) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginPhase(phase); } + ~AutoPhase() { stats.endPhase(phase); } + + Statistics &stats; + Phase phase; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} /* namespace gcstats */ +} /* namespace js */ + +#endif /* jsgc_statistics_h___ */ diff --git a/deps/mozjs/js/src/gnuplot/gcTimer.gnu b/deps/mozjs/js/src/gnuplot/gcTimer.gnu index ff6c0a290da..b8b3ac9d85e 100644 --- a/deps/mozjs/js/src/gnuplot/gcTimer.gnu +++ b/deps/mozjs/js/src/gnuplot/gcTimer.gnu @@ -6,19 +6,19 @@ set terminal png set title "Title goes here!" set datafile missing "-" set noxtics -set ytics nomirror +#set ytics nomirror set ylabel "msec" -set y2tics nomirror -set y2label "Chunk count" set key below set style data linespoints #set data file plot 'gcTimer.dat' using 2 title columnheader(2), \ -'' u 3 title columnheader(3), \ +'' u 3 title columnheader(3) with points, \ '' u 4 title columnheader(4), \ -'' u 5 title columnheader(5) with points, \ +'' u 5 title columnheader(5), \ '' u 6 title columnheader(6) with points, \ '' u 7 title columnheader(7) with points, \ -'' u 8 title columnheader(8) with points axis x1y2, \ -'' u 9 title columnheader(9) with points axis x1y2 +'' u 8 title columnheader(8) with points, \ +'' u 9 title columnheader(9) with points, \ +'' u 10 title columnheader(10) with points, \ +'' u 11 title columnheader(11) with points diff --git a/deps/mozjs/js/src/imacro_asm.py b/deps/mozjs/js/src/imacro_asm.py deleted file mode 100644 index 2e85e5543f8..00000000000 --- a/deps/mozjs/js/src/imacro_asm.py +++ /dev/null @@ -1,467 +0,0 @@ -#!/usr/bin/env python -# -*- Mode: Python; tab-width: 4; indent-tabs-mode: nil -*- -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the TraceMonkey IMacro Assembler. -# -# The Initial Developer of the Original Code is -# Brendan Eich . -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# An imacro (interpreter-macro) assembler in Python. -# -# Filename suffix conventions, used by Makefile.in rules: -# .jsasm SpiderMonkey JS assembly source, which could be input to other -# assemblers than imacro_asm.js, hence the generic suffix! -# .c.out C source output by imacro_asm.js - -import re -import os - -class Op: - def __init__(self, jsop, opcode, opname, opsrc, oplen, pops, pushes, precedence, flags): - self.jsop = jsop - self.opcode = opcode - self.opname = opname - self.opsrc = opsrc - self.oplen = oplen - self.pops = pops - self.pushes = pushes - self.precedence = precedence - self.flags = flags - -def readFileLines(filename): - f = open(filename) - try: - return f.readlines() - finally: - f.close() - -def load_ops(filename): - opdef_regexp = re.compile(r'''(?x) - ^ OPDEF \( (JSOP_\w+), \s* # op - ([0-9]+), \s* # val - ("[^"]+" | [\w_]+), \s* # name - ("[^"]+" | [\w_]+), \s* # image - (-1|[0-9]+), \s* # len - (-1|[0-9]+), \s* # uses - (-1|[0-9]+), \s* # defs - ([0-9]+), \s* # prec - ([\w_| ]+) \s* # format - \) \s* $''') - - def decode_string_expr(expr): - if expr == 'NULL': - return None - if expr[0] == '"': - assert expr[-1] == '"' - return expr[1:-1] - assert expr.startswith('js_') and expr.endswith('_str') - return expr[3:-4] - - opinfo = [] - for lineno, line in enumerate(readFileLines(filename)): - if line.startswith('OPDEF'): - m = opdef_regexp.match(line) - if m is None: - raise ValueError("OPDEF line of wrong format in jsopcode.tbl at line %d" % (lineno + 1)) - jsop, opcode, opname, opsrc, oplen, pops, pushes, precedence, flags = m.groups() - assert int(opcode) == len(opinfo) - opinfo.append(Op(jsop, int(opcode), decode_string_expr(opname), - decode_string_expr(opsrc), int(oplen), int(pops), int(pushes), - int(precedence), flags.replace(' ', '').split('|'))) - return opinfo - -opinfo = load_ops(os.path.join(os.path.dirname(__file__), "jsopcode.tbl")) -opname2info = dict((info.opname, info) for info in opinfo) -jsop2opcode = dict((info.jsop, info.opcode) for info in opinfo) - -def to_uint8(s): - try: - n = int(s) - except ValueError: - n = -1 - if 0 <= n < (1<<8): - return n - raise ValueError("invalid 8-bit operand: " + s) - -def to_uint16(s): - try: - n = int(s) - except ValueError: - n = -1 - if 0 <= n < (1<<16): - return n - raise ValueError("invalid 16-bit operand: " + s) - -def immediate(op): - info = op.info - imm1Expr = op.imm1.startswith('(') - if 'JOF_ATOM' in info.flags: - if op.imm1 in ('void', 'object', 'function', 'string', 'number', 'boolean'): - return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_%s)" % op.imm1.upper() - return "0, COMMON_ATOM_INDEX(%s)" % op.imm1 - if 'JOF_JUMP' in info.flags: - assert not imm1Expr - return "%d, %d" % ((op.target >> 8) & 0xff, op.target & 0xff) - if 'JOF_UINT8' in info.flags or 'JOF_INT8' in info.flags: - if imm1Expr: - return op.imm1 - return str(to_uint8(op.imm1)) - if 'JOF_UINT16' in info.flags: - if imm1Expr: - return '(%s & 0xff00) >> 8, (%s & 0xff)' % (op.imm1, op.imm1) - v = to_uint16(op.imm1) - return "%d, %d" % ((v & 0xff00) >> 8, v & 0xff) - raise NotImplementedError(info.jsop + " format not yet implemented") - -def simulate_cfg(igroup, imacro, depth, i): - any_group_opcode = None - expected_depth = None - for opcode in igroup.ops: - opi = opinfo[opcode] - if any_group_opcode is None: - any_group_opcode = opcode - if opi.pops < 0: - expected_depth = None - else: - expected_depth = opi.pushes - opi.pops - elif expected_depth is None: - if opi.pops >= 0: - raise ValueError("imacro shared by constant- and variable-stack-defs/uses instructions") - else: - if opi.pops < 0: - raise ValueError("imacro shared by constant- and variable-stack-defs/uses instructions") - if opi.pushes - opi.pops != expected_depth: - raise ValueError("imacro shared by instructions with different stack depths") - - for i in range(i, len(imacro.code)): - op = imacro.code[i] - opi = op.info - if opi.opname == 'imacop': - opi = opinfo[any_group_opcode] - - if opi.pops < 0: - depth -= 2 + int(op.imm1) - else: - depth -= opi.pops - depth += opi.pushes - - if i in imacro.depths and imacro.depths[i] != depth: - raise ValueError("Mismatched depth at %s:%d" % (imacro.filename, op.line)) - - # Underflowing depth isn't necessarily fatal; most of the imacros - # assume they are called with N>0 args so some assume it's ok to go - # to some depth imacro.maxdepth: - imacro.maxdepth = depth - imacro.depths[i] = depth - - if hasattr(op, "target_index"): - if op.target_index <= i: - raise ValueError("Backward jump at %s:%d" % (imacro.filename, op.line)) - simulate_cfg(igroup, imacro, depth, op.target_index) - if op.info.opname in ('goto', 'gotox'): - return - - if expected_depth is not None and depth != expected_depth: - raise ValueError("Expected depth %d, got %d" % (expected_depth, depth)) - - -# Syntax (spaces are significant only to delimit tokens): -# -# Assembly ::= (Directive? '\n')* -# Directive ::= (name ':')? Operation -# Operation ::= opname Operands? -# Operands ::= Operand (',' Operand)* -# Operand ::= name | number | '(' Expr ')' -# Expr ::= a constant-expression in the C++ language -# containing no parentheses -# -# We simplify given line structure and the maximum of one immediate operand, -# by parsing using split and regexps. For ease of parsing, parentheses are -# banned in an Expr for now, even in quotes or a C++ comment. -# -# Pseudo-ops start with . and include .igroup and .imacro, terminated by .end. -# .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for -# examples. -# -line_regexp = re.compile(r'''(?x) - ^ - (?: (\w+): )? # optional label at start of line - \s* (\.?\w+) # optional spaces, (pseudo-)opcode - (?: \s+ ([+-]?\w+ | \([^)]*\)) )? # optional first immediate operand - (?: \s+ ([\w,-]+ | \([^)]*\)) )? # optional second immediate operand - (?: \s* (?:\#.*) )? # optional spaces and comment - $''') - -oprange_regexp = re.compile(r'^\w+(?:-\w+)?(?:,\w+(?:-\w+)?)*$') - -class IGroup(object): - def __init__(self, name, ops): - self.name = name - self.ops = ops - self.imacros = [] - -class IMacro(object): - def __init__(self, name, filename): - self.name = name - self.offset = 0 - self.code = [] - self.labeldefs = {} - self.labeldef_indexes = {} - self.labelrefs = {} - self.filename = filename - self.depths = {} - self.initdepth = 0 - -class Instruction(object): - def __init__(self, offset, info, imm1, imm2, lineno): - self.offset = offset - self.info = info - self.imm1 = imm1 - self.imm2 = imm2 - self.lineno = lineno - -def assemble(filename, outfile): - write = outfile.write - igroup = None - imacro = None - opcode2extra = {} - igroups = [] - - write("/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */\n") - - def fail(msg, *args): - raise ValueError("%s at %s:%d" % (msg % args, filename, lineno + 1)) - - for lineno, line in enumerate(readFileLines(filename)): - # strip comments - line = re.sub(r'#.*', '', line).rstrip() - if line == "": - continue - m = line_regexp.match(line) - if m is None: - fail(line) - - label, opname, imm1, imm2 = m.groups() - - if opname.startswith('.'): - if label is not None: - fail("invalid label %s before %s" % (label, opname)) - - if opname == '.igroup': - if imm1 is None: - fail("missing .igroup name") - if igroup is not None: - fail("nested .igroup " + imm1) - if oprange_regexp.match(imm2) is None: - fail("invalid igroup operator range " + imm2) - - ops = set() - for current in imm2.split(","): - split = current.split('-') - opcode = jsop2opcode[split[0]] - if len(split) == 1: - lastopcode = opcode - else: - assert len(split) == 2 - lastopcode = jsop2opcode[split[1]] - if opcode >= lastopcode: - fail("invalid opcode range: " + current) - - for opcode in range(opcode, lastopcode + 1): - if opcode in ops: - fail("repeated opcode " + opinfo[opcode].jsop) - ops.add(opcode) - - igroup = IGroup(imm1, ops) - - elif opname == '.imacro': - if igroup is None: - fail(".imacro outside of .igroup") - if imm1 is None: - fail("missing .imacro name") - if imacro: - fail("nested .imacro " + imm1) - imacro = IMacro(imm1, filename) - - elif opname == '.fixup': - if imacro is None: - fail(".fixup outside of .imacro") - if len(imacro.code) != 0: - fail(".fixup must be first item in .imacro") - if imm1 is None: - fail("missing .fixup argument") - try: - fixup = int(imm1) - except ValueError: - fail(".fixup argument must be a nonzero integer") - if fixup == 0: - fail(".fixup argument must be a nonzero integer") - if imacro.initdepth != 0: - fail("more than one .fixup in .imacro") - imacro.initdepth = fixup - - elif opname == '.end': - if imacro is None: - if igroup is None: - fail(".end without prior .igroup or .imacro") - if imm1 is not None and (imm1 != igroup.name or imm2 is not None): - fail(".igroup/.end name mismatch") - - maxdepth = 0 - - write("static struct {\n") - for imacro in igroup.imacros: - write(" jsbytecode %s[%d];\n" % (imacro.name, imacro.offset)) - write("} %s_imacros = {\n" % igroup.name) - - for imacro in igroup.imacros: - depth = 0 - write(" {\n") - for op in imacro.code: - operand = "" - if op.imm1 is not None: - operand = ", " + immediate(op) - write("/*%2d*/ %s%s,\n" % (op.offset, op.info.jsop, operand)) - - imacro.maxdepth = imacro.initdepth - simulate_cfg(igroup, imacro, imacro.initdepth, 0) - if imacro.maxdepth > maxdepth: - maxdepth = imacro.maxdepth - - write(" },\n") - write("};\n") - - for opcode in igroup.ops: - opcode2extra[opcode] = maxdepth - igroups.append(igroup) - igroup = None - else: - assert igroup is not None - - if imm1 is not None and imm1 != imacro.name: - fail(".imacro/.end name mismatch") - - # Backpatch the forward references to labels that must now be defined. - for label in imacro.labelrefs: - if label not in imacro.labeldefs: - fail("label " + label + " used but not defined") - link = imacro.labelrefs[label] - assert link >= 0 - while True: - op = imacro.code[link] - next = op.target - op.target = imacro.labeldefs[label] - op.offset - op.target_index = imacro.labeldef_indexes[label] - if next < 0: - break - link = next - - igroup.imacros.append(imacro) - imacro = None - - else: - fail("unknown pseudo-op " + opname) - continue - - if opname not in opname2info: - fail("unknown opcode " + opname) - - info = opname2info[opname] - if info.oplen == -1: - fail("unimplemented opcode " + opname) - - if imacro is None: - fail("opcode %s outside of .imacro", opname) - - # Blacklist ops that may or must use an atomized double immediate. - if info.opname in ('double', 'lookupswitch', 'lookupswitchx'): - fail(op.opname + " opcode not yet supported") - - if label: - imacro.labeldefs[label] = imacro.offset - imacro.labeldef_indexes[label] = len(imacro.code) - - op = Instruction(imacro.offset, info, imm1, imm2, lineno + 1) - if 'JOF_JUMP' in info.flags: - if imm1 in imacro.labeldefs: - # Backward reference can be resolved right away, no backpatching needed. - op.target = imacro.labeldefs[imm1] - op.offset - op.target_index = imacro.labeldef_indexes[imm1] - else: - # Link op into the .target-linked backpatch chain at labelrefs[imm1]. - # The linked list terminates with a -1 sentinel. - if imm1 in imacro.labelrefs: - op.target = imacro.labelrefs[imm1] - else: - op.target = -1 - imacro.labelrefs[imm1] = len(imacro.code) - - imacro.code.append(op) - imacro.offset += info.oplen - - write("uint8 js_opcode2extra[JSOP_LIMIT] = {\n") - for i in range(len(opinfo)): - write(" %d, /* %s */\n" % (opcode2extra.get(i, 0), opinfo[i].jsop)) - write("};\n") - - write("#define JSOP_IS_IMACOP(x) (0 \\\n") - for i in sorted(opcode2extra): - write(" || x == %s \\\n" % opinfo[i].jsop) - write(")\n") - - write("jsbytecode*\njs_GetImacroStart(jsbytecode* pc) {\n") - for g in igroups: - for m in g.imacros: - start = g.name + "_imacros." + m.name - write(" if (size_t(pc - %s) < %d) return %s;\n" % (start, m.offset, start)) - - write(" return NULL;\n") - write("}\n") - -if __name__ == '__main__': - import sys - if len(sys.argv) != 3: - print "usage: python imacro_asm.py infile.jsasm outfile.c.out" - sys.exit(1) - - f = open(sys.argv[2], 'w') - try: - assemble(sys.argv[1], f) - finally: - f.close() - diff --git a/deps/mozjs/js/src/imacros.jsasm b/deps/mozjs/js/src/imacros.jsasm deleted file mode 100644 index 798adac78d5..00000000000 --- a/deps/mozjs/js/src/imacros.jsasm +++ /dev/null @@ -1,718 +0,0 @@ -# -*- indent-tabs-mode: nil; -*- -# vim: set sw=4 ts=8 et tw=78 ft=asm: -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the TraceMonkey IMacro Assembler. -# -# The Initial Developer of the Original Code is -# Brendan Eich . -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -.igroup equality JSOP_EQ-JSOP_NE - - .imacro any_obj # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string void # any obj valueOf obj "void" - call 1 # any obj rval - ifprimtop 3 # any obj rval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj rval - primtop (JSTYPE_NUMBER) # any obj rval -3: swap # any rval obj - pop # any rval - imacop # eqval - stop - .end - - .imacro obj_any # obj any - swap # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string void # any obj valueOf obj "void" - call 1 # any obj lval - ifprimtop 3 # any obj lval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj lval - primtop (JSTYPE_NUMBER) # any obj rval -3: swap # any lval obj - pop # any lval - swap # lval any - imacop # eqval - stop - .end - -.end equality - -# A single range, split up like so to test groups over multiple ranges of ops -.igroup binary JSOP_BITOR-JSOP_BITAND,JSOP_EQ-JSOP_DIV,JSOP_MOD - - .imacro any_obj # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string number # any obj valueOf obj "number" - call 1 # any obj rval - ifprimtop 3 # any obj rval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj rval - primtop (JSTYPE_NUMBER) # any obj rval -3: swap # any rval obj - pop # any rval - imacop # bval - stop - .end - - .imacro obj_any # obj any - swap # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string number # any obj valueOf obj "number" - call 1 # any obj lval - ifprimtop 3 # any obj lval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj lval - primtop (JSTYPE_NUMBER) # any obj lval -3: swap # any lval obj - pop # any lval - swap # lval any - imacop # bval - stop - .end - - .imacro obj_obj # obj1 obj2 - swap # obj2 obj1 - dup # obj2 obj1 obj1 - dup # obj2 obj1 obj1 obj1 - getprop valueOf # obj2 obj1 obj1 valueOf - ifprimtop 1 # obj2 obj1 obj1 valueOf - swap # obj2 obj1 valueOf obj1 - string number # obj2 obj1 valueOf obj1 "number" - call 1 # obj2 obj1 lval - ifprimtop 3 # obj2 obj1 lval - pop # obj2 obj1 - dup # obj2 obj1 obj1 - goto 2 -1: pop # obj2 obj1 obj1 -2: callprop toString # obj2 obj1 toString obj1 - call 0 # obj2 obj1 lval - primtop (JSTYPE_NUMBER) # obj2 obj1 lval -3: swap # obj2 lval obj1 - pop # obj2 lval - swap # lval obj2 - dup # lval obj1 obj1 - dup # lval obj obj obj - getprop valueOf # lval obj obj valueOf - ifprimtop 4 # lval obj obj valueOf - swap # lval obj valueOf obj - string number # lval obj valueOf obj "number" - call 1 # lval obj rval - ifprimtop 6 # lval obj rval - pop # lval obj - dup # lval obj obj - goto 5 -4: pop # lval obj obj -5: callprop toString # lval obj toString obj - call 0 # lval obj rval - primtop (JSTYPE_NUMBER) # lval obj rval -6: swap # lval rval obj - pop # lval rval - imacop # bval - stop - .end - -.end binary - -.igroup add JSOP_ADD - - .imacro any_obj # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string void # any obj valueOf obj "void" - call 1 # any obj rval - ifprimtop 3 # any obj rval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj rval - primtop (JSTYPE_VOID) # any obj rval -3: swap # any rval obj - pop # any rval - add # aval - stop - .end - - .imacro obj_any # obj any - swap # any obj - dup # any obj obj - dup # any obj obj obj - getprop valueOf # any obj obj valueOf - ifprimtop 1 # any obj obj valueOf - swap # any obj valueOf obj - string void # any obj valueOf obj "void" - call 1 # any obj lval - ifprimtop 3 # any obj lval - pop # any obj - dup # any obj obj - goto 2 -1: pop # any obj obj -2: callprop toString # any obj toString obj - call 0 # any obj lval - primtop (JSTYPE_VOID) # any obj lval -3: swap # any lval obj - pop # any lval - swap # lval any - add # aval - stop - .end - - .imacro obj_obj # obj1 obj2 - swap # obj2 obj1 - dup # obj2 obj1 obj1 - dup # obj2 obj1 obj1 obj1 - getprop valueOf # obj2 obj1 obj1 valueOf - ifprimtop 1 # obj2 obj1 obj1 valueOf - swap # obj2 obj1 valueOf obj1 - string void # obj2 obj1 valueOf obj1 "void" - call 1 # obj2 obj1 lval - ifprimtop 3 # obj2 obj1 lval - pop # obj2 obj1 - dup # obj2 obj1 obj1 - goto 2 -1: pop # obj2 obj1 obj1 -2: callprop toString # obj2 obj1 toString obj1 - call 0 # obj2 obj1 lval - primtop (JSTYPE_VOID) # obj2 obj1 lval -3: swap # obj2 lval obj1 - pop # obj2 lval - swap # lval obj2 - dup # lval obj obj - dup # lval obj obj obj - getprop valueOf # lval obj obj valueOf - ifprimtop 4 # lval obj obj valueOf - swap # lval obj valueOf obj - string void # lval obj valueOf obj "void" - call 1 # lval obj rval - ifprimtop 6 # lval obj rval - pop # lval obj - dup # lval obj obj - goto 5 -4: pop # lval obj obj -5: callprop toString # lval obj toString obj - call 0 # lval obj rval - primtop (JSTYPE_VOID) # lval obj rval -6: swap # lval rval obj - pop # lval rval - add # aval - stop - .end - -.end add - -.igroup unary JSOP_NEG-JSOP_POS - - .imacro sign # obj - dup # obj obj - dup # obj obj obj - getprop valueOf # obj obj valueOf - ifprimtop 2 # obj obj valueOf - swap # obj valueOf obj - string number # obj valueOf obj "number" - call 1 # obj lval - ifprimtop 1 # obj lval - pop # obj - dup # obj obj - goto 3 -1: swap # lval obj - pop # lval - goto 4 -2: pop # obj obj -3: callprop toString # obj toString obj - call 0 # obj lval - primtop (JSTYPE_NUMBER) # obj lval - swap # lval obj - pop # lval -4: imacop # aval - stop - .end - -.end unary - -.igroup incelem JSOP_INCELEM,JSOP_ELEMINC - .imacro incelem # obj id - dup2 # obj id obj id - getelem # obj id val - pos # obj id n - one # obj id n 1 - add # obj id m - setelem # m - stop - .end - - .imacro eleminc # obj id - dup2 # obj id obj id - getelem # obj id val - pos # obj id n - dup # obj id n n - one # obj id n n 1 - add # obj id n m - pick 3 # id n m obj - pick 3 # n m obj id - pick 2 # n obj id m - setelem # n m - pop # n - stop - .end -.end incelem - -.igroup decelem JSOP_DECELEM,JSOP_ELEMDEC - .imacro decelem # obj id - dup2 # obj id obj id - getelem # obj id val - pos # obj id n - one # obj id n 1 - sub # obj id m - setelem # m - stop - .end - - .imacro elemdec # obj id - dup2 # obj id obj id - getelem # obj id val - pos # obj id n - dup # obj id n n - one # obj id n n 1 - sub # obj id n m - pick 3 # id n m obj - pick 3 # n m obj id - pick 2 # n obj id m - setelem # n m - pop # n - stop - .end -.end decelem - -.igroup call JSOP_CALL - - .imacro String # String this obj - dup # String this obj obj - dup # String this obj obj obj - getprop toString # String this obj obj toString - ifprimtop 1 # String this obj obj toString - swap # String this obj toString obj - call 0 # String this obj rval - ifprimtop 3 # String this obj rval - pop # String this obj - dup # String this obj obj - goto 2 -1: pop # String this obj obj -2: callprop valueOf # String this obj valueOf obj - string string # String this obj valueOf obj "string" - call 1 # String this obj rval - primtop (JSTYPE_STRING) # String this obj rval -3: swap # String this rval obj - pop # String this rval - call 1 # str - stop # str - .end - -.end call - -.igroup new JSOP_NEW - - .imacro String # String this obj - dup # String this obj obj - dup # String this obj obj obj - getprop toString # String this obj obj toString - ifprimtop 1 # String this obj obj toString - swap # String this obj toString obj - call 0 # String this obj rval - ifprimtop 3 # String this obj rval - pop # String this obj - dup # String this obj obj - goto 2 -1: pop # String this obj obj -2: callprop valueOf # String this obj valueOf obj - string string # String this obj valueOf obj "string" - call 1 # String this obj rval - primtop (JSTYPE_STRING) # String this obj rval -3: swap # String this rval obj - pop # String this rval - new 1 # strobj - stop # strobj - .end - -.end new - -.igroup funapply JSOP_FUNAPPLY - - .imacro apply0 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - pop # fun this - call 0 # - stop # - .end # - - .imacro apply1 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - pop # fun this arg0 - call 1 # - stop # - .end # - - .imacro apply2 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - pop # fun this arg0 arg1 - call 2 # - stop # - .end # - - .imacro apply3 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - pop # fun this arg0 arg1 arg2 - call 3 # - stop # - .end # - - .imacro apply4 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - dup # fun this arg0 arg1 arg2 arr arr - int8 3 # fun this arg0 arg1 arg2 arr arr 3 - getelem # fun this arg0 arg1 arg2 arr arg3 - swap # fun this arg0 arg1 arg2 arg3 arr - pop # fun this arg0 arg1 arg2 arg3 - call 4 # - stop # - .end # - - .imacro apply5 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - dup # fun this arg0 arg1 arg2 arr arr - int8 3 # fun this arg0 arg1 arg2 arr arr 3 - getelem # fun this arg0 arg1 arg2 arr arg3 - swap # fun this arg0 arg1 arg2 arg3 arr - dup # fun this arg0 arg1 arg2 arg3 arr arr - int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 - getelem # fun this arg0 arg1 arg2 arg3 arr arg4 - swap # fun this arg0 arg1 arg2 arg3 arg4 arr - pop # fun this arg0 arg1 arg2 arg3 arg4 - call 5 # - stop # - .end # - - .imacro apply6 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - dup # fun this arg0 arg1 arg2 arr arr - int8 3 # fun this arg0 arg1 arg2 arr arr 3 - getelem # fun this arg0 arg1 arg2 arr arg3 - swap # fun this arg0 arg1 arg2 arg3 arr - dup # fun this arg0 arg1 arg2 arg3 arr arr - int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 - getelem # fun this arg0 arg1 arg2 arg3 arr arg4 - swap # fun this arg0 arg1 arg2 arg3 arg4 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr - int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr - pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 - call 6 # - stop # - .end # - - .imacro apply7 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - dup # fun this arg0 arg1 arg2 arr arr - int8 3 # fun this arg0 arg1 arg2 arr arr 3 - getelem # fun this arg0 arg1 arg2 arr arg3 - swap # fun this arg0 arg1 arg2 arg3 arr - dup # fun this arg0 arg1 arg2 arg3 arr arr - int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 - getelem # fun this arg0 arg1 arg2 arg3 arr arg4 - swap # fun this arg0 arg1 arg2 arg3 arg4 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr - int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr - int8 6 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr 6 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arg6 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr - pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 - call 7 # - stop # - .end # - - .imacro apply8 # apply fun this arr - pick 3 # fun this arr apply - pop # fun this arr - dup # fun this arr arr - zero # fun this arr arr 0 - getelem # fun this arr arg0 - swap # fun this arg0 arr - dup # fun this arg0 arr arr - one # fun this arg0 arr arr 1 - getelem # fun this arg0 arr arg1 - swap # fun this arg0 arg1 arr - dup # fun this arg0 arg1 arr arr - int8 2 # fun this arg0 arg1 arr arr 2 - getelem # fun this arg0 arg1 arr arg2 - swap # fun this arg0 arg1 arg2 arr - dup # fun this arg0 arg1 arg2 arr arr - int8 3 # fun this arg0 arg1 arg2 arr arr 3 - getelem # fun this arg0 arg1 arg2 arr arg3 - swap # fun this arg0 arg1 arg2 arg3 arr - dup # fun this arg0 arg1 arg2 arg3 arr arr - int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 - getelem # fun this arg0 arg1 arg2 arg3 arr arg4 - swap # fun this arg0 arg1 arg2 arg3 arg4 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr - int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr - int8 6 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr 6 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arg6 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr - dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arr - int8 7 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arr 7 - getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arg7 - swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arg7 arr - pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arg7 - call 8 # - stop # - .end # - -.end funapply - -.igroup funcall JSOP_FUNCALL - - .imacro call0 # call fun - swap # fun call - pop # fun - null # fun this - call 0 # - stop # - .end # - - .imacro call1 # call fun this - pick 2 # fun this call - pop # fun this - call 0 # - stop # - .end # - - .imacro call2 # call fun this arg0 - pick 3 # fun this arg0 call - pop # fun this arg0 - call 1 # - stop # - .end # - - .imacro call3 # call fun this arg0 arg1 - pick 4 # fun this arg0 arg1 call - pop # fun this arg0 arg1 - call 2 # - stop # - .end # - - .imacro call4 # call fun this arg0 arg1 arg2 - pick 5 # fun this arg0 arg1 arg2 call - pop # fun this arg0 arg1 arg2 - call 3 # - stop # - .end # - - .imacro call5 # call fun this arg0 arg1 arg2 arg3 - pick 6 # fun this arg0 arg1 arg2 arg3 call - pop # fun this arg0 arg1 arg2 arg3 - call 4 # - stop # - .end # - - .imacro call6 # call fun this arg0 arg1 arg2 arg3 arg4 - pick 7 # fun this arg0 arg1 arg2 arg3 arg4 call - pop # fun this arg0 arg1 arg2 arg3 arg4 - call 5 # - stop # - .end # - - .imacro call7 # call fun this arg0 arg1 arg2 arg3 arg4 arg5 - pick 8 # fun this arg0 arg1 arg2 arg3 arg4 arg5 call - pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 - call 6 # - stop # - .end # - - .imacro call8 # call fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 - pick 9 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 call - pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 - call 7 # - stop # - .end # - -.end funcall - -.igroup getprop JSOP_GETPROP - .imacro scriptgetter # obj - .fixup +1 # getter obj - call 0 # val - stop - .end -.end getprop - -.igroup callprop JSOP_CALLPROP - .imacro scriptgetter # obj - .fixup +2 # obj getter obj - call 0 # obj method - swap # method obj - stop - .end -.end callprop - -.igroup getthisprop JSOP_GETTHISPROP,JSOP_GETARGPROP,JSOP_GETLOCALPROP - .imacro scriptgetter # - .fixup +2 # getter obj - call 0 # val - stop - .end -.end getthisprop diff --git a/deps/mozjs/js/src/jit-test/README b/deps/mozjs/js/src/jit-test/README index ddde38dc962..4034ea8fb0a 100644 --- a/deps/mozjs/js/src/jit-test/README +++ b/deps/mozjs/js/src/jit-test/README @@ -52,10 +52,10 @@ The general format in EBNF is: cookie ::= "|jit-test|" item ::= flag | attribute - flag ::= "slow" | "allow-oom" + flag ::= "slow" | "allow-oom" | "valgrind" | "mjitalways" | "debug" attribute ::= name ":" value - name ::= "TMFLAGS" | "error" + name ::= "error" | "exitstatus" value ::= The metaline may appear anywhere in the first line of the file: this allows it @@ -66,9 +66,11 @@ The meaning of the items: slow Test runs slowly. Do not run if the --no-slow option is given. allow-oom If the test runs out of memory, it counts as passing. valgrind Run test under valgrind. + mjitalways Run js with -a, whether --jitflags says to or not + debug Run js with -d, whether --jitflags says to or not error The test should be considered to pass iff it throws the given JS exception. - TMFLAGS Set the environment variable TMFLAGS to the given value. + exitstatus The test should exit with the given status value (an integer). * END diff --git a/deps/mozjs/js/src/jit-test/jit_test.py b/deps/mozjs/js/src/jit-test/jit_test.py index 0284d1febdd..76174177c93 100755 --- a/deps/mozjs/js/src/jit-test/jit_test.py +++ b/deps/mozjs/js/src/jit-test/jit_test.py @@ -2,9 +2,10 @@ # jit_test.py -- Python harness for JavaScript trace tests. -import datetime, os, re, sys, tempfile, traceback +import datetime, os, re, sys, tempfile, traceback, time, shlex import subprocess from subprocess import * +from threading import Thread DEBUGGER_INFO = { "gdb": { @@ -49,8 +50,8 @@ def __init__(self, path): self.slow = False # True means the test is slow-running self.allow_oom = False # True means that OOM is not considered a failure self.valgrind = False # True means run under valgrind - self.tmflags = '' # Value of TMFLAGS env var to pass - self.error = '' # Errors to expect and consider passing + self.expect_error = '' # Errors to expect and consider passing + self.expect_status = 0 # Exit status to expect from shell def copy(self): t = Test(self.path) @@ -58,8 +59,8 @@ def copy(self): t.slow = self.slow t.allow_oom = self.allow_oom t.valgrind = self.valgrind - t.tmflags = self.tmflags - t.error = self.error + t.expect_error = self.expect_error + t.expect_status = self.expect_status return t COOKIE = '|jit-test|' @@ -80,10 +81,13 @@ def from_file(cls, path, options): name, _, value = part.partition(':') if value: value = value.strip() - if name == 'TMFLAGS': - test.tmflags = value - elif name == 'error': - test.error = value + if name == 'error': + test.expect_error = value + elif name == 'exitstatus': + try: + test.expect_status = int(value, 0); + except ValueError: + print("warning: couldn't parse exit status %s"%value) else: print('warning: unrecognized |jit-test| attribute %s'%part) else: @@ -97,6 +101,8 @@ def from_file(cls, path, options): test.jitflags.append('-a') elif name == 'debug': test.jitflags.append('-d') + elif name == 'mjit': + test.jitflags.append('-m') else: print('warning: unrecognized |jit-test| attribute %s'%part) @@ -122,22 +128,24 @@ def find_tests(dir, substring = None): ans.append(test) return ans -def get_test_cmd(path, jitflags, lib_dir): +def get_test_cmd(path, jitflags, lib_dir, shell_args): libdir_var = lib_dir if not libdir_var.endswith('/'): libdir_var += '/' expr = "const platform=%r; const libdir=%r;"%(sys.platform, libdir_var) # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. - return [ JS ] + list(set(jitflags)) + [ '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), - '-f', path ] + return ([ JS ] + list(set(jitflags)) + shell_args + + [ '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), '-f', path ]) -def run_cmd(cmdline, env): - # close_fds is not supported on Windows and will cause a ValueError. - close_fds = sys.platform != 'win32' - p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds, env=env) - out, err = p.communicate() - return out.decode(), err.decode(), p.returncode +def set_limits(): + # resource module not supported on all platforms + try: + import resource + GB = 2**30 + resource.setrlimit(resource.RLIMIT_AS, (1*GB, 1*GB)) + except: + return def tmppath(token): fd, path = tempfile.mkstemp(prefix=token) @@ -151,21 +159,52 @@ def read_and_unlink(path): os.unlink(path) return d -def run_cmd_avoid_stdio(cmdline, env): +def th_run_cmd(cmdline, options, l): + # close_fds and preexec_fn are not supported on Windows and will + # cause a ValueError. + if sys.platform != 'win32': + options["close_fds"] = True + options["preexec_fn"] = set_limits + p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, **options) + + l[0] = p + out, err = p.communicate() + l[1] = (out, err, p.returncode) + +def run_timeout_cmd(cmdline, options, timeout=60.0): + l = [ None, None ] + timed_out = False + th = Thread(target=th_run_cmd, args=(cmdline, options, l)) + th.start() + th.join(timeout) + while th.isAlive(): + if l[0] is not None: + try: + # In Python 3, we could just do l[0].kill(). + import signal + if sys.platform != 'win32': + os.kill(l[0].pid, signal.SIGKILL) + time.sleep(.1) + timed_out = True + except OSError: + # Expecting a "No such process" error + pass + th.join() + (out, err, code) = l[1] + return (out, err, code, timed_out) + +def run_cmd(cmdline, env, timeout): + return run_timeout_cmd(cmdline, { 'env': env }, timeout) + +def run_cmd_avoid_stdio(cmdline, env, timeout): stdoutPath, stderrPath = tmppath('jsstdout'), tmppath('jsstderr') env['JS_STDOUT'] = stdoutPath env['JS_STDERR'] = stderrPath - # close_fds is not supported on Windows and will cause a ValueError. - close_fds = sys.platform != 'win32' - p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds, env=env) - _, __ = p.communicate() - return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), p.returncode - -def run_test(test, lib_dir): - env = os.environ.copy() - if test.tmflags: - env['TMFLAGS'] = test.tmflags - cmd = get_test_cmd(test.path, test.jitflags, lib_dir) + _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout) + return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code + +def run_test(test, lib_dir, shell_args): + cmd = get_test_cmd(test.path, test.jitflags, lib_dir, shell_args) if (test.valgrind and any([os.path.exists(os.path.join(d, 'valgrind')) @@ -183,9 +222,10 @@ def run_test(test, lib_dir): print(subprocess.list2cmdline(cmd)) if OPTIONS.avoid_stdio: - out, err, code = run_cmd_avoid_stdio(cmd, env) + run = run_cmd_avoid_stdio else: - out, err, code = run_cmd(cmd, env) + run = run_cmd + out, err, code, timed_out = run(cmd, os.environ, OPTIONS.timeout) if OPTIONS.show_output: sys.stdout.write(out) @@ -193,12 +233,12 @@ def run_test(test, lib_dir): sys.stdout.write('Exit code: %s\n' % code) if test.valgrind: sys.stdout.write(err) - return (check_output(out, err, code, test.allow_oom, test.error), - out, err, code) + return (check_output(out, err, code, test), + out, err, code, timed_out) -def check_output(out, err, rc, allow_oom, expectedError): - if expectedError: - return expectedError in err +def check_output(out, err, rc, test): + if test.expect_error: + return test.expect_error in err for line in out.split('\n'): if line.startswith('Trace stats check failed'): @@ -208,10 +248,10 @@ def check_output(out, err, rc, allow_oom, expectedError): if 'Assertion failed:' in line: return False - if rc != 0: + if rc != test.expect_status: # Allow a non-zero exit code if we want to allow OOM, but only if we # actually got OOM. - return allow_oom and ': out of memory' in err + return test.allow_oom and ': out of memory' in err return True @@ -222,26 +262,29 @@ def print_tinderbox(label, test, message=None): result += ": " + message print result -def run_tests(tests, test_dir, lib_dir): +def run_tests(tests, test_dir, lib_dir, shell_args): pb = None if not OPTIONS.hide_progress and not OPTIONS.show_cmd: try: from progressbar import ProgressBar - pb = ProgressBar('', len(tests), 16) + pb = ProgressBar('', len(tests), 24) except ImportError: pass failures = [] + timeouts = 0 complete = False doing = 'before starting' try: for i, test in enumerate(tests): doing = 'on %s'%test.path - ok, out, err, code = run_test(test, lib_dir) + ok, out, err, code, timed_out = run_test(test, lib_dir, shell_args) doing = 'after %s'%test.path if not ok: - failures.append([ test, out, err, code ]) + failures.append([ test, out, err, code, timed_out ]) + if timed_out: + timeouts += 1 if OPTIONS.tinderbox: if ok: @@ -257,7 +300,7 @@ def run_tests(tests, test_dir, lib_dir): n = i + 1 if pb: - pb.label = '[%4d|%4d|%4d]'%(n - len(failures), len(failures), n) + pb.label = '[%4d|%4d|%4d|%4d]'%(n - len(failures), len(failures), timeouts, n) pb.update(n) complete = True except KeyboardInterrupt: @@ -272,7 +315,7 @@ def run_tests(tests, test_dir, lib_dir): out = open(OPTIONS.write_failures, 'w') # Don't write duplicate entries when we are doing multiple failures per job. written = set() - for test, fout, ferr, fcode in failures: + for test, fout, ferr, fcode, _ in failures: if test.path not in written: out.write(os.path.relpath(test.path, test_dir) + '\n') if OPTIONS.write_failure_output: @@ -287,12 +330,22 @@ def run_tests(tests, test_dir, lib_dir): traceback.print_exc() sys.stderr.write('---\n') - print('FAILURES:') - for test, _, __, ___ in failures: + def show_test(test): if OPTIONS.show_failed: - print(' ' + subprocess.list2cmdline(get_test_cmd(test.path, test.jitflags, lib_dir))) + print(' ' + subprocess.list2cmdline(get_test_cmd(test.path, test.jitflags, lib_dir, shell_args))) else: print(' ' + ' '.join(test.jitflags + [ test.path ])) + + print('FAILURES:') + for test, _, __, ___, timed_out in failures: + if not timed_out: + show_test(test) + + print('TIMEOUTS:') + for test, _, __, ___, timed_out in failures: + if timed_out: + show_test(test) + return False else: print('PASSED ALL' + ('' if complete else ' (partial run -- interrupted by user %s)'%doing)) @@ -303,7 +356,7 @@ def parse_jitflags(): for flags in OPTIONS.jitflags.split(',') ] for flags in jitflags: for flag in flags: - if flag not in ('-j', '-m', '-a', '-p', '-d'): + if flag not in ('-m', '-a', '-p', '-d', '-n'): print('Invalid jit flag: "%s"'%flag) sys.exit(1) return jitflags @@ -347,10 +400,14 @@ def main(argv): help='exclude given test dir or path') op.add_option('--no-slow', dest='run_slow', action='store_false', help='do not run tests marked as slow') + op.add_option('-t', '--timeout', dest='timeout', type=float, default=150.0, + help='set test timeout in seconds') op.add_option('--no-progress', dest='hide_progress', action='store_true', help='hide progress bar') op.add_option('--tinderbox', dest='tinderbox', action='store_true', help='Tinderbox-parseable output format') + op.add_option('--args', dest='shell_args', default='', + help='extra args to pass to the JS shell') op.add_option('-w', '--write-failures', dest='write_failures', metavar='FILE', help='Write a list of failed tests to [FILE]') op.add_option('-r', '--read-tests', dest='read_tests', metavar='FILE', @@ -363,8 +420,8 @@ def main(argv): help='Enable the |valgrind| flag, if valgrind is in $PATH.') op.add_option('--valgrind-all', dest='valgrind_all', action='store_true', help='Run all tests with valgrind, if valgrind is in $PATH.') - op.add_option('--jitflags', dest='jitflags', default='mjp', - help='Example: --jitflags=j,mj,mjp to run each test with -j, -m -j, -m -j -p [default=%default]') + op.add_option('--jitflags', dest='jitflags', default='m,mn', + help='Example: --jitflags=m,mn to run each test with -m, -m -n [default=%default]') op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', help='Use js-shell file indirection instead of piping stdio.') op.add_option('--write-failure-output', dest='write_failure_output', action='store_true', @@ -442,6 +499,8 @@ def main(argv): job_list.append(new_test) + shell_args = shlex.split(OPTIONS.shell_args) + if OPTIONS.debug: if len(job_list) > 1: print('Multiple tests match command line arguments, debugger can only run one') @@ -450,12 +509,12 @@ def main(argv): sys.exit(1) tc = job_list[0] - cmd = [ 'gdb', '--args' ] + get_test_cmd(tc.path, tc.jitflags, lib_dir) + cmd = [ 'gdb', '--args' ] + get_test_cmd(tc.path, tc.jitflags, lib_dir, shell_args) call(cmd) sys.exit() try: - ok = run_tests(job_list, test_dir, lib_dir) + ok = run_tests(job_list, test_dir, lib_dir, shell_args) if not ok: sys.exit(2) except OSError: diff --git a/deps/mozjs/js/src/jit-test/lib/asserts.js b/deps/mozjs/js/src/jit-test/lib/asserts.js new file mode 100644 index 00000000000..2b33e9ce4d7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/lib/asserts.js @@ -0,0 +1,36 @@ + +if (typeof assertThrowsInstanceOf === 'undefined') { + var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) { + var fullmsg; + try { + f(); + } catch (exc) { + if (exc instanceof ctor) + return; + fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); + }; +} + +if (typeof assertThrowsValue === 'undefined') { + var assertThrowsValue = function assertThrowsValue(f, val, msg) { + var fullmsg; + try { + f(); + } catch (exc) { + if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val)) + return; + fullmsg = "Assertion failed: expected exception " + val + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); + }; +} diff --git a/deps/mozjs/js/src/jit-test/lib/prolog.js b/deps/mozjs/js/src/jit-test/lib/prolog.js index 5d577b39bda..84ab993cb5d 100644 --- a/deps/mozjs/js/src/jit-test/lib/prolog.js +++ b/deps/mozjs/js/src/jit-test/lib/prolog.js @@ -1,43 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -const HAVE_TM = 'tracemonkey' in this; - -const HOTLOOP = HAVE_TM ? tracemonkey.HOTLOOP : 8; -const RECORDLOOP = HOTLOOP; -const RUNLOOP = HOTLOOP + 1; - -var checkStats; -if (HAVE_TM) { - checkStats = function(stats) - { - // Temporarily disabled while we work on heuristics. - return; - function jit(on) - { - if (on && !options().match(/tracejit/)) - { - options('tracejit'); - } - else if (!on && options().match(/tracejit/)) - { - options('tracejit'); - } - } - - jit(false); - for (var name in stats) { - var expected = stats[name]; - var actual = tracemonkey[name]; - if (expected != actual) { - print('Trace stats check failed: got ' + actual + ', expected ' + expected + ' for ' + name); - } - } - jit(true); - }; -} else { - checkStats = function() {}; -} - var appendToActual = function(s) { actual += s + ','; } diff --git a/deps/mozjs/js/src/jit-test/lib/referencesVia.js b/deps/mozjs/js/src/jit-test/lib/referencesVia.js new file mode 100644 index 00000000000..da90d51937d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/lib/referencesVia.js @@ -0,0 +1,26 @@ +function referencesVia(from, edge, to) { + if (typeof findReferences !== 'function') + return true; + + edge = "edge: " + edge; + var edges = findReferences(to); + if (edge in edges && edges[edge].indexOf(from) != -1) + return true; + + // Be nice: make it easy to fix if the edge name has just changed. + var alternatives = []; + for (var e in edges) { + if (edges[e].indexOf(from) != -1) + alternatives.push(e); + } + if (alternatives.length == 0) { + print("referent not referred to by referrer after all"); + } else { + print("referent is not referenced via: " + uneval(edge)); + print("but it is referenced via: " + uneval(alternatives)); + } + print("all incoming edges, from any object:"); + for (var e in edges) + print(e); + return false; +} diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/bug633020.js b/deps/mozjs/js/src/jit-test/tests/arguments/bug633020.js index 5f9c18ca999..6e30bd1250d 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/bug633020.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/bug633020.js @@ -1,4 +1,4 @@ -var N = HOTLOOP + 2; +var N = 10; function f(b) { var a = []; for (var i = 0; i < N; i++) diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/e4x-descendants-with-arguments.js b/deps/mozjs/js/src/jit-test/tests/arguments/e4x-descendants-with-arguments.js new file mode 100644 index 00000000000..fc64ffd8144 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/arguments/e4x-descendants-with-arguments.js @@ -0,0 +1,6 @@ +function f() +{ + var x = <>; + x..arguments; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-flushstack.js b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-flushstack.js index b6094d4122c..257018ba3f5 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-flushstack.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-flushstack.js @@ -9,7 +9,7 @@ function test() "use strict"; eval("args = arguments;"); var a = []; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) a.push(arguments); return a; } @@ -17,11 +17,11 @@ function test() var a = test(); assertEq(Array.isArray(a), true); -assertEq(a.length, RUNLOOP); +assertEq(a.length, 9); var count = 0; a.forEach(function(v, i) { count++; assertEq(v, args); }); -assertEq(count, RUNLOOP); +assertEq(count, 9); assertEq(Object.prototype.toString.call(args), "[object Arguments]"); assertEq(args.length, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js index ea754671617..8370f4602be 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js @@ -8,7 +8,7 @@ function upToTen() { "use strict"; eval("args = arguments;"); - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) yield i; } @@ -21,7 +21,7 @@ for (var v in gen) i++; } -assertEq(i, RUNLOOP); +assertEq(i, 9); assertEq(Object.prototype.toString.call(args), "[object Arguments]"); assertEq(args.length, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args.js b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args.js index 82682ff2aa7..c6d68b2b1ff 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/strict-args.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/strict-args.js @@ -13,7 +13,7 @@ function strictArgs(a) } var a1, a2, a3; -for (var i = 0; i < HOTLOOP+1; i++) +for (var i = 0; i < 9; i++) { a1 = strictArgs(); a2 = strictArgs(1); diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg1.js b/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg1.js index 3603dd47825..a6b5eee3264 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg1.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg1.js @@ -7,7 +7,7 @@ function f(x,y,z) { assertEq(o[2] == undefined, true); } -for (var i = 0; i < HOTLOOP+2; ++i) { +for (var i = 0; i < 10; ++i) { print(i); f(1,2,3) } diff --git a/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg2.js b/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg2.js index 7b4bce8b488..09fcf1f57d6 100644 --- a/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg2.js +++ b/deps/mozjs/js/src/jit-test/tests/arguments/testDelArg2.js @@ -2,7 +2,7 @@ function f(del) { o = arguments; if (del) delete o[2]; - for (var i = 0; i < HOTLOOP+2; ++i) + for (var i = 0; i < 10; ++i) assertEq(o[2] == undefined, del); } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/Proxy-function-freeze.js b/deps/mozjs/js/src/jit-test/tests/basic/Proxy-function-freeze.js new file mode 100644 index 00000000000..5387da2fd3f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/Proxy-function-freeze.js @@ -0,0 +1,115 @@ +// Once a function proxy has been frozen, its handler's traps are no longer called, +// but its call traps are. + +var callTrapCalls; +function handleCall() { + print('call'); + callTrapCalls++; + return 'favor'; +} +function handleConstruct() { + print('construct'); + callTrapCalls++; + return 'compliment'; +} + +var descriptorForX = { configurable: true, enumerable: true, writable: true, value: 42 }; +var trapCalls; +var handler = { + getOwnPropertyNames: function () { + print('getOwnPropertyNames'); + trapCalls++; + return ['x']; + }, + getPropertyNames: function() { + print('getPropertyNames'); + trapCalls++; + return ['x']; + }, + getOwnPropertyDescriptor: function(name) { + print('getOwnPropertyDescriptor'); + trapCalls++; + assertEq(name, 'x'); + return descriptorForX; + }, + getPropertyDescriptor: function(name) { + print('getPropertyDescriptor'); + trapCalls++; + assertEq(name, 'x'); + return descriptorForX; + }, + defineProperty: function(name, propertyDescriptor) { + print('defineProperty'); + trapCalls++; + }, + delete: function(name) { + print('delete'); + trapCalls++; + return false; + }, + fix: function() { + print('fix'); + trapCalls++; + return { x: descriptorForX }; + } +}; + +var fp = Proxy.createFunction(handler, handleCall, handleConstruct); + +trapCalls = callTrapCalls = 0; +assertEq(Object.getOwnPropertyNames(fp)[0], 'x'); +assertEq(trapCalls > 0, true); +assertEq(callTrapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(Object.getOwnPropertyDescriptor(fp, 'x').value, 42); +assertEq(trapCalls > 0, true); +assertEq(callTrapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(delete fp.x, false); +assertEq(trapCalls > 0, true); +assertEq(callTrapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(fp(), 'favor'); +assertEq(trapCalls, 0); +assertEq(callTrapCalls, 1); + +trapCalls = callTrapCalls = 0; +assertEq(new fp, 'compliment'); +assertEq(trapCalls, 0); +assertEq(callTrapCalls, 1); + +trapCalls = callTrapCalls = 0; +Object.freeze(fp); +assertEq(trapCalls > 0, true); +assertEq(callTrapCalls, 0); + +// Once the proxy has been frozen, its traps should never be invoked any +// more. +trapCalls = callTrapCalls = 0; +assertEq(Object.getOwnPropertyNames(fp)[0], 'x'); +assertEq(trapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(Object.getOwnPropertyDescriptor(fp, 'x').value, 42); +assertEq(trapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(delete fp.x, false); +assertEq(trapCalls, 0); + +trapCalls = callTrapCalls = 0; +assertEq(fp(), 'favor'); +assertEq(trapCalls, 0); +assertEq(callTrapCalls, 1); + +trapCalls = callTrapCalls = 0; +assertEq(new fp, 'compliment'); +assertEq(trapCalls, 0); +assertEq(callTrapCalls, 1); + +trapCalls = callTrapCalls = 0; +Object.freeze(fp); +assertEq(trapCalls, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js b/deps/mozjs/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js new file mode 100644 index 00000000000..35767eb4022 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js @@ -0,0 +1,9 @@ +try { } +catch (e) { } + +try { throw 2; } +catch (e) +{ + try { throw 3; } + catch (e2) { } +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/arith.js b/deps/mozjs/js/src/jit-test/tests/basic/arith.js deleted file mode 100644 index b7f551dccd0..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/arith.js +++ /dev/null @@ -1,11 +0,0 @@ -// |jit-test| TMFLAGS: full,fragprofile,treevis - -function arith() -{ - var accum = 0; - for (var i = 0; i < 100; i++) { - accum += (i * 2) - 1; - } - return accum; -} -assertEq(arith(), 9800); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/arrayConcat.js b/deps/mozjs/js/src/jit-test/tests/basic/arrayConcat.js new file mode 100644 index 00000000000..17ad8771139 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/arrayConcat.js @@ -0,0 +1,18 @@ + +/* Test concat compiler paths. */ + +for (var i = 9; i < 10; i++) + assertEq([2].concat([3])[0], 2); + +function f(a, b) { + return a.concat(b)[0]; +} +function g() { + var x = []; + var y = [1]; + for (var i = 0; i < 50; i++) + assertEq(f(x, y), 1); + eval('y[0] = "three"'); + assertEq(f(x, y), "three"); +} +g(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/arrayNatives.js b/deps/mozjs/js/src/jit-test/tests/basic/arrayNatives.js new file mode 100644 index 00000000000..5bb2099b2ce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/arrayNatives.js @@ -0,0 +1,18 @@ + +/* Array natives applied to non-arrays. */ + +var oa = {}; +Array.pop(oa); +assertEq(oa.length, 0); + +var ob = {}; +Array.push(ob, "twelve"); +assertEq(ob.length, 1); + +var oc = {}; +Array.shift(oc); +assertEq(oc.length, 0); + +var od = {}; +Array.unshift(od, "eight"); +assertEq(od.length, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/arrayPopShift.js b/deps/mozjs/js/src/jit-test/tests/basic/arrayPopShift.js new file mode 100644 index 00000000000..9213f3c9ac9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/arrayPopShift.js @@ -0,0 +1,30 @@ + +/* Test pop/shift compiler paths. */ + +function a() { + var x = []; + for (var i = 0; i < 50; i++) + x.push(i); + for (var j = 0; j < 100; j++) { + var z = x.shift(); + if (j < 50) + assertEq(z, j); + else + assertEq(z, undefined); + } +} +a(); + +function b() { + var x = []; + for (var i = 0; i < 50; i++) + x.push(i); + for (var j = 0; j < 100; j++) { + var z = x.pop(); + if (j < 50) + assertEq(z, 49 - j); + else + assertEq(z, undefined); + } +} +b(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/arrayProto.js b/deps/mozjs/js/src/jit-test/tests/basic/arrayProto.js new file mode 100644 index 00000000000..0186d161e63 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/arrayProto.js @@ -0,0 +1,12 @@ + +for (var i = 0; i < 15; i++) { + var x = Object.create([]); + assertEq(x.length, 0); +} + +for (var i = 0; i < 15; i++) { + function foo() {} + foo.prototype = []; + var x = new foo(); + assertEq(x.length, 0); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bigLoadStoreDisp.js b/deps/mozjs/js/src/jit-test/tests/basic/bigLoadStoreDisp.js index 528ba254b3c..4f0df81550d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bigLoadStoreDisp.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bigLoadStoreDisp.js @@ -8,7 +8,7 @@ // 16-bit value it will truncate to 14452 (because 79988 - 65536 == 14452). // This means that the increments in the second loop will be done to one of // the array elements instead of x.y. And so x.y's final value will be -// (99 + HOTLOOP) instead of 1099. +// (99 + 8) instead of 1099. // // Note that setting x.y to 99 and checking its value at the end will // access the correct location because those lines are interpreted. Phew. diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bindname-in-strict-eval.js b/deps/mozjs/js/src/jit-test/tests/basic/bindname-in-strict-eval.js index fa231022d30..e052a65dd53 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bindname-in-strict-eval.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bindname-in-strict-eval.js @@ -1,3 +1,3 @@ 'use strict'; -eval("var i = 0; var end = RUNLOOP; for(var j = 0; j < end; i++, j++) { i = 0; }"); +eval("var i = 0; var end = 9; for(var j = 0; j < end; i++, j++) { i = 0; }"); print("done"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug489098.js b/deps/mozjs/js/src/jit-test/tests/basic/bug489098.js deleted file mode 100644 index 6767ba63442..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug489098.js +++ /dev/null @@ -1,18 +0,0 @@ -// Check that the loop is trace-compiled even though it's run in an eval. - -code = "\ -j = 0;\ -for (i = 0; i < 10; i++)\ -{\ - j += 5;\ -}\ -"; - -eval(code); -print (j); - -checkStats({ -recorderStarted: 1, -recorderAborted: 0, -traceCompleted: 1, -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug510437.js b/deps/mozjs/js/src/jit-test/tests/basic/bug510437.js index 00f5d8d62f3..2418b9b117d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug510437.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug510437.js @@ -1,5 +1,6 @@ // Don't crash or assert. +var d; this.watch("d", eval); (function () { (eval("\ diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug522136.js b/deps/mozjs/js/src/jit-test/tests/basic/bug522136.js index 9267c99ccfa..c6e71053a9e 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug522136.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug522136.js @@ -1,10 +1,11 @@ var Q = 0; +var thrown = false; try { - (function f(i) { Q = i; if (i == 100000) return; f(i+1); })(1) + (function f(i) { Q = i; if (i == 200000) return; f(i+1); })(1) } catch (e) { + thrown = true; } // Exact behavior of recursion check depends on which JIT we use. -var ok = (Q == 3000 || Q == 3001); -assertEq(ok, true); +assertEq(thrown && Q > 10000, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug541191-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug541191-2.js index 5be269a0608..1d232dc332a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug541191-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug541191-2.js @@ -12,7 +12,7 @@ function f(a, b, c) { if (x) arguments.length = 4; var k; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) k = g.apply(this, arguments); return k; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug557168-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug557168-1.js index fedcafbbee0..cf417397411 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug557168-1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug557168-1.js @@ -3,7 +3,7 @@ try { Function("\ (function f() {\ ({x:{b}}=x);\ - f()\ + f.apply(null, new Array(100))\ })()\ ")() } catch (e) { diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug557168-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug557168-2.js index 8221f91769b..8b57537b3b9 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug557168-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug557168-2.js @@ -3,7 +3,7 @@ try { Function("\ (function f() {\ ({x}=x);\ - f()\ + f.apply(null, new Array(100))\ })()\ ")() } catch (e) { diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug561359-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug561359-1.js new file mode 100644 index 00000000000..4439174e06d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug561359-1.js @@ -0,0 +1,4 @@ +for (let z = 0; z < 2; z++) { + with ({x: function () {}}) + f = x; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug561359-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug561359-2.js new file mode 100644 index 00000000000..017b9bf3384 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug561359-2.js @@ -0,0 +1,8 @@ +function f(s) { + var obj = {m: function () { return a; }}; + eval(s); + return obj; +} +var obj = f("var a = 'right';"); +var a = 'wrong'; +assertEq(obj.m(), 'right'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug563125.js b/deps/mozjs/js/src/jit-test/tests/basic/bug563125.js index d5da0521d97..79e43eae4f9 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug563125.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug563125.js @@ -1,5 +1,5 @@ var array = new Array(4294967295); -for (var j = 0; j < RUNLOOP; ++j) { '' + array.length; } +for (var j = 0; j < 9; ++j) { '' + array.length; } // Don't assert. diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug584499-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug584499-2.js index bd8b0ab26f5..bb31a731e33 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug584499-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug584499-2.js @@ -1,7 +1,7 @@ function f(x) { var s = "a"; var last = ""; - for (var i = 0; i < HOTLOOP + 2; i++) { + for (var i = 0; i < 10; i++) { last = s[x]; } return last; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug586917.js b/deps/mozjs/js/src/jit-test/tests/basic/bug586917.js index 5cefe710b81..8365ab0c1a8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug586917.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug586917.js @@ -7,7 +7,7 @@ /* The tracer should properly parse JSOP_TABLESWITCHX instructions. */ var x = 1; -for (i = 0; i < HOTLOOP; i++) { +for (i = 0; i < 8; i++) { switch (x) { case 2: try {} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug590036.js b/deps/mozjs/js/src/jit-test/tests/basic/bug590036.js new file mode 100644 index 00000000000..25add41ff1b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug590036.js @@ -0,0 +1,13 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +load(libdir + 'eqArrayHelper.js'); + +assertEqArray(eval('[]'), []); +assertEqArray(eval('[,]'), [,]); +assertEqArray(eval('[,,]'), [,,]); +assertEqArray(eval('[1, 1, ]'), [1,1, ]); +assertEqArray(eval('[1, 1, true]'), [1, 1, true]); +assertEqArray(eval('[1, false, true]'), [1, false, true]); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug592927.js b/deps/mozjs/js/src/jit-test/tests/basic/bug592927.js index b6412c6a7c2..dc74a5af906 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug592927.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug592927.js @@ -13,13 +13,13 @@ function g(x) { function f2(x, y) { arguments; x(f2); - assertEq(y, "bye"); + assertEq(y, "hello"); } function g2(x) { assertEq(x.arguments[1], "hello"); x.arguments[1] = "bye"; - assertEq(x.arguments[1], "bye"); + assertEq(x.arguments[1], "hello"); } f(g, "hello"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug596502-version.js b/deps/mozjs/js/src/jit-test/tests/basic/bug596502-version.js index dbc4344b6f3..5508328a418 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug596502-version.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug596502-version.js @@ -15,7 +15,7 @@ revertVersion(); for (vno in {160: null, 170: null, 180: null}) { print('Setting version to: ' + vno); - version(vno); + version(Number(vno)); assertEq(syntaxErrorFromXML(), false); revertVersion(); } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug609502-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug609502-1.js index 72cf9650c76..a62316d6047 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug609502-1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug609502-1.js @@ -1,8 +1,8 @@ -for(var i = 0; i < RUNLOOP; i++) { +for(var i = 0; i < 9; i++) { x = ''.charCodeAt(NaN); } -for(var i = 0; i < RUNLOOP; i++) { +for(var i = 0; i < 9; i++) { x = ''.charAt(NaN); } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug609502-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug609502-2.js index 7998d843374..4c1469dcea5 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug609502-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug609502-2.js @@ -1,4 +1,4 @@ -for (var i = 0; i < RUNLOOP; i++) { +for (var i = 0; i < 9; i++) { Math.abs(-2147483648) } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug613122.js b/deps/mozjs/js/src/jit-test/tests/basic/bug613122.js index a28b40f29ec..64c6b068835 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug613122.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug613122.js @@ -1,4 +1,4 @@ var a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; a.push(1); -a.splice(0); +a.splice(0, a.length); a.d = ""; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug614688.js b/deps/mozjs/js/src/jit-test/tests/basic/bug614688.js new file mode 100644 index 00000000000..c075cadab8f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug614688.js @@ -0,0 +1,7 @@ +function Foo() { + u = 0; +} + +var x = new Foo(); +assertEq(Object.getPrototypeOf(x) === Foo.prototype, true); +assertEq(Object.getPrototypeOf(x) === Object.prototype, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug617745.js b/deps/mozjs/js/src/jit-test/tests/basic/bug617745.js new file mode 100644 index 00000000000..f7f60e20eba --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug617745.js @@ -0,0 +1,4 @@ + +var array1 = ['0']; +var array2 = (new Array(1)).splice(0,0, array1); +assertEq("" + array2, ""); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug618853.js b/deps/mozjs/js/src/jit-test/tests/basic/bug618853.js new file mode 100644 index 00000000000..9c80b7fee2b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug618853.js @@ -0,0 +1,12 @@ + +try { new isNaN; } catch (e) {} +new Array; +new Boolean; +new Date; +new Number; +new Object; +new String; +try { new [].push(4); } catch (e) {} +try { new [1,2,3].pop(); } catch (e) {} +try { new "a,b,c".split(","); } catch (e) {} +try { new Array.concat(3); } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug619004.js b/deps/mozjs/js/src/jit-test/tests/basic/bug619004.js index 8c7cec43917..90d3bca9931 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug619004.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug619004.js @@ -1,4 +1,3 @@ -// don't crash -gczeal(2) -evalcx('split') - +// don't crash +gczeal(2); +evalcx('lazy'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug619338.js b/deps/mozjs/js/src/jit-test/tests/basic/bug619338.js new file mode 100644 index 00000000000..2548c0c7102 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug619338.js @@ -0,0 +1 @@ +try { new Function.prototype } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug621487.js b/deps/mozjs/js/src/jit-test/tests/basic/bug621487.js new file mode 100644 index 00000000000..ebe92e3d53c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug621487.js @@ -0,0 +1,4 @@ + +(function() { while (0) { var a, a = b; } })(); + +(function(b) { do { var a, a = b; } while (0); assertEq(a, 10); })(10); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug625399.js b/deps/mozjs/js/src/jit-test/tests/basic/bug625399.js index e45cf21d8d8..01b616df400 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug625399.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug625399.js @@ -3,5 +3,5 @@ function a(bb) { return; this.d = function() { bb; }; } -for (var i = 0; i <= RUNLOOP; i++) +for (var i = 0; i <= 9; i++) a(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug626398.js b/deps/mozjs/js/src/jit-test/tests/basic/bug626398.js index 8c93511c84b..f9571e93331 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug626398.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug626398.js @@ -13,7 +13,7 @@ function g(n) { return s; } -// Do it twice with different initial values for 'i' to allow for HOTLOOP +// Do it twice with different initial values for 'i' to allow for 8 // being even or odd. var s = ""; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug627692-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug627692-1.js index a8952883c9e..9e41eb4efd4 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug627692-1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug627692-1.js @@ -1,7 +1,7 @@ var loop1 = '', loop2 = '', actual = ''; var obj = {}; -for (var i = 0; i < HOTLOOP + 2; i++) { +for (var i = 0; i < 10; i++) { obj['a' + i] = i; loop1 += i; loop2 += 'a' + i; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630377.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630377.js index 8fe97e29c0a..5ef8a7528b2 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630377.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug630377.js @@ -1,7 +1,7 @@ var a = []; -for (var i = 0; i < RUNLOOP; i++) +for (var i = 0; i < 9; i++) a[i] = 0; -var b = #1=[x === "0" && (#1#.slow = 1) for (x in a)]; -assertEq(b[0], 1); -for (var i = 1; i < RUNLOOP; i++) +var b = [x === "0" && true for (x in a)]; +assertEq(b[0], true); +for (var i = 1; i < 9; i++) assertEq(b[i], false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-1.js deleted file mode 100644 index 5203d1383d7..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-1.js +++ /dev/null @@ -1,6 +0,0 @@ -Object.defineProperty(Function.prototype, "prototype", {set:function(){}}); -var x; -for (var i = 0; i < HOTLOOP + 2; i++) - x = new Function.prototype; -assertEq(toString.call(x), "[object Object]"); -assertEq(Object.getPrototypeOf(x), Object.prototype); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-2.js deleted file mode 100644 index 4616891b3b3..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-2.js +++ /dev/null @@ -1,6 +0,0 @@ -Function.prototype.prototype = function () {}; -var x; -for (var i = 0; i < HOTLOOP + 2; i++) - x = new Function.prototype; -assertEq(toString.call(x), "[object Object]"); -assertEq(Object.getPrototypeOf(x), Function.prototype.prototype); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-3.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-3.js deleted file mode 100644 index 5933b2cb312..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-3.js +++ /dev/null @@ -1,12 +0,0 @@ -var a = []; -function next() { - var x = {}; - a.push(x); - return x; -} -Object.defineProperty(Function.prototype, 'prototype', {get: next}); -var b = []; -for (var i = 0; i < HOTLOOP + 2; i++) - b[i] = new Function.prototype; -for (var i = 0; i < HOTLOOP + 2; i++) - assertEq(Object.getPrototypeOf(b[i]), a[i]); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-4.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-4.js deleted file mode 100644 index fd9f8b5e2bb..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-4.js +++ /dev/null @@ -1,10 +0,0 @@ -Object.defineProperty(Function.prototype, 'prototype', - {get: function () { if (i == HOTLOOP + 1) throw "X"; }}); -var x; -try { - for (var i = 0; i < HOTLOOP + 2; i++) - x = new Function.prototype; -} catch (exc) { - assertEq(i, HOTLOOP + 1); - assertEq(exc, "X"); -} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-5.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-5.js index 7057ab29fe1..67fdb7e5b0a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-5.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-5.js @@ -5,9 +5,9 @@ function C(a, b) { var f = C.bind(null, 2); Object.defineProperty(f, "prototype", {get: function () { throw "FAIL"; }}); var x; -for (var i = 0; i < HOTLOOP + 2; i++) +for (var i = 0; i < 10; i++) x = new f(i); assertEq(toString.call(x), "[object Object]"); assertEq(Object.getPrototypeOf(x), C.prototype); assertEq(x.a, 2); -assertEq(x.b, HOTLOOP + 1); +assertEq(x.b, 9); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-6.js b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-6.js index 2080726ddcf..0532101bd0f 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug630865-6.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug630865-6.js @@ -1,11 +1,11 @@ var a = []; var x, i; -for (i = 0; i < HOTLOOP + 10; i++) { +for (i = 0; i < 18; i++) { a[i] = function (b) { this.b = b; }; - if (i != HOTLOOP + 9) + if (i != 17) x = a[i].prototype; } -for (i = 0; i < HOTLOOP + 10; i++) +for (i = 0; i < 18; i++) x = new a[i]; assertEq(toString.call(x), "[object Object]"); -assertEq(Object.getPrototypeOf(x), a[HOTLOOP + 9].prototype); +assertEq(Object.getPrototypeOf(x), a[17].prototype); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug632964-regexp.js b/deps/mozjs/js/src/jit-test/tests/basic/bug632964-regexp.js index 7151d371364..75612dbc735 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/bug632964-regexp.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug632964-regexp.js @@ -1,5 +1,3 @@ -// |jit-test| error: InternalError: regular expression too complex - var sText = "s"; for (var i = 0; i < 250000; ++i) @@ -12,6 +10,5 @@ var match = sText.match(/s(\s|.)*?e/gi); //var match = sText.match(/s([\s\S]*?)e/gi); //var match = sText.match(/s(?:[\s\S]*?)e/gi); var end = new Date(); -print(end - start); assertEq(match.length, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug638981.js b/deps/mozjs/js/src/jit-test/tests/basic/bug638981.js new file mode 100644 index 00000000000..944daa76eae --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug638981.js @@ -0,0 +1,4 @@ +/* Don't crash. */ + +delete Function; +Object.getOwnPropertyNames(this); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639126.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639126.js new file mode 100644 index 00000000000..7283fe6ca7e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639126.js @@ -0,0 +1,4 @@ +Array.__proto__ = Array.__proto__; +gc(); +Array["name" + ""]; +Array(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639128.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639128.js new file mode 100644 index 00000000000..fadd559aae8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639128.js @@ -0,0 +1,10 @@ +function f(o) { + Object.seal(o); +} +gc(); +if(2 != 2) { + g = new f(g); +} +with({}) { + f({}); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639311.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639311.js new file mode 100644 index 00000000000..0ee9815388f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639311.js @@ -0,0 +1,18 @@ +/* Avoid use-after-free while sweeping type objects. */ + +try { + Reflparse("") +} catch(e) {} +Reflect.parse("for(var a;a;j){if(a%2==0){c()}}") +try { + (function() { + for (a = 0;; j) { + gc() + } + })() +} catch(e) { + delete this.Math +} +gc() +Reflect.parse("let(x){}") +gc() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639529.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639529.js new file mode 100644 index 00000000000..dccf67e210c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639529.js @@ -0,0 +1,4 @@ +function f() {} +g = wrap(f); +g.__defineGetter__('toString', f.toString); +g.toString; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639591.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639591.js new file mode 100644 index 00000000000..435cc31fc1d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639591.js @@ -0,0 +1,4 @@ +gczeal(2); +var x; +[eval("x")] ? eval("x") : 3; +eval("Object()"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639759.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639759.js new file mode 100644 index 00000000000..ede9713748f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639759.js @@ -0,0 +1,9 @@ +gczeal(2); +function f() { + for(var i=0; i<10; i++) { + [1, [1, 2, 3]]; + eval(""); + } +} +eval("Array(f() ? 0 : 1)"); +eval("Array((eval(\"f()\")) ? true : eval(''))"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639797.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639797.js new file mode 100644 index 00000000000..c1511ba1790 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639797.js @@ -0,0 +1 @@ +Function("with([])const x=0")() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug639807.js b/deps/mozjs/js/src/jit-test/tests/basic/bug639807.js new file mode 100644 index 00000000000..42702b46bd6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug639807.js @@ -0,0 +1,4 @@ + +try { + eval("const[]=*,[x]=r") +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug640078.js b/deps/mozjs/js/src/jit-test/tests/basic/bug640078.js new file mode 100644 index 00000000000..27e5470815b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug640078.js @@ -0,0 +1,4 @@ +eval("\ + try{}\ + catch(w if(function(){})){4067286856}\ +") diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug640203.js b/deps/mozjs/js/src/jit-test/tests/basic/bug640203.js new file mode 100644 index 00000000000..aa066bfb27f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug640203.js @@ -0,0 +1,2 @@ +var x = [,].splice(0); +assertEq(x[0], undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug640993.js b/deps/mozjs/js/src/jit-test/tests/basic/bug640993.js new file mode 100644 index 00000000000..45ce0856a31 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug640993.js @@ -0,0 +1,7 @@ +function f() { + return f; +} +f.__proto__ = null; +gc(); +f(); +new f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641224.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641224.js new file mode 100644 index 00000000000..9c976f047e6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641224.js @@ -0,0 +1,5 @@ +try { +x = evalcx('lazy'); +x.__iterator__ = Object.isFrozen +for each(x in x) {} +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641229.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641229.js new file mode 100644 index 00000000000..27854a8e005 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641229.js @@ -0,0 +1,2 @@ +__defineSetter__("x",Math.max) +Function("({x}=[])")() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641231.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641231.js new file mode 100644 index 00000000000..0e778a02440 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641231.js @@ -0,0 +1 @@ +try { Function("function a(){this(*)}new a")() } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641235.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641235.js new file mode 100644 index 00000000000..8534b53c040 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641235.js @@ -0,0 +1,21 @@ +try { +function g(code) { + code = code.replace(/\/\*DUPTRY\d+\*\//, function(k) { + var n = parseInt(k.substr(8), 10); + return aa("try{}catch(e){}", n); + }); + var f = new Function(code); + f() +} +function aa(s, n) { + if (n == 1) { + return s; + } + var s2 = s + s; + var r = n % 2; + var d = (n - r) / 2; + var m = aa(s2, d); + return r ? m + s : m; +} +g("switch(x){default:case l:/*DUPTRY5338*/case 0:x}"); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641491.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641491.js new file mode 100644 index 00000000000..de6cb510aaa --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641491.js @@ -0,0 +1,19 @@ +function f1() { +} +function f2() { +} +function f3(o) { + f2 = XML.prototype; +} +var key = Object.getOwnPropertyNames(f1)[30]; +if(key) { + f3 = f1[key]; +} +gc(); +gc(); +try { +for(var i=0; i<10; i++) { + delete f2[1]; + f3(function() {}); +} +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641525.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641525.js new file mode 100644 index 00000000000..7ed9cd2e84a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641525.js @@ -0,0 +1,34 @@ + +var o2 = Proxy.create({}); +function f1() {} +function f2() {} +function f4(o) { + var key = Object.getOwnPropertyNames(o)[18]; + o4 = o[key]; + o.prototype = {}; +} +f4(f1); +f4(f1); +f4(f2); +new f2(o2); + +// these will hold only if type inference is enabled. +//assertEq(shapeOf(f1) == shapeOf(f2), false); +//assertEq(shapeOf(f1) == shapeOf(f4), false); + +function factory() { + function foo() {} + foo.x = 0; + return foo; +} + +var fobjs = []; +for (var i = 0; i < 10; i++) { + var of = fobjs[i] = factory(); + if (i > 0) { + assertEq(fobjs[i - 1] === of, false); + assertEq(shapeOf(fobjs[i - 1]), shapeOf(of)); + } +} + +assertEq(shapeOf(fobjs[0]) == shapeOf(f1), false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug641741.js b/deps/mozjs/js/src/jit-test/tests/basic/bug641741.js new file mode 100644 index 00000000000..760f7ec51fe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug641741.js @@ -0,0 +1 @@ +try { eval("var[]=(++false[x])()=[],x") } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642154.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642154.js new file mode 100644 index 00000000000..8518e2f8782 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642154.js @@ -0,0 +1,16 @@ +assertEq(Math.pow(1, undefined), NaN); +assertEq(Math.pow(1, null), 1); +assertEq(Math.pow(1, true), 1); +assertEq(Math.pow(1, false), 1); +assertEq(Math.pow(1, 0), 1); +assertEq(Math.pow(1, -0), 1); +assertEq(Math.pow(1, NaN), NaN); +assertEq(Math.pow(1, {}), NaN); +assertEq(Math.pow(1, {valueOf: function() { return undefined; }}), NaN); + +x = 2.2; +assertEq(Math.pow(x - 1.2, undefined), NaN); + +var y; +assertEq(Math.pow(1, y), NaN); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642161.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642161.js new file mode 100644 index 00000000000..18679dd9ed2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642161.js @@ -0,0 +1 @@ +assertEq(JSON.stringify(0 | "prefix" || Boolean), undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642164.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642164.js new file mode 100644 index 00000000000..6b97e82ea72 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642164.js @@ -0,0 +1,14 @@ +function raisesException(exception) { + return function (code) { + eval(code); + }; +}; +function obj() { + var o = { assertEq: true, y: 1 }; + Object.defineProperty(o, 'x', { writable: false }); + return o; +} +function in_strict_with(expr) { + return "with(obj()) { (function () { 'use strict'; " + expr + " })(); }"; +} +try { assertEq(raisesException(TypeError)(in_strict_with('x++;')), true); } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642206.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642206.js new file mode 100644 index 00000000000..da0d9d74058 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642206.js @@ -0,0 +1,30 @@ +this.__proto__ = null; + +function testLenientAndStrict(code, lenient_pred, strict_pred) { + return (strict_pred("'use strict'; " + code) && + lenient_pred(code)); +} +function raisesException(exception) { + return function (code) { + try { + eval(code); + } catch (actual) { + } + }; +}; +try { +function arr() { + return Object.defineProperty(Object()* delete Object, 0, {writable: false}); +} +assertEq(testLenientAndStrict('var a = arr(); [a.splice(0, 1), a]', + raisesException(TypeError), + raisesException(TypeError)), + true); +} catch (e) {} +ForIn_2(this); +function ForIn_2(object) { + for ( property in object ) { + with ( object ) { + } + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642248.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642248.js new file mode 100644 index 00000000000..8c37b767592 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642248.js @@ -0,0 +1,10 @@ +function test(makeNonArray) { + function C() {} + C.prototype = [] + c = new C(); + c.push("foo"); + return c.length +} +assertEq(test(true), 1); +var a = []; +var b = Object.create(a); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642254.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642254.js new file mode 100644 index 00000000000..a603920fe33 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642254.js @@ -0,0 +1,4 @@ +function f(i) { + for (var n = 0; n < 0; n = i) { } +} +actual = f(.5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642319.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642319.js new file mode 100644 index 00000000000..6be3a4c59d9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642319.js @@ -0,0 +1,10 @@ + +test(); +function test() { + function f() { + function test( ) { summary( summary, test, false ); } + } + f.__proto__ = this; +} +gc(); +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642326.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642326.js new file mode 100644 index 00000000000..3292f9a3de4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642326.js @@ -0,0 +1,4 @@ +this.__proto__ = []; +gczeal(2); +gc(); +var box = evalcx('lazy'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642422.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642422.js new file mode 100644 index 00000000000..bb9cfea83ae --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642422.js @@ -0,0 +1,4 @@ +gczeal(2); +var x; +var foo = "for (var z = 0; z < 2; ++z) { new Object(new String(this), x)}"; +eval(foo); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642569.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642569.js new file mode 100644 index 00000000000..6f84492d8bd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642569.js @@ -0,0 +1,13 @@ +function main() { + var v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, + v43, v44, v45, v46, v47; + var v48 = 0, v49 = 0; + if (true) { + var v50 = v48 - 1; + var v51 = v49 + 1; + return v51; + } +} +assertEq(main(), 1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642592.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642592.js new file mode 100644 index 00000000000..57e40facac9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642592.js @@ -0,0 +1,2 @@ +var strings = new Array(); +strings[0x7fffffff] = 0; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642758.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642758.js new file mode 100644 index 00000000000..e92e657309a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642758.js @@ -0,0 +1,4 @@ +function Integer( value, exception ) { } +try { +new Integer( Math.LN2, ++INVALID_INTEGER_VALUE? exception+1.1: 1900 ); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642894.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642894.js new file mode 100644 index 00000000000..5de6f3597e1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642894.js @@ -0,0 +1,9 @@ + +function foo() { + var x = {}; + x.__proto__ = function() { return 0 } + return x; +} +var a = foo(); +var b = foo(); +assertEq(a.__proto__ === b.__proto__, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642985-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642985-1.js new file mode 100644 index 00000000000..fb90691a945 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642985-1.js @@ -0,0 +1,23 @@ +gczeal(2); +function complex(aReal, aImag) {} +function mandelbrotValueOO (aC, aIterMax) { + for (var iter = 0; iter < aIterMax; iter++) { } +} +function f(trace) { + const width = 5; + const height = 5; + const max_iters = 5; + var output = []; + for (let img_x = 0; img_x < width; img_x++) { + for (let img_y = 0; img_y < height; img_y++) { + let C = new complex(-2 + (img_x / width) * 3, + -1.5 + (img_y / height) * 3); + var res = mandelbrotValueOO(C, max_iters); + if (output.length > 0 && complex(5)) { + } else { + output.push([res, 1]); + } + } + } +} +var timenonjit = f(false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug642985-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug642985-2.js new file mode 100644 index 00000000000..478b585591b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug642985-2.js @@ -0,0 +1,23 @@ +function f(N) +{ + for (var i = 0; i != N; ++i) { + var obj1 = {}, obj2 = {}; + obj1['a'+i] = 0; + obj2['b'+i] = 0; + for (var repeat = 0;repeat != 2; ++repeat) { + for (var j in obj1) { + for (var k in obj2) { + gc(); + } + } + } + } +} +var array = [function() { f(10); }, + function(array) { f(50); }, + function() { propertyIsEnumerable.call(undefined, {}); }, + ]; +try { + for (var i = 0; i != array.length; ++i) + array[i](); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643113.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643113.js new file mode 100644 index 00000000000..54ade989162 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643113.js @@ -0,0 +1,11 @@ +function printBugNumber (num) +{ + print ('BUGNUMBER: ' + num); +} +try { test(); } catch (e) {} +function test() +{ + printBugNumber(typeof BUGNUMBER == 'undefined'); + 1|| q + 48? new q( printBugNumber, + eval("var EXP_1 = new MyValuelessObject('string'); var EXP_2 = new MyValuelessObject(false); EXP_1 + EXP_2") ): 1; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643169.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643169.js new file mode 100644 index 00000000000..28f3ac02525 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643169.js @@ -0,0 +1,7 @@ +for(var i=0; i<3; i++) { + var x; + function ff() {} + with(this) {} + x; + x = true; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643243.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643243.js new file mode 100644 index 00000000000..71530c56a17 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643243.js @@ -0,0 +1,11 @@ +{ + function newSandbox(n) {} +} +var o12 = Float32Array.prototype; +function f12(o) { + eval('o')['__proto_' + '_'] = null; +} +for (var i = 0; i < 14; i++) { + gc() + new f12(o12); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643244.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643244.js new file mode 100644 index 00000000000..c42769bf876 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643244.js @@ -0,0 +1,2 @@ +delete(0).__proto__.valueOf +eval("(function(){(0).valueOf();})")() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643249.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643249.js new file mode 100644 index 00000000000..86a000228e9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643249.js @@ -0,0 +1,13 @@ +{ + function x() {} +} +for (i = 0; i < 10; i++) { + _someglobal_ = /a/; + (function() { + return function() { + return _someglobal_ + } () + } () == /a/); + gc(); + try { _someglobal_ = new Function.__lookupSetter__ } catch (e) {} +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643285.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643285.js new file mode 100644 index 00000000000..0f8d7559238 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643285.js @@ -0,0 +1,4 @@ +function fun() { + (new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) ('function pf' + fun + '() {}'); +} +fun(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug643733.js b/deps/mozjs/js/src/jit-test/tests/basic/bug643733.js new file mode 100644 index 00000000000..89a6695b3d6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug643733.js @@ -0,0 +1,4 @@ +var x; +assertEq(-(typeof (x+x)), NaN); +assertEq(-(typeof Math.abs()), NaN); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug645293.js b/deps/mozjs/js/src/jit-test/tests/basic/bug645293.js new file mode 100644 index 00000000000..366c498fa2a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug645293.js @@ -0,0 +1,12 @@ +/* Don't assert. */ +function f() { + NaN++; + --NaN; + Infinity--; + ++Infinity; + undefined++; + --undefined; + ++Math; + Math--; +} +f(); \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug645632.js b/deps/mozjs/js/src/jit-test/tests/basic/bug645632.js new file mode 100644 index 00000000000..dbb5b8fd75c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug645632.js @@ -0,0 +1,6 @@ + +function f(o) { + o[{}] = 1; + with(Object) {} +} +f(Object.prototype); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646393.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646393.js new file mode 100644 index 00000000000..5d98e15410f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646393.js @@ -0,0 +1,4 @@ +try { + x.y; +} catch(ex) {} +x = Number(1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-1.js new file mode 100644 index 00000000000..00b948a28e6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-1.js @@ -0,0 +1,7 @@ +var x = 5; +let (x = x) + assertEq(x, 5); +let (x = eval("x")) + assertEq(x, 5); +let (x = function () { with ({}) return x; }) + assertEq(x(), 5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-2.js new file mode 100644 index 00000000000..e86e3a6e915 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-2.js @@ -0,0 +1,4 @@ +var x = 5; +(let (x = x) assertEq(x, 5)); +(let (x = eval("x")) assertEq(x, 5)); +(let (x = function () { with ({}) return x; }) assertEq(x(), 5)); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-3.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-3.js new file mode 100644 index 00000000000..ee4ed9e8506 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-3.js @@ -0,0 +1,16 @@ +var s, x = 0; + +s = ''; +for (let x = x; x < 3; x++) + s += x; +assertEq(s, '012'); + +s = ''; +for (let x = eval('x'); x < 3; x++) + s += x; +assertEq(s, '012'); + +s = '' +for (let x = function () { with ({}) return x; }(); x < 3; x++) + s += x; +assertEq(s, '012'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-4.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-4.js new file mode 100644 index 00000000000..ffb7ffed08a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-4.js @@ -0,0 +1,4 @@ +var s = '', x = {a: 1, b: 2, c: 3}; +for (let x in eval('x')) + s += x; +assertEq(s, 'abc'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-5.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-5.js new file mode 100644 index 00000000000..abc372a853f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-5.js @@ -0,0 +1,9 @@ +var y = 7; + +switch (function () { with ({}) return y; }()) { +case 7: + let y; + break; +default: + throw 'FAIL'; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-6.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-6.js new file mode 100644 index 00000000000..c24eb4bbcef --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-6.js @@ -0,0 +1,6 @@ +function test(s) { + eval(s); + let (a =

.q = 0, x = x) + assertEq(x, 5); +} +test('var x = 5;'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-7.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-7.js new file mode 100644 index 00000000000..f2451fa831d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-7.js @@ -0,0 +1,9 @@ +// |jit-test| debug + +function test(s) { + eval(s); + let (y = evalInFrame(0, '3'), x = x) { + assertEq(x, 5); + } +} +test('var x = 5;'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug646968-8.js b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-8.js new file mode 100644 index 00000000000..d242b845752 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug646968-8.js @@ -0,0 +1,7 @@ +// |jit-test| debug + +var x = 5; +let (x = eval("x++")) { + assertEq(evalInFrame(0, "x"), 5); +} +assertEq(x, 6); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug647463.js b/deps/mozjs/js/src/jit-test/tests/basic/bug647463.js new file mode 100644 index 00000000000..0f3d5cd57a2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug647463.js @@ -0,0 +1,6 @@ + +try { + eval("\ + [0].sort()\ + ") +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug647695.js b/deps/mozjs/js/src/jit-test/tests/basic/bug647695.js new file mode 100644 index 00000000000..e5ebd5dbf41 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug647695.js @@ -0,0 +1,15 @@ +try { let(x = XMLList(7), y = let(a = x)("")) {} } catch (e) {} + +try { +with({ + y: Float64Array +}) +for each(let y in [y]) {} +} catch (e) {} + +try { test(); } catch (e) {} +function test() { + try { + var result, arguments = 'adddb', arguments; + } catch (ex) {} +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug648357.js b/deps/mozjs/js/src/jit-test/tests/basic/bug648357.js new file mode 100644 index 00000000000..9c2c939e08a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug648357.js @@ -0,0 +1,3 @@ +var x = [1, 2, 3, 4, 5, 6, 7, 8]; +x.pop(); +x.push(9); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug648773.js b/deps/mozjs/js/src/jit-test/tests/basic/bug648773.js new file mode 100644 index 00000000000..b4d3b1d5fd1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug648773.js @@ -0,0 +1,4 @@ +gczeal(2); +for (var loopa2 = 0; loopa2 < 13; loopa2++) { + [, , , , , , ][Float64Array()] = 72413.8139177333; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug649439.js b/deps/mozjs/js/src/jit-test/tests/basic/bug649439.js new file mode 100644 index 00000000000..0f19afca8ce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug649439.js @@ -0,0 +1,3 @@ +var o1 = new String("abc"); +var o2 = o1[1]; +o2[1]; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug649771.js b/deps/mozjs/js/src/jit-test/tests/basic/bug649771.js new file mode 100644 index 00000000000..6ecc2d83e60 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug649771.js @@ -0,0 +1,7 @@ +function f(o) { + f = o.constructor; + eval('delete o.x'); +} +for(var i=0; i<3; i++) { + f(RegExp.prototype); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug649939.js b/deps/mozjs/js/src/jit-test/tests/basic/bug649939.js new file mode 100644 index 00000000000..2f1b054730b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug649939.js @@ -0,0 +1,27 @@ +// This was the actual bug +assertRaises(StopIteration, function() { + Iterator.prototype.next(); + Iterator.prototype.next(); +}); + +// The error should have triggered here, but was masked by a latent bug +assertRaises(StopIteration, function() { + Iterator.prototype.next(); +}); + +// Found by fuzzing +assertRaises(StopIteration, function() { + (new Iterator({})).__proto__.next(); +}); + + +function assertRaises(exc, callback) { + var caught = false; + try { + callback(); + } catch (e) { + assertEq(e instanceof StopIteration, true); + caught = true; + } + assertEq(caught, true); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug650148.js b/deps/mozjs/js/src/jit-test/tests/basic/bug650148.js new file mode 100644 index 00000000000..9f6b45548b7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug650148.js @@ -0,0 +1,11 @@ +summary=/(?!AB+D)AB/.exec("AB") + ''; +try { + var s = "throw 42"; +} catch (e) {} +test(); +function test() { + [ {0xBe: /l/|| 'Error' ? s++ : summary } ] +} +function foo(code) + Function(code)(); +foo("for each (y in this);"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug651451-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug651451-2.js new file mode 100644 index 00000000000..d5594b89228 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug651451-2.js @@ -0,0 +1,6 @@ +var arr = [1, 2, 3, 4, 5]; +arr.length = 100; +arr.pop(); +assertEq(arr.length, 99); +arr.pop(); +assertEq(arr.length, 98); \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug651451.js b/deps/mozjs/js/src/jit-test/tests/basic/bug651451.js new file mode 100644 index 00000000000..e547fa4d781 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug651451.js @@ -0,0 +1,4 @@ +var arr = [2]; +arr.pop(); +arr[0] = 2; +assertEq(arr.length, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug651966.js b/deps/mozjs/js/src/jit-test/tests/basic/bug651966.js new file mode 100644 index 00000000000..5e91fe38b74 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug651966.js @@ -0,0 +1,38 @@ + +function f(code) { + g = eval("(function(){" + code + "})"); + g() +} +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +f(); +try { f(".(x=[]);function x(){}(x())"); } catch (e) {} + +function f2() { + a = { + x + } = x, (x._) + function + x()( * :: * ) +} +try { f2(); } catch (e) {} + +function f3() { + var x = 0; + with ({}) { x = 'three'; } + return x; +} +f3(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug652054.js b/deps/mozjs/js/src/jit-test/tests/basic/bug652054.js new file mode 100644 index 00000000000..61ab3a2fc26 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug652054.js @@ -0,0 +1,55 @@ +var M4x4 = {}; +M4x4.mul = function M4x4_mul(a, b, r) { + a11 = a[0] + a21 = a[1] + a31 = a[2] + a12 = a[4] + a22 = a[5] + a32 = a[6] + a13 = a[8] + a23 = a[9] + a33 = a[10] + a14 = a[12] + a24 = a[13] + a34 = a[14] + b[3] + b[4] + b13 = b[8] + b23 = b[9] + b33 = b[10] + b43 = b[11] + r[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43 + r[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43 + r[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43 + return r; +}; +M4x4.scale3 = function M4x4_scale3(x, y, z, m) { + m[0] *= x; + m[3] *= x; + m[4] *= y; + m[11] *= z; +}; +M4x4.makeLookAt = function M4x4_makeLookAt() { + tm1 = Float32Array(16); + tm2 = Float32Array(16); + r = new Float32Array(16) + return M4x4.mul(tm1, tm2, r); +}; +var jellyfish = {}; +jellyfish.order = []; +function jellyfishInstance() {} +jellyfishInstance.prototype.drawShadow = function () { + pMatrix = M4x4.makeLookAt(); + M4x4.mul(M4x4.makeLookAt(), pMatrix, pMatrix); + M4x4.scale3(6, 180, 0, pMatrix); +} +function drawScene() { + jellyfish.order.push([0, 0]) + jellyfish[0] = new jellyfishInstance() + for (var i = 0, j = 0; i < jellyfish.count, j < 30; ++j) { + jellyfish.order[i][0] + jellyfish[0].drawShadow(); + } +} +drawScene(); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug652422.js b/deps/mozjs/js/src/jit-test/tests/basic/bug652422.js new file mode 100644 index 00000000000..64525ee36ac --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug652422.js @@ -0,0 +1,6 @@ +try { (function() { + var o = {}; + with (o) o='recorder not started, '; + ('arguments' in o, false, + "property deletion observable") +})() } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug652646.js b/deps/mozjs/js/src/jit-test/tests/basic/bug652646.js new file mode 100644 index 00000000000..5f54c32dea3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug652646.js @@ -0,0 +1,11 @@ +function foo() { + try { + return 0; + } catch (e) { + try { + return 1; + } catch (e) { + } + } +} +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug653153.js b/deps/mozjs/js/src/jit-test/tests/basic/bug653153.js new file mode 100644 index 00000000000..c06e3b82b8a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug653153.js @@ -0,0 +1,76 @@ +// ES5 15.1.2.2 step 1 + +/* + * Boundary testing for super-large positive numbers between non-exponential + * and in-exponential-form. + * + * NB: While 1e21 is exactly representable as an IEEE754 double-precision + * number, its nearest neighboring representable values are a good distance + * away, 65536 to be precise. + */ + +// This is the boundary in theory. +assertEq(parseInt(1e21), 1); + +// This is the boundary in practice. +assertEq(parseInt(1e21 - 65537) > 1e20, true); +assertEq(parseInt(1e21 - 65536), 1); +assertEq(parseInt(1e21 + 65536), 1); + +// Check that we understand floating point accuracy near the boundary +assertEq(1e21 - 65537 !== 1e21 - 65536, true); +assertEq(1e21 - 65536, 1e21); +assertEq(1e21 + 65535, 1e21); +assertEq(1e21 + 65536, 1e21); + +// ES5 leaves exact precision in ToString(bigMagNum) undefined, which +// might make this value inconsistent across implementations (maybe, +// nobody's done the math here). Regardless, it's definitely a number +// very close to 1, and not a large-magnitude positive number. +assertEq(1e21 + 65537 !== 1e21, true); +assertEq(parseInt(1e21 + 65537) < 1.001, true); + + +/* + * Now do the same tests for super-large negative numbers crossing the + * opposite boundary. + */ + +// This is the boundary in theory. +assertEq(parseInt(-1e21), -1); + +// This is the boundary in practice. +assertEq(parseInt(-1e21 + 65537) < -1e20, true); +assertEq(parseInt(-1e21 + 65536), -1); +assertEq(parseInt(-1e21 - 65536), -1); + +// Check that we understand floating point accuracy near the boundary +assertEq(-1e21 + 65537 !== -1e21 + 65536, true); +assertEq(-1e21 + 65536, -1e21); +assertEq(-1e21 - 65535, -1e21); +assertEq(-1e21 - 65536, -1e21); + +// ES5 leaves exact precision in ToString(bigMagNum) undefined, which +// might make this value inconsistent across implementations (maybe, +// nobody's done the math here). Regardless, it's definitely a number +// very close to -1, and not a large-magnitude negative number. +assertEq(-1e21 - 65537 !== 1e21, true); +assertEq(parseInt(-1e21 - 65537) > -1.001, true); + + +/* Check values around the boundary. */ +arr = [1e0, 5e1, 9e19, 0.1e20, 1.3e20, 1e20, 9e20, 9.99e20, 0.1e21, + 1e21, 1.0e21, 2e21, 2e20, 2.1e22, 9e21, 0.1e22, 1e22, 3e46, 3e23, 3e100, 3.4e200, 7e1000, + 1e21, 1e21+65537, 1e21+65536, 1e21-65536, 1e21-65537]; + +/* Check across a range of values in case we missed anything. */ +for (var i = 0; i < 4000; i++) { + arr.push(1e19 + i*1e19); +} + +for (var i in arr) { + assertEq(parseInt( arr[i]), parseInt(String( arr[i]))); + assertEq(parseInt(-arr[i]), parseInt(String(-arr[i]))); +} + + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug653262.js b/deps/mozjs/js/src/jit-test/tests/basic/bug653262.js new file mode 100644 index 00000000000..9a488bbaa11 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug653262.js @@ -0,0 +1,4 @@ +with(evalcx(''))(function eval() {}, this.__defineGetter__("x", Function)); +var i = 0; +var o; +new(x); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug653672.js b/deps/mozjs/js/src/jit-test/tests/basic/bug653672.js new file mode 100644 index 00000000000..c9d5e202b41 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug653672.js @@ -0,0 +1,5 @@ +// don't crash + +var regexp1 = /(?:(?=g))|(?:m).{2147483648,}/; +var regexp2 = /(?:(?=g)).{2147483648,}/; + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug654073.js b/deps/mozjs/js/src/jit-test/tests/basic/bug654073.js new file mode 100644 index 00000000000..8ec978e6b83 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug654073.js @@ -0,0 +1,3 @@ +this.__proto__ = null; +Object.prototype.__proto__ = this; +for (var x in Object.prototype); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug654668.js b/deps/mozjs/js/src/jit-test/tests/basic/bug654668.js new file mode 100644 index 00000000000..8706c154a25 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug654668.js @@ -0,0 +1,10 @@ +var summary = 'foo'; +function X(n) { + var y = []; + while (summary + y[0]) { + break; + } +} +X(); + +// Don't crash wthi -m -n -a diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug656261.js b/deps/mozjs/js/src/jit-test/tests/basic/bug656261.js new file mode 100644 index 00000000000..e62ab334a45 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug656261.js @@ -0,0 +1,31 @@ +function build_getter(i) { + var x = [i]; + return function f() { return x; } +} + +function test() +{ + var N = internalConst("MARK_STACK_LENGTH") + 2; + var o = {}; + var descriptor = { enumerable: true}; + for (var i = 0; i != N; ++i) { + descriptor.get = build_getter(i); + Object.defineProperty(o, i, descriptor); + } + + // At this point we have an object o with N getters. Each getter in turn + // is a closure storing an array. During the GC we push to the object + // marking stack all the getters found in an object after we mark it. As N + // exceeds the size of the object marking stack, this requires to run the + // dealyed scanning for some closures to mark the array objects stored in + // them. + // + // We run the GC twice to make sure that the background finalization + // finishes before we access the objects. + gc(); + gc(); + for (var i = 0; i != N; ++i) + assertEq(o[i][0], i); +} + +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug656381.js b/deps/mozjs/js/src/jit-test/tests/basic/bug656381.js new file mode 100644 index 00000000000..b48f0cbb1c2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug656381.js @@ -0,0 +1,4 @@ +// |jit-test| debug +var f = (function () {with ({}) {}}); +trap(f, 5, ''); // trap "nullblockchain" op +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug656555.js b/deps/mozjs/js/src/jit-test/tests/basic/bug656555.js new file mode 100644 index 00000000000..4f81ea50de1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug656555.js @@ -0,0 +1,4 @@ +// |jit-test| debug +function f() { ({}).m = function(){}; } +trap(f, 11, ''); +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657197.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657197.js new file mode 100644 index 00000000000..fde956abee5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657197.js @@ -0,0 +1,5 @@ +try { (function() { + new function() { + throw []; + } +})() } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657225.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657225.js new file mode 100644 index 00000000000..40f0dbf962c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657225.js @@ -0,0 +1,9 @@ + +function reportCompare(expected, actual, description) + ++actual + "'"; +var summary = 'Object.prototype.toLocaleString() should track Object.prototype.toString() '; +var o = { + toString: function () {} +}; +expect = o; +actual = o.toLocaleString(); +reportCompare(expect, actual, summary); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657227.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657227.js new file mode 100644 index 00000000000..99a7b4fb059 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657227.js @@ -0,0 +1,39 @@ +var obj; +var counter = 0; +var p = Proxy.create({ + has : function(id) { + if (id == 'xyz') { + ++counter; + if (counter == 7) { + obj.__proto__ = null; + } + return true; + } + return false; + }, + get : function(id) { + if (id == 'xyz') + return 10; + } +}); + +function test() +{ + Object.prototype.__proto__ = null; + obj = { xyz: 1}; + var n = 0; + for (var i = 0; i != 100; ++i) { + var s = obj.xyz; + if (s) + ++n; + if (i == 10) { + delete obj.xyz; + Object.prototype.__proto__ = p; + } + + } +} + +try { + test(); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657245.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657245.js new file mode 100644 index 00000000000..15bea1c94f7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657245.js @@ -0,0 +1,4 @@ + +var length = 4294967295; +var array1 = Array(length); +array1.pop(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657901.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657901.js new file mode 100644 index 00000000000..a0531ae758b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657901.js @@ -0,0 +1,8 @@ +function f() {}; +function g(o) { + f = new Function(""); + eval(""); +} +g({}); +g({}); +f++; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug657975.js b/deps/mozjs/js/src/jit-test/tests/basic/bug657975.js new file mode 100644 index 00000000000..f3046ff5ac5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug657975.js @@ -0,0 +1,79 @@ +// |jit-test| debug +setDebug(true); + +// bug 657975 +function f1(){ "use strict"; options('strict'); } +trap(f1, 0, '') +f1() + +// bug 657979 +function f2(){ with({a:0}){}; } +trap(f2, 0, '') +f2() + +x = 0; + +// bug 657984 #1 +function f3(){ for(y in x); } +trap(f3, 5, '') +f3() + +// bug 657984 #2 +function f4(){ for(y in x); } +trap(f4, 8, '') +f4() + +// bug 658464 +function f5() { + for ([, x] in 0) {} +} +trap(f5, 7, '') +f5() + +// bug 658465 +function f6() { + "use strict"; + print(Math.min(0, 1)); +} +trap(f6, 10, '') +f6() + +// bug 658491 +function f7() { + try { y = w; } catch(y) {} +} +trap(f7, 14, '') +f7() + +// bug 658950 +f8 = (function() { + let x; + yield +}) +trap(f8, 6, undefined); +for (a in f8()) + (function() {})() + +// bug 659043 +f9 = (function() { + for (let a = 0; a < 0; ++a) { + for each(let w in []) {} + } +}) +trap(f9, 23, undefined); +for (b in f9()) + (function() {})() + +// bug 659233 +f10 = (function() { + while (h) { + continue + } +}) +trap(f10, 0, ''); +try { f10() } catch (e) {} + +// bug 659337 +f11 = Function("for (x = 0; x < 6; x++) { gc() }"); +trap(f11, 23, ''); +f11() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug658539.js b/deps/mozjs/js/src/jit-test/tests/basic/bug658539.js new file mode 100644 index 00000000000..04543941131 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug658539.js @@ -0,0 +1,2 @@ +with(newGlobal('same-compartment')) +new Number() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug660081.js b/deps/mozjs/js/src/jit-test/tests/basic/bug660081.js new file mode 100644 index 00000000000..1c62a654c33 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug660081.js @@ -0,0 +1 @@ +(function() { "length" in (true && arguments); })() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug660173.js b/deps/mozjs/js/src/jit-test/tests/basic/bug660173.js new file mode 100644 index 00000000000..99d0f8ed8c2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug660173.js @@ -0,0 +1,4 @@ +(function() {}).apply(null, (function() { + var x; + return (x = arguments); +})()); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug660203.js b/deps/mozjs/js/src/jit-test/tests/basic/bug660203.js new file mode 100644 index 00000000000..faed7b6864c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug660203.js @@ -0,0 +1,9 @@ + +function throwsRangeError(t) { + try { + var date = arguments; + date.setTime + } catch (err) { + } +} +throwsRangeError(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug660204.js b/deps/mozjs/js/src/jit-test/tests/basic/bug660204.js new file mode 100644 index 00000000000..eddd4ba839b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug660204.js @@ -0,0 +1,12 @@ + +eval("try { name(); } catch(e) {}"); +function Employee ( name, dept ) { + this.name=name || "" + this.dept +} +function WorkerBee ( name, dept, projs ) { + this.base=Employee + this.base( name, print("WHAT")) +} +new WorkerBee; +WorkerBee(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug660538.js b/deps/mozjs/js/src/jit-test/tests/basic/bug660538.js new file mode 100644 index 00000000000..ccbf489c3d7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug660538.js @@ -0,0 +1,12 @@ +// |jit-test| error: SyntaxError; + +Error.prototype.__proto__.p = 5; +f = Function("return( \"\" +} catch (e) {} +try { + eval("d = ''") + done = true; +} catch (e) {} +assertEq(done, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug690732.js b/deps/mozjs/js/src/jit-test/tests/basic/bug690732.js new file mode 100644 index 00000000000..bfcb728914b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug690732.js @@ -0,0 +1,4 @@ + +var o4 = Object.freeze({ + set: function(summary) {} +}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug691299-regexp.js b/deps/mozjs/js/src/jit-test/tests/basic/bug691299-regexp.js new file mode 100644 index 00000000000..63f1e4948fb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug691299-regexp.js @@ -0,0 +1,3 @@ +// |jit-test| error: SyntaxError: invalid quantifier + +String.fromCharCode(256).replace(/[^a$]{4294967295}/,"aaaa"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-1.js new file mode 100644 index 00000000000..b78c0cb0632 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-1.js @@ -0,0 +1,4 @@ +var re = /.*star.*/i; +var str = "The Shawshank Redemption (1994)"; +for (var k = 0; k < 100; k++) + assertEq(false, re.test(str)); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-2.js new file mode 100644 index 00000000000..a8c0c44864c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug691797-regexp-2.js @@ -0,0 +1,6 @@ +var re = /.*(a\w).*/i; +var str = "abccccccad"; +for (var k = 0; k < 100; k++) { + re.test(str); + assertEq('ad', RegExp['$1']); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug695922-syntax.js b/deps/mozjs/js/src/jit-test/tests/basic/bug695922-syntax.js new file mode 100644 index 00000000000..599888080e1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug695922-syntax.js @@ -0,0 +1,2 @@ +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { eval("({p:"); }, SyntaxError); // don't crash diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug698584.js b/deps/mozjs/js/src/jit-test/tests/basic/bug698584.js new file mode 100644 index 00000000000..a02d74b6291 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug698584.js @@ -0,0 +1,21 @@ +// |jit-test| allow-oom; +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +try +{ + const MAX = 10000; + var str = ""; + for (var i = 0; i < MAX; ++i) { + /x/.test(str); + str += str + 'xxxxxxxxxxxxxx'; + } +} +catch (e) +{ + assertEq(""+e, "InternalError: allocation size overflow"); +} + +/* Don't crash */ diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug699166.js b/deps/mozjs/js/src/jit-test/tests/basic/bug699166.js new file mode 100644 index 00000000000..ac4c89eb032 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug699166.js @@ -0,0 +1,7 @@ +a = "".__proto__ +b = uneval().__proto__ +for (var i = 0; i < 2; i++) { + a.__defineSetter__("valueOf", function() {}) + a + "" + delete b.valueOf +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug700300.js b/deps/mozjs/js/src/jit-test/tests/basic/bug700300.js new file mode 100644 index 00000000000..ccee3ecfd21 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug700300.js @@ -0,0 +1,4 @@ +for (let j = 0; j < (20); ++(__lookupSetter__)) { + function g() { j; } + j++; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug700501.js b/deps/mozjs/js/src/jit-test/tests/basic/bug700501.js new file mode 100644 index 00000000000..65a130987e1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug700501.js @@ -0,0 +1,13 @@ +Function.prototype.__proto__["p"] = 3 +c = [].__proto__ +c[5] = 3 +Namespace.prototype.__proto__[4] = function() {} +gc() +Function("\ + {\ + function f(d) {}\ + for each(let z in[0]) {\ + f(z)\ + }\ + }\ +")() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug700799.js b/deps/mozjs/js/src/jit-test/tests/basic/bug700799.js new file mode 100644 index 00000000000..7a543325fa8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug700799.js @@ -0,0 +1 @@ +for (let x in [.(let(x) function() {})]) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug702426-regexp-gc.js b/deps/mozjs/js/src/jit-test/tests/basic/bug702426-regexp-gc.js new file mode 100644 index 00000000000..206e2fd9579 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug702426-regexp-gc.js @@ -0,0 +1,5 @@ +function g(code) { + eval("(function(){" + code + "})")() +} +g("evalcx(\"(/a/g,function(){})\")"); +g("'a'.replace(/a/g,gc)") diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug702572.js b/deps/mozjs/js/src/jit-test/tests/basic/bug702572.js new file mode 100644 index 00000000000..50595d09e57 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug702572.js @@ -0,0 +1,10 @@ +eval("function f() { function g() {} return g; }"); +assertEq(f().prototype !== f().prototype, true); + +function f1() { + function g1() { + function h1() { return h1; } + } + return g1; +} +assertEq(f1().prototype !== f1().prototype, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug703157.js b/deps/mozjs/js/src/jit-test/tests/basic/bug703157.js new file mode 100644 index 00000000000..2acdd92ebd3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug703157.js @@ -0,0 +1,36 @@ +// Define a test object +var test = {x:1,y:2}; + +// Put the object into dictionary mode by deleting +// a property that was not the last one added. +delete test.x; + +// Define a an accessor property with a setter that +// itself calls Object.defineProperty +Object.defineProperty(test, "foo", { + get: function() { return 1; }, + set: function(v) { + Object.defineProperty(this, "foo", { value: v }); + // Prints the correct object descriptor + assertEq(this.foo, 33); + }, + configurable: true +}); + +// Add another property, so generateOwnShape does not replace the foo property. +test.other = 0; + +// This line prints 1, as expected +assertEq(test.foo, 1); + +// Now set the property. This calls the setter method above. +// And the setter method prints the expected value and property descriptor. +test.foo = 33; + +// Finally read the newly set value. +assertEq(test.foo, 33); + +// Check that enumeration order is correct. +var arr = []; +for (var x in test) arr.push(x); +assertEq("" + arr, "y,other"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug703544.js b/deps/mozjs/js/src/jit-test/tests/basic/bug703544.js new file mode 100644 index 00000000000..87d1a1b0d0b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug703544.js @@ -0,0 +1,7 @@ +gczeal(4); +function testInterpreterReentry7() { + var arr = [0, 1, 2, 3, 4]; + for (var i = (1); i < 5; i++) + arr[i] = "grue"; +} +testInterpreterReentry7(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug703818.js b/deps/mozjs/js/src/jit-test/tests/basic/bug703818.js new file mode 100644 index 00000000000..be8fdaf5a1f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug703818.js @@ -0,0 +1,3 @@ +Object.defineProperty(Namespace.prototype, "toString", { + enumerable: true +}) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug704134.js b/deps/mozjs/js/src/jit-test/tests/basic/bug704134.js new file mode 100644 index 00000000000..947a0b13bbb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug704134.js @@ -0,0 +1,12 @@ +function f(s) { + eval(s); + return function() { + with({}) {}; + return b; + }; +} +var b = 1; +var g1 = f(""); +var g2 = f("var b = 2;"); +g1(''); +assertEq(g2(''), 2); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug704795.js b/deps/mozjs/js/src/jit-test/tests/basic/bug704795.js new file mode 100644 index 00000000000..d39b6915cad --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug704795.js @@ -0,0 +1,13 @@ +Function("\ + gczeal(4,false);\ + function f(){\ + \"use strict\";\ + this.x = Object\ + }\ + for each(y in[0,0]){\ + try{\ + new f\ + }\ + catch(e){}\ + }\ +")() \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug705895-1.js b/deps/mozjs/js/src/jit-test/tests/basic/bug705895-1.js new file mode 100644 index 00000000000..a22a217cbeb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug705895-1.js @@ -0,0 +1,11 @@ +c = (0).__proto__ +function f(o) { + o.__proto__ = null + for (x in o) {} +} +for (i = 0; i < 9; i++) { + f(c) + Function.prototype.__proto__.__proto__ = c + for (x in Function.prototype.__proto__) {} + f(Math.__proto__) +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug705895-2.js b/deps/mozjs/js/src/jit-test/tests/basic/bug705895-2.js new file mode 100644 index 00000000000..445efb56a82 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug705895-2.js @@ -0,0 +1,13 @@ +// |jit-test| error: TypeError +function f(o) { + for (j = 0; j < 9; j++) { + if (j) { + o.__proto__ = null + } + for (v in o) {} + } +} +for (i = 0; i < 9; i++) { + (new Boolean).__proto__.__defineGetter__("toString", function() {}) + f(Boolean.prototype) +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug706316.js b/deps/mozjs/js/src/jit-test/tests/basic/bug706316.js new file mode 100644 index 00000000000..983994df17a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug706316.js @@ -0,0 +1,11 @@ +// |jit-test| error: ReferenceError +gczeal(4); +function setprop() { + var obj = { a:({ } ) }; + var obj2 = { b:-1, a:-1 }; + for (var i = 0; i < 20; (length(resultsY.global0, 42))) { + obj2.b = obj.a = i; + } +} +assertEq(setprop(), "19,-1,19"); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug706795.js b/deps/mozjs/js/src/jit-test/tests/basic/bug706795.js new file mode 100644 index 00000000000..57d1a41a4e6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug706795.js @@ -0,0 +1,7 @@ +executed = false; +Object.defineProperty(Object.prototype, 'x', { set: function() { executed = true; } }); +function A() { + this.x = 12; +} +new A(); +assertEq(executed, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug706808.js b/deps/mozjs/js/src/jit-test/tests/basic/bug706808.js new file mode 100644 index 00000000000..8bbba9d98ae --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug706808.js @@ -0,0 +1,5 @@ + +Object.defineProperty(Object.prototype, "a", {}); +function t2(b) { this.a = b; } +var x = new t2(3); +assertEq(x.a, undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug707750.js b/deps/mozjs/js/src/jit-test/tests/basic/bug707750.js new file mode 100644 index 00000000000..56df1043c69 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug707750.js @@ -0,0 +1,9 @@ +// |jit-test| error: ReferenceError +var lfcode = new Array(); +lfcode.push("gczeal(4);"); +lfcode.push('print(BUGNUMBER + ": " + (W -- ));'); +while (true) { + var file = lfcode.shift(); if (file == undefined) { break; } + eval(file); +} + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug708228.js b/deps/mozjs/js/src/jit-test/tests/basic/bug708228.js new file mode 100644 index 00000000000..4a627fd1fb0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug708228.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +gczeal(4); +var g_rx = /(?:)/; +(3).replace(g_rx.compile("test", "g"), {}); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug708805.js b/deps/mozjs/js/src/jit-test/tests/basic/bug708805.js new file mode 100644 index 00000000000..95fd99668f1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug708805.js @@ -0,0 +1,4 @@ +gczeal(4); +test(); +function test() +eval("with({}) let(x=[])(function(){x})()"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug708819.js b/deps/mozjs/js/src/jit-test/tests/basic/bug708819.js new file mode 100644 index 00000000000..8f3f0a552e0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug708819.js @@ -0,0 +1,12 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +try { + var e = new Error(); + e.name = e; + "" + e; +} catch (e) { + assertEq(e.message, 'too much recursion'); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug709634.js b/deps/mozjs/js/src/jit-test/tests/basic/bug709634.js new file mode 100644 index 00000000000..57de9720aa7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug709634.js @@ -0,0 +1,6 @@ + +Function.prototype.toString = function () f(this, true); +function f(obj) { + f.caller.p +} +decodeURI + 3; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug710947.js b/deps/mozjs/js/src/jit-test/tests/basic/bug710947.js new file mode 100644 index 00000000000..c6dd61bb69c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug710947.js @@ -0,0 +1,8 @@ +// |jit-test| allow-oom; +function startTest() { + if (typeof document != "object" + || !document.location.href.match(/jsreftest.html/)) {} +}; +gczeal(4); +startTest(); +ArrayBuffer( 946684800000 ); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug713226.js b/deps/mozjs/js/src/jit-test/tests/basic/bug713226.js new file mode 100644 index 00000000000..78a6aacd764 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug713226.js @@ -0,0 +1,21 @@ +// |jit-test| mjitalways; +gczeal(4); +var optionNames = options().split(','); + for (var i = 0; i < optionNames.length; i++) + var optionName = optionNames[i]; + options(optionName); +evaluate("\ +function addDebug(g, id) {\ + var debuggerGlobal = newGlobal('new-compartment');\ + debuggerGlobal.debuggee = g;\ + debuggerGlobal.id = id;\ + debuggerGlobal.print = function (s) { (g) += s; };\ + debuggerGlobal.eval('var dbg = new Debugger(debuggee);dbg.onDebuggerStatement = function () { print(id); debugger; };');\ + return debuggerGlobal;\ +}\ +var base = newGlobal('new-compartment');\ +var top = base;\ +for (var i = 0; i < 8; i++ )\ + top = addDebug(top, i);\ +base.eval('debugger;');\ +"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug714614.js b/deps/mozjs/js/src/jit-test/tests/basic/bug714614.js new file mode 100644 index 00000000000..fd01dce5614 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug714614.js @@ -0,0 +1,5 @@ +function testForVarInWith(foo, foo) { + return eval("with ({}) { for (var x = 0; x < 5; x++); } (function() { return delete x; })"); +} +f = testForVarInWith()(); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug714616.js b/deps/mozjs/js/src/jit-test/tests/basic/bug714616.js new file mode 100644 index 00000000000..706594d0559 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug714616.js @@ -0,0 +1,8 @@ +array1 = new Array(); +size = 10; +for (i = 0; i < size; (array1.length)++) +{ + array1.push(array1.shift()); + ++i +} + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug716013.js b/deps/mozjs/js/src/jit-test/tests/basic/bug716013.js new file mode 100644 index 00000000000..b6ff2f991de --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug716013.js @@ -0,0 +1,4 @@ +f = (function() { + for (x in [arguments, arguments]) yield(gczeal(4, function(){})) +}) +for (i in f()) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug716713.js b/deps/mozjs/js/src/jit-test/tests/basic/bug716713.js new file mode 100644 index 00000000000..95054b2ccac --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug716713.js @@ -0,0 +1 @@ +uneval(function() { @o() }) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug717208.js b/deps/mozjs/js/src/jit-test/tests/basic/bug717208.js new file mode 100644 index 00000000000..30420010850 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug717208.js @@ -0,0 +1,15 @@ +var count = 0; +var a = {__noSuchMethod__: function() { count++; } } + +function f() { + for (var i = 0; i < 10; i++) { + try { + a.b(); + } catch (e) { + assertEq(true, false); + } + } +} +f(); + +assertEq(count, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug720675.js b/deps/mozjs/js/src/jit-test/tests/basic/bug720675.js new file mode 100644 index 00000000000..dd664d508be --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug720675.js @@ -0,0 +1,9 @@ +// |jit-test| allow-oom; + +gcparam("maxBytes", gcparam("gcBytes") + 4*1024); +arr = [1e0, 5e1, 9e19, 0.1e20, 1.3e20, 1e20, 9e20, 9.99e20, + 0.1e21, 1e21, 1e21+65537, 1e21+65536, 1e21-65536, 1]; +for (var i = 0; i < 4000; i++) { + arr.push(1e19 + i*1e19); +} +for (var i in arr) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/bug722028.js b/deps/mozjs/js/src/jit-test/tests/basic/bug722028.js new file mode 100644 index 00000000000..837e34460b3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/bug722028.js @@ -0,0 +1,13 @@ + +gczeal(4); +var BUGNUMBER = 668024; +var summary = +print(BUGNUMBER + ": " + summary); +var arr = [0, 1, 2, 3, 4, 5, , 7]; +var seen = []; +for (var p in arr) { + if (seen.indexOf(unescape) >= 0) {} + arr.splice(2, 3); + seen.push(p); +} + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/builtinLocals.js b/deps/mozjs/js/src/jit-test/tests/basic/builtinLocals.js new file mode 100644 index 00000000000..01175dc93ea --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/builtinLocals.js @@ -0,0 +1,35 @@ + +/* Resolve 'arguments' and the name of the function itself in the presence of such local variables. */ + +function f() { + return typeof arguments; + function arguments() { + return 7; + } +} +assertEq(f(), "object"); + +function g() { + var arguments = 0; + return typeof arguments; +} +assertEq(g(), "number"); + +function h() { + return typeof h; + function h() { + return 7; + } +} +assertEq(h(), "function"); + +function i() { + return typeof i; + var i; +} +assertEq(i(), "undefined"); + +function j() { + return typeof j; +} +assertEq(j(), "function"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/deepForInLoop.js b/deps/mozjs/js/src/jit-test/tests/basic/deepForInLoop.js index 0e58b063d7b..8bae45eb15a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/deepForInLoop.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/deepForInLoop.js @@ -1,5 +1,5 @@ function deepForInLoop() { - // NB: the number of props set in C is arefully tuned to match HOTLOOP = 2. + // NB: the number of props set in C is arefully tuned to match 8 = 2. function C(){this.p = 1, this.q = 2} C.prototype = {p:1, q:2, r:3, s:4, t:5}; var o = new C; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object-elem.js b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object-elem.js new file mode 100644 index 00000000000..b5a28be5546 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object-elem.js @@ -0,0 +1,4 @@ +var arr = ["foo", "bar"]; +var obj = { toString: {}, valueOf: function() { return 1; } }; +for (var i = 0; i < 25; i++) + assertEq(arr[obj], "bar"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object.js b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object.js new file mode 100644 index 00000000000..f0a30d11583 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-toString-is-noncallable-object.js @@ -0,0 +1,3 @@ +var obj = { toString: {}, valueOf: function() { return "foopy"; } }; +for (var i = 0; i < 25; i++) + assertEq(String(obj), "foopy"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-valueOf-is-noncallable-object.js b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-valueOf-is-noncallable-object.js new file mode 100644 index 00000000000..9bb2126b938 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/defaultvalue-valueOf-is-noncallable-object.js @@ -0,0 +1,3 @@ +var obj = { valueOf: {}, toString: function() { return 0; } }; +for (var i = 0; i < 25; i++) + assertEq(obj < 1, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/delete-array-elements.js b/deps/mozjs/js/src/jit-test/tests/basic/delete-array-elements.js index 09dd6dd77b4..e56fa36bd05 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/delete-array-elements.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/delete-array-elements.js @@ -5,8 +5,3 @@ assertEq(a.length, 11); for (i = 0; i < 10; i++) assertEq(a.hasOwnProperty(i), false); -checkStats({ - recorderAborted:0, - traceCompleted:2, - sideExitIntoInterpreter:2 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/delete-indexed-names.js b/deps/mozjs/js/src/jit-test/tests/basic/delete-indexed-names.js index 1585fca5a4f..9d30ebd17d8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/delete-indexed-names.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/delete-indexed-names.js @@ -5,8 +5,3 @@ for (var i in o) for each (var i in a) assertEq(o.hasOwnProperty(i), false); -checkStats({ - recorderAborted:0, - traceCompleted:2, - sideExitIntoInterpreter:2 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/delete-named-names.js b/deps/mozjs/js/src/jit-test/tests/basic/delete-named-names.js index 1e0ac407321..a4647bedd6a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/delete-named-names.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/delete-named-names.js @@ -10,8 +10,3 @@ for (var i in o) { for each (var i in a) assertEq(o.hasOwnProperty(i), false); -checkStats({ - recorderAborted:0, - traceCompleted:1, - sideExitIntoInterpreter:1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/delete-non-config.js b/deps/mozjs/js/src/jit-test/tests/basic/delete-non-config.js index f65b6954020..afc7631007c 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/delete-non-config.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/delete-non-config.js @@ -3,8 +3,3 @@ for (var i = 0; i < 10; i++) delete a.length; assertEq(delete a.length, false); -checkStats({ - recorderAborted:0, - traceCompleted:1, - sideExitIntoInterpreter:1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/deleteToString.js b/deps/mozjs/js/src/jit-test/tests/basic/deleteToString.js new file mode 100644 index 00000000000..0b8dfc59d4a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/deleteToString.js @@ -0,0 +1,5 @@ + +/* Inheritance of shadowed function properties from Object.prototype. */ + +delete Function.prototype.toString; +assertEq(Function.prototype.toString, Object.prototype.toString); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/error-toString.js b/deps/mozjs/js/src/jit-test/tests/basic/error-toString.js new file mode 100644 index 00000000000..d6709adb470 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/error-toString.js @@ -0,0 +1,9 @@ +var errorToString = Error.prototype.toString; + + +assertEq(errorToString.call({message: "", name: ""}), "Error"); +assertEq(errorToString.call({name: undefined, message: ""}), "Error"); +assertEq(errorToString.call({name: "Test", message: undefined}), "Test"); +assertEq(errorToString.call({name: "Test", message: ""}), "Test"); +assertEq(errorToString.call({name: "", message: "Test"}), "Test"); +assertEq(errorToString.call({name: "Test", message: "it!"}), "Test: it!"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/jitstatsArchFlags.js b/deps/mozjs/js/src/jit-test/tests/basic/jitstatsArchFlags.js deleted file mode 100644 index a4ec0c4ee4b..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/jitstatsArchFlags.js +++ /dev/null @@ -1,14 +0,0 @@ -// Make sure the arch flags are valid on startup, even if nothing has -// been traced yet. We don't know what arch the user is building on, -// but presumably we want at least 1 flag to be set on all supported -// platforms. - -if (HAVE_TM) { - assertEq(jitstats.archIsIA32 || - jitstats.archIs64BIT || - jitstats.archIsARM || - jitstats.archIsSPARC || - jitstats.archIsPPC || - jitstats.archIsAMD64, - 1); - } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/math-jit-tests.js b/deps/mozjs/js/src/jit-test/tests/basic/math-jit-tests.js index 418210fe15b..0892b47089f 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/math-jit-tests.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/math-jit-tests.js @@ -33,18 +33,18 @@ function testmath(funcname, args, expected) { // values at the front to get the loop body jitted, and then our // actual test value. var dummies_and_input = []; - for (i = 0; i < RUNLOOP; i++) { + for (i = 0; i < 9; i++) { var dummy_list = []; for (j = 0; j < arity; j++) dummy_list[j] = .0078125 * ((i + j) % 128); dummies_and_input[i] = dummy_list; } - dummies_and_input[RUNLOOP] = arg_value_list; + dummies_and_input[9] = arg_value_list; function testfunc() { // Map the function across the dummy values and the test input. mapfunc(dummies_and_input); - return dummies_and_input[RUNLOOP]; + return dummies_and_input[9]; } assertEq(close_enough(testfunc(), expected), true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/mathRoundBig.js b/deps/mozjs/js/src/jit-test/tests/basic/mathRoundBig.js new file mode 100644 index 00000000000..e01ffa4f545 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/mathRoundBig.js @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(Math.round(9007199254740991), 9007199254740991); +assertEq(Math.round(-19007199254740990), -19007199254740990); + +assertEq(Math.round("9007199254740991"), 9007199254740991); +assertEq(Math.round("-19007199254740990"), -19007199254740990); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/multiple-declared-args-syntax.js b/deps/mozjs/js/src/jit-test/tests/basic/multiple-declared-args-syntax.js new file mode 100644 index 00000000000..d41a98fccac --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/multiple-declared-args-syntax.js @@ -0,0 +1 @@ +assertEq((function (a, b, a) { return a; })(2, 4, 6), 6); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/name-inactive.js b/deps/mozjs/js/src/jit-test/tests/basic/name-inactive.js index d9475b490e0..6f3b9c4c0a8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/name-inactive.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/name-inactive.js @@ -13,8 +13,3 @@ for (var i = 0; i < 5; ++i) { assertEq(ans, '10,11,12,13,14,'); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/nestedContinue.js b/deps/mozjs/js/src/jit-test/tests/basic/nestedContinue.js new file mode 100644 index 00000000000..23cd702bf95 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/nestedContinue.js @@ -0,0 +1,11 @@ +x = 10; +outer: +while (x < 10) { + while (x < 10) { + if (x < 10) + continue outer; + while (x < 10) { + y = 0; + } + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/new-Function-prototype.js b/deps/mozjs/js/src/jit-test/tests/basic/new-Function-prototype.js index 943cbba350a..65198cabb11 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/new-Function-prototype.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/new-Function-prototype.js @@ -2,7 +2,7 @@ var funProto = Function.prototype; assertEq(Object.getOwnPropertyDescriptor(funProto, "prototype"), undefined); assertEq(parseInt.prototype, undefined); var oldObj; -for (var i = 0, sz = RUNLOOP; i < sz; oldObj = obj, i++) +for (var i = 0, sz = 9; i < sz; oldObj = obj, i++) { try { @@ -13,9 +13,4 @@ for (var i = 0, sz = RUNLOOP; i < sz; oldObj = obj, i++) assertEq(Object.getOwnPropertyDescriptor(parseInt, "prototype"), undefined); assertEq(parseInt.prototype, undefined); } -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1, - traceCompleted: 1, -}); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/new-bound-function.js b/deps/mozjs/js/src/jit-test/tests/basic/new-bound-function.js index db531a74311..49d06d8c3c5 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/new-bound-function.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/new-bound-function.js @@ -8,7 +8,7 @@ var YAxisPoint = Point.bind(null, 0); assertEq(YAxisPoint.prototype, undefined); var oldPoint; -for (var i = 0, sz = RUNLOOP; i < sz; oldPoint = point, i++) +for (var i = 0, sz = 9; i < sz; oldPoint = point, i++) { var point = new YAxisPoint(5); assertEq(point === oldPoint, false); @@ -18,9 +18,3 @@ for (var i = 0, sz = RUNLOOP; i < sz; oldPoint = point, i++) assertEq(Object.getOwnPropertyDescriptor(YAxisPoint, "prototype"), undefined); } -checkStats({ - recorderStarted: 1, - recorderAborted: 1, - traceTriggered: 0, - traceCompleted: 0, -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/primitiveProto.js b/deps/mozjs/js/src/jit-test/tests/basic/primitiveProto.js new file mode 100644 index 00000000000..e9043225c89 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/primitiveProto.js @@ -0,0 +1,24 @@ + +function fna() {} +fna.prototype = undefined; +new fna; + +function fnb() {} +fnb.prototype = null; +new fnb; + +function fnc() {} +fnc.prototype = 3; +new fnc; + +function fnd() {} +fnd.prototype = true; +new fnd; + +function fne() {} +fne.prototype = "foo"; +new fne; + +function fnf() {} +fnf.prototype = /foo/; +new fnf; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/regexp-multiline.js b/deps/mozjs/js/src/jit-test/tests/basic/regexp-multiline.js new file mode 100644 index 00000000000..7455b4e89ef --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/regexp-multiline.js @@ -0,0 +1,12 @@ +// visibility of updates to RegExp.multiline + +function foo(value) { + for (var i = 0; i < 50; i++) { + var re = /erwe/; + assertEq(re.multiline, value); + } +} + +foo(false); +RegExp.multiline = true; +foo(true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/regexp-test-direct-bug-694752.js b/deps/mozjs/js/src/jit-test/tests/basic/regexp-test-direct-bug-694752.js new file mode 100644 index 00000000000..7e0bae7dae2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/regexp-test-direct-bug-694752.js @@ -0,0 +1,15 @@ +function whoo() { + // Cause mjit to use the script's RegExpObject directly when it compiles this function. + assertEq(/foo.*baz/.test('foobarbaz'), true); +} + +var orig_test = RegExp.prototype.test +for (var i = 0; i < 50; ++i) + whoo(); +eval('RegExp.prototype.test = function(str) { return orig_test.call(this, str) }') +for (var i = 0; i < 50; ++i) + whoo(); +RegExp.prototype.test = orig_test; +gc(); +whoo(); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/regress-bug720680.js b/deps/mozjs/js/src/jit-test/tests/basic/regress-bug720680.js new file mode 100644 index 00000000000..ebb794fb545 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/regress-bug720680.js @@ -0,0 +1,15 @@ +// |jit-test| error: InternalError +version(0); +eval("\ +function TimeFromYear( y ) {}\ +addTestCase( -2208988800000 );\ +function addTestCase( t ) {\ + var start = TimeFromYear((addTestCase(addTestCase << t, 0)));\ + new TestCase( \ + SECTION,\ + '(new Date('+d+')).getUTCDay()',\ + WeekDay((d)),\ + (new Date(let ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \ + );\ +}\ +"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/setCall.js b/deps/mozjs/js/src/jit-test/tests/basic/setCall.js index fa9e95d8f1e..0e4950e1c29 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/setCall.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/setCall.js @@ -20,7 +20,3 @@ function f() { } f(); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/setprop-with-index.js b/deps/mozjs/js/src/jit-test/tests/basic/setprop-with-index.js new file mode 100644 index 00000000000..8d39c776003 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/setprop-with-index.js @@ -0,0 +1,5 @@ +function f() +{ + arguments['4294967295'] = 2; +} +assertEq(f(), undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/shapelessCalleeTest.js b/deps/mozjs/js/src/jit-test/tests/basic/shapelessCalleeTest.js index 78c42adefa8..fe10e9b4283 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/shapelessCalleeTest.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/shapelessCalleeTest.js @@ -1,6 +1,6 @@ // The following functions use a delay line of length 2 to change the value // of the callee without exiting the traced loop. This is obviously tuned to -// match the current HOTLOOP setting of 2. +// match the current 8 setting of 2. function shapelessArgCalleeLoop(f, g, h, a) { for (var i = 0; i < 10; i++) { diff --git a/deps/mozjs/js/src/jit-test/tests/basic/singleton.js b/deps/mozjs/js/src/jit-test/tests/basic/singleton.js new file mode 100644 index 00000000000..8e7c4c8faf1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/singleton.js @@ -0,0 +1,12 @@ + +var a = [1,2,3,4]; +var b = [{a:0,b:1},{a:0,b:1},{a:0,b:1}]; +var c = {a:0,b:4.5}; +var d = [1,2,3,true]; +var e = {a:0,b:1,c:2}; +var f = {a:0,b:1,c:true}; + +var w = JSON.parse('[1,2,3,4]'); +var x = JSON.parse('{"a":0,"b":true,"c":4.5}'); +var y = JSON.parse('{"d":true,"b":true,"c":4.5}'); +var z = JSON.parse('[{"a":0,"b":1},{"a":0,"b":1},{"a":0,"b":1}]'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-675164.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-675164.js new file mode 100644 index 00000000000..10d0d97887f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-675164.js @@ -0,0 +1,15 @@ +function NPList() {} +NPList.prototype = new Array; + +var list = new NPList(); +list.push('a'); + +var cut = list.splice(0, 1); + +assertEq(cut[0], 'a'); +assertEq(cut.length, 1); +assertEq(list.length, 0); + +var desc = Object.getOwnPropertyDescriptor(list, "0"); +assertEq(desc, undefined); +assertEq("0" in list, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js new file mode 100644 index 00000000000..4a0dc5d1592 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js @@ -0,0 +1,8 @@ +var o = { 0: 1, 1: 2, 2: 3, length: 3 }; +Array.prototype.splice.call(o, 0, 1); + +assertEq(o[0], 2); +assertEq(o[1], 3); +assertEq(Object.getOwnPropertyDescriptor(o, 2), undefined); +assertEq("2" in o, false); +assertEq(o.length, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-check-steps.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-check-steps.js new file mode 100644 index 00000000000..5f5e04fa065 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-check-steps.js @@ -0,0 +1,327 @@ +/* + * Check the order of splice's internal operations, because the ordering is + * visible externally. + */ + +function handlerMaker(obj, expected_exceptions) { + var order = []; + function note(trap, name) + { + order.push(trap + '-' + name); + if (expected_exceptions[trap] === name) { + throw ("fail"); + } + } + + return [{ + /* this is the only trap we care about */ + delete: function(name) { + note("del", name); + return delete obj[name]; + }, + + // Fundamental traps + getOwnPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + // a trapping proxy's properties must always be configurable + if (desc !== undefined) + desc.configurable = true; + return desc; + }, + getPropertyDescriptor: function(name) { + var desc = Object.getPropertyDescriptor(obj, name); // not in ES5 + // a trapping proxy's properties must always be configurable + if (desc !== undefined) + desc.configurable = true; + return desc; + }, + getOwnPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + }, + getPropertyNames: function() { + return Object.getPropertyNames(obj); // not in ES5 + }, + defineProperty: function(name, desc) { + note("def", name); + Object.defineProperty(obj, name, desc); + }, + fix: function() { + if (Object.isFrozen(obj)) { + return Object.getOwnPropertyNames(obj).map(function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }); + } + // As long as obj is not frozen, the proxy won't allow itself to be fixed + return undefined; // will cause a TypeError to be thrown + }, + + // derived traps + has: function(name) { + note("has", name); + return name in obj; + }, + hasOwn: function(name) { return Object.prototype.hasOwnProperty.call(obj, name); }, + get: function(receiver, name) { + note("get", name); + return obj[name]; + }, + set: function(receiver, name, val) { + note("set", name); + obj[name] = val; + return true; // bad behavior when set fails in non-strict mode + }, + enumerate: function() { + var result = []; + for (name in obj) + result.push(name); + return result; + }, + keys: function() { return Object.keys(obj) } + }, order]; +} + +// arr: the array to splice +// expected_order: the expected order of operations on arr, stringified +function check_splice_proxy(arr, expected_order, expected_exceptions, expected_array, expected_result) { + print (arr); + var [handler, store] = handlerMaker(arr, expected_exceptions); + var proxy = Proxy.create(handler); + + try { + var args = Array.prototype.slice.call(arguments, 5); + var result = Array.prototype.splice.apply(proxy, args); + assertEq(Object.keys(expected_exceptions).length, 0); + } catch (e) { + assertEq(Object.keys(expected_exceptions).length > 0, true); + } + + // check the order of the property accesses, etc + assertEq(store.toString(), expected_order); + + // The deleted elements are returned in an object that's always an Array. + assertEq(Array.isArray(result) || result === undefined, true); + + // check the return value + for (var i in expected_result) { + assertEq(result[i], expected_result[i]); + } + for (var i in result) { + assertEq(result[i], expected_result[i]); + } + + // check the value of arr + for (var i in expected_array) { + assertEq(arr[i], expected_array[i]); + } + for (var i in arr) { + assertEq(arr[i], expected_array[i]); + } + + return result; +} + +// Shrinking array +check_splice_proxy( + [10,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," + + "del-5,del-4,del-3," + + "set-length", + {}, + [3,4,5], + [10,1,2], + 0, 3 +); + +// Growing array +check_splice_proxy( + [11,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-9,has-4,get-4,set-8,has-3,get-3,set-7," + + "set-0,set-1,set-2,set-3,set-4,set-5,set-6," + + "set-length", + {}, + [9,9,9,9,9,9,9,3,4,5], + [11,1,2], + 0, 3, 9, 9, 9, 9, 9, 9, 9 +); + +// Same sized array +check_splice_proxy( + [12,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "set-0,set-1,set-2," + + "set-length", + {}, + [9,9,9,3,4,5], + [12,1,2], + 0, 3, 9, 9, 9 +); + + +/* + * Check that if we fail at a particular step in the algorithm, we don't + * continue with the algorithm beyond that step. + */ + + +// Step 3: fail when getting length +check_splice_proxy( + [13,1,2,3,4,5], + "get-length", + {get: 'length'}, + [13,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 9b: fail when [[HasProperty]] +check_splice_proxy( + [14,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1", + {has: '1'}, + [14,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 9c(i): fail when [[Get]] +check_splice_proxy( + [15,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1", + {get: '1'}, + [15,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 12b(iii): fail when [[HasProperty]] +check_splice_proxy( + [16,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4", + {has: '4'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + + +// Step 12b(iv)1: fail when [[Get]] +check_splice_proxy( + [17,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4", + {get: '4'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + + +// Step 12b(iv)2: fail when [[Put]] +check_splice_proxy( + [18,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1", + {set: '1'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + +// Step 12b(v)1: fail when [[Delete]] +check_splice_proxy( + [19,1,2,3,,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,del-1", + {del: '1'}, + [3,1,2,3,,5], + undefined, + 0, 3 +); + +// Step 12d(i): fail when [[Delete]] +check_splice_proxy( + [20,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," + + "del-5,del-4", + {del: '4'}, + [3,4,5,3,4], + undefined, + 0, 3 +); + +// Step 13b(iii): fail when [[HasProperty]] +check_splice_proxy( + [21,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4", + {has: '4'}, + [21,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + + +// Step 13b(iv)1: fail when [[Get]] +check_splice_proxy( + [22,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4", + {get: '4'}, + [22,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + + +// Step 13b(iv)2: fail when [[Put]] +check_splice_proxy( + [23,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4,set-7", + {set: '7'}, + [23,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + +// Step 13b(v)1: fail when [[Delete]] +check_splice_proxy( + [24,1,2,3,,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,del-7", + {del: '7'}, + [24,1,2,3,,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + +// Step 15b: fail when [[Put]] +check_splice_proxy( + [25,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4,set-7,has-3,get-3,set-6," + + "set-0,set-1,set-2", + {set: '2'}, + [9,9,2,3,4,5,3,4,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js new file mode 100644 index 00000000000..93dddb5cc6e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js @@ -0,0 +1,26 @@ +/* Test that splice causing deletion of a non-configurable property stops at exactly step 12(v) of ES5 15.4.4.12 */ + +var O = [1,2,3,4,5,6]; +var A = undefined; +Object.defineProperty(O, 3, { configurable: false }); + +try +{ + A = O.splice(0, 6); + throw new Error("didn't throw, returned " + A); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "deleting O[3] should have caused a TypeError"); +} + +assertEq(O.length, 6); // setting length not reached +assertEq(A, undefined); // return value not reached + +assertEq(O[5], undefined); // deletion reached +assertEq(O[4], undefined); // deletion reached +assertEq(O[3], 4); // deletion caused exception +assertEq(O[2], 3); // deletion not reached +assertEq(O[1], 2); // deletion not reached +assertEq(O[0], 1); // deletion not reached diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-fail-step-16.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-fail-step-16.js new file mode 100644 index 00000000000..e4ea35ada94 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-fail-step-16.js @@ -0,0 +1,29 @@ +// |jit-test| error: InternalError +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Remove when [].length is redefinable! + +/* Test that arrays resize normally during splice, even if .length is non-writable. */ + +var arr = [1, 2, 3, 4, 5, 6]; + +Object.defineProperty(arr, "length", {writable: false}); + +try +{ + var removed = arr.splice(3, 3, 9, 9, 9, 9); + throw new Error("splice didn't throw, returned [" + removed + "]"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "should have thrown a TypeError, instead threw " + e); +} + +// The exception should happen in step 16, which means we've already removed the array elements. +assertEq(arr[0], 1); +assertEq(arr[1], 2); +assertEq(arr[2], 3); +assertEq(arr[3], 9); +assertEq(arr[4], 9); +assertEq(arr[5], 9); +assertEq(arr[6], 9); +assertEq(arr.length, 6); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-huge-array-finishes.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-huge-array-finishes.js new file mode 100644 index 00000000000..d894e508880 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-huge-array-finishes.js @@ -0,0 +1,15 @@ +// Making the array huge and sparse shouldn't leave us iterating through the entire array. +// But it does, sadly. Disable, because it takes too long. +if (0) { + var arr = [1, 2, 3, 4, 5, 6, 7, 8]; + arr.length = Math.pow(2, 32) - 2; + arr.splice(5); // also test overflow + + assertEq(arr.length, 5); + assertEq(arr[0], 1); + assertEq(arr[1], 2); + assertEq(arr[2], 3); + assertEq(arr[3], 4); + assertEq(arr[4], 5); + assertEq(arr[5], undefined); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-on-arguments.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-on-arguments.js new file mode 100644 index 00000000000..e36522ae0c0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-on-arguments.js @@ -0,0 +1,39 @@ +// test whether splice works on arguments + +function splice_args () { + args = arguments; + return Array.prototype.splice.apply(args, [0, 5]); +} + +var args; +var O = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +var A = splice_args.apply(undefined, O) + +// args: [5, 6, 7, 8, 9] +assertEq(args[0], 5); +assertEq(args[1], 6); +assertEq(args[2], 7); +assertEq(args[3], 8); +assertEq(args[4], 9); +assertEq(args.length, 5); + +// A: [0, 1, 2, 3, 4] +assertEq(A[0], 0); +assertEq(A[1], 1); +assertEq(A[2], 2); +assertEq(A[3], 3); +assertEq(A[4], 4); +assertEq(A.length, 5); + +// O: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +assertEq(O[0], 0); +assertEq(O[1], 1); +assertEq(O[2], 2); +assertEq(O[3], 3); +assertEq(O[4], 4); +assertEq(O[5], 5); +assertEq(O[6], 6); +assertEq(O[7], 7); +assertEq(O[8], 8); +assertEq(O[9], 9); +assertEq(O.length, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js b/deps/mozjs/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js new file mode 100644 index 00000000000..695460b5c8a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js @@ -0,0 +1,9 @@ +try +{ + Array.prototype.splice.call({ get length() { throw 'error'; } }); + throw new Error("should have thrown, didn't"); +} +catch (e) +{ + assertEq(e, "error", "wrong error thrown: " + e); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/strict-catch-ident-syntax.js b/deps/mozjs/js/src/jit-test/tests/basic/strict-catch-ident-syntax.js new file mode 100644 index 00000000000..2058e5aa29c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/strict-catch-ident-syntax.js @@ -0,0 +1,9 @@ +/* Parse correctly. */ + +function assignToClassListStrict(e) { + "use strict"; + try { + e.classList = "foo"; + ok(false, "assigning to classList didn't throw"); + } catch (e) { } +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/strict-eval-loop-error.js b/deps/mozjs/js/src/jit-test/tests/basic/strict-eval-loop-error.js index f28467740ef..f09ad440282 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/strict-eval-loop-error.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/strict-eval-loop-error.js @@ -1,3 +1,3 @@ -var COUNT = RUNLOOP; +var COUNT = 9; eval("'use strict'; for (let j = 0; j < COUNT; j++); try { x; throw new Error(); } catch (e) { if (!(e instanceof ReferenceError)) throw e; }"); assertEq(typeof j, "undefined"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/terminate.js b/deps/mozjs/js/src/jit-test/tests/basic/terminate.js new file mode 100644 index 00000000000..f2966d783aa --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/terminate.js @@ -0,0 +1,10 @@ +// |jit-test| exitstatus: 3 +try { + terminate(); + assertEq("execution continued", "execution should not continue"); +} catch (x) { + assertEq("caught exception", "uncatchable"); +} finally { + assertEq("'finally' clause ran", "'finally' clause should not run"); +} +assertEq("top-level execution continued", "top-level execution should not continue"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/test-apply-many-args.js b/deps/mozjs/js/src/jit-test/tests/basic/test-apply-many-args.js new file mode 100644 index 00000000000..ea02f4df353 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/test-apply-many-args.js @@ -0,0 +1,14 @@ +function f(x) { + if (x == 0) + return; + arguments[0]--; + f.apply(null, arguments); +} + +// When the apply-optimization isn't on, each recursive call chews up the C +// stack, so don't push it. +a = [20]; + +for (var i = 0; i < 2000; ++i) + a.push(i); +f.apply(null, a); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testAddAnyInconvertibleObject.js b/deps/mozjs/js/src/jit-test/tests/basic/testAddAnyInconvertibleObject.js index 9995471d32a..4a4b1d85755 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testAddAnyInconvertibleObject.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testAddAnyInconvertibleObject.js @@ -26,8 +26,3 @@ function testAddAnyInconvertibleObject() return "pass"; } assertEq(testAddAnyInconvertibleObject(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectAny.js b/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectAny.js index 76c072e8013..aabf8ec5739 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectAny.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectAny.js @@ -32,8 +32,3 @@ function testAddInconvertibleObjectAny() return "pass"; } assertEq(testAddInconvertibleObjectAny(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectInconvertibleObject.js b/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectInconvertibleObject.js index eabc75774e9..5004dbbe5ca 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectInconvertibleObject.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testAddInconvertibleObjectInconvertibleObject.js @@ -31,8 +31,3 @@ function testAddInconvertibleObjectInconvertibleObject() return "pass"; } assertEq(testAddInconvertibleObjectInconvertibleObject(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testAddNull.js b/deps/mozjs/js/src/jit-test/tests/basic/testAddNull.js index c5aa863abaf..78b40a519a0 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testAddNull.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testAddNull.js @@ -1,13 +1,8 @@ function testAddNull() { var rv; - for (var x = 0; x < HOTLOOP + 1; ++x) + for (var x = 0; x < 9; ++x) rv = null + [,,]; return rv; } assertEq(testAddNull(), "null,"); -checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 1, - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testApplyAtJoinPoint.js b/deps/mozjs/js/src/jit-test/tests/basic/testApplyAtJoinPoint.js index 0ed3f583c5c..bb7f219abf4 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testApplyAtJoinPoint.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testApplyAtJoinPoint.js @@ -8,5 +8,5 @@ function f() { return g.apply(null, isTrue ? ["happy"] : arguments); } -for (var i = 0; i < HOTLOOP + 10; ++i) +for (var i = 0; i < 18; ++i) assertEq(f("sad"), "happy"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered.js b/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered.js new file mode 100644 index 00000000000..e35659f9e82 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered.js @@ -0,0 +1,15 @@ + +/* Read correct return value when the interpreter pops a lowered call/apply. */ + +function recompile() {} + +function foo() { + if (arguments[0] == 9) + recompile(); + return arguments[0]; +} +function bar() { + for (var i = 0; i < 10; i++) + assertEq(foo.apply(null, [i]), i); +} +bar(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered2.js b/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered2.js new file mode 100644 index 00000000000..0564ead0b65 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testApplyInterpretLowered2.js @@ -0,0 +1,12 @@ + +/* Make sure the interpreter can pop lowered frames pushed by CompileFunction. */ + +function recompile() {} + +function bar() { + for (var i = 0; i < 50; i++) { + var foo = new Function("recompile(arguments[0] + " + i + "); return arguments[0]"); + assertEq(foo.apply(null, [i]), i); + } +} +bar(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testArrayBufferSlice.js b/deps/mozjs/js/src/jit-test/tests/basic/testArrayBufferSlice.js new file mode 100644 index 00000000000..caff58f43ad --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testArrayBufferSlice.js @@ -0,0 +1,45 @@ +function testSlice() { + function test(subBuf, starts, size) { + var byteLength = size; + var subBuffer = eval(subBuf); + var subArray = new Int8Array(subBuffer); + assertEq(subBuffer.byteLength, byteLength); + for (var i = 0; i < size; ++i) + assertEq(starts + i, subArray[i]); + } + + var buffer = new ArrayBuffer(32); + var array = new Int8Array(buffer); + for (var i = 0; i < 32; ++i) + array[i] = i; + + test("buffer.slice(0)", 0, 32); + test("buffer.slice(16)", 16, 16); + test("buffer.slice(24)", 24, 8); + test("buffer.slice(32)", 32, 0); + test("buffer.slice(40)", 32, 0); + test("buffer.slice(80)", 32, 0); + + test("buffer.slice(-8)", 24, 8); + test("buffer.slice(-16)", 16, 16); + test("buffer.slice(-24)", 8, 24); + test("buffer.slice(-32)", 0, 32); + test("buffer.slice(-40)", 0, 32); + test("buffer.slice(-80)", 0, 32); + + test("buffer.slice(0, 32)", 0, 32); + test("buffer.slice(0, 16)", 0, 16); + test("buffer.slice(8, 24)", 8, 16); + test("buffer.slice(16, 32)", 16, 16); + test("buffer.slice(24, 16)", 24, 0); + + test("buffer.slice(16, -8)", 16, 8); + test("buffer.slice(-20, 30)", 12, 18); + + test("buffer.slice(-8, -20)", 24, 0); + test("buffer.slice(-20, -8)", 12, 12); + test("buffer.slice(-40, 16)", 0, 16); + test("buffer.slice(-40, 40)", 0, 32); +} + +testSlice(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testArrayConcat.js b/deps/mozjs/js/src/jit-test/tests/basic/testArrayConcat.js new file mode 100644 index 00000000000..ec29e435365 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testArrayConcat.js @@ -0,0 +1,10 @@ + +var x = Array(4); +x[0] = 1; +x[1] = 2; +x[2] = 3; +var y = x.concat(); +assertEq(y[3], undefined); + +var z = x.concat(/abc/).pop(); +assertEq(z.source, "abc"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testArrayIn.js b/deps/mozjs/js/src/jit-test/tests/basic/testArrayIn.js deleted file mode 100644 index 47b7cf6c352..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testArrayIn.js +++ /dev/null @@ -1,54 +0,0 @@ -// Give us enough room to work with some holes too -const array_size = RUNLOOP + 20; - -function testArrayIn() -{ - var arr = new Array(array_size); - var i; - for (i = 0; i < array_size; ++i) { - arr[i] = i; - } - - // Sanity check here - checkStats({ - traceCompleted: 1, - traceTriggered: 1, - sideExitIntoInterpreter: 1 - }); - - delete arr[RUNLOOP + 5]; - delete arr[RUNLOOP + 10]; - - var ret = []; - for (i = 0; i < array_size; ++i) { - ret.push(i in arr); - } - - checkStats({ - traceCompleted: 2, - traceTriggered: 2, - sideExitIntoInterpreter: 2 - }); - - - var ret2; - for (i = 0; i < RUNLOOP; ++i) { - ret2 = array_size in arr; - } - - checkStats({ - traceCompleted: 3, - traceTriggered: 3, - sideExitIntoInterpreter: 3 - }); - - return [ret, ret2]; -} - -var [ret, ret2] = testArrayIn(); - -assertEq(ret2, false); - -for (var i = 0; i < array_size; ++i) { - assertEq(ret[i], i != RUNLOOP + 5 && i != RUNLOOP + 10); -} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testArrayInWithIndexedProto.js b/deps/mozjs/js/src/jit-test/tests/basic/testArrayInWithIndexedProto.js index 8bfc8a6a605..97da0ee07a1 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testArrayInWithIndexedProto.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testArrayInWithIndexedProto.js @@ -2,17 +2,17 @@ function testArrayInWithIndexedProto() { Array.prototype[0] = "Got me"; var zeroPresent, zeroPresent2; - // Need to go to 2*RUNLOOP because in the failure mode this is + // Need to go to 18 because in the failure mode this is // testing we have various side-exits in there due to interp and // tracer not agreeing that confuse the issue and cause us to not - // hit the bad case within RUNLOOP iterations. - for (var j = 0; j < 2*RUNLOOP; ++j) { + // hit the bad case within 9 iterations. + for (var j = 0; j < 18; ++j) { zeroPresent = 0 in []; } var arr = [1, 2]; delete arr[0]; - for (var j = 0; j < 2*RUNLOOP; ++j) { + for (var j = 0; j < 18; ++j) { zeroPresent2 = 0 in arr; } return [zeroPresent, zeroPresent2]; @@ -22,10 +22,3 @@ var [ret, ret2] = testArrayInWithIndexedProto(); assertEq(ret, true); assertEq(ret2, true); -checkStats({ - traceCompleted: 0, - traceTriggered: 0, - sideExitIntoInterpreter: 0, - recorderStarted: 2, - recorderAborted: 2 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js b/deps/mozjs/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js index 62fa710f5a9..c22eabed087 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js @@ -1,7 +1,7 @@ var o = {}; o.watch("p", function() { }); -for (var i = 0; i < HOTLOOP + 2; i++) { +for (var i = 0; i < 10; i++) { o.p = 123; delete o.p; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrAnyInconvertibleObject.js b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrAnyInconvertibleObject.js index f0d1db3952f..d3daadbea46 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrAnyInconvertibleObject.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrAnyInconvertibleObject.js @@ -26,8 +26,3 @@ function testBitOrAnyInconvertibleObject() return "pass"; } assertEq(testBitOrAnyInconvertibleObject(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectAny.js b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectAny.js index 2ccb0d16e58..29d983ab090 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectAny.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectAny.js @@ -26,8 +26,3 @@ function testBitOrInconvertibleObjectAny() return "pass"; } assertEq(testBitOrInconvertibleObjectAny(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectInconvertibleObject.js b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectInconvertibleObject.js index fd4242f5530..420b431ea07 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectInconvertibleObject.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBitOrInconvertibleObjectInconvertibleObject.js @@ -31,8 +31,3 @@ function testBitOrInconvertibleObjectInconvertibleObject() return "pass"; } assertEq(testBitOrInconvertibleObjectInconvertibleObject(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBitopWithConstan.js b/deps/mozjs/js/src/jit-test/tests/basic/testBitopWithConstan.js new file mode 100644 index 00000000000..ddd4ff95fda --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBitopWithConstan.js @@ -0,0 +1,741 @@ +function test1 (x) { + assertEq(x | -1, -1); + assertEq(-1 | x, -1); + assertEq(x & -1, 1); + assertEq(-1 & x, 1); + assertEq(x ^ -1, -2); + assertEq(-1 ^ x, -2); + assertEq(x >> -1, 0); + assertEq(-1 >> x, -1); + assertEq(x >>> -1, 0); + assertEq(-1 >>> x, 2147483647); + assertEq(x << -1, -2147483648); + assertEq(-1 << x, -2); + assertEq(x | 1, 1); + assertEq(1 | x, 1); + assertEq(x & 1, 1); + assertEq(1 & x, 1); + assertEq(x ^ 1, 0); + assertEq(1 ^ x, 0); + assertEq(x >> 1, 0); + assertEq(1 >> x, 0); + assertEq(x >>> 1, 0); + assertEq(1 >>> x, 0); + assertEq(x << 1, 2); + assertEq(1 << x, 2); + assertEq(x | 0, 1); + assertEq(0 | x, 1); + assertEq(x & 0, 0); + assertEq(0 & x, 0); + assertEq(x ^ 0, 1); + assertEq(0 ^ x, 1); + assertEq(x >> 0, 1); + assertEq(0 >> x, 0); + assertEq(x >>> 0, 1); + assertEq(0 >>> x, 0); + assertEq(x << 0, 1); + assertEq(0 << x, 0); + assertEq(x | 0xffffffff, -1); + assertEq(0xffffffff | x, -1); + assertEq(x & 0xffffffff, 1); + assertEq(0xffffffff & x, 1); + assertEq(x ^ 0xffffffff, -2); + assertEq(0xffffffff ^ x, -2); + assertEq(x >> 0xffffffff, 0); + assertEq(0xffffffff >> x, -1); + assertEq(x >>> 0xffffffff, 0); + assertEq(0xffffffff >>> x, 2147483647); + assertEq(x << 0xffffffff, -2147483648); + assertEq(0xffffffff << x, -2); + assertEq(x | "10.6", 11); + assertEq("10.6" | x, 11); + assertEq(x & "10.6", 0); + assertEq("10.6" & x, 0); + assertEq(x ^ "10.6", 11); + assertEq("10.6" ^ x, 11); + assertEq(x >> "10.6", 0); + assertEq("10.6" >> x, 5); + assertEq(x >>> "10.6", 0); + assertEq("10.6" >>> x, 5); + assertEq(x << "10.6", 1024); + assertEq("10.6" << x, 20); + assertEq(x | 2147483648, -2147483647); + assertEq(2147483648 | x, -2147483647); + assertEq(x & 2147483648, 0); + assertEq(2147483648 & x, 0); + assertEq(x ^ 2147483648, -2147483647); + assertEq(2147483648 ^ x, -2147483647); + assertEq(x >> 2147483648, 1); + assertEq(2147483648 >> x, -1073741824); + assertEq(x >>> 2147483648, 1); + assertEq(2147483648 >>> x, 1073741824); + assertEq(x << 2147483648, 1); + assertEq(2147483648 << x, 0); + assertEq(x | 4294967296, 1); + assertEq(4294967296 | x, 1); + assertEq(x & 4294967296, 0); + assertEq(4294967296 & x, 0); + assertEq(x ^ 4294967296, 1); + assertEq(4294967296 ^ x, 1); + assertEq(x >> 4294967296, 1); + assertEq(4294967296 >> x, 0); + assertEq(x >>> 4294967296, 1); + assertEq(4294967296 >>> x, 0); + assertEq(x << 4294967296, 1); + assertEq(4294967296 << x, 0); + assertEq(x | undefined, 1); + assertEq(undefined | x, 1); + assertEq(x & undefined, 0); + assertEq(undefined & x, 0); + assertEq(x ^ undefined, 1); + assertEq(undefined ^ x, 1); + assertEq(x >> undefined, 1); + assertEq(undefined >> x, 0); + assertEq(x >>> undefined, 1); + assertEq(undefined >>> x, 0); + assertEq(x << undefined, 1); + assertEq(undefined << x, 0); + assertEq(x | null, 1); + assertEq(null | x, 1); + assertEq(x & null, 0); + assertEq(null & x, 0); + assertEq(x ^ null, 1); + assertEq(null ^ x, 1); + assertEq(x >> null, 1); + assertEq(null >> x, 0); + assertEq(x >>> null, 1); + assertEq(null >>> x, 0); + assertEq(x << null, 1); + assertEq(null << x, 0); + assertEq(x | false, 1); + assertEq(false | x, 1); + assertEq(x & false, 0); + assertEq(false & x, 0); + assertEq(x ^ false, 1); + assertEq(false ^ x, 1); + assertEq(x >> false, 1); + assertEq(false >> x, 0); + assertEq(x >>> false, 1); + assertEq(false >>> x, 0); + assertEq(x << false, 1); + assertEq(false << x, 0); + assertEq(x | true, 1); + assertEq(true | x, 1); + assertEq(x & true, 1); + assertEq(true & x, 1); + assertEq(x ^ true, 0); + assertEq(true ^ x, 0); + assertEq(x >> true, 0); + assertEq(true >> x, 0); + assertEq(x >>> true, 0); + assertEq(true >>> x, 0); + assertEq(x << true, 2); + assertEq(true << x, 2); + assertEq(x | -1.5, -1); + assertEq(-1.5 | x, -1); + assertEq(x & -1.5, 1); + assertEq(-1.5 & x, 1); + assertEq(x ^ -1.5, -2); + assertEq(-1.5 ^ x, -2); + assertEq(x >> -1.5, 0); + assertEq(-1.5 >> x, -1); + assertEq(x >>> -1.5, 0); + assertEq(-1.5 >>> x, 2147483647); + assertEq(x << -1.5, -2147483648); + assertEq(-1.5 << x, -2); +} +test1(1) + +function test2 (x) { + assertEq(x | -1, -1); + assertEq(-1 | x, -1); + assertEq(x & -1, 0); + assertEq(-1 & x, 0); + assertEq(x ^ -1, -1); + assertEq(-1 ^ x, -1); + assertEq(x >> -1, 0); + assertEq(-1 >> x, -1); + assertEq(x >>> -1, 0); + assertEq(-1 >>> x, 4294967295); + assertEq(x << -1, 0); + assertEq(-1 << x, -1); + assertEq(x | 1, 1); + assertEq(1 | x, 1); + assertEq(x & 1, 0); + assertEq(1 & x, 0); + assertEq(x ^ 1, 1); + assertEq(1 ^ x, 1); + assertEq(x >> 1, 0); + assertEq(1 >> x, 1); + assertEq(x >>> 1, 0); + assertEq(1 >>> x, 1); + assertEq(x << 1, 0); + assertEq(1 << x, 1); + assertEq(x | 0, 0); + assertEq(0 | x, 0); + assertEq(x & 0, 0); + assertEq(0 & x, 0); + assertEq(x ^ 0, 0); + assertEq(0 ^ x, 0); + assertEq(x >> 0, 0); + assertEq(0 >> x, 0); + assertEq(x >>> 0, 0); + assertEq(0 >>> x, 0); + assertEq(x << 0, 0); + assertEq(0 << x, 0); + assertEq(x | 0xffffffff, -1); + assertEq(0xffffffff | x, -1); + assertEq(x & 0xffffffff, 0); + assertEq(0xffffffff & x, 0); + assertEq(x ^ 0xffffffff, -1); + assertEq(0xffffffff ^ x, -1); + assertEq(x >> 0xffffffff, 0); + assertEq(0xffffffff >> x, -1); + assertEq(x >>> 0xffffffff, 0); + assertEq(0xffffffff >>> x, 4294967295); + assertEq(x << 0xffffffff, 0); + assertEq(0xffffffff << x, -1); + assertEq(x | "10.6", 10); + assertEq("10.6" | x, 10); + assertEq(x & "10.6", 0); + assertEq("10.6" & x, 0); + assertEq(x ^ "10.6", 10); + assertEq("10.6" ^ x, 10); + assertEq(x >> "10.6", 0); + assertEq("10.6" >> x, 10); + assertEq(x >>> "10.6", 0); + assertEq("10.6" >>> x, 10); + assertEq(x << "10.6", 0); + assertEq("10.6" << x, 10); + assertEq(x | 2147483648, -2147483648); + assertEq(2147483648 | x, -2147483648); + assertEq(x & 2147483648, 0); + assertEq(2147483648 & x, 0); + assertEq(x ^ 2147483648, -2147483648); + assertEq(2147483648 ^ x, -2147483648); + assertEq(x >> 2147483648, 0); + assertEq(2147483648 >> x, -2147483648); + assertEq(x >>> 2147483648, 0); + assertEq(2147483648 >>> x, 2147483648); + assertEq(x << 2147483648, 0); + assertEq(2147483648 << x, -2147483648); + assertEq(x | 4294967296, 0); + assertEq(4294967296 | x, 0); + assertEq(x & 4294967296, 0); + assertEq(4294967296 & x, 0); + assertEq(x ^ 4294967296, 0); + assertEq(4294967296 ^ x, 0); + assertEq(x >> 4294967296, 0); + assertEq(4294967296 >> x, 0); + assertEq(x >>> 4294967296, 0); + assertEq(4294967296 >>> x, 0); + assertEq(x << 4294967296, 0); + assertEq(4294967296 << x, 0); + assertEq(x | undefined, 0); + assertEq(undefined | x, 0); + assertEq(x & undefined, 0); + assertEq(undefined & x, 0); + assertEq(x ^ undefined, 0); + assertEq(undefined ^ x, 0); + assertEq(x >> undefined, 0); + assertEq(undefined >> x, 0); + assertEq(x >>> undefined, 0); + assertEq(undefined >>> x, 0); + assertEq(x << undefined, 0); + assertEq(undefined << x, 0); + assertEq(x | null, 0); + assertEq(null | x, 0); + assertEq(x & null, 0); + assertEq(null & x, 0); + assertEq(x ^ null, 0); + assertEq(null ^ x, 0); + assertEq(x >> null, 0); + assertEq(null >> x, 0); + assertEq(x >>> null, 0); + assertEq(null >>> x, 0); + assertEq(x << null, 0); + assertEq(null << x, 0); + assertEq(x | false, 0); + assertEq(false | x, 0); + assertEq(x & false, 0); + assertEq(false & x, 0); + assertEq(x ^ false, 0); + assertEq(false ^ x, 0); + assertEq(x >> false, 0); + assertEq(false >> x, 0); + assertEq(x >>> false, 0); + assertEq(false >>> x, 0); + assertEq(x << false, 0); + assertEq(false << x, 0); + assertEq(x | true, 1); + assertEq(true | x, 1); + assertEq(x & true, 0); + assertEq(true & x, 0); + assertEq(x ^ true, 1); + assertEq(true ^ x, 1); + assertEq(x >> true, 0); + assertEq(true >> x, 1); + assertEq(x >>> true, 0); + assertEq(true >>> x, 1); + assertEq(x << true, 0); + assertEq(true << x, 1); + assertEq(x | -1.5, -1); + assertEq(-1.5 | x, -1); + assertEq(x & -1.5, 0); + assertEq(-1.5 & x, 0); + assertEq(x ^ -1.5, -1); + assertEq(-1.5 ^ x, -1); + assertEq(x >> -1.5, 0); + assertEq(-1.5 >> x, -1); + assertEq(x >>> -1.5, 0); + assertEq(-1.5 >>> x, 4294967295); + assertEq(x << -1.5, 0); + assertEq(-1.5 << x, -1); +} +test2(0) + +function test3 (x) { + assertEq(x | -1, -1); + assertEq(-1 | x, -1); + assertEq(x & -1, -1); + assertEq(-1 & x, -1); + assertEq(x ^ -1, 0); + assertEq(-1 ^ x, 0); + assertEq(x >> -1, -1); + assertEq(-1 >> x, -1); + assertEq(x >>> -1, 1); + assertEq(-1 >>> x, 1); + assertEq(x << -1, -2147483648); + assertEq(-1 << x, -2147483648); + assertEq(x | 1, -1); + assertEq(1 | x, -1); + assertEq(x & 1, 1); + assertEq(1 & x, 1); + assertEq(x ^ 1, -2); + assertEq(1 ^ x, -2); + assertEq(x >> 1, -1); + assertEq(1 >> x, 0); + assertEq(x >>> 1, 2147483647); + assertEq(1 >>> x, 0); + assertEq(x << 1, -2); + assertEq(1 << x, -2147483648); + assertEq(x | 0, -1); + assertEq(0 | x, -1); + assertEq(x & 0, 0); + assertEq(0 & x, 0); + assertEq(x ^ 0, -1); + assertEq(0 ^ x, -1); + assertEq(x >> 0, -1); + assertEq(0 >> x, 0); + assertEq(x >>> 0, 4294967295); + assertEq(0 >>> x, 0); + assertEq(x << 0, -1); + assertEq(0 << x, 0); + assertEq(x | 0xffffffff, -1); + assertEq(0xffffffff | x, -1); + assertEq(x & 0xffffffff, -1); + assertEq(0xffffffff & x, -1); + assertEq(x ^ 0xffffffff, 0); + assertEq(0xffffffff ^ x, 0); + assertEq(x >> 0xffffffff, -1); + assertEq(0xffffffff >> x, -1); + assertEq(x >>> 0xffffffff, 1); + assertEq(0xffffffff >>> x, 1); + assertEq(x << 0xffffffff, -2147483648); + assertEq(0xffffffff << x, -2147483648); + assertEq(x | "10.6", -1); + assertEq("10.6" | x, -1); + assertEq(x & "10.6", 10); + assertEq("10.6" & x, 10); + assertEq(x ^ "10.6", -11); + assertEq("10.6" ^ x, -11); + assertEq(x >> "10.6", -1); + assertEq("10.6" >> x, 0); + assertEq(x >>> "10.6", 4194303); + assertEq("10.6" >>> x, 0); + assertEq(x << "10.6", -1024); + assertEq("10.6" << x, 0); + assertEq(x | 2147483648, -1); + assertEq(2147483648 | x, -1); + assertEq(x & 2147483648, -2147483648); + assertEq(2147483648 & x, -2147483648); + assertEq(x ^ 2147483648, 2147483647); + assertEq(2147483648 ^ x, 2147483647); + assertEq(x >> 2147483648, -1); + assertEq(2147483648 >> x, -1); + assertEq(x >>> 2147483648, 4294967295); + assertEq(2147483648 >>> x, 1); + assertEq(x << 2147483648, -1); + assertEq(2147483648 << x, 0); + assertEq(x | 4294967296, -1); + assertEq(4294967296 | x, -1); + assertEq(x & 4294967296, 0); + assertEq(4294967296 & x, 0); + assertEq(x ^ 4294967296, -1); + assertEq(4294967296 ^ x, -1); + assertEq(x >> 4294967296, -1); + assertEq(4294967296 >> x, 0); + assertEq(x >>> 4294967296, 4294967295); + assertEq(4294967296 >>> x, 0); + assertEq(x << 4294967296, -1); + assertEq(4294967296 << x, 0); + assertEq(x | undefined, -1); + assertEq(undefined | x, -1); + assertEq(x & undefined, 0); + assertEq(undefined & x, 0); + assertEq(x ^ undefined, -1); + assertEq(undefined ^ x, -1); + assertEq(x >> undefined, -1); + assertEq(undefined >> x, 0); + assertEq(x >>> undefined, 4294967295); + assertEq(undefined >>> x, 0); + assertEq(x << undefined, -1); + assertEq(undefined << x, 0); + assertEq(x | null, -1); + assertEq(null | x, -1); + assertEq(x & null, 0); + assertEq(null & x, 0); + assertEq(x ^ null, -1); + assertEq(null ^ x, -1); + assertEq(x >> null, -1); + assertEq(null >> x, 0); + assertEq(x >>> null, 4294967295); + assertEq(null >>> x, 0); + assertEq(x << null, -1); + assertEq(null << x, 0); + assertEq(x | false, -1); + assertEq(false | x, -1); + assertEq(x & false, 0); + assertEq(false & x, 0); + assertEq(x ^ false, -1); + assertEq(false ^ x, -1); + assertEq(x >> false, -1); + assertEq(false >> x, 0); + assertEq(x >>> false, 4294967295); + assertEq(false >>> x, 0); + assertEq(x << false, -1); + assertEq(false << x, 0); + assertEq(x | true, -1); + assertEq(true | x, -1); + assertEq(x & true, 1); + assertEq(true & x, 1); + assertEq(x ^ true, -2); + assertEq(true ^ x, -2); + assertEq(x >> true, -1); + assertEq(true >> x, 0); + assertEq(x >>> true, 2147483647); + assertEq(true >>> x, 0); + assertEq(x << true, -2); + assertEq(true << x, -2147483648); + assertEq(x | -1.5, -1); + assertEq(-1.5 | x, -1); + assertEq(x & -1.5, -1); + assertEq(-1.5 & x, -1); + assertEq(x ^ -1.5, 0); + assertEq(-1.5 ^ x, 0); + assertEq(x >> -1.5, -1); + assertEq(-1.5 >> x, -1); + assertEq(x >>> -1.5, 1); + assertEq(-1.5 >>> x, 1); + assertEq(x << -1.5, -2147483648); + assertEq(-1.5 << x, -2147483648); +} +test3(-1) + +function test4 (x) { + assertEq(x | -1, -1); + assertEq(-1 | x, -1); + assertEq(x & -1, -2147483648); + assertEq(-1 & x, -2147483648); + assertEq(x ^ -1, 2147483647); + assertEq(-1 ^ x, 2147483647); + assertEq(x >> -1, -1); + assertEq(-1 >> x, -1); + assertEq(x >>> -1, 1); + assertEq(-1 >>> x, 4294967295); + assertEq(x << -1, 0); + assertEq(-1 << x, -1); + assertEq(x | 1, -2147483647); + assertEq(1 | x, -2147483647); + assertEq(x & 1, 0); + assertEq(1 & x, 0); + assertEq(x ^ 1, -2147483647); + assertEq(1 ^ x, -2147483647); + assertEq(x >> 1, -1073741824); + assertEq(1 >> x, 1); + assertEq(x >>> 1, 1073741824); + assertEq(1 >>> x, 1); + assertEq(x << 1, 0); + assertEq(1 << x, 1); + assertEq(x | 0, -2147483648); + assertEq(0 | x, -2147483648); + assertEq(x & 0, 0); + assertEq(0 & x, 0); + assertEq(x ^ 0, -2147483648); + assertEq(0 ^ x, -2147483648); + assertEq(x >> 0, -2147483648); + assertEq(0 >> x, 0); + assertEq(x >>> 0, 2147483648); + assertEq(0 >>> x, 0); + assertEq(x << 0, -2147483648); + assertEq(0 << x, 0); + assertEq(x | 0xffffffff, -1); + assertEq(0xffffffff | x, -1); + assertEq(x & 0xffffffff, -2147483648); + assertEq(0xffffffff & x, -2147483648); + assertEq(x ^ 0xffffffff, 2147483647); + assertEq(0xffffffff ^ x, 2147483647); + assertEq(x >> 0xffffffff, -1); + assertEq(0xffffffff >> x, -1); + assertEq(x >>> 0xffffffff, 1); + assertEq(0xffffffff >>> x, 4294967295); + assertEq(x << 0xffffffff, 0); + assertEq(0xffffffff << x, -1); + assertEq(x | "10.6", -2147483638); + assertEq("10.6" | x, -2147483638); + assertEq(x & "10.6", 0); + assertEq("10.6" & x, 0); + assertEq(x ^ "10.6", -2147483638); + assertEq("10.6" ^ x, -2147483638); + assertEq(x >> "10.6", -2097152); + assertEq("10.6" >> x, 10); + assertEq(x >>> "10.6", 2097152); + assertEq("10.6" >>> x, 10); + assertEq(x << "10.6", 0); + assertEq("10.6" << x, 10); + assertEq(x | 2147483648, -2147483648); + assertEq(2147483648 | x, -2147483648); + assertEq(x & 2147483648, -2147483648); + assertEq(2147483648 & x, -2147483648); + assertEq(x ^ 2147483648, 0); + assertEq(2147483648 ^ x, 0); + assertEq(x >> 2147483648, -2147483648); + assertEq(2147483648 >> x, -2147483648); + assertEq(x >>> 2147483648, 2147483648); + assertEq(2147483648 >>> x, 2147483648); + assertEq(x << 2147483648, -2147483648); + assertEq(2147483648 << x, -2147483648); + assertEq(x | 4294967296, -2147483648); + assertEq(4294967296 | x, -2147483648); + assertEq(x & 4294967296, 0); + assertEq(4294967296 & x, 0); + assertEq(x ^ 4294967296, -2147483648); + assertEq(4294967296 ^ x, -2147483648); + assertEq(x >> 4294967296, -2147483648); + assertEq(4294967296 >> x, 0); + assertEq(x >>> 4294967296, 2147483648); + assertEq(4294967296 >>> x, 0); + assertEq(x << 4294967296, -2147483648); + assertEq(4294967296 << x, 0); + assertEq(x | undefined, -2147483648); + assertEq(undefined | x, -2147483648); + assertEq(x & undefined, 0); + assertEq(undefined & x, 0); + assertEq(x ^ undefined, -2147483648); + assertEq(undefined ^ x, -2147483648); + assertEq(x >> undefined, -2147483648); + assertEq(undefined >> x, 0); + assertEq(x >>> undefined, 2147483648); + assertEq(undefined >>> x, 0); + assertEq(x << undefined, -2147483648); + assertEq(undefined << x, 0); + assertEq(x | null, -2147483648); + assertEq(null | x, -2147483648); + assertEq(x & null, 0); + assertEq(null & x, 0); + assertEq(x ^ null, -2147483648); + assertEq(null ^ x, -2147483648); + assertEq(x >> null, -2147483648); + assertEq(null >> x, 0); + assertEq(x >>> null, 2147483648); + assertEq(null >>> x, 0); + assertEq(x << null, -2147483648); + assertEq(null << x, 0); + assertEq(x | false, -2147483648); + assertEq(false | x, -2147483648); + assertEq(x & false, 0); + assertEq(false & x, 0); + assertEq(x ^ false, -2147483648); + assertEq(false ^ x, -2147483648); + assertEq(x >> false, -2147483648); + assertEq(false >> x, 0); + assertEq(x >>> false, 2147483648); + assertEq(false >>> x, 0); + assertEq(x << false, -2147483648); + assertEq(false << x, 0); + assertEq(x | true, -2147483647); + assertEq(true | x, -2147483647); + assertEq(x & true, 0); + assertEq(true & x, 0); + assertEq(x ^ true, -2147483647); + assertEq(true ^ x, -2147483647); + assertEq(x >> true, -1073741824); + assertEq(true >> x, 1); + assertEq(x >>> true, 1073741824); + assertEq(true >>> x, 1); + assertEq(x << true, 0); + assertEq(true << x, 1); + assertEq(x | -1.5, -1); + assertEq(-1.5 | x, -1); + assertEq(x & -1.5, -2147483648); + assertEq(-1.5 & x, -2147483648); + assertEq(x ^ -1.5, 2147483647); + assertEq(-1.5 ^ x, 2147483647); + assertEq(x >> -1.5, -1); + assertEq(-1.5 >> x, -1); + assertEq(x >>> -1.5, 1); + assertEq(-1.5 >>> x, 4294967295); + assertEq(x << -1.5, 0); + assertEq(-1.5 << x, -1); +} +test4(2147483648) + +function test5 (x) { + assertEq(x | -1, -1); + assertEq(-1 | x, -1); + assertEq(x & -1, -2147483648); + assertEq(-1 & x, -2147483648); + assertEq(x ^ -1, 2147483647); + assertEq(-1 ^ x, 2147483647); + assertEq(x >> -1, -1); + assertEq(-1 >> x, -1); + assertEq(x >>> -1, 1); + assertEq(-1 >>> x, 4294967295); + assertEq(x << -1, 0); + assertEq(-1 << x, -1); + assertEq(x | 1, -2147483647); + assertEq(1 | x, -2147483647); + assertEq(x & 1, 0); + assertEq(1 & x, 0); + assertEq(x ^ 1, -2147483647); + assertEq(1 ^ x, -2147483647); + assertEq(x >> 1, -1073741824); + assertEq(1 >> x, 1); + assertEq(x >>> 1, 1073741824); + assertEq(1 >>> x, 1); + assertEq(x << 1, 0); + assertEq(1 << x, 1); + assertEq(x | 0, -2147483648); + assertEq(0 | x, -2147483648); + assertEq(x & 0, 0); + assertEq(0 & x, 0); + assertEq(x ^ 0, -2147483648); + assertEq(0 ^ x, -2147483648); + assertEq(x >> 0, -2147483648); + assertEq(0 >> x, 0); + assertEq(x >>> 0, 2147483648); + assertEq(0 >>> x, 0); + assertEq(x << 0, -2147483648); + assertEq(0 << x, 0); + assertEq(x | 0xffffffff, -1); + assertEq(0xffffffff | x, -1); + assertEq(x & 0xffffffff, -2147483648); + assertEq(0xffffffff & x, -2147483648); + assertEq(x ^ 0xffffffff, 2147483647); + assertEq(0xffffffff ^ x, 2147483647); + assertEq(x >> 0xffffffff, -1); + assertEq(0xffffffff >> x, -1); + assertEq(x >>> 0xffffffff, 1); + assertEq(0xffffffff >>> x, 4294967295); + assertEq(x << 0xffffffff, 0); + assertEq(0xffffffff << x, -1); + assertEq(x | "10.6", -2147483638); + assertEq("10.6" | x, -2147483638); + assertEq(x & "10.6", 0); + assertEq("10.6" & x, 0); + assertEq(x ^ "10.6", -2147483638); + assertEq("10.6" ^ x, -2147483638); + assertEq(x >> "10.6", -2097152); + assertEq("10.6" >> x, 10); + assertEq(x >>> "10.6", 2097152); + assertEq("10.6" >>> x, 10); + assertEq(x << "10.6", 0); + assertEq("10.6" << x, 10); + assertEq(x | 2147483648, -2147483648); + assertEq(2147483648 | x, -2147483648); + assertEq(x & 2147483648, -2147483648); + assertEq(2147483648 & x, -2147483648); + assertEq(x ^ 2147483648, 0); + assertEq(2147483648 ^ x, 0); + assertEq(x >> 2147483648, -2147483648); + assertEq(2147483648 >> x, -2147483648); + assertEq(x >>> 2147483648, 2147483648); + assertEq(2147483648 >>> x, 2147483648); + assertEq(x << 2147483648, -2147483648); + assertEq(2147483648 << x, -2147483648); + assertEq(x | 4294967296, -2147483648); + assertEq(4294967296 | x, -2147483648); + assertEq(x & 4294967296, 0); + assertEq(4294967296 & x, 0); + assertEq(x ^ 4294967296, -2147483648); + assertEq(4294967296 ^ x, -2147483648); + assertEq(x >> 4294967296, -2147483648); + assertEq(4294967296 >> x, 0); + assertEq(x >>> 4294967296, 2147483648); + assertEq(4294967296 >>> x, 0); + assertEq(x << 4294967296, -2147483648); + assertEq(4294967296 << x, 0); + assertEq(x | undefined, -2147483648); + assertEq(undefined | x, -2147483648); + assertEq(x & undefined, 0); + assertEq(undefined & x, 0); + assertEq(x ^ undefined, -2147483648); + assertEq(undefined ^ x, -2147483648); + assertEq(x >> undefined, -2147483648); + assertEq(undefined >> x, 0); + assertEq(x >>> undefined, 2147483648); + assertEq(undefined >>> x, 0); + assertEq(x << undefined, -2147483648); + assertEq(undefined << x, 0); + assertEq(x | null, -2147483648); + assertEq(null | x, -2147483648); + assertEq(x & null, 0); + assertEq(null & x, 0); + assertEq(x ^ null, -2147483648); + assertEq(null ^ x, -2147483648); + assertEq(x >> null, -2147483648); + assertEq(null >> x, 0); + assertEq(x >>> null, 2147483648); + assertEq(null >>> x, 0); + assertEq(x << null, -2147483648); + assertEq(null << x, 0); + assertEq(x | false, -2147483648); + assertEq(false | x, -2147483648); + assertEq(x & false, 0); + assertEq(false & x, 0); + assertEq(x ^ false, -2147483648); + assertEq(false ^ x, -2147483648); + assertEq(x >> false, -2147483648); + assertEq(false >> x, 0); + assertEq(x >>> false, 2147483648); + assertEq(false >>> x, 0); + assertEq(x << false, -2147483648); + assertEq(false << x, 0); + assertEq(x | true, -2147483647); + assertEq(true | x, -2147483647); + assertEq(x & true, 0); + assertEq(true & x, 0); + assertEq(x ^ true, -2147483647); + assertEq(true ^ x, -2147483647); + assertEq(x >> true, -1073741824); + assertEq(true >> x, 1); + assertEq(x >>> true, 1073741824); + assertEq(true >>> x, 1); + assertEq(x << true, 0); + assertEq(true << x, 1); + assertEq(x | -1.5, -1); + assertEq(-1.5 | x, -1); + assertEq(x & -1.5, -2147483648); + assertEq(-1.5 & x, -2147483648); + assertEq(x ^ -1.5, 2147483647); + assertEq(-1.5 ^ x, 2147483647); + assertEq(x >> -1.5, -1); + assertEq(-1.5 >> x, -1); + assertEq(x >>> -1.5, 1); + assertEq(-1.5 >>> x, 4294967295); + assertEq(x << -1.5, 0); + assertEq(-1.5 << x, -1); +} +test5(-2147483648) + + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBreak.js b/deps/mozjs/js/src/jit-test/tests/basic/testBreak.js deleted file mode 100644 index 3ef76218a40..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBreak.js +++ /dev/null @@ -1,6 +0,0 @@ -for (var i = 10; i < 19; i++) - for (var j = 0; j < 9; j++) - if (j < i) - break; - -checkStats({recorderStarted: 3, recorderAborted: 1, traceCompleted: 2}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug458838.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug458838.js index cd1e05cf77b..59b55a50c99 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug458838.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug458838.js @@ -12,8 +12,3 @@ function testBug458838() { return g(); } assertEq(testBug458838(), 10); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug504520.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug504520.js index 73e2e3013fb..ba5f767640d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug504520.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug504520.js @@ -1,7 +1,7 @@ function testBug504520() { // A bug involving comparisons. var arr = [1/0, 1/0, 1/0, 1/0, 1/0, 1/0, 1/0, 1/0, 1/0, 0]; - assertEq(arr.length > RUNLOOP, true); + assertEq(arr.length > 9, true); var s = ''; for (var i = 0; i < arr.length; i++) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug504520Harder.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug504520Harder.js index 0a63ebf4075..381bd3ae636 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug504520Harder.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug504520Harder.js @@ -12,7 +12,7 @@ function testBug504520Harder() { var yz = eval(y + op + z); var arr = [x, x, x, x, x, x, x, x, x, y]; - assertEq(arr.length > RUNLOOP, true); + assertEq(arr.length > 9, true); var expected = [xz, xz, xz, xz, xz, xz, xz, xz, xz, yz]; // ?: looks superfluous but that's what we're testing here diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug558446.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug558446.js index 91c018727a7..8560de09479 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug558446.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug558446.js @@ -8,4 +8,3 @@ for (var i = 0; i < 10; ++i) { assertEq(s, 'ab'); } -checkStats({ traceTriggered:1 }); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug586866.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug586866.js index c9ccd458ca7..425bf2b7824 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug586866.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug586866.js @@ -1,4 +1,4 @@ -var magic = HOTLOOP; +var magic = 8; var obj = {}; for (var i = 1; i <= magic; ++i) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug597736.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug597736.js deleted file mode 100644 index ded33842776..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug597736.js +++ /dev/null @@ -1,32 +0,0 @@ -function leak_test() { - // Create a reference loop function->script->traceFragment->object->function - // that GC must be able to break. To embedd object into the fragment the - // code use prototype chain of depth 2 which caches obj.__proto__.__proto__ - // into the fragment. - - // To make sure that we have no references to the function f after this - // function returns due via the conservative scan of the native stack we - // loop here multiple times overwriting the stack and registers with new garabge. - for (var j = 0; j != 8; ++j) { - var f = Function("a", "var s = 0; for (var i = 0; i != 100; ++i) s += a.b; return s;"); - var c = {b: 1, f: f, leakDetection: makeFinalizeObserver()}; - f({ __proto__: { __proto__: c}}); - f = c = a = null; - gc(); - } -} - -function test() -{ - if (typeof finalizeCount != "function") - return; - - var base = finalizeCount(); - leak_test(); - gc(); - gc(); - var n = finalizeCount(); - assertEq(base + 4 < finalizeCount(), true, "Some finalizations must happen"); -} - -test(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug614653.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug614653.js index 2b86e97c385..e09759ae585 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug614653.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug614653.js @@ -1,8 +1,8 @@ /* Bug 614653 - This test .2 seconds with the fix, 20 minutes without. */ -for (var i = 0; i < 100; ++i) { +for (var i = 0; i < 10; ++i) { var arr = []; var s = "abcdefghijklmnop"; - for (var i = 0; i < 50000; ++i) { + for (var j = 0; j < 50000; ++j) { s = "<" + s + ">"; arr.push(s); } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug628564.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug628564.js index 2c217d0afc3..7aebb561a2f 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug628564.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug628564.js @@ -1,6 +1,6 @@ x = 0 -for (a = 0; a < HOTLOOP+5; ++a) { - if (a == HOTLOOP-1) { +for (a = 0; a < 13; ++a) { + if (a == 7) { if (!x) { __defineSetter__("x", Object.defineProperties) } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug634590.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug634590.js index 5787c61c9d7..32df4f3bd85 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug634590.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug634590.js @@ -9,4 +9,4 @@ assertEq(evalcx('this.f = parent.f;\n' + ' s += f();\n' + 's', sb), - "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug634590ma.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug634590ma.js index 413f3a2fc78..0bdba71fd51 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testBug634590ma.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug634590ma.js @@ -11,4 +11,4 @@ assertEq(evalcx('this.f = parent.f;\n' + ' s += f();\n' + 's', sb), - "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug648438.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug648438.js new file mode 100644 index 00000000000..9bdfc0fbd83 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug648438.js @@ -0,0 +1,7 @@ +for (var i = 0; i < 20; i++) { + (function () { + try { + JSON.parse(); + } catch (e) {} + }).call(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug653396.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug653396.js new file mode 100644 index 00000000000..6eefdf2289d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug653396.js @@ -0,0 +1,6 @@ +// |jit-test| error: RangeError +function g(a, b, c, d) {} +function f(a, b, c) { + arguments.length = getMaxArgs() + 1; + g.apply(this, arguments); +}f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug660734.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug660734.js new file mode 100644 index 00000000000..304715cc249 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug660734.js @@ -0,0 +1,6 @@ +if (Proxy.fix) { + var handler = { fix: function() { return []; } }; + var p = Proxy.createFunction(handler, function(){}, function(){}); + Proxy.fix(p); + new p(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-1.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-1.js new file mode 100644 index 00000000000..4fdc262b514 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-1.js @@ -0,0 +1,6 @@ +function f() { + a = arguments; + return a[0] - a[1]; +} + +[1,2,3,4].sort(f); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-2.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-2.js new file mode 100644 index 00000000000..d7ee3059986 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug663789-2.js @@ -0,0 +1,6 @@ +// |jit-test| debug;mjit + +o = { toString:function() { return evalInFrame(1, "x") } } +var x = 'C'; +var s = "aaaaaaaaaa".replace(/a/g, function() { var x = 'B'; return o }); +assertEq(s, "CCCCCCCCCC"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug666003.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug666003.js new file mode 100644 index 00000000000..b508e7aa559 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug666003.js @@ -0,0 +1,13 @@ +function f() { + f = function() { g(); }; + f(); +} +g = f; + +var caught = false; +try { + f(); +} catch(e) { + caught = true; +} +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug666292.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug666292.js new file mode 100644 index 00000000000..12238e75ca3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug666292.js @@ -0,0 +1,14 @@ +// |jit-test| debug + +function f(){ + this.zzz.zzz; + for(let d in []); +} +trap(f, 16, '') +try { + f() +} catch(e) { + caught = true; + assertEq(""+e, "TypeError: this.zzz is undefined"); +} +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug667915.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug667915.js new file mode 100644 index 00000000000..76d565b69be --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug667915.js @@ -0,0 +1,8 @@ +for each(let y in [0, 0]) { + eval("\ + for each(e in[0,0,0,0,0,0,0,0]) {\ + x = undefined\ + }\ + ") +} + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug668479.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug668479.js new file mode 100644 index 00000000000..30eaca5c402 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug668479.js @@ -0,0 +1,3 @@ +function a() { + with(a) eval("arguments[0]"); +} a(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug672436.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug672436.js new file mode 100644 index 00000000000..95a6d07a1d6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug672436.js @@ -0,0 +1,25 @@ +try { new Error() } catch (e) {} + +const N = 18; + +var arr = []; +for (var i = 0; i < N; ++i) + arr[i] = 'a'; +arr[N] = '%'; + +function inner(i) { + decodeURI(arr[i]); +} +function outer() { + for (var i = 0; i <= N; ++i) + inner(i); +} + +var caught = false; +try { + outer(); +} catch (e) { + caught = true; +} +assertEq(caught, true); + diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug673066.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug673066.js new file mode 100644 index 00000000000..28aa5180285 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug673066.js @@ -0,0 +1,23 @@ +function f(code) { + a = code.replace(/s/, ""); + wtt = a + code = code.replace(/\/\*DUPTRY\d+\*\//, function(k) { + n = parseInt(k.substr(8), 0); + return g("try{}catch(e){}", n) + }); + f = eval("(function(){" + code + "})") + if (typeof disassemble == 'function') { + disassemble("-r", f) + } +} +function g(s, n) { + if (n == 0) { + return s + } + s2 = s + s + r = n % 2 + d = (n - r) / 2 + m = g(s2, d) + return r ? m + s : m +} +f("switch(''){default:break;/*DUPTRY525*/}") diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug673068.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug673068.js new file mode 100644 index 00000000000..5523b98e760 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug673068.js @@ -0,0 +1,12 @@ +// |jit-test| mjitalways + +function f(x) { + var x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10; + var y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10; + var z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10; + if (x == 0) + return; + f(x - 1); +} + +f(1000); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug676486.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug676486.js new file mode 100644 index 00000000000..b9f7bb20762 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug676486.js @@ -0,0 +1,10 @@ +var proxy = Proxy.createFunction( + {}, + function() { + return (function () { eval("foo") })(); + }); + +try { + new proxy(); +} catch (e) { +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug677367.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug677367.js new file mode 100644 index 00000000000..3dd1d26b52a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug677367.js @@ -0,0 +1,5 @@ +// |jit-test| debug + +function f() {} +trap(f, 0, 'eval("2+2")'); +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug683470.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug683470.js new file mode 100644 index 00000000000..e6e27346bea --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug683470.js @@ -0,0 +1,15 @@ +// |jit-test| debug + +f = (function() { + function b() { + "use strict"; + Object.defineProperty(this, "x", ({})); + } + for each(let d in [0, 0]) { + try { + b(d); + } catch (e) {} + } +}) +trap(f, 40, undefined); +f() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug690959.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug690959.js new file mode 100644 index 00000000000..6f4b800c1ec --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug690959.js @@ -0,0 +1,4 @@ +var s = 'abcdFF0123456789012345fail'; +s = s.replace("abcd", "0123456789012345678901234567890123456789012FF"); +s = s.replace("FF0123456789012345fail", "ok"); +assertEq(s, '0123456789012345678901234567890123456789012FFok'); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-1.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-1.js new file mode 100644 index 00000000000..953a1cbda8a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-1.js @@ -0,0 +1,6 @@ +// |jit-test| debug; error: TypeError +function f() { + ""(this.z) +} +trap(f, 0, '') +f() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-2.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-2.js new file mode 100644 index 00000000000..dff5c905236 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-2.js @@ -0,0 +1,7 @@ +function f() { + var ss = [new f("abc"), new String("foobar"), new String("quux")]; + for (let a6 = this ;; ) {} +} +try { + f(); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-3.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-3.js new file mode 100644 index 00000000000..a8c0afe65f0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-3.js @@ -0,0 +1,16 @@ +var x = -false; +switch(x) { + case 11: + let y = 42; +} +switch(x) { + case 11: + let y = 42; + let z = 'ponies'; +} +switch(x) { + case 11: + let y = 42; + let z = 'ponies'; + let a = false; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-4.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-4.js new file mode 100644 index 00000000000..8c02cdc1138 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug692274-4.js @@ -0,0 +1,4 @@ +// |jit-test| error: TypeError +var obj = {}; +let ([] = print) 3; +let ( i = "a" ) new i [ obj[i] ]; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug701227.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug701227.js new file mode 100644 index 00000000000..cf12409fea4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug701227.js @@ -0,0 +1 @@ +(function f() { try {} catch(e) { let x } }).toString() diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug701239.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug701239.js new file mode 100644 index 00000000000..665134c1d1b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug701239.js @@ -0,0 +1 @@ +(function f(w) { for([w] in 1) {} }).toString(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug701244.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug701244.js new file mode 100644 index 00000000000..4a9931f8045 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug701244.js @@ -0,0 +1 @@ +(function f({length}) {}).toString(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug703857.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug703857.js new file mode 100644 index 00000000000..0f230e461ab --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug703857.js @@ -0,0 +1,12 @@ +Function.prototype.X = 42; +function ownProperties() { + var props = {}; + var r = function () {}; + for (var a in r) { + let (a = function() { for (var r=0;r<6;++r) ++a; }) { + a(); + } + props[a] = true; + } +} +ownProperties(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug704351.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug704351.js new file mode 100644 index 00000000000..16b13146868 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug704351.js @@ -0,0 +1,6 @@ +if (this.dis !== undefined) { + var generatorPrototype = (function() { yield 3; })().__proto__; + try { + this.dis(generatorPrototype); + } catch(e) {} +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug705879.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug705879.js new file mode 100644 index 00000000000..36168564c82 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug705879.js @@ -0,0 +1,10 @@ +f = eval("\ + (function() {\ + with({}) {\ + yield\ + }\ + for(let d in[gc()])\ + for(b in[0,function(){}]);\ + })\ +") +for (e in f()) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug709633.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug709633.js new file mode 100644 index 00000000000..1ce097b85d4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug709633.js @@ -0,0 +1,9 @@ +test(); +function test() { + var f; + f = function() { (let(x) {y: z}) } + let (f = function() { + for (var t=0;t<6;++t) ++f; + }) { f(); } // { } + actual = f + ''; +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug709929.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug709929.js new file mode 100644 index 00000000000..9f7e4ce10d9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug709929.js @@ -0,0 +1,11 @@ +f = eval("\ + (function() {\ + for each(l in[0,0]) {\ + with(gc()>[this for(x in[function(){}])]) {\ + var a;\ + yield\ + }\ + }\ + })\ +") +for (i in f()) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug714650.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug714650.js new file mode 100644 index 00000000000..1ceec2a35ea --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug714650.js @@ -0,0 +1,36 @@ +let ([] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, + [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1) +{ + print("ok"); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testBug720695.js b/deps/mozjs/js/src/jit-test/tests/basic/testBug720695.js new file mode 100644 index 00000000000..4e3dc764ff5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testBug720695.js @@ -0,0 +1,16 @@ +var v = [ -0x80000003, -0x80000002, -0x80000001, -0x80000000, -0x7fffffff, -0x7ffffffe, -0x7ffffffd, + -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 20, 21, 100, 101, 110, 111, 500, + 0x7ffffffd, 0x7ffffffe, 0x7fffffff, 0x80000000, 0x80000001, 0x80000002, 0x80000003]; + +function strcmp(x, y) { + return Number(String(x) > String(y)) +} + +for (var i = 0; i < v.length; ++i) { + for (var j = 0; j < v.length; ++j) { + var builtin = String([v[i], v[j]].sort()); + var manual = String([v[i], v[j]].sort(strcmp)); + assertEq(builtin, manual); + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCallApply.js b/deps/mozjs/js/src/jit-test/tests/basic/testCallApply.js index 46eee91c13e..9141f80bcb3 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testCallApply.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCallApply.js @@ -19,13 +19,13 @@ function test0() { assertEq(script1.call(null, 1), 1); assertEq(script1.call(null, 1,2), 2); assertEq(native1.call("aabc", /b/), 2); - assertEq(native1.call("abc"), -1); + assertEq(native1.call("abc"), 0); assertEq(tricky1.call(null, 9), 9); assertEq(script1.apply(null), 0); assertEq(script1.apply(null, [1]), 1); assertEq(script1.apply(null, [1,2]), 2); assertEq(native1.apply("aabc", [/b/]), 2); - assertEq(native1.apply("abc"), -1); + assertEq(native1.apply("abc"), 0); assertEq(tricky1.apply(null, 1), 1); } test0(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCallFunctionPrototypeInALoop.js b/deps/mozjs/js/src/jit-test/tests/basic/testCallFunctionPrototypeInALoop.js new file mode 100644 index 00000000000..5930cdc90b5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCallFunctionPrototypeInALoop.js @@ -0,0 +1,4 @@ +var x; +for (var a = 0; a < 8; a++) { + Function.prototype() +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCallNull.js b/deps/mozjs/js/src/jit-test/tests/basic/testCallNull.js deleted file mode 100644 index 53f11cd9984..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testCallNull.js +++ /dev/null @@ -1,9 +0,0 @@ -function f() {} -for (var i = 0; i < HOTLOOP; ++i) { - f.call(null); -} - -checkStats({ - recorderStarted: 1, - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCaseAbort.js b/deps/mozjs/js/src/jit-test/tests/basic/testCaseAbort.js index e4b8049d2a1..4e594bb86e3 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testCaseAbort.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCaseAbort.js @@ -14,6 +14,3 @@ function testCaseAbort() return "" + r; } assertEq(testCaseAbort(), "10"); -checkStats({ - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCaseTypeMismatchBadness.js b/deps/mozjs/js/src/jit-test/tests/basic/testCaseTypeMismatchBadness.js index 351225570ec..d6132ef97ec 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testCaseTypeMismatchBadness.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCaseTypeMismatchBadness.js @@ -17,6 +17,3 @@ function testCaseTypeMismatchBadness() return "no crash"; } assertEq(testCaseTypeMismatchBadness(), "no crash"); -checkStats({ - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testChangingObjectWithLength.js b/deps/mozjs/js/src/jit-test/tests/basic/testChangingObjectWithLength.js index 5521ce65977..9269d0de57d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testChangingObjectWithLength.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testChangingObjectWithLength.js @@ -31,7 +31,3 @@ function testChangingObjectWithLength() return counter; } assertEq(testChangingObjectWithLength(), 400); -checkStats({ - recorderAborted: 0, - sideExitIntoInterpreter: 15 // empirically determined -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCompileScript.js b/deps/mozjs/js/src/jit-test/tests/basic/testCompileScript.js new file mode 100644 index 00000000000..8cdfc953cb9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCompileScript.js @@ -0,0 +1,40 @@ +// |jit-test| slow; mjitalways + +var nlocals = 50; +var localstr = ""; +for (var i = 0; i < nlocals; ++i) + localstr += "var x" + i + "; "; + +/* + * Attempt to test, in a stack-parameter-independent manner, ComileFunction + * hitting a stack-commit boundary (which is not an OOM, but requires checking + * and updating the stack limit). + */ +var arr = [function() {return 0}, function() {return 1}, function() {return 2}]; +var arg = "x"; +var body = localstr + + "if (x == 0) return; " + + "arr[3] = (new Function(arg, body));" + + "for (var i = 0; i < 4; ++i) arr[i](x-1);"; + +// XXX interpreter bailouts during recursion below can cause us to hit the limit quickly. +try { (new Function(arg, body))(1000); } catch (e) {} + +/* + * Also check for OOM in CompileFunction. To avoid taking 5 seconds, use a + * monster apply to chew up most the stack. + */ +var gotIn = false; +var threwOut = false; +try { + (function() { + gotIn = true; + (new Function(arg, body))(10000000); + }).apply(null, new Array(getMaxArgs())); +} catch(e) { + assertEq(""+e, "InternalError: too much recursion"); + threwOut = true; +} +assertEq(threwOut, true); +/* If tweaking some stack parameter makes this fail, shrink monster apply. */ +assertEq(gotIn, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testConcatNWithSideEffects.js b/deps/mozjs/js/src/jit-test/tests/basic/testConcatNWithSideEffects.js index 6272d54eaca..b7b636e5d83 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testConcatNWithSideEffects.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testConcatNWithSideEffects.js @@ -10,4 +10,3 @@ function g() { for (var i = 0; i < 1000; ++i) g(); -checkStats({recorderStarted:1, recorderAborted:0, traceCompleted:1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-1.js b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-1.js index 81630f064de..46b6c4e08a8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-1.js @@ -3,7 +3,7 @@ function f(a, b) { assertEq(b, 'x'); } -for (var x = 0; x < RUNLOOP; ++x) { +for (var x = 0; x < 9; ++x) { f.prototype = {}; var obj = new f(x, 'x'); assertEq(obj.a, x); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-2.js b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-2.js index 5a85470776b..0a8292dd8ca 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-2.js @@ -4,7 +4,7 @@ function f(a, b, c) { assertEq(c, void 0); } -for (var x = 0; x < RUNLOOP; ++x) { +for (var x = 0; x < 9; ++x) { f.prototype = {}; var obj = new f(x, 'x'); // fewer than f.length arguments assertEq(obj.a, x); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-3.js b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-3.js index 4b1eb55c086..eb594b3dd86 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-3.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testConstructorArgs-3.js @@ -3,7 +3,7 @@ function f(a) { assertEq(arguments[1], 'x'); } -for (var x = 0; x < RUNLOOP; ++x) { +for (var x = 0; x < 9; ++x) { f.prototype = {}; var obj = new f(x, 'x'); // more than f.length arguments assertEq(obj.a, x); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testContinueWithLabel2.js b/deps/mozjs/js/src/jit-test/tests/basic/testContinueWithLabel2.js deleted file mode 100644 index fdc8db0b575..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testContinueWithLabel2.js +++ /dev/null @@ -1,7 +0,0 @@ -outer: -for (var i = 10; i < 19; i++) - for (var j = 0; j < 9; j++) - if (j < i) - continue outer; - -checkStats({recorderStarted: 3, recorderAborted: 1, traceCompleted: 2}); \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testConvertibleObjectEqUndefined.js b/deps/mozjs/js/src/jit-test/tests/basic/testConvertibleObjectEqUndefined.js index 788c789372d..89431affe8e 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testConvertibleObjectEqUndefined.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testConvertibleObjectEqUndefined.js @@ -28,6 +28,3 @@ assertEq(testConvertibleObjectEqUndefined(), x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + "20"); -checkStats({ - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency.js b/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency.js new file mode 100644 index 00000000000..d9d15d24a21 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency.js @@ -0,0 +1,172 @@ +// |jit-test| error: done + +var g1 = newGlobal('same-compartment'); +var g2 = newGlobal('new-compartment'); +var proxyStr = "Proxy.create( "+ +" { getOwnPropertyDescriptor: function() assertEq(true,false), "+ +" getPropertyDescriptor: function() assertEq(true,false), "+ +" getOwnPropertyNames: function() assertEq(true,false), "+ +" getPropertyNames: function() assertEq(true,false), "+ +" defineProperty: function() assertEq(true,false), "+ +" delete: function() assertEq(true,false), "+ +" fix: function() assertEq(true,false), }, "+ +" Object.prototype "+ +"); "; +var proxy1 = g1.eval(proxyStr); +var proxy2 = g2.eval(proxyStr); + +function test(str, f) { + "use strict"; + + var x = f(eval(str)); + assertEq(x, f(g1.eval(str))); + assertEq(x, f(g2.eval(str))); + + var threw = false; + try { + f(g1.eval("new Object()")); + } catch (e) { + assertEq(Object.prototype.toString.call(e), "[object Error]"); + threw = true; + } + assertEq(threw, true); + threw = false; + try { + f(g2.eval("new Object()")); + } catch (e) { + assertEq(Object.prototype.toString.call(e), "[object Error]"); + threw = true; + } + assertEq(threw, true); + threw = false; + try { + f(proxy1); + } catch (e) { + assertEq(Object.prototype.toString.call(e), "[object Error]"); + threw = true; + } + assertEq(threw, true); + threw = false; + try { + f(proxy2); + } catch (e) { + assertEq(Object.prototype.toString.call(e), "[object Error]"); + threw = true; + } + assertEq(threw, true); +} + +test("new Array(1,2,3)", function(a) Array.prototype.toSource.call(a)); +test("new Boolean(true)", function(b) Boolean.prototype.toSource.call(b)); +test("new Boolean(true)", function(b) Boolean.prototype.toString.call(b)); +test("new Boolean(true)", function(b) Boolean.prototype.valueOf.call(b)); +test("new Number(1)", function(n) Number.prototype.toSource.call(n)); +test("new Number(1)", function(n) Number.prototype.toString.call(n)); +test("new Number(1)", function(n) Number.prototype.valueOf.call(n)); +test("new Number(1)", function(n) Number.prototype.toFixed.call(n)); +test("new Number(1)", function(n) Number.prototype.toExponential.call(n)); +test("new Number(1)", function(n) Number.prototype.toPrecision.call(n)); +test("new Iterator({x:1})", function(i) Iterator.prototype.next.call(i).toString()); +test("(function(){yield 1})()", function(i) (function(){yield})().next.call(i).toString()); +test("new String('one')", function(s) String.prototype.toSource.call(s)); +test("new String('one')", function(s) String.prototype.toString.call(s)); +test("new RegExp('1')", function(r) RegExp.prototype.toString.call(r)); +test("new RegExp('1')", function(r) RegExp.prototype.exec.call(r, '1').toString()); +test("new RegExp('1')", function(r) RegExp.prototype.test.call(r, '1')); +test("new RegExp('1')", function(r) RegExp.prototype.compile.call(r, '1').toString()); +test("new RegExp('1')", function(r) assertEq("a1".search(r), 1)); +test("new RegExp('1')", function(r) assertEq("a1".match(r)[0], '1')); +test("new RegExp('1')", function(r) assertEq("a1".replace(r, 'A'), 'aA')); +test("new RegExp('1')", function(r) assertEq(String("a1b".split(r)), "a,b")); +test("new RegExp('1')", function(r) assertEq(r, RegExp(r))); +test("new RegExp('1')", function(r) assertEq(String(new RegExp(r)), String(r))); +test("new RegExp('1')", function(r) assertEq(String(/x/.compile(r)), String(r))); +test("new WeakMap()", function(w) WeakMap.prototype.has.call(w, {})); +test("new WeakMap()", function(w) WeakMap.prototype.get.call(w, {})); +test("new WeakMap()", function(w) WeakMap.prototype.delete.call(w, {})); +test("new WeakMap()", function(w) WeakMap.prototype.set.call(w, {})); +test("new Map()", function(w) Map.prototype.has.call(w, {})); +test("new Map()", function(w) Map.prototype.get.call(w, {})); +test("new Map()", function(w) Map.prototype.delete.call(w, {})); +test("new Map()", function(w) Map.prototype.set.call(w, {})); +test("new Set()", function(w) Set.prototype.has.call(w, {})); +test("new Set()", function(w) Set.prototype.add.call(w, {})); +test("new Set()", function(w) Set.prototype.delete.call(w, {})); + +test("new Int8Array(1)", function(a) Int8Array.prototype.subarray.call(a).toString()); +test("new Uint8Array(1)", function(a) Uint8Array.prototype.subarray.call(a).toString()); +test("new Int16Array(1)", function(a) Int16Array.prototype.subarray.call(a).toString()); +test("new Uint16Array(1)", function(a) Uint16Array.prototype.subarray.call(a).toString()); +test("new Int32Array(1)", function(a) Int32Array.prototype.subarray.call(a).toString()); +test("new Uint32Array(1)", function(a) Uint32Array.prototype.subarray.call(a).toString()); +test("new Float32Array(1)", function(a) Float32Array.prototype.subarray.call(a).toString()); +test("new Float64Array(1)", function(a) Float64Array.prototype.subarray.call(a).toString()); +test("new Uint8ClampedArray(1)", function(a) Uint8ClampedArray.prototype.subarray.call(a).toString()); + +test("new Int8Array(1)", function(a) Int8Array.subarray(a).toString()); +test("new Uint8Array(1)", function(a) Uint8Array.subarray(a).toString()); +test("new Int16Array(1)", function(a) Int16Array.subarray(a).toString()); +test("new Uint16Array(1)", function(a) Uint16Array.subarray(a).toString()); +test("new Int32Array(1)", function(a) Int32Array.subarray(a).toString()); +test("new Uint32Array(1)", function(a) Uint32Array.subarray(a).toString()); +test("new Float32Array(1)", function(a) Float32Array.subarray(a).toString()); +test("new Float64Array(1)", function(a) Float64Array.subarray(a).toString()); +test("new Uint8ClampedArray(1)", function(a) Uint8ClampedArray.subarray(a).toString()); + +test("new Int8Array(1)", function(a) Int8Array.prototype.set.call(a, [])); +test("new Uint8Array(1)", function(a) Uint8Array.prototype.set.call(a, [])); +test("new Int16Array(1)", function(a) Int16Array.prototype.set.call(a, [])); +test("new Uint16Array(1)", function(a) Uint16Array.prototype.set.call(a, [])); +test("new Int32Array(1)", function(a) Int32Array.prototype.set.call(a, [])); +test("new Uint32Array(1)", function(a) Uint32Array.prototype.set.call(a, [])); +test("new Float32Array(1)", function(a) Float32Array.prototype.set.call(a, [])); +test("new Float64Array(1)", function(a) Float64Array.prototype.set.call(a, [])); +test("new Uint8ClampedArray(1)", function(a) Uint8ClampedArray.prototype.set.call(a, [])); + +function justDontThrow() {} +test("new Date()", function(d) justDontThrow(Date.prototype.getTime.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getFullYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCFullYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getMonth.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCMonth.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getDate.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCDate.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getDay.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCDay.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getHours.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCHours.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getMinutes.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCMinutes.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getSeconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getUTCSeconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.getTimezoneOffset.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setTime.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setMilliseconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCMilliseconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setSeconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCSeconds.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setMinutes.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCMinutes.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setHours.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCHours.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setDate.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCDate.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setMonth.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCMonth.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setFullYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setUTCFullYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.setYear.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toGMTString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toISOString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toLocaleString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toLocaleDateString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toLocaleTimeString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toLocaleFormat.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toTimeString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toDateString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toSource.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.toString.call(d))); +test("new Date()", function(d) justDontThrow(Date.prototype.valueOf.call(d))); + +throw "done"; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency2.js b/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency2.js new file mode 100644 index 00000000000..74ae158f6d8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testCrossCompartmentTransparency2.js @@ -0,0 +1,25 @@ +var g = newGlobal('new-compartment'); + +var array = g.eval("new Array(1,2,3)"); +assertEq([array,array].concat().toString(), "1,2,3,1,2,3"); +assertEq(Array.isArray(array), true); + +var number = g.eval("new Number(42)"); +var bool = g.eval("new Boolean(false)"); +var string = g.eval("new String('ponies')"); +assertEq(JSON.stringify({n:number, b:bool, s:string}), "{\"n\":42,\"b\":false,\"s\":\"ponies\"}"); +assertEq(JSON.stringify({arr:array}), "{\"arr\":[1,2,3]}"); +assertEq(JSON.stringify({2:'ponies', unicorns:'not real'}, array), "{\"2\":\"ponies\"}"); +assertEq(JSON.stringify({42:true, ponies:true, unicorns:'sad'}, [number, string]), "{\"42\":true,\"ponies\":true}"); +assertEq(JSON.stringify({a:true,b:false}, undefined, number), "{\n \"a\": true,\n \"b\": false\n}"); +assertEq(JSON.stringify({a:true,b:false}, undefined, string), "{\nponies\"a\": true,\nponies\"b\": false\n}"); + +var o = Proxy.create({getPropertyDescriptor:function(name) {}}, Object.prototype); +var threw = false; +try { + print([].concat(o).toString()); +} catch(e) { + assertEq(e instanceof TypeError, true); + threw = true; +} +assertEq(threw, true); diff --git a/deps/mozjs/js/src/jit-test/tests/testCrossGlobalInvokeSession.js b/deps/mozjs/js/src/jit-test/tests/basic/testCrossGlobalInvokeSession.js similarity index 100% rename from deps/mozjs/js/src/jit-test/tests/testCrossGlobalInvokeSession.js rename to deps/mozjs/js/src/jit-test/tests/basic/testCrossGlobalInvokeSession.js diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDateNow.js b/deps/mozjs/js/src/jit-test/tests/basic/testDateNow.js index 4fdb6b51c44..0a186caa281 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDateNow.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDateNow.js @@ -3,13 +3,8 @@ function testDateNow() { // so do it before the loop starts; otherwise we have to loop an extra time // to pick things up. var time = Date.now(); - for (var j = 0; j < RUNLOOP; ++j) + for (var j = 0; j < 9; ++j) time = Date.now(); return "ok"; } assertEq(testDateNow(), "ok"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDecElem1.js b/deps/mozjs/js/src/jit-test/tests/basic/testDecElem1.js index e9540308688..6739cab40e5 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDecElem1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDecElem1.js @@ -5,6 +5,3 @@ for (var i = 0; i < 10; i++) a[i] = --obj[name]; assertEq(a.join(','), '99,98,97,96,95,94,93,92,91,90'); assertEq(obj.p, 90); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); - diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDecElem2.js b/deps/mozjs/js/src/jit-test/tests/basic/testDecElem2.js index 5327f40ab93..8fe74ff50f3 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDecElem2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDecElem2.js @@ -1,11 +1,11 @@ var obj = {s: ""}; var name = "s"; var a = []; -for (var i = 0; i <= RECORDLOOP + 5; i++) { +for (var i = 0; i <= 13; i++) { a[i] = 'x'; - if (i > RECORDLOOP) + if (i > 8) a[i] = --obj[name]; // first recording changes obj.s from string to number } -assertEq(a.join(','), Array(RECORDLOOP + 2).join('x,') + '-1,-2,-3,-4,-5'); +assertEq(a.join(','), Array(10).join('x,') + '-1,-2,-3,-4,-5'); assertEq(obj.s, -5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDeepBail1.js b/deps/mozjs/js/src/jit-test/tests/basic/testDeepBail1.js index f3590771089..99b1ce80c69 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDeepBail1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDeepBail1.js @@ -1,6 +1,6 @@ function testDeepBail1() { var y = ; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) "" in y; } testDeepBail1(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDeepBailWhileRecording.js b/deps/mozjs/js/src/jit-test/tests/basic/testDeepBailWhileRecording.js index 7ee42184071..17c1086aa15 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDeepBailWhileRecording.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDeepBailWhileRecording.js @@ -13,8 +13,3 @@ for (var i = 0; i < 10; ++i) { assertEq(out.length, 10 * arr.length); for (var i = 0; i < out.length; ++i) assertEq(out[i], 1); - -checkStats({ - traceCompleted:2, - recorderAborted:1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDestructuring.js b/deps/mozjs/js/src/jit-test/tests/basic/testDestructuring.js index 3e6cfdb26bf..05a267802b1 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDestructuring.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDestructuring.js @@ -1,9 +1,9 @@ function testDestructuring() { var t = 0; - for (var i = 0; i < HOTLOOP + 1; ++i) { + for (var i = 0; i < 9; ++i) { var [r, g, b] = [1, 1, 1]; t += r + g + b; } return t } -assertEq(testDestructuring(), (HOTLOOP + 1) * 3); +assertEq(testDestructuring(), (9) * 3); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testDoubleComparison.js b/deps/mozjs/js/src/jit-test/tests/basic/testDoubleComparison.js index e102e421bac..1d4b3e33a58 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testDoubleComparison.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testDoubleComparison.js @@ -11,6 +11,3 @@ function testDoubleComparison() return "finished"; } assertEq(testDoubleComparison(), "finished"); -checkStats({ - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testElemDec1.js b/deps/mozjs/js/src/jit-test/tests/basic/testElemDec1.js index 5c5f7a705bf..c03ea2ae059 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testElemDec1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testElemDec1.js @@ -5,6 +5,3 @@ for (var i = 0; i < 10; i++) a[i] = obj[name]--; assertEq(a.join(','), '100,99,98,97,96,95,94,93,92,91'); assertEq(obj.p, 90); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); - diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testElemDec2.js b/deps/mozjs/js/src/jit-test/tests/basic/testElemDec2.js index 1388d39e546..7dc674645fd 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testElemDec2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testElemDec2.js @@ -1,7 +1,7 @@ var obj = {s: ""}; var name = "s"; -for (var i = 0; i <= RECORDLOOP + 5; i++) - if (i > RECORDLOOP) +for (var i = 0; i <= 13; i++) + if (i > 8) obj[name]--; // first recording changes obj.s from string to number assertEq(obj.s, -5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testElemInc1.js b/deps/mozjs/js/src/jit-test/tests/basic/testElemInc1.js index 84a0c74b2e5..c0d4f8c1133 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testElemInc1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testElemInc1.js @@ -5,6 +5,3 @@ for (var i = 0; i < 10; i++) a[i] = obj[name]++; assertEq(a.join(','), '100,101,102,103,104,105,106,107,108,109'); assertEq(obj.p, 110); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); - diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testElemInc2.js b/deps/mozjs/js/src/jit-test/tests/basic/testElemInc2.js index 9a1712d6376..56a926b2e20 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testElemInc2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testElemInc2.js @@ -1,7 +1,7 @@ var obj = {s: ""}; var name = "s"; -for (var i = 0; i <= RECORDLOOP + 5; i++) - if (i > RECORDLOOP) +for (var i = 0; i <= 13; i++) + if (i > 8) obj[name]++; // first recording changes obj.s from string to number assertEq(obj.s, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testEliminatedGuardWithinAnchor.js b/deps/mozjs/js/src/jit-test/tests/basic/testEliminatedGuardWithinAnchor.js index cc80e779c37..25d0be96ca6 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testEliminatedGuardWithinAnchor.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testEliminatedGuardWithinAnchor.js @@ -4,9 +4,3 @@ function testEliminatedGuardWithinAnchor() { } assertEq(testEliminatedGuardWithinAnchor(), "ok"); - -if (HAVE_TM) { - checkStats({ - sideExitIntoInterpreter: (jitstats.archIsARM ? 1 : 3) - }); -} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testErrorInFinalizerCalledWhileUnwinding.js b/deps/mozjs/js/src/jit-test/tests/basic/testErrorInFinalizerCalledWhileUnwinding.js new file mode 100644 index 00000000000..41b1e3d0ef4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testErrorInFinalizerCalledWhileUnwinding.js @@ -0,0 +1,32 @@ +var finalizerRun = false; +var caught = false; + +function foo(arr) { + finalizerRun = true; + return not_defined; +} + +function gen() { + try { + yield 1; + } finally { + foo(); + } +} + +function test() { + var i_have_locals; + for (i in gen()) { + "this won't work"(); + } +} + +try { + test(); +} catch(e) { + caught = true; + assertEq(''+e, "ReferenceError: not_defined is not defined"); +} + +assertEq(finalizerRun, true); +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testEvalFromTrap.js b/deps/mozjs/js/src/jit-test/tests/basic/testEvalFromTrap.js new file mode 100644 index 00000000000..ddfdce62058 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testEvalFromTrap.js @@ -0,0 +1,4 @@ +// |jit-test| debug +function f() {} +trap(f, 0, 'eval("2+2")'); +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-2.js b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-2.js index b6bc0677bd5..002da3f61b2 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-2.js @@ -1,5 +1,5 @@ function f() { - var a = [], i, N = HOTLOOP + 2; + var a = [], i, N = 10; for (i = 0; i < N; i++) a[i] = {m: i, m: function() { return 0; }}; assertEq(a[N - 2].m === a[N - 1].m, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-3.js b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-3.js index 8566d1ca48c..573c33ef110 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-3.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-3.js @@ -1,5 +1,5 @@ function f() { - var a = [], i, N = HOTLOOP + 2; + var a = [], i, N = 10; for (i = 0; i < N; i++) { a[i] = {}; a[i].m = function() { return 0; }; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-4.js b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-4.js index 1d874871e6d..8d0bc1b157e 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-4.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-4.js @@ -1,5 +1,5 @@ function f() { - var a = [], i, N = HOTLOOP + 2; + var a = [], i, N = 10; for (i = 0; i < N; i++) a[i] = {m: function() { return 0; }, m: function() { return 1; }}; assertEq(a[N - 2].m === a[N - 1].m, false); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js index fd5939873c2..0faafe2f4b9 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js @@ -1,5 +1,5 @@ function f() { - var a = [], i, N = HOTLOOP + 2; + var a = [], i, N = 10; for (i = 0; i < N; i++) a[i] = {m: i}; for (i = 0; i < N; i++) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testForEach.js b/deps/mozjs/js/src/jit-test/tests/basic/testForEach.js index 76463550490..09f6c8d9cde 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testForEach.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testForEach.js @@ -1,7 +1,7 @@ function testForEach() { var r; var a = ["zero", "one", "two", "three"]; - for (var i = 0; i < RUNLOOP; i++) { + for (var i = 0; i < 9; i++) { r = ""; for each (var s in a) r += s + " "; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testFunctionIdentityChange.js b/deps/mozjs/js/src/jit-test/tests/basic/testFunctionIdentityChange.js index bb0830c7aab..9f4b7dddf9d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testFunctionIdentityChange.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testFunctionIdentityChange.js @@ -14,8 +14,3 @@ function testFunctionIdentityChange() return true; } assertEq(testFunctionIdentityChange(), true); -checkStats({ - recorderStarted: 2, - traceCompleted: 2, - sideExitIntoInterpreter: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testGCWhileRecording.js b/deps/mozjs/js/src/jit-test/tests/basic/testGCWhileRecording.js index 2dffbb2400c..cb1f0e80634 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testGCWhileRecording.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testGCWhileRecording.js @@ -6,7 +6,3 @@ function test() { return "pass"; } assertEq(test(), "pass"); -checkStats({ - recorderStarted: 1, - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testGroupAssignment.js b/deps/mozjs/js/src/jit-test/tests/basic/testGroupAssignment.js index 11ef77ae072..015efbe54b4 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testGroupAssignment.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testGroupAssignment.js @@ -13,10 +13,3 @@ assertEq( return arr; })().toSource(), "[7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 32]"); - -checkStats({ - recorderStarted: 2, - traceCompleted: 2, - sideExitIntoInterpreter: 3, - traceTriggered: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack.js b/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack.js index 37af1a274ef..87939ef992a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack.js @@ -29,10 +29,3 @@ var obj = { loop(C.call(obj, false), false); C.call(obj, true); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 2, - traceTriggered: 4 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack2.js b/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack2.js index 6281fc6677e..9bf3c0c383e 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testGuardCalleeSneakAttack2.js @@ -30,10 +30,3 @@ Function.prototype.toString = function () { loop(this, true); return "hah"; }; // Fail hard if we don't handle the implicit call out of C to F.p.toString. C.call(obj, true); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 2, - traceTriggered: 4 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPCorrectness.js b/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPCorrectness.js deleted file mode 100644 index b295c6975ff..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPCorrectness.js +++ /dev/null @@ -1,14 +0,0 @@ -function testHOTLOOPCorrectness() { - var b = 0; - for (var i = 0; i < HOTLOOP; ++i) - ++b; - return b; -} -// Change the global shape right before doing the test -this.testHOTLOOPCorrectnessVar = 1; -assertEq(testHOTLOOPCorrectness(), HOTLOOP); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPSize.js b/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPSize.js index d4985924386..9245a7f2f5b 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPSize.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testHOTLOOPSize.js @@ -1,4 +1,4 @@ -function testHOTLOOPSize() { - return HOTLOOP > 1; +function test8Size() { + return 8 > 1; } -assertEq(testHOTLOOPSize(), true); +assertEq(test8Size(), true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testHoleInDenseArray.js b/deps/mozjs/js/src/jit-test/tests/basic/testHoleInDenseArray.js index ba7a2c01e16..29523262c25 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testHoleInDenseArray.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testHoleInDenseArray.js @@ -14,5 +14,3 @@ var arr = [ false, false, false, false, false, , , , , , true ]; for (var i = 0; i < 10; ++i) { (s = arr[i]) + f(i); } - -checkStats({ traceTriggered: 2, sideExitIntoInterpreter: 2 }) diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem1.js b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem1.js index 2f8ddf69e17..633c327cf01 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem1.js @@ -5,6 +5,3 @@ for (var i = 0; i < 10; i++) a[i] = ++obj[name]; assertEq(a.join(','), '101,102,103,104,105,106,107,108,109,110'); assertEq(obj.p, 110); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); - diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem2.js b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem2.js index 93ae1891e07..cc2d7d08e39 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem2.js @@ -1,11 +1,11 @@ var obj = {s: ""}; var name = "s"; var a = []; -for (var i = 0; i <= RECORDLOOP + 5; i++) { +for (var i = 0; i <= 13; i++) { a[i] = 'x'; - if (i > RECORDLOOP) + if (i > 8) a[i] = ++obj[name]; // first recording changes obj.s from string to number } -assertEq(a.join(','), Array(RECORDLOOP + 2).join('x,') + '1,2,3,4,5'); +assertEq(a.join(','), Array(10).join('x,') + '1,2,3,4,5'); assertEq(obj.s, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem3.js b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem3.js index 308fd689fb5..922aeb9b8fd 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem3.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem3.js @@ -1,5 +1,5 @@ var arr; -for (var j = 0; j < 2 * RUNLOOP; ++j ) { +for (var j = 0; j < 18; ++j ) { arr = [,]; ++arr[0]; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testIncElem4.js b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem4.js new file mode 100644 index 00000000000..a3cec166114 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testIncElem4.js @@ -0,0 +1,10 @@ + +var counter = 0; +var x = { toString: function() { counter++; } }; +var y = {}; + +for (var i = 0; i < 50; i++) + ++y[x]; + +// :FIXME: bug 672076 +//assertEq(counter, 50); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testInitDictionary.js b/deps/mozjs/js/src/jit-test/tests/basic/testInitDictionary.js index d83ea8defb8..2baded26a36 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testInitDictionary.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testInitDictionary.js @@ -47,5 +47,5 @@ function test3() { pa:0,pb:1,pc:2,pd:3,pe:4,pf:5,pg:6,ph:7,pi:8,pj:9, }); } -for (var i = 0; i < HOTLOOP + 2; i++) +for (var i = 0; i < 10; i++) assertEq(test3(), "aa:0,ab:1,ac:2,ad:3,ae:4,af:5,ag:6,ah:7,ai:8,aj:9,ba:0,bb:1,bc:2,bd:3,be:4,bf:5,bg:6,bh:7,bi:8,bj:9,ca:0,cb:1,cc:2,cd:3,ce:4,cf:5,cg:6,ch:7,ci:8,cj:9,da:0,db:1,dc:2,dd:3,de:4,df:5,dg:6,dh:7,di:8,dj:9,ea:0,eb:1,ec:2,ed:3,ee:4,ef:5,eg:6,eh:7,ei:8,ej:9,fa:0,fb:1,fc:2,fd:3,fe:4,ff:5,fg:6,fh:7,fi:8,fj:9,ga:0,gb:1,gc:2,gd:3,ge:4,gf:5,gg:6,gh:7,gi:8,gj:9,ha:0,hb:1,hc:2,hd:3,he:4,hf:5,hg:6,hh:7,hi:8,hj:9,ia:0,ib:1,ic:2,id:3,ie:4,if:5,ig:6,ih:7,ii:8,ij:9,ja:0,jb:1,jc:2,jd:3,je:4,jf:5,jg:6,jh:7,ji:8,jj:9,ka:0,kb:1,kc:2,kd:3,ke:4,kf:5,kg:6,kh:7,ki:8,kj:9,la:0,lb:1,lc:2,ld:3,le:4,lf:5,lg:6,lh:7,li:8,lj:9,ma:0,mb:1,mc:2,md:3,me:4,mf:5,mg:6,mh:7,mi:8,mj:9,na:0,nb:1,nc:2,nd:3,ne:4,nf:5,ng:6,nh:7,ni:8,nj:9,oa:0,ob:1,oc:2,od:3,oe:4,of:5,og:6,oh:7,oi:8,oj:9,pa:0,pb:1,pc:2,pd:3,pe:4,pf:5,pg:6,ph:7,pi:8,pj:9,"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testInitMethod.js b/deps/mozjs/js/src/jit-test/tests/basic/testInitMethod.js deleted file mode 100644 index abd3f3ec97f..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testInitMethod.js +++ /dev/null @@ -1,9 +0,0 @@ -for (var i = 0; i < 9; i++) - x = {a: function() {}, b: function() {}}; - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testInitPropOverMethod.js b/deps/mozjs/js/src/jit-test/tests/basic/testInitPropOverMethod.js index 8f808c4392f..5f70d3cd6fc 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testInitPropOverMethod.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testInitPropOverMethod.js @@ -1,4 +1,4 @@ -for (var i = 0; i < HOTLOOP + 2; i++) +for (var i = 0; i < 10; i++) x = {a: function () { return 33; }, a: 4}; try { diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testInitPropWithIntName.js b/deps/mozjs/js/src/jit-test/tests/basic/testInitPropWithIntName.js index 7bfa4ce1e9c..755cd726b53 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testInitPropWithIntName.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testInitPropWithIntName.js @@ -1,2 +1,2 @@ -for (var j=0; j -2147483648); } assertEq(testIntUnderflow(), false); -checkStats({ - recorderStarted: 2, - recorderAborted: 0, - traceCompleted: 2, - traceTriggered: 2, -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testLambdaCtor.js b/deps/mozjs/js/src/jit-test/tests/basic/testLambdaCtor.js index 2284de9e600..ea91cd9a481 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testLambdaCtor.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testLambdaCtor.js @@ -1,14 +1,13 @@ function testLambdaCtor() { var a = []; - for (var x = 0; x < RUNLOOP; ++x) { + for (var x = 0; x < 9; ++x) { var f = function(){}; a[a.length] = new f; } - // This prints false until the upvar2 bug is fixed: - // print(a[HOTLOOP].__proto__ !== a[HOTLOOP-1].__proto__); + assertEq([8].__proto__ !== a[7].__proto__, true); // Assert that the last f was properly constructed. - return a[RUNLOOP-1].__proto__ === f.prototype; + return a[8].__proto__ === f.prototype; } assertEq(testLambdaCtor(), true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testLet.js b/deps/mozjs/js/src/jit-test/tests/basic/testLet.js new file mode 100644 index 00000000000..d617259f323 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testLet.js @@ -0,0 +1,350 @@ +var otherGlobal = newGlobal('new-compartment'); + +function test(str, arg, result) +{ + arg = arg || 'ponies'; + result = result || 'ponies'; + + var fun = new Function('x', str); + + var got = fun.toSource().replace(/\n/g,''); + var expect = '(function anonymous(x) {' + str + '})'; + if (got !== expect) { + print("GOT: " + got); + print("EXPECT: " + expect); + assertEq(got, expect); + } + + // test reflection logic + Reflect.parse(got); + + // test xdr by cloning a cross-compartment function + otherGlobal.str = str; + var c = clone(otherGlobal.eval("new Function('x', str)")); + assertEq(c.toSource(), fun.toSource()); + + var got = fun(arg); + var expect = result; + if (got !== expect) { + print("GOT:" + got); + print("EXPECT: " + expect); + assertEq(got, expect); + } +} + +function isError(str) +{ + var caught = false; + try { + new Function(str); + } catch(e) { + assertEq(String(e).indexOf('TypeError') == 0 || String(e).indexOf('SyntaxError') == 0, true); + caught = true; + } + assertEq(caught, true); +} + +// let expr +test('return let (y) x;'); +test('return let (x) "" + x;', 'unicorns', 'undefined'); +test('return let (y = x) (y++, "" + y);', 'unicorns', 'NaN'); +test('return let (y = 1) (y = x, y);'); +test('return let ([] = x) x;'); +test('return let (x = {a: x}) x.a;'); +test('return let ({a: x} = {a: x}) x;'); +test('return let ([x] = {0: x}) x;'); +test('return let ({0: x} = [x]) x;'); +test('return let ({0: []} = []) x;'); +test('return let ([, ] = x) x;'); +test('return let ([, , , , ] = x) x;'); +test('return let ([[]] = x) x;'); +test('return let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) x;'); +test('return let ([[], []] = x) x;'); +test('return let ([[[[]]], [], , [], [[]]] = x) x;'); +test('return let ({x: []} = x) x;'); +test('return let ({x: [], y: {x: []}} = x) "ponies";', {y:{}}); +test('return let ({x: []} = x, [{x: []}] = x) "ponies";'); +test('return let (x = x) x;'); +test('return let (x = eval("x")) x;'); +test('return let (x = (let (x = x + 1) x) + 1) x;', 1, 3); +test('return let (x = (let (x = eval("x") + 1) eval("x")) + 1) eval("x");', 1, 3); +test('return let (x = x + 1, y = x) y;'); +test('return let (x = x + 1, [] = x, [[, , ]] = x, y = x) y;'); +test('return let ([{a: x}] = x, [, {b: y}] = x) let (x = x + 1, y = y + 2) x + y;', [{a:"p"},{b:"p"}], "p1p2"); +test('return let ([] = []) x;'); +test('return let ([] = [x]) x;'); +test('return let ([x] = [x]) x;'); +test('return let ([[a, [b, c]]] = [[x, []]]) a;'); +test('return let ([x, y] = [x, x + 1]) x + y;', 1, 3); +test('return let ([x, y, z] = [x, x + 1, x + 2]) x + y + z;', 1, 6); +test('return let ([[x]] = [[x]]) x;'); +test('return let ([x, y] = [x, x + 1]) x;'); +test('return let ([x, [y, z]] = [x, x + 1]) x;'); +test('return let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) x;',{x:['ponies']}); +test('return let (x = (3, x)) x;'); +test('return let (x = x + "s") x;', 'ponie'); +test('return let ([x] = (3, [x])) x;'); +test('return let ([] = [[]] = {}) x;'); +test('return let (y = x) function () {return eval("y");}();'); +test('return eval("let (y = x) y");'); +test('return let (y = x) (eval("var y = 2"), y);', 'ponies', 2); +test('"use strict";return let (y = x) (eval("var y = 2"), y);'); +test('this.y = x;return let (y = 1) this.eval("y");'); +test('try {let (x = x) eval("throw x");} catch (e) {return e;}'); +test('try {return let (x = eval("throw x")) x;} catch (e) {return e;}'); +isError('let (x = 1, x = 2) x'); +isError('let ([x, y] = a, {a:x} = b) x'); +isError('let ([x, y, x] = a) x'); +isError('let ([x, [y, [x]]] = a) x'); +isError('let (x = function() { return x}) x()return x;'); +isError('(let (x = function() { return x}) x())return x;'); + +// let block +test('let (y) {return x;}'); +test('let (y = x) {y++;return "" + y;}', 'unicorns', 'NaN'); +test('let (y = 1) {y = x;return y;}'); +test('let (x) {return "" + x;}', 'unicorns', 'undefined'); +test('let ([] = x) {return x;}'); +test('let (x) {}return x;'); +test('let (x = {a: x}) {return x.a;}'); +test('let ({a: x} = {a: x}) {return x;}'); +test('let ([x] = {0: x}) {return x;}'); +test('let ({0: x} = [x]) {return x;}'); +test('let ({0: []} = []) {return x;}'); +test('let ([, ] = x) {return x;}'); +test('let ([, , , , ] = x) {return x;}'); +test('let ([[]] = x) {return x;}'); +test('let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) {return x;}'); +test('let ([[], []] = x) {return x;}'); +test('let ([[[[]]], [], , [], [[]]] = x) {return x;}'); +test('let ({x: []} = x) {return x;}'); +test('let ({x: [], y: {x: []}} = x) {return "ponies";}', {y:{}}); +test('let ({x: []} = x, [{x: []}] = x) {return "ponies";}'); +test('let (x = x) {return x;}'); +test('let (x = eval("x")) {return x;}'); +test('let (x = (let (x = x + 1) x) + 1) {return x;}', 1, 3); +test('let (x = (let (x = eval("x") + 1) eval("x")) + 1) {return eval("x");}', 1, 3); +test('let (x = x + 1, y = x) {return y;}'); +test('let (x = x + 1, [] = x, [[, , ]] = x, y = x) {return y;}'); +test('let ([{a: x}] = x, [, {b: y}] = x) {let (x = x + 1, y = y + 2) {return x + y;}}', [{a:"p"},{b:"p"}], "p1p2"); +test('let ([] = []) {return x;}'); +test('let ([] = [x]) {return x;}'); +test('let ([x] = [x]) {return x;}'); +test('let ([[a, [b, c]]] = [[x, []]]) {return a;}'); +test('let ([x, y] = [x, x + 1]) {return x + y;}', 1, 3); +test('let ([x, y, z] = [x, x + 1, x + 2]) {return x + y + z;}', 1, 6); +test('let ([[x]] = [[x]]) {return x;}'); +test('let ([x, y] = [x, x + 1]) {return x;}'); +test('let ([x, [y, z]] = [x, x + 1]) {return x;}'); +test('let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) {return x;}',{x:['ponies']}); +test('let (y = x[1]) {let (x = x[0]) {try {let (y = "unicorns") {throw y;}} catch (e) {return x + y;}}}', ['pon','ies']); +test('let (x = x) {try {let (x = "unicorns") eval("throw x");} catch (e) {return x;}}'); +test('let ([] = [[]] = {}) {return x;}'); +test('let (y = x) {return function () {return eval("y");}();}'); +test('return eval("let (y = x) {y;}");'); +test('let (y = x) {eval("var y = 2");return y;}', 'ponies', 2); +test('"use strict";let (y = x) {eval("var y = 2");return y;}'); +test('this.y = x;let (y = 1) {return this.eval("y");}'); +isError('let (x = 1, x = 2) {x}'); +isError('let ([x, y] = a, {a:x} = b) {x}'); +isError('let ([x, y, x] = a) {x}'); +isError('let ([x, [y, [x]]] = a) {x}'); + +// var declarations +test('var y;return x;'); +test('var y = x;return x;'); +test('var [] = x;return x;'); +test('var [, ] = x;return x;'); +test('var [, , , , ] = x;return x;'); +test('var [[]] = x;return x;'); +test('var [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;'); +test('var [[], []] = x;return x;'); +test('var [[[[]]], [], , [], [[]]] = x;return x;'); +test('var {x: []} = x;return x;'); +test('var {x: [], y: {x: []}} = x;return "ponies";', {y:{}}); +test('var {x: []} = x, [{x: []}] = x;return "ponies";'); +test('var x = x;return x;'); +test('var y = y;return "" + y;', 'unicorns', 'undefined'); +test('var x = eval("x");return x;'); +test('var x = (let (x = x + 1) x) + 1;return x;', 1, 3); +test('var x = (let (x = eval("x") + 1) eval("x")) + 1;return eval("x");', 1, 3); +test('var X = x + 1, y = x;return y;'); +test('var X = x + 1, [] = X, [[, , ]] = X, y = x;return y;'); +test('var [{a: X}] = x, [, {b: y}] = x;var X = X + 1, y = y + 2;return X + y;', [{a:"p"},{b:"p"}], "p1p2"); +test('var [x] = [x];return x;'); +test('var [[a, [b, c]]] = [[x, []]];return a;'); +test('var [y] = [x];return y;'); +test('var [x, y] = [x, x + 1];return x + y;', 1, 3); +test('var [x, y, z] = [x, x + 1, x + 2];return x + y + z;', 1, 6); +test('var [[x]] = [[x]];return x;'); +test('var [x, y] = [x, x + 1];return x;'); +test('var [x, [y, z]] = [x, x + 1];return x;'); +test('var [{x: [x]}, {y1: y, z1: z}] = [x, x + 1];return x;',{x:['ponies']}); +test('var [] = [[]] = {};return x;'); +test('if (x) {var y = x;return x;}'); +test('if (x) {y = x;var y = y;return y;}'); +test('if (x) {var z = y;var [y] = x;z += y;}return z;', ['-'], 'undefined-'); + +// let declaration in context +test('if (x) {let y;return x;}'); +test('if (x) {let x;return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = x;return x;}'); +test('if (x) {y = x;let y = y;return y;}'); +test('if (x) {var z = y;let [y] = x;z += y;}return z;', ['-'], 'undefined-'); +test('if (x) {let y = x;return x;}'); +test('if (x) {let [] = x;return x;}'); +test('if (x) {let [, ] = x;return x;}'); +test('if (x) {let [, , , , ] = x;return x;}'); +test('if (x) {let [[]] = x;return x;}'); +test('if (x) {let [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;}'); +test('if (x) {let [[], []] = x;return x;}'); +test('if (x) {let [[[[]]], [], , [], [[]]] = x;return x;}'); +test('if (x) {let {x: []} = x;return x;}'); +test('if (x) {let {x: [], y: {x: []}} = x;return "ponies";}', {y:{}}); +test('if (x) {let {x: []} = x, [{x: []}] = x;return "ponies";}'); +test('if (x) {let x = x;return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = y;return "" + y;}', 'unicorns', 'undefined'); +test('if (x) {let x = eval("x");return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = (let (x = x + 1) x) + 1;return y;}', 1, 3); +test('if (x) {let y = (let (x = eval("x") + 1) eval("x")) + 1;return eval("y");}', 1, 3); +test('if (x) {let X = x + 1, y = x;return y;}'); +test('if (x) {let X = x + 1, [] = X, [[, , ]] = X, y = x;return y;}'); +test('if (x) {let [{a: X}] = x, [, {b: Y}] = x;var XX = X + 1, YY = Y + 2;return XX + YY;}', [{a:"p"},{b:"p"}], "p1p2"); +test('if (x) {let [[a, [b, c]]] = [[x, []]];return a;}'); +test('if (x) {let [X] = [x];return X;}'); +test('if (x) {let [y] = [x];return y;}'); +test('if (x) {let [X, y] = [x, x + 1];return X + y;}', 1, 3); +test('if (x) {let [X, y, z] = [x, x + 1, x + 2];return X + y + z;}', 1, 6); +test('if (x) {let [[X]] = [[x]];return X;}'); +test('if (x) {let [X, y] = [x, x + 1];return X;}'); +test('if (x) {let [X, [y, z]] = [x, x + 1];return X;}'); +test('if (x) {let [{x: [X]}, {y1: y, z1: z}] = [x, x + 1];return X;}',{x:['ponies']}); +test('if (x) {let y = x;try {let x = 1;throw 2;} catch (e) {return y;}}'); +test('if (x) {let [] = [[]] = {};return x;}'); +test('let (y, [] = x) {}try {let a = b(), b;} catch (e) {return x;}'); +test('try {let x = 1;throw 2;} catch (e) {return x;}'); +test('let (y = x) {let x;return y;}'); +test('let (y = x) {let x = y;return x;}'); +test('let ([y, z] = x) {let a = x, b = y;return a;}'); +test('let ([y, z] = x, a = x, [] = x) {let b = x, c = y;return a;}'); +test('function f() {return unicorns;}try {let (x = 1) {let a, b;f();}} catch (e) {return x;}'); +test('function f() {return unicorns;}try {let (x = 1) {let a, b;}f();} catch (e) {return x;}'); +test('x.foo;{let y = x;return y;}'); +test('x.foo;if (x) {x.bar;let y = x;return y;}'); +test('if (x) {let y = x;return function () {return eval("y");}();}'); +test('return eval("let y = x; y");'); +test('if (x) {let y = x;eval("var y = 2");return y;}', 'ponies', 2); +test('"use strict";if (x) {let y = x;eval("var y = 2");return y;}'); +test('"use strict";if (x) {let y = x;eval("let y = 2");return y;}'); +test('"use strict";if (x) {let y = 1;return eval("let y = x;y;");}'); +test('this.y = x;if (x) {let y = 1;return this.eval("y");}'); +isError('if (x) {let (x = 1, x = 2) {x}}'); +isError('if (x) {let ([x, y] = a, {a:x} = b) {x}}'); +isError('if (x) {let ([x, y, x] = a) {x}}'); +isError('if (x) {let ([x, [y, [x]]] = a) {x}}'); +isError('let ([x, y] = x) {let x;}'); + +// for(;;) +test('for (;;) {return x;}'); +test('for (let y = 1;;) {return x;}'); +test('for (let y = 1;; ++y) {return x;}'); +test('for (let y = 1; ++y;) {return x;}'); +test('for (let (x = 1) x; x != 1; ++x) {return x;}'); +test('for (let [, {a: [], b: []}] = x, [] = x; x;) {return x;}'); +test('for (let x = 1, [y, z] = x, a = x; z < 4; ++z) {return x + y;}', [2,3], 3); +test('for (let (x = 1, [{a: b, c: d}] = [{a: 1, c: 2}]) x; x != 1; ++x) {return x;}'); +test('for (let [[a, [b, c]]] = [[x, []]];;) {return a;}'); +test('var sum = 0;for (let y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6); +test('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;', 1, 6); +test('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;', 1, 1); +test('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;', 1, 6); +test('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6); +test('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6); +test('for (var y = 1;;) {return x;}'); +test('for (var y = 1;; ++y) {return x;}'); +test('for (var y = 1; ++y;) {return x;}'); +test('for (var [, {a: [], b: []}] = x, [] = x; x;) {return x;}'); +test('for (var X = 1, [y, z] = x, a = x; z < 4; ++z) {return X + y;}', [2,3], 3); +test('var sum = 0;for (var y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6); +test('var sum = 0;for (var X = x, y = 10; X < 4; ++X) {sum += X;}return sum;', 1, 6); +test('var sum = 0;for (var X = x; X < 4; ++X) {sum += X;}return x;', 1, 1); +test('var sum = 0;for (var X = eval("x"); X < 4; ++X) {sum += X;}return sum;', 1, 6); +test('var sum = 0;for (var X = x; eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6); +test('var sum = 0;for (var X = eval("x"); eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6); +test('try {for (let x = eval("throw x");;) {}} catch (e) {return e;}'); +test('try {for (let x = x + "s"; eval("throw x");) {}} catch (e) {return e;}', 'ponie'); +test('for (let y = x;;) {let x;return y;}'); +test('for (let y = x;;) {let y;return x;}'); +test('for (let y;;) {let y;return x;}'); +test('for (let a = x;;) {let c = x, d = x;return c;}'); +test('for (let [a, b] = x;;) {let c = x, d = x;return c;}'); +test('for (let [] = [[]] = {};;) {return x;}'); +isError('for (let x = 1, x = 2;;) {}'); +isError('for (let [x, y] = a, {a:x} = b;;) {}'); +isError('for (let [x, y, x] = a;;) {}'); +isError('for (let [x, [y, [x]]] = a;;) {}'); + +// for(in) +test('for (let i in x) {return x;}'); +test('for (let i in x) {let y;return x;}'); +test('for each (let [a, b] in x) {let y;return x;}'); +test('for (let i in x) {let (i = x) {return i;}}'); +test('for (let i in x) {let i = x;return i;}'); +test('for each (let [x, y] in x) {return x + y;}', [['ponies', '']]); +test('for each (let [{0: x, 1: y}, z] in x) {return x + y + z;}', [[['po','nies'], '']]); +test('var s = "";for (let a in x) {for (let b in x) {s += a + b;}}return s;', [1,2], '00011011'); +test('var res = "";for (let i in x) {res += x[i];}return res;'); +test('var res = "";for (var i in x) {res += x[i];}return res;'); +test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}'); +test('for (let x in eval("x")) {return x;}', {ponies:true}); +test('for (let x in x) {return eval("x");}', {ponies:true}); +test('for (let x in eval("x")) {return eval("x");}', {ponies:true}); +test('for ((let (x = {y: true}) x).y in eval("x")) {return eval("x");}'); +test('for (let i in x) {break;}return x;'); +test('for (let i in x) {break;}return eval("x");'); +test('for (let x in x) {break;}return x;'); +test('for (let x in x) {break;}return eval("x");'); +test('a:for (let i in x) {for (let j in x) {break a;}}return x;'); +test('a:for (let i in x) {for (let j in x) {break a;}}return eval("x");'); +test('var j;for (let i in x) {j = i;break;}return j;', {ponies:true}); +test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}'); +test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies']); +isError('for (let [x, x] in o) {}'); +isError('for (let [x, y, x] in o) {}'); +isError('for (let [x, [y, [x]]] in o) {}'); + +// genexps +test('return (i for (i in x)).next();', {ponies:true}); +test('return (eval("i") for (i in x)).next();', {ponies:true}); +test('return (eval("i") for (i in eval("x"))).next();', {ponies:true}); +test('try {return (eval("throw i") for (i in x)).next();} catch (e) {return e;}', {ponies:true}); + +// array comprehension +test('return [i for (i in x)][0];', {ponies:true}); +test('return [eval("i") for (i in x)][0];', {ponies:true}); +test('return [eval("i") for (i in eval("x"))][0];', {ponies:true}); +test('try {return [eval("throw i") for (i in x)][0];} catch (e) {return e;}', {ponies:true}); + +// don't forget about switch craziness +test('var y = 3;switch (function () {return eval("y");}()) {case 3:let y;return x;default:;}'); +test('switch (x) {case 3:let y;return 3;case 4:let z;return 4;default:return x;}'); +test('switch (x) {case 3:let x;break;default:if (x === undefined) {return "ponies";}}'); +test('switch (x) {case 3:default:let y;let (y = x) {return y;}}'); +isError('switch (x) {case 3:let y;return 3;case 4:let y;return 4;default:;}'); + +// test weird cases where the decompiler changes tokens +function testWeird(str, printedAs, arg, result) +{ + var fun = new Function('x', str); + + // this is lame and doesn't normalize whitespace so if an assert fails + // here, see if its just whitespace and fix the caller + assertEq(fun.toSource(), '(function anonymous(x) {' + printedAs + '})'); + + test(printedAs, arg, result); +} + +testWeird('let y = x;return x;', 'var y = x;return x;'); +testWeird('let y = 1, y = x;return y;', 'var y = 1, y = x;return y;'); +testWeird('return let ({x:x, y:y} = x) x + y', 'return let ({x, y} = x) x + y;', {x:'pon', y:'ies'}); +testWeird('let ({x:x, y:y} = x) {return x + y;}', 'let ({x, y} = x) {return x + y;}', {x:'pon', y:'ies'}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testLocaleCompare.js b/deps/mozjs/js/src/jit-test/tests/basic/testLocaleCompare.js new file mode 100644 index 00000000000..20c0f6f0c88 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testLocaleCompare.js @@ -0,0 +1,7 @@ +assertEq("a".localeCompare(), 0); +assertEq("a".localeCompare("b"), -1); +assertEq("a".localeCompare("b", "a"), -1); +assertEq("b".localeCompare("a"), 1); +assertEq("b".localeCompare("a", "b"), 1); +assertEq("a".localeCompare("a"), 0); +assertEq("a".localeCompare("a", "b", "c"), 0); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMathMinMax.js b/deps/mozjs/js/src/jit-test/tests/basic/testMathMinMax.js new file mode 100644 index 00000000000..4c057fa6da9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMathMinMax.js @@ -0,0 +1,74 @@ +for (var i = 2; i < 10; i++) { + assertEq(Math.min(i, 1), 1); + assertEq(Math.min(i, -1), -1); + assertEq(Math.min(1, i), 1); + assertEq(Math.min(-1, i), -1); + assertEq(Math.min(5, 2), 2); + assertEq(Math.min(2, 5), 2); + assertEq(Math.min(5, -2), -2); + assertEq(Math.min(-2, 5), -2); +} + +for (i = 2; i < 10; i++) { + assertEq(Math.max(i, 1), i); + assertEq(Math.max(i, -1), i); + assertEq(Math.max(1, i), i); + assertEq(Math.max(-1, i), i); + assertEq(Math.max(5, -2), 5); + assertEq(Math.max(-2, 5), 5); + assertEq(Math.max(5, 2), 5); + assertEq(Math.max(2, 5), 5); +} + +for (i = 2.1; i < 13; i += 3.17584) { + assertEq(Math.max(i, 1), i); + assertEq(Math.max(i, 1.5), i); + assertEq(Math.max(1, i), i); + assertEq(Math.max(1.5, i), i); + + assertEq(Math.max(NaN, NaN), NaN); + assertEq(Math.max(NaN, Infinity), NaN); + assertEq(Math.max(Infinity, NaN), NaN); + + assertEq(Math.max(NaN, i), NaN); + assertEq(Math.max(i, NaN), NaN); + + assertEq(Math.max(i, Infinity), Infinity); + assertEq(Math.max(Infinity, i), Infinity); + + assertEq(Math.max(i, -Infinity), i); + assertEq(Math.max(-Infinity, i), i); +} + +for (i = 2.1; i < 13; i += 3.17584) { + assertEq(Math.min(i, 1), 1); + assertEq(Math.min(i, 1.5), 1.5); + assertEq(Math.min(1, i), 1); + assertEq(Math.min(1.5, i), 1.5); + + assertEq(Math.min(NaN, NaN), NaN); + assertEq(Math.min(NaN, Infinity), NaN); + assertEq(Math.min(Infinity, NaN), NaN); + + assertEq(Math.min(NaN, i), NaN); + assertEq(Math.min(i, NaN), NaN); + + assertEq(Math.min(i, Infinity), i); + assertEq(Math.min(Infinity, i), i); + + assertEq(Math.min(i, -Infinity), -Infinity); + assertEq(Math.min(-Infinity, i), -Infinity); +} + +function isNegZero(n) { + return n === 0 && 1/n === -Infinity; +} + +for (i = 0; i < 5; i++) { + assertEq(isNegZero(Math.min(0, -0)), true); + assertEq(isNegZero(Math.min(-0, 0)), true); + assertEq(isNegZero(Math.min(-0, -0)), true); + assertEq(isNegZero(Math.max(0, -0)), false); + assertEq(isNegZero(Math.max(-0, 0)), false); + assertEq(isNegZero(Math.max(-0, -0)), true); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInc.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInc.js index d965adfe6b7..f283f840669 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInc.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInc.js @@ -3,10 +3,3 @@ for (var i = 0; i < 9; i++) { x.f++; assertEq(""+x.f, "NaN"); } - -checkStats({ - recorderStarted: 1, - recorderAborted: 1, - traceCompleted: 0, - sideExitIntoInterpreter: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInit.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInit.js index bd74a8132a7..64cfa4c37c5 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInit.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInit.js @@ -8,8 +8,3 @@ function testMethodInit() { // bug 503198 return x.o() + x.k(); } assertEq(testMethodInit(), "ok"); -checkStats({ - recorderStarted: 1, - traceCompleted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitDeref.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitDeref.js deleted file mode 100644 index 620f6e3c1f9..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitDeref.js +++ /dev/null @@ -1,9 +0,0 @@ -for (var j = 0; j < 7; ++j) - ({x: function () {}}).x; - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitSafety.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitSafety.js index ebd6309bdf9..34177ab8f79 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitSafety.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitSafety.js @@ -4,7 +4,7 @@ function testMethodInitSafety() { var s; var arr = [f, f, f, f, g]; - //assertEq(arr.length > RUNLOOP, true); + //assertEq(arr.length > 9, true); for (var i = 0; i < arr.length; i++) { var x = {m: arr[i]}; s = x.m(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitUneval.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitUneval.js deleted file mode 100644 index 5ca7570a716..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodInitUneval.js +++ /dev/null @@ -1,9 +0,0 @@ -for (var j = 0; j < 7; ++j) - uneval({x: function () {}}); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - sideExitIntoInterpreter: 4 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodOverride.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodOverride.js deleted file mode 100644 index bd19dd6ad15..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodOverride.js +++ /dev/null @@ -1,20 +0,0 @@ -function Q() { - this.m = function () {}; -} -function callm(q) { - q.m(); -} -var a = []; -for (var i = 0; i < 5; i++) { - var q = new Q; - callm(q); - q.m = function () { return 42; }; - a[i] = q; -} -for (var i = 0; i < 5; i++) - callm(a[i]); -checkStats({ - recorderStarted: 2, - traceCompleted: 3, - sideExitIntoInterpreter: 4 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodSet.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodSet.js index 1549c1b552a..d7fcd7fc086 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodSet.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMethodSet.js @@ -11,8 +11,3 @@ function testMethodSet() { // bug 503198 return x.o() + x.k(); } assertEq(testMethodSet(), "ok"); -checkStats({ - recorderStarted: 1, - traceCompleted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMethodWriteBarrier4.js b/deps/mozjs/js/src/jit-test/tests/basic/testMethodWriteBarrier4.js index 686df372e51..f120c9450d2 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMethodWriteBarrier4.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMethodWriteBarrier4.js @@ -2,11 +2,11 @@ var z = 0; function f() { this.b = function() {}; this.b = undefined; - if (z++ > HOTLOOP) + if (z++ > 8) this.b(); } try { - for (var i = 0; i < HOTLOOP + 2; i++) + for (var i = 0; i < 10; i++) new f(); } catch (exc) {} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod.js b/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod.js index 20917f3a882..50ebb4bea36 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod.js @@ -1,9 +1,9 @@ var o = {y: function () {}}; var a = [o, o, o, o, o, o, o, o, o]; -a[RECORDLOOP - 1] = {}; +a[7] = {}; try { for (var i = 0; i < 9; i++) a[i].y(); } catch (exc) { - assertEq(exc.name, "TypeError"); // should happen when i == RECORDLOOP - 1 + assertEq(exc.name, "TypeError"); // should happen when i == 7 } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod2.js b/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod2.js index 2c5c381617e..fabfa08a60f 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testMissingMethod2.js @@ -1,10 +1,10 @@ var o = {y: function() {}}; var a = [o, o, o, o, o, o, o, o, o]; Number.prototype.y = 0; -a[RECORDLOOP - 1] = 0; +a[7] = 0; try { for (var i = 0; i < 9; i++) a[i].y(); } catch (exc) { - assertEq(exc.name, "TypeError"); // should happen when i == RECORDLOOP - 1 + assertEq(exc.name, "TypeError"); // should happen when i == 7 } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNativeSetter.js b/deps/mozjs/js/src/jit-test/tests/basic/testNativeSetter.js index b2c7d757a0b..79ddf56f935 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNativeSetter.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNativeSetter.js @@ -1,14 +1,8 @@ function testNativeSetter() { var re = /foo/; - var N = RUNLOOP + 10; + var N = 19; for (var i = 0; i < N; i++) re.lastIndex = i; assertEq(re.lastIndex, N - 1); } testNativeSetter(); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNegativeArrayLength.js b/deps/mozjs/js/src/jit-test/tests/basic/testNegativeArrayLength.js index 77f1335feb9..1719ceeb970 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNegativeArrayLength.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNegativeArrayLength.js @@ -1,6 +1,6 @@ function f() { try { - for ( var i = HOTLOOP-1; i > -2; i-- ) + for ( var i = 7; i > -2; i-- ) new Array(i).join('*'); } catch (e) { return e instanceof RangeError; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNestedExitStackOuter.js b/deps/mozjs/js/src/jit-test/tests/basic/testNestedExitStackOuter.js index 88b795e7cbc..5da8a1f968a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNestedExitStackOuter.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNestedExitStackOuter.js @@ -2,13 +2,13 @@ function testNestedExitStackInner(j, counter) { ++counter; var b = 0; - for (var i = 1; i <= RUNLOOP; i++) { + for (var i = 1; i <= 9; i++) { ++b; var a; // Make sure that once everything has been traced we suddenly switch to // a different control flow the first time we run the outermost tree, // triggering a side exit. - if (j < RUNLOOP) + if (j < 9) a = 1; else a = 0; @@ -19,8 +19,8 @@ function testNestedExitStackInner(j, counter) { } function testNestedExitStackOuter() { var counter = 0; - for (var j = 1; j <= RUNLOOP; ++j) { - for (var k = 1; k <= RUNLOOP; ++k) { + for (var j = 1; j <= 9; ++j) { + for (var k = 1; k <= 9; ++k) { counter = testNestedExitStackInner(j, counter); } } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNewDate.js b/deps/mozjs/js/src/jit-test/tests/basic/testNewDate.js deleted file mode 100644 index 2775451ec61..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNewDate.js +++ /dev/null @@ -1,17 +0,0 @@ -function testNewDate() -{ - // Accessing global.Date for the first time will change the global shape, - // so do it before the loop starts; otherwise we have to loop an extra time - // to pick things up. - var start = new Date(); - var time = new Date(); - for (var j = 0; j < RUNLOOP; ++j) - time = new Date(); - return time > 0 && time >= start; -} -assertEq(testNewDate(), true); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNewString.js b/deps/mozjs/js/src/jit-test/tests/basic/testNewString.js index 81e1c210c97..e0f378bbc06 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNewString.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNewString.js @@ -19,9 +19,3 @@ function testNewString() return r.length === 35 && r.every(function(v) { return v === "object"; }); } assertEq(testNewString(), true); -checkStats({ - recorderStarted: 7, - recorderAborted: 0, - traceCompleted: 7, - sideExitIntoInterpreter: 7 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNewWithClone.js b/deps/mozjs/js/src/jit-test/tests/basic/testNewWithClone.js new file mode 100644 index 00000000000..ef976df5858 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNewWithClone.js @@ -0,0 +1,7 @@ +with({}) { + function f() { + this.foo = "bar"; + } + o = new f(); + assertEq(o.foo, "bar"); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNewWithNonNativeProto.js b/deps/mozjs/js/src/jit-test/tests/basic/testNewWithNonNativeProto.js index 11066baabc0..dc950efc7cc 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNewWithNonNativeProto.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNewWithNonNativeProto.js @@ -7,8 +7,3 @@ function testNewWithNonNativeProto() return Object.getPrototypeOf(o) === a && o.splice === Array.prototype.splice; } assertEq(testNewWithNonNativeProto(), true); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNullIncrement.js b/deps/mozjs/js/src/jit-test/tests/basic/testNullIncrement.js index 71e49b74b57..b7ffd03b4c6 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNullIncrement.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNullIncrement.js @@ -1,7 +1,7 @@ function f() { var n; var k; - for (var i = 0; i < 2*RUNLOOP; ++i) { + for (var i = 0; i < 18; ++i) { n = null; k = n++; if (k) { } @@ -12,10 +12,3 @@ function f() { var [a, b] = f(); assertEq(a, 0); assertEq(b, 1); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - traceTriggered: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testNullToString.js b/deps/mozjs/js/src/jit-test/tests/basic/testNullToString.js index a422ce98732..c80feb1da1a 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testNullToString.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testNullToString.js @@ -11,8 +11,3 @@ function testNullToString() return a.join(","); } assertEq(testNullToString(), "null,null,null,null,null,null,null,null,null,null"); -checkStats({ - recorderStarted: 2, - sideExitIntoInterpreter: 2, - recorderAborted: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js b/deps/mozjs/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js new file mode 100644 index 00000000000..9d282af71c8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js @@ -0,0 +1,12 @@ +foo = evalcx("(function foo() { foo.bar() })"); +foo.bar = evalcx("(function bar() {})"); + +function fatty() { + try { + fatty(); + } catch (e) { + foo(); + } +} + +fatty(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testObjectConstructorReturningObject.js b/deps/mozjs/js/src/jit-test/tests/basic/testObjectConstructorReturningObject.js index 21d8a08a8a8..c18863db648 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testObjectConstructorReturningObject.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testObjectConstructorReturningObject.js @@ -1,4 +1,4 @@ -for (var i = 0; i < HOTLOOP+4; ++i) { +for (var i = 0; i < 12; ++i) { var o; o = new Object(Object); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testObjectLength.js b/deps/mozjs/js/src/jit-test/tests/basic/testObjectLength.js index c19c1b2cf21..683b1243919 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testObjectLength.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testObjectLength.js @@ -8,8 +8,3 @@ function testObjectLength() return counter; } assertEq(testObjectLength(), 10000000); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testOverOOMInFixupArity.js b/deps/mozjs/js/src/jit-test/tests/basic/testOverOOMInFixupArity.js new file mode 100644 index 00000000000..1b81d84ec00 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testOverOOMInFixupArity.js @@ -0,0 +1,15 @@ +function foo() { + bar(1,2,3,4,5,6,7,8,9); +} + +function bar() { + foo(1,2,3,4,5,6,7,8,9); +} + +var caught = false; +try { + foo(); +} catch (e) { + caught = true; +} +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testOwnPropertyWithInOperator.js b/deps/mozjs/js/src/jit-test/tests/basic/testOwnPropertyWithInOperator.js index 7c9501fd005..2224dba2d6b 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testOwnPropertyWithInOperator.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testOwnPropertyWithInOperator.js @@ -7,6 +7,3 @@ function testOwnPropertyWithInOperator() return a.join(","); } assertEq(testOwnPropertyWithInOperator(), "true,true,true,true,true,true,true"); -checkStats({ - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testParseInt.js b/deps/mozjs/js/src/jit-test/tests/basic/testParseInt.js new file mode 100644 index 00000000000..d3706b2f08f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testParseInt.js @@ -0,0 +1,48 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function testInt(n, result) { + var x = 0; + for (var i = 0; i < 15; i++) { + assertEq(parseInt(n, 10), result); + assertEq(parseInt(n, 0), result); + assertEq(parseInt(n), result); + assertEq(parseInt(n, x), result); + + if (x % 2 == 0) + x = 10; + else + x = 0; + } +} + +function testDouble(n, result) { + var x = 0; + for (var i = 0; i < 15; i++) { + assertEq(parseInt(n, 10), result); + assertEq(parseInt(n, 0), result); + assertEq(parseInt(n), result); + assertEq(parseInt(n, x), result); + + if (x % 2 == 0) + x = 10; + else + x = 0; + } +} + +testInt(2147483647, 2147483647); +testInt(-2147483648, -2147483648); +testInt(17, 17); +testInt(-1, -1); +testInt(0, 0); + +testDouble(1e21, 1); +testDouble(-5.7, -5); +testDouble(1.7, 1); +testDouble(1.0e-6, 0); +testDouble(1.0e-7, 1); +testDouble(NaN, NaN); +testDouble(1e20, 1e20); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testPartialFlatClosure.js b/deps/mozjs/js/src/jit-test/tests/basic/testPartialFlatClosure.js index da022fb6d68..9998ce306b8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testPartialFlatClosure.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testPartialFlatClosure.js @@ -8,9 +8,3 @@ assertEq((('-r', function (s) { a[a.length] = new C(i); return a; })(42))[4].m(), 168); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testProxyDefinePropertyWithMissingSetter.js b/deps/mozjs/js/src/jit-test/tests/basic/testProxyDefinePropertyWithMissingSetter.js index fa52756c22f..dc08afde475 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testProxyDefinePropertyWithMissingSetter.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testProxyDefinePropertyWithMissingSetter.js @@ -1,6 +1,5 @@ // throw, don't crash -var expect = "TypeError: property descriptor's setter field is neither undefined nor a function"; var actual = ""; try { @@ -20,4 +19,4 @@ Object.defineProperty(x, "", ({ actual = '' + e; } -assertEq(expect, actual); +assertEq(actual, "InternalError: too much recursion"); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testRUNLOOPCorrectness.js b/deps/mozjs/js/src/jit-test/tests/basic/testRUNLOOPCorrectness.js deleted file mode 100644 index 5ec5529c07e..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testRUNLOOPCorrectness.js +++ /dev/null @@ -1,15 +0,0 @@ -function testRUNLOOPCorrectness() { - var b = 0; - for (var i = 0; i < RUNLOOP; ++i) { - ++b; - } - return b; -} -// Change the global shape right before doing the test -this.testRUNLOOPCorrectnessVar = 1; -assertEq(testRUNLOOPCorrectness(), RUNLOOP); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceTriggered: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testRebranding2.js b/deps/mozjs/js/src/jit-test/tests/basic/testRebranding2.js index 2bf26eeaff5..7349c4fc029 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testRebranding2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testRebranding2.js @@ -11,7 +11,7 @@ function testRebranding2() { function h(){ x = "ok"; } var obj = {m: g}; var arr = [g, g, g, g, h]; - //assertEq(arr.length > RUNLOOP, true); + //assertEq(arr.length > 9, true); for (var i = 0; i < 5; i++) { obj.m = arr[i]; obj.m(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testRegExpLiteral.js b/deps/mozjs/js/src/jit-test/tests/basic/testRegExpLiteral.js deleted file mode 100644 index 8e0c7feaa95..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testRegExpLiteral.js +++ /dev/null @@ -1,17 +0,0 @@ -function testRegExpLiteral() -{ - var a; - for (var i = 0; i < 15; i++) - a = /foobar/; -} - -testRegExpLiteral(); - -checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 1, - recorderAborted: 0, - traceTriggered: 1, - traceCompleted: 1 - }); - diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testRegExpTest.js b/deps/mozjs/js/src/jit-test/tests/basic/testRegExpTest.js index 78917ba9be3..d7121b98719 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testRegExpTest.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testRegExpTest.js @@ -1,4 +1,4 @@ -// |jit-test| TMFLAGS: full,fragprofile,treevis; valgrind +// |jit-test| valgrind function testRegExpTest() { var r = /abc/; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testRopeMarking.js b/deps/mozjs/js/src/jit-test/tests/basic/testRopeMarking.js new file mode 100644 index 00000000000..fcf53114168 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testRopeMarking.js @@ -0,0 +1,13 @@ +for (var i = 0; i < 10; ++i) { + var arr = []; + var s = "abcdefghijklmnop"; + for (var j = 0; j < 5000; ++j) { + s = "<" + s + ">"; + arr.push(s); + } + gc(); + for (var j = 0; j < 5000; ++j) { + arr[j].search("a"); + } + gc(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_CALLPROP.js b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_CALLPROP.js index 14574b146bf..d16895e15d7 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_CALLPROP.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_CALLPROP.js @@ -7,5 +7,3 @@ var g = ''; for (var i = 0; i < 9; i++) g += a.p(); assertEq(g, 'qqqqqqqqq'); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETARGPROP.js b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETARGPROP.js index 5483781800a..3a865edcf58 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETARGPROP.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETARGPROP.js @@ -5,5 +5,3 @@ function test(a) { assertEq(s, 'qqqqqqqqq'); } test({get p() { return 'q'; }}); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETLOCALPROP.js b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETLOCALPROP.js index c00e07e50da..1add26e6eba 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETLOCALPROP.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETLOCALPROP.js @@ -6,5 +6,3 @@ function test() { assertEq(s, 'qqqqqqqqq'); } test(); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETPROP.js b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETPROP.js index 59e545d97b0..963c506c480 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETPROP.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETPROP.js @@ -6,5 +6,3 @@ var g = 0; for (var i = 0; i < 9; i++) g += f().p; assertEq(g, 99); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETTHISPROP.js b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETTHISPROP.js index 9c4b958cbc8..b878a78efb8 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETTHISPROP.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testScriptGetter_JSOP_GETTHISPROP.js @@ -7,5 +7,3 @@ var a = { assertEq(s, 99); }}; a.test(); - -checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSetMethod.js b/deps/mozjs/js/src/jit-test/tests/basic/testSetMethod.js deleted file mode 100644 index 45e940497a7..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSetMethod.js +++ /dev/null @@ -1,13 +0,0 @@ -function C() { - this.a = function() {}; - this.b = function() {}; -} -for (var i = 0; i < 9; i++) - new C; - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testShiftLeft.js b/deps/mozjs/js/src/jit-test/tests/basic/testShiftLeft.js index 4a7f5d98289..4cc78f5eaaa 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testShiftLeft.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testShiftLeft.js @@ -1,4 +1,4 @@ -// |jit-test| TMFLAGS: full,fragprofile,treevis; valgrind +// |jit-test| valgrind /* Test the proper operation of the left shift operator. This is especially * important on ARM as an explicit mask is required at the native instruction diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSideExitInConstructor.js b/deps/mozjs/js/src/jit-test/tests/basic/testSideExitInConstructor.js deleted file mode 100644 index d46543ef7bd..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSideExitInConstructor.js +++ /dev/null @@ -1,39 +0,0 @@ -// |jit-test| TMFLAGS: full,fragprofile,treevis; valgrind - -function testSideExitInConstructor() { - var FCKConfig = {}; - FCKConfig.CoreStyles = - { - 'Bold': { }, - 'Italic': { }, - 'FontFace': { }, - 'Size' : - { - Overrides: [ ] - }, - - 'Color' : - { - Element: '', - Styles: { }, - Overrides: [ ] - }, - 'BackColor': { - Element : '', - Styles : { 'background-color' : '' } - }, - - }; - var FCKStyle = function(A) { - A.Element; - }; - - var pass = true; - for (var s in FCKConfig.CoreStyles) { - var x = new FCKStyle(FCKConfig.CoreStyles[s]); - if (!x) - pass = false; - } - return pass; -} -assertEq(testSideExitInConstructor(), true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayLength.js b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayLength.js index 1e8fc3b0b82..e261d3654eb 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayLength.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayLength.js @@ -8,8 +8,3 @@ function testSlowArrayLength() return counter; } assertEq(testSlowArrayLength(), 10000000); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPop.js b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPop.js index 6c55a0ce1c9..f3602aa091d 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPop.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPop.js @@ -1,11 +1,11 @@ function testSlowArrayPop() { var a = []; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) a[i] = [0]; - a[RUNLOOP-1].__defineGetter__("0", function () { return 'xyzzy'; }); + a[8].__defineGetter__("0", function () { return 'xyzzy'; }); var last; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) last = a[i].pop(); // reenters interpreter in getter return last; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopMultiFrame.js b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopMultiFrame.js index 3a5fb8d0ad8..a1917ff64e2 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopMultiFrame.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopMultiFrame.js @@ -2,9 +2,9 @@ // multiple functions called inside the loop) function testSlowArrayPopMultiFrame() { var a = []; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) a[i] = [0]; - a[RUNLOOP-1].__defineGetter__("0", function () { return 23; }); + a[8].__defineGetter__("0", function () { return 23; }); function child(a, i) { return a[i].pop(); // reenters interpreter in getter @@ -17,7 +17,7 @@ function testSlowArrayPopMultiFrame() { } var last; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) last = gramps(a, i); return last; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopNestedTrees.js b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopNestedTrees.js index ae137c3c6c7..0ad4dbaaeef 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopNestedTrees.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testSlowArrayPopNestedTrees.js @@ -4,32 +4,32 @@ function testSlowArrayPopNestedTrees() { var a = []; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) a[i] = [0]; - a[RUNLOOP-1].__defineGetter__("0", function () { return 3.14159 }); + a[8].__defineGetter__("0", function () { return 3.14159 }); function child(a, i, j, k) { var last = 2.71828; - for (var l = 0; l < RUNLOOP; l++) - if (i == RUNLOOP-1 && j == RUNLOOP-1 && k == RUNLOOP-1) + for (var l = 0; l < 9; l++) + if (i == 8 && j == 8 && k == 8) last = a[l].pop(); // reenters interpreter in getter return last; } function parent(a, i, j) { var last; - for (var k = 0; k < RUNLOOP; k++) + for (var k = 0; k < 9; k++) last = child(a, i, j, k); return last; } function gramps(a, i) { var last; - for (var j = 0; j < RUNLOOP; j++) + for (var j = 0; j < 9; j++) last = parent(a, i, j); return last; } var last; - for (var i = 0; i < RUNLOOP; i++) + for (var i = 0; i < 9; i++) last = gramps(a, i); return last; } diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStackIter.js b/deps/mozjs/js/src/jit-test/tests/basic/testStackIter.js new file mode 100644 index 00000000000..ad3741e5c30 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testStackIter.js @@ -0,0 +1,156 @@ +function stackToString(stack) { + var str = "|"; + for (var i = 0; i < stack.length; ++i) { + if (typeof stack[i] === "string") + str += stack[i]; + else + str += stack[i].name; + str += "|"; + } + return str; +} + +function assertStackIs(s1) { + var s2 = dumpStack(); + var me = s2.shift(); + assertEq(me, assertStackIs); + try { + if (s1.length != s2.length) + throw "Length " + s1.length + " not equal " + s2.length; + for (var i = 0; i < s1.length; ++i) { + var match; + if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) { + if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0) + throw "Element " + i + " not bound function"; + } else if (s1[i] != s2[i]) { + throw "Element " + i + " not equal"; + } + } + } + catch (e) { + print("Given = " + stackToString(s1)); + print("dumpStack = " + stackToString(s2)); + throw e; + } +} + +/***********/ + +assertStackIs(["global-code"]); +(function f() { assertStackIs([f, "global-code"]) })(); +eval("assertStackIs(['eval-code', 'global-code'])"); +(function f() { eval("assertStackIs(['eval-code', f, 'global-code'])"); })(); +(function f() { eval("(function g() { assertStackIs([g, 'eval-code', f, 'global-code']); })()"); })(); +(function f() { assertStackIs([f, 'bound(f)', 'global-code']); }).bind()() +this['eval']("assertStackIs(['eval-code', eval, 'global-code'])"); +eval.bind(null, "assertStackIs(['eval-code', eval, 'bound(eval)', 'global-code'])")(); +(function f() { assertStackIs([f, Function.prototype.call, 'global-code']) }).call(null); +(function f() { (function g(x,y,z) { assertStackIs([g,f,'global-code']); })() })(1); + +/***********/ + +var gen = (function g() { assertStackIs([g, gen.next, fun, 'global-code']); yield; })(); +var fun = function f() { gen.next() }; +fun(); + +var gen = (function g(x) { assertStackIs([g, gen.next, fun, 'global-code']); yield; })(1,2,3); +var fun = function f() { gen.next() }; +fun(); + +var gen = (function g(x) { assertStackIs([g, gen.next, 'eval-code', fun, 'global-code']); yield; })(1,2,3); +var fun = function f() { eval('gen.next()') }; +fun(); + +/***********/ + +const N = 100; + +(function f(x) { + if (x === 0) { + var s = dumpStack(); + for (var i = 0; i < N; ++i) + assertEq(s[i], f); + return; + } + f(x-1); +})(N); + +/***********/ + +"abababab".replace(/b/g, function g() { + assertStackIs([g, String.prototype.replace, "global-code"]); +}); + +/***********/ + +var obj = { + toString:function toString() { + assertStackIs([toString, String.prototype.concat, "global-code"]); + return ""; + } +} +"a".concat(obj); + +(function f() { + var obj = { + toString:(Array.prototype.sort.bind([1,2], function cb() { + assertStackIs([cb, Array.prototype.sort, "bound(sort)", + String.prototype.concat, f, "global-code"]); + throw "OK"; + })) + } + + try { + "a".concat(obj); + } catch(e) { + assertEq(e, "OK"); + } +})(); + +/***********/ + +var obj = { valueOf:function valueOf() { + assertStackIs([valueOf, Math.sin, Array.prototype.sort, "global-code"]); +} }; +[obj, obj].sort(Math.sin); + +var obj = { valueOf:(function valueOf() { + assertStackIs([valueOf, "bound(valueOf)", Math.sin, Array.prototype.sort, "global-code"]); +}).bind() }; +[obj, obj].sort(Math.sin); + +var obj = { valueOf:(function valueOf() { + assertStackIs([valueOf, "bound(valueOf)", "bound(valueOf)", "bound(valueOf)", + Math.sin, Array.prototype.sort, "global-code"]); +}).bind().bind().bind() }; +[obj, obj].sort(Math.sin); + +/***********/ + +var proxy = Proxy.createFunction({}, function f() { assertStackIs([f, "global-code"]) }); +proxy(); +new proxy(); + +/***********/ + +for (var i = 0; i < 10; ++i) { + eval("'no imacros please'"); + + /* No loss for scripts. */ + (function f() { + assertStackIs([f, Function.prototype.call, 'global-code']); + }).call(null); + + /* Loss for natives. */ + (function f() { + var stack = dumpStack(); + assertEq(stack[0], f); + if (stack.length === 4) { + assertEq(stack[1].name, 'f'); + assertEq(stack[2], Function.prototype.call); + } else { + assertEq(stack.length, 3); + assertEq(stack[1], Function.prototype.call); + } + }).bind().call(null); +} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStackIterDebug.js b/deps/mozjs/js/src/jit-test/tests/basic/testStackIterDebug.js new file mode 100644 index 00000000000..92e1a7f9206 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testStackIterDebug.js @@ -0,0 +1,62 @@ +// |jit-test| mjitalways;debug + +function stackToString(stack) { + var str = "|"; + for (var i = 0; i < stack.length; ++i) { + if (typeof stack[i] === "string") + str += stack[i]; + else + str += stack[i].name; + str += "|"; + } + return str; +} + +function assertStackIs(s1) { + var s2 = dumpStack(); + var me = s2.shift(); + assertEq(me, assertStackIs); + try { + if (s1.length != s2.length) + throw "Length " + s1.length + " not equal " + s2.length; + for (var i = 0; i < s1.length; ++i) { + var match; + if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) { + if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0) + throw "Element " + i + " not bound function"; + } else if (s1[i] != s2[i]) { + throw "Element " + i + " not equal"; + } + } + } + catch (e) { + print("Given = " + stackToString(s1)); + print("dumpStack = " + stackToString(s2)); + throw e; + } +} + +/*********************************************/ + +(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])"); })(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); })() })(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])"); })() }).bind()(); + +(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])", true); })(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); })() })(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); })() }).bind()(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind()() }).bind()(); +(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind().bind()() }).bind()(); + +(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); }}; [o,o].sort() })(); +(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); }}; [o,o].sort() })(); + +function inner() { + (function puppies() { + evalInFrame(1, "assertStackIs(['eval-code', inner, String.prototype.replace, outer, String.prototype.replace, 'global-code'])"); + })(); +} +function outer() { + "bbb".replace(/b/g, inner); +} +"aaa".replace(/a/g, outer); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStackQuotaExhausted.js b/deps/mozjs/js/src/jit-test/tests/basic/testStackQuotaExhausted.js deleted file mode 100644 index f65957a74b7..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testStackQuotaExhausted.js +++ /dev/null @@ -1,29 +0,0 @@ -const numFatArgs = Math.pow(2,19) - 1024; - -function fun(x) { - if (x <= 0) - return 0; - return fun(x-1); -} - -function fatStack() { - return fun(10000); -} - -function assertRightFailure(e) { - assertEq(e.toString() == "InternalError: script stack space quota is exhausted" || - e.toString() == "InternalError: too much recursion", - true); -} - -exception = false; -try { - fatStack.apply(null, new Array(numFatArgs)); -} catch (e) { - assertRightFailure(e); - exception = true; -} -assertEq(exception, true); - -// No more trace recursion w/ JM -checkStats({traceCompleted:0}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testString.js b/deps/mozjs/js/src/jit-test/tests/basic/testString.js index b8a50217d46..cb7e1e5b3a2 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testString.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testString.js @@ -1,6 +1,6 @@ function testString() { var q; - for (var i = 0; i <= RUNLOOP; ++i) { + for (var i = 0; i <= 9; ++i) { q = []; q.push(String(void 0)); q.push(String(true)); @@ -12,7 +12,3 @@ function testString() { return q.join(","); } assertEq(testString(), "undefined,true,5,5.5,5,5"); -checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js b/deps/mozjs/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js new file mode 100644 index 00000000000..e016f3b6bc6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js @@ -0,0 +1,18 @@ +// first build a big honkin' string +str = "a"; +for (var i = 0; i < 20; ++i) + str = str + str; +str.indexOf('a'); + +var f; +f = makeFinalizeObserver(); +assertEq(finalizeCount(), 0); + +// Create another observer to make sure that we overwrite all conservative +// roots for the previous one and can observer the GC. +f = makeFinalizeObserver(); + +// if the assert fails, add more iterations +for (var i = 0; i < 80; ++i) + str.replace(/(a)/, '$1'); +//assertEq(finalizeCount(), 1); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStringIncrement.js b/deps/mozjs/js/src/jit-test/tests/basic/testStringIncrement.js index 65f7de5a303..b0cc6775ecb 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testStringIncrement.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testStringIncrement.js @@ -3,7 +3,7 @@ var a, b; function f(str) { var n; var k; - for (var i = 0; i < 2*RUNLOOP; ++i) { + for (var i = 0; i < 18; ++i) { n = str; k = n++; if (k) { } @@ -18,10 +18,3 @@ assertEq(b, 11); [a, b] = f("5"); assertEq(a, 5); assertEq(b, 6); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - traceTriggered: 2 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStringPropIncrement.js b/deps/mozjs/js/src/jit-test/tests/basic/testStringPropIncrement.js deleted file mode 100644 index 4832d90c6cf..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/basic/testStringPropIncrement.js +++ /dev/null @@ -1,16 +0,0 @@ -function f() { - var o = { n: "" }; - for (var i = 0; i < 2*RUNLOOP; ++i) { - o.n = ""; - if (o.n++) { } - } -} - -f(); - -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - traceTriggered: 1 -}); \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testStringToNumber.js b/deps/mozjs/js/src/jit-test/tests/basic/testStringToNumber.js index e3e2fe7ddc5..167c26b5b43 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testStringToNumber.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testStringToNumber.js @@ -4,7 +4,7 @@ function convertToInt(str) { function convertToIntOnTrace(str) { var z; - for (var i = 0; i < RUNLOOP; ++i) { + for (var i = 0; i < 9; ++i) { z = str | 0; } return z; @@ -16,7 +16,7 @@ function convertToDouble(str) { function convertToDoubleOnTrace(str) { var z; - for (var i = 0; i < RUNLOOP; ++i) { + for (var i = 0; i < 9; ++i) { z = str * 1.5; } return z; @@ -26,22 +26,10 @@ assertEq(convertToInt("0x10"), 16); assertEq(convertToInt("-0x10"), 0); assertEq(convertToIntOnTrace("0x10"), 16); -checkStats({ - traceTriggered: 1 -}); assertEq(convertToIntOnTrace("-0x10"), 0); -checkStats({ - traceTriggered: 2 -}); assertEq(convertToDouble("0x10"), 24); assertEq(convertToDouble("-0x10"), NaN); assertEq(convertToDoubleOnTrace("0x10"), 24); -checkStats({ - traceTriggered: 3 -}); assertEq(convertToDoubleOnTrace("-0x10"), NaN); -checkStats({ - traceTriggered: 4 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch1.js b/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch1.js index fa394a4596e..db212c0de83 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch1.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch1.js @@ -1,7 +1,7 @@ function testTableSwitch1() { var x = 'miss'; var i, j = 0; - for (i = 0; i < RUNLOOP + 10; i++) { + for (i = 0; i < 19; i++) { switch (x) { case 1: case 2: case 3: case 4: case 5: throw "FAIL"; default: j++; @@ -11,12 +11,3 @@ function testTableSwitch1() { } testTableSwitch1(); - -if (HAVE_TM && jitstats.archIsIA32) { - checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 1, - recorderAborted: 0, - traceCompleted: 1 - }); -} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch2.js b/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch2.js index 9a350fdc264..8953aa2eb22 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch2.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testTableSwitch2.js @@ -16,12 +16,3 @@ function testTableSwitch2() { } testTableSwitch2(); - -if (HAVE_TM && jitstats.archIsIA32) { - checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 4, - recorderAborted: 0, - traceCompleted: 3 - }); -} diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js b/deps/mozjs/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js new file mode 100644 index 00000000000..1f309b962a4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js @@ -0,0 +1,4 @@ +// |jit-test| error: 3 +var str = (function (x) {return (i for (i in x));}).toSource().replace('\n', ''); +assertEq(str, "(function (x) {return (i for (i in x));})"); +throw 3; diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testThinLoopDemote.js b/deps/mozjs/js/src/jit-test/tests/basic/testThinLoopDemote.js index 6feb063fbcc..b26c460a784 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testThinLoopDemote.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testThinLoopDemote.js @@ -11,10 +11,3 @@ function testThinLoopDemote() { return f(); } assertEq(testThinLoopDemote(), 10000); -checkStats({ - recorderStarted: 1, - recorderAborted: 0, - traceCompleted: 1, - traceTriggered: 2, - unstableLoopVariable: 0 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testThrowWhileWrappingException.js b/deps/mozjs/js/src/jit-test/tests/basic/testThrowWhileWrappingException.js new file mode 100644 index 00000000000..51169339ab4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testThrowWhileWrappingException.js @@ -0,0 +1,7 @@ +var caught = false; +try { + evalcx("eval(\"throw\")", Function.a) +} catch (e) { + caught = true; +} +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testThrowingObjectEqUndefined.js b/deps/mozjs/js/src/jit-test/tests/basic/testThrowingObjectEqUndefined.js index af2016130fd..5104c52ee76 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testThrowingObjectEqUndefined.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testThrowingObjectEqUndefined.js @@ -13,6 +13,3 @@ function testThrowingObjectEqUndefined() } } assertEq(testThrowingObjectEqUndefined(), true); -checkStats({ - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testToStringBeforeValueOf.js b/deps/mozjs/js/src/jit-test/tests/basic/testToStringBeforeValueOf.js index ac35541d1de..b3550371ee9 100644 --- a/deps/mozjs/js/src/jit-test/tests/basic/testToStringBeforeValueOf.js +++ b/deps/mozjs/js/src/jit-test/tests/basic/testToStringBeforeValueOf.js @@ -7,7 +7,3 @@ function testToStringBeforeValueOf() return a.join(","); } assertEq(testToStringBeforeValueOf(), "s,s,s,s,s,s,s,s,s,s"); -checkStats({ - recorderStarted: 1, - sideExitIntoInterpreter: 1 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testTrapOnEval.js b/deps/mozjs/js/src/jit-test/tests/basic/testTrapOnEval.js new file mode 100644 index 00000000000..52ffc887d1a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testTrapOnEval.js @@ -0,0 +1,4 @@ +// |jit-test| debug +function f() { eval(''); } +trap(f, 6, ''); +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/basic/testTypedArrayByteRegs.js b/deps/mozjs/js/src/jit-test/tests/basic/testTypedArrayByteRegs.js new file mode 100644 index 00000000000..09c6d320ba7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/basic/testTypedArrayByteRegs.js @@ -0,0 +1,30 @@ +// Uint8Array and Int8Array need single byte registers. +// Test for id and value having the same backing. +function f() { + var x = new Uint8Array(30); + for (var i=0; i= 0, true); + } + hits++; +}; +g.f("var x = 'local';"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-02.js new file mode 100644 index 00000000000..d05c56f5940 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-02.js @@ -0,0 +1,20 @@ +// The last Environment on the environment chain always has .type == "object" and .object === the global object. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +g.eval("function h() { debugger; }"); +var hits = 0; +dbg.onDebuggerStatement = function (hframe) { + var env = hframe.older.environment; + while (env.parent) + env = env.parent; + assertEq(env.type, "object"); + assertEq(env.object, gw); + hits++; +}; + +g.eval("h();"); +g.eval("(function () { h(); return []; })();"); +g.eval("with (Math) { h(-2 * PI); }"); +assertEq(hits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-01.js new file mode 100644 index 00000000000..684ffe9e5bb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-01.js @@ -0,0 +1,19 @@ +// find sees that vars are hoisted out of with statements. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.environment.find("x").type, "object"); + hits++; +}; + +assertEq(g.eval("(function () {\n" + + " function g() { x = 1; }\n" + + " with ({x: 2}) {\n" + + " var x;\n" + + " debugger;\n" + + " return x;\n" + + " }\n" + + "})();"), 2); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-02.js new file mode 100644 index 00000000000..ef4e058d6a0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-02.js @@ -0,0 +1,18 @@ +// env.find() finds nonenumerable names in the global environment. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var env = dbg.getNewestFrame().environment; + var last = env; + while (last.parent) + last = last.parent; + + assertEq(env.find("Array"), last); + hits++; +}; + +g.eval("h();"); +g.eval("(function () { let (x = 1, y = 2) h(); })();"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-03.js new file mode 100644 index 00000000000..04bfb616749 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-03.js @@ -0,0 +1,20 @@ +// env.find() finds noneumerable properties in with statements. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var frame = dbg.getNewestFrame(); + var target = frame.eval("obj").return; + var env = frame.environment.find("PI"); + assertEq(env.object, target); + hits++; +}; + +g.obj = g.Math; +g.eval("with (obj) h();"); +g.eval("with (Math) { let x = 12; h(); }"); +g.eval("obj = {};\n" + + "Object.defineProperty(obj, 'PI', {enumerable: false, value: 'Marlowe'});\n" + + "with (obj) h();\n"); +assertEq(hits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-04.js new file mode 100644 index 00000000000..39f986c818f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-04.js @@ -0,0 +1,21 @@ +// env.find throws a TypeError if the argument is not an identifier. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var env = dbg.getNewestFrame().environment; + assertThrowsInstanceOf(function () { env.find(); }, TypeError); + assertThrowsInstanceOf(function () { env.find(""); }, TypeError); + assertThrowsInstanceOf(function () { env.find(" "); }, TypeError); + assertThrowsInstanceOf(function () { env.find(0); }, TypeError); + assertThrowsInstanceOf(function () { env.find("0"); }, TypeError); + assertThrowsInstanceOf(function () { env.find("0xc"); }, TypeError); + assertThrowsInstanceOf(function () { env.find("Anna Karenina"); }, TypeError); + hits++; +}; +g.eval("h();"); +g.eval("with ([1]) h();"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/autom4te.cache/output.0t b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-05.js similarity index 100% rename from deps/mozjs/js/src/autom4te.cache/output.0t rename to deps/mozjs/js/src/jit-test/tests/debug/Environment-find-05.js diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-06.js new file mode 100644 index 00000000000..f49dc31cf2e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-find-06.js @@ -0,0 +1,49 @@ +// Environment.prototype.find finds bindings that are function arguments, 'let' +// bindings, or FunctionExpression names. + +var g = newGlobal('new-compartment'); +g.eval("function h() { debugger; }"); + +var dbg = new Debugger(g); + +function test1(code) { + var hits = 0; + dbg.onDebuggerStatement = function (frame) { + var env = frame.older.environment.find('X'); + assertEq(env.names().indexOf('X') !== -1, true); + assertEq(env.type, 'declarative'); + assertEq(env.parent !== null, true); + hits++; + }; + g.eval(code); + assertEq(hits, 1); +} + +var manyNames = ''; +for (var i = 0; i < 4096; i++) + manyNames += 'x' + i + ', '; +manyNames += 'X'; + +function test2(code) { + print(code + " : one"); + test1(code.replace('@@', 'X')); + print(code + " : many"); + test1(code.replace('@@', manyNames)); +} + +test2('function f(@@) { h(); } f(1);'); +test2('function f(@@) { h(); } f();'); +test2('function f(@@) { return function g() { h(X); }; } f(1)();'); +test2('function f(@@) { return function g() { h(X); }; } f()();'); + +test2(' { let @@ = 0; h(); }'); +test2('function f(a, b, c) { let @@ = 0; h(); } f(1, 2, 3);'); +test2(' { let @@ = 0; { let y = 0; h(); } }'); +test2('function f() { let @@ = 0; { let y = 0; h(); } } f();'); +test2(' { for (let @@ = 0; X < 1; X++) h(); }'); +test2('function f() { for (let @@ = 0; X < 1; X++) h(); } f();'); +test2(' { (let (@@ = 0) let (y = 2, z = 3) h()); }'); +test2('function f() { return (let (@@ = 0) let (y = 2, z = 3) h()); } f();'); + +test1('(function X() { h(); })();'); +test1('(function X(a, b, c) { h(); })(1, 2, 3);'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-01.js new file mode 100644 index 00000000000..b9318f646c4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-01.js @@ -0,0 +1,19 @@ +// An Environment keeps its referent alive. + +var g = newGlobal('new-compartment'); +g.eval("function f(x) { return 2 * x; }"); +var dbg = Debugger(g); +var env; +dbg.onEnterFrame = function (frame) { env = frame.environment; }; +assertEq(g.f(22), 44); +dbg.onEnterFrame = undefined; + +assertEq(env.find("x"), env); +assertEq(env.names().join(","), "x"); + +gc(); +g.gc(g); +gc(env); + +assertEq(env.find("x"), env); +assertEq(env.names().join(","), "x"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-02.js new file mode 100644 index 00000000000..098a04c1d87 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-gc-02.js @@ -0,0 +1,28 @@ +// A closure's .environment keeps the lexical environment alive even if the closure is destroyed. + +var N = 4; +var g = newGlobal('new-compartment'); +g.eval("function add(a) { return function (b) { return eval('a + b'); }; }"); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var aw = gw.getOwnPropertyDescriptor("add").value; + +// Create N closures and collect environments. +var arr = []; +for (var i = 0; i < N; i++) + arr[i] = aw.call(null, i).return.environment; + +// Test that they work now. +function check() { + for (var i = 0; i < N; i++) { + assertEq(arr[i].find("b"), null); + assertEq(arr[i].find("a"), arr[i]); + } +} +check(); + +// Test that they work after gc. +gc(); +gc({}); +g.gc(g); +check(); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-01.js new file mode 100644 index 00000000000..f7e76d1d87e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-01.js @@ -0,0 +1,40 @@ +// The value of frame.environment is the same Environment object at different +// times within a single visit to a scope. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +g.eval("function h() { debugger; }"); +var hits, env; +dbg.onDebuggerStatement = function (hframe) { + var frame = hframe.older; + var e = frame.environment; + + // frame.environment is at least cached from one moment to the next. + assertEq(e, frame.environment); + + // frame.environment is cached from statement to statement within a call frame. + if (env === undefined) + env = e; + else + assertEq(e, env); + + hits++; +}; + +hits = 0; +env = undefined; +g.eval("function f() { (function () { var i = 0; h(); var j = 2; h(); })(); }"); +g.f(); +assertEq(hits, 2); + +hits = 0; +env = undefined; +g.eval("function f2() { { let i = 0; h(); let j = 2; h(); } }"); +g.f2(); +assertEq(hits, 2); + +hits = 0; +env = undefined; +g.eval("function f3() { { let i; for (i = 0; i < 2; i++) h(); } }"); +g.f3(); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-02.js new file mode 100644 index 00000000000..0ab4b7d216c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-02.js @@ -0,0 +1,29 @@ +// frame.environment is different for different activations of a scope. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +g.eval("function h() { debugger; }"); +var arr; +dbg.onDebuggerStatement = function (hframe) { + var e = hframe.older.environment; + assertEq(arr.indexOf(e), -1); + arr.push(e); +}; + +function test(code, expectedHits) { + arr = []; + g.eval(code); + assertEq(arr.length, expectedHits); +} + +// two separate calls to a function +test("(function () { var f = function (a) { h(); return a; }; f(1); f(2); })();", 2); + +// recursive calls to a function +test("(function f(n) { h(); return n < 2 ? 1 : n * f(n - 1); })(3);", 3); + +// separate visits to a block in the same call frame +test("(function () { for (var i = 0; i < 3; i++) { let j = i * 4; h(); }})();", 3); + +// two strict direct eval calls in the same function scope +test("(function () { 'use strict'; for (var i = 0; i < 3; i++) eval('h();'); })();", 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-03.js new file mode 100644 index 00000000000..9a020182006 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-03.js @@ -0,0 +1,109 @@ +// Two Environments nested in the same runtime scope share the correct tail of their parent chains. + +// The compiler must be allowed to elide empty scopes and so forth, so this +// test does not check the number of unshared Environments. Instead, each test +// case identifies the expected innermost shared scope by the name of a +// variable in it. + +var g = newGlobal('new-compartment'); +g.eval("function h() { debugger; }"); +var dbg = Debugger(g); +var hits, name, shared, unshared; +dbg.onDebuggerStatement = function (hframe) { + var frame = hframe.older; + + // Find name in frame.environment. + var env, child = null; + for (env = frame.environment; env !== null; env = env.parent) { + if (env.names().indexOf(name) != -1) + break; + child = env; + } + assertEq(env !== null, true, "expected '" + name + "' to be in scope"); + assertEq(env, frame.environment.find(name), + "env.find should find the same frame as the written out search"); + + if (hits === 0) { + // First hit. + shared = env; + unshared = child; + } else { + // Subsequent hit. + assertEq(env, shared, "the environment containing '" + name + "' should be shared"); + assertEq(child === null || unshared === null || unshared !== child, true, + "environments nested within the one containing '" + name + "' should not be shared"); + } + hits++; +}; + +function test(sharedName, expectedHits, code) { + hits = 0; + name = sharedName; + shared = unshared = undefined; + g.eval(code); + assertEq(hits, expectedHits); +} + +// Basic test cases. +// +// (The stray "a = b" assignments in these tests are to inhibit the flat closure +// optimization, which Environments expose. There's nothing really wrong with +// the optimization or with the debugger exposing it, but that's not what we +// want to test here.) + +test("q", 2, "var q = function (a) { h(); }; q(1); q(2);"); +test("a", 2, "q = function (a) { (function (b) { h(); a = b; })(2); h(); }; q(1);"); +test("a", 2, "q = function (a) { h(); return function (b) { h(); a = b; }; }; q(1)(2);"); +test("n", 3, "q = function (n) { for (var i = 0; i < n; i++) { let (j = i) { h(); } } }; q(3);"); + +// Don't crash in E4X filter scopes. +test("x", 2, "q = function () { var x = <>.(function (e) { h(); }(this)); }; q();"); + +// A function with long dynamic and static chains. +var N = 80; + +var code = "function f" + N + "(a" + N + ") {\neval('a0 + a1'); h();\n}\n"; +for (var i = N; --i >= 0;) { + var call = "f" + (i + 1) + "(a" + i + " - 1);\n"; + code = ("function f" + i + "(a" + i + ") {\n" + + code + + call + + "if (a" + i + " === 0) " + call + + "}\n"); +} + +g.eval(code); +test("a0", 2, "f0(0);"); +test("a17", 2, "f0(17);"); +test("a" + (N-2), 2, "f0(" + (N-2) + ");"); +test("a" + (N-1), 2, "f0(" + (N-1) + ");"); + +// A function with a short dynamic chain and a long static chain. +N = 60; + +function DeepStaticShallowDynamic(i, n) { + var code = "function f" + i + "(a" + i + ") {\n"; + if (i >= n) + code += "eval('a1 + a2'); h();\n"; + else + code += "return " + DeepStaticShallowDynamic(i+1, n) + ";\n"; + code += "}"; + return code; +} +g.eval(DeepStaticShallowDynamic(1, N)); + +function range(start, stop) { + for (var i = start; i < stop; i++) + yield i; +} + +function DSSDsplit(s) { + return ("var mid = f1" + ["(" + i + ")" for (i in range(0, s))].join("") + ";\n" + + "mid" + ["(" + i + ")" for (i in range(s, N))].join("") + ";\n" + + "mid" + ["(" + i + ")" for (i in range(s, N))].join("") + ";\n"); +} + +test("a1", 2, DSSDsplit(1)); +test("a17", 2, DSSDsplit(17)); +test("a" + (N-2), 2, DSSDsplit(N-2)); +test("a" + (N-1), 2, DSSDsplit(N-1)); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-04.js new file mode 100644 index 00000000000..a9d2b21dcd1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-identity-04.js @@ -0,0 +1,19 @@ +// Observably different visits to the same with-statement produce distinct Environments. + +var g = newGlobal('new-compartment'); +g.eval("function f(a, obj) { with (obj) return function () { return a; }; }"); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + // Even though the two visits to the with-statement have the same target + // object, Math, the environments are observably different. + var f1 = frame.eval("f(1, Math);").return; + var f2 = frame.eval("f(2, Math);").return; + assertEq(f1.environment !== f2.environment, true); + assertEq(f1.object, f2.object); + assertEq(f1.call().return, 1); + assertEq(f2.call().return, 2); + hits++; +}; +g.eval("debugger;"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-01.js new file mode 100644 index 00000000000..557e4f976ec --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-01.js @@ -0,0 +1,17 @@ +// env.names() lists nonenumerable names in with-statement environments. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var env = dbg.getNewestFrame().environment; + var names = env.names(); + assertEq(names.indexOf("a") !== -1, true); + assertEq(names.indexOf("b") !== -1, true); + assertEq(names.indexOf("isPrototypeOf") !== -1, true); + hits++; +}; +g.eval("var obj = {a: 1};\n" + + "Object.defineProperty(obj, 'b', {value: 2});\n" + + "with (obj) h();"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-02.js new file mode 100644 index 00000000000..0073cadcf85 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-names-02.js @@ -0,0 +1,15 @@ +// env.names() on object environments ignores property names that are not identifiers. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var names; +g.h = function () { + names = dbg.getNewestFrame().environment.names(); +}; +g.eval("var obj = {a: 1};\n" + + "with ({a: 1, '0xcafe': 2, ' ': 3, '': 4, '0': 5}) h();"); +assertEq(names.indexOf("a") !== -1, true); +assertEq(names.indexOf("0xcafe"), -1); +assertEq(names.indexOf(" "), -1); +assertEq(names.indexOf(""), -1); +assertEq(names.indexOf("0"), -1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-parent-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-parent-01.js new file mode 100644 index 00000000000..77e0ddb2380 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-parent-01.js @@ -0,0 +1,18 @@ +// The objects on the environment chain are all Debugger.Environment objects. +// The environment chain ends in null. + +var g = newGlobal('new-compartment') +g.eval("function f(a) { return function (b) { return function (c) { h(); return a + b + c; }; }; }"); +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var n = 0; + for (var env = dbg.getNewestFrame().environment; env !== null; env = env.parent) { + n++; + assertEq(env instanceof Debugger.Environment, true); + } + assertEq(n >= 4, true); + hits++; +}; +assertEq(g.f(5)(7)(9), 21); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Environment-type-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Environment-type-01.js new file mode 100644 index 00000000000..695f6c3e1a2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Environment-type-01.js @@ -0,0 +1,38 @@ +// env.type is 'object' in global environments and with-blocks, and 'declarative' otherwise. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +function test(code, expected) { + var actual = ''; + g.h = function () { actual += dbg.getNewestFrame().environment.type; } + g.eval(code); + assertEq(actual, expected); +} + +test("h();", 'object'); +test("(function (s) { eval(s); })('var v = h();')", 'declarative'); +test("(function (s) { h(); })();", 'declarative'); +test("{let x = 1, y = 2; h();}", 'declarative'); +test("with({x: 1, y: 2}) h();", 'object'); +test("(function (s) { with ({x: 1, y: 2}) h(); })();", 'object'); +test("let (x = 1) { h(); }", 'declarative'); +test("(let (x = 1) h());", 'declarative'); +test("for (let x = 0; x < 1; x++) h();", 'declarative'); +test("for (let x in h()) ;", 'object'); +test("for (let x in {a:1}) h();", 'declarative'); +test("try { throw new Error; } catch (x) { h(x) }", 'declarative'); +test("'use strict'; eval('var z = 1; h();');", 'declarative'); +test("for (var x in [h(m) for (m in [1])]) ;", 'declarative'); +test("for (var x in (h(m) for (m in [1]))) ;", 'declarative'); + +// Since a generator-expression is effectively a function, the innermost scope +// is a function scope, and thus declarative. Thanks to an odd design decision, +// m is already in scope at the point of the call to h(). The answer here is +// not all that important, but we shouldn't crash. +test("for (var x in (0 for (m in h()))) ;", 'declarative'); + +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.eval("h(), 2 + 2;").return, 4); +} +test("debugger;", 'object'); +test("(function f() { debugger; })();", 'declarative'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-01.js new file mode 100644 index 00000000000..b9fe4477be8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-01.js @@ -0,0 +1,31 @@ +// Test .type and .generator fields of topmost stack frame passed to onDebuggerStatement. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var expected, hits; +dbg.onDebuggerStatement = function (f) { + assertEq(Object.getPrototypeOf(f), Debugger.Frame.prototype); + assertEq(f.type, expected.type); + assertEq(f.generator, expected.generator); + assertEq(f.constructing, expected.constructing); + hits++; +}; + +function test(code, expectobj, expectedHits) { + expected = expectobj; + hits = 0; + g.evaluate(code); + assertEq(hits, arguments.length < 3 ? 1 : expectedHits); +} + +test("debugger;", {type: "global", generator: false, constructing: false}); +test("(function () { debugger; })();", {type: "call", generator: false, constructing: false}); +test("new function() { debugger; };", {type: "call", generator: false, constructing: true}); +test("new function () { (function() { debugger; })(); }", {type: "call", generator: false, constructing: false}); +test("eval('debugger;');", {type: "eval", generator: false, constructing: false}); +test("this.eval('debugger;'); // indirect eval", {type: "eval", generator: false, constructing: false}); +test("(function () { eval('debugger;'); })();", {type: "eval", generator: false, constructing: false}); +test("new function () { eval('debugger'); }", {type: "eval", generator: false, constructing: false}); +test("function gen() { debugger; yield 1; debugger; }\n" + + "for (var x in gen()) {}\n", + {type: "call", generator: true, constructing: false}, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-02.js new file mode 100644 index 00000000000..e09f1949b27 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-02.js @@ -0,0 +1,24 @@ +// When the debugger is triggered twice from the same stack frame, the same +// Debugger.Frame object is passed to the hook both times. + +var g = newGlobal('new-compartment'); +var hits, frame; +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (f) { + if (hits++ == 0) + frame = f; + else + assertEq(f, frame); +}; + +hits = 0; +g.evaluate("debugger; debugger;"); +assertEq(hits, 2); + +hits = 0; +g.evaluate("function f() { debugger; debugger; } f();"); +assertEq(hits, 2); + +hits = 0; +g.evaluate("eval('debugger; debugger;');"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-03.js new file mode 100644 index 00000000000..e63fa1b04b1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-03.js @@ -0,0 +1,19 @@ +// When the debugger is triggered from different stack frames that happen to +// occupy the same memory, it delivers different Debugger.Frame objects. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits; +var a = []; +dbg.onDebuggerStatement = function (frame) { + for (var i = 0; i < a.length; i++) + assertEq(a[i] === frame, false); + a.push(frame); + hits++; +}; + +g.eval("function f() { debugger; }"); +g.eval("function h() { debugger; f(); }"); +hits = 0; +g.eval("for (var i = 0; i < 4; i++) h();"); +assertEq(hits, 8); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-01.js new file mode 100644 index 00000000000..c41b2f66dce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-01.js @@ -0,0 +1,39 @@ +// Frame.prototype.arguments with primitive values + +var g = newGlobal('new-compartment'); +g.args = null; +var dbg = new Debugger(g); +var hits; +var v; +dbg.onDebuggerStatement = function (frame) { + hits++; + var args = frame.arguments; + assertEq(args instanceof Array, true); + assertEq(Array.isArray(args), false); + assertEq(args, frame.arguments); + assertEq(args.length, g.args.length); + for (var i = 0; i < args.length; i++) + assertEq(args[i], g.args[i]); +}; + +// no formal parameters +g.eval("function f() { debugger; }"); + +hits = 0; +g.eval("args = []; f();"); +g.eval("this.f();"); +g.eval("args = ['hello', 3.14, true, false, null, undefined]; f.apply(undefined, args);"); +g.eval("f('hello', 3.14, true, false, null, undefined);"); +g.eval("args = [-0, NaN, -1/0]; this.f(-0, NaN, -1/0);"); +assertEq(hits, 5); + +// with formal parameters +g.eval("function f(a, b) { debugger; }"); + +hits = 0; +g.eval("args = []; f();"); +g.eval("this.f();"); +g.eval("args = ['a', 'b']; f('a', 'b');"); +g.eval("this.f('a', 'b');"); +g.eval("f.bind(null, 'a')('b');"); +assertEq(hits, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-02.js new file mode 100644 index 00000000000..4c801fd06b8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-02.js @@ -0,0 +1,19 @@ +// Object arguments. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var args = frame.arguments; + assertEq(args, frame.arguments); + assertEq(args instanceof Array, true); + assertEq(args.length, 2); + assertEq(args[0] instanceof Debugger.Object, true); + assertEq(args[0].class, args[1]); + hits++; +}; + +g.eval("function f(obj, cls) { debugger; }"); +g.eval("f({}, 'Object');"); +g.eval("f(Date, 'Function');"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-03.js new file mode 100644 index 00000000000..dd304909702 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-03.js @@ -0,0 +1,34 @@ +// Destructuring arguments. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var args = frame.arguments; + assertEq(args[0], 1); + assertEq(args.length, 4); + + assertEq(args[1] instanceof Debugger.Object, true); + assertEq(args[1].class, "Array"); + var getprop = frame.eval("(function (p) { return this[p]; })").return; + assertEq(getprop instanceof Debugger.Object, true); + assertEq(getprop.apply(args[1], ["length"]).return, 2); + assertEq(getprop.apply(args[1], [0]).return, 2); + assertEq(getprop.apply(args[1], [1]).return, 3); + + assertEq(args[2] instanceof Debugger.Object, true); + assertEq(args[2].class, "Object"); + var x = getprop.apply(args[2], ["x"]).return; + assertEq(x.class, "Array"); + assertEq(getprop.apply(x, ["0"]).return, 4); + assertEq(getprop.apply(args[2], ["z"]).return, 5); + + assertEq(args[3] instanceof Debugger.Object, true); + assertEq(args[3].class, "Object"); + assertEq(getprop.apply(args[3], ["q"]).return, 6); + hits++; +}; + +g.eval("function f(a, [b, c], {x: [y], z: w}, {q}) { debugger; }"); +g.eval("f(1, [2, 3], {x: [4], z: 5}, {q: 6});"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-04.js new file mode 100644 index 00000000000..dc21e32f609 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-04.js @@ -0,0 +1,18 @@ +// frame.arguments works for all live frames + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + for (var i = 0; i <= 4; i++) { + assertEq(frame.arguments.length, 1); + assertEq(frame.arguments[0], i); + frame = frame.older; + } + assertEq(frame, null); + hits++; +}; + +g.eval("function f(n) { if (n == 0) debugger; else f(n - 1); }"); +g.f(4); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-05.js new file mode 100644 index 00000000000..44c1083d7db --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-05.js @@ -0,0 +1,19 @@ +// frame.arguments is "live" (it reflects assignments to arguments). + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var log = ''; +var args; +dbg.onDebuggerStatement = function (frame) { + if (args === undefined) + args = frame.arguments; + else + assertEq(frame.arguments, args); + log += args[0]; + assertEq(frame.eval("x = '0';").return, '0'); + log += args[0]; +}; + +g.eval("function f(x) { x = '2'; debugger; x = '3'; debugger; }"); +g.f("1"); +assertEq(log, "2030"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-06.js new file mode 100644 index 00000000000..ace009fd612 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-06.js @@ -0,0 +1,38 @@ +// Test extracting frame.arguments element getters and calling them in +// various awkward ways. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +var fframe, farguments, fgetter; +dbg.onDebuggerStatement = function (frame) { + if (hits === 0) { + fframe = frame; + farguments = frame.arguments; + fgetter = Object.getOwnPropertyDescriptor(farguments, "0").get; + assertEq(fgetter instanceof Function, true); + + // Calling the getter without an appropriate this-object + // fails, but shouldn't assert or crash. + assertThrowsInstanceOf(function () { fgetter.call(Math); }, TypeError); + } else { + // Since fframe is still on the stack, fgetter can be applied to it. + assertEq(fframe.live, true); + assertEq(fgetter.call(farguments), 100); + + // Since h was called without arguments, there is no argument 0. + assertEq(fgetter.call(frame.arguments), undefined); + } + hits++; +}; + +g.eval("function h() { debugger; }"); +g.eval("function f(x) { debugger; h(); }"); +g.f(100); +assertEq(hits, 2); + +// Now that fframe is no longer live, trying to get its arguments should throw. +assertEq(fframe.live, false); +assertThrowsInstanceOf(function () { fgetter.call(farguments); }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-07.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-07.js new file mode 100644 index 00000000000..a27361fcadf --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-arguments-07.js @@ -0,0 +1,23 @@ +// When argument[x] is assigned, where x > callee.length, frame.arguments reflects the change. + +var g = newGlobal('new-compartment'); +g.eval("function f(a, b) {\n" + + " for (var i = 0; i < arguments.length; i++)\n" + + " arguments[i] = i;\n" + + " debugger;\n" + + "}\n"); + +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var argc = frame.eval("arguments.length").return; + var args = frame.arguments; + assertEq(args.length, argc); + for (var i = 0; i < argc; i++) + assertEq(args[i], i); + hits++; +} + +g.f(9); +g.f(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-01.js new file mode 100644 index 00000000000..73b5582dc5e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-01.js @@ -0,0 +1,13 @@ +// frame.environment is a Debugger.Environment object + +var g = newGlobal('new-compartment') +var dbg = Debugger(g); +g.h = function () { + assertEq(dbg.getNewestFrame().environment instanceof Debugger.Environment, true); +}; + +g.eval("h()"); +g.evaluate("h()"); +g.eval("eval('h()')"); +g.eval("function f() { h(); }"); +g.f(); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-02.js new file mode 100644 index 00000000000..c712c919ec2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-02.js @@ -0,0 +1,12 @@ +// dbg.getNewestFrame().environment works. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +g.h = function () { + var env = dbg.getNewestFrame().environment; + assertEq(env instanceof Debugger.Environment, true); + assertEq(env.object, gw); + assertEq(env.parent, null); +}; +g.eval("h()"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-03.js new file mode 100644 index 00000000000..c7ed7eaa4ac --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-03.js @@ -0,0 +1,11 @@ +// If !frame.live, frame.environment throws. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +g.h = function () { frame = dbg.getNewestFrame(); }; +g.eval("h();"); +assertEq(frame.live, false); +assertThrowsInstanceOf(function () { frame.environment; }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-04.js new file mode 100644 index 00000000000..b41a7888f53 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-environment-04.js @@ -0,0 +1,12 @@ +// frame.environment can be called from the onEnterFrame hook. + +var g = newGlobal('new-compartment'); +g.eval("function f(x) { return 2 * x; }"); +var dbg = Debugger(g); +var hits = 0; +dbg.onEnterFrame = function (frame) { + assertEq(frame.environment.names().join(","), "x"); + hits++; +}; +assertEq(g.f(22), 44); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-01.js new file mode 100644 index 00000000000..7969292e0b6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-01.js @@ -0,0 +1,8 @@ +// simplest possible test of Debugger.Frame.prototype.eval + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var c; +dbg.onDebuggerStatement = function (frame) { c = frame.eval("2 + 2"); }; +g.eval("debugger;"); +assertEq(c.return, 4); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-02.js new file mode 100644 index 00000000000..b70f3eef391 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-02.js @@ -0,0 +1,10 @@ +// frame.eval() throws if frame is not live + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var f; +dbg.onDebuggerStatement = function (frame) { f = frame; }; +g.eval("debugger;"); +assertThrowsInstanceOf(function () { f.eval("2 + 2"); }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-03.js new file mode 100644 index 00000000000..45963224b90 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-03.js @@ -0,0 +1,19 @@ +// Test eval-ing names in a topmost script frame + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.eval("a").return, 2); + assertEq(frame.eval("c").return, 4); + var exc = frame.eval("d").throw; + assertEq(exc instanceof Debugger.Object, true); + assertEq(exc.proto, frame.eval("ReferenceError.prototype").return); + hits++; +}; +g.eval("function f(a, b) { var c = a + b; debugger; eval('debugger;'); }"); +g.eval("f(2, 2);"); +g.eval("var a = 2, b = 2, c = a + b; debugger;"); +assertEq(hits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-04.js new file mode 100644 index 00000000000..4504761c64b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-04.js @@ -0,0 +1,11 @@ +// frame.eval SyntaxErrors are reflected, not thrown + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var exc, SEp; +dbg.onDebuggerStatement = function (frame) { + exc = frame.eval("#$@!").throw; + SEp = frame.eval("SyntaxError.prototype").return; +}; +g.eval("debugger;"); +assertEq(exc.proto, SEp); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-05.js new file mode 100644 index 00000000000..7aeecd594ce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-05.js @@ -0,0 +1,14 @@ +// var declarations in strict frame.eval do not modify the frame + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var cv; +dbg.onDebuggerStatement = function (frame) { + cv = frame.eval("'use strict'; var a = 2; h();"); +}; +g.a = 1; +g.eval("function f(s) { function h() { return a; } eval(s); debugger; } "); +g.eval("f('0');"); +assertEq(cv.return, 1); +g.eval("f('var a = 3;');"); +assertEq(cv.return, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-06.js new file mode 100644 index 00000000000..67492cc48ef --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-06.js @@ -0,0 +1,19 @@ +// frame.eval throws if frame is a generator frame that isn't currently on the stack + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +g.eval("function gen(a) { debugger; yield a; }"); +g.eval("function test() { debugger; }"); +var dbg = new Debugger(g); +var genframe; +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + if (frame.callee.name == 'gen') + genframe = frame; + else + assertThrowsInstanceOf(function () { genframe.eval("a"); }, Error); + hits++; +}; +g.eval("var it = gen(42); assertEq(it.next(), 42); test();"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-07.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-07.js new file mode 100644 index 00000000000..872a05a10c3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-07.js @@ -0,0 +1,31 @@ +// test frame.eval in non-top frames + +var g = newGlobal('new-compartment'); +var N = g.N = 12; // must be even +assertEq(N % 2, 0); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var n = frame.eval("n").return; + if (n === 0) { + for (var i = 0; i <= N; i++) { + assertEq(frame.type, 'call'); + assertEq(frame.callee.name, i % 2 === 0 ? 'even' : 'odd'); + assertEq(frame.eval("n").return, i); + frame = frame.older; + } + assertEq(frame.type, 'call'); + assertEq(frame.callee.name, undefined); + frame = frame.older; + assertEq(frame.type, 'eval'); + hits++; + } +}; + +var result = g.eval("(" + function () { + function odd(n) { return n > 0 && !even(n - 1); } + function even(n) { debugger; return n == 0 || !odd(n - 1); } + return even(N); + } + ")();"); +assertEq(result, true); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-08.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-08.js new file mode 100644 index 00000000000..e0d339e45e7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-08.js @@ -0,0 +1,23 @@ +// The arguments can escape from a function via a debugging hook. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); + +// capture arguments object and test function +var args, testfn; +dbg.onDebuggerStatement = function (frame) { + args = frame.eval("arguments").return; + testfn = frame.eval("test").return; +}; +g.eval("function f() { debugger; }"); +g.eval("var test = " + function test(args) { + assertEq(args.length, 3); + assertEq(args[0], this); + assertEq(args[1], f); + assertEq(args[2].toString(), "[object Object]"); + return 42; + } + ";"); +g.eval("f(this, f, {});"); + +var cv = testfn.apply(null, [args]); +assertEq(cv.return, 42); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-09.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-09.js new file mode 100644 index 00000000000..6de540e5e6a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-09.js @@ -0,0 +1,20 @@ +// assigning to local variables in frame.eval code + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + frame.eval("outerarg = 1; outervar = 2; innerarg = 3; innervar = 4;"); +}; + +var result = g.eval("(" + function outer(outerarg) { + var outervar = 200; + function inner(innerarg) { + var innervar = 400; + debugger; + return innerarg + innervar; + } + var innersum = inner(300); + return outerarg + outervar + innersum; + } + ")(100)"); + +assertEq(result, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-10.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-10.js new file mode 100644 index 00000000000..f522c342c21 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-eval-10.js @@ -0,0 +1,13 @@ +// frame.eval returns null if the eval code fails with an uncatchable error. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + if (hits++ === 0) + assertEq(frame.eval("debugger;"), null); + else + return null; +}; +assertEq(g.eval("debugger; 'ok';"), "ok"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js new file mode 100644 index 00000000000..b8e89377828 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js @@ -0,0 +1,35 @@ +// evalWithBindings basics + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.evalWithBindings("x", {x: 2}).return, 2); + assertEq(frame.evalWithBindings("x + y", {x: 2}).return, 5); + hits++; +}; + +// in global code +g.y = 3; +g.eval("debugger;"); + +// in function code +g.y = "fail"; +g.eval("function f(y) { debugger; }"); +g.f(3); + +// in direct eval code +g.eval("function f() { var y = 3; eval('debugger;'); }"); +g.f(); + +// in strict eval code with var +g.eval("function f() { 'use strict'; eval('var y = 3; debugger;'); }"); +g.f(); + +// in a with block +g.eval("with ({y: 3}) { debugger; }"); + +// shadowing +g.eval("let (x = 50, y = 3) { debugger; }"); + +assertEq(hits, 6); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js new file mode 100644 index 00000000000..54edf1b6dad --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js @@ -0,0 +1,18 @@ +// evalWithBindings to call a method of a debuggee object +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var global = dbg.addDebuggee(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var obj = frame.arguments[0]; + var expected = frame.arguments[1]; + assertEq(frame.evalWithBindings("obj.toString()", {obj: obj}).return, expected); + hits++; +}; + +g.eval("function f(obj, expected) { debugger; }"); + +g.eval("f(new Number(-0), '0');"); +g.eval("f(new String('ok'), 'ok');"); +g.eval("f({toString: function () { return f; }}, f);"); +assertEq(hits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js new file mode 100644 index 00000000000..3dc85d86673 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js @@ -0,0 +1,16 @@ +// arguments works in evalWithBindings (it does not interpose a function scope) +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var global = dbg.addDebuggee(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var argc = frame.arguments.length; + assertEq(argc, 7); + assertEq(frame.evalWithBindings("arguments[prop]", {prop: "length"}).return, argc); + for (var i = 0; i < argc; i++) + assertEq(frame.evalWithBindings("arguments[i]", {i: i}).return, frame.arguments[i]); + hits++; +}; +g.eval("function f() { debugger; }"); +g.eval("f(undefined, -0, NaN, '\uffff', Array.prototype, Math, f);"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js new file mode 100644 index 00000000000..e084a77ffcc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js @@ -0,0 +1,17 @@ +// evalWithBindings works on non-top frames. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var f1; +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.older.evalWithBindings("q + r", {r: 3}).return, 5); + + // frame.older.older is in the same function as frame, but a different activation of it + assertEq(frame.older.older.evalWithBindings("q + r", {r: 3}).return, 6); + hits++; +}; + +g.eval("function f1(q) { if (q == 1) debugger; else f2(2); }"); +g.eval("function f2(arg) { var q = arg; f1(1); }"); +g.f1(3); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js new file mode 100644 index 00000000000..5ae28399a3e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js @@ -0,0 +1,12 @@ +// evalWithBindings code can assign to the bindings. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.evalWithBindings("for (i = 0; i < 5; i++) {} i;", {i: 10}).return, 5); + hits++; +}; + +g.eval("debugger;"); +assertEq("i" in g, false); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js new file mode 100644 index 00000000000..44d19bfd779 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js @@ -0,0 +1,9 @@ +// In evalWithBindings code, assignment to any name not in the bindings works just as in eval. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.evalWithBindings("y = z; x = w;", {z: 2, w: 3}).return, 3); +}; +g.eval("function f(x) { debugger; assertEq(x, 3); }"); +g.eval("var y = 0; f(0);"); +assertEq(g.y, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js new file mode 100644 index 00000000000..3be9833c7b1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js @@ -0,0 +1,16 @@ +// var statements in strict evalWithBindings code behave like strict eval. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.evalWithBindings("var i = a*a + b*b; i === 25;", {a: 3, b: 4}).return, true); + hits++; +}; +g.eval("'use strict'; debugger;"); +assertEq(hits, 1); +assertEq("i" in g, false); + +g.eval("function die() { throw fit; }"); +g.eval("Object.defineProperty(this, 'i', {get: die, set: die});"); +g.eval("'use strict'; debugger;"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js new file mode 100644 index 00000000000..54c2a7d9c5f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js @@ -0,0 +1,13 @@ +// evalWithBindings ignores non-enumerable and non-own properties. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.evalWithBindings("toString + constructor + length", []).return, 112233); + var obj = Object.create({constructor: "FAIL"}, {length: {value: "fail"}}); + assertEq(frame.evalWithBindings("toString + constructor + length", obj).return, 112233); + hits++; +}; +g.eval("function f() { var toString = 111111, constructor = 1111, length = 11; debugger; }"); +g.f(); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js new file mode 100644 index 00000000000..6e6f74656a7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js @@ -0,0 +1,27 @@ +// evalWithBindings code is debuggee code, so it can trip the debugger. It nests! +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var f1; +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + f1 = frame; + + // This trips the onExceptionUnwind hook. + var x = frame.evalWithBindings("wrongSpeling", {rightSpelling: 2}).throw; + + assertEq(frame.evalWithBindings("exc.name", {exc: x}).return, "ReferenceError"); + hits++; +}; +dbg.onExceptionUnwind = function (frame, exc) { + assertEq(frame !== f1, true); + + // f1's environment does not contain the binding for the first evalWithBindings call. + assertEq(f1.eval("rightSpelling").return, "dependent"); + assertEq(f1.evalWithBindings("n + rightSpelling", {n: "in"}).return, "independent"); + + // frame's environment does contain the binding. + assertEq(frame.eval("rightSpelling").return, 2); + assertEq(frame.evalWithBindings("rightSpelling + three", {three: 3}).return, 5); + hits++; +}; +g.eval("(function () { var rightSpelling = 'dependent'; debugger; })();"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js new file mode 100644 index 00000000000..99e3cf3d48a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js @@ -0,0 +1,16 @@ +// Direct eval code under evalWithBindings sees both the bindings and the enclosing scope. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var code = + "assertEq(a, 1234);\n" + + "assertEq(b, null);\n" + + "assertEq(c, 'ok');\n"; + assertEq(frame.evalWithBindings("eval(s)", {s: code, a: 1234}).return, undefined); + hits++; +}; +g.eval("function f(b) { var c = 'ok'; debugger; }"); +g.f(null); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-01.js new file mode 100644 index 00000000000..78bbd057df3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-01.js @@ -0,0 +1,20 @@ +// |jit-test| debug +// Check that {return:} resumption kills the current stack frame. + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.eval("(" + function () { + var dbg = new Debugger(debuggeeGlobal); + var prev = null; + dbg.onDebuggerStatement = function (frame) { + assertEq(frame === prev, false); + if (prev) + assertEq(prev.live, false); + prev = frame; + return {return: frame.arguments[0]}; + }; + } + ")();"); + +function f(i) { debugger; } +for (var i = 0; i < 10; i++) + assertEq(f(i), i); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-02.js new file mode 100644 index 00000000000..a39ff2231dd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-02.js @@ -0,0 +1,22 @@ +// |jit-test| debug +// Check that {throw:} resumption kills the current stack frame. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.eval("(" + function () { + var dbg = new Debugger(debuggeeGlobal); + var prev = null; + dbg.onDebuggerStatement = function (frame) { + assertEq(frame === prev, false); + if (prev) + assertEq(prev.live, false); + prev = frame; + return {throw: debuggeeGlobal.i}; + }; + } + ")();"); + +function f() { debugger; } +for (var i = 0; i < 10; i++) + assertThrowsValue(f, i); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-03.js new file mode 100644 index 00000000000..dfce82e908e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-03.js @@ -0,0 +1,50 @@ +// |jit-test| debug +// Test that we create new Debugger.Frames and reuse old ones correctly with recursion. + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.eval("(" + function () { + function id(f) { + return ("id" in f) ? f.id : (f.id = nextid++); + } + + var dbg = new Debugger(debuggeeGlobal); + dbg.onDebuggerStatement = function (frame) { + var a = []; + for (; frame; frame = frame.older) + a.push(frame); + var s = ''; + while (a.length) + s += id(a.pop()); + results.push(s); + }; + } + ")();"); + +function cons(a, b) { + debugger; + return [a, b]; +} + +function tree(n) { + if (n < 2) + return n; + return cons(tree(n - 1), tree(n - 2)); +} + +g.eval("results = []; nextid = 0;"); +debugger; +assertEq(g.results.join(","), "0"); +assertEq(g.nextid, 1); + +g.eval("results = [];"); +tree(2); +assertEq(g.results.join(","), "012"); // 0=global, 1=tree, 2=cons + +g.eval("results = []; nextid = 1;"); +tree(3); +assertEq(g.results.join(","), "0123,014"); //0=global, 1=tree(3), 2=tree(2), 3=cons, 4=cons + +g.eval("results = []; nextid = 1;"); +tree(4); +// 0=global, 1=tree(4), 2=tree(3), 3=tree(2), 4=cons, tree(1), 5=cons, 6=tree(2), 7=cons, 8=cons +assertEq(g.results.join(","), "01234,0125,0167,018"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-04.js new file mode 100644 index 00000000000..feeb53f1866 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-identity-04.js @@ -0,0 +1,20 @@ +// Test that on-stack Debugger.Frames are not GC'd even if they are only reachable +// from the js::Debugger::frames table. + +var g = newGlobal('new-compartment'); +g.eval("function f(n) { if (n) f(n - 1); debugger; }"); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + if (hits === 0) { + for (; frame; frame = frame.older) + frame.seen = true; + } else { + for (; frame; frame = frame.older) + assertEq(frame.seen, true); + } + gc(); + hits++; +}; +g.f(20); +assertEq(hits, 21); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-01.js new file mode 100644 index 00000000000..4586917aa41 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-01.js @@ -0,0 +1,42 @@ +// |jit-test| debug +// Debugger.Frame.prototype.live is true for frames on the stack and false for +// frames that have returned + +var desc = Object.getOwnPropertyDescriptor(Debugger.Frame.prototype, "live"); +assertEq(typeof desc.get, "function"); +assertEq(desc.set, undefined); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); + +var loc; + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.eval("var hits = 0;"); +g.eval("(" + function () { + var a = []; + var dbg = Debugger(debuggeeGlobal); + dbg.onDebuggerStatement = function (frame) { + var loc = debuggeeGlobal.loc; + a[loc] = frame; + for (var i = 0; i < a.length; i++) { + assertEq(a[i] === frame, i === loc); + assertEq(!!(a[i] && a[i].live), i >= loc); + } + hits++; + }; + } + ")()"); + +function f(n) { + loc = n; debugger; + if (n !== 0) { + f(n - 1); + loc = n; debugger; + eval("f(n - 1);"); + loc = n; debugger; + } +} + +f(4); +assertEq(g.hits, 16 + 8*3 + 4*3 + 2*3 + 1*3); + diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-02.js new file mode 100644 index 00000000000..68d727f5741 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-02.js @@ -0,0 +1,33 @@ +// |jit-test| debug +// Debugger.Frame.prototype.live is false for frames that have thrown or been thrown through + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.eval("var finalCheck;"); +g.eval("(" + function () { + var a = []; + var dbg = Debugger(debuggeeGlobal); + dbg.onDebuggerStatement = function (frame) { + a.push(frame); + for (var i = 0; i < a.length; i++) + assertEq(a[i].live, true); + }; + finalCheck = function (n) { + assertEq(a.length, n); + for (var i = 0; i < n; i++) + assertEq(a[i].live, false); + }; + } + ")()"); + +function f(n) { + debugger; + if (--n > 0) + f(n); + else + throw "fit"; +} + +assertThrowsValue(function () { f(10); }, "fit"); +g.finalCheck(10); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-03.js new file mode 100644 index 00000000000..70a4b98c226 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-03.js @@ -0,0 +1,28 @@ +// |jit-test| debug +// frame properties throw if !frame.live + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var f; +Debugger(g).onDebuggerStatement = function (frame) { + assertEq(frame.live, true); + assertEq(frame.type, "call"); + assertEq(frame.this instanceof Object, true); + assertEq(frame.older instanceof Debugger.Frame, true); + assertEq(frame.callee instanceof Debugger.Object, true); + assertEq(frame.generator, false); + assertEq(frame.constructing, false); + assertEq(frame.arguments.length, 0); + f = frame; +}; + +g.eval("(function () { debugger; }).call({});"); +assertEq(f.live, false); +assertThrowsInstanceOf(function () { f.type; }, Error); +assertThrowsInstanceOf(function () { f.this; }, Error); +assertThrowsInstanceOf(function () { f.older; }, Error); +assertThrowsInstanceOf(function () { f.callee; }, Error); +assertThrowsInstanceOf(function () { f.generator; }, Error); +assertThrowsInstanceOf(function () { f.constructing; }, Error); +assertThrowsInstanceOf(function () { f.arguments; }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-04.js new file mode 100644 index 00000000000..7ca259bdf8e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-04.js @@ -0,0 +1,27 @@ +// frame.live is false for frames discarded during uncatchable error unwinding. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +var snapshot; +dbg.onDebuggerStatement = function (frame) { + var stack = []; + for (var f = frame; f; f = f.older) { + if (f.type === "call" && f.script !== null) + stack.push(f); + } + snapshot = stack; + if (hits++ === 0) + assertEq(frame.eval("x();"), null); + else + return null; +}; + +g.eval("function z() { debugger; }"); +g.eval("function y() { z(); }"); +g.eval("function x() { y(); }"); +assertEq(g.eval("debugger; 'ok';"), "ok"); +assertEq(hits, 2); +assertEq(snapshot.length, 3); +for (var i = 0; i < snapshot.length; i++) + assertEq(snapshot[i].live, false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-05.js new file mode 100644 index 00000000000..d48ad013e3c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-live-05.js @@ -0,0 +1,29 @@ +// frame.live is false for frames removed after their compartments stopped being debuggees. + +var g1 = newGlobal('new-compartment'); +var g2 = newGlobal('new-compartment'); +var dbg = Debugger(g1, g2); +var hits = 0; +var snapshot = []; +dbg.onDebuggerStatement = function (frame) { + if (hits++ === 0) { + assertEq(frame.eval("x();"), null); + } else { + for (var f = frame; f; f = f.older) { + if (f.type === "call" && f.script !== null) + snapshot.push(f); + } + dbg.removeDebuggee(g2); + return null; + } +}; + +g1.eval("function z() { debugger; }"); +g2.z = g1.z; +g2.eval("function y() { z(); }"); +g2.eval("function x() { y(); }"); +assertEq(g2.eval("debugger; 'ok';"), "ok"); +assertEq(hits, 2); +assertEq(snapshot.length, 3); +for (var i = 0; i < snapshot.length; i++) + assertEq(snapshot[i].live, false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-01.js new file mode 100644 index 00000000000..602073b70cb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-01.js @@ -0,0 +1,11 @@ +// frame.offset throws if !frame.live. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var f; +dbg.onDebuggerStatement = function (frame) { f = frame; }; +g.eval("debugger;"); +assertEq(f.live, false); +assertThrowsInstanceOf(function () { f.offset; }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-02.js new file mode 100644 index 00000000000..68ec48fadd0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-offset-02.js @@ -0,0 +1,16 @@ +// frame.offset gives different values at different points in a script. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var s = undefined, a = [] +dbg.onDebuggerStatement = function (frame) { + if (s === undefined) + s = frame.script; + else + assertEq(s, frame.script); + assertEq(frame.offset !== undefined, true); + assertEq(a.indexOf(frame.offset), -1); + a.push(frame.offset); +}; +g.eval("debugger; debugger; debugger;"); +assertEq(a.length, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-older-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-older-01.js new file mode 100644 index 00000000000..f37df2ee6ee --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-older-01.js @@ -0,0 +1,20 @@ +// |jit-test| debug +// Basic call chain. + +var g = newGlobal('new-compartment'); +var result = null; +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + var a = []; + assertEq(frame === frame.older, false); + for (; frame; frame = frame.older) + a.push(frame.type === 'call' ? frame.callee.name : frame.type); + a.reverse(); + result = a.join(", "); +}; + +g.eval("function first() { return second(); }"); +g.eval("function second() { return eval('third()'); }"); +g.eval("function third() { debugger; }"); +g.evaluate("first();"); +assertEq(result, "global, first, second, eval, third"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-01.js new file mode 100644 index 00000000000..d93fe4b818a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-01.js @@ -0,0 +1,24 @@ +// Simple Debugger.Frame.prototype.onStep test. +// Test that onStep fires often enough to see all four values of a. + +var g = newGlobal('new-compartment'); +g.a = 0; +g.eval("function f() {\n" + + " a += 2;\n" + + " a += 2;\n" + + " a += 2;\n" + + " return a;\n" + + "}\n"); + +var dbg = Debugger(g); +var seen = [0, 0, 0, 0, 0, 0, 0]; +dbg.onEnterFrame = function (frame) { + frame.onStep = function () { + assertEq(arguments.length, 0); + assertEq(this, frame); + seen[g.a] = 1; + }; +} + +g.f(); +assertEq(seen.join(""), "1010101"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-02.js new file mode 100644 index 00000000000..84b99cb3891 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-02.js @@ -0,0 +1,27 @@ +// Setting frame.onStep to undefined turns off single-stepping. + +var g = newGlobal('new-compartment'); +g.a = 0; +g.eval("function f() {\n" + + " a++;\n" + + " a++;\n" + + " a++;\n" + + " a++;\n" + + " return a;\n" + + "}\n"); + +var dbg = Debugger(g); +var seen = [0, 0, 0, 0, 0]; +dbg.onEnterFrame = function (frame) { + seen[g.a] = 1; + frame.onStep = function () { + seen[g.a] = 1; + if (g.a === 2) { + frame.onStep = undefined; + assertEq(frame.onStep, undefined); + } + }; +} + +g.f(); +assertEq(seen.join(","), "1,1,1,0,0"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-03.js new file mode 100644 index 00000000000..c3d59331e79 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-03.js @@ -0,0 +1,28 @@ +// Setting onStep does not affect later calls to the same function. +// (onStep is per-frame, not per-function.) + +var g = newGlobal('new-compartment'); +g.a = 1; +g.eval("function f(a) {\n" + + " var x = 2 * a;\n" + + " return x * x;\n" + + "}\n"); + +var dbg = Debugger(g); +var log = ''; +dbg.onEnterFrame = function (frame) { + log += '+'; + frame.onStep = function () { + if (log.charAt(log.length - 1) != 's') + log += 's'; + }; +}; + +g.f(1); +log += '|'; +g.f(2); +log += '|'; +dbg.onEnterFrame = undefined; +g.f(3); + +assertEq(log, '+s|+s|'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-04.js new file mode 100644 index 00000000000..d0d251f1693 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-04.js @@ -0,0 +1,34 @@ +// When a recursive function has many frames on the stack, onStep may be set or +// not independently on each frame. + +var g = newGlobal('new-compartment'); +g.eval("function f(x) {\n" + + " if (x > 0)\n" + + " f(x - 1);\n" + + " else\n" + + " debugger;\n" + + " return x;\n" + + "}"); + +var dbg = Debugger(g); +var seen = [0, 0, 0, 0, 0, 0, 0, 0]; +function step() { + seen[this.arguments[0]] = 1; +} +dbg.onEnterFrame = function (frame) { + // Turn on stepping for even-numbered frames. + var x = frame.arguments[0]; + if (x % 2 === 0) + frame.onStep = step; +}; +dbg.onDebuggerStatement = function (frame) { + // This is called with 8 call frames on the stack, 7 down to 0. + // At this point we should have seen all the even-numbered frames. + assertEq(seen.join(""), "10101010"); + + // Now reset seen to see which frames fire onStep on the way out. + seen = [0, 0, 0, 0, 0, 0, 0, 0]; +}; + +g.f(7); +assertEq(seen.join(""), "10101010"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-05.js new file mode 100644 index 00000000000..36e8b94a976 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-05.js @@ -0,0 +1,14 @@ +// Upon returning to a frame with an onStep hook, the hook is called before the +// next line. + +var g = newGlobal('new-compartment'); +g.log = ''; +g.eval("function f() { debugger; }"); + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + frame.older.onStep = function () { g.log += 's'; }; +}; +g.eval("f();\n" + + "log += 'x';\n"); +assertEq(g.log.charAt(0), 's'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-06.js new file mode 100644 index 00000000000..379a4dd95c6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-06.js @@ -0,0 +1,66 @@ +// After returning from an implicit toString call, the calling frame's onStep +// hook fires. + +var g = newGlobal('new-compartment'); +g.eval("var originalX = {toString: function () { debugger; log += 'x'; return 1; }};\n"); + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + g.log += 'd'; + frame.older.onStep = function () { + if (!g.log.match(/[sy]$/)) + g.log += 's'; + }; +}; + +// expr is an expression that will trigger an implicit toString call. +function check(expr) { + g.log = ''; + g.x = g.originalX; + g.eval(expr + ";\n" + + "log += 'y';\n"); + assertEq(g.log, 'dxsy'); +} + +check("'' + x"); +check("0 + x"); +check("0 - x"); +check("0 * x"); +check("0 / x"); +check("0 % x"); +check("+x"); +check("x in {}"); +check("x++"); +check("++x"); +check("x--"); +check("--x"); +check("x < 0"); +check("x > 0"); +check("x >= 0"); +check("x <= 0"); +check("x == 0"); +check("x != 0"); +check("x & 1"); +check("x | 1"); +check("x ^ 1"); +check("~x"); +check("x << 1"); +check("x >> 1"); +check("x >>> 1"); + +g.eval("function lastStep() { throw StopIteration; }"); +g.eval("function emptyIterator() { debugger; log += 'x'; return { next: lastStep }; }"); +g.eval("var customEmptyIterator = { __iterator__: emptyIterator };"); +g.log = ''; +g.eval("for (i in customEmptyIterator);\n" + + "log += 'y';\n"); +assertEq(g.log, 'dxsy'); + +g.eval("var getter = { get x() { debugger; return log += 'x'; } }"); +check("getter.x"); + +g.eval("var setter = { set x(v) { debugger; return log += 'x'; } }"); +check("setter.x = 1"); + +g.eval("Object.defineProperty(this, 'thisgetter', { get: function() { debugger; log += 'x'; }});"); +check("thisgetter"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-07.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-07.js new file mode 100644 index 00000000000..f6002c25af9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-07.js @@ -0,0 +1,23 @@ +// The tracejit does not interfere with frame.onStep. +// +// The function f() writes 'L' to the log in a loop. If we enable stepping and +// write an 's' each time frame.onStep is called, any two Ls should have at +// least one 's' between them. + +var g = newGlobal('new-compartment'); +g.N = 11; +g.log = ''; +g.eval("function f() {\n" + + " for (var i = 0; i <= N; i++)\n" + + " log += 'L';\n" + + "}\n"); +g.f(); +assertEq(/LL/.exec(g.log) !== null, true); + +var dbg = Debugger(g); +dbg.onEnterFrame = function (frame) { + frame.onStep = function () { g.log += 's'; }; +}; +g.log = ''; +g.f(); +assertEq(/LL/.exec(g.log), null); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-08.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-08.js new file mode 100644 index 00000000000..b904140199f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-08.js @@ -0,0 +1,29 @@ +// frame.onStep can coexist with breakpoints. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var log = ''; +dbg.onEnterFrame = function (frame) { + var handler = {hit: function () { log += 'B'; }}; + var lines = frame.script.getAllOffsets(); + for (var line in lines) { + line = Number(line); + var offs = lines[line]; + for (var i = 0; i < offs.length; i++) + frame.script.setBreakpoint(offs[i], handler); + } + + frame.onStep = function () { log += 's'; }; +}; + +g.eval("one = 1;\n" + + "two = 2;\n" + + "three = 3;\n" + + "four = 4;\n"); +assertEq(g.four, 4); + +// Breakpoints hit on all four lines. +assertEq(log.replace(/[^B]/g, ''), 'BBBB'); + +// onStep was called between each pair of breakpoints. +assertEq(/BB/.exec(log), null); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-09.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-09.js new file mode 100644 index 00000000000..3646a2f8f3e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-09.js @@ -0,0 +1,24 @@ +// After an implicit toString call throws an exception, the calling frame's +// onStep hook fires. + +var g = newGlobal('new-compartment'); +g.eval("var x = {toString: function () { debugger; log += 'x'; throw 'mud'; }};"); + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + g.log += 'd'; + frame.older.onStep = function () { + if (!g.log.match(/[sy]$/)) + g.log += 's'; + }; +}; + +g.log = ''; +g.eval("try { x + ''; } catch (x) { }\n" + + "log += 'y';\n"); +assertEq(g.log, "dxsy"); + +g.log = ''; +g.eval("try { '' + x; } catch (x) { }\n" + + "log += 'y';\n"); +assertEq(g.log, "dxsy"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-10.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-10.js new file mode 100644 index 00000000000..a2bccbfa221 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-10.js @@ -0,0 +1,28 @@ +// Throwing and catching an error in an onStep handler shouldn't interfere +// with throwing and catching in the debuggee. + +var g = newGlobal('new-compartment'); +g.eval("function f() { debugger; throw 'mud'; }"); + +var dbg = Debugger(g); +var stepped = false; +dbg.onDebuggerStatement = function (frame) { + frame.older.onStep = function () { + stepped = true; + try { + throw 'snow'; + } catch (x) { + assertEq(x, 'snow'); + } + }; +}; + +stepped = false; +g.eval("var caught;\n" + + "try {\n" + + " f();\n" + + "} catch (x) {\n" + + " caught = x;\n" + + "}\n"); +assertEq(stepped, true); +assertEq(g.caught, 'mud'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js new file mode 100644 index 00000000000..2d6a65f764a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js @@ -0,0 +1,78 @@ +// Test that a frame's onStep handler gets called at least once on each line of a function. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); + +// When we hit a 'debugger' statement, set offsets to the frame's script's +// table of line offsets --- a sparse array indexed by line number. Begin +// single-stepping the current frame; for each source line we hit, delete +// the line's entry in offsets. Thus, at the end, offsets is an array with +// an element for each line we did not reach. +var doSingleStep = true; +var offsets; +dbg.onDebuggerStatement = function (frame) { + var script = frame.script; + offsets = script.getAllOffsets(); + print("debugger line: " + script.getOffsetLine(frame.offset)); + print("original lines: " + uneval(Object.keys(offsets))); + if (doSingleStep) { + frame.onStep = function onStepHandler() { + var line = script.getOffsetLine(this.offset); + delete offsets[line]; + }; + } +}; + +g.eval( + 'function t(a, b, c) { \n' + + ' debugger; \n' + + ' var x = a; \n' + + ' x += b; \n' + + ' if (x < 10) \n' + + ' x -= c; \n' + + ' return x; \n' + + '} \n' + ); + +// This should stop at every line but the first of the function. +g.eval('t(1,2,3)'); +assertEq(Object.keys(offsets).length, 1); + +// This should stop at every line but the first of the function, and the +// body of the 'if'. +g.eval('t(10,20,30)'); +assertEq(Object.keys(offsets).length, 2); + +// This shouldn't stop at all. It's the frame that's in single-step mode, +// not the script, so the prior execution of t in single-step mode should +// have no effect on this one. +doSingleStep = false; +g.eval('t(0, 0, 0)'); +assertEq(Object.keys(offsets).length, 6); +doSingleStep = true; + +// Single-step in an eval frame. This should reach every line but the +// first. +g.eval( + 'debugger; \n' + + 'var a=1, b=2, c=3; \n' + + 'var x = a; \n' + + 'x += b; \n' + + 'if (x < 10) \n' + + ' x -= c; \n' + ); +print("final lines: " + uneval(Object.keys(offsets))); +assertEq(Object.keys(offsets).length, 1); + +// Single-step in a global code frame. This should reach every line but the +// first. +g.evaluate( + 'debugger; \n' + + 'var a=1, b=2, c=3; \n' + + 'var x = a; \n' + + 'x += b; \n' + + 'if (x < 10) \n' + + ' x -= c; \n' + ); +print("final lines: " + uneval(Object.keys(offsets))); +assertEq(Object.keys(offsets).length, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-01.js new file mode 100644 index 00000000000..71b2d4f4fd3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-01.js @@ -0,0 +1,14 @@ +// If frame.onStep returns {return:val}, the frame returns. + +var g = newGlobal('new-compartment'); +g.eval("function f(x) {\n" + + " var a = x * x;\n" + + " return a;\n" + + "}\n"); + +var dbg = Debugger(g); +dbg.onEnterFrame = function (frame) { + frame.onStep = function () { return {return: "pass"}; }; +}; + +assertEq(g.f(4), "pass"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-02.js new file mode 100644 index 00000000000..f55726b58a4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-02.js @@ -0,0 +1,17 @@ +// If frame.onStep returns {throw:}, an exception is thrown in the debuggee. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +g.eval("function h() { debugger; }\n" + + "function f() {\n" + + " h();\n" + + " return 'fail';\n" + + "}\n"); + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + frame.older.onStep = function () { return {throw: "pass"}; }; +}; + +assertThrowsValue(g.f, "pass"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-03.js new file mode 100644 index 00000000000..ceca07bf39b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-03.js @@ -0,0 +1,19 @@ +// If frame.onStep returns null, the debuggee terminates. + +var g = newGlobal('new-compartment'); +g.eval("function h() { debugger; }"); + +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + if (hits == 1) { + var rv = frame.eval("h();\n" + + "throw 'fail';\n"); + assertEq(rv, null); + } else { + frame.older.onStep = function () { return null; }; + } +}; +g.h(); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-04.js new file mode 100644 index 00000000000..b1f574301e2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-onStep-resumption-04.js @@ -0,0 +1,31 @@ +// If frame.onStep returns null, debuggee catch and finally blocks are skipped. + +var g = newGlobal('new-compartment'); +g.eval("function h() { debugger; }"); + +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + if (hits == 1) { + var rv = frame.eval("try {\n" + + " h();\n" + + " throw 'fail';\n" + + "} catch (exc) {\n" + + " caught = exc;\n" + + "} finally {\n" + + " finallyHit = true;\n" + + "}\n"); + assertEq(rv, null); + } else { + frame.older.onStep = function () { + this.onStep = undefined; + return null; + }; + } +}; + +g.h(); +assertEq(hits, 2); +assertEq("caught" in g, false); +assertEq("finallyHit" in g, false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-01.js new file mode 100644 index 00000000000..2b5f73ebe92 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-01.js @@ -0,0 +1,26 @@ +// |jit-test| debug +// Frame.prototype.script for eval frames. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); + +// Apply |f| to each frame that is |skip| frames up from each frame that +// executes a 'debugger' statement when evaluating |code| in the global g. +function ApplyToFrameScript(code, skip, f) { + dbg.onDebuggerStatement = function (frame) { + while (skip-- > 0) + frame = frame.older; + assertEq(frame.type, "eval"); + f(frame.script); + }; + g.eval(code); +} + +ApplyToFrameScript('debugger;', 0, + function (script) { + assertEq(script instanceof Debugger.Script, true); + }); +ApplyToFrameScript("(function () { eval('debugger;'); })();", 0, + function (script) { + assertEq(script instanceof Debugger.Script, true); + }); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-02.js new file mode 100644 index 00000000000..a911a6ec8ab --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-02.js @@ -0,0 +1,28 @@ +// |jit-test| debug +// Frame.prototype.script for call frames. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); + +// Apply |f| to each frame that is |skip| frames up from each frame that +// executes a 'debugger' statement when evaluating |code| in the global g. +function ApplyToFrameScript(code, skip, f) { + dbg.onDebuggerStatement = function (frame) { + while (skip-- > 0) + frame = frame.older; + assertEq(frame.type, "call"); + f(frame.script); + }; + g.eval(code); +} + +ApplyToFrameScript('(function () { debugger; })();', 0, + function (script) { + assertEq(script instanceof Debugger.Script, true); + }); + +// This would be nice, once we can get host call frames: +// ApplyToFrameScript("(function () { debugger; }).call(null);", 1, +// function (script) { +// assertEq(script, null); +// }); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-03.js new file mode 100644 index 00000000000..0342d6a2fbb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-script-03.js @@ -0,0 +1,8 @@ +// frame.script can create a Debugger.Script for a JS_Evaluate* script. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var s; +dbg.onDebuggerStatement = function (frame) { s = frame.script; }; +g.evaluate("debugger;"); +assertEq(s instanceof Debugger.Script, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-01.js new file mode 100644 index 00000000000..90aaa108803 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-01.js @@ -0,0 +1,22 @@ +// Frame.prototype.this in strict-mode functions, with primitive values + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + assertEq(frame.this, g.v); +}; + +g.eval("function f() { 'use strict'; debugger; }"); + +g.eval("Boolean.prototype.f = f; v = true; v.f();"); +g.eval("f.call(v);"); +g.eval("Number.prototype.f = f; v = 3.14; v.f();"); +g.eval("f.call(v);"); +g.eval("String.prototype.f = f; v = 'hello'; v.f();"); +g.eval("f.call(v);"); +g.eval("v = undefined; f.call(v);"); +g.eval("v = null; f.call(v);"); + +assertEq(hits, 8); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-02.js new file mode 100644 index 00000000000..7a8258481bd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-02.js @@ -0,0 +1,17 @@ +// Frame.prototype.this in strict direct eval frames + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + assertEq(frame.this, g.v); +}; + +g.eval("function f() { 'use strict'; eval('debugger;'); }"); + +g.eval("Boolean.prototype.f = f; v = true; v.f();"); +g.eval("f.call(v);"); +g.eval("v = null; f.call(v);"); + +assertEq(hits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-03.js new file mode 100644 index 00000000000..4a2345332db --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-03.js @@ -0,0 +1,27 @@ +// Frame.prototype.this in non-strict-mode functions, with primitive values + +function classOf(obj) { + return Object.prototype.toString.call(obj).match(/^\[object (.*)\]$/)[1]; +} + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + assertEq(frame.this instanceof Debugger.Object, true); + assertEq(frame.this.class, g.v == null ? classOf(g) : classOf(Object(g.v))); +}; + +g.eval("function f() { debugger; }"); + +g.eval("Boolean.prototype.f = f; v = true; v.f();"); +g.eval("f.call(v);"); +g.eval("Number.prototype.f = f; v = 3.14; v.f();"); +g.eval("f.call(v);"); +g.eval("String.prototype.f = f; v = 'hello'; v.f();"); +g.eval("f.call(v);"); +g.eval("v = undefined; f.call(v);"); +g.eval("v = null; f.call(v);"); + +assertEq(hits, 8); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-04.js new file mode 100644 index 00000000000..cdc8979a699 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Frame-this-04.js @@ -0,0 +1,25 @@ +// Debugger.Frame.prototype.this in functions, with object values + +function classOf(obj) { + return Object.prototype.toString.call(obj).match(/^\[object (.*)\]$/)[1]; +} + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + hits++; + assertEq(frame.this instanceof Debugger.Object, true); + assertEq(frame.this.class, classOf(Object(g.v))); +}; + +g.eval("function f() { debugger; }"); + +g.eval("v = {}; f.call(v);"); +g.eval("v.f = f; v.f();"); +g.eval("v = new Date; f.call(v);"); +g.eval("v.f = f; v.f();"); +g.eval("v = []; f.call(v);"); +g.eval("Object.prototype.f = f; v.f();"); +g.eval("v = this; f();"); +assertEq(hits, 7); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-01.js new file mode 100644 index 00000000000..9c1dbd7ecc0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-01.js @@ -0,0 +1,17 @@ +// Debugger.Object basics + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0], frame.callee); + assertEq(Object.getPrototypeOf(frame.arguments[0]), Debugger.Object.prototype); + assertEq(frame.arguments[0] instanceof Debugger.Object, true); + assertEq(frame.arguments[0] !== frame.arguments[1], true); + assertEq(Object.getPrototypeOf(frame.arguments[1]), Debugger.Object.prototype); + assertEq(frame.arguments[1] instanceof Debugger.Object, true); + hits++; +}; + +g.eval("var obj = {}; function f(a, b) { debugger; } f(f, obj);"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-02.js new file mode 100644 index 00000000000..70a663ffed6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-02.js @@ -0,0 +1,13 @@ +// Debugger.Object referents can be transparent wrappers of objects in the debugger compartment. + +var g = newGlobal('new-compartment'); +g.f = function (a, b) { return a + "/" + b; }; +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var f = frame.eval("f").return; + assertEq(f.call(null, "a", "b").return, "a/b"); + hits++; +}; +g.eval("debugger;"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-01.js new file mode 100644 index 00000000000..7058cdb9144 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-01.js @@ -0,0 +1,59 @@ +// tests calling script functions via Debugger.Object.prototype.apply/call + +load(libdir + "asserts.js"); + +var g = newGlobal("new-compartment"); +g.eval("function f() { debugger; }"); +var dbg = new Debugger(g); + +var hits = 0; +function test(usingApply) { + dbg.onDebuggerStatement = function (frame) { + var fn = frame.arguments[0]; + var cv = usingApply ? fn.apply(null, [9, 16]) : fn.call(null, 9, 16); + assertEq(Object.keys(cv).join(","), "return"); + assertEq(Object.getPrototypeOf(cv), Object.prototype); + assertEq(cv.return, 25); + + cv = usingApply ? fn.apply(null, ["hello ", "world"]) : fn.call(null, "hello ", "world"); + assertEq(Object.keys(cv).join(","), "return"); + assertEq(cv.return, "hello world"); + + // Handle more or less arguments. + assertEq((usingApply ? fn.apply(null, [1, 5, 100]) : fn.call(null, 1, 5, 100)).return, 6); + assertEq((usingApply ? fn.apply(null, []) : fn.call(null)).return, NaN); + assertEq((usingApply ? fn.apply() : fn.call()).return, NaN); + + // Throw if a this-value or argument is an object but not a Debugger.Object. + assertThrowsInstanceOf(function () { usingApply ? fn.apply({}, []) : fn.call({}); }, + TypeError); + assertThrowsInstanceOf(function () { usingApply ? fn.apply(null, [{}]) : fn.call(null, {}); }, + TypeError); + hits++; + }; + g.eval("f(function (a, b) { return a + b; });"); + + // The callee receives the right arguments even if more arguments are provided + // than the callee's .length. + dbg.onDebuggerStatement = function (frame) { + assertEq((usingApply ? frame.arguments[0].apply(null, ['one', 'two']) + : frame.arguments[0].call(null, 'one', 'two')).return, + 2); + hits++; + }; + g.eval("f(function () { return arguments.length; });"); + + // Exceptions are reported as {throw:} completion values. + dbg.onDebuggerStatement = function (frame) { + var lose = frame.arguments[0]; + var cv = usingApply ? lose.apply(null, []) : lose.call(null); + assertEq(Object.keys(cv).join(","), "throw"); + assertEq(cv.throw, frame.callee); + hits++; + }; + g.eval("f(function lose() { throw f; });"); +} + +test(true); +test(false); +assertEq(hits, 6); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-02.js new file mode 100644 index 00000000000..87481e4a644 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-02.js @@ -0,0 +1,52 @@ +// tests calling native functions via Debugger.Object.prototype.apply/call + +load(libdir + "asserts.js"); + +var g = newGlobal("new-compartment"); +g.eval("function f() { debugger; }"); +var dbg = new Debugger(g); + +function test(usingApply) { + dbg.onDebuggerStatement = function (frame) { + var max = frame.arguments[0]; + var cv = usingApply ? max.apply(null, [9, 16]) : max.call(null, 9, 16); + assertEq(cv.return, 16); + + cv = usingApply ? max.apply() : max.call(); + assertEq(cv.return, -1/0); + + cv = usingApply ? max.apply(null, [2, 5, 3, 8, 1, 9, 4, 6, 7]) + : max.call(null, 2, 5, 3, 8, 1, 9, 4, 6, 7); + assertEq(cv.return, 9); + + // second argument to apply must be an array + assertThrowsInstanceOf(function () { max.apply(null, 12); }, TypeError); + }; + g.eval("f(Math.max);"); + + dbg.onDebuggerStatement = function (frame) { + var push = frame.arguments[0]; + var arr = frame.arguments[1]; + var cv; + + cv = usingApply ? push.apply(arr, [0, 1, 2]) : push.call(arr, 0, 1, 2); + assertEq(cv.return, 3); + + cv = usingApply ? push.apply(arr, [arr]) : push.call(arr, arr); + assertEq(cv.return, 4); + + cv = usingApply ? push.apply(arr) : push.call(arr); + assertEq(cv.return, 4); + + // you can apply Array.prototype.push to a string; it does ToObject on it. + cv = usingApply ? push.apply("hello", ["world"]) : push.call("hello", "world"); + assertEq(cv.return, 6); + }; + g.eval("var a = []; f(Array.prototype.push, a);"); + assertEq(g.a.length, 4); + assertEq(g.a.slice(0, 3).join(","), "0,1,2"); + assertEq(g.a[3], g.a); +} + +test(true); +test(false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-03.js new file mode 100644 index 00000000000..61e9ce0aa01 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-03.js @@ -0,0 +1,21 @@ +// reentering the debugger several times via onDebuggerStatement and apply/call on a single stack + +var g = newGlobal("new-compartment"); +var dbg = Debugger(g); + +function test(usingApply) { + dbg.onDebuggerStatement = function (frame) { + var n = frame.arguments[0]; + if (n > 1) { + var result = usingApply ? frame.callee.apply(null, [n - 1]) + : frame.callee.call(null, n - 1); + result.return *= n; + return result; + } + }; + g.eval("function fac(n) { debugger; return 1; }"); + assertEq(g.fac(5), 5 * 4 * 3 * 2 * 1); +} + +test(true); +test(false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-04.js new file mode 100644 index 00000000000..e7ed8188807 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-apply-04.js @@ -0,0 +1,15 @@ +// Debugger.Object.prototype.apply/call works with function proxies + +var g = newGlobal('new-compartment'); +g.eval("function f() { debugger; }"); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var proxy = frame.arguments[0]; + assertEq(proxy.name, undefined); + assertEq(proxy.apply(null, [33]).return, 34); + assertEq(proxy.call(null, 33).return, 34); + hits++; +}; +g.eval("f(Proxy.createFunction({}, function (arg) { return arg + 1; }));"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-callable.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-callable.js new file mode 100644 index 00000000000..625de33b5c1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-callable.js @@ -0,0 +1,18 @@ +// Test Debugger.Object.prototype.callable. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0].callable, frame.arguments[1]); + hits++; +}; + +g.eval("function f(obj, iscallable) { debugger; }"); + +g.eval("f({}, false);"); +g.eval("f(Function.prototype, true);"); +g.eval("f(f, true);"); +g.eval("f(Proxy.create({}), false);"); +g.eval("f(Proxy.createFunction(f, f), true);"); +assertEq(hits, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-class.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-class.js new file mode 100644 index 00000000000..a8baa1622ee --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-class.js @@ -0,0 +1,15 @@ +// Basic tests for Debugger.Object.prototype.class. +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var arr = frame.arguments; + assertEq(arr[0].class, "Object"); + assertEq(arr[1].class, "Array"); + assertEq(arr[2].class, "Function"); + assertEq(arr[3].class, "Date"); + assertEq(arr[4].class, "Proxy"); + hits++; +}; +g.eval("(function () { debugger; })(Object.prototype, [], eval, new Date, Proxy.create({}));"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-01.js new file mode 100644 index 00000000000..be286c07c0b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-01.js @@ -0,0 +1,46 @@ +// Debug.Object.prototype.defineProperties. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +var descProps = ['configurable', 'enumerable', 'writable', 'value', 'get', 'set']; +function test(objexpr, descs) { + g.eval("obj = (" + objexpr + ");"); + var gobjw = gw.getOwnPropertyDescriptor("obj").value; + gobjw.defineProperties(descs); + + var indirectEval = eval; + var obj = indirectEval("(" + objexpr + ");"); + Object.defineProperties(obj, descs); + + var ids = Object.keys(descs); + for (var i = 0; i < ids.length; i++) { + var actual = gobjw.getOwnPropertyDescriptor(ids[i]); + var expected = Object.getOwnPropertyDescriptor(obj, ids[i]); + assertEq(Object.getPrototypeOf(actual), Object.prototype); + assertEq(actual.configurable, expected.configurable); + assertEq(actual.enumerable, expected.enumerable); + for (var j = 0; j < descProps; j++) { + var prop = descProps[j]; + assertEq(prop in actual, prop in expected); + assertEq(actual[prop], expected[prop]); + } + } +} + +test("{}", {}); +test("/abc/", {}); + +g.eval("var aglobal = newGlobal('same-compartment');"); +var aglobal = newGlobal('same-compartment'); +test("aglobal", {}); + +var adescs = {a: {enumerable: true, writable: true, value: 0}}; +test("{}", adescs); +test("{a: 1}", adescs); + +var arrdescs = [{value: 'a'}, {value: 'b'}, , {value: 'd'}]; +test("{}", arrdescs); +test("[]", arrdescs); +test("[0, 1, 2, 3]", arrdescs); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-02.js new file mode 100644 index 00000000000..b7ce6aab525 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-02.js @@ -0,0 +1,32 @@ +// Exceptions thrown by obj.defineProperties are copied into the debugger compartment. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +function test(objexpr, descs) { + var exca, excb; + + g.eval("obj = (" + objexpr + ");"); + var gobjw = gw.getOwnPropertyDescriptor("obj").value; + try { + gobjw.defineProperties(descs); + } catch (exc) { + exca = exc; + } + + var indirectEval = eval; + var obj = indirectEval("(" + objexpr + ");"); + try { + Object.defineProperties(obj, descs); + } catch (exc) { + excb = exc; + } + + assertEq(Object.getPrototypeOf(exca), Object.getPrototypeOf(excb)); + assertEq(exca.message, excb.message); + assertEq(typeof exca.fileName, "string"); + assertEq(typeof exca.stack, "string"); +} + +test("Object.create(null, {p: {value: 1}})", {p: {value: 2}}); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-03.js new file mode 100644 index 00000000000..fe91c011a0f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperties-03.js @@ -0,0 +1,20 @@ +// obj.defineProperties can define accessor properties. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +g.value = undefined; +g.eval("function gf() { return 12; }\n" + + "function sf(v) { value = v; }\n"); +var gfw = gw.getOwnPropertyDescriptor("gf").value; +var sfw = gw.getOwnPropertyDescriptor("sf").value; +gw.defineProperties({x: {configurable: true, get: gfw, set: sfw}}); +assertEq(g.x, 12); +g.x = 'ok'; +assertEq(g.value, 'ok'); + +var desc = g.Object.getOwnPropertyDescriptor(g, "x"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.get, g.gf); +assertEq(desc.set, g.sf); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-01.js new file mode 100644 index 00000000000..9406bc6d425 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-01.js @@ -0,0 +1,12 @@ +// obj.defineProperty can define simple data properties. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gobj = dbg.addDebuggee(g); +gobj.defineProperty("x", {configurable: true, enumerable: true, writable: true, value: 'ok'}); +assertEq(g.x, 'ok'); + +var desc = g.Object.getOwnPropertyDescriptor(g, "x"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-02.js new file mode 100644 index 00000000000..8df296e90d7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-02.js @@ -0,0 +1,10 @@ +// obj.defineProperty can define a data property with object value. + +var g = newGlobal('new-compartment'); +g.eval("var a = {};"); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var desc = gw.getOwnPropertyDescriptor("a"); +assertEq(desc.value instanceof Debugger.Object, true); +gw.defineProperty("b", desc); +assertEq(g.a, g.b); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-03.js new file mode 100644 index 00000000000..40802e7ed5c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-03.js @@ -0,0 +1,21 @@ +// defineProperty can set array elements + +var g = newGlobal('new-compartment'); +g.a = g.Array(0, 1, 2); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var aw = gw.getOwnPropertyDescriptor("a").value; + +aw.defineProperty(0, {value: 'ok0'}); // by number +assertEq(g.a[0], 'ok0'); +var desc = g.Object.getOwnPropertyDescriptor(g.a, "0"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); + +aw.defineProperty("1", {value: 'ok1'}); // by string +assertEq(g.a[1], 'ok1'); +desc = g.Object.getOwnPropertyDescriptor(g.a, "1"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-04.js new file mode 100644 index 00000000000..00bae949665 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-04.js @@ -0,0 +1,9 @@ +// defineProperty can add array elements, bumping length + +var g = newGlobal('new-compartment'); +g.a = g.Array(0, 1, 2); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var aw = gw.getOwnPropertyDescriptor("a").value; +aw.defineProperty(3, {configurable: true, enumerable: true, writable: true, value: 3}); +assertEq(g.a.length, 4); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-05.js new file mode 100644 index 00000000000..95f687f6128 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-05.js @@ -0,0 +1,20 @@ +// defineProperty can define accessor properties. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +g.value = undefined; +g.eval("function gf() { return 12; }\n" + + "function sf(v) { value = v; }\n"); +var gfw = gw.getOwnPropertyDescriptor("gf").value; +var sfw = gw.getOwnPropertyDescriptor("sf").value; +gw.defineProperty("x", {configurable: true, get: gfw, set: sfw}); +assertEq(g.x, 12); +g.x = 'ok'; +assertEq(g.value, 'ok'); + +var desc = g.Object.getOwnPropertyDescriptor(g, "x"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.get, g.gf); +assertEq(desc.set, g.sf); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-06.js new file mode 100644 index 00000000000..933c4a1bb7e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-06.js @@ -0,0 +1,21 @@ +// obj.defineProperty with vague descriptors works like Object.defineProperty + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +gw.defineProperty("p", {configurable: true, enumerable: true}); +assertEq(g.p, undefined); +var desc = g.Object.getOwnPropertyDescriptor(g, "p"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.value, undefined); +assertEq(desc.writable, false); + +gw.defineProperty("q", {}); +assertEq(g.q, undefined); +var desc = g.Object.getOwnPropertyDescriptor(g, "q"); +assertEq(desc.configurable, false); +assertEq(desc.enumerable, false); +assertEq(desc.value, undefined); +assertEq(desc.writable, false); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-07.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-07.js new file mode 100644 index 00000000000..461cc9422a8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-07.js @@ -0,0 +1,10 @@ +// obj.defineProperty throws if a value, getter, or setter is not a debuggee value. + +load(libdir + "asserts.js"); +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gobj = dbg.addDebuggee(g); +assertThrowsInstanceOf(function () { gobj.defineProperty('x', {value: {}}); }, TypeError); +assertThrowsInstanceOf(function () { gobj.defineProperty('x', {get: Number}); }, TypeError); +assertThrowsInstanceOf(function () { gobj.defineProperty('x', {get: gobj, set: Number}) }, + TypeError); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-08.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-08.js new file mode 100644 index 00000000000..7751b3ac25b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-08.js @@ -0,0 +1,10 @@ +// obj.defineProperty throws if a value, getter, or setter is in a different compartment than obj + +load(libdir + "asserts.js"); +var g1 = newGlobal('new-compartment'); +var g2 = newGlobal('new-compartment'); +var dbg = new Debugger; +var g1w = dbg.addDebuggee(g1); +var g2w = dbg.addDebuggee(g2); +assertThrowsInstanceOf(function () { g1w.defineProperty('x', {value: g2w}); }, TypeError); +assertThrowsInstanceOf(function () { g1w.defineProperty('x', {get: g1w, set: g2w}); }, TypeError); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-09.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-09.js new file mode 100644 index 00000000000..a2bb3000b84 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-09.js @@ -0,0 +1,24 @@ +// defineProperty can't re-define non-configurable properties. +// Also: when defineProperty throws, the exception is native to the debugger +// compartment, not a wrapper. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +gw.defineProperty("p", {value: 1}); +g.p = 4; +assertEq(g.p, 1); + +var threw; +try { + gw.defineProperty("p", {value: 2}); + threw = false; +} catch (exc) { + threw = true; + assertEq(exc instanceof TypeError, true); + assertEq(typeof exc.message, "string"); + assertEq(typeof exc.stack, "string"); +} +assertEq(threw, true); + +assertEq(g.p, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-10.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-10.js new file mode 100644 index 00000000000..4fc6e10fd23 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-10.js @@ -0,0 +1,10 @@ +// defineProperty can make a non-configurable writable property non-writable + +load(libdir + "asserts.js"); +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +gw.defineProperty("p", {writable: true, value: 1}); +gw.defineProperty("p", {writable: false}); +g.p = 4; +assertEq(g.p, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-11.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-11.js new file mode 100644 index 00000000000..e078f3a4c15 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-11.js @@ -0,0 +1,16 @@ +// obj.defineProperty works when obj's referent is a wrapper. + +var x = {}; +var g = newGlobal('new-compartment'); +g.x = x; +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var xw = gw.getOwnPropertyDescriptor("x").value; +xw.defineProperty("p", {configurable: true, enumerable: true, writable: true, value: gw}); +assertEq(x.p, g); + +var desc = Object.getOwnPropertyDescriptor(x, "p"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); +assertEq(desc.value, g); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-12.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-12.js new file mode 100644 index 00000000000..7cb8bdc06de --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-12.js @@ -0,0 +1,18 @@ +// obj.defineProperty redefining an existing property leaves unspecified attributes unchanged. + +var g = newGlobal('new-compartment'); +g.p = 1; +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +gw.defineProperty("p", {value: 2}); +assertEq(g.p, 2); + +var desc = Object.getOwnPropertyDescriptor(g, "p"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); +assertEq(desc.value, 2); + +g.p = 3; +assertEq(g.p, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-surfaces-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-surfaces-01.js new file mode 100644 index 00000000000..2763dd4ee2f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-defineProperty-surfaces-01.js @@ -0,0 +1,8 @@ +// Debugger.Object.prototype.defineProperty with too few arguments throws. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +assertThrowsInstanceOf(function () { gw.defineProperty("x"); }, TypeError); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-01.js new file mode 100644 index 00000000000..46e412a2e5f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-01.js @@ -0,0 +1,17 @@ +// Basic deleteProperty tests. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +assertEq(gw.deleteProperty("no such property"), true); + +g.Object.defineProperty(g, "p", {configurable: true, value: 0}); +assertEq(gw.deleteProperty("p"), true); + +g[0] = 0; +assertEq(gw.deleteProperty(0), true); +assertEq("0" in g, false); + +assertEq(gw.deleteProperty(), false); // can't delete g.undefined +assertEq(g.undefined, undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-error-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-error-01.js new file mode 100644 index 00000000000..3388ea5bbfc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-deleteProperty-error-01.js @@ -0,0 +1,16 @@ +// Don't crash when a scripted proxy handler throws Error.prototype. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + try { + frame.arguments[0].deleteProperty("x"); + } catch (exc) { + return; + } + throw new Error("deleteProperty should throw"); +}; + +g.eval("function h(x) { debugger; }"); +g.eval("h(Proxy.create({delete: function () { throw Error.prototype; }}));"); + diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-01.js new file mode 100644 index 00000000000..168bc780bb6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-01.js @@ -0,0 +1,17 @@ +// obj.environment is undefined when the referent is not a JS function. + +var g = newGlobal('new-compartment') +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +assertEq(gw.environment, undefined); + +g.eval("var r = /x/;"); +var rw = gw.getOwnPropertyDescriptor("r").value; +assertEq(rw.class, "RegExp"); +assertEq(rw.environment, undefined); + +// Native function. +var fw = gw.getOwnPropertyDescriptor("parseInt").value; +assertEq(fw.class, "Function"); +assertEq(fw.environment, undefined); + diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-02.js new file mode 100644 index 00000000000..5f1a849feef --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-environment-02.js @@ -0,0 +1,20 @@ +// The .environment of a function Debugger.Object is an Environment object. + +var g = newGlobal('new-compartment') +var dbg = Debugger(g); +var hits = 0; +g.h = function () { + var frame = dbg.getNewestFrame(); + var fn = frame.eval("j").return; + assertEq(fn.environment instanceof Debugger.Environment, true); + var closure = frame.eval("f").return; + assertEq(closure.environment instanceof Debugger.Environment, true); + hits++; +}; +g.eval("function j(a) {\n" + + " var f = function () { return a; };\n" + + " h();\n" + + " return f;\n" + + "}\n" + + "j(0);\n"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-gc-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-gc-01.js new file mode 100644 index 00000000000..e223e676ee0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-gc-01.js @@ -0,0 +1,14 @@ +// Debugger.Objects keep their referents alive. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var arr = []; +dbg.onDebuggerStatement = function (frame) { arr.push(frame.eval("[]").return); }; +g.eval("for (var i = 0; i < 10; i++) debugger;"); +assertEq(arr.length, 10); + +gc(); + +for (var i = 0; i < arr.length; i++) + assertEq(arr[i].class, "Array"); + diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-01.js new file mode 100644 index 00000000000..33768c39948 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-01.js @@ -0,0 +1,59 @@ +// getOwnPropertyDescriptor works with simple data properties. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits; +var expected; +dbg.onDebuggerStatement = function (frame) { + var args = frame.arguments; + var obj = args[0], id = args[1]; + var desc = obj.getOwnPropertyDescriptor(id); + if (expected === undefined) { + assertEq(desc, undefined); + } else { + assertEq(desc instanceof Object, true); + assertEq(desc.enumerable, expected.enumerable); + assertEq(desc.configurable, expected.configurable); + assertEq(desc.hasOwnProperty("value"), true); + assertEq(desc.value, expected.value); + assertEq(desc.writable, expected.writable === undefined ? true : expected.writable); + assertEq("get" in desc, false); + assertEq("set" in desc, false); + } + hits++; +}; + +g.eval("function f(obj, id) { debugger; }"); + +function test(obj, id, desc) { + expected = desc; + hits = 0; + g.f(obj, id); + assertEq(hits, 1); +} + +var obj = g.eval("({a: 1, ' ': undefined, '0': 0})"); +test(obj, "a", {value: 1, enumerable: true, configurable: true}); +test(obj, " ", {value: undefined, enumerable: true, configurable: true}); +test(obj, "b", undefined); +test(obj, "0", {value: 0, enumerable: true, configurable: true}); +test(obj, 0, {value: 0, enumerable: true, configurable: true}); + +var arr = g.eval("[7,,]"); +test(arr, 'length', {value: 2, enumerable: false, configurable: false}); +test(arr, 0, {value: 7, enumerable: true, configurable: true}); +test(arr, "0", {value: 7, enumerable: true, configurable: true}); +test(arr, 1, undefined); +test(arr, "1", undefined); +test(arr, 2, undefined); +test(arr, "2", undefined); +test(arr, "argelfraster", undefined); + +var re = g.eval("/erwe/"); +test(re, 'lastIndex', {value: 0, enumerable: false, configurable: false}); + +// String objects have a well-behaved resolve hook. +var str = g.eval("new String('hello world')"); +test(str, 'length', {value: 11, enumerable: false, configurable: false, writable: false}); +test(str, 0, {value: 'h', enumerable: true, configurable: false, writable: false}); +test(str, "0", {value: 'h', enumerable: true, configurable: false, writable: false}); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-02.js new file mode 100644 index 00000000000..1d7f81a295e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-02.js @@ -0,0 +1,8 @@ +// Property values that are objects are reflected as Debugger.Objects. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(); +var gobj = dbg.addDebuggee(g); +g.self = g; +var desc = gobj.getOwnPropertyDescriptor("self"); +assertEq(desc.value, gobj); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-03.js new file mode 100644 index 00000000000..c88e24c1841 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-03.js @@ -0,0 +1,23 @@ +// obj.getOwnPropertyDescriptor works on global objects. + +var g = newGlobal('new-compartment'); +g.eval("var v; const k = 42;"); +this.eval("var v; const k = 42;"); + +var dbg = Debugger(); +var obj = dbg.addDebuggee(g); + +function test(name) { + var desc = obj.getOwnPropertyDescriptor(name); + assertEq(desc instanceof Object, true); + var expected = Object.getOwnPropertyDescriptor(this, name); + assertEq(Object.prototype.toString.call(desc), Object.prototype.toString.call(expected)); + assertEq(desc.enumerable, expected.enumerable); + assertEq(desc.configurable, expected.configurable); + assertEq(desc.writable, expected.writable); + assertEq(desc.value, expected.value); +} + +test("Infinity"); +test("v"); +test("k"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-04.js new file mode 100644 index 00000000000..7017722bed1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-04.js @@ -0,0 +1,18 @@ +// obj.getOwnPropertyDescriptor works on accessor properties. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger; +var gdo = dbg.addDebuggee(g); + +g.called = false; +g.eval("var a = {get x() { called = true; }};"); + +var desc = gdo.getOwnPropertyDescriptor("a").value.getOwnPropertyDescriptor("x"); +assertEq(g.called, false); +assertEq(desc.enumerable, true); +assertEq(desc.configurable, true); +assertEq("value" in desc, false); +assertEq("writable" in desc, false); +assertEq(desc.get instanceof Debugger.Object, true); +assertEq(desc.get.class, "Function"); +assertEq(desc.set, undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-05.js new file mode 100644 index 00000000000..5e1c4ed0d1b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-05.js @@ -0,0 +1,17 @@ +// obj.getOwnPropertyDescriptor presents getters and setters as Debugger.Object objects. + +var g = newGlobal('new-compartment'); +g.S = function foreignFunction(v) {}; +g.eval("var a = {};\n" + + "function G() {}\n" + + "Object.defineProperty(a, 'p', {get: G, set: S})"); + +var dbg = new Debugger; +var gdo = dbg.addDebuggee(g); +var desc = gdo.getOwnPropertyDescriptor("a").value.getOwnPropertyDescriptor("p"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, false); +assertEq("value" in desc, false); +assertEq("writable" in desc, false); +assertEq(desc.get, gdo.getOwnPropertyDescriptor("G").value); +assertEq(desc.set, gdo.getOwnPropertyDescriptor("S").value); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-06.js new file mode 100644 index 00000000000..e6b95bcb69a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-06.js @@ -0,0 +1,29 @@ +// obj.getOwnPropertyDescriptor works when obj is a transparent cross-compartment wrapper. + +var g1 = newGlobal('new-compartment'); +var g2 = newGlobal('new-compartment'); +g1.next = g2; + +// This test is a little hard to follow, especially the !== assertions. +// +// Bottom line: the value of a property of g1 can only be an object in g1's +// compartment, so any Debugger.Objects obtained by calling +// g1obj.getOwnPropertyDescriptor should all have referents in g1's +// compartment. + +var dbg = new Debugger; +var g1obj = dbg.addDebuggee(g1); +var g2obj = dbg.addDebuggee(g2); +var wobj = g1obj.getOwnPropertyDescriptor("next").value; +assertEq(wobj instanceof Debugger.Object, true); +assertEq(wobj !== g2obj, true); // referents are in two different compartments + +g2.x = "ok"; +assertEq(wobj.getOwnPropertyDescriptor("x").value, "ok"); + +g1.g2min = g2.min = g2.Math.min; +g2.eval("Object.defineProperty(this, 'y', {get: min});"); +assertEq(g2.y, Infinity); +var wmin = wobj.getOwnPropertyDescriptor("y").get; +assertEq(wmin !== g2obj.getOwnPropertyDescriptor("min").value, true); // as above +assertEq(wmin, g1obj.getOwnPropertyDescriptor("g2min").value); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-01.js new file mode 100644 index 00000000000..3b9c417fd5f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-01.js @@ -0,0 +1,14 @@ +// The argument to Debugger.Object.prototype.getOwnPropertyDescriptor can be omitted. + +var g = newGlobal('new-compartment'); +g.eval("var obj = {};"); + +var dbg = Debugger(g); +var obj; +dbg.onDebuggerStatement = function (frame) { obj = frame.eval("obj").return; }; +g.eval("debugger;"); + +assertEq(obj.getOwnPropertyDescriptor(), undefined); +g.obj.undefined = 17; +var desc = obj.getOwnPropertyDescriptor(); +assertEq(desc.value, 17); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-02.js new file mode 100644 index 00000000000..3fdd316f132 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyDescriptor-surfaces-02.js @@ -0,0 +1,14 @@ +// The argument to Debugger.Object.prototype.getOwnPropertyDescriptor can be an object. +var g = newGlobal('new-compartment'); +g.eval("var obj = {};"); + +var dbg = Debugger(g); +var obj; +dbg.onDebuggerStatement = function (frame) { obj = frame.eval("obj").return; }; +g.eval("debugger;"); + +var nameobj = {toString: function () { return 'x'; }}; +assertEq(obj.getOwnPropertyDescriptor(nameobj), undefined); +g.obj.x = 17; +var desc = obj.getOwnPropertyDescriptor(nameobj); +assertEq(desc.value, 17); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-01.js new file mode 100644 index 00000000000..657908d293c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-01.js @@ -0,0 +1,28 @@ +// Basic getOwnPropertyNames tests. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(); +var gobj = dbg.addDebuggee(g); + +function test(code) { + code = "(" + code + ");"; + var expected = Object.getOwnPropertyNames(eval(code)); + g.eval("obj = " + code); + var actual = gobj.getOwnPropertyDescriptor("obj").value.getOwnPropertyNames(); + assertEq(JSON.stringify(actual.sort()), JSON.stringify(expected.sort())); +} + +test("{}"); +test("{a: 0, b: 1}"); +test("{'name with space': 0}"); +test("{get x() {}, set y(v) {}}"); +test("{get x() { throw 'fit'; }}"); +test("Object.create({a: 1})"); +test("Object.create({get a() {}, set a(v) {}})"); +test("(function () { var x = {a: 0, b: 1}; delete a; return x; })()"); +test("Object.create(null, {x: {value: 0}})"); +test("[]"); +test("[0, 1, 2]"); +test("[,,,,,]"); +test("/a*a/"); +test("function () {}"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-02.js new file mode 100644 index 00000000000..827e8b3626c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-getOwnPropertyNames-02.js @@ -0,0 +1,7 @@ +// obj.getOwnPropertyNames() works when obj's referent is itself a cross-compartment wrapper. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(); +var gobj = dbg.addDebuggee(g); +g.p = {xyzzy: 8}; // makes a cross-compartment wrapper +assertEq(gobj.getOwnPropertyDescriptor("p").value.getOwnPropertyDescriptor("xyzzy").value, 8); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-01.js new file mode 100644 index 00000000000..6fe74c49d56 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-01.js @@ -0,0 +1,10 @@ +// Two references to the same object get the same Debugger.Object wrapper. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0], frame.arguments[1]); + hits++; +}; +g.eval("var obj = {}; function f(a, b) { debugger; } f(obj, obj);"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-02.js new file mode 100644 index 00000000000..fd6b9fc6bbe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-02.js @@ -0,0 +1,10 @@ +// Different objects get different Debugger.Object wrappers. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0] === frame.arguments[1], false); + hits++; +}; +g.eval("function f(a, b) { debugger; } f({}, {});"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-03.js new file mode 100644 index 00000000000..67c50accee0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-identity-03.js @@ -0,0 +1,25 @@ +// The same object gets the same Debugger.Object wrapper at different times, if the difference would be observable. + +var N = 12; + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var wrappers = []; + +dbg.onDebuggerStatement = function (frame) { wrappers.push(frame.arguments[0]); }; +g.eval("var originals = []; function f(x) { originals.push(x); debugger; }"); +for (var i = 0; i < N; i++) + g.eval("f({});"); +assertEq(wrappers.length, N); + +for (var i = 0; i < N; i++) + for (var j = i + 1; j < N; j++) + assertEq(wrappers[i] === wrappers[j], false); + +gc(); + +dbg.onDebuggerStatement = function (frame) { assertEq(frame.arguments[0], wrappers.pop()); }; +g.eval("function h(x) { debugger; }"); +for (var i = 0; i < N; i++) + g.eval("h(originals.pop());"); +assertEq(wrappers.length, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-name-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-name-01.js new file mode 100644 index 00000000000..7ec300a1663 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-name-01.js @@ -0,0 +1,13 @@ +// Debugger.Object.prototype.name + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var name; +dbg.onDebuggerStatement = function (frame) { name = frame.callee.name; }; + +g.eval("(function f() { debugger; })();"); +assertEq(name, "f"); +g.eval("(function () { debugger; })();"); +assertEq(name, undefined); +g.eval("Function('debugger;')();"); +assertEq(name, "anonymous"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-name-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-name-02.js new file mode 100644 index 00000000000..669e3dbe7e5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-name-02.js @@ -0,0 +1,16 @@ +// The .name of a non-function object is undefined. + +var g = newGlobal('new-compartment'); +var hits = 0; +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0].name, undefined); + hits++; +}; +g.eval("function f(nonfunction) { debugger; }"); + +g.eval("f({});"); +g.eval("f(/a*/);"); +g.eval("f({name: 'bad'});"); +g.eval("f(Proxy.createFunction({name: {value: 'bad'}}, function () {}));"); +assertEq(hits, 4); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-parameterNames.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-parameterNames.js new file mode 100644 index 00000000000..8868b53fe31 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-parameterNames.js @@ -0,0 +1,33 @@ +load(libdir + 'array-compare.js'); + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var arr = frame.arguments; + assertEq(arraysEqual(arr[0].parameterNames, []), true); + assertEq(arraysEqual(arr[1].parameterNames, ["x"]), true); + assertEq(arraysEqual(arr[2].parameterNames, + ["a","b","c","d","e","f","g","h","i","j","k","l","m", + "n","o","p","q","r","s","t","u","v","w","x","y","z"]), + true); + assertEq(arraysEqual(arr[3].parameterNames, ["a", (void 0), (void 0)]), true); + assertEq(arr[4].parameterNames, (void 0)); + assertEq(arraysEqual(arr[5].parameterNames, [(void 0), (void 0)]), true); + assertEq(arr.length, 6); + hits++; +}; + +g.eval("(" + + function () { + (function () { debugger; } + (function () {}, + function (x) {}, + function (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z) {}, + function (a, [b, c], {d, e:f}) { }, + {a:1}, + Math.atan2 + )); + } + +")()"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-preventExtensions-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-preventExtensions-01.js new file mode 100644 index 00000000000..6a2cfc882f8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-preventExtensions-01.js @@ -0,0 +1,16 @@ +// Basic preventExtensions test. + +var g = newGlobal('new-compartment'); +var obj = g.eval("({x: 1})"); +assertEq(g.Object.isExtensible(obj), true); + +var dbg = new Debugger; +var objw = dbg.addDebuggee(obj); +assertEq(objw.isExtensible(), true); + +assertEq(objw.preventExtensions(), undefined); +assertEq(g.Object.isExtensible(obj), false); +assertEq(objw.isExtensible(), false); + +// Calling preventExtensions again has no effect. +assertEq(objw.preventExtensions(), undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-proto.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-proto.js new file mode 100644 index 00000000000..68f3b154576 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-proto.js @@ -0,0 +1,23 @@ +// Debugger.Object.prototype.proto +var g = newGlobal('new-compartment'); +var dbgeval = function () { + var dbg = new Debugger(g); + var hits = 0; + g.eval("function f() { debugger; }"); + var lastval; + dbg.onDebuggerStatement = function (frame) { lastval = frame.arguments[0]; }; + return function dbgeval(s) { + g.eval("f(" + s + ");"); + return lastval; + }; + }(); + +var Op = dbgeval("Object.prototype"); +assertEq(Op.proto, null); +assertEq(dbgeval("({})").proto, Op); + +var Ap = dbgeval("[]").proto; +assertEq(Ap, dbgeval("Array.prototype")); +assertEq(Ap.proto, Op); + +assertEq(dbgeval("Object").proto, dbgeval("Function.prototype")); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-script.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-script.js new file mode 100644 index 00000000000..70cf7f7f689 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-script.js @@ -0,0 +1,15 @@ +// |jit-test| debug + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var arr = frame.arguments; + assertEq(arr[0].script instanceof Debugger.Script, true); + assertEq(arr[1].script, undefined); + assertEq(arr[2].script, undefined); + hits++; +}; + +g.eval("(function () { debugger; })(function g(){}, {}, Math.atan2);"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Object-seal-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Object-seal-01.js new file mode 100644 index 00000000000..9abe10b66dd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Object-seal-01.js @@ -0,0 +1,64 @@ +// Basic tests for obj.{seal,freeze,preventExtensions,isSealed,isFrozen,isExtensible}. + +var g = newGlobal("new-compartment"); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +g.eval("function compareObjects() {\n" + + " assertEq(Object.isExtensible(x), Object.isExtensible(y));\n" + + " var xnames = Object.getOwnPropertyNames(x).sort();\n" + + " var ynames = Object.getOwnPropertyNames(y).sort();\n" + + " assertEq(xnames.length, ynames.length);\n" + + " for (var i = 0; i < xnames.length; i++) {\n" + + " assertEq(xnames[i], ynames[i]);\n" + + " var name = xnames[i];\n" + + " var xd = Object.getOwnPropertyDescriptor(x, name);\n" + + " var yd = Object.getOwnPropertyDescriptor(y, name);\n" + + " assertEq(xd.configurable, yd.configurable, code + '.' + name + ' .configurable');\n" + + " assertEq(xd.enumerable, yd.enumerable, code + '.' + name + ' .enumerable');\n" + + " assertEq(xd.writable, yd.writable, code + '.' + name + ' .writable');\n" + + " }\n" + + "}\n"); + +function test(code) { + g.code = code; + g.eval("x = (" + code + ");"); + g.eval("y = (" + code + ");"); + var xw = gw.getOwnPropertyDescriptor("x").value; + + function check() { + // The Debugger.Object seal/freeze/preventExtensions methods + // had the same effect as the corresponding ES5 Object methods. + g.compareObjects(); + + // The Debugger.Object introspection methods agree with the ES5 Object methods. + assertEq(xw.isExtensible(), g.Object.isExtensible(g.x), code + ' isExtensible'); + assertEq(xw.isSealed(), g.Object.isSealed(g.x), code + ' isSealed'); + assertEq(xw.isFrozen(), g.Object.isFrozen(g.x), code + ' isFrozen'); + } + + check(); + + xw.preventExtensions(); + assertEq(g.Object.isExtensible(g.x), false, code + ' preventExtensions'); + g.Object.preventExtensions(g.y); + check(); + + xw.seal(); + assertEq(g.Object.isSealed(g.x), true, code + ' seal'); + g.Object.seal(g.y); + check(); + + xw.freeze(); + assertEq(g.Object.isFrozen(g.x), true, code + ' freeze'); + g.Object.freeze(g.y); + check(); +} + +test("{}"); +test("{a: [1], get b() { return -1; }}"); +test("Object.create(null, {x: {value: 3}, y: {get: Math.min}})"); +test("[]"); +test("[,,,,,]"); +test("[0, 1, 2]"); +test("newGlobal('same-compartment')"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-01.js new file mode 100644 index 00000000000..e3500918cfe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-01.js @@ -0,0 +1,71 @@ +// |jit-test| debug +// We get the same Debugger.Script object instance each time we ask. + +var global = newGlobal('new-compartment'); +global.eval('function f() { debugger; }'); +global.eval('function g() { debugger; }'); + +var debug = new Debugger(global); + +function evalAndNoteScripts(prog) { + var scripts = {}; + debug.onDebuggerStatement = function(frame) { + if (frame.type == "call") + assertEq(frame.script, frame.callee.script); + scripts.frame = frame.script; + if (frame.arguments[0]) + scripts.argument = frame.arguments[0].script; + }; + global.eval(prog); + return scripts; +} + +// If we create a frame for a function and pass it as a value, those should +// both yield the same Debugger.Script instance. +var scripts = evalAndNoteScripts('f(f)'); +assertEq(scripts.frame, scripts.argument); +var fScript = scripts.argument; + +// If we call a second time, we should still get the same instance. +scripts = evalAndNoteScripts('f(f)'); +assertEq(scripts.frame, fScript); +assertEq(scripts.argument, fScript); + +// If we call with a different argument, we should get a different Debugger.Script. +scripts = evalAndNoteScripts('f(g)'); +assertEq(scripts.frame !== scripts.argument, true); +assertEq(scripts.frame, fScript); +var gScript = scripts.argument; + +// See if we can get g via the frame. +scripts = evalAndNoteScripts('g(f)'); +assertEq(scripts.frame !== scripts.argument, true); +assertEq(scripts.frame, gScript); +assertEq(scripts.argument, fScript); + +// Different closures made from the same 'function' expression should yield +// the same script. +global.eval('function gen1(x) { return function clo(y) { return x+y; }; }'); +global.eval('var clo1 = gen1(42);'); +global.eval('var clo2 = gen1("smoot");'); +var scripts1 = evalAndNoteScripts('f(clo1)'); +var scripts2 = evalAndNoteScripts('f(clo2)'); +assertEq(scripts1.argument, scripts2.argument); + +// Different closures made from the same 'function' declaration should yield +// the same script. +global.eval('function gen2(x) { function clo(y) { return x+y; }; return clo; }'); +global.eval('var clo1 = gen2(42);'); +global.eval('var clo2 = gen2("smoot");'); +var scripts1 = evalAndNoteScripts('f(clo1)'); +var scripts2 = evalAndNoteScripts('f(clo2)'); +assertEq(scripts1.argument, scripts2.argument); + +// Different closures made from the same 'function' statement should yield +// the same script. +global.eval('function gen3(x) { if (true) { function clo(y) { return x+y; }; return clo; } }'); +global.eval('var clo1 = gen3(42);'); +global.eval('var clo2 = gen3("smoot");'); +var scripts1 = evalAndNoteScripts('f(clo1)'); +var scripts2 = evalAndNoteScripts('f(clo2)'); +assertEq(scripts1.argument, scripts2.argument); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-02.js new file mode 100644 index 00000000000..b64166c08a0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-02.js @@ -0,0 +1,7 @@ +// |jit-test| debug +// Debugger.Script throws when applied as a constructor. + +load(libdir + 'asserts.js'); + +assertThrowsInstanceOf(function() { Debugger.Script(); }, TypeError); +assertThrowsInstanceOf(function() { new Debugger.Script(); }, TypeError); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-01.js new file mode 100644 index 00000000000..9cf67a8f3f6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-01.js @@ -0,0 +1,19 @@ +// A breakpoint handler may clear itself. + +var g = newGlobal('new-compartment'); +var bphits = 0; +var handler = {hit: function (frame) { frame.script.clearBreakpoint(this); bphits++; }}; +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var offs = frame.script.getLineOffsets(g.line0 + 3); + for (var i = 0; i < offs.length; i++) + frame.script.setBreakpoint(offs[i], handler); + hits++; +}; +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "for (var i = 0; i < 4; i++)\n" + // line0 + 2 + " result = 'ok';\n"); // line0 + 3 +assertEq(hits, 1); +assertEq(bphits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-02.js new file mode 100644 index 00000000000..77aed0deded --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-02.js @@ -0,0 +1,26 @@ +// A breakpoint cleared during dispatch does not fire. +// (Breakpoint dispatch is well-behaved even when breakpoint handlers clear other breakpoints.) + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var log = ''; +dbg.onDebuggerStatement = function (frame) { + var s = frame.script; + function handler(i) { + if (i === 1) + return function () { log += i; s.clearBreakpoint(h[1]); s.clearBreakpoint(h[2]); }; + return function () { log += i; }; + } + var offs = s.getLineOffsets(g.line0 + 2); + var h = []; + for (var i = 0; i < 4; i++) { + h[i] = {hit: handler(i)}; + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], h[i]); + } +}; + +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "result = 'ok';\n"); // line0 + 2 +assertEq(log, '013'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-03.js new file mode 100644 index 00000000000..5aa6c49135a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-03.js @@ -0,0 +1,25 @@ +// Clearing a breakpoint by handler can clear multiple breakpoints. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var s; +dbg.onDebuggerStatement = function (frame) { + s = frame.eval("f").return.script; +}; +g.eval("var line0 = Error().lineNumber;\n" + + "function f(a, b) {\n" + // line0 + 1 + " return a + b;\n" + // line0 + 2 + "}\n" + + "debugger;\n"); + +var hits = 0; +var handler = {hit: function (frame) { hits++; s.clearBreakpoint(handler); }}; +var offs = s.getLineOffsets(g.line0 + 2); +for (var i = 0; i < 4; i++) { + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], handler); +} + +assertEq(g.f(2, 2), 4); +assertEq(hits, 1); +assertEq(s.getBreakpoints().length, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-04.js new file mode 100644 index 00000000000..0402d15b972 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-clearBreakpoint-04.js @@ -0,0 +1,28 @@ +// clearBreakpoint clears breakpoints for the current Debugger object only. + +var g = newGlobal('new-compartment'); + +var hits = 0; +var handler = { + hit: function (frame) { + hits++; + frame.script.clearBreakpoint(handler); + } +}; + +function attach(i) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var s = frame.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], handler); + }; +} +for (var i = 0; i < 4; i++) + attach(i); + +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "Math.sin(0);\n"); // line0 + 2 +assertEq(hits, 4); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-01.js new file mode 100644 index 00000000000..adc4adbace1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-01.js @@ -0,0 +1,27 @@ +// |jit-test| debug +// Debugger.Script instances with live referents stay alive. + +var N = 4; +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var i; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.script instanceof Debugger.Script, true); + frame.script.id = i; +}; + +g.eval('var arr = [];') +for (i = 0; i < N; i++) // loop to defeat conservative GC + g.eval("arr.push(function () { debugger }); arr[arr.length - 1]();"); + +gc(); + +var hits; +dbg.onDebuggerStatement = function (frame) { + hits++; + assertEq(frame.script.id, i); +}; +hits = 0; +for (i = 0; i < N; i++) + g.arr[i](); +assertEq(hits, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-02.js new file mode 100644 index 00000000000..51746b99dfa --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-02.js @@ -0,0 +1,14 @@ +// Debugger.Scripts keep their referents alive. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var arr = []; +dbg.onDebuggerStatement = function (frame) { arr.push(frame.script); }; +g.eval("for (var i = 0; i < 10; i++) Function('debugger;')();"); +assertEq(arr.length, 10); + +gc(); + +for (var i = 0; i < arr.length; i++) + assertEq(arr[i].lineCount, 1); + diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-03.js new file mode 100644 index 00000000000..f7f5433c2d6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-gc-03.js @@ -0,0 +1,15 @@ +// Referents of Debugger.Scripts in other compartments always survive per-compartment GC. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var arr = []; +dbg.onDebuggerStatement = function (frame) { arr.push(frame.script); }; +g.eval("for (var i = 0; i < 100; i++) Function('debugger;')();"); +assertEq(arr.length, 100); + +gc(g); + +for (var i = 0; i < arr.length; i++) + assertEq(arr[i].lineCount, 1); + +gc(); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-01.js new file mode 100644 index 00000000000..50bbd583a3e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-01.js @@ -0,0 +1,40 @@ +// Basic Script.prototype.getBreakpoints tests. + +var g = newGlobal('new-compartment'); +g.eval("var line0 = Error().lineNumber;\n" + + "function f(x) {\n" + // line0 + 1 + " if (x < 0)\n" + // line0 + 2 + " return -x;\n" + // line0 + 3 + " return x;\n" + + "}"); + +var s; +var offsets = []; +var handlers = []; +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + s = frame.eval("f").return.script; + var off; + + for (var i = 0; i < 3; i++) { + var off = s.getLineOffsets(g.line0 + 2 + i)[0]; + assertEq(typeof off, 'number'); + handlers[i] = {}; + s.setBreakpoint(off, handlers[i]); + offsets[i] = off; + } +}; +g.eval("debugger;"); + +// getBreakpoints without an offset gets all breakpoints in the script. +var bps = s.getBreakpoints(); +assertEq(bps.length, handlers.length); +for (var i = 0; i < bps.length; i++) + assertEq(bps.indexOf(handlers[i]) !== -1, true); + +// getBreakpoints with an offset finds only breakpoints at that offset. +for (var i = 0; i < offsets.length; i++) { + var bps = s.getBreakpoints(offsets[i]); + assertEq(bps.length, 1); + assertEq(bps[0], handlers[i]); +} diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-02.js new file mode 100644 index 00000000000..4da6b07ca34 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getBreakpoints-02.js @@ -0,0 +1,42 @@ +// Script.prototype.getBreakpoints returns breakpoints for the current Debugger object only. + +var g = newGlobal('new-compartment'); + +var debuggers = []; +var hits = 0; +function attach(g, i) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var s = frame.script; + var offs = s.getLineOffsets(g.line0 + 2); + var hitAny = false; + var handler = { + hit: function (frame) { + assertEq(this, handler); + assertEq(frame.script, s); + var bps = s.getBreakpoints(); + assertEq(bps.length, offs.length); + for (var i = 0; i < bps.length; i++) + assertEq(bps[i], handler); + + if (!hitAny) { + hitAny = true; + hits++; + } + } + }; + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], handler); + hits++; + }; + debuggers[i] = dbg; +} + +for (var i = 0; i < 3; i++) + attach(g, i); +g.done = false; +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "done = true;\n"); // line0 + 2 +assertEq(hits, 6); +assertEq(g.done, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-01.js new file mode 100644 index 00000000000..3707c6ae06a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-01.js @@ -0,0 +1,42 @@ +// Basic getChildScripts tests. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var log; +function note(s) { + assertEq(s instanceof Debugger.Script, true); + log += 'S'; + var c = s.getChildScripts(); + if (c.length > 0) { + log += '['; + for (var i = 0; i < c.length; i++) + note(c[i]); + log += ']'; + } +} +dbg.onDebuggerStatement = function (frame) { note(frame.script); }; + +function test(code, expected) { + log = ''; + g.eval(code); + assertEq(log, expected); +} + +test("debugger;", + "S"); +test("function f() {} debugger;", + "S[S]"); +test("function g() { function h() { function k() {} return k; } return h; } debugger;", + "S[S[S[S]]]"); +test("function q() {} function qq() {} debugger;", + "S[SS]"); +test("[0].map(function id(a) { return a; }); debugger;", + "S[S]"); +test("Function('return 2+2;')(); debugger;", + "S"); +test("var obj = {get x() { return 0; }, set x(v) {}}; debugger;", + "S[SS]"); +test("function r(n) { for (var i = 0; i < n; i++) yield i; } debugger;", + "S[S]"); +test("var it = (3 for (p in obj)); debugger;", + "S[S]"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-02.js new file mode 100644 index 00000000000..5b300ff5a90 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-02.js @@ -0,0 +1,20 @@ +// getChildScripts returns scripts in source order. + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var scripts = []; +var cs; +dbg.onDebuggerStatement = function (frame) { + scripts.push(frame.script); + if (scripts.length === 1) + cs = frame.script.getChildScripts(); +}; + +g.eval("function f() { debugger; }\n" + + "var g = function () { debugger; }\n" + + "debugger; f(); g();"); + +assertEq(scripts.length, 3); +assertEq(cs.length, 2); +assertEq(cs[0], scripts[1]); +assertEq(cs[1], scripts[2]); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-03.js new file mode 100644 index 00000000000..2d39a69ba62 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-03.js @@ -0,0 +1,16 @@ +// getChildScripts on a direct eval script returns the right scripts. +// (A bug had it also returning the script for the calling function.) + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var arr = frame.script.getChildScripts(); + assertEq(arr.length, 1); + assertEq(arr[0], frame.eval("h").return.script); + hits++; +}; + +g.eval("function f(s) { eval(s); }"); +g.f("debugger; function h(a) { return a + 1; }"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-04.js new file mode 100644 index 00000000000..c32c0a526da --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getChildScripts-04.js @@ -0,0 +1,15 @@ +// script.getChildScripts() works correctly during the newScript hook. +// (A bug had it including the script for the calling function.) + +var g = newGlobal('new-compartment'); +g.eval("function h(a) { eval(a); }"); + +var dbg = Debugger(g); +var arr, kscript; +dbg.onNewScript = function (script) { arr = script.getChildScripts(); }; +dbg.onDebuggerStatement = function (frame) { kscript = frame.callee.script; }; + +g.h("function k(a) { debugger; return a + 1; } k(-1);"); +assertEq(kscript instanceof Debugger.Script, true); +assertEq(arr.length, 1); +assertEq(arr[0], kscript); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-01.js new file mode 100644 index 00000000000..d40f6b2633d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-01.js @@ -0,0 +1,13 @@ +// getLineOffsets on a line that is definitely outside a script returns an empty array. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var offs = frame.script.getLineOffsets(g.line0 + 2); + assertEq(Array.isArray(offs), true); + assertEq(offs.length, 0); + hits++; +}; +g.eval("var line0 = Error().lineNumber; debugger;"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-02.js new file mode 100644 index 00000000000..efe18abf338 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-02.js @@ -0,0 +1,33 @@ +// getLineOffsets correctly places the various parts of a ForStatement. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + function handler(line) { + return {hit: function (frame) { g.log += "" + line; }}; + } + + var s = frame.eval("f").return.script; + for (var line = 2; line <= 6; line++) { + var offs = s.getLineOffsets(g.line0 + line); + var h = handler(line); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), g.line0 + line); + s.setBreakpoint(offs[i], h); + } + } +}; + +g.log = ''; +g.eval("var line0 = Error().lineNumber;\n" + + "function f(n) {\n" + // line0 + 1 + " for (var i = 0;\n" + // line0 + 2 + " i < n;\n" + // line0 + 3 + " i++)\n" + // line0 + 4 + " log += '.';\n" + // line0 + 5 + " log += '!';\n" + // line0 + 6 + "}\n" + + "debugger;\n"); +assertEq(g.log, ""); +g.f(3); +assertEq(g.log, "235.435.435.436!"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-03.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-03.js new file mode 100644 index 00000000000..07a755be367 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-03.js @@ -0,0 +1,36 @@ +// getLineOffsets treats one-line compound statements as having only one entry-point. +// (A breakpoint on a line that only executes once will only hit once.) + +var g = newGlobal('new-compartment'); +g.line0 = null; +var dbg = Debugger(g); +var log; +dbg.onDebuggerStatement = function (frame) { + var s = frame.script; + var lineno = g.line0 + 2; + var offs = s.getLineOffsets(lineno); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), lineno); + s.setBreakpoint(offs[i], {hit: function () { log += 'B'; }}); + } + log += 'A'; +}; + +function test(s) { + log = ''; + g.eval("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + s); // line0 + 2 + assertEq(log, 'AB'); +} + +g.i = 0; +g.j = 0; +test("{i++; i--; i++; i--; }"); +test("if (i === 0) i++; else i--;"); +test("while (i < 5) i++;"); +test("do --i; while (i > 0);"); +test("for (i = 0; i < 5; i++) j++;"); +test("for (var x in [0, 1, 2]) j++;"); +test("switch (i) { case 0: j = 0; case 1: j = 1; case 2: j = 2; default: j = i; }"); +test("switch (i) { case 'A': j = 0; case 'B': j = 1; case 'C': j = 2; default: j = i; }"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-04.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-04.js new file mode 100644 index 00000000000..a640c573a1b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-04.js @@ -0,0 +1,53 @@ +// getLineOffsets works with instructions reachable only by breaking out of a loop or switch. + +var g = newGlobal('new-compartment'); +g.line0 = null; +var dbg = Debugger(g); +var where; +dbg.onDebuggerStatement = function (frame) { + var s = frame.eval("f").return.script; + var lineno = g.line0 + where; + var offs = s.getLineOffsets(lineno); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), lineno); + s.setBreakpoint(offs[i], {hit: function () { g.log += 'B'; }}); + } + g.log += 'A'; +}; + +function test(s) { + var count = (s.split(/\n/).length - 1); // number of newlines in s + g.log = ''; + where = 3 + count + 1; + g.eval("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "function f(i) {\n" + // line0 + 2 + s + // line0 + 3 ... line0 + where - 2 + " log += '?';\n" + // line0 + where - 1 + " log += '!';\n" + // line0 + where + "}\n"); + g.f(0); + assertEq(g.log, 'A?B!'); +} + +test("i = 128;\n" + + "for (;;) {\n" + + " var x = i - 10;;\n" + + " if (x < 0)\n" + + " break;\n" + + " i >>= 2;\n" + + "}\n"); + +test("while (true)\n" + + " if (++i === 2) break;\n"); + +test("do {\n" + + " if (++i === 2) break;\n" + + "} while (true);\n"); + +test("switch (i) {\n" + + " case 2: return 7;\n" + + " case 1: return 8;\n" + + " case 0: break;\n" + + " default: return -i;\n" + + "}\n"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-05.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-05.js new file mode 100644 index 00000000000..e0a7b3793b8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-05.js @@ -0,0 +1,65 @@ +// getLineOffsets identifies multiple ways to land on a line. + +var g = newGlobal('new-compartment'); +g.line0 = null; +var dbg = Debugger(g); +var where; +dbg.onDebuggerStatement = function (frame) { + var s = frame.script, lineno, offs; + + lineno = g.line0 + where; + offs = s.getLineOffsets(lineno); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), lineno); + s.setBreakpoint(offs[i], {hit: function () { g.log += 'B'; }}); + } + + lineno++; + offs = s.getLineOffsets(lineno); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), lineno); + s.setBreakpoint(offs[i], {hit: function () { g.log += 'C'; }}); + } + + g.log += 'A'; +}; + +function test(s) { + assertEq(s.charAt(s.length - 1), '\n'); + var count = (s.split(/\n/).length - 1); // number of lines in s + g.log = ''; + where = 1 + count; + g.eval("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + s + // line0 + 2 ... line0 + where + "log += 'D';\n"); + assertEq(g.log, 'AB!CD'); +} + +// if-statement with yes and no paths on a single line +g.i = 0; +test("if (i === 0)\n" + + " log += '!'; else log += 'X';\n"); +test("if (i === 2)\n" + + " log += 'X'; else log += '!';\n"); + +// break to a line that has code inside and outside the loop +g.i = 2; +test("while (1) {\n" + + " if (i === 2) break;\n" + + " log += 'X'; } log += '!';\n"); + +// leaving a while loop by failing the test, when the last line has stuff both inside and outside the loop +g.i = 0; +test("while (i > 0) {\n" + + " if (i === 70) log += 'X';\n" + + " --i; } log += '!';\n"); + +// multiple case-labels on the same line +g.i = 0; +test("switch (i) {\n" + + " case 0: case 1: log += '!'; break; }\n"); +test("switch ('' + i) {\n" + + " case '0': case '1': log += '!'; break; }\n"); +test("switch (i) {\n" + + " case 'ok' + i: case i - i: log += '!'; break; }\n"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-06.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-06.js new file mode 100644 index 00000000000..4bbbdd3bb02 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getLineOffsets-06.js @@ -0,0 +1,99 @@ +// getLineOffsets works with extended instructions, such as JSOP_GOTOX. + +var g = newGlobal('new-compartment'); +g.line0 = null; +var dbg = Debugger(g); +var where; +dbg.onDebuggerStatement = function (frame) { + var s = frame.script; + var offs; + var lineno = g.line0 + where; + offs = s.getLineOffsets(lineno); + for (var i = 0; i < offs.length; i++) { + assertEq(s.getOffsetLine(offs[i]), lineno); + s.setBreakpoint(offs[i], {hit: function (frame) { g.log += 'B'; }}); + } + g.log += 'A'; +}; + +function test(s) { + assertEq(s.charAt(s.length - 1) !== '\n', true); + var count = s.split(/\n/).length; // number of lines in s + g.i = 0; + g.log = ''; + where = 1 + count; + g.eval("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + s + // line0 + 2 ... line0 + where + " log += 'C';\n"); + assertEq(g.log, 'ABC'); +} + +function repeat(s) { + return Array((1 << 14) + 1).join(s); // 16K copies of s +} +var long_expr = "i" + repeat(" + i"); +var long_throw_stmt = "throw " + long_expr + ";\n"; + +// long break (JSOP_GOTOX) +test("for (;;) {\n" + + " if (i === 0)\n" + + " break;\n" + + " " + long_throw_stmt + + "}"); + +// long continue (JSOP_GOTOX) +test("do {\n" + + " if (i === 0)\n" + + " continue;\n" + + " " + long_throw_stmt + + "} while (i !== 0);"); + +// long if consequent (JSOP_IFEQX) +test("if (i === 2) {\n" + + " " + long_throw_stmt + + "}"); + +// long catch-block with finally (JSOP_GOSUBX) +test("try {\n" + + " i = 0;\n" + + "} catch (exc) {\n" + + " throw " + long_expr + ";\n" + + "} finally {\n" + + " i = 1;\n" + + "}"); + +// long case (JSOP_TABLESWITCHX) +test("switch (i) {\n" + + " default:\n" + + " case 1: " + long_throw_stmt + + " case 0: i++; }"); + +test("switch (i) {\n" + + " case 1: case 2: case 3: " + long_throw_stmt + + " default: i++; }"); + +// long case (JSOP_LOOKUPSWITCHX) +test("switch ('' + i) {\n" + + " default:\n" + + " case '1': " + long_throw_stmt + + " case '0': i++; }"); + +test("switch (i) {\n" + + " case '1': case '2': case '3': " + long_throw_stmt + + " default: i++; }"); + +// long case or case-expression (JSOP_CASEX) +test("switch (i) {\n" + + " case i + 1 - i:\n" + + " default:\n" + + " " + long_throw_stmt + + " case i + i:\n" + + " i++; break; }"); + +// long case when JSOP_CASE is used (JSOP_DEFAULTX) +test("switch (i) {\n" + + " case i + 1 - i:\n" + + " " + long_throw_stmt + + " default:\n" + + " i++; break; }"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-01.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-01.js new file mode 100644 index 00000000000..390e0f248c4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-01.js @@ -0,0 +1,25 @@ +// Basic getOffsetLine test, using Error.lineNumber as the gold standard. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits; +dbg.onDebuggerStatement = function (frame) { + var knownLine = frame.eval("line").return; + assertEq(frame.script.getOffsetLine(frame.offset), knownLine); + hits++; +}; + +hits = 0; +g.eval("var line = new Error().lineNumber; debugger;"); +assertEq(hits, 1); + +hits = 0; +g.eval("var s = 2 + 2;\n" + + "s += 2;\n" + + "line = new Error().lineNumber; debugger;\n" + + "s += 2;\n" + + "s += 2;\n" + + "line = new Error().lineNumber; debugger;\n" + + "s += 2;\n" + + "assertEq(s, 12);\n"); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-02.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-02.js new file mode 100644 index 00000000000..2677ce20874 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-getOffsetLine-02.js @@ -0,0 +1,19 @@ +// Frame.script/offset and Script.getOffsetLine work in non-top frames. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + var a = []; + for (; frame.type == "call"; frame = frame.older) + a.push(frame.script.getOffsetLine(frame.offset) - g.line0); + assertEq(a.join(","), "1,2,3,4"); + hits++; +}; +g.eval("var line0 = Error().lineNumber;\n" + + "function f0() { debugger; }\n" + + "function f1() { f0(); }\n" + + "function f2() { f1(); }\n" + + "function f3() { f2(); }\n" + + "f3();\n"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/Script-startLine.js b/deps/mozjs/js/src/jit-test/tests/debug/Script-startLine.js new file mode 100644 index 00000000000..78fb14c4017 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/Script-startLine.js @@ -0,0 +1,43 @@ +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var start, count; +dbg.onDebuggerStatement = function (frame) { + assertEq(start, undefined); + start = frame.script.startLine; + count = frame.script.lineCount; + assertEq(typeof frame.script.url, 'string'); +}; + +function test(f) { + start = count = g.first = g.last = undefined; + f(); + assertEq(start, g.first); + assertEq(count, g.last + 1 - g.first); + print(start, count); +} + +test(function () { + g.eval("first = Error().lineNumber;\n" + + "debugger;\n" + + "last = Error().lineNumber;\n"); +}); + +test(function () { + g.evaluate("first = Error().lineNumber;\n" + + "debugger;\n" + + Array(17000).join("\n") + + "last = Error().lineNumber;\n"); +}); + +test(function () { + g.eval("function f1() { first = Error().lineNumber\n" + + " debugger;\n" + + " last = Error().lineNumber; }\n" + + "f1();"); +}); + +g.eval("function f2() {\n" + + " eval('first = Error().lineNumber\\n\\ndebugger;\\n\\nlast = Error().lineNumber;');\n" + + "}\n"); +test(g.f2); +test(g.f2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-01.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-01.js new file mode 100644 index 00000000000..1e72a80c603 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-01.js @@ -0,0 +1,22 @@ +// Basic breakpoint test. + +var g = newGlobal('new-compartment'); +g.s = ''; +var handler = { + hit: function (frame) { + assertEq(this, handler); + g.s += '1'; + } +}; +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + g.s += '0'; + var line0 = frame.script.getOffsetLine(frame.offset); + var offs = frame.script.getLineOffsets(line0 + 2); + for (var i = 0; i < offs.length; i++) + frame.script.setBreakpoint(offs[i], handler); +}; +g.eval("debugger;\n" + + "s += 'a';\n" + // line0 + 1 + "s += 'b';\n"); // line0 + 2 +assertEq(g.s, "0a1b"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-02.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-02.js new file mode 100644 index 00000000000..68aa5aec64f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-02.js @@ -0,0 +1,15 @@ +// Setting a breakpoint in a non-debuggee Script is an error. + +load(libdir + "asserts.js"); + +var g1 = newGlobal('new-compartment'); +var g2 = g1.eval("newGlobal('same-compartment')"); +g2.eval("function f() { return 2; }"); +g1.f = g2.f; + +var dbg = Debugger(g1); +var s; +dbg.onDebuggerStatement = function (frame) { s = frame.eval("f").return.script; }; +g1.eval("debugger;"); + +assertThrowsInstanceOf(function () { s.setBreakpoint(0, {}); }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-03.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-03.js new file mode 100644 index 00000000000..bb2500e8fc4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-03.js @@ -0,0 +1,16 @@ +// Setting a breakpoint in a script we are no longer debugging is an error. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(); +var gobj = dbg.addDebuggee(g); +g.eval("function f() { return 2; }"); + +var s; +dbg.onDebuggerStatement = function (frame) { s = frame.eval("f").return.script; }; +g.eval("debugger;"); +s.setBreakpoint(0, {}); // ok + +dbg.removeDebuggee(gobj); +assertThrowsInstanceOf(function () { s.setBreakpoint(0, {}); }, Error); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-04.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-04.js new file mode 100644 index 00000000000..350ad4365cb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-04.js @@ -0,0 +1,30 @@ +// Hitting a breakpoint with no hit method does nothing. + +var g = newGlobal('new-compartment'); +g.s = ''; +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " debugger;\n" + // line0 + 2 + " s += 'x';\n" + // line0 + 3 + "}\n") +var dbg = Debugger(g); +var bp = []; +dbg.onDebuggerStatement = function (frame) { + g.s += 'D'; + var arr = frame.script.getLineOffsets(g.line0 + 3); + for (var i = 0; i < arr.length; i++) { + var obj = {}; + bp[i] = obj; + frame.script.setBreakpoint(arr[i], obj); + } +}; + +g.f(); +assertEq(g.s, "Dx"); + +dbg.onDebuggerStatement = undefined; + +for (var i = 0; i < bp.length; i++) + bp[i].hit = function () { g.s += 'B'; }; +g.f(); +assertEq(g.s, "DxBx"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-05.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-05.js new file mode 100644 index 00000000000..1ecfbb7d96f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-05.js @@ -0,0 +1,19 @@ +// If the offset parameter to setBreakpoint is invalid, throw an error. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame) { + // We assume at least one offset between 0 and frame.offset is invalid. + assertThrowsInstanceOf( + function () { + for (var i = 0; i < frame.offset; i++) + frame.script.setBreakpoint(i, {}); + }, + Error); + hits++; +}; +g.eval("x = 256; debugger;"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-06.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-06.js new file mode 100644 index 00000000000..cb640a1b6ae --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-06.js @@ -0,0 +1,20 @@ +// The argument to a breakpoint hit method is a frame. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame1) { + function hit(frame2) { + assertEq(frame2, frame1); + hits++; + } + var s = frame1.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], {hit: hit}); +}; +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "x = 1;\n"); // line0 + 2 +assertEq(hits, 1); +assertEq(g.x, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-07.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-07.js new file mode 100644 index 00000000000..75dc96ecfb8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-07.js @@ -0,0 +1,30 @@ +// Code runs fine if do-nothing breakpoints are set on every line. + +var g = newGlobal('new-compartment'); +var src = ("var line0 = Error().lineNumber;\n" + + "function gcd(a, b) {\n" + // line0 + 1 + " if (a > b)\n" + // line0 + 2 + " return gcd(b, a);\n" + // line0 + 3 + " var c = b % a;\n" + // line0 + 4 + " if (c === 0)\n" + // line0 + 5 + " return a;\n" + // line0 + 6 + " return gcd(c, a);\n" + // line0 + 7 + "}\n"); // line0 + 8 +g.eval(src); + +var dbg = Debugger(g); +var hits = 0 ; +dbg.onDebuggerStatement = function (frame) { + var s = frame.eval("gcd").return.script; + var offs; + for (var lineno = g.line0 + 2; (offs = s.getLineOffsets(lineno)).length > 0; lineno++) { + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], {hit: function (f) { hits++; }}); + } + assertEq(lineno > g.line0 + 7, true); + assertEq(lineno <= g.line0 + 9, true); +}; + +g.eval("debugger;"); +assertEq(g.gcd(31 * 7 * 5 * 3 * 2, 11 * 3 * 3 * 2), 6); +assertEq(hits >= 18, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-08.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-08.js new file mode 100644 index 00000000000..c6864932457 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-08.js @@ -0,0 +1,31 @@ +// Breakpoints are dropped from eval scripts when they finish executing. +// (The eval cache doesn't cache breakpoints.) + +var g = newGlobal('new-compartment'); + +g.line0 = undefined; +g.eval("function f() {\n" + + " return eval(s);\n" + + "}\n"); +g.s = ("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "result = 'ok';\n"); // line0 + 2 + +var dbg = Debugger(g); +var hits = 0, bphits = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.type, 'eval'); + assertEq(frame.script.getBreakpoints().length, 0); + var h = {hit: function (frame) { bphits++; }}; + var offs = frame.script.getLineOffsets(g.line0 + 2); + for (var i = 0; i < offs.length; i++) + frame.script.setBreakpoint(offs[i], h); + hits++; +}; + +for (var i = 0; i < 3; i++) { + assertEq(g.f(), 'ok'); + assertEq(g.result, 'ok'); +} +assertEq(hits, 3); +assertEq(bphits, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-01.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-01.js new file mode 100644 index 00000000000..0dd133df208 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-01.js @@ -0,0 +1,25 @@ +// Handlers for breakpoints in an eval script are live as long as the script is on the stack. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var log = ''; +dbg.onDebuggerStatement = function (frame) { + function handler(i) { + return {hit: function () { log += '' + i; }}; + } + + var s = frame.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var i = 0; i < 7; i++) { + var h = handler(i); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], h); + } + gc(); +}; + + +g.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "x = 1;\n"); // line0 + 2 +assertEq(log, '0123456'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-02.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-02.js new file mode 100644 index 00000000000..45c49826b47 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-02.js @@ -0,0 +1,28 @@ +// A Debugger with live breakpoints is live. + +var g = newGlobal('new-compartment'); +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " return 2;\n" + // line0 + 2 + "}\n"); + +var N = 4; +var hits = 0; +for (var i = 0; i < N; i++) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var handler = {hit: function () { hits++; }}; + var s = frame.eval("f").return.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], handler); + }; + g.eval('debugger;'); + dbg.onDebuggerStatement = undefined; + dbg = null; +} + +gc(); + +assertEq(g.f(), 2); +assertEq(hits, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-03.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-03.js new file mode 100644 index 00000000000..1ee23c24526 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-03.js @@ -0,0 +1,25 @@ +// |jit-test| debug +// The GC can cope with old and new breakpoints at the same position. + +// This is a regression test for a bug Bill McCloskey found just by looking at +// the source code. See bug 677386 comment 8. Here we're testing that the trap +// string is correctly marked. (The silly expression for the trap string is to +// ensure that it isn't constant-folded; it's harder to get a compile-time +// constant to be GC'd.) + +var g = newGlobal('new-compartment'); +g.eval("var d = 0;\n" + + "function f() { return 'ok'; }\n" + + "trap(f, 0, Array(17).join('\\n') + 'd++;');\n"); + +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); +var fw = gw.getOwnPropertyDescriptor("f").value; +var bp = {hits: 0, hit: function (frame) { this.hits++; }}; +fw.script.setBreakpoint(0, bp); + +gc(); + +g.f(); +assertEq(g.d, 1); +assertEq(bp.hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-04.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-04.js new file mode 100644 index 00000000000..fefeb259908 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-04.js @@ -0,0 +1,23 @@ +// Enabled debuggers keep breakpoint handlers alive. + +var g = newGlobal('new-compartment'); +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " return 2;\n" + // line0 + 2 + "}\n"); +var N = 4; +var hits = 0; +for (var i = 0; i < N; i++) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var handler = {hit: function () { hits++; }}; + var s = frame.eval("f").return.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], handler); + }; +} +g.eval('debugger;'); +gc({}); +assertEq(g.f(), 2); +assertEq(hits, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-05.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-05.js new file mode 100644 index 00000000000..8093b61b69a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-gc-05.js @@ -0,0 +1,25 @@ +// Disabled debuggers keep breakpoint handlers alive. + +var g = newGlobal('new-compartment'); +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " return 2;\n" + // line0 + 2 + "}\n"); +var N = 4; +var hits = 0; +for (var i = 0; i < N; i++) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var handler = {hit: function () { hits++; }}; + var s = frame.eval("f").return.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], handler); + }; +} +g.eval('debugger;'); +dbg.enabled = false; +gc({}); +dbg.enabled = true; +assertEq(g.f(), 2); +assertEq(hits, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-01.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-01.js new file mode 100644 index 00000000000..08fb1ab9a17 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-01.js @@ -0,0 +1,28 @@ +// A single Debugger object can set multiple breakpoints at an instruction. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var log = ''; +dbg.onDebuggerStatement = function (frame) { + log += 'D'; + function handler(i) { + return {hit: function (frame) { log += '' + i; }}; + } + var f = frame.eval("f").return; + var s = f.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var i = 0; i < 10; i++) { + var bp = handler(i); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], bp); + } + assertEq(f.call().return, 42); + log += 'X'; +}; + +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " return 42;\n" + // line0 + 2 + "}\n" + + "debugger;\n"); +assertEq(log, 'D0123456789X'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-02.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-02.js new file mode 100644 index 00000000000..4cc4297df33 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-02.js @@ -0,0 +1,42 @@ +// After clearing one breakpoint, another breakpoint at the same instruction still works. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var script = null; +var handlers = []; +dbg.onDebuggerStatement = function (frame) { + function handler(i) { + return {hit: function (frame) { g.log += '' + i; }}; + } + var f = frame.eval("f").return; + var s = f.script; + if (script === null) + script = s; + else + assertEq(s, script); + + var offs = s.getLineOffsets(g.line0 + 3); + for (var i = 0; i < 3; i++) { + handlers[i] = handler(i); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], handlers[i]); + } +}; + +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " log += 'x';\n" + // line0 + 2 + " log += 'y';\n" + // line0 + 3 + "}\n" + + "debugger;\n"); +assertEq(handlers.length, 3); + +g.log = ''; +g.f(); +assertEq(g.log, 'x012y'); + +script.clearBreakpoint(handlers[0]); + +g.log = ''; +g.f(); +assertEq(g.log, 'x12y'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-03.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-03.js new file mode 100644 index 00000000000..39e10aa13f8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-03.js @@ -0,0 +1,27 @@ +// Multiple Debugger objects can set breakpoints at the same instruction. + +var g = newGlobal('new-compartment'); +function attach(g, i) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var s = frame.eval("f").return.script; + var offs = s.getLineOffsets(g.line0 + 3); + for (var j = 0; j < offs.length; j++) + s.setBreakpoint(offs[j], {hit: function () { g.log += "" + i; }}); + }; +} + +g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " log += 'a';\n" + // line0 + 2 + " log += 'b';\n" + // line0 + 3 + "}\n"); + +for (var i = 0; i < 3; i++) + attach(g, i); + +g.log = ''; +g.eval('debugger;'); +g.log += 'x'; +g.f(); +assertEq(g.log, 'xa012b'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-04.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-04.js new file mode 100644 index 00000000000..4819c48f146 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-multi-04.js @@ -0,0 +1,48 @@ +// After clearing one breakpoint, another breakpoint from a different Debugger object at the same instruction still works. + +function test(which) { + var g = newGlobal('new-compartment'); + g.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " return " + which + ";\n" + // line0 + 2 + "}\n"); + + var log; + var scripts = []; + var handlers = []; + function addDebugger(g, i) { + var dbg = Debugger(g); + dbg.onDebuggerStatement = function (frame) { + var s = frame.eval("f").return.script; + scripts[i] = s; + var offs = s.getLineOffsets(g.line0 + 2); + var handler = {hit: function (frame) { log += '' + i; } }; + s.setBreakpoint(0, handler); + handlers[i] = handler; + }; + } + + var expected = ''; + for (var i = 0; i < 3; i++) { + addDebugger(g, i); + if (i !== which) + expected += '' + i; + } + g.eval('debugger;'); + + for (var i = 0; i < 3; i++) + assertEq(scripts[i].getBreakpoints()[0], handlers[i]); + + log = ''; + g.f(); + assertEq(log, '012'); + + scripts[which].clearAllBreakpoints(); + + log = ''; + g.f(); + assertEq(log, expected); +} + +for (var j = 0; j < 3; j++) + test(j); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-01.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-01.js new file mode 100644 index 00000000000..9d992966d01 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-01.js @@ -0,0 +1,25 @@ +// A breakpoint handler hit method can return {throw: exc} to throw an exception. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + function hit(frame) { + return {throw: frame.eval("new Error('PASS')").return}; + } + + var s = frame.script; + var offs = s.getLineOffsets(g.line0 + 3); + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], {hit: hit}); +}; + +g.s = null; +g.eval("line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "try {\n" + // line0 + 2 + " s = 'FAIL';\n" + // line0 + 3 + "} catch (exc) {\n" + + " assertEq(exc.constructor, Error);\n" + + " s = exc.message;\n" + + "}\n"); +assertEq(g.s, 'PASS'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-02.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-02.js new file mode 100644 index 00000000000..f4abbe3a7e8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-02.js @@ -0,0 +1,34 @@ +// A breakpoint handler hit method can return null to raise an uncatchable error. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + g.log += 'D'; + + function hit(frame) { + g.log += 'H'; + return null; + } + + var f = frame.eval("f").return; + var s = f.script; + var offs = s.getLineOffsets(g.line0 + 3); + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], {hit: hit}); + + var rv = f.call(); + assertEq(rv, null); + g.log += 'X'; +}; + +g.log = ''; +g.eval("line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " try {\n" + // line0 + 2 + " log += '3';\n" + // line0 + 3 + " } catch (exc) {\n" + + " log += '5';\n" + + " }\n" + + "}\n" + + "debugger;\n"); +assertEq(g.log, 'DHX'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-03.js b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-03.js new file mode 100644 index 00000000000..a3b47d2a3e5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/breakpoint-resume-03.js @@ -0,0 +1,30 @@ +// A breakpoint handler hit method can return {return: val} to force the frame to return. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + g.log += 'D'; + + function hit(frame) { + g.log += 'H'; + return {return: 'ok'}; + } + + var f = frame.eval("f").return; + var s = f.script; + var offs = s.getLineOffsets(g.line0 + 2); + for (var i = 0; i < offs.length; i++) + s.setBreakpoint(offs[i], {hit: hit}); + + var rv = f.call(); + assertEq(rv.return, 'ok'); + g.log += 'X'; +}; + +g.log = ''; +g.eval("line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " log += '2';\n" + // line0 + 2 + "}\n" + + "debugger;\n"); +assertEq(g.log, 'DHX'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/dispatch-01.js b/deps/mozjs/js/src/jit-test/tests/debug/dispatch-01.js new file mode 100644 index 00000000000..f2dd59594c6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/dispatch-01.js @@ -0,0 +1,22 @@ +// Test removing hooks during dispatch. + +var g = newGlobal('new-compartment'); +var log = ''; + +function addDebug(n) { + for (var i = 0; i < n; i++) { + var dbg = new Debugger(g); + dbg.num = i; + dbg.onDebuggerStatement = function (stack) { + log += this.num + ', '; + this.enabled = false; + this.onDebuggerStatement = undefined; + gc(); + }; + } + dbg = null; +} + +addDebug(10); +g.eval("debugger;"); +assertEq(log, '0, 1, 2, 3, 4, 5, 6, 7, 8, 9, '); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/dispatch-02.js b/deps/mozjs/js/src/jit-test/tests/debug/dispatch-02.js new file mode 100644 index 00000000000..cd72fdbbba6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/dispatch-02.js @@ -0,0 +1,21 @@ +// Disabling a Debugger object causes events to stop being delivered to it +// immediately, even if we're in the middle of dispatching. + +var g = newGlobal('new-compartment'); +var log; + +var arr = []; +for (var i = 0; i < 4; i++) { + arr[i] = new Debugger(g); + arr[i].num = i; + arr[i].onDebuggerStatement = function () { + log += this.num; + // Disable them all. + for (var j = 0; j < arr.length; j++) + arr[j].enabled = false; + }; +} + +log = ''; +g.eval("debugger; debugger;"); +assertEq(log, '0'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-01.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-01.js new file mode 100644 index 00000000000..a2829b68edb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-01.js @@ -0,0 +1,20 @@ +// Debuggers with enabled hooks should not be GC'd even if they are otherwise +// unreachable. + +var g = newGlobal('new-compartment'); +var actual = 0; +var expected = 0; + +function f() { + for (var i = 0; i < 20; i++) { + var dbg = new Debugger(g); + dbg.num = i; + dbg.onDebuggerStatement = function (stack) { actual += this.num; }; + expected += i; + } +} + +f(); +gc(); gc(); gc(); +g.eval("debugger;"); +assertEq(actual, expected); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-02.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-02.js new file mode 100644 index 00000000000..f3104fcf78a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-02.js @@ -0,0 +1,28 @@ +// Dispatching an event to a debugger must keep enough of it gc-alive to avoid +// crashing. + +var g = newGlobal('new-compartment'); +var hits; + +function addDebug() { + // The loop is here to defeat the conservative GC. :-\ + for (var i = 0; i < 4; i++) { + var dbg = new Debugger(g); + dbg.onDebuggerStatement = function (stack) { + hits++; + this.enabled = false; + this.onDebuggerStatement = undefined; + gc(); + }; + if (i > 0) { + dbg.enabled = false; + dbg.onDebuggerStatement = undefined; + dbg = null; + } + } +} + +addDebug(); +hits = 0; +g.eval("debugger;"); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-03.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-03.js new file mode 100644 index 00000000000..b110aac683e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-03.js @@ -0,0 +1,24 @@ +// Storing a property on a Debugger.Object protects it from GC as long as the +// referent is alive. + +var g = newGlobal('new-compartment'); +var N = g.N = 3; +var dbg = Debugger(g); + +var i = 0; +dbg.onDebuggerStatement = function (frame) { + frame.arguments[0].id = i++; +}; +g.eval("function f(x) { debugger; }"); +g.eval("var arr = [], j; for (j = 0; j < N; j++) arr[j] = {};"); +g.eval("for (j = 0; j < N; j++) f(arr[j]);"); +assertEq(i, N); + +gc(); gc(); + +i = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(frame.arguments[0].id, i++) +} +g.eval("for (j = 0; j < N; j++) f(arr[j]);"); +assertEq(i, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-04.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-04.js new file mode 100644 index 00000000000..a4ee0439240 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-04.js @@ -0,0 +1,25 @@ +// Storing a Debugger.Object as a key in a WeakMap protects it from GC as long as +// the referent is alive. + +var g = newGlobal('new-compartment'); +var N = g.N = 10; +var dbg = Debugger(g); +var cache = new WeakMap; + +var i = 0; +dbg.onDebuggerStatement = function (frame) { + cache.set(frame.arguments[0], i++); +}; +g.eval("function f(x) { debugger; }"); +g.eval("var arr = [], j; for (j = 0; j < N; j++) arr[j] = {};"); +g.eval("for (j = 0; j < N; j++) f(arr[j]);"); +assertEq(i, N); + +gc(); gc(); + +i = 0; +dbg.onDebuggerStatement = function (frame) { + assertEq(cache.get(frame.arguments[0]), i++) +}; +g.eval("for (j = 0; j < N; j++) f(arr[j]);"); +assertEq(i, N); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-05.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-05.js new file mode 100644 index 00000000000..69a1c6a9442 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-05.js @@ -0,0 +1,41 @@ +// If a Debugger survives its debuggee, its object cache must still be swept. + +var g2arr = []; // non-debuggee globals +var xarr = []; // debuggee objects + +var N = 4, M = 4; +for (var i = 0; i < N; i++) { + var g1 = newGlobal('new-compartment'); + g1.M = M; + var dbg = new Debugger(g1); + var g2 = g1.eval("newGlobal('same-compartment')"); + g1.x = g2.eval("x = {};"); + + dbg.onDebuggerStatement = function (frame) { xarr.push(frame.eval("x").return); }; + g1.eval("debugger;"); + g2arr.push(g2); + + g1 = null; + gc(); +} + +// At least some of the debuggees have probably been collected at this +// point. It is nondeterministic, though. +assertEq(g2arr.length, N); +assertEq(xarr.length, N); + +// Try to make g2arr[i].eval eventually allocate a new object in the same +// location as a previously gc'd object. If the object caches are not being +// swept, the pointer coincidence will cause a Debugger.Object to be erroneously +// reused. +for (var i = 0; i < N; i++) { + var obj = xarr[i]; + for (j = 0; j < M; j++) { + assertEq(obj instanceof Debugger.Object, true); + g2arr[i].eval("x = x.__proto__ = {};"); + obj = obj.proto; + assertEq("seen" in obj, false); + obj.seen = true; + gc(); + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-06.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-06.js new file mode 100644 index 00000000000..ba37c1185f6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-06.js @@ -0,0 +1,6 @@ +// Debugger objects do not keep debuggee globals live. +var dbg = new Debugger; +for (var i = 0; i < 10; i++) + dbg.addDebuggee(newGlobal('new-compartment')); +gc(); +assertEq(dbg.getDebuggees().length < 10, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-07.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-07.js new file mode 100644 index 00000000000..079ce740f21 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-07.js @@ -0,0 +1,9 @@ +// Don't assert with dead Debugger.Object and live cross-compartment wrapper of referent. +var g = newGlobal('new-compartment'); +for (var j = 0; j < 4; j++) { + var dbg = new Debugger; + dbg.addDebuggee(g); + dbg.enabled = false; + dbg = null; + gc(); gc(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-08.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-08.js new file mode 100644 index 00000000000..5a725179108 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-08.js @@ -0,0 +1,22 @@ +// Debuggers with enabled onExceptionUnwind hooks should not be GC'd even if +// they are otherwise unreachable. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var actual = 0; +var expected = 0; + +function f() { + for (var i = 0; i < 20; i++) { + var dbg = new Debugger(g); + dbg.num = i; + dbg.onExceptionUnwind = function (stack, exc) { actual += this.num; }; + expected += i; + } +} + +f(); +gc(); +assertThrowsValue(function () { g.eval("throw 'fit';"); }, "fit"); +assertEq(actual, expected); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-09.2.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-09.2.js new file mode 100644 index 00000000000..740b802efa9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-09.2.js @@ -0,0 +1,16 @@ +// Bug 717104 - Unreachable debuggee globals should not keep their debuggers +// alive. The loop is to defeat conservative stack scanning; if the same stack +// locations are used each time through the loop, at least three of the +// debuggers should be collected. +// +// This is a slight modification of gc-09.js, which contains a cycle. + +for (var i = 0; i < 4; i++) { + var g = newGlobal('new-compartment'); + var dbg = new Debugger(g); + dbg.onDebuggerStatement = function () { throw "FAIL"; }; + dbg.o = makeFinalizeObserver(); +} + +gc(); +assertEq(finalizeCount() > 0, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-09.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-09.js new file mode 100644 index 00000000000..20f27fa88fe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-09.js @@ -0,0 +1,15 @@ +// Bug 717104 - Unreachable debuggee globals should not keep their debuggers +// alive. The loop is to defeat conservative stack scanning; if the same stack +// locations are used each time through the loop, at least three of the +// debuggers should be collected. + +for (var i = 0; i < 4; i++) { + var g = newGlobal('new-compartment'); + var dbg = new Debugger(g); + dbg.onDebuggerStatement = function () { throw "FAIL"; }; + dbg.o = makeFinalizeObserver(); + dbg.loop = g; // make a cycle of strong references with dbg and g +} + +gc(); +assertEq(finalizeCount() > 0, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-01.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-01.js new file mode 100644 index 00000000000..4f0450acd1f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-01.js @@ -0,0 +1,6 @@ +// A debugger can survive per-compartment GC. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +gc(g); +gc(this); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-02.js b/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-02.js new file mode 100644 index 00000000000..c6e47bd32ce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/gc-compartment-02.js @@ -0,0 +1,13 @@ +// Referents of Debugger.Objects in other compartments always survive per-compartment GC. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var arr = []; +dbg.onDebuggerStatement = function (frame) { arr.push(frame.eval("[]").return); }; +g.eval("for (var i = 0; i < 10; i++) debugger;"); +assertEq(arr.length, 10); + +gc(g); + +for (var i = 0; i < arr.length; i++) + assertEq(arr[i].class, "Array"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-01.js b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-01.js new file mode 100644 index 00000000000..e28d180e1a8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-01.js @@ -0,0 +1,7 @@ +var g = newGlobal('new-compartment'); +g.log = ''; + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (stack) { g.log += '!'; }; +assertEq(g.eval("log += '1'; debugger; log += '2'; 3;"), 3); +assertEq(g.log, '1!2'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-02.js b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-02.js new file mode 100644 index 00000000000..cf1c6121915 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-02.js @@ -0,0 +1,22 @@ +// Activity in the debugger compartment should not trigger debug hooks. + +var g = newGlobal('new-compartment'); +var hit = false; + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (stack) { hit = true; }; + +debugger; +assertEq(hit, false, "raw debugger statement in debugger compartment should not hit"); + +g.f = function () { debugger; }; +g.eval("f();"); +assertEq(hit, false, "debugger statement in debugger compartment function should not hit"); + +g.outerEval = eval; +g.eval("outerEval('debugger;');"); +assertEq(hit, false, "debugger statement in debugger compartment eval code should not hit"); + +var g2 = newGlobal('new-compartment'); +g2.eval("debugger;"); +assertEq(hit, false, "debugger statement in unrelated non-debuggee compartment should not hit"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-03.js b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-03.js new file mode 100644 index 00000000000..e5e922e0d9e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-03.js @@ -0,0 +1,13 @@ +// A debugger statement in an onDebuggerStatement hook should not reenter. + +var g = newGlobal('new-compartment'); +var calls = 0; + +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (stack) { + calls++; + debugger; +}; + +assertEq(g.eval("debugger; 7;"), 7); +assertEq(calls, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-04.js b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-04.js new file mode 100644 index 00000000000..f36c35f099d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onDebuggerStatement-04.js @@ -0,0 +1,10 @@ +var g = newGlobal('new-compartment'); +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + var code = "assertEq(c, 'ok');\n"; + assertEq(frame.evalWithBindings("eval(s)", {s: code, a: 1234}).return, undefined); +}; +g.eval("function first() { return second(); }"); +g.eval("function second() { return eval('third()'); }"); +g.eval("function third() { debugger; }"); +g.evaluate("first();"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-01.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-01.js new file mode 100644 index 00000000000..8d674c0cb40 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-01.js @@ -0,0 +1,29 @@ +// Basic enterFrame hook tests. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var type; +dbg.onEnterFrame = function (frame) { + try { + assertEq(frame instanceof Debugger.Frame, true); + assertEq(frame.live, true); + type = frame.type; + } catch (exc) { + type = "Exception thrown: " + exc; + } +}; + +function test(f, expected) { + type = undefined; + f(); + assertEq(type, expected); +} + +// eval triggers the hook +test(function () { g.eval("function h() { return 1; }"); }, "eval"); + +// function calls trigger it +test(function () { assertEq(g.h(), 1); }, "call"); + +// global scripts trigger it +test(function () { g.evaluate("var x = 5;"); assertEq(g.x, 5); }, "global"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-02.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-02.js new file mode 100644 index 00000000000..41bcd277705 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-02.js @@ -0,0 +1,22 @@ +// enterFrame test with recursive debuggee function. + +var g = newGlobal('new-compartment'); +var N = g.N = 9; +g.eval("function f(i) { if (i < N) f(i + 1); }"); + +var dbg = Debugger(g); +var arr = []; +dbg.onEnterFrame = function (frame) { + var i; + for (i = 0; i < arr.length; i++) + assertEq(frame !== arr[i], true); + arr[i] = frame; + + // Check that the whole stack is as expected. + var j = i; + for (; frame; frame = frame.older) + assertEq(arr[j--], frame); +}; + +g.f(0); +assertEq(arr.length, N + 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-03.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-03.js new file mode 100644 index 00000000000..55ae4ff8b8e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-03.js @@ -0,0 +1,23 @@ +// frame.eval works in the enterFrame hook. +// It triggers the enterFrame hook again, recursively. (!) + +var g = newGlobal('new-compartment'); +g.a = "."; + +var dbg = Debugger(g); +var nestCount = 0, N = 9; +var log = ""; +dbg.onEnterFrame = function (frame) { + assertEq(frame.type, "eval"); + if (nestCount < N) { + log += '('; + nestCount++; + var a = frame.eval("a").return; + log += a; + nestCount--; + log += ')'; + } +}; + +assertEq(g.eval("a"), "."); +assertEq(log, Array(N + 1).join("(") + Array(N + 1).join(".)")); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-04.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-04.js new file mode 100644 index 00000000000..2ad582cb3da --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-04.js @@ -0,0 +1,49 @@ +// We detect and stop the runaway recursion caused by making onEnterFrame a +// wrapper of a debuggee function. + +// This is all a bit silly. In any reasonable design, both debugger re-entry +// (the second onEnterFrame invocation) and debuggee re-entry (the call to g.f +// from within the debugger, not via a Debugger invocation function) would raise +// errors immediately. We have plans to do so, but in the mean time, we settle +// for at least detecting the recursion. + +load(libdir + 'asserts.js'); + +var g = newGlobal('new-compartment'); +g.eval("function f(frame) { n++; return 42; }"); +g.n = 0; + +var dbg = Debugger(g); + +// Register the debuggee function as the onEnterFrame handler. When we first +// call or eval in the debuggee: +// +// - The onEnterFrame call reporting that frame's creation is itself an event +// that must be reported, so we call onEnterFrame again. +// +// - SpiderMonkey detects the out-of-control recursion, and generates a "too +// much recursion" InternalError in the youngest onEnterFrame call. +// +// - We don't catch it, so the onEnterFrame handler call itself throws. +// +// - Since the Debugger doesn't have an uncaughtExceptionHook (it can't; such a +// hook would itself raise a "too much recursion" exception), Spidermonkey +// reports the exception immediately and terminates the debuggee --- which is +// the next-older onEnterFrame call. +// +// - This termination propagates all the way out to the initial attempt to +// create a frame in the debuggee. +dbg.onEnterFrame = g.f; + +// Get a Debugger.Object instance referring to f. +var debuggeeF = dbg.addDebuggee(g.f); + +// Using f.call allows us to catch the termination. +assertEq(debuggeeF.call(), null); + +// We should never actually begin execution of the function. +assertEq(g.n, 0); + +// When an error is reported, the shell usually exits with a nonzero exit code. +// If we get here, the test passed, so override that behavior. +quit(0); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-05.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-05.js new file mode 100644 index 00000000000..5377f913b61 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-05.js @@ -0,0 +1,15 @@ +// The tracejit does not prevent onEnterFrame from being called. + +var g = newGlobal('new-compartment'); +g.eval("function f() { return 1; }\n"); +var N = g.N = 11; +g.eval("function h() {\n" + + " for (var i = 0; i < N; i += f()) {}\n" + + "}"); +g.h(); // record loop + +var dbg = Debugger(g); +var log = ''; +dbg.onEnterFrame = function (frame) { log += frame.callee.name; }; +g.h(); +assertEq(log, 'h' + Array(N + 1).join('f')); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-06.js b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-06.js new file mode 100644 index 00000000000..132f4af36e6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onEnterFrame-06.js @@ -0,0 +1,19 @@ +// The tracejit does not prevent onEnterFrame from being called after entering +// a debuggee compartment from a non-debuggee compartment. + +var g1 = newGlobal('new-compartment'); +var g2 = newGlobal('new-compartment'); +var dbg = Debugger(g1, g2); +dbg.removeDebuggee(g2); // turn off debug mode in g2 + +g1.eval("function f() { return 1; }\n"); +var N = g1.N = 11; +g1.eval("function h() {\n" + + " for (var i = 0; i < N; i += f()) {}\n" + + "}"); +g1.h(); // record loop + +var log = ''; +dbg.onEnterFrame = function (frame) { log += frame.callee.name; }; +g1.h(); +assertEq(log, 'h' + Array(N + 1).join('f')); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-01.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-01.js new file mode 100644 index 00000000000..d21c198398b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-01.js @@ -0,0 +1,24 @@ +// Basic onExceptionUnwind hook test. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hit = false; +dbg.onExceptionUnwind = function (frame, exc) { + // onExceptionUnwind is called multiple times as the stack is unwound. + // Only check the first hit. + assertEq(arguments.length, 2); + assertEq(frame instanceof Debugger.Frame, true); + if (!hit) { + assertEq(exc, 101); + assertEq(frame.type, "call"); + assertEq(frame.callee.name, "f"); + assertEq(frame.older.type, "eval"); + hit = true; + } +}; + +g.eval("function f() { throw 101; }"); +assertThrowsValue(function () { g.eval("f();"); }, 101); +assertEq(hit, true); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-02.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-02.js new file mode 100644 index 00000000000..8f8bb2a6e59 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-02.js @@ -0,0 +1,48 @@ +// |jit-test| debug +// The onExceptionUnwind hook is called multiple times as the stack unwinds. + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.dbg = null; +g.eval("(" + function () { + dbg = new Debugger(debuggeeGlobal); + dbg.onExceptionUnwind = function (frame, exc) { + assertEq(frame instanceof Debugger.Frame, true); + assertEq(exc instanceof Debugger.Object, true); + var s = '!'; + for (var f = frame; f; f = f.older) + if (f.type === "call") + s += f.callee.name; + s += ', '; + debuggeeGlobal.log += s; + }; + } + ")();"); + +var log; + +function k() { + throw new Error("oops"); // hook call 1 +} + +function j() { + k(); // hook call 2 + log += 'j-unreached, '; +} + +function h() { + j(); // hook call 3 + log += 'h-unreached, '; +} + +function f() { + try { + h(); // hook call 4 + } catch (exc) { + log += 'f-catch'; + } +} + +log = ''; +f(); +g.dbg.enabled = false; +assertEq(log, '!kjhf, !jhf, !hf, !f, f-catch'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-03.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-03.js new file mode 100644 index 00000000000..97c166188c2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-03.js @@ -0,0 +1,58 @@ +// |jit-test| debug +// The onExceptionUnwind hook is called multiple times as the stack unwinds. + +var g = newGlobal('new-compartment'); +g.debuggeeGlobal = this; +g.dbg = null; +g.eval("(" + function () { + dbg = new Debugger(debuggeeGlobal); + dbg.onExceptionUnwind = function (frame, exc) { + assertEq(frame instanceof Debugger.Frame, true); + assertEq(exc instanceof Debugger.Object, true); + var s = '!'; + for (var f = frame; f; f = f.older) + if (f.type === "call") + s += f.callee.name; + s += ', '; + debuggeeGlobal.log += s; + }; + } + ")();"); + +var log; + +function k() { + try { + throw new Error("oops"); // hook call 1 + } finally { + log += 'k-finally, '; + } // hook call 2 +} + +function j() { + k(); // hook call 3 + log += 'j-unreached, '; +} + +function h() { + try { + j(); // hook call 4 + log += 'h-unreached, '; + } catch (exc) { + log += 'h-catch, '; + throw exc; // hook call 5 + } +} + +function f() { + try { + h(); // hook call 6 + } catch (exc) { + log += 'f-catch, '; + } + log += 'f-after, '; +} + +log = ''; +f(); +g.dbg.enabled = false; +assertEq(log, '!kjhf, k-finally, !kjhf, !jhf, !hf, h-catch, !hf, !f, f-catch, f-after, '); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-04.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-04.js new file mode 100644 index 00000000000..b7a580dd6ad --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-04.js @@ -0,0 +1,17 @@ +// onExceptionUnwind is not called for exceptions thrown and handled in the debugger. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +g.log = ''; +dbg.onDebuggerStatement = function (frame) { + try { + throw new Error("oops"); + } catch (exc) { + g.log += exc.message; + } +}; +dbg.onExceptionUnwind = function (frame) { + g.log += 'BAD'; +}; + +g.eval("debugger; log += ' ok';"); +assertEq(g.log, 'oops ok'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-05.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-05.js new file mode 100644 index 00000000000..e99be046a52 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-05.js @@ -0,0 +1,13 @@ +// |jit-test| debug +// onExceptionUnwind returning undefined does not affect the thrown exception. + +var g = newGlobal('new-compartment'); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () {};"); + +var obj = new Error("oops"); +try { + throw obj; +} catch (exc) { + assertEq(exc, obj); +} diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-06.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-06.js new file mode 100644 index 00000000000..16c0d7b51ea --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-06.js @@ -0,0 +1,14 @@ +// |jit-test| debug +// onExceptionUnwind assigning to argv[1] does not affect the thrown exception. + +var g = newGlobal('new-compartment'); +g.parent = this; +g.eval("function f(frame, exc) { f2 = function () { return exc; }; exc = 123; }"); +g.eval("new Debugger(parent).onExceptionUnwind = f;"); + +var obj = new Error("oops"); +try { + throw obj; +} catch (exc) { + assertEq(exc, obj); +} diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-07.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-07.js new file mode 100644 index 00000000000..8a4a3010af2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-07.js @@ -0,0 +1,15 @@ +// Unwinding due to uncatchable errors does not trigger onExceptionUnwind. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var hits = 0; +dbg.onExceptionUnwind = function (frame, value) { hits = 'BAD'; }; +dbg.onDebuggerStatement = function (frame) { + if (hits++ === 0) + assertEq(frame.eval("debugger;"), null); + else + return null; +} + +assertEq(g.eval("debugger; 2"), 2); +assertEq(hits, 2); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-08.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-08.js new file mode 100644 index 00000000000..9cea3e626e2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-08.js @@ -0,0 +1,18 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// throws an uncaught exception. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.live, true); + throw 'unhandled'; +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); + +// Don't fail just because we reported an uncaught exception. +quit(0); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-09.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-09.js new file mode 100644 index 00000000000..41c364f5914 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-09.js @@ -0,0 +1,15 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// terminates execution. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.live, true); + return null; +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-10.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-10.js new file mode 100644 index 00000000000..719245c8373 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-10.js @@ -0,0 +1,16 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// terminates execution. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.type, 'eval'); + assertEq(frame.live, true); + terminate(); +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-01.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-01.js new file mode 100644 index 00000000000..b34d0d872ac --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-01.js @@ -0,0 +1,9 @@ +// Check that an onExceptionUnwind hook can force a frame to return a value early. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onExceptionUnwind = function (frame, exc) { + return { return:"sproon" }; +}; +g.eval("function f() { throw 'ksnife'; }"); +assertEq(g.f(), "sproon"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-02.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-02.js new file mode 100644 index 00000000000..8f93336472b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-02.js @@ -0,0 +1,10 @@ +// Check that if an onExceptionUnwind hook forces a constructor frame to +// return a primitive value, it still gets wrapped up in an object. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onExceptionUnwind = function (frame, exc) { + return { return:"sproon" }; +}; +g.eval("function f() { throw 'ksnife'; }"); +assertEq(typeof new g.f, "object"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-03.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-03.js new file mode 100644 index 00000000000..37053a65537 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-03.js @@ -0,0 +1,11 @@ +// Check that an onExceptionUnwind hook can force a frame to throw a different exception. + +load(libdir + "asserts.js"); + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +dbg.onExceptionUnwind = function (frame, exc) { + return { throw:"sproon" }; +}; +g.eval("function f() { throw 'ksnife'; }"); +assertThrowsValue(g.f, "sproon"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-04.js b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-04.js new file mode 100644 index 00000000000..c2221642811 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-04.js @@ -0,0 +1,17 @@ +// Check that an onExceptionUnwind hook can force a frame to terminate. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +g.eval("function f() { throw 'ksnife'; }"); +var log = ''; +dbg.onDebuggerStatement = function (frame) { + log += 'd1'; + assertEq(frame.eval("f();"), null); + log += 'd2'; +}; +dbg.onExceptionUnwind = function (frame, exc) { + log += 'u'; + return null; +}; +g.eval("debugger;"); +assertEq(log, "d1ud2"); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-01.js b/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-01.js new file mode 100644 index 00000000000..906997b68a3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-01.js @@ -0,0 +1,44 @@ +// Basic newScript hook tests. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var seen = WeakMap(); +var hits = 0; +dbg.onNewScript = function (s) { + // Exceptions thrown from onNewScript are swept under the rug, but they + // will at least prevent hits from being the expected number. + assertEq(s instanceof Debugger.Script, true); + assertEq(!seen.has(s), true); + seen.set(s, true); + hits++; +}; + +dbg.uncaughtExceptionHook = function () { hits = -999; }; + +// eval code +hits = 0; +assertEq(g.eval("2 + 2"), 4); +assertEq(hits, 1); + +hits = 0; +assertEq(g.eval("eval('2 + 3')"), 5); +assertEq(hits, 2); + +// global code +hits = 0; +g.evaluate("3 + 4"); +assertEq(hits, 1); + +// function code +hits = 0; +var fn = g.Function("a", "return 5 + a;"); +assertEq(hits, 1); +assertEq(fn(8), 13); +assertEq(hits, 1); + +// cloning functions across compartments +var g2 = newGlobal('new-compartment'); +dbg.addDebuggee(g2, dbg); +hits = 0; +g2.clone(fn); +assertEq(hits, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-02.js b/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-02.js new file mode 100644 index 00000000000..2f1ebcc35f3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/debug/onNewScript-02.js @@ -0,0 +1,65 @@ +// Creating a new script with any number of subscripts triggers the newScript hook exactly once. + +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var seen = WeakMap(); +var hits; +dbg.onNewScript = function (s) { + assertEq(s instanceof Debugger.Script, true); + assertEq(!seen.has(s), true); + seen.set(s, true); + hits++; +}; + +dbg.uncaughtExceptionHook = function () { hits = -999; }; + +function test(f) { + hits = 0; + f(); + assertEq(hits, 1); +} + +// eval declaring a function +test(function () { g.eval("function A(m, n) { return m===0?n+1:n===0?A(m-1,1):A(m-1,A(m,n-1)); }"); }); + +// evaluate declaring a function +test(function () { g.eval("function g(a, b) { return b===0?a:g(b,a%b); }"); }); + +// eval declaring multiple functions +test(function () { + g.eval("function e(i) { return i===0||o(i-1); }\n" + + "function o(i) { return i!==0&&e(i-1); }\n"); +}); + +// eval declaring nested functions +test(function () { g.eval("function plus(x) { return function plusx(y) { return x + y; }; }"); }); + +// eval with a function-expression +test(function () { g.eval("[3].map(function (i) { return -i; });"); }); + +// eval with getters and setters +test(function () { g.eval("var obj = {get x() { return 1; }, set x(v) { print(v); }};"); }); + +// Function with nested functions +test(function () { return g.Function("a", "b", "return b - a;"); }); + +// cloning a function with nested functions +test(function () { g.clone(Function("x", "return x + 1")); }); + +// eval declaring a generator +test(function () { g.eval("function r(n) { for (var i=0;i= 0; i--) + x += arguments[i]; + return x; +} + +function bar() { + var x = 0; + for (var i = 0; i < arguments.length; i++) + x += arguments[i]; + return x; +} + +function baz(a,b,c,d,e) { + var x = 0; + for (var i = 0; i < arguments.length; i++) + x += arguments[i]; + return x; +} + +for (var i = 0; i < 10; i++) { + assertEq(foo(1,2,3,4,5), 15); + assertEq(bar(1,2.5,true,{valueOf:function() { return 10}},"five"), "14.5five"); + assertEq(baz(1,2,3,4,5), 15); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug549393-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug549393-1.js index d98cc0b45ea..817d1255b7e 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug549393-1.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug549393-1.js @@ -12,7 +12,7 @@ function MersenneTwister19937() { Array[mti] = 1 } }; - this.genrand_int32 = function() { + this.genrand_int32_t = function() { if (mti > 4) { mti = 0 } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug550665.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug550665.js index 6cfe99e0e76..816888b5ef8 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug550665.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug550665.js @@ -4,5 +4,5 @@ })(); this.__defineSetter__("l", function() { gc() }); -this.watch("l", function(x) { yield #1={} }); +this.watch("l", function(x) { yield {} }); l = true; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug554580-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug554580-3.js index 9ebde6d75f7..7728e5adf7e 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug554580-3.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug554580-3.js @@ -1,4 +1,4 @@ -// |jit-test| error: SyntaxError +// |jit-test| error: TypeError Function("\n\ for (a = 0; a < 3; a++) {\n\ if (a == 0) {} else {\n\ diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-call-newvar.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-call-newvar.js index eafcaa07610..1d53acf8816 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-call-newvar.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-call-newvar.js @@ -2,13 +2,12 @@ setDebug(true); function callee() { - assertJit(); evalInFrame(1, "var x = 'success'"); } -function caller() { - assertJit(); +function caller(code) { + eval(code); callee(); return x; } -assertEq(caller(), "success"); +assertEq(caller('var y = "ignominy"'), "success"); assertEq(typeof x, "undefined"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-getter-newvar.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-getter-newvar.js index d55e706a983..4de1fcbe0e9 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-getter-newvar.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-getter-newvar.js @@ -2,9 +2,9 @@ setDebug(true); this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); }); -function caller(obj) { - assertJit(); +function caller(code, obj) { + eval(code); // Make the compiler give up on binding analysis. obj.someProperty; return x; } -assertEq(caller(this), "success"); +assertEq(caller("var y = 'ignominy'", this), "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-global-newvar.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-global-newvar.js new file mode 100644 index 00000000000..2d363878be4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-global-newvar.js @@ -0,0 +1,9 @@ +// |jit-test| mjitalways;debug +setDebug(true); + +function callee() { + assertJit(); + evalInFrame(1, "var x = 'success'"); +} +callee(); +assertEq(x, "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js index fb048587faf..2b2439be2b9 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js @@ -2,9 +2,9 @@ setDebug(true); function nop(){} -function caller(obj) { - assertJit(); +function caller(code, obj) { + eval(code); // Make the compiler give up on binding analysis. return x; } -trap(caller, 7, "var x = 'success'; nop()"); -assertEq(caller(this), "success"); +trap(caller, 13, "var x = 'success'; nop()"); +assertEq(caller("var y = 'ignominy'", this), "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap.js index 43d013da935..e83941e1be8 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/eif-trap.js @@ -7,5 +7,5 @@ function caller(obj) { var x = "failure"; return x; } -trap(caller, 14, "x = 'success'; nop()"); +trap(caller, 15, "x = 'success'; nop()"); assertEq(caller(this), "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-1.js index ef77a86e530..41a6c82309a 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-1.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-1.js @@ -3,8 +3,8 @@ setDebug(true); var x = "failure"; function main() { x = "success"; } -/* The JSOP_STOP in a. */ -trap(main, 11, ""); +/* The JSOP_STOP in main. */ +trap(main, 10, ""); main(); assertEq(x, "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-2.js index c12fdff984c..9f2580a73fc 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-trap-2.js @@ -4,7 +4,7 @@ var x = "notset"; function main() { x = "failure"; } function success() { x = "success"; } -/* The JSOP_STOP in a. */ +/* The JSOP_STOP in main. */ trap(main, 10, "success()"); main(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-untrap.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-untrap.js index 68780212716..2ea10b561a1 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-untrap.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/simple-untrap.js @@ -4,9 +4,9 @@ var x = "notset"; function main() { x = "success"; } function failure() { x = "failure"; } -/* The JSOP_STOP in a. */ -trap(main, 8, "failure()"); -untrap(main, 8); +/* The JSOP_STOP in main. */ +trap(main, 10, "failure()"); +untrap(main, 10); main(); assertEq(x, "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js index d66c0c977c9..9234f163765 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js @@ -2,8 +2,8 @@ setDebug(true); x = "notset"; function main() { - /* The JSOP_STOP in a. */ - a = { valueOf: function () { trap(main, 36, "success()"); } }; + /* The JSOP_STOP in main. */ + a = { valueOf: function () { trap(main, 39, "success()"); } }; a + ""; x = "failure"; } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js index 5f0f41b6be2..3eaec1353b5 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js @@ -2,8 +2,8 @@ setDebug(true); x = "notset"; function main() { - /* The JSOP_STOP in a. */ - a = { valueOf: function () { trap(main, 57, "success()"); } }; + /* The JSOP_STOP in main. */ + a = { valueOf: function () { trap(main, 60, "success()"); } }; b = ""; eval(); a + b; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js index 16c9bd0a940..35ffb68d16f 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -5,7 +5,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 50, "success()"); + trap(myparent, 48, "success()"); } else { myparent(true); x = "failure"; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js index ef5b3f53a97..8f9c186dbad 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -7,14 +7,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 24, "success()"); + trap(myparent, 28, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 35, "myparent(true)"); +trap(myparent, 41, "myparent(true)"); function success() { x = "success"; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self.js index 5f8e11e97a4..d02977e9600 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/trap-self.js @@ -2,7 +2,7 @@ setDebug(true); x = "notset"; function main() { - /* The JSOP_STOP in a. */ + /* The JSOP_STOP in main. */ trap(main, 25, "success()"); x = "failure"; } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/untrap-self.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/untrap-self.js index b4b80d1a8df..f90ca06bcef 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/untrap-self.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug563000/untrap-self.js @@ -3,12 +3,12 @@ setDebug(true); x = "notset"; function main() { /* JSOP_STOP in main. */ - untrap(main, 23); + untrap(main, 22); x = "success"; } function failure() { x = "failure"; } /* JSOP_STOP in main. */ -trap(main, 23, "failure()"); +trap(main, 22, "failure()"); main(); assertEq(x, "success"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580883.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug580883.js deleted file mode 100644 index 90d8cb378cc..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580883.js +++ /dev/null @@ -1,6 +0,0 @@ -// |jit-test| error: invalid sharp variable use #1# -[] = #1# -with(7) { - var c -} - diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-2.js deleted file mode 100644 index fdc4f532105..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-2.js +++ /dev/null @@ -1,23 +0,0 @@ -assertEq( - (function () { - eval(); - var arr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ]; - - for (var i = 0; i < 4; ++i) { - var src = i * 8; - var dst = i * 8 + 7; - for (var j = 0; j < 4; ++j) { - [arr[dst--], arr[src++]] = [arr[src], arr[dst]]; - } - } - return arr; - })().toSource(), - "[7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 32]"); - -checkStats({ - recorderStarted: 2, - traceCompleted: 2, - sideExitIntoInterpreter: 3, - traceTriggered: 3 -}); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-3.js index 485f3065772..fb22882b101 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-3.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug580884-3.js @@ -1,10 +1,10 @@ function testDestructuring() { eval(); var t = 0; - for (var i = 0; i < HOTLOOP + 1; ++i) { + for (var i = 0; i < 9; ++i) { var [r, g, b] = [1, 1, 1]; t += r + g + b; } return t } -assertEq(testDestructuring(), (HOTLOOP + 1) * 3); +assertEq(testDestructuring(), (9) * 3); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-1.js index 9afcbdb8311..4949c92f61c 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-1.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-1.js @@ -1,4 +1,4 @@ -for (a = 0; a < HOTLOOP + 5; a++) { +for (a = 0; a < 13; a++) { (function e() { yield eval() }()) diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-2.js index 3acdbe36958..4e549867d59 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-2.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-2.js @@ -1,4 +1,4 @@ -for (a = 0; a < HOTLOOP + 5; a++) { +for (a = 0; a < 13; a++) { (function n() { with({}) { yield diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-3.js index 7f6c622b912..360399ea60e 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-3.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug588362-3.js @@ -1,4 +1,4 @@ -for (a = 0; a < HOTLOOP + 5; a++) { +for (a = 0; a < 13; a++) { (function n() { { function s() {} } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug600139.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug600139.js index 03284798640..2fdedf42d5c 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug600139.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug600139.js @@ -7,5 +7,4 @@ function f(a, b, c) { this.restoreWindow(a, b, c); eval(); } -dis(f); f(1, 2, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug601982.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug601982.js index b8744449293..7f5c93a3118 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug601982.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug601982.js @@ -22,7 +22,7 @@ function g(i) { } function f() { - for (var i = 0; i < RUNLOOP * 2; i++) { + for (var i = 0; i < 9 * 2; i++) { g(i); } } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-1.js deleted file mode 100644 index 63ec9d045d8..00000000000 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-1.js +++ /dev/null @@ -1,5 +0,0 @@ -// don't assert -new(function() { - #1# -}) - diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-2.js index b4fae0da18f..dc38cbb3307 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-2.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug606662-2.js @@ -1,5 +1,5 @@ -// don't crash -try{a()}catch(e){} -try{for(e in((JSON.e)(x=/x/)))throw []}catch(e){} -try{(function(){c()})()}catch(e){} -try{new function(){#1#}}catch(e){} +// don't crash +try{a()}catch(e){} +try{for(e in((JSON.e)(x=/x/)))throw []}catch(e){} +try{(function(){c()})()}catch(e){} +try{new function(){}}catch(e){} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617433.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617433.js new file mode 100644 index 00000000000..768f5a41493 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617433.js @@ -0,0 +1,12 @@ +// don't crash + +function foo(x) { + (x >>> 3.14); + (x >>> true); + (x >>> (0/0)); + (x >>> 100); + (x >>> -10); + (x >>> (1/0)); + (x >>> (void 0)); +} +foo(10); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617440.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617440.js new file mode 100644 index 00000000000..48311e76143 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617440.js @@ -0,0 +1,11 @@ + +function f() { + var x = 1.23; + function g() { + var y = x++; + assertEq(y, 1.23); + } + g(); + assertEq(x, 2.23); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617458.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617458.js new file mode 100644 index 00000000000..1dc9b2f87ce --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617458.js @@ -0,0 +1,6 @@ + +function f(x) { + var a = 4 | x; + a = +a; +} +f(10); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617460.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617460.js new file mode 100644 index 00000000000..5b9f7662fc7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617460.js @@ -0,0 +1,10 @@ + +function f() { + var x = NaN; + if (2 > 0) {} + var y = {}; + var z = (1234 - x); + y.foo = z; + assertEq(x, NaN); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617549.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617549.js new file mode 100644 index 00000000000..a48e80f3c59 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617549.js @@ -0,0 +1,9 @@ + +function f() { + var a, b, c; + a = -c; + b = c & 2; + a = b; + a = 123 * a; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617558.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617558.js new file mode 100644 index 00000000000..1bbd30f21a7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617558.js @@ -0,0 +1,7 @@ + +Array.prototype.__proto__ = Function.prototype; +var x = [1,2,3]; +x[0]; + +[].__proto__.foo = true; +eval("[]"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug617624.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617624.js new file mode 100644 index 00000000000..ebf1e661e86 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug617624.js @@ -0,0 +1,7 @@ + +function f() { + var x; + var a = x; + Boolean(a = Number(12.34)); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug618849.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618849.js new file mode 100644 index 00000000000..0df67a069f6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618849.js @@ -0,0 +1,11 @@ + +function f() { + function g() { + var b = x; + var c = b++ & b; + return c; + } + var x = x--; + return g(); +} +assertEq(f(), 0); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug618850.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618850.js new file mode 100644 index 00000000000..8c423d87b3c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618850.js @@ -0,0 +1,6 @@ +function f() { + var x = false; + NaN ? x = Math.floor() : x = Math.ceil(); + return x * 12345; +} +assertEq(f(), NaN); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug618863.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618863.js new file mode 100644 index 00000000000..9393361190a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug618863.js @@ -0,0 +1,23 @@ +function f() { + for(var i=0; i<3; i++) { + var x = -i / 100; + assertEq(x * -100, i); + } +} +f(); + +function g() { + for (var i = 0; i < 2; i++) { + var a = i ? true : false; + var x = -a / 100; + assertEq(x * -100, i); + } +} +g(); + +function h() { + for (var i = 0; i < 20; i++) + var x = (0x7ffffff4 + i) / 100; + assertEq(x, 21474836.55); +} +h(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug619339.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619339.js new file mode 100644 index 00000000000..2445d24fcc5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619339.js @@ -0,0 +1,3 @@ + +var a; +assertEq(true && 1.1 + a, NaN); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-1.js new file mode 100644 index 00000000000..8ffb23838ff --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-1.js @@ -0,0 +1,22 @@ + +/* Test compiling JSOP_STRICTEQ on known doubles. */ + +function foo(x) { + return x === x; +} + +for (var i = 0; i < 20; i++) { + assertEq(foo(1.2), true); + assertEq(foo(NaN), false); +} + +function bar(x) { + if (x === x) + return true; + return false; +} + +for (var i = 0; i < 20; i++) { + assertEq(bar(1.2), true); + assertEq(bar(NaN), false); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-2.js new file mode 100644 index 00000000000..947557da374 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug619433-2.js @@ -0,0 +1,10 @@ + +function foo(x) { + var y = 2.5; + y = -x; + var z = [1,2,y]; + return x + 5; +} +for (var i = 0; i < 20; i++) + foo(i); +assertEq(foo(20), 25); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug621522.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug621522.js new file mode 100644 index 00000000000..a952502d821 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug621522.js @@ -0,0 +1,14 @@ + +/* Don't crash. */ +function f() { + var x; + x.a; + x = {}; +} + +try { + f(); + assertEq(0, 1); +} catch(e) { + +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug621655.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug621655.js new file mode 100644 index 00000000000..5b14df90b1a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug621655.js @@ -0,0 +1,10 @@ +/* Don't assert. */ +for(p in 0.3) { } + +Number.prototype.foo = function() {} +var arr = []; + +for(p in 1.2) { + arr.push(p); +} +assertEq(arr[0], "foo"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639459.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639459.js new file mode 100644 index 00000000000..035952e816f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639459.js @@ -0,0 +1,6 @@ +function f() { + var a = [].length; + return a / a; +} +assertEq(f(), NaN); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-1.js new file mode 100644 index 00000000000..a0f2fd37e25 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-1.js @@ -0,0 +1,5 @@ + +function f(x) { + return (x % 123.45) >> x; +} +assertEq(f(-123), -4); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-2.js new file mode 100644 index 00000000000..3412dfa3e40 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639478-2.js @@ -0,0 +1,12 @@ +var y; +function f() { + for(var _ in [3.14]) { + y = 3.14; + y = y ^ y; + return y; + + function g() { + } + } +} +assertEq(f(), 0); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639587.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639587.js new file mode 100644 index 00000000000..462c118463a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639587.js @@ -0,0 +1,8 @@ +/* Don't assert. */ + +function f(o) { + o == 1; + if (o == 2) {} +} +for (var i = 0; i < 20; i++) + f(3.14); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639792.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639792.js new file mode 100644 index 00000000000..601f047ae15 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639792.js @@ -0,0 +1,8 @@ + +for(var i=0; i<20; i++) { + function f(){}; + x = -1; + x = x % 1; + assertEq(x, -0); +} + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug639808.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639808.js new file mode 100644 index 00000000000..bf6b16c3d10 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug639808.js @@ -0,0 +1,16 @@ +function f() { + var x = 1.23; + var y = [].length; + x = ++y; + y - 1; +} +f(); + +function g(q) { + var x = 1.23; + var y = [].length; + x = ++y; + if (q) + assertEq(y + 5, 6); +} +g(1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug640098.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640098.js new file mode 100644 index 00000000000..b037df8a11f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640098.js @@ -0,0 +1,15 @@ + +eval("(function(){({6953421313:0})})")(); + +function f() { + var x = {6953421313: 123}; + assertEq(x[6953421313], 123); + + x[6953421313] = "a"; + assertEq(x[6953421313], "a"); + + var y = {3.3: true}; + assertEq(y[3.3], true); +} +f(); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug640102.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640102.js new file mode 100644 index 00000000000..e3ad9fbc014 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640102.js @@ -0,0 +1,3 @@ +try { + eval("v>>([]=x)") +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug640614.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640614.js new file mode 100644 index 00000000000..f17ba2719d8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug640614.js @@ -0,0 +1,5 @@ +function f(x) { + x = 2 ^ x++; + if (x) {} +} +f(1.1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug642198.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug642198.js new file mode 100644 index 00000000000..dec6e7294f2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug642198.js @@ -0,0 +1,9 @@ + +function test() { + for(var e=1.2; true; e=20.2) { + if (e > 10) + break; + } +} +test(); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-1.js new file mode 100644 index 00000000000..7900f8ddd65 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-1.js @@ -0,0 +1,10 @@ +function f(x) { + var a; + var b = [1].length; + var c = x; + var d = b + 1; + AA = x; + a = d; + assertEq(b + d, 3); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-2.js new file mode 100644 index 00000000000..aea6896f1db --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643653-2.js @@ -0,0 +1,58 @@ +var HEAP, IHEAP, FHEAP; +var TOTAL_MEMORY = 50 * 1024 * 1024; +HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); +STACK_ROOT = STACKTOP = undefined; +var _rng; +var __str2; +var __str3; +{ + var __stackBase__ = STACKTOP; + var $n; + var $tmp5 = __stackBase__ + 12; + var $tmp6 = $n; + var $mul7 = ($tmp6) * 3; + $this_addr_i23 = $tmp5; + $id_addr_i = __str2; + $desc_addr_i = __str3; + $N_addr_i = $mul7; + var $this1_i24 = $this_addr_i23; + var $tmp_i25 = $id_addr_i; + var $tmp2_i = $desc_addr_i; + var $tmp3_i = $N_addr_i; + __Z9makeFastaI10RandomizedEvPKcS2_jRT_($tmp_i25, $tmp2_i, $tmp3_i, $this1_i24); +} +function __Z9makeFastaI10RandomizedEvPKcS2_jRT_($id, $desc, $N, $output) +{ + $output_addr = $output; + var $tmp4 = $output_addr; + $this_addr_i = $tmp4; + var $this1_i = $this_addr_i; + var $table_i = $this1_i; + var $call_i = __ZN10LineBuffer7genrandER10Cumulativej(0, $table_i, 0); +} +function __ZN10LineBuffer7genrandER10Cumulativej($this, $table, $N) +{ + var $this_addr_i1; + var $pct_addr_i; + $table_addr = $table; + var $tmp3 = $table_addr; + $this_addr_i = _rng; + $max_addr_i = 1; + var $this1_i = $this_addr_i; + var $last_i = $this1_i; + var $tmp_i = IHEAP[$last_i]; + var $mul_i = ($tmp_i) * 3877; + var $add_i = ($mul_i) + 29573; + var $rem_i = ($add_i) % 139968; + var $last2_i = $this1_i; + IHEAP[$last2_i] = $rem_i; + var $tmp3_i = $max_addr_i; + var $last4_i = $this1_i; + var $tmp5_i = IHEAP[$last4_i]; + var $conv_i = ($tmp5_i); + var $mul6_i = ($tmp3_i) * ($conv_i); + var $div_i = ($mul6_i) / 139968; + $this_addr_i1 = $tmp3; + $pct_addr_i = $div_i; + assertEq($pct_addr_i, NaN); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug643805.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643805.js new file mode 100644 index 00000000000..14a9edb48dd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643805.js @@ -0,0 +1,50 @@ +function _tt_face_get_name() { + var __label__ = -1; + var $rec; + var $n; + while(true) { + switch(__label__) { + case -1: + $rec=0; + $n=0; + case 0: + if ($rec == 20) { + __label__ = 2; + break; + } + var $63 = $n; + var $64 = $63 + 1; + $n = $64; + var $65 = $rec; + $rec = $rec + 1; + assertEq($64 < 30, true); + __label__ = 0; + break; + case 1: + $rec = 0; + case 2: + return; + } + } +} +_tt_face_get_name(); + +/* Test tracking of lifetimes around backedges in nested loops. */ +function nested() { + var x = 100; + var i = 0; + while (i < 10) { + while (i < 10) { + i++; + if (x < 20) + break; + if (i > 10) { + x = 200; + i++; + } + } + if (i > 10) + x = 100; + } +} +nested(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug643829.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643829.js new file mode 100644 index 00000000000..43766c01256 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643829.js @@ -0,0 +1,12 @@ +function _build_tree() { + var $n; + var $elems = 20; + while (true) { + var $tmp18 = $n; + var $tmp19 = $elems; + var $cmp = ($n | 0) < ($elems | 0); + return $cmp; + } +} +assertEq(_build_tree(), true); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug643913.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643913.js new file mode 100644 index 00000000000..1dc8ac5d044 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug643913.js @@ -0,0 +1,8 @@ +function f() { + var x; + eval("x = 3.14"); + x = 123; + var y = -(-x); + assertEq(y, 123); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug645629.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645629.js new file mode 100644 index 00000000000..c92d2c7bf54 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645629.js @@ -0,0 +1,8 @@ +/* Don't assert. */ +o1 = {}; +o1 = 2; +function f(o) { + o.hasOwnProperty("x"); +} +new f(o1); +f(o1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug645657.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645657.js new file mode 100644 index 00000000000..d33870934ed --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645657.js @@ -0,0 +1,8 @@ +function f() { + var x = 3; + var y = 1.2; + var z; + y = --z; + x = z++; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug645985.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645985.js new file mode 100644 index 00000000000..7ba1a0386f9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug645985.js @@ -0,0 +1,5 @@ +function f(o) { + o += ""; + o.hasOwnProperty("x"); +} +f({}); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug646001.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646001.js new file mode 100644 index 00000000000..eabdb12dc47 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646001.js @@ -0,0 +1,12 @@ +function jit(on) +{ + if (on && !options().match(/tracejit/)) { } +} +try { test(); } catch (e) {} +function test( + ) +{ + for (var j=0;j<5;++j) { switch(1.1) { case 2: case NaN: } } + jit(false); + reportCompare('xxxxxxx'.test(new j('(x+)(x*)'))); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug646060.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646060.js new file mode 100644 index 00000000000..f399e128398 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646060.js @@ -0,0 +1,8 @@ +function f0(i) { + switch(i) { + case "a": + case { TITLE: false, VERSION: false }('test') + : + } +} +try { new TestCase(SECTION, 'switch statement', f0("a"), "ab*"); } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug646411.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646411.js new file mode 100644 index 00000000000..c7bb43232f1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646411.js @@ -0,0 +1,9 @@ +__proto__ = Function(); +eval("\ +var MS = 16;\ +addNewTestCase(new Date(1899,11,31,16,0,0), \"new Date(1899,11,31,16,0,0)\",\ +typeof UTC_DAY == 'undefined');\ +function addNewTestCase( DateCase, DateString, ResultArray ) {\ + ResultArray[MS];\ +}\ +"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug646495.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646495.js new file mode 100644 index 00000000000..f961de74727 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug646495.js @@ -0,0 +1,6 @@ +function f() { + var x = 1; + var y; + if (x = y = Math) {} +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug647440.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647440.js new file mode 100644 index 00000000000..f03f2c2075b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647440.js @@ -0,0 +1,7 @@ +if (false) + function g() {} +function f() { + var x; + (x = Infinity) >> x--; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug647657.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647657.js new file mode 100644 index 00000000000..09c999fa7ae --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647657.js @@ -0,0 +1 @@ +Function("var{}=Array()")() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug647785.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647785.js new file mode 100644 index 00000000000..8c53e3f8da4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug647785.js @@ -0,0 +1,4 @@ +assertEq(-(!Int32Array(5)), -0); +assertEq(-(!Math), -0); +assertEq(-(!{}), -0); +assertEq(-(![]), -0); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648004.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648004.js new file mode 100644 index 00000000000..ba215bdead8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648004.js @@ -0,0 +1,4 @@ +var x = eval("gc(); 30"); +var y = x.toString(); +isNaN(x); +assertEq(y, "30"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-1.js new file mode 100644 index 00000000000..f684bc68975 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-1.js @@ -0,0 +1,13 @@ + +function f() { + -null; + -null; + -null; + -null; + -null; +} +{ + function g() {}; +} +f(); +x = Math.abs(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-2.js new file mode 100644 index 00000000000..d9e1eb421f7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648230-2.js @@ -0,0 +1,14 @@ +var i = 1; +var j = 2; +function f() { + if (false) + function g() {}; + return i / j; +} +-null; +-null; +-null; +-null; +-null; +f(); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648498.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648498.js new file mode 100644 index 00000000000..8e54209e5a6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648498.js @@ -0,0 +1,5 @@ +function f(x, y) { + return x; +} +var a = 3.3; +a ? f(f(1, 2), 3) : a; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648586.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648586.js new file mode 100644 index 00000000000..dd2dfeb91ec --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648586.js @@ -0,0 +1,11 @@ +try { eval("\ + function a(y){y.x}\ + for each(let d in[\ + ({}),({}),({}),({}),({}),({}),({}),({}),({}),({})\ + ]){\ + try{\ + a(d)\ + }catch(e){}\ + }\ + n\ +") } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug648708.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648708.js new file mode 100644 index 00000000000..1f79596371c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug648708.js @@ -0,0 +1,6 @@ +thrown = false +try { + ("".x = Object.seal) + "".x.valueOf(); +} catch (e) {thrown = true} +assertEq(thrown, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649272.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649272.js new file mode 100644 index 00000000000..b09e324ad12 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649272.js @@ -0,0 +1,4 @@ +function f(x) {return x;} +x = f(/abc/); +eval("this.__defineSetter__(\"x\", function(){}); x = 3;"); +eval("var BUGNUMBER = 233483;"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649593.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649593.js new file mode 100644 index 00000000000..9d878d4a287 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649593.js @@ -0,0 +1,10 @@ +function RunSingleBenchmark(data) { + if (data == null) + return { runs: 0, elapsed: 0 }; + data.runs += 10; + return data; +} +var data; +data = RunSingleBenchmark(data); +data = RunSingleBenchmark(data); +assertEq(data.runs, 10); \ No newline at end of file diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649689.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649689.js new file mode 100644 index 00000000000..147197e00f1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649689.js @@ -0,0 +1,6 @@ +function f(x) { + eval("a = 3"); + x.p = x.p = a; + assertEq(x.p, 3); +} +f({p: 2}); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649775.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649775.js new file mode 100644 index 00000000000..abf136f5931 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649775.js @@ -0,0 +1,17 @@ +var x = [, ]; +var n = [, ]; +var np = 18229; +sa = Array; +function copy_(x, y) { + var i; + var k = x < y ? x.length : y.length; + for (i = 0; i < k; i--) { + x[i]; + if (i == -100) + return; + } +} +function mont_(x, y, n, np) { + copy_(x, sa); +} +mont_(x, x, n, np); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649824.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649824.js new file mode 100644 index 00000000000..619641c92e8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649824.js @@ -0,0 +1,6 @@ +var o = { + w: 2, + x: 3 +}; +var o = 430717; +o.x = 4; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug649973.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649973.js new file mode 100644 index 00000000000..89b28822354 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug649973.js @@ -0,0 +1,2 @@ +x = 2147483647; +(x+10, false) ? [x % x] : [2 * x]; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug650076.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650076.js new file mode 100644 index 00000000000..a72faa0feb1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650076.js @@ -0,0 +1,13 @@ +var v0, v1, v2, v3, v4, v5, v6, v7, v8, v9; +var v10, v11, v12, v13, v14, v15, v16, v17, v18, v19; +var v20, v21, v22, v23, v24, v25, v26, v27, v28, v29; +var v30, v31, v32, v33, v34, v35, v36, v37, v38, v39; +var v40, v41, v42, v43; + +function f() {} + +v1 = new Date(0); +v1.setMilliseconds(12); +v2 = eval("''"); +v3 = ""; +f((v2 + v3).indexOf(v3)); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug650662.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650662.js new file mode 100644 index 00000000000..4c4b2bc853b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650662.js @@ -0,0 +1,6 @@ +test(); +function test() { + var a = []; + a*=3; + a.length; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug650663.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650663.js new file mode 100644 index 00000000000..786230fb343 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug650663.js @@ -0,0 +1,15 @@ +var cnBadSyntax = '1=2'; +var obj = new testObject; +f.call(obj); +g(); +function testObject() { + this.badSyntax = cnBadSyntax; +} +function f() { + try { + eval(this.badSyntax) + } catch (e) {} +} +function g() { + f.call(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug651147.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug651147.js new file mode 100644 index 00000000000..08971faa091 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug651147.js @@ -0,0 +1,7 @@ +var BUGNUMBER = 96284; +var BUGNUMBER = "410725"; +function iteratorToArray(iterator) { + var result = []; + for (var i in iterator) BUGNUMBER[result.length]; +} +try { obj = { a: 1, }('["a", "b"]', iteratorToArray(), 'uneval(iteratorToArray(new Iterator(obj,true)))'); } catch (e) { } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug652305.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652305.js new file mode 100644 index 00000000000..78df18aadfe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652305.js @@ -0,0 +1,25 @@ +var sjcl = { + cipher: {}, +}; +var global = 99; +sjcl.cipher.aes = function (a) { + var b, c, d, e, f = this.h[0][4], + g = this.h[1]; + d = a.slice(0); + this.a = [d, []]; + for (a = 8; a < 60; a++) { + c = d[a - 1]; + if (a % 8 === 0) { + c = global; + if (0 === 0) { + h = 2; + } + } + d[a] = c; + } + assertEq(this.a[0][50], 99); +}; +sjcl.cipher.aes.prototype = { + h: [[[], [], [], [], [99, 0]], [[67175681, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 65537], [17039621, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 16777472], [83952641, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 65537], [17105156, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 16777472], []]], +}; +new sjcl.cipher.aes([0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug652314.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652314.js new file mode 100644 index 00000000000..80b4a320841 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652314.js @@ -0,0 +1,7 @@ +(function() { + for (a in [0]) { + try { + return + } catch(e) {} + } +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug652590.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652590.js new file mode 100644 index 00000000000..5709411a53d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug652590.js @@ -0,0 +1,5 @@ +function f() { + var x = undefined ? 1 : 4294967295; + print(false || x); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug653243.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653243.js new file mode 100644 index 00000000000..53e93028030 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653243.js @@ -0,0 +1,12 @@ +try { + obj[i] = "a"; +} catch (e) {} +var obj = { + p: 100 +}; +var name = "p"; +var a = []; +for (var i = 0; i < 10; i++) { + a[i] = obj[name]--; +} +assertEq(a.join(), '100,99,98,97,96,95,94,93,92,91'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug653249.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653249.js new file mode 100644 index 00000000000..b85ddd743fc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653249.js @@ -0,0 +1,13 @@ + +function testUnaryImacros() { + function checkArg(x) { + o = { + valueOf: checkArg + } + } + var v = 0; + v += +toString; + for (var i = 0; i;) { + v += [].checkArg.checkArg; + } +}(testUnaryImacros(), "valueOf passed, toString passed"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug653397.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653397.js new file mode 100644 index 00000000000..f17167fac14 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug653397.js @@ -0,0 +1,16 @@ +try { + function testSlowArrayPopMultiFrame() { + a = undefined; + function parent(a, i) { i }; + function gramps(a, i) { + return parent; + } + var last; + for (var i = 0; ; gramps++) { + last = gramps(a, i) + } + }(testSlowArrayPopMultiFrame(), 23); + assertEq(0, 1); +} catch(e) { + assertEq(e instanceof TypeError, true); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug655505.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655505.js new file mode 100644 index 00000000000..4d2e07c6687 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655505.js @@ -0,0 +1,15 @@ +var a = [, , , , , , ]; +var res = 0; +exhaustiveSliceTest("exhaustive slice test 1", a); +function mySlice(a, from, to) { + var to2 = to; + if (to2 > 0) { + res += to2; + to2 = to2.length + to; + } +} +function exhaustiveSliceTest(testname, a) { x = a; } +for (y = a.length; y >= 0; y--) { + mySlice(a, x, y); +} +assertEq(res, 21); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug655508.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655508.js new file mode 100644 index 00000000000..712c1c0cf8e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655508.js @@ -0,0 +1,15 @@ +switch (3) { +case + function () { + var x; + (function () {})() && false; + x = undefined; + try { + JSON.parse(x) + } catch (e) {} + }([]): +case + function () { + [typeof loopa1] + }(0): +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug655810.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655810.js new file mode 100644 index 00000000000..e31d56234c3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655810.js @@ -0,0 +1,6 @@ +function f(arr) { + var x = arr[0]; + if (typeof x) {}; + Math.abs(x); +} +f([1.2]); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug655990.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655990.js new file mode 100644 index 00000000000..4ca4fd1f6cb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug655990.js @@ -0,0 +1,12 @@ +expect = 0; +summary = 0; +test(); +function test() { + function f(a, b, c) { + return c; + } + if (gczeal == 'function') actual = f(1.5, 1.25, 1.125) + else expect; + (expect, actual, summary); + var actual = ''; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656096.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656096.js new file mode 100644 index 00000000000..53090dc9f3f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656096.js @@ -0,0 +1,37 @@ +function aes(a) { + d = a.slice() + for (; a < 28; a++) + d[0] = d[0] ^ undefined +} +var sjcl = {}; +sjcl.bitArray = { + concat: function (a, b) { + return d ? a : []; + }, + clamp: function (a, b) { + return a + } +}; +function G(a, b, c, d, e, f) { + var g = [], + h = sjcl.bitArray, + f = []; + f = h.concat(f, c) + if (c) g = [] + else c = h.concat([], []) + h.concat(g, d) + h.clamp(f, ) +} +function decrypt(a, b, c, d, e) { + G(a, 1, c, d, e, b) + var g = [], + h = sjcl.bitArray, + f = []; + h.concat(f, c) + if (c) g = [] + else c = h.concat([], []) + h.concat(g, d) + h.concat([], c).concat.slice +} +aes(sjcl.bitArray.clamp([])); +decrypt(1, 2, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656252.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656252.js new file mode 100644 index 00000000000..07b0f94f1fb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656252.js @@ -0,0 +1,14 @@ +o14 = [undefined].__proto__ +function f18() { + try { + [] = o[p] + } catch (e) {} +} +for (var i; i < 20; i++) { + ({ + x: function() { + return eval("o14") + } + }.x().__proto__ = null); + f18() +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656259.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656259.js new file mode 100644 index 00000000000..35de2172657 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656259.js @@ -0,0 +1,11 @@ + +function throwsRangeError(t) { + try { + t: for (t[t++] in object) { + t++ + break t; + } + date(t) + } catch (err) {} +} +throwsRangeError(Infinity); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656591.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656591.js new file mode 100644 index 00000000000..8d86cdf2b5f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656591.js @@ -0,0 +1,9 @@ +// |jit-test| error: TypeError +(function () { + var i = 0; + (function () { + var x; + (x = "3") || 1; + (x = "")(i || x); + })(); +})(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656748.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656748.js new file mode 100644 index 00000000000..fd0cb2c1103 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656748.js @@ -0,0 +1,8 @@ +function f() { + var x = -0; + x++; + if (3 > 2) {}; + var y = x + 2.14; + assertEq(y, 3.14); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug656914.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656914.js new file mode 100644 index 00000000000..e57e0a97479 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug656914.js @@ -0,0 +1,23 @@ +try { + new MyObject; +} catch (e) {} + +function MyObject() { + return; + return this; +} + +function Foo(x) { + if (x) + this.f = x; +} +var q = new Foo(false); +for (a in q) { assertEq(true, false); } + +function Bar(x, y) { + if (!x) + return; + this.f = y; +} +var q2 = new Bar(false, true); +for (b in q2) { assertEq(true, false); } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug657120.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657120.js new file mode 100644 index 00000000000..6e8af1a4958 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657120.js @@ -0,0 +1,6 @@ +function f() { + var x = Object(2); + var y = 3.14; + assertEq(true && x < y, true); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug657247.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657247.js new file mode 100644 index 00000000000..539cdf55c90 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657247.js @@ -0,0 +1,5 @@ + +a = new Array; +for (var i = 0; i != 1000; ++i) a[i] = 17; +var x = '123' + '\0' + '456'; +(1, a[x], ': 123\\0456'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug657890.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657890.js new file mode 100644 index 00000000000..1d74fc48cb7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug657890.js @@ -0,0 +1,6 @@ +function f() {}; +var x; +for(var i=0; i<200; i++) { + x = f.bind(x, x, 2); + gc(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug658240.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658240.js new file mode 100644 index 00000000000..033f2fbdab6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658240.js @@ -0,0 +1,11 @@ +function f() { + var x = 0; + for(var i=0; i<5; i++) { + (function() { + var q = parseFloat("2"); + x += q; + })(); + } + return x; +} +assertEq(f(), 10); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug658294.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658294.js new file mode 100644 index 00000000000..e1496b96142 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658294.js @@ -0,0 +1,15 @@ +test(); +function test() +{ + try { + instances = [] + for (var i = 0; i != 2; ++i) + instances[i]=constructor + var i = 0; + var instance = instances[i]; + var name = instance.name; + for (var j = 1; j != instances; ++j) + if (i != j && instance instanceof name[j].constructor) {} + } catch(ex) {} +} +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug658579.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658579.js new file mode 100644 index 00000000000..7affcbd5fb2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658579.js @@ -0,0 +1,5 @@ +(function () { + var x = 1 || 1.2; + true ? ~x : x; + x >> x; +})(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug658968.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658968.js new file mode 100644 index 00000000000..74a15afa19f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug658968.js @@ -0,0 +1,13 @@ +function f(){ + var tarr = []; + var elemArray = [ + {name: "C0", checked: true}, + {name: "C1", checked: false}, + ]; + for (var i = 0; i < elemArray.length; i++) { + var element = elemArray[i]; + tarr[i] = (element.checked == true) ? 1 : 2; + } + assertEq(tarr.join(""), "12"); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug659438.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659438.js new file mode 100644 index 00000000000..fddfe6a0262 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659438.js @@ -0,0 +1,8 @@ +var o1 = {x: {}}; +function f() { + var o = o1; + for(var i=0; i<10; i++) { + o1 = o.x; + } +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug659439.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659439.js new file mode 100644 index 00000000000..e83afebfd11 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659439.js @@ -0,0 +1,16 @@ +function bind(f) { + return f.call.apply(f.bind, arguments); +}; +function h(a, b) { + a.x; +} +function g(a, b) { + a.x = b; + h(a); +} +function f() { + for(var i=0; i<20; i++) { + g.call(this, {}, bind(function(){})); + } +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug659448.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659448.js new file mode 100644 index 00000000000..d9d7c9f16eb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659448.js @@ -0,0 +1,9 @@ +function TestCase(n, d, e, a) { + this.expect = e; + this.passed = getTestCaseResult(this.expect, this.actual); +} +function getTestCaseResult(expect, actual) {} +new TestCase( + TestCase(3000000000.5) +); +new TestCase(null,null, String('Sally and Fred are sure to come'.match(/^[a-z\s]*/i))); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug659456.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659456.js new file mode 100644 index 00000000000..b2155f000c8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug659456.js @@ -0,0 +1,12 @@ +// |jit-test| error: InternalError +function foo() { return "tracejit,methodjit"; }; +function baz(on) { + foo('bar'); +} +eval("\ +test();\ +function test() {\ + baz(true);\ + test();\ +}\ +"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug660002.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug660002.js new file mode 100644 index 00000000000..f1bc7ab7378 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug660002.js @@ -0,0 +1,4 @@ +// |jit-test| error: ReferenceError +(function() { + let V = x(x, x = w), x +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug662072.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug662072.js new file mode 100644 index 00000000000..04c198ccdda --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug662072.js @@ -0,0 +1,7 @@ +(function () { + var x; + x = arguments.length; + return function () { + [1][x = arguments.length]; + }; +}).call().apply(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug662082.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug662082.js new file mode 100644 index 00000000000..533fee8ba3f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug662082.js @@ -0,0 +1,11 @@ + +function foo(z) +{ + var x = 2; + if (z) { + x = 2.5; + } + var y = x * 10; + assertEq(y, 20); +} +foo(false); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug663485.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug663485.js new file mode 100644 index 00000000000..c6d3555d71c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug663485.js @@ -0,0 +1,17 @@ +function makeExpectedMatch(arr) { + expectedMatch = { + x: arr.length + } +} +var expected = makeExpectedMatch(new Int32Array); +this.toSource(); + +function testTypedArrayOther() { + var ar = new Int32Array; + for (; i < ar; ++i) { + ar[i] = i; + } + for (var i = 0; i<40; i++) { + } +} +testTypedArrayOther(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug663910.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug663910.js new file mode 100644 index 00000000000..33997a3c4c6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug663910.js @@ -0,0 +1,22 @@ +var otherGlobalSameCompartment = newGlobal("same-compartment"); +eval = otherGlobalSameCompartment.eval; +doesNotNeedParens(1, "if (xx) { }"); +needParens(2, "if (1, xx) { }"); +function doesNotNeedParens(section, pat) { + try { + f = new Function + } catch (e) {} + roundTripTest(section, f) +} +function needParens(section, pat, exp) { + var f, ft; + roundTripTest(section, f, exp) +} +function roundTripTest(section, f, exp) { + uf = "" + f + var euf; + try { + euf = eval("(" + uf + ")"); + } catch (e) { } + + euf +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug669706.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug669706.js new file mode 100644 index 00000000000..a3ddb435880 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug669706.js @@ -0,0 +1,14 @@ +function printStatus(msg) { + msg.toString() + lines = msg + for (var i = 0; i < lines; i++) i +} +var summary = new Float32Array; +try { + printStatus(summary) + x = { + } + toString.__proto__ = x +} catch (e) {} +var summary = 6; +printStatus(summary) diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug670885.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug670885.js new file mode 100644 index 00000000000..a1d8bcbe7ba --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug670885.js @@ -0,0 +1,16 @@ +// arr is an Int8Array, then changes to Int16Array. +// This should trigger recompilation of f. +var arr = new Int8Array(100); +var arr16 = new Int16Array(100); +arr16[2] = 12345; +function f(a) { + var x; + for(var i=0; i<30; i++) { + x = a[2]; + } + return x; +} +assertEq(f(arr), 0); +assertEq(f(arr), 0); +this.arr = arr16; +assertEq(f(arr), 12345); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug672122.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug672122.js new file mode 100644 index 00000000000..14802554e20 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug672122.js @@ -0,0 +1,3 @@ +// |jit-test| error: ReferenceError + +if (x) {} else if ((evalcx('lazy'))++) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug673788.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug673788.js new file mode 100644 index 00000000000..edaf0b611a6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug673788.js @@ -0,0 +1,10 @@ +// |jit-test| error: ReferenceError +p = Proxy.create({ + has: function() {} +}) +Object.prototype.__proto__ = p +n = []; +(function() { + var a = []; + if (b) t = a.s() +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug678141.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678141.js new file mode 100644 index 00000000000..644f1364464 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678141.js @@ -0,0 +1,10 @@ + +c = {}.__proto__[1] = 3; +(function() { + function b(a) { + return a + } + for each(let z in [{}]) { + print(new b(z)) + } +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug678234.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678234.js new file mode 100644 index 00000000000..52a421553a4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678234.js @@ -0,0 +1,10 @@ +a = {} +function f(o) { + for (x in o) { + print + } +} +for (var i = 0; i < 3; i++) { + new f(a) + a.__proto__ = null +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug678782.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678782.js new file mode 100644 index 00000000000..1549bbe7707 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug678782.js @@ -0,0 +1,9 @@ +var i = -1; var j = -1; var s = ''; var f = ''; +var buf = serialize(new Date(NaN)); +var a = [1/0, -1/0, 8.64e15 + 1, -(8.64e15 + 1)]; +for (var i = 0; i < a.length; i++) { + var n = a[i]; + var nbuf = serialize(n); + for (var Number ; j < 8; j++) + buf[j + 8] = nbuf[j]; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug679666.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug679666.js new file mode 100644 index 00000000000..986195ebc3a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug679666.js @@ -0,0 +1,22 @@ +var arr = new Int32Array(20); + +function f(a1, a2) { + for (var i=0; i<10; i++) { + g1 = a2; + arr[a1] = a2; + assertEq(g1, a2); + + if ([1].length === 10) { + a1 = {}; + } + } +} + +f(1, eval("{}")); + +for (var i=0; i<5; i++) { + f(2, 3); + f(5, -6.1); +} +assertEq(arr[2], 3); +assertEq(arr[5], -6); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug680842.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug680842.js new file mode 100644 index 00000000000..016673fb72c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug680842.js @@ -0,0 +1,27 @@ + +var gTestcases = new Array; +var gTc = gTestcases; +var msg = ''; +function TestCase(n, d, e, a) { + gTestcases[gTc++]=this; +} +TestCase.prototype.dump=function () { + lines = msg + for (var i=0; i v3 + v3=1.7 + } while (p1 * v0 > p0); + + v3; +} +f0(4105,8307); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug682345.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug682345.js new file mode 100644 index 00000000000..94d610748d4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug682345.js @@ -0,0 +1,16 @@ +// |jit-test| error: TypeError + +for (var i = 0; i <= 0x017f; i++) { + var U = new Unicode(i); +} +function Unicode(c) { + u = GetUnicodeValues(c); + this.upper = u[0]; +} +function GetUnicodeValues(c) { + u = new Array(); + if ((c >= 0x0100 && c < 0x0138) || (c > 0x0149 && c < 0x0178)) try {} finally { + return; + } + return u; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084-2.js new file mode 100644 index 00000000000..34ddae320a6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084-2.js @@ -0,0 +1,8 @@ +function Function() { + try { + var g = this; + g.c("evil", eval); + } catch(b) {} +} +var o0 = Function.prototype; +var f = new Function( (null ) ); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084.js new file mode 100644 index 00000000000..ac2c11a3844 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684084.js @@ -0,0 +1,7 @@ +// |jit-test| error: TypeError +function Integer( value, exception ) { + try { } catch ( e ) { } + new (value = this)( this.value ); + if ( Math.floor(value) != value || isNaN(value) ) { } +} +new Integer( 3, false ); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug684576.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684576.js new file mode 100644 index 00000000000..b845d2160d2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684576.js @@ -0,0 +1,10 @@ +// |jit-test| error: TypeError +function f0(p0,p1) { + var v3; + do { + p1 > v3 + v3=1.7 + } while (((p0[p1][5]==1)||(p0[p1][5]==2)||(p0[p1][5] == 3)) + 0 > p0); + + (v3(f0)); +} +f0(4105,8307); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug684824.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684824.js new file mode 100644 index 00000000000..9e8ad9c7ee7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684824.js @@ -0,0 +1,7 @@ + +function X(n) { + while ('' + (n--)) { + break; + } +} +X(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug684943.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684943.js new file mode 100644 index 00000000000..69ec5ca2bd6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug684943.js @@ -0,0 +1,7 @@ + +function foo(x) { + for (var i = 0; i < 100; i++) { + x.f === i; + } +} +foo({f:"three"}); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug687768.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug687768.js new file mode 100644 index 00000000000..a4b2b4adeb9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug687768.js @@ -0,0 +1,17 @@ + +expected = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,'; +function slice(a, b) { + return expected; +} +function f() { + var length = 4; + var index = 0; + function get3() { + if (length - index < 3) + return null; + return slice(index, ++index); + } + var bytes = null; + while (bytes = get3()) { } +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug693311.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug693311.js new file mode 100644 index 00000000000..39d69ab2573 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug693311.js @@ -0,0 +1,8 @@ +function f(x) { + assertEq("a" !== x, true); + assertEq("b" != x, true); + assertEq("c" === x, false); + assertEq("d" == x, false); +} +f(1); +f(1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug704138.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug704138.js new file mode 100644 index 00000000000..a724350c467 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug704138.js @@ -0,0 +1,17 @@ +function TestCase(n, d, e, a) + this.name=n; +function reportCompare (expected, actual, description) { + new TestCase +} +reportCompare(true, "isGenerator" in Function, "Function.prototype.isGenerator present"); +var p = Proxy.create({ + has : function(id) {} +}); +function test() { + Object.prototype.__proto__=null + if (new TestCase) + Object.prototype.__proto__=p +} +test(); +new TestCase; +test() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug705873.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug705873.js new file mode 100644 index 00000000000..1d4d65246a9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug705873.js @@ -0,0 +1,7 @@ +a = [] +function f(o) { + o[5] = {} +} +for (var i = 0; i < 20; i++) { + with(a) f(a) +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug706110.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug706110.js new file mode 100644 index 00000000000..ef1e657ca62 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug706110.js @@ -0,0 +1,18 @@ +function test() { + function Bug() { this.prototype } + var actual = (new Bug instanceof Bug); + assertEq(actual, true); +} +test(); +test(); + +function testLambdaCtor() { + var q; + for (var x = 0; x < 2; ++x) { + var f = function(){}; + if (x == 1) gc(); + q = new f; + } + return q.__proto__ === f.prototype; +} +assertEq(testLambdaCtor(), true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug707384.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug707384.js new file mode 100644 index 00000000000..6890d7f615a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug707384.js @@ -0,0 +1,8 @@ +// |jit-test| debug +function f() { + do { + return + } while (e) +} +trap(f, 1, '') +f() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug707641.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug707641.js new file mode 100644 index 00000000000..3df58939412 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug707641.js @@ -0,0 +1,7 @@ +function a(a, prototype) { + try { + typeof (arguments[a]) in code + } catch(e) {} +} +a(); +a(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug709067.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug709067.js new file mode 100644 index 00000000000..3e3bd70b8b9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug709067.js @@ -0,0 +1,11 @@ +called = 0; +Object.defineProperty(Object.prototype, 0, {set: function() { called++; }}); +function testInit() +{ + var a = []; + for (var i = 0; i < 5; i++) + a[i] = 0; +} +for (var i = 0; i < 100; i++) + testInit(); +assertEq(called, 100); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug710780.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug710780.js new file mode 100644 index 00000000000..84d2646921d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug710780.js @@ -0,0 +1,12 @@ + +function foo() { + function bar() { + function baz() { + assertEq(arguments.callee.caller === null, false); + } + for (var i = 0; i < 10; i++) + baz(); + } + bar(); +} +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug714645.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug714645.js new file mode 100644 index 00000000000..2bcfa259821 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug714645.js @@ -0,0 +1,12 @@ + +function testAddInconvertibleObjectAny() { + var count = 0; + function toString() { } + try { + for (var i = 0; i < 100; i++) + var q = count[count] && this ? testAddInconvertibleObjectAny : ++toString; + } catch (e) { + var dbg = count(toString); + } +} +testAddInconvertibleObjectAny(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug719758.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug719758.js new file mode 100644 index 00000000000..e1569a901af --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug719758.js @@ -0,0 +1,10 @@ + +function test() { + try { + for (var i = 0 in this) throw p; + } catch (e) { + if (i !== 94) + return "what"; + } +} +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/bug719918.js b/deps/mozjs/js/src/jit-test/tests/jaeger/bug719918.js new file mode 100644 index 00000000000..9ea89a0be91 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/bug719918.js @@ -0,0 +1,19 @@ +function test(m) { + do { + if (m = arr[0]) break; + m = 0; + } + while (0); + arr[1] = m; +} + +mjitChunkLimit(10); + +arr = new Float64Array(2); + +// run function a lot to trigger methodjit compile +for(var i=0; i<200; i++) + test(0); + +// should return 0, not NaN +assertEq(arr[1], 0) diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712265.js b/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712265.js new file mode 100644 index 00000000000..06de921f00f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712265.js @@ -0,0 +1,6 @@ +// |jit-test| error: ReferenceError +mjitChunkLimit(5); +eval("\ +try { \ + let (t1 = x) {}\ +} finally {}"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712267.js b/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712267.js new file mode 100644 index 00000000000..5e726fe4fd2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/chunk/bug712267.js @@ -0,0 +1,16 @@ + +evaluate("mjitChunkLimit(5)"); +expected = 100; +function slice(a, b) { + return expected--; +} +function f() { + var length = 8.724e02 ; + var index = 0; + function get3() { + return slice(index, ++index); + } + var bytes = null; + while (bytes = get3()) { } +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/clonefun.js b/deps/mozjs/js/src/jit-test/tests/jaeger/clonefun.js new file mode 100644 index 00000000000..950ae392278 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/clonefun.js @@ -0,0 +1,17 @@ + +// Functions which have been marked as singletons should not be cloned. + +BeatDetektor = function() +{ + this.config = BeatDetektor.config; + + assertEq(this.config.a, 0); + assertEq(this.config.b, 1); +} + +BeatDetektor.config_default = { a:0, b:1 }; +BeatDetektor.config = BeatDetektor.config_default; + +var bd = new BeatDetektor(); + +assertEq(bd.config === BeatDetektor.config, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/closure-01.js b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-01.js new file mode 100644 index 00000000000..4c82b6fd350 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-01.js @@ -0,0 +1,18 @@ + +/* Non-reentrant call on an inner and outer closure. */ + +function foo() { + var x = 0; + function bar() { + var y = 0; + function baz() { + return ++x + ++y; + } + return baz; + } + return bar(); +} + +var a = foo(); +var b = foo(); +assertEq(a() + a() + b() + b(), 12); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/closure-02.js b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-02.js new file mode 100644 index 00000000000..902c8f5ae78 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-02.js @@ -0,0 +1,14 @@ + +/* Non-reentrant closure used in an invoke session. */ + +var last = null; + +var a = [1,2,3,4,5,6,7,8]; +var b = a.map(function(x) { + x++; + var res = last ? last() : 0; + last = function() { return x; }; + return res; + }); + +assertEq("" + b, "0,2,3,4,5,6,7,8"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/closure-03.js b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-03.js new file mode 100644 index 00000000000..e2d1ebe3f85 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-03.js @@ -0,0 +1,15 @@ + +/* Recovering non-reentrant information on singletons after a GC. */ + +function foo(a) { + return function() { + gc(); + var n = 0; + for (var i = 0; i < 20; i++) + n = a++; + assertEq(n, 29); + }; +} +var a = foo(10); +var b = foo(20); +a(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/closure-04.js b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-04.js new file mode 100644 index 00000000000..72b188f03b2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-04.js @@ -0,0 +1,23 @@ +test(); +function test() { + var catch1, catch2, catch3, finally1, finally2, finally3; + function gen() { + yield 1; + try { + try { + try { + yield 1; + } finally { + test(); + } + } catch (e) { + finally2 = true; + } + } catch (e) { } + } + iter = gen(); + iter.next(); + iter.next(); + iter.close(); + gc(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/closure-05.js b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-05.js new file mode 100644 index 00000000000..b2aeffa3650 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/closure-05.js @@ -0,0 +1,17 @@ +var gTestcases = new Array(); +var gTc = gTestcases.length; +function TestCase(n, d, e, a) { + gTestcases[gTc++] = this; +} +new TestCase("SECTION", "with MyObject, eval should return square of "); +test(); +function test() { + for (gTc = 0; gTc < gTestcases.length; gTc++) { + var MYOBJECT = (function isPrototypeOf(message) { + delete input; + })(); + with({}) { + gTestcases[gTc].actual = eval(""); + } + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/fromCharCode.js b/deps/mozjs/js/src/jit-test/tests/jaeger/fromCharCode.js new file mode 100644 index 00000000000..971878372d3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/fromCharCode.js @@ -0,0 +1,8 @@ +/* +* Any copyright is dedicated to the Public Domain. +* http://creativecommons.org/licenses/publicdomain/ +*/ + +for (var i = 0; i <= 0xFFFF; i++) { + assertEq(String.fromCharCode(i).charCodeAt(0), i); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-5.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-5.js index 9d3811c6d47..e8254715304 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-5.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-5.js @@ -1,4 +1,4 @@ var x = { 0: 5, 1: 5 }; assertEq(x[0] + x[1], 10); -/* int32 getelem on object. */ +/* int32_t getelem on object. */ diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-8.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-8.js new file mode 100644 index 00000000000..f203216ac2c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-8.js @@ -0,0 +1,13 @@ + +// TI does not account for GETELEM accessing strings, so the GETELEM PIC must +// update type constraints according to generated stubs. +function foo(a, b) { + for (var j = 0; j < 5; j++) + a[b[j]] + " what"; +} +var a = {a:"zero", b:"one", c:"two", d:"three", e:"four"}; +var b = ["a", "b", "c", "d", "e"]; +foo(a, b); +foo(a, b); +a.e = 4; +foo(a, b); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-1.js index 88764556333..d278a97dc4f 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-1.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-1.js @@ -4,4 +4,4 @@ assertEq('works', arr[1]); assertEq('for', arr[2]); assertEq('me', arr[3]); -/* Multiple int32 getelem for dense array. */ +/* Multiple int32_t getelem for dense array. */ diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-2.js index 3df39d3b014..add4568ef73 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-2.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-2.js @@ -9,4 +9,4 @@ for (var i = 0; i < arr.length; ++i) { } } -/* int32 getelem for dense array. */ +/* int32_t getelem for dense array. */ diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-3.js index f0eaacd9926..91fb50f50ef 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-3.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getelem-sanity-int-3.js @@ -16,4 +16,4 @@ for (var i = 0; i < 3; i++) { } } -/* int32 and string getelem for non-dense array. */ +/* int32_t and string getelem for non-dense array. */ diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-1.js new file mode 100644 index 00000000000..29ab9552ebe --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-1.js @@ -0,0 +1,18 @@ +// GETPROP PIC with multiple stubs containing getter hooks. + +function foo(arr) { + for (var i = 0; i < 100; i++) + arr[i].caller; +} +arr = Object.create(Object.prototype); +first = Object.create({}); +first.caller = null; +second = Object.create({}); +second.caller = null; +for (var i = 0; i < 100; ) { + arr[i++] = first; + arr[i++] = foo; + arr[i++] = second; +} +foo.caller; +foo(arr); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-2.js new file mode 100644 index 00000000000..4361ce1262d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/getter-hook-2.js @@ -0,0 +1,19 @@ +// PIC on CALLPROP invoking getter hook. + +function foo(arr) { + for (var i = 0; i < 100; i++) + arr[i].caller(false); +} +arr = Object.create(Object.prototype); +first = Object.create({}); +first.caller = bar; +second = Object.create({}); +second.caller = bar; +for (var i = 0; i < 100; ) + arr[i++] = foo; +foo.caller; +function bar(x) { + if (x) + foo(arr); +} +bar(true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-2.js new file mode 100644 index 00000000000..2756ec5a36e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-2.js @@ -0,0 +1,10 @@ + +x = 30; +function foo() { + assertEq(x, 30); + delete x; + y = 20; + Object.defineProperty(this, 'x', {value:10}); + assertEq(x, 10); +} +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-3.js new file mode 100644 index 00000000000..c3f15c19f07 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-3.js @@ -0,0 +1,18 @@ + +y = 30; +function bar() { + assertEq(y, 30); + Object.defineProperty(this, 'y', {writable:false}); + y = 10; + assertEq(y, 30); +} +bar(); + +x = 30; +function foo() { + assertEq(x, 30); + Object.freeze(this); + x = 10; + assertEq(x, 30); +} +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-4.js b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-4.js new file mode 100644 index 00000000000..5df15f55a53 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/globalOptimize-4.js @@ -0,0 +1,8 @@ + +x = 30; +assertEq(x, 30); + +for (var i = 0; i < 10000; i++) + this[i] = 0; + +assertEq(x, 30); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/in.js b/deps/mozjs/js/src/jit-test/tests/jaeger/in.js new file mode 100644 index 00000000000..a179d60ecb2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/in.js @@ -0,0 +1,21 @@ +function f(arr, b) { + var res = ""; + var a; + if (b) + a = arr; + for (var i=100; i>-200; i--) { + if (i in a) { + res += i; + } + } + return res; +} + +assertEq(f([1, , 2, 3], true), "320"); + +try { + f([1, , 2, 3], false); + assertEq(0, 1); +} catch(e) { + assertEq(e instanceof TypeError, true); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645645.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645645.js new file mode 100644 index 00000000000..6649b559edc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645645.js @@ -0,0 +1,4 @@ +function f() { + f = Math.x; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645666.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645666.js new file mode 100644 index 00000000000..34fd6822ab7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug645666.js @@ -0,0 +1,16 @@ +function f1() { + gc(); +} +function f2() { + with(this) {}; + f1(); +} +function f3() { + f2(); +} +function f4() { + f3(); +} +f3(); +f3(); +f4(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646004.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646004.js new file mode 100644 index 00000000000..8ffb4bd55a7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646004.js @@ -0,0 +1,9 @@ +function reportCompare (expected, actual, description) {} +function f() +{ + f(f, 0x09AA, 0x09B0, f); +} +try { + reportCompare ("outer", f(), + "Inner function statement should not have been called."); +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646480.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646480.js new file mode 100644 index 00000000000..e870e33efec --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug646480.js @@ -0,0 +1,7 @@ +if (true) + function f1() {}; +function f2() { + var y = -8; + return y % 2; +} +f2() / 3; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug647973.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug647973.js new file mode 100644 index 00000000000..c19797a2428 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug647973.js @@ -0,0 +1,11 @@ +function f(a1, a2, a3, a4) { +} +function g(a1, a2) { + var d = new Date(0); + f(); + assertEq(typeof d, 'object'); +} +g(); +gc(); +f(2, 2, 2, f(2, 2, 2, 12 === 12)); +g(false, false); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug651209.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug651209.js new file mode 100644 index 00000000000..27e14a17b19 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug651209.js @@ -0,0 +1,11 @@ +var i = 0; +try { test(); } catch (e) { } +function test() { + var jstop = 900; + var code = ''; + code+=createCode(i); + eval(); +} +function createCode(i) { + jstop+= + + + i + " string.';"; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug655954.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug655954.js new file mode 100644 index 00000000000..f57488b9594 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug655954.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +foo(); +function foo() { + this(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug656221.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug656221.js new file mode 100644 index 00000000000..8319bcf8388 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug656221.js @@ -0,0 +1,5 @@ +function f() { + var a = []; + a.push(a); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug676491.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug676491.js new file mode 100644 index 00000000000..82e6095a0cb --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug676491.js @@ -0,0 +1,14 @@ +Array.prototype.__defineSetter__(32, function() { print("Hello from arbitrary JS");}); +var UBound = 0; +var expect= ''; +var expectedvalues = []; +for (var j=0; j < 10; j++) { + addThis(); + addThis(); + addThis(); + addThis(); +} +function addThis() { + expectedvalues[UBound] = expect; + UBound++; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug680759.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug680759.js new file mode 100644 index 00000000000..975f9e3b76a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/bug680759.js @@ -0,0 +1,14 @@ + +TryToCatch(); +TryToCatch(); +function Thrower( v ) { + throw "Caught"; +} +function Eval( v ) { + SECTION : Thrower(TryToCatch, v, ': 3') +} +function TryToCatch( value, expect ) { + try { + Eval( value ) + } catch (e) { } +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/doubleArg.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/doubleArg.js new file mode 100644 index 00000000000..bd442259982 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/doubleArg.js @@ -0,0 +1,10 @@ +function foo(x, y) { + if (y < 0) {} + return x * 1000; +} +function bar(x, y) { + while (false) {} + assertEq(foo(x, false), 10500); + assertEq(foo(y, false), 11000); +} +bar(10.5, 11); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathAbs.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathAbs.js new file mode 100644 index 00000000000..b1d077add10 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathAbs.js @@ -0,0 +1,31 @@ + +assertEq(Math.abs(-10), 10); +assertEq(Math.abs(-2147483648), 2147483648); +assertEq(Math.abs(2147483648), 2147483648); +assertEq(Math.abs(-0), 0); +assertEq(Math.abs(0), 0); +assertEq(Math.abs(-3.14), 3.14); +assertEq(Math.abs(NaN), NaN); + +/* Inferred as abs(int). */ +function abs1(x) { + return Math.abs(x); +} +assertEq(abs1(1), 1); +assertEq(abs1(-1), 1); +assertEq(abs1(0), 0); +assertEq(abs1(-123) + abs1(234), 357); +assertEq(abs1(-2147483648), 2147483648); // recompile to return double +assertEq(abs1(-2), 2); + +/* Inferred as abs(double). */ +function abs2(x) { + return Math.abs(x); +} +assertEq(abs2(-2.2), 2.2); +assertEq(abs2(123), 123); +assertEq(abs2(-456), 456); +assertEq(abs2(-0), 0); +assertEq(abs2(1.3), 1.3); +assertEq(abs2(NaN), NaN); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathFloor.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathFloor.js new file mode 100644 index 00000000000..1419beb9f3b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathFloor.js @@ -0,0 +1,34 @@ + +assertEq(Math.floor(3.14), 3); +assertEq(Math.floor(-0), -0); +assertEq(Math.floor(0), 0); +assertEq(Math.floor(-1.23), -2); +assertEq(Math.floor(2147483649), 2147483649); +assertEq(Math.floor(2147483648.5), 2147483648); +assertEq(Math.floor(2147483647.1), 2147483647); + +/* Inferred as floor(double). */ +function floor1(x) { + return Math.floor(x); +} +assertEq(floor1(10.3), 10); +assertEq(floor1(-3.14), -4); +assertEq(floor1(-0), -0); // recompile to return double +assertEq(floor1(678.3), 678); + +/* Inferred as floor(double). */ +function floor2(x) { + return Math.floor(x); +} +assertEq(floor2(3.4), 3); +assertEq(floor2(NaN), NaN); // recompile to return double +assertEq(floor2(-4.4), -5); + +/* Inferred as floor(int). */ +function floor3(x) { + return Math.floor(x); +} +assertEq(floor3(4), 4); +assertEq(floor3(-5), -5); +assertEq(floor3(0), 0); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathPow.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathPow.js new file mode 100644 index 00000000000..717e087c969 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathPow.js @@ -0,0 +1,35 @@ + +assertEq(Math.pow(100, 2), 10000); +assertEq(Math.pow(-Infinity, -0.5), 0); +assertEq(Math.pow(-Infinity, 0.5), Infinity); +assertEq(Math.pow(Infinity, -0.5), 0); +assertEq(Math.pow(Infinity, 0.5), Infinity); +assertEq(Math.pow(NaN, -0.5), NaN); +assertEq(Math.pow(NaN, 0.5), NaN); +assertEq(Math.pow(-3.14, -0.5), NaN); +assertEq(Math.pow(-1.23, 0.5), NaN); +assertEq(Math.pow(-0, -0.5), Infinity); +assertEq(Math.pow(-0, 0.5), 0); +assertEq(Math.pow(-1, -0.5), NaN); +assertEq(Math.pow(-1, 0.5), NaN); +assertEq(Math.pow(0, -0.5), Infinity); +assertEq(Math.pow(0, 0.5), 0); +assertEq(Math.pow(1, -0.5), 1); +assertEq(Math.pow(1, 0.5), 1); +assertEq(Math.pow(100, -0.5), 0.1); +assertEq(Math.pow(100, 0.5), 10); + +/* Inferred as pow(double, double). */ +function pow1(x) { + return Math.pow(x, 0.5); +} +assertEq(pow1(100), 10); +assertEq(pow1(144), 12); +assertEq(pow1(-0), 0); +assertEq(pow1(0), 0); +assertEq(pow1(1), 1); +assertEq(pow1(-1), NaN); +assertEq(pow1(NaN), NaN); +assertEq(pow1(-Infinity), Infinity); +assertEq(pow1(Infinity), Infinity); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathRound.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathRound.js new file mode 100644 index 00000000000..60a59e2020c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathRound.js @@ -0,0 +1,41 @@ + +assertEq(Math.round(3.14), 3); +assertEq(Math.round(0.5), 1); +assertEq(Math.round(-0), -0); +assertEq(Math.round(0), 0); +assertEq(Math.round(-1.03), -1); +assertEq(Math.round(2147483649), 2147483649); +assertEq(Math.round(2147483647.5), 2147483648); +assertEq(Math.floor(2147483647.1), 2147483647); + +/* Inferred as round(double). */ +function round1(x) { + return Math.round(x); +} +assertEq(round1(10.3), 10); +assertEq(round1(-3.14), -3); +assertEq(round1(-3.5), -3); +assertEq(round1(-3.6), -4); +assertEq(round1(3.5), 4); +assertEq(round1(3.6), 4); +assertEq(round1(0), 0); +assertEq(round1(-0), -0); // recompile to return double +assertEq(round1(12345), 12345); +assertEq(round1(654.6), 655); + +/* Inferred as round(double). */ +function round2(x) { + return Math.round(x); +} +assertEq(round2(1234.5), 1235); +assertEq(round2(NaN), NaN); // recompile to return double +assertEq(round2(4.6), 5); + +/* Inferred as round(int). */ +function round3(x) { + return Math.round(x); +} +assertEq(round3(4), 4); +assertEq(round3(-5), -5); +assertEq(round3(0), 0); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathSqrt.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathSqrt.js new file mode 100644 index 00000000000..34014994272 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/mathSqrt.js @@ -0,0 +1,29 @@ + +assertEq(Math.sqrt(-Infinity), NaN); +assertEq(Math.sqrt(-3.14), NaN); +assertEq(Math.sqrt(-2), NaN); +assertEq(Math.sqrt(-0), -0); +assertEq(Math.sqrt(0), 0); +assertEq(Math.sqrt(2), Math.SQRT2); +assertEq(Math.sqrt(49), 7); +assertEq(Math.sqrt(Infinity), Infinity); + +/* Inferred as sqrt(double). */ +function sqrt1(x) { + return Math.sqrt(x); +} +assertEq(sqrt1(NaN), NaN); +assertEq(sqrt1(-Infinity), NaN); +assertEq(sqrt1(Infinity), Infinity); +assertEq(sqrt1(-0), -0); +assertEq(sqrt1(2), Math.SQRT2); +assertEq(sqrt1(16), 4); + +/* Inferred as sqrt(int). */ +function sqrt2(x) { + return Math.sqrt(x); +} +assertEq(sqrt2(4), 2); +assertEq(sqrt2(169), 13); +assertEq(sqrt2(0), 0); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-01.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-01.js new file mode 100644 index 00000000000..f6a585d0547 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-01.js @@ -0,0 +1,17 @@ +function bar(x, y) { + return x + y; +} + +function foo(x, y) { + var a = 0; + for (var i = 0; i < 1000; i++) { + a += bar(x, y); + a += bar(x, y); + a += bar(x, y); + a += bar(x, y); + } + return a; +} + +var q = foo(0, 1); +assertEq(q, 4000); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-02.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-02.js new file mode 100644 index 00000000000..fede7f49438 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-02.js @@ -0,0 +1,22 @@ +function getter(a, i) { + return a[i]; +} + +function foo(a, n) { + var res = 0; + for (var i = 0; i < 10; i++) { + res = 0; + for (var j = 0; j < n; j++) { + res += getter(a, j); + } + } + return res; +} + +var n = 100; +var a = Array(n); +for (var i = 0; i < n; i++) + a[i] = i; + +var q = foo(a, n); +assertEq(q, 4950); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-03.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-03.js new file mode 100644 index 00000000000..f54c1c66da1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-03.js @@ -0,0 +1,14 @@ +function choose(x, y, z) { + return x ? y : z; +} + +function foo(x, y, z) { + var a = 0; + for (var i = 0; i < 100; i++) { + a += choose(x, y, z); + } + return a; +} + +var q = foo(true, 10, 0); +assertEq(q, 1000); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-04.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-04.js new file mode 100644 index 00000000000..d2f8535bcfa --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-04.js @@ -0,0 +1,12 @@ +function adder(x, y) { + return x + y; +} + +function foo(x) { + for (var i = 0; i < 100; i++) + var a = adder(x, i); + return a; +} + +var q = foo(0x7ffffff0); +assertEq(q, 2147483731); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-05.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-05.js new file mode 100644 index 00000000000..b763fb3649b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-05.js @@ -0,0 +1,13 @@ +function adder(x, y) { + return Math.floor(x + y); +} + +function foo(x) { + for (var i = 0; i < 100; i++) { + var a = adder(x, i); + } + return a; +} + +var q = foo(0x7ffffff0 + .5); +assertEq(q, 2147483731); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-06.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-06.js new file mode 100644 index 00000000000..074bb520178 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-06.js @@ -0,0 +1,17 @@ +function popper(a) { + return a.pop(); +} + +function foo(x) { + for (var i = 0; i < 10; i++) { + var q = popper(x); + if (i < 5) + assertEq(q, 5 - i); + else + assertEq(q, undefined); + } + return q; +} + +var q = foo([1,2,3,4,5]); +assertEq(q, undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-07.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-07.js new file mode 100644 index 00000000000..dfe076a6565 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-07.js @@ -0,0 +1,15 @@ +function multiple(a) { + if (a > 10) + return 1; + return 0; +} + +function foo(x) { + var a = 0; + for (var i = 0; i < 100; i++) + a += multiple(i); + return a; +} + +var q = foo([1,2,3,4,5]); +assertEq(q, 89); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-08.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-08.js new file mode 100644 index 00000000000..22198388ef6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-08.js @@ -0,0 +1,21 @@ +function first(a, b) { + return second(a, b); +} + +function second(a, b) { + return third(a, b, a + b); +} + +function third(a, b, c) { + return a + b + c; +} + +function foo(x) { + var a = 0; + for (var i = 0; i < 100; i++) + a += first(x, i); + return a; +} + +var q = foo(10); +assertEq(q, 11900); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-09.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-09.js new file mode 100644 index 00000000000..e970b77e43b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-09.js @@ -0,0 +1,19 @@ +function multiple(a) { + if (a > 10) + return a * 20; + return 0; +} + +function deeper(a, b) { + return multiple(a + b); +} + +function foo() { + var a = 0; + for (var i = 0; i < 10; i++) + a += deeper(0x7ffffff0, i); + return a; +} + +var q = foo(); +assertEq(q, 429496727300); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-10.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-10.js new file mode 100644 index 00000000000..492d055e84c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-10.js @@ -0,0 +1,13 @@ +function copied(x, y) { + return x + y; +} + +function foo(x) { + var a = 0; + for (var i = 0; i < 100; i++) + a += copied(x, x); + return a; +} + +var q = foo(5); +assertEq(q, 1000); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-11.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-11.js new file mode 100644 index 00000000000..5097c1a0519 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/scripted-11.js @@ -0,0 +1,13 @@ +what = 0; + +function f(x) { + g(x); +} + +function g(x) { + var a = ; + eval("what = true"); +} + +f(10); +assertEq(what, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharAt.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharAt.js new file mode 100644 index 00000000000..bf2bbbd67f7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharAt.js @@ -0,0 +1,49 @@ + +assertEq("foo".charAt(-123), ""); +assertEq("foo".charAt(-1), ""); +assertEq("foo".charAt(0), "f"); +assertEq("foo".charAt(1), "o"); +assertEq("foo".charAt(2), "o"); +assertEq("foo".charAt(3.4), ""); +assertEq("foo".charAt(), "f"); +assertEq("".charAt(), ""); +assertEq("".charAt(0), ""); +assertEq("abc\u9123".charAt(3), "\u9123"); // char without unit string + +/* Inferred as string.charAt(int). */ +function charAt1(x) { + return "abc".charAt(x); +} +assertEq(charAt1(-1), ""); +assertEq(charAt1(0), "a"); +assertEq(charAt1(1), "b"); +assertEq(charAt1(2), "c"); +assertEq(charAt1(3), ""); +assertEq(charAt1(1234), ""); + +/* Inferred as string.charAt(double). */ +function charAt2(x) { + return "abc".charAt(x); +} +assertEq(charAt2(-1.3), ""); +assertEq(charAt2(-0), "a"); +assertEq(charAt2(2), "c"); +assertEq(charAt2(2.3), "c"); +assertEq(charAt2(3.14), ""); +assertEq(charAt2(NaN), "a"); + +/* Test ropes. */ +function charAt3(s, i) { + var s2 = "abcdef" + s + "12345"; + return s2.charAt(i); +} +assertEq(charAt3("abcdef", 14), "3"); +assertEq(charAt3("a" + "b", 1), "b"); +assertEq(charAt3("abcdefg" + "hijklmnop", 10), "e"); + +/* Other 'this'. */ +var arr = [1, 2]; +arr.charAt = String.prototype.charAt; +assertEq(arr.charAt(1), ","); + + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharCodeAt.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharCodeAt.js new file mode 100644 index 00000000000..3f0bfe2cfd2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/stringCharCodeAt.js @@ -0,0 +1,47 @@ + +assertEq("foo".charCodeAt(-123), NaN); +assertEq("foo".charCodeAt(-0), 102); +assertEq("foo".charCodeAt(0), 102); +assertEq("foo".charCodeAt(2), 111); +assertEq("foo".charCodeAt(3.4), NaN); +assertEq("foo".charCodeAt(), 102); +assertEq("".charCodeAt(), NaN); +assertEq("".charCodeAt(0), NaN); + +/* Inferred as string.charCodeAt(int). */ +function charCodeAt1(x) { + return "abc".charCodeAt(x); +} +assertEq(charCodeAt1(-1), NaN); +assertEq(charCodeAt1(0), 97); +assertEq(charCodeAt1(1), 98); +assertEq(charCodeAt1(2), 99); +assertEq(charCodeAt1(3), NaN); +assertEq(charCodeAt1(1234), NaN); + +/* Inferred as string.charCodeAt(double). */ +function charCodeAt2(x) { + return "abc".charCodeAt(x); +} +assertEq(charCodeAt2(-1.3), NaN); +assertEq(charCodeAt2(-0), 97); +assertEq(charCodeAt2(2), 99); +assertEq(charCodeAt2(2.3), 99); +assertEq(charCodeAt2(3.14), NaN); +assertEq(charCodeAt2(NaN), 97); + +/* Test ropes. */ +function charCodeAt3(s, i) { + var s2 = "abcdef" + s + "12345"; + return s2.charCodeAt(i); +} +assertEq(charCodeAt3("abcdef", 14), 51); +assertEq(charCodeAt3("a" + "b", 1), 98); +assertEq(charCodeAt3("abcdefg" + "hijklmnop", 10), 101); + +/* Other 'this'. */ +var n = new Number(123); +n.charCodeAt = String.prototype.charCodeAt; +assertEq(n.charCodeAt(1), 50); + + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/inline/undefinedLocal.js b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/undefinedLocal.js new file mode 100644 index 00000000000..f6fb1299661 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/inline/undefinedLocal.js @@ -0,0 +1,19 @@ + +function bar(n) { + var a; + if (n < 50) + a = n; + return a; +} + +function foo() { + for (var i = 0; i < 100; i++) { + var q = bar(i); + if (i < 50) + assertEq(q, i); + else + assertEq(q, undefined); + } +} + +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug651155.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug651155.js new file mode 100644 index 00000000000..37c16fff193 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug651155.js @@ -0,0 +1,6 @@ +ForIn_2(); +function ForIn_2( object ) { + PropertyArray=new Array; + var PropertyArray = 'Do not assert: !cx->throwing'; + for ( i in object ) PropertyArray.length-1; +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug654393.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug654393.js new file mode 100644 index 00000000000..e603392a406 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug654393.js @@ -0,0 +1,12 @@ +var VERSION = "ECMA_2"; +DoWhile( + new DoWhileObject(false, false, false, VERSION) +); +function DoWhileObject( out1, out2, out3, in1 ) { + this.breakIn = in1 +} +function DoWhile(object) { + do { + if (object.breakIn) {} + } while(false); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug655854.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug655854.js new file mode 100644 index 00000000000..81c71943142 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug655854.js @@ -0,0 +1,8 @@ + +function foo(a, b, c) { + var res = 0; + for (var b = 0; b < c; b++) + res += a[b]; + return res; +} +assertEq(foo([1,2,3], 0, 10), NaN); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug658290.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug658290.js new file mode 100644 index 00000000000..cfb42066af8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug658290.js @@ -0,0 +1,8 @@ +var SECTION = "15.4.5.2-2"; +addCase(new Array, 0, Math, Math.pow(2, SECTION)); +var arg = "", i = 0; +var a = eval("new Array(" + arg + ")"); +addCase(a, i, +i + 1, Math.pow(2, 12) + i + 1, true); +function addCase(object, old_len, set_len, new_len, checkitems) { + for (var i = old_len; i < new_len; i++) if (object[i] != 0) {} +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug659452.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug659452.js new file mode 100644 index 00000000000..cf5015c7270 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug659452.js @@ -0,0 +1,5 @@ +test(); +function test() { + var t = function () function printStatus() {}; + for (var j = 0; j < 10; j++) t["-1"] +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug668643.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug668643.js new file mode 100644 index 00000000000..fe41c7ffc78 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug668643.js @@ -0,0 +1,12 @@ + +function foo(a,n) { + var x = {a:[]}; + for (var i = 0; i < n; ) { + a[i]; + x.a[i]; + a[++i]; + } +} +var a = [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]; +var n = a.length; +foo(a,n); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug671814.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug671814.js new file mode 100644 index 00000000000..69b581f772b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug671814.js @@ -0,0 +1,12 @@ +var ta = Int8Array([]); +function Int8Array(summary) { + summary.length; +} +function test() { + ctors = [ Int8Array ] + for (var i = 0; i < 10; i++) { + ctor = ctors[0] + b = ctor(0) + } +} +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug680809.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug680809.js new file mode 100644 index 00000000000..22bcbf3c46d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug680809.js @@ -0,0 +1,8 @@ +function f0(p0) { + var v0; + v0 = 1.7; + loop0: while (v0) { + v0 = p0; + } +} +f0(0); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug684621.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug684621.js new file mode 100644 index 00000000000..9ca13bc7f4c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/bug684621.js @@ -0,0 +1,15 @@ +function runRichards() { + queue = new Packet; + Packet(queue, ID_DEVICE_A, KIND_DEVICE); + new Packet; +} +var ID_DEVICE_A = 4; +var KIND_DEVICE = 0; +Packet = function (queue) { + this.link = null + if (queue == null) return; + var peek, next = queue; + while ((peek = next.link) != null) + ID_HANDLER_B +}; +runRichards() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-01.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-01.js new file mode 100644 index 00000000000..0c0f12357ad --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-01.js @@ -0,0 +1,35 @@ +function foo(x, n) { + for (var i = 0; i < n; i++) + x[i] = i; + var q = 0; + for (var i = 0; i < 10; i++) { + for (var j = 0; j < n; j++) + q += x[j]; + } + return q; +} + +var a = foo([], 100); +assertEq(a, 49500); + +function basic1(x) { + var q = 0; + for (var i = 0; i < 4; i++) + q += x[i]; + return q; +} + +var b = basic1([1,2,3,4]); +assertEq(b, 10); + +ARRAY = [1,2,3,4]; + +function basic2() { + var q = 0; + for (var i = 0; i < 4; i++) + q += ARRAY[i]; + return q; +} + +var c = basic2(); +assertEq(c, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-02.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-02.js new file mode 100644 index 00000000000..79c3f119521 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-02.js @@ -0,0 +1,12 @@ +function foo(x, n) { + var a = 0; + for (var i = 0; i < n; i++) + a += x[3]; + return a; +} + +var a = foo([1,2,3,4], 100); +assertEq(a, 400); + +var b = foo([1,2], 100); +assertEq(b, NaN); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-03.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-03.js new file mode 100644 index 00000000000..c5c2ba6190f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-03.js @@ -0,0 +1,12 @@ +function foo(x, j, n) { + var a = 0; + for (var i = 0; i < n; i++) + a += x[j]; + return a; +} + +var a = foo([1,2,3,4], 3, 100); +assertEq(a, 400); + +var b = foo([1,2,3,4], 5, 100); +assertEq(b, NaN); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-04.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-04.js new file mode 100644 index 00000000000..f17bd7f737c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-04.js @@ -0,0 +1,18 @@ +function bar(x, i) { + if (i == 50) + x.length = 0; +} + +function foo(x, j, n) { + var a = 0; + for (var i = 0; i < n; i++) { + var q = x[j]; + bar(x, i); + if (typeof q == 'undefined') + a++; + } + return a; +} + +var a = foo([1,2,3,4], 3, 100); +assertEq(a, 49); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-05.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-05.js new file mode 100644 index 00000000000..b99b07893be --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-05.js @@ -0,0 +1,19 @@ +function bar(x, i) { + if (i == 50) + foo.arguments[1] = 20; +} + +function foo(x, j, n) { + var a = 0; + arguments; + for (var i = 0; i < n; i++) { + var q = x[j]; + bar(x, i); + if (typeof q == 'undefined') + a++; + } + return a; +} + +var a = foo([1,2,3,4], 3, 100); +assertEq(a, 0); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-06.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-06.js new file mode 100644 index 00000000000..fc2919cda2c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-06.js @@ -0,0 +1,14 @@ + +function foo(x, n, y) { + var q = 0; + for (var j = 0; j < n; j++) { + if (x[j] < y) + q++; + } + assertEq(q, 1); +} + +var x = [1,2,3,4,5]; +var y = { valueOf: function() { x.length = 0; return 6; } }; + +var a = foo(x, 5, y); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-07.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-07.js new file mode 100644 index 00000000000..a409f705591 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-07.js @@ -0,0 +1,17 @@ + +var res = 0; + +function foo(x, n, y) { + for (var j = 0; j < n; j++) { + x[j]; + y.f; + } +} + +var x = [1,2,3,4,5]; +var y = {}; +Object.defineProperty(y, 'f', {get:function() { res++; x.length = 2; }}); + +var a = foo(x, 5, y); + +assertEq(res, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-08.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-08.js new file mode 100644 index 00000000000..a695dd1a762 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-08.js @@ -0,0 +1,7 @@ + +function foo(x,n) { + for (var i = -5; i < n; i++) { + x[i] = 10; + } +} +foo([1,2,3,4,5],5); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-09.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-09.js new file mode 100644 index 00000000000..1b398c7c26c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-09.js @@ -0,0 +1,11 @@ + +function foo(x, y) { + for (var i = 0; i < x.length; i++) { + x[i]; + if (i < 20) + y[i + 1] = 0; + } +} + +var q = Array(1,2,3,4,5); +foo(q, []); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-10.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-10.js new file mode 100644 index 00000000000..43a3084c9d3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/hoist-10.js @@ -0,0 +1,39 @@ +function foo1(x, n) { + var i = 0; + while (--n >= 0) { + x[i++] = 0; + } +} +foo1([1,2,3,4,5],5); + +function foo2(x, n) { + var i = 0; + while (--n >= 0) { + x[i++] = 0; + } +} +foo2([1,2,3,4,5],6); + +function foo3(x, n) { + var i = 0; + while (n-- >= 0) { + x[i++] = 0; + } +} +foo3([1,2,3,4,5],5); + +function foo4(x, n) { + var i = 0; + while (--n >= 0) { + x[++i] = 0; + } +} +foo4([1,2,3,4,5],5); + +function foo5(x, n) { + var i = 0; + while (--n >= 0) { + x[++i] = 0; + } +} +foo5([1,2,3,4,5,6],5); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-1.js new file mode 100644 index 00000000000..f0ef122f3c5 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-1.js @@ -0,0 +1,7 @@ +function foo(x) { + for (var i = 0x7ffffff0; i <= x; i++) { + var y = i; + } + return y; +} +assertEq(foo(0x7fffffff), 0x7fffffff); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-2.js new file mode 100644 index 00000000000..129c92f1fef --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/integer-2.js @@ -0,0 +1,10 @@ +function foo(x, y, z, a) { + for (var i = 0x7fff; i < 0xffff; i++) { + var y = ((x + y) + (z + a[0])) | 0; + } + return y; +} +assertEq(foo(0x7fffffff, 0x7fffffff, 0x7fffffff, [0x7fffffff]), 2147385343); + +var q = [0x7fffffff]; +assertEq(eval("foo(0x7fffffff, 0x7fffffff, {valueOf:function() {q[0] = 'e4'; return 0;}}, q)"), 438048096); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/loops/property-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/property-1.js new file mode 100644 index 00000000000..81a6b3c3601 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/loops/property-1.js @@ -0,0 +1,19 @@ + +function foo(x, y) { + var res = 0; + for (var i = 0; i < 10; i++) { + res += x.f + y[i]; + } + return res; +} + +var x = {f:0}; +var y = Array(10); +for (var i = 0; i < 10; i++) { + if (i == 5) + Object.defineProperty(Object.prototype, 5, {get: function() { x.f = 10; return 5}}); + else + y[i] = i; +} + +assertEq(foo(x, y), 85); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-1.js new file mode 100644 index 00000000000..47ac2fc6ef1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-1.js @@ -0,0 +1,29 @@ + +function Foo(x) +{ + this.f = x + 10; +} + +function Bar() +{ + this.g = 0; +} + +Bar.prototype = Foo.prototype; + +var x = new Foo(0); +var y = new Bar(); + +assertEq(10, eval("x.f")); +assertEq(undefined, eval("y.f")); + +function Other(x) +{ + this.f = x + 10; +} + +var a = new Other(0); +var b = Object.create(Other.prototype); + +assertEq(10, eval("a.f")); +assertEq(undefined, eval("b.f")); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-2.js new file mode 100644 index 00000000000..88df13e2c75 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-2.js @@ -0,0 +1,16 @@ + +function Foo(x) +{ + this.f = x + 10; +} + +var x = new Foo(0); +assertEq(10, eval("x.f")); + +called = false; +Object.defineProperty(Foo.prototype, 'f', {set: function() { called = true; }}); + +var y = new Foo(0); +assertEq(10, eval("x.f")); +assertEq(undefined, eval("y.f")); +assertEq(called, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-3.js b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-3.js new file mode 100644 index 00000000000..90327e47a33 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-3.js @@ -0,0 +1,67 @@ + +// Properties cleared in the middle of a single function constructor. + +function foo(x, y) { + this.f = 0; + this.g = x + y; + this.h = 2; +} + +var called = false; +var a = 0; +var b = {valueOf: function() { Object.defineProperty(Object.prototype, 'g', {set: function() { called = true }}) }}; +var c = new foo(a, b); + +assertEq(called, true); +assertEq(c.g, undefined); + +// Properties cleared in the middle of a constructor callee. + +function foo2(x, y) { + this.a = 0; + this.b = 1; + bar2.call(this, x, y); + this.c = 2; +} +function bar2(x, y) { + this.d = x + y; + this.e = 3; +} + +var called2 = false; +var xa = 0; +var xb = {valueOf: function() { Object.defineProperty(Object.prototype, 'e', {set: function() { called2 = true }}) }}; +var xc = new foo2(xa, xb); + +assertEq(called2, true); +assertEq(xc.e, undefined); +assertEq(xc.c, 2); + +// Properties cleared after a constructor callee. + +function foo3() { + this.aa = 0; + this.bb = 1; + bar3.call(this); + this.cc = 2; + baz(); + xbar3.call(this); + this.dd = 3; +} +function bar3() { + this.ee = 4; +} +function xbar3() { + this.ff = 5; +} +function baz() { + eval("xbar3.call = function() { called3 = true }"); +} + +var called3 = false; +var c3 = new foo3(); +assertEq(c3.cc, 2); +assertEq(c3.ee, 4); +assertEq(c3.ff, undefined); +assertEq(c3.dd, 3); +assertEq(called3, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-4.js b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-4.js new file mode 100644 index 00000000000..942474875a1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/propertyOptimize-4.js @@ -0,0 +1,17 @@ +// Make sure new script properties can be invalidated on specialized prototype +// types while they are still being constructed. + +function Foo(a, b, c) { + this.x = a + b; + this.y = c; +} + +updated = false; +var o = {valueOf: function() { + Object.defineProperty(Object.prototype, 'y', {set:function() { updated = true; }}) + }}; + +function Bar() {} +Bar.prototype = new Foo(o, 1, 2); +assertEq(updated, true); +assertEq(Bar.prototype.y, undefined); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/arith.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/arith.js new file mode 100644 index 00000000000..a705a2937b6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/arith.js @@ -0,0 +1,49 @@ + +/* Handle recompilation of arithmetic operations, and on-stack int -> double conversion. */ + +function add(x, y) +{ + var z = x + y; + assertEq(z, 2147483732); + assertEq(z - 10, 2147483722); +} +add(0x7ffffff0, 100); + +function mul(x, y) +{ + var z = x * y; + assertEq(z, 4294967264); +} +mul(0x7ffffff0, 2); + +function div1(x, y) +{ + var z = x / y; + assertEq(z + 10, 20); +} +div1(100, 10); + +function div2(x, y) +{ + var z = x / y; + assertEq(z + 10, 20.5); +} +div2(105, 10); + +function uncopy(x, y) +{ + var q = x; + x += y; + q++; + assertEq(q, 2147483633); + assertEq(x, 2147483732); +} +uncopy(0x7ffffff0, 100); + +function addmore(x, y) +{ + var q = (x + 10) + (x + y); + assertEq(q, 4294967374); + x = q; +} +addmore(0x7ffffff0, 100); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug617592.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug617592.js new file mode 100644 index 00000000000..cec17748039 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug617592.js @@ -0,0 +1,3 @@ + +var x; +-(x === null); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621292.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621292.js new file mode 100644 index 00000000000..11dca94710b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621292.js @@ -0,0 +1,24 @@ + +/* Don't crash. */ + +var count = 0; + +function callbackfn(v) { + if (++count == 98) + count = 0x7ffffff0; + return arr[0] + count; +} + +function foo() { + arr = [1,2,3,4,5]; + for (var i = 0; i < 50; i++) + arr = arr.map(callbackfn); +} +foo(); + +function f(a,b,c) { + a = 1; b = 'str'; c = 2.1; + return arguments[0]; +} +for (var i = 0; i < 20; i++) + assertEq(f(10,'sss',1), 1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621328.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621328.js new file mode 100644 index 00000000000..f755549c96b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug621328.js @@ -0,0 +1,9 @@ +function foo() { +}; +function f() { + var e = foo; + a = new e(); + assertEq(typeof(a), "object"); + e=a; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug638977.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug638977.js new file mode 100644 index 00000000000..0fac7822497 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug638977.js @@ -0,0 +1,6 @@ +function f() { + gc(); + [].unshift(false); +} +f(); +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639508.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639508.js new file mode 100644 index 00000000000..a25a70c601f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639508.js @@ -0,0 +1,2 @@ +var o = 2; +o = o.p; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639882.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639882.js new file mode 100644 index 00000000000..3f9f898a291 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug639882.js @@ -0,0 +1,5 @@ +var x = 2; +x = -(x == 3); + +var y = ""; +typeof(z) + typeof(eval("z = y")); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug640608.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug640608.js new file mode 100644 index 00000000000..1c5c3c81067 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug640608.js @@ -0,0 +1,10 @@ +try { +{ + function x() {} +} +o = (0).__proto__; +function f(o) { + o._("", function() {}) +} +f(o) +} catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641225.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641225.js new file mode 100644 index 00000000000..0a407f80d4d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641225.js @@ -0,0 +1,151 @@ +gczeal(1); +var o0 = Function.prototype; +o1 = {}; +var o4 = Error.prototype; +o5 = new Int32Array(100); +o6 = Proxy.create({ +}); +o8 = Proxy.createFunction({ + get: function() {return 10; }, +}, function(){ return {}; }, function(){ return {}; }); +o9 = {}; +var o10 = -500; +var o12 = new Int32Array(100); +function f0(o) { +} +function f1(o) { +} +function f3(o) { + with(o) {} + o[o5] = o5; +} +function f5(o) { +}for(var f2 in o9) { + for(var i1=0; i1<83; i1++) { + for(var i3=0; i3<98; i3++) { + for(var x in f1) { f1[x]; }; + } + Object.defineProperty(o0, 'constructor', {enumerable: true,unused: 1 }); + f1(f5); + f4(f3); + f3(o8); + f2(o5); + o9.toString(1, 2); + f4.caller = o3; + f2(o6); + f0(f2); + f1(f2); + f0(f1); + var key = Object.getOwnPropertyNames(o9)[2]; if(key) delete o9[key]; + var props = Object.getOwnPropertyNames(o5); + if (props.length > 6) { + var prop = props[6]; + o8[prop] = o5[prop]; + } + f3(f1); + f1(f5); + } + for(var o3 in f1) { + f1(o3); + f4(o3); + o0 == f4; + f1(f3); + Object.freeze(o12); + f0(o9); + new f0(o1); + o4 = o5.call(o4, o4, o4, o4, o4, o4); + f2(o10); + var prop = Object.getOwnPropertyNames(o0)[15]; + if (prop) { Object.defineProperty(o0, prop, {configurable: true,enumerable: true,get: function(){},set: function(){},unused: 1 }); } + f3(f1); + new f2(o0); + } + f5(o9); + gc(); + f0(o2); + f3(f4); + new f4(o7); + f1 = new o10(f1, f1, f1, f1); + f5(o10); + f5(o7); + f0(o7); + f1(o10); + f3(o10); + delete f0.constructor; + f0(f3); + f1 = wrap(f3); + f4(f1); + delete o1.prototype; + f4(o5); + f2(f2); + o1 + ''; + f2(f2); + f0(o12); + f0(o12); + f1(o3); + o5[3] = 8.3; + o10['__proto_' + '_']; +} +for(var o2 in f5) { + for(var o10 in f3) { + delete f2['__proto_' + '_']; + o8 = f1.toString; + f1(o1); + f0(o9); + f2(o12); + var key = Object.getOwnPropertyNames(o3)[19]; if(key) o9 = o3[key]; + f1(o10); + f4(f1); + f1(o1); + f1(o7); + for(var x in o1) { o1[x]; }; + f0(o8); + f4(o1); + f0(o1); + f0.p0 = o6; + f3(o9); + f5(o8); + f2 >>> o7; + if(o3 === o8) {} + f5(o3); + } + o5[0] = f4; + o0.caller = function() { }; + Object.freeze(f0); + f4(o3); + o7.p0 = o3; + f1(f5); + f4(o10); + f2(o5); + f2(o5); + f0(o3); + o9.watch('p3', function() {}); + o8[o8] = o8; + f0(o5); + f1(o6); + f2 = Object.create(o5); + var key = Object.getOwnPropertyNames(o11)[23]; if(key) f2 = o11[key]; + f5(o9); + o12 = o6.bind(o12, o12, o12); + f5(f4); + f1(o1); + f0(o11); + f1(o11); + eval('f4'); + f4(o1); + Object.isExtensible(o7); +} +(function() { + f1(o12); + f5 + ''; + if(o8 != o3) {} +})(); +f1(o10); +f3(f0); +o4.toSource = function() { }; +var _o = o1; +var prop = Object.getOwnPropertyNames(_o)[5]; +if (prop) { _o[prop](o2, o2); } +f3(o0); +f1(f3); +Object.isExtensible(f1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641269.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641269.js new file mode 100644 index 00000000000..e5e9ebce9b6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641269.js @@ -0,0 +1,5 @@ +// |jit-test| debug + +(function() { + const x = [][x] +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641535.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641535.js new file mode 100644 index 00000000000..732eeb08cbd --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug641535.js @@ -0,0 +1,2 @@ +var o = {}; +o[o.p] = 2; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug642405.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug642405.js new file mode 100644 index 00000000000..2bbca348326 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug642405.js @@ -0,0 +1,13 @@ +function startTest() {}; +try { +} +catch(ex) +{ + actual = ex + ''; +} +var actual = 'no error'; +var prefValue; +DESCRIPTION = "var class = true"; +EXPECTED = "error"; +foo(EXPECTED[prefValue], DESCRIPTION[prefValue], startTest[prefValue]); +function foo() {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643182.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643182.js new file mode 100644 index 00000000000..cb134fe0bdc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643182.js @@ -0,0 +1,7 @@ +x = 123; +function f() {} +function g(o) { + y = x.p; + eval('o'); +} +g(f); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643376.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643376.js new file mode 100644 index 00000000000..300fbc90d73 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643376.js @@ -0,0 +1,8 @@ +SECTION = 0; +function TestCase() {} +function outer_func(x) +{ + var y = "inner"; + new TestCase( SECTION, { SECTION: ++y }); +} +outer_func(1111); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643552.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643552.js new file mode 100644 index 00000000000..7e2a024de41 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643552.js @@ -0,0 +1,2 @@ +var x = Proxy.create( {get:function(r,name){Proxy = 0;}} ); +try { x.watch('e', function(){}); } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643669.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643669.js new file mode 100644 index 00000000000..d66639df405 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug643669.js @@ -0,0 +1,3 @@ +try {(function () { + eval("gc().l()") + })() } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug645044.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug645044.js new file mode 100644 index 00000000000..e31762a8e6a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug645044.js @@ -0,0 +1,4 @@ + +this.__defineGetter__("x", gc); +x.__proto__ = this; +__proto__ = 44; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug646267.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug646267.js new file mode 100644 index 00000000000..636fedd343e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug646267.js @@ -0,0 +1,8 @@ +function t(code) { + var f = new Function(code); + try { f(); } catch (e) { } +} +t(""); +t(""); +t(""); +t("this.function::a = 7;"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647183.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647183.js new file mode 100644 index 00000000000..0405a548a45 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647183.js @@ -0,0 +1,23 @@ +var SECTION = ""; +var VERSION = ""; +function test() {} +function writeTestCaseResult( expect, actual, string ) { + if (typeof document != "object" || +!document.location.href.match(/jsreftest.html/)) { + } +} +TIME_0000 = now = new Date; +TIME_NOW = now.valueOf(); +function DaysInYear( y ) { +function MakeDate( day, time ) { + +} +} +function TimeClip( t ) { + if ( isNaN ) { Number.NaN; } +} +function runDSTOffsetCachingTestsFraction(part, parts) { print; }; +test_times=( TIME_NOW, TIME_0000, ( SECTION, VERSION+".getUTCMinutes()", + TIME_NOW.test_times,VERSION.SECTION ) , TIME_0000, TIME_0000, + 0, 0 ); +try { j = 0( SECTION, TimeClip(1.1), 0 ); } catch (e) {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647199.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647199.js new file mode 100644 index 00000000000..b9d330f8fc1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647199.js @@ -0,0 +1,11 @@ +TryInWhile( new TryObject( "hello", ThrowException, true ) ); +function TryObject( value, throwFunction, result ) { + this.thrower=throwFunction +} +function ThrowException() TryInWhile(1); +function TryInWhile( object ) { + try { + object.thrower() + } catch ( e ) { + } +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647532.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647532.js new file mode 100644 index 00000000000..3c8bc3f404f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647532.js @@ -0,0 +1,4 @@ +try { Function("\ + __defineSetter__(\"x\",Object.keys)\ + (z=x instanceof[].some)\ +")() } catch (e) { } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647547.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647547.js new file mode 100644 index 00000000000..6f9c970aa7b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647547.js @@ -0,0 +1,9 @@ +DoWhile(new DoWhileObject); +new DoWhileObject("", Boolean); +function DoWhileObject( d, e, s ) { + this.whileExpression=e; +} +function DoWhile( object ) { + while ( object.whileExpression ) eval( ); + Boolean +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-1.js new file mode 100644 index 00000000000..25975244786 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-1.js @@ -0,0 +1,18 @@ +function f() { + function g() { + eval(""); + gc(); + Math.abs(4); + NaN; + } + g(); +} +function h() { + var x, y; + x = Math.floor(-0); + y = parseInt("1"); +} + +f(); +h(); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-2.js new file mode 100644 index 00000000000..6ebea8482d7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug647991-2.js @@ -0,0 +1,2 @@ +[""][NaN] = 2; +-([][[""][String] = ""] = null); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648502.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648502.js new file mode 100644 index 00000000000..26caf6b615b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648502.js @@ -0,0 +1,12 @@ +function f(x, y) { + -(undefined ? 0 : 0); + assertEq(y === y, true); + return 0; +} +f(1, 2); +{ + f(3, 3.14); + f(true, f(4, 5)); + + function g() {} +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648567.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648567.js new file mode 100644 index 00000000000..5b7df219d95 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648567.js @@ -0,0 +1,16 @@ +var arr = [-10, true]; +true || arr[0]; + +function g() { + var x = arr[12]; + var y = arr.length; + arr[undefined] = x; + assertEq(y, 2); +} +{ + function f() { + gc(); + g(); + } + f(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648843.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648843.js new file mode 100644 index 00000000000..aaf5843dcd0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648843.js @@ -0,0 +1,8 @@ + +function jit(on) +{ + options().match +} +function options() { return "methodjit"; } +gczeal(2); +for (i = 0; i < 100 ; ++i) { jit(jit(42, [])); } diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648966.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648966.js new file mode 100644 index 00000000000..df0f9d8cd3a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug648966.js @@ -0,0 +1,7 @@ + +function f(x) { + gc(); + -x; + -null; +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649261.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649261.js new file mode 100644 index 00000000000..fabc38e73e8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649261.js @@ -0,0 +1,2 @@ +var DESCRIPTION; +eval("DESCRIPTION += \"Non-character escapes in identifiers negative test.\";"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649769.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649769.js new file mode 100644 index 00000000000..28c823b4227 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug649769.js @@ -0,0 +1,16 @@ + +function g(x) { + if (!x) { + throw 1; + } +} + +function f(a, b, c, d) { + var x = [].push(3); + g(true); + assertEq(x, 1); +} +f(1.2, 2, 3, 4); +gc(); +f(1, 2, 3, 4); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug651119.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug651119.js new file mode 100644 index 00000000000..598a36574d9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug651119.js @@ -0,0 +1,51 @@ +Object.extend = function(destination, source) { + for (var property in source) + destination[property] = source[property]; +}; +var Enumerable = { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + each: function(iterator, context) { + var index = 0; + this._each(function(value) { + iterator.call(context, value, index++); + }); + }, + map: function(iterator, context) { + var results = []; + this.each(function(value, index) { + var res = iterator.call(context, value); + results.push(res); + }); + return results; + }, + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, +}; +Object.extend(Array.prototype, Enumerable); +function $A(iterable) { + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} +function g() { + return [1, 2, 3, 4, 5].each(function(part) { + return 0; + }); +} +function f() { + g(); + g(); + g(); + g(); + var result = [[2, 1, 3], [6, 5, 4]]; + result = result.invoke('invoke', 'toString', 2); + result[0].join(', '); +}; +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug653980.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug653980.js new file mode 100644 index 00000000000..f5a4d2a9f0d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug653980.js @@ -0,0 +1,13 @@ + +function f(code) { + try { + Function(code)() + } catch(r) {} +} { + function x() {} +} +f("") +f("") +f("") +f("x::e") +if (typeof w == "") {} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug654536.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug654536.js new file mode 100644 index 00000000000..5a3266ff6a6 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug654536.js @@ -0,0 +1,6 @@ +function f() { + var x = Object.prototype.hasOwnProperty.call(1); + assertEq(x, false); + isNaN(2); +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655949.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655949.js new file mode 100644 index 00000000000..1adace1a100 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655949.js @@ -0,0 +1,6 @@ +var a; +try { + a(); +} catch(e) { + assertEq(e instanceof TypeError, true); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655998.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655998.js new file mode 100644 index 00000000000..a48eb32152b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug655998.js @@ -0,0 +1,7 @@ +function f(x) { + var y; + gc(); + ++x.x; +} +f(1); +f.call(2, 3); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug656753.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug656753.js new file mode 100644 index 00000000000..b897c7d0951 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug656753.js @@ -0,0 +1,20 @@ + +var global = 0; + +function foo(i) { + global = i; + if (global == 8) { + eval("global = 'three'"); + throw global; + } +} + +var caught = false; +try { + for (var i = 0; i < 10; i++) { + Array.map([i], foo); + } +} catch (e) { caught = true; } + +assertEq(caught, true); +assertEq(global, 'three'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug657288.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug657288.js new file mode 100644 index 00000000000..2a19c5c5152 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug657288.js @@ -0,0 +1,9 @@ +// |jit-test| error: TypeError +new DoWhileObject; +function DoWhileObject(breakOut, breakIn, iterations, loops) { + loops.prototype = new DoWhile; + this.looping; +} +function DoWhile(object) { + do {} while (object); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658209.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658209.js new file mode 100644 index 00000000000..966c10f28b8 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658209.js @@ -0,0 +1,10 @@ +for (var i=0; i<20; i++) { + (function () { + var x; + (function () { + x = /abc/; + x++; + gc(); + })(); + })(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658211.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658211.js new file mode 100644 index 00000000000..02de2925917 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658211.js @@ -0,0 +1,13 @@ +function foo(x) { + return bar(x); +} +function bar(x) { + return x.f + 10; +} +var g = Object(); +g.f = 10; +assertEq(foo(g), 20); +assertEq(foo(g), 20); +assertEq(foo(g), 20); +eval("g.f = 'three'"); +assertEq(foo(g), 'three10'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658212.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658212.js new file mode 100644 index 00000000000..a1876492348 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658212.js @@ -0,0 +1,30 @@ +var gTestcases = Array; +function TestCase(n, d, e, a) { + this.description = d + gTestcases[gTc] = this +} +TestCase.prototype.dump=function () + + + + + this.description + + + + + '\n';function printStatus (msg) +function toPrinted(value) { +} +function reportCompare(expected, actual, description) { + new TestCase("unknown-test-name", description, expected, actual) +} +gTc = 0;; +function jsTestDriverEnd() { + for (var i = 0; i < gTestcases.length; i++) + gTestcases[i].dump() +} +var summary = 'Do not assert with try/finally inside finally'; +var expect = 'No Crash'; +reportCompare(expect, printStatus, summary); +jsTestDriverEnd(); +jsTestDriverEnd(); +try { + f +} catch (ex) { + actual = '' +} +reportCompare(expect, actual, 5); +jsTestDriverEnd() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658561.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658561.js new file mode 100644 index 00000000000..ad10b3dac31 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658561.js @@ -0,0 +1,5 @@ +var s1 = 'xx'; +for (var x = 0; x < 10 ; ++x ) { + new function() s1++; + gc(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658777.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658777.js new file mode 100644 index 00000000000..9bc6854f435 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug658777.js @@ -0,0 +1,11 @@ +function Employee(name, dept) this.name = name || ""; +function WorkerBee(name, dept, projs) { + this.base = Employee + this.base(name, dept) +} +function Engineer(name, projs, machine) { + this.base = WorkerBee + this.base(name, "engineering", projs) + __proto__["a" + constructor] = 1 +} +new Engineer; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659639.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659639.js new file mode 100644 index 00000000000..4e7ffe28800 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659639.js @@ -0,0 +1,16 @@ +test(); +function iso(d) { new Date(d).toISOString() } +function check(s,millis) { iso(millis); } +function dd(year, month, day, hour, minute, second, millis) { + return Date.UTC(year, 1, day, hour, minute, second, millis); +} +function test() { + try { + check("", 20092353211) + check("", 2009) + check("", 0) + check("", dd(BUGNUMBER, 7, 23, 19, 53, 21, 1)) + } catch (e) {} +} +var BUGNUMBER = "10278"; +test() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659766.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659766.js new file mode 100644 index 00000000000..012447ba438 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug659766.js @@ -0,0 +1,29 @@ +var gTestcases = new Array; +var gTc = gTestcases; +function TestCase(n, d, e, a) { + this.description=d + this.reason='' + gTestcases[gTc++]=this +} +TestCase.prototype.dump=function () + toPrinted(this.description) + toPrinted(this.reason) + '\n'; +function toPrinted(value) value=value.replace(/\\n/g, 'NL').replace(/[^\x20-\x7E]+/g, escapeString); +function escapeString (str) { + try { + err + } catch(ex) { } +} +function jsTestDriverEnd() { + for (var i = 0; i < gTestcases.length; i++) + gTestcases[i].dump() +} +var SECTION = "dowhile-007"; +DoWhile(); +function DoWhile( object ) result1=false; +new TestCase( + SECTION, + "break one: ", + result1 +); +jsTestDriverEnd(); +new TestCase( SECTION, "'�O� �:i��'.match(new RegExp('.+'))", [], '�O� �:i��'); +jsTestDriverEnd(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug660737.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug660737.js new file mode 100644 index 00000000000..b36b8ee779e --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug660737.js @@ -0,0 +1,10 @@ +(function() { + function f(l) { + w++ + } + for each(let w in ['', '', 0]) { + try { + f(w) + } catch (e) {} + } +})() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug661859.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug661859.js new file mode 100644 index 00000000000..856ca586d8f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug661859.js @@ -0,0 +1,18 @@ +function TestCase(n, d, e, a) this.expect = e; +function reportCompare(expected, actual, description) { + typeof actual +} +expect = 1; +var summary = 'Do not assert: top < ss->printer->script->depth'; +var actual = 'No Crash'; +var expect = 'No Crash'; +test(); +function test() { + try { + p = [1].some(function (y) test()) ? 4 : 0x0041 + } catch (ex) {} + reportCompare(expect, actual, summary) +} +test(); +TestCase(); +test() diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug663690.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug663690.js new file mode 100644 index 00000000000..1738d01968c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug663690.js @@ -0,0 +1,14 @@ + +function g(c) { + b = b = h(c); +} +function j(s) { + return Function(s) +} +function h(c) { + return j(c)() +} +g() +var a +Boolean.__proto__[a] = [] +g("return gc()") diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-1.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-1.js new file mode 100644 index 00000000000..3562f9327d0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-1.js @@ -0,0 +1,12 @@ +gczeal(2); +o1 = Iterator; +var o2 = (function() { return arguments; })(); +function f(o) { + for(var j=0; j<20; j++) { + Object.seal(o2); + (function() { return eval(o); })() == o1; + (function() { return {x: arguments}.x; })(); + if (false) {}; + } +} +f({}); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-2.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-2.js new file mode 100644 index 00000000000..7b262b9b675 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug671943-2.js @@ -0,0 +1,10 @@ +if (typeof schedulegc != 'undefined') + schedulegc(11); +function foo(n) { + if (n == 10) + foo.apply = function(a, b) { return b[0]; } + return n; +} +function bar() { return foo.apply(null, arguments); } +for (var i = 0; i < 20; i++) + assertEq(bar(i), i); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug672123.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug672123.js new file mode 100644 index 00000000000..d2ca2497e73 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug672123.js @@ -0,0 +1,46 @@ +var caught = false; +function h(code) { + f = eval("(function(){" + code + "})") + g() +} +function g() { + try { + f(); + } catch (r) { caught = true } +} +h("") +for (i = 0; i < 9; i++) { + h("") +} +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("") +h("\"\"(gc())") +assertEq(caught, true); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug673812.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug673812.js new file mode 100644 index 00000000000..cf16ba78b50 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug673812.js @@ -0,0 +1,18 @@ +gczeal(2); +try { + DoWhile_3(); +} catch (e) {} +function f() { + test(); + yield 170; +} +function test() { + function foopy() { + try { + for (var i in f()); + } catch (e) {} + } + foopy(); + gc(); +} +test(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug674391.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug674391.js new file mode 100644 index 00000000000..7fdc2c30375 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug674391.js @@ -0,0 +1,11 @@ +a = []; +for (var i = 0; i < 1000; i++) { + a[i] = i; +} +function foo(x) { + for (var i in x) { + } +} +if (typeof schedulegc != "undefined") + schedulegc(100); +foo(a); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug676764.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug676764.js new file mode 100644 index 00000000000..1cb8ef8fd4a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/bug676764.js @@ -0,0 +1,14 @@ + +try { with( ) { + (function () { + for (;;) { + t + } + })() +} } catch (e) {} + +with( ) { + (function () { + for (b = 0; b < 18; ++b) {} + })(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/callic.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/callic.js new file mode 100644 index 00000000000..6647c9acf63 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/callic.js @@ -0,0 +1,27 @@ + +/* Recompilation while being processed by a call IC. */ + +var g; +function foo() { + for (g = 0; g < 5; g++) { + bar(); + } + function bar() { + with ({}) { + eval("g = undefined;"); + } + } +} +foo(); + +assertEq(g, NaN); + +/* Recompilation while being processed by a native call IC. */ + +function native() { + var x; + x = x; + x = Math.ceil(NaN); + assertEq(x, NaN); +} +native(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/exotic.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/exotic.js new file mode 100644 index 00000000000..81185c4dce3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/exotic.js @@ -0,0 +1,74 @@ + +// Test exotic ways of triggering recompilation. + +// Lowered native call. + +var x = 0; +var y = true; +for (var i = 0; i < 20; i++) { + x += Array.map.apply(undefined, [[0], function(x) { if (i == 10) eval("y = 20"); return 1; }])[0]; +} +assertEq(x, 20); +assertEq(y, 20); + +// Recompilation triggered by local function. + +var o = {}; +function what(q) { + function inner() { return q; } + o.f = inner; + var a = o.f(); + return a; +} +for (var i = 0; i < 10; i++) { + var a = what(i); + assertEq(a, i); +} + +// Lowered scripted call to apply returning code pointer. + +var global = 3; +function foo(x, y) { + var q = x.apply(null, y); + if (q != 10) + assertEq(global, true); +} +foo(function(a) { global = a; return 10; }, [1]); +foo(function(a) { global = a; return 10; }, [1]); +foo(function(a) { global = a; return 10; }, [1]); +assertEq(global, 1); +foo(function(a) { global = a; return 3; }, [true]); +assertEq(global, true); + +// Lowered scripted call returning NULL. + +var oglobal = 3; +function xfoo(x, y) { + var q = x.apply(null, y); + if (q != 10) + assertEq(oglobal, true); +} +xfoo(function(a) { oglobal = a; return 10; }, [1]); +xfoo(function(a) { oglobal = a; return 10; }, [1]); +xfoo(function(a) { oglobal = a; return 10; }, [1]); +assertEq(oglobal, 1); +xfoo(function(a) { ; oglobal = a; return 3; }, [true]); +assertEq(oglobal, true); + +// Recompilation out of SplatApplyArgs. + +weirdarray = [,,1,2,3]; +Object.defineProperty(weirdarray, 0, {get: function() { vglobal = 'true'; }}); + +var vglobal = 3; +function yfoo(x, y) { + var q = x.apply(null, y); + if (q != 10) + assertEq(vglobal, 'true'); + else + assertEq(vglobal, 3); +} +yfoo(function(a) { return 10; }, [1]); +yfoo(function(a) { return 10; }, [1]); +yfoo(function(a) { return 10; }, [1]); +yfoo(function() { return 0; }, weirdarray); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/flush.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/flush.js new file mode 100644 index 00000000000..81681e60404 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/flush.js @@ -0,0 +1,18 @@ + +/* Handle flushing of scripted call ICs pointing to recompiled functions. */ + +function add(x, y) { + var z = x + y; + return String(x + y); +} + +function foo() { + var x = 0x7ffffff0; + var res = ""; + for (var i = 0; i < 20; i++) { + res += add(x, i) + ","; + res += add(x, i) + ","; + } + assertEq(res, "2147483632,2147483632,2147483633,2147483633,2147483634,2147483634,2147483635,2147483635,2147483636,2147483636,2147483637,2147483637,2147483638,2147483638,2147483639,2147483639,2147483640,2147483640,2147483641,2147483641,2147483642,2147483642,2147483643,2147483643,2147483644,2147483644,2147483645,2147483645,2147483646,2147483646,2147483647,2147483647,2147483648,2147483648,2147483649,2147483649,2147483650,2147483650,2147483651,2147483651,"); +} +foo(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/getelem.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/getelem.js new file mode 100644 index 00000000000..1c976ac7ee9 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/getelem.js @@ -0,0 +1,26 @@ + +/* Unexpected values out of GETELEM */ + +function foo() { + var x = [1,2,3]; + var y; + var z = x[y]; + y = 10; + assertEq(z, "twelve"); +} +Array.prototype["undefined"] = "twelve"; +foo(); + +function fna() { + var a = {}; + a[true] = 1; + assertEq(a["true"], 1); +} +fna(); + +function fnb() { + var a = []; + a[1.1] = 2; + assertEq(a["1.1"], 2); +} +fnb(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/incdec.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/incdec.js new file mode 100644 index 00000000000..93786ba3497 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/incdec.js @@ -0,0 +1,92 @@ + +/* Handle recompilation on overflow of inc/dec operations. */ + +function local() +{ + var j = 0x7ffffff0; + for (var i = 0; i < 100; i++) + j++; + assertEq(j, 2147483732); +} +local(); + +function olocal() +{ + var j = 0x7ffffff0; + for (var i = 0; i < 100; i++) { + if (j++ == 5000) + break; + } + assertEq(j, 2147483732); +} +olocal(); + +function arg(j) +{ + for (var i = 0; i < 100; i++) + j++; + assertEq(j, 2147483732); +} +arg(0x7ffffff0); + +function oarg(j) +{ + for (var i = 0; i < 100; i++) { + if (j++ == 5000) + break; + } + assertEq(j, 2147483732); +} +oarg(0x7ffffff0); + +// basic global inc/dec correctness +var x = 1.23; +x = x--; +x = x++; +x = ++x; +x = --x; +assertEq(x, 1.23); + +var g = 0x7ffffff0; +function glob() +{ + for (var i = 0; i < 100; i++) + g++; + assertEq(g, 2147483732); +} +glob(); + +function gname() +{ + n = 0x7ffffff0; + for (var i = 0; i < 100; i++) + n++; + assertEq(n, 2147483732); +} +gname(); + +function prop() +{ + var v = {f: 0x7ffffff0}; + for (var i = 0; i < 100; i++) + v.f++; + assertEq(v.f, 2147483732); +} +prop(); + +function elem(v, f) +{ + for (var i = 0; i < 100; i++) + v[f]++; + assertEq(v.f, 2147483732); +} +elem({f: 0x7ffffff0}, "f"); + +function name() +{ + var v = 0x7ffffff0; + var i; + eval("for (i = 0; i < 100; i++) v++"); + assertEq(v + 10, 2147483742); +} +name(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/inlinestubs.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/inlinestubs.js new file mode 100644 index 00000000000..05a7ee8ef3f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/inlinestubs.js @@ -0,0 +1,43 @@ +// rejoining after recompilation from CompileFunction or a native called by an inlined frame. + +var global = 0; +var arr = [ + function() { return 0; }, + function() { return 1; }, + function() { return 2; }, + function() { return 3; }, + function() { return 4; }, + function() { return 5; }, + function() { return 6; }, + function() { return 7; }, + function() { global = -"three"; return 8; } + ]; +function wrap_call(i) { + var fn = arr["" + i]; + return fn(); +} +function foo1() { + var res = 0; + for (var i = 0; i < arr.length; i++) { + res += wrap_call(i); + var expected = (i == arr.length - 1) ? NaN : 10; + assertEq(global + 10, expected); + } +} +foo1(); + +var callfn = Function.call; +function wrap_floor(x, y) { + var z = x; + if (y) + z = {}; // trick the compiler into not inlining the floor() call. + return Math.floor(z); +} + +function foo2(x, y) { + var z = 0; + for (var i = 0; i < y; i++) + z = wrap_floor(x + i, false); + assertEq(z + 10, 2147483661); +} +foo2(0x7ffffff0 + .5, 20); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-01.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-01.js new file mode 100644 index 00000000000..aa147462124 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-01.js @@ -0,0 +1,12 @@ + +function foo(n) { + for (var i = 0; i < n; i++) {} + return i; +} + +assertEq(foo(1000), 1000); + +gc(); + +eval("assertEq(foo(1000.5), 1001)"); + diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-02.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-02.js new file mode 100644 index 00000000000..875487abc2d --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-02.js @@ -0,0 +1,19 @@ + +var g = 10; + +function bar(n) { + return g; +} + +function foo(n, v) { + for (var i = 0; i < n; i++) + assertEq(bar(i), v); +} + +foo(10, 10); + +gc(); + +eval("g = 10.5"); + +foo(10, 10.5); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-03.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-03.js new file mode 100644 index 00000000000..c5eaf82961a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-03.js @@ -0,0 +1,6 @@ + +eval("var x = 10; function foo() { return x; }"); + +assertEq(foo(), 10); +gc(); +assertEq(foo(), 10); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-04.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-04.js new file mode 100644 index 00000000000..794c93802d4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/memory-04.js @@ -0,0 +1,8 @@ + +function foo(x, y) { + gc(); + var z = x + y; + print(z); +} + +foo(0x7ffffff0, 100); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/native.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/native.js new file mode 100644 index 00000000000..72a6c25761f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/native.js @@ -0,0 +1,26 @@ + +/* Handle recompilations triggered by native calls. */ + +function dofloor(v) +{ + var res = ""; + for (var i = 0; i < 10; i++) { + var q = Math.floor(v + i); + res += q + ","; + } + assertEq(res, "2147483642,2147483643,2147483644,2147483645,2147483646,2147483647,2147483648,2147483649,2147483650,2147483651,"); +} +dofloor(0x7ffffffa + .5); + +function mapfloor(a) +{ + var b = a.map(function(v) { return Math.floor(v); }); + + var res = ""; + for (var i = 0; i < b.length; i++) + res += b[i] + ","; + return res; +} +mapfloor([1,2]); +mapfloor([3,4]); +assertEq(mapfloor([0x7ffffffa + 2.5, 0x7ffffffa + 20.5]), "2147483644,2147483662,"); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativemulti.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativemulti.js new file mode 100644 index 00000000000..d611586970c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativemulti.js @@ -0,0 +1,21 @@ + +/* Recompilation that requires patching the same native stub multiple times. */ + +var first = 0; +var second = 0; + +function foreachweird(a, f, vfirst, vsecond) +{ + a.forEach(f); + assertEq(first, vfirst); + assertEq(second, vsecond); +} + +function weird() { + eval("first = 'one';"); + eval("second = 'two';"); +} + +foreachweird([0], function() {}, 0, 0); +foreachweird([0], function() {}, 0, 0); +foreachweird([0], weird, 'one', 'two'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativestack.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativestack.js new file mode 100644 index 00000000000..350649afcda --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/nativestack.js @@ -0,0 +1,25 @@ + +/* Recompilation that requires patching the same native stub multiple times on one stack. */ + +var first = 0; +var second = 0; + +function eacher(f, vfirst, vsecond) { + var a = [0]; + a.forEach(f); + assertEq(first, vfirst); + assertEq(second, vsecond); +} + +function one() { + eacher(two, 'one', 'two'); +} + +function two() { + eval("first = 'one';"); + eval("second = 'two';"); +} + +eacher(function () {}, 0, 0); +eacher(function () {}, 0, 0); +eacher(one, 'one', 'two'); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/patchdouble.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/patchdouble.js new file mode 100644 index 00000000000..4de08381387 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/patchdouble.js @@ -0,0 +1,7 @@ +// only fix doubles for slots which the recompiled script thinks are doubles. +function foo(x) { + var y = x & 0xffff; + y = (y * (x * 1000)); + assertEq(y, 140735340806145000); +} +foo(0x7fffffff); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/property.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/property.js new file mode 100644 index 00000000000..37223563af0 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/property.js @@ -0,0 +1,26 @@ + +/* Handle recompilation on undefined properties and array holes. */ + +var v = {}; +if (typeof v == 'string') + v.x = 0; +function prop(v) +{ + var z = v.x + 1; + assertEq(z, NaN); +} +prop(v); + +v = []; +v[0] = 0; +v[1] = 1; +v[3] = 3; +v[4] = 4; +function elem(x) +{ + var x = ""; + for (var i = 0; i < 5; i++) + x += v[i]; + assertEq(x, "01undefined34"); +} +elem(v); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/propic.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/propic.js new file mode 100644 index 00000000000..f394c4c4673 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/propic.js @@ -0,0 +1,24 @@ + +/* Recompilation while being processed by property ICs. */ + +var ga = 10; +var gb = 10; + +Object.defineProperty(Object.prototype, "a", { + set: function(a) { eval("ga = true;"); }, + get: function() { eval("gb = true;"); } + }); + +function foo() { + var x = {}; + x.a = 10; + assertEq(ga + 1, 2); +} +foo(); + +function bar() { + var x = {}; + var a = x.a; + assertEq(gb + 1, 2); +} +bar(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/staticoverflow.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/staticoverflow.js new file mode 100644 index 00000000000..7387772444a --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/staticoverflow.js @@ -0,0 +1,26 @@ + +// overflows occurring during constant folding + +var y = -null - y; +assertEq(y, NaN); + +var x = -(void 0); +assertEq(x, NaN); + +function overdiv() { + for(var i=0; i<25; i++) { + var a, b; + function f() { + } + a = f(); + b = (123 ^ 1) / 1234; + } +} +overdiv(); + +function overadd() { + var a = 0x7ffffff0; + var b = 100; + return a + b; +} +overadd(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/undef.js b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/undef.js new file mode 100644 index 00000000000..a738c802c68 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/recompile/undef.js @@ -0,0 +1,61 @@ + +/* Handle recompilation on undefined variables. */ + +function local() +{ + var x; + x++; + assertEq(x, NaN); + x = 0; +} +local(); + +function name(v) +{ + var x; + with (v) { + x++; + assertEq(x, NaN); + } + assertEq(x, NaN); + x = 0; +} +name({}); + +function letname(v) +{ + if (v) { + let x; + with (v) { + x = "twelve"; + } + assertEq(x, "twelve"); + } +} +letname({}); + +function upvar() +{ + var x; + function inner() { + x++; + assertEq(x, NaN); + } + inner(); +} +upvar(); + +var x; +var y; + +function global() +{ + x++; + assertEq(x, NaN); + var z = 2 + y; + assertEq(z, NaN); +} +global(); + +x = 0; +y = 0; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-double.js b/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-double.js new file mode 100644 index 00000000000..8d49eb54f57 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-double.js @@ -0,0 +1,12 @@ +// register allocation involving doubles. + +function foo(a,b) { + var c; + if (a < b) { + c = a + 1; + } else { + c = 0.5; + } + return c; +} +assertEq(foo(0, 1), 1); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-live.js b/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-live.js new file mode 100644 index 00000000000..b50ee2a2d3f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/regalloc-live.js @@ -0,0 +1,41 @@ + +// test local/arg liveness analysis in presence of many locals + +function foo(a, b, c) { + var x = 0, y = 0, z = 0; + if (a < b) { + x = a + 0; + y = b + 0; + z = c + 0; + } else { + x = a; + y = b; + z = c; + } + return x + y + z; +} +assertEq(foo(1, 2, 3), 6); + +// restore liveness correctly before switch statements + +function foo(a, b, c) { + var x = 0, y = 0, z = 0; + if (a < b) { + x = a + 0; + y = b + 0; + z = c + 0; + } else { + switch (c) { + case 1: + case 2: + case 3: + case 4: + case 5: return 0; + } + x = 0; + y = 0; + z = 0; + } + return x + y + z; +} +assertEq(foo(1, 2, 3), 6); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/setPropTypeGuard.js b/deps/mozjs/js/src/jit-test/tests/jaeger/setPropTypeGuard.js new file mode 100644 index 00000000000..3786fc88af3 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/setPropTypeGuard.js @@ -0,0 +1,23 @@ + +/* + * Get a SETPROP site which is monitored (unknown lhs) and is repeatedly + * invoked on objects with the same shape but different types (and without + * triggering a recompile of the function). The SETPROP PIC needs a type guard + * when the object is being monitored. + */ +var x = {g:0}; +var y = {g:0,f:"fubar"}; +x.f = 10; + +function foo(x) { + for (var i = 0; i < 30; i++) + x.f = 10; +} +function access(x) { + return x.f + 10; +} +foo(Object.create({})); +eval("foo(x)"); +assertEq(access(y), "fubar10"); +eval("foo(y)"); +assertEq(access(y), 20); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/testForOps.js b/deps/mozjs/js/src/jit-test/tests/jaeger/testForOps.js index cb7d583e051..5dfa261496e 100644 --- a/deps/mozjs/js/src/jit-test/tests/jaeger/testForOps.js +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/testForOps.js @@ -12,8 +12,10 @@ function assertObjectsEqual(obj1, obj2) { assertEq(obj2.d, 12); } +// :XXX: below calls to assertJit can throw us out of JIT code for some TI settings. + function forName(obj) { - assertJit(); + // assertJit(); eval(''); var r = { }; for (x in obj) @@ -22,7 +24,7 @@ function forName(obj) { } function forGlobalName(obj) { - assertJit(); + // assertJit(); var r = { }; for (x in obj) r[x] = obj[x]; @@ -30,7 +32,7 @@ function forGlobalName(obj) { } function forProp(obj) { - assertJit(); + // assertJit(); var r = { }; var c = { }; for (c.x in obj) @@ -39,7 +41,7 @@ function forProp(obj) { } function forElem(obj, x) { - assertJit(); + // assertJit(); var r = { }; var c = { }; for (c[x] in obj) @@ -48,7 +50,7 @@ function forElem(obj, x) { } function forLocal(obj) { - assertJit(); + // assertJit(); var r = { }; for (var x in obj) r[x] = obj[x]; @@ -56,7 +58,7 @@ function forLocal(obj) { } function forArg(obj, x) { - assertJit(); + // assertJit(); var r = { }; for (x in obj) r[x] = obj[x]; diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/testIfEqX.js b/deps/mozjs/js/src/jit-test/tests/jaeger/testIfEqX.js new file mode 100644 index 00000000000..6940619e6fc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/testIfEqX.js @@ -0,0 +1,37 @@ +// Tests for IFEQX and GOTOX ops. +function testIfElse() { + var src = + "var a = 0;\n" + + "if (x) {\n"; + for (var i=0; i<7000; i++) { + src += "a = 1;"; + } + src += "} else {\n"; + for (var i=0; i<7000; i++) { + src += "a = 2;"; + } + src += "}\n"; + src += "return a;"; + + var f = new Function("x", src); + assertEq(f(true), 1); + assertEq(f(false), 2); + assertEq(f([1, 2, 3]), 1); + assertEq(f(), 2); +} +testIfElse(); + +function testWhile() { + var src = + "var i = 0, j = 0;\n" + + "while (i++ < 50) {\n"; + for (var i=0; i<5000; i++) { + src += "j = i;"; + } + src += "}\n"; + src += "return j;"; + + var f = new Function(src); + assertEq(f(), 50); +} +testWhile(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/testTableSwitchX.js b/deps/mozjs/js/src/jit-test/tests/jaeger/testTableSwitchX.js new file mode 100644 index 00000000000..c92d4c27588 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/testTableSwitchX.js @@ -0,0 +1,25 @@ +// Tests for JSOP_TABLESWITCHX. +function test1() { + var src = + "var a = 0;\n" + + "switch(x) {\n"; + for (var i=-1; i<4; i++) { + src += (i >= 0) ? + "case " + i + ":\n" : + "default:\n"; + for (var j=0; j<1500; j++) { + src += "a = " + i + ";"; + } + src += "break;\n"; + } + src += "}\n"; + src += "return a;"; + + var f = new Function("x", src); + assertEq(f(0), 0); + assertEq(f(4), -1); + assertEq(f(), -1); + assertEq(f(1.1), -1); + assertEq(f(3), 3); +} +test1(); diff --git a/deps/mozjs/js/src/jit-test/tests/jaeger/undoAdd.js b/deps/mozjs/js/src/jit-test/tests/jaeger/undoAdd.js new file mode 100644 index 00000000000..007a29c973c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/jaeger/undoAdd.js @@ -0,0 +1,25 @@ + +/* Test undoing addition in overflow paths when under heavy register pressure. */ + +function add1(x, y, a, b, res) { var nres = res + 0; var z = (x + a) + (y + b); assertEq(z, nres); } +function add2(x, y, a, b, res) { var nres = res + 0; var z = (x + a) + (y + b); assertEq(z, nres); } +function add3(x, y, a, b, res) { var nres = res + 0; var z = (x + a) + (y + b); assertEq(z, nres); } +add1(0x7ffffff0, 100, 0, 0, 2147483732); +add2(-1000, -0x80000000, 0, 0, -2147484648); +add3(-0x80000000, -1000, 0, 0, -2147484648); + +function cadd1(x, a, b, res) { + var nres = res + 0; + var nb = b + 0; + var z = (x + a) + 1000; + assertEq(z, nres + nb); +} +cadd1(0x7ffffff0, 0, 0, 2147484632); + +function cadd2(x, a, b, res) { + var nres = res + 0; + var nb = b + 0; + var z = (x + a) + (-0x80000000); + assertEq(z, nres + nb); +} +cadd2(-1000, 0, 0, -2147484648); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/arguments.js b/deps/mozjs/js/src/jit-test/tests/pic/arguments.js new file mode 100644 index 00000000000..ffe75e8db03 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/arguments.js @@ -0,0 +1,23 @@ +function f() { + var args = arguments, r; + + for (var i = 0; i < args.length; i++) + r = args[i]; + + return r; +} + +assertEq(f.apply(null, [1, 2, 3, 4, 5, 6]), 6) +assertEq(f.apply(null, [1, 2, 3, 4, 5]), 5) +assertEq(f.apply(null, [1, 2, 3, 4]), 4) + +function g(arg) { + var r; + for (var i = 0; i < arg.length; i++) + r = arg[i]; + return r; +} + +assertEq(g((function () arguments).call(null, 1, 2, 3)), 3); +assertEq(g(new Float32Array(3)), 0.0); +assertEq(g([1, 2, 3, 4]), 4); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/bug645184.js b/deps/mozjs/js/src/jit-test/tests/pic/bug645184.js index dc44d6edc3d..80d20d45a4b 100644 --- a/deps/mozjs/js/src/jit-test/tests/pic/bug645184.js +++ b/deps/mozjs/js/src/jit-test/tests/pic/bug645184.js @@ -1,8 +1,8 @@ -var obj = new Object(); -var passed = true; -for (var i = 0; i < 100; i++) { -if (obj['-1'] == null) - obj['-1'] = new Array(); - assertEq(obj['-1'] == null, false); - obj = new Object(); -} +var obj = new Object(); +var passed = true; +for (var i = 0; i < 100; i++) { + if (obj['-1'] == null) + obj['-1'] = new Array(); + assertEq(obj['-1'] == null, false); + obj = new Object(); +} diff --git a/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this1.js b/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this1.js new file mode 100644 index 00000000000..1052ada52a4 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this1.js @@ -0,0 +1,11 @@ +this.name = "outer"; + +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; + +function f() { + assertEq(this.name, "outer"); +} + +evalcx('with(this) { ff = parent.f; }; (function() { eval(""); for(var i=0; i<10; i++) { ff() } })()', sb); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this2.js b/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this2.js new file mode 100644 index 00000000000..86bd740d14c --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/callname-eager-this2.js @@ -0,0 +1,25 @@ +this.name = "outer"; + +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; + +var res = 0; + +function f() { + assertEq(this.name, "outer"); + res++; +} + +// ff is a property of the inner global object. Generate a CALLNAME IC, then +// change ff to a function on the outer global. It should get the inner this +// value. +evalcx('this.ff = function() {};' + + '(function() { ' + + 'eval("");' + + 'for(var i=0; i<10; i++) {' + + 'ff();' + + 'if (i == 5) ff = parent.f;' + + '}' + + '})()', sb); +assertEq(res, 4); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/callname-global1.js b/deps/mozjs/js/src/jit-test/tests/pic/callname-global1.js new file mode 100644 index 00000000000..ed34ec6b2a2 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/callname-global1.js @@ -0,0 +1,35 @@ +// Check that the implicit-this logic needed for CALLNAME global stubs +// handles non-function values correctly. +var self = this; +var count = 0; +function g1() { + assertEq(this, self); + this.count++; +} +function g2() { + this.count += 10; +} +function f() { + function f1(other) { + eval("gc(); h = g1"); + try { + for(var i=0; i<20; i++) { + h(); + if (i === 9) { + h = other; + } + } + assertEq(typeof other, "function"); + } catch(e) { + assertEq(typeof other !== "function", true); + assertEq(e instanceof TypeError, true); + } + } + f1(3); + f1(null); + f1({}); + f1(Math.abs); + f1(g2); +} +f(); +assertEq(count, 150); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/callname-global2.js b/deps/mozjs/js/src/jit-test/tests/pic/callname-global2.js new file mode 100644 index 00000000000..5ee3aa2650f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/callname-global2.js @@ -0,0 +1,15 @@ +g0 = function(i) { + this["g"+(i+1)] = g0; + return "g"+(i+1); +} +function f() { + a = eval("g0"); + for(var i=0; i<40; i++) { + a = this[a(i)]; + if (i === 30) { + gc(); + } + assertEq(this["g" + i], g0); + } +} +f(); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/callname-with.js b/deps/mozjs/js/src/jit-test/tests/pic/callname-with.js new file mode 100644 index 00000000000..0a988edbf3b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/callname-with.js @@ -0,0 +1,15 @@ +var res; +var x = 0; + +function f() { + x = {x: 1, f: function() { res = this.x; }}; + with(x) { + g = function() { + eval(""); + f(); + } + g(); + } +} +f(); +assertEq(res, 1); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/getelem-large-index.js b/deps/mozjs/js/src/jit-test/tests/pic/getelem-large-index.js new file mode 100644 index 00000000000..c7f813ea2a7 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/getelem-large-index.js @@ -0,0 +1,13 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ +function testProperty(i) +{ + actual = obj[String(i)]; +} + +var obj = {}; +var index = [null, 1073741824, 1073741825]; +for (var j in index) + testProperty(index[j]); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch1.js b/deps/mozjs/js/src/jit-test/tests/pic/watch1.js new file mode 100644 index 00000000000..09d6347bf99 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch1.js @@ -0,0 +1,7 @@ +// assignments to watched objects must not be cached +var obj = {x: 0}; +var hits = 0; +obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); +for (var i = 0; i < 10; i++) + obj.x = i; +assertEq(hits, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch1a.js b/deps/mozjs/js/src/jit-test/tests/pic/watch1a.js new file mode 100644 index 00000000000..4b404f507cc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch1a.js @@ -0,0 +1,17 @@ +// assignments to watched objects must not be traced +var hits = 0; +function counter(id, oldval, newval) { + hits++; + return newval; +} + +(function () { + var obj = {x: 0, y: 0}; + var a = ['x', 'y']; + obj.watch('z', counter); + for (var i = 0; i < 14; i++) { + obj.watch(a[+(i > 8)], counter); + obj.y = i; + } +})(); +assertEq(hits, 5); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch2.js b/deps/mozjs/js/src/jit-test/tests/pic/watch2.js new file mode 100644 index 00000000000..fb3e296176b --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch2.js @@ -0,0 +1,8 @@ +// assignments to watched properties via ++ must not be cached +var obj = {x: 0}; +var hits = 0; +obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); +for (var i = 0; i < 10; i++) + obj.x++; +assertEq(hits, 10); + diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch2a.js b/deps/mozjs/js/src/jit-test/tests/pic/watch2a.js new file mode 100644 index 00000000000..ce3294ba3f1 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch2a.js @@ -0,0 +1,18 @@ +// assignments to watched properties via ++ must not be traced +var hits = 0; +function counter(id, oldval, newval) { + hits++; + return newval; +} + +(function () { + var obj = {x: 0, y: 0}; + var a = ['x', 'y']; + obj.watch('z', counter); + for (var i = 0; i < 14; i++) { + obj.watch(a[+(i > 8)], counter); + obj.y++; + } +})(); +assertEq(hits, 5); + diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch3.js b/deps/mozjs/js/src/jit-test/tests/pic/watch3.js new file mode 100644 index 00000000000..4c5c93d8bbc --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch3.js @@ -0,0 +1,7 @@ +// assignment to watched global properties must not be cached +x = 0; +var hits = 0; +this.watch("x", function (id, oldval, newval) { hits++; return newval; }); +for (var i = 0; i < 10; i++) + x = i; +assertEq(hits, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch3a.js b/deps/mozjs/js/src/jit-test/tests/pic/watch3a.js new file mode 100644 index 00000000000..700daf6af9f --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch3a.js @@ -0,0 +1,19 @@ +// assignment to watched global properties must not be traced +var hits = 0; +function counter(id, oldval, newval) { + hits++; + return newval; +} + +var x = 0; +var y = 0; +(function () { + var a = ['x', 'y']; + this.watch('z', counter); + for (var i = 0; i < 14; i++) { + this.watch(a[+(i > 8)], counter); + y = 1; + } +})(); +assertEq(hits, 5); + diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch3b.js b/deps/mozjs/js/src/jit-test/tests/pic/watch3b.js new file mode 100644 index 00000000000..9b0dc8cc957 --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch3b.js @@ -0,0 +1,20 @@ +// assignment to watched global properties must not be traced +var hits = 0; +function counter(id, oldval, newval) { + hits++; + return newval; +} + +var x = 0; +var y = 0; +function f() { + var a = [{}, this]; + for (var i = 0; i < 14; i++) { + print(shapeOf(this)); + Object.prototype.watch.call(a[+(i > 8)], "y", counter); + y++; + } +} +f(); +assertEq(hits, 5); + diff --git a/deps/mozjs/js/src/jit-test/tests/pic/watch4.js b/deps/mozjs/js/src/jit-test/tests/pic/watch4.js new file mode 100644 index 00000000000..4b640c4dfec --- /dev/null +++ b/deps/mozjs/js/src/jit-test/tests/pic/watch4.js @@ -0,0 +1,9 @@ +// adding assignment + watchpoint vs. caching +var hits = 0; +var obj = {}; +obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); +for (var i = 0; i < 10; i++) { + obj.x = 1; + delete obj.x; +} +assertEq(hits, 10); diff --git a/deps/mozjs/js/src/jit-test/tests/sunspider/check-3d-raytrace.js b/deps/mozjs/js/src/jit-test/tests/sunspider/check-3d-raytrace.js index 5e020608c0e..bcb3616be5b 100644 --- a/deps/mozjs/js/src/jit-test/tests/sunspider/check-3d-raytrace.js +++ b/deps/mozjs/js/src/jit-test/tests/sunspider/check-3d-raytrace.js @@ -312,7 +312,6 @@ Camera.prototype.render = function(scene, pixels, width, height) { function raytraceScene() { - var startDate = new Date().getTime(); var numTriangles = 2 * 6; var triangles = new Array();//numTriangles); var tfl = createVector(-10, 10, -10); diff --git a/deps/mozjs/js/src/jit-test/tests/sunspider/check-crypto-aes.js b/deps/mozjs/js/src/jit-test/tests/sunspider/check-crypto-aes.js index 6bcdb549640..875d68fe858 100644 --- a/deps/mozjs/js/src/jit-test/tests/sunspider/check-crypto-aes.js +++ b/deps/mozjs/js/src/jit-test/tests/sunspider/check-crypto-aes.js @@ -176,7 +176,8 @@ function AESEncryptCtr(plaintext, password, nBits) { // block counter in 2nd 8 bytes var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES var counterBlock = new Array(blockSize); // block size fixed at 16 bytes / 128 bits (Nb=4) for AES - var nonce = (new Date()).getTime(); // milliseconds since 1-Jan-1970 + var nonce = (new Date("2000-01-01")).getTime(); // milliseconds since 1-Jan-1970; + // fixed for repeatability // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff; diff --git a/deps/mozjs/js/src/jit-test/tests/sunspider/check-math-cordic.js b/deps/mozjs/js/src/jit-test/tests/sunspider/check-math-cordic.js index f1ade3a3b2e..0abf3db0f96 100644 --- a/deps/mozjs/js/src/jit-test/tests/sunspider/check-math-cordic.js +++ b/deps/mozjs/js/src/jit-test/tests/sunspider/check-math-cordic.js @@ -84,17 +84,11 @@ function cordicsincos() { function cordic( runs ) { var actual; - var start = new Date(); - for ( var i = 0 ; i < runs ; i++ ) { actual = cordicsincos(); } - var end = new Date(); - assertEq(actual, 1834995.3515519998) - - return end.getTime() - start.getTime(); } cordic(25000); diff --git a/deps/mozjs/js/src/jitstats.tbl b/deps/mozjs/js/src/jitstats.tbl index 74a6b7f43eb..14727db0c61 100644 --- a/deps/mozjs/js/src/jitstats.tbl +++ b/deps/mozjs/js/src/jitstats.tbl @@ -85,6 +85,7 @@ JITSTAT(archIs64BIT) JITSTAT(archIsARM) JITSTAT(archIsSPARC) JITSTAT(archIsPPC) +JITSTAT(archIsMIPS) #ifdef DEFINED_MONITOR_JITSTAT #undef DEFINED_MONITOR_JITSTAT diff --git a/deps/mozjs/js/src/js-config.h.in b/deps/mozjs/js/src/js-config.h.in index e3a5f74f52c..402262e7f58 100644 --- a/deps/mozjs/js/src/js-config.h.in +++ b/deps/mozjs/js/src/js-config.h.in @@ -55,9 +55,17 @@ entirely too much GC. */ #undef JS_GC_ZEAL -/* Define to 1 if the standard header is present and - useable. See jstypes.h and jsstdint.h. */ -#undef JS_HAVE_STDINT_H +/* Define to 1 if the header is present and + useable. See jscpucfg.h. */ +#undef JS_HAVE_ENDIAN_H + +/* Define to 1 if the header is present and + useable. See jscpucfg.h. */ +#undef JS_HAVE_MACHINE_ENDIAN_H + +/* Define to 1 if the header is present and + useable. See jscpucfg.h. */ +#undef JS_HAVE_SYS_ISA_DEFS_H /* Define to 1 if the defines int8_t, etc. */ #undef JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES @@ -83,9 +91,8 @@ #undef JS_INTPTR_TYPE #undef JS_BYTES_PER_WORD -/* Some mozilla code uses JS-friend APIs that depend on JS_TRACER and - JS_METHODJIT being correct. */ -#undef JS_TRACER +/* Some mozilla code uses JS-friend APIs that depend on JS_METHODJIT being + correct. */ #undef JS_METHODJIT #endif /* js_config_h___ */ diff --git a/deps/mozjs/js/src/js-config.in b/deps/mozjs/js/src/js-config.in index 59b47198a51..1dc71ea2a8e 100644 --- a/deps/mozjs/js/src/js-config.in +++ b/deps/mozjs/js/src/js-config.in @@ -1,4 +1,40 @@ #!/bin/sh +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is SpiderMonkey JavaScript engine. +# +# The Initial Developer of the Original Code is +# Mozilla Corporation. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jim Blandy +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** prefix='@prefix@' mozilla_version='@MOZILLA_VERSION@' diff --git a/deps/mozjs/js/src/js.mdp b/deps/mozjs/js/src/js.mdp deleted file mode 100644 index 14fb02a255f..00000000000 --- a/deps/mozjs/js/src/js.mdp +++ /dev/null @@ -1 +0,0 @@ -Binary file (standard input) matches diff --git a/deps/mozjs/js/src/js.msg b/deps/mozjs/js/src/js.msg index 957e3496ef9..cab9b55819b 100644 --- a/deps/mozjs/js/src/js.msg +++ b/deps/mozjs/js/src/js.msg @@ -75,8 +75,12 @@ * * "Rhino is not a member of the Monkey family" * - * Before adding a new MSG_DEF at the end, look for JSMSG_UNUSED free - * index placeholders in the middle of the list. + * When removing MSG_DEFs, convert them to JSMSG_UNUSED placeholders: + * + * MSG_DEF(JSMSG_UNUSED7, 7, 0, JSEXN_NONE, "") + * + * Before adding a new MSG_DEF at the end, look for existing JSMSG_UNUSED + * free index placeholders in the middle of the list. */ MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") @@ -103,7 +107,7 @@ MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal MSG_DEF(JSMSG_CANT_DELETE, 21, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted") MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") -MSG_DEF(JSMSG_SCRIPT_STACK_QUOTA, 24, 0, JSEXN_INTERNALERR, "script stack space quota is exhausted") +MSG_DEF(JSMSG_INVALID_DATE, 24, 0, JSEXN_RANGEERR, "invalid date") MSG_DEF(JSMSG_TOO_DEEP, 25, 1, JSEXN_INTERNALERR, "{0} nested too deeply") MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") @@ -140,7 +144,7 @@ MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") MSG_DEF(JSMSG_NO_INPUT, 59, 5, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}{3}{4}") MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") -MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") +MSG_DEF(JSMSG_TOO_MANY_FUN_APPLY_ARGS, 61, 0, JSEXN_RANGEERR, "arguments array passed to Function.prototype.apply is too large") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") @@ -191,7 +195,7 @@ MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in par MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}") -MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") +MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?") MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "can't access optimized closure") MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") @@ -230,7 +234,7 @@ MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught excep MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") -MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") +MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_TYPEERR, "invalid {0} usage") MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") @@ -290,7 +294,7 @@ MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") -MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") +MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") @@ -309,7 +313,7 @@ MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}") MSG_DEF(JSMSG_JSON_BAD_PARSE, 228, 1, JSEXN_SYNTAXERR, "JSON.parse: {0}") MSG_DEF(JSMSG_JSON_BAD_STRINGIFY, 229, 0, JSEXN_ERR, "JSON.stringify") -MSG_DEF(JSMSG_XDR_CLOSURE_WRAPPER, 230, 1, JSEXN_INTERNALERR, "can't XDR closure wrapper for function {0}") +MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 230, 0, JSEXN_TYPEERR, "value is not a function or undefined") MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 231, 0, JSEXN_TYPEERR, "value is not a non-null object") MSG_DEF(JSMSG_DEPRECATED_OCTAL, 232, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated") MSG_DEF(JSMSG_STRICT_CODE_WITH, 233, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements") @@ -349,4 +353,25 @@ MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope o MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function") MSG_DEF(JSMSG_INVALID_FOR_IN_INIT, 268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer") MSG_DEF(JSMSG_CLEARED_SCOPE, 269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope") -MSG_DEF(JSMSG_INVALID_DATE, 270, 0, JSEXN_RANGEERR, "invalid date") +MSG_DEF(JSMSG_MALFORMED_ESCAPE, 270, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence") +MSG_DEF(JSMSG_BAD_GENEXP_BODY, 271, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression") +MSG_DEF(JSMSG_XML_PROTO_FORBIDDEN, 272, 0, JSEXN_TYPEERR, "can't set prototype of an object to an XML value") +MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 273, 0, JSEXN_SYNTAXERR, "function statement requires a name") +MSG_DEF(JSMSG_CCW_REQUIRED, 274, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment") +MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION, 275, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null") +MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 276, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null") +MSG_DEF(JSMSG_DEBUG_NOT_LIVE, 277, 1, JSEXN_ERR, "{0} is not live") +MSG_DEF(JSMSG_DEBUG_OBJECT_WRONG_OWNER, 278, 0, JSEXN_TYPEERR, "Debugger.Object belongs to a different Debugger") +MSG_DEF(JSMSG_DEBUG_OBJECT_PROTO, 279, 0, JSEXN_TYPEERR, "Debugger.Object.prototype is not a valid Debugger.Object") +MSG_DEF(JSMSG_DEBUG_LOOP, 280, 0, JSEXN_TYPEERR, "cannot debug an object in same compartment as debugger or a compartment that is already debugging the debugger") +MSG_DEF(JSMSG_DEBUG_NOT_IDLE, 281, 0, JSEXN_ERR, "can't start debugging: a debuggee script is on the stack") +MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 282, 0, JSEXN_TYPEERR, "invalid script offset") +MSG_DEF(JSMSG_DEBUG_BAD_LINE, 283, 0, JSEXN_TYPEERR, "invalid line number") +MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGING, 284, 0, JSEXN_ERR, "can't set breakpoint: script global is not a debuggee") +MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 285, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object") +MSG_DEF(JSMSG_DEBUG_NOT_SCRIPT_FRAME, 286, 0, JSEXN_ERR, "stack frame is not running JavaScript code") +MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose names are objects can't be watched") +MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked by CSP") +MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects") +MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?") +MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterable") diff --git a/deps/mozjs/js/src/jsalloc.cpp b/deps/mozjs/js/src/jsalloc.cpp index 1f1eca9b33e..c9cc78b333d 100644 --- a/deps/mozjs/js/src/jsalloc.cpp +++ b/deps/mozjs/js/src/jsalloc.cpp @@ -42,13 +42,13 @@ namespace js { void * -ContextAllocPolicy::onOutOfMemory(void *p, size_t nbytes) +TempAllocPolicy::onOutOfMemory(void *p, size_t nbytes) { return cx->runtime->onOutOfMemory(p, nbytes, cx); } void -ContextAllocPolicy::reportAllocOverflow() const +TempAllocPolicy::reportAllocOverflow() const { js_ReportAllocationOverflow(cx); } diff --git a/deps/mozjs/js/src/jsalloc.h b/deps/mozjs/js/src/jsalloc.h index 33089d88f2e..aefc7aac45e 100644 --- a/deps/mozjs/js/src/jsalloc.h +++ b/deps/mozjs/js/src/jsalloc.h @@ -41,7 +41,6 @@ #include "jspubtd.h" #include "jsutil.h" -#include "jsstaticcheck.h" namespace js { @@ -52,6 +51,9 @@ namespace js { * Responsible for OOM reporting on NULL return value. * - void *realloc_(size_t) * Responsible for OOM reporting on NULL return value. + * The *used* bytes of the previous buffer is passed in + * (rather than the old allocation size), in addition to + * the *new* allocation size requested. * - void free_(void *) * - reportAllocOverflow() * Called on overflow before the container returns NULL. @@ -62,7 +64,7 @@ class SystemAllocPolicy { public: void *malloc_(size_t bytes) { return js_malloc(bytes); } - void *realloc_(void *p, size_t bytes) { return js_realloc(p, bytes); } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); } void free_(void *p) { js_free(p); } void reportAllocOverflow() const {} }; @@ -76,7 +78,7 @@ class SystemAllocPolicy * FIXME bug 647103 - rewrite this in terms of temporary allocation functions, * not the system ones. */ -class ContextAllocPolicy +class TempAllocPolicy { JSContext *const cx; @@ -87,7 +89,7 @@ class ContextAllocPolicy JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes); public: - ContextAllocPolicy(JSContext *cx) : cx(cx) {} + TempAllocPolicy(JSContext *cx) : cx(cx) {} JSContext *context() const { return cx; @@ -100,7 +102,7 @@ class ContextAllocPolicy return p; } - void *realloc_(void *p, size_t bytes) { + void *realloc_(void *p, size_t oldBytes, size_t bytes) { void *p2 = js_realloc(p, bytes); if (JS_UNLIKELY(!p2)) p2 = onOutOfMemory(p2, bytes); diff --git a/deps/mozjs/js/src/jsanalyze.cpp b/deps/mozjs/js/src/jsanalyze.cpp index e7ebbeaaeb2..8be96a0bf4b 100644 --- a/deps/mozjs/js/src/jsanalyze.cpp +++ b/deps/mozjs/js/src/jsanalyze.cpp @@ -42,145 +42,59 @@ #include "jscompartment.h" #include "jscntxt.h" +#include "jsinferinlines.h" +#include "jsobjinlines.h" + namespace js { namespace analyze { ///////////////////////////////////////////////////////////////////// -// Script +// Bytecode ///////////////////////////////////////////////////////////////////// +#ifdef DEBUG void -Script::destroy() -{ - JS_FinishArenaPool(&pool); -} - -template -inline T * -ArenaArray(JSArenaPool &pool, unsigned count) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T)); - return (T *) v; -} - -template -inline T * -ArenaNew(JSArenaPool &pool) +PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc) { - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return new (v) T(); + printf("#%u:", script->id()); + Sprinter sprinter(cx); + if (!sprinter.init()) + return; + js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter); + fprintf(stdout, "%s", sprinter.string()); } - -///////////////////////////////////////////////////////////////////// -// Bytecode -///////////////////////////////////////////////////////////////////// - -bool -Bytecode::mergeDefines(JSContext *cx, Script *script, bool initial, unsigned newDepth, - uint32 *newArray, unsigned newCount) -{ - if (initial) { - /* - * Haven't handled any incoming edges to this bytecode before. - * Define arrays are copy on write, so just reuse the array for this bytecode. - */ - stackDepth = newDepth; - defineArray = newArray; - defineCount = newCount; - return true; - } - - /* - * This bytecode has multiple incoming edges, intersect the new array with any - * variables known to be defined along other incoming edges. - */ - if (analyzed) { -#ifdef DEBUG - /* - * Once analyzed, a bytecode has its full set of definitions. There are two - * properties we depend on to ensure this. First, bytecode for a function - * is emitted in topological order, and since we analyze bytecodes in the - * order they were emitted we will have seen all incoming jumps except - * for any loop back edges. Second, javascript has structured control flow, - * so loop heads dominate their bodies; the set of variables defined - * on a back edge will be at least as large as at the head of the loop, - * so no intersection or further analysis needs to be done. - */ - JS_ASSERT(stackDepth == newDepth); - for (unsigned i = 0; i < defineCount; i++) { - bool found = false; - for (unsigned j = 0; j < newCount; j++) { - if (newArray[j] == defineArray[i]) - found = true; - } - JS_ASSERT(found); - } #endif - } else { - JS_ASSERT(stackDepth == newDepth); - bool owned = false; - for (unsigned i = 0; i < defineCount; i++) { - bool found = false; - for (unsigned j = 0; j < newCount; j++) { - if (newArray[j] == defineArray[i]) - found = true; - } - if (!found) { - /* - * Get a mutable copy of the defines. This can end up making - * several copies for a bytecode if it has many incoming edges - * with progressively smaller sets of defined variables. - */ - if (!owned) { - uint32 *reallocArray = ArenaArray(script->pool, defineCount); - if (!reallocArray) { - script->setOOM(cx); - return false; - } - memcpy(reallocArray, defineArray, defineCount * sizeof(uint32)); - defineArray = reallocArray; - owned = true; - } - - /* Swap with the last element and pop the array. */ - defineArray[i--] = defineArray[--defineCount]; - } - } - } - - return true; -} ///////////////////////////////////////////////////////////////////// -// Analysis +// Bytecode Analysis ///////////////////////////////////////////////////////////////////// inline bool -Script::addJump(JSContext *cx, unsigned offset, - unsigned *currentOffset, unsigned *forwardJump, - unsigned stackDepth, uint32 *defineArray, unsigned defineCount) +ScriptAnalysis::addJump(JSContext *cx, unsigned offset, + unsigned *currentOffset, unsigned *forwardJump, + unsigned stackDepth) { JS_ASSERT(offset < script->length); - Bytecode *&bytecode = code[offset]; - bool initial = (bytecode == NULL); - if (initial) { - bytecode = ArenaNew(pool); - if (!bytecode) { + Bytecode *&code = codeArray[offset]; + if (!code) { + code = cx->typeLifoAlloc().new_(); + if (!code) { setOOM(cx); return false; } + code->stackDepth = stackDepth; } + JS_ASSERT(code->stackDepth == stackDepth); - if (!bytecode->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount)) - return false; - bytecode->jumpTarget = true; + code->jumpTarget = true; if (offset < *currentOffset) { + /* Scripts containing loops are never inlined. */ + isInlineable = false; + /* Don't follow back edges to bytecode which has already been analyzed. */ - if (!bytecode->analyzed) { + if (!code->analyzed) { if (*forwardJump == 0) *forwardJump = *currentOffset; *currentOffset = offset; @@ -192,146 +106,108 @@ Script::addJump(JSContext *cx, unsigned offset, return true; } -inline void -Script::setLocal(uint32 local, uint32 offset) +void +ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc) { - JS_ASSERT(local < localCount()); - JS_ASSERT(offset != LOCAL_CONDITIONALLY_DEFINED); - /* - * It isn't possible to change the point when a variable becomes unconditionally - * defined, or to mark it as unconditionally defined after it has already been - * marked as having a use before def. It *is* possible to mark it as having - * a use before def after marking it as unconditionally defined. In a loop such as: - * - * while ((a = b) != 0) { x = a; } - * - * When walking through the body of this loop, we will first analyze the test - * (which comes after the body in the bytecode stream) as unconditional code, - * and mark a as definitely defined. a is not in the define array when taking - * the loop's back edge, so it is treated as possibly undefined when written to x. + * Check to see if an accessed name aliases a local or argument in the + * current script, and mark that local/arg as escaping. We don't need to + * worry about marking locals/arguments in scripts this is nested in, as + * the escaping name will be caught by the parser and the nested local/arg + * will be marked as closed. */ - JS_ASSERT(locals[local] == LOCAL_CONDITIONALLY_DEFINED || - locals[local] == offset || offset == LOCAL_USE_BEFORE_DEF); - - locals[local] = offset; -} - -static inline ptrdiff_t -GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) -{ - uint32 type = JOF_OPTYPE(*pc); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) - return GET_JUMPX_OFFSET(pc2); - return GET_JUMP_OFFSET(pc2); -} -static inline unsigned -GetBytecodeLength(jsbytecode *pc) -{ - JSOp op = (JSOp)*pc; - JS_ASSERT(op < JSOP_LIMIT); - JS_ASSERT(op != JSOP_TRAP); - - if (js_CodeSpec[op].length != -1) - return js_CodeSpec[op].length; - return js_GetVariableBytecodeLength(pc); -} - -// return whether op bytecodes do not fallthrough (they may do a jump). -static inline bool -BytecodeNoFallThrough(JSOp op) -{ - switch (op) { - case JSOP_GOTO: - case JSOP_GOTOX: - case JSOP_DEFAULT: - case JSOP_DEFAULTX: - case JSOP_RETURN: - case JSOP_STOP: - case JSOP_RETRVAL: - case JSOP_THROW: - case JSOP_TABLESWITCH: - case JSOP_TABLESWITCHX: - case JSOP_LOOKUPSWITCH: - case JSOP_LOOKUPSWITCHX: - case JSOP_FILTER: - return true; - case JSOP_GOSUB: - case JSOP_GOSUBX: - // these fall through indirectly, after executing a 'finally'. - return false; - default: - return false; + JSAtom *atom; + if (JSOp(*pc) == JSOP_DEFFUN) { + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(pc)); + atom = fun->atom; + } else { + JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM); + atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); } -} - -/* Untrap a single PC, and retrap it at scope exit. */ -struct UntrapOpcode -{ - jsbytecode *pc; - bool trap; - UntrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) - : pc(pc), trap(JSOp(*pc) == JSOP_TRAP) - { - if (trap) - *pc = JS_GetTrapOpcode(cx, script, pc); - } + uintN index; + BindingKind kind = script->bindings.lookup(cx, atom, &index); - ~UntrapOpcode() - { - if (trap) - *pc = JSOP_TRAP; - } -}; + if (kind == ARGUMENT) + escapedSlots[ArgSlot(index)] = true; + else if (kind == VARIABLE) + escapedSlots[LocalSlot(script, index)] = true; +} void -Script::analyze(JSContext *cx, JSScript *script) +ScriptAnalysis::analyzeBytecode(JSContext *cx) { - JS_ASSERT(!code && !locals); - this->script = script; - - JS_InitArenaPool(&pool, "script_analyze", 256, 8, NULL); + JS_ASSERT(cx->compartment->activeAnalysis); + JS_ASSERT(!ranBytecode()); + LifoAlloc &tla = cx->typeLifoAlloc(); unsigned length = script->length; - unsigned nfixed = localCount(); + unsigned nargs = script->function() ? script->function()->nargs : 0; + + numSlots = TotalSlots(script); - code = ArenaArray(pool, length); - locals = ArenaArray(pool, nfixed); + codeArray = tla.newArray(length); + escapedSlots = tla.newArray(numSlots); - if (!code || !locals) { + if (!codeArray || !escapedSlots) { setOOM(cx); return; } - PodZero(code, length); - - for (unsigned i = 0; i < nfixed; i++) - locals[i] = LOCAL_CONDITIONALLY_DEFINED; + PodZero(codeArray, length); /* - * Treat locals as having a possible use-before-def if they could be accessed - * by debug code or by eval, or if they could be accessed by an inner script. + * Populate arg and local slots which can escape and be accessed in ways + * other than through ARG* and LOCAL* opcodes (though arguments can still + * be indirectly read but not written through 'arguments' properties). + * All escaping locals are treated as having possible use-before-defs. */ - if (script->usesEval || cx->compartment->debugMode) { - for (uint32 i = 0; i < nfixed; i++) - setLocal(i, LOCAL_USE_BEFORE_DEF); + PodZero(escapedSlots, numSlots); + + if (script->usesEval || script->usesArguments || script->compartment()->debugMode()) { + for (unsigned i = 0; i < nargs; i++) + escapedSlots[ArgSlot(i)] = true; + } else { + for (unsigned i = 0; i < script->nClosedArgs; i++) { + unsigned arg = script->getClosedArg(i); + JS_ASSERT(arg < nargs); + escapedSlots[ArgSlot(arg)] = true; + } } - for (uint32 i = 0; i < script->nClosedVars; i++) { - uint32 slot = script->getClosedVar(i); - if (slot < nfixed) - setLocal(slot, LOCAL_USE_BEFORE_DEF); + if (script->usesEval || script->compartment()->debugMode()) { + for (unsigned i = 0; i < script->nfixed; i++) + escapedSlots[LocalSlot(script, i)] = true; + } else { + for (uint32_t i = 0; i < script->nClosedVars; i++) { + unsigned local = script->getClosedVar(i); + JS_ASSERT(local < script->nfixed); + escapedSlots[LocalSlot(script, local)] = true; + } } /* * If the script is in debug mode, JS_SetFrameReturnValue can be called at * any safe point. */ - if (cx->compartment->debugMode) - usesRval = true; + if (cx->compartment->debugMode()) + usesReturnValue_ = true; + + bool heavyweight = script->function() && script->function()->isHeavyweight(); + + isInlineable = true; + if (script->nClosedArgs || script->nClosedVars || heavyweight || + script->usesEval || script->usesArguments || cx->compartment->debugMode()) { + isInlineable = false; + } + + modifiesArguments_ = false; + if (script->nClosedArgs || heavyweight) + modifiesArguments_ = true; + + canTrackVars = true; /* * If we are in the middle of one or more jumps, the offset of the highest @@ -347,14 +223,18 @@ Script::analyze(JSContext *cx, JSScript *script) unsigned forwardCatch = 0; /* Fill in stack depth and definitions at initial bytecode. */ - Bytecode *startcode = ArenaNew(pool); + Bytecode *startcode = tla.new_(); if (!startcode) { setOOM(cx); return; } startcode->stackDepth = 0; - code[0] = startcode; + codeArray[0] = startcode; + + /* Number of JOF_TYPESET opcodes we have encountered. */ + unsigned nTypeSets = 0; + types::TypeSet *typeArray = script->types->typeArray(); unsigned offset, nextOffset = 0; while (nextOffset < length) { @@ -368,11 +248,9 @@ Script::analyze(JSContext *cx, JSScript *script) if (forwardCatch && forwardCatch == offset) forwardCatch = 0; - Bytecode *&bytecode = code[offset]; + Bytecode *code = maybeCode(offset); jsbytecode *pc = script->code + offset; - UntrapOpcode untrap(cx, script, pc); - JSOp op = (JSOp)*pc; JS_ASSERT(op < JSOP_LIMIT); @@ -385,67 +263,71 @@ Script::analyze(JSContext *cx, JSScript *script) */ nextOffset = successorOffset; - if (!bytecode) { + if (!code) { /* Haven't found a path by which this bytecode is reachable. */ continue; } - if (bytecode->analyzed) { + if (code->analyzed) { /* No need to reanalyze, see Bytecode::mergeDefines. */ continue; } - bytecode->analyzed = true; + code->analyzed = true; if (forwardCatch) - bytecode->inTryBlock = true; - - unsigned stackDepth = bytecode->stackDepth; - uint32 *defineArray = bytecode->defineArray; - unsigned defineCount = bytecode->defineCount; + code->inTryBlock = true; - if (!forwardJump) { - /* - * There is no jump over this bytecode, nor a containing try block. - * Either this bytecode will definitely be executed, or an exception - * will be thrown which the script does not catch. Either way, - * any variables definitely defined at this bytecode will stay - * defined throughout the rest of the script. We just need to - * remember the offset where the variable became unconditionally - * defined, rather than continue to maintain it in define arrays. - */ - for (unsigned i = 0; i < defineCount; i++) { - uint32 local = defineArray[i]; - JS_ASSERT_IF(locals[local] != LOCAL_CONDITIONALLY_DEFINED && - locals[local] != LOCAL_USE_BEFORE_DEF, - locals[local] <= offset); - if (locals[local] == LOCAL_CONDITIONALLY_DEFINED) - setLocal(local, offset); - } - defineArray = bytecode->defineArray = NULL; - defineCount = bytecode->defineCount = 0; + if (script->hasBreakpointsAt(pc)) { + code->safePoint = true; + isInlineable = canTrackVars = false; } - unsigned nuses, ndefs; - if (js_CodeSpec[op].nuses == -1) - nuses = js_GetVariableStackUses(op, pc); - else - nuses = js_CodeSpec[op].nuses; + unsigned stackDepth = code->stackDepth; - if (js_CodeSpec[op].ndefs == -1) - ndefs = js_GetEnterBlockStackDefs(cx, script, pc); - else - ndefs = js_CodeSpec[op].ndefs; + if (!forwardJump) + code->unconditional = true; - JS_ASSERT(stackDepth >= nuses); - stackDepth -= nuses; - stackDepth += ndefs; + /* + * Treat decompose ops as no-ops which do not adjust the stack. We will + * pick up the stack depths as we go through the decomposed version. + */ + if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) { + unsigned nuses = GetUseCount(script, offset); + unsigned ndefs = GetDefCount(script, offset); + + JS_ASSERT(stackDepth >= nuses); + stackDepth -= nuses; + stackDepth += ndefs; + } + + /* + * Assign an observed type set to each reachable JOF_TYPESET opcode. + * This may be less than the number of type sets in the script if some + * are unreachable, and may be greater in case the number of type sets + * overflows a uint16. In the latter case a single type set will be + * used for the observed types of all ops after the overflow. + */ + if ((js_CodeSpec[op].format & JOF_TYPESET) && cx->typeInferenceEnabled()) { + if (nTypeSets < script->nTypeSets) { + code->observedTypes = &typeArray[nTypeSets++]; + } else { + JS_ASSERT(nTypeSets == UINT16_MAX); + code->observedTypes = &typeArray[nTypeSets - 1]; + } + } switch (op) { + case JSOP_RETURN: + case JSOP_STOP: + numReturnSites_++; + break; + case JSOP_SETRVAL: case JSOP_POPV: - usesRval = true; + usesReturnValue_ = true; + isInlineable = false; break; case JSOP_NAME: @@ -453,71 +335,96 @@ Script::analyze(JSContext *cx, JSScript *script) case JSOP_BINDNAME: case JSOP_SETNAME: case JSOP_DELNAME: - case JSOP_INCNAME: - case JSOP_DECNAME: - case JSOP_NAMEINC: - case JSOP_NAMEDEC: - case JSOP_FORNAME: - usesScope = true; - break; - - /* Watch for opcodes the method JIT doesn't compile. */ - case JSOP_GOSUB: - case JSOP_GOSUBX: - case JSOP_IFPRIMTOP: - case JSOP_FILTER: - case JSOP_ENDFILTER: - case JSOP_TABLESWITCHX: - case JSOP_LOOKUPSWITCHX: - hadFailure = true; - return; + case JSOP_QNAMEPART: + case JSOP_QNAMECONST: + checkAliasedName(cx, pc); + usesScopeChain_ = true; + isInlineable = false; + break; + + case JSOP_DEFFUN: + case JSOP_DEFVAR: + case JSOP_DEFCONST: + case JSOP_SETCONST: + checkAliasedName(cx, pc); + extendsScope_ = true; + isInlineable = canTrackVars = false; + break; + + case JSOP_EVAL: + extendsScope_ = true; + isInlineable = canTrackVars = false; + break; + + case JSOP_ENTERWITH: + addsScopeObjects_ = true; + isInlineable = canTrackVars = false; + break; + + case JSOP_ENTERLET0: + case JSOP_ENTERLET1: + case JSOP_ENTERBLOCK: + case JSOP_LEAVEBLOCK: + addsScopeObjects_ = true; + isInlineable = false; + break; + + case JSOP_THIS: + usesThisValue_ = true; + break; + + case JSOP_CALL: + case JSOP_NEW: + /* Only consider potentially inlineable calls here. */ + hasFunctionCalls_ = true; + break; case JSOP_TABLESWITCH: { - jsbytecode *pc2 = pc; - unsigned defaultOffset = offset + GetJumpOffset(pc, pc2); - pc2 += JUMP_OFFSET_LEN; + isInlineable = false; + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; jsint low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; jsint high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; - if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, - stackDepth, defineArray, defineCount)) { + if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth)) return; - } + getCode(defaultOffset).switchTarget = true; + getCode(defaultOffset).safePoint = true; for (jsint i = low; i <= high; i++) { - unsigned targetOffset = offset + GetJumpOffset(pc, pc2); + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); if (targetOffset != offset) { - if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, - stackDepth, defineArray, defineCount)) { + if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth)) return; - } } + getCode(targetOffset).switchTarget = true; + getCode(targetOffset).safePoint = true; pc2 += JUMP_OFFSET_LEN; } break; } case JSOP_LOOKUPSWITCH: { - jsbytecode *pc2 = pc; - unsigned defaultOffset = offset + GetJumpOffset(pc, pc2); - pc2 += JUMP_OFFSET_LEN; + isInlineable = false; + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; unsigned npairs = GET_UINT16(pc2); pc2 += UINT16_LEN; - if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, - stackDepth, defineArray, defineCount)) { + if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth)) return; - } + getCode(defaultOffset).switchTarget = true; + getCode(defaultOffset).safePoint = true; while (npairs) { pc2 += INDEX_LEN; - unsigned targetOffset = offset + GetJumpOffset(pc, pc2); - if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, - stackDepth, defineArray, defineCount)) { + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth)) return; - } + getCode(targetOffset).switchTarget = true; + getCode(targetOffset).safePoint = true; pc2 += JUMP_OFFSET_LEN; npairs--; } @@ -531,10 +438,11 @@ Script::analyze(JSContext *cx, JSScript *script) * exception but is not caught by a later handler in the same function: * no more code will execute, and it does not matter what is defined. */ + isInlineable = false; JSTryNote *tn = script->trynotes()->vector; JSTryNote *tnlimit = tn + script->trynotes()->length; for (; tn < tnlimit; tn++) { - unsigned startOffset = script->main - script->code + tn->start; + unsigned startOffset = script->mainOffset + tn->start; if (startOffset == offset + 1) { unsigned catchOffset = startOffset + tn->length; @@ -543,134 +451,1506 @@ Script::analyze(JSContext *cx, JSScript *script) forwardCatch = catchOffset; if (tn->kind != JSTRY_ITER) { - if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, - stackDepth, defineArray, defineCount)) { + if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, stackDepth)) return; - } - code[catchOffset]->exceptionEntry = true; + getCode(catchOffset).exceptionEntry = true; + getCode(catchOffset).safePoint = true; } } } break; } - case JSOP_GETLOCAL: + case JSOP_GETLOCAL: { /* * Watch for uses of variables not known to be defined, and mark * them as having possible uses before definitions. Ignore GETLOCAL * followed by a POP, these are generated for, e.g. 'var x;' */ - if (pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP) { - uint32 local = GET_SLOTNO(pc); - if (local < nfixed && !localDefined(local, offset)) - setLocal(local, LOCAL_USE_BEFORE_DEF); + jsbytecode *next = pc + JSOP_GETLOCAL_LENGTH; + if (JSOp(*next) != JSOP_POP || jumpTarget(next)) { + uint32_t local = GET_SLOTNO(pc); + if (local >= script->nfixed) { + localsAliasStack_ = true; + break; + } } break; + } case JSOP_CALLLOCAL: - case JSOP_GETLOCALPROP: case JSOP_INCLOCAL: case JSOP_DECLOCAL: case JSOP_LOCALINC: - case JSOP_LOCALDEC: { - uint32 local = GET_SLOTNO(pc); - if (local < nfixed && !localDefined(local, offset)) - setLocal(local, LOCAL_USE_BEFORE_DEF); + case JSOP_LOCALDEC: + case JSOP_SETLOCAL: { + uint32_t local = GET_SLOTNO(pc); + if (local >= script->nfixed) { + localsAliasStack_ = true; + break; + } break; } - case JSOP_SETLOCAL: - case JSOP_FORLOCAL: { - uint32 local = GET_SLOTNO(pc); + case JSOP_SETARG: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + modifiesArguments_ = true; + isInlineable = false; + break; - /* - * The local variable may already have been marked as unconditionally - * defined at a later point in the script, if that definition was in the - * condition for a loop which then jumped back here. In such cases we - * will not treat the variable as ever being defined in the loop body - * (see setLocal). - */ - if (local < nfixed && locals[local] == LOCAL_CONDITIONALLY_DEFINED) { - if (forwardJump) { - /* Add this local to the variables defined after this bytecode. */ - uint32 *newArray = ArenaArray(pool, defineCount + 1); - if (!newArray) { - setOOM(cx); - return; - } - if (defineCount) - memcpy(newArray, defineArray, defineCount * sizeof(uint32)); - defineArray = newArray; - defineArray[defineCount++] = local; - } else { - /* This local is unconditionally defined by this bytecode. */ - setLocal(local, offset); - } - } + /* Additional opcodes which can be compiled but which can't be inlined. */ + case JSOP_ARGUMENTS: + case JSOP_THROW: + case JSOP_EXCEPTION: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: + case JSOP_LAMBDA: + case JSOP_LAMBDA_FC: + case JSOP_GETFCSLOT: + case JSOP_CALLFCSLOT: + case JSOP_DEBUGGER: + case JSOP_FUNCALL: + case JSOP_FUNAPPLY: + isInlineable = false; break; - } default: break; } - uint32 type = JOF_TYPE(js_CodeSpec[op].format); + uint32_t type = JOF_TYPE(js_CodeSpec[op].format); /* Check basic jump opcodes, which may or may not have a fallthrough. */ - if (type == JOF_JUMP || type == JOF_JUMPX) { + if (type == JOF_JUMP) { /* Some opcodes behave differently on their branching path. */ - unsigned newStackDepth; + unsigned newStackDepth = stackDepth; switch (op) { - case JSOP_OR: - case JSOP_AND: - case JSOP_ORX: - case JSOP_ANDX: - /* OR/AND instructions push the operation result when short circuiting. */ - newStackDepth = stackDepth + 1; - break; - case JSOP_CASE: - case JSOP_CASEX: /* Case instructions do not push the lvalue back when branching. */ - newStackDepth = stackDepth - 1; + newStackDepth--; break; - default: - newStackDepth = stackDepth; - break; + default:; } - unsigned targetOffset = offset + GetJumpOffset(pc, pc); - if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, - newStackDepth, defineArray, defineCount)) { + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc); + if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, newStackDepth)) return; - } } /* Handle any fallthrough from this opcode. */ if (!BytecodeNoFallThrough(op)) { JS_ASSERT(successorOffset < script->length); - Bytecode *&nextcode = code[successorOffset]; - bool initial = (nextcode == NULL); + Bytecode *&nextcode = codeArray[successorOffset]; - if (initial) { - nextcode = ArenaNew(pool); + if (!nextcode) { + nextcode = tla.new_(); if (!nextcode) { setOOM(cx); return; } + nextcode->stackDepth = stackDepth; } + JS_ASSERT(nextcode->stackDepth == stackDepth); - if (!nextcode->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount)) - return; + if (type == JOF_JUMP) + nextcode->jumpFallthrough = true; + + /* Treat the fallthrough of a branch instruction as a jump target. */ + if (type == JOF_JUMP) + nextcode->jumpTarget = true; + else + nextcode->fallthrough = true; } } JS_ASSERT(!failed()); JS_ASSERT(forwardJump == 0 && forwardCatch == 0); + + ranBytecode_ = true; } +///////////////////////////////////////////////////////////////////// +// Lifetime Analysis +///////////////////////////////////////////////////////////////////// + +void +ScriptAnalysis::analyzeLifetimes(JSContext *cx) +{ + JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed()); + + if (!ranBytecode()) { + analyzeBytecode(cx); + if (failed()) + return; + } + + LifoAlloc &tla = cx->typeLifoAlloc(); + + lifetimes = tla.newArray(numSlots); + if (!lifetimes) { + setOOM(cx); + return; + } + PodZero(lifetimes, numSlots); + + /* + * Variables which are currently dead. On forward branches to locations + * where these are live, they need to be marked as live. + */ + LifetimeVariable **saved = (LifetimeVariable **) + cx->calloc_(numSlots * sizeof(LifetimeVariable*)); + if (!saved) { + setOOM(cx); + return; + } + unsigned savedCount = 0; + + LoopAnalysis *loop = NULL; + + uint32_t offset = script->length - 1; + while (offset < script->length) { + Bytecode *code = maybeCode(offset); + if (!code) { + offset--; + continue; + } + + if (loop && code->safePoint) + loop->hasSafePoints = true; + + jsbytecode *pc = script->code + offset; + + JSOp op = (JSOp) *pc; + + if (op == JSOP_LOOPHEAD && code->loop) { + /* + * This is the head of a loop, we need to go and make sure that any + * variables live at the head are live at the backedge and points prior. + * For each such variable, look for the last lifetime segment in the body + * and extend it to the end of the loop. + */ + JS_ASSERT(loop == code->loop); + unsigned backedge = code->loop->backedge; + for (unsigned i = 0; i < numSlots; i++) { + if (lifetimes[i].lifetime) + extendVariable(cx, lifetimes[i], offset, backedge); + } + + loop = loop->parent; + JS_ASSERT_IF(loop, loop->head < offset); + } + + /* Find the last jump target in the loop, other than the initial entry point. */ + if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock) + loop->lastBlock = offset; + + if (code->exceptionEntry) { + DebugOnly found = false; + JSTryNote *tn = script->trynotes()->vector; + JSTryNote *tnlimit = tn + script->trynotes()->length; + for (; tn < tnlimit; tn++) { + unsigned startOffset = script->mainOffset + tn->start; + if (startOffset + tn->length == offset) { + /* + * Extend all live variables at exception entry to the start of + * the try block. + */ + for (unsigned i = 0; i < numSlots; i++) { + if (lifetimes[i].lifetime) + ensureVariable(lifetimes[i], startOffset - 1); + } + + found = true; + break; + } + } + JS_ASSERT(found); + } + + switch (op) { + case JSOP_GETARG: + case JSOP_CALLARG: + case JSOP_GETLOCAL: + case JSOP_CALLLOCAL: + case JSOP_THIS: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (!slotEscapes(slot)) + addVariable(cx, lifetimes[slot], offset, saved, savedCount); + break; + } + + case JSOP_SETARG: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (!slotEscapes(slot)) + killVariable(cx, lifetimes[slot], offset, saved, savedCount); + break; + } + + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (!slotEscapes(slot)) { + killVariable(cx, lifetimes[slot], offset, saved, savedCount); + addVariable(cx, lifetimes[slot], offset, saved, savedCount); + } + break; + } + + case JSOP_LOOKUPSWITCH: + case JSOP_TABLESWITCH: + /* Restore all saved variables. :FIXME: maybe do this precisely. */ + for (unsigned i = 0; i < savedCount; i++) { + LifetimeVariable &var = *saved[i]; + var.lifetime = tla.new_(offset, var.savedEnd, var.saved); + if (!var.lifetime) { + cx->free_(saved); + setOOM(cx); + return; + } + var.saved = NULL; + saved[i--] = saved[--savedCount]; + } + savedCount = 0; + break; + + case JSOP_TRY: + for (unsigned i = 0; i < numSlots; i++) { + LifetimeVariable &var = lifetimes[i]; + if (var.ensured) { + JS_ASSERT(var.lifetime); + if (var.lifetime->start == offset) + var.ensured = false; + } + } + break; + + case JSOP_NEW: + case JSOP_CALL: + case JSOP_EVAL: + case JSOP_FUNAPPLY: + case JSOP_FUNCALL: + if (loop) + loop->hasCallsLoops = true; + break; + + default:; + } + + uint32_t type = JOF_TYPE(js_CodeSpec[op].format); + if (type == JOF_JUMP) { + /* + * Forward jumps need to pull in all variables which are live at + * their target offset --- the variables live before the jump are + * the union of those live at the fallthrough and at the target. + */ + uint32_t targetOffset = FollowBranch(cx, script, offset); + + /* + * Watch for 'continue' statements in the loop body, which are + * jumps to the entry offset separate from the initial jump. + */ + if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock) + loop->lastBlock = loop->entry; + + if (targetOffset < offset) { + /* This is a loop back edge, no lifetime to pull in yet. */ + +#ifdef DEBUG + JSOp nop = JSOp(script->code[targetOffset]); + JS_ASSERT(nop == JSOP_LOOPHEAD); +#endif + + /* + * If we already have a loop, it is an outer loop and we + * need to prune the last block in the loop --- we do not + * track 'continue' statements for outer loops. + */ + if (loop && loop->entry > loop->lastBlock) + loop->lastBlock = loop->entry; + + LoopAnalysis *nloop = tla.new_(); + if (!nloop) { + cx->free_(saved); + setOOM(cx); + return; + } + PodZero(nloop); + + if (loop) + loop->hasCallsLoops = true; + + nloop->parent = loop; + loop = nloop; + + getCode(targetOffset).loop = loop; + loop->head = targetOffset; + loop->backedge = offset; + loop->lastBlock = loop->head; + + /* + * Find the entry jump, which will be a GOTO for 'for' or + * 'while' loops or a fallthrough for 'do while' loops. + */ + uint32_t entry = targetOffset; + if (entry) { + do { + entry--; + } while (!maybeCode(entry)); + + jsbytecode *entrypc = script->code + entry; + + if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER) + loop->entry = entry + GET_JUMP_OFFSET(entrypc); + else + loop->entry = targetOffset; + } else { + /* Do-while loop at the start of the script. */ + loop->entry = targetOffset; + } + JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD || + script->code[loop->entry] == JSOP_LOOPENTRY); + } else { + for (unsigned i = 0; i < savedCount; i++) { + LifetimeVariable &var = *saved[i]; + JS_ASSERT(!var.lifetime && var.saved); + if (var.live(targetOffset)) { + /* + * Jumping to a place where this variable is live. Make a new + * lifetime segment for the variable. + */ + var.lifetime = tla.new_(offset, var.savedEnd, var.saved); + if (!var.lifetime) { + cx->free_(saved); + setOOM(cx); + return; + } + var.saved = NULL; + saved[i--] = saved[--savedCount]; + } else if (loop && !var.savedEnd) { + /* + * This jump precedes the basic block which killed the variable, + * remember it and use it for the end of the next lifetime + * segment should the variable become live again. This is needed + * for loops, as if we wrap liveness around the loop the isLive + * test below may have given the wrong answer. + */ + var.savedEnd = offset; + } + } + } + } + + offset--; + } + + cx->free_(saved); + + ranLifetimes_ = true; +} + +#ifdef DEBUG +void +LifetimeVariable::print() const +{ + Lifetime *segment = lifetime ? lifetime : saved; + while (segment) { + printf(" (%u,%u%s)", segment->start, segment->end, segment->loopTail ? ",tail" : ""); + segment = segment->next; + } + printf("\n"); +} +#endif /* DEBUG */ + +inline void +ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, + LifetimeVariable **&saved, unsigned &savedCount) +{ + if (var.lifetime) { + if (var.ensured) + return; + + JS_ASSERT(offset < var.lifetime->start); + var.lifetime->start = offset; + } else { + if (var.saved) { + /* Remove from the list of saved entries. */ + for (unsigned i = 0; i < savedCount; i++) { + if (saved[i] == &var) { + JS_ASSERT(savedCount); + saved[i--] = saved[--savedCount]; + break; + } + } + } + var.lifetime = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); + if (!var.lifetime) { + setOOM(cx); + return; + } + var.saved = NULL; + } +} + +inline void +ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, + LifetimeVariable **&saved, unsigned &savedCount) +{ + if (!var.lifetime) { + /* Make a point lifetime indicating the write. */ + if (!var.saved) + saved[savedCount++] = &var; + var.saved = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); + if (!var.saved) { + setOOM(cx); + return; + } + var.saved->write = true; + var.savedEnd = 0; + return; + } + + JS_ASSERT_IF(!var.ensured, offset < var.lifetime->start); + unsigned start = var.lifetime->start; + + /* + * The variable is considered to be live at the bytecode which kills it + * (just not at earlier bytecodes). This behavior is needed by downstream + * register allocation (see FrameState::bestEvictReg). + */ + var.lifetime->start = offset; + var.lifetime->write = true; + + if (var.ensured) { + /* + * The variable is live even before the write, due to an enclosing try + * block. We need to split the lifetime to indicate there was a write. + * We set the new interval's savedEnd to 0, since it will always be + * adjacent to the old interval, so it never needs to be extended. + */ + var.lifetime = cx->typeLifoAlloc().new_(start, 0, var.lifetime); + if (!var.lifetime) { + setOOM(cx); + return; + } + var.lifetime->end = offset; + } else { + var.saved = var.lifetime; + var.savedEnd = 0; + var.lifetime = NULL; + + saved[savedCount++] = &var; + } +} + +inline void +ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var, + unsigned start, unsigned end) +{ + JS_ASSERT(var.lifetime); + if (var.ensured) { + /* + * If we are still ensured to be live, the try block must scope over + * the loop, in which case the variable is already guaranteed to be + * live for the entire loop. + */ + JS_ASSERT(var.lifetime->start < start); + return; + } + + var.lifetime->start = start; + + /* + * Consider this code: + * + * while (...) { (#1) + * use x; (#2) + * ... + * x = ...; (#3) + * ... + * } (#4) + * + * Just before analyzing the while statement, there would be a live range + * from #1..#2 and a "point range" at #3. The job of extendVariable is to + * create a new live range from #3..#4. + * + * However, more extensions may be required if the definition of x is + * conditional. Consider the following. + * + * while (...) { (#1) + * use x; (#2) + * ... + * if (...) (#5) + * x = ...; (#3) + * ... + * } (#4) + * + * Assume that x is not used after the loop. Then, before extendVariable is + * run, the live ranges would be the same as before (#1..#2 and #3..#3). We + * still need to create a range from #3..#4. But, since the assignment at #3 + * may never run, we also need to create a range from #2..#3. This is done + * as follows. + * + * Each time we create a Lifetime, we store the start of the most recently + * seen sequence of conditional code in the Lifetime's savedEnd field. So, + * when creating the Lifetime at #2, we set the Lifetime's savedEnd to + * #5. (The start of the most recent conditional is cached in each + * variable's savedEnd field.) Consequently, extendVariable is able to + * create a new interval from #2..#5 using the savedEnd field of the + * existing #1..#2 interval. + */ + + Lifetime *segment = var.lifetime; + while (segment && segment->start < end) { + uint32_t savedEnd = segment->savedEnd; + if (!segment->next || segment->next->start >= end) { + /* + * savedEnd is only set for variables killed in the middle of the + * loop. Make a tail segment connecting the last use with the + * back edge. + */ + if (segment->end >= end) { + /* Variable known to be live after the loop finishes. */ + break; + } + savedEnd = end; + } + JS_ASSERT(savedEnd <= end); + if (savedEnd > segment->end) { + Lifetime *tail = cx->typeLifoAlloc().new_(savedEnd, 0, segment->next); + if (!tail) { + setOOM(cx); + return; + } + tail->start = segment->end; + tail->loopTail = true; + + /* + * Clear the segment's saved end, but preserve in the tail if this + * is the last segment in the loop and the variable is killed in an + * outer loop before the backedge. + */ + if (segment->savedEnd > end) { + JS_ASSERT(savedEnd == end); + tail->savedEnd = segment->savedEnd; + } + segment->savedEnd = 0; + + segment->next = tail; + segment = tail->next; + } else { + JS_ASSERT(segment->savedEnd == 0); + segment = segment->next; + } + } +} + +inline void +ScriptAnalysis::ensureVariable(LifetimeVariable &var, unsigned until) +{ + JS_ASSERT(var.lifetime); + + /* + * If we are already ensured, the current range we are trying to ensure + * should already be included. + */ + if (var.ensured) { + JS_ASSERT(var.lifetime->start <= until); + return; + } + + JS_ASSERT(until < var.lifetime->start); + var.lifetime->start = until; + var.ensured = true; +} + +void +ScriptAnalysis::clearAllocations() +{ + /* + * Clear out storage used for register allocations in a compilation once + * that compilation has finished. Register allocations are only used for + * a single compilation. + */ + for (unsigned i = 0; i < script->length; i++) { + Bytecode *code = maybeCode(i); + if (code) + code->allocation = NULL; + } +} + +///////////////////////////////////////////////////////////////////// +// SSA Analysis +///////////////////////////////////////////////////////////////////// + +void +ScriptAnalysis::analyzeSSA(JSContext *cx) +{ + JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed()); + + if (!ranLifetimes()) { + analyzeLifetimes(cx); + if (failed()) + return; + } + + LifoAlloc &tla = cx->typeLifoAlloc(); + unsigned maxDepth = script->nslots - script->nfixed; + + /* + * Current value of each variable and stack value. Empty for missing or + * untracked entries, i.e. escaping locals and arguments. + */ + SSAValue *values = (SSAValue *) + cx->calloc_((numSlots + maxDepth) * sizeof(SSAValue)); + if (!values) { + setOOM(cx); + return; + } + struct FreeSSAValues { + JSContext *cx; + SSAValue *values; + FreeSSAValues(JSContext *cx, SSAValue *values) : cx(cx), values(values) {} + ~FreeSSAValues() { cx->free_(values); } + } free(cx, values); + + SSAValue *stack = values + numSlots; + uint32_t stackDepth = 0; + + for (uint32_t slot = ArgSlot(0); slot < numSlots; slot++) { + if (trackSlot(slot)) + values[slot].initInitial(slot); + } + + /* + * All target offsets for forward jumps we in the middle of. We lazily add + * pending entries at these targets for the original value of variables + * modified before the branch rejoins. + */ + Vector branchTargets(cx); + + /* + * Subset of branchTargets which are also exception handlers. Any value of + * a variable modified before the target is reached is a potential value + * at that target, along with the lazily added original value. + */ + Vector exceptionTargets(cx); + + uint32_t offset = 0; + while (offset < script->length) { + jsbytecode *pc = script->code + offset; + JSOp op = (JSOp)*pc; + + uint32_t successorOffset = offset + GetBytecodeLength(pc); + + Bytecode *code = maybeCode(pc); + if (!code) { + offset = successorOffset; + continue; + } + + if (code->stackDepth > stackDepth) + PodZero(stack + stackDepth, code->stackDepth - stackDepth); + stackDepth = code->stackDepth; + + if (op == JSOP_LOOPHEAD && code->loop) { + /* + * Make sure there is a pending value array for phi nodes at the + * loop head. We won't be able to clear these until we reach the + * loop's back edge. + * + * We need phi nodes for all variables which might be modified + * during the loop. This ensures that in the loop body we have + * already updated state to reflect possible changes that happen + * before the back edge, and don't need to go back and fix things + * up when we *do* get to the back edge. This could be made lazier. + * + * We don't make phi nodes for values on the stack at the head of + * the loop. These may be popped during the loop (i.e. for ITER + * loops), but in such cases the original value is pushed back. + */ + Vector *&pending = code->pendingValues; + if (pending) { + removeBranchTarget(branchTargets, exceptionTargets, offset); + } else { + pending = cx->new_< Vector >(cx); + if (!pending) { + setOOM(cx); + return; + } + } + + /* + * Make phi nodes and update state for slots which are already in + * pending from previous branches to the loop head, and which are + * modified in the body of the loop. + */ + for (unsigned i = 0; i < pending->length(); i++) { + SlotValue &v = (*pending)[i]; + if (v.slot < numSlots && liveness(v.slot).firstWrite(code->loop) != UINT32_MAX) { + if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() < offset) { + SSAValue ov = v.value; + if (!makePhi(cx, v.slot, offset, &ov)) + return; + insertPhi(cx, ov, v.value); + v.value = ov; + } + } + if (code->fallthrough || code->jumpFallthrough) + mergeValue(cx, offset, values[v.slot], &v); + mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets); + values[v.slot] = v.value; + } + + /* + * Make phi nodes for all other slots which might be modified + * during the loop. This ensures that in the loop body we have + * already updated state to reflect possible changes that happen + * before the back edge, and don't need to go back and fix things + * up when we *do* get to the back edge. This could be made lazier. + */ + for (uint32_t slot = ArgSlot(0); slot < numSlots + stackDepth; slot++) { + if (slot >= numSlots || !trackSlot(slot)) + continue; + if (liveness(slot).firstWrite(code->loop) == UINT32_MAX) + continue; + if (values[slot].kind() == SSAValue::PHI && values[slot].phiOffset() == offset) { + /* There is already a pending entry for this slot. */ + continue; + } + SSAValue ov; + if (!makePhi(cx, slot, offset, &ov)) + return; + if (code->fallthrough || code->jumpFallthrough) + insertPhi(cx, ov, values[slot]); + mergeBranchTarget(cx, values[slot], slot, branchTargets); + values[slot] = ov; + if (!pending->append(SlotValue(slot, ov))) { + setOOM(cx); + return; + } + } + } else if (code->pendingValues) { + /* + * New values at this point from a previous jump to this bytecode. + * If there is fallthrough from the previous instruction, merge + * with the current state and create phi nodes where necessary, + * otherwise replace current values with the new values. + * + * Catch blocks are artifically treated as having fallthrough, so + * that values written inside the block but not subsequently + * overwritten are picked up. + */ + bool exception = removeBranchTarget(branchTargets, exceptionTargets, offset); + Vector *pending = code->pendingValues; + for (unsigned i = 0; i < pending->length(); i++) { + SlotValue &v = (*pending)[i]; + if (code->fallthrough || code->jumpFallthrough || + (exception && values[v.slot].kind() != SSAValue::EMPTY)) { + mergeValue(cx, offset, values[v.slot], &v); + } + mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets); + values[v.slot] = v.value; + } + freezeNewValues(cx, offset); + } + + if (js_CodeSpec[op].format & JOF_DECOMPOSE) { + offset = successorOffset; + continue; + } + + unsigned nuses = GetUseCount(script, offset); + unsigned ndefs = GetDefCount(script, offset); + JS_ASSERT(stackDepth >= nuses); + + unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses; + + if (xuses) { + code->poppedValues = tla.newArray(xuses); + if (!code->poppedValues) { + setOOM(cx); + return; + } + for (unsigned i = 0; i < nuses; i++) { + SSAValue &v = stack[stackDepth - 1 - i]; + code->poppedValues[i] = v; + v.clear(); + } + if (xuses > nuses) { + /* + * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped + * value holding the value of the local before the op. + */ + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) + code->poppedValues[nuses] = values[slot]; + else + code->poppedValues[nuses].clear(); + } + + if (xuses) { + SSAUseChain *useChains = tla.newArray(xuses); + if (!useChains) { + setOOM(cx); + return; + } + PodZero(useChains, xuses); + for (unsigned i = 0; i < xuses; i++) { + const SSAValue &v = code->poppedValues[i]; + if (trackUseChain(v)) { + SSAUseChain *&uses = useChain(v); + useChains[i].popped = true; + useChains[i].offset = offset; + useChains[i].u.which = i; + useChains[i].next = uses; + uses = &useChains[i]; + } + } + } + } + + stackDepth -= nuses; + + for (unsigned i = 0; i < ndefs; i++) + stack[stackDepth + i].initPushed(offset, i); + + unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs; + if (xdefs) { + code->pushedUses = tla.newArray(xdefs); + if (!code->pushedUses) { + setOOM(cx); + return; + } + PodZero(code->pushedUses, xdefs); + } + + stackDepth += ndefs; + + if (BytecodeUpdatesSlot(op)) { + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + mergeBranchTarget(cx, values[slot], slot, branchTargets); + mergeExceptionTarget(cx, values[slot], slot, exceptionTargets); + values[slot].initWritten(slot, offset); + } + } + + switch (op) { + case JSOP_GETARG: + case JSOP_GETLOCAL: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + /* + * Propagate the current value of the local to the pushed value, + * and remember it with an extended use on the opcode. + */ + stack[stackDepth - 1] = code->poppedValues[0] = values[slot]; + } + break; + } + + /* Short circuit ops which push back one of their operands. */ + + case JSOP_MOREITER: + stack[stackDepth - 2] = code->poppedValues[0]; + break; + + case JSOP_INITPROP: + case JSOP_INITMETHOD: + stack[stackDepth - 1] = code->poppedValues[1]; + break; + + case JSOP_INITELEM: + stack[stackDepth - 1] = code->poppedValues[2]; + break; + + case JSOP_DUP: + stack[stackDepth - 1] = stack[stackDepth - 2] = code->poppedValues[0]; + break; + + case JSOP_DUP2: + stack[stackDepth - 1] = stack[stackDepth - 3] = code->poppedValues[0]; + stack[stackDepth - 2] = stack[stackDepth - 4] = code->poppedValues[1]; + break; + + case JSOP_SWAP: + /* Swap is like pick 1. */ + case JSOP_PICK: { + unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]); + stack[stackDepth - 1] = code->poppedValues[pickedDepth]; + for (unsigned i = 0; i < pickedDepth; i++) + stack[stackDepth - 2 - i] = code->poppedValues[i]; + break; + } + + /* + * Switch and try blocks preserve the stack between the original op + * and all case statements or exception/finally handlers. + */ + + case JSOP_TABLESWITCH: { + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; + jsint low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + jsint high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + + checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth); + + for (jsint i = low; i <= high; i++) { + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + if (targetOffset != offset) + checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth); + pc2 += JUMP_OFFSET_LEN; + } + break; + } + + case JSOP_LOOKUPSWITCH: { + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; + unsigned npairs = GET_UINT16(pc2); + pc2 += UINT16_LEN; + + checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth); + + while (npairs) { + pc2 += INDEX_LEN; + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth); + pc2 += JUMP_OFFSET_LEN; + npairs--; + } + break; + } + + case JSOP_TRY: { + JSTryNote *tn = script->trynotes()->vector; + JSTryNote *tnlimit = tn + script->trynotes()->length; + for (; tn < tnlimit; tn++) { + unsigned startOffset = script->mainOffset + tn->start; + if (startOffset == offset + 1) { + unsigned catchOffset = startOffset + tn->length; + + if (tn->kind != JSTRY_ITER) { + checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth); + checkExceptionTarget(cx, catchOffset, exceptionTargets); + } + } + } + break; + } + + case JSOP_THROW: + case JSOP_RETURN: + case JSOP_STOP: + case JSOP_RETRVAL: + mergeAllExceptionTargets(cx, values, exceptionTargets); + break; + + default:; + } + + uint32_t type = JOF_TYPE(js_CodeSpec[op].format); + if (type == JOF_JUMP) { + unsigned targetOffset = FollowBranch(cx, script, offset); + checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth); + + /* + * If this is a back edge, we're done with the loop and can freeze + * the phi values at the head now. + */ + if (targetOffset < offset) + freezeNewValues(cx, targetOffset); + } + + offset = successorOffset; + } + + ranSSA_ = true; +} + +/* Get a phi node's capacity for a given length. */ +static inline unsigned +PhiNodeCapacity(unsigned length) +{ + if (length <= 4) + return 4; + + unsigned log2; + JS_FLOOR_LOG2(log2, length - 1); + return 1 << (log2 + 1); +} + +bool +ScriptAnalysis::makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv) +{ + SSAPhiNode *node = cx->typeLifoAlloc().new_(); + SSAValue *options = cx->typeLifoAlloc().newArray(PhiNodeCapacity(0)); + if (!node || !options) { + setOOM(cx); + return false; + } + node->slot = slot; + node->options = options; + pv->initPhi(offset, node); + return true; +} + +void +ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v) +{ + JS_ASSERT(phi.kind() == SSAValue::PHI); + SSAPhiNode *node = phi.phiNode(); + + /* + * Filter dupes inserted into small nodes to keep things clean and avoid + * extra type constraints, but don't bother on large phi nodes to avoid + * quadratic behavior. + */ + if (node->length <= 8) { + for (unsigned i = 0; i < node->length; i++) { + if (v == node->options[i]) + return; + } + } + + if (trackUseChain(v)) { + SSAUseChain *&uses = useChain(v); + + SSAUseChain *use = cx->typeLifoAlloc().new_(); + if (!use) { + setOOM(cx); + return; + } + + use->popped = false; + use->offset = phi.phiOffset(); + use->u.phi = node; + use->next = uses; + uses = use; + } + + if (node->length < PhiNodeCapacity(node->length)) { + node->options[node->length++] = v; + return; + } + + SSAValue *newOptions = + cx->typeLifoAlloc().newArray(PhiNodeCapacity(node->length + 1)); + if (!newOptions) { + setOOM(cx); + return; + } + + PodCopy(newOptions, node->options, node->length); + node->options = newOptions; + node->options[node->length++] = v; +} + +inline void +ScriptAnalysis::mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv) +{ + /* Make sure that v is accounted for in the pending value or phi value at pv. */ + JS_ASSERT(v.kind() != SSAValue::EMPTY && pv->value.kind() != SSAValue::EMPTY); + + if (v == pv->value) + return; + + if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() < offset) { + SSAValue ov = pv->value; + if (makePhi(cx, pv->slot, offset, &pv->value)) { + insertPhi(cx, pv->value, v); + insertPhi(cx, pv->value, ov); + } + return; + } + + JS_ASSERT(pv->value.phiOffset() == offset); + insertPhi(cx, pv->value, v); +} + +void +ScriptAnalysis::checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot, + Vector *pending) +{ + JS_ASSERT(v.kind() != SSAValue::EMPTY); + + for (unsigned i = 0; i < pending->length(); i++) { + if ((*pending)[i].slot == slot) + return; + } + + if (!pending->append(SlotValue(slot, v))) + setOOM(cx); +} + +void +ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset, + Vector &branchTargets, + SSAValue *values, uint32_t stackDepth) +{ + unsigned targetDepth = getCode(targetOffset).stackDepth; + JS_ASSERT(targetDepth <= stackDepth); + + /* + * If there is already an active branch to target, make sure its pending + * values reflect any changes made since the first branch. Otherwise, add a + * new pending branch and determine its pending values lazily. + */ + Vector *&pending = getCode(targetOffset).pendingValues; + if (pending) { + for (unsigned i = 0; i < pending->length(); i++) { + SlotValue &v = (*pending)[i]; + mergeValue(cx, targetOffset, values[v.slot], &v); + } + } else { + pending = cx->new_< Vector >(cx); + if (!pending || !branchTargets.append(targetOffset)) { + setOOM(cx); + return; + } + } + + /* + * Make sure there is a pending entry for each value on the stack. + * The number of stack entries at join points is usually zero, and + * we don't want to look at the active branches while popping and + * pushing values in each opcode. + */ + for (unsigned i = 0; i < targetDepth; i++) { + uint32_t slot = StackSlot(script, i); + checkPendingValue(cx, values[slot], slot, pending); + } +} + +void +ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset, + Vector &exceptionTargets) +{ + /* + * The catch offset will already be in the branch targets, just check + * whether this is already a known exception target. + */ + for (unsigned i = 0; i < exceptionTargets.length(); i++) { + if (exceptionTargets[i] == catchOffset) + return; + } + if (!exceptionTargets.append(catchOffset)) + setOOM(cx); +} + +void +ScriptAnalysis::mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32_t slot, + const Vector &branchTargets) +{ + if (slot >= numSlots) { + /* + * There is no need to lazily check that there are pending values at + * branch targets for slots on the stack, these are added to pending + * eagerly. + */ + return; + } + + JS_ASSERT(trackSlot(slot)); + + /* + * Before changing the value of a variable, make sure the old value is + * marked at the target of any branches jumping over the current opcode. + */ + for (unsigned i = 0; i < branchTargets.length(); i++) { + Vector *pending = getCode(branchTargets[i]).pendingValues; + checkPendingValue(cx, value, slot, pending); + } +} + +void +ScriptAnalysis::mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot, + const Vector &exceptionTargets) +{ + JS_ASSERT(trackSlot(slot)); + + /* + * Update the value at exception targets with the value of a variable + * before it is overwritten. Unlike mergeBranchTarget, this is done whether + * or not the overwritten value is the value of the variable at the + * original branch. Values for a variable which are written after the + * try block starts and overwritten before it is finished can still be + * seen at exception handlers via exception paths. + */ + for (unsigned i = 0; i < exceptionTargets.length(); i++) { + unsigned offset = exceptionTargets[i]; + Vector *pending = getCode(offset).pendingValues; + + bool duplicate = false; + for (unsigned i = 0; i < pending->length(); i++) { + if ((*pending)[i].slot == slot) { + duplicate = true; + SlotValue &v = (*pending)[i]; + mergeValue(cx, offset, value, &v); + break; + } + } + + if (!duplicate && !pending->append(SlotValue(slot, value))) + setOOM(cx); + } +} + +void +ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValue *values, + const Vector &exceptionTargets) +{ + for (unsigned i = 0; i < exceptionTargets.length(); i++) { + Vector *pending = getCode(exceptionTargets[i]).pendingValues; + for (unsigned i = 0; i < pending->length(); i++) { + const SlotValue &v = (*pending)[i]; + if (trackSlot(v.slot)) + mergeExceptionTarget(cx, values[v.slot], v.slot, exceptionTargets); + } + } +} + +bool +ScriptAnalysis::removeBranchTarget(Vector &branchTargets, + Vector &exceptionTargets, + uint32_t offset) +{ + bool exception = false; + for (unsigned i = 0; i < exceptionTargets.length(); i++) { + if (exceptionTargets[i] == offset) { + exceptionTargets[i] = branchTargets.back(); + exceptionTargets.popBack(); + exception = true; + break; + } + } + for (unsigned i = 0; i < branchTargets.length(); i++) { + if (branchTargets[i] == offset) { + branchTargets[i] = branchTargets.back(); + branchTargets.popBack(); + return exception; + } + } + JS_ASSERT(OOM()); + return exception; +} + +void +ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset) +{ + Bytecode &code = getCode(offset); + + Vector *pending = code.pendingValues; + code.pendingValues = NULL; + + unsigned count = pending->length(); + if (count == 0) { + cx->delete_(pending); + return; + } + + code.newValues = cx->typeLifoAlloc().newArray(count + 1); + if (!code.newValues) { + setOOM(cx); + return; + } + + for (unsigned i = 0; i < count; i++) + code.newValues[i] = (*pending)[i]; + code.newValues[count].slot = 0; + code.newValues[count].value.clear(); + + cx->delete_(pending); +} + +CrossSSAValue +CrossScriptSSA::foldValue(const CrossSSAValue &cv) +{ + const Frame &frame = getFrame(cv.frame); + const SSAValue &v = cv.v; + + JSScript *parentScript = NULL; + ScriptAnalysis *parentAnalysis = NULL; + if (frame.parent != INVALID_FRAME) { + parentScript = getFrame(frame.parent).script; + parentAnalysis = parentScript->analysis(); + } + + if (v.kind() == SSAValue::VAR && v.varInitial() && parentScript) { + uint32_t slot = v.varSlot(); + if (slot >= ArgSlot(0) && slot < LocalSlot(frame.script, 0)) { + uint32_t argc = GET_ARGC(frame.parentpc); + SSAValue argv = parentAnalysis->poppedValue(frame.parentpc, argc - 1 - (slot - ArgSlot(0))); + return foldValue(CrossSSAValue(frame.parent, argv)); + } + } + + if (v.kind() == SSAValue::PUSHED) { + jsbytecode *pc = frame.script->code + v.pushedOffset(); + + switch (JSOp(*pc)) { + case JSOP_THIS: + if (parentScript) { + uint32_t argc = GET_ARGC(frame.parentpc); + SSAValue thisv = parentAnalysis->poppedValue(frame.parentpc, argc); + return foldValue(CrossSSAValue(frame.parent, thisv)); + } + break; + + case JSOP_CALL: { + /* + * If there is a single inline callee with a single return site, + * propagate back to that. + */ + JSScript *callee = NULL; + uint32_t calleeFrame = INVALID_FRAME; + for (unsigned i = 0; i < numFrames(); i++) { + if (iterFrame(i).parent == cv.frame && iterFrame(i).parentpc == pc) { + if (callee) + return cv; /* Multiple callees */ + callee = iterFrame(i).script; + calleeFrame = iterFrame(i).index; + } + } + if (callee && callee->analysis()->numReturnSites() == 1) { + ScriptAnalysis *analysis = callee->analysis(); + uint32_t offset = 0; + while (offset < callee->length) { + jsbytecode *pc = callee->code + offset; + if (analysis->maybeCode(pc) && JSOp(*pc) == JSOP_RETURN) + return foldValue(CrossSSAValue(calleeFrame, analysis->poppedValue(pc, 0))); + offset += GetBytecodeLength(pc); + } + } + break; + } + + case JSOP_TOID: { + /* + * TOID acts as identity for integers, so to get better precision + * we should propagate its popped values forward if it acted as + * identity. + */ + ScriptAnalysis *analysis = frame.script->analysis(); + SSAValue toidv = analysis->poppedValue(pc, 0); + if (analysis->getValueTypes(toidv)->getKnownTypeTag(cx) == JSVAL_TYPE_INT32) + return foldValue(CrossSSAValue(cv.frame, toidv)); + break; + } + + default:; + } + } + + return cv; +} + +#ifdef DEBUG + +void +ScriptAnalysis::printSSA(JSContext *cx) +{ + AutoEnterAnalysis enter(cx); + + printf("\n"); + + for (unsigned offset = 0; offset < script->length; offset++) { + Bytecode *code = maybeCode(offset); + if (!code) + continue; + + jsbytecode *pc = script->code + offset; + + PrintBytecode(cx, script, pc); + + SlotValue *newv = code->newValues; + if (newv) { + while (newv->slot) { + if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) { + newv++; + continue; + } + printf(" phi "); + newv->value.print(); + printf(" ["); + for (unsigned i = 0; i < newv->value.phiLength(); i++) { + if (i) + printf(","); + newv->value.phiValue(i).print(); + } + printf("]\n"); + newv++; + } + } + + unsigned nuses = GetUseCount(script, offset); + unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses; + + for (unsigned i = 0; i < xuses; i++) { + printf(" popped%d: ", i); + code->poppedValues[i].print(); + printf("\n"); + } + } + + printf("\n"); +} + +void +SSAValue::print() const +{ + switch (kind()) { + + case EMPTY: + printf("empty"); + break; + + case PUSHED: + printf("pushed:%05u#%u", pushedOffset(), pushedIndex()); + break; + + case VAR: + if (varInitial()) + printf("initial:%u", varSlot()); + else + printf("write:%05u", varOffset()); + break; + + case PHI: + printf("phi:%05u#%u", phiOffset(), phiSlot()); + break; + + default: + JS_NOT_REACHED("Bad kind"); + } +} + +void +ScriptAnalysis::assertMatchingDebugMode() +{ + JS_ASSERT(!!script->compartment()->debugMode() == !!originalDebugMode_); +} + +#endif /* DEBUG */ + } /* namespace analyze */ } /* namespace js */ diff --git a/deps/mozjs/js/src/jsanalyze.h b/deps/mozjs/js/src/jsanalyze.h index de743029029..d85a01f737a 100644 --- a/deps/mozjs/js/src/jsanalyze.h +++ b/deps/mozjs/js/src/jsanalyze.h @@ -41,25 +41,78 @@ #ifndef jsanalyze_h___ #define jsanalyze_h___ -#include "jsarena.h" +#include "jscompartment.h" #include "jscntxt.h" +#include "jsinfer.h" #include "jsscript.h" +#include "ds/LifoAlloc.h" +#include "js/TemplateLib.h" + struct JSScript; +/* Forward declaration of downstream register allocations computed for join points. */ +namespace js { namespace mjit { struct RegisterAllocation; } } + namespace js { namespace analyze { -class Script; +/* + * There are three analyses we can perform on a JSScript, outlined below. + * The results of all three are stored in ScriptAnalysis, but the analyses + * themselves can be performed separately. Along with type inference results, + * per-script analysis results are tied to the per-compartment analysis pool + * and are freed on every garbage collection. + * + * - Basic bytecode analysis. For each bytecode, determine the stack depth at + * that point and control flow information needed for compilation. Also does + * a defined-variables analysis to look for local variables which have uses + * before definitions. + * + * - Lifetime analysis. Makes a backwards pass over the script to approximate + * the regions where each variable is live, avoiding a full fixpointing + * live-variables pass. This is based on the algorithm described in: + * + * "Quality and Speed in Linear-scan Register Allocation" + * Traub et. al. + * PLDI, 1998 + * + * - SSA analysis of the script's variables and stack values. For each stack + * value popped and non-escaping local variable or argument read, determines + * which push(es) or write(s) produced that value. + * + * Intermediate type inference results are additionally stored here. The above + * analyses are independent from type inference. + */ /* Information about a bytecode instruction. */ -struct Bytecode +class Bytecode { - friend class Script; + friend class ScriptAnalysis; + + public: + Bytecode() { PodZero(this); } + + /* --------- Bytecode analysis --------- */ /* Whether there are any incoming jumps to this instruction. */ bool jumpTarget : 1; + /* Whether there is fallthrough to this instruction from a non-branching instruction. */ + bool fallthrough : 1; + + /* Whether this instruction is the fall through point of a conditional jump. */ + bool jumpFallthrough : 1; + + /* Whether this instruction can be branched to from a switch statement. Implies jumpTarget. */ + bool switchTarget : 1; + + /* + * Whether this instruction must always execute, unless the script throws + * an exception which it does not later catch. + */ + bool unconditional : 1; + /* Whether this instruction has been analyzed to get its output defines and stack. */ bool analyzed : 1; @@ -69,129 +122,1050 @@ struct Bytecode /* Whether this is in a try block. */ bool inTryBlock : 1; - /* Whether this is a method JIT safe point. */ + /* Method JIT safe point. */ bool safePoint : 1; + /* + * Side effects of this bytecode were not determined by type inference. + * Either a property set with unknown lvalue, or call with unknown callee. + */ + bool monitoredTypes : 1; + + /* Call whose result should be monitored. */ + bool monitoredTypesReturn : 1; + + /* + * Dynamically observed state about the execution of this opcode. These are + * hints about the script for use during compilation. + */ + bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */ + bool getStringElement:1; /* GETELEM which has accessed string properties. */ + bool accessGetter: 1; /* Property read on a shape with a getter hook. */ + /* Stack depth before this opcode. */ - uint32 stackDepth; + uint32_t stackDepth; + + private: + + union { + /* If this is a JOF_TYPESET opcode, index into the observed types for the op. */ + types::TypeSet *observedTypes; + + /* If this is a loop head (TRACE or NOTRACE), information about the loop. */ + LoopAnalysis *loop; + }; + + /* --------- Lifetime analysis --------- */ + + /* Any allocation computed downstream for this bytecode. */ + mjit::RegisterAllocation *allocation; + + /* --------- SSA analysis --------- */ + + /* Generated location of each value popped by this bytecode. */ + SSAValue *poppedValues; + + /* Points where values pushed or written by this bytecode are popped. */ + SSAUseChain **pushedUses; + + union { + /* + * If this is a join point (implies jumpTarget), any slots at this + * point which can have a different values than at the immediate + * predecessor in the bytecode. Array is terminated by an entry with + * a zero slot. + */ + SlotValue *newValues; + + /* + * Vector used during SSA analysis to store values in need of merging + * at this point. If this has incoming forward jumps and we have not + * yet reached this point, stores values for entries on the stack and + * for variables which have changed since the branch. If this is a loop + * head and we haven't reached the back edge yet, stores loop phi nodes + * for variables and entries live at the head of the loop. + */ + Vector *pendingValues; + }; + + /* --------- Type inference --------- */ + + /* Types for all values pushed by this bytecode. */ + types::TypeSet *pushedTypes; + + /* Any type barriers in place at this bytecode. */ + types::TypeBarrier *typeBarriers; +}; + +static inline unsigned +GetDefCount(JSScript *script, unsigned offset) +{ + JS_ASSERT(offset < script->length); + jsbytecode *pc = script->code + offset; /* - * The set of locals defined at this point. This does not include locals which - * were unconditionally defined at an earlier point in the script. + * Add an extra pushed value for OR/AND opcodes, so that they are included + * in the pushed array of stack values for type inference. */ - uint32 defineCount; - uint32 *defineArray; + switch (JSOp(*pc)) { + case JSOP_OR: + case JSOP_AND: + return 1; + case JSOP_FILTER: + return 2; + case JSOP_PICK: + /* + * Pick pops and pushes how deep it looks in the stack + 1 + * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2 + * would pop b, c, and d to rearrange the stack to |a c[0] + * d[1] b[2]|. + */ + return (pc[1] + 1); + default: + return StackDefs(script, pc); + } +} - Bytecode() - { - PodZero(this); +static inline unsigned +GetUseCount(JSScript *script, unsigned offset) +{ + JS_ASSERT(offset < script->length); + jsbytecode *pc = script->code + offset; + + if (JSOp(*pc) == JSOP_PICK) + return (pc[1] + 1); + if (js_CodeSpec[*pc].nuses == -1) + return StackUses(script, pc); + return js_CodeSpec[*pc].nuses; +} + +/* + * For opcodes which assign to a local variable or argument, track an extra def + * during SSA analysis for the value's use chain and assigned type. + */ +static inline bool +ExtendedDef(jsbytecode *pc) +{ + switch ((JSOp)*pc) { + case JSOP_SETARG: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + return true; + default: + return false; } +} - private: - bool mergeDefines(JSContext *cx, - Script *script, bool initial, uint32 newDepth, - uint32 *newArray, uint32 newCount); +/* Return whether op bytecodes do not fallthrough (they may do a jump). */ +static inline bool +BytecodeNoFallThrough(JSOp op) +{ + switch (op) { + case JSOP_GOTO: + case JSOP_DEFAULT: + case JSOP_RETURN: + case JSOP_STOP: + case JSOP_RETRVAL: + case JSOP_THROW: + case JSOP_TABLESWITCH: + case JSOP_LOOKUPSWITCH: + case JSOP_FILTER: + return true; + case JSOP_GOSUB: + /* These fall through indirectly, after executing a 'finally'. */ + return false; + default: + return false; + } +} - /* Whether a local variable is in the define set at this bytecode. */ - bool isDefined(uint32 slot) - { - JS_ASSERT(analyzed); - for (size_t ind = 0; ind < defineCount; ind++) { - if (defineArray[ind] == slot) - return true; - } +/* + * For opcodes which access local variables or arguments, we track an extra + * use during SSA analysis for the value of the variable before/after the op. + */ +static inline bool +ExtendedUse(jsbytecode *pc) +{ + if (ExtendedDef(pc)) + return true; + switch ((JSOp)*pc) { + case JSOP_GETARG: + case JSOP_CALLARG: + case JSOP_GETLOCAL: + case JSOP_CALLLOCAL: + return true; + default: return false; } +} + +static inline JSOp +ReverseCompareOp(JSOp op) +{ + switch (op) { + case JSOP_GT: + return JSOP_LT; + case JSOP_GE: + return JSOP_LE; + case JSOP_LT: + return JSOP_GT; + case JSOP_LE: + return JSOP_GE; + default: + JS_NOT_REACHED("unrecognized op"); + return op; + } +} + +static inline unsigned +FollowBranch(JSContext *cx, JSScript *script, unsigned offset) +{ + /* + * Get the target offset of a branch. For GOTO opcodes implementing + * 'continue' statements, short circuit any artificial backwards jump + * inserted by the emitter. + */ + jsbytecode *pc = script->code + offset; + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc); + if (targetOffset < offset) { + jsbytecode *target = script->code + targetOffset; + JSOp nop = JSOp(*target); + if (nop == JSOP_GOTO) + return targetOffset + GET_JUMP_OFFSET(target); + } + return targetOffset; +} + +/* Common representation of slots throughout analyses and the compiler. */ +static inline uint32_t CalleeSlot() { + return 0; +} +static inline uint32_t ThisSlot() { + return 1; +} +static inline uint32_t ArgSlot(uint32_t arg) { + return 2 + arg; +} +static inline uint32_t LocalSlot(JSScript *script, uint32_t local) { + return 2 + (script->function() ? script->function()->nargs : 0) + local; +} +static inline uint32_t TotalSlots(JSScript *script) { + return LocalSlot(script, 0) + script->nfixed; +} + +static inline uint32_t StackSlot(JSScript *script, uint32_t index) { + return TotalSlots(script) + index; +} + +static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc) +{ + switch (JSOp(*pc)) { + + case JSOP_GETARG: + case JSOP_CALLARG: + case JSOP_SETARG: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + return ArgSlot(GET_SLOTNO(pc)); + + case JSOP_GETLOCAL: + case JSOP_CALLLOCAL: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + return LocalSlot(script, GET_SLOTNO(pc)); + + case JSOP_THIS: + return ThisSlot(); + + default: + JS_NOT_REACHED("Bad slot opcode"); + return 0; + } +} + +/* Slot opcodes which update SSA information. */ +static inline bool +BytecodeUpdatesSlot(JSOp op) +{ + switch (op) { + case JSOP_SETARG: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + return true; + default: + return false; + } +} + +static inline int32_t +GetBytecodeInteger(jsbytecode *pc) +{ + switch (JSOp(*pc)) { + case JSOP_ZERO: return 0; + case JSOP_ONE: return 1; + case JSOP_UINT16: return GET_UINT16(pc); + case JSOP_UINT24: return GET_UINT24(pc); + case JSOP_INT8: return GET_INT8(pc); + case JSOP_INT32: return GET_INT32(pc); + default: + JS_NOT_REACHED("Bad op"); + return 0; + } +} + +/* + * Information about the lifetime of a local or argument. These form a linked + * list describing successive intervals in the program where the variable's + * value may be live. At points in the script not in one of these segments + * (points in a 'lifetime hole'), the variable is dead and registers containing + * its type/payload can be discarded without needing to be synced. + */ +struct Lifetime +{ + /* + * Start and end offsets of this lifetime. The variable is live at the + * beginning of every bytecode in this (inclusive) range. + */ + uint32_t start; + uint32_t end; + + /* + * In a loop body, endpoint to extend this lifetime with if the variable is + * live in the next iteration. + */ + uint32_t savedEnd; + + /* + * This is an artificial segment extending the lifetime of this variable + * when it is live at the head of the loop. It will not be used until the + * next iteration. + */ + bool loopTail; + + /* + * The start of this lifetime is a bytecode writing the variable. Each + * write to a variable is associated with a lifetime. + */ + bool write; + + /* Next lifetime. The variable is dead from this->end to next->start. */ + Lifetime *next; + + Lifetime(uint32_t offset, uint32_t savedEnd, Lifetime *next) + : start(offset), end(offset), savedEnd(savedEnd), + loopTail(false), write(false), next(next) + {} +}; + +/* Basic information for a loop. */ +class LoopAnalysis +{ + public: + /* Any loop this one is nested in. */ + LoopAnalysis *parent; + + /* Offset of the head of the loop. */ + uint32_t head; + + /* + * Offset of the unique jump going to the head of the loop. The code + * between the head and the backedge forms the loop body. + */ + uint32_t backedge; + + /* Target offset of the initial jump or fallthrough into the loop. */ + uint32_t entry; + + /* + * Start of the last basic block in the loop, excluding the initial jump to + * entry. All code between lastBlock and the backedge runs in every + * iteration, and if entry >= lastBlock all code between entry and the + * backedge runs when the loop is initially entered. + */ + uint32_t lastBlock; + + /* + * This loop contains safe points in its body which the interpreter might + * join at directly. + */ + bool hasSafePoints; + + /* This loop has calls or inner loops. */ + bool hasCallsLoops; +}; + +/* Current lifetime information for a variable. */ +struct LifetimeVariable +{ + /* If the variable is currently live, the lifetime segment. */ + Lifetime *lifetime; + + /* If the variable is currently dead, the next live segment. */ + Lifetime *saved; + + /* Jump preceding the basic block which killed this variable. */ + uint32_t savedEnd : 31; + + /* If the variable needs to be kept alive until lifetime->start. */ + bool ensured : 1; + + /* Whether this variable is live at offset. */ + Lifetime * live(uint32_t offset) const { + if (lifetime && lifetime->end >= offset) + return lifetime; + Lifetime *segment = lifetime ? lifetime : saved; + while (segment && segment->start <= offset) { + if (segment->end >= offset) + return segment; + segment = segment->next; + } + return NULL; + } + + /* + * Get the offset of the first write to the variable in an inclusive range, + * UINT32_MAX if the variable is not written in the range. + */ + uint32_t firstWrite(uint32_t start, uint32_t end) const { + Lifetime *segment = lifetime ? lifetime : saved; + while (segment && segment->start <= end) { + if (segment->start >= start && segment->write) + return segment->start; + segment = segment->next; + } + return UINT32_MAX; + } + uint32_t firstWrite(LoopAnalysis *loop) const { + return firstWrite(loop->head, loop->backedge); + } + + /* Return true if the variable cannot decrease during the body of a loop. */ + bool nonDecreasing(JSScript *script, LoopAnalysis *loop) const { + Lifetime *segment = lifetime ? lifetime : saved; + while (segment && segment->start <= loop->backedge) { + if (segment->start >= loop->head && segment->write) { + switch (JSOp(script->code[segment->start])) { + case JSOP_INCLOCAL: + case JSOP_LOCALINC: + case JSOP_INCARG: + case JSOP_ARGINC: + break; + default: + return false; + } + } + segment = segment->next; + } + return true; + } + + /* + * If the variable is only written once in the body of a loop, offset of + * that write. UINT32_MAX otherwise. + */ + uint32_t onlyWrite(LoopAnalysis *loop) const { + uint32_t offset = UINT32_MAX; + Lifetime *segment = lifetime ? lifetime : saved; + while (segment && segment->start <= loop->backedge) { + if (segment->start >= loop->head && segment->write) { + if (offset != UINT32_MAX) + return UINT32_MAX; + offset = segment->start; + } + segment = segment->next; + } + return offset; + } + +#ifdef DEBUG + void print() const; +#endif }; -/* Information about a script. */ -class Script +struct SSAPhiNode; + +/* + * Representation of values on stack or in slots at each point in the script. + * Values are independent from the bytecode position, and mean the same thing + * everywhere in the script. SSA values are immutable, except for contents of + * the values and types in an SSAPhiNode. + */ +class SSAValue { - friend struct Bytecode; + friend class ScriptAnalysis; - JSScript *script; - Bytecode **code; + public: + enum Kind { + EMPTY = 0, /* Invalid entry. */ + PUSHED = 1, /* Value pushed by some bytecode. */ + VAR = 2, /* Initial or written value to some argument or local. */ + PHI = 3 /* Selector for one of several values. */ + }; + + Kind kind() const { + JS_ASSERT(u.pushed.kind == u.var.kind && u.pushed.kind == u.phi.kind); + + /* Use a bitmask because MSVC wants to use -1 for PHI nodes. */ + return (Kind) (u.pushed.kind & 0x3); + } + + bool operator==(const SSAValue &o) const { + return !memcmp(this, &o, sizeof(SSAValue)); + } + + /* Accessors for values pushed by a bytecode within this script. */ + + uint32_t pushedOffset() const { + JS_ASSERT(kind() == PUSHED); + return u.pushed.offset; + } + + uint32_t pushedIndex() const { + JS_ASSERT(kind() == PUSHED); + return u.pushed.index; + } - /* Maximum number of locals to consider for a script. */ - static const unsigned LOCAL_LIMIT = 50; + /* Accessors for initial and written values of arguments and (undefined) locals. */ - /* Offsets at which each local becomes unconditionally defined, or a value below. */ - uint32 *locals; + bool varInitial() const { + JS_ASSERT(kind() == VAR); + return u.var.initial; + } + + uint32_t varSlot() const { + JS_ASSERT(kind() == VAR); + return u.var.slot; + } + + uint32_t varOffset() const { + JS_ASSERT(!varInitial()); + return u.var.offset; + } - static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1); - static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2); + /* Accessors for phi nodes. */ + + uint32_t phiSlot() const; + uint32_t phiLength() const; + const SSAValue &phiValue(uint32_t i) const; + types::TypeSet *phiTypes() const; + + /* Offset at which this phi node was created. */ + uint32_t phiOffset() const { + JS_ASSERT(kind() == PHI); + return u.phi.offset; + } + + SSAPhiNode *phiNode() const { + JS_ASSERT(kind() == PHI); + return u.phi.node; + } + + /* Other accessors. */ + +#ifdef DEBUG + void print() const; +#endif + + void clear() { + PodZero(this); + JS_ASSERT(kind() == EMPTY); + } + + void initPushed(uint32_t offset, uint32_t index) { + clear(); + u.pushed.kind = PUSHED; + u.pushed.offset = offset; + u.pushed.index = index; + } + + static SSAValue PushedValue(uint32_t offset, uint32_t index) { + SSAValue v; + v.initPushed(offset, index); + return v; + } + + void initInitial(uint32_t slot) { + clear(); + u.var.kind = VAR; + u.var.initial = true; + u.var.slot = slot; + } + + void initWritten(uint32_t slot, uint32_t offset) { + clear(); + u.var.kind = VAR; + u.var.initial = false; + u.var.slot = slot; + u.var.offset = offset; + } + + static SSAValue WrittenVar(uint32_t slot, uint32_t offset) { + SSAValue v; + v.initWritten(slot, offset); + return v; + } + + void initPhi(uint32_t offset, SSAPhiNode *node) { + clear(); + u.phi.kind = PHI; + u.phi.offset = offset; + u.phi.node = node; + } + + static SSAValue PhiValue(uint32_t offset, SSAPhiNode *node) { + SSAValue v; + v.initPhi(offset, node); + return v; + } + + private: + union { + struct { + Kind kind : 2; + uint32_t offset : 30; + uint32_t index; + } pushed; + struct { + Kind kind : 2; + bool initial : 1; + uint32_t slot : 29; + uint32_t offset; + } var; + struct { + Kind kind : 2; + uint32_t offset : 30; + SSAPhiNode *node; + } phi; + } u; +}; + +/* + * Mutable component of a phi node, with the possible values of the phi + * and the possible types of the node as determined by type inference. + * When phi nodes are copied around, any updates to the original will + * be seen by all copies made. + */ +struct SSAPhiNode +{ + types::TypeSet types; + uint32_t slot; + uint32_t length; + SSAValue *options; + SSAUseChain *uses; + SSAPhiNode() { PodZero(this); } +}; + +inline uint32_t +SSAValue::phiSlot() const +{ + return u.phi.node->slot; +} + +inline uint32_t +SSAValue::phiLength() const +{ + JS_ASSERT(kind() == PHI); + return u.phi.node->length; +} + +inline const SSAValue & +SSAValue::phiValue(uint32_t i) const +{ + JS_ASSERT(kind() == PHI && i < phiLength()); + return u.phi.node->options[i]; +} + +inline types::TypeSet * +SSAValue::phiTypes() const +{ + JS_ASSERT(kind() == PHI); + return &u.phi.node->types; +} + +class SSAUseChain +{ + public: + bool popped : 1; + uint32_t offset : 31; + /* FIXME: Assert that only the proper arm of this union is accessed. */ + union { + uint32_t which; + SSAPhiNode *phi; + } u; + SSAUseChain *next; + + SSAUseChain() { PodZero(this); } +}; + +class SlotValue +{ + public: + uint32_t slot; + SSAValue value; + SlotValue(uint32_t slot, const SSAValue &value) : slot(slot), value(value) {} +}; + +/* Analysis information about a script. */ +class ScriptAnalysis +{ + friend class Bytecode; + + JSScript *script; + + Bytecode **codeArray; + + uint32_t numSlots; bool outOfMemory; bool hadFailure; - bool usesRval; - bool usesScope; + + JSPackedBool *escapedSlots; + + /* Which analyses have been performed. */ + bool ranBytecode_; + bool ranSSA_; + bool ranLifetimes_; + bool ranInference_; + +#ifdef DEBUG + /* Whether the compartment was in debug mode when we performed the analysis. */ + bool originalDebugMode_: 1; +#endif + + /* --------- Bytecode analysis --------- */ + + bool usesReturnValue_:1; + bool usesScopeChain_:1; + bool usesThisValue_:1; + bool hasFunctionCalls_:1; + bool modifiesArguments_:1; + bool extendsScope_:1; + bool addsScopeObjects_:1; + bool localsAliasStack_:1; + bool isInlineable:1; + bool canTrackVars:1; + + uint32_t numReturnSites_; + + /* --------- Lifetime analysis --------- */ + + LifetimeVariable *lifetimes; public: - /* Pool for allocating analysis structures which will not outlive this script. */ - JSArenaPool pool; - void analyze(JSContext *cx, JSScript *script); - void destroy(); + ScriptAnalysis(JSScript *script) { + PodZero(this); + this->script = script; +#ifdef DEBUG + this->originalDebugMode_ = script->compartment()->debugMode(); +#endif + } - /* - * For analysis scripts allocated on the stack. Scripts don't have constructors, - * and must be zeroed out before being used. - */ - ~Script() { destroy(); } + bool ranBytecode() { return ranBytecode_; } + bool ranSSA() { return ranSSA_; } + bool ranLifetimes() { return ranLifetimes_; } + bool ranInference() { return ranInference_; } - /* Whether we ran out of memory during analysis. */ - bool OOM() { return outOfMemory; } + void analyzeBytecode(JSContext *cx); + void analyzeSSA(JSContext *cx); + void analyzeLifetimes(JSContext *cx); + void analyzeTypes(JSContext *cx); + + /* Analyze the effect of invoking 'new' on script. */ + void analyzeTypesNew(JSContext *cx); - /* Whether the script was analyzed successfully. */ + bool OOM() { return outOfMemory; } bool failed() { return hadFailure; } + bool inlineable(uint32_t argc) { return isInlineable && argc == script->function()->nargs; } /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */ - bool usesReturnValue() const { return usesRval; } + bool usesReturnValue() const { return usesReturnValue_; } /* Whether there are NAME bytecodes which can access the frame's scope chain. */ - bool usesScopeChain() const { return usesScope; } + bool usesScopeChain() const { return usesScopeChain_; } + + bool usesThisValue() const { return usesThisValue_; } + bool hasFunctionCalls() const { return hasFunctionCalls_; } + uint32_t numReturnSites() const { return numReturnSites_; } + + /* + * True if all named formal arguments are not modified. If the arguments + * object cannot escape, the arguments are never modified within the script. + */ + bool modifiesArguments() { return modifiesArguments_; } + + /* + * True if the script may extend declarations in its top level scope with + * dynamic fun/var declarations or through eval. + */ + bool extendsScope() { return extendsScope_; } + + /* True if the script may add block or with objects to its scope chain. */ + bool addsScopeObjects() { return addsScopeObjects_; } + + /* + * True if there are any LOCAL opcodes aliasing values on the stack (above + * script->nfixed). + */ + bool localsAliasStack() { return localsAliasStack_; } /* Accessors for bytecode information. */ - Bytecode& getCode(uint32 offset) { + Bytecode& getCode(uint32_t offset) { JS_ASSERT(offset < script->length); - JS_ASSERT(code[offset]); - return *code[offset]; + JS_ASSERT(codeArray[offset]); + return *codeArray[offset]; } - Bytecode& getCode(jsbytecode *pc) { return getCode(pc - script->code); } + Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); } - Bytecode* maybeCode(uint32 offset) { + Bytecode* maybeCode(uint32_t offset) { JS_ASSERT(offset < script->length); - return code[offset]; + return codeArray[offset]; } - Bytecode* maybeCode(jsbytecode *pc) { return maybeCode(pc - script->code); } + Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); } - bool jumpTarget(uint32 offset) { + bool jumpTarget(uint32_t offset) { JS_ASSERT(offset < script->length); - return code[offset] && code[offset]->jumpTarget; + return codeArray[offset] && codeArray[offset]->jumpTarget; } - bool jumpTarget(jsbytecode *pc) { return jumpTarget(pc - script->code); } + bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); } - /* Accessors for local variable information. */ + bool popGuaranteed(jsbytecode *pc) { + jsbytecode *next = pc + GetBytecodeLength(pc); + return JSOp(*next) == JSOP_POP && !jumpTarget(next); + } - unsigned localCount() { - return (script->nfixed >= LOCAL_LIMIT) ? LOCAL_LIMIT : script->nfixed; + bool incrementInitialValueObserved(jsbytecode *pc) { + const JSCodeSpec *cs = &js_CodeSpec[*pc]; + return (cs->format & JOF_POST) && !popGuaranteed(pc); } - bool localHasUseBeforeDef(uint32 local) { - JS_ASSERT(local < script->nfixed && !failed()); - return local >= localCount() || locals[local] == LOCAL_USE_BEFORE_DEF; + types::TypeSet *bytecodeTypes(const jsbytecode *pc) { + JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); + return getCode(pc).observedTypes; } - /* These return true for variables that may have a use before def. */ - bool localDefined(uint32 local, uint32 offset) { - return localHasUseBeforeDef(local) || (locals[local] <= offset) || - getCode(offset).isDefined(local); + const SSAValue &poppedValue(uint32_t offset, uint32_t which) { + JS_ASSERT(offset < script->length); + JS_ASSERT(which < GetUseCount(script, offset) + + (ExtendedUse(script->code + offset) ? 1 : 0)); + return getCode(offset).poppedValues[which]; } - bool localDefined(uint32 local, jsbytecode *pc) { - return localDefined(local, pc - script->code); + const SSAValue &poppedValue(const jsbytecode *pc, uint32_t which) { + return poppedValue(pc - script->code, which); + } + + const SlotValue *newValues(uint32_t offset) { + JS_ASSERT(offset < script->length); + return getCode(offset).newValues; + } + const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script->code); } + + types::TypeSet *pushedTypes(uint32_t offset, uint32_t which = 0) { + JS_ASSERT(offset < script->length); + JS_ASSERT(which < GetDefCount(script, offset) + + (ExtendedDef(script->code + offset) ? 1 : 0)); + types::TypeSet *array = getCode(offset).pushedTypes; + JS_ASSERT(array); + return array + which; + } + types::TypeSet *pushedTypes(const jsbytecode *pc, uint32_t which) { + return pushedTypes(pc - script->code, which); + } + + bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; } + + types::TypeBarrier *typeBarriers(JSContext *cx, uint32_t offset) { + if (getCode(offset).typeBarriers) + pruneTypeBarriers(cx, offset); + return getCode(offset).typeBarriers; + } + types::TypeBarrier *typeBarriers(JSContext *cx, const jsbytecode *pc) { + return typeBarriers(cx, pc - script->code); + } + void addTypeBarrier(JSContext *cx, const jsbytecode *pc, + types::TypeSet *target, types::Type type); + void addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, + types::TypeSet *target, JSObject *singleton, jsid singletonId); + + /* Remove obsolete type barriers at the given offset. */ + void pruneTypeBarriers(JSContext *cx, uint32_t offset); + + /* + * Remove still-active type barriers at the given offset. If 'all' is set, + * then all barriers are removed, otherwise only those deemed excessive + * are removed. + */ + void breakTypeBarriers(JSContext *cx, uint32_t offset, bool all); + + /* Break all type barriers used in computing v. */ + void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v); + + inline void addPushedType(JSContext *cx, uint32_t offset, uint32_t which, types::Type type); + + types::TypeSet *getValueTypes(const SSAValue &v) { + switch (v.kind()) { + case SSAValue::PUSHED: + return pushedTypes(v.pushedOffset(), v.pushedIndex()); + case SSAValue::VAR: + JS_ASSERT(!slotEscapes(v.varSlot())); + if (v.varInitial()) { + return types::TypeScript::SlotTypes(script, v.varSlot()); + } else { + /* + * Results of intermediate assignments have the same type as + * the first type pushed by the assignment op. Note that this + * may not be the exact same value as was pushed, due to + * post-inc/dec ops. + */ + return pushedTypes(v.varOffset(), 0); + } + case SSAValue::PHI: + return &v.phiNode()->types; + default: + /* Cannot compute types for empty SSA values. */ + JS_NOT_REACHED("Bad SSA value"); + return NULL; + } + } + + types::TypeSet *poppedTypes(uint32_t offset, uint32_t which) { + return getValueTypes(poppedValue(offset, which)); + } + types::TypeSet *poppedTypes(const jsbytecode *pc, uint32_t which) { + return getValueTypes(poppedValue(pc, which)); + } + + /* Whether an arithmetic operation is operating on integers, with an integer result. */ + bool integerOperation(JSContext *cx, jsbytecode *pc); + + bool trackUseChain(const SSAValue &v) { + JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot())); + return v.kind() != SSAValue::EMPTY && + (v.kind() != SSAValue::VAR || !v.varInitial()); } + /* + * Get the use chain for an SSA value. May be invalid for some opcodes in + * scripts where localsAliasStack(). You have been warned! + */ + SSAUseChain *& useChain(const SSAValue &v) { + JS_ASSERT(trackUseChain(v)); + if (v.kind() == SSAValue::PUSHED) + return getCode(v.pushedOffset()).pushedUses[v.pushedIndex()]; + if (v.kind() == SSAValue::VAR) + return getCode(v.varOffset()).pushedUses[GetDefCount(script, v.varOffset())]; + return v.phiNode()->uses; + } + + mjit::RegisterAllocation *&getAllocation(uint32_t offset) { + JS_ASSERT(offset < script->length); + return getCode(offset).allocation; + } + mjit::RegisterAllocation *&getAllocation(const jsbytecode *pc) { + return getAllocation(pc - script->code); + } + + LoopAnalysis *getLoop(uint32_t offset) { + JS_ASSERT(offset < script->length); + return getCode(offset).loop; + } + LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); } + + /* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */ + jsbytecode *getCallPC(jsbytecode *pc) + { + SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 0)); + JS_ASSERT(uses && uses->popped); + JS_ASSERT_IF(uses->next, + !uses->next->next && + uses->next->popped && + script->code[uses->next->offset] == JSOP_SWAP); + return script->code + uses->offset; + } + + /* Accessors for local variable information. */ + + /* + * Escaping slots include all slots that can be accessed in ways other than + * through the corresponding LOCAL/ARG opcode. This includes all closed + * slots in the script, all slots in scripts which use eval or are in debug + * mode, and slots which are aliased by NAME or similar opcodes in the + * containing script (which does not imply the variable is closed). + */ + bool slotEscapes(uint32_t slot) { + JS_ASSERT(script->compartment()->activeAnalysis); + if (slot >= numSlots) + return true; + return escapedSlots[slot]; + } + + /* + * Whether we distinguish different writes of this variable while doing + * SSA analysis. Escaping locals can be written in other scripts, and the + * presence of NAME opcodes which could alias local variables or arguments + * keeps us from tracking variable values at each point. + */ + bool trackSlot(uint32_t slot) { return !slotEscapes(slot) && canTrackVars; } + + const LifetimeVariable & liveness(uint32_t slot) { + JS_ASSERT(script->compartment()->activeAnalysis); + JS_ASSERT(!slotEscapes(slot)); + return lifetimes[slot]; + } + + /* + * If a NAME or similar opcode is definitely accessing a particular slot + * of a script this one is nested in, get that script/slot. + */ + struct NameAccess { + JSScript *script; + types::TypeScriptNesting *nesting; + uint32_t slot; + + /* Decompose the slot above. */ + bool arg; + uint32_t index; + + const Value **basePointer() const { + return arg ? &nesting->argArray : &nesting->varArray; + } + }; + NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false); + + void printSSA(JSContext *cx); + void printTypes(JSContext *cx); + + void clearAllocations(); + private: void setOOM(JSContext *cx) { if (!outOfMemory) @@ -200,14 +1174,194 @@ class Script hadFailure = true; } + /* Bytecode helpers */ inline bool addJump(JSContext *cx, unsigned offset, unsigned *currentOffset, unsigned *forwardJump, - unsigned stackDepth, uint32 *defineArray, unsigned defineCount); + unsigned stackDepth); + void checkAliasedName(JSContext *cx, jsbytecode *pc); + + /* Lifetime helpers */ + inline void addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, + LifetimeVariable **&saved, unsigned &savedCount); + inline void killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, + LifetimeVariable **&saved, unsigned &savedCount); + inline void extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end); + inline void ensureVariable(LifetimeVariable &var, unsigned until); + + /* SSA helpers */ + bool makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv); + void insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v); + void mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv); + void checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot, + Vector *pending); + void checkBranchTarget(JSContext *cx, uint32_t targetOffset, Vector &branchTargets, + SSAValue *values, uint32_t stackDepth); + void checkExceptionTarget(JSContext *cx, uint32_t catchOffset, + Vector &exceptionTargets); + void mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32_t slot, + const Vector &branchTargets); + void mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot, + const Vector &exceptionTargets); + void mergeAllExceptionTargets(JSContext *cx, SSAValue *values, + const Vector &exceptionTargets); + bool removeBranchTarget(Vector &branchTargets, + Vector &exceptionTargets, + uint32_t offset); + void freezeNewValues(JSContext *cx, uint32_t offset); + + struct TypeInferenceState { + Vector phiNodes; + bool hasGetSet; + bool hasHole; + types::TypeSet *forTypes; + TypeInferenceState(JSContext *cx) + : phiNodes(cx), hasGetSet(false), hasHole(false), forTypes(NULL) + {} + }; + + /* Type inference helpers */ + bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state); + bool followEscapingArguments(JSContext *cx, const SSAValue &v, Vector *seen); + bool followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector *seen); + + public: +#ifdef DEBUG + void assertMatchingDebugMode(); +#else + void assertMatchingDebugMode() { } +#endif +}; + +/* Protect analysis structures from GC while they are being used. */ +class AutoEnterAnalysis +{ + JSCompartment *compartment; + bool oldActiveAnalysis; + bool left; + + void construct(JSCompartment *compartment) + { + this->compartment = compartment; + oldActiveAnalysis = compartment->activeAnalysis; + compartment->activeAnalysis = true; + left = false; + } + + public: + AutoEnterAnalysis(JSContext *cx) { construct(cx->compartment); } + AutoEnterAnalysis(JSCompartment *compartment) { construct(compartment); } + + void leave() + { + if (!left) { + left = true; + compartment->activeAnalysis = oldActiveAnalysis; + } + } + + ~AutoEnterAnalysis() + { + leave(); + } +}; - inline void setLocal(uint32 local, uint32 offset); +/* SSA value as used by CrossScriptSSA, identifies the frame it came from. */ +struct CrossSSAValue +{ + unsigned frame; + SSAValue v; + CrossSSAValue(unsigned frame, const SSAValue &v) : frame(frame), v(v) {} }; +/* + * Analysis for managing SSA values from multiple call stack frames. These are + * created by the backend compiler when inlining functions, and allow for + * values to be tracked as they flow into or out of the inlined frames. + */ +class CrossScriptSSA +{ + public: + + static const uint32_t OUTER_FRAME = UINT32_MAX; + static const unsigned INVALID_FRAME = uint32_t(-2); + + struct Frame { + uint32_t index; + JSScript *script; + uint32_t depth; /* Distance from outer frame to this frame, in sizeof(Value) */ + uint32_t parent; + jsbytecode *parentpc; + + Frame(uint32_t index, JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc) + : index(index), script(script), depth(depth), parent(parent), parentpc(parentpc) + {} + }; + + const Frame &getFrame(uint32_t index) { + if (index == OUTER_FRAME) + return outerFrame; + return inlineFrames[index]; + } + + unsigned numFrames() { return 1 + inlineFrames.length(); } + const Frame &iterFrame(unsigned i) { + if (i == 0) + return outerFrame; + return inlineFrames[i - 1]; + } + + JSScript *outerScript() { return outerFrame.script; } + + /* Total length of scripts preceding a frame. */ + size_t frameLength(uint32_t index) { + if (index == OUTER_FRAME) + return 0; + size_t res = outerFrame.script->length; + for (unsigned i = 0; i < index; i++) + res += inlineFrames[i].script->length; + return res; + } + + types::TypeSet *getValueTypes(const CrossSSAValue &cv) { + return getFrame(cv.frame).script->analysis()->getValueTypes(cv.v); + } + + bool addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc) + { + uint32_t index = inlineFrames.length(); + return inlineFrames.append(Frame(index, script, depth, parent, parentpc)); + } + + CrossScriptSSA(JSContext *cx, JSScript *outer) + : cx(cx), outerFrame(OUTER_FRAME, outer, 0, INVALID_FRAME, NULL), inlineFrames(cx) + {} + + CrossSSAValue foldValue(const CrossSSAValue &cv); + + private: + JSContext *cx; + + Frame outerFrame; + Vector inlineFrames; +}; + +#ifdef DEBUG +void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc); +#endif + } /* namespace analyze */ } /* namespace js */ +namespace js { +namespace tl { + +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; + +} /* namespace tl */ +} /* namespace js */ + #endif // jsanalyze_h___ diff --git a/deps/mozjs/js/src/jsapi-tests/Makefile.in b/deps/mozjs/js/src/jsapi-tests/Makefile.in index 04c78342040..b871ff7a9a2 100644 --- a/deps/mozjs/js/src/jsapi-tests/Makefile.in +++ b/deps/mozjs/js/src/jsapi-tests/Makefile.in @@ -49,41 +49,53 @@ PROGRAM = jsapi-tests$(BIN_SUFFIX) CPPSRCS = \ tests.cpp \ selfTest.cpp \ + testAddPropertyPropcache.cpp \ + testArgumentsObject.cpp \ testBug604087.cpp \ + testChromeBuffer.cpp \ testClassGetter.cpp \ testCloneScript.cpp \ testConservativeGC.cpp \ testContexts.cpp \ + testCustomIterator.cpp \ testDebugger.cpp \ testDeepFreeze.cpp \ testDefineGetterSetterNonEnumerable.cpp \ testDefineProperty.cpp \ testExtendedEq.cpp \ + testExternalStrings.cpp \ testFuncCallback.cpp \ - testGCChunkAlloc.cpp \ + testFunctionProperties.cpp \ + testGCOutOfMemory.cpp \ testGetPropertyDefault.cpp \ + testIndexToString.cpp \ testIntString.cpp \ + testIntTypesABI.cpp \ + testIntern.cpp \ testLookup.cpp \ testLooselyEqual.cpp \ testNewObject.cpp \ testOps.cpp \ + testOriginPrincipals.cpp \ testParseJSON.cpp \ testPropCache.cpp \ + testRegExp.cpp \ testResolveRecursion.cpp \ testSameValue.cpp \ + testScriptInfo.cpp \ testScriptObject.cpp \ testSetProperty.cpp \ testStringBuffer.cpp \ - testThreadGC.cpp \ - testThreads.cpp \ testTrap.cpp \ testUTF8.cpp \ + testValueABI.cpp \ testVersion.cpp \ testXDR.cpp \ - testCustomIterator.cpp \ - testExternalStrings.cpp \ $(NULL) +CSRCS = \ + valueABI.c + # Disabled: an entirely unrelated test seems to cause this to fail. Moreover, # given the test's dependence on interactions between the compiler, the GC, and # conservative stack scanning, the fix isn't obvious: more investigation @@ -93,12 +105,9 @@ CPPSRCS = \ # $(NULL) DEFINES += -DEXPORT_JS_API - -# Some platforms that have stdint.h include it in system headers. So -# to reliably get limit macros defined, we'd always have to define the -# one below before including any header, but that's obscure and -# fragile, so we do it here. -DEFINES += -D__STDC_LIMIT_MACROS +# Building against js_static requires that we declare mfbt sybols "exported" +# on its behalf. +DEFINES += -DIMPL_MFBT LIBS = $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) $(NSPR_LIBS) diff --git a/deps/mozjs/js/src/jsapi-tests/selfTest.cpp b/deps/mozjs/js/src/jsapi-tests/selfTest.cpp index 4ae7629be38..fd28b0d5877 100644 --- a/deps/mozjs/js/src/jsapi-tests/selfTest.cpp +++ b/deps/mozjs/js/src/jsapi-tests/selfTest.cpp @@ -18,7 +18,7 @@ END_TEST(selfTest_NaNsAreSame) BEGIN_TEST(selfTest_globalHasNoParent) { - CHECK(JS_GetParent(cx, global) == NULL); + CHECK(JS_GetParent(global) == NULL); return true; } END_TEST(selfTest_globalHasNoParent) diff --git a/deps/mozjs/js/src/jsapi-tests/testAddPropertyPropcache.cpp b/deps/mozjs/js/src/jsapi-tests/testAddPropertyPropcache.cpp new file mode 100644 index 00000000000..b30437baebe --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testAddPropertyPropcache.cpp @@ -0,0 +1,72 @@ +/* -*- 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" +#include "jsxdrapi.h" + +/* Do the test a bunch of times, because sometimes we seem to randomly + miss the propcache */ +static const int expectedCount = 100; +static int callCount = 0; + +static JSBool +addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + callCount++; + return true; +} + +JSClass addPropertyClass = { + "AddPropertyTester", + 0, + addProperty, + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + +BEGIN_TEST(testAddPropertyHook) +{ + jsvalRoot proto(cx); + JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); + CHECK(obj); + proto = OBJECT_TO_JSVAL(obj); + JS_InitClass(cx, global, obj, &addPropertyClass, NULL, 0, NULL, NULL, NULL, + NULL); + + jsvalRoot arr(cx); + obj = JS_NewArrayObject(cx, 0, NULL); + CHECK(obj); + arr = OBJECT_TO_JSVAL(obj); + + CHECK(JS_DefineProperty(cx, global, "arr", arr, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE)); + + for (int i = 0; i < expectedCount; ++i) { + jsvalRoot vobj(cx); + obj = JS_NewObject(cx, &addPropertyClass, NULL, NULL); + CHECK(obj); + vobj = OBJECT_TO_JSVAL(obj); + CHECK(JS_DefineElement(cx, JSVAL_TO_OBJECT(arr), i, vobj, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE)); + } + + // Now add a prop to each of the objects, but make sure to do + // so at the same bytecode location so we can hit the propcache. + EXEC("'use strict'; \n" + "for (var i = 0; i < arr.length; ++i) \n" + " arr[i].prop = 42; \n" + ); + + CHECK(callCount == expectedCount); + + return true; +} +END_TEST(testAddPropertyHook) + diff --git a/deps/mozjs/js/src/jsapi-tests/testArgumentsObject.cpp b/deps/mozjs/js/src/jsapi-tests/testArgumentsObject.cpp new file mode 100644 index 00000000000..64968bb969b --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testArgumentsObject.cpp @@ -0,0 +1,107 @@ +/* -*- 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" + +#include "vm/Stack-inl.h" + +using namespace js; + +static const char NORMAL_ZERO[] = + "function f() { return arguments; }"; +static const char NORMAL_ONE[] = + "function f(a) { return arguments; }"; +static const char NORMAL_TWO[] = + "function f(a, b) { return arguments; }"; +static const char NORMAL_THREE[] = + "function f(a, b, c) { return arguments; }"; + +static const char STRICT_ZERO[] = + "function f() { 'use strict'; return arguments; }"; +static const char STRICT_ONE[] = + "function f() { 'use strict'; return arguments; }"; +static const char STRICT_TWO[] = + "function f() { 'use strict'; return arguments; }"; +static const char STRICT_THREE[] = + "function f() { 'use strict'; return arguments; }"; + +static const char *CALL_CODES[] = + { "f()", "f(0)", "f(0, 1)", "f(0, 1, 2)", "f(0, 1, 2, 3)", "f(0, 1, 2, 3, 4)" }; + +static const size_t MAX_ELEMS = 6; + +static void +ClearElements(Value elems[MAX_ELEMS]) +{ + for (size_t i = 0; i < MAX_ELEMS - 1; i++) + elems[i] = NullValue(); + elems[MAX_ELEMS - 1] = Int32Value(42); +} + +BEGIN_TEST(testArgumentsObject) +{ + return ExhaustiveTest<0>(NORMAL_ZERO) && + ExhaustiveTest<1>(NORMAL_ZERO) && + ExhaustiveTest<2>(NORMAL_ZERO) && + ExhaustiveTest<0>(NORMAL_ONE) && + ExhaustiveTest<1>(NORMAL_ONE) && + ExhaustiveTest<2>(NORMAL_ONE) && + ExhaustiveTest<3>(NORMAL_ONE) && + ExhaustiveTest<0>(NORMAL_TWO) && + ExhaustiveTest<1>(NORMAL_TWO) && + ExhaustiveTest<2>(NORMAL_TWO) && + ExhaustiveTest<3>(NORMAL_TWO) && + ExhaustiveTest<4>(NORMAL_TWO) && + ExhaustiveTest<0>(NORMAL_THREE) && + ExhaustiveTest<1>(NORMAL_THREE) && + ExhaustiveTest<2>(NORMAL_THREE) && + ExhaustiveTest<3>(NORMAL_THREE) && + ExhaustiveTest<4>(NORMAL_THREE) && + ExhaustiveTest<5>(NORMAL_THREE) && + ExhaustiveTest<0>(STRICT_ZERO) && + ExhaustiveTest<1>(STRICT_ZERO) && + ExhaustiveTest<2>(STRICT_ZERO) && + ExhaustiveTest<0>(STRICT_ONE) && + ExhaustiveTest<1>(STRICT_ONE) && + ExhaustiveTest<2>(STRICT_ONE) && + ExhaustiveTest<3>(STRICT_ONE) && + ExhaustiveTest<0>(STRICT_TWO) && + ExhaustiveTest<1>(STRICT_TWO) && + ExhaustiveTest<2>(STRICT_TWO) && + ExhaustiveTest<3>(STRICT_TWO) && + ExhaustiveTest<4>(STRICT_TWO) && + ExhaustiveTest<0>(STRICT_THREE) && + ExhaustiveTest<1>(STRICT_THREE) && + ExhaustiveTest<2>(STRICT_THREE) && + ExhaustiveTest<3>(STRICT_THREE) && + ExhaustiveTest<4>(STRICT_THREE) && + ExhaustiveTest<5>(STRICT_THREE); +} + +template bool +ExhaustiveTest(const char funcode[]) +{ + jsval v; + EVAL(funcode, &v); + + EVAL(CALL_CODES[ArgCount], &v); + ArgumentsObject &argsobj = JSVAL_TO_OBJECT(v)->asArguments(); + + Value elems[MAX_ELEMS]; + + for (size_t i = 0; i <= ArgCount; i++) { + for (size_t j = 0; j <= ArgCount - i; j++) { + ClearElements(elems); + CHECK(argsobj.getElements(i, j, elems)); + for (size_t k = 0; k < j; k++) + CHECK_SAME(elems[k], INT_TO_JSVAL(i + k)); + for (size_t k = j; k < MAX_ELEMS - 1; k++) + CHECK_SAME(elems[k], JSVAL_NULL); + CHECK_SAME(elems[MAX_ELEMS - 1], INT_TO_JSVAL(42)); + } + } + + return true; +} +END_TEST(testArgumentsObject) diff --git a/deps/mozjs/js/src/jsapi-tests/testBug604087.cpp b/deps/mozjs/js/src/jsapi-tests/testBug604087.cpp index 402c362a81e..27b7e0b54aa 100644 --- a/deps/mozjs/js/src/jsapi-tests/testBug604087.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testBug604087.cpp @@ -5,11 +5,12 @@ */ #include "tests.h" +#include "jsobj.h" #include "jswrapper.h" -struct OuterWrapper : JSWrapper +struct OuterWrapper : js::Wrapper { - OuterWrapper() : JSWrapper(0) {} + OuterWrapper() : Wrapper(0) {} virtual bool isOuterWindow() { return true; @@ -44,36 +45,36 @@ PreWrap(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) static JSObject * Wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, uintN flags) { - return JSWrapper::New(cx, obj, proto, parent, &JSCrossCompartmentWrapper::singleton); + return js::Wrapper::New(cx, obj, proto, parent, &js::CrossCompartmentWrapper::singleton); } BEGIN_TEST(testBug604087) { - JSObject *outerObj = JSWrapper::New(cx, global, global->getProto(), global, - &OuterWrapper::singleton); + JSObject *outerObj = js::Wrapper::New(cx, global, global->getProto(), global, + &OuterWrapper::singleton); JSObject *compartment2 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); JSObject *compartment3 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); JSObject *compartment4 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); JSObject *c2wrapper = wrap(cx, outerObj, compartment2); CHECK(c2wrapper); - c2wrapper->setProxyExtra(js::Int32Value(2)); + js::SetProxyExtra(c2wrapper, 0, js::Int32Value(2)); JSObject *c3wrapper = wrap(cx, outerObj, compartment3); CHECK(c3wrapper); - c3wrapper->setProxyExtra(js::Int32Value(3)); + js::SetProxyExtra(c3wrapper, 0, js::Int32Value(3)); JSObject *c4wrapper = wrap(cx, outerObj, compartment4); CHECK(c4wrapper); - c4wrapper->setProxyExtra(js::Int32Value(4)); + js::SetProxyExtra(c4wrapper, 0, js::Int32Value(4)); compartment4 = c4wrapper = NULL; JSObject *next; { JSAutoEnterCompartment ac; CHECK(ac.enter(cx, compartment2)); - next = JSWrapper::New(cx, compartment2, compartment2->getProto(), compartment2, - &OuterWrapper::singleton); + next = js::Wrapper::New(cx, compartment2, compartment2->getProto(), compartment2, + &OuterWrapper::singleton); CHECK(next); } diff --git a/deps/mozjs/js/src/jsapi-tests/testChromeBuffer.cpp b/deps/mozjs/js/src/jsapi-tests/testChromeBuffer.cpp new file mode 100644 index 00000000000..f221365334a --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testChromeBuffer.cpp @@ -0,0 +1,170 @@ +#include "tests.h" + +static void +Destroy(JSContext *cx, JSPrincipals *prin); + +JSPrincipals system_principals = { + (char *)"", 1, Destroy, NULL +}; + +static void +Destroy(JSContext *cx, JSPrincipals *prin) +{ + JS_ASSERT(prin == &system_principals); +} + +JSClass global_class = { + "global", + JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, + JS_PropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JS::Anchor trusted_glob, trusted_fun; + +JSBool +CallTrusted(JSContext *cx, uintN argc, jsval *vp) +{ + if (!JS_SaveFrameChain(cx)) + return JS_FALSE; + + JSBool ok = JS_FALSE; + { + JSAutoEnterCompartment ac; + ok = ac.enter(cx, trusted_glob.get()); + if (!ok) + goto out; + ok = JS_CallFunctionValue(cx, NULL, OBJECT_TO_JSVAL(trusted_fun.get()), + 0, NULL, vp); + } + out: + JS_RestoreFrameChain(cx); + return ok; +} + +BEGIN_TEST(testChromeBuffer) +{ + JS_SetTrustedPrincipals(rt, &system_principals); + + JSFunction *fun; + JSObject *o; + + CHECK(o = JS_NewCompartmentAndGlobalObject(cx, &global_class, &system_principals)); + trusted_glob.set(o); + + /* + * Check that, even after untrusted content has exhausted the stack, code + * compiled with "trusted principals" can run using reserved trusted-only + * buffer space. + */ + { + { + JSAutoEnterCompartment ac; + CHECK(ac.enter(cx, trusted_glob.get())); + const char *paramName = "x"; + const char *bytes = "return x ? 1 + trusted(x-1) : 0"; + CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals, + "trusted", 1, ¶mName, bytes, strlen(bytes), + "", 0)); + trusted_fun.set(JS_GetFunctionObject(fun)); + } + + jsval v = OBJECT_TO_JSVAL(trusted_fun.get()); + CHECK(JS_WrapValue(cx, &v)); + + const char *paramName = "trusted"; + const char *bytes = "try { " + " return untrusted(trusted); " + "} catch (e) { " + " return trusted(100); " + "} "; + CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, + bytes, strlen(bytes), "", 0)); + + jsval rval; + CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval)); + CHECK(JSVAL_TO_INT(rval) == 100); + } + + /* + * Check that content called from chrome in the reserved-buffer space + * immediately ooms. + */ + { + { + JSAutoEnterCompartment ac; + CHECK(ac.enter(cx, trusted_glob.get())); + const char *paramName = "untrusted"; + const char *bytes = "try { " + " untrusted(); " + "} catch (e) { " + " return 'From trusted: ' + e; " + "} "; + CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals, + "trusted", 1, ¶mName, bytes, strlen(bytes), + "", 0)); + trusted_fun.set(JS_GetFunctionObject(fun)); + } + + jsval v = OBJECT_TO_JSVAL(trusted_fun.get()); + CHECK(JS_WrapValue(cx, &v)); + + const char *paramName = "trusted"; + const char *bytes = "try { " + " return untrusted(trusted); " + "} catch (e) { " + " return trusted(untrusted); " + "} "; + CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, + bytes, strlen(bytes), "", 0)); + + jsval rval; + CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval)); + JSBool match; + CHECK(JS_StringEqualsAscii(cx, JSVAL_TO_STRING(rval), "From trusted: InternalError: too much recursion", &match)); + CHECK(match); + } + + /* + * Check that JS_SaveFrameChain called on the way from content to chrome + * (say, as done by XPCJSContextSTack::Push) works. + */ + { + { + JSAutoEnterCompartment ac; + CHECK(ac.enter(cx, trusted_glob.get())); + const char *bytes = "return 42"; + CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals, + "trusted", 0, NULL, bytes, strlen(bytes), + "", 0)); + trusted_fun.set(JS_GetFunctionObject(fun)); + } + + JSFunction *fun = JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted"); + JS::Anchor callTrusted(JS_GetFunctionObject(fun)); + + const char *paramName = "f"; + const char *bytes = "try { " + " return untrusted(trusted); " + "} catch (e) { " + " return f(); " + "} "; + CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, + bytes, strlen(bytes), "", 0)); + + jsval arg = OBJECT_TO_JSVAL(callTrusted.get()); + jsval rval; + CHECK(JS_CallFunction(cx, NULL, fun, 1, &arg, &rval)); + CHECK(JSVAL_TO_INT(rval) == 42); + } + + return true; +} +END_TEST(testChromeBuffer) diff --git a/deps/mozjs/js/src/jsapi-tests/testConservativeGC.cpp b/deps/mozjs/js/src/jsapi-tests/testConservativeGC.cpp index 47aa05996bd..726aae766af 100644 --- a/deps/mozjs/js/src/jsapi-tests/testConservativeGC.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testConservativeGC.cpp @@ -1,13 +1,14 @@ #include "tests.h" #include "jsobj.h" -#include "jsstr.h" +#include "vm/String.h" BEGIN_TEST(testConservativeGC) { jsval v2; EVAL("({foo: 'bar'});", &v2); CHECK(JSVAL_IS_OBJECT(v2)); - JSObject objCopy = *JSVAL_TO_OBJECT(v2); + char objCopy[sizeof(JSObject)]; + js_memcpy(&objCopy, JSVAL_TO_OBJECT(v2), sizeof(JSObject)); jsval v3; EVAL("String(Math.PI);", &v3); @@ -18,7 +19,8 @@ BEGIN_TEST(testConservativeGC) EVAL("({foo2: 'bar2'});", &tmp); CHECK(JSVAL_IS_OBJECT(tmp)); JSObject *obj2 = JSVAL_TO_OBJECT(tmp); - JSObject obj2Copy = *obj2; + char obj2Copy[sizeof(JSObject)]; + js_memcpy(&obj2Copy, obj2, sizeof(JSObject)); EVAL("String(Math.sqrt(3));", &tmp); CHECK(JSVAL_IS_STRING(tmp)); @@ -36,10 +38,10 @@ BEGIN_TEST(testConservativeGC) JS_GC(cx); - checkObjectFields(&objCopy, JSVAL_TO_OBJECT(v2)); + checkObjectFields((JSObject *)objCopy, JSVAL_TO_OBJECT(v2)); CHECK(!memcmp(&strCopy, JSVAL_TO_STRING(v3), sizeof(strCopy))); - checkObjectFields(&obj2Copy, obj2); + checkObjectFields((JSObject *)obj2Copy, obj2); CHECK(!memcmp(&str2Copy, str2, sizeof(str2Copy))); return true; @@ -47,13 +49,9 @@ BEGIN_TEST(testConservativeGC) bool checkObjectFields(JSObject *savedCopy, JSObject *obj) { - /* - * The GC can change the shape and shrink dslots so we update them before - * doing memcmp. - */ - savedCopy->objShape = obj->objShape; - savedCopy->slots = obj->slots; - CHECK(!memcmp(savedCopy, obj, sizeof(*obj))); + /* Ignore fields which are unstable across GCs. */ + CHECK(savedCopy->lastProperty() == obj->lastProperty()); + CHECK(savedCopy->getProto() == obj->getProto()); return true; } diff --git a/deps/mozjs/js/src/jsapi-tests/testCustomIterator.cpp b/deps/mozjs/js/src/jsapi-tests/testCustomIterator.cpp index c05eef3e7d9..5a889bea621 100644 --- a/deps/mozjs/js/src/jsapi-tests/testCustomIterator.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testCustomIterator.cpp @@ -1,6 +1,6 @@ #include "tests.h" -#include "jsvalue.h" +#include "jsclass.h" int count = 0; @@ -27,13 +27,13 @@ IterHook(JSContext *cx, JSObject *obj, JSBool keysonly) js::Class HasCustomIterClass = { "HasCustomIter", 0, - js::PropertyStub, - js::PropertyStub, - js::PropertyStub, - js::StrictPropertyStub, - js::EnumerateStub, - js::ResolveStub, - js::ConvertStub, + JS_PropertyStub, + JS_PropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, NULL, NULL, /* reserved0 */ NULL, /* checkAccess */ @@ -73,8 +73,8 @@ BEGIN_TEST(testCustomIterator_bug612523) "j;", &result); CHECK(JSVAL_IS_INT(result)); - CHECK(JSVAL_TO_INT(result) == 100); - CHECK(count == 101); + CHECK_EQUAL(JSVAL_TO_INT(result), 100); + CHECK_EQUAL(count, 101); return true; } diff --git a/deps/mozjs/js/src/jsapi-tests/testDebugger.cpp b/deps/mozjs/js/src/jsapi-tests/testDebugger.cpp index d67929ad506..6b789a6895a 100644 --- a/deps/mozjs/js/src/jsapi-tests/testDebugger.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testDebugger.cpp @@ -4,6 +4,7 @@ #include "tests.h" #include "jsdbgapi.h" +#include "jscntxt.h" static int callCount[2] = {0, 0}; @@ -26,8 +27,8 @@ BEGIN_TEST(testDebugger_bug519719) "function f(g) { for (var i = 0; i < 9; i++) call(g); }\n" "f(Math.sin);\n" // record loop, starting in f "f(Math.cos);\n"); // side exit in f -> call - CHECK(callCount[0] == 20); - CHECK(callCount[1] == 20); + CHECK_EQUAL(callCount[0], 20); + CHECK_EQUAL(callCount[1], 20); return true; } END_TEST(testDebugger_bug519719) @@ -103,3 +104,219 @@ BEGIN_TEST(testDebugger_getThisStrict) return true; } END_TEST(testDebugger_getThisStrict) + +bool called = false; + +static JSTrapStatus +ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure) +{ + called = true; + + JSObject *global = JS_GetGlobalForScopeChain(cx); + + char text[] = "new Error()"; + jsval _; + JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_); + + return JSTRAP_CONTINUE; +} + +BEGIN_TEST(testDebugger_throwHook) +{ + uint32_t newopts = JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS; + uint32_t oldopts = JS_SetOptions(cx, newopts); + + JSDebugHooks hooks = { 0 }; + hooks.throwHook = ThrowHook; + JSDebugHooks *old = JS_SetContextDebugHooks(cx, &hooks); + EXEC("function foo() { throw 3 };\n" + "for (var i = 0; i < 10; ++i) { \n" + " var x = ;\n" + " try {\n" + " foo(); \n" + " } catch(e) {}\n" + "}\n"); + CHECK(called); + + JS_SetContextDebugHooks(cx, old); + JS_SetOptions(cx, oldopts); + return true; +} +END_TEST(testDebugger_throwHook) + +BEGIN_TEST(testDebugger_debuggerObjectVsDebugMode) +{ + CHECK(JS_DefineDebuggerObject(cx, global)); + JSObject *debuggee = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + CHECK(debuggee); + + { + JSAutoEnterCompartment ae; + CHECK(ae.enter(cx, debuggee)); + CHECK(JS_SetDebugMode(cx, true)); + CHECK(JS_InitStandardClasses(cx, debuggee)); + } + + JSObject *debuggeeWrapper = debuggee; + CHECK(JS_WrapObject(cx, &debuggeeWrapper)); + jsval v = OBJECT_TO_JSVAL(debuggeeWrapper); + CHECK(JS_SetProperty(cx, global, "debuggee", &v)); + + EVAL("var dbg = new Debugger(debuggee);\n" + "var hits = 0;\n" + "dbg.onDebuggerStatement = function () { hits++; };\n" + "debuggee.eval('debugger;');\n" + "hits;\n", + &v); + CHECK_SAME(v, JSVAL_ONE); + + { + JSAutoEnterCompartment ae; + CHECK(ae.enter(cx, debuggee)); + CHECK(JS_SetDebugMode(cx, false)); + } + + EVAL("debuggee.eval('debugger; debugger; debugger;');\n" + "hits;\n", + &v); + CHECK_SAME(v, INT_TO_JSVAL(4)); + + return true; +} +END_TEST(testDebugger_debuggerObjectVsDebugMode) + +BEGIN_TEST(testDebugger_newScriptHook) +{ + // Test that top-level indirect eval fires the newScript hook. + CHECK(JS_DefineDebuggerObject(cx, global)); + JSObject *g1, *g2; + g1 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + CHECK(g1); + { + JSAutoEnterCompartment ae; + CHECK(ae.enter(cx, g1)); + CHECK(JS_InitStandardClasses(cx, g1)); + g2 = JS_NewGlobalObject(cx, getGlobalClass()); + CHECK(g2); + CHECK(JS_InitStandardClasses(cx, g2)); + } + + JSObject *g1Wrapper = g1; + CHECK(JS_WrapObject(cx, &g1Wrapper)); + jsval v = OBJECT_TO_JSVAL(g1Wrapper); + CHECK(JS_SetProperty(cx, global, "g1", &v)); + + JSObject *g2Wrapper = g2; + CHECK(JS_WrapObject(cx, &g2Wrapper)); + v = OBJECT_TO_JSVAL(g2Wrapper); + CHECK(JS_SetProperty(cx, global, "g2", &v)); + + EXEC("var dbg = Debugger(g1);\n" + "var hits = 0;\n" + "dbg.onNewScript = function (s) {\n" + " hits += Number(s instanceof Debugger.Script);\n" + "};\n"); + + // Since g1 is a debuggee and g2 is not, g1.eval should trigger newScript + // and g2.eval should not, regardless of what scope object we use to enter + // the compartment. + // + // (Not all scripts are permanently associated with specific global + // objects, but eval scripts are, so we deliver them only to debuggers that + // are watching that particular global.) + // + bool ok = true; + ok = ok && testIndirectEval(g1, g1, "Math.abs(0)", 1); + ok = ok && testIndirectEval(g2, g1, "Math.abs(1)", 1); + ok = ok && testIndirectEval(g1, g2, "Math.abs(-1)", 0); + ok = ok && testIndirectEval(g2, g2, "Math.abs(-2)", 0); + return ok; +} + +bool testIndirectEval(JSObject *scope, JSObject *g, const char *code, int expectedHits) +{ + EXEC("hits = 0;"); + + { + JSAutoEnterCompartment ae; + CHECK(ae.enter(cx, scope)); + JSString *codestr = JS_NewStringCopyZ(cx, code); + CHECK(codestr); + jsval argv[1] = { STRING_TO_JSVAL(codestr) }; + jsval v; + CHECK(JS_CallFunctionName(cx, g, "eval", 1, argv, &v)); + } + + jsval hitsv; + EVAL("hits", &hitsv); + CHECK_SAME(hitsv, INT_TO_JSVAL(expectedHits)); + return true; +} +END_TEST(testDebugger_newScriptHook) + +BEGIN_TEST(testDebugger_singleStepThrow) + { + CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment, true)); + CHECK(JS_SetInterrupt(rt, onStep, NULL)); + + uint32_t opts = JS_GetOptions(cx); + opts |= JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS; + JS_SetOptions(cx, opts); + + CHECK(JS_DefineFunction(cx, global, "setStepMode", setStepMode, 0, 0)); + EXEC("var e;\n" + "setStepMode();\n" + "function f() { throw 0; }\n" + "try { f(); }\n" + "catch (x) { e = x; }\n"); + return true; + } + + static JSBool + setStepMode(JSContext *cx, uintN argc, jsval *vp) + { + JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL); + JS_ASSERT(fp); + JSScript *script = JS_GetFrameScript(cx, fp); + JS_ASSERT(script); + if (!JS_SetSingleStepMode(cx, script, true)) + return false; + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return true; + } + + static JSTrapStatus + onStep(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure) + { + return JSTRAP_CONTINUE; + } +END_TEST(testDebugger_singleStepThrow) + +BEGIN_TEST(testDebugger_emptyObjectPropertyIterator) +{ + JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); + JSScopeProperty *prop = NULL; + CHECK(!JS_PropertyIterator(obj, &prop)); + CHECK(!prop); + + return true; +} +END_TEST(testDebugger_emptyObjectPropertyIterator) + +BEGIN_TEST(testDebugger_nonEmptyObjectPropertyIterator) +{ + jsval v; + EVAL("({a: 15})", &v); + JSObject *obj = JSVAL_TO_OBJECT(v); + JSScopeProperty *prop = NULL; + CHECK(JS_PropertyIterator(obj, &prop)); + JSPropertyDesc desc; + CHECK(JS_GetPropertyDesc(cx, obj, prop, &desc)); + CHECK_EQUAL(JSVAL_IS_INT(desc.value), true); + CHECK_EQUAL(JSVAL_TO_INT(desc.value), 15); + CHECK(!JS_PropertyIterator(obj, &prop)); + CHECK(!prop); + + return true; +} +END_TEST(testDebugger_nonEmptyObjectPropertyIterator) diff --git a/deps/mozjs/js/src/jsapi-tests/testExtendedEq.cpp b/deps/mozjs/js/src/jsapi-tests/testExtendedEq.cpp index e47664e0dc4..8670dfac173 100644 --- a/deps/mozjs/js/src/jsapi-tests/testExtendedEq.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testExtendedEq.cpp @@ -18,27 +18,27 @@ my_Equality(JSContext *cx, JSObject *obj, const jsval *, JSBool *bp) js::Class TestExtendedEq_JSClass = { "TestExtendedEq", 0, - js::PropertyStub, /* addProperty */ - js::PropertyStub, /* delProperty */ - js::PropertyStub, /* getProperty */ - js::StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ JS_EnumerateStub, JS_ResolveStub, - NULL, /* convert */ - NULL, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - NULL, /* mark */ + NULL, /* convert */ + NULL, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* mark */ { - js::Valueify(my_Equality), - NULL, /* outerObject */ - NULL, /* innerObject */ - NULL, /* iteratorObject */ - NULL, /* wrappedObject */ + my_Equality, + NULL, /* outerObject */ + NULL, /* innerObject */ + NULL, /* iteratorObject */ + NULL, /* wrappedObject */ } }; diff --git a/deps/mozjs/js/src/jsapi-tests/testExternalStrings.cpp b/deps/mozjs/js/src/jsapi-tests/testExternalStrings.cpp index 6cc0492f858..6510aa3c274 100644 --- a/deps/mozjs/js/src/jsapi-tests/testExternalStrings.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testExternalStrings.cpp @@ -1,37 +1,39 @@ #include "tests.h" #include "jsutil.h" -const jschar arr[] = { 'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0' }; -size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1; -void *magic = (void *)0x42; +static const jschar arr[] = { + 'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0' +}; +static const size_t arrlen = sizeof(arr) / sizeof(arr[0]) - 1; -int finalized_noclosure = 0; -int finalized_closure = 0; +static int finalized1 = 0; +static int finalized2 = 0; -void finalize_str(JSContext *cx, JSString *str) +static void +finalize_str(const JSStringFinalizer *fin, jschar *chars); + +static const JSStringFinalizer finalizer1 = { finalize_str }; +static const JSStringFinalizer finalizer2 = { finalize_str }; + +static void +finalize_str(const JSStringFinalizer *fin, jschar *chars) { - size_t len; - const jschar *chars = JS_GetStringCharsAndLength(cx, str, &len); - if (chars && len == arrlen && js::PodEqual(chars, arr, len)) { - void *closure = JS_GetExternalStringClosure(cx, str); - if (closure) { - if (closure == magic) - ++finalized_closure; - } else { - ++finalized_noclosure; + if (chars && js::PodEqual(const_cast(chars), arr, arrlen)) { + if (fin == &finalizer1) { + ++finalized1; + } else if (fin == &finalizer2) { + ++finalized2; } } } BEGIN_TEST(testExternalStrings) { - intN op = JS_AddExternalStringFinalizer(finalize_str); - const unsigned N = 1000; for (unsigned i = 0; i < N; ++i) { - CHECK(JS_NewExternalString(cx, arr, arrlen, op)); - CHECK(JS_NewExternalStringWithClosure(cx, arr, arrlen, op, magic)); + CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer1)); + CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer2)); } // clear that newborn root @@ -42,8 +44,8 @@ BEGIN_TEST(testExternalStrings) // a generous fudge factor to account for strings rooted by conservative gc const unsigned epsilon = 10; - CHECK((N - finalized_noclosure) < epsilon); - CHECK((N - finalized_closure) < epsilon); + CHECK((N - finalized1) < epsilon); + CHECK((N - finalized2) < epsilon); return true; } diff --git a/deps/mozjs/js/src/jsapi-tests/testFuncCallback.cpp b/deps/mozjs/js/src/jsapi-tests/testFuncCallback.cpp index 42fb45804cb..a78b07e86da 100644 --- a/deps/mozjs/js/src/jsapi-tests/testFuncCallback.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testFuncCallback.cpp @@ -2,8 +2,7 @@ #include "jsfun.h" #include "jscntxt.h" -// For TRACING_ENABLED -#include "jstracer.h" +#include "jsobjinlines.h" #ifdef MOZ_TRACE_JSCALLS @@ -21,8 +20,7 @@ funcTransition(const JSFunction *, if (entering > 0) { ++depth; ++enters; - if (! JS_ON_TRACE(cx)) - ++interpreted; + ++interpreted; } else { --depth; ++leaves; @@ -61,46 +59,47 @@ BEGIN_TEST(testFuncCallback_bug507012) // Check whether JS_Execute() tracking works EXEC("42"); - CHECK(enters == 1 && leaves == 1 && depth == 0); + CHECK_EQUAL(enters, 1); + CHECK_EQUAL(leaves, 1); + CHECK_EQUAL(depth, 0); interpreted = enters = leaves = depth = 0; // Check whether the basic function tracking works EXEC("f(1)"); - CHECK(enters == 1+1 && leaves == 1+1 && depth == 0); + CHECK_EQUAL(enters, 1+1); + CHECK_EQUAL(leaves, 1+1); + CHECK_EQUAL(depth, 0); // Can we switch to a different callback? enters = 777; JS_SetFunctionCallback(cx, funcTransition2); EXEC("f(1)"); - CHECK(called2 && enters == 777); + CHECK(called2); + CHECK_EQUAL(enters, 777); // Check whether we can turn off function tracing JS_SetFunctionCallback(cx, NULL); EXEC("f(1)"); - CHECK(enters == 777); + CHECK_EQUAL(enters, 777); interpreted = enters = leaves = depth = 0; // Check nested invocations JS_SetFunctionCallback(cx, funcTransition); enters = leaves = depth = 0; EXEC("f(3)"); - CHECK(enters == 1+3 && leaves == 1+3 && depth == 0); + CHECK_EQUAL(enters, 1+3); + CHECK_EQUAL(leaves, 1+3); + CHECK_EQUAL(depth, 0); interpreted = enters = leaves = depth = 0; - // Check calls invoked while running on trace + // Check calls invoked while running on trace -- or now, perhaps on + // IonMonkey's equivalent, if it ever starts to exist? EXEC("function g () { ++x; }"); interpreted = enters = leaves = depth = 0; - EXEC("for (i = 0; i < 50; ++i) { g(); }"); - CHECK(enters == 1+50 && leaves == 1+50 && depth == 0); - - // If this fails, it means that the code was interpreted rather - // than trace-JITted, and so is not testing what it's supposed to - // be testing. Which doesn't necessarily imply that the - // functionality is broken. -#ifdef JS_TRACER - if (TRACING_ENABLED(cx)) - CHECK(interpreted < enters); -#endif + EXEC("for (i = 0; i < 5000; ++i) { g(); }"); + CHECK_EQUAL(enters, 1+5000); + CHECK_EQUAL(leaves, 1+5000); + CHECK_EQUAL(depth, 0); // Test nesting callbacks via JS_GetFunctionCallback() JS_SetFunctionCallback(cx, funcTransition); @@ -111,27 +110,29 @@ BEGIN_TEST(testFuncCallback_bug507012) interpreted = enters = leaves = depth = overlays = 0; EXEC("42.5"); - CHECK(enters == 1); - CHECK(leaves == 1); - CHECK(depth == 0); - CHECK(overlays == enters + leaves); + CHECK_EQUAL(enters, 1); + CHECK_EQUAL(leaves, 1); + CHECK_EQUAL(depth, 0); + CHECK_EQUAL(overlays, enters + leaves); interpreted = enters = leaves = depth = overlays = 0; #endif + // Uncomment this to validate whether you're hitting all runmodes (interp, + // mjit, ...?) Unfortunately, that still doesn't cover all + // transitions between the various runmodes, but it's a start. + //JS_DumpAllProfiles(cx); + return true; } -// Not strictly necessary, but part of the test attempts to check -// whether these callbacks still trigger when traced, so force -// JSOPTION_JIT just to be sure. Once the method jit and tracing jit -// are integrated, this'll probably have to change (and we'll probably -// want to test in all modes.) +// Make sure that the method jit is enabled. +// We'll probably want to test in all modes. virtual JSContext *createContext() { JSContext *cx = JSAPITest::createContext(); if (cx) - JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_JIT); + JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_PCCOUNT); return cx; } diff --git a/deps/mozjs/js/src/jsapi-tests/testFunctionProperties.cpp b/deps/mozjs/js/src/jsapi-tests/testFunctionProperties.cpp new file mode 100644 index 00000000000..b1f8012551e --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testFunctionProperties.cpp @@ -0,0 +1,23 @@ +/* -*- 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" + +BEGIN_TEST(testFunctionProperties) +{ + jsvalRoot x(cx); + EVAL("(function f() {})", x.addr()); + + JSObject *obj = JSVAL_TO_OBJECT(x.value()); + jsvalRoot y(cx); + + CHECK(JS_GetProperty(cx, obj, "arguments", y.addr())); + CHECK_SAME(y, JSVAL_NULL); + + CHECK(JS_GetProperty(cx, obj, "caller", y.addr())); + CHECK_SAME(y, JSVAL_NULL); + + return true; +} +END_TEST(testFunctionProperties) diff --git a/deps/mozjs/js/src/jsapi-tests/testGCChunkAlloc.cpp b/deps/mozjs/js/src/jsapi-tests/testGCChunkAlloc.cpp deleted file mode 100644 index a63af4b9241..00000000000 --- a/deps/mozjs/js/src/jsapi-tests/testGCChunkAlloc.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - * - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Igor Bukanov - */ - -#include "tests.h" -#include "jsgcchunk.h" -#include "jscntxt.h" - -/* We allow to allocate only single chunk. */ - -class CustomGCChunkAllocator: public js::GCChunkAllocator { - public: - CustomGCChunkAllocator() : pool(NULL) {} - void *pool; - - private: - - virtual void *doAlloc() { - if (!pool) - return NULL; - void *chunk = pool; - pool = NULL; - return chunk; - } - - virtual void doFree(void *chunk) { - JS_ASSERT(!pool); - pool = chunk; - } -}; - -static CustomGCChunkAllocator customGCChunkAllocator; - -static unsigned errorCount = 0; - -static void -ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) -{ - ++errorCount; -} - -BEGIN_TEST(testGCChunkAlloc) -{ - JS_SetErrorReporter(cx, ErrorCounter); - - jsvalRoot root(cx); - - /* - * We loop until out-of-memory happens during the chunk allocation. But - * we have to disable the jit since it cannot tolerate OOM during the - * chunk allocation. - */ - JS_ToggleOptions(cx, JSOPTION_JIT); - - static const char source[] = - "var max = 0; (function() {" - " var array = [];" - " for (; ; ++max)" - " array.push({});" - "})();"; - JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, - root.addr()); - - /* Check that we get OOM. */ - CHECK(!ok); - CHECK(!JS_IsExceptionPending(cx)); - CHECK(errorCount == 1); - CHECK(!customGCChunkAllocator.pool); - JS_GC(cx); - JS_ToggleOptions(cx, JSOPTION_JIT); - EVAL("(function() {" - " var array = [];" - " for (var i = max >> 1; i != 0;) {" - " --i;" - " array.push({});" - " }" - "})();", root.addr()); - CHECK(errorCount == 1); - return true; -} - -virtual JSRuntime * createRuntime() { - /* - * To test failure of chunk allocation allow to use GC twice the memory - * the single chunk contains. - */ - JSRuntime *rt = JS_NewRuntime(2 * js::GC_CHUNK_SIZE); - if (!rt) - return NULL; - - customGCChunkAllocator.pool = js::AllocGCChunk(); - JS_ASSERT(customGCChunkAllocator.pool); - - rt->setCustomGCChunkAllocator(&customGCChunkAllocator); - return rt; -} - -virtual void destroyRuntime() { - JS_DestroyRuntime(rt); - - /* We should get the initial chunk back at this point. */ - JS_ASSERT(customGCChunkAllocator.pool); - js::FreeGCChunk(customGCChunkAllocator.pool); - customGCChunkAllocator.pool = NULL; -} - -END_TEST(testGCChunkAlloc) diff --git a/deps/mozjs/js/src/jsapi-tests/testGCOutOfMemory.cpp b/deps/mozjs/js/src/jsapi-tests/testGCOutOfMemory.cpp new file mode 100644 index 00000000000..cdac4cf4a69 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testGCOutOfMemory.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Igor Bukanov + */ + +#include "tests.h" +#include "jscntxt.h" + +static unsigned errorCount = 0; + +static void +ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) +{ + ++errorCount; +} + +BEGIN_TEST(testGCOutOfMemory) +{ + JS_SetErrorReporter(cx, ErrorCounter); + + jsvalRoot root(cx); + + static const char source[] = + "var max = 0; (function() {" + " var array = [];" + " for (; ; ++max)" + " array.push({});" + " array = []; array.push(0);" + "})();"; + JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, + root.addr()); + + /* Check that we get OOM. */ + CHECK(!ok); + CHECK(!JS_IsExceptionPending(cx)); + CHECK_EQUAL(errorCount, 1); + JS_GC(cx); + EVAL("(function() {" + " var array = [];" + " for (var i = max >> 2; i != 0;) {" + " --i;" + " array.push({});" + " }" + "})();", root.addr()); + CHECK_EQUAL(errorCount, 1); + return true; +} + +virtual JSRuntime * createRuntime() { + return JS_NewRuntime(512 * 1024); +} + +virtual void destroyRuntime() { + JS_DestroyRuntime(rt); +} + +END_TEST(testGCOutOfMemory) diff --git a/deps/mozjs/js/src/jsapi-tests/testIndexToString.cpp b/deps/mozjs/js/src/jsapi-tests/testIndexToString.cpp new file mode 100644 index 00000000000..db2a879f8c2 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testIndexToString.cpp @@ -0,0 +1,113 @@ +/* -*- 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" + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsnum.h" +#include "jsstr.h" + +#include "vm/String-inl.h" + +using namespace mozilla; + +template JSFlatString * +NewString(JSContext *cx, const jschar (&chars)[N]) +{ + return js_NewStringCopyN(cx, chars, N); +} + +static const struct TestPair { + uint32_t num; + const char *expected; +} tests[] = { + { 0, "0" }, + { 1, "1" }, + { 2, "2" }, + { 9, "9" }, + { 10, "10" }, + { 15, "15" }, + { 16, "16" }, + { 17, "17" }, + { 99, "99" }, + { 100, "100" }, + { 255, "255" }, + { 256, "256" }, + { 257, "257" }, + { 999, "999" }, + { 1000, "1000" }, + { 4095, "4095" }, + { 4096, "4096" }, + { 9999, "9999" }, + { 1073741823, "1073741823" }, + { 1073741824, "1073741824" }, + { 1073741825, "1073741825" }, + { 2147483647, "2147483647" }, + { 2147483648u, "2147483648" }, + { 2147483649u, "2147483649" }, + { 4294967294u, "4294967294" }, + { 4294967295u, "4294967295" }, +}; + +BEGIN_TEST(testIndexToString) +{ + for (size_t i = 0, sz = ArrayLength(tests); i < sz; i++) { + uint32_t u = tests[i].num; + JSString *str = js::IndexToString(cx, u); + CHECK(str); + + if (!js::StaticStrings::hasUint(u)) + CHECK(cx->compartment->dtoaCache.lookup(10, u) == str); + + JSBool match = JS_FALSE; + CHECK(JS_StringEqualsAscii(cx, str, tests[i].expected, &match)); + CHECK(match); + } + + return true; +} +END_TEST(testIndexToString) + +BEGIN_TEST(testStringIsIndex) +{ + for (size_t i = 0, sz = ArrayLength(tests); i < sz; i++) { + uint32_t u = tests[i].num; + JSFlatString *str = js::IndexToString(cx, u); + CHECK(str); + + uint32_t n; + CHECK(str->isIndex(&n)); + CHECK(u == n); + } + + return true; +} +END_TEST(testStringIsIndex) + +BEGIN_TEST(testStringToPropertyName) +{ + uint32_t index; + + static const jschar hiChars[] = { 'h', 'i' }; + JSFlatString *hiStr = NewString(cx, hiChars); + CHECK(hiStr); + CHECK(!hiStr->isIndex(&index)); + CHECK(hiStr->toPropertyName(cx) != NULL); + + static const jschar maxChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '5' }; + JSFlatString *maxStr = NewString(cx, maxChars); + CHECK(maxStr); + CHECK(maxStr->isIndex(&index)); + CHECK(index == UINT32_MAX); + + static const jschar maxPlusOneChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '6' }; + JSFlatString *maxPlusOneStr = NewString(cx, maxPlusOneChars); + CHECK(maxPlusOneStr); + CHECK(!maxPlusOneStr->isIndex(&index)); + CHECK(maxPlusOneStr->toPropertyName(cx) != NULL); + + return true; +} +END_TEST(testStringToPropertyName) diff --git a/deps/mozjs/js/src/jsapi-tests/testIntString.cpp b/deps/mozjs/js/src/jsapi-tests/testIntString.cpp index 46881bda287..bab6351ce90 100644 --- a/deps/mozjs/js/src/jsapi-tests/testIntString.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testIntString.cpp @@ -3,7 +3,7 @@ */ #include "tests.h" -#include "jsstr.h" +#include "vm/String.h" BEGIN_TEST(testIntString_bug515273) { @@ -11,28 +11,28 @@ BEGIN_TEST(testIntString_bug515273) EVAL("'1';", v.addr()); JSString *str = JSVAL_TO_STRING(v.value()); - CHECK(str->isStaticAtom()); + CHECK(JS_StringHasBeenInterned(cx, str)); CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1")); EVAL("'42';", v.addr()); str = JSVAL_TO_STRING(v.value()); - CHECK(str->isStaticAtom()); + CHECK(JS_StringHasBeenInterned(cx, str)); CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42")); EVAL("'111';", v.addr()); str = JSVAL_TO_STRING(v.value()); - CHECK(str->isStaticAtom()); + CHECK(JS_StringHasBeenInterned(cx, str)); CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111")); /* Test other types of static strings. */ EVAL("'a';", v.addr()); str = JSVAL_TO_STRING(v.value()); - CHECK(str->isStaticAtom()); + CHECK(JS_StringHasBeenInterned(cx, str)); CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a")); EVAL("'bc';", v.addr()); str = JSVAL_TO_STRING(v.value()); - CHECK(str->isStaticAtom()); + CHECK(JS_StringHasBeenInterned(cx, str)); CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc")); return true; diff --git a/deps/mozjs/js/src/jsapi-tests/testIntTypesABI.cpp b/deps/mozjs/js/src/jsapi-tests/testIntTypesABI.cpp new file mode 100644 index 00000000000..1a607752397 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testIntTypesABI.cpp @@ -0,0 +1,70 @@ +#include "tests.h" + +/* + * This test exercises the full, deliberately-exposed JSAPI interface to ensure + * that no internal integer typedefs leak out. Include every intentionally + * public header file (and those headers included by them, for completeness), + * even the ones tests.h itself included, to verify this. + */ +#include "js-config.h" +#include "jsapi.h" +#include "jsclass.h" +#include "jscompat.h" +#include "jscpucfg.h" +#include "jspubtd.h" +#include "jstypes.h" +#include "jsval.h" +#include "jsxdrapi.h" + +#include "js/HashTable.h" +#include "js/MemoryMetrics.h" +#include "js/TemplateLib.h" +#include "js/Utility.h" +#include "js/Vector.h" + +/* + * Verify that our public (and intended to be public, versus being that way + * because we haven't made them private yet) headers don't define + * {u,}int{8,16,32,64} or JS{Ui,I}nt{8,16,32,64} types. If any do, they will + * assuredly conflict with a corresponding typedef below mapping to a *struct*. + * + * Note that tests.h includes a few internal headers; in order that this + * jsapi-test be writable, those internal headers must not import the legacy + * typedefs. + */ + +struct ConflictingType { + uint64_t u64; +}; + +typedef ConflictingType uint8; +typedef ConflictingType uint16; +typedef ConflictingType uint32; +typedef ConflictingType uint64; + +typedef ConflictingType int8; +typedef ConflictingType int16; +typedef ConflictingType int32; +typedef ConflictingType int64; + +typedef ConflictingType JSUint8; +typedef ConflictingType JSUint16; +typedef ConflictingType JSUint32; +typedef ConflictingType JSUint64; + +typedef ConflictingType JSInt8; +typedef ConflictingType JSInt16; +typedef ConflictingType JSInt32; +typedef ConflictingType JSInt64; + +typedef ConflictingType jsword; +typedef ConflictingType jsuword; +typedef ConflictingType JSWord; +typedef ConflictingType JSUword; + +BEGIN_TEST(testIntTypesABI) +{ + /* This passes if the typedefs didn't conflict at compile time. */ + return true; +} +END_TEST(testIntTypesABI) diff --git a/deps/mozjs/js/src/jsapi-tests/testIntern.cpp b/deps/mozjs/js/src/jsapi-tests/testIntern.cpp new file mode 100644 index 00000000000..5e5af0c79a4 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testIntern.cpp @@ -0,0 +1,44 @@ +#include "tests.h" +#include "jsatom.h" + +#include "vm/String.h" + +using namespace mozilla; + +BEGIN_TEST(testAtomizedIsNotInterned) +{ + /* Try to pick a string that won't be interned by other tests in this runtime. */ + static const char someChars[] = "blah blah blah? blah blah blah"; + JSAtom *atom = js_Atomize(cx, someChars, ArrayLength(someChars)); + CHECK(!JS_StringHasBeenInterned(cx, atom)); + CHECK(JS_InternJSString(cx, atom)); + CHECK(JS_StringHasBeenInterned(cx, atom)); + return true; +} +END_TEST(testAtomizedIsNotInterned) + +struct StringWrapper +{ + JSString *str; + bool strOk; +} sw; + +JSBool +GCCallback(JSContext *cx, JSGCStatus status) +{ + if (status == JSGC_MARK_END) + sw.strOk = !JS_IsAboutToBeFinalized(sw.str); + return true; +} + +BEGIN_TEST(testInternAcrossGC) +{ + sw.str = JS_InternString(cx, "wrapped chars that another test shouldn't be using"); + sw.strOk = false; + CHECK(sw.str); + JS_SetGCCallback(cx, GCCallback); + JS_GC(cx); + CHECK(sw.strOk); + return true; +} +END_TEST(testInternAcrossGC) diff --git a/deps/mozjs/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp b/deps/mozjs/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp index 48462771adc..c4b30de2b64 100644 --- a/deps/mozjs/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp @@ -18,7 +18,7 @@ TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status) for (jsuint i = 0; i != checkPointersLength; ++i) { void *p = checkPointers[i]; JS_ASSERT(p); - if (JS_IsAboutToBeFinalized(cx, p)) + if (JS_IsAboutToBeFinalized(p)) checkPointers[i] = NULL; } } @@ -60,7 +60,7 @@ BEGIN_TEST(testIsAboutToBeFinalized_bug528645) --checkPointersStaticStrings; } } - CHECK(checkPointersStaticStrings == 0); + CHECK_EQUAL(checkPointersStaticStrings, 0); free(checkPointers); checkPointers = NULL; diff --git a/deps/mozjs/js/src/jsapi-tests/testLookup.cpp b/deps/mozjs/js/src/jsapi-tests/testLookup.cpp index 784ed8b6232..121a89f721a 100644 --- a/deps/mozjs/js/src/jsapi-tests/testLookup.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testLookup.cpp @@ -5,6 +5,8 @@ #include "tests.h" #include "jsfun.h" // for js::IsInternalFunctionObject +#include "jsobjinlines.h" + BEGIN_TEST(testLookup_bug522590) { // Define a function that makes method-bearing objects. @@ -26,7 +28,7 @@ BEGIN_TEST(testLookup_bug522590) JSObject *funobj = JSVAL_TO_OBJECT(r); CHECK(funobj->isFunction()); CHECK(!js::IsInternalFunctionObject(funobj)); - CHECK(GET_FUNCTION_PRIVATE(cx, funobj) != (JSFunction *) funobj); + CHECK(funobj->toFunction()->isClonedMethod()); return true; } diff --git a/deps/mozjs/js/src/jsapi-tests/testNewObject.cpp b/deps/mozjs/js/src/jsapi-tests/testNewObject.cpp index 7f289f04f84..5974131f71e 100644 --- a/deps/mozjs/js/src/jsapi-tests/testNewObject.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testNewObject.cpp @@ -18,7 +18,7 @@ constructHook(JSContext *cx, uintN argc, jsval *vp) JS_ReportError(cx, "test failed, could not construct object"); return false; } - if (strcmp(JS_GET_CLASS(cx, obj)->name, "Object") != 0) { + if (strcmp(JS_GetClass(obj)->name, "Object") != 0) { JS_ReportError(cx, "test failed, wrong class for 'this'"); return false; } @@ -57,7 +57,7 @@ BEGIN_TEST(testNewObject_1) CHECK(JS_IsArrayObject(cx, obj)); jsuint len; CHECK(JS_GetArrayLength(cx, obj, &len)); - CHECK(len == 0); + CHECK_EQUAL(len, 0); // With one argument. argv[0] = INT_TO_JSVAL(4); @@ -66,7 +66,7 @@ BEGIN_TEST(testNewObject_1) rt = OBJECT_TO_JSVAL(obj); CHECK(JS_IsArrayObject(cx, obj)); CHECK(JS_GetArrayLength(cx, obj, &len)); - CHECK(len == 4); + CHECK_EQUAL(len, 4); // With N arguments. for (size_t i = 0; i < N; i++) @@ -76,7 +76,7 @@ BEGIN_TEST(testNewObject_1) rt = OBJECT_TO_JSVAL(obj); CHECK(JS_IsArrayObject(cx, obj)); CHECK(JS_GetArrayLength(cx, obj, &len)); - CHECK(len == N); + CHECK_EQUAL(len, N); CHECK(JS_GetElement(cx, obj, N - 1, &v)); CHECK_SAME(v, INT_TO_JSVAL(N - 1)); diff --git a/deps/mozjs/js/src/jsapi-tests/testOriginPrincipals.cpp b/deps/mozjs/js/src/jsapi-tests/testOriginPrincipals.cpp new file mode 100644 index 00000000000..ae448e5ab8e --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testOriginPrincipals.cpp @@ -0,0 +1,130 @@ +#include "tests.h" +#include "jsdbgapi.h" +#include "jsobjinlines.h" + +JSPrincipals *sCurrentGlobalPrincipals = NULL; + +JSPrincipals * +ObjectPrincipalsFinder(JSContext *, JSObject *) +{ + return sCurrentGlobalPrincipals; +} + +JSSecurityCallbacks seccb = { + NULL, + NULL, + ObjectPrincipalsFinder, + NULL +}; + +static void +Destroy(JSContext *, JSPrincipals *) +{} + +static JSBool +Subsume(JSPrincipals *, JSPrincipals *) +{ + return true; +} + +JSPrincipals *sOriginPrincipalsInErrorReporter = NULL; + +static void +ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + sOriginPrincipalsInErrorReporter = report->originPrincipals; +} + +char p1str[] = "principal1"; +JSPrincipals prin1 = { p1str, 0, Destroy, Subsume }; +char p2str[] = "principal2"; +JSPrincipals prin2 = { p2str, 0, Destroy, Subsume }; + +BEGIN_TEST(testOriginPrincipals) +{ + JS_SetContextSecurityCallbacks(cx, &seccb); + + /* + * Currently, the only way to set a non-trivial originPrincipal is to use + * JS_EvaluateUCScriptForPrincipalsVersionOrigin. This does not expose the + * compiled script, so we can only test nested scripts. + */ + + CHECK(testOuter("function f() {return 1}; f;")); + CHECK(testOuter("function outer() { return (function () {return 2}); }; outer();")); + CHECK(testOuter("eval('(function() {return 3})');")); + CHECK(testOuter("(function (){ return eval('(function() {return 4})'); })()")); + CHECK(testOuter("(function (){ return eval('(function() { return eval(\"(function(){return 5})\") })()'); })()")); + CHECK(testOuter("new Function('return 6')")); + CHECK(testOuter("function f() { return new Function('return 7') }; f();")); + CHECK(testOuter("eval('new Function(\"return 8\")')")); + CHECK(testOuter("(new Function('return eval(\"(function(){return 9})\")'))()")); + CHECK(testOuter("(function(){return function(){return 10}}).bind()()")); + CHECK(testOuter("var e = eval; (function() { return e('(function(){return 11})') })()")); + + JS_SetErrorReporter(cx, ErrorReporter); + CHECK(testError("eval(-)")); + CHECK(testError("-")); + CHECK(testError("new Function('x', '-')")); + CHECK(testError("eval('new Function(\"x\", \"-\")')")); + + /* + * NB: uncaught exceptions, when reported, have nothing on the stack so + * both the filename and originPrincipals are null. E.g., this would fail: + * + * CHECK(testError("throw 3")); + */ + return true; +} + +bool +eval(const char *asciiChars, JSPrincipals *principals, JSPrincipals *originPrincipals, jsval *rval) +{ + size_t len = strlen(asciiChars); + jschar *chars = new jschar[len+1]; + for (size_t i = 0; i < len; ++i) + chars[i] = asciiChars[i]; + chars[len] = 0; + + bool ok = JS_EvaluateUCScriptForPrincipalsVersionOrigin(cx, global, + principals, + originPrincipals, + chars, len, "", 0, rval, + JSVERSION_DEFAULT); + delete[] chars; + return ok; +} + +bool +testOuter(const char *asciiChars) +{ + CHECK(testInner(asciiChars, &prin1, &prin1)); + CHECK(testInner(asciiChars, &prin1, &prin2)); + return true; +} + +bool +testInner(const char *asciiChars, JSPrincipals *principal, JSPrincipals *originPrincipal) +{ + sCurrentGlobalPrincipals = principal; + + jsval rval; + CHECK(eval(asciiChars, principal, originPrincipal, &rval)); + + JSScript *script = JS_GetFunctionScript(cx, JSVAL_TO_OBJECT(rval)->toFunction()); + CHECK(JS_GetScriptPrincipals(cx, script) == principal); + CHECK(JS_GetScriptOriginPrincipals(cx, script) == originPrincipal); + + return true; +} + +bool +testError(const char *asciiChars) +{ + jsval rval; + CHECK(!eval(asciiChars, &prin1, &prin2 /* = originPrincipals */, &rval)); + CHECK(JS_ReportPendingException(cx)); + CHECK(sOriginPrincipalsInErrorReporter == &prin2); + return true; +} +END_TEST(testOriginPrincipals) diff --git a/deps/mozjs/js/src/jsapi-tests/testParseJSON.cpp b/deps/mozjs/js/src/jsapi-tests/testParseJSON.cpp index 6e62a280483..c82053ae7e5 100644 --- a/deps/mozjs/js/src/jsapi-tests/testParseJSON.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testParseJSON.cpp @@ -7,6 +7,9 @@ #include "tests.h" #include "jsstr.h" +#include "vm/String.h" + +using namespace js; class AutoInflatedString { JSContext * const cx; @@ -21,7 +24,7 @@ class AutoInflatedString { template void operator=(const char (&str)[N]) { length_ = N - 1; - chars_ = js_InflateString(cx, str, &length_); + chars_ = InflateString(cx, str, &length_); if (!chars_) abort(); } diff --git a/deps/mozjs/js/src/jsapi-tests/testPropCache.cpp b/deps/mozjs/js/src/jsapi-tests/testPropCache.cpp index 37026e94a29..a8a3c5f945e 100644 --- a/deps/mozjs/js/src/jsapi-tests/testPropCache.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testPropCache.cpp @@ -29,7 +29,7 @@ BEGIN_TEST(testPropCache_bug505798) EXEC("var arr = [x, y];\n" "for (var i = 0; i < arr.length; i++)\n" " arr[i].p = 1;\n"); - CHECK(g_counter == 1); + CHECK_EQUAL(g_counter, 1); return true; } END_TEST(testPropCache_bug505798) diff --git a/deps/mozjs/js/src/jsapi-tests/testRegExp.cpp b/deps/mozjs/js/src/jsapi-tests/testRegExp.cpp new file mode 100644 index 00000000000..8dfa18cc3bc --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testRegExp.cpp @@ -0,0 +1,53 @@ +#include "tests.h" + +BEGIN_TEST(testObjectIsRegExp) +{ + jsvalRoot val(cx); + JSObject *obj; + + EVAL("new Object", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + CHECK(!JS_ObjectIsRegExp(cx, obj)); + + EVAL("/foopy/", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + CHECK(JS_ObjectIsRegExp(cx, obj)); + + return true; +} +END_TEST(testObjectIsRegExp) + +BEGIN_TEST(testGetRegExpFlags) +{ + jsvalRoot val(cx); + JSObject *obj; + + EVAL("/foopy/", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), 0); + + EVAL("/foopy/g", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), JSREG_GLOB); + + EVAL("/foopy/gi", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), (JSREG_FOLD | JSREG_GLOB)); + + return true; +} +END_TEST(testGetRegExpFlags) + +BEGIN_TEST(testGetRegExpSource) +{ + jsvalRoot val(cx); + JSObject *obj; + + EVAL("/foopy/", val.addr()); + obj = JSVAL_TO_OBJECT(val.value()); + JSString *source = JS_GetRegExpSource(cx, obj); + CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(source), "foopy")); + + return true; +} +END_TEST(testGetRegExpSource) diff --git a/deps/mozjs/js/src/jsapi-tests/testRegExpInstanceProperties.cpp b/deps/mozjs/js/src/jsapi-tests/testRegExpInstanceProperties.cpp index 7e183b5aadd..b560921c5b3 100644 --- a/deps/mozjs/js/src/jsapi-tests/testRegExpInstanceProperties.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testRegExpInstanceProperties.cpp @@ -22,14 +22,14 @@ BEGIN_TEST(testRegExpInstanceProperties) JS_GC(cx); - CHECK(regexpProto->getCompartment()->initialRegExpShape == NULL); + CHECK_EQUAL(regexpProto->compartment()->initialRegExpShape, NULL); jsval regexp; EVAL("/foopy/", ®exp); JSObject *robj = JSVAL_TO_OBJECT(regexp); CHECK(robj->lastProperty()); - CHECK(robj->getCompartment()->initialRegExpShape == robj->lastProperty()); + CHECK_EQUAL(robj->compartment()->initialRegExpShape, robj->lastProperty()); return true; } @@ -46,7 +46,7 @@ JS_NEVER_INLINE bool helper(JSObject *regexpProto) const js::Shape *shape = regexpProto->lastProperty(); js::AutoShapeRooter root(cx, shape); for (js::Shape::Range r = shape; - &r.front() != regexpProto->getCompartment()->initialRegExpShape; + &r.front() != regexpProto->compartment()->initialRegExpShape; r.popFront()) { CHECK(!r.empty()); @@ -63,7 +63,7 @@ JS_NEVER_INLINE bool helper(JSObject *regexpProto) js::AutoShapeRooter root2(cx, shape2); js::Shape::Range r2 = shape2; while (!r2.empty()) { - CHECK(&r2.front() != regexpProto->getCompartment()->initialRegExpShape); + CHECK(&r2.front() != regexpProto->compartment()->initialRegExpShape); r2.popFront(); } diff --git a/deps/mozjs/js/src/jsapi-tests/testResolveRecursion.cpp b/deps/mozjs/js/src/jsapi-tests/testResolveRecursion.cpp index 38b62d97173..205bd88a811 100644 --- a/deps/mozjs/js/src/jsapi-tests/testResolveRecursion.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testResolveRecursion.cpp @@ -30,8 +30,8 @@ BEGIN_TEST(testResolveRecursion) 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)); + JS_SetPrivate(obj1, this); + JS_SetPrivate(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)); @@ -42,9 +42,9 @@ BEGIN_TEST(testResolveRecursion) /* 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); + CHECK_SAME(v, JSVAL_FALSE); + CHECK_EQUAL(resolveEntryCount, 4); + CHECK_EQUAL(resolveExitCount, 4); return true; } @@ -69,9 +69,9 @@ struct AutoIncrCounters { bool doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp) { - CHECK(resolveExitCount == 0); + CHECK_EQUAL(resolveExitCount, 0); AutoIncrCounters incr(this); - CHECK(obj == obj1 || obj == obj2); + CHECK_EQUAL(obj, obj1 || obj == obj2); CHECK(JSID_IS_STRING(id)); @@ -81,31 +81,31 @@ doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp) if (JS_FlatStringEqualsAscii(str, "x")) { if (obj == obj1) { /* First resolve hook invocation. */ - CHECK(resolveEntryCount == 1); + CHECK_EQUAL(resolveEntryCount, 1); EVAL("obj2.y = true", &v); - CHECK(v == JSVAL_TRUE); + CHECK_SAME(v, JSVAL_TRUE); CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, NULL, NULL, 0)); *objp = obj; return true; } if (obj == obj2) { - CHECK(resolveEntryCount == 4); + CHECK_EQUAL(resolveEntryCount, 4); *objp = NULL; return true; } } else if (JS_FlatStringEqualsAscii(str, "y")) { if (obj == obj2) { - CHECK(resolveEntryCount == 2); + CHECK_EQUAL(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); + CHECK_SAME(v, JSVAL_ZERO); *objp = obj; return true; } if (obj == obj1) { - CHECK(resolveEntryCount == 3); + CHECK_EQUAL(resolveEntryCount, 3); EVAL("obj1.x", &v); CHECK(JSVAL_IS_VOID(v)); EVAL("obj1.y", &v); @@ -115,7 +115,7 @@ doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp) EVAL("obj2.x", &v); CHECK(JSVAL_IS_VOID(v)); EVAL("obj1.y = 0", &v); - CHECK(v == JSVAL_ZERO); + CHECK_SAME(v, JSVAL_ZERO); *objp = obj; return true; } @@ -127,7 +127,7 @@ doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp) static JSBool my_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { - return static_cast(JS_GetPrivate(cx, obj))-> + return static_cast(JS_GetPrivate(obj))-> doResolve(obj, id, flags, objp); } diff --git a/deps/mozjs/js/src/jsapi-tests/testScriptInfo.cpp b/deps/mozjs/js/src/jsapi-tests/testScriptInfo.cpp new file mode 100644 index 00000000000..cae608937d6 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testScriptInfo.cpp @@ -0,0 +1,50 @@ +/* -*- 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" +#include "jsdbgapi.h" + +const char code[] = + "xx = 1; \n\ + \n\ +try { \n\ + debugger; \n\ + \n\ + xx += 1; \n\ +} \n\ +catch (e) \n\ +{ \n\ + xx += 1; \n\ +}\n\ +//@sourceMappingURL=http://example.com/path/to/source-map.json"; + + +static bool +CharsMatch(const jschar *p, const char *q) { + while (*q) { + if (*p++ != *q++) + return false; + } + return true; +} + +// Bug 670958 - fix JS_GetScriptLineExtent, among others +BEGIN_TEST(testScriptInfo) +{ + uintN startLine = 1000; + + JSScript *script = JS_CompileScript(cx, global, code, strlen(code), __FILE__, startLine); + + CHECK(script); + + jsbytecode *start = JS_LineNumberToPC(cx, script, startLine); + CHECK_EQUAL(JS_GetScriptBaseLineNumber(cx, script), startLine); + CHECK_EQUAL(JS_PCToLineNumber(cx, script, start), startLine); + CHECK_EQUAL(JS_GetScriptLineExtent(cx, script), 10); + CHECK(strcmp(JS_GetScriptFilename(cx, script), __FILE__) == 0); + CHECK(CharsMatch(JS_GetScriptSourceMap(cx, script), "http://example.com/path/to/source-map.json")); + + return true; +} +END_TEST(testScriptInfo) diff --git a/deps/mozjs/js/src/jsapi-tests/testScriptObject.cpp b/deps/mozjs/js/src/jsapi-tests/testScriptObject.cpp index 13ed97565dc..3aed403f6ff 100644 --- a/deps/mozjs/js/src/jsapi-tests/testScriptObject.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testScriptObject.cpp @@ -15,15 +15,15 @@ struct ScriptObjectFixture : public JSAPITest { uc_code[i] = code[i]; } - bool tryScript(JSObject *scriptObj) + bool tryScript(JSScript *script) { - CHECK(scriptObj); + CHECK(script); JS_GC(cx); /* After a garbage collection, the script should still work. */ jsval result; - CHECK(JS_ExecuteScript(cx, global, scriptObj, &result)); + CHECK(JS_ExecuteScript(cx, global, script, &result)); return true; } @@ -87,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(); - JSObject *scriptObj = JS_CompileFile(cx, global, script_filename); + JSScript *script = JS_CompileUTF8File(cx, global, script_filename); tempScript.remove(); - return tryScript(scriptObj); + return tryScript(script); } END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile) @@ -99,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(); - JSObject *scriptObj = JS_CompileFile(cx, global, script_filename); + JSScript *script = JS_CompileUTF8File(cx, global, script_filename); tempScript.remove(); - return tryScript(scriptObj); + return tryScript(script); } END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile_empty) @@ -111,8 +111,8 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle) FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandle"); CHECK(fputs(code, script_stream) != EOF); CHECK(fseek(script_stream, 0, SEEK_SET) != EOF); - return tryScript(JS_CompileFileHandle(cx, global, "temporary file", - script_stream)); + return tryScript(JS_CompileUTF8FileHandle(cx, global, "temporary file", + script_stream)); } END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle) @@ -120,8 +120,8 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle_empty) { TempFile tempScript; FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandle_empty"); - return tryScript(JS_CompileFileHandle(cx, global, "empty temporary file", - script_stream)); + return tryScript(JS_CompileUTF8FileHandle(cx, global, "empty temporary file", + script_stream)); } END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle_empty) @@ -131,8 +131,8 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincip FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandleForPrincipals"); CHECK(fputs(code, script_stream) != EOF); CHECK(fseek(script_stream, 0, SEEK_SET) != EOF); - return tryScript(JS_CompileFileHandleForPrincipals(cx, global, - "temporary file", - script_stream, NULL)); + return tryScript(JS_CompileUTF8FileHandleForPrincipals(cx, global, + "temporary file", + script_stream, NULL)); } END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincipals) diff --git a/deps/mozjs/js/src/jsapi-tests/testSetProperty.cpp b/deps/mozjs/js/src/jsapi-tests/testSetProperty.cpp index 7b242ddffaf..f4f960fc4d7 100644 --- a/deps/mozjs/js/src/jsapi-tests/testSetProperty.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testSetProperty.cpp @@ -70,7 +70,7 @@ BEGIN_TEST(testSetProperty_InheritedGlobalSetter) // This is a JSAPI test because jsapi-test globals do not have a resolve // hook and therefore can use the property cache in some cases where the // shell can't. - JS_ASSERT(JS_GET_CLASS(cx, global)->resolve == &JS_ResolveStub); + JS_ASSERT(JS_GetClass(global)->resolve == &JS_ResolveStub); CHECK(JS_DefineProperty(cx, global, "HOTLOOP", INT_TO_JSVAL(8), NULL, NULL, 0)); EXEC("var n = 0;\n" diff --git a/deps/mozjs/js/src/jsapi-tests/testStringBuffer.cpp b/deps/mozjs/js/src/jsapi-tests/testStringBuffer.cpp index a70296007de..62e66b00e13 100644 --- a/deps/mozjs/js/src/jsapi-tests/testStringBuffer.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testStringBuffer.cpp @@ -14,7 +14,7 @@ BEGIN_TEST(testStringBuffer_finishString) JSString *str = JS_NewStringCopyZ(cx, "foopy"); CHECK(str); - JSAtom *atom = js_AtomizeString(cx, str, 0); + JSAtom *atom = js_AtomizeString(cx, str); CHECK(atom); js::StringBuffer buffer(cx); @@ -22,7 +22,7 @@ BEGIN_TEST(testStringBuffer_finishString) JSAtom *finishedAtom = buffer.finishAtom(); CHECK(finishedAtom); - CHECK(atom == finishedAtom); + CHECK_EQUAL(atom, finishedAtom); return true; } END_TEST(testStringBuffer_finishString) diff --git a/deps/mozjs/js/src/jsapi-tests/testThreadGC.cpp b/deps/mozjs/js/src/jsapi-tests/testThreadGC.cpp deleted file mode 100644 index fadbf97187d..00000000000 --- a/deps/mozjs/js/src/jsapi-tests/testThreadGC.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - */ - -#ifdef JS_THREADSAFE - -#include "tests.h" -#include "prthread.h" - -#include "jscntxt.h" - -/* - * We test that if a GC callback cancels the GC on a child thread the GC can - * still proceed on the main thread even if the child thread continue to - * run uninterrupted. - */ - -struct SharedData { - enum ChildState { - CHILD_STARTING, - CHILD_RUNNING, - CHILD_DONE, - CHILD_ERROR - }; - - JSRuntime *const runtime; - PRThread *const mainThread; - PRLock *const lock; - PRCondVar *const signal; - ChildState childState; - bool childShouldStop; - JSContext *childContext; - - SharedData(JSRuntime *rt, bool *ok) - : runtime(rt), - mainThread(PR_GetCurrentThread()), - lock(PR_NewLock()), - signal(lock ? PR_NewCondVar(lock) : NULL), - childState(CHILD_STARTING), - childShouldStop(false), - childContext(NULL) - { - JS_ASSERT(!*ok); - *ok = !!signal; - } - - ~SharedData() { - if (signal) - PR_DestroyCondVar(signal); - if (lock) - PR_DestroyLock(lock); - } -}; - -static SharedData *shared; - -static JSBool -CancelNonMainThreadGCCallback(JSContext *cx, JSGCStatus status) -{ - return status != JSGC_BEGIN || PR_GetCurrentThread() == shared->mainThread; -} - -static JSBool -StopChildOperationCallback(JSContext *cx) -{ - bool shouldStop; - PR_Lock(shared->lock); - shouldStop = shared->childShouldStop; - PR_Unlock(shared->lock); - return !shouldStop; -} - -static JSBool -NotifyMainThreadAboutBusyLoop(JSContext *cx, uintN argc, jsval *vp) -{ - PR_Lock(shared->lock); - JS_ASSERT(shared->childState == SharedData::CHILD_STARTING); - shared->childState = SharedData::CHILD_RUNNING; - shared->childContext = cx; - PR_NotifyCondVar(shared->signal); - PR_Unlock(shared->lock); - - return true; -} - -static void -ChildThreadMain(void *arg) -{ - JS_ASSERT(!arg); - bool error = true; - JSContext *cx = JS_NewContext(shared->runtime, 8192); - if (cx) { - JS_SetOperationCallback(cx, StopChildOperationCallback); - JSAutoRequest ar(cx); - JSObject *global = JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(), - NULL); - if (global) { - JS_SetGlobalObject(cx, global); - if (JS_InitStandardClasses(cx, global) && - JS_DefineFunction(cx, global, "notify", NotifyMainThreadAboutBusyLoop, 0, 0)) { - - jsval rval; - static const char code[] = "var i = 0; notify(); for (var i = 0; ; ++i);"; - JSBool ok = JS_EvaluateScript(cx, global, code, strlen(code), - __FILE__, __LINE__, &rval); - if (!ok && !JS_IsExceptionPending(cx)) { - /* Evaluate should only return via the callback cancellation. */ - error = false; - } - } - } - } - - PR_Lock(shared->lock); - shared->childState = error ? SharedData::CHILD_DONE : SharedData::CHILD_ERROR; - shared->childContext = NULL; - PR_NotifyCondVar(shared->signal); - PR_Unlock(shared->lock); - - if (cx) - JS_DestroyContextNoGC(cx); -} - -BEGIN_TEST(testThreadGC_bug590533) - { - /* - * Test the child thread busy running while the current thread calls - * the GC both with JSRuntime->gcIsNeeded set and unset. - */ - bool ok = TestChildThread(true); - CHECK(ok); - ok = TestChildThread(false); - CHECK(ok); - return ok; - } - - bool TestChildThread(bool setGCIsNeeded) - { - bool ok = false; - shared = new SharedData(rt, &ok); - CHECK(ok); - - JSGCCallback oldGCCallback = JS_SetGCCallback(cx, CancelNonMainThreadGCCallback); - - PRThread *thread = - PR_CreateThread(PR_USER_THREAD, ChildThreadMain, NULL, - PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - if (!thread) - return false; - - PR_Lock(shared->lock); - while (shared->childState == SharedData::CHILD_STARTING) - PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(shared->childState != SharedData::CHILD_DONE); - ok = (shared->childState == SharedData::CHILD_RUNNING); - PR_Unlock(shared->lock); - - CHECK(ok); - - if (setGCIsNeeded) { - /* - * Use JS internal API to set the GC trigger flag after we know - * that the child is in a request and is about to run an infinite - * loop. Then run the GC with JSRuntime->gcIsNeeded flag set. - */ - js::AutoLockGC lock(rt); - js::TriggerGC(rt); - } - - JS_GC(cx); - - PR_Lock(shared->lock); - shared->childShouldStop = true; - while (shared->childState == SharedData::CHILD_RUNNING) { - JS_TriggerOperationCallback(shared->childContext); - PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT); - } - JS_ASSERT(shared->childState != SharedData::CHILD_STARTING); - ok = (shared->childState == SharedData::CHILD_DONE); - PR_Unlock(shared->lock); - - JS_SetGCCallback(cx, oldGCCallback); - - PR_JoinThread(thread); - - delete shared; - shared = NULL; - - return true; - } - - -END_TEST(testThreadGC_bug590533) - -#endif diff --git a/deps/mozjs/js/src/jsapi-tests/testThreads.cpp b/deps/mozjs/js/src/jsapi-tests/testThreads.cpp deleted file mode 100644 index c6ab6943116..00000000000 --- a/deps/mozjs/js/src/jsapi-tests/testThreads.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - */ - -#ifdef JS_THREADSAFE - -#include "tests.h" -#include "prthread.h" - -struct ThreadData { - JSRuntime *rt; - JSObject *obj; - const char *code; - bool ok; -}; - -BEGIN_TEST(testThreads_bug561444) - { - const char *code = ".b.@c = '';"; - EXEC(code); - - jsrefcount rc = JS_SuspendRequest(cx); - { - ThreadData data = {rt, global, code, false}; - PRThread *thread = - PR_CreateThread(PR_USER_THREAD, threadMain, &data, - PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - CHECK(thread); - PR_JoinThread(thread); - CHECK(data.ok); - } - JS_ResumeRequest(cx, rc); - return true; - } - - static void threadMain(void *arg) { - ThreadData *d = (ThreadData *) arg; - - JSContext *cx = JS_NewContext(d->rt, 8192); - if (!cx) - return; - JS_BeginRequest(cx); - { - JSAutoEnterCompartment ac; - jsval v; - d->ok = ac.enter(cx, d->obj) && - JS_EvaluateScript(cx, d->obj, d->code, strlen(d->code), __FILE__, __LINE__, - &v); - } - JS_DestroyContext(cx); - } -END_TEST(testThreads_bug561444) - -const PRUint32 NATIVE_STACK_SIZE = 64 * 1024; -const PRUint32 NATIVE_STACK_HEADROOM = 8 * 1024; - -template -class Repeat { - size_t n; - const T &t; - - public: - Repeat(size_t n, const T &t) : n(n), t(t) {} - - bool operator()() const { - for (size_t i = 0; i < n; i++) - if (!t()) - return false; - return true; - } -}; - -template Repeat repeat(size_t n, const T &t) { return Repeat(n, t); } - -/* Class of callable that does something in n parallel threads. */ -template -class Parallel { - size_t n; - const T &t; - - struct pair { const Parallel *self; bool ok; }; - - static void threadMain(void *arg) { - pair *p = (pair *) arg; - if (!p->self->t()) - p->ok = false; - } - - public: - Parallel(size_t n, const T &t) : n(n), t(t) {} - - bool operator()() const { - pair p = {this, true}; - - PRThread **thread = new PRThread *[n]; - if (!thread) - return false; - - size_t i; - for (i = 0; i < n; i++) { - thread[i] = PR_CreateThread(PR_USER_THREAD, threadMain, &p, PR_PRIORITY_NORMAL, - PR_LOCAL_THREAD, PR_JOINABLE_THREAD, NATIVE_STACK_SIZE); - if (thread[i] == NULL) { - p.ok = false; - break; - } - } - while (i--) - PR_JoinThread(thread[i]); - - delete[] thread; - return p.ok; - } -}; - -template Parallel parallel(size_t n, const T &t) { return Parallel(n, t); } - -/* Class of callable that creates a compartment and runs some code in it. */ -class eval { - JSRuntime *rt; - const char *code; - - public: - eval(JSRuntime *rt, const char *code) : rt(rt), code(code) {} - - bool operator()() const { - JSContext *cx = JS_NewContext(rt, 8192); - if (!cx) - return false; - - JS_SetNativeStackQuota(cx, NATIVE_STACK_SIZE - NATIVE_STACK_HEADROOM); - bool ok = false; - { - JSAutoRequest ar(cx); - JSObject *global = - JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(), NULL); - if (global) { - JS_SetGlobalObject(cx, global); - jsval rval; - ok = JS_InitStandardClasses(cx, global) && - JS_EvaluateScript(cx, global, code, strlen(code), "", 0, &rval); - } - } - JS_DestroyContextMaybeGC(cx); - return ok; - } -}; - -BEGIN_TEST(testThreads_bug604782) -{ - jsrefcount rc = JS_SuspendRequest(cx); - bool ok = repeat(20, parallel(3, eval(rt, "for(i=0;i<1000;i++);")))(); - JS_ResumeRequest(cx, rc); - CHECK(ok); - return true; -} -END_TEST(testThreads_bug604782) - -BEGIN_TEST(testThreads_bug609103) -{ - const char *code = - "var x = {};\n" - "for (var i = 0; i < 10000; i++)\n" - " x = {next: x};\n"; - - jsrefcount rc = JS_SuspendRequest(cx); - bool ok = parallel(2, eval(rt, code))(); - JS_ResumeRequest(cx, rc); - CHECK(ok); - return true; -} -END_TEST(testThreads_bug609103) - -#endif diff --git a/deps/mozjs/js/src/jsapi-tests/testTrap.cpp b/deps/mozjs/js/src/jsapi-tests/testTrap.cpp index 6368dd2c248..24cd3bda35f 100644 --- a/deps/mozjs/js/src/jsapi-tests/testTrap.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testTrap.cpp @@ -30,17 +30,14 @@ BEGIN_TEST(testTrap_gc) ; // compile - JSObject *scriptObj = JS_CompileScript(cx, global, source, strlen(source), __FILE__, 1); - CHECK(scriptObj); + JSScript *script = JS_CompileScript(cx, global, source, strlen(source), __FILE__, 1); + CHECK(script); // execute jsvalRoot v2(cx); - CHECK(JS_ExecuteScript(cx, global, scriptObj, v2.addr())); + CHECK(JS_ExecuteScript(cx, global, script, v2.addr())); CHECK(JSVAL_IS_OBJECT(v2)); - CHECK(emptyTrapCallCount == 0); - - // Disable JIT for debugging - JS_SetOptions(cx, JS_GetOptions(cx) & ~JSOPTION_JIT); + CHECK_EQUAL(emptyTrapCallCount, 0); // Enable debug mode CHECK(JS_SetDebugMode(cx, JS_TRUE)); @@ -51,26 +48,25 @@ BEGIN_TEST(testTrap_gc) // 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, scriptObj, v2.addr())); - CHECK(emptyTrapCallCount == 11); + CHECK(JS_ExecuteScript(cx, global, script, v2.addr())); + CHECK_EQUAL(emptyTrapCallCount, 11); JS_GC(cx); diff --git a/deps/mozjs/js/src/jsapi-tests/testUTF8.cpp b/deps/mozjs/js/src/jsapi-tests/testUTF8.cpp index fdbe5e999d8..e40da990b07 100644 --- a/deps/mozjs/js/src/jsapi-tests/testUTF8.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testUTF8.cpp @@ -11,10 +11,10 @@ BEGIN_TEST(testUTF8_bug589917) size_t utf8_len = sizeof(output_buffer); CHECK(JS_EncodeCharacters(cx, surrogate_pair, 2, output_buffer, &utf8_len)); - CHECK(utf8_len == 4); + CHECK_EQUAL(utf8_len, 4); CHECK(JS_EncodeCharacters(cx, surrogate_pair, 2, NULL, &utf8_len)); - CHECK(utf8_len == 4); + CHECK_EQUAL(utf8_len, 4); return true; } diff --git a/deps/mozjs/js/src/jsapi-tests/testValueABI.cpp b/deps/mozjs/js/src/jsapi-tests/testValueABI.cpp new file mode 100644 index 00000000000..0725715e1c3 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/testValueABI.cpp @@ -0,0 +1,50 @@ +#include "tests.h" + +/* + * Bug 689101 - jsval is technically a non-POD type because it has a private + * data member. On gcc, this doesn't seem to matter. On MSVC, this prevents + * returning a jsval from a function between C and C++ because it will use a + * retparam in C++ and a direct return value in C. + * + * Bug 712289 - jsval alignment was different on 32-bit platforms between C and + * C++ because the default alignments of js::Value and jsval_layout differ. + */ + +extern "C" { + +extern JSBool +C_ValueToObject(JSContext *cx, jsval v, JSObject **obj); + +extern jsval +C_GetEmptyStringValue(JSContext *cx); + +extern size_t +C_jsvalAlignmentTest(); + +} + +BEGIN_TEST(testValueABI_retparam) +{ + JSObject* obj = JS_GetGlobalObject(cx); + jsval v = OBJECT_TO_JSVAL(obj); + obj = NULL; + CHECK(C_ValueToObject(cx, v, &obj)); + JSBool equal; + CHECK(JS_StrictlyEqual(cx, v, OBJECT_TO_JSVAL(obj), &equal)); + CHECK(equal); + + v = C_GetEmptyStringValue(cx); + CHECK(JSVAL_IS_STRING(v)); + + return true; +} +END_TEST(testValueABI_retparam) + +BEGIN_TEST(testValueABI_alignment) +{ + typedef struct { char c; jsval v; } AlignTest; + CHECK(C_jsvalAlignmentTest() == sizeof(AlignTest)); + + return true; +} +END_TEST(testValueABI_alignment) diff --git a/deps/mozjs/js/src/jsapi-tests/testVersion.cpp b/deps/mozjs/js/src/jsapi-tests/testVersion.cpp index a366382f03f..4d6d9567527 100644 --- a/deps/mozjs/js/src/jsapi-tests/testVersion.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testVersion.cpp @@ -2,6 +2,9 @@ #include "jsscript.h" #include "jscntxt.h" +#include "jscntxtinlines.h" +#include "jsobjinlines.h" + using namespace js; struct VersionFixture; @@ -42,7 +45,7 @@ struct VersionFixture : public JSAPITest EvalScriptVersion16, 0, 0); } - JSObject *fakeScript(const char *contents, size_t length) { + JSScript *fakeScript(const char *contents, size_t length) { return JS_CompileScript(cx, global, contents, length, "", 1); } @@ -75,9 +78,9 @@ struct VersionFixture : public JSAPITest /* Check that script compilation results in a version without XML. */ bool checkNewScriptNoXML() { - JSObject *scriptObj = fakeScript("", 0); - CHECK(scriptObj); - CHECK(!hasXML(JS_GetScriptFromObject(scriptObj)->getVersion())); + JSScript *script = fakeScript("", 0); + CHECK(script); + CHECK(!hasXML(script->getVersion())); return true; } @@ -101,9 +104,9 @@ struct VersionFixture : public JSAPITest } bool toggleXML(bool shouldEnable) { - CHECK(hasXML() == !shouldEnable); + CHECK_EQUAL(hasXML(), !shouldEnable); JS_ToggleOptions(cx, JSOPTION_XML); - CHECK(hasXML() == shouldEnable); + CHECK_EQUAL(hasXML(), shouldEnable); return true; } @@ -196,9 +199,9 @@ BEGIN_FIXTURE_TEST(VersionFixture, testOptionsAreUsedForVersionFlags) "disableXMLOption();" "callSetVersion17();" "checkNewScriptNoXML();"; - JSObject *toActivate = fakeScript(toActivateChars, sizeof(toActivateChars) - 1); + JSScript *toActivate = fakeScript(toActivateChars, sizeof(toActivateChars) - 1); CHECK(toActivate); - CHECK(hasXML(JS_GetScriptFromObject(toActivate))); + CHECK(hasXML(toActivate)); disableXML(); @@ -218,13 +221,13 @@ END_FIXTURE_TEST(VersionFixture, testOptionsAreUsedForVersionFlags) BEGIN_FIXTURE_TEST(VersionFixture, testEntryLosesOverride) { EXEC("overrideVersion15(); evalScriptVersion16('checkOverride(false); captureVersion()');"); - CHECK(captured == JSVERSION_1_6); + CHECK_EQUAL(captured, JSVERSION_1_6); /* * Override gets propagated to default version as non-override when you leave the VM's execute * call. */ - CHECK(JS_GetVersion(cx) == JSVERSION_1_5); + CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_1_5); CHECK(!cx->isVersionOverridden()); return true; } @@ -238,28 +241,28 @@ END_FIXTURE_TEST(VersionFixture, testEntryLosesOverride) */ BEGIN_FIXTURE_TEST(VersionFixture, testReturnLosesOverride) { - CHECK(JS_GetVersion(cx) == JSVERSION_ECMA_5); + CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_ECMA_5); EXEC( "checkOverride(false);" "evalScriptVersion16('overrideVersion15();');" "checkOverride(false);" "captureVersion();" ); - CHECK(captured == JSVERSION_ECMA_5); + CHECK_EQUAL(captured, JSVERSION_ECMA_5); return true; } END_FIXTURE_TEST(VersionFixture, testReturnLosesOverride) BEGIN_FIXTURE_TEST(VersionFixture, testEvalPropagatesOverride) { - CHECK(JS_GetVersion(cx) == JSVERSION_ECMA_5); + CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_ECMA_5); EXEC( "checkOverride(false);" "eval('overrideVersion15();');" "checkOverride(true);" "captureVersion();" ); - CHECK(captured == JSVERSION_1_5); + CHECK_EQUAL(captured, JSVERSION_1_5); return true; } END_FIXTURE_TEST(VersionFixture, testEvalPropagatesOverride) diff --git a/deps/mozjs/js/src/jsapi-tests/testXDR.cpp b/deps/mozjs/js/src/jsapi-tests/testXDR.cpp index 5921b2a24c4..f2b3b12cf8a 100644 --- a/deps/mozjs/js/src/jsapi-tests/testXDR.cpp +++ b/deps/mozjs/js/src/jsapi-tests/testXDR.cpp @@ -16,31 +16,31 @@ BEGIN_TEST(testXDR_bug506491) "var f = makeClosure('0;', 'status', 'ok');\n"; // compile - JSObject *scriptObj = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__); - CHECK(scriptObj); + JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__); + CHECK(script); // freeze JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); CHECK(w); - CHECK(JS_XDRScriptObject(w, &scriptObj)); - uint32 nbytes; + CHECK(JS_XDRScript(w, &script)); + uint32_t nbytes; void *p = JS_XDRMemGetData(w, &nbytes); CHECK(p); void *frozen = JS_malloc(cx, nbytes); CHECK(frozen); - memcpy(frozen, p, nbytes); + js_memcpy(frozen, p, nbytes); JS_XDRDestroy(w); // thaw - scriptObj = NULL; + script = NULL; JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE); JS_XDRMemSetData(r, frozen, nbytes); - CHECK(JS_XDRScriptObject(r, &scriptObj)); + CHECK(JS_XDRScript(r, &script)); JS_XDRDestroy(r); // this frees `frozen` // execute jsvalRoot v2(cx); - CHECK(JS_ExecuteScript(cx, global, scriptObj, v2.addr())); + CHECK(JS_ExecuteScript(cx, global, script, v2.addr())); // try to break the Block object that is the parent of f JS_GC(cx); @@ -56,30 +56,30 @@ END_TEST(testXDR_bug506491) BEGIN_TEST(testXDR_bug516827) { // compile an empty script - JSObject *scriptObj = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__); - CHECK(scriptObj); + JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__); + CHECK(script); // freeze JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); CHECK(w); - CHECK(JS_XDRScriptObject(w, &scriptObj)); - uint32 nbytes; + CHECK(JS_XDRScript(w, &script)); + uint32_t nbytes; void *p = JS_XDRMemGetData(w, &nbytes); CHECK(p); void *frozen = JS_malloc(cx, nbytes); CHECK(frozen); - memcpy(frozen, p, nbytes); + js_memcpy(frozen, p, nbytes); JS_XDRDestroy(w); // thaw - scriptObj = NULL; + script = NULL; JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE); JS_XDRMemSetData(r, frozen, nbytes); - CHECK(JS_XDRScriptObject(r, &scriptObj)); + CHECK(JS_XDRScript(r, &script)); JS_XDRDestroy(r); // this frees `frozen` // execute with null result meaning no result wanted - CHECK(JS_ExecuteScript(cx, global, scriptObj, NULL)); + CHECK(JS_ExecuteScript(cx, global, script, NULL)); return true; } END_TEST(testXDR_bug516827) diff --git a/deps/mozjs/js/src/jsapi-tests/tests.h b/deps/mozjs/js/src/jsapi-tests/tests.h index 183b0cd70bd..5868b083152 100644 --- a/deps/mozjs/js/src/jsapi-tests/tests.h +++ b/deps/mozjs/js/src/jsapi-tests/tests.h @@ -38,9 +38,14 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/Util.h" + #include "jsapi.h" #include "jsprvtd.h" -#include "jsvector.h" +#include "jsalloc.h" + +#include "js/Vector.h" + #include #include #include @@ -172,7 +177,7 @@ class JSAPITest fail(bytes, filename, lineno); } - JSAPITestString toSource(jsval v) { + JSAPITestString jsvalToSource(jsval v) { JSString *str = JS_ValueToSource(cx, v); if (str) { JSAutoByteString bytes(cx, str); @@ -183,9 +188,75 @@ class JSAPITest return JSAPITestString("<>"); } -#define CHECK_SAME(actual, expected) \ + JSAPITestString toSource(long v) { + char buf[40]; + sprintf(buf, "%ld", v); + return JSAPITestString(buf); + } + + JSAPITestString toSource(unsigned long v) { + char buf[40]; + sprintf(buf, "%lu", v); + return JSAPITestString(buf); + } + + JSAPITestString toSource(long long v) { + char buf[40]; + sprintf(buf, "%lld", v); + return JSAPITestString(buf); + } + + JSAPITestString toSource(unsigned long long v) { + char buf[40]; + sprintf(buf, "%llu", v); + return JSAPITestString(buf); + } + + JSAPITestString toSource(unsigned int v) { + return toSource((unsigned long)v); + } + + JSAPITestString toSource(int v) { + return toSource((long)v); + } + + JSAPITestString toSource(bool v) { + return JSAPITestString(v ? "true" : "false"); + } + + JSAPITestString toSource(JSAtom *v) { + return jsvalToSource(STRING_TO_JSVAL((JSString*)v)); + } + + JSAPITestString toSource(JSVersion v) { + return JSAPITestString(JS_VersionToString(v)); + } + + template + bool checkEqual(const T &actual, const T &expected, + const char *actualExpr, const char *expectedExpr, + const char *filename, int lineno) { + return (actual == expected) || + fail(JSAPITestString("CHECK_EQUAL failed: expected (") + + expectedExpr + ") = " + toSource(expected) + + ", got (" + actualExpr + ") = " + toSource(actual), filename, lineno); + } + + // There are many cases where the static types of 'actual' and 'expected' + // are not identical, and C++ is understandably cautious about automatic + // coercions. So catch those cases and forcibly coerce, then use the + // identical-type specialization. This may do bad things if the types are + // actually *not* compatible. + template + bool checkEqual(const T &actual, const U &expected, + const char *actualExpr, const char *expectedExpr, + const char *filename, int lineno) { + return checkEqual(U(actual), expected, actualExpr, expectedExpr, filename, lineno); + } + +#define CHECK_EQUAL(actual, expected) \ do { \ - if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ + if (!checkEqual(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ return false; \ } while (false) @@ -196,9 +267,15 @@ class JSAPITest return (JS_SameValue(cx, actual, expected, &same) && same) || fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") + actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " + - toSource(actual) + ", " + toSource(expected) + ")", filename, lineno); + jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno); } +#define CHECK_SAME(actual, expected) \ + do { \ + if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ + return false; \ + } while (false) + #define CHECK(expr) \ do { \ if (!(expr)) \ @@ -261,7 +338,25 @@ class JSAPITest } virtual JSRuntime * createRuntime() { - return JS_NewRuntime(8L * 1024 * 1024); + JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024); + if (!rt) + return NULL; + + const size_t MAX_STACK_SIZE = +/* Assume we can't use more than 5e5 bytes of C stack by default. */ +#if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC) + /* + * Sun compiler uses a larger stack space for js::Interpret() with + * debug. Use a bigger gMaxStackSize to make "make check" happy. + */ + 5000000 +#else + 500000 +#endif + ; + + JS_SetNativeStackQuota(rt, MAX_STACK_SIZE); + return rt; } virtual void destroyRuntime() { @@ -281,23 +376,7 @@ class JSAPITest JSContext *cx = JS_NewContext(rt, 8192); if (!cx) return NULL; - - const size_t MAX_STACK_SIZE = -/* Assume we can't use more than 5e5 bytes of C stack by default. */ -#if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC) - /* - * Sun compiler uses a larger stack space for js::Interpret() with - * debug. Use a bigger gMaxStackSize to make "make check" happy. - */ - 5000000 -#else - 500000 -#endif - ; - - JS_SetNativeStackQuota(cx, MAX_STACK_SIZE); - - JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT); + JS_SetOptions(cx, JSOPTION_VAROBJFIX); JS_SetVersion(cx, JSVERSION_LATEST); JS_SetErrorReporter(cx, &reportError); return cx; @@ -307,9 +386,9 @@ class JSAPITest return basicGlobalClass(); } - virtual JSObject * createGlobal() { + virtual JSObject * createGlobal(JSPrincipals *principals = NULL) { /* Create the global object. */ - JSObject *global = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + JSObject *global = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), principals); if (!global) return NULL; @@ -355,7 +434,7 @@ class JSAPITest /* * A class for creating and managing one temporary file. - * + * * We could use the ISO C temporary file functions here, but those try to * create files in the root directory on Windows, which fails for users * without Administrator privileges. @@ -386,7 +465,7 @@ class TempFile { fprintf(stderr, "error opening temporary file '%s': %s\n", fileName, strerror(errno)); exit(1); - } + } name = fileName; return stream; } diff --git a/deps/mozjs/js/src/jsapi-tests/valueABI.c b/deps/mozjs/js/src/jsapi-tests/valueABI.c new file mode 100644 index 00000000000..3d67fb966d4 --- /dev/null +++ b/deps/mozjs/js/src/jsapi-tests/valueABI.c @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=c: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" + +/* See testValueABI.cpp */ + +JSBool +C_ValueToObject(JSContext *cx, jsval v, JSObject **obj) +{ + return JS_ValueToObject(cx, v, obj); +} + +jsval +C_GetEmptyStringValue(JSContext *cx) +{ + return JS_GetEmptyStringValue(cx); +} + +size_t +C_jsvalAlignmentTest() +{ + typedef struct { char c; jsval v; } AlignTest; + return sizeof(AlignTest); +} diff --git a/deps/mozjs/js/src/jsapi.cpp b/deps/mozjs/js/src/jsapi.cpp index 82c60f173fa..a214c1f6758 100644 --- a/deps/mozjs/js/src/jsapi.cpp +++ b/deps/mozjs/js/src/jsapi.cpp @@ -47,8 +47,6 @@ #include #include #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsdhash.h" @@ -57,12 +55,11 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jsclone.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdate.h" -#include "jsemit.h" +#include "jsdtoa.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" @@ -71,33 +68,41 @@ #include "jsiter.h" #include "jslock.h" #include "jsmath.h" +#include "jsnativestack.h" #include "jsnum.h" #include "json.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsparse.h" +#include "jsprobes.h" #include "jsproxy.h" -#include "jsregexp.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#include "jstracer.h" #include "prmjtime.h" -#include "jsstaticcheck.h" -#include "jsvector.h" #include "jsweakmap.h" #include "jswrapper.h" #include "jstypedarray.h" +#include "ds/LifoAlloc.h" +#include "builtin/MapObject.h" +#include "builtin/RegExp.h" +#include "frontend/BytecodeCompiler.h" +#include "frontend/BytecodeEmitter.h" +#include "gc/Memory.h" +#include "js/MemoryMetrics.h" +#include "mozilla/Util.h" +#include "yarr/BumpPointerAllocator.h" + #include "jsatominlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsscopeinlines.h" -#include "jsregexpinlines.h" #include "jsscriptinlines.h" -#include "jsstrinlines.h" -#include "assembler/wtf/Platform.h" +#include "vm/RegExpObject-inl.h" +#include "vm/RegExpStatics-inl.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" @@ -110,6 +115,7 @@ using namespace js; using namespace js::gc; +using namespace js::types; /* * This class is a version-establising barrier at the head of a VM entry or @@ -140,12 +146,6 @@ class AutoVersionAPI , oldCompileOptions(cx->getCompileOptions()) #endif { - /* - * Note: ANONFUNFIX in newVersion is ignored for backwards - * compatibility, must be set via JS_SetOptions. (Because of this, we - * inherit the current ANONFUNFIX setting from the options. - */ - VersionSetAnonFunFix(&newVersion, OptionsHasAnonFunFix(cx->getCompileOptions())); this->newVersion = newVersion; cx->clearVersionOverride(); cx->setDefaultVersion(newVersion); @@ -170,26 +170,24 @@ class AutoVersionAPI #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES -JS_PUBLIC_DATA(jsid) JS_DEFAULT_XML_NAMESPACE_ID = { size_t(JSID_TYPE_DEFAULT_XML_NAMESPACE) }; -JS_PUBLIC_DATA(jsid) JSID_VOID = { size_t(JSID_TYPE_VOID) }; -JS_PUBLIC_DATA(jsid) JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) }; +#ifdef JS_USE_JSID_STRUCT_TYPES +jsid JS_DEFAULT_XML_NAMESPACE_ID = { size_t(JSID_TYPE_DEFAULT_XML_NAMESPACE) }; +jsid JSID_VOID = { size_t(JSID_TYPE_VOID) }; +jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) }; #endif -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES -JS_PUBLIC_DATA(jsval) JSVAL_NULL = { BUILD_JSVAL(JSVAL_TAG_NULL, 0) }; -JS_PUBLIC_DATA(jsval) JSVAL_ZERO = { BUILD_JSVAL(JSVAL_TAG_INT32, 0) }; -JS_PUBLIC_DATA(jsval) JSVAL_ONE = { BUILD_JSVAL(JSVAL_TAG_INT32, 1) }; -JS_PUBLIC_DATA(jsval) JSVAL_FALSE = { BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_FALSE) }; -JS_PUBLIC_DATA(jsval) JSVAL_TRUE = { BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_TRUE) }; -JS_PUBLIC_DATA(jsval) JSVAL_VOID = { BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0) }; -#endif +const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); +const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); +const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); +const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_FALSE)); +const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_TRUE)); +const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); /* Make sure that jschar is two bytes unsigned integer */ JS_STATIC_ASSERT((jschar)-1 > 0); JS_STATIC_ASSERT(sizeof(jschar) == 2); -JS_PUBLIC_API(int64) +JS_PUBLIC_API(int64_t) JS_Now() { return PRMJ_Now(); @@ -198,19 +196,19 @@ JS_Now() JS_PUBLIC_API(jsval) JS_GetNaNValue(JSContext *cx) { - return Jsvalify(cx->runtime->NaNValue); + return cx->runtime->NaNValue; } JS_PUBLIC_API(jsval) JS_GetNegativeInfinityValue(JSContext *cx) { - return Jsvalify(cx->runtime->negativeInfinityValue); + return cx->runtime->negativeInfinityValue; } JS_PUBLIC_API(jsval) JS_GetPositiveInfinityValue(JSContext *cx) { - return Jsvalify(cx->runtime->positiveInfinityValue); + return cx->runtime->positiveInfinityValue; } JS_PUBLIC_API(jsval) @@ -222,7 +220,7 @@ JS_GetEmptyStringValue(JSContext *cx) JS_PUBLIC_API(JSString *) JS_GetEmptyString(JSRuntime *rt) { - JS_ASSERT(rt->state == JSRTS_UP); + JS_ASSERT(rt->hasContexts()); return rt->emptyString; } @@ -243,12 +241,36 @@ TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, jsval * return JS_FALSE; } +static void +AssertNoGC(JSRuntime *rt) +{ + JS_ASSERT(!rt->gcRunning); +} + +static void +AssertNoGC(JSContext *cx) +{ + AssertNoGC(cx->runtime); +} + +static void +AssertNoGCOrFlatString(JSContext *cx, JSString *str) +{ + /* + * We allow some functions to be called during a GC as long as the argument + * is a flat string, since that will not cause allocation. + */ + JS_ASSERT_IF(cx->runtime->gcRunning, str->isFlat()); +} + JS_PUBLIC_API(JSBool) JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, ...) { va_list ap; JSBool ok; + AssertNoGC(cx); + va_start(ap, format); ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); va_end(ap); @@ -266,6 +288,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format JSString *str; JSObject *obj; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, JSValueArray(argv - 2, argc + 2)); sp = argv; @@ -279,7 +302,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format } if (sp == argv + argc) { if (required) { - fun = js_ValueToFunction(cx, Valueify(&argv[-2]), 0); + fun = js_ValueToFunction(cx, &argv[-2], 0); if (fun) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", argc); @@ -295,22 +318,22 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format } switch (c) { case 'b': - *va_arg(ap, JSBool *) = js_ValueToBoolean(Valueify(*sp)); + *va_arg(ap, JSBool *) = js_ValueToBoolean(*sp); break; case 'c': - if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) + if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16_t *))) return JS_FALSE; break; case 'i': - if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) + if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32_t *))) return JS_FALSE; break; case 'u': - if (!JS_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) + if (!JS_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32_t *))) return JS_FALSE; break; case 'j': - if (!JS_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) + if (!JS_ValueToInt32(cx, *sp, va_arg(ap, int32_t *))) return JS_FALSE; break; case 'd': @@ -324,7 +347,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format break; case 'S': case 'W': - str = js_ValueToString(cx, Valueify(*sp)); + str = ToString(cx, *sp); if (!str) return JS_FALSE; *sp = STRING_TO_JSVAL(str); @@ -338,17 +361,17 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format } break; case 'o': - if (!js_ValueToObjectOrNull(cx, Valueify(*sp), &obj)) + if (!js_ValueToObjectOrNull(cx, *sp, &obj)) return JS_FALSE; *sp = OBJECT_TO_JSVAL(obj); *va_arg(ap, JSObject **) = obj; break; case 'f': - obj = js_ValueToFunctionObject(cx, Valueify(sp), 0); + obj = js_ValueToFunction(cx, sp, 0); if (!obj) return JS_FALSE; *sp = OBJECT_TO_JSVAL(obj); - *va_arg(ap, JSFunction **) = GET_FUNCTION_PRIVATE(cx, obj); + *va_arg(ap, JSFunction **) = obj->toFunction(); break; case 'v': *va_arg(ap, jsval *) = *sp; @@ -423,6 +446,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) JSString *str; jsdouble d; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); switch (type) { @@ -431,17 +455,17 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) ok = JS_TRUE; break; case JSTYPE_OBJECT: - ok = js_ValueToObjectOrNull(cx, Valueify(v), &obj); + ok = js_ValueToObjectOrNull(cx, v, &obj); if (ok) *vp = OBJECT_TO_JSVAL(obj); break; case JSTYPE_FUNCTION: *vp = v; - obj = js_ValueToFunctionObject(cx, Valueify(vp), JSV2F_SEARCH_STACK); + obj = js_ValueToFunction(cx, vp, JSV2F_SEARCH_STACK); ok = (obj != NULL); break; case JSTYPE_STRING: - str = js_ValueToString(cx, Valueify(v)); + str = ToString(cx, v); ok = (str != NULL); if (ok) *vp = STRING_TO_JSVAL(str); @@ -452,7 +476,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) *vp = DOUBLE_TO_JSVAL(d); break; case JSTYPE_BOOLEAN: - *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(Valueify(v))); + *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(v)); return JS_TRUE; default: { char numBuf[12]; @@ -468,51 +492,57 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) JS_PUBLIC_API(JSBool) JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return js_ValueToObjectOrNull(cx, Valueify(v), objp); + return js_ValueToObjectOrNull(cx, v, objp); } JS_PUBLIC_API(JSFunction *) JS_ValueToFunction(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return js_ValueToFunction(cx, Valueify(&v), JSV2F_SEARCH_STACK); + return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); } JS_PUBLIC_API(JSFunction *) JS_ValueToConstructor(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return js_ValueToFunction(cx, Valueify(&v), JSV2F_SEARCH_STACK); + return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); } JS_PUBLIC_API(JSString *) JS_ValueToString(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return js_ValueToString(cx, Valueify(v)); + return ToString(cx, v); } JS_PUBLIC_API(JSString *) JS_ValueToSource(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return js_ValueToSource(cx, Valueify(v)); + return js_ValueToSource(cx, v); } JS_PUBLIC_API(JSBool) JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - AutoValueRooter tvr(cx, Valueify(v)); - return ValueToNumber(cx, tvr.value(), dp); + AutoValueRooter tvr(cx, v); + return ToNumber(cx, tvr.value(), dp); } JS_PUBLIC_API(JSBool) @@ -521,73 +551,79 @@ JS_DoubleIsInt32(jsdouble d, jsint *ip) return JSDOUBLE_IS_INT32(d, (int32_t *)ip); } -JS_PUBLIC_API(int32) +JS_PUBLIC_API(int32_t) JS_DoubleToInt32(jsdouble d) { return js_DoubleToECMAInt32(d); } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_DoubleToUint32(jsdouble d) { return js_DoubleToECMAUint32(d); } JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - AutoValueRooter tvr(cx, Valueify(v)); - return ValueToECMAInt32(cx, tvr.value(), (int32_t *)ip); + AutoValueRooter tvr(cx, v); + return ToInt32(cx, tvr.value(), (int32_t *)ip); } JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - AutoValueRooter tvr(cx, Valueify(v)); - return ValueToECMAUint32(cx, tvr.value(), (uint32_t *)ip); + AutoValueRooter tvr(cx, v); + return ToUint32(cx, tvr.value(), (uint32_t *)ip); } JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) +JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - AutoValueRooter tvr(cx, Valueify(v)); - return ValueToInt32(cx, tvr.value(), (int32_t *)ip); + AutoValueRooter tvr(cx, v); + return NonstandardToInt32(cx, tvr.value(), (int32_t *)ip); } JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) +JS_ValueToUint16(JSContext *cx, jsval v, uint16_t *ip) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - AutoValueRooter tvr(cx, Valueify(v)); + AutoValueRooter tvr(cx, v); return ValueToUint16(cx, tvr.value(), (uint16_t *)ip); } JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - *bp = js_ValueToBoolean(Valueify(v)); + *bp = js_ValueToBoolean(v); return JS_TRUE; } JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return TypeOfValue(cx, Valueify(v)); + return TypeOfValue(cx, v); } JS_PUBLIC_API(const char *) @@ -601,22 +637,40 @@ JS_GetTypeName(JSContext *cx, JSType type) JS_PUBLIC_API(JSBool) JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal) { + AssertNoGC(cx); + CHECK_REQUEST(cx); assertSameCompartment(cx, v1, v2); - return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal); + bool eq; + if (!StrictlyEqual(cx, v1, v2, &eq)) + return false; + *equal = eq; + return true; } JS_PUBLIC_API(JSBool) JS_LooselyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal) { + AssertNoGC(cx); + CHECK_REQUEST(cx); assertSameCompartment(cx, v1, v2); - return LooselyEqual(cx, Valueify(v1), Valueify(v2), equal); + bool eq; + if (!LooselyEqual(cx, v1, v2, &eq)) + return false; + *equal = eq; + return true; } JS_PUBLIC_API(JSBool) JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same) { + AssertNoGC(cx); + CHECK_REQUEST(cx); assertSameCompartment(cx, v1, v2); - return SameValue(cx, Valueify(v1), Valueify(v2), same); + bool s; + if (!SameValue(cx, v1, v2, &s)) + return false; + *same = s; + return true; } JS_PUBLIC_API(JSBool) @@ -636,79 +690,164 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun) /* * Has a new runtime ever been created? This flag is used to detect unsafe * changes to js_CStringsAreUTF8 after a runtime has been created, and to - * ensure that "first checks" on runtime creation are run only once. + * control things that should happen only once across all runtimes. */ -#ifdef DEBUG static JSBool js_NewRuntimeWasCalled = JS_FALSE; -#endif JSRuntime::JSRuntime() - : gcChunkAllocator(&defaultGCChunkAllocator) + : atomsCompartment(NULL), +#ifdef JS_THREADSAFE + ownerThread_(NULL), +#endif + tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + execAlloc_(NULL), + bumpAlloc_(NULL), + nativeStackBase(0), + nativeStackQuota(0), + interpreterFrames(NULL), + cxCallback(NULL), + compartmentCallback(NULL), + activityCallback(NULL), + activityCallbackArg(NULL), +#ifdef JS_THREADSAFE + suspendCount(0), + requestDepth(0), +# ifdef DEBUG + checkRequestDepth(0), +# endif +#endif + gcSystemAvailableChunkListHead(NULL), + gcUserAvailableChunkListHead(NULL), + gcKeepAtoms(0), + gcBytes(0), + gcMaxBytes(0), + gcMaxMallocBytes(0), + gcNumArenasFreeCommitted(0), + gcNumber(0), + gcIncrementalTracer(NULL), + gcVerifyData(NULL), + gcChunkAllocationSinceLastGC(false), + gcNextFullGCTime(0), + gcJitReleaseTime(0), + gcMode(JSGC_MODE_GLOBAL), + gcIsNeeded(0), + gcWeakMapList(NULL), + gcStats(thisFromCtor()), + gcTriggerReason(gcreason::NO_REASON), + gcTriggerCompartment(NULL), + gcCurrentCompartment(NULL), + gcCheckCompartment(NULL), + gcPoke(false), + gcMarkAndSweep(false), + gcRunning(false), +#ifdef JS_GC_ZEAL + gcZeal_(0), + gcZealFrequency(0), + gcNextScheduled(0), + gcDebugCompartmentGC(false), +#endif + gcCallback(NULL), + gcFinishedCallback(NULL), + gcMallocBytes(0), + gcBlackRootsTraceOp(NULL), + gcBlackRootsData(NULL), + gcGrayRootsTraceOp(NULL), + gcGrayRootsData(NULL), + scriptPCCounters(NULL), + NaNValue(UndefinedValue()), + negativeInfinityValue(UndefinedValue()), + positiveInfinityValue(UndefinedValue()), + emptyString(NULL), + debugMode(false), + profilingScripts(false), + hadOutOfMemory(false), + data(NULL), +#ifdef JS_THREADSAFE + gcLock(NULL), + gcHelperThread(thisFromCtor()), +#endif + debuggerMutations(0), + securityCallbacks(NULL), + structuredCloneCallbacks(NULL), + telemetryCallback(NULL), + propertyRemovals(0), + thousandsSeparator(0), + decimalSeparator(0), + numGrouping(0), + anynameObject(NULL), + functionNamespaceObject(NULL), + waiveGCQuota(false), + dtoaState(NULL), + pendingProxyOperation(NULL), + trustedPrincipals_(NULL), + wrapObjectCallback(TransparentObjectWrapper), + preWrapObjectCallback(NULL), + preserveWrapperCallback(NULL), +#ifdef DEBUG + noGCOrAllocationCheck(0), +#endif + inOOMReport(0) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ JS_INIT_CLIST(&contextList); - JS_INIT_CLIST(&trapList); - JS_INIT_CLIST(&watchPointList); + JS_INIT_CLIST(&debuggerList); + + PodZero(&globalDebugHooks); + PodZero(&atomState); + +#if JS_STACK_GROWTH_DIRECTION > 0 + nativeStackLimit = UINTPTR_MAX; +#endif } bool -JSRuntime::init(uint32 maxbytes) +JSRuntime::init(uint32_t maxbytes) { -#ifdef JS_METHODJIT_SPEW - JMCheckLogging(); +#ifdef JS_THREADSAFE + ownerThread_ = PR_GetCurrentThread(); #endif -#ifdef DEBUG - functionMeterFilename = getenv("JS_FUNCTION_STATFILE"); - if (functionMeterFilename) { - if (!methodReadBarrierCountMap.init()) - return false; - if (!unjoinedFunctionCountMap.init()) - return false; - } - propTreeStatFilename = getenv("JS_PROPTREE_STATFILE"); - propTreeDumpFilename = getenv("JS_PROPTREE_DUMPFILE"); +#ifdef JS_METHODJIT_SPEW + JMCheckLogging(); #endif if (!js_InitGC(this, maxbytes)) return false; if (!(atomsCompartment = this->new_(this)) || - !atomsCompartment->init() || + !atomsCompartment->init(NULL) || !compartments.append(atomsCompartment)) { Foreground::delete_(atomsCompartment); return false; } - atomsCompartment->setGCLastBytes(8192); + atomsCompartment->isSystemCompartment = true; + atomsCompartment->setGCLastBytes(8192, GC_NORMAL); if (!js_InitAtomState(this)) return false; - wrapObjectCallback = js::TransparentObjectWrapper; - -#ifdef JS_THREADSAFE - /* this is asymmetric with JS_ShutDown: */ - if (!js_SetupLocks(8, 16)) - return false; - rtLock = JS_NEW_LOCK(); - if (!rtLock) + if (!InitRuntimeNumberState(this)) return false; - stateChange = JS_NEW_CONDVAR(gcLock); - if (!stateChange) - return false; - debuggerLock = JS_NEW_LOCK(); - if (!debuggerLock) + + dtoaState = js_NewDtoaState(); + if (!dtoaState) return false; -#endif - debugMode = JS_FALSE; + if (!stackSpace.init()) + return false; - return js_InitThreads(this); + nativeStackBase = GetNativeStackBase(); + return true; } JSRuntime::~JSRuntime() { + JS_ASSERT(onOwnerThread()); + + delete_(execAlloc_); + delete_(bumpAlloc_); + #ifdef DEBUG /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ if (!JS_CLIST_IS_EMPTY(&contextList)) { @@ -726,32 +865,57 @@ JSRuntime::~JSRuntime() } #endif - js_FinishThreads(this); - js_FreeRuntimeScriptState(this); + FinishRuntimeNumberState(this); js_FinishAtomState(this); + if (dtoaState) + js_DestroyDtoaState(dtoaState); + js_FinishGC(this); #ifdef JS_THREADSAFE if (gcLock) - JS_DESTROY_LOCK(gcLock); - if (gcDone) - JS_DESTROY_CONDVAR(gcDone); - if (requestDone) - JS_DESTROY_CONDVAR(requestDone); - if (rtLock) - JS_DESTROY_LOCK(rtLock); - if (stateChange) - JS_DESTROY_CONDVAR(stateChange); - if (debuggerLock) - JS_DESTROY_LOCK(debuggerLock); + PR_DestroyLock(gcLock); #endif } +#ifdef JS_THREADSAFE +void +JSRuntime::setOwnerThread() +{ + JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */ + JS_ASSERT(requestDepth == 0); + ownerThread_ = PR_GetCurrentThread(); + nativeStackBase = GetNativeStackBase(); + if (nativeStackQuota) + JS_SetNativeStackQuota(this, nativeStackQuota); +} + +void +JSRuntime::clearOwnerThread() +{ + JS_ASSERT(onOwnerThread()); + JS_ASSERT(requestDepth == 0); + ownerThread_ = (void *)0xc1ea12; /* "clear" */ + nativeStackBase = 0; +#if JS_STACK_GROWTH_DIRECTION > 0 + nativeStackLimit = UINTPTR_MAX; +#else + nativeStackLimit = 0; +#endif +} + +JS_FRIEND_API(bool) +JSRuntime::onOwnerThread() const +{ + return ownerThread_ == PR_GetCurrentThread(); +} +#endif /* JS_THREADSAFE */ + JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes) +JS_NewRuntime(uint32_t maxbytes) { -#ifdef DEBUG if (!js_NewRuntimeWasCalled) { +#ifdef DEBUG /* * This code asserts that the numbers associated with the error names * in jsmsg.def are monotonically increasing. It uses values for the @@ -776,55 +940,37 @@ JS_NewRuntime(uint32 maxbytes) JS_END_MACRO; #include "js.msg" #undef MSG_DEF +#endif /* DEBUG */ + + InitMemorySubsystem(); js_NewRuntimeWasCalled = JS_TRUE; } -#endif /* DEBUG */ - void *mem = OffTheBooks::calloc_(sizeof(JSRuntime)); - if (!mem) + JSRuntime *rt = OffTheBooks::new_(); + if (!rt) return NULL; - JSRuntime *rt = new (mem) JSRuntime(); if (!rt->init(maxbytes)) { JS_DestroyRuntime(rt); return NULL; } + Probes::createRuntime(rt); return rt; } JS_PUBLIC_API(void) JS_DestroyRuntime(JSRuntime *rt) { + Probes::destroyRuntime(rt); Foreground::delete_(rt); } -#ifdef JS_REPRMETER -namespace reprmeter { - extern void js_DumpReprMeter(); -} -#endif - JS_PUBLIC_API(void) JS_ShutDown(void) { -#ifdef MOZ_TRACEVIS - StopTraceVis(); -#endif - -#ifdef JS_OPMETER - extern void js_DumpOpMeters(); - js_DumpOpMeters(); -#endif - -#ifdef JS_REPRMETER - reprmeter::js_DumpReprMeter(); -#endif - -#ifdef JS_THREADSAFE - js_CleanupLocks(); -#endif + Probes::shutdown(); PRMJ_NowShutdown(); } @@ -840,37 +986,46 @@ JS_SetRuntimePrivate(JSRuntime *rt, void *data) rt->data = data; } +JS_PUBLIC_API(size_t) +JS::SystemCompartmentCount(const JSRuntime *rt) +{ + size_t n = 0; + for (size_t i = 0; i < rt->compartments.length(); i++) { + if (rt->compartments[i]->isSystemCompartment) { + ++n; + } + } + return n; +} + +JS_PUBLIC_API(size_t) +JS::UserCompartmentCount(const JSRuntime *rt) +{ + size_t n = 0; + for (size_t i = 0; i < rt->compartments.length(); i++) { + if (!rt->compartments[i]->isSystemCompartment) { + ++n; + } + } + return n; +} + #ifdef JS_THREADSAFE static void StartRequest(JSContext *cx) { - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); - if (t->data.requestDepth) { - t->data.requestDepth++; + if (rt->requestDepth) { + rt->requestDepth++; } else { - JSRuntime *rt = cx->runtime; AutoLockGC lock(rt); - /* Wait until the GC is finished. */ - if (rt->gcThread != cx->thread()) { - while (rt->gcThread) - JS_AWAIT_GC_DONE(rt); - } - /* Indicate that a request is running. */ - rt->requestCount++; - t->data.requestDepth = 1; - - /* - * Adjust rt->interruptCounter to reflect any interrupts added while the - * thread was suspended. - */ - if (t->data.interruptFlags) - JS_ATOMIC_INCREMENT(&rt->interruptCounter); + rt->requestDepth = 1; - if (rt->requestCount == 1 && rt->activityCallback) + if (rt->activityCallback) rt->activityCallback(rt->activityCallbackArg, true); } } @@ -878,37 +1033,21 @@ StartRequest(JSContext *cx) static void StopRequest(JSContext *cx) { - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - JS_ASSERT(t->data.requestDepth != 0); - if (t->data.requestDepth != 1) { - t->data.requestDepth--; + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); + JS_ASSERT(rt->requestDepth != 0); + if (rt->requestDepth != 1) { + rt->requestDepth--; } else { - LeaveTrace(cx); /* for GC safety */ - - t->data.conservativeGC.updateForRequestEnd(t->suspendCount); + rt->conservativeGC.updateForRequestEnd(rt->suspendCount); /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - JSRuntime *rt = cx->runtime; AutoLockGC lock(rt); - t->data.requestDepth = 0; + rt->requestDepth = 0; - /* - * Adjust rt->interruptCounter to reflect any interrupts added while the - * thread still had active requests. - */ - if (t->data.interruptFlags) - JS_ATOMIC_DECREMENT(&rt->interruptCounter); - - /* Give the GC a chance to run if this was the last request running. */ - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) { - JS_NOTIFY_REQUEST_DONE(rt); - if (rt->activityCallback) - rt->activityCallback(rt->activityCallbackArg, false); - } + if (rt->activityCallback) + rt->activityCallback(rt->activityCallbackArg, false); } } #endif /* JS_THREADSAFE */ @@ -946,15 +1085,15 @@ JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx) { #ifdef JS_THREADSAFE - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); - jsrefcount saveDepth = t->data.requestDepth; + jsrefcount saveDepth = rt->requestDepth; if (!saveDepth) return 0; - t->suspendCount++; - t->data.requestDepth = 1; + rt->suspendCount++; + rt->requestDepth = 1; StopRequest(cx); return saveDepth; #else @@ -966,40 +1105,39 @@ JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) { #ifdef JS_THREADSAFE - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); if (saveDepth == 0) return; JS_ASSERT(saveDepth >= 1); - JS_ASSERT(!t->data.requestDepth); - JS_ASSERT(t->suspendCount); + JS_ASSERT(!rt->requestDepth); + JS_ASSERT(rt->suspendCount); StartRequest(cx); - t->data.requestDepth = saveDepth; - t->suspendCount--; + rt->requestDepth = saveDepth; + rt->suspendCount--; #endif } JS_PUBLIC_API(JSBool) -JS_IsInRequest(JSContext *cx) +JS_IsInRequest(JSRuntime *rt) { #ifdef JS_THREADSAFE - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - return JS_THREAD_DATA(cx)->requestDepth != 0; + JS_ASSERT(rt->onOwnerThread()); + return rt->requestDepth != 0; #else return false; #endif } -JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt) -{ - JS_LOCK_RUNTIME(rt); -} - -JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt) +JS_PUBLIC_API(JSBool) +JS_IsInSuspendedRequest(JSRuntime *rt) { - JS_UNLOCK_RUNTIME(rt); +#ifdef JS_THREADSAFE + JS_ASSERT(rt->onOwnerThread()); + return rt->suspendCount != 0; +#else + return false; +#endif } JS_PUBLIC_API(JSContextCallback) @@ -1048,6 +1186,18 @@ JS_SetContextPrivate(JSContext *cx, void *data) cx->data = data; } +JS_PUBLIC_API(void *) +JS_GetSecondContextPrivate(JSContext *cx) +{ + return cx->data2; +} + +JS_PUBLIC_API(void) +JS_SetSecondContextPrivate(JSContext *cx, void *data) +{ + cx->data2 = data; +} + JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx) { @@ -1060,6 +1210,12 @@ JS_ContextIterator(JSRuntime *rt, JSContext **iterp) return js_ContextIterator(rt, JS_TRUE, iterp); } +JS_PUBLIC_API(JSContext *) +JS_ContextIteratorUnlocked(JSRuntime *rt, JSContext **iterp) +{ + return js_ContextIterator(rt, JS_FALSE, iterp); +} + JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx) { @@ -1132,7 +1288,7 @@ JS_StringToVersion(const char *string) return JSVERSION_UNKNOWN; } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_GetOptions(JSContext *cx) { /* @@ -1156,15 +1312,15 @@ SetOptionsCommon(JSContext *cx, uintN options) return oldopts; } -JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options) +JS_PUBLIC_API(uint32_t) +JS_SetOptions(JSContext *cx, uint32_t options) { AutoLockGC lock(cx->runtime); return SetOptionsCommon(cx, options); } -JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options) +JS_PUBLIC_API(uint32_t) +JS_ToggleOptions(JSContext *cx, uint32_t options) { AutoLockGC lock(cx->runtime); uintN oldopts = cx->allOptions(); @@ -1172,6 +1328,12 @@ JS_ToggleOptions(JSContext *cx, uint32 options) return SetOptionsCommon(cx, newopts); } +JS_PUBLIC_API(void) +JS_SetJitHardening(JSRuntime *rt, JSBool enabled) +{ + rt->setJitHardening(!!enabled); +} + JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void) { @@ -1200,6 +1362,7 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt, JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target) { + AssertNoGC(cx); CHECK_REQUEST(cx); JS_ASSERT(target); @@ -1213,36 +1376,50 @@ JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target) return reinterpret_cast(call); } +namespace js { + +// Declared in jscompartment.h +Class dummy_class = { + "jdummy", + JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub +}; + +} /*namespace js */ + 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 - }; - + AssertNoGC(cx); CHECK_REQUEST(cx); - - JS_ASSERT(target); - JSObject *scriptObject = target->u.object; - if (!scriptObject) { - SwitchToCompartment sc(cx, target->compartment); - scriptObject = JS_NewGlobalObject(cx, &dummy_class); - if (!scriptObject) + JS_ASSERT(!target->isCachedEval); + GlobalObject *global = target->globalObject; + if (!global) { + SwitchToCompartment sc(cx, target->compartment()); + global = GlobalObject::create(cx, &dummy_class); + if (!global) return NULL; } - return JS_EnterCrossCompartmentCall(cx, scriptObject); + return JS_EnterCrossCompartmentCall(cx, global); +} + +JS_PUBLIC_API(JSCrossCompartmentCall *) +JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + + return JS_EnterCrossCompartmentCall(cx, &Valueify(target)->scopeChain().global()); } JS_PUBLIC_API(void) JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call) { AutoCompartment *realcall = reinterpret_cast(call); + AssertNoGC(realcall->context); CHECK_REQUEST(realcall->context); realcall->leave(); Foreground::delete_(realcall); @@ -1251,13 +1428,21 @@ JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call) bool JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target) { - JS_ASSERT(!call); - if (cx->compartment == target->getCompartment()) { - call = reinterpret_cast(1); + AssertNoGC(cx); + JS_ASSERT(state == STATE_UNENTERED); + if (cx->compartment == target->compartment()) { + state = STATE_SAME_COMPARTMENT; return true; } - call = JS_EnterCrossCompartmentCall(cx, target); - return call != NULL; + + JS_STATIC_ASSERT(sizeof(bytes) == sizeof(AutoCompartment)); + CHECK_REQUEST(cx); + AutoCompartment *call = new (bytes) AutoCompartment(cx, target); + if (call->enter()) { + state = STATE_OTHER_COMPARTMENT; + return true; + } + return false; } void @@ -1266,13 +1451,22 @@ JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSObject *target) (void) enter(cx, target); } +JSAutoEnterCompartment::~JSAutoEnterCompartment() +{ + if (state == STATE_OTHER_COMPARTMENT) { + AutoCompartment* ac = reinterpret_cast(bytes); + CHECK_REQUEST(ac->context); + ac->~AutoCompartment(); + } +} + namespace JS { bool AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target) { JS_ASSERT(!call); - if (cx->compartment == target->compartment) { + if (cx->compartment == target->compartment()) { call = reinterpret_cast(1); return true; } @@ -1280,6 +1474,18 @@ AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target) return call != NULL; } +bool +AutoEnterFrameCompartment::enter(JSContext *cx, JSStackFrame *target) +{ + JS_ASSERT(!call); + if (cx->compartment == Valueify(target)->scopeChain().compartment()) { + call = reinterpret_cast(1); + return true; + } + call = JS_EnterCrossCompartmentCallStackFrame(cx, target); + return call != NULL; +} + } /* namespace JS */ JS_PUBLIC_API(void *) @@ -1295,12 +1501,13 @@ JS_PUBLIC_API(void *) JS_GetCompartmentPrivate(JSContext *cx, JSCompartment *compartment) { CHECK_REQUEST(cx); - return compartment->data; + return js_GetCompartmentPrivate(compartment); } JS_PUBLIC_API(JSBool) JS_WrapObject(JSContext *cx, JSObject **objp) { + AssertNoGC(cx); CHECK_REQUEST(cx); return cx->compartment->wrap(cx, objp); } @@ -1308,22 +1515,25 @@ JS_WrapObject(JSContext *cx, JSObject **objp) JS_PUBLIC_API(JSBool) JS_WrapValue(JSContext *cx, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return cx->compartment->wrap(cx, Valueify(vp)); + return cx->compartment->wrap(cx, vp); } JS_PUBLIC_API(JSObject *) JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target) { + AssertNoGC(cx); + // This function is called when an object moves between two // different compartments. In that case, we need to "move" the // window from origobj's compartment to target's compartment. - JSCompartment *destination = target->getCompartment(); + JSCompartment *destination = target->compartment(); WrapperMap &map = destination->crossCompartmentWrappers; Value origv = ObjectValue(*origobj); JSObject *obj; - if (origobj->getCompartment() == destination) { + if (origobj->compartment() == destination) { // If the original object is in the same compartment as the // destination, then we know that we won't find wrapper in the // destination's cross compartment map and that the same @@ -1395,14 +1605,14 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target) } // Lastly, update the original object to point to the new one. - if (origobj->getCompartment() != destination) { + if (origobj->compartment() != destination) { AutoCompartment ac(cx, origobj); JSObject *tobj = obj; if (!ac.enter() || !JS_WrapObject(cx, &tobj)) return NULL; if (!origobj->swap(cx, tobj)) return NULL; - origobj->getCompartment()->crossCompartmentWrappers.put(targetv, origv); + origobj->compartment()->crossCompartmentWrappers.put(targetv, origv); } return obj; @@ -1423,8 +1633,10 @@ js_TransplantObjectWithWrapper(JSContext *cx, JSObject *targetobj, JSObject *targetwrapper) { + AssertNoGC(cx); + JSObject *obj; - JSCompartment *destination = targetobj->getCompartment(); + JSCompartment *destination = targetobj->compartment(); WrapperMap &map = destination->crossCompartmentWrappers; // |origv| is the map entry we're looking up. The map entries are going to @@ -1499,8 +1711,8 @@ js_TransplantObjectWithWrapper(JSContext *cx, return NULL; if (!origwrapper->swap(cx, tobj)) return NULL; - origwrapper->getCompartment()->crossCompartmentWrappers.put(targetv, - ObjectValue(*origwrapper)); + origwrapper->compartment()->crossCompartmentWrappers.put(targetv, + ObjectValue(*origwrapper)); } return obj; @@ -1515,10 +1727,11 @@ JS_GetGlobalObject(JSContext *cx) JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); cx->globalObject = obj; - if (!cx->running()) + if (!cx->hasfp()) cx->resetCompartment(); } @@ -1526,6 +1739,7 @@ JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); /* @@ -1535,12 +1749,13 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) */ if (!cx->globalObject) JS_SetGlobalObject(cx, obj); + assertSameCompartment(cx, obj); - return obj->asGlobal()->initStandardClasses(cx); + return obj->global().initStandardClasses(cx); } -#define CLASP(name) (&js_##name##Class) +#define CLASP(name) (&name##Class) #define TYPED_ARRAY_CLASP(type) (&TypedArray::fastClasses[TypedArray::type]) #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL @@ -1566,7 +1781,7 @@ StdNameToAtom(JSContext *cx, JSStdName *stdn) if (!atom) { name = stdn->name; if (name) { - atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); + atom = js_Atomize(cx, name, strlen(name), InternAtom); OFFSET_TO_ATOM(cx->runtime, offset) = atom; } } @@ -1578,8 +1793,8 @@ StdNameToAtom(JSContext *cx, JSStdName *stdn) * If you add a "standard" class, remember to update this table. */ static JSStdName standard_class_atoms[] = { - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, + {js_InitFunctionClass, EAGER_ATOM_AND_CLASP(Function)}, + {js_InitObjectClass, EAGER_ATOM_AND_CLASP(Object)}, {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, @@ -1597,8 +1812,10 @@ static JSStdName standard_class_atoms[] = { {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, #endif {js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)}, - {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::jsclass}, - {js_InitWeakMapClass, EAGER_CLASS_ATOM(WeakMap), &WeakMap::jsclass}, + {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::slowClass}, + {js_InitWeakMapClass, EAGER_CLASS_ATOM(WeakMap), &js::WeakMapClass}, + {js_InitMapClass, EAGER_CLASS_ATOM(Map), &js::MapObject::class_}, + {js_InitSetClass, EAGER_CLASS_ATOM(Set), &js::SetObject::class_}, {NULL, 0, NULL, NULL} }; @@ -1646,11 +1863,10 @@ static JSStdName standard_class_names[] = { #if JS_HAS_GENERATORS {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, #endif /* Typed Arrays */ - {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::jsclass}, + {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferClass}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int8Array), TYPED_ARRAY_CLASP(TYPE_INT8)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8Array), TYPED_ARRAY_CLASP(TYPE_UINT8)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int16Array), TYPED_ARRAY_CLASP(TYPE_INT16)}, @@ -1662,6 +1878,7 @@ static JSStdName standard_class_names[] = { {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray), TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)}, + {js_InitWeakMapClass, EAGER_ATOM_AND_CLASP(WeakMap)}, {js_InitProxyClass, EAGER_ATOM_AND_CLASP(Proxy)}, {NULL, 0, NULL, NULL} @@ -1702,13 +1919,15 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsid id, JSBool *resolved) JSStdName *stdnm; uintN i; + RootObject objRoot(cx, &obj); + + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); *resolved = JS_FALSE; rt = cx->runtime; - JS_ASSERT(rt->state != JSRTS_DOWN); - if (rt->state == JSRTS_LANDING || !JSID_IS_ATOM(id)) + if (!rt->hasContexts() || !JSID_IS_ATOM(id)) return JS_TRUE; idstr = JSID_TO_STRING(id); @@ -1717,8 +1936,8 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsid id, JSBool *resolved) atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (idstr == atom) { *resolved = JS_TRUE; - return obj->defineProperty(cx, ATOM_TO_JSID(atom), UndefinedValue(), - PropertyStub, StrictPropertyStub, + return obj->defineProperty(cx, atom->asPropertyName(), UndefinedValue(), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY); } @@ -1788,9 +2007,9 @@ JS_PUBLIC_API(JSBool) JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) { JSRuntime *rt; - JSAtom *atom; uintN i; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); rt = cx->runtime; @@ -1799,10 +2018,10 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) * Check whether we need to bind 'undefined' and define it if so. * Since ES5 15.1.1.3 undefined can't be deleted. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (!obj->nativeContains(ATOM_TO_JSID(atom)) && - !obj->defineProperty(cx, ATOM_TO_JSID(atom), UndefinedValue(), - PropertyStub, StrictPropertyStub, + PropertyName *name = rt->atomState.typeAtoms[JSTYPE_VOID]; + if (!obj->nativeContains(cx, ATOM_TO_JSID(name)) && + !obj->defineProperty(cx, name, UndefinedValue(), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) { return JS_FALSE; } @@ -1819,9 +2038,7 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) return JS_TRUE; } -namespace js { - -JSIdArray * +static JSIdArray * NewIdArray(JSContext *cx, jsint length) { JSIdArray *ida; @@ -1833,8 +2050,6 @@ NewIdArray(JSContext *cx, jsint length) return ida; } -} - /* * Unlike realloc(3), this function frees ida on failure. */ @@ -1867,7 +2082,7 @@ AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) return NULL; JS_ASSERT(i < ida->length); } - ida->vector[i] = ATOM_TO_JSID(atom); + ida->vector[i].init(ATOM_TO_JSID(atom)); *ip = i + 1; return ida; } @@ -1876,7 +2091,7 @@ static JSIdArray * EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, jsint *ip, JSBool *foundp) { - *foundp = obj->nativeContains(ATOM_TO_JSID(atom)); + *foundp = obj->nativeContains(cx, ATOM_TO_JSID(atom)); if (*foundp) ida = AddAtomToArray(cx, atom, ida, ip); return ida; @@ -1891,6 +2106,7 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida JSBool found; JSObjectOp init; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, ida); rt = cx->runtime; @@ -1952,28 +2168,32 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return js_GetClassObject(cx, obj, key, objp); } JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx) +JS_GetObjectPrototype(JSContext *cx, JSObject *forObj) { CHECK_REQUEST(cx); - return GetScopeChain(cx); + assertSameCompartment(cx, forObj); + return forObj->global().getOrCreateObjectPrototype(cx); } JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); assertSameCompartment(cx, obj); - return obj->getGlobal(); + return &obj->global(); } JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx) { + AssertNoGC(cx); CHECK_REQUEST(cx); return GetGlobalForScopeChain(cx); } @@ -1981,22 +2201,27 @@ JS_GetGlobalForScopeChain(JSContext *cx) JS_PUBLIC_API(jsval) JS_ComputeThis(JSContext *cx, jsval *vp) { + AssertNoGC(cx); assertSameCompartment(cx, JSValueArray(vp, 2)); - CallReceiver call = CallReceiverFromVp(Valueify(vp)); + CallReceiver call = CallReceiverFromVp(vp); if (!BoxNonStrictThis(cx, call)) return JSVAL_NULL; - return Jsvalify(call.thisv()); + return call.thisv(); } JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes) { + AssertNoGC(cx); + CHECK_REQUEST(cx); return cx->malloc_(nbytes); } JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes) { + AssertNoGC(cx); + CHECK_REQUEST(cx); return cx->realloc_(p, nbytes); } @@ -2009,27 +2234,26 @@ JS_free(JSContext *cx, void *p) JS_PUBLIC_API(void) JS_updateMallocCounter(JSContext *cx, size_t nbytes) { - return cx->runtime->updateMallocCounter(nbytes); + return cx->runtime->updateMallocCounter(cx, nbytes); } JS_PUBLIC_API(char *) JS_strdup(JSContext *cx, const char *s) { - size_t n; - void *p; - - n = strlen(s) + 1; - p = cx->malloc_(n); + AssertNoGC(cx); + size_t n = strlen(s) + 1; + void *p = cx->malloc_(n); if (!p) return NULL; - return (char *)memcpy(p, s, n); + return (char *)js_memcpy(p, s, n); } JS_PUBLIC_API(JSBool) JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) { + AssertNoGC(cx); d = JS_CANONICALIZE_NAN(d); - Valueify(rval)->setNumber(d); + rval->setNumber(d); return JS_TRUE; } @@ -2038,13 +2262,15 @@ JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) JS_PUBLIC_API(JSBool) JS_AddValueRoot(JSContext *cx, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_AddRoot(cx, Valueify(vp), NULL); + return js_AddRoot(cx, vp, NULL); } JS_PUBLIC_API(JSBool) JS_AddStringRoot(JSContext *cx, JSString **rp) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, NULL); } @@ -2052,6 +2278,7 @@ JS_AddStringRoot(JSContext *cx, JSString **rp) JS_PUBLIC_API(JSBool) JS_AddObjectRoot(JSContext *cx, JSObject **rp) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, NULL); } @@ -2059,6 +2286,7 @@ JS_AddObjectRoot(JSContext *cx, JSObject **rp) JS_PUBLIC_API(JSBool) JS_AddGCThingRoot(JSContext *cx, void **rp) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, NULL); } @@ -2066,13 +2294,15 @@ JS_AddGCThingRoot(JSContext *cx, void **rp) JS_PUBLIC_API(JSBool) JS_AddNamedValueRoot(JSContext *cx, jsval *vp, const char *name) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_AddRoot(cx, Valueify(vp), name); + return js_AddRoot(cx, vp, name); } JS_PUBLIC_API(JSBool) JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, name); } @@ -2080,6 +2310,15 @@ JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name) JS_PUBLIC_API(JSBool) JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + return js_AddGCThingRoot(cx, (void **)rp, name); +} + +JS_PUBLIC_API(JSBool) +JS_AddNamedScriptRoot(JSContext *cx, JSScript **rp, const char *name) +{ + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, name); } @@ -2087,10 +2326,13 @@ JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name) JS_PUBLIC_API(JSBool) JS_AddNamedGCThingRoot(JSContext *cx, void **rp, const char *name) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_AddGCThingRoot(cx, (void **)rp, name); } +/* We allow unrooting from finalizers within the GC */ + JS_PUBLIC_API(JSBool) JS_RemoveValueRoot(JSContext *cx, jsval *vp) { @@ -2112,6 +2354,13 @@ JS_RemoveObjectRoot(JSContext *cx, JSObject **rp) return js_RemoveRoot(cx->runtime, (void *)rp); } +JS_PUBLIC_API(JSBool) +JS_RemoveScriptRoot(JSContext *cx, JSScript **rp) +{ + CHECK_REQUEST(cx); + return js_RemoveRoot(cx->runtime, (void *)rp); +} + JS_PUBLIC_API(JSBool) JS_RemoveGCThingRoot(JSContext *cx, void **rp) { @@ -2136,7 +2385,7 @@ JS_DumpNamedRoots(JSRuntime *rt, #endif /* DEBUG */ -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { return js_MapGCRoots(rt, map, data); @@ -2147,6 +2396,7 @@ JS_LockGCThing(JSContext *cx, void *thing) { JSBool ok; + AssertNoGC(cx); CHECK_REQUEST(cx); ok = js_LockGCThingRT(cx->runtime, thing); if (!ok) @@ -2163,6 +2413,7 @@ JS_LockGCThingRT(JSRuntime *rt, void *thing) JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing) { + AssertNoGC(cx); CHECK_REQUEST(cx); js_UnlockGCThingRT(cx->runtime, thing); return true; @@ -2176,23 +2427,42 @@ JS_UnlockGCThingRT(JSRuntime *rt, void *thing) } JS_PUBLIC_API(void) -JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data) +JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) +{ + AssertNoGC(rt); + rt->gcBlackRootsTraceOp = traceOp; + rt->gcBlackRootsData = data; +} + +JS_PUBLIC_API(void) +JS_TracerInit(JSTracer *trc, JSContext *cx, JSTraceCallback callback) { - rt->gcExtraRootsTraceOp = traceOp; - rt->gcExtraRootsData = data; + trc->runtime = cx->runtime; + trc->context = cx; + trc->callback = callback; + trc->debugPrinter = NULL; + trc->debugPrintArg = NULL; + trc->debugPrintIndex = size_t(-1); + trc->eagerlyTraceWeakMaps = true; } JS_PUBLIC_API(void) JS_TraceRuntime(JSTracer *trc) { + AssertNoGC(trc->runtime); TraceRuntime(trc); } JS_PUBLIC_API(void) -JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) +JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind) +{ + js::TraceChildren(trc, thing, kind); +} + +JS_PUBLIC_API(void) +JS_CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind) { - JS_ASSERT(thing); - MarkKind(trc, thing, kind); + js::CallTracer(trc, thing, kind); } #ifdef DEBUG @@ -2202,10 +2472,10 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) #endif JS_PUBLIC_API(void) -JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, uint32 kind, - JSBool details) +JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, + JSGCTraceKind kind, JSBool details) { - const char *name; + const char *name = NULL; /* silence uninitialized warning */ size_t n; if (bufsize == 0) @@ -2237,25 +2507,33 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui : "string"; break; + case JSTRACE_SCRIPT: + name = "script"; + break; + case JSTRACE_SHAPE: name = "shape"; break; + case JSTRACE_BASE_SHAPE: + name = "base_shape"; + break; + + case JSTRACE_TYPE_OBJECT: + name = "type_object"; + break; + #if JS_HAS_XML_SUPPORT case JSTRACE_XML: name = "xml"; break; #endif - default: - JS_ASSERT(0); - return; - break; } n = strlen(name); if (n > bufsize - 1) n = bufsize - 1; - memcpy(buf, name, n + 1); + js_memcpy(buf, name, n + 1); buf += n; bufsize -= n; @@ -2268,11 +2546,11 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui { JSObject *obj = (JSObject *)thing; Class *clasp = obj->getClass(); - if (clasp == &js_FunctionClass) { - JSFunction *fun = GET_FUNCTION_PRIVATE(trc->context, obj); + if (clasp == &FunctionClass) { + JSFunction *fun = obj->toFunction(); if (!fun) { JS_snprintf(buf, bufsize, ""); - } else if (FUN_OBJECT(fun) != obj) { + } else if (fun != obj) { JS_snprintf(buf, bufsize, "%p", fun); } else { if (fun->atom) @@ -2296,12 +2574,18 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui break; } - case JSTRACE_SHAPE: + case JSTRACE_SCRIPT: { - JS_snprintf(buf, bufsize, ""); + JSScript *script = static_cast(thing); + JS_snprintf(buf, bufsize, "%s:%u", script->filename, unsigned(script->lineno)); break; } + case JSTRACE_SHAPE: + case JSTRACE_BASE_SHAPE: + case JSTRACE_TYPE_OBJECT: + break; + #if JS_HAS_XML_SUPPORT case JSTRACE_XML: { @@ -2312,19 +2596,32 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui break; } #endif - default: - JS_ASSERT(0); - break; } } buf[bufsize - 1] = '\0'; } +extern JS_PUBLIC_API(const char *) +JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize) +{ + if (trc->debugPrinter) { + trc->debugPrinter(trc, buffer, bufferSize); + return buffer; + } + if (trc->debugPrintIndex != (size_t) - 1) { + JS_snprintf(buffer, bufferSize, "%s[%lu]", + (const char *)trc->debugPrintArg, + trc->debugPrintIndex); + return buffer; + } + return (const char*)trc->debugPrintArg; +} + typedef struct JSHeapDumpNode JSHeapDumpNode; struct JSHeapDumpNode { void *thing; - uint32 kind; + JSGCTraceKind kind; JSHeapDumpNode *next; /* next sibling */ JSHeapDumpNode *parent; /* node with the thing that refer to thing from this node */ @@ -2345,14 +2642,12 @@ typedef struct JSDumpingTracer { } JSDumpingTracer; static void -DumpNotify(JSTracer *trc, void *thing, uint32 kind) +DumpNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) { + void *thing = *thingp; JSDumpingTracer *dtrc; JSContext *cx; JSDHashEntryStub *entry; - JSHeapDumpNode *node; - const char *edgeName; - size_t edgeNameSize; JS_ASSERT(trc->callback == DumpNotify); dtrc = (JSDumpingTracer *)trc; @@ -2391,20 +2686,10 @@ DumpNotify(JSTracer *trc, void *thing, uint32 kind) entry->key = thing; } - if (dtrc->base.debugPrinter) { - dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer)); - edgeName = dtrc->buffer; - } else if (dtrc->base.debugPrintIndex != (size_t)-1) { - JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]", - (const char *)dtrc->base.debugPrintArg, - dtrc->base.debugPrintIndex); - edgeName = dtrc->buffer; - } else { - edgeName = (const char*)dtrc->base.debugPrintArg; - } - - edgeNameSize = strlen(edgeName) + 1; - node = (JSHeapDumpNode *) cx->malloc_(offsetof(JSHeapDumpNode, edgeName) + edgeNameSize); + const char *edgeName = JS_GetTraceEdgeName(&dtrc->base, dtrc->buffer, sizeof(dtrc->buffer)); + size_t edgeNameSize = strlen(edgeName) + 1; + size_t bytes = offsetof(JSHeapDumpNode, edgeName) + edgeNameSize; + JSHeapDumpNode *node = (JSHeapDumpNode *) OffTheBooks::malloc_(bytes); if (!node) { dtrc->ok = JS_FALSE; return; @@ -2414,7 +2699,7 @@ DumpNotify(JSTracer *trc, void *thing, uint32 kind) node->kind = kind; node->next = NULL; node->parent = dtrc->parentNode; - memcpy(node->edgeName, edgeName, edgeNameSize); + js_memcpy(node->edgeName, edgeName, edgeNameSize); JS_ASSERT(!*dtrc->lastNodep); *dtrc->lastNodep = node; @@ -2488,7 +2773,7 @@ DumpNode(JSDumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node) } JS_PUBLIC_API(JSBool) -JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, +JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind startKind, void *thingToFind, size_t maxDepth, void *thingToIgnore) { JSDumpingTracer dtrc; @@ -2499,7 +2784,7 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, if (maxDepth == 0) return JS_TRUE; - JS_TRACER_INIT(&dtrc.base, cx, DumpNotify); + JS_TracerInit(&dtrc.base, cx, DumpNotify); if (!JS_DHashTableInit(&dtrc.visited, JS_DHashGetStubOps(), NULL, sizeof(JSDHashEntryStub), JS_DHASH_DEFAULT_CAPACITY(100))) { @@ -2514,7 +2799,7 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, node = NULL; dtrc.lastNodep = &node; if (!startThing) { - JS_ASSERT(startKind == 0); + JS_ASSERT(startKind == JSTRACE_OBJECT); TraceRuntime(&dtrc.base); } else { JS_TraceChildren(&dtrc.base, startThing, startKind); @@ -2556,7 +2841,7 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, for (;;) { next = node->next; parent = node->parent; - cx->free_(node); + Foreground::free_(node); node = next; if (node) break; @@ -2583,31 +2868,33 @@ JS_IsGCMarkingTracer(JSTracer *trc) } JS_PUBLIC_API(void) -JS_GC(JSContext *cx) +JS_CompartmentGC(JSContext *cx, JSCompartment *comp) { - LeaveTrace(cx); + AssertNoGC(cx); + + /* We cannot GC the atoms compartment alone; use a full GC instead. */ + JS_ASSERT(comp != cx->runtime->atomsCompartment); - /* Don't nuke active arenas if executing or compiling. */ - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); - js_GC(cx, NULL, GC_NORMAL); + js::gc::VerifyBarriers(cx, true); + js_GC(cx, comp, GC_NORMAL, gcreason::API); } JS_PUBLIC_API(void) -JS_MaybeGC(JSContext *cx) +JS_GC(JSContext *cx) { - LeaveTrace(cx); - - /* Don't nuke active arenas if executing or compiling. */ - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); + JS_CompartmentGC(cx, NULL); +} +JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx) +{ MaybeGC(cx); } JS_PUBLIC_API(JSGCCallback) JS_SetGCCallback(JSContext *cx, JSGCCallback cb) { + AssertNoGC(cx); CHECK_REQUEST(cx); return JS_SetGCCallbackRT(cx->runtime, cb); } @@ -2617,63 +2904,58 @@ JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) { JSGCCallback oldcb; + AssertNoGC(rt); oldcb = rt->gcCallback; rt->gcCallback = cb; return oldcb; } JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing) +JS_IsAboutToBeFinalized(void *thing) { - JS_ASSERT(thing); - JS_ASSERT(!cx->runtime->gcMarkingTracer); - return IsAboutToBeFinalized(cx, thing); + gc::Cell *t = static_cast(thing); + JS_ASSERT(!t->compartment()->rt->gcIncrementalTracer); + return IsAboutToBeFinalized(t); } JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value) { switch (key) { - case JSGC_MAX_BYTES: + case JSGC_MAX_BYTES: { + AutoLockGC lock(rt); + JS_ASSERT(value >= rt->gcBytes); rt->gcMaxBytes = value; break; + } case JSGC_MAX_MALLOC_BYTES: rt->setGCMaxMallocBytes(value); break; - case JSGC_STACKPOOL_LIFESPAN: - rt->gcEmptyArenaPoolLifespan = value; - break; - case JSGC_MODE: + default: + JS_ASSERT(key == JSGC_MODE); rt->gcMode = JSGCMode(value); JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL || rt->gcMode == JSGC_MODE_COMPARTMENT); - break; - default: - JS_ASSERT(key == JSGC_TRIGGER_FACTOR); - JS_ASSERT(value >= 100); - rt->setGCTriggerFactor(value); return; } } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) { switch (key) { case JSGC_MAX_BYTES: - return rt->gcMaxBytes; + return uint32_t(rt->gcMaxBytes); case JSGC_MAX_MALLOC_BYTES: return rt->gcMaxMallocBytes; - case JSGC_STACKPOOL_LIFESPAN: - return rt->gcEmptyArenaPoolLifespan; - case JSGC_TRIGGER_FACTOR: - return rt->gcTriggerFactor; case JSGC_BYTES: - return rt->gcBytes; + return uint32_t(rt->gcBytes); case JSGC_MODE: - return uint32(rt->gcMode); + return uint32_t(rt->gcMode); case JSGC_UNUSED_CHUNKS: - return uint32(rt->gcChunksWaitingToExpire); + return uint32_t(rt->gcChunkPool.getEmptyCount()); + case JSGC_TOTAL_CHUNKS: + return uint32_t(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount()); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; @@ -2681,117 +2963,84 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) } JS_PUBLIC_API(void) -JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value) +JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value) { JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); -#ifdef JS_TRACER - SetMaxCodeCacheBytes(cx, value); -#endif } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key) { JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); -#ifdef JS_TRACER - return JS_THREAD_DATA(cx)->maxCodeCacheBytes; -#else return 0; -#endif } JS_PUBLIC_API(void) JS_FlushCaches(JSContext *cx) { -#ifdef JS_TRACER - FlushJITCache(cx, &cx->compartment->traceMonitor); -#endif -} - -JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return JSExternalString::changeFinalizer(NULL, finalizer); -} - -JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return JSExternalString::changeFinalizer(finalizer, NULL); } JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type) -{ - CHECK_REQUEST(cx); - return JSExternalString::new_(cx, chars, length, type, NULL); -} - -extern JS_PUBLIC_API(JSString *) -JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length, - intN type, void *closure) +JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, + const JSStringFinalizer *fin) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return JSExternalString::new_(cx, chars, length, type, closure); + JSString *s = JSExternalString::new_(cx, chars, length, fin); + Probes::createString(cx, s, length); + return s; } extern JS_PUBLIC_API(JSBool) -JS_IsExternalString(JSContext *cx, JSString *str) +JS_IsExternalString(JSString *str) { - CHECK_REQUEST(cx); return str->isExternal(); } -extern JS_PUBLIC_API(void *) -JS_GetExternalStringClosure(JSContext *cx, JSString *str) -{ - CHECK_REQUEST(cx); - return str->asExternal().externalClosure(); -} - -JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) +extern JS_PUBLIC_API(const JSStringFinalizer *) +JS_GetExternalStringFinalizer(JSString *str) { -#if JS_STACK_GROWTH_DIRECTION > 0 - if (limitAddr == 0) - limitAddr = jsuword(-1); -#endif - cx->stackLimit = limitAddr; + return str->asExternal().externalFinalizer(); } JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSContext *cx, size_t stackSize) +JS_SetNativeStackQuota(JSRuntime *rt, size_t stackSize) { -#ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()); -#endif + rt->nativeStackQuota = stackSize; + if (!rt->nativeStackBase) + return; #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { - cx->stackLimit = jsuword(-1); + rt->nativeStackLimit = UINTPTR_MAX; } else { - jsuword stackBase = reinterpret_cast(JS_THREAD_DATA(cx)->nativeStackBase); - JS_ASSERT(stackBase <= size_t(-1) - stackSize); - cx->stackLimit = stackBase + stackSize - 1; + JS_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize); + rt->nativeStackLimit = rt->nativeStackBase + stackSize - 1; } #else if (stackSize == 0) { - cx->stackLimit = 0; + rt->nativeStackLimit = 0; } else { - jsuword stackBase = reinterpret_cast(JS_THREAD_DATA(cx)->nativeStackBase); - JS_ASSERT(stackBase >= stackSize); - cx->stackLimit = stackBase - (stackSize - 1); + JS_ASSERT(rt->nativeStackBase >= stackSize); + rt->nativeStackLimit = rt->nativeStackBase - (stackSize - 1); } #endif } -JS_PUBLIC_API(void) -JS_SetScriptStackQuota(JSContext *cx, size_t quota) +/************************************************************************/ + +JS_PUBLIC_API(jsint) +JS_IdArrayLength(JSContext *cx, JSIdArray *ida) { - cx->scriptStackQuota = quota; + return ida->length; } -/************************************************************************/ +JS_PUBLIC_API(jsid) +JS_IdArrayGet(JSContext *cx, JSIdArray *ida, jsint index) +{ + JS_ASSERT(index >= 0 && index < ida->length); + return ida->vector[index]; +} JS_PUBLIC_API(void) JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) @@ -2802,20 +3051,32 @@ JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) JS_PUBLIC_API(JSBool) JS_ValueToId(JSContext *cx, jsval v, jsid *idp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - return ValueToId(cx, Valueify(v), idp); + return ValueToId(cx, v, idp); } JS_PUBLIC_API(JSBool) JS_IdToValue(JSContext *cx, jsid id, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); *vp = IdToJsval(id); assertSameCompartment(cx, *vp); return JS_TRUE; } +JS_PUBLIC_API(JSBool) +JS_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + JS_ASSERT(obj != NULL); + JS_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER); + return obj->defaultValue(cx, hint, vp); +} + JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { @@ -2844,7 +3105,8 @@ JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { JS_ASSERT(type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION); - return js_TryValueOf(cx, obj, type, Valueify(vp)); + JS_ASSERT(obj); + return DefaultValue(cx, obj, type, vp); } JS_PUBLIC_API(void) @@ -2857,30 +3119,30 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, parent_proto); - return js_InitClass(cx, obj, parent_proto, Valueify(clasp), - Valueify(constructor), nargs, - ps, fs, static_ps, static_fs); + RootObject objRoot(cx, &obj); + return js_InitClass(cx, objRoot, parent_proto, Valueify(clasp), constructor, + nargs, ps, fs, static_ps, static_fs); } -#ifdef JS_THREADSAFE -JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj) +JS_PUBLIC_API(JSBool) +JS_LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto) { - return Jsvalify(obj->getClass()); + return LinkConstructorAndPrototype(cx, ctor, proto); } -#else + JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj) { - return Jsvalify(obj->getClass()); + return obj->getJSClass(); } -#endif JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) { + AssertNoGC(cx); CHECK_REQUEST(cx); #ifdef DEBUG if (argv) { @@ -2890,7 +3152,7 @@ JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) #endif if (!obj || obj->getJSClass() != clasp) { if (argv) - ReportIncompatibleMethod(cx, Valueify(argv - 2), Valueify(clasp)); + ReportIncompatibleMethod(cx, CallReceiverFromArgv(argv), Valueify(clasp)); return false; } return true; @@ -2899,21 +3161,23 @@ JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) JS_PUBLIC_API(JSBool) JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { + AssertNoGC(cx); assertSameCompartment(cx, obj, v); - return HasInstance(cx, obj, Valueify(&v), bp); + return HasInstance(cx, obj, &v, bp); } JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj) +JS_GetPrivate(JSObject *obj) { + /* This function can be called by a finalizer. */ return obj->getPrivate(); } -JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) +JS_PUBLIC_API(void) +JS_SetPrivate(JSObject *obj, void *data) { + /* This function can be called by a finalizer. */ obj->setPrivate(data); - return true; } JS_PUBLIC_API(void *) @@ -2925,44 +3189,36 @@ JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) } JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj) +JS_GetPrototype(JSObject *obj) { - JSObject *proto; - - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); - proto = obj->getProto(); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return proto && !proto->isNewborn() ? proto : NULL; + return obj->getProto(); } JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, proto); return SetProto(cx, obj, proto, JS_FALSE); } JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj) +JS_GetParent(JSObject *obj) { - assertSameCompartment(cx, obj); - JSObject *parent = obj->getParent(); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return parent && !parent->isNewborn() ? parent : NULL; + JS_ASSERT(!obj->isScope()); + return obj->getParent(); } JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) { + AssertNoGC(cx); CHECK_REQUEST(cx); + JS_ASSERT(!obj->isScope()); JS_ASSERT(parent || !obj->getParent()); assertSameCompartment(cx, obj, parent); - obj->setParent(parent); - return true; + return obj->setParent(cx, parent); } JS_PUBLIC_API(JSObject *) @@ -2970,16 +3226,16 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) { Value cval; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, proto); { JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); - if (!proto->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval)) + if (!proto->getProperty(cx, cx->runtime->atomState.constructorAtom, &cval)) return NULL; } - JSObject *funobj; - if (!IsFunctionObject(cval, &funobj)) { + if (!IsFunctionObject(cval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, proto->getClass()->name); return NULL; @@ -2990,6 +3246,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) { + AssertNoGC(cx); assertSameCompartment(cx, obj); *idp = OBJECT_TO_JSID(obj); return JS_TRUE; @@ -2999,6 +3256,7 @@ JS_PUBLIC_API(JSObject *) JS_NewGlobalObject(JSContext *cx, JSClass *clasp) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); return GlobalObject::create(cx, Valueify(clasp)); @@ -3024,6 +3282,7 @@ class AutoHoldCompartment { JS_PUBLIC_API(JSObject *) JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals) { + AssertNoGC(cx); CHECK_REQUEST(cx); JSCompartment *compartment = NewCompartment(cx, principals); if (!compartment) @@ -3032,9 +3291,9 @@ JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *pr AutoHoldCompartment hold(compartment); JSCompartment *saved = cx->compartment; - cx->compartment = compartment; + cx->setCompartment(compartment); JSObject *obj = JS_NewGlobalObject(cx, clasp); - cx->compartment = saved; + cx->setCompartment(saved); return obj; } @@ -3043,19 +3302,26 @@ JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, proto, parent); Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ - JS_ASSERT(clasp != &js_FunctionClass); + JS_ASSERT(clasp != &FunctionClass); JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); - JSObject *obj = NewNonFunction(cx, clasp, proto, parent); - if (obj) - obj->syncSpecialEquality(); + if (proto && !proto->setNewTypeUnknown(cx)) + return NULL; + + JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent); + if (obj) { + if (clasp->ext.equality) + MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_SPECIAL_EQUALITY); + MarkTypeObjectUnknownProperties(cx, obj->type()); + } JS_ASSERT_IF(obj, obj->getParent()); return obj; @@ -3065,25 +3331,27 @@ JS_PUBLIC_API(JSObject *) JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, proto, parent); Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ - JS_ASSERT(clasp != &js_FunctionClass); + JS_ASSERT(clasp != &FunctionClass); JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); - JSObject *obj = NewNonFunction(cx, clasp, proto, parent); + JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent); if (obj) - obj->syncSpecialEquality(); + MarkTypeObjectUnknownProperties(cx, obj->type()); return obj; } JS_PUBLIC_API(JSObject *) JS_NewObjectForConstructor(JSContext *cx, const jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, *vp); @@ -3105,6 +3373,7 @@ JS_IsNative(JSObject *obj) JS_PUBLIC_API(JSBool) JS_FreezeObject(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); @@ -3114,6 +3383,7 @@ JS_FreezeObject(JSContext *cx, JSObject *obj) JS_PUBLIC_API(JSBool) JS_DeepFreezeObject(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); @@ -3125,7 +3395,7 @@ JS_DeepFreezeObject(JSContext *cx, JSObject *obj) return false; /* Walk slots in obj and if any value is a non-null object, seal it. */ - for (uint32 i = 0, n = obj->slotSpan(); i < n; ++i) { + for (uint32_t i = 0, n = obj->slotSpan(); i < n; ++i) { const Value &v = obj->getSlot(i); if (v.isPrimitive()) continue; @@ -3137,38 +3407,59 @@ JS_DeepFreezeObject(JSContext *cx, JSObject *obj) } JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) +JS_ConstructObject(JSContext *cx, JSClass *jsclasp, JSObject *parent) { - CHECK_REQUEST(cx); - assertSameCompartment(cx, proto, parent); - Class *clasp = Valueify(jsclasp); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); + return JS_ConstructObjectWithArguments(cx, jsclasp, parent, 0, NULL); } JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv) +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *parent, + uintN argc, jsval *argv) { + AssertNoGC(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, proto, parent, JSValueArray(argv, argc)); + assertSameCompartment(cx, parent, JSValueArray(argv, argc)); + + AutoArrayRooter argtvr(cx, argc, argv); + Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, argc, Valueify(argv)); + clasp = &ObjectClass; /* default class is Object */ + + JSProtoKey protoKey = GetClassProtoKey(clasp); + + /* Protect constructor in case a crazy getter for .prototype uproots it. */ + AutoValueRooter tvr(cx); + if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp)) + return NULL; + + Value rval; + if (!InvokeConstructor(cx, tvr.value(), argc, argv, &rval)) + return NULL; + + /* + * If the instance's class differs from what was requested, throw a type + * error. + */ + if (!rval.isObject() || rval.toObject().getClass() != clasp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_WRONG_CONSTRUCTOR, clasp->name); + return NULL; + } + return &rval.toObject(); } static JSBool LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); JSAutoResolveFlags rf(cx, flags); id = js_CheckForStringIndex(id); - return obj->lookupProperty(cx, id, objp, propp); + return obj->lookupGeneric(cx, id, objp, propp); } #define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) @@ -3187,14 +3478,13 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id, Shape *shape = (Shape *) prop; if (shape->isMethod()) { - AutoShapeRooter root(cx, shape); - vp->setObject(shape->methodObject()); + vp->setObject(*obj2->nativeGetMethod(shape)); return !!obj2->methodReadBarrier(cx, *shape, vp); } /* Peek at the native property's slot value, without doing a Get. */ - if (obj2->containsSlot(shape->slot)) { - *vp = obj2->nativeGetSlot(shape->slot); + if (shape->hasSlot()) { + *vp = obj2->nativeGetSlot(shape->slot()); return true; } } else { @@ -3202,7 +3492,7 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id, return js_GetDenseArrayElementValue(cx, obj2, id, vp); if (obj2->isProxy()) { AutoPropertyDescriptorRooter desc(cx); - if (!JSProxy::getPropertyDescriptor(cx, obj2, id, false, &desc)) + if (!Proxy::getPropertyDescriptor(cx, obj2, id, false, &desc)) return false; if (!(desc.attrs & JSPROP_SHARED)) { *vp = desc.value; @@ -3222,26 +3512,30 @@ JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JSObject *obj2; JSProperty *prop; return LookupPropertyById(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) && - LookupResult(cx, obj, obj2, id, prop, Valueify(vp)); + LookupResult(cx, obj, obj2, id, prop, vp); } JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +JS_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { - return JS_LookupPropertyById(cx, obj, INT_TO_JSID(index), vp); + CHECK_REQUEST(cx); + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return JS_LookupPropertyById(cx, obj, id, vp); } JS_PUBLIC_API(JSBool) JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_LookupUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } @@ -3252,19 +3546,20 @@ JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *obj, jsid id, uintN flag JSBool ok; JSProperty *prop; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); ok = obj->isNative() - ? js_LookupPropertyWithFlags(cx, obj, id, flags, objp, &prop) >= 0 - : obj->lookupProperty(cx, id, objp, &prop); - return ok && LookupResult(cx, obj, *objp, id, prop, Valueify(vp)); + ? LookupPropertyWithFlags(cx, obj, id, flags, objp, &prop) + : obj->lookupGeneric(cx, id, objp, &prop); + return ok && LookupResult(cx, obj, *objp, id, prop, vp); } JS_PUBLIC_API(JSBool) JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, uintN flags, jsval *vp) { JSObject *obj2; - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_LookupPropertyWithFlagsById(cx, obj, ATOM_TO_JSID(atom), flags, &obj2, vp); } @@ -3280,28 +3575,34 @@ JS_HasPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) } JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) +JS_HasElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *foundp) { - return JS_HasPropertyById(cx, obj, INT_TO_JSID(index), foundp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return JS_HasPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(JSBool) JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_HasPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp); } JS_PUBLIC_API(JSBool) JS_HasUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSBool *foundp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_HasPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp); } JS_PUBLIC_API(JSBool) JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); @@ -3317,20 +3618,25 @@ JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foun return JS_TRUE; } - *foundp = obj->nativeContains(id); + *foundp = obj->nativeContains(cx, id); return JS_TRUE; } JS_PUBLIC_API(JSBool) -JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) +JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *foundp) { - return JS_AlreadyHasOwnPropertyById(cx, obj, INT_TO_JSID(index), foundp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(JSBool) JS_AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_AlreadyHasOwnPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp); } @@ -3338,7 +3644,7 @@ JS_PUBLIC_API(JSBool) JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSBool *foundp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_AlreadyHasOwnPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp); } @@ -3347,6 +3653,16 @@ DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, const Value &value, PropertyOp getter, StrictPropertyOp setter, uintN attrs, uintN flags, intN tinyid) { + /* + * JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd + * throw if this happens, but we've accepted it for long enough that it's + * not worth trying to make callers change their ways. Just flip it off on + * its way through the API layer so that we can enforce this internally. + */ + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) + attrs &= ~JSPROP_READONLY; + + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, value, (attrs & JSPROP_GETTER) @@ -3358,26 +3674,29 @@ DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, const Value &value, JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); if (flags != 0 && obj->isNative()) { - return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter, - attrs, flags, tinyid, NULL); + return !!DefineNativeProperty(cx, obj, id, value, getter, setter, + attrs, flags, tinyid); } - return obj->defineProperty(cx, id, value, getter, setter, attrs); + return obj->defineGeneric(cx, id, value, getter, setter, attrs); } JS_PUBLIC_API(JSBool) JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefinePropertyById(cx, obj, id, Valueify(value), Valueify(getter), - Valueify(setter), attrs, 0, 0); + return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, +JS_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefinePropertyById(cx, obj, INT_TO_JSID(index), Valueify(value), - Valueify(getter), Valueify(setter), attrs, 0, 0); + AssertNoGC(cx); + CHECK_REQUEST(cx); + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); } static JSBool @@ -3388,33 +3707,56 @@ DefineProperty(JSContext *cx, JSObject *obj, const char *name, const Value &valu jsid id; JSAtom *atom; + RootObject objRoot(cx, &obj); + RootValue valueRoot(cx, &value); + if (attrs & JSPROP_INDEX) { id = INT_TO_JSID(intptr_t(name)); atom = NULL; attrs &= ~JSPROP_INDEX; } else { - atom = js_Atomize(cx, name, strlen(name), 0); + atom = js_Atomize(cx, name, strlen(name)); if (!atom) return JS_FALSE; id = ATOM_TO_JSID(atom); } + + if (attrs & JSPROP_NATIVE_ACCESSORS) { + RootId idRoot(cx, &id); + + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + attrs &= ~JSPROP_NATIVE_ACCESSORS; + if (getter) { + JSObject *getobj = JS_NewFunction(cx, (Native) getter, 0, 0, &obj->global(), NULL); + if (!getobj) + return false; + getter = JS_DATA_TO_FUNC_PTR(PropertyOp, getobj); + attrs |= JSPROP_GETTER; + } + if (setter) { + JSObject *setobj = JS_NewFunction(cx, (Native) setter, 1, 0, &obj->global(), NULL); + if (!setobj) + return false; + setter = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setobj); + attrs |= JSPROP_SETTER; + } + } + return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid); } JS_PUBLIC_API(JSBool) JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) + PropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefineProperty(cx, obj, name, Valueify(value), Valueify(getter), - Valueify(setter), attrs, 0, 0); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) -JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8 tinyid, - jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) +JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8_t tinyid, + jsval value, PropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefineProperty(cx, obj, name, Valueify(value), Valueify(getter), - Valueify(setter), attrs, Shape::HAS_SHORTID, tinyid); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, Shape::HAS_SHORTID, tinyid); } static JSBool @@ -3422,7 +3764,7 @@ DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namele const Value &value, PropertyOp getter, StrictPropertyOp setter, uintN attrs, uintN flags, intN tinyid) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && DefinePropertyById(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, flags, tinyid); } @@ -3431,44 +3773,46 @@ JS_PUBLIC_API(JSBool) JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefineUCProperty(cx, obj, name, namelen, Valueify(value), - Valueify(getter), Valueify(setter), attrs, 0, 0); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, - int8 tinyid, jsval value, + int8_t tinyid, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs) { - return DefineUCProperty(cx, obj, name, namelen, Valueify(value), Valueify(getter), - Valueify(setter), attrs, Shape::HAS_SHORTID, tinyid); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, + Shape::HAS_SHORTID, tinyid); } JS_PUBLIC_API(JSBool) JS_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, jsval descriptor, JSBool *bp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, descriptor); - return js_DefineOwnProperty(cx, obj, id, Valueify(descriptor), bp); + return js_DefineOwnProperty(cx, obj, id, descriptor, bp); } JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *jsclasp, JSObject *proto, uintN attrs) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, proto); Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ - JSObject *nobj = NewObject(cx, clasp, proto, obj); + RootObject root(cx, &obj); + RootedVarObject nobj(cx); + + nobj = NewObjectWithClassProto(cx, clasp, proto, obj); if (!nobj) return NULL; - nobj->syncSpecialEquality(); - if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0)) return NULL; @@ -3481,6 +3825,7 @@ JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) JSBool ok; uintN attrs; + AssertNoGC(cx); CHECK_REQUEST(cx); for (ok = JS_TRUE; cds->name; cds++) { Value value = DoubleValue(cds->dval); @@ -3498,10 +3843,10 @@ JS_PUBLIC_API(JSBool) JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) { JSBool ok; + RootObject root(cx, &obj); for (ok = true; ps->name; ps++) { - ok = DefineProperty(cx, obj, ps->name, UndefinedValue(), - Valueify(ps->getter), Valueify(ps->setter), + ok = DefineProperty(cx, obj, ps->name, UndefinedValue(), ps->getter, ps->setter, ps->flags, Shape::HAS_SHORTID, ps->tinyid); if (!ok) break; @@ -3509,79 +3854,6 @@ JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) return ok; } -JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias) -{ - JSObject *obj2; - JSProperty *prop; - JSBool ok; - Shape *shape; - - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); - - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), JSRESOLVE_QUALIFIED, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !obj->isNative()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - alias, name, obj2->getClass()->name); - return JS_FALSE; - } - atom = js_Atomize(cx, alias, strlen(alias), 0); - if (!atom) { - ok = JS_FALSE; - } else { - shape = (Shape *)prop; - ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), - shape->getter(), shape->setter(), shape->slot, - shape->attributes(), shape->getFlags() | Shape::ALIAS, - shape->shortid) - != NULL); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) -{ - JSObject *obj2; - JSProperty *prop; - Shape *shape; - - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); - - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), JSRESOLVE_QUALIFIED, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !obj->isNative()) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - numBuf, name, obj2->getClass()->name); - return JS_FALSE; - } - shape = (Shape *)prop; - return js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), - shape->getter(), shape->setter(), shape->slot, - shape->attributes(), shape->getFlags() | Shape::ALIAS, - shape->shortid) - != NULL; -} - static JSBool GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSBool own, PropertyDescriptor *desc) @@ -3607,14 +3879,14 @@ GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, desc->attrs = shape->attributes(); if (shape->isMethod()) { - desc->getter = PropertyStub; - desc->setter = StrictPropertyStub; - desc->value.setObject(shape->methodObject()); + desc->getter = JS_PropertyStub; + desc->setter = JS_StrictPropertyStub; + desc->value.setObject(*obj2->nativeGetMethod(shape)); } else { desc->getter = shape->getter(); desc->setter = shape->setter(); - if (obj2->containsSlot(shape->slot)) - desc->value = obj2->nativeGetSlot(shape->slot); + if (shape->hasSlot()) + desc->value = obj2->nativeGetSlot(shape->slot()); else desc->value.setUndefined(); } @@ -3622,10 +3894,10 @@ GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (obj2->isProxy()) { JSAutoResolveFlags rf(cx, flags); return own - ? JSProxy::getOwnPropertyDescriptor(cx, obj2, id, false, desc) - : JSProxy::getPropertyDescriptor(cx, obj2, id, false, desc); + ? Proxy::getOwnPropertyDescriptor(cx, obj2, id, false, desc) + : Proxy::getPropertyDescriptor(cx, obj2, id, false, desc); } - if (!obj2->getAttributes(cx, id, &desc->attrs)) + if (!obj2->getGenericAttributes(cx, id, &desc->attrs)) return false; desc->getter = NULL; desc->setter = NULL; @@ -3638,7 +3910,7 @@ JS_PUBLIC_API(JSBool) JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSPropertyDescriptor *desc) { - return GetPropertyDescriptorById(cx, obj, id, flags, JS_FALSE, Valueify(desc)); + return GetPropertyDescriptorById(cx, obj, id, flags, JS_FALSE, desc); } JS_PUBLIC_API(JSBool) @@ -3653,9 +3925,9 @@ JS_GetPropertyAttrsGetterAndSetterById(JSContext *cx, JSObject *obj, jsid id, *attrsp = desc.attrs; *foundp = (desc.obj != NULL); if (getterp) - *getterp = Jsvalify(desc.getter); + *getterp = desc.getter; if (setterp) - *setterp = Jsvalify(desc.setter); + *setterp = desc.setter; return true; } @@ -3663,7 +3935,7 @@ JS_PUBLIC_API(JSBool) JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, ATOM_TO_JSID(atom), attrsp, foundp, NULL, NULL); } @@ -3672,7 +3944,7 @@ JS_PUBLIC_API(JSBool) JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, ATOM_TO_JSID(atom), attrsp, foundp, NULL, NULL); } @@ -3682,7 +3954,7 @@ JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, const char *nam uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSStrictPropertyOp *setterp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, ATOM_TO_JSID(atom), attrsp, foundp, getterp, setterp); } @@ -3693,7 +3965,7 @@ JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSStrictPropertyOp *setterp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, ATOM_TO_JSID(atom), attrsp, foundp, getterp, setterp); } @@ -3701,8 +3973,9 @@ JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, JS_PUBLIC_API(JSBool) JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_GetOwnPropertyDescriptor(cx, obj, id, Valueify(vp)); + return GetOwnPropertyDescriptor(cx, obj, id, vp); } static JSBool @@ -3719,7 +3992,7 @@ SetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JS } JSBool ok = obj->isNative() ? js_SetNativeAttributes(cx, obj, (Shape *) prop, attrs) - : obj->setAttributes(cx, id, &attrs); + : obj->setGenericAttributes(cx, id, &attrs); if (ok) *foundp = true; return ok; @@ -3729,7 +4002,7 @@ JS_PUBLIC_API(JSBool) JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN attrs, JSBool *foundp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && SetPropertyAttributesById(cx, obj, ATOM_TO_JSID(atom), attrs, foundp); } @@ -3737,58 +4010,91 @@ JS_PUBLIC_API(JSBool) JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN attrs, JSBool *foundp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && SetPropertyAttributesById(cx, obj, ATOM_TO_JSID(atom), attrs, foundp); } JS_PUBLIC_API(JSBool) JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { + return JS_ForwardGetPropertyTo(cx, obj, id, obj, vp); +} + +JS_PUBLIC_API(JSBool) +JS_ForwardGetPropertyTo(JSContext *cx, JSObject *obj, jsid id, JSObject *onBehalfOf, jsval *vp) +{ + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); + assertSameCompartment(cx, onBehalfOf); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); - return obj->getProperty(cx, id, Valueify(vp)); + return obj->getGeneric(cx, onBehalfOf, id, vp); } JS_PUBLIC_API(JSBool) JS_GetPropertyByIdDefault(JSContext *cx, JSObject *obj, jsid id, jsval def, jsval *vp) { - return GetPropertyDefault(cx, obj, id, Valueify(def), Valueify(vp)); + return GetPropertyDefault(cx, obj, id, def, vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) +{ + return JS_ForwardGetElementTo(cx, obj, index, obj, vp); +} + +JS_PUBLIC_API(JSBool) +JS_ForwardGetElementTo(JSContext *cx, JSObject *obj, uint32_t index, JSObject *onBehalfOf, jsval *vp) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->getElement(cx, onBehalfOf, index, vp); } JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +JS_GetElementIfPresent(JSContext *cx, JSObject *obj, uint32_t index, JSObject *onBehalfOf, jsval *vp, JSBool* present) { - return JS_GetPropertyById(cx, obj, INT_TO_JSID(index), vp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + bool isPresent; + if (!obj->getElementIfPresent(cx, onBehalfOf, index, vp, &isPresent)) + return false; + *present = isPresent; + return true; } JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_GetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_GetPropertyDefault(JSContext *cx, JSObject *obj, const char *name, jsval def, jsval *vp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_GetPropertyByIdDefault(cx, obj, ATOM_TO_JSID(atom), def, vp); } JS_PUBLIC_API(JSBool) JS_GetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_GetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); - if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, Valueify(vp))) + if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp)) return JS_FALSE; if (objp) *objp = obj; @@ -3798,66 +4104,94 @@ JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval * JS_PUBLIC_API(JSBool) JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); } JS_PUBLIC_API(JSBool) JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); - return obj->setProperty(cx, id, Valueify(vp), false); + return obj->setGeneric(cx, id, vp, false); } JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { - return JS_SetPropertyById(cx, obj, INT_TO_JSID(index), vp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return obj->setElement(cx, index, vp, false); } JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && JS_SetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_SetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_SetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); - return obj->deleteProperty(cx, id, Valueify(rval), false); + + if (JSID_IS_SPECIAL(id)) + return obj->deleteSpecial(cx, JSID_TO_SPECIALID(id), rval, false); + + return obj->deleteByValue(cx, IdToValue(id), rval, false); } JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) +JS_DeleteElement2(JSContext *cx, JSObject *obj, uint32_t index, jsval *rval) { - return JS_DeletePropertyById2(cx, obj, INT_TO_JSID(index), rval); + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->deleteElement(cx, index, rval, false); } JS_PUBLIC_API(JSBool) JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, jsval *rval) { - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); - return atom && JS_DeletePropertyById2(cx, obj, ATOM_TO_JSID(atom), rval); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + JSAtom *atom = js_Atomize(cx, name, strlen(name)); + if (!atom) + return false; + + return obj->deleteByValue(cx, StringValue(atom), rval, false); } JS_PUBLIC_API(JSBool) JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *rval) { - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - return atom && JS_DeletePropertyById2(cx, obj, ATOM_TO_JSID(atom), rval); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); + if (!atom) + return false; + + return obj->deleteByValue(cx, StringValue(atom), rval, false); } JS_PUBLIC_API(JSBool) @@ -3868,7 +4202,7 @@ JS_DeletePropertyById(JSContext *cx, JSObject *obj, jsid id) } JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) +JS_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index) { jsval junk; return JS_DeleteElement2(cx, obj, index, &junk); @@ -3884,6 +4218,7 @@ JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); @@ -3896,7 +4231,7 @@ JS_ClearScope(JSContext *cx, JSObject *obj) /* Clear cached class objects on the global object. */ if (obj->isGlobal()) - obj->asGlobal()->clear(cx); + obj->asGlobal().clear(cx); js_InitRandom(cx); } @@ -3904,6 +4239,7 @@ JS_ClearScope(JSContext *cx, JSObject *obj) JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); @@ -3923,7 +4259,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj) * + native case here uses a Shape *, but that iterates in reverse! * + so we make non-native match, by reverse-iterating after JS_Enumerating */ -const uint32 JSSLOT_ITER_INDEX = 0; +const uint32_t JSSLOT_ITER_INDEX = 0; static void prop_iter_finalize(JSContext *cx, JSObject *obj) @@ -3947,8 +4283,12 @@ prop_iter_trace(JSTracer *trc, JSObject *obj) return; if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { - /* Native case: just mark the next property to visit. */ - MarkShape(trc, (Shape *)pdata, "prop iter shape"); + /* + * Native case: just mark the next property to visit. We don't need a + * barrier here because the pointer is updated via setPrivate, which + * always takes a barrier. + */ + MarkShapeUnbarriered(trc, (Shape *)pdata, "prop iter shape"); } else { /* Non-native case: mark each id in the JSIdArray private. */ JSIdArray *ida = (JSIdArray *) pdata; @@ -3959,13 +4299,13 @@ prop_iter_trace(JSTracer *trc, JSObject *obj) static Class prop_iter_class = { "PropertyIterator", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, prop_iter_finalize, NULL, /* reserved0 */ NULL, /* checkAccess */ @@ -3980,19 +4320,20 @@ JS_PUBLIC_API(JSObject *) JS_NewPropertyIterator(JSContext *cx, JSObject *obj) { JSObject *iterobj; - const void *pdata; + void *pdata; jsint index; JSIdArray *ida; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - iterobj = NewNonFunction(cx, &prop_iter_class, NULL, obj); + iterobj = NewObjectWithClassProto(cx, &prop_iter_class, NULL, obj); if (!iterobj) return NULL; if (obj->isNative()) { /* Native case: start with the last property in obj. */ - pdata = obj->lastProperty(); + pdata = (void *)obj->lastProperty(); index = -1; } else { /* @@ -4001,7 +4342,6 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) * Note: we have to make sure that we root obj around the call to * JS_Enumerate to protect against multiple allocations under it. */ - AutoObjectRooter tvr(cx, iterobj); ida = JS_Enumerate(cx, obj); if (!ida) return NULL; @@ -4010,8 +4350,8 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) } /* iterobj cannot escape to other threads here. */ - iterobj->setPrivate(const_cast(pdata)); - iterobj->getSlotRef(JSSLOT_ITER_INDEX).setInt32(index); + iterobj->setPrivate(pdata); + iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(index)); return iterobj; } @@ -4022,6 +4362,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) const Shape *shape; JSIdArray *ida; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, iterobj); i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32(); @@ -4030,15 +4371,15 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) JS_ASSERT(iterobj->getParent()->isNative()); shape = (Shape *) iterobj->getPrivate(); - while (shape->previous() && (!shape->enumerable() || shape->isAlias())) + while (shape->previous() && !shape->enumerable()) shape = shape->previous(); if (!shape->previous()) { - JS_ASSERT(JSID_IS_EMPTY(shape->id)); + JS_ASSERT(shape->isEmptyShape()); *idp = JSID_VOID; } else { - iterobj->setPrivate(const_cast(shape->previous())); - *idp = shape->id; + iterobj->setPrivate(const_cast(shape->previous().get())); + *idp = shape->propid(); } } else { /* Non-native case: use the ida enumerated when iterobj was created. */ @@ -4055,43 +4396,63 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) return JS_TRUE; } -JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - return js_GetReservedSlot(cx, obj, index, Valueify(vp)); + return ElementIteratorObject::create(cx, obj); } -JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) +JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly) { - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, v); - return js_SetReservedSlot(cx, obj, index, Valueify(v)); + JS_ASSERT(!keysonly); + return JS_NewElementIterator(cx, obj); +} + +JS_PUBLIC_API(jsval) +JS_GetReservedSlot(JSObject *obj, uint32_t index) +{ + if (!obj->isNative()) + return UndefinedValue(); + + return GetReservedSlot(obj, index); +} + +JS_PUBLIC_API(void) +JS_SetReservedSlot(JSObject *obj, uint32_t index, jsval v) +{ + if (!obj->isNative()) + return; + + SetReservedSlot(obj, index, v); + GCPoke(obj->compartment()->rt, NullValue()); } JS_PUBLIC_API(JSObject *) JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); /* NB: jsuint cast does ToUint32. */ assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0)); - return NewDenseCopiedArray(cx, (jsuint)length, Valueify(vector)); + return NewDenseCopiedArray(cx, (jsuint)length, vector); } JS_PUBLIC_API(JSBool) JS_IsArrayObject(JSContext *cx, JSObject *obj) { assertSameCompartment(cx, obj); - return obj->isArray() || - (obj->isWrapper() && obj->unwrap()->isArray()); + return ObjectClassIs(*obj, ESClass_Array, cx); } JS_PUBLIC_API(JSBool) JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return js_GetLengthProperty(cx, obj, lengthp); @@ -4100,26 +4461,20 @@ JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) JS_PUBLIC_API(JSBool) JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return js_SetLengthProperty(cx, obj, length); } -JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); - return js_HasLengthProperty(cx, obj, lengthp); -} - JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); - return CheckAccess(cx, obj, id, mode, Valueify(vp), attrsp); + return CheckAccess(cx, obj, id, mode, vp, attrsp); } #ifdef JS_THREADSAFE @@ -4173,6 +4528,12 @@ JS_GetSecurityCallbacks(JSContext *cx) : cx->runtime->securityCallbacks; } +JS_PUBLIC_API(void) +JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin) +{ + rt->setTrustedPrincipals(prin); +} + JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent, const char *name) @@ -4180,17 +4541,20 @@ JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); JSAtom *atom; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, parent); if (!name) { atom = NULL; } else { - atom = js_Atomize(cx, name, strlen(name), 0); + atom = js_Atomize(cx, name, strlen(name)); if (!atom) return NULL; } - return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, atom); + + RootObject parentRoot(cx, &parent); + return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, atom); } JS_PUBLIC_API(JSFunction *) @@ -4199,26 +4563,29 @@ JS_NewFunctionById(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSO { JS_ASSERT(JSID_IS_STRING(id)); JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, parent); - return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, JSID_TO_ATOM(id)); + RootObject parentRoot(cx, &parent); + return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, JSID_TO_ATOM(id)); } JS_PUBLIC_API(JSObject *) JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, parent); // XXX no funobj for now if (!parent) { - if (cx->running()) - parent = GetScopeChain(cx, cx->fp()); + if (cx->hasfp()) + parent = &cx->fp()->scopeChain(); if (!parent) parent = cx->globalObject; JS_ASSERT(parent); } - if (funobj->getClass() != &js_FunctionClass) { + if (!funobj->isFunction()) { /* * We cannot clone this object, so fail (we used to return funobj, bad * idea, but we changed incompatibly to teach any abusers a lesson!). @@ -4228,9 +4595,18 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) return NULL; } - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); - if (!FUN_FLAT_CLOSURE(fun)) - return CloneFunctionObject(cx, fun, parent); + JSFunction *fun = funobj->toFunction(); + if (!fun->isInterpreted()) + return CloneFunctionObject(cx, fun, parent, fun->getAllocKind()); + + if (fun->script()->compileAndGo) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_CLONE_FUNOBJ_SCOPE); + return NULL; + } + + if (!fun->isFlatClosure()) + return CloneFunctionObject(cx, fun, parent, fun->getAllocKind()); /* * A flat closure carries its own environment, so why clone it? In case @@ -4248,8 +4624,8 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) if (!clone) return NULL; - JSUpvarArray *uva = fun->u.i.script->upvars(); - uint32 i = uva->length; + JSUpvarArray *uva = fun->script()->upvars(); + uint32_t i = uva->length; JS_ASSERT(i != 0); for (Shape::Range r(fun->script()->bindings.lastUpvar()); i-- != 0; r.popFront()) { @@ -4261,11 +4637,13 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) JSMSG_BAD_CLONE_FUNOBJ_SCOPE); return NULL; } - obj = obj->getParent(); + obj = obj->enclosingScope(); } - if (!obj->getProperty(cx, r.front().id, clone->getFlatClosureUpvars() + i)) + Value v; + if (!obj->getGeneric(cx, r.front().propid(), &v)) return NULL; + clone->toFunction()->setFlatClosureUpvar(i, v); } return clone; @@ -4274,7 +4652,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun) { - return FUN_OBJECT(fun); + return fun; } JS_PUBLIC_API(JSString *) @@ -4289,7 +4667,7 @@ JS_GetFunctionFlags(JSFunction *fun) return fun->flags; } -JS_PUBLIC_API(uint16) +JS_PUBLIC_API(uint16_t) JS_GetFunctionArity(JSFunction *fun) { return fun->nargs; @@ -4298,7 +4676,7 @@ JS_GetFunctionArity(JSFunction *fun) JS_PUBLIC_API(JSBool) JS_ObjectIsFunction(JSContext *cx, JSObject *obj) { - return obj->getClass() == &js_FunctionClass; + return obj->isFunction(); } JS_PUBLIC_API(JSBool) @@ -4307,10 +4685,20 @@ JS_ObjectIsCallable(JSContext *cx, JSObject *obj) return obj->isCallable(); } -static JSBool +JS_PUBLIC_API(JSBool) +JS_IsNativeFunction(JSObject *funobj, JSNative call) +{ + if (!funobj->isFunction()) + return false; + JSFunction *fun = funobj->toFunction(); + return fun->isNative() && fun->native() == call; +} + +JSBool js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp) { - JSFunctionSpec *fs = (JSFunctionSpec *) vp->toObject().getReservedSlot(0).toPrivate(); + JSFunctionSpec *fs = (JSFunctionSpec *) + vp->toObject().toFunction()->getExtendedSlot(0).toPrivate(); JS_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0); if (argc < 1) { @@ -4329,35 +4717,36 @@ js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp) /* Clear the last parameter in case too few arguments were passed. */ vp[2 + --argc].setUndefined(); - Native native = -#ifdef JS_TRACER - (fs->flags & JSFUN_TRCINFO) - ? JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, fs->call)->native - : -#endif - Valueify(fs->call); - return native(cx, argc, vp); + return fs->call(cx, argc, vp); } JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) { + RootObject objRoot(cx, &obj); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); uintN flags; - JSObject *ctor; + RootedVarObject ctor(cx); JSFunction *fun; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - ctor = NULL; for (; fs->name; fs++) { flags = fs->flags; + JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name)); + if (!atom) + return JS_FALSE; + /* * Define a generic arity N+1 static method for the arity N prototype * method if flags contains JSFUN_GENERIC_NATIVE. */ if (flags & JSFUN_GENERIC_NATIVE) { + RootAtom root(cx, &atom); + if (!ctor) { ctor = JS_GetConstructor(cx, obj); if (!ctor) @@ -4365,10 +4754,11 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) } flags &= ~JSFUN_GENERIC_NATIVE; - fun = JS_DefineFunction(cx, ctor, fs->name, - Jsvalify(js_generic_native_method_dispatcher), + fun = js_DefineFunction(cx, ctor, ATOM_TO_JSID(atom), + js_generic_native_method_dispatcher, fs->nargs + 1, - flags & ~JSFUN_TRCINFO); + flags, + JSFunction::ExtendedFinalizeKind); if (!fun) return JS_FALSE; @@ -4376,12 +4766,11 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) * As jsapi.h notes, fs must point to storage that lives as long * as fun->object lives. */ - Value priv = PrivateValue(fs); - if (!js_SetReservedSlot(cx, FUN_OBJECT(fun), 0, priv)) - return JS_FALSE; + fun->setExtendedSlot(0, PrivateValue(fs)); } - fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); + fun = js_DefineFunction(cx, objRoot, + ATOM_TO_JSID(atom), fs->call, fs->nargs, flags); if (!fun) return JS_FALSE; } @@ -4392,13 +4781,16 @@ JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, uintN nargs, uintN attrs) { + RootObject objRoot(cx, &obj); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); if (!atom) return NULL; - return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), Valueify(call), nargs, attrs); + return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs); } JS_PUBLIC_API(JSFunction *) @@ -4406,70 +4798,75 @@ JS_DefineUCFunction(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSNative call, uintN nargs, uintN attrs) { + RootObject objRoot(cx, &obj); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return NULL; - return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), Valueify(call), nargs, attrs); + return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs); } extern JS_PUBLIC_API(JSFunction *) JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call, uintN nargs, uintN attrs) { + RootObject objRoot(cx, &obj); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - return js_DefineFunction(cx, obj, id, Valueify(call), nargs, attrs); + return js_DefineFunction(cx, objRoot, id, call, nargs, attrs); } -inline static void -LAST_FRAME_EXCEPTION_CHECK(JSContext *cx, bool result) -{ - if (!result && !cx->hasRunOption(JSOPTION_DONT_REPORT_UNCAUGHT)) - js_ReportUncaughtException(cx); -} +struct AutoLastFrameCheck { + AutoLastFrameCheck(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx) { + JS_ASSERT(cx); + JS_GUARD_OBJECT_NOTIFIER_INIT; + } -inline static void -LAST_FRAME_CHECKS(JSContext *cx, bool result) -{ - if (!JS_IsRunning(cx)) { - LAST_FRAME_EXCEPTION_CHECK(cx, result); + ~AutoLastFrameCheck() { + if (cx->isExceptionPending() && + !JS_IsRunning(cx) && + !cx->hasRunOption(JSOPTION_DONT_REPORT_UNCAUGHT)) { + js_ReportUncaughtException(cx); + } } -} -inline static uint32 + private: + JSContext *cx; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +inline static uint32_t JS_OPTIONS_TO_TCFLAGS(JSContext *cx) { return (cx->hasRunOption(JSOPTION_COMPILE_N_GO) ? TCF_COMPILE_N_GO : 0) | (cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL) ? TCF_NO_SCRIPT_RVAL : 0); } -static JSObject * +static JSScript * CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *principals, - const jschar *chars, size_t length, - const char *filename, uintN lineno, JSVersion version) + const jschar *chars, size_t length, + const char *filename, uintN lineno, JSVersion version) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, principals); + AutoLastFrameCheck lfc(cx); - 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); - JSObject *scriptObj = NULL; - if (script) { - scriptObj = js_NewScriptObject(cx, script); - if (!scriptObj) - js_DestroyScript(cx, script); - } - LAST_FRAME_CHECKS(cx, scriptObj); - return scriptObj; + uint32_t tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_SCRIPT_GLOBAL; + return frontend::CompileScript(cx, obj, NULL, principals, NULL, tcflags, + chars, length, filename, lineno, version); } -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, @@ -4481,7 +4878,7 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, avi.version()); } -JS_PUBLIC_API(JSObject *) +JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, const char *filename, uintN lineno) @@ -4490,7 +4887,7 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *prin cx->findVersion()); } -JS_PUBLIC_API(JSObject *) +JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { @@ -4498,7 +4895,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(JSObject *) +JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, @@ -4509,25 +4906,26 @@ JS_CompileScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, return JS_CompileScriptForPrincipals(cx, obj, principals, bytes, length, filename, lineno); } -JS_PUBLIC_API(JSObject *) +JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, const char *filename, uintN lineno) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); - jschar *chars = js_InflateString(cx, bytes, &length); + jschar *chars = InflateString(cx, bytes, &length); if (!chars) return NULL; - JSObject *scriptObj = + JSScript *script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno); cx->free_(chars); - return scriptObj; + return script; } -JS_PUBLIC_API(JSObject *) +JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) { @@ -4543,12 +4941,13 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSBool bytes_are_utf8, JSObject *obj, c JSExceptionState *exnState; JSErrorReporter older; + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); if (bytes_are_utf8) - chars = js_InflateString(cx, bytes, &length, JS_TRUE); + chars = InflateString(cx, bytes, &length, CESU8Encoding); else - chars = js_InflateString(cx, bytes, &length); + chars = InflateString(cx, bytes, &length); if (!chars) return JS_TRUE; @@ -4588,29 +4987,30 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSBool bytes_are_utf8, JSObject *obj, c # define fast_getc getc #endif -static JSObject * -CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, - const char* filename, FILE *fp) +static JSScript * +CompileUTF8FileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, + const char* filename, FILE *fp) { struct stat st; int ok = fstat(fileno(fp), &st); if (ok != 0) return NULL; - jschar *buf = NULL; + char *buf = NULL; size_t len = st.st_size; size_t i = 0; JSScript *script; /* Read in the whole file, then compile it. */ if (fp == stdin) { - JS_ASSERT(len == 0); - len = 8; /* start with a small buffer, expand as necessary */ + if (len == 0) + len = 8; /* start with a small buffer, expand as necessary */ + int c; bool hitEOF = false; while (!hitEOF) { len *= 2; - jschar* tmpbuf = (jschar *) cx->realloc_(buf, len * sizeof(jschar)); + char* tmpbuf = (char *) cx->realloc_(buf, len * sizeof(char)); if (!tmpbuf) { cx->free_(buf); return NULL; @@ -4623,99 +5023,100 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, hitEOF = true; break; } - buf[i++] = (jschar) (unsigned char) c; + buf[i++] = c; } } } else { - buf = (jschar *) cx->malloc_(len * sizeof(jschar)); + buf = (char *) cx->malloc_(len * sizeof(char)); if (!buf) return NULL; int c; - while ((c = fast_getc(fp)) != EOF) - buf[i++] = (jschar) (unsigned char) c; + // The |i < len| is necessary for files that lie about their length, + // e.g. /dev/zero and /dev/random. See bug 669434. + while (i < len && (c = fast_getc(fp)) != EOF) + buf[i++] = c; } 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()); + size_t decodelen = len; + jschar *decodebuf = (jschar *)cx->malloc_(decodelen * sizeof(jschar)); + if (JS_DecodeUTF8(cx, buf, len, decodebuf, &decodelen)) { + uint32_t tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_SCRIPT_GLOBAL; + script = frontend::CompileScript(cx, obj, NULL, principals, NULL, + tcflags, decodebuf, decodelen, + filename, 1, cx->findVersion()); + } else { + script = NULL; + } cx->free_(buf); - if (!script) - return NULL; - - JSObject *scriptObj = js_NewScriptObject(cx, script); - if (!scriptObj) - js_DestroyScript(cx, script); - - return scriptObj; + cx->free_(decodebuf); + return script; } -JS_PUBLIC_API(JSObject *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) +JS_PUBLIC_API(JSScript *) +JS_CompileUTF8File(JSContext *cx, JSObject *obj, const char *filename) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - 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; - } - } + AutoLastFrameCheck lfc(cx); - scriptObj = CompileFileHelper(cx, obj, NULL, filename, fp); - if (fp != stdin) - fclose(fp); - } while (false); + 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"); + return NULL; + } + } - LAST_FRAME_CHECKS(cx, scriptObj); - return scriptObj; + JSScript *script = CompileUTF8FileHelper(cx, obj, NULL, filename, fp); + if (fp != stdin) + fclose(fp); + return script; } -JS_PUBLIC_API(JSObject *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, - FILE *file, JSPrincipals *principals) +JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, + FILE *file, JSPrincipals *principals) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, principals); - JSObject *scriptObj = CompileFileHelper(cx, obj, principals, filename, file); - LAST_FRAME_CHECKS(cx, scriptObj); - return scriptObj; + AutoLastFrameCheck lfc(cx); + + return CompileUTF8FileHelper(cx, obj, principals, filename, file); } -JS_PUBLIC_API(JSObject *) -JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, const char *filename, - FILE *file, JSPrincipals *principals, JSVersion version) +JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, const char *filename, + FILE *file, JSPrincipals *principals, JSVersion version) { AutoVersionAPI ava(cx, version); - return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, principals); + return JS_CompileUTF8FileHandleForPrincipals(cx, obj, filename, file, principals); } -JS_PUBLIC_API(JSObject *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *file) +JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandle(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); + return JS_CompileUTF8FileHandleForPrincipals(cx, obj, filename, file, NULL); } -JS_PUBLIC_API(JSScript *) -JS_GetScriptFromObject(JSObject *scriptObj) +JS_PUBLIC_API(JSObject *) +JS_GetGlobalFromScript(JSScript *script) { - JS_ASSERT(scriptObj->isScript()); + JS_ASSERT(!script->isCachedEval); + JS_ASSERT(script->globalObject); - return (JSScript *) scriptObj->getPrivate(); + return script->globalObject; } static JSFunction * @@ -4725,82 +5126,51 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno, JSVersion version) { - JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - JSFunction *fun; - JSAtom *funAtom, *argAtom; - uintN i; + RootObject objRoot(cx, &obj); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, principals); + AutoLastFrameCheck lfc(cx); + + JSAtom *funAtom; if (!name) { funAtom = NULL; } else { - funAtom = js_Atomize(cx, name, strlen(name), 0); - if (!funAtom) { - fun = NULL; - goto out2; - } + funAtom = js_Atomize(cx, name, strlen(name)); + if (!funAtom) + return NULL; + } + + Bindings bindings(cx); + for (uintN i = 0; i < nargs; i++) { + uint16_t dummy; + JSAtom *argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i])); + if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy)) + return NULL; } - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom); + JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, objRoot, funAtom); if (!fun) - goto out2; + return NULL; + if (!frontend::CompileFunctionBody(cx, fun, principals, NULL, &bindings, + chars, length, filename, lineno, version)) { - 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, emptyCallShape); - AutoBindingsRooter root(cx, bindings); - for (i = 0; i < nargs; i++) { - argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); - if (!argAtom) { - fun = NULL; - goto out2; - } - - uint16 dummy; - if (!bindings.addArgument(cx, argAtom, &dummy)) { - fun = NULL; - goto out2; - } - } - - if (!Compiler::compileFunctionBody(cx, fun, principals, &bindings, - chars, length, filename, lineno, version)) { - fun = NULL; - goto out2; - } - - if (obj && funAtom && - !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), ObjectValue(*fun), - NULL, NULL, JSPROP_ENUMERATE)) { - fun = NULL; - } - -#ifdef JS_SCOPE_DEPTH_METER - if (fun && obj) { - JSObject *pobj = obj; - uintN depth = 1; + return NULL; + } - while ((pobj = pobj->getParent()) != NULL) - ++depth; - JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth); - } -#endif + if (obj && funAtom && + !obj->defineGeneric(cx, ATOM_TO_JSID(funAtom), ObjectValue(*fun), NULL, NULL, + JSPROP_ENUMERATE)) + { + return NULL; } - out2: - LAST_FRAME_CHECKS(cx, fun); return fun; } + JS_PUBLIC_API(JSFunction *) JS_CompileUCFunctionForPrincipalsVersion(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, @@ -4844,7 +5214,7 @@ JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, const char *filename, uintN lineno) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - jschar *chars = js_InflateString(cx, bytes, &length); + jschar *chars = InflateString(cx, bytes, &length); if (!chars) return NULL; JSFunction *fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, @@ -4872,10 +5242,11 @@ JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN inde JSPrinter *jp; JSString *str; + AssertNoGC(cx); CHECK_REQUEST(cx); #ifdef DEBUG - if (cx->compartment != script->compartment) - CompartmentChecker::fail(cx->compartment, script->compartment); + if (cx->compartment != script->compartment()) + CompartmentChecker::fail(cx->compartment, script->compartment()); #endif jp = js_NewPrinter(cx, name, NULL, indent & ~JS_DONT_PRETTY_PRINT, @@ -4891,16 +5262,11 @@ 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) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return js_DecompileToString(cx, "JS_DecompileFunction", fun, @@ -4913,6 +5279,7 @@ JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return js_DecompileToString(cx, "JS_DecompileFunctionBody", fun, @@ -4922,51 +5289,60 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) } JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - + AssertNoGC(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, scriptObj); + assertSameCompartment(cx, obj, script); + AutoLastFrameCheck lfc(cx); - JSBool ok = Execute(cx, *obj, scriptObj->getScript(), NULL, 0, Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - return ok; + return Execute(cx, script, *obj, rval); } JS_PUBLIC_API(JSBool) -JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval, +JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval, JSVersion version) { AutoVersionAPI ava(cx, version); - return JS_ExecuteScript(cx, obj, scriptObj, rval); + return JS_ExecuteScript(cx, obj, script, rval); } bool EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, - JSPrincipals *principals, + JSPrincipals *principals, JSPrincipals *originPrincipals, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval, JSVersion compileVersion) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + uint32_t flags = TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_GLOBAL; + if (!rval) + flags |= TCF_NO_SCRIPT_RVAL; + CHECK_REQUEST(cx); - JSScript *script = Compiler::compileScript(cx, obj, NULL, principals, - !rval - ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL - : TCF_COMPILE_N_GO, - chars, length, filename, lineno, compileVersion); - if (!script) { - LAST_FRAME_CHECKS(cx, script); + AutoLastFrameCheck lfc(cx); + JSScript *script = frontend::CompileScript(cx, obj, NULL, principals, originPrincipals, + flags, chars, length, filename, lineno, + compileVersion); + if (!script) return false; - } + JS_ASSERT(script->getVersion() == compileVersion); - bool ok = Execute(cx, *obj, script, NULL, 0, Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - js_DestroyScript(cx, script); - return ok; + return Execute(cx, script, *obj, rval); +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + return EvaluateUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length, + filename, lineno, rval, cx->findVersion()); } JS_PUBLIC_API(JSBool) @@ -4977,19 +5353,22 @@ JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, jsval *rval, JSVersion version) { AutoVersionAPI avi(cx, version); - return EvaluateUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, + return EvaluateUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length, filename, lineno, rval, avi.version()); } -JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + JSPrincipals *originPrincipals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval, JSVersion version) { - return EvaluateUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, - filename, lineno, rval, cx->findVersion()); + AutoVersionAPI avi(cx, version); + return EvaluateUCScriptForPrincipalsCommon(cx, obj, principals, originPrincipals, + chars, length, filename, lineno, rval, + avi.version()); } JS_PUBLIC_API(JSBool) @@ -5008,7 +5387,7 @@ JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *princ { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); size_t length = nbytes; - jschar *chars = js_InflateString(cx, bytes, &length); + jschar *chars = InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; JSBool ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, @@ -5040,12 +5419,12 @@ JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval jsval *rval) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fun, JSValueArray(argv, argc)); - JSBool ok = ExternalInvoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), argc, - Valueify(argv), Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - return ok; + AutoLastFrameCheck lfc(cx); + + return Invoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), argc, argv, rval); } JS_PUBLIC_API(JSBool) @@ -5053,18 +5432,16 @@ JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *rval) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, JSValueArray(argv, argc)); + AutoLastFrameCheck lfc(cx); - AutoValueRooter tvr(cx); - JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); - JSBool ok = - atom && - js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, tvr.addr()) && - ExternalInvoke(cx, ObjectOrNullValue(obj), tvr.value(), argc, Valueify(argv), - Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - return ok; + Value v; + JSAtom *atom = js_Atomize(cx, name, strlen(name)); + return atom && + js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, &v) && + Invoke(cx, ObjectOrNullValue(obj), v, argc, argv, rval); } JS_PUBLIC_API(JSBool) @@ -5072,13 +5449,12 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval jsval *rval) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fval, JSValueArray(argv, argc)); - JSBool ok = ExternalInvoke(cx, ObjectOrNullValue(obj), Valueify(fval), argc, Valueify(argv), - Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - return ok; + AutoLastFrameCheck lfc(cx); + + return Invoke(cx, ObjectOrNullValue(obj), fval, argc, argv, rval); } namespace JS { @@ -5086,13 +5462,12 @@ namespace JS { JS_PUBLIC_API(bool) Call(JSContext *cx, jsval thisv, jsval fval, uintN argc, jsval *argv, jsval *rval) { - JSBool ok; - + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, thisv, fval, JSValueArray(argv, argc)); - ok = ExternalInvoke(cx, Valueify(thisv), Valueify(fval), argc, Valueify(argv), Valueify(rval)); - LAST_FRAME_CHECKS(cx, ok); - return ok; + AutoLastFrameCheck lfc(cx); + + return Invoke(cx, thisv, fval, argc, argv, rval); } } // namespace JS @@ -5100,50 +5475,45 @@ Call(JSContext *cx, jsval thisv, jsval fval, uintN argc, jsval *argv, jsval *rva JS_PUBLIC_API(JSObject *) JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, ctor, JSValueArray(argv, argc)); + AutoLastFrameCheck lfc(cx); // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW // is not a simple variation of JSOP_CALL. We have to determine what class // of object to create, create it, and clamp the return value to an object, - // among other details. js_InvokeConstructor does the hard work. + // among other details. InvokeConstructor does the hard work. InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return NULL; args.calleev().setObject(*ctor); args.thisv().setNull(); - memcpy(args.argv(), argv, argc * sizeof(jsval)); + PodCopy(args.array(), argv, argc); - bool ok = InvokeConstructor(cx, args); + if (!InvokeConstructor(cx, args)) + return NULL; - JSObject *obj = NULL; - if (ok) { - if (args.rval().isObject()) { - obj = &args.rval().toObject(); - } else { - /* - * Although constructors may return primitives (via proxies), this - * API is asking for an object, so we report an error. - */ - JSAutoByteString bytes; - if (js_ValueToPrintable(cx, args.rval(), &bytes)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, - bytes.ptr()); - } + if (!args.rval().isObject()) { + /* + * Although constructors may return primitives (via proxies), this + * API is asking for an object, so we report an error. + */ + JSAutoByteString bytes; + if (js_ValueToPrintable(cx, args.rval(), &bytes)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, + bytes.ptr()); } + return NULL; } - LAST_FRAME_CHECKS(cx, ok); - return obj; + return &args.rval().toObject(); } JS_PUBLIC_API(JSOperationCallback) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback) { -#ifdef JS_THREADSAFE - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); -#endif JSOperationCallback old = cx->operationCallback; cx->operationCallback = callback; return old; @@ -5161,63 +5531,62 @@ JS_TriggerOperationCallback(JSContext *cx) #ifdef JS_THREADSAFE AutoLockGC lock(cx->runtime); #endif - TriggerOperationCallback(cx); + cx->runtime->triggerOperationCallback(); } JS_PUBLIC_API(void) -JS_TriggerAllOperationCallbacks(JSRuntime *rt) +JS_TriggerRuntimeOperationCallback(JSRuntime *rt) { #ifdef JS_THREADSAFE AutoLockGC lock(rt); #endif - TriggerAllOperationCallbacks(rt); + rt->triggerOperationCallback(); } JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) { - /* - * The use of cx->fp below is safe. Rationale: Here we don't care if the - * interpreter state is stale. We just want to know if there *is* any - * interpreter state. - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - -#ifdef JS_TRACER - JS_ASSERT_IF(JS_ON_TRACE(cx) && JS_TRACE_MONITOR_ON_TRACE(cx)->tracecx == cx, cx->running()); -#endif StackFrame *fp = cx->maybefp(); while (fp && fp->isDummyFrame()) fp = fp->prev(); return fp != NULL; } -JS_PUBLIC_API(JSStackFrame *) +JS_PUBLIC_API(JSBool) JS_SaveFrameChain(JSContext *cx) { + AssertNoGC(cx); CHECK_REQUEST(cx); - StackFrame *fp = js_GetTopStackFrame(cx); - if (!fp) - return NULL; - cx->stack.saveActiveSegment(); - return Jsvalify(fp); + return cx->stack.saveFrameChain(); } JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) +JS_RestoreFrameChain(JSContext *cx) { + AssertNoGC(cx); CHECK_REQUEST(cx); - JS_ASSERT_NOT_ON_TRACE(cx); - JS_ASSERT(!cx->running()); - if (!fp) - return; - cx->stack.restoreSegment(); + cx->stack.restoreFrameChain(); } +#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 + /************************************************************************/ JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewStringCopyN(cx, s, n); } @@ -5229,11 +5598,12 @@ JS_NewStringCopyZ(JSContext *cx, const char *s) jschar *js; JSString *str; + AssertNoGC(cx); CHECK_REQUEST(cx); - if (!s) + if (!s || !*s) return cx->runtime->emptyString; n = strlen(s); - js = js_InflateString(cx, s, &n); + js = InflateString(cx, s, &n); if (!js) return NULL; str = js_NewString(cx, js, n); @@ -5243,28 +5613,41 @@ JS_NewStringCopyZ(JSContext *cx, const char *s) } JS_PUBLIC_API(JSBool) -JS_StringHasBeenInterned(JSString *str) +JS_StringHasBeenInterned(JSContext *cx, JSString *str) { - return str->isAtom(); + AssertNoGC(cx); + CHECK_REQUEST(cx); + + if (!str->isAtom()) + return false; + + return AtomIsInterned(cx, &str->asAtom()); } JS_PUBLIC_API(JSString *) JS_InternJSString(JSContext *cx, JSString *str) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_AtomizeString(cx, str, 0); + JSAtom *atom = js_AtomizeString(cx, str, InternAtom); + JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom)); + return atom; } JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_Atomize(cx, s, strlen(s), ATOM_INTERNED); + JSAtom *atom = js_Atomize(cx, s, strlen(s), InternAtom); + JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom)); + return atom; } JS_PUBLIC_API(JSString *) JS_NewUCString(JSContext *cx, jschar *chars, size_t length) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewString(cx, chars, length); } @@ -5272,6 +5655,7 @@ JS_NewUCString(JSContext *cx, jschar *chars, size_t length) JS_PUBLIC_API(JSString *) JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewStringCopyN(cx, s, n); } @@ -5279,6 +5663,7 @@ JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) JS_PUBLIC_API(JSString *) JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) { + AssertNoGC(cx); CHECK_REQUEST(cx); if (!s) return cx->runtime->emptyString; @@ -5288,8 +5673,11 @@ JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) JS_PUBLIC_API(JSString *) JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return js_AtomizeChars(cx, s, length, ATOM_INTERNED); + JSAtom *atom = js_AtomizeChars(cx, s, length, InternAtom); + JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom)); + return atom; } JS_PUBLIC_API(JSString *) @@ -5307,6 +5695,7 @@ JS_GetStringLength(JSString *str) JS_PUBLIC_API(const jschar *) JS_GetStringCharsZ(JSContext *cx, JSString *str) { + AssertNoGCOrFlatString(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); return str->getCharsZ(cx); @@ -5315,6 +5704,7 @@ JS_GetStringCharsZ(JSContext *cx, JSString *str) JS_PUBLIC_API(const jschar *) JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength) { + AssertNoGCOrFlatString(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); *plength = str->length(); @@ -5324,6 +5714,7 @@ JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength) JS_PUBLIC_API(const jschar *) JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength) { + AssertNoGCOrFlatString(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); *plength = str->length(); @@ -5347,6 +5738,7 @@ JS_GetInternedStringCharsAndLength(JSString *str, size_t *plength) extern JS_PUBLIC_API(JSFlatString *) JS_FlattenString(JSContext *cx, JSString *str) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, str); return str->getCharsZ(cx) ? (JSFlatString *)str : NULL; @@ -5359,14 +5751,20 @@ JS_GetFlatStringChars(JSFlatString *str) } JS_PUBLIC_API(JSBool) -JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + return CompareStrings(cx, str1, str2, result); } JS_PUBLIC_API(JSBool) JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + JSLinearString *linearStr = str->ensureLinear(cx); if (!linearStr) return false; @@ -5389,6 +5787,7 @@ JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote JS_PUBLIC_API(size_t) JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote) { + AssertNoGC(cx); JSLinearString *linearStr = str->ensureLinear(cx); if (!linearStr) return size_t(-1); @@ -5405,6 +5804,7 @@ JS_FileEscapedString(FILE *fp, JSString *str, char quote) JS_PUBLIC_API(JSString *) JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewString(cx, chars, length); } @@ -5412,6 +5812,7 @@ JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) JS_PUBLIC_API(JSString *) JS_NewDependentString(JSContext *cx, JSString *str, size_t start, size_t length) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewDependentString(cx, str, start, length); } @@ -5419,6 +5820,7 @@ JS_NewDependentString(JSContext *cx, JSString *str, size_t start, size_t length) JS_PUBLIC_API(JSString *) JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_ConcatStrings(cx, left, right); } @@ -5426,6 +5828,7 @@ JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) JS_PUBLIC_API(const jschar *) JS_UndependString(JSContext *cx, JSString *str) { + AssertNoGC(cx); CHECK_REQUEST(cx); return str->getCharsZ(cx); } @@ -5433,6 +5836,7 @@ JS_UndependString(JSContext *cx, JSString *str) JS_PUBLIC_API(JSBool) JS_MakeStringImmutable(JSContext *cx, JSString *str) { + AssertNoGC(cx); CHECK_REQUEST(cx); return !!str->ensureFixed(cx); } @@ -5440,9 +5844,12 @@ JS_MakeStringImmutable(JSContext *cx, JSString *str) JS_PUBLIC_API(JSBool) JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, size_t *dstlenp) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + size_t n; if (!dst) { - n = js_GetDeflatedStringLength(cx, src, srclen); + n = GetDeflatedStringLength(cx, src, srclen); if (n == (size_t)-1) { *dstlenp = 0; return JS_FALSE; @@ -5451,45 +5858,58 @@ JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, return JS_TRUE; } - return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); + return DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); } JS_PUBLIC_API(JSBool) JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp) { - return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + return InflateStringToBuffer(cx, src, srclen, dst, dstlenp); } JS_PUBLIC_API(JSBool) JS_DecodeUTF8(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp) { - return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp); + AssertNoGC(cx); + CHECK_REQUEST(cx); + return InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp); } JS_PUBLIC_API(char *) JS_EncodeString(JSContext *cx, JSString *str) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + const jschar *chars = str->getChars(cx); if (!chars) return NULL; - return js_DeflateString(cx, chars, str->length()); + return DeflateString(cx, chars, str->length()); } JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext *cx, JSString *str) { + /* jsd calls us with a NULL cx. Ugh. */ + if (cx) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + } + const jschar *chars = str->getChars(cx); if (!chars) return size_t(-1); - return js_GetDeflatedStringLength(cx, chars, str->length()); + return GetDeflatedStringLength(cx, chars, str->length()); } JS_PUBLIC_API(size_t) JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length) { /* - * FIXME bug 612141 - fix js_DeflateStringToBuffer interface so the result + * FIXME bug 612141 - fix DeflateStringToBuffer interface so the result * would allow to distinguish between insufficient buffer and encoding * error. */ @@ -5497,12 +5917,12 @@ JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length) const jschar *chars = str->getChars(NULL); if (!chars) return size_t(-1); - if (js_DeflateStringToBuffer(NULL, chars, str->length(), buffer, &writtenLength)) { + if (DeflateStringToBuffer(NULL, chars, str->length(), buffer, &writtenLength)) { JS_ASSERT(writtenLength <= length); return writtenLength; } JS_ASSERT(writtenLength <= length); - size_t necessaryLength = js_GetDeflatedStringLength(NULL, chars, str->length()); + size_t necessaryLength = GetDeflatedStringLength(NULL, chars, str->length()); if (necessaryLength == size_t(-1)) return size_t(-1); if (writtenLength != length) { @@ -5517,44 +5937,46 @@ JS_PUBLIC_API(JSBool) JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, JSONWriteCallback callback, void *data) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, replacer, space); StringBuffer sb(cx); - if (!js_Stringify(cx, Valueify(vp), replacer, Valueify(space), sb)) + if (!js_Stringify(cx, vp, replacer, space, sb)) return false; + if (sb.empty()) { + JSAtom *nullAtom = cx->runtime->atomState.nullAtom; + return callback(nullAtom->chars(), nullAtom->length(), data); + } return callback(sb.begin(), sb.length(), data); } JS_PUBLIC_API(JSBool) -JS_TryJSON(JSContext *cx, jsval *vp) -{ - CHECK_REQUEST(cx); - assertSameCompartment(cx, *vp); - return js_TryJSON(cx, Valueify(vp)); -} - -JS_PUBLIC_API(JSBool) -JS_ParseJSON(JSContext *cx, const jschar *chars, uint32 len, jsval *vp) +JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return ParseJSONWithReviver(cx, chars, len, NullValue(), Valueify(vp)); + return ParseJSONWithReviver(cx, chars, len, NullValue(), vp); } JS_PUBLIC_API(JSBool) -JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32 len, jsval reviver, jsval *vp) +JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval reviver, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return ParseJSONWithReviver(cx, chars, len, Valueify(reviver), Valueify(vp)); + return ParseJSONWithReviver(cx, chars, len, reviver, vp); } JS_PUBLIC_API(JSBool) -JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes, - uint32 version, jsval *vp, +JS_ReadStructuredClone(JSContext *cx, const uint64_t *buf, size_t nbytes, + uint32_t version, jsval *vp, const JSStructuredCloneCallbacks *optionalCallbacks, void *closure) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + if (version > JS_STRUCTURED_CLONE_VERSION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); return false; @@ -5563,20 +5985,22 @@ JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes, optionalCallbacks ? optionalCallbacks : cx->runtime->structuredCloneCallbacks; - return ReadStructuredClone(cx, buf, nbytes, Valueify(vp), callbacks, closure); + return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); } JS_PUBLIC_API(JSBool) -JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp, +JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **bufp, size_t *nbytesp, const JSStructuredCloneCallbacks *optionalCallbacks, void *closure) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + const JSStructuredCloneCallbacks *callbacks = optionalCallbacks ? optionalCallbacks : cx->runtime->structuredCloneCallbacks; - return WriteStructuredClone(cx, Valueify(v), (uint64_t **) bufp, nbytesp, - callbacks, closure); + return WriteStructuredClone(cx, v, (uint64_t **) bufp, nbytesp, callbacks, closure); } JS_PUBLIC_API(JSBool) @@ -5584,13 +6008,107 @@ JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, const JSStructuredCloneCallbacks *optionalCallbacks, void *closure) { + AssertNoGC(cx); + CHECK_REQUEST(cx); + const JSStructuredCloneCallbacks *callbacks = optionalCallbacks ? optionalCallbacks : cx->runtime->structuredCloneCallbacks; JSAutoStructuredCloneBuffer buf; return buf.write(cx, v, callbacks, closure) && - buf.read(vp, cx, callbacks, closure); + buf.read(cx, vp, callbacks, closure); +} + +void +JSAutoStructuredCloneBuffer::clear() +{ + if (data_) { + Foreground::free_(data_); + data_ = NULL; + nbytes_ = 0; + version_ = 0; + } +} + +void +JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) +{ + clear(); + data_ = data; + nbytes_ = nbytes; + version_ = version; +} + +bool +JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) +{ + uint64_t *newData = static_cast(OffTheBooks::malloc_(nbytes)); + if (!newData) + return false; + + js_memcpy(newData, srcData, nbytes); + + clear(); + data_ = newData; + nbytes_ = nbytes; + version_ = version; + return true; +} +void +JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) +{ + *datap = data_; + *nbytesp = nbytes_; + if (versionp) + *versionp = version_; + + data_ = NULL; + nbytes_ = 0; + version_ = 0; +} + +bool +JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) const +{ + JS_ASSERT(cx); + JS_ASSERT(data_); + return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, + optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval v, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + clear(); + bool ok = !!JS_WriteStructuredClone(cx, v, &data_, &nbytes_, + optionalCallbacks, closure); + if (!ok) { + data_ = NULL; + nbytes_ = 0; + version_ = JS_STRUCTURED_CLONE_VERSION; + } + return ok; +} + +void +JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) +{ + uint64_t *data = other.data_; + size_t nbytes = other.nbytes_; + uint32_t version = other.version_; + + other.data_ = this->data_; + other.nbytes_ = this->nbytes_; + other.version_ = this->version_; + + this->data_ = data; + this->nbytes_ = nbytes; + this->version_ = version; } JS_PUBLIC_API(void) @@ -5600,7 +6118,7 @@ JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks * } JS_PUBLIC_API(JSBool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) { return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); } @@ -5612,7 +6130,7 @@ JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) } JS_PUBLIC_API(JSBool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32 tag, uint32 data) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) { return w->output().writePair(tag, data); } @@ -5655,6 +6173,7 @@ JS_ReportError(JSContext *cx, const char *format, ...) { va_list ap; + AssertNoGC(cx); va_start(ap, format); js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); va_end(ap); @@ -5666,6 +6185,7 @@ JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, { va_list ap; + AssertNoGC(cx); va_start(ap, errorNumber); js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, JS_TRUE, ap); @@ -5678,6 +6198,7 @@ JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, { va_list ap; + AssertNoGC(cx); va_start(ap, errorNumber); js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, JS_FALSE, ap); @@ -5690,6 +6211,7 @@ JS_ReportWarning(JSContext *cx, const char *format, ...) va_list ap; JSBool ok; + AssertNoGC(cx); va_start(ap, format); ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); va_end(ap); @@ -5704,6 +6226,7 @@ JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, va_list ap; JSBool ok; + AssertNoGC(cx); va_start(ap, errorNumber); ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, JS_TRUE, ap); @@ -5719,6 +6242,7 @@ JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, va_list ap; JSBool ok; + AssertNoGC(cx); va_start(ap, errorNumber); ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, JS_FALSE, ap); @@ -5738,6 +6262,12 @@ JS_ReportAllocationOverflow(JSContext *cx) js_ReportAllocationOverflow(cx); } +JS_PUBLIC_API(JSErrorReporter) +JS_GetErrorReporter(JSContext *cx) +{ + return cx->errorReporter; +} + JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) { @@ -5756,6 +6286,7 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) JS_PUBLIC_API(JSObject *) JS_NewDateObject(JSContext *cx, int year, int mon, int mday, int hour, int min, int sec) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewDateObject(cx, year, mon, mday, hour, min, sec); } @@ -5763,6 +6294,7 @@ JS_NewDateObject(JSContext *cx, int year, int mon, int mday, int hour, int min, JS_PUBLIC_API(JSObject *) JS_NewDateObjectMsec(JSContext *cx, jsdouble msec) { + AssertNoGC(cx); CHECK_REQUEST(cx); return js_NewDateObjectMsec(cx, msec); } @@ -5770,6 +6302,7 @@ JS_NewDateObjectMsec(JSContext *cx, jsdouble msec) JS_PUBLIC_API(JSBool) JS_ObjectIsDate(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); JS_ASSERT(obj); return obj->isDate(); } @@ -5782,12 +6315,14 @@ JS_ObjectIsDate(JSContext *cx, JSObject *obj) JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uintN flags) { + AssertNoGC(cx); CHECK_REQUEST(cx); - jschar *chars = js_InflateString(cx, bytes, &length); + jschar *chars = InflateString(cx, bytes, &length); if (!chars) return NULL; - RegExpStatics *res = RegExpStatics::extractFrom(obj->asGlobal()); - JSObject *reobj = RegExp::createObject(cx, res, chars, length, flags); + + RegExpStatics *res = obj->asGlobal().getRegExpStatics(); + RegExpObject *reobj = RegExpObject::create(cx, res, chars, length, RegExpFlag(flags), NULL); cx->free_(chars); return reobj; } @@ -5795,81 +6330,100 @@ JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uin JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, JSObject *obj, jschar *chars, size_t length, uintN flags) { + AssertNoGC(cx); CHECK_REQUEST(cx); - RegExpStatics *res = RegExpStatics::extractFrom(obj->asGlobal()); - return RegExp::createObject(cx, res, chars, length, flags); + RegExpStatics *res = obj->asGlobal().getRegExpStatics(); + return RegExpObject::create(cx, res, chars, length, RegExpFlag(flags), NULL); } JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSObject *obj, JSString *input, JSBool multiline) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, input); - RegExpStatics::extractFrom(obj->asGlobal())->reset(input, !!multiline); + obj->asGlobal().getRegExpStatics()->reset(cx, input, !!multiline); } JS_PUBLIC_API(void) JS_ClearRegExpStatics(JSContext *cx, JSObject *obj) { + AssertNoGC(cx); CHECK_REQUEST(cx); JS_ASSERT(obj); - RegExpStatics::extractFrom(obj->asGlobal())->clear(); + obj->asGlobal().getRegExpStatics()->clear(); } JS_PUBLIC_API(JSBool) JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length, size_t *indexp, JSBool test, jsval *rval) { + AssertNoGC(cx); CHECK_REQUEST(cx); - RegExp *re = RegExp::extractFrom(reobj); - if (!re) - return false; - - JSString *str = js_NewStringCopyN(cx, chars, length); - if (!str) - return false; - - return re->execute(cx, RegExpStatics::extractFrom(obj->asGlobal()), str, indexp, test, - Valueify(rval)); + RegExpStatics *res = obj->asGlobal().getRegExpStatics(); + return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length, + indexp, test ? RegExpTest : RegExpExec, rval); } JS_PUBLIC_API(JSObject *) JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags) { + AssertNoGC(cx); CHECK_REQUEST(cx); - jschar *chars = js_InflateString(cx, bytes, &length); + jschar *chars = InflateString(cx, bytes, &length); if (!chars) return NULL; - JSObject *obj = RegExp::createObjectNoStatics(cx, chars, length, flags); + RegExpObject *reobj = RegExpObject::createNoStatics(cx, chars, length, RegExpFlag(flags), NULL); cx->free_(chars); - return obj; + return reobj; } JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObjectNoStatics(JSContext *cx, jschar *chars, size_t length, uintN flags) { + AssertNoGC(cx); CHECK_REQUEST(cx); - return RegExp::createObjectNoStatics(cx, chars, length, flags); + return RegExpObject::createNoStatics(cx, chars, length, RegExpFlag(flags), NULL); } JS_PUBLIC_API(JSBool) JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length, size_t *indexp, JSBool test, jsval *rval) { + AssertNoGC(cx); CHECK_REQUEST(cx); - RegExp *re = RegExp::extractFrom(obj); - if (!re) - return false; + return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp, + test ? RegExpTest : RegExpExec, rval); +} - JSString *str = js_NewStringCopyN(cx, chars, length); - if (!str) - return false; +JS_PUBLIC_API(JSBool) +JS_ObjectIsRegExp(JSContext *cx, JSObject *obj) +{ + AssertNoGC(cx); + JS_ASSERT(obj); + return obj->isRegExp(); +} - return re->executeNoStatics(cx, str, indexp, test, Valueify(rval)); +JS_PUBLIC_API(uintN) +JS_GetRegExpFlags(JSContext *cx, JSObject *obj) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + + return obj->asRegExp().getFlags(); +} + +JS_PUBLIC_API(JSString *) +JS_GetRegExpSource(JSContext *cx, JSObject *obj) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + + return obj->asRegExp().getSource(); } /************************************************************************/ @@ -5877,12 +6431,14 @@ JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t le JS_PUBLIC_API(void) JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) { + AssertNoGC(cx); cx->localeCallbacks = callbacks; } JS_PUBLIC_API(JSLocaleCallbacks *) JS_GetLocaleCallbacks(JSContext *cx) { + /* This function can be called by a finalizer. */ return cx->localeCallbacks; } @@ -5891,16 +6447,18 @@ JS_GetLocaleCallbacks(JSContext *cx) JS_PUBLIC_API(JSBool) JS_IsExceptionPending(JSContext *cx) { + /* This function can be called by a finalizer. */ return (JSBool) cx->isExceptionPending(); } JS_PUBLIC_API(JSBool) JS_GetPendingException(JSContext *cx, jsval *vp) { + AssertNoGC(cx); CHECK_REQUEST(cx); if (!cx->isExceptionPending()) return JS_FALSE; - Valueify(*vp) = cx->getPendingException(); + *vp = cx->getPendingException(); assertSameCompartment(cx, *vp); return JS_TRUE; } @@ -5908,14 +6466,16 @@ JS_GetPendingException(JSContext *cx, jsval *vp) JS_PUBLIC_API(void) JS_SetPendingException(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); - cx->setPendingException(Valueify(v)); + cx->setPendingException(v); } JS_PUBLIC_API(void) JS_ClearPendingException(JSContext *cx) { + AssertNoGC(cx); cx->clearPendingException(); } @@ -5925,6 +6485,7 @@ JS_ReportPendingException(JSContext *cx) JSBool ok; JSPackedBool save; + AssertNoGC(cx); CHECK_REQUEST(cx); /* @@ -5950,12 +6511,13 @@ JS_SaveExceptionState(JSContext *cx) { JSExceptionState *state; + AssertNoGC(cx); CHECK_REQUEST(cx); state = (JSExceptionState *) cx->malloc_(sizeof(JSExceptionState)); if (state) { state->throwing = JS_GetPendingException(cx, &state->exception); if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - js_AddRoot(cx, Valueify(&state->exception), "JSExceptionState.exception"); + js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); } return state; } @@ -5963,6 +6525,7 @@ JS_SaveExceptionState(JSContext *cx) JS_PUBLIC_API(void) JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) { + AssertNoGC(cx); CHECK_REQUEST(cx); if (state) { if (state->throwing) @@ -5976,6 +6539,7 @@ JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) JS_PUBLIC_API(void) JS_DropExceptionState(JSContext *cx, JSExceptionState *state) { + AssertNoGC(cx); CHECK_REQUEST(cx); if (state) { if (state->throwing && JSVAL_IS_GCTHING(state->exception)) { @@ -5989,6 +6553,7 @@ JS_DropExceptionState(JSContext *cx, JSExceptionState *state) JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v) { + AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); return js_ErrorFromException(cx, v); @@ -5998,6 +6563,7 @@ JS_PUBLIC_API(JSBool) JS_ThrowReportedError(JSContext *cx, const char *message, JSErrorReport *reportp) { + AssertNoGC(cx); return JS_IsRunning(cx) && js_ErrorToException(cx, message, reportp, NULL, NULL); } @@ -6005,91 +6571,72 @@ JS_ThrowReportedError(JSContext *cx, const char *message, JS_PUBLIC_API(JSBool) JS_ThrowStopIteration(JSContext *cx) { + AssertNoGC(cx); return js_ThrowStopIteration(cx); } -/* - * Get the owning thread id of a context. Returns 0 if the context is not - * owned by any thread. - */ -JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx) +JS_PUBLIC_API(intptr_t) +JS_GetCurrentThread() { #ifdef JS_THREADSAFE - return reinterpret_cast(JS_THREAD_ID(cx)); + return reinterpret_cast(PR_GetCurrentThread()); #else return 0; #endif } -/* - * Set the current thread as the owning thread of a context. Returns the - * old owning thread id, or -1 if the operation failed. - */ -JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx) +extern JS_PUBLIC_API(void) +JS_ClearRuntimeThread(JSRuntime *rt) { + AssertNoGC(rt); #ifdef JS_THREADSAFE - JS_ASSERT(!cx->outstandingRequests); - if (cx->thread()) { - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - return reinterpret_cast(cx->thread()->id); - } - - if (!js_InitContextThread(cx)) { - js_ReportOutOfMemory(cx); - return -1; - } - - /* Here the GC lock is still held after js_InitContextThread took it. */ - JS_UNLOCK_GC(cx->runtime); + rt->clearOwnerThread(); #endif - return 0; } -JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx) +extern JS_PUBLIC_API(void) +JS_SetRuntimeThread(JSRuntime *rt) { + AssertNoGC(rt); #ifdef JS_THREADSAFE - /* - * cx must have exited all requests it entered and, if cx is associated - * with a thread, this must be called only from that thread. If not, this - * is a harmless no-op. - */ - JS_ASSERT(cx->outstandingRequests == 0); - JSThread *t = cx->thread(); - if (!t) - return 0; - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - - /* - * We must not race with a GC that accesses cx->thread for all threads, - * see bug 476934. - */ - JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); - js_WaitForGC(rt); - js_ClearContextThread(cx); - JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); + rt->setOwnerThread(); +#endif +} - /* - * We can access t->id as long as the GC lock is held and we cannot race - * with the GC that may delete t. - */ - return reinterpret_cast(t->id); -#else - return 0; +extern JS_NEVER_INLINE JS_PUBLIC_API(void) +JS_AbortIfWrongThread(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + if (!rt->onOwnerThread()) + MOZ_Assert("rt->onOwnerThread()", __FILE__, __LINE__); #endif } #ifdef JS_GC_ZEAL JS_PUBLIC_API(void) -JS_SetGCZeal(JSContext *cx, uint8 zeal) +JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment) { - cx->runtime->gcZeal = zeal; + bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold; + cx->runtime->gcZeal_ = zeal; + cx->runtime->gcZealFrequency = frequency; + cx->runtime->gcNextScheduled = schedule ? frequency : 0; + cx->runtime->gcDebugCompartmentGC = !!compartment; +} + +JS_PUBLIC_API(void) +JS_ScheduleGC(JSContext *cx, uint32_t count, JSBool compartment) +{ + cx->runtime->gcNextScheduled = count; + cx->runtime->gcDebugCompartmentGC = !!compartment; } #endif +JS_FRIEND_API(void *) +js_GetCompartmentPrivate(JSCompartment *compartment) +{ + return compartment->data; +} + /************************************************************************/ #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN) @@ -6105,3 +6652,74 @@ BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) } #endif + +JS_PUBLIC_API(JSBool) +JS_IndexToId(JSContext *cx, uint32_t index, jsid *id) +{ + return IndexToId(cx, index, id); +} + +JS_PUBLIC_API(JSBool) +JS_IsIdentifier(JSContext *cx, JSString *str, JSBool *isIdentifier) +{ + assertSameCompartment(cx, str); + + JSLinearString* linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + + *isIdentifier = js::IsIdentifier(linearStr); + return true; +} + +#ifdef JS_THREADSAFE +static PRStatus +CallOnce(void *func) +{ + JSInitCallback init = JS_DATA_TO_FUNC_PTR(JSInitCallback, func); + return init() ? PR_SUCCESS : PR_FAILURE; +} +#endif + +JS_PUBLIC_API(JSBool) +JS_CallOnce(JSCallOnceType *once, JSInitCallback func) +{ +#ifdef JS_THREADSAFE + return PR_CallOnceWithArg(once, CallOnce, JS_FUNC_TO_DATA_PTR(void *, func)) == PR_SUCCESS; +#else + if (!*once) { + *once = true; + return func(); + } else { + return JS_TRUE; + } +#endif +} + +namespace JS { + +AutoGCRooter::AutoGCRooter(JSContext *cx, ptrdiff_t tag) + : down(cx->autoGCRooters), tag(tag), context(cx) +{ + JS_ASSERT(this != cx->autoGCRooters); + CHECK_REQUEST(cx); + cx->autoGCRooters = this; +} + +AutoGCRooter::~AutoGCRooter() +{ + JS_ASSERT(this == context->autoGCRooters); + CHECK_REQUEST(context); + context->autoGCRooters = down; +} + +AutoEnumStateRooter::~AutoEnumStateRooter() +{ + if (!stateValue.isNull()) { + DebugOnly ok = + obj->enumerate(context, JSENUMERATE_DESTROY, &stateValue, 0); + JS_ASSERT(ok); + } +} + +} // namespace JS diff --git a/deps/mozjs/js/src/jsapi.h b/deps/mozjs/js/src/jsapi.h index c15273c6f45..509c2e17c4b 100644 --- a/deps/mozjs/js/src/jsapi.h +++ b/deps/mozjs/js/src/jsapi.h @@ -43,92 +43,1619 @@ /* * JavaScript API. */ + +#include "mozilla/StdInt.h" + #include #include #include "js-config.h" #include "jspubtd.h" #include "jsutil.h" +#include "jsval.h" -JS_BEGIN_EXTERN_C +#include "js/Utility.h" + +#ifdef __cplusplus +#include "jsalloc.h" +#include "js/Vector.h" +#include "mozilla/Attributes.h" +#endif + +/************************************************************************/ + +/* JS::Value can store a full int32_t. */ +#define JSVAL_INT_BITS 32 +#define JSVAL_INT_MIN ((jsint)0x80000000) +#define JSVAL_INT_MAX ((jsint)0x7fffffff) + +/************************************************************************/ + +#define JS_Assert MOZ_Assert + +#ifdef __cplusplus +namespace JS { /* - * In release builds, jsval and jsid are defined to be integral types. This - * prevents many bugs from being caught at compile time. E.g.: + * Protecting non-jsval, non-JSObject *, non-JSString * values from collection + * + * Most of the time, the garbage collector's conservative stack scanner works + * behind the scenes, finding all live values and protecting them from being + * collected. However, when JSAPI client code obtains a pointer to data the + * scanner does not know about, owned by an object the scanner does know about, + * Care Must Be Taken. + * + * The scanner recognizes only a select set of types: pointers to JSObjects and + * similar things (JSFunctions, and so on), pointers to JSStrings, and jsvals. + * So while the scanner finds all live |JSString| pointers, it does not notice + * |jschar| pointers. + * + * So suppose we have: + * + * void f(JSString *str) { + * const jschar *ch = JS_GetStringCharsZ(str); + * ... do stuff with ch, but no uses of str ...; + * } + * + * After the call to |JS_GetStringCharsZ|, there are no further uses of + * |str|, which means that the compiler is within its rights to not store + * it anywhere. But because the stack scanner will not notice |ch|, there + * is no longer any live value in this frame that would keep the string + * alive. If |str| is the last reference to that |JSString|, and the + * collector runs while we are using |ch|, the string's array of |jschar|s + * may be freed out from under us. + * + * Note that there is only an issue when 1) we extract a thing X the scanner + * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y + * gets garbage-collected, then X gets freed. If we have code like this: + * + * void g(JSObject *obj) { + * jsval x; + * JS_GetProperty(obj, "x", &x); + * ... do stuff with x ... + * } + * + * there's no problem, because the value we've extracted, x, is a jsval, a + * type that the conservative scanner recognizes. + * + * Conservative GC frees us from the obligation to explicitly root the types it + * knows about, but when we work with derived values like |ch|, we must root + * their owners, as the derived value alone won't keep them alive. + * + * A JS::Anchor is a kind of GC root that allows us to keep the owners of + * derived values like |ch| alive throughout the Anchor's lifetime. We could + * fix the above code as follows: * - * jsval v = ... - * if (v == JS_TRUE) // error - * ... + * void f(JSString *str) { + * JS::Anchor a_str(str); + * const jschar *ch = JS_GetStringCharsZ(str); + * ... do stuff with ch, but no uses of str ...; + * } * - * jsid id = v; // error + * This simply ensures that |str| will be live until |a_str| goes out of scope. + * As long as we don't retain a pointer to the string's characters for longer + * than that, we have avoided all garbage collection hazards. + */ +template class AnchorPermitted; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; +template<> class AnchorPermitted { }; + +template +class Anchor: AnchorPermitted +{ + public: + Anchor() { } + explicit Anchor(T t) { hold = t; } + inline ~Anchor(); + T &get() { return hold; } + const T &get() const { return hold; } + void set(const T &t) { hold = t; } + void clear() { hold = 0; } + private: + T hold; + /* Anchors should not be assigned or passed to functions. */ + Anchor(const Anchor &); + const Anchor &operator=(const Anchor &); +}; + +#ifdef __GNUC__ +template +inline Anchor::~Anchor() +{ + /* + * No code is generated for this. But because this is marked 'volatile', G++ will + * assume it has important side-effects, and won't delete it. (G++ never looks at + * the actual text and notices it's empty.) And because we have passed |hold| to + * it, GCC will keep |hold| alive until this point. + * + * The "memory" clobber operand ensures that G++ will not move prior memory + * accesses after the asm --- it's a barrier. Unfortunately, it also means that + * G++ will assume that all memory has changed after the asm, as it would for a + * call to an unknown function. I don't know of a way to avoid that consequence. + */ + asm volatile("":: "g" (hold) : "memory"); +} +#else +template +inline Anchor::~Anchor() +{ + /* + * An adequate portable substitute, for non-structure types. + * + * The compiler promises that, by the end of an expression statement, the + * last-stored value to a volatile object is the same as it would be in an + * unoptimized, direct implementation (the "abstract machine" whose behavior the + * language spec describes). However, the compiler is still free to reorder + * non-volatile accesses across this store --- which is what we must prevent. So + * assigning the held value to a volatile variable, as we do here, is not enough. + * + * In our case, however, garbage collection only occurs at function calls, so it + * is sufficient to ensure that the destructor's store isn't moved earlier across + * any function calls that could collect. It is hard to imagine the compiler + * analyzing the program so thoroughly that it could prove that such motion was + * safe. In practice, compilers treat calls to the collector as opaque operations + * --- in particular, as operations which could access volatile variables, across + * which this destructor must not be moved. + * + * ("Objection, your honor! *Alleged* killer whale!") + * + * The disadvantage of this approach is that it does generate code for the store. + * We do need to use Anchors in some cases where cycles are tight. + * + * NB: there is a Anchor::~Anchor() specialization below. + */ + volatile T sink; + sink = hold; +} +#endif /* defined(__GNUC__) */ + +/* + * Methods for poisoning GC heap pointer words and checking for poisoned words. + * These are in this file for use in Value methods and so forth. * - * To catch more errors, jsval and jsid are given struct types in debug builds. - * Struct assignment and (in C++) operator== allow correct code to be mostly - * oblivious to the change. This feature can be explicitly disabled in debug - * builds by defining JS_NO_JSVAL_JSID_STRUCT_TYPES. + * If the moving GC hazard analysis is in use and detects a non-rooted stack + * pointer to a GC thing, one byte of that pointer is poisoned to refer to an + * invalid location. For both 32 bit and 64 bit systems, the fourth byte of the + * pointer is overwritten, to reduce the likelihood of accidentally changing + * a live integer value. */ -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES -/* Well-known JS values. N.B. These constants are initialized at startup. */ -extern JS_PUBLIC_DATA(jsval) JSVAL_NULL; -extern JS_PUBLIC_DATA(jsval) JSVAL_ZERO; -extern JS_PUBLIC_DATA(jsval) JSVAL_ONE; -extern JS_PUBLIC_DATA(jsval) JSVAL_FALSE; -extern JS_PUBLIC_DATA(jsval) JSVAL_TRUE; -extern JS_PUBLIC_DATA(jsval) JSVAL_VOID; +inline void PoisonPtr(uintptr_t *v) +{ +#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) + uint8_t *ptr = (uint8_t *) v + 3; + *ptr = JS_FREE_PATTERN; +#endif +} +template +inline bool IsPoisonedPtr(T *v) +{ +#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) + uint32_t mask = uintptr_t(v) & 0xff000000; + return mask == uint32_t(JS_FREE_PATTERN << 24); #else + return false; +#endif +} + +/* + * JS::Value is the C++ interface for a single JavaScript Engine value. + * A few general notes on JS::Value: + * + * - JS::Value has setX() and isX() members for X in + * + * { Int32, Double, String, Boolean, Undefined, Null, Object, Magic } + * + * JS::Value also contains toX() for each of the non-singleton types. + * + * - Magic is a singleton type whose payload contains a JSWhyMagic "reason" for + * the magic value. By providing JSWhyMagic values when creating and checking + * for magic values, it is possible to assert, at runtime, that only magic + * values with the expected reason flow through a particular value. For + * example, if cx->exception has a magic value, the reason must be + * JS_GENERATOR_CLOSING. + * + * - A key difference between JSVAL_* and JS::Value operations is that + * JS::Value gives null a separate type. Thus + * + * JSVAL_IS_OBJECT(v) === v.isObjectOrNull() + * !JSVAL_IS_PRIMITIVE(v) === v.isObject() + * + * To help prevent mistakenly boxing a nullable JSObject* as an object, + * Value::setObject takes a JSObject&. (Conversely, Value::asObject returns a + * JSObject&. A convenience member Value::setObjectOrNull is provided. + * + * - JSVAL_VOID is the same as the singleton value of the Undefined type. + * + * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on + * 32-bit user code should avoid copying jsval/JS::Value as much as possible, + * preferring to pass by const Value &. + */ +class Value +{ + public: + /* + * N.B. the default constructor leaves Value unitialized. Adding a default + * constructor prevents Value from being stored in a union. + */ + + /*** Mutators ***/ + + JS_ALWAYS_INLINE + void setNull() { + data.asBits = BUILD_JSVAL(JSVAL_TAG_NULL, 0).asBits; + } + + JS_ALWAYS_INLINE + void setUndefined() { + data.asBits = BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0).asBits; + } + + JS_ALWAYS_INLINE + void setInt32(int32_t i) { + data = INT32_TO_JSVAL_IMPL(i); + } + + JS_ALWAYS_INLINE + int32_t &getInt32Ref() { + JS_ASSERT(isInt32()); + return data.s.payload.i32; + } + + JS_ALWAYS_INLINE + void setDouble(double d) { + data = DOUBLE_TO_JSVAL_IMPL(d); + } + + JS_ALWAYS_INLINE + double &getDoubleRef() { + JS_ASSERT(isDouble()); + return data.asDouble; + } + + JS_ALWAYS_INLINE + void setString(JSString *str) { + JS_ASSERT(!IsPoisonedPtr(str)); + data = STRING_TO_JSVAL_IMPL(str); + } + + JS_ALWAYS_INLINE + void setString(const JS::Anchor &str) { + setString(str.get()); + } + + JS_ALWAYS_INLINE + void setObject(JSObject &obj) { + JS_ASSERT(!IsPoisonedPtr(&obj)); + data = OBJECT_TO_JSVAL_IMPL(&obj); + } + + JS_ALWAYS_INLINE + void setBoolean(bool b) { + data = BOOLEAN_TO_JSVAL_IMPL(b); + } + + JS_ALWAYS_INLINE + void setMagic(JSWhyMagic why) { + data = MAGIC_TO_JSVAL_IMPL(why); + } + + JS_ALWAYS_INLINE + bool setNumber(uint32_t ui) { + if (ui > JSVAL_INT_MAX) { + setDouble((double)ui); + return false; + } else { + setInt32((int32_t)ui); + return true; + } + } + + JS_ALWAYS_INLINE + bool setNumber(double d) { + int32_t i; + if (JSDOUBLE_IS_INT32(d, &i)) { + setInt32(i); + return true; + } else { + setDouble(d); + return false; + } + } + + JS_ALWAYS_INLINE + void setObjectOrNull(JSObject *arg) { + if (arg) + setObject(*arg); + else + setNull(); + } + + JS_ALWAYS_INLINE + void swap(Value &rhs) { + uint64_t tmp = rhs.data.asBits; + rhs.data.asBits = data.asBits; + data.asBits = tmp; + } + + /*** Value type queries ***/ + + JS_ALWAYS_INLINE + bool isUndefined() const { + return JSVAL_IS_UNDEFINED_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isNull() const { + return JSVAL_IS_NULL_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isNullOrUndefined() const { + return isNull() || isUndefined(); + } + + JS_ALWAYS_INLINE + bool isInt32() const { + return JSVAL_IS_INT32_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isInt32(int32_t i32) const { + return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32); + } + + JS_ALWAYS_INLINE + bool isDouble() const { + return JSVAL_IS_DOUBLE_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isNumber() const { + return JSVAL_IS_NUMBER_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isString() const { + return JSVAL_IS_STRING_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isObject() const { + return JSVAL_IS_OBJECT_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isPrimitive() const { + return JSVAL_IS_PRIMITIVE_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isObjectOrNull() const { + return JSVAL_IS_OBJECT_OR_NULL_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isGCThing() const { + return JSVAL_IS_GCTHING_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isBoolean() const { + return JSVAL_IS_BOOLEAN_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isTrue() const { + return JSVAL_IS_SPECIFIC_BOOLEAN(data, true); + } + + JS_ALWAYS_INLINE + bool isFalse() const { + return JSVAL_IS_SPECIFIC_BOOLEAN(data, false); + } + + JS_ALWAYS_INLINE + bool isMagic() const { + return JSVAL_IS_MAGIC_IMPL(data); + } + + JS_ALWAYS_INLINE + bool isMagic(JSWhyMagic why) const { + JS_ASSERT_IF(isMagic(), data.s.payload.why == why); + return JSVAL_IS_MAGIC_IMPL(data); + } + + /* + * Although the Value class comment says 'magic' is a singleton type, it is + * technically possible to use the payload. This should be avoided to + * preserve the ability for the strong assertions in isMagic(). + */ + JS_ALWAYS_INLINE + bool isParticularMagic(JSWhyMagic why) const { + return isMagic() && data.s.payload.why == why; + } + + JS_ALWAYS_INLINE + bool isMarkable() const { + return JSVAL_IS_TRACEABLE_IMPL(data); + } + + JS_ALWAYS_INLINE + JSGCTraceKind gcKind() const { + JS_ASSERT(isMarkable()); + return JSGCTraceKind(JSVAL_TRACE_KIND_IMPL(data)); + } + + JS_ALWAYS_INLINE + JSWhyMagic whyMagic() const { + JS_ASSERT(isMagic()); + return data.s.payload.why; + } + + /*** Comparison ***/ + + JS_ALWAYS_INLINE + bool operator==(const Value &rhs) const { + return data.asBits == rhs.data.asBits; + } + + JS_ALWAYS_INLINE + bool operator!=(const Value &rhs) const { + return data.asBits != rhs.data.asBits; + } + + friend inline bool SameType(const Value &lhs, const Value &rhs); + + /*** Extract the value's typed payload ***/ + + JS_ALWAYS_INLINE + int32_t toInt32() const { + JS_ASSERT(isInt32()); + return JSVAL_TO_INT32_IMPL(data); + } + + JS_ALWAYS_INLINE + double toDouble() const { + JS_ASSERT(isDouble()); + return data.asDouble; + } + + JS_ALWAYS_INLINE + double toNumber() const { + JS_ASSERT(isNumber()); + return isDouble() ? toDouble() : double(toInt32()); + } + + JS_ALWAYS_INLINE + JSString *toString() const { + JS_ASSERT(isString()); + return JSVAL_TO_STRING_IMPL(data); + } + + JS_ALWAYS_INLINE + JSObject &toObject() const { + JS_ASSERT(isObject()); + return *JSVAL_TO_OBJECT_IMPL(data); + } + + JS_ALWAYS_INLINE + JSObject *toObjectOrNull() const { + JS_ASSERT(isObjectOrNull()); + return JSVAL_TO_OBJECT_IMPL(data); + } + + JS_ALWAYS_INLINE + void *toGCThing() const { + JS_ASSERT(isGCThing()); + return JSVAL_TO_GCTHING_IMPL(data); + } + + JS_ALWAYS_INLINE + bool toBoolean() const { + JS_ASSERT(isBoolean()); + return JSVAL_TO_BOOLEAN_IMPL(data); + } + + JS_ALWAYS_INLINE + uint32_t payloadAsRawUint32() const { + JS_ASSERT(!isDouble()); + return data.s.payload.u32; + } + + JS_ALWAYS_INLINE + uint64_t asRawBits() const { + return data.asBits; + } + + JS_ALWAYS_INLINE + JSValueType extractNonDoubleType() const { + return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data); + } + + /* + * Private API + * + * Private setters/getters allow the caller to read/write arbitrary types + * that fit in the 64-bit payload. It is the caller's responsibility, after + * storing to a value with setPrivateX to read only using getPrivateX. + * Privates values are given a type type which ensures they are not marked. + */ + + JS_ALWAYS_INLINE + void setPrivate(void *ptr) { + data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr); + } + + JS_ALWAYS_INLINE + void *toPrivate() const { + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); + return JSVAL_TO_PRIVATE_PTR_IMPL(data); + } + + JS_ALWAYS_INLINE + void setPrivateUint32(uint32_t ui) { + data = PRIVATE_UINT32_TO_JSVAL_IMPL(ui); + } + + JS_ALWAYS_INLINE + uint32_t toPrivateUint32() const { + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); + return JSVAL_TO_PRIVATE_UINT32_IMPL(data); + } + + JS_ALWAYS_INLINE + uint32_t &getPrivateUint32Ref() { + JS_ASSERT(isDouble()); + return data.s.payload.u32; + } + + /* + * An unmarked value is just a void* cast as a Value. Thus, the Value is + * not safe for GC and must not be marked. This API avoids raw casts + * and the ensuing strict-aliasing warnings. + */ + + JS_ALWAYS_INLINE + void setUnmarkedPtr(void *ptr) { + data.asPtr = ptr; + } + + JS_ALWAYS_INLINE + void *toUnmarkedPtr() const { + return data.asPtr; + } + + const size_t *payloadWord() const { +#if JS_BITS_PER_WORD == 32 + return &data.s.payload.word; +#elif JS_BITS_PER_WORD == 64 + return &data.asWord; +#endif + } + +#ifndef _MSC_VER + /* To make jsval binary compatible when linking across C and C++ with MSVC, + * JS::Value needs to be POD. Otherwise, jsval will be passed in memory + * in C++ but by value in C (bug 645111). + */ + private: +#endif + + jsval_layout data; + + private: + void staticAssertions() { + JS_STATIC_ASSERT(sizeof(JSValueType) == 1); + JS_STATIC_ASSERT(sizeof(JSValueTag) == 4); + JS_STATIC_ASSERT(sizeof(JSBool) == 4); + JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4); + JS_STATIC_ASSERT(sizeof(Value) == 8); + } + + friend jsval_layout (::JSVAL_TO_IMPL)(Value); + friend Value (::IMPL_TO_JSVAL)(jsval_layout l); +}; + +inline bool +IsPoisonedValue(const Value &v) +{ + if (v.isString()) + return IsPoisonedPtr(v.toString()); + if (v.isObject()) + return IsPoisonedPtr(&v.toObject()); + return false; +} + +/************************************************************************/ + +static JS_ALWAYS_INLINE Value +NullValue() +{ + Value v; + v.setNull(); + return v; +} + +static JS_ALWAYS_INLINE Value +UndefinedValue() +{ + Value v; + v.setUndefined(); + return v; +} + +static JS_ALWAYS_INLINE Value +Int32Value(int32_t i32) +{ + Value v; + v.setInt32(i32); + return v; +} + +static JS_ALWAYS_INLINE Value +DoubleValue(double dbl) +{ + Value v; + v.setDouble(dbl); + return v; +} + +static JS_ALWAYS_INLINE Value +StringValue(JSString *str) +{ + Value v; + v.setString(str); + return v; +} + +static JS_ALWAYS_INLINE Value +BooleanValue(bool boo) +{ + Value v; + v.setBoolean(boo); + return v; +} + +static JS_ALWAYS_INLINE Value +ObjectValue(JSObject &obj) +{ + Value v; + v.setObject(obj); + return v; +} + +static JS_ALWAYS_INLINE Value +MagicValue(JSWhyMagic why) +{ + Value v; + v.setMagic(why); + return v; +} + +static JS_ALWAYS_INLINE Value +NumberValue(double dbl) +{ + Value v; + v.setNumber(dbl); + return v; +} + +static JS_ALWAYS_INLINE Value +ObjectOrNullValue(JSObject *obj) +{ + Value v; + v.setObjectOrNull(obj); + return v; +} + +static JS_ALWAYS_INLINE Value +PrivateValue(void *ptr) +{ + Value v; + v.setPrivate(ptr); + return v; +} + +static JS_ALWAYS_INLINE Value +PrivateUint32Value(uint32_t ui) +{ + Value v; + v.setPrivateUint32(ui); + return v; +} + +JS_ALWAYS_INLINE bool +SameType(const Value &lhs, const Value &rhs) +{ + return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data); +} + +/************************************************************************/ + +#ifndef __GNUC__ + +/* + * The default assignment operator for |struct C| has the signature: + * + * C& C::operator=(const C&) + * + * And in particular requires implicit conversion of |this| to type |C| for the + * return value. But |volatile C| cannot thus be converted to |C|, so just + * doing |sink = hold| as in the non-specialized version would fail to compile. + * Do the assignment on asBits instead, since I don't think we want to give + * jsval_layout an assignment operator returning |volatile jsval_layout|. + */ +template<> +inline Anchor::~Anchor() +{ + volatile uint64_t bits; + bits = JSVAL_TO_IMPL(hold).asBits; +} + +#endif + +#if defined JS_THREADSAFE && defined DEBUG + +class JS_PUBLIC_API(AutoCheckRequestDepth) +{ + JSContext *cx; + public: + AutoCheckRequestDepth(JSContext *cx); + ~AutoCheckRequestDepth(); +}; + +# define CHECK_REQUEST(cx) \ + JS::AutoCheckRequestDepth _autoCheckRequestDepth(cx) + +#else + +# define CHECK_REQUEST(cx) \ + ((void) 0) + +#endif + +class JS_PUBLIC_API(AutoGCRooter) { + public: + AutoGCRooter(JSContext *cx, ptrdiff_t tag); + ~AutoGCRooter(); + + /* Implemented in jsgc.cpp. */ + inline void trace(JSTracer *trc); + void traceAll(JSTracer *trc); + + protected: + AutoGCRooter * const down; + + /* + * Discriminates actual subclass of this being used. If non-negative, the + * subclass roots an array of values of the length stored in this field. + * If negative, meaning is indicated by the corresponding value in the enum + * below. Any other negative value indicates some deeper problem such as + * memory corruption. + */ + ptrdiff_t tag; + + JSContext * const context; + + enum { + JSVAL = -1, /* js::AutoValueRooter */ + VALARRAY = -2, /* js::AutoValueArrayRooter */ + PARSER = -3, /* js::Parser */ + SHAPEVECTOR = -4, /* js::AutoShapeVector */ + ENUMERATOR = -5, /* js::AutoEnumStateRooter */ + IDARRAY = -6, /* js::AutoIdArray */ + DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */ + NAMESPACES = -8, /* js::AutoNamespaceArray */ + XML = -9, /* js::AutoXMLRooter */ + OBJECT = -10, /* js::AutoObjectRooter */ + ID = -11, /* js::AutoIdRooter */ + VALVECTOR = -12, /* js::AutoValueVector */ + DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ + STRING = -14, /* js::AutoStringRooter */ + IDVECTOR = -15, /* js::AutoIdVector */ + OBJVECTOR = -16 /* js::AutoObjectVector */ + }; + + private: + /* No copy or assignment semantics. */ + AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE; + void operator=(AutoGCRooter &ida) MOZ_DELETE; +}; + +class AutoValueRooter : private AutoGCRooter +{ + public: + explicit AutoValueRooter(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, JSVAL), val(NullValue()) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + AutoValueRooter(JSContext *cx, const Value &v + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, JSVAL), val(v) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + /* + * If you are looking for Object* overloads, use AutoObjectRooter instead; + * rooting Object*s as a js::Value requires discerning whether or not it is + * a function object. Also, AutoObjectRooter is smaller. + */ + + void set(Value v) { + JS_ASSERT(tag == JSVAL); + val = v; + } + + const Value &value() const { + JS_ASSERT(tag == JSVAL); + return val; + } + + Value *addr() { + JS_ASSERT(tag == JSVAL); + return &val; + } + + const Value &jsval_value() const { + JS_ASSERT(tag == JSVAL); + return val; + } + + Value *jsval_addr() { + JS_ASSERT(tag == JSVAL); + return &val; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + Value val; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoObjectRooter : private AutoGCRooter { + public: + AutoObjectRooter(JSContext *cx, JSObject *obj = NULL + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, OBJECT), obj(obj) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + void setObject(JSObject *obj) { + this->obj = obj; + } + + JSObject * object() const { + return obj; + } + + JSObject ** addr() { + return &obj; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + JSObject *obj; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoStringRooter : private AutoGCRooter { + public: + AutoStringRooter(JSContext *cx, JSString *str = NULL + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, STRING), str(str) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + void setString(JSString *str) { + this->str = str; + } + + JSString * string() const { + return str; + } + + JSString ** addr() { + return &str; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + JSString *str; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoArrayRooter : private AutoGCRooter { + public: + AutoArrayRooter(JSContext *cx, size_t len, Value *vec + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, len), array(vec) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_ASSERT(tag >= 0); + } + + void changeLength(size_t newLength) { + tag = ptrdiff_t(newLength); + JS_ASSERT(tag >= 0); + } + + void changeArray(Value *newArray, size_t newLength) { + changeLength(newLength); + array = newArray; + } + + Value *array; + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* The auto-root for enumeration object and its state. */ +class AutoEnumStateRooter : private AutoGCRooter +{ + public: + AutoEnumStateRooter(JSContext *cx, JSObject *obj + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, ENUMERATOR), obj(obj), stateValue() + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_ASSERT(obj); + } + + ~AutoEnumStateRooter(); + + friend void AutoGCRooter::trace(JSTracer *trc); + + const Value &state() const { return stateValue; } + Value *addr() { return &stateValue; } + + protected: + void trace(JSTracer *trc); + + JSObject * const obj; + + private: + Value stateValue; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +template +class AutoVectorRooter : protected AutoGCRooter +{ + public: + explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, tag), vector(cx) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + size_t length() const { return vector.length(); } + + 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(); } + T popCopy() { return vector.popCopy(); } + + bool growBy(size_t inc) { + size_t oldLength = vector.length(); + if (!vector.growByUninitialized(inc)) + return false; + makeRangeGCSafe(oldLength); + return true; + } + + bool resize(size_t newLength) { + size_t oldLength = vector.length(); + if (newLength <= oldLength) { + vector.shrinkBy(oldLength - newLength); + return true; + } + if (!vector.growByUninitialized(newLength - oldLength)) + return false; + makeRangeGCSafe(oldLength); + return true; + } + + void clear() { vector.clear(); } + + bool reserve(size_t newLength) { + return vector.reserve(newLength); + } + + T &operator[](size_t i) { return vector[i]; } + const T &operator[](size_t i) const { return vector[i]; } + + const T *begin() const { return vector.begin(); } + T *begin() { return vector.begin(); } + + const T *end() const { return vector.end(); } + T *end() { return vector.end(); } + + const T &back() const { return vector.back(); } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + void makeRangeGCSafe(size_t oldLength) { + T *t = vector.begin() + oldLength; + for (size_t i = oldLength; i < vector.length(); ++i, ++t) + memset(t, 0, sizeof(T)); + } + + typedef js::Vector VectorImpl; + VectorImpl vector; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoValueVector : public AutoVectorRooter +{ + public: + explicit AutoValueVector(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooter(cx, VALVECTOR) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class AutoIdVector : public AutoVectorRooter +{ + public: + explicit AutoIdVector(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooter(cx, IDVECTOR) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} /* namespace JS */ + +/************************************************************************/ + +/* + * JS::Value and jsval are the same type; jsval is the old name, kept around + * for backwards compatibility along with all the JSVAL_* operations below. + * jsval_layout is an implementation detail and should not be used externally. + */ +typedef JS::Value jsval; + +static JS_ALWAYS_INLINE jsval_layout +JSVAL_TO_IMPL(jsval v) +{ + return v.data; +} + +static JS_ALWAYS_INLINE jsval +IMPL_TO_JSVAL(jsval_layout l) +{ + JS::Value v; + v.data = l; + return v; +} + +#ifdef DEBUG +struct JSValueAlignmentTester { char c; JS::Value v; }; +JS_STATIC_ASSERT(sizeof(JSValueAlignmentTester) == 16); +#endif /* DEBUG */ + +#else /* defined(__cplusplus) */ + +/* + * For SpiderMonkey C clients, there is no JS::Value class, only the + * traditional jsval with the traditional JSVAL_* operations. Since + * SpiderMonkey itself is always compiled as C++, this relies on the binary + * compatibility of jsval_layout and JS::Value (statically asserted below). + */ +typedef union jsval_layout jsval; + +static JS_ALWAYS_INLINE jsval_layout +JSVAL_TO_IMPL(jsval v) +{ + return v; +} + +static JS_ALWAYS_INLINE jsval +IMPL_TO_JSVAL(jsval_layout l) +{ + return l; +} + +#endif /* defined(__cplusplus) */ + +#ifdef DEBUG +typedef struct { char c; jsval_layout l; } JSLayoutAlignmentTester; +JS_STATIC_ASSERT(sizeof(JSLayoutAlignmentTester) == 16); +#endif /* DEBUG */ + +JS_STATIC_ASSERT(sizeof(jsval_layout) == sizeof(jsval)); + +/************************************************************************/ + +/* JSClass operation signatures. */ + +/* + * Add, delete, or get a property named by id in obj. Note the jsid id + * type -- id may be a string (Unicode property identifier) or an int (element + * index). The *vp out parameter, on success, is the new property value after + * an add or get. After a successful delete, *vp is JSVAL_FALSE iff + * obj[id] can't be deleted (because it's permanent). + */ +typedef JSBool +(* JSPropertyOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +/* + * Set a property named by id in obj, treating the assignment as strict + * mode code if strict is true. Note the jsid id type -- id may be a string + * (Unicode property identifier) or an int (element index). The *vp out + * parameter, on success, is the new property value after the + * set. + */ +typedef JSBool +(* JSStrictPropertyOp)(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp); + +/* + * This function type is used for callbacks that enumerate the properties of + * a JSObject. The behavior depends on the value of enum_op: + * + * JSENUMERATE_INIT + * A new, opaque iterator state should be allocated and stored in *statep. + * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). + * + * The number of properties that will be enumerated should be returned as + * an integer jsval in *idp, if idp is non-null, and provided the number of + * enumerable properties is known. If idp is non-null and the number of + * enumerable properties can't be computed in advance, *idp should be set + * to JSVAL_ZERO. + * + * JSENUMERATE_INIT_ALL + * Used identically to JSENUMERATE_INIT, but exposes all properties of the + * object regardless of enumerability. + * + * JSENUMERATE_NEXT + * A previously allocated opaque iterator state is passed in via statep. + * Return the next jsid in the iteration using *idp. The opaque iterator + * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL + * if there are no properties left to enumerate. + * + * JSENUMERATE_DESTROY + * Destroy the opaque iterator state previously allocated in *statep by a + * call to this function when enum_op was JSENUMERATE_INIT or + * JSENUMERATE_INIT_ALL. + * + * The return value is used to indicate success, with a value of JS_FALSE + * indicating failure. + */ +typedef JSBool +(* JSNewEnumerateOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp); + +/* + * The old-style JSClass.enumerate op should define all lazy properties not + * yet reflected in obj. + */ +typedef JSBool +(* JSEnumerateOp)(JSContext *cx, JSObject *obj); + +/* + * Resolve a lazy property named by id in obj by defining it directly in obj. + * Lazy properties are those reflected from some peer native property space + * (e.g., the DOM attributes for a given node reflected as obj) on demand. + * + * JS looks for a property in an object, and if not found, tries to resolve + * the given id. If resolve succeeds, the engine looks again in case resolve + * defined obj[id]. If no such property exists directly in obj, the process + * is repeated with obj's prototype, etc. + * + * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. + */ +typedef JSBool +(* JSResolveOp)(JSContext *cx, JSObject *obj, jsid id); + +/* + * Like JSResolveOp, but flags provide contextual information as follows: + * + * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id + * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment + * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence + * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode + * JSRESOLVE_CLASSNAME class name used when constructing + * + * The *objp out parameter, on success, should be null to indicate that id + * was not resolved; and non-null, referring to obj or one of its prototypes, + * if id was resolved. + * + * This hook instead of JSResolveOp is called via the JSClass.resolve member + * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. + * + * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further + * extends this hook by passing in the starting object on the prototype chain + * via *objp. Thus a resolve hook implementation may define the property id + * being resolved in the object in which the id was first sought, rather than + * in a prototype object whose class led to the resolve hook being called. + * + * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore + * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no + * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. + * This is not good practice, but enough existing hook implementations count + * on it that we can't break compatibility by passing the starting object in + * *objp without a new JSClass flag. + */ +typedef JSBool +(* JSNewResolveOp)(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp); + +/* + * Convert obj to the given type, returning true with the resulting value in + * *vp on success, and returning false on error or exception. + */ +typedef JSBool +(* JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp); + +/* + * Delegate typeof to an object so it can cloak a primitive or another object. + */ +typedef JSType +(* JSTypeOfOp)(JSContext *cx, JSObject *obj); + +/* + * Finalize obj, which the garbage collector has determined to be unreachable + * from other live objects or from GC roots. Obviously, finalizers must never + * store a reference to obj. + */ +typedef void +(* JSFinalizeOp)(JSContext *cx, JSObject *obj); + +/* + * Finalizes external strings created by JS_NewExternalString. + */ +typedef struct JSStringFinalizer JSStringFinalizer; + +struct JSStringFinalizer { + void (*finalize)(const JSStringFinalizer *fin, jschar *chars); +}; + +/* + * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, + * returning false on error/exception, true on success with obj[id]'s last-got + * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id + * is either a string or an int jsval. + */ +typedef JSBool +(* JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp); + +/* + * Encode or decode an object, given an XDR state record representing external + * data. See jsxdrapi.h. + */ +typedef JSBool +(* JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); + +/* + * Check whether v is an instance of obj. Return false on error or exception, + * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in + * *bp otherwise. + */ +typedef JSBool +(* JSHasInstanceOp)(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp); + +/* + * Function type for trace operation of the class called to enumerate all + * traceable things reachable from obj's private data structure. For each such + * thing, a trace implementation must call + * + * JS_CallTracer(trc, thing, kind); + * + * or one of its convenience macros as described in jsapi.h. + * + * JSTraceOp implementation can assume that no other threads mutates object + * state. It must not change state of the object or corresponding native + * structures. The only exception for this rule is the case when the embedding + * needs a tight integration with GC. In that case the embedding can check if + * 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. + */ +typedef void +(* JSTraceOp)(JSTracer *trc, JSObject *obj); + +/* + * DEBUG only callback that JSTraceOp implementation can provide to return + * a string describing the reference traced with JS_CallTracer. + */ +typedef void +(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize); + +typedef JSBool +(* JSEqualityOp)(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp); + +/* + * Typedef for native functions called by the JS VM. + * + * See jsapi.h, the JS_CALLEE, JS_THIS, etc. macros. + */ + +typedef JSBool +(* JSNative)(JSContext *cx, uintN argc, jsval *vp); + +/* Callbacks and their arguments. */ + +typedef enum JSContextOp { + JSCONTEXT_NEW, + JSCONTEXT_DESTROY +} JSContextOp; + +/* + * The possible values for contextOp when the runtime calls the callback are: + * JSCONTEXT_NEW JS_NewContext successfully created a new JSContext + * instance. The callback can initialize the instance as + * required. If the callback returns false, the instance + * will be destroyed and JS_NewContext returns null. In + * this case the callback is not called again. + * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The + * callback may perform its own cleanup and must always + * return true. + * Any other value For future compatibility the callback must do nothing + * and return true in this case. + */ +typedef JSBool +(* JSContextCallback)(JSContext *cx, uintN contextOp); + +typedef enum JSGCStatus { + JSGC_BEGIN, + JSGC_END, + JSGC_MARK_END, + JSGC_FINALIZE_END +} JSGCStatus; + +typedef JSBool +(* JSGCCallback)(JSContext *cx, JSGCStatus status); + +/* + * Generic trace operation that calls JS_CallTracer on each traceable thing + * stored in data. + */ +typedef void +(* JSTraceDataOp)(JSTracer *trc, void *data); + +typedef JSBool +(* JSOperationCallback)(JSContext *cx); + +typedef void +(* JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report); + +#ifdef MOZ_TRACE_JSCALLS +typedef void +(* JSFunctionCallback)(const JSFunction *fun, + const JSScript *scr, + const JSContext *cx, + int entering); +#endif + +/* + * Possible exception types. These types are part of a JSErrorFormatString + * structure. They define which error to throw in case of a runtime error. + * JSEXN_NONE marks an unthrowable error. + */ +typedef enum JSExnType { + JSEXN_NONE = -1, + JSEXN_ERR, + JSEXN_INTERNALERR, + JSEXN_EVALERR, + JSEXN_RANGEERR, + JSEXN_REFERENCEERR, + JSEXN_SYNTAXERR, + JSEXN_TYPEERR, + JSEXN_URIERR, + JSEXN_LIMIT +} JSExnType; + +typedef struct JSErrorFormatString { + /* The error format string (UTF-8 if js_CStringsAreUTF8). */ + const char *format; + + /* The number of arguments to expand in the formatted error message. */ + uint16_t argCount; + + /* One of the JSExnType constants above. */ + int16_t exnType; +} JSErrorFormatString; + +typedef const JSErrorFormatString * +(* JSErrorCallback)(void *userRef, const char *locale, + const uintN errorNumber); + +#ifdef va_start +#define JS_ARGUMENT_FORMATTER_DEFINED 1 + +typedef JSBool +(* JSArgumentFormatter)(JSContext *cx, const char *format, JSBool fromJS, + jsval **vpp, va_list *app); +#endif + +typedef JSBool +(* JSLocaleToUpperCase)(JSContext *cx, JSString *src, jsval *rval); + +typedef JSBool +(* JSLocaleToLowerCase)(JSContext *cx, JSString *src, jsval *rval); + +typedef JSBool +(* JSLocaleCompare)(JSContext *cx, JSString *src1, JSString *src2, + jsval *rval); + +typedef JSBool +(* JSLocaleToUnicode)(JSContext *cx, const char *src, jsval *rval); + +/* + * Security protocol types. + */ + +/* + * XDR-encode or -decode a principals instance, based on whether xdr->mode is + * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, + * in which case implementations must return a held (via JSPRINCIPALS_HOLD), + * non-null *principalsp out parameter. Return true on success, false on any + * error, which the implementation must have reported. + */ +typedef JSBool +(* JSPrincipalsTranscoder)(JSXDRState *xdr, JSPrincipals **principalsp); + +/* + * Return a weak reference to the principals associated with obj, possibly via + * the immutable parent chain leading from obj to a top-level container (e.g., + * a window object in the DOM level 0). If there are no principals associated + * with obj, return null. Therefore null does not mean an error was reported; + * in no event should an error be reported or an exception be thrown by this + * callback's implementation. + */ +typedef JSPrincipals * +(* JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); + +/* + * Used to check if a CSP instance wants to disable eval() and friends. + * See js_CheckCSPPermitsJSAction() in jsobj. + */ +typedef JSBool +(* JSCSPEvalChecker)(JSContext *cx); + +/* + * Callback used to ask the embedding for the cross compartment wrapper handler + * that implements the desired prolicy for this kind of object in the + * destination compartment. + */ +typedef JSObject * +(* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, + uintN flags); + +/* + * Callback used by the wrap hook to ask the embedding to prepare an object + * for wrapping in a context. This might include unwrapping other wrappers + * or even finding a more suitable object for the new compartment. + */ +typedef JSObject * +(* JSPreWrapCallback)(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags); + +typedef enum { + JSCOMPARTMENT_DESTROY +} JSCompartmentOp; + +typedef JSBool +(* JSCompartmentCallback)(JSContext *cx, JSCompartment *compartment, uintN compartmentOp); + +/* + * Read structured data from the reader r. This hook is used to read a value + * previously serialized by a call to the WriteStructuredCloneOp hook. + * + * tag and data are the pair of uint32_t values from the header. The callback + * may use the JS_Read* APIs to read any other relevant parts of the object + * from the reader r. closure is any value passed to the JS_ReadStructuredClone + * function. Return the new object on success, NULL on error/exception. + */ +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32_t tag, uint32_t data, void *closure); + +/* + * Structured data serialization hook. The engine can write primitive values, + * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other + * type of object requires application support. This callback must first use + * the JS_WriteUint32Pair API to write an object header, passing a value + * greater than JS_SCTAG_USER to the tag parameter. Then it can use the + * JS_Write* APIs to write any other relevant parts of the value v to the + * writer w. closure is any value passed to the JS_WriteStructuredCLone function. + * + * Return true on success, false on error/exception. + */ +typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, + JSObject *obj, void *closure); + +/* + * This is called when JS_WriteStructuredClone finds that the object to be + * written is recursive. To follow HTML5, the application must throw a + * DATA_CLONE_ERR DOMException. errorid is always JS_SCERR_RECURSION. + */ +typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); + +/************************************************************************/ + +JS_BEGIN_EXTERN_C + +/* + * Silence warning about returning JS::Value (aka jsval) from functions with C + * linkage. For C JSAPI clients, jsval will be jsval_layout, which should be + * ABI compatible. + */ +#ifdef _MSC_VER +# pragma warning(disable:4190) +#endif -/* Well-known JS values. */ -#define JSVAL_NULL BUILD_JSVAL(JSVAL_TAG_NULL, 0) -#define JSVAL_ZERO BUILD_JSVAL(JSVAL_TAG_INT32, 0) -#define JSVAL_ONE BUILD_JSVAL(JSVAL_TAG_INT32, 1) -#define JSVAL_FALSE BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_FALSE) -#define JSVAL_TRUE BUILD_JSVAL(JSVAL_TAG_BOOLEAN, JS_TRUE) -#define JSVAL_VOID BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0) +/************************************************************************/ -#endif +/* + * JS constants. For efficiency, prefer predicates (e.g., JSVAL_IS_NULL). + * N.B. These constants are initialized at startup. + */ +extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; /************************************************************************/ static JS_ALWAYS_INLINE JSBool JSVAL_IS_NULL(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_NULL_IMPL(l); + return JSVAL_IS_NULL_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_VOID(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_UNDEFINED_IMPL(l); + return JSVAL_IS_UNDEFINED_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_INT(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_INT32_IMPL(l); + return JSVAL_IS_INT32_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE jsint JSVAL_TO_INT(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_INT(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_INT32_IMPL(l); + return JSVAL_TO_INT32_IMPL(JSVAL_TO_IMPL(v)); } -#define JSVAL_INT_BITS 32 -#define JSVAL_INT_MIN ((jsint)0x80000000) -#define JSVAL_INT_MAX ((jsint)0x7fffffff) - static JS_ALWAYS_INLINE jsval -INT_TO_JSVAL(int32 i) +INT_TO_JSVAL(int32_t i) { return IMPL_TO_JSVAL(INT32_TO_JSVAL_IMPL(i)); } @@ -136,9 +1663,7 @@ INT_TO_JSVAL(int32 i) static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_DOUBLE_IMPL(l); + return JSVAL_IS_DOUBLE_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE jsdouble @@ -146,48 +1671,51 @@ JSVAL_TO_DOUBLE(jsval v) { jsval_layout l; JS_ASSERT(JSVAL_IS_DOUBLE(v)); - l.asBits = JSVAL_BITS(v); + l = JSVAL_TO_IMPL(v); return l.asDouble; } static JS_ALWAYS_INLINE jsval DOUBLE_TO_JSVAL(jsdouble d) { - d = JS_CANONICALIZE_NAN(d); - return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d)); + /* This is a manually inlined version of: + * d = JS_CANONICALIZE_NAN(d); + * return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d)); + * because GCC from XCode 3.1.4 miscompiles the above code. */ + jsval_layout l; + if (JS_UNLIKELY(d != d)) { + l.asBits = 0x7FF8000000000000LL; + } else { + l.asDouble = d; + } + return IMPL_TO_JSVAL(l); } static JS_ALWAYS_INLINE jsval -UINT_TO_JSVAL(uint32 i) +UINT_TO_JSVAL(uint32_t i) { if (i <= JSVAL_INT_MAX) - return INT_TO_JSVAL((int32)i); + return INT_TO_JSVAL((int32_t)i); return DOUBLE_TO_JSVAL((jsdouble)i); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_NUMBER(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_NUMBER_IMPL(l); + return JSVAL_IS_NUMBER_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_STRING(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_STRING_IMPL(l); + return JSVAL_IS_STRING_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSString * JSVAL_TO_STRING(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_STRING(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_STRING_IMPL(l); + return JSVAL_TO_STRING_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE jsval @@ -199,18 +1727,14 @@ STRING_TO_JSVAL(JSString *str) static JS_ALWAYS_INLINE JSBool JSVAL_IS_OBJECT(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_OBJECT_OR_NULL_IMPL(l); + return JSVAL_IS_OBJECT_OR_NULL_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSObject * JSVAL_TO_OBJECT(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_OBJECT(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_OBJECT_IMPL(l); + return JSVAL_TO_OBJECT_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE jsval @@ -224,18 +1748,14 @@ OBJECT_TO_JSVAL(JSObject *obj) static JS_ALWAYS_INLINE JSBool JSVAL_IS_BOOLEAN(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_BOOLEAN_IMPL(l); + return JSVAL_IS_BOOLEAN_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSBool JSVAL_TO_BOOLEAN(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_BOOLEAN(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_BOOLEAN_IMPL(l); + return JSVAL_TO_BOOLEAN_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE jsval @@ -247,26 +1767,20 @@ BOOLEAN_TO_JSVAL(JSBool b) static JS_ALWAYS_INLINE JSBool JSVAL_IS_PRIMITIVE(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_PRIMITIVE_IMPL(l); + return JSVAL_IS_PRIMITIVE_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_GCTHING(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_GCTHING_IMPL(l); + return JSVAL_IS_GCTHING_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE void * JSVAL_TO_GCTHING(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_GCTHING(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_GCTHING_IMPL(l); + return JSVAL_TO_GCTHING_IMPL(JSVAL_TO_IMPL(v)); } /* To be GC-safe, privates are tagged as doubles. */ @@ -280,10 +1794,8 @@ PRIVATE_TO_JSVAL(void *ptr) static JS_ALWAYS_INLINE void * JSVAL_TO_PRIVATE(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_DOUBLE(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TO_PRIVATE_PTR_IMPL(l); + return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v)); } /************************************************************************/ @@ -323,7 +1835,7 @@ static JS_ALWAYS_INLINE JSString * JSID_TO_STRING(jsid id) { JS_ASSERT(JSID_IS_STRING(id)); - return (JSString *)(JSID_BITS(id)); + return (JSString *)JSID_BITS(id); } static JS_ALWAYS_INLINE JSBool @@ -333,16 +1845,22 @@ JSID_IS_ZERO(jsid id) } JS_PUBLIC_API(JSBool) -JS_StringHasBeenInterned(JSString *str); +JS_StringHasBeenInterned(JSContext *cx, JSString *str); -/* A jsid may only hold an interned JSString. */ +/* + * Only JSStrings that have been interned via the JSAPI can be turned into + * jsids by API clients. + * + * N.B. if a jsid is backed by a string which has not been interned, that + * string must be appropriately rooted to avoid being collected by the GC. + */ static JS_ALWAYS_INLINE jsid -INTERNED_STRING_TO_JSID(JSString *str) +INTERNED_STRING_TO_JSID(JSContext *cx, JSString *str) { jsid id; JS_ASSERT(str); - JS_ASSERT(JS_StringHasBeenInterned(str)); JS_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0); + JS_ASSERT(JS_StringHasBeenInterned(cx, str)); JSID_BITS(id) = (size_t)str; return id; } @@ -353,11 +1871,11 @@ JSID_IS_INT(jsid id) return !!(JSID_BITS(id) & JSID_TYPE_INT); } -static JS_ALWAYS_INLINE int32 +static JS_ALWAYS_INLINE int32_t JSID_TO_INT(jsid id) { JS_ASSERT(JSID_IS_INT(id)); - return ((int32)JSID_BITS(id)) >> 1; + return ((int32_t)JSID_BITS(id)) >> 1; } /* @@ -368,14 +1886,14 @@ JSID_TO_INT(jsid id) #define JSID_INT_MAX ((1 << 30) - 1) static JS_ALWAYS_INLINE JSBool -INT_FITS_IN_JSID(int32 i) +INT_FITS_IN_JSID(int32_t i) { return ((jsuint)(i) - (jsuint)JSID_INT_MIN <= (jsuint)(JSID_INT_MAX - JSID_INT_MIN)); } static JS_ALWAYS_INLINE jsid -INT_TO_JSID(int32 i) +INT_TO_JSID(int32_t i) { jsid id; JS_ASSERT(INT_FITS_IN_JSID(i)); @@ -432,10 +1950,10 @@ JSID_IS_DEFAULT_XML_NAMESPACE(jsid id) return ((size_t)JSID_BITS(id) == JSID_TYPE_DEFAULT_XML_NAMESPACE); } -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES +#ifdef JS_USE_JSID_STRUCT_TYPES extern JS_PUBLIC_DATA(jsid) JS_DEFAULT_XML_NAMESPACE_ID; #else -#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid)JSID_TYPE_DEFAULT_XML_NAMESPACE) +# define JS_DEFAULT_XML_NAMESPACE_ID ((jsid)JSID_TYPE_DEFAULT_XML_NAMESPACE) #endif /* @@ -461,14 +1979,57 @@ JSID_IS_EMPTY(jsid id) #undef id -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES +#ifdef JS_USE_JSID_STRUCT_TYPES extern JS_PUBLIC_DATA(jsid) JSID_VOID; extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; #else -# define JSID_VOID ((jsid)JSID_TYPE_VOID) -# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT) +# define JSID_VOID ((jsid)JSID_TYPE_VOID) +# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT) #endif +/* + * Returns true iff the given jsval is immune to GC and can be used across + * multiple JSRuntimes without requiring any conversion API. + */ +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_UNIVERSAL(jsval v) +{ + return !JSVAL_IS_GCTHING(v); +} + +#ifdef __cplusplus + +namespace JS { + +class AutoIdRooter : private AutoGCRooter +{ + public: + explicit AutoIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0) + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, ID), id_(id) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + jsid id() { + return id_; + } + + jsid * addr() { + return &id_; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + jsid id_; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} /* namespace JS */ + +#endif /* __cplusplus */ + /************************************************************************/ /* Lock and unlock the GC thing held by a jsval. */ @@ -481,7 +2042,10 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; /* Property attributes, set in JSPropertySpec and passed to API functions. */ #define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ -#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ +#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op. + This flag is only valid when neither + JSPROP_GETTER nor JSPROP_SETTER is + set. */ #define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ #define JSPROP_GETTER 0x10 /* property holds getter function */ #define JSPROP_SETTER 0x20 /* property holds setter function */ @@ -491,10 +2055,12 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; object that delegates to a prototype containing this property */ #define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ -#define JSPROP_SHORTID 0x100 /* set in JSPropertyDescriptor.attrs +#define JSPROP_SHORTID 0x100 /* set in JS_DefineProperty attrs if getters/setters use a shortid */ +#define JSPROP_NATIVE_ACCESSORS 0x08 /* set in JSPropertyDescriptor.flags + if getters/setters are JSNatives */ -/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ +/* Function flags, internal use only, returned by JS_GetFunctionFlags. */ #define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ #define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ @@ -526,10 +2092,18 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; #define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA /* - * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the - * comment in jstypes.h regarding safe int64 usage. + * The first call to JS_CallOnce by any thread in a process will call 'func'. + * Later calls to JS_CallOnce with the same JSCallOnceType object will be + * suppressed. + * + * Equivalently: each distinct JSCallOnceType object will allow one JS_CallOnce + * to invoke its JSInitCallback. */ -extern JS_PUBLIC_API(int64) +extern JS_PUBLIC_API(JSBool) +JS_CallOnce(JSCallOnceType *once, JSInitCallback func); + +/* Microseconds since the epoch, midnight, January 1, 1970 UTC. */ +extern JS_PUBLIC_API(int64_t) JS_Now(void); /* Don't want to export data, so provide accessors for non-inline jsvals. */ @@ -553,10 +2127,10 @@ JS_GetEmptyString(JSRuntime *rt); * specifying the tabulated type conversions: * * b JSBool Boolean - * c uint16/jschar ECMA uint16, Unicode char - * i int32 ECMA int32 - * u uint32 ECMA uint32 - * j int32 Rounded int32 (coordinate) + * c uint16_t/jschar ECMA uint16_t, Unicode char + * i int32_t ECMA int32_t + * u uint32_t ECMA uint32_t + * j int32_t Rounded int32_t (coordinate) * d jsdouble IEEE double * I jsdouble Integral IEEE double * S JSString * Unicode string, accessed by a JSString pointer @@ -594,7 +2168,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, /* * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. - * The handler function has this signature (see jspubtd.h): + * The handler function has this signature: * * JSBool MyArgumentFormatter(JSContext *cx, const char *format, * JSBool fromJS, jsval **vpp, va_list *app); @@ -664,39 +2238,39 @@ JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); extern JS_PUBLIC_API(JSBool) JS_DoubleIsInt32(jsdouble d, jsint *ip); -extern JS_PUBLIC_API(int32) +extern JS_PUBLIC_API(int32_t) JS_DoubleToInt32(jsdouble d); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_DoubleToUint32(jsdouble d); /* - * Convert a value to a number, then to an int32, according to the ECMA rules + * Convert a value to a number, then to an int32_t, according to the ECMA rules * for ToInt32. */ extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip); /* - * Convert a value to a number, then to a uint32, according to the ECMA rules + * Convert a value to a number, then to a uint32_t, according to the ECMA rules * for ToUint32. */ extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip); /* - * Convert a value to a number, then to an int32 if it fits by rounding to + * Convert a value to a number, then to an int32_t if it fits by rounding to * nearest; but failing with an error report if the double is out of range * or unordered. */ extern JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); +JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip); /* * ECMA ToUint16, for mapping a jsval to a Unicode point. */ extern JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); +JS_ValueToUint16(JSContext *cx, jsval v, uint16_t *ip); extern JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); @@ -739,7 +2313,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun); #define JS_UnlockRuntime JS_Unlock extern JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes); +JS_NewRuntime(uint32_t maxbytes); /* Deprecated. */ #define JS_CommenceRuntimeShutDown(rt) ((void) 0) @@ -753,6 +2327,9 @@ JS_ShutDown(void); JS_PUBLIC_API(void *) JS_GetRuntimePrivate(JSRuntime *rt); +extern JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx); + JS_PUBLIC_API(void) JS_SetRuntimePrivate(JSRuntime *rt, void *data); @@ -773,11 +2350,24 @@ extern JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); extern JS_PUBLIC_API(JSBool) -JS_IsInRequest(JSContext *cx); +JS_IsInRequest(JSRuntime *rt); + +extern JS_PUBLIC_API(JSBool) +JS_IsInSuspendedRequest(JSRuntime *rt); #ifdef __cplusplus JS_END_EXTERN_C +inline bool +IsPoisonedId(jsid iden) +{ + if (JSID_IS_STRING(iden)) + return JS::IsPoisonedPtr(JSID_TO_STRING(iden)); + if (JSID_IS_OBJECT(iden)) + return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden)); + return false; +} + class JSAutoRequest { public: JSAutoRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) @@ -845,14 +2435,14 @@ class JSAutoCheckRequest { JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) { #if defined JS_THREADSAFE && defined DEBUG mContext = cx; - JS_ASSERT(JS_IsInRequest(cx)); + JS_ASSERT(JS_IsInRequest(JS_GetRuntime(cx))); #endif JS_GUARD_OBJECT_NOTIFIER_INIT; } ~JSAutoCheckRequest() { #if defined JS_THREADSAFE && defined DEBUG - JS_ASSERT(JS_IsInRequest(mContext)); + JS_ASSERT(JS_IsInRequest(JS_GetRuntime(mContext))); #endif } @@ -867,12 +2457,6 @@ class JSAutoCheckRequest { JS_BEGIN_EXTERN_C #endif -extern JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt); - -extern JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt); - extern JS_PUBLIC_API(JSContextCallback) JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); @@ -894,12 +2478,21 @@ JS_GetContextPrivate(JSContext *cx); extern JS_PUBLIC_API(void) JS_SetContextPrivate(JSContext *cx, void *data); +extern JS_PUBLIC_API(void *) +JS_GetSecondContextPrivate(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SetSecondContextPrivate(JSContext *cx, void *data); + extern JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx); extern JS_PUBLIC_API(JSContext *) JS_ContextIterator(JSRuntime *rt, JSContext **iterp); +extern JS_PUBLIC_API(JSContext *) +JS_ContextIteratorUnlocked(JSRuntime *rt, JSContext **iterp); + extern JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx); @@ -954,11 +2547,9 @@ JS_StringToVersion(const char *string); backtracks more than n^3 times, where n is length of the input string */ -#define JSOPTION_ANONFUNFIX JS_BIT(10) /* Disallow function () {} in - statement context per - ECMA-262 Edition 3. */ +/* JS_BIT(10) is currently unused. */ -#define JSOPTION_JIT JS_BIT(11) /* Enable JIT compilation. */ +/* JS_BIT(11) is currently unused. */ #define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12) /* A promise to the compiler that a null rval out-param @@ -971,25 +2562,33 @@ JS_StringToVersion(const char *string); embedding. */ #define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */ -#define JSOPTION_PROFILING JS_BIT(15) /* Profiler to make tracer/methodjit choices. */ + +/* JS_BIT(15) is currently unused. */ + #define JSOPTION_METHODJIT_ALWAYS \ JS_BIT(16) /* Always whole-method JIT, don't tune at run-time. */ +#define JSOPTION_PCCOUNT JS_BIT(17) /* Collect per-op execution counts */ + +#define JSOPTION_TYPE_INFERENCE JS_BIT(18) /* Perform type inference. */ /* Options which reflect compile-time properties of scripts. */ -#define JSCOMPILEOPTION_MASK (JSOPTION_XML | JSOPTION_ANONFUNFIX) +#define JSCOMPILEOPTION_MASK (JSOPTION_XML) -#define JSRUNOPTION_MASK (JS_BITMASK(17) & ~JSCOMPILEOPTION_MASK) +#define JSRUNOPTION_MASK (JS_BITMASK(19) & ~JSCOMPILEOPTION_MASK) #define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK) -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_GetOptions(JSContext *cx); -extern JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options); +extern JS_PUBLIC_API(uint32_t) +JS_SetOptions(JSContext *cx, uint32_t options); + +extern JS_PUBLIC_API(uint32_t) +JS_ToggleOptions(JSContext *cx, uint32_t options); -extern JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options); +extern JS_PUBLIC_API(void) +JS_SetJitHardening(JSRuntime *rt, JSBool enabled); extern JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void); @@ -1037,32 +2636,48 @@ js_TransplantObjectWithWrapper(JSContext *cx, JSObject *targetobj, JSObject *targetwrapper); +extern JS_FRIEND_API(void *) +js_GetCompartmentPrivate(JSCompartment *compartment); + #ifdef __cplusplus JS_END_EXTERN_C class JS_PUBLIC_API(JSAutoEnterCompartment) { - JSCrossCompartmentCall *call; + /* + * This is a poor man's Maybe, because we don't have + * access to the AutoCompartment definition here. We statically assert in + * jsapi.cpp that we have the right size here. + * + * In practice, 32-bit Windows and Android get 16-word |bytes|, while + * other platforms get 13-word |bytes|. + */ + void* bytes[sizeof(void*) == 4 && MOZ_ALIGNOF(uint64_t) == 8 ? 16 : 13]; + + /* + * This object may be in one of three states. If enter() or + * enterAndIgnoreErrors() hasn't been called, it's in STATE_UNENTERED. + * Otherwise, if we were asked to enter into the current compartment, our + * state is STATE_SAME_COMPARTMENT. If we actually created an + * AutoCompartment and entered another compartment, our state is + * STATE_OTHER_COMPARTMENT. + */ + enum State { + STATE_UNENTERED, + STATE_SAME_COMPARTMENT, + STATE_OTHER_COMPARTMENT + } state; public: - JSAutoEnterCompartment() : call(NULL) {} + JSAutoEnterCompartment() : state(STATE_UNENTERED) {} bool enter(JSContext *cx, JSObject *target); void enterAndIgnoreErrors(JSContext *cx, JSObject *target); - bool entered() const { return call != NULL; } - - ~JSAutoEnterCompartment() { - if (call && call != reinterpret_cast(1)) - JS_LeaveCrossCompartmentCall(call); - } + bool entered() const { return state != STATE_UNENTERED; } - void swap(JSAutoEnterCompartment &other) { - JSCrossCompartmentCall *tmp = call; - call = other.call; - other.call = tmp; - } + ~JSAutoEnterCompartment(); }; JS_BEGIN_EXTERN_C @@ -1117,8 +2732,12 @@ extern JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); +/* + * Returns the original value of |Object.prototype| from the global object in + * which |forObj| was created. + */ extern JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx); +JS_GetObjectPrototype(JSContext *cx, JSObject *forObj); extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj); @@ -1126,6 +2745,12 @@ JS_GetGlobalForObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx); +/* + * Initialize the 'Reflect' object on a global object. + */ +extern JS_PUBLIC_API(JSObject *) +JS_InitReflect(JSContext *cx, JSObject *global); + #ifdef JS_HAS_CTYPES /* * Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes' @@ -1159,10 +2784,20 @@ typedef struct JSCTypesCallbacks JSCTypesCallbacks; * may safely be altered after calling this function and without having * to call this function again. */ -extern JS_PUBLIC_API(JSBool) -JS_SetCTypesCallbacks(JSContext *cx, JSObject *ctypesObj, JSCTypesCallbacks *callbacks); +extern JS_PUBLIC_API(void) +JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks *callbacks); #endif +typedef JSBool +(* JSEnumerateDiagnosticMemoryCallback)(void *ptr, size_t length); + +/* + * Enumerate memory regions that contain diagnostic information + * intended to be included in crash report minidumps. + */ +extern JS_PUBLIC_API(void) +JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback); + /* * Macros to hide interpreter stack layout details from a JSFastNative using * its jsval *vp parameter. The stack layout underlying invocation can't change @@ -1297,6 +2932,9 @@ JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name); extern JS_PUBLIC_API(JSBool) JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name); +extern JS_PUBLIC_API(JSBool) +JS_AddNamedScriptRoot(JSContext *cx, JSScript **rp, const char *name); + extern JS_PUBLIC_API(JSBool) JS_AddNamedGCThingRoot(JSContext *cx, void **rp, const char *name); @@ -1307,183 +2945,24 @@ extern JS_PUBLIC_API(JSBool) JS_RemoveStringRoot(JSContext *cx, JSString **rp); extern JS_PUBLIC_API(JSBool) -JS_RemoveObjectRoot(JSContext *cx, JSObject **rp); - -extern JS_PUBLIC_API(JSBool) -JS_RemoveGCThingRoot(JSContext *cx, void **rp); - -/* TODO: remove these APIs */ - -extern JS_FRIEND_API(JSBool) -js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name); - -extern JS_FRIEND_API(JSBool) -js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name); - -extern JS_FRIEND_API(JSBool) -js_RemoveRoot(JSRuntime *rt, void *rp); - -#ifdef __cplusplus -JS_END_EXTERN_C - -namespace JS { - -/* - * Protecting non-jsval, non-JSObject *, non-JSString * values from collection - * - * Most of the time, the garbage collector's conservative stack scanner works - * behind the scenes, finding all live values and protecting them from being - * collected. However, when JSAPI client code obtains a pointer to data the - * scanner does not know about, owned by an object the scanner does know about, - * Care Must Be Taken. - * - * The scanner recognizes only a select set of types: pointers to JSObjects and - * similar things (JSFunctions, and so on), pointers to JSStrings, and jsvals. - * So while the scanner finds all live |JSString| pointers, it does not notice - * |jschar| pointers. - * - * So suppose we have: - * - * void f(JSString *str) { - * const jschar *ch = JS_GetStringCharsZ(str); - * ... do stuff with ch, but no uses of str ...; - * } - * - * After the call to |JS_GetStringCharsZ|, there are no further uses of - * |str|, which means that the compiler is within its rights to not store - * it anywhere. But because the stack scanner will not notice |ch|, there - * is no longer any live value in this frame that would keep the string - * alive. If |str| is the last reference to that |JSString|, and the - * collector runs while we are using |ch|, the string's array of |jschar|s - * may be freed out from under us. - * - * Note that there is only an issue when 1) we extract a thing X the scanner - * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y - * gets garbage-collected, then X gets freed. If we have code like this: - * - * void g(JSObject *obj) { - * jsval x; - * JS_GetProperty(obj, "x", &x); - * ... do stuff with x ... - * } - * - * there's no problem, because the value we've extracted, x, is a jsval, a - * type that the conservative scanner recognizes. - * - * Conservative GC frees us from the obligation to explicitly root the types it - * knows about, but when we work with derived values like |ch|, we must root - * their owners, as the derived value alone won't keep them alive. - * - * A JS::Anchor is a kind of GC root that allows us to keep the owners of - * derived values like |ch| alive throughout the Anchor's lifetime. We could - * fix the above code as follows: - * - * void f(JSString *str) { - * JS::Anchor a_str(str); - * const jschar *ch = JS_GetStringCharsZ(str); - * ... do stuff with ch, but no uses of str ...; - * } - * - * This simply ensures that |str| will be live until |a_str| goes out of scope. - * As long as we don't retain a pointer to the string's characters for longer - * than that, we have avoided all garbage collection hazards. - */ -template class AnchorPermitted; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; -template<> class AnchorPermitted { }; - -template -class Anchor: AnchorPermitted { - public: - Anchor() { } - explicit Anchor(T t) { hold = t; } - inline ~Anchor(); - T &get() { return hold; } - const T &get() const { return hold; } - void set(const T &t) { hold = t; } - void clear() { hold = 0; } - private: - T hold; - /* Anchors should not be assigned or passed to functions. */ - Anchor(const Anchor &); - const Anchor &operator=(const Anchor &); -}; - -#ifdef __GNUC__ -template -inline Anchor::~Anchor() { - /* - * No code is generated for this. But because this is marked 'volatile', G++ will - * assume it has important side-effects, and won't delete it. (G++ never looks at - * the actual text and notices it's empty.) And because we have passed |hold| to - * it, GCC will keep |hold| alive until this point. - * - * The "memory" clobber operand ensures that G++ will not move prior memory - * accesses after the asm --- it's a barrier. Unfortunately, it also means that - * G++ will assume that all memory has changed after the asm, as it would for a - * call to an unknown function. I don't know of a way to avoid that consequence. - */ - asm volatile("":: "g" (hold) : "memory"); -} -#else -template -inline Anchor::~Anchor() { - /* - * An adequate portable substitute, for non-structure types. - * - * The compiler promises that, by the end of an expression statement, the - * last-stored value to a volatile object is the same as it would be in an - * unoptimized, direct implementation (the "abstract machine" whose behavior the - * language spec describes). However, the compiler is still free to reorder - * non-volatile accesses across this store --- which is what we must prevent. So - * assigning the held value to a volatile variable, as we do here, is not enough. - * - * In our case, however, garbage collection only occurs at function calls, so it - * is sufficient to ensure that the destructor's store isn't moved earlier across - * any function calls that could collect. It is hard to imagine the compiler - * analyzing the program so thoroughly that it could prove that such motion was - * safe. In practice, compilers treat calls to the collector as opaque operations - * --- in particular, as operations which could access volatile variables, across - * which this destructor must not be moved. - * - * ("Objection, your honor! *Alleged* killer whale!") - * - * The disadvantage of this approach is that it does generate code for the store. - * We do need to use Anchors in some cases where cycles are tight. - */ - volatile T sink; - sink = hold; -} +JS_RemoveObjectRoot(JSContext *cx, JSObject **rp); -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES -/* - * The default assignment operator for |struct C| has the signature: - * - * C& C::operator=(const C&) - * - * And in particular requires implicit conversion of |this| to type |C| for the return - * value. But |volatile C| cannot thus be converted to |C|, so just doing |sink = hold| as - * in the non-specialized version would fail to compile. Do the assignment on asBits - * instead, since I don't think we want to give jsval_layout an assignment operator - * returning |volatile jsval_layout|. - */ -template<> -inline Anchor::~Anchor() { - volatile jsval sink; - sink.asBits = hold.asBits; -} -#endif -#endif +extern JS_PUBLIC_API(JSBool) +JS_RemoveScriptRoot(JSContext *cx, JSScript **rp); -} /* namespace JS */ +extern JS_PUBLIC_API(JSBool) +JS_RemoveGCThingRoot(JSContext *cx, void **rp); -JS_BEGIN_EXTERN_C -#endif +/* TODO: remove these APIs */ + +extern JS_FRIEND_API(JSBool) +js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name); + +extern JS_FRIEND_API(JSBool) +js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name); + +extern JS_FRIEND_API(JSBool) +js_RemoveRoot(JSRuntime *rt, void *rp); /* * C-compatible version of the Anchor class. It should be called after the last @@ -1499,7 +2978,6 @@ JS_AnchorPtr(void *p); #define JS_TYPED_ROOTING_API /* Obsolete rooting APIs. */ -#define JS_ClearNewbornRoots(cx) ((void) 0) #define JS_EnterLocalRootScope(cx) (JS_TRUE) #define JS_LeaveLocalRootScope(cx) ((void) 0) #define JS_LeaveLocalRootScopeWithResult(cx, rval) ((void) 0) @@ -1546,7 +3024,7 @@ JS_DumpNamedRoots(JSRuntime *rt, typedef intN (* JSGCRootMapFun)(void *rp, JSGCRootType type, const char *name, void *data); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); extern JS_PUBLIC_API(JSBool) @@ -1569,7 +3047,7 @@ JS_UnlockGCThingRT(JSRuntime *rt, void *thing); * data: the data argument to pass to each invocation of traceOp. */ extern JS_PUBLIC_API(void) -JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data); +JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); /* * JS_CallTracer API and related macros for implementors of JSTraceOp, to @@ -1577,17 +3055,9 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data); * other strong ref identified for debugging purposes by name or index or * a naming callback. * - * By definition references to traceable things include non-null pointers - * to JSObject, JSString and jsdouble and corresponding jsvals. - * - * See the JSTraceOp typedef in jspubtd.h. + * See the JSTraceOp typedef. */ -/* Trace kinds to pass to JS_Tracing. */ -#define JSTRACE_OBJECT 0 -#define JSTRACE_STRING 1 -#define JSTRACE_SHAPE 2 - /* * Use the following macros to check if a particular jsval is a traceable * thing and to extract the thing and its kind to pass to JS_CallTracer. @@ -1595,9 +3065,7 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data); static JS_ALWAYS_INLINE JSBool JSVAL_IS_TRACEABLE(jsval v) { - jsval_layout l; - l.asBits = JSVAL_BITS(v); - return JSVAL_IS_TRACEABLE_IMPL(l); + return JSVAL_IS_TRACEABLE_IMPL(JSVAL_TO_IMPL(v)); } static JS_ALWAYS_INLINE void * @@ -1606,21 +3074,40 @@ JSVAL_TO_TRACEABLE(jsval v) return JSVAL_TO_GCTHING(v); } -static JS_ALWAYS_INLINE uint32 +static JS_ALWAYS_INLINE JSGCTraceKind JSVAL_TRACE_KIND(jsval v) { - jsval_layout l; JS_ASSERT(JSVAL_IS_GCTHING(v)); - l.asBits = JSVAL_BITS(v); - return JSVAL_TRACE_KIND_IMPL(l); + return (JSGCTraceKind) JSVAL_TRACE_KIND_IMPL(JSVAL_TO_IMPL(v)); } +/* + * Tracer callback, called for each traceable thing directly referenced by a + * particular object or runtime structure. It is the callback responsibility + * to ensure the traversal of the full object graph via calling eventually + * JS_TraceChildren on the passed thing. In this case the callback must be + * prepared to deal with cycles in the traversal graph. + * + * kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting + * internal implementation-specific traversal kind. In the latter case the only + * operations on thing that the callback can do is to call JS_TraceChildren or + * DEBUG-only JS_PrintTraceThingInfo. + * + * If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all + * of its mappings. This should be used in cases where the tracer + * wants to use the existing liveness of entries. + */ +typedef void +(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind); + struct JSTracer { + JSRuntime *runtime; JSContext *context; JSTraceCallback callback; JSTraceNamePrinter debugPrinter; const void *debugPrintArg; size_t debugPrintIndex; + JSBool eagerlyTraceWeakMaps; }; /* @@ -1630,7 +3117,7 @@ struct JSTracer { * describing the reference using the macros below. */ extern JS_PUBLIC_API(void) -JS_CallTracer(JSTracer *trc, void *thing, uint32 kind); +JS_CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind); /* * Set debugging information about a reference to a traceable thing to prepare @@ -1642,7 +3129,7 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind); * that stores the reference. * * When printer callback is not null, the arg and index arguments are - * available to the callback as debugPrinterArg and debugPrintIndex fields + * available to the callback as debugPrintArg and debugPrintIndex fields * of JSTracer. * * The storage for name or callback's arguments needs to live only until @@ -1713,17 +3200,11 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind); /* * API for JSTraceCallback implementations. */ -# define JS_TRACER_INIT(trc, cx_, callback_) \ - JS_BEGIN_MACRO \ - (trc)->context = (cx_); \ - (trc)->callback = (callback_); \ - (trc)->debugPrinter = NULL; \ - (trc)->debugPrintArg = NULL; \ - (trc)->debugPrintIndex = (size_t)-1; \ - JS_END_MACRO +extern JS_PUBLIC_API(void) +JS_TracerInit(JSTracer *trc, JSContext *cx, JSTraceCallback callback); extern JS_PUBLIC_API(void) -JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind); +JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind); extern JS_PUBLIC_API(void) JS_TraceRuntime(JSTracer *trc); @@ -1732,7 +3213,10 @@ JS_TraceRuntime(JSTracer *trc); extern JS_PUBLIC_API(void) JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, - void *thing, uint32 kind, JSBool includeDetails); + void *thing, JSGCTraceKind kind, JSBool includeDetails); + +extern JS_PUBLIC_API(const char *) +JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize); /* * DEBUG-only method to dump the object graph of heap-allocated things. @@ -1741,8 +3225,8 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, * start: when non-null, dump only things reachable from start * thing. Otherwise dump all things reachable from the * runtime roots. - * startKind: trace kind of start if start is not null. Must be 0 when - * start is null. + * startKind: trace kind of start if start is not null. Must be + * JSTRACE_OBJECT when start is null. * thingToFind: dump only paths in the object graph leading to thingToFind * when non-null. * maxDepth: the upper bound on the number of edges to descend from the @@ -1750,7 +3234,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, * thingToIgnore: thing to ignore during the graph traversal when non-null. */ extern JS_PUBLIC_API(JSBool) -JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, +JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind kind, void *thingToFind, size_t maxDepth, void *thingToIgnore); #endif @@ -1761,6 +3245,9 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, extern JS_PUBLIC_API(void) JS_GC(JSContext *cx); +extern JS_PUBLIC_API(void) +JS_CompartmentGC(JSContext *cx, JSCompartment *comp); + extern JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx); @@ -1774,7 +3261,7 @@ extern JS_PUBLIC_API(JSBool) JS_IsGCMarkingTracer(JSTracer *trc); extern JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing); +JS_IsAboutToBeFinalized(void *thing); typedef enum JSGCParamKey { /* Maximum nominal heap before last ditch GC. */ @@ -1783,33 +3270,23 @@ typedef enum JSGCParamKey { /* Number of JS_malloc bytes before last ditch GC. */ JSGC_MAX_MALLOC_BYTES = 1, - /* Hoard stackPools for this long, in ms, default is 30 seconds. */ - JSGC_STACKPOOL_LIFESPAN = 2, - - /* - * The factor that defines when the GC is invoked. The factor is a - * percent of the memory allocated by the GC after the last run of - * the GC. When the current memory allocated by the GC is more than - * this percent then the GC is invoked. The factor cannot be less - * than 100 since the current memory allocated by the GC cannot be less - * than the memory allocated after the last run of the GC. - */ - JSGC_TRIGGER_FACTOR = 3, - /* Amount of bytes allocated by the GC. */ - JSGC_BYTES = 4, + JSGC_BYTES = 3, /* Number of times when GC was invoked. */ - JSGC_NUMBER = 5, + JSGC_NUMBER = 4, /* Max size of the code cache in bytes. */ - JSGC_MAX_CODE_CACHE_BYTES = 6, + JSGC_MAX_CODE_CACHE_BYTES = 5, /* Select GC mode. */ - JSGC_MODE = 7, + JSGC_MODE = 6, + + /* Number of cached empty GC chunks. */ + JSGC_UNUSED_CHUNKS = 7, - /* Number of GC chunks waiting to expire. */ - JSGC_UNUSED_CHUNKS = 8 + /* Total number of allocated GC chunks. */ + JSGC_TOTAL_CHUNKS = 8 } JSGCParamKey; typedef enum JSGCMode { @@ -1821,15 +3298,15 @@ typedef enum JSGCMode { } JSGCMode; extern JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key); extern JS_PUBLIC_API(void) -JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value); +JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key); /* @@ -1841,99 +3318,34 @@ JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key); extern JS_PUBLIC_API(void) JS_FlushCaches(JSContext *cx); -/* - * Add a finalizer for external strings created by JS_NewExternalString (see - * below) using a type-code returned from this function, and that understands - * how to free or release the memory pointed at by JS_GetStringChars(str). - * - * Return a nonnegative type index if there is room for finalizer in the - * global GC finalizers table, else return -1. If the engine is compiled - * JS_THREADSAFE and used in a multi-threaded environment, this function must - * be invoked on the primordial thread only, at startup -- or else the entire - * program must single-thread itself while loading a module that calls this - * function. - */ -extern JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); - -/* - * Remove finalizer from the global GC finalizers table, returning its type - * code if found, -1 if not found. - * - * As with JS_AddExternalStringFinalizer, there is a threading restriction - * if you compile the engine JS_THREADSAFE: this function may be called for a - * given finalizer pointer on only one thread; different threads may call to - * remove distinct finalizers safely. - * - * You must ensure that all strings with finalizer's type have been collected - * before calling this function. Otherwise, string data will be leaked by the - * GC, for want of a finalizer to call. - */ -extern JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); - /* * Create a new JSString whose chars member refers to external memory, i.e., - * memory requiring type-specific finalization. The type code must be a - * nonnegative return value from JS_AddExternalStringFinalizer. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type); - -/* - * Like JS_NewExternalString, except that 'closure' can be retrieved later via - * JS_GetExternalStringClosure. This closure data is a black blox to the JS - * engine and may be used by the embedding to associate extra data with an - * external string. E.g., an embedding may want to associate a pointer to the - * object that owns the chars of an external string so that, when this external - * string is finalized, the owner object can be deleted. + * memory requiring application-specific finalization. */ extern JS_PUBLIC_API(JSString *) -JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length, - intN type, void *closure); +JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, + const JSStringFinalizer *fin); /* * Return whether 'str' was created with JS_NewExternalString or * JS_NewExternalStringWithClosure. */ extern JS_PUBLIC_API(JSBool) -JS_IsExternalString(JSContext *cx, JSString *str); +JS_IsExternalString(JSString *str); /* * Return the 'closure' arg passed to JS_NewExternalStringWithClosure or NULL * if the external string was created via JS_NewExternalString. */ -extern JS_PUBLIC_API(void *) -JS_GetExternalStringClosure(JSContext *cx, JSString *str); - -/* - * Deprecated. Use JS_SetNativeStackQuoata instead. - */ -extern JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); +extern JS_PUBLIC_API(const JSStringFinalizer *) +JS_GetExternalStringFinalizer(JSString *str); /* * Set the size of the native stack that should not be exceed. To disable * stack size checking pass 0. */ extern JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSContext *cx, size_t stackSize); - - -/* - * Set the quota on the number of bytes that stack-like data structures can - * use when the runtime compiles and executes scripts. These structures - * consume heap space, so JS_SetThreadStackLimit does not bound their size. - * The default quota is 128MB which is very generous. - * - * The function must be called before any script compilation or execution API - * calls, i.e. either immediately after JS_NewContext or from JSCONTEXT_NEW - * context callback. - */ -extern JS_PUBLIC_API(void) -JS_SetScriptStackQuota(JSContext *cx, size_t quota); - -#define JS_DEFAULT_SCRIPT_STACK_QUOTA ((size_t) 0x8000000) +JS_SetNativeStackQuota(JSRuntime *cx, size_t stackSize); /************************************************************************/ @@ -1942,10 +3354,9 @@ JS_SetScriptStackQuota(JSContext *cx, size_t quota); */ typedef void (*JSClassInternal)(); -/* For detailed comments on the function pointer types, see jspubtd.h. */ struct JSClass { const char *name; - uint32 flags; + uint32_t flags; /* Mandatory non-null function pointer members. */ JSPropertyOp addProperty; @@ -1967,21 +3378,18 @@ struct JSClass { JSTraceOp trace; JSClassInternal reserved1; - void *reserved[19]; + void *reserved[40]; }; #define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ #define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ #define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ #define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ -#define JSCLASS_CONCURRENT_FINALIZER (1<<4) /* finalize is called on background thread */ -#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting +#define JSCLASS_NEW_RESOLVE_GETS_START (1<<4) /* JSNewResolveOp gets starting object in prototype chain passed in via *objp in/out parameter */ -#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class - prototype */ -#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ +#define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */ /* * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or @@ -2000,7 +3408,12 @@ struct JSClass { #define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ JSCLASS_RESERVED_SLOTS_WIDTH) -#define JSCLASS_INTERNAL_FLAG1 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) +/* + * Call the iteratorObject hook only to iterate over contents (for-of), not to + * enumerate properties (for-in, for-each, Object.keys, etc.) + */ +#define JSCLASS_FOR_OF_ITERATION (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) + #define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) #define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) #define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) @@ -2010,6 +3423,8 @@ struct JSClass { #define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) #define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6)) +#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7)) + /* Global flags. */ #define JSGLOBAL_FLAGS_CLEARED 0x1 @@ -2024,9 +3439,14 @@ struct JSClass { * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was * prevously allowed, but is now an ES5 violation and thus unsupported. */ -#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 6) +#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 8) +#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ + (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ - (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT)) + JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0) +#define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \ + (((clasp)->flags & JSCLASS_IS_GLOBAL) \ + && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT) /* Fast access to the original value of each standard class's prototype. */ #define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) @@ -2042,14 +3462,66 @@ struct JSClass { #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 { - jsint length; - jsid vector[1]; /* actually, length jsid words */ -}; +extern JS_PUBLIC_API(jsint) +JS_IdArrayLength(JSContext *cx, JSIdArray *ida); + +extern JS_PUBLIC_API(jsid) +JS_IdArrayGet(JSContext *cx, JSIdArray *ida, jsint index); extern JS_PUBLIC_API(void) JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); +#ifdef __cplusplus + +namespace JS { + +class AutoIdArray : private AutoGCRooter { + public: + AutoIdArray(JSContext *cx, JSIdArray *ida JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, IDARRAY), idArray(ida) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + ~AutoIdArray() { + if (idArray) + JS_DestroyIdArray(context, idArray); + } + bool operator!() { + return !idArray; + } + jsid operator[](size_t i) const { + JS_ASSERT(idArray); + JS_ASSERT(i < length()); + return JS_IdArrayGet(context, idArray, i); + } + size_t length() const { + return JS_IdArrayLength(context, idArray); + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + JSIdArray *steal() { + JSIdArray *copy = idArray; + idArray = NULL; + return copy; + } + + protected: + inline void trace(JSTracer *trc); + + private: + JSIdArray *idArray; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + + /* No copy or assignment semantics. */ + AutoIdArray(AutoIdArray &ida) MOZ_DELETE; + void operator=(AutoIdArray &ida) MOZ_DELETE; +}; + +} /* namespace JS */ + +#endif /* __cplusplus */ + extern JS_PUBLIC_API(JSBool) JS_ValueToId(JSContext *cx, jsval v, jsid *idp); @@ -2066,6 +3538,15 @@ JS_IdToValue(JSContext *cx, jsid id, jsval *vp); #define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ #define JSRESOLVE_WITH 0x20 /* resolve inside a with statement */ +/* + * Invoke the [[DefaultValue]] hook (see ES5 8.6.2) with the provided hint on + * the specified object, computing a primitive default value for the object. + * The hint must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no hint). On + * success the resulting value is stored in *vp. + */ +extern JS_PUBLIC_API(JSBool) +JS_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); + extern JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp); @@ -2087,8 +3568,8 @@ JS_FinalizeStub(JSContext *cx, JSObject *obj); struct JSConstDoubleSpec { jsdouble dval; const char *name; - uint8 flags; - uint8 spare[3]; + uint8_t flags; + uint8_t spare[3]; }; /* @@ -2098,8 +3579,8 @@ struct JSConstDoubleSpec { */ struct JSPropertySpec { const char *name; - int8 tinyid; - uint8 flags; + int8_t tinyid; + uint8_t flags; JSPropertyOp getter; JSStrictPropertyOp setter; }; @@ -2107,8 +3588,8 @@ struct JSPropertySpec { struct JSFunctionSpec { const char *name; JSNative call; - uint16 nargs; - uint16 flags; + uint16_t nargs; + uint16_t flags; }; /* @@ -2133,18 +3614,16 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs); -#ifdef JS_THREADSAFE -extern JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj); +/* + * Set up ctor.prototype = proto and proto.constructor = ctor with the + * right property flags. + */ +extern JS_PUBLIC_API(JSBool) +JS_LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto); -#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) -#else extern JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj); -#define JS_GET_CLASS(cx,obj) JS_GetClass(obj) -#endif - extern JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); @@ -2152,23 +3631,23 @@ extern JS_PUBLIC_API(JSBool) JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); extern JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj); +JS_GetPrivate(JSObject *obj); -extern JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); +extern JS_PUBLIC_API(void) +JS_SetPrivate(JSObject *obj, void *data); extern JS_PUBLIC_API(void *) JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); extern JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj); +JS_GetPrototype(JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); extern JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj); +JS_GetParent(JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); @@ -2223,12 +3702,11 @@ extern JS_PUBLIC_API(JSBool) JS_FreezeObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent); +JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *parent); extern JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv); +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *parent, + uintN argc, jsval *argv); extern JS_PUBLIC_API(JSObject *) JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv); @@ -2295,14 +3773,10 @@ JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, extern JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, - int8 tinyid, jsval value, + int8_t tinyid, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); -extern JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, - const char *alias); - extern JS_PUBLIC_API(JSBool) JS_AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); @@ -2364,6 +3838,9 @@ JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetPropertyByIdDefault(JSContext *cx, JSObject *obj, jsid id, jsval def, jsval *vp); +extern JS_PUBLIC_API(JSBool) +JS_ForwardGetPropertyTo(JSContext *cx, JSObject *obj, jsid id, JSObject *onBehalfOf, jsval *vp); + extern JS_PUBLIC_API(JSBool) JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp); @@ -2435,7 +3912,7 @@ JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, extern JS_PUBLIC_API(JSBool) JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, - int8 tinyid, jsval value, + int8_t tinyid, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); @@ -2481,36 +3958,42 @@ extern JS_PUBLIC_API(JSBool) JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); extern JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); +JS_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, jsval value, + JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); extern JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); +JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *foundp); extern JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); +JS_HasElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *foundp); extern JS_PUBLIC_API(JSBool) -JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, jsint index, - JSBool *foundp); +JS_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp); extern JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); +JS_GetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp); extern JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); +JS_ForwardGetElementTo(JSContext *cx, JSObject *obj, uint32_t index, JSObject *onBehalfOf, + jsval *vp); +/* + * Get the property with name given by |index|, if it has one. If + * not, |*present| will be set to false and the value of |vp| must not + * be relied on. + */ extern JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); +JS_GetElementIfPresent(JSContext *cx, JSObject *obj, uint32_t index, JSObject *onBehalfOf, + jsval *vp, JSBool* present); extern JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); +JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp); extern JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); +JS_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index); extern JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); +JS_DeleteElement2(JSContext *cx, JSObject *obj, uint32_t index, jsval *rval); extern JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj); @@ -2534,15 +4017,30 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); +/* + * Create an object to iterate over the elements of obj in for-of order. This + * can be used to implement the iteratorObject hook for an array-like Class. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj); + +/* + * To make your array-like class iterable using the for-of loop, set the + * JSCLASS_FOR_OF_ITERATION bit in the class's flags field and set its + * .ext.iteratorObject hook to this function. + */ +extern JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly); + extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); -extern JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); +extern JS_PUBLIC_API(jsval) +JS_GetReservedSlot(JSObject *obj, uint32_t index); -extern JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); +extern JS_PUBLIC_API(void) +JS_SetReservedSlot(JSObject *obj, uint32_t index, jsval v); /************************************************************************/ @@ -2552,10 +4050,6 @@ JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); struct JSPrincipals { char *codebase; - /* XXX unspecified and unused by Mozilla code -- can we remove these? */ - void * (* getPrincipalArray)(JSContext *cx, JSPrincipals *); - JSBool (* globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); - /* Don't call "destroy"; use reference counting macros below. */ jsrefcount refcount; @@ -2601,6 +4095,21 @@ JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks); extern JS_PUBLIC_API(JSSecurityCallbacks *) JS_GetSecurityCallbacks(JSContext *cx); +/* + * Code running with "trusted" principals will be given a deeper stack + * allocation than ordinary scripts. This allows trusted script to run after + * untrusted script has exhausted the stack. This function sets the + * runtime-wide trusted principal. + * + * This principals is not held (via JS_HoldPrincipals/JS_DropPrincipals) since + * there is no available JSContext. Instead, the caller must ensure that the + * given principals stays valid for as long as 'rt' may point to it. If the + * principals would be destroyed before 'rt', JS_SetTrustedPrincipals must be + * called again, passing NULL for 'prin'. + */ +extern JS_PUBLIC_API(void) +JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin); + /************************************************************************/ /* @@ -2639,7 +4148,7 @@ JS_GetFunctionFlags(JSFunction *fun); /* * Return the arity (length) of fun. */ -extern JS_PUBLIC_API(uint16) +extern JS_PUBLIC_API(uint16_t) JS_GetFunctionArity(JSFunction *fun); /* @@ -2654,6 +4163,9 @@ JS_ObjectIsFunction(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_ObjectIsCallable(JSContext *cx, JSObject *obj); +extern JS_PUBLIC_API(JSBool) +JS_IsNativeFunction(JSObject *funobj, JSNative call); + extern JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); @@ -2684,59 +4196,62 @@ extern JS_PUBLIC_API(JSBool) JS_BufferIsCompilableUnit(JSContext *cx, JSBool bytes_are_utf8, JSObject *obj, const char *bytes, size_t length); -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno); -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, const char *filename, uintN lineno); -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) 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(JSObject *) +extern JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno); -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, const char *filename, uintN lineno); -extern JS_PUBLIC_API(JSObject *) +extern JS_PUBLIC_API(JSScript *) 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(JSObject *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); +extern JS_PUBLIC_API(JSScript *) +JS_CompileUTF8File(JSContext *cx, JSObject *obj, const char *filename); -extern JS_PUBLIC_API(JSObject *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, - FILE *fh); +extern JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *fh); -extern JS_PUBLIC_API(JSObject *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, - const char *filename, FILE *fh, - JSPrincipals *principals); +extern JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *fh, + JSPrincipals *principals); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUTF8FileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, + const char *filename, FILE *fh, + JSPrincipals *principals, + JSVersion version); extern JS_PUBLIC_API(JSObject *) -JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, - const char *filename, FILE *fh, - JSPrincipals *principals, - JSVersion version); +JS_GetGlobalFromScript(JSScript *script); extern JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, @@ -2773,7 +4288,7 @@ JS_CompileUCFunctionForPrincipalsVersion(JSContext *cx, JSObject *obj, JSVersion version); extern JS_PUBLIC_API(JSString *) -JS_DecompileScriptObject(JSContext *cx, JSObject *scriptObj, const char *name, uintN indent); +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent); /* * API extension: OR this into indent to avoid pretty-printing the decompiled @@ -2823,10 +4338,10 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); * etc., entry points. */ extern JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval); +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); extern JS_PUBLIC_API(JSBool) -JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval, +JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval, JSVersion version); /* @@ -2861,6 +4376,13 @@ JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const char *filename, uintN lineno, jsval *rval); +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + extern JS_PUBLIC_API(JSBool) JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, JSPrincipals *principals, @@ -2868,12 +4390,20 @@ JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj, const char *filename, uintN lineno, jsval *rval, JSVersion version); +/* + * JSAPI clients may optionally specify the 'originPrincipals' of a script. + * A script's originPrincipals may be retrieved through the debug API (via + * JS_GetScriptOriginPrincipals) and the originPrincipals are transitively + * assigned to any nested scripts (including scripts dynamically created via + * eval and the Function constructor). + */ extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); +JS_EvaluateUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + JSPrincipals *originPrincipals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval, JSVersion version); extern JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, @@ -2915,7 +4445,7 @@ Call(JSContext *cx, jsval thisv, JSObject *funObj, uintN argc, jsval *argv, jsva return Call(cx, thisv, OBJECT_TO_JSVAL(funObj), argc, argv, rval); } -} /* namespace js */ +} /* namespace JS */ JS_BEGIN_EXTERN_C #endif /* __cplusplus */ @@ -2947,7 +4477,7 @@ extern JS_PUBLIC_API(void) JS_TriggerOperationCallback(JSContext *cx); extern JS_PUBLIC_API(void) -JS_TriggerAllOperationCallbacks(JSRuntime *rt); +JS_TriggerRuntimeOperationCallback(JSRuntime *rt); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); @@ -2961,15 +4491,30 @@ JS_IsRunning(JSContext *cx); * must be balanced and all nested calls to JS_SaveFrameChain must have had * matching JS_RestoreFrameChain calls. * - * JS_SaveFrameChain deals with cx not having any code running on it. A null - * return does not signify an error, and JS_RestoreFrameChain handles a null - * frame pointer argument safely. + * JS_SaveFrameChain deals with cx not having any code running on it. */ -extern JS_PUBLIC_API(JSStackFrame *) +extern JS_PUBLIC_API(JSBool) JS_SaveFrameChain(JSContext *cx); extern JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); +JS_RestoreFrameChain(JSContext *cx); + +#ifdef MOZ_TRACE_JSCALLS +/* + * 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 */ /************************************************************************/ @@ -3010,7 +4555,7 @@ extern JS_PUBLIC_API(JSString *) JS_InternUCString(JSContext *cx, const jschar *s); extern JS_PUBLIC_API(JSBool) -JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); +JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result); extern JS_PUBLIC_API(JSBool) JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match); @@ -3309,7 +4854,7 @@ class JSAutoByteString { /* * JSON functions */ -typedef JSBool (* JSONWriteCallback)(const jschar *buf, uint32 len, void *data); +typedef JSBool (* JSONWriteCallback)(const jschar *buf, uint32_t len, void *data); /* * JSON.stringify as specified by ES5. @@ -3318,20 +4863,15 @@ JS_PUBLIC_API(JSBool) JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, JSONWriteCallback callback, void *data); -/* - * Retrieve a toJSON function. If found, set vp to its result. - */ -JS_PUBLIC_API(JSBool) -JS_TryJSON(JSContext *cx, jsval *vp); - /* * JSON.parse as specified by ES5. */ JS_PUBLIC_API(JSBool) -JS_ParseJSON(JSContext *cx, const jschar *chars, uint32 len, jsval *vp); +JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, jsval *vp); JS_PUBLIC_API(JSBool) -JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32 len, jsval reviver, jsval *vp); +JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval reviver, + jsval *vp); /************************************************************************/ @@ -3347,14 +4887,14 @@ struct JSStructuredCloneCallbacks { }; JS_PUBLIC_API(JSBool) -JS_ReadStructuredClone(JSContext *cx, const uint64 *data, size_t nbytes, - uint32 version, jsval *vp, +JS_ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, + uint32_t version, jsval *vp, const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); /* Note: On success, the caller is responsible for calling js::Foreground::free(*datap). */ JS_PUBLIC_API(JSBool) -JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **datap, size_t *nbytesp, +JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **datap, size_t *nbytesp, const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); @@ -3364,126 +4904,69 @@ JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, void *closure); #ifdef __cplusplus +JS_END_EXTERN_C + /* RAII sugar for JS_WriteStructuredClone. */ -class JSAutoStructuredCloneBuffer { - JSContext *cx_; - uint64 *data_; +class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { + uint64_t *data_; size_t nbytes_; - uint32 version_; + uint32_t version_; public: JSAutoStructuredCloneBuffer() - : cx_(NULL), data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} + : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} ~JSAutoStructuredCloneBuffer() { clear(); } - JSContext *cx() const { return cx_; } - uint64 *data() const { return data_; } + uint64_t *data() const { return data_; } size_t nbytes() const { return nbytes_; } - void clear(JSContext *cx=NULL) { - if (data_) { - if (!cx) - cx = cx_; - JS_ASSERT(cx); - JS_free(cx, data_); - cx_ = NULL; - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } - } + void clear(); + + /* Copy some memory. It will be automatically freed by the destructor. */ + bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); /* * Adopt some memory. It will be automatically freed by the destructor. - * data must have been allocated using JS_malloc. + * data must have been allocated by the JS engine (e.g., extracted via + * JSAutoStructuredCloneBuffer::steal). */ - void adopt(JSContext *cx, uint64 *data, size_t nbytes, - uint32 version=JS_STRUCTURED_CLONE_VERSION) { - clear(cx); - cx_ = cx; - data_ = data; - nbytes_ = nbytes; - version_ = version; - } + void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); /* * Remove the buffer so that it will not be automatically freed. - * After this, the caller is responsible for calling JS_free(*datap). + * After this, the caller is responsible for feeding the memory back to + * JSAutoStructuredCloneBuffer::adopt. */ - void steal(uint64 **datap, size_t *nbytesp, JSContext **cxp=NULL, - uint32 *versionp=NULL) { - *datap = data_; - *nbytesp = nbytes_; - if (cxp) - *cxp = cx_; - if (versionp) - *versionp = version_; - - cx_ = NULL; - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } - - bool read(jsval *vp, JSContext *cx=NULL, + void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); + + bool read(JSContext *cx, jsval *vp, const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL) const { - if (!cx) - cx = cx_; - JS_ASSERT(cx); - JS_ASSERT(data_); - return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, - optionalCallbacks, closure); - } + void *closure=NULL) const; bool write(JSContext *cx, jsval v, const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL) { - clear(cx); - cx_ = cx; - bool ok = !!JS_WriteStructuredClone(cx, v, &data_, &nbytes_, - optionalCallbacks, closure); - if (!ok) { - data_ = NULL; - nbytes_ = 0; - version_ = JS_STRUCTURED_CLONE_VERSION; - } - return ok; - } + void *closure=NULL); /** * Swap ownership with another JSAutoStructuredCloneBuffer. */ - void swap(JSAutoStructuredCloneBuffer &other) { - JSContext *cx = other.cx_; - uint64 *data = other.data_; - size_t nbytes = other.nbytes_; - uint32 version = other.version_; - - other.cx_ = this->cx_; - other.data_ = this->data_; - other.nbytes_ = this->nbytes_; - other.version_ = this->version_; - - this->cx_ = cx; - this->data_ = data; - this->nbytes_ = nbytes; - this->version_ = version; - } + void swap(JSAutoStructuredCloneBuffer &other); private: /* Copy and assignment are not supported. */ JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); }; + +JS_BEGIN_EXTERN_C #endif /* API for implementing custom serialization behavior (for ImageData, File, etc.) */ /* The range of tag values the application may use for its own custom object types. */ -#define JS_SCTAG_USER_MIN ((uint32) 0xFFFF8000) -#define JS_SCTAG_USER_MAX ((uint32) 0xFFFFFFFF) +#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) +#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) #define JS_SCERR_RECURSION 0 @@ -3491,13 +4974,13 @@ JS_PUBLIC_API(void) JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); JS_PUBLIC_API(JSBool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2); +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); JS_PUBLIC_API(JSBool) JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); JS_PUBLIC_API(JSBool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32 tag, uint32 data); +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); JS_PUBLIC_API(JSBool) JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); @@ -3538,8 +5021,7 @@ JS_GetLocaleCallbacks(JSContext *cx); /* * Report an exception represented by the sprintf-like conversion of format * and its arguments. This exception message string is passed to a pre-set - * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for - * the JSErrorReporter typedef). + * JSErrorReporter function (set by JS_SetErrorReporter). */ extern JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...); @@ -3591,6 +5073,7 @@ JS_ReportAllocationOverflow(JSContext *cx); struct JSErrorReport { const char *filename; /* source file name, URL, etc., or null */ + JSPrincipals *originPrincipals; /* see 'originPrincipals' comment above */ uintN lineno; /* source line number */ const char *linebuf; /* offending source line without final \n */ const char *tokenptr; /* pointer to error token in linebuf */ @@ -3631,6 +5114,8 @@ struct JSErrorReport { #define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) #define JSREPORT_IS_STRICT_MODE_ERROR(flags) (((flags) & \ JSREPORT_STRICT_MODE_ERROR) != 0) +extern JS_PUBLIC_API(JSErrorReporter) +JS_GetErrorReporter(JSContext *cx); extern JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); @@ -3662,8 +5147,6 @@ JS_ObjectIsDate(JSContext *cx, JSObject *obj); #define JSREG_GLOB 0x02 /* global exec, creates array of matches */ #define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ #define JSREG_STICKY 0x08 /* only match starting at lastIndex */ -#define JSREG_FLAT 0x10 /* parse as a flat regexp */ -#define JSREG_NOCOMPILE 0x20 /* do not try to compile to native code */ extern JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uintN flags); @@ -3693,6 +5176,15 @@ extern JS_PUBLIC_API(JSBool) JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *reobj, jschar *chars, size_t length, size_t *indexp, JSBool test, jsval *rval); +extern JS_PUBLIC_API(JSBool) +JS_ObjectIsRegExp(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(uintN) +JS_GetRegExpFlags(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSString *) +JS_GetRegExpSource(JSContext *cx, JSObject *obj); + /************************************************************************/ extern JS_PUBLIC_API(JSBool) @@ -3754,23 +5246,53 @@ JS_ThrowReportedError(JSContext *cx, const char *message, extern JS_PUBLIC_API(JSBool) JS_ThrowStopIteration(JSContext *cx); +extern JS_PUBLIC_API(intptr_t) +JS_GetCurrentThread(); + /* - * Associate the current thread with the given context. This is done - * implicitly by JS_NewContext. + * A JS runtime always has an "owner thread". The owner thread is set when the + * runtime is created (to the current thread) and practically all entry points + * into the JS engine check that a runtime (or anything contained in the + * runtime: context, compartment, object, etc) is only touched by its owner + * thread. Embeddings may check this invariant outside the JS engine by calling + * JS_AbortIfWrongThread (which will abort if not on the owner thread, even for + * non-debug builds). * - * Returns the old thread id for this context, which should be treated as - * an opaque value. This value is provided for comparison to 0, which - * indicates that ClearContextThread has been called on this context - * since the last SetContextThread, or non-0, which indicates the opposite. + * It is possible to "move" a runtime between threads. This is accomplished by + * calling JS_ClearRuntimeThread on a runtime's owner thread and then calling + * JS_SetRuntimeThread on the new owner thread. The runtime must not be + * accessed between JS_ClearRuntimeThread and JS_SetRuntimeThread. Also, the + * caller is responsible for synchronizing the calls to Set/Clear. */ -extern JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx); -extern JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx); +extern JS_PUBLIC_API(void) +JS_AbortIfWrongThread(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_ClearRuntimeThread(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_SetRuntimeThread(JSRuntime *rt); + +#ifdef __cplusplus +JS_END_EXTERN_C + +class JSAutoSetRuntimeThread +{ + JSRuntime *runtime; + + public: + JSAutoSetRuntimeThread(JSRuntime *runtime) : runtime(runtime) { + JS_SetRuntimeThread(runtime); + } + + ~JSAutoSetRuntimeThread() { + JS_ClearRuntimeThread(runtime); + } +}; -extern JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx); +JS_BEGIN_EXTERN_C +#endif /************************************************************************/ @@ -3784,65 +5306,17 @@ JS_ClearContextThread(JSContext *cx); static JS_ALWAYS_INLINE JSBool JS_IsConstructing(JSContext *cx, const jsval *vp) { - jsval_layout l; - -#ifdef DEBUG - JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - if (JS_ObjectIsFunction(cx, callee)) { - JSFunction *fun = JS_ValueToFunction(cx, JS_CALLEE(cx, vp)); - JS_ASSERT((JS_GetFunctionFlags(fun) & JSFUN_CONSTRUCTOR) != 0); - } else { - JS_ASSERT(JS_GET_CLASS(cx, callee)->construct != NULL); - } -#endif - - l.asBits = JSVAL_BITS(vp[1]); - return JSVAL_IS_MAGIC_IMPL(l); -} - -/* - * In the case of a constructor called from JS_ConstructObject and - * JS_InitClass where the class has the JSCLASS_CONSTRUCT_PROTOTYPE flag set, - * the JS engine passes the constructor a non-standard 'this' object. In such - * cases, the following query provides the additional information of whether a - * special 'this' was supplied. E.g.: - * - * JSBool foo_native(JSContext *cx, uintN argc, jsval *vp) { - * JSObject *maybeThis; - * if (JS_IsConstructing_PossiblyWithGivenThisObject(cx, vp, &maybeThis)) { - * // native called as a constructor - * if (maybeThis) - * // native called as a constructor with maybeThis as 'this' - * } else { - * // native called as function, maybeThis is still uninitialized - * } - * } - * - * Note that embeddings do not need to use this query unless they use the - * aforementioned API/flags. - */ -static JS_ALWAYS_INLINE JSBool -JS_IsConstructing_PossiblyWithGivenThisObject(JSContext *cx, const jsval *vp, - JSObject **maybeThis) -{ - jsval_layout l; - JSBool isCtor; - #ifdef DEBUG JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); if (JS_ObjectIsFunction(cx, callee)) { JSFunction *fun = JS_ValueToFunction(cx, JS_CALLEE(cx, vp)); JS_ASSERT((JS_GetFunctionFlags(fun) & JSFUN_CONSTRUCTOR) != 0); } else { - JS_ASSERT(JS_GET_CLASS(cx, callee)->construct != NULL); + JS_ASSERT(JS_GetClass(callee)->construct != NULL); } #endif - l.asBits = JSVAL_BITS(vp[1]); - isCtor = JSVAL_IS_MAGIC_IMPL(l); - if (isCtor) - *maybeThis = MAGIC_JSVAL_TO_OBJECT_OR_NULL_IMPL(l); - return isCtor; + return JSVAL_IS_MAGIC_IMPL(JSVAL_TO_IMPL(vp[1])); } /* @@ -3860,10 +5334,27 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp); #endif #ifdef JS_GC_ZEAL +#define JS_DEFAULT_ZEAL_FREQ 100 + +extern JS_PUBLIC_API(void) +JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment); + extern JS_PUBLIC_API(void) -JS_SetGCZeal(JSContext *cx, uint8 zeal); +JS_ScheduleGC(JSContext *cx, uint32_t count, JSBool compartment); #endif +/* + * Convert a uint32_t index into a jsid. + */ +extern JS_PUBLIC_API(JSBool) +JS_IndexToId(JSContext *cx, uint32_t index, jsid *id); + +/* + * Test if the given string is a valid ECMAScript identifier + */ +extern JS_PUBLIC_API(JSBool) +JS_IsIdentifier(JSContext *cx, JSString *str, JSBool *isIdentifier); + JS_END_EXTERN_C #endif /* jsapi_h___ */ diff --git a/deps/mozjs/js/src/jsarena.cpp b/deps/mozjs/js/src/jsarena.cpp deleted file mode 100644 index bfe78e229ed..00000000000 --- a/deps/mozjs/js/src/jsarena.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - */ -#include -#include -#include "jsalloc.h" -#include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" -#include "jsarena.h" -#include "jsprvtd.h" - -using namespace js; - -#ifdef JS_ARENAMETER -static JSArenaStats *arena_stats_list; - -#define COUNT(pool,what) (pool)->stats.what++ -#else -#define COUNT(pool,what) /* nothing */ -#endif - -#define JS_ARENA_DEFAULT_ALIGN sizeof(double) - -JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, - size_t align, size_t *quotap) -{ - if (align == 0) - align = JS_ARENA_DEFAULT_ALIGN; - pool->mask = JS_BITMASK(JS_CeilingLog2(align)); - pool->first.next = NULL; - pool->first.base = pool->first.avail = pool->first.limit = - JS_ARENA_ALIGN(pool, &pool->first + 1); - pool->current = &pool->first; - pool->arenasize = size; - pool->quotap = quotap; -#ifdef JS_ARENAMETER - memset(&pool->stats, 0, sizeof pool->stats); - pool->stats.name = strdup(name); - pool->stats.next = arena_stats_list; - arena_stats_list = &pool->stats; -#endif -} - -/* - * An allocation that consumes more than pool->arenasize also has a header - * pointing back to its previous arena's next member. This header is not - * included in [a->base, a->limit), so its space can't be wrongly claimed. - * - * As the header is a pointer, it must be well-aligned. If pool->mask is - * greater than or equal to POINTER_MASK, the header just preceding a->base - * for an oversized arena a is well-aligned, because a->base is well-aligned. - * However, we may need to add more space to pad the JSArena ** back-pointer - * so that it lies just behind a->base, because a might not be aligned such - * that (jsuword)(a + 1) is on a pointer boundary. - * - * By how much must we pad? Let M be the alignment modulus for pool and P - * the modulus for a pointer. Given M >= P, the base of an oversized arena - * that satisfies M is well-aligned for P. - * - * On the other hand, if M < P, we must include enough space in the header - * size to align the back-pointer on a P boundary so that it can be found by - * subtracting P from a->base. This means a->base must be on a P boundary, - * even though subsequent allocations from a may be aligned on a lesser (M) - * boundary. Given powers of two M and P as above, the extra space needed - * when M < P is P-M or POINTER_MASK - pool->mask. - * - * The size of a header including padding is given by the HEADER_SIZE macro, - * below, for any pool (for any value of M). - * - * The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or - * HEADER_BASE_MASK(pool). - * - * PTR_TO_HEADER computes the address of the back-pointer, given an oversized - * allocation at p. By definition, p must be a->base for the arena a that - * contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in - * the case of SET_HEADER with back-pointer ap. - */ -#define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1)) -#define HEADER_SIZE(pool) (sizeof(JSArena **) \ - + (((pool)->mask < POINTER_MASK) \ - ? POINTER_MASK - (pool)->mask \ - : 0)) -#define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) -#define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ - & HEADER_BASE_MASK(pool)) \ - == 0), \ - (JSArena ***)(p) - 1) -#define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base)) -#define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap)) - -JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb) -{ - JSArena **ap, *a, *b; - jsuword extra, hdrsz, gross; - void *p; - - /* - * Search pool from current forward till we find or make enough space. - * - * NB: subtract nb from a->limit in the loop condition, instead of adding - * nb to a->avail, to avoid overflowing a 32-bit address space (possible - * when running a 32-bit program on a 64-bit system where the kernel maps - * the heap up against the top of the 32-bit address space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ - JS_ASSERT((nb & pool->mask) == 0); - for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; - pool->current = a) { - ap = &a->next; - if (!*ap) { - /* Not enough space in pool, so we must malloc. */ - extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; - hdrsz = sizeof *a + extra + pool->mask; - gross = hdrsz + JS_MAX(nb, pool->arenasize); - if (gross < nb) - return NULL; - if (pool->quotap) { - if (gross > *pool->quotap) - return NULL; - b = (JSArena *) OffTheBooks::malloc_(gross); - if (!b) - return NULL; - *pool->quotap -= gross; - } else { - b = (JSArena *) OffTheBooks::malloc_(gross); - if (!b) - return NULL; - } - - b->next = NULL; - b->limit = (jsuword)b + gross; - JS_COUNT_ARENA(pool,++); - COUNT(pool, nmallocs); - - /* If oversized, store ap in the header, just before a->base. */ - *ap = a = b; - JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); - if (extra) { - a->base = a->avail = - ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - SET_HEADER(pool, a, ap); - } else { - a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1); - } - continue; - } - a = *ap; /* move to next arena */ - } - - p = (void *)a->avail; - a->avail += nb; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - return p; -} - -JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - JSArena **ap, *a, *b; - jsuword boff, aoff, extra, hdrsz, gross, growth; - - /* - * Use the oversized-single-allocation header to avoid searching for ap. - * See JS_ArenaAllocate, the SET_HEADER call. - */ - if (size > pool->arenasize) { - ap = *PTR_TO_HEADER(pool, p); - a = *ap; - } else { - ap = &pool->first.next; - while ((a = *ap) != pool->current) - ap = &a->next; - } - - JS_ASSERT(a->base == (jsuword)p); - boff = JS_UPTRDIFF(a->base, a); - aoff = JS_ARENA_ALIGN(pool, size + incr); - JS_ASSERT(aoff > pool->arenasize); - extra = HEADER_SIZE(pool); /* oversized header holds ap */ - hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ - gross = hdrsz + aoff; - JS_ASSERT(gross > aoff); - if (pool->quotap) { - growth = gross - (a->limit - (jsuword) a); - if (growth > *pool->quotap) - return NULL; - a = (JSArena *) OffTheBooks::realloc_(a, gross); - if (!a) - return NULL; - *pool->quotap -= growth; - } else { - a = (JSArena *) OffTheBooks::realloc_(a, gross); - if (!a) - return NULL; - } -#ifdef JS_ARENAMETER - pool->stats.nreallocs++; -#endif - - if (a != *ap) { - /* Oops, realloc moved the allocation: update other pointers to a. */ - if (pool->current == *ap) - pool->current = a; - b = a->next; - if (b && b->avail - b->base > pool->arenasize) { - JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next); - SET_HEADER(pool, b, &a->next); - } - - /* Now update *ap, the next link of the arena before a. */ - *ap = a; - } - - a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - a->limit = (jsuword)a + gross; - a->avail = a->base + aoff; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - /* Check whether realloc aligned differently, and copy if necessary. */ - if (boff != JS_UPTRDIFF(a->base, a)) - memmove((void *)a->base, (char *)a + boff, size); - - /* Store ap in the oversized-load arena header. */ - SET_HEADER(pool, a, ap); - return (void *)a->base; -} - -JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - void *newp; - - /* - * If p points to an oversized allocation, it owns an entire arena, so we - * can simply realloc the arena. - */ - if (size > pool->arenasize) - return JS_ArenaRealloc(pool, p, size, incr); - - JS_ARENA_ALLOCATE(newp, pool, size + incr); - if (newp) - memcpy(newp, p, size); - return newp; -} - -/* - * Free tail arenas linked after head, which may not be the true list head. - * Reset pool->current to point to head in case it pointed at a tail arena. - */ -static void -FreeArenaList(JSArenaPool *pool, JSArena *head) -{ - JSArena **ap, *a; - - ap = &head->next; - a = *ap; - if (!a) - return; - -#ifdef DEBUG - do { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - a->avail = a->base; - JS_CLEAR_UNUSED(a); - } while ((a = a->next) != NULL); - a = *ap; -#endif - - do { - *ap = a->next; - if (pool->quotap) - *pool->quotap += a->limit - (jsuword) a; - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - UnwantedForeground::free_(a); - } while ((a = *ap) != NULL); - - pool->current = head; -} - -JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark) -{ - JSArena *a; - - for (a = &pool->first; a; a = a->next) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (JS_ARENA_MARK_MATCH(a, mark)) { - a->avail = JS_ARENA_ALIGN(pool, mark); - JS_ASSERT(a->avail <= a->limit); - FreeArenaList(pool, a); - return; - } - } -} - -JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); - COUNT(pool, ndeallocs); -} - -JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); -#ifdef JS_ARENAMETER - { - JSArenaStats *stats, **statsp; - - if (pool->stats.name) { - UnwantedForeground::free_(pool->stats.name); - pool->stats.name = NULL; - } - for (statsp = &arena_stats_list; (stats = *statsp) != 0; - statsp = &stats->next) { - if (stats == &pool->stats) { - *statsp = stats->next; - return; - } - } - } -#endif -} - -JS_PUBLIC_API(void) -JS_ArenaFinish() -{ -} - -JS_PUBLIC_API(void) -JS_ArenaShutDown(void) -{ -} - -#ifdef JS_ARENAMETER -JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb) -{ - pool->stats.nallocs++; - pool->stats.nbytes += nb; - if (nb > pool->stats.maxalloc) - pool->stats.maxalloc = nb; - pool->stats.variance += nb * nb; -} - -JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ninplace++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ngrows++; - pool->stats.nbytes += incr; - pool->stats.variance -= size * size; - size += incr; - if (size > pool->stats.maxalloc) - pool->stats.maxalloc = size; - pool->stats.variance += size * size; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark) -{ - pool->stats.nreleases++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark) -{ - pool->stats.nfastrels++; -} - -#include - -JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp) -{ - JSArenaStats *stats; - double mean, sigma; - - for (stats = arena_stats_list; stats; stats = stats->next) { - mean = JS_MeanAndStdDev(stats->nallocs, stats->nbytes, stats->variance, - &sigma); - - fprintf(fp, "\n%s allocation statistics:\n", stats->name); - fprintf(fp, " number of arenas: %u\n", stats->narenas); - fprintf(fp, " number of allocations: %u\n", stats->nallocs); - fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs); - fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs); - fprintf(fp, " number of allocation growths: %u\n", stats->ngrows); - fprintf(fp, " number of in-place growths: %u\n", stats->ninplace); - fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs); - fprintf(fp, "number of released allocations: %u\n", stats->nreleases); - fprintf(fp, " number of fast releases: %u\n", stats->nfastrels); - fprintf(fp, " total bytes allocated: %u\n", stats->nbytes); - fprintf(fp, " mean allocation size: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc); - } -} -#endif /* JS_ARENAMETER */ diff --git a/deps/mozjs/js/src/jsarena.h b/deps/mozjs/js/src/jsarena.h deleted file mode 100644 index 184a43d29c6..00000000000 --- a/deps/mozjs/js/src/jsarena.h +++ /dev/null @@ -1,291 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsarena_h___ -#define jsarena_h___ -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - * - * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). - */ -#include -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -typedef struct JSArena JSArena; -typedef struct JSArenaPool JSArenaPool; - -struct JSArena { - JSArena *next; /* next arena for this lifetime */ - jsuword base; /* aligned base address, follows this header */ - jsuword limit; /* one beyond last byte in arena */ - jsuword avail; /* points to next available byte */ -}; - -#ifdef JS_ARENAMETER -typedef struct JSArenaStats JSArenaStats; - -struct JSArenaStats { - JSArenaStats *next; /* next in arenaStats list */ - char *name; /* name for debugging */ - uint32 narenas; /* number of arenas in pool */ - uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ - uint32 nmallocs; /* number of malloc() calls */ - uint32 ndeallocs; /* number of lifetime deallocations */ - uint32 ngrows; /* number of JS_ARENA_GROW() calls */ - uint32 ninplace; /* number of in-place growths */ - uint32 nreallocs; /* number of arena grow extending reallocs */ - uint32 nreleases; /* number of JS_ARENA_RELEASE() calls */ - uint32 nfastrels; /* number of "fast path" releases */ - size_t nbytes; /* total bytes allocated */ - size_t maxalloc; /* maximum allocation size in bytes */ - double variance; /* size variance accumulator */ -}; -#endif - -struct JSArenaPool { - JSArena first; /* first arena in pool list */ - JSArena *current; /* arena from which to allocate space */ - size_t arenasize; /* net exact size of a new arena */ - jsuword mask; /* alignment mask (power-of-2 - 1) */ - size_t *quotap; /* pointer to the quota on pool allocation - size or null if pool is unlimited */ -#ifdef JS_ARENAMETER - JSArenaStats stats; -#endif -}; - -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) - -#define JS_ARENA_ALLOCATE(p, pool, nb) \ - JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) - -#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ - JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) - -#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ - JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) - -/* - * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb - * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit - * address space (possible when running a 32-bit program on a 64-bit system - * where the kernel maps the heap up against the top of the 32-bit address - * space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ -#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - size_t _nb = JS_ARENA_ALIGN(pool, nb); \ - jsuword _p = _a->avail; \ - if ((guard) || _p > _a->limit - _nb) \ - _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ - else \ - _a->avail = _p + _nb; \ - p = (type) _p; \ - STATIC_ASSUME(!p || ubound((char *)p) >= nb) \ - JS_ArenaCountAllocation(pool, nb); \ - JS_END_MACRO - -#define JS_ARENA_GROW(p, pool, size, incr) \ - JS_ARENA_GROW_CAST(p, void *, pool, size, incr) - -#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ - size_t _nb = (size) + (incr); \ - _nb = JS_ARENA_ALIGN(pool, _nb); \ - if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ - _a->avail = (jsuword)(p) + _nb; \ - JS_ArenaCountInplaceGrowth(pool, size, incr); \ - } else if ((jsuword)(p) == _a->base) { \ - p = (type) JS_ArenaRealloc(pool, p, size, incr); \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - STATIC_ASSUME(!p || ubound((char *)p) >= size + incr); \ - JS_ArenaCountGrowth(pool, size, incr); \ - JS_END_MACRO - -#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) -#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) - -/* - * Check if the mark is inside arena's allocated area. - */ -#define JS_ARENA_MARK_MATCH(a, mark) \ - (JS_UPTRDIFF(mark, (a)->base) <= JS_UPTRDIFF((a)->avail, (a)->base)) - -#ifdef DEBUG -#define JS_FREE_PATTERN 0xDA -#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ - memset((void*)(a)->avail, JS_FREE_PATTERN, \ - (a)->limit - (a)->avail)) -#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ - (a)->limit - (jsuword)(a)) -#else -#define JS_CLEAR_UNUSED(a) /* nothing */ -#define JS_CLEAR_ARENA(a) /* nothing */ -#endif - -#define JS_ARENA_RELEASE(pool, mark) \ - JS_BEGIN_MACRO \ - char *_m = (char *)(mark); \ - JSArena *_a = (pool)->current; \ - if (_a != &(pool)->first && JS_ARENA_MARK_MATCH(_a, _m)) { \ - _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ - JS_ASSERT(_a->avail <= _a->limit); \ - JS_CLEAR_UNUSED(_a); \ - JS_ArenaCountRetract(pool, _m); \ - } else { \ - JS_ArenaRelease(pool, _m); \ - } \ - JS_ArenaCountRelease(pool, _m); \ - JS_END_MACRO - -#ifdef JS_ARENAMETER -#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) -#else -#define JS_COUNT_ARENA(pool,op) -#endif - -#define JS_ARENA_DESTROY(pool, a, pnext) \ - JS_BEGIN_MACRO \ - JS_COUNT_ARENA(pool,--); \ - if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ - *(pnext) = (a)->next; \ - JS_CLEAR_ARENA(a); \ - js::UnwantedForeground::free_(a); \ - (a) = NULL; \ - JS_END_MACRO - -/* - * Initialize an arena pool with a minimum size per arena of size bytes. - */ -extern JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, - size_t align, size_t *quotap); - -/* - * Free the arenas in pool. The user may continue to allocate from pool - * after calling this function. There is no need to call JS_InitArenaPool() - * again unless JS_FinishArenaPool(pool) has been called. - */ -extern JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool); - -/* - * Free the arenas in pool and finish using it altogether. - */ -extern JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFinish(void); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaShutDown(void); - -/* - * Friend functions used by the JS_ARENA_*() macros. - */ -extern JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark); - -#ifdef JS_ARENAMETER - -#include - -extern JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp); - -#else /* !JS_ARENAMETER */ - -#define JS_ArenaCountAllocation(ap, nb) /* nothing */ -#define JS_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountRelease(ap, mark) /* nothing */ -#define JS_ArenaCountRetract(ap, mark) /* nothing */ - -#endif /* !JS_ARENAMETER */ - -JS_END_EXTERN_C - -#endif /* jsarena_h___ */ diff --git a/deps/mozjs/js/src/jsarray.cpp b/deps/mozjs/js/src/jsarray.cpp index ebcf193074e..dc1e0bbbc03 100644 --- a/deps/mozjs/js/src/jsarray.cpp +++ b/deps/mozjs/js/src/jsarray.cpp @@ -44,28 +44,47 @@ * Array objects begin as "dense" arrays, optimized for index-only property * access over a vector of slots with high load factor. Array methods * optimize for denseness by testing that the object's class is - * &js_ArrayClass, and can then directly manipulate the slots for efficiency. + * &ArrayClass, and can then directly manipulate the slots for efficiency. * * We track these pieces of metadata for arrays in dense mode: * - The array's length property as a uint32, accessible with * getArrayLength(), setArrayLength(). * - The number of element slots (capacity), gettable with * getDenseArrayCapacity(). + * - The array's initialized length, accessible with + * getDenseArrayInitializedLength(). * * In dense mode, holes in the array are represented by * MagicValue(JS_ARRAY_HOLE) invalid values. * * NB: the capacity and length of a dense array are entirely unrelated! The * length may be greater than, less than, or equal to the capacity. The first - * case may occur when the user writes "new Array(100), in which case the + * case may occur when the user writes "new Array(100)", in which case the * length is 100 while the capacity remains 0 (indices below length and above - * capaicty must be treated as holes). See array_length_setter for another + * capacity must be treated as holes). See array_length_setter for another * explanation of how the first case may occur. * - * Arrays are converted to use js_SlowArrayClass when any of these conditions + * The initialized length of a dense array specifies the number of elements + * that have been initialized. All elements above the initialized length are + * holes in the array, and the memory for all elements between the initialized + * length and capacity is left uninitialized. When type inference is disabled, + * the initialized length always equals the array's capacity. When inference is + * enabled, the initialized length is some value less than or equal to both the + * array's length and the array's capacity. + * + * With inference enabled, there is flexibility in exactly the value the + * initialized length must hold, e.g. if an array has length 5, capacity 10, + * completely empty, it is valid for the initialized length to be any value + * between zero and 5, as long as the in memory values below the initialized + * length have been initialized with a hole value. However, in such cases we + * want to keep the initialized length as small as possible: if the array is + * known to have no hole values below its initialized length, then it is a + * "packed" array and can be accessed much faster by JIT code. + * + * Arrays are converted to use SlowArrayClass when any of these conditions * are met: - * - there are more than MIN_SPARSE_INDEX slots total - * - the load factor (COUNT / capacity) is less than 0.25 + * - there are more than MIN_SPARSE_INDEX slots total and the load factor + * (COUNT / capacity) is less than 0.25 * - a property is set that is not indexed (and not "length") * - a property is defined that has non-default property attributes. * @@ -74,20 +93,22 @@ * properties in the order they were created. We could instead maintain the * scope to track property enumeration order, but still use the fast slot * access. That would have the same memory cost as just using a - * js_SlowArrayClass, but have the same performance characteristics as a dense + * SlowArrayClass, but have the same performance characteristics as a dense * array for slot accesses, at some cost in code complexity. */ +#include #include #include + +#include "mozilla/RangedPtr.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" + #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" -#include "jsbit.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" #include "jsfun.h" @@ -100,33 +121,62 @@ #include "jsobj.h" #include "jsscope.h" #include "jsstr.h" -#include "jsstaticcheck.h" -#include "jstracer.h" -#include "jsvector.h" #include "jswrapper.h" +#include "methodjit/MethodJIT.h" +#include "methodjit/StubCalls.h" +#include "methodjit/StubCalls-inl.h" + +#include "vm/ArgumentsObject.h" +#include "ds/Sort.h" + +#include "jsarrayinlines.h" #include "jsatominlines.h" #include "jscntxtinlines.h" -#include "jsinterpinlines.h" #include "jsobjinlines.h" +#include "jsscopeinlines.h" +#include "jscntxtinlines.h" #include "jsstrinlines.h" +#include "vm/ArgumentsObject-inl.h" #include "vm/Stack-inl.h" +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; -/* 2^32 - 1 as a number and a string */ -#define MAXINDEX 4294967295u -#define MAXSTR "4294967295" - -static inline bool -ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj) +JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { - return obj->getClass() == &js_SlowArrayClass || - obj->makeDenseArraySlow(cx); + if (obj->isArray()) { + *lengthp = obj->getArrayLength(); + return true; + } + + if (obj->isArguments()) { + ArgumentsObject &argsobj = obj->asArguments(); + if (!argsobj.hasOverriddenLength()) { + *lengthp = argsobj.initialLength(); + return true; + } + } + + AutoValueRooter tvr(cx); + if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr())) + return false; + + if (tvr.value().isInt32()) { + *lengthp = jsuint(jsint(tvr.value().toInt32())); /* jsuint cast does ToUint32_t */ + return true; + } + + JS_STATIC_ASSERT(sizeof(jsuint) == sizeof(uint32_t)); + return ToUint32(cx, tvr.value(), (uint32_t *)lengthp); } +namespace js { + /* * Determine if the id represents an array index or an XML property index. * @@ -137,131 +187,63 @@ ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj) * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal * to 2^32-1." * + * This means the largest allowed index is actually 2^32-2 (4294967294). + * * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) * except that by using signed 31-bit integers we miss the top half of the * valid range. This function checks the string representation itself; note * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. * - * 'id' is passed as a jsboxedword since the given id need not necessarily hold - * an atomized string. */ -bool -js_StringIsIndex(JSLinearString *str, jsuint *indexp) -{ - const jschar *cp = str->chars(); - if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10*index + c; - cp++; - } - } - - /* Ensure that all characters were consumed and we didn't overflow. */ - if (*cp == 0 && - (oldIndex < (MAXINDEX / 10) || - (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) - { - *indexp = index; - return true; - } - } - return false; -} - -static bool -ValueToLength(JSContext *cx, Value* vp, jsuint* plength) +JS_FRIEND_API(bool) +StringIsArrayIndex(JSLinearString *str, jsuint *indexp) { - if (vp->isInt32()) { - int32_t i = vp->toInt32(); - if (i < 0) - goto error; - - *plength = (jsuint)(i); - return true; - } - - jsdouble d; - if (!ValueToNumber(cx, *vp, &d)) - goto error; - - if (JSDOUBLE_IS_NaN(d)) - goto error; - - jsuint length; - length = (jsuint) d; - if (d != (jsdouble) length) - goto error; + const jschar *s = str->chars(); + uint32_t length = str->length(); + const jschar *end = s + length; + if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s)) + return false; - *plength = length; - return true; + uint32_t c = 0, previous = 0; + uint32_t index = JS7_UNDEC(*s++); -error: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return false; -} + /* Don't allow leading zeros. */ + if (index == 0 && s != end) + return false; -JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - if (obj->isArray()) { - *lengthp = obj->getArrayLength(); - return true; - } + for (; s < end; s++) { + if (!JS7_ISDEC(*s)) + return false; - if (obj->isArguments() && !obj->isArgsLengthOverridden()) { - *lengthp = obj->getArgsInitialLength(); - return true; + previous = index; + c = JS7_UNDEC(*s); + index = 10 * index + c; } - AutoValueRooter tvr(cx); - if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr())) - return false; - - if (tvr.value().isInt32()) { - *lengthp = jsuint(jsint(tvr.value().toInt32())); /* jsuint cast does ToUint32 */ + /* Make sure we didn't overflow. */ + if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) && + c <= (MAX_ARRAY_INDEX % 10))) { + JS_ASSERT(index <= MAX_ARRAY_INDEX); + *indexp = index; return true; } - JS_STATIC_ASSERT(sizeof(jsuint) == sizeof(uint32_t)); - return ValueToECMAUint32(cx, tvr.value(), (uint32_t *)lengthp); + return false; } -JSBool JS_FASTCALL -js_IndexToId(JSContext *cx, jsuint index, jsid *idp) -{ - JSString *str; - - if (index <= JSID_INT_MAX) { - *idp = INT_TO_JSID(index); - return JS_TRUE; - } - str = js_NumberToString(cx, index); - if (!str) - return JS_FALSE; - return js_ValueToStringId(cx, StringValue(str), idp); } static JSBool BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, jsid *idp) { - jschar buf[10], *start; - Class *clasp; - JSAtom *atom; JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); - JS_ASSERT(index > JSID_INT_MAX); - start = JS_ARRAY_END(buf); + jschar buf[10]; + jschar *start = ArrayEnd(buf); do { --start; *start = (jschar)('0' + index % 10); @@ -272,21 +254,18 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, * Skip the atomization if the class is known to store atoms corresponding * to big indexes together with elements. In such case we know that the * array does not have an element at the given index if its atom does not - * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for - * any indexes, though it would be rare to see them have a big index - * in any case. + * exist. Dense arrays don't use atoms for any indexes, though it would be + * rare to see them have a big index in any case. */ - if (!createAtom && - ((clasp = obj->getClass()) == &js_SlowArrayClass || - clasp == &js_ArgumentsClass || - clasp == &js_ObjectClass)) { - atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); + JSAtom *atom; + if (!createAtom && (obj->isSlowArray() || obj->isArguments() || obj->isObject())) { + atom = js_GetExistingStringAtom(cx, start, ArrayEnd(buf) - start); if (!atom) { *idp = JSID_VOID; return JS_TRUE; } } else { - atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); + atom = js_AtomizeChars(cx, start, ArrayEnd(buf) - start); if (!atom) return JS_FALSE; } @@ -301,10 +280,10 @@ JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint) JS_ASSERT(isDenseArray()); JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX); - uintN cap = numSlots(); + uintN cap = getDenseArrayCapacity(); JS_ASSERT(requiredCapacity >= cap); - if (requiredCapacity >= JSObject::NSLOTS_LIMIT) + if (requiredCapacity >= JSObject::NELEMENTS_LIMIT) return true; uintN minimalDenseCount = requiredCapacity / 4; @@ -315,8 +294,9 @@ JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint) if (minimalDenseCount > cap) return true; - Value *elems = getDenseArrayElements(); - for (uintN i = 0; i < cap; i++) { + uintN len = getDenseArrayInitializedLength(); + const Value *elems = getDenseArrayElements(); + for (uintN i = 0; i < len; i++) { if (!elems[i].isMagic(JS_ARRAY_HOLE) && !--minimalDenseCount) return false; } @@ -349,33 +329,41 @@ IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp, return ReallyBigIndexToId(cx, index, idp); } +bool +JSObject::arrayGetOwnDataElement(JSContext *cx, size_t i, Value *vp) +{ + JS_ASSERT(isArray()); + + if (isDenseArray()) { + if (i >= getArrayLength()) + vp->setMagic(JS_ARRAY_HOLE); + else + *vp = getDenseArrayElement(uint32_t(i)); + return true; + } + + JSBool hole; + jsid id; + if (!IndexToId(cx, this, i, &hole, &id)) + return false; + + const Shape *shape = nativeLookup(cx, id); + if (!shape || !shape->isDataDescriptor()) + vp->setMagic(JS_ARRAY_HOLE); + else + *vp = getSlot(shape->slot()); + return true; +} + /* * If the property at the given index exists, get its value into location * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp * to JSVAL_VOID. This function assumes that the location pointed by vp is * properly rooted and can be used as GC-protected storage for temporaries. */ -static JSBool -GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp) +static inline JSBool +DoGetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp) { - JS_ASSERT(index >= 0); - if (obj->isDenseArray() && index < obj->getDenseArrayCapacity() && - !(*vp = obj->getDenseArrayElement(uint32(index))).isMagic(JS_ARRAY_HOLE)) { - *hole = JS_FALSE; - return JS_TRUE; - } - if (obj->isArguments() && - index < obj->getArgsInitialLength() && - !(*vp = obj->getArgsElement(uint32(index))).isMagic(JS_ARGS_HOLE)) { - *hole = JS_FALSE; - StackFrame *fp = (StackFrame *)obj->getPrivate(); - if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) { - if (fp) - *vp = fp->canonicalActualArg(index); - return JS_TRUE; - } - } - AutoIdRooter idr(cx); *hole = JS_FALSE; @@ -388,76 +376,89 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp JSObject *obj2; JSProperty *prop; - if (!obj->lookupProperty(cx, idr.id(), &obj2, &prop)) + if (!obj->lookupGeneric(cx, idr.id(), &obj2, &prop)) return JS_FALSE; if (!prop) { - *hole = JS_TRUE; vp->setUndefined(); + *hole = JS_TRUE; } else { - if (!obj->getProperty(cx, idr.id(), vp)) + if (!obj->getGeneric(cx, idr.id(), vp)) return JS_FALSE; *hole = JS_FALSE; } return JS_TRUE; } +static inline JSBool +DoGetElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *hole, Value *vp) +{ + bool present; + if (!obj->getElementIfPresent(cx, obj, index, vp, &present)) + return false; + + *hole = !present; + if (*hole) + vp->setUndefined(); + + return true; +} + +template +static JSBool +GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp) +{ + JS_ASSERT(index >= 0); + if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() && + !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) { + *hole = JS_FALSE; + return JS_TRUE; + } + if (obj->isArguments()) { + if (obj->asArguments().getElement(uint32_t(index), vp)) { + *hole = JS_FALSE; + return true; + } + } + + return DoGetElement(cx, obj, index, hole, vp); +} + namespace js { -struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo +static bool +GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp) { - CopyNonHoleArgsTo(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {} - JSObject *aobj; - Value *dst; - bool operator()(uintN argi, Value *src) { - if (aobj->getArgsElement(argi).isMagic(JS_ARGS_HOLE)) + for (uint32_t i = 0; i < length; i++) { + if (!aobj->getElement(cx, i, &vp[i])) return false; - *dst++ = *src; - return true; } -}; + + return true; +} bool GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp) { - if (aobj->isDenseArray() && length <= aobj->getDenseArrayCapacity() && + if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() && !js_PrototypeHasIndexedProperties(cx, aobj)) { /* The prototype does not have indexed properties so hole = undefined */ - Value *srcbeg = aobj->getDenseArrayElements(); - Value *srcend = srcbeg + length; - for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) + const Value *srcbeg = aobj->getDenseArrayElements(); + const Value *srcend = srcbeg + length; + const Value *src = srcbeg; + for (Value *dst = vp; src < srcend; ++dst, ++src) *dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src; - } else if (aobj->isArguments() && !aobj->isArgsLengthOverridden() && - !js_PrototypeHasIndexedProperties(cx, aobj)) { - /* - * If the argsobj is for an active call, then the elements are the - * live args on the stack. Otherwise, the elements are the args that - * were copied into the argsobj by PutActivationObjects when the - * function returned. In both cases, it is necessary to fall off the - * fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since - * this requires general-purpose property lookup. - */ - if (StackFrame *fp = (StackFrame *) aobj->getPrivate()) { - JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX); - if (!fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(aobj, vp))) - goto found_deleted_prop; - } else { - Value *srcbeg = aobj->getArgsElements(); - Value *srcend = srcbeg + length; - for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) { - if (src->isMagic(JS_ARGS_HOLE)) - goto found_deleted_prop; - *dst = *src; - } - } - } else { - found_deleted_prop: - for (uintN i = 0; i < length; i++) { - if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &vp[i])) - return JS_FALSE; + return true; + } + + if (aobj->isArguments()) { + ArgumentsObject &argsobj = aobj->asArguments(); + if (!argsobj.hasOverriddenLength()) { + if (argsobj.getElements(0, length, vp)) + return true; } } - return true; + return GetElementsSlow(cx, aobj, length, vp); } } @@ -481,8 +482,8 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, const Value &v) if (result != JSObject::ED_OK) break; if (idx >= obj->getArrayLength()) - obj->setArrayLength(idx + 1); - obj->setDenseArrayElement(idx, v); + obj->setDenseArrayLength(idx + 1); + obj->setDenseArrayElementWithType(cx, idx, v); return true; } while (false); @@ -500,27 +501,8 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, const Value &v) JS_ASSERT(!JSID_IS_VOID(idr.id())); Value tmp = v; - return obj->setProperty(cx, idr.id(), &tmp, true); -} - -#ifdef JS_TRACER -JSBool JS_FASTCALL -js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i) -{ -#ifdef DEBUG - Class *origObjClasp = obj->clasp; -#endif - jsuint u = jsuint(i); - JSBool ret = (obj->ensureDenseArrayElements(cx, u, 1) == JSObject::ED_OK); - - /* Partially check the CallInfo's storeAccSet is correct. */ - JS_ASSERT(obj->clasp == origObjClasp); - return ret; + return obj->setGeneric(cx, idr.id(), &tmp, true); } -/* This function and its callees do not touch any object's .clasp field. */ -JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT, INT32, - 0, nanojit::ACCSET_STORE_ANY & ~tjit::ACCSET_OBJ_CLASP) -#endif /* * Delete the element |index| from |obj|. If |strict|, do a strict @@ -539,28 +521,30 @@ static int DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, bool strict) { JS_ASSERT(index >= 0); + JS_ASSERT(floor(index) == index); + if (obj->isDenseArray()) { - if (index <= jsuint(-1)) { - jsuint idx = jsuint(index); - if (idx < obj->getDenseArrayCapacity()) { + if (index <= UINT32_MAX) { + uint32_t idx = uint32_t(index); + if (idx < obj->getDenseArrayInitializedLength()) { + obj->markDenseArrayNotPacked(cx); obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE)); - if (!js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1)) + if (!js_SuppressDeletedElement(cx, obj, idx)) return -1; } } return 1; } - AutoIdRooter idr(cx); - - if (!IndexToId(cx, obj, index, NULL, idr.addr())) - return -1; - if (JSID_IS_VOID(idr.id())) - return 1; - Value v; - if (!obj->deleteProperty(cx, idr.id(), &v, strict)) - return -1; + if (index <= UINT32_MAX) { + if (!obj->deleteElement(cx, uint32_t(index), &v, strict)) + return -1; + } else { + if (!obj->deleteByValue(cx, DoubleValue(index), &v, strict)) + return -1; + } + return v.isTrue() ? 1 : 0; } @@ -582,30 +566,10 @@ SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length) { - Value v; - jsid id; + Value v = NumberValue(length); - v.setNumber(length); - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); /* We don't support read-only array length yet. */ - return obj->setProperty(cx, id, &v, false); -} - -JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - JSErrorReporter older = JS_SetErrorReporter(cx, NULL); - AutoValueRooter tvr(cx); - jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - JSBool ok = obj->getProperty(cx, id, tvr.addr()); - JS_SetErrorReporter(cx, older); - if (!ok) - return false; - - if (!ValueToLength(cx, tvr.addr(), lengthp)) - return false; - - return true; + return obj->setProperty(cx, cx->runtime->atomState.lengthAtom, &v, false); } /* @@ -630,54 +594,60 @@ array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp) static JSBool array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { - jsuint newlen, oldlen, gap, index; - Value junk; - if (!obj->isArray()) { - jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - - return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE); + return obj->defineProperty(cx, cx->runtime->atomState.lengthAtom, *vp, + NULL, NULL, JSPROP_ENUMERATE); } - if (!ValueToLength(cx, vp, &newlen)) + uint32_t newlen; + if (!ToUint32(cx, *vp, &newlen)) + return false; + + jsdouble d; + if (!ToNumber(cx, *vp, &d)) return false; - oldlen = obj->getArrayLength(); + if (d != newlen) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); + return false; + } + uint32_t oldlen = obj->getArrayLength(); if (oldlen == newlen) return true; vp->setNumber(newlen); if (oldlen < newlen) { - obj->setArrayLength(newlen); + obj->setArrayLength(cx, newlen); return true; } if (obj->isDenseArray()) { /* * Don't reallocate if we're not actually shrinking our slots. If we do - * shrink slots here, ensureDenseArrayElements will fill all slots to the - * right of newlen with JS_ARRAY_HOLE. This permits us to disregard - * length when reading from arrays as long we are within the capacity. + * shrink slots here, shrink the initialized length too. This permits us + * us to disregard length when reading from arrays as long we are within + * the initialized capacity. */ jsuint oldcap = obj->getDenseArrayCapacity(); + jsuint oldinit = obj->getDenseArrayInitializedLength(); + if (oldinit > newlen) + obj->setDenseArrayInitializedLength(newlen); if (oldcap > newlen) - obj->shrinkDenseArrayElements(cx, newlen); - obj->setArrayLength(newlen); + obj->shrinkElements(cx, newlen); } else if (oldlen - newlen < (1 << 24)) { do { --oldlen; if (!JS_CHECK_OPERATION_LIMIT(cx)) { - obj->setArrayLength(oldlen + 1); + obj->setArrayLength(cx, oldlen + 1); return false; } int deletion = DeleteArrayElement(cx, obj, oldlen, strict); if (deletion <= 0) { - obj->setArrayLength(oldlen + 1); + obj->setArrayLength(cx, oldlen + 1); return deletion >= 0; } } while (oldlen != newlen); - obj->setArrayLength(newlen); } else { /* * We are going to remove a lot of indexes in a presumably sparse @@ -690,28 +660,37 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value if (!iter) return false; - /* Protect iter against GC under JSObject::deleteProperty. */ - AutoObjectRooter tvr(cx, iter); - - gap = oldlen - newlen; + jsuint gap = oldlen - newlen; for (;;) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id)) return false; if (JSID_IS_VOID(id)) break; + jsuint index; + Value junk; if (js_IdIsIndex(id, &index) && index - newlen < gap && - !obj->deleteProperty(cx, id, &junk, false)) { + !obj->deleteElement(cx, index, &junk, false)) { return false; } } - obj->setArrayLength(newlen); } + obj->setArrayLength(cx, newlen); return true; } +/* Returns true if the dense array has an own property at the index. */ +static inline bool +IsDenseArrayIndex(JSObject *obj, uint32_t index) +{ + JS_ASSERT(obj->isDenseArray()); + + return index < obj->getDenseArrayInitializedLength() && + !obj->getDenseArrayElement(index).isMagic(JS_ARRAY_HOLE); +} + /* - * We have only indexed properties up to capacity (excepting holes), plus the + * We have only indexed properties up to initialized length, plus the * length property. For all else, we delegate to the prototype. */ static inline bool @@ -719,17 +698,14 @@ IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id) { JS_ASSERT(obj->isDenseArray()); - uint32 i; + uint32_t i; return JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) || - (js_IdIsIndex(id, &i) && - obj->getArrayLength() != 0 && - i < obj->getDenseArrayCapacity() && - !obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)); + (js_IdIsIndex(id, &i) && IsDenseArrayIndex(obj, i)); } static JSBool -array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) +array_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) { if (!obj->isDenseArray()) return js_LookupProperty(cx, obj, id, objp, propp); @@ -746,7 +722,42 @@ array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, *propp = NULL; return JS_TRUE; } - return proto->lookupProperty(cx, id, objp, propp); + return proto->lookupGeneric(cx, id, objp, propp); +} + +static JSBool +array_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, + JSProperty **propp) +{ + return array_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp); +} + +static JSBool +array_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, + JSProperty **propp) +{ + if (!obj->isDenseArray()) + return js_LookupElement(cx, obj, index, objp, propp); + + if (IsDenseArrayIndex(obj, index)) { + *propp = (JSProperty *) 1; /* non-null to indicate found */ + *objp = obj; + return true; + } + + if (JSObject *proto = obj->getProto()) + return proto->lookupElement(cx, index, objp, propp); + + *objp = NULL; + *propp = NULL; + return true; +} + +static JSBool +array_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, + JSProperty **propp) +{ + return array_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp); } JSBool @@ -754,7 +765,7 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, Value *vp) { JS_ASSERT(obj->isDenseArray()); - uint32 i; + uint32_t i; if (!js_IdIsIndex(id, &i)) { JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)); vp->setNumber(obj->getArrayLength()); @@ -765,81 +776,119 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, Value *vp) } static JSBool -array_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) +array_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp) { - uint32 i; - - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + if (name == cx->runtime->atomState.lengthAtom) { vp->setNumber(obj->getArrayLength()); - return JS_TRUE; + return true; } - if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) { + if (name == cx->runtime->atomState.protoAtom) { vp->setObjectOrNull(obj->getProto()); - return JS_TRUE; + return true; } if (!obj->isDenseArray()) - return js_GetProperty(cx, obj, id, vp); - - if (!js_IdIsIndex(id, &i) || i >= obj->getDenseArrayCapacity() || - obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { - JSObject *obj2; - JSProperty *prop; - const Shape *shape; - - JSObject *proto = obj->getProto(); - if (!proto) { - vp->setUndefined(); - return JS_TRUE; - } + return js_GetProperty(cx, obj, receiver, ATOM_TO_JSID(name), vp); + JSObject *proto = obj->getProto(); + if (!proto) { vp->setUndefined(); - if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, - &obj2, &prop) < 0) - return JS_FALSE; - - if (prop && obj2->isNative()) { - shape = (const Shape *) prop; - if (!js_NativeGet(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, vp)) - return JS_FALSE; - } - return JS_TRUE; + return true; } - *vp = obj->getDenseArrayElement(i); - return JS_TRUE; + return proto->getProperty(cx, receiver, name, vp); } static JSBool -slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) +array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) { - jsuint index, length; + if (!obj->isDenseArray()) + return js_GetElement(cx, obj, receiver, index, vp); - if (!js_IdIsIndex(id, &index)) - return JS_TRUE; - length = obj->getArrayLength(); - if (index >= length) - obj->setArrayLength(index + 1); - return JS_TRUE; + if (index < obj->getDenseArrayInitializedLength()) { + *vp = obj->getDenseArrayElement(index); + if (!vp->isMagic(JS_ARRAY_HOLE)) { + /* Type information for dense array elements must be correct. */ + JS_ASSERT_IF(!obj->hasSingletonType(), + js::types::TypeHasProperty(cx, obj->type(), JSID_VOID, *vp)); + + return true; + } + } + + JSObject *proto = obj->getProto(); + if (!proto) { + vp->setUndefined(); + return true; + } + + return proto->getElement(cx, receiver, index, vp); } -static JSType -array_typeOf(JSContext *cx, JSObject *obj) +static JSBool +array_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp) { - return JSTYPE_OBJECT; + if (obj->isDenseArray() && !obj->getProto()) { + vp->setUndefined(); + return true; + } + + return js_GetProperty(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp); } static JSBool -array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { - uint32 i; + Value idval = IdToValue(id); + + uint32_t index; + if (IsDefinitelyIndex(idval, &index)) + return array_getElement(cx, obj, receiver, index, vp); + + SpecialId sid; + if (ValueIsSpecial(obj, &idval, &sid, cx)) + return array_getSpecial(cx, obj, receiver, sid, vp); + + JSAtom *atom; + if (!js_ValueToAtom(cx, idval, &atom)) + return false; + + if (atom->isIndex(&index)) + return array_getElement(cx, obj, receiver, index, vp); + + return array_getProperty(cx, obj, receiver, atom->asPropertyName(), vp); +} + +static JSBool +slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) +{ + jsuint index, length; + + if (!js_IdIsIndex(id, &index)) + return JS_TRUE; + length = obj->getArrayLength(); + if (index >= length) + obj->setArrayLength(cx, index + 1); + return JS_TRUE; +} + +static JSType +array_typeOf(JSContext *cx, JSObject *obj) +{ + return JSTYPE_OBJECT; +} + +static JSBool +array_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +{ + uint32_t i; if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return array_length_setter(cx, obj, id, strict, vp); if (!obj->isDenseArray()) - return js_SetProperty(cx, obj, id, vp, strict); + return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); do { if (!js_IdIsIndex(id, &i)) @@ -856,14 +905,65 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric } if (i >= obj->getArrayLength()) - obj->setArrayLength(i + 1); - obj->setDenseArrayElement(i, *vp); + obj->setDenseArrayLength(i + 1); + obj->setDenseArrayElementWithType(cx, i, *vp); + return true; + } while (false); + + if (!obj->makeDenseArraySlow(cx)) + return false; + return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); +} + +static JSBool +array_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) +{ + return array_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); +} + +static JSBool +array_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + + if (!obj->isDenseArray()) + return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); + + do { + /* + * UINT32_MAX is not an array index and must not affect the length + * property, so specifically reject it. + */ + if (index == UINT32_MAX) + break; + if (js_PrototypeHasIndexedProperties(cx, obj)) + break; + + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1); + if (result != JSObject::ED_OK) { + if (result == JSObject::ED_FAILED) + return false; + JS_ASSERT(result == JSObject::ED_SPARSE); + break; + } + + if (index >= obj->getArrayLength()) + obj->setDenseArrayLength(index + 1); + obj->setDenseArrayElementWithType(cx, index, *vp); return true; } while (false); if (!obj->makeDenseArraySlow(cx)) return false; - return js_SetProperty(cx, obj, id, vp, strict); + return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); +} + +static JSBool +array_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict) +{ + return array_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict); } JSBool @@ -889,8 +989,8 @@ js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj) } static JSBool -array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, - PropertyOp getter, StrictPropertyOp setter, uintN attrs) +array_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *value, + JSPropertyOp getter, StrictPropertyOp setter, uintN attrs) { if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return JS_TRUE; @@ -899,7 +999,7 @@ array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); do { - uint32 i = 0; // init to shut GCC up + uint32_t i = 0; // init to shut GCC up bool isIndex = js_IdIsIndex(id, &i); if (!isIndex || attrs != JSPROP_ENUMERATE) break; @@ -913,8 +1013,8 @@ array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, } if (i >= obj->getArrayLength()) - obj->setArrayLength(i + 1); - obj->setDenseArrayElement(i, *value); + obj->setDenseArrayLength(i + 1); + obj->setDenseArrayElementWithType(cx, i, *value); return true; } while (false); @@ -924,42 +1024,167 @@ array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, } static JSBool -array_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +array_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value, + JSPropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return array_defineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs); +} + +namespace js { + +/* non-static for direct definition of array elements within the engine */ +JSBool +array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + if (!obj->isDenseArray()) + return js_DefineElement(cx, obj, index, value, getter, setter, attrs); + + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + + do { + /* + * UINT32_MAX is not an array index and must not affect the length + * property, so specifically reject it. + */ + if (attrs != JSPROP_ENUMERATE || index == UINT32_MAX) + break; + + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1); + if (result != JSObject::ED_OK) { + if (result == JSObject::ED_FAILED) + return false; + JS_ASSERT(result == JSObject::ED_SPARSE); + break; + } + + if (index >= obj->getArrayLength()) + obj->setDenseArrayLength(index + 1); + obj->setDenseArrayElementWithType(cx, index, *value); + return true; + } while (false); + + if (!obj->makeDenseArraySlow(cx)) + return false; + return js_DefineElement(cx, obj, index, value, getter, setter, attrs); +} + +} // namespace js + +static JSBool +array_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return array_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), value, getter, setter, attrs); +} + +static JSBool +array_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { *attrsp = JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ? JSPROP_PERMANENT : JSPROP_ENUMERATE; - return JS_TRUE; + return true; } static JSBool -array_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +array_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SET_ARRAY_ATTRS); - return JS_FALSE; + *attrsp = (name == cx->runtime->atomState.lengthAtom) + ? JSPROP_PERMANENT + : JSPROP_ENUMERATE; + return true; +} + +static JSBool +array_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + *attrsp = JSPROP_ENUMERATE; + return true; +} + +static JSBool +array_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + *attrsp = JSPROP_ENUMERATE; + return true; +} + +static JSBool +array_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} + +static JSBool +array_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} + +static JSBool +array_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; } static JSBool -array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) +array_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) { - uint32 i; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} +static JSBool +array_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict) +{ if (!obj->isDenseArray()) - return js_DeleteProperty(cx, obj, id, rval, strict); + return js_DeleteProperty(cx, obj, name, rval, strict); - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + if (name == cx->runtime->atomState.lengthAtom) { rval->setBoolean(false); - return JS_TRUE; + return true; } - if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity()) - obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE)); + rval->setBoolean(true); + return true; +} + +namespace js { + +/* non-static for direct deletion of array elements within the engine */ +JSBool +array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) +{ + if (!obj->isDenseArray()) + return js_DeleteElement(cx, obj, index, rval, strict); + + if (index < obj->getDenseArrayInitializedLength()) { + obj->markDenseArrayNotPacked(cx); + obj->setDenseArrayElement(index, MagicValue(JS_ARRAY_HOLE)); + } - if (!js_SuppressDeletedProperty(cx, obj, id)) + if (!js_SuppressDeletedElement(cx, obj, index)) return false; rval->setBoolean(true); - return JS_TRUE; + return true; +} + +} // namespace js + +static JSBool +array_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) +{ + if (!obj->isDenseArray()) + return js_DeleteSpecial(cx, obj, sid, rval, strict); + + rval->setBoolean(true); + return true; } static void @@ -967,8 +1192,8 @@ array_trace(JSTracer *trc, JSObject *obj) { JS_ASSERT(obj->isDenseArray()); - uint32 capacity = obj->getDenseArrayCapacity(); - MarkValueRange(trc, capacity, obj->slots, "element"); + uint32_t initLength = obj->getDenseArrayInitializedLength(); + MarkValueRange(trc, initLength, obj->getDenseArrayElements(), "element"); } static JSBool @@ -988,18 +1213,16 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props) return true; } -Class js_ArrayClass = { +Class js::ArrayClass = { "Array", - Class::NON_NATIVE | - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - js_TryValueOf, + Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, NULL, NULL, /* reserved0 */ NULL, /* checkAccess */ @@ -1008,15 +1231,44 @@ Class js_ArrayClass = { NULL, /* xdrObject */ NULL, /* hasInstance */ array_trace, /* trace */ - JS_NULL_CLASS_EXT, { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ + }, + { + array_lookupGeneric, array_lookupProperty, + array_lookupElement, + array_lookupSpecial, + array_defineGeneric, array_defineProperty, + array_defineElement, + array_defineSpecial, + array_getGeneric, array_getProperty, + array_getElement, + NULL, /* getElementIfPresent, because this is hard for now for + slow arrays */ + array_getSpecial, + array_setGeneric, array_setProperty, - array_getAttributes, - array_setAttributes, + array_setElement, + array_setSpecial, + array_getGenericAttributes, + array_getPropertyAttributes, + array_getElementAttributes, + array_getSpecialAttributes, + array_setGenericAttributes, + array_setPropertyAttributes, + array_setElementAttributes, + array_setSpecialAttributes, array_deleteProperty, + array_deleteElement, + array_deleteSpecial, NULL, /* enumerate */ array_typeOf, array_fix, @@ -1025,24 +1277,63 @@ Class js_ArrayClass = { } }; -Class js_SlowArrayClass = { +Class js::SlowArrayClass = { "Array", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, slowarray_addProperty, - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - js_TryValueOf + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* trace */ + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ + } }; +bool +JSObject::allocateSlowArrayElements(JSContext *cx) +{ + JS_ASSERT(hasClass(&js::SlowArrayClass)); + JS_ASSERT(elements == emptyObjectElements); + + ObjectElements *header = cx->new_(0, 0); + if (!header) + return false; + + elements = header->elements(); + return true; +} + static bool AddLengthProperty(JSContext *cx, JSObject *obj) { + /* + * Add the 'length' property for a newly created or converted slow array, + * and update the elements to be an empty array owned by the object. + * The shared emptyObjectElements singleton cannot be used for slow arrays, + * as accesses to 'length' will use the elements header. + */ + const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - JS_ASSERT(!obj->nativeLookup(lengthId)); + JS_ASSERT(!obj->nativeLookup(cx, lengthId)); + + if (!obj->allocateSlowArrayElements(cx)) + return false; return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter, SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0); @@ -1056,26 +1347,53 @@ JSObject::makeDenseArraySlow(JSContext *cx) { JS_ASSERT(isDenseArray()); + MarkTypeObjectFlags(cx, this, + OBJECT_FLAG_NON_PACKED_ARRAY | + OBJECT_FLAG_NON_DENSE_ARRAY); + + uint32_t arrayCapacity = getDenseArrayCapacity(); + uint32_t arrayInitialized = getDenseArrayInitializedLength(); + + /* + * Get an allocated array of the existing elements, evicting from the fixed + * slots if necessary. + */ + if (!hasDynamicElements()) { + if (!growElements(cx, arrayCapacity)) + return false; + JS_ASSERT(hasDynamicElements()); + } + /* * Save old map now, before calling InitScopeForObject. We'll have to undo - * on error. This is gross, but a better way is not obvious. + * on error. This is gross, but a better way is not obvious. Note: the + * exact contents of the array are not preserved on error. */ - js::Shape *oldMap = lastProp; + js::Shape *oldShape = lastProperty(); /* Create a native scope. */ - JSObject *arrayProto = getProto(); - js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind()); - if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto, kind)) + gc::AllocKind kind = getAllocKind(); + Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, getProto(), + oldShape->getObjectParent(), kind); + if (!shape) return false; + this->shape_ = shape; + + /* Take ownership of the dense elements, reset to an empty dense array. */ + HeapValue *elems = elements; + elements = emptyObjectElements; - uint32 capacity = getDenseArrayCapacity(); + /* Root all values in the array during conversion. */ + AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized); /* * Begin with the length property to share more of the property tree. * The getter/setter here will directly access the object's private value. */ if (!AddLengthProperty(cx, this)) { - setMap(oldMap); + this->shape_ = oldShape; + cx->free_(getElementsHeader()); + elements = elems; return false; } @@ -1083,43 +1401,32 @@ JSObject::makeDenseArraySlow(JSContext *cx) * Create new properties pointing to existing elements. Pack the array to * remove holes, so that shapes use successive slots (as for other objects). */ - uint32 next = 0; - for (uint32 i = 0; i < capacity; i++) { + uint32_t next = 0; + for (uint32_t i = 0; i < arrayInitialized; i++) { + /* Dense array indexes can always fit in a jsid. */ jsid id; - if (!ValueToId(cx, Int32Value(i), &id)) { - setMap(oldMap); - return false; - } + JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id)); - if (getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) + if (elems[i].isMagic(JS_ARRAY_HOLE)) continue; - setDenseArrayElement(next, getDenseArrayElement(i)); - if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) { - setMap(oldMap); + this->shape_ = oldShape; + cx->free_(getElementsHeader()); + elements = elems; return false; } + initSlot(next, elems[i]); + next++; } - /* - * Dense arrays with different numbers of slots but the same number of fixed - * slots and the same non-hole indexes must use their fixed slots consistently. - */ - if (hasSlotsArray() && next <= numFixedSlots()) - revertToFixedSlots(cx); + ObjectElements *oldheader = ObjectElements::fromElements(elems); - ClearValueRange(slots + next, this->capacity - next, false); + getElementsHeader()->length = oldheader->length; + cx->free_(oldheader); - /* - * Finally, update class. If |this| is Array.prototype, then js_InitClass - * will create an emptyShape whose class is &js_SlowArrayClass, to ensure - * that delegating instances can share shapes in the tree rooted at the - * proto's empty shape. - */ - clasp = &js_SlowArrayClass; return true; } @@ -1127,20 +1434,20 @@ JSObject::makeDenseArraySlow(JSContext *cx) class ArraySharpDetector { JSContext *cx; - jschar *chars; JSHashEntry *he; + bool alreadySeen; bool sharp; public: ArraySharpDetector(JSContext *cx) : cx(cx), - chars(NULL), he(NULL), + alreadySeen(false), sharp(false) {} bool init(JSObject *obj) { - he = js_EnterSharpObject(cx, obj, NULL, &chars); + he = js_EnterSharpObject(cx, obj, NULL, &alreadySeen); if (!he) return false; sharp = IS_SHARP(he); @@ -1148,27 +1455,11 @@ class ArraySharpDetector } bool initiallySharp() const { - JS_ASSERT_IF(sharp, hasSharpChars()); + JS_ASSERT_IF(sharp, alreadySeen); return sharp; } - void makeSharp() { - MAKE_SHARP(he); - } - - bool hasSharpChars() const { - return chars != NULL; - } - - jschar *takeSharpChars() { - jschar *ret = chars; - chars = NULL; - return ret; - } - ~ArraySharpDetector() { - if (chars) - cx->free_(chars); if (he && !sharp) js_LeaveSharpObject(cx, NULL); } @@ -1179,13 +1470,12 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - if (!obj->isArray()) { - ReportIncompatibleMethod(cx, vp, &js_ArrayClass); - return false; - } + if (!obj->isArray()) + return HandleNonGenericMethodClassMismatch(cx, args, array_toSource, &ArrayClass); ArraySharpDetector detector(cx); if (!detector.init(obj)) @@ -1193,23 +1483,11 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) StringBuffer sb(cx); -#if JS_HAS_SHARP_VARS - if (detector.initiallySharp()) { - jschar *chars = detector.takeSharpChars(); - sb.replaceRawBuffer(chars, js_strlen(chars)); - goto make_string; - } else if (detector.hasSharpChars()) { - detector.makeSharp(); - jschar *chars = detector.takeSharpChars(); - sb.replaceRawBuffer(chars, js_strlen(chars)); - } -#else if (detector.initiallySharp()) { if (!sb.append("[]")) return false; goto make_string; } -#endif if (!sb.append('[')) return false; @@ -1220,9 +1498,9 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) for (jsuint index = 0; index < length; index++) { JSBool hole; - Value tmp; + Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, index, &hole, &tmp)) { + !GetElement(cx, obj, index, &hole, &elt)) { return false; } @@ -1231,7 +1509,7 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) if (hole) { str = cx->runtime->emptyString; } else { - str = js_ValueToSource(cx, tmp); + str = js_ValueToSource(cx, elt); if (!str) return false; } @@ -1257,7 +1535,7 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) if (!str) return false; - JS_SET_RVAL(cx, vp, StringValue(str)); + args.rval().setString(str); return true; } #endif @@ -1266,7 +1544,7 @@ class AutoArrayCycleDetector { JSContext *cx; JSObject *obj; - uint32 genBefore; + uint32_t genBefore; BusyArraysSet::AddPtr hashPointer; bool cycle; JS_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -1310,7 +1588,7 @@ class AutoArrayCycleDetector static JSBool array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, - JSString *sepstr, Value *rval) + JSString *sepstr, CallArgs &args) { static const jschar comma = ','; const jschar *sep; @@ -1330,7 +1608,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return false; if (detector.foundCycle()) { - rval->setString(cx->runtime->atomState.emptyAtom); + args.rval().setString(cx->runtime->atomState.emptyAtom); return true; } @@ -1341,15 +1619,36 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, StringBuffer sb(cx); if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) { - /* Elements beyond 'capacity' are 'undefined' and thus can be ignored. */ - Value *beg = obj->getDenseArrayElements(); - Value *end = beg + Min(length, obj->getDenseArrayCapacity()); - for (Value *vp = beg; vp != end; ++vp) { + const Value *start = obj->getDenseArrayElements(); + const Value *end = start + obj->getDenseArrayInitializedLength(); + const Value *elem; + for (elem = start; elem < end; elem++) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* + * Object stringifying is slow; delegate it to a separate loop to + * keep this one tight. + */ + if (elem->isObject()) + break; + + if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) { + if (!ValueToStringBuffer(cx, *elem, sb)) + return false; + } + } + + for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - if (!vp->isMagic(JS_ARRAY_HOLE) && !vp->isNullOrUndefined()) { - if (!ValueToStringBuffer(cx, *vp, sb)) + JSBool hole; + Value v; + if (!GetElement(cx, obj, i, &hole, &v)) + return false; + if (!hole && !v.isNullOrUndefined()) { + if (!ValueToStringBuffer(cx, v, sb)) return false; } } @@ -1359,19 +1658,20 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return false; JSBool hole; - if (!GetElement(cx, obj, index, &hole, rval)) + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) return false; - if (!hole && !rval->isNullOrUndefined()) { + if (!hole && !elt.isNullOrUndefined()) { if (locale) { - JSObject *robj = ToObject(cx, rval); + JSObject *robj = ToObject(cx, &elt); if (!robj) return false; jsid id = ATOM_TO_JSID(cx->runtime->atomState.toLocaleStringAtom); - if (!robj->callMethod(cx, id, 0, NULL, rval)) + if (!robj->callMethod(cx, id, 0, NULL, &elt)) return false; } - if (!ValueToStringBuffer(cx, *rval, sb)) + if (!ValueToStringBuffer(cx, elt, sb)) return false; } @@ -1385,7 +1685,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, JSString *str = sb.finishString(); if (!str) return false; - rval->setString(str); + args.rval().setString(str); return true; } @@ -1395,34 +1695,34 @@ array_toString(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - Value &join = vp[0]; - if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.joinAtom), &join)) + Value join = args.calleev(); + if (!obj->getProperty(cx, cx->runtime->atomState.joinAtom, &join)) return false; if (!js_IsCallable(join)) { JSString *str = obj_toStringHelper(cx, obj); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } - LeaveTrace(cx); - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, 0, &args)) + InvokeArgsGuard ag; + if (!cx->stack.pushInvokeArgs(cx, 0, &ag)) return false; - args.calleev() = join; - args.thisv().setObject(*obj); + ag.calleev() = join; + ag.thisv().setObject(*obj); /* Do the call. */ - if (!Invoke(cx, args)) + if (!Invoke(cx, ag)) return false; - *vp = args.rval(); + args.rval() = ag.rval(); return true; } @@ -1431,7 +1731,8 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -1439,13 +1740,45 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp) * Passing comma here as the separator. Need a way to get a * locale-specific version. */ - return array_toString_sub(cx, obj, JS_TRUE, NULL, vp); + return array_toString_sub(cx, obj, JS_TRUE, NULL, args); } -static JSBool -InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector) +static inline bool +InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count) +{ + if (cx->typeInferenceEnabled() && !type->unknownProperties()) { + AutoEnterTypeInference enter(cx); + + TypeSet *types = type->getProperty(cx, JSID_VOID, true); + if (!types) + return false; + + for (unsigned i = 0; i < count; i++) { + if (vector[i].isMagic(JS_ARRAY_HOLE)) + continue; + Type valtype = GetValueType(cx, vector[i]); + types->addType(cx, valtype); + } + } + return true; +} + +enum ShouldUpdateTypes +{ + UpdateTypes = true, + DontUpdateTypes = false +}; + +static bool +InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) { - JS_ASSERT(count < MAXINDEX); + JS_ASSERT(count <= MAX_ARRAY_INDEX); + + if (count == 0) + return true; + + if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count)) + return false; /* * Optimize for dense arrays so long as adding the given set of elements @@ -1466,61 +1799,76 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu } jsuint newlen = start + count; if (newlen > obj->getArrayLength()) - obj->setArrayLength(newlen); + obj->setDenseArrayLength(newlen); - JS_ASSERT(count < uint32(-1) / sizeof(Value)); - memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count); + JS_ASSERT(count < UINT32_MAX / sizeof(Value)); + obj->copyDenseArrayElements(start, vector, count); JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE)); return true; } while (false); - Value* end = vector + count; - while (vector != end && start < MAXINDEX) { + const Value* end = vector + count; + while (vector < end && start <= MAX_ARRAY_INDEX) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, start++, *vector++)) { - return JS_FALSE; + return false; } } if (vector == end) - return JS_TRUE; + return true; /* Finish out any remaining elements past the max array index. */ - if (obj->isDenseArray() && !ENSURE_SLOW_ARRAY(cx, obj)) - return JS_FALSE; + if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) + return false; - JS_ASSERT(start == MAXINDEX); + JS_ASSERT(start == MAX_ARRAY_INDEX + 1); AutoValueRooter tvr(cx); AutoIdRooter idr(cx); - Value idval = DoubleValue(MAXINDEX); + Value idval = DoubleValue(MAX_ARRAY_INDEX + 1); do { *tvr.addr() = *vector++; if (!js_ValueToStringId(cx, idval, idr.addr()) || - !obj->setProperty(cx, idr.id(), tvr.addr(), true)) { - return JS_FALSE; + !obj->setGeneric(cx, idr.id(), tvr.addr(), true)) { + return false; } idval.getDoubleRef() += 1; } while (vector != end); - return JS_TRUE; + return true; } +#if 0 static JSBool InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector) { JS_ASSERT(obj->isArray()); JS_ASSERT(obj->isDenseArray()); - obj->setArrayLength(length); + obj->setArrayLength(cx, length); if (!vector || !length) return true; + if (!InitArrayTypes(cx, obj->getType(cx), vector, length)) + return false; + /* Avoid ensureDenseArrayElements to skip sparse array checks there. */ - if (!obj->ensureSlots(cx, length)) + if (!obj->ensureElements(cx, length)) return false; - memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value)); + + obj->setDenseArrayInitializedLength(length); + + bool hole = false; + for (jsuint i = 0; i < length; i++) { + obj->setDenseArrayElement(i, vector[i]); + hole |= vector[i].isMagic(JS_ARRAY_HOLE); + } + if (hole) + obj->markDenseArrayNotPacked(cx); + return true; } +#endif /* * Perl-inspired join, reverse, and sort. @@ -1530,32 +1878,33 @@ array_join(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); + CallArgs args = CallArgsFromVp(argc, vp); JSString *str; - if (argc == 0 || vp[2].isUndefined()) { + if (args.length() == 0 || args[0].isUndefined()) { str = NULL; } else { - str = js_ValueToString(cx, vp[2]); + str = ToString(cx, args[0]); if (!str) return JS_FALSE; - vp[2].setString(str); + args[0].setString(str); } - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - return array_toString_sub(cx, obj, JS_FALSE, str, vp); + return array_toString_sub(cx, obj, JS_FALSE, str, args); } static JSBool array_reverse(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; jsuint len; if (!js_GetLengthProperty(cx, obj, &len)) return false; - vp->setObject(*obj); do { if (!obj->isDenseArray()) @@ -1564,8 +1913,10 @@ array_reverse(JSContext *cx, uintN argc, Value *vp) break; /* An empty array or an array with no elements is already reversed. */ - if (len == 0 || obj->getDenseArrayCapacity() == 0) + if (len == 0 || obj->getDenseArrayCapacity() == 0) { + args.rval().setObject(*obj); return true; + } /* * It's actually surprisingly complicated to reverse an array due to the @@ -1584,7 +1935,10 @@ array_reverse(JSContext *cx, uintN argc, Value *vp) break; } - uint32 lo = 0, hi = len - 1; + /* Fill out the array's initialized length to its proper length. */ + obj->ensureDenseArrayInitializedLength(cx, len, 0); + + uint32_t lo = 0, hi = len - 1; for (; lo < hi; lo++, hi--) { Value origlo = obj->getDenseArrayElement(lo); Value orighi = obj->getDenseArrayElement(hi); @@ -1605,270 +1959,258 @@ array_reverse(JSContext *cx, uintN argc, Value *vp) * array has trailing holes (and thus the original array began with * holes). */ + args.rval().setObject(*obj); return true; } while (false); - AutoValueRooter tvr(cx); + Value lowval, hival; for (jsuint i = 0, half = len / 2; i < half; i++) { JSBool hole, hole2; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, i, &hole, tvr.addr()) || - !GetElement(cx, obj, len - i - 1, &hole2, vp) || - !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) || - !SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) { + !GetElement(cx, obj, i, &hole, &lowval) || + !GetElement(cx, obj, len - i - 1, &hole2, &hival) || + !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, lowval) || + !SetOrDeleteArrayElement(cx, obj, i, hole2, hival)) { return false; } } - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } -typedef struct MSortArgs { - size_t elsize; - JSComparator cmp; - void *arg; - JSBool isValue; -} MSortArgs; +namespace { -/* Helper function for js_MergeSort. */ -static JSBool -MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2) -{ - void *arg, *a, *b, *c; - size_t elsize, runtotal; - int cmp_result; - JSComparator cmp; - JSBool isValue; - - runtotal = run1 + run2; - - elsize = msa->elsize; - cmp = msa->cmp; - arg = msa->arg; - isValue = msa->isValue; - -#define CALL_CMP(a, b) \ - if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; - - /* Copy runs already in sorted order. */ - b = (char *)src + run1 * elsize; - a = (char *)b - elsize; - CALL_CMP(a, b); - if (cmp_result <= 0) { - memcpy(dest, src, runtotal * elsize); - return JS_TRUE; - } +inline bool +CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp) +{ + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; -#define COPY_ONE(p,q,n) \ - (isValue ? (void)(*(Value*)p = *(Value*)q) : (void)memcpy(p, q, n)) + JSString *astr = a.toString(); + JSString *bstr = b.toString(); + int32_t result; + if (!CompareStrings(cx, astr, bstr, &result)) + return false; - a = src; - c = dest; - for (; runtotal != 0; runtotal--) { - JSBool from_a = run2 == 0; - if (!from_a && run1 != 0) { - CALL_CMP(a,b); - from_a = cmp_result <= 0; - } + *lessOrEqualp = (result <= 0); + return true; +} + +static uint32_t const powersOf10[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static inline unsigned +NumDigitsBase10(uint32_t n) +{ + /* + * This is just floor_log10(n) + 1 + * Algorithm taken from + * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + */ + uint32_t log2, t; + JS_CEILING_LOG2(log2, n); + t = log2 * 1233 >> 12; + return t - (n < powersOf10[t]) + 1; +} + +static JS_ALWAYS_INLINE uint32_t +NegateNegativeInt32(int32_t i) +{ + /* + * We cannot simply return '-i' because this is undefined for INT32_MIN. + * 2s complement does actually give us what we want, however. That is, + * ~0x80000000 + 1 = 0x80000000 which is correct when interpreted as a + * uint32_t. To avoid undefined behavior, we write out 2s complement + * explicitly and rely on the peephole optimizer to generate 'neg'. + */ + return ~uint32_t(i) + 1; +} + +inline bool +CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp) +{ + int32_t aint = a.toInt32(); + int32_t bint = b.toInt32(); - if (from_a) { - COPY_ONE(c, a, elsize); - run1--; - a = (char *)a + elsize; + /* + * If both numbers are equal ... trivial + * If only one of both is negative --> arithmetic comparison as char code + * of '-' is always less than any other digit + * If both numbers are negative convert them to positive and continue + * handling ... + */ + if (aint == bint) { + *lessOrEqualp = true; + } else if ((aint < 0) && (bint >= 0)) { + *lessOrEqualp = true; + } else if ((aint >= 0) && (bint < 0)) { + *lessOrEqualp = false; + } else { + uint32_t auint, buint; + if (aint >= 0) { + auint = aint; + buint = bint; } else { - COPY_ONE(c, b, elsize); - run2--; - b = (char *)b + elsize; + auint = NegateNegativeInt32(aint); + buint = NegateNegativeInt32(bint); } - c = (char *)c + elsize; + + /* + * ... get number of digits of both integers. + * If they have the same number of digits --> arithmetic comparison. + * If digits_a > digits_b: a < b*10e(digits_a - digits_b). + * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b. + */ + unsigned digitsa = NumDigitsBase10(auint); + unsigned digitsb = NumDigitsBase10(buint); + if (digitsa == digitsb) + *lessOrEqualp = (auint <= buint); + else if (digitsa > digitsb) + *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]); + else /* if (digitsb > digitsa) */ + *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint)); } -#undef COPY_ONE -#undef CALL_CMP - return JS_TRUE; + return true; } -/* - * This sort is stable, i.e. sequence of equal elements is preserved. - * See also bug #224128. - */ -bool -js_MergeSort(void *src, size_t nel, size_t elsize, - JSComparator cmp, void *arg, void *tmp, - JSMergeSortElemType elemType) +inline bool +CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1, + const jschar *s2, size_t l2, bool *lessOrEqualp) { - void *swap, *vec1, *vec2; - MSortArgs msa; - size_t i, j, lo, hi, run; - int cmp_result; + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + int32_t result; + if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result)) + return false; - JS_ASSERT_IF(JS_SORTING_VALUES, elsize == sizeof(Value)); - bool isValue = elemType == JS_SORTING_VALUES; + *lessOrEqualp = (result <= 0); + return true; +} - /* Avoid memcpy overhead for word-sized and word-aligned elements. */ -#define COPY_ONE(p,q,n) \ - (isValue ? (void)(*(Value*)p = *(Value*)q) : (void)memcpy(p, q, n)) -#define CALL_CMP(a, b) \ - if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; -#define INS_SORT_INT 4 +struct SortComparatorStrings +{ + JSContext *const cx; - /* - * Apply insertion sort to small chunks to reduce the number of merge - * passes needed. - */ - for (lo = 0; lo < nel; lo += INS_SORT_INT) { - hi = lo + INS_SORT_INT; - if (hi >= nel) - hi = nel; - for (i = lo + 1; i < hi; i++) { - vec1 = (char *)src + i * elsize; - vec2 = (char *)vec1 - elsize; - for (j = i; j > lo; j--) { - CALL_CMP(vec2, vec1); - /* "<=" instead of "<" insures the sort is stable */ - if (cmp_result <= 0) { - break; - } + SortComparatorStrings(JSContext *cx) + : cx(cx) {} - /* Swap elements, using "tmp" as tmp storage */ - COPY_ONE(tmp, vec2, elsize); - COPY_ONE(vec2, vec1, elsize); - COPY_ONE(vec1, tmp, elsize); - vec1 = vec2; - vec2 = (char *)vec1 - elsize; - } - } + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) { + return CompareStringValues(cx, a, b, lessOrEqualp); } -#undef CALL_CMP -#undef COPY_ONE - - msa.elsize = elsize; - msa.cmp = cmp; - msa.arg = arg; - msa.isValue = isValue; - - vec1 = src; - vec2 = tmp; - for (run = INS_SORT_INT; run < nel; run *= 2) { - for (lo = 0; lo < nel; lo += 2 * run) { - hi = lo + run; - if (hi >= nel) { - memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize, - (nel - lo) * elsize); - break; - } - if (!MergeArrays(&msa, (char *)vec1 + lo * elsize, - (char *)vec2 + lo * elsize, run, - hi + run > nel ? nel - hi : run)) { - return JS_FALSE; - } - } - swap = vec1; - vec1 = vec2; - vec2 = swap; +}; + +struct SortComparatorLexicographicInt32 +{ + JSContext *const cx; + + SortComparatorLexicographicInt32(JSContext *cx) + : cx(cx) {} + + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) { + return CompareLexicographicInt32(cx, a, b, lessOrEqualp); } - if (src != vec1) - memcpy(src, tmp, nel * elsize); +}; - return JS_TRUE; -} +struct StringifiedElement +{ + size_t charsBegin; + size_t charsEnd; + size_t elementIndex; +}; -struct CompareArgs +struct SortComparatorStringifiedElements { - JSContext *context; - InvokeSessionGuard session; + JSContext *const cx; + const StringBuffer &sb; - CompareArgs(JSContext *cx) - : context(cx) - {} + SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb) + : cx(cx), sb(sb) {} + + bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) { + return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin, + sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin, + lessOrEqualp); + } }; -static JS_REQUIRES_STACK JSBool -sort_compare(void *arg, const void *a, const void *b, int *result) +struct SortComparatorFunction { - const Value *av = (const Value *)a, *bv = (const Value *)b; - CompareArgs *ca = (CompareArgs *) arg; - JSContext *cx = ca->context; + JSContext *const cx; + const Value &fval; + InvokeArgsGuard &ag; + + SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag) + : cx(cx), fval(fval), ag(ag) { } + + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp); +}; +bool +SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp) +{ /* * array_sort deals with holes and undefs on its own and they should not * come here. */ - JS_ASSERT(!av->isMagic() && !av->isUndefined()); - JS_ASSERT(!av->isMagic() && !bv->isUndefined()); + JS_ASSERT(!a.isMagic() && !a.isUndefined()); + JS_ASSERT(!a.isMagic() && !b.isUndefined()); if (!JS_CHECK_OPERATION_LIMIT(cx)) - return JS_FALSE; + return false; - InvokeSessionGuard &session = ca->session; - session[0] = *av; - session[1] = *bv; + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag)) + return false; - if (!session.invoke(cx)) - return JS_FALSE; + ag.setCallee(fval); + ag.thisv() = UndefinedValue(); + ag[0] = a; + ag[1] = b; - jsdouble cmp; - if (!ValueToNumber(cx, session.rval(), &cmp)) - return JS_FALSE; + if (!Invoke(cx, ag)) + return false; - /* Clamp cmp to -1, 0, 1. */ - *result = 0; - if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0) - *result = cmp > 0 ? 1 : -1; + jsdouble cmp; + if (!ToNumber(cx, ag.rval(), &cmp)) + return false; /* - * XXX else report some kind of error here? ECMA talks about 'consistent - * compare functions' that don't return NaN, but is silent about what the - * result should be. So we currently ignore it. + * XXX eport some kind of error here if cmp is NaN? ECMA talks about + * 'consistent compare functions' that don't return NaN, but is silent + * about what the result should be. So we currently ignore it. */ - - return JS_TRUE; -} - -typedef JSBool (JS_REQUIRES_STACK *JSRedComparator)(void*, const void*, - const void*, int *); - -static inline JS_IGNORE_STACK JSComparator -comparator_stack_cast(JSRedComparator func) -{ - return func; + *lessOrEqualp = (JSDOUBLE_IS_NaN(cmp) || cmp <= 0); + return true; } -static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result) -{ - JSContext *cx = (JSContext *)arg; - JSString *astr = ((const Value *)a)->toString(); - JSString *bstr = ((const Value *)b)->toString(); - return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result); -} +} /* namespace anonymous */ JSBool js::array_sort(JSContext *cx, uintN argc, Value *vp) { - jsuint len, newlen, i, undefs; - size_t elemsize; - JSString *str; - - Value *argv = JS_ARGV(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); Value fval; - if (argc > 0 && !argv[0].isUndefined()) { - if (argv[0].isPrimitive()) { + if (args.length() > 0 && !args[0].isUndefined()) { + if (args[0].isPrimitive()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG); return false; } - fval = argv[0]; /* non-default compare function */ + fval = args[0]; /* non-default compare function */ } else { fval.setNull(); } - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; + + jsuint len; if (!js_GetLengthProperty(cx, obj, &len)) return false; if (len == 0) { - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } @@ -1894,23 +2236,12 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) * access the tail of vec corresponding to properties that do not * exist, allowing OS to avoiding committing RAM. See bug 330812. */ + size_t n, undefs; { - Value *vec = (Value *) cx->malloc_(2 * size_t(len) * sizeof(Value)); - if (!vec) + AutoValueVector vec(cx); + if (!vec.reserve(2 * size_t(len))) return false; - DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION(AutoFreeVector) { - JSContext *const cx; - Value *&vec; - public: - AutoFreeVector(JSContext *cx, Value *&vec) : cx(cx), vec(vec) { } - ~AutoFreeVector() { - cx->free_(vec); - } - } free_(cx, vec); - - AutoArrayRooter tvr(cx, 0, vec); - /* * By ECMA 262, 15.4.4.11, a property that does not exist (which we * call a "hole") is always greater than an existing property with @@ -1920,344 +2251,293 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) * undefs. */ undefs = 0; - newlen = 0; bool allStrings = true; - for (i = 0; i < len; i++) { + bool allInts = true; + for (jsuint i = 0; i < len; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; /* Clear vec[newlen] before including it in the rooted set. */ JSBool hole; - vec[newlen].setNull(); - tvr.changeLength(newlen + 1); - if (!GetElement(cx, obj, i, &hole, &vec[newlen])) + Value v; + if (!GetElement(cx, obj, i, &hole, &v)) return false; - if (hole) continue; - - if (vec[newlen].isUndefined()) { + if (v.isUndefined()) { ++undefs; continue; } - - allStrings = allStrings && vec[newlen].isString(); - - ++newlen; + vec.infallibleAppend(v); + allStrings = allStrings && v.isString(); + allInts = allInts && v.isInt32(); } - if (newlen == 0) { - vp->setObject(*obj); + n = vec.length(); + if (n == 0) { + args.rval().setObject(*obj); return true; /* The array has only holes and undefs. */ } - /* - * The first newlen elements of vec are copied from the array object - * (above). The remaining newlen positions are used as GC-rooted scratch - * space for mergesort. We must clear the space before including it to - * the root set covered by tvr.count. - */ - Value *mergesort_tmp = vec + newlen; - MakeRangeGCSafe(mergesort_tmp, newlen); - tvr.changeLength(newlen * 2); + JS_ALWAYS_TRUE(vec.resize(n * 2)); - /* Here len == 2 * (newlen + undefs + number_of_holes). */ + /* Here len == n + undefs + number_of_holes. */ + Value *result = vec.begin(); if (fval.isNull()) { /* * Sort using the default comparator converting all elements to * strings. */ if (allStrings) { - elemsize = sizeof(Value); + if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx))) + return false; + } else if (allInts) { + if (!MergeSort(vec.begin(), n, vec.begin() + n, + SortComparatorLexicographicInt32(cx))) { + return false; + } } else { /* - * To avoid string conversion on each compare we do it only once - * prior to sorting. But we also need the space for the original - * values to recover the sorting result. To reuse - * sort_compare_strings we move the original values to the odd - * indexes in vec, put the string conversion results in the even - * indexes and pass 2 * sizeof(Value) as an element size to the - * sorting function. In this way sort_compare_strings will only - * see the string values when it casts the compare arguments as - * pointers to Value. - * - * This requires doubling the temporary storage including the - * scratch space for the merge sort. Since vec already contains - * the rooted scratch space for newlen elements at the tail, we - * can use it to rearrange and convert to strings first and try - * realloc only when we know that we successfully converted all - * the elements. + * Convert all elements to a jschar array in StringBuffer. + * Store the index and length of each stringified element with + * the corresponding index of the element in the array. Sort + * the stringified elements and with this result order the + * original array. */ -#if JS_BITS_PER_WORD == 32 - if (size_t(newlen) > size_t(-1) / (4 * sizeof(Value))) { - js_ReportAllocationOverflow(cx); + StringBuffer sb(cx); + Vector strElements(cx); + if (!strElements.reserve(2 * n)) return false; - } -#endif - /* - * Rearrange and string-convert the elements of the vector from - * the tail here and, after sorting, move the results back - * starting from the start to prevent overwrite the existing - * elements. - */ - i = newlen; - do { - --i; + int cursor = 0; + for (size_t i = 0; i < n; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - const Value &v = vec[i]; - str = js_ValueToString(cx, v); - if (!str) + + if (!ValueToStringBuffer(cx, vec[i], sb)) return false; - // Copying v must come first, because the following line overwrites v - // when i == 0. - vec[2 * i + 1] = v; - vec[2 * i].setString(str); - } while (i != 0); - - JS_ASSERT(tvr.array == vec); - vec = (Value *) cx->realloc_(vec, 4 * size_t(newlen) * sizeof(Value)); - if (!vec) { - vec = tvr.array; /* N.B. AutoFreeVector */ + + StringifiedElement el = { cursor, sb.length(), i }; + strElements.infallibleAppend(el); + cursor = sb.length(); + } + + /* Resize strElements so we can perform the sorting */ + JS_ALWAYS_TRUE(strElements.resize(2 * n)); + + if (!MergeSort(strElements.begin(), n, strElements.begin() + n, + SortComparatorStringifiedElements(cx, sb))) { return false; } - mergesort_tmp = vec + 2 * newlen; - MakeRangeGCSafe(mergesort_tmp, 2 * newlen); - tvr.changeArray(vec, newlen * 4); - elemsize = 2 * sizeof(Value); - } - if (!js_MergeSort(vec, size_t(newlen), elemsize, - sort_compare_strings, cx, mergesort_tmp, - JS_SORTING_GENERIC)) { - return false; - } - if (!allStrings) { - /* - * We want to make the following loop fast and to unroot the - * cached results of toString invocations before the operation - * callback has a chance to run the GC. For this reason we do - * not call JS_CHECK_OPERATION_LIMIT in the loop. - */ - i = 0; - do { - vec[i] = vec[2 * i + 1]; - } while (++i != newlen); + + /* Order vec[n:2n-1] using strElements.index */ + for (size_t i = 0; i < n; i ++) + vec[n + i] = vec[strElements[i].elementIndex]; + + result = vec.begin() + n; } } else { - CompareArgs ca(cx); - if (!ca.session.start(cx, fval, UndefinedValue(), 2)) - return false; - - if (!js_MergeSort(vec, size_t(newlen), sizeof(Value), - comparator_stack_cast(sort_compare), - &ca, mergesort_tmp, - JS_SORTING_VALUES)) { + InvokeArgsGuard args; + if (!MergeSort(vec.begin(), n, vec.begin() + n, + SortComparatorFunction(cx, fval, args))) { return false; } } - /* - * We no longer need to root the scratch space for the merge sort, so - * unroot it now to make the job of a potential GC under - * InitArrayElements easier. - */ - tvr.changeLength(newlen); - if (!InitArrayElements(cx, obj, 0, newlen, vec)) + if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes)) return false; } /* Set undefs that sorted after the rest of elements. */ while (undefs != 0) { --undefs; - if (!JS_CHECK_OPERATION_LIMIT(cx) || - !SetArrayElement(cx, obj, newlen++, UndefinedValue())) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, n++, UndefinedValue())) return false; - } } /* Re-create any holes that sorted to the end of the array. */ - while (len > newlen) { + while (len > n) { if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0) return false; } - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } /* * Perl-inspired push, pop, shift, unshift, and splice methods. */ -static JSBool -array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) +static bool +array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args) { jsuint length; if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (!InitArrayElements(cx, obj, length, argc, argv)) - return JS_FALSE; + return false; + if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes)) + return false; /* Per ECMA-262, return the new array length. */ - jsdouble newlength = length + jsdouble(argc); - rval->setNumber(newlength); + jsdouble newlength = length + jsdouble(args.length()); + args.rval().setNumber(newlength); return js_SetLengthProperty(cx, obj, newlength); } -static JSBool -array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval) +static bool +array_push1_dense(JSContext* cx, JSObject* obj, CallArgs &args) { - uint32 length = obj->getArrayLength(); - do { - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1); - if (result != JSObject::ED_OK) { - if (result == JSObject::ED_FAILED) - return false; - JS_ASSERT(result == JSObject::ED_SPARSE); - break; - } - - obj->setArrayLength(length + 1); + JS_ASSERT(args.length() == 1); - JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE)); - obj->setDenseArrayElement(length, v); - rval->setNumber(obj->getArrayLength()); - return true; - } while (false); + uint32_t length = obj->getArrayLength(); + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1); + if (result != JSObject::ED_OK) { + if (result == JSObject::ED_FAILED) + return false; + JS_ASSERT(result == JSObject::ED_SPARSE); + if (!obj->makeDenseArraySlow(cx)) + return false; + return array_push_slowly(cx, obj, args); + } - if (!obj->makeDenseArraySlow(cx)) - return false; - Value tmp = v; - return array_push_slowly(cx, obj, 1, &tmp, rval); + obj->setDenseArrayLength(length + 1); + obj->setDenseArrayElementWithType(cx, length, args[0]); + args.rval().setNumber(obj->getArrayLength()); + return true; } JS_ALWAYS_INLINE JSBool -ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v) +NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v) { - uint32 length = obj->getArrayLength(); + JS_ASSERT(!v.isMagic()); + + uint32_t length = obj->getArrayLength(); if (obj->isSlowArray()) { /* This can happen in one evil case. See bug 630377. */ jsid id; - return js_IndexToId(cx, length, &id) && + return IndexToId(cx, length, &id) && js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE); } JS_ASSERT(obj->isDenseArray()); JS_ASSERT(length <= obj->getDenseArrayCapacity()); - if (length == obj->getDenseArrayCapacity()) { - if (length > JS_ARGS_LENGTH_MAX) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_ARRAY_INIT_TOO_BIG); - return false; - } + if (!obj->ensureElements(cx, length + 1)) + return false; - /* - * An array comprehension cannot add holes to the array. So we can use - * ensureSlots instead of ensureDenseArrayElements. - */ - if (!obj->ensureSlots(cx, length + 1)) - return false; - } - obj->setArrayLength(length + 1); - obj->setDenseArrayElement(length, v); + obj->setDenseArrayInitializedLength(length + 1); + obj->setDenseArrayLength(length + 1); + obj->initDenseArrayElementWithType(cx, length, v); return true; } JSBool -js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &vp) +js_NewbornArrayPush(JSContext *cx, JSObject *obj, const Value &vp) { - return ArrayCompPushImpl(cx, obj, vp); + return NewbornArrayPushImpl(cx, obj, vp); } -#ifdef JS_TRACER -JSBool JS_FASTCALL -js_ArrayCompPush_tn(JSContext *cx, JSObject *obj, ValueArgType v) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - if (!ArrayCompPushImpl(cx, obj, ValueArgToConstRef(v))) { - SetBuiltinError(tm); - return JS_FALSE; - } - - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, js_ArrayCompPush_tn, CONTEXT, OBJECT, - VALUE, 0, nanojit::ACCSET_STORE_ANY) -#endif - -static JSBool -array_push(JSContext *cx, uintN argc, Value *vp) +JSBool +js::array_push(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; /* Insist on one argument and obj of the expected class. */ - if (argc != 1 || !obj->isDenseArray()) - return array_push_slowly(cx, obj, argc, vp + 2, vp); + if (args.length() != 1 || !obj->isDenseArray()) + return array_push_slowly(cx, obj, args); - return array_push1_dense(cx, obj, vp[2], vp); + return array_push1_dense(cx, obj, args); } static JSBool -array_pop_slowly(JSContext *cx, JSObject* obj, Value *vp) +array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) { jsuint index; - JSBool hole; - if (!js_GetLengthProperty(cx, obj, &index)) - return JS_FALSE; - if (index == 0) { - vp->setUndefined(); - } else { - index--; + return false; - /* Get the to-be-deleted property's value into vp. */ - if (!GetElement(cx, obj, index, &hole, vp)) - return JS_FALSE; - if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) - return JS_FALSE; + if (index == 0) { + args.rval().setUndefined(); + return js_SetLengthProperty(cx, obj, index); } + + index--; + + JSBool hole; + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) + return false; + + if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) + return false; + + args.rval() = elt; return js_SetLengthProperty(cx, obj, index); } static JSBool -array_pop_dense(JSContext *cx, JSObject* obj, Value *vp) +array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args) { - jsuint index; - JSBool hole; - - index = obj->getArrayLength(); + jsuint index = obj->getArrayLength(); if (index == 0) { - vp->setUndefined(); + args.rval().setUndefined(); return JS_TRUE; } + index--; - if (!GetElement(cx, obj, index, &hole, vp)) + + JSBool hole; + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) return JS_FALSE; + if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) return JS_FALSE; - obj->setArrayLength(index); + if (obj->getDenseArrayInitializedLength() > index) + obj->setDenseArrayInitializedLength(index); + + obj->setArrayLength(cx, index); + + args.rval() = elt; return JS_TRUE; } -static JSBool -array_pop(JSContext *cx, uintN argc, Value *vp) +JSBool +js::array_pop(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; if (obj->isDenseArray()) - return array_pop_dense(cx, obj, vp); - return array_pop_slowly(cx, obj, vp); + return array_pop_dense(cx, obj, args); + return array_pop_slowly(cx, obj, args); } -static JSBool -array_shift(JSContext *cx, uintN argc, Value *vp) +#ifdef JS_METHODJIT +void JS_FASTCALL +mjit::stubs::ArrayShift(VMFrame &f) +{ + JSObject *obj = &f.regs.sp[-1].toObject(); + JS_ASSERT(obj->isDenseArray()); + + /* + * At this point the length and initialized length have already been + * decremented and the result fetched, so just shift the array elements + * themselves. + */ + uint32_t initlen = obj->getDenseArrayInitializedLength(); + obj->moveDenseArrayElementsUnbarriered(0, 1, initlen); +} +#endif /* JS_METHODJIT */ + +JSBool +js::array_shift(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return JS_FALSE; @@ -2266,27 +2546,26 @@ array_shift(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; if (length == 0) { - vp->setUndefined(); + args.rval().setUndefined(); } else { length--; if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && - length < obj->getDenseArrayCapacity()) { - *vp = obj->getDenseArrayElement(0); - if (vp->isMagic(JS_ARRAY_HOLE)) - vp->setUndefined(); - Value *elems = obj->getDenseArrayElements(); - memmove(elems, elems + 1, length * sizeof(jsval)); - obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE)); - obj->setArrayLength(length); + length < obj->getDenseArrayCapacity() && + 0 < obj->getDenseArrayInitializedLength()) { + args.rval() = obj->getDenseArrayElement(0); + if (args.rval().isMagic(JS_ARRAY_HOLE)) + args.rval().setUndefined(); + obj->moveDenseArrayElements(0, 1, obj->getDenseArrayInitializedLength() - 1); + obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1); + obj->setArrayLength(cx, length); if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length))) return JS_FALSE; return JS_TRUE; } - /* Get the to-be-deleted property's value into vp ASAP. */ JSBool hole; - if (!GetElement(cx, obj, 0, &hole, vp)) + if (!GetElement(cx, obj, 0u, &hole, &args.rval())) return JS_FALSE; /* Slide down the array above the first element. */ @@ -2309,21 +2588,18 @@ array_shift(JSContext *cx, uintN argc, Value *vp) static JSBool array_unshift(JSContext *cx, uintN argc, Value *vp) { - Value *argv; - JSBool hole; - jsdouble last, newlen; - - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; jsuint length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; - newlen = length; - if (argc > 0) { - /* Slide up the array to make room for argc at the bottom. */ - argv = JS_ARGV(cx, vp); + + jsdouble newlen = length; + if (args.length() > 0) { + /* Slide up the array to make room for all args at the bottom. */ if (length > 0) { bool optimized = false; do { @@ -2331,26 +2607,26 @@ array_unshift(JSContext *cx, uintN argc, Value *vp) break; if (js_PrototypeHasIndexedProperties(cx, obj)) break; - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, argc); + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, args.length()); if (result != JSObject::ED_OK) { if (result == JSObject::ED_FAILED) return false; JS_ASSERT(result == JSObject::ED_SPARSE); break; } - Value *elems = obj->getDenseArrayElements(); - memmove(elems + argc, elems, length * sizeof(jsval)); - for (uint32 i = 0; i < argc; i++) + obj->moveDenseArrayElements(args.length(), 0, length); + for (uint32_t i = 0; i < args.length(); i++) obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE)); optimized = true; } while (false); if (!optimized) { - last = length; - jsdouble upperIndex = last + argc; + jsdouble last = length; + jsdouble upperIndex = last + args.length(); AutoValueRooter tvr(cx); do { --last, --upperIndex; + JSBool hole; if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, last, &hole, tvr.addr()) || !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) { @@ -2360,184 +2636,299 @@ array_unshift(JSContext *cx, uintN argc, Value *vp) } } - /* Copy from argv to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, argc, argv)) + /* Copy from args to the bottom of the array. */ + if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes)) return JS_FALSE; - newlen += argc; + newlen += args.length(); } if (!js_SetLengthProperty(cx, obj, newlen)) return JS_FALSE; /* Follow Perl by returning the new array length. */ - vp->setNumber(newlen); + args.rval().setNumber(newlen); return JS_TRUE; } -static JSBool -array_splice(JSContext *cx, uintN argc, Value *vp) +static inline void +TryReuseArrayType(JSObject *obj, JSObject *nobj) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) + /* + * Try to change the type of a newly created array nobj to the same type + * as obj. This can only be performed if the original object is an array + * and has the same prototype. + */ + JS_ASSERT(nobj->isDenseArray()); + JS_ASSERT(nobj->getProto()->hasNewType(nobj->type())); + + if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto()) + nobj->setType(obj->type()); +} + +/* + * Returns true if this is a dense array whose |count| properties starting from + * |startingIndex| may be accessed (get, set, delete) directly through its + * contiguous vector of elements without fear of getters, setters, etc. along + * the prototype chain, or of enumerators requiring notification of + * modifications. + */ +static inline bool +CanOptimizeForDenseStorage(JSObject *arr, uint32_t startingIndex, uint32_t count, JSContext *cx) +{ + /* If the desired properties overflow dense storage, we can't optimize. */ + if (UINT32_MAX - startingIndex < count) return false; - jsuint length, begin, end, count, delta, last; - JSBool hole; + /* There's no optimizing possible if it's not a dense array. */ + if (!arr->isDenseArray()) + return false; - /* Create a new array value to return. */ - JSObject *obj2 = NewDenseEmptyArray(cx); - if (!obj2) - return JS_FALSE; - vp->setObject(*obj2); + /* + * Don't optimize if the array might be in the midst of iteration. We + * rely on this to be able to safely move dense array elements around with + * just a memmove (see JSObject::moveDenseArrayElements), without worrying + * about updating any in-progress enumerators for properties implicitly + * deleted if a hole is moved from one location to another location not yet + * visited. See bug 690622. + * + * Another potential wrinkle: what if the enumeration is happening on an + * object which merely has |arr| on its prototype chain? It turns out this + * case can't happen, because any dense array used as the prototype of + * another object is first slowified, for type inference's sake. + */ + if (JS_UNLIKELY(arr->getType(cx)->hasAllFlags(OBJECT_FLAG_ITERATED))) + return false; - /* Nothing to do if no args. Otherwise get length. */ - if (argc == 0) - return JS_TRUE; - Value *argv = JS_ARGV(cx, vp); - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - jsuint origlength = length; + /* Now just watch out for getters and setters along the prototype chain. */ + return !js_PrototypeHasIndexedProperties(cx, arr) && + startingIndex + count <= arr->getDenseArrayInitializedLength(); +} - /* Convert the first argument into a starting index. */ - jsdouble d; - if (!ToInteger(cx, *argv, &d)) - return JS_FALSE; - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; /* d has been clamped to uint32 */ - argc--; - argv++; - - /* Convert the second argument from a count into a fencepost index. */ - delta = length - begin; - if (argc == 0) { - count = delta; - end = length; +/* ES5 15.4.4.12. */ +static JSBool +array_splice(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Steps 3-4. */ + uint32_t len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 5. */ + double relativeStart; + if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart)) + return false; + + /* Step 6. */ + uint32_t actualStart; + if (relativeStart < 0) + actualStart = JS_MAX(len + relativeStart, 0); + else + actualStart = JS_MIN(relativeStart, len); + + /* Step 7. */ + uint32_t actualDeleteCount; + if (argc != 1) { + jsdouble deleteCountDouble; + if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble)) + return false; + actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart); } else { - if (!ToInteger(cx, *argv, &d)) + /* + * Non-standard: if start was specified but deleteCount was omitted, + * delete to the end of the array. See bug 668024 for discussion. + */ + actualDeleteCount = len - actualStart; + } + + JS_ASSERT(len - actualStart >= actualDeleteCount); + + /* Steps 2, 8-9. */ + JSObject *arr; + if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) { + arr = NewDenseCopiedArray(cx, actualDeleteCount, + obj->getDenseArrayElements() + actualStart); + if (!arr) return false; - if (d < 0) - d = 0; - else if (d > delta) - d = delta; - count = (jsuint)d; - end = begin + count; - argc--; - argv++; + TryReuseArrayType(obj, arr); + } else { + arr = NewDenseAllocatedArray(cx, actualDeleteCount); + if (!arr) + return false; + TryReuseArrayType(obj, arr); + + for (uint32_t k = 0; k < actualDeleteCount; k++) { + JSBool hole; + Value fromValue; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetElement(cx, obj, actualStart + k, &hole, &fromValue) || + (!hole && !arr->defineElement(cx, k, fromValue))) + { + return false; + } + } } - AutoValueRooter tvr(cx); + /* Step 11. */ + uint32_t itemCount = (argc >= 2) ? (argc - 2) : 0; - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && - end <= obj->getDenseArrayCapacity()) { - if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin)) - return JS_FALSE; + if (itemCount < actualDeleteCount) { + /* Step 12: the array is being shrunk. */ + uint32_t sourceIndex = actualStart + actualDeleteCount; + uint32_t targetIndex = actualStart + itemCount; + uint32_t finalLength = len - actualDeleteCount + itemCount; + + if (CanOptimizeForDenseStorage(obj, 0, len, cx)) { + /* Steps 12(a)-(b). */ + obj->moveDenseArrayElements(targetIndex, sourceIndex, len - sourceIndex); + + /* + * Update the initialized length. Do so before shrinking so that we + * can apply the write barrier to the old slots. + */ + if (cx->typeInferenceEnabled()) + obj->setDenseArrayInitializedLength(finalLength); + + /* Steps 12(c)-(d). */ + obj->shrinkElements(cx, finalLength); + + /* Fix running enumerators for the deleted items. */ + if (!js_SuppressDeletedElements(cx, obj, finalLength, len)) + return false; } else { - for (last = begin; last < end; last++) { + /* + * This is all very slow if the length is very large. We don't yet + * have the ability to iterate in sorted order, so we just do the + * pessimistic thing and let JS_CHECK_OPERATION_LIMIT handle the + * fallout. + */ + + /* Steps 12(a)-(b). */ + for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) { + JSBool hole; + Value fromValue; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr())) { - return JS_FALSE; + !GetElement(cx, obj, from, &hole, &fromValue) || + !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue)) + { + return false; } - - /* Copy tvr.value() to the new array unless it's a hole. */ - if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value())) - return JS_FALSE; } - if (!js_SetLengthProperty(cx, obj2, count)) - return JS_FALSE; - } - } - - /* Find the direction (up or down) to copy and make way for argv. */ - if (argc > count) { - delta = (jsuint)argc - count; - last = length; - bool optimized = false; - do { - if (!obj->isDenseArray()) - break; - if (js_PrototypeHasIndexedProperties(cx, obj)) - break; - if (length > obj->getDenseArrayCapacity()) - break; - if (length != 0 && obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE)) - break; - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, delta); - if (result != JSObject::ED_OK) { - if (result == JSObject::ED_FAILED) + /* Steps 12(c)-(d). */ + for (uint32_t k = len; k > finalLength; k--) { + if (DeleteArrayElement(cx, obj, k - 1, true) < 0) return false; - JS_ASSERT(result == JSObject::ED_SPARSE); - break; } - Value *arraybeg = obj->getDenseArrayElements(); - Value *srcbeg = arraybeg + last - 1; - Value *srcend = arraybeg + end - 1; - Value *dstbeg = srcbeg + delta; - for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst) - *dst = *src; - - obj->setArrayLength(obj->getArrayLength() + delta); - optimized = true; - } while (false); + } + } else if (itemCount > actualDeleteCount) { + /* Step 13. */ - if (!optimized) { - /* (uint) end could be 0, so we can't use a vanilla >= test. */ - while (last-- > end) { - if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr()) || - !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) { - return JS_FALSE; - } + /* + * Optimize only if the array is already dense and we can extend it to + * its new length. + */ + if (obj->isDenseArray()) { + JSObject::EnsureDenseResult res = + obj->ensureDenseArrayElements(cx, obj->getArrayLength(), + itemCount - actualDeleteCount); + if (res == JSObject::ED_FAILED) + return false; + + if (res == JSObject::ED_SPARSE) { + if (!obj->makeDenseArraySlow(cx)) + return false; + } else { + JS_ASSERT(res == JSObject::ED_OK); } } - length += delta; - } else if (argc < count) { - delta = count - (jsuint)argc; - if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && - length <= obj->getDenseArrayCapacity()) { - - Value *arraybeg = obj->getDenseArrayElements(); - Value *srcbeg = arraybeg + end; - Value *srcend = arraybeg + length; - Value *dstbeg = srcbeg - delta; - for (Value *src = srcbeg, *dst = dstbeg; src < srcend; ++src, ++dst) - *dst = *src; + + if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { + obj->moveDenseArrayElements(actualStart + itemCount, + actualStart + actualDeleteCount, + len - (actualStart + actualDeleteCount)); + + if (cx->typeInferenceEnabled()) + obj->setDenseArrayInitializedLength(len + itemCount - actualDeleteCount); } else { - for (last = end; last < length; last++) { + for (jsdouble k = len - actualDeleteCount; k > actualStart; k--) { + jsdouble from = k + actualDeleteCount - 1; + jsdouble to = k + itemCount - 1; + + JSBool hole; + Value fromValue; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr()) || - !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) { - return JS_FALSE; + !GetElement(cx, obj, from, &hole, &fromValue) || + !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue)) + { + return false; } } } - length -= delta; } - if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength)) - return JS_FALSE; + /* Step 10. */ + Value *items = args.array() + 2; - /* - * Copy from argv into the hole to complete the splice, and update length in - * case we deleted elements from the end. - */ - return InitArrayElements(cx, obj, begin, argc, argv) && - js_SetLengthProperty(cx, obj, length); + /* Steps 14-15. */ + for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) { + if (!SetArrayElement(cx, obj, k, items[i])) + return false; + } + + /* Step 16. */ + jsdouble finalLength = jsdouble(len) - actualDeleteCount + itemCount; + if (!js_SetLengthProperty(cx, obj, finalLength)) + return false; + + /* Step 17. */ + args.rval().setObject(*arr); + return true; } +#ifdef JS_METHODJIT +void JS_FASTCALL +mjit::stubs::ArrayConcatTwoArrays(VMFrame &f) +{ + JSObject *result = &f.regs.sp[-3].toObject(); + JSObject *obj1 = &f.regs.sp[-2].toObject(); + JSObject *obj2 = &f.regs.sp[-1].toObject(); + + JS_ASSERT(result->isDenseArray() && obj1->isDenseArray() && obj2->isDenseArray()); + + uint32_t initlen1 = obj1->getDenseArrayInitializedLength(); + JS_ASSERT(initlen1 == obj1->getArrayLength()); + + uint32_t initlen2 = obj2->getDenseArrayInitializedLength(); + JS_ASSERT(initlen2 == obj2->getArrayLength()); + + /* No overflow here due to nslots limit. */ + uint32_t len = initlen1 + initlen2; + + if (!result->ensureElements(f.cx, len)) + THROW(); + + JS_ASSERT(!result->getDenseArrayInitializedLength()); + result->setDenseArrayInitializedLength(len); + + result->initDenseArrayElements(0, obj1->getDenseArrayElements(), initlen1); + result->initDenseArrayElements(initlen1, obj2->getDenseArrayElements(), initlen2); + + result->setDenseArrayLength(len); +} +#endif /* JS_METHODJIT */ + /* * Python-esque sequence operations. */ -static JSBool -array_concat(JSContext *cx, uintN argc, Value *vp) +JSBool +js::array_concat(JSContext *cx, uintN argc, Value *vp) { /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ Value *p = JS_ARGV(cx, vp) - 1; @@ -2550,18 +2941,14 @@ array_concat(JSContext *cx, uintN argc, Value *vp) JSObject *nobj; jsuint length; if (aobj->isDenseArray()) { - /* - * Clone aobj but pass the minimum of its length and capacity, to - * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In - * the normal case where length is <= capacity, nobj and aobj will have - * the same capacity. - */ length = aobj->getArrayLength(); - jsuint capacity = aobj->getDenseArrayCapacity(); - nobj = NewDenseCopiedArray(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements()); + const Value *vector = aobj->getDenseArrayElements(); + jsuint initlen = aobj->getDenseArrayInitializedLength(); + nobj = NewDenseCopiedArray(cx, initlen, vector); if (!nobj) return JS_FALSE; - nobj->setArrayLength(length); + TryReuseArrayType(aobj, nobj); + nobj->setArrayLength(cx, length); vp->setObject(*nobj); if (argc == 0) return JS_TRUE; @@ -2575,38 +2962,29 @@ array_concat(JSContext *cx, uintN argc, Value *vp) length = 0; } - AutoValueRooter tvr(cx); - /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ for (uintN i = 0; i <= argc; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; const Value &v = p[i]; if (v.isObject()) { - aobj = &v.toObject(); - if (aobj->isArray() || - (aobj->isWrapper() && aobj->unwrap()->isArray())) { - jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - if (!aobj->getProperty(cx, id, tvr.addr())) - return false; + JSObject &obj = v.toObject(); + if (ObjectClassIs(obj, ESClass_Array, cx)) { jsuint alength; - if (!ValueToLength(cx, tvr.addr(), &alength)) + if (!js_GetLengthProperty(cx, &obj, &alength)) return false; - for (jsuint slot = 0; slot < alength; slot++) { + for (uint32_t slot = 0; slot < alength; slot++) { JSBool hole; - if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, aobj, slot, &hole, tvr.addr())) { + Value tmp; + if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, &obj, slot, &hole, &tmp)) return false; - } /* * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent * properties. */ - if (!hole && - !SetArrayElement(cx, nobj, length+slot, tvr.value())) { + if (!hole && !SetArrayElement(cx, nobj, length + slot, tmp)) return false; - } } length += alength; continue; @@ -2624,14 +3002,13 @@ array_concat(JSContext *cx, uintN argc, Value *vp) static JSBool array_slice(JSContext *cx, uintN argc, Value *vp) { - Value *argv; JSObject *nobj; jsuint length, begin, end, slot; JSBool hole; - argv = JS_ARGV(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -2640,9 +3017,9 @@ array_slice(JSContext *cx, uintN argc, Value *vp) begin = 0; end = length; - if (argc > 0) { + if (args.length() > 0) { jsdouble d; - if (!ToInteger(cx, argv[0], &d)) + if (!ToInteger(cx, args[0], &d)) return false; if (d < 0) { d += length; @@ -2653,8 +3030,8 @@ array_slice(JSContext *cx, uintN argc, Value *vp) } begin = (jsuint)d; - if (argc > 1 && !argv[1].isUndefined()) { - if (!ToInteger(cx, argv[1], &d)) + if (args.length() > 1 && !args[1].isUndefined()) { + if (!ToInteger(cx, args[1], &d)) return false; if (d < 0) { d += length; @@ -2670,20 +3047,20 @@ array_slice(JSContext *cx, uintN argc, Value *vp) if (begin > end) begin = end; - if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() && + if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() && !js_PrototypeHasIndexedProperties(cx, obj)) { nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin); if (!nobj) return JS_FALSE; - vp->setObject(*nobj); + TryReuseArrayType(obj, nobj); + args.rval().setObject(*nobj); return JS_TRUE; } - /* Create a new Array object and root it using *vp. */ nobj = NewDenseAllocatedArray(cx, end - begin); if (!nobj) return JS_FALSE; - vp->setObject(*nobj); + TryReuseArrayType(obj, nobj); AutoValueRooter tvr(cx); for (slot = begin; slot < end; slot++) { @@ -2695,20 +3072,24 @@ array_slice(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } + args.rval().setObject(*nobj); return JS_TRUE; } -#if JS_HAS_ARRAY_EXTRAS +enum IndexOfKind { + IndexOf, + LastIndexOf +}; static JSBool -array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) +array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) { jsuint length, i, stop; Value tosearch; jsint direction; JSBool hole; - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; if (!js_GetLengthProperty(cx, obj, &length)) @@ -2716,26 +3097,26 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) if (length == 0) goto not_found; - if (argc <= 1) { - i = isLast ? length - 1 : 0; - tosearch = (argc != 0) ? vp[2] : UndefinedValue(); + if (args.length() <= 1) { + i = (mode == LastIndexOf) ? length - 1 : 0; + tosearch = (args.length() != 0) ? args[0] : UndefinedValue(); } else { jsdouble start; - tosearch = vp[2]; - if (!ToInteger(cx, vp[3], &start)) + tosearch = args[0]; + if (!ToInteger(cx, args[1], &start)) return false; if (start < 0) { start += length; if (start < 0) { - if (isLast) + if (mode == LastIndexOf) goto not_found; i = 0; } else { i = (jsuint)start; } } else if (start >= length) { - if (!isLast) + if (mode == IndexOf) goto not_found; i = length - 1; } else { @@ -2743,7 +3124,7 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } } - if (isLast) { + if (mode == LastIndexOf) { stop = 0; direction = -1; } else { @@ -2752,17 +3133,18 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } for (;;) { + Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, (jsuint)i, &hole, vp)) { + !GetElement(cx, obj, (jsuint)i, &hole, &elt)) { return JS_FALSE; } if (!hole) { - JSBool equal; - if (!StrictlyEqual(cx, *vp, tosearch, &equal)) - return JS_FALSE; + bool equal; + if (!StrictlyEqual(cx, elt, tosearch, &equal)) + return false; if (equal) { - vp->setNumber(i); - return JS_TRUE; + args.rval().setNumber(i); + return true; } } if (i == stop) @@ -2771,267 +3153,450 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } not_found: - vp->setInt32(-1); + args.rval().setInt32(-1); return JS_TRUE; } static JSBool array_indexOf(JSContext *cx, uintN argc, Value *vp) { - return array_indexOfHelper(cx, JS_FALSE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_indexOfHelper(cx, IndexOf, args); } static JSBool array_lastIndexOf(JSContext *cx, uintN argc, Value *vp) { - return array_indexOfHelper(cx, JS_TRUE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_indexOfHelper(cx, LastIndexOf, args); } -/* Order is important; extras that take a predicate funarg must follow MAP. */ -typedef enum ArrayExtraMode { - FOREACH, - REDUCE, - REDUCE_RIGHT, - MAP, - FILTER, - SOME, - EVERY -} ArrayExtraMode; - -#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) +/* ECMA 15.4.4.16-15.4.4.18. */ +class ArrayForEachBehavior +{ + public: + static bool shouldExit(Value &callval, Value *rval) { return false; } + static Value lateExitValue() { return UndefinedValue(); } +}; -static JSBool -array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) +class ArrayEveryBehavior { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) + public: + static bool shouldExit(Value &callval, Value *rval) + { + if (!js_ValueToBoolean(callval)) { + *rval = BooleanValue(false); + return true; + } return false; - - jsuint length; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - - /* - * First, get or compute our callee, so that we error out consistently - * when passed a non-callable object. - */ - if (argc == 0) { - js_ReportMissingArg(cx, *vp, 0); - return JS_FALSE; } - Value *argv = vp + 2; - JSObject *callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); - if (!callable) - return JS_FALSE; + static Value lateExitValue() { return BooleanValue(true); } +}; - /* - * Set our initial return condition, used for zero-length array cases - * (and pre-size our map return to match our known length, for all cases). - */ - jsuint newlen; - JSObject *newarr; -#ifdef __GNUC__ /* quell GCC overwarning */ - newlen = 0; - newarr = NULL; -#endif - jsint start = 0, end = length, step = 1; - - switch (mode) { - case REDUCE_RIGHT: - start = length - 1, end = -1, step = -1; - /* FALL THROUGH */ - case REDUCE: - if (length == 0 && argc == 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_EMPTY_ARRAY_REDUCE); - return JS_FALSE; - } - if (argc >= 2) { - *vp = argv[1]; - } else { - JSBool hole; - do { - if (!GetElement(cx, obj, start, &hole, vp)) - return JS_FALSE; - start += step; - } while (hole && start != end); - - if (hole && start == end) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_EMPTY_ARRAY_REDUCE); - return JS_FALSE; - } +class ArraySomeBehavior +{ + public: + static bool shouldExit(Value &callval, Value *rval) + { + if (js_ValueToBoolean(callval)) { + *rval = BooleanValue(true); + return true; } - break; - case MAP: - case FILTER: - newlen = (mode == MAP) ? length : 0; - newarr = NewDenseAllocatedArray(cx, newlen); - if (!newarr) - return JS_FALSE; - vp->setObject(*newarr); - break; - case SOME: - vp->setBoolean(false); - break; - case EVERY: - vp->setBoolean(true); - break; - case FOREACH: - vp->setUndefined(); - break; + return false; } + static Value lateExitValue() { return BooleanValue(false); } +}; - if (length == 0) - return JS_TRUE; - - Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue(); +template +static inline bool +array_readonlyCommon(JSContext *cx, CallArgs &args) +{ + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; - /* - * For all but REDUCE, we call with 3 args (value, index, array). REDUCE - * requires 4 args (accum, value, index, array). - */ - argc = 3 + REDUCE_MODE(mode); + /* Step 2-3. */ + uint32_t len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; - InvokeSessionGuard session; - if (!session.start(cx, ObjectValue(*callable), thisv, argc)) - return JS_FALSE; + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; - MUST_FLOW_THROUGH("out"); - JSBool ok = JS_TRUE; - JSBool cond; + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); - Value objv = ObjectValue(*obj); - AutoValueRooter tvr(cx); - for (jsint i = start; i != end; i += step) { - JSBool hole; - ok = JS_CHECK_OPERATION_LIMIT(cx) && - GetElement(cx, obj, i, &hole, tvr.addr()); - if (!ok) - goto out; - if (hole) - continue; + /* Step 6. */ + uint32_t k = 0; - /* - * Push callable and 'this', then args. We must do this for every - * iteration around the loop since Invoke clobbers its arguments. - */ - uintN argi = 0; - if (REDUCE_MODE(mode)) - session[argi++] = *vp; - session[argi++] = tvr.value(); - session[argi++] = Int32Value(i); - session[argi] = objv; - - /* Do the call. */ - ok = session.invoke(cx); - if (!ok) - break; + /* Step 7. */ + InvokeArgsGuard ag; + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; - const Value &rval = session.rval(); + /* Step a, b, and c.i. */ + Value kValue; + JSBool kNotPresent; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; - if (mode > MAP) - cond = js_ValueToBoolean(rval); -#ifdef __GNUC__ /* quell GCC overwarning */ - else - cond = JS_FALSE; -#endif + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.setCallee(ObjectValue(*callable)); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; - switch (mode) { - case FOREACH: - break; - case REDUCE: - case REDUCE_RIGHT: - *vp = rval; - break; - case MAP: - ok = SetArrayElement(cx, newarr, i, rval); - if (!ok) - goto out; - break; - case FILTER: - if (!cond) - break; - /* The element passed the filter, so push it onto our result. */ - ok = SetArrayElement(cx, newarr, newlen++, tvr.value()); - if (!ok) - goto out; - break; - case SOME: - if (cond) { - vp->setBoolean(true); - goto out; - } - break; - case EVERY: - if (!cond) { - vp->setBoolean(false); - goto out; - } - break; + if (Behavior::shouldExit(ag.rval(), &args.rval())) + return true; } + + /* Step d. */ + k++; } - out: - if (ok && mode == FILTER) - ok = js_SetLengthProperty(cx, newarr, newlen); - return ok; -} + /* Step 8. */ + args.rval() = Behavior::lateExitValue(); + return true; + } +/* ES5 15.4.4.16. */ static JSBool -array_forEach(JSContext *cx, uintN argc, Value *vp) +array_every(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, FOREACH, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_readonlyCommon(cx, args); } +/* ES5 15.4.4.17. */ static JSBool -array_map(JSContext *cx, uintN argc, Value *vp) +array_some(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, MAP, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_readonlyCommon(cx, args); } +/* ES5 15.4.4.18. */ static JSBool -array_reduce(JSContext *cx, uintN argc, Value *vp) +array_forEach(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, REDUCE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_readonlyCommon(cx, args); } +/* ES5 15.4.4.19. */ static JSBool -array_reduceRight(JSContext *cx, uintN argc, Value *vp) +array_map(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, REDUCE_RIGHT, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32_t len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); + + /* Step 6. */ + JSObject *arr = NewDenseAllocatedArray(cx, len); + if (!arr) + return false; + TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); + if (!newtype) + return false; + arr->setType(newtype); + + /* Step 7. */ + uint32_t k = 0; + + /* Step 8. */ + InvokeArgsGuard ag; + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.setCallee(ObjectValue(*callable)); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + if(!SetArrayElement(cx, arr, k, ag.rval())) + return false; + } + + /* Step d. */ + k++; + } + + /* Step 9. */ + args.rval().setObject(*arr); + return true; } +/* ES5 15.4.4.20. */ static JSBool array_filter(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, FILTER, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32_t len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); + + /* Step 6. */ + JSObject *arr = NewDenseAllocatedArray(cx, 0); + if (!arr) + return false; + TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); + if (!newtype) + return false; + arr->setType(newtype); + + /* Step 7. */ + uint32_t k = 0; + + /* Step 8. */ + uint32_t to = 0; + + /* Step 9. */ + InvokeArgsGuard ag; + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.setCallee(ObjectValue(*callable)); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + + if (js_ValueToBoolean(ag.rval())) { + if(!SetArrayElement(cx, arr, to, kValue)) + return false; + to++; + } + } + + /* Step d. */ + k++; + } + + /* Step 10. */ + args.rval().setObject(*arr); + return true; +} + +/* ES5 15.4.4.21-15.4.4.22. */ +class ArrayReduceBehavior +{ + public: + static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step) + { + *start = 0; + *step = 1; + *end = len; + } +}; + +class ArrayReduceRightBehavior +{ + public: + static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step) + { + *start = len - 1; + *step = -1; + /* + * We rely on (well defined) unsigned integer underflow to check our + * end condition after visiting the full range (including 0). + */ + *end = UINT32_MAX; + } +}; + +template +static inline bool +array_reduceCommon(JSContext *cx, CallArgs &args) +{ + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32_t len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + if (len == 0 && args.length() < 2) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE); + return false; + } + + /* Step 6. */ + uint32_t k, end; + int32_t step; + Behavior::initialize(len, &k, &end, &step); + + /* Step 7-8. */ + Value accumulator; + if (args.length() >= 2) { + accumulator = args[1]; + } else { + JSBool kNotPresent = true; + while (kNotPresent && k != end) { + if (!GetElement(cx, obj, k, &kNotPresent, &accumulator)) + return false; + k += step; + } + if (kNotPresent) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE); + return false; + } + } + + /* Step 9. */ + InvokeArgsGuard ag; + while (k != end) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag)) + return false; + ag.setCallee(ObjectValue(*callable)); + ag.thisv() = UndefinedValue(); + ag[0] = accumulator; + ag[1] = kValue; + ag[2] = NumberValue(k); + ag[3] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + accumulator = ag.rval(); + } + + /* Step d. */ + k += step; + } + + /* Step 10. */ + args.rval() = accumulator; + return true; } +/* ES5 15.4.4.21. */ static JSBool -array_some(JSContext *cx, uintN argc, Value *vp) +array_reduce(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, SOME, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_reduceCommon(cx, args); } +/* ES5 15.4.4.22. */ static JSBool -array_every(JSContext *cx, uintN argc, Value *vp) +array_reduceRight(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, EVERY, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_reduceCommon(cx, args); } -#endif static JSBool array_isArray(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj; - vp->setBoolean(argc > 0 && - vp[2].isObject() && - ((obj = &vp[2].toObject())->isArray() || - (obj->isWrapper() && obj->unwrap()->isArray()))); + CallArgs args = CallArgsFromVp(argc, vp); + bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx); + args.rval().setBoolean(isArray); return true; } +#define GENERIC JSFUN_GENERIC_NATIVE + static JSFunctionSpec array_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, array_toSource, 0,0), @@ -3053,7 +3618,6 @@ static JSFunctionSpec array_methods[] = { JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE), JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE), -#if JS_HAS_ARRAY_EXTRAS JS_FN("indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE), JS_FN("lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE), JS_FN("forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE), @@ -3063,7 +3627,6 @@ static JSFunctionSpec array_methods[] = { JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE), JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE), JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE), -#endif JS_FS_END }; @@ -3073,47 +3636,101 @@ static JSFunctionSpec array_static_methods[] = { JS_FS_END }; +/* ES5 15.4.2 */ JSBool js_Array(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj; + CallArgs args = CallArgsFromVp(argc, vp); + TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array); + if (!type) + return JS_FALSE; - if (argc == 0) { - obj = NewDenseEmptyArray(cx); - } else if (argc > 1) { - obj = NewDenseCopiedArray(cx, argc, vp + 2); - } else if (!vp[2].isNumber()) { - obj = NewDenseCopiedArray(cx, 1, vp + 2); + if (args.length() != 1 || !args[0].isNumber()) { + if (!InitArrayTypes(cx, type, args.array(), args.length())) + return false; + JSObject *obj = (args.length() == 0) + ? NewDenseEmptyArray(cx) + : NewDenseCopiedArray(cx, args.length(), args.array()); + if (!obj) + return false; + obj->setType(type); + args.rval().setObject(*obj); + return true; + } + + uint32_t length; + if (args[0].isInt32()) { + int32_t i = args[0].toInt32(); + if (i < 0) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); + return false; + } + length = uint32_t(i); } else { - jsuint length; - if (!ValueToLength(cx, vp + 2, &length)) - return JS_FALSE; - obj = NewDenseUnallocatedArray(cx, length); + jsdouble d = args[0].toDouble(); + length = js_DoubleToECMAUint32(d); + if (d != jsdouble(length)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); + return false; + } } + JSObject *obj = NewDenseUnallocatedArray(cx, length); if (!obj) - return JS_FALSE; - vp->setObject(*obj); + return false; - return JS_TRUE; + obj->setType(type); + + /* If the length calculation overflowed, make sure that is marked for the new type. */ + if (obj->getArrayLength() > INT32_MAX) + obj->setArrayLength(cx, obj->getArrayLength()); + + args.rval().setObject(*obj); + return true; } JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1, - NULL, array_methods, NULL, array_static_methods); - if (!proto) + JS_ASSERT(obj->isNative()); + + RootedVar global(cx); + global = &obj->asGlobal(); + + RootedVarObject arrayProto(cx); + arrayProto = global->createBlankPrototype(cx, &SlowArrayClass); + if (!arrayProto || !AddLengthProperty(cx, arrayProto)) + return NULL; + arrayProto->setArrayLength(cx, 0); + + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, js_Array, &ArrayClass, + CLASS_ATOM(cx, Array), 1); + if (!ctor) return NULL; /* - * Assert that js_InitClass used the correct (slow array, not dense array) - * class for proto's emptyShape class. + * The default 'new' type of Array.prototype is required by type inference + * to have unknown properties, to simplify handling of e.g. heterogenous + * arrays in JSON and script literals and allows setDenseArrayElement to + * be used without updating the indexed type set for such default arrays. */ - JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass()); + if (!arrayProto->setNewTypeUnknown(cx)) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, arrayProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, arrayProto, NULL, array_methods) || + !DefinePropertiesAndBrand(cx, ctor, NULL, array_static_methods)) + { + return NULL; + } - proto->setArrayLength(0); - return proto; + if (!DefineConstructorAndPrototype(cx, global, JSProto_Array, ctor, arrayProto)) + return NULL; + + return arrayProto; } /* @@ -3121,22 +3738,84 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) */ namespace js { +static inline bool +EnsureNewArrayElements(JSContext *cx, JSObject *obj, jsuint length) +{ + /* + * If ensureElements creates dynamically allocated slots, then having + * fixedSlots is a waste. + */ + DebugOnly cap = obj->getDenseArrayCapacity(); + + if (!obj->ensureElements(cx, length)) + return false; + + JS_ASSERT_IF(cap, !obj->hasDynamicElements()); + + return true; +} + template static JS_ALWAYS_INLINE JSObject * -NewArray(JSContext *cx, jsuint length, JSObject *proto) +NewArray(JSContext *cx, uint32_t length, JSObject *proto) { - JS_ASSERT_IF(proto, proto->isArray()); + gc::AllocKind kind = GuessArrayGCKind(length); + +#ifdef JS_THREADSAFE + JS_ASSERT(CanBeFinalizedInBackground(kind, &ArrayClass)); + kind = GetBackgroundAllocKind(kind); +#endif + + GlobalObject *parent = GetCurrentGlobal(cx); + + NewObjectCache &cache = cx->compartment->newObjectCache; + + NewObjectCache::EntryIndex entry = -1; + if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) { + JSObject *obj = cache.newObjectFromHit(cx, entry); + if (!obj) + return NULL; + /* Fixup the elements pointer and length, which may be incorrect. */ + obj->setFixedElements(); + obj->setArrayLength(cx, length); + if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length)) + return NULL; + return obj; + } + + Root parentRoot(cx, &parent); + + if (!proto && !FindProto(cx, &ArrayClass, parentRoot, &proto)) + return NULL; + + RootObject protoRoot(cx, &proto); + RootedVarTypeObject type(cx); + + type = proto->getNewType(cx); + if (!type) + return NULL; - gc::FinalizeKind kind = GuessObjectGCKind(length, true); - JSObject *obj = detail::NewObject(cx, &js_ArrayClass, proto, NULL, kind); + /* + * Get a shape with zero fixed slots, regardless of the size class. + * See JSObject::createDenseArray. + */ + RootedVarShape shape(cx); + shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto, + parent, gc::FINALIZE_OBJECT0); + if (!shape) + return NULL; + + JSObject* obj = JSObject::createDenseArray(cx, kind, shape, type, length); if (!obj) return NULL; - obj->setArrayLength(length); + if (entry != -1) + cache.fillGlobal(entry, &ArrayClass, parent, kind, obj); - if (allocateCapacity && !obj->ensureSlots(cx, length)) + if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length)) return NULL; + Probes::createObject(cx, obj); return obj; } @@ -3147,19 +3826,39 @@ NewDenseEmptyArray(JSContext *cx, JSObject *proto) } JSObject * JS_FASTCALL -NewDenseAllocatedArray(JSContext *cx, uint32 length, JSObject *proto) +NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto) +{ + return NewArray(cx, length, proto); +} + +JSObject * JS_FASTCALL +NewDenseAllocatedEmptyArray(JSContext *cx, uint32_t length, JSObject *proto) { return NewArray(cx, length, proto); } JSObject * JS_FASTCALL -NewDenseUnallocatedArray(JSContext *cx, uint32 length, JSObject *proto) +NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto) { return NewArray(cx, length, proto); } +#ifdef JS_METHODJIT +JSObject * JS_FASTCALL +mjit::stubs::NewDenseUnallocatedArray(VMFrame &f, uint32_t length) +{ + JSObject *proto = (JSObject *) f.scratch; + JSObject *obj = NewArray(f.cx, length, proto); + if (!obj) { + js_ReportOutOfMemory(f.cx); + THROWV(NULL); + } + return obj; +} +#endif + JSObject * -NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto) +NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *vp, JSObject *proto /* = NULL */) { JSObject* obj = NewArray(cx, length, proto); if (!obj) @@ -3167,31 +3866,22 @@ NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto) JS_ASSERT(obj->getDenseArrayCapacity() >= length); + obj->setDenseArrayInitializedLength(vp ? length : 0); + if (vp) - memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value)); + obj->initDenseArrayElements(0, vp, length); return obj; } -#ifdef JS_TRACER -JS_DEFINE_CALLINFO_2(extern, OBJECT, NewDenseEmptyArray, CONTEXT, OBJECT, 0, - nanojit::ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseAllocatedArray, CONTEXT, UINT32, OBJECT, 0, - nanojit::ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseUnallocatedArray, CONTEXT, UINT32, OBJECT, 0, - nanojit::ACCSET_STORE_ANY) -#endif - - - JSObject * NewSlowEmptyArray(JSContext *cx) { - JSObject *obj = NewNonFunction(cx, &js_SlowArrayClass, NULL, NULL); + JSObject *obj = NewBuiltinClassInstance(cx, &SlowArrayClass); if (!obj || !AddLengthProperty(cx, obj)) return NULL; - obj->setArrayLength(0); + obj->setArrayLength(cx, 0); return obj; } @@ -3202,11 +3892,11 @@ NewSlowEmptyArray(JSContext *cx) JSBool js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp) { - uintN i; + CallArgs args = CallArgsFromVp(argc, vp); JSObject *array; - for (i = 0; i < argc; i++) { - Value arg = Valueify(JS_ARGV(cx, vp)[i]); + for (uintN i = 0; i < args.length(); i++) { + Value arg = args[i]; char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NULL); if (!bytes) @@ -3228,135 +3918,7 @@ js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp) cx->free_(bytes); } - JS_SET_RVAL(cx, vp, JSVAL_VOID); + args.rval().setUndefined(); return true; } #endif - -JS_FRIEND_API(JSBool) -js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count, - JSUint8 *dest) -{ - uint32 length; - - if (!obj || !obj->isDenseArray()) - return JS_FALSE; - - length = obj->getArrayLength(); - if (length < offset + count) - return JS_FALSE; - - JSUint8 *dp = dest; - for (uintN i = offset; i < offset+count; i++) { - const Value &v = obj->getDenseArrayElement(i); - if (v.isInt32()) { - jsint vi = v.toInt32(); - if (jsuint(vi) > 255) - vi = (vi < 0) ? 0 : 255; - *dp++ = JSUint8(vi); - } else if (v.isDouble()) { - jsdouble vd = v.toDouble(); - if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */ - *dp++ = 0; - else if (vd > 255) - *dp++ = 255; - else { - jsdouble toTruncate = vd + 0.5; - JSUint8 val = JSUint8(toTruncate); - - /* - * now val is rounded to nearest, ties rounded up. We want - * rounded to nearest ties to even, so check whether we had a - * tie. - */ - if (val == toTruncate) { - /* - * It was a tie (since adding 0.5 gave us the exact integer - * we want). Since we rounded up, we either already have an - * even number or we have an odd number but the number we - * want is one less. So just unconditionally masking out the - * ones bit should do the trick to get us the value we - * want. - */ - *dp++ = (val & ~1); - } else { - *dp++ = val; - } - } - } else { - return JS_FALSE; - } - } - - return JS_TRUE; -} - -JS_FRIEND_API(JSBool) -js_IsDensePrimitiveArray(JSObject *obj) -{ - if (!obj || !obj->isDenseArray()) - return JS_FALSE; - - jsuint capacity = obj->getDenseArrayCapacity(); - for (jsuint i = 0; i < capacity; i++) { - if (obj->getDenseArrayElement(i).isObject()) - return JS_FALSE; - } - - return JS_TRUE; -} - -JS_FRIEND_API(JSBool) -js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone) -{ - JS_ASSERT(obj); - if (!obj->isDenseArray()) { - /* - * This wasn't a dense array. Return JS_TRUE but a NULL clone to signal - * that no exception was encountered. - */ - *clone = NULL; - return JS_TRUE; - } - - jsuint length = obj->getArrayLength(); - - /* - * Must use the minimum of original array's length and capacity, to handle - * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In the normal - * case where length is <= capacity, the clone and original array will have - * the same capacity. - */ - jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length); - - AutoValueVector vector(cx); - if (!vector.reserve(jsvalCount)) - return JS_FALSE; - - for (jsuint i = 0; i < jsvalCount; i++) { - const Value &val = obj->getDenseArrayElement(i); - - if (val.isString()) { - // Strings must be made immutable before being copied to a clone. - if (!val.toString()->ensureFixed(cx)) - return JS_FALSE; - } else if (val.isObject()) { - /* - * This wasn't an array of primitives. Return JS_TRUE but a null - * clone to signal that no exception was encountered. - */ - *clone = NULL; - return JS_TRUE; - } - - vector.infallibleAppend(val); - } - - *clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin()); - if (!*clone) - return JS_FALSE; - - /* The length will be set to the JS_MIN, above, but length might be larger. */ - (*clone)->setArrayLength(length); - return JS_TRUE; -} diff --git a/deps/mozjs/js/src/jsarray.h b/deps/mozjs/js/src/jsarray.h index 1f9508c654b..dc1ebfafcd7 100644 --- a/deps/mozjs/js/src/jsarray.h +++ b/deps/mozjs/js/src/jsarray.h @@ -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 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -42,55 +42,20 @@ /* * JS Array interface. */ +#include "jscntxt.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsatom.h" #include "jsobj.h" -#include "jsstr.h" /* Small arrays are dense, no matter what. */ const uintN MIN_SPARSE_INDEX = 256; -inline JSObject::EnsureDenseResult -JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra) -{ - JS_ASSERT(isDenseArray()); - uintN currentCapacity = numSlots(); - - uintN requiredCapacity; - if (extra == 1) { - /* Optimize for the common case. */ - if (index < currentCapacity) - return ED_OK; - requiredCapacity = index + 1; - if (requiredCapacity == 0) { - /* Overflow. */ - return ED_SPARSE; - } - } else { - requiredCapacity = index + extra; - if (requiredCapacity < index) { - /* Overflow. */ - return ED_SPARSE; - } - if (requiredCapacity <= currentCapacity) - return ED_OK; - } - - /* - * We use the extra argument also as a hint about number of non-hole - * elements to be inserted. - */ - if (requiredCapacity > MIN_SPARSE_INDEX && - willBeSparseDenseArray(requiredCapacity, extra)) { - return ED_SPARSE; - } - return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED; +namespace js { +/* 2^32-2, inclusive */ +const uint32_t MAX_ARRAY_INDEX = 4294967294u; } -extern bool -js_StringIsIndex(JSLinearString *str, jsuint *indexp); - inline JSBool js_IdIsIndex(jsid id, jsuint *indexp) { @@ -106,27 +71,7 @@ js_IdIsIndex(jsid id, jsuint *indexp) if (JS_UNLIKELY(!JSID_IS_STRING(id))) return JS_FALSE; - return js_StringIsIndex(JSID_TO_ATOM(id), indexp); -} - -extern js::Class js_ArrayClass, js_SlowArrayClass; - -inline bool -JSObject::isDenseArray() const -{ - return getClass() == &js_ArrayClass; -} - -inline bool -JSObject::isSlowArray() const -{ - return getClass() == &js_SlowArrayClass; -} - -inline bool -JSObject::isArray() const -{ - return isDenseArray() || isSlowArray(); + return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp); } /* @@ -148,11 +93,8 @@ JSObject::isArray() const * Callers of js_GetProtoIfDenseArray must take care to use the original object * (obj) for the |this| value of a getter, setter, or method call (bug 476447). */ -static JS_INLINE JSObject * -js_GetProtoIfDenseArray(JSObject *obj) -{ - return obj->isDenseArray() ? obj->getProto() : obj; -} +inline JSObject * +js_GetProtoIfDenseArray(JSObject *obj); extern JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj); @@ -160,33 +102,40 @@ js_InitArrayClass(JSContext *cx, JSObject *obj); extern bool js_InitContextBusyArrayTable(JSContext *cx); -namespace js -{ +namespace js { /* Create a dense array with no capacity allocated, length set to 0. */ extern JSObject * JS_FASTCALL NewDenseEmptyArray(JSContext *cx, JSObject *proto=NULL); -/* Create a dense array with length and capacity == 'length'. */ +/* Create a dense array with length and capacity == 'length', initialized length set to 0. */ extern JSObject * JS_FASTCALL -NewDenseAllocatedArray(JSContext *cx, uint length, JSObject *proto=NULL); +NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto=NULL); + +/* + * Create a dense array with length, capacity and initialized length == 'length', and filled with holes. + * This is a kludge, as the tracer doesn't yet track/update initialized length when initializing + * array elements. + */ +extern JSObject * JS_FASTCALL +NewDenseAllocatedEmptyArray(JSContext *cx, uint32_t length, JSObject *proto=NULL); /* * Create a dense array with a set length, but without allocating space for the * contents. This is useful, e.g., when accepting length from the user. */ extern JSObject * JS_FASTCALL -NewDenseUnallocatedArray(JSContext *cx, uint length, JSObject *proto=NULL); +NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto=NULL); /* Create a dense array with a copy of vp. */ extern JSObject * -NewDenseCopiedArray(JSContext *cx, uint length, Value *vp, JSObject *proto=NULL); +NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *vp, JSObject *proto = NULL); /* Create a sparse array. */ extern JSObject * NewSlowEmptyArray(JSContext *cx); -} +} /* namespace js */ extern JSBool js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); @@ -194,84 +143,57 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); extern JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length); -extern JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); +namespace js { -extern JSBool JS_FASTCALL -js_IndexToId(JSContext *cx, jsuint index, jsid *idp); +extern JSBool +array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); -namespace js { +extern JSBool +array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict); /* + * Copy 'length' elements from aobj to vp. + * * This function assumes 'length' is effectively the result of calling * js_GetLengthProperty on aobj. */ extern bool GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp); -} +/* Natives exposed for optimization by the interpreter and JITs. */ -/* - * JS-specific merge sort function. - */ -typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, - int *result); +extern JSBool +array_sort(JSContext *cx, uintN argc, js::Value *vp); -enum JSMergeSortElemType { - JS_SORTING_VALUES, - JS_SORTING_GENERIC -}; +extern JSBool +array_push(JSContext *cx, uintN argc, js::Value *vp); -/* - * NB: vec is the array to be sorted, tmp is temporary space at least as big - * as vec. Both should be GC-rooted if appropriate. - * - * isValue should true iff vec points to an array of js::Value - * - * The sorted result is in vec. vec may be in an inconsistent state if the - * comparator function cmp returns an error inside a comparison, so remember - * to check the return value of this function. - */ -extern bool -js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, - void *arg, void *tmp, JSMergeSortElemType elemType); +extern JSBool +array_pop(JSContext *cx, uintN argc, js::Value *vp); -/* - * The Array.prototype.sort fast-native entry point is exported for joined - * function optimization in js{interp,tracer}.cpp. - */ -namespace js { extern JSBool -array_sort(JSContext *cx, uintN argc, js::Value *vp); -} +array_concat(JSContext *cx, uintN argc, js::Value *vp); + +extern JSBool +array_shift(JSContext *cx, uintN argc, js::Value *vp); + +} /* namespace js */ #ifdef DEBUG extern JSBool js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp); #endif -extern JSBool -js_ArrayCompPush(JSContext *cx, JSObject *obj, const js::Value &vp); - /* - * Fast dense-array-to-buffer conversion for use by canvas. - * - * If the array is a dense array, fill [offset..offset+count] values into - * destination, assuming that types are consistent. Return JS_TRUE if - * successful, otherwise JS_FALSE -- note that the destination buffer may be - * modified even if JS_FALSE is returned (e.g. due to finding an inappropriate - * type later on in the array). If JS_FALSE is returned, no error conditions - * or exceptions are set on the context. - * - * This method succeeds if each element of the array is an integer or a double. - * Values outside the 0-255 range are clamped to that range. Double values are - * converted to integers in this range by clamping and then rounding to - * nearest, ties to even. + * Append the given (non-hole) value to the end of an array. The array must be + * a newborn array -- that is, one which has not been exposed to script for + * arbitrary manipulation. (This method optimizes on the assumption that + * extending the array to accommodate the element will never make the array + * sparse, which requires that the array be completely filled.) */ - -JS_FRIEND_API(JSBool) -js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count, - JSUint8 *dest); +extern JSBool +js_NewbornArrayPush(JSContext *cx, JSObject *obj, const js::Value &v); JSBool js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj); @@ -287,27 +209,4 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, JSBool js_Array(JSContext *cx, uintN argc, js::Value *vp); -/* - * Makes a fast clone of a dense array as long as the array only contains - * primitive values. - * - * If the return value is JS_FALSE then clone will not be set. - * - * If the return value is JS_TRUE then clone will either be set to the address - * of a new JSObject or to NULL if the array was not dense or contained values - * that were not primitives. - */ -JS_FRIEND_API(JSBool) -js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone); - -/* - * Returns JS_TRUE if the given object is a dense array that contains only - * primitive values. - */ -JS_FRIEND_API(JSBool) -js_IsDensePrimitiveArray(JSObject *obj); - -extern JSBool JS_FASTCALL -js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i); - #endif /* jsarray_h___ */ diff --git a/deps/mozjs/js/src/jsarrayinlines.h b/deps/mozjs/js/src/jsarrayinlines.h new file mode 100644 index 00000000000..eeaec6b9911 --- /dev/null +++ b/deps/mozjs/js/src/jsarrayinlines.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsarrayinlines_h___ +#define jsarrayinlines_h___ + +#include "jsinferinlines.h" +#include "jsobjinlines.h" + +inline void +JSObject::markDenseArrayNotPacked(JSContext *cx) +{ + JS_ASSERT(isDenseArray()); + MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY); +} + +inline void +JSObject::ensureDenseArrayInitializedLength(JSContext *cx, uint32_t index, uint32_t extra) +{ + /* + * Ensure that the array's contents have been initialized up to index, and + * mark the elements through 'index + extra' as initialized in preparation + * for a write. + */ + JS_ASSERT(index + extra <= getDenseArrayCapacity()); + uint32_t &initlen = getElementsHeader()->initializedLength; + if (initlen < index) + markDenseArrayNotPacked(cx); + + if (initlen < index + extra) { + js::InitValueRange(elements + initlen, index + extra - initlen, true); + initlen = index + extra; + } +} + +inline JSObject::EnsureDenseResult +JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra) +{ + JS_ASSERT(isDenseArray()); + + uintN currentCapacity = getDenseArrayCapacity(); + + uintN requiredCapacity; + if (extra == 1) { + /* Optimize for the common case. */ + if (index < currentCapacity) { + ensureDenseArrayInitializedLength(cx, index, 1); + return ED_OK; + } + requiredCapacity = index + 1; + if (requiredCapacity == 0) { + /* Overflow. */ + return ED_SPARSE; + } + } else { + requiredCapacity = index + extra; + if (requiredCapacity < index) { + /* Overflow. */ + return ED_SPARSE; + } + if (requiredCapacity <= currentCapacity) { + ensureDenseArrayInitializedLength(cx, index, extra); + return ED_OK; + } + } + + /* + * We use the extra argument also as a hint about number of non-hole + * elements to be inserted. + */ + if (requiredCapacity > MIN_SPARSE_INDEX && + willBeSparseDenseArray(requiredCapacity, extra)) { + return ED_SPARSE; + } + if (!growElements(cx, requiredCapacity)) + return ED_FAILED; + + ensureDenseArrayInitializedLength(cx, index, extra); + return ED_OK; +} + +#endif /* jsarrayinlines_h___ */ diff --git a/deps/mozjs/js/src/jsatom.cpp b/deps/mozjs/js/src/jsatom.cpp index c6083796785..e92801b3a09 100644 --- a/deps/mozjs/js/src/jsatom.cpp +++ b/deps/mozjs/js/src/jsatom.cpp @@ -42,53 +42,46 @@ */ #include #include + +#include "mozilla/RangedPtr.h" +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jshash.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" -#include "jsbit.h" #include "jscntxt.h" #include "jsgc.h" #include "jsgcmark.h" #include "jslock.h" #include "jsnum.h" -#include "jsparse.h" #include "jsstr.h" #include "jsversion.h" #include "jsxml.h" +#include "frontend/Parser.h" + #include "jsstrinlines.h" #include "jsatominlines.h" #include "jsobjinlines.h" +#include "vm/String-inl.h" + +using namespace mozilla; using namespace js; using namespace js::gc; +const size_t JSAtomState::commonAtomsOffset = offsetof(JSAtomState, emptyAtom); +const size_t JSAtomState::lazyAtomsOffset = offsetof(JSAtomState, lazy); + /* * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems. */ JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); JS_STATIC_ASSERT(sizeof(JSAtom *) == JS_BYTES_PER_WORD); -/* - * Start and limit offsets for atom pointers in JSAtomState must be aligned - * on the word boundary. - */ -JS_STATIC_ASSERT(ATOM_OFFSET_START % sizeof(JSAtom *) == 0); -JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT % sizeof(JSAtom *) == 0); - -/* - * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the - * index 1 and type name starts from the index 1+2 atoms in JSAtomState. - */ -JS_STATIC_ASSERT(1 * sizeof(JSAtom *) == - offsetof(JSAtomState, booleanAtoms) - ATOM_OFFSET_START); -JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom *) == - offsetof(JSAtomState, typeAtoms) - ATOM_OFFSET_START); - const char * js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes) { @@ -134,6 +127,7 @@ const char *const js_common_atom_names[] = { js_apply_str, /* applyAtom */ js_arguments_str, /* argumentsAtom */ js_arity_str, /* arityAtom */ + js_BYTES_PER_ELEMENT_str, /* BYTES_PER_ELEMENTAtom */ js_call_str, /* callAtom */ js_callee_str, /* calleeAtom */ js_caller_str, /* callerAtom */ @@ -160,6 +154,7 @@ const char *const js_common_atom_names[] = { js_noSuchMethod_str, /* noSuchMethodAtom */ "[object Null]", /* objectNullAtom */ "[object Undefined]", /* objectUndefinedAtom */ + "of", /* ofAtom */ js_proto_str, /* protoAtom */ js_set_str, /* setAtom */ js_source_str, /* sourceAtom */ @@ -214,11 +209,36 @@ const char *const js_common_atom_names[] = { "keys", /* keysAtom */ "iterate", /* iterateAtom */ - "WeakMap" /* WeakMapAtom */ + "WeakMap", /* WeakMapAtom */ + + "byteLength", /* byteLengthAtom */ + + "return", /* returnAtom */ + "throw" /* throwAtom */ }; -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) == - LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START); +void +JSAtomState::checkStaticInvariants() +{ + /* + * Start and limit offsets for atom pointers in JSAtomState must be aligned + * on the word boundary. + */ + JS_STATIC_ASSERT(commonAtomsOffset % sizeof(JSAtom *) == 0); + JS_STATIC_ASSERT(sizeof(*this) % sizeof(JSAtom *) == 0); + + /* + * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the + * index 1 and type name starts from the index 1+2 atoms in JSAtomState. + */ + JS_STATIC_ASSERT(1 * sizeof(JSAtom *) == + offsetof(JSAtomState, booleanAtoms) - commonAtomsOffset); + JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom *) == + offsetof(JSAtomState, typeAtoms) - commonAtomsOffset); + + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) == + lazyAtomsOffset - commonAtomsOffset); +} /* * Interpreter macros called by the trace recorder assume common atom indexes @@ -232,6 +252,7 @@ const char js_anonymous_str[] = "anonymous"; const char js_apply_str[] = "apply"; const char js_arguments_str[] = "arguments"; const char js_arity_str[] = "arity"; +const char js_BYTES_PER_ELEMENT_str[] = "BYTES_PER_ELEMENT"; const char js_call_str[] = "call"; const char js_callee_str[] = "callee"; const char js_caller_str[] = "caller"; @@ -295,40 +316,6 @@ const char js_close_str[] = "close"; const char js_send_str[] = "send"; #endif -/* - * Helper macros to access and modify JSAtomHashEntry. - */ - -inline AtomEntryType -StringToInitialAtomEntry(JSString *str) -{ - return (AtomEntryType) str; -} - -inline uintN -AtomEntryFlags(AtomEntryType entry) -{ - return (uintN) (entry & ATOM_ENTRY_FLAG_MASK); -} - -/* - * Conceptually, we have compressed a HashMap into a - * HashMap. Here, we promise that we are only changing the "value" of - * the HashMap entry, so the const_cast is safe. - */ - -inline void -AddAtomEntryFlags(const AtomEntryType &entry, uintN flags) -{ - const_cast(entry) |= AtomEntryType(flags); -} - -inline void -ClearAtomEntryFlags(const AtomEntryType &entry, uintN flags) -{ - const_cast(entry) &= ~AtomEntryType(flags); -} - /* * For a browser build from 2007-08-09 after the browser starts up there are * just 55 double atoms, but over 15000 string atoms. Not to penalize more @@ -346,11 +333,8 @@ js_InitAtomState(JSRuntime *rt) if (!state->atoms.init(JS_STRING_HASH_COUNT)) return false; -#ifdef JS_THREADSAFE - js_InitLock(&state->lock); -#endif JS_ASSERT(state->atoms.initialized()); - return JS_TRUE; + return true; } void @@ -367,152 +351,174 @@ js_FinishAtomState(JSRuntime *rt) } for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) - AtomEntryToKey(r.front())->finalize(rt); - -#ifdef JS_THREADSAFE - js_FinishLock(&state->lock); -#endif + r.front().asPtr()->finalize(rt); } -JSBool +bool js_InitCommonAtoms(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; - uintN i; - JSAtom **atoms; - - atoms = COMMON_ATOMS_START(state); - for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) { - *atoms = js_Atomize(cx, js_common_atom_names[i], - strlen(js_common_atom_names[i]), ATOM_PINNED); - if (!*atoms) - return JS_FALSE; + JSAtom **atoms = state->commonAtomsStart(); + for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) { + JSAtom *atom = js_Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]), + InternAtom); + if (!atom) + return false; + *atoms = atom->asPropertyName(); } - JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START); - memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START); + state->clearLazyAtoms(); cx->runtime->emptyString = state->emptyAtom; - return JS_TRUE; + return true; } void js_FinishCommonAtoms(JSContext *cx) { cx->runtime->emptyString = NULL; - JSAtomState *state = &cx->runtime->atomState; - - for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) - ClearAtomEntryFlags(r.front(), ATOM_PINNED); - -#ifdef DEBUG - memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN, - ATOM_OFFSET_LIMIT - ATOM_OFFSET_START); -#endif + cx->runtime->atomState.junkAtoms(); } void js_TraceAtomState(JSTracer *trc) { - JSRuntime *rt = trc->context->runtime; + JSRuntime *rt = trc->runtime; JSAtomState *state = &rt->atomState; -#ifdef DEBUG - size_t number = 0; -#endif - if (rt->gcKeepAtoms) { - for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { - JS_SET_TRACING_INDEX(trc, "locked_atom", number++); - MarkString(trc, AtomEntryToKey(r.front())); - } + for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) + MarkStringRoot(trc, r.front().asPtr(), "locked_atom"); } else { for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { - AtomEntryType entry = r.front(); - uintN flags = AtomEntryFlags(entry); - if (flags & (ATOM_PINNED | ATOM_INTERNED)) { - JS_SET_TRACING_INDEX(trc, - flags & ATOM_PINNED - ? "pinned_atom" - : "interned_atom", - number++); - MarkString(trc, AtomEntryToKey(entry)); - } + AtomStateEntry entry = r.front(); + if (!entry.isTagged()) + continue; + + MarkStringRoot(trc, entry.asPtr(), "interned_atom"); } } } void -js_SweepAtomState(JSContext *cx) +js_SweepAtomState(JSRuntime *rt) { - JSAtomState *state = &cx->runtime->atomState; + JSAtomState *state = &rt->atomState; for (AtomSet::Enum e(state->atoms); !e.empty(); e.popFront()) { - AtomEntryType entry = e.front(); - if (AtomEntryFlags(entry) & (ATOM_PINNED | ATOM_INTERNED)) { + AtomStateEntry entry = e.front(); + + if (entry.isTagged()) { /* Pinned or interned key cannot be finalized. */ - JS_ASSERT(!IsAboutToBeFinalized(cx, AtomEntryToKey(entry))); - } else if (IsAboutToBeFinalized(cx, AtomEntryToKey(entry))) { - e.removeFront(); + JS_ASSERT(!IsAboutToBeFinalized(entry.asPtr())); + continue; } + + if (IsAboutToBeFinalized(entry.asPtr())) + e.removeFront(); } } +bool +AtomIsInterned(JSContext *cx, JSAtom *atom) +{ + /* We treat static strings as interned because they're never collected. */ + if (StaticStrings::isStatic(atom)) + return true; + + AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom); + if (!p) + return false; + + return p->isTagged(); +} + +enum OwnCharsBehavior +{ + CopyChars, /* in other words, do not take ownership */ + TakeCharOwnership +}; + /* - * This call takes ownership of 'chars' if ATOM_NOCOPY is set. + * Callers passing OwnChars have freshly allocated *pchars and thus this + * memory can be used as a new JSAtom's buffer without copying. When this flag + * is set, the contract is that callers will free *pchars iff *pchars == NULL. */ +JS_ALWAYS_INLINE static JSAtom * -Atomize(JSContext *cx, const jschar *chars, size_t length, uintN flags) +AtomizeInline(JSContext *cx, const jschar **pchars, size_t length, + InternBehavior ib, OwnCharsBehavior ocb = CopyChars) { - JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_NOCOPY))); + const jschar *chars = *pchars; - if (JSAtom *s = JSAtom::lookupStatic(chars, length)) + if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length)) return s; - AutoLockAtomsCompartment lock(cx); - AtomSet &atoms = cx->runtime->atomState.atoms; AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length)); - JSAtom *atom; if (p) { - atom = AtomEntryToKey(*p); - } else { - SwitchToCompartment sc(cx, cx->runtime->atomsCompartment); - - JSFixedString *key; - if (flags & ATOM_NOCOPY) { - key = js_NewString(cx, const_cast(chars), length); - if (!key) { - cx->free_(const_cast(chars)); - return NULL; - } - } else { - key = js_NewStringCopyN(cx, chars, length); - if (!key) - return NULL; - } + JSAtom *atom = p->asPtr(); + p->setTagged(bool(ib)); + return atom; + } - /* - * We have to relookup the key as the last ditch GC invoked from the - * string allocation or OOM handling may unlock the atomsCompartment. - */ - AtomHasher::Lookup lookup(chars, length); - if (!atoms.relookupOrAdd(p, lookup, StringToInitialAtomEntry(key))) { - JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */ + SwitchToCompartment sc(cx, cx->runtime->atomsCompartment); + + JSFixedString *key; + + if (ocb == TakeCharOwnership) { + key = js_NewString(cx, const_cast(chars), length); + if (!key) return NULL; - } + *pchars = NULL; /* Called should not free *pchars. */ + } else { + JS_ASSERT(ocb == CopyChars); + key = js_NewStringCopyN(cx, chars, length); + if (!key) + return NULL; + } - atom = key->morphInternedStringIntoAtom(); + /* + * We have to relookup the key as the last ditch GC invoked from the + * string allocation or OOM handling unlocks the atomsCompartment. + * + * N.B. this avoids recomputing the hash but still has a potential + * (# collisions * # chars) comparison cost in the case of a hash + * collision! + */ + AtomHasher::Lookup lookup(chars, length); + if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry((JSAtom *) key, bool(ib)))) { + JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */ + return NULL; } - AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED)); - return atom; + return key->morphAtomizedStringIntoAtom(); +} + +static JSAtom * +Atomize(JSContext *cx, const jschar **pchars, size_t length, + InternBehavior ib, OwnCharsBehavior ocb = CopyChars) +{ + return AtomizeInline(cx, pchars, length, ib, ocb); } JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags) +js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib) { - JS_ASSERT(!(flags & ATOM_NOCOPY)); + if (str->isAtom()) { + JSAtom &atom = str->asAtom(); + /* N.B. static atoms are effectively always interned. */ + if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) + return &atom; + + AtomSet &atoms = cx->runtime->atomState.atoms; + AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom)); + JS_ASSERT(p); /* Non-static atom must exist in atom state set. */ + JS_ASSERT(p->asPtr() == &atom); + JS_ASSERT(ib == InternAtom); + p->setTagged(bool(ib)); + return &atom; + } if (str->isAtom()) return &str->asAtom(); @@ -523,20 +529,19 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) return NULL; JS_ASSERT(length <= JSString::MAX_LENGTH); - return Atomize(cx, chars, length, flags); + return Atomize(cx, &chars, length, ib); } JSAtom * -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags, bool useCESU8) +js_Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, FlationCoding fc) { - JS_ASSERT(!(flags & ATOM_NOCOPY)); CHECK_REQUEST(cx); - if (!CheckStringLength(cx, length)) + if (!JSString::validateLength(cx, length)) return NULL; /* - * Avoiding the malloc in js_InflateString on shorter strings saves us + * Avoiding the malloc in InflateString on shorter strings saves us * over 20,000 malloc calls on mozilla browser startup. This compares to * only 131 calls where the string is longer than a 31 char (net) buffer. * The vast majority of atomized strings are already in the hashtable. So @@ -547,44 +552,47 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags, bool us size_t inflatedLength = ATOMIZE_BUF_MAX - 1; const jschar *chars; + OwnCharsBehavior ocb = CopyChars; if (length < ATOMIZE_BUF_MAX) { - if (useCESU8) - js_InflateUTF8StringToBuffer(cx, bytes, length, inflated, &inflatedLength, true); + if (fc == CESU8Encoding) + InflateUTF8StringToBuffer(cx, bytes, length, inflated, &inflatedLength, fc); else - js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); + InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); inflated[inflatedLength] = 0; chars = inflated; } else { inflatedLength = length; - chars = js_InflateString(cx, bytes, &inflatedLength, useCESU8); + chars = InflateString(cx, bytes, &inflatedLength, fc); if (!chars) return NULL; - flags |= ATOM_NOCOPY; + ocb = TakeCharOwnership; } - return Atomize(cx, chars, inflatedLength, flags); + JSAtom *atom = Atomize(cx, &chars, inflatedLength, ib, ocb); + if (ocb == TakeCharOwnership && chars) + cx->free_((void *)chars); + return atom; } JSAtom * -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib) { - JS_ASSERT(!(flags & ATOM_NOCOPY)); CHECK_REQUEST(cx); - if (!CheckStringLength(cx, length)) + if (!JSString::validateLength(cx, length)) return NULL; - return Atomize(cx, chars, length, flags); + return AtomizeInline(cx, &chars, length, ib); } JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { - if (JSAtom *atom = JSAtom::lookupStatic(chars, length)) + if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length)) return atom; - AutoLockAtomsCompartment lock(cx); - AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)); - return p ? AtomEntryToKey(*p) : NULL; + if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length))) + return p->asPtr(); + return NULL; } #ifdef DEBUG @@ -596,34 +604,18 @@ js_DumpAtoms(JSContext *cx, FILE *fp) fprintf(fp, "atoms table contents:\n"); unsigned number = 0; for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { - AtomEntryType entry = r.front(); + AtomStateEntry entry = r.front(); fprintf(fp, "%3u ", number++); - if (entry == 0) { - fputs("", fp); - } else { - JSAtom *key = AtomEntryToKey(entry); - FileEscapedString(fp, key, '"'); - uintN flags = AtomEntryFlags(entry); - if (flags != 0) { - fputs((flags & (ATOM_PINNED | ATOM_INTERNED)) - ? " pinned | interned" - : (flags & ATOM_PINNED) ? " pinned" : " interned", - fp); - } - } + JSAtom *key = entry.asPtr(); + FileEscapedString(fp, key, '"'); + if (entry.isTagged()) + fputs(" interned", fp); putc('\n', fp); } putc('\n', fp); } #endif -static JSHashNumber -js_hash_atom_ptr(const void *key) -{ - const JSAtom *atom = (const JSAtom *) key; - return ATOM_HASH(atom); -} - #if JS_BITS_PER_WORD == 32 # define TEMP_SIZE_START_LOG2 5 #else @@ -636,314 +628,120 @@ js_hash_atom_ptr(const void *key) JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable)); -static void * -js_alloc_temp_space(void *priv, size_t size) +void +js_InitAtomMap(JSContext *cx, AtomIndexMap *indices, JSAtom **atoms) { - Parser *parser = (Parser *) priv; - - void *space; - if (size < TEMP_SIZE_LIMIT) { - int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2; - JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS); - - space = parser->tempFreeList[bin]; - if (space) { - parser->tempFreeList[bin] = *(void **)space; - return space; + if (indices->isMap()) { + typedef AtomIndexMap::WordMap WordMap; + const WordMap &wm = indices->asMap(); + for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) { + JSAtom *atom = r.front().key; + jsatomid index = r.front().value; + JS_ASSERT(index < indices->count()); + atoms[index] = atom; + } + } else { + for (const AtomIndexMap::InlineElem *it = indices->asInline(), *end = indices->inlineEnd(); + it != end; ++it) { + JSAtom *atom = it->key; + if (!atom) + continue; + JS_ASSERT(it->value < indices->count()); + atoms[it->value] = atom; } } - - JS_ARENA_ALLOCATE(space, &parser->context->tempPool, size); - if (!space) - js_ReportOutOfScriptQuota(parser->context); - return space; } -static void -js_free_temp_space(void *priv, void *item, size_t size) -{ - if (size >= TEMP_SIZE_LIMIT) - return; - - Parser *parser = (Parser *) priv; - int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2; - JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS); - - *(void **)item = parser->tempFreeList[bin]; - parser->tempFreeList[bin] = item; -} +namespace js { -static JSHashEntry * -js_alloc_temp_entry(void *priv, const void *key) +bool +IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp) { - Parser *parser = (Parser *) priv; - JSAtomListElement *ale; + JS_ASSERT(index > JSID_INT_MAX); - ale = parser->aleFreeList; - if (ale) { - parser->aleFreeList = ALE_NEXT(ale); - return &ale->entry; - } + jschar buf[UINT32_CHAR_BUFFER_LENGTH]; + RangedPtr end(ArrayEnd(buf), buf, ArrayEnd(buf)); + RangedPtr start = BackfillIndexInCharBuffer(index, end); - JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &parser->context->tempPool); - if (!ale) { - js_ReportOutOfScriptQuota(parser->context); - return NULL; - } - return &ale->entry; -} - -static void -js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) -{ - Parser *parser = (Parser *) priv; - JSAtomListElement *ale = (JSAtomListElement *) he; + JSAtom *atom = js_AtomizeChars(cx, start.get(), end - start); + if (!atom) + return false; - ALE_SET_NEXT(ale, parser->aleFreeList); - parser->aleFreeList = ale; + *idp = ATOM_TO_JSID(atom); + JS_ASSERT(js_CheckForStringIndex(*idp) == *idp); + return true; } -static JSHashAllocOps temp_alloc_ops = { - js_alloc_temp_space, js_free_temp_space, - js_alloc_temp_entry, js_free_temp_entry -}; +} /* namespace js */ + +/* JSBOXEDWORD_INT_MAX as a string */ +#define JSBOXEDWORD_INT_MAX_STRING "1073741823" -JSAtomListElement * -JSAtomList::rawLookup(JSAtom *atom, JSHashEntry **&hep) +/* + * Convert string indexes that convert to int jsvals as ints to save memory. + * Care must be taken to use this macro every time a property name is used, or + * else double-sets, incorrect property cache misses, or other mistakes could + * occur. + */ +jsid +js_CheckForStringIndex(jsid id) { - if (table) { - hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom); - return (JSAtomListElement *) *hep; - } + if (!JSID_IS_ATOM(id)) + return id; - JSHashEntry **alep = &list; - hep = NULL; - JSAtomListElement *ale; - while ((ale = (JSAtomListElement *)*alep) != NULL) { - if (ALE_ATOM(ale) == atom) { - /* Hit, move atom's element to the front of the list. */ - *alep = ale->entry.next; - ale->entry.next = list; - list = &ale->entry; - break; - } - alep = &ale->entry.next; - } - return ale; -} + JSAtom *atom = JSID_TO_ATOM(id); + const jschar *s = atom->chars(); + jschar ch = *s; -#define ATOM_LIST_HASH_THRESHOLD 12 + JSBool negative = (ch == '-'); + if (negative) + ch = *++s; -JSAtomListElement * -JSAtomList::add(Parser *parser, JSAtom *atom, AddHow how) -{ - JS_ASSERT(!set); - - JSAtomListElement *ale, *ale2, *next; - JSHashEntry **hep; - - ale = rawLookup(atom, hep); - if (!ale || how != UNIQUE) { - if (count < ATOM_LIST_HASH_THRESHOLD && !table) { - /* Few enough for linear search and no hash table yet needed. */ - ale = (JSAtomListElement *)js_alloc_temp_entry(parser, atom); - if (!ale) - return NULL; - ALE_SET_ATOM(ale, atom); - - if (how == HOIST) { - ale->entry.next = NULL; - hep = (JSHashEntry **) &list; - while (*hep) - hep = &(*hep)->next; - *hep = &ale->entry; - } else { - ale->entry.next = list; - list = &ale->entry; - } - } else { - /* - * We should hash, or else we already are hashing, but count was - * reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD. - * Check whether we should create the table. - */ - if (!table) { - /* No hash table yet, so hep had better be null! */ - JS_ASSERT(!hep); - table = JS_NewHashTable(count + 1, js_hash_atom_ptr, - JS_CompareValues, JS_CompareValues, - &temp_alloc_ops, parser); - if (!table) - return NULL; - - /* - * Set ht->nentries explicitly, because we are moving entries - * from list to ht, not calling JS_HashTable(Raw|)Add. - */ - table->nentries = count; - - /* - * Insert each ale on list into the new hash table. Append to - * the hash chain rather than inserting at the bucket head, to - * preserve order among entries with the same key. - */ - for (ale2 = (JSAtomListElement *)list; ale2; ale2 = next) { - next = ALE_NEXT(ale2); - ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2)); - hep = JS_HashTableRawLookup(table, ale2->entry.keyHash, - ale2->entry.key); - while (*hep) - hep = &(*hep)->next; - *hep = &ale2->entry; - ale2->entry.next = NULL; - } - list = NULL; - - /* Set hep for insertion of atom's ale, immediately below. */ - hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom); - } - - /* Finally, add an entry for atom into the hash bucket at hep. */ - ale = (JSAtomListElement *) - JS_HashTableRawAdd(table, hep, ATOM_HASH(atom), atom, NULL); - if (!ale) - return NULL; - - /* - * If hoisting, move ale to the end of its chain after we called - * JS_HashTableRawAdd, since RawAdd may have grown the table and - * then recomputed hep to refer to the pointer to the first entry - * with the given key. - */ - if (how == HOIST && ale->entry.next) { - JS_ASSERT(*hep == &ale->entry); - *hep = ale->entry.next; - ale->entry.next = NULL; - do { - hep = &(*hep)->next; - } while (*hep); - *hep = &ale->entry; - } - } + if (!JS7_ISDEC(ch)) + return id; - ALE_SET_INDEX(ale, count++); - } - return ale; -} + size_t n = atom->length() - negative; + if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1) + return id; -void -JSAtomList::rawRemove(Parser *parser, JSAtomListElement *ale, JSHashEntry **hep) -{ - JS_ASSERT(!set); - JS_ASSERT(count != 0); + const jschar *cp = s; + const jschar *end = s + n; - if (table) { - JS_ASSERT(hep); - JS_HashTableRawRemove(table, hep, &ale->entry); - } else { - JS_ASSERT(!hep); - hep = &list; - while (*hep != &ale->entry) { - JS_ASSERT(*hep); - hep = &(*hep)->next; + jsuint index = JS7_UNDEC(*cp++); + jsuint oldIndex = 0; + jsuint c = 0; + + if (index != 0) { + while (JS7_ISDEC(*cp)) { + oldIndex = index; + c = JS7_UNDEC(*cp); + index = 10 * index + c; + cp++; } - *hep = ale->entry.next; - js_free_temp_entry(parser, &ale->entry, HT_FREE_ENTRY); } - --count; -} - -JSAutoAtomList::~JSAutoAtomList() -{ - if (table) { - JS_HashTableDestroy(table); + /* + * Non-integer indexes can't be represented as integers. Also, distinguish + * index "-0" from "0", because JSBOXEDWORD_INT cannot. + */ + if (cp != end || (negative && index == 0)) + return id; + + if (negative) { + if (oldIndex < -(JSID_INT_MIN / 10) || + (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10))) + { + id = INT_TO_JSID(-jsint(index)); + } } else { - JSHashEntry *hep = list; - while (hep) { - JSHashEntry *next = hep->next; - js_free_temp_entry(parser, hep, HT_FREE_ENTRY); - hep = next; + if (oldIndex < JSID_INT_MAX / 10 || + (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) + { + id = INT_TO_JSID(jsint(index)); } } -} -JSAtomListElement * -JSAtomListIterator::operator ()() -{ - JSAtomListElement *ale; - JSHashTable *ht; - - if (index == uint32(-1)) - return NULL; - - ale = next; - if (!ale) { - ht = list->table; - if (!ht) - goto done; - do { - if (index == JS_BIT(JS_HASH_BITS - ht->shift)) - goto done; - next = (JSAtomListElement *) ht->buckets[index++]; - } while (!next); - ale = next; - } - - next = ALE_NEXT(ale); - return ale; - - done: - index = uint32(-1); - return NULL; -} - -static intN -js_map_atom(JSHashEntry *he, intN i, void *arg) -{ - JSAtomListElement *ale = (JSAtomListElement *)he; - JSAtom **vector = (JSAtom **) arg; - - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - return HT_ENUMERATE_NEXT; -} - -#ifdef DEBUG -static jsrefcount js_atom_map_count; -static jsrefcount js_atom_map_hash_table_count; -#endif - -void -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) -{ - JSAtom **vector; - JSAtomListElement *ale; - uint32 count; - - /* Map length must already be initialized. */ - JS_ASSERT(al->count == map->length); -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_count); -#endif - ale = (JSAtomListElement *)al->list; - if (!ale && !al->table) { - JS_ASSERT(!map->vector); - return; - } - - count = al->count; - vector = map->vector; - if (al->table) { -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); -#endif - JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); - } else { - do { - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - } while ((ale = ALE_NEXT(ale)) != NULL); - } - al->clear(); + return id; } #if JS_HAS_XML_SUPPORT @@ -957,9 +755,7 @@ js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval, return true; } - if (!js_IsFunctionQName(cx, &idval.toObject(), idp)) - return JS_FALSE; - if (!JSID_IS_VOID(*idp)) + if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) return true; return js_ValueToStringId(cx, idval, idp); @@ -977,9 +773,7 @@ js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval, return true; } - if (!js_IsFunctionQName(cx, &idval.toObject(), idp)) - return JS_FALSE; - if (!JSID_IS_VOID(*idp)) { + if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) { *vp = IdToValue(*idp); return true; } diff --git a/deps/mozjs/js/src/jsatom.h b/deps/mozjs/js/src/jsatom.h index 108ecbf53c2..93dc888bb20 100644 --- a/deps/mozjs/js/src/jsatom.h +++ b/deps/mozjs/js/src/jsatom.h @@ -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 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -39,24 +39,23 @@ #ifndef jsatom_h___ #define jsatom_h___ -/* - * JS atom table. - */ + #include #include "jsversion.h" +#include "jsalloc.h" #include "jsapi.h" #include "jsprvtd.h" #include "jshash.h" -#include "jshashtable.h" -#include "jsnum.h" #include "jspubtd.h" -#include "jsstr.h" #include "jslock.h" -#include "jsvalue.h" -#define ATOM_PINNED 0x1 /* atom is pinned against GC */ -#define ATOM_INTERNED 0x2 /* pinned variant for JS_Intern* API */ -#define ATOM_NOCOPY 0x4 /* don't copy atom string bytes */ +#include "gc/Barrier.h" +#include "js/HashTable.h" + +struct JSIdArray { + jsint length; + js::HeapId vector[1]; /* actually, length jsid words */ +}; /* Engine-internal extensions of jsid */ @@ -94,8 +93,29 @@ JSID_TO_ATOM(jsid id) return (JSAtom *)JSID_TO_STRING(id); } +extern jsid +js_CheckForStringIndex(jsid id); + +JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); +JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); + namespace js { +static JS_ALWAYS_INLINE JSHashNumber +HashId(jsid id) +{ + JS_ASSERT(js_CheckForStringIndex(id) == id); + JSHashNumber n = +#if JS_BYTES_PER_WORD == 4 + JSHashNumber(JSID_BITS(id)); +#elif JS_BYTES_PER_WORD == 8 + JSHashNumber(JSID_BITS(id)) ^ JSHashNumber(JSID_BITS(id) >> 32); +#else +# error "Unsupported configuration" +#endif + return n * JS_GOLDEN_RATIO; +} + static JS_ALWAYS_INLINE Value IdToValue(jsid id) { @@ -112,17 +132,7 @@ IdToValue(jsid id) static JS_ALWAYS_INLINE jsval IdToJsval(jsid id) { - return Jsvalify(IdToValue(id)); -} - -static JS_ALWAYS_INLINE JSString * -IdToString(JSContext *cx, jsid id) -{ - if (JSID_IS_STRING(id)) - return JSID_TO_STRING(id); - if (JS_LIKELY(JSID_IS_INT(id))) - return js_IntToString(cx, JSID_TO_INT(id)); - return js_ValueToString(cx, IdToValue(id)); + return IdToValue(id); } template<> @@ -131,7 +141,7 @@ struct DefaultHasher typedef jsid Lookup; static HashNumber hash(const Lookup &l) { JS_ASSERT(l == js_CheckForStringIndex(l)); - return JSID_BITS(l); + return HashNumber(JSID_BITS(l)); } static bool match(const jsid &id, const Lookup &l) { JS_ASSERT(l == js_CheckForStringIndex(l)); @@ -144,8 +154,8 @@ struct DefaultHasher #if JS_BYTES_PER_WORD == 4 # define ATOM_HASH(atom) ((JSHashNumber)(atom) >> 2) #elif JS_BYTES_PER_WORD == 8 -# define ATOM_HASH(atom) (((JSHashNumber)(jsuword)(atom) >> 3) ^ \ - (JSHashNumber)((jsuword)(atom) >> 32)) +# define ATOM_HASH(atom) (((JSHashNumber)(uintptr_t)(atom) >> 3) ^ \ + (JSHashNumber)((uintptr_t)(atom) >> 32)) #else # error "Unsupported configuration" #endif @@ -157,163 +167,90 @@ struct DefaultHasher extern const char * js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes); -struct JSAtomListElement { - JSHashEntry entry; -}; - -#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) -#define ALE_INDEX(ale) (jsatomid(uintptr_t((ale)->entry.value))) -#define ALE_VALUE(ale) ((jsboxedword) (ale)->entry.value) -#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) - -/* - * In an upvars list, ALE_DEFN(ale)->resolve() is the outermost definition the - * name may reference. If a with block or a function that calls eval encloses - * the use, the name may end up referring to something else at runtime. - */ -#define ALE_DEFN(ale) ((JSDefinition *) (ale)->entry.value) - -#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) -#define ALE_SET_INDEX(ale,index)((ale)->entry.value = (void *)(index)) -#define ALE_SET_DEFN(ale, dn) ((ale)->entry.value = (void *)(dn)) -#define ALE_SET_VALUE(ale, v) ((ale)->entry.value = (void *)(v)) -#define ALE_SET_NEXT(ale,nxt) ((ale)->entry.next = (JSHashEntry *)(nxt)) +namespace js { -/* - * NB: JSAtomSet must be plain-old-data as it is embedded in the pn_u union in - * JSParseNode. JSAtomList encapsulates all operational uses of a JSAtomSet. - * - * The JSAtomList name is traditional, even though the implementation is a map - * (not to be confused with JSAtomMap). In particular the "ALE" and "ale" short - * names for JSAtomListElement variables roll off the fingers, compared to ASE - * or AME alternatives. - */ -struct JSAtomSet { - JSHashEntry *list; /* literals indexed for mapping */ - JSHashTable *table; /* hash table if list gets too long */ - jsuint count; /* count of indexed literals */ -}; +/* Compute a hash function from chars/length. */ +inline uint32_t +HashChars(const jschar *chars, size_t length) +{ + uint32_t h = 0; + for (; length; chars++, length--) + h = JS_ROTATE_LEFT32(h, 4) ^ *chars; + return h; +} -struct JSAtomList : public JSAtomSet +class AtomStateEntry { -#ifdef DEBUG - const JSAtomSet* set; /* asserted null in mutating methods */ -#endif + uintptr_t bits; - JSAtomList() { - list = NULL; table = NULL; count = 0; -#ifdef DEBUG - set = NULL; -#endif - } + static const uintptr_t NO_TAG_MASK = uintptr_t(-1) - 1; - JSAtomList(const JSAtomSet& as) { - list = as.list; table = as.table; count = as.count; -#ifdef DEBUG - set = &as; -#endif + public: + AtomStateEntry() : bits(0) {} + AtomStateEntry(const AtomStateEntry &other) : bits(other.bits) {} + AtomStateEntry(JSAtom *ptr, bool tagged) + : bits(uintptr_t(ptr) | uintptr_t(tagged)) + { + JS_ASSERT((uintptr_t(ptr) & 0x1) == 0); } - void clear() { JS_ASSERT(!set); list = NULL; table = NULL; count = 0; } - - JSAtomListElement *lookup(JSAtom *atom) { - JSHashEntry **hep; - return rawLookup(atom, hep); + bool isTagged() const { + return bits & 0x1; } - JSAtomListElement *rawLookup(JSAtom *atom, JSHashEntry **&hep); - - enum AddHow { UNIQUE, SHADOW, HOIST }; - - JSAtomListElement *add(js::Parser *parser, JSAtom *atom, AddHow how = UNIQUE); - - void remove(js::Parser *parser, JSAtom *atom) { - JSHashEntry **hep; - JSAtomListElement *ale = rawLookup(atom, hep); - if (ale) - rawRemove(parser, ale, hep); + /* + * Non-branching code sequence. Note that the const_cast is safe because + * the hash function doesn't consider the tag to be a portion of the key. + */ + void setTagged(bool enabled) const { + const_cast(this)->bits |= uintptr_t(enabled); } - void rawRemove(js::Parser *parser, JSAtomListElement *ale, JSHashEntry **hep); + JSAtom *asPtr() const; }; -/* - * A subclass of JSAtomList with a destructor. This atom list owns its - * hash table and its entries, but no keys or values. - */ -struct JSAutoAtomList: public JSAtomList +struct AtomHasher { - JSAutoAtomList(js::Parser *p): parser(p) {} - ~JSAutoAtomList(); - private: - js::Parser *parser; /* For freeing list entries. */ -}; - -/* - * Iterate over an atom list. We define a call operator to minimize the syntax - * tax for users. We do not use a more standard pattern using ++ and * because - * (a) it's the wrong pattern for a non-scalar; (b) it's overkill -- one method - * is enough. (This comment is overkill!) - */ -class JSAtomListIterator { - JSAtomList* list; - JSAtomListElement* next; - uint32 index; - - public: - JSAtomListIterator(JSAtomList* al) : list(al) { reset(); } - - void reset() { - next = (JSAtomListElement *) list->list; - index = 0; - } + struct Lookup + { + const jschar *chars; + size_t length; + const JSAtom *atom; /* Optional. */ - JSAtomListElement* operator ()(); -}; + Lookup(const jschar *chars, size_t length) : chars(chars), length(length), atom(NULL) {} + inline Lookup(const JSAtom *atom); + }; -struct JSAtomMap { - JSAtom **vector; /* array of ptrs to indexed atoms */ - jsatomid length; /* count of (to-be-)indexed atoms */ + static HashNumber hash(const Lookup &l) { return HashChars(l.chars, l.length); } + static inline bool match(const AtomStateEntry &entry, const Lookup &lookup); }; -namespace js { - -#define ATOM_ENTRY_FLAG_MASK ((size_t)(ATOM_PINNED | ATOM_INTERNED)) - -JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN); - -typedef uintptr_t AtomEntryType; +typedef HashSet AtomSet; -static JS_ALWAYS_INLINE JSAtom * -AtomEntryToKey(AtomEntryType entry) -{ - JS_ASSERT(entry != 0); - return (JSAtom *)(entry & ~ATOM_ENTRY_FLAG_MASK); -} +/* + * On encodings: + * + * - Some string functions have an optional FlationCoding argument that allow + * the caller to force CESU-8 encoding handling. + * - Functions that don't take a FlationCoding base their NormalEncoding + * behavior on the js_CStringsAreUTF8 value. NormalEncoding is either raw + * (simple zero-extension) or UTF-8 depending on js_CStringsAreUTF8. + * - Functions that explicitly state their encoding do not use the + * js_CStringsAreUTF8 value. + * + * CESU-8 (Compatibility Encoding Scheme for UTF-16: 8-bit) is a variant of + * UTF-8 that allows us to store any wide character string as a narrow + * character string. For strings containing mostly ascii, it saves space. + * http://www.unicode.org/reports/tr26/ + */ -struct AtomHasher +enum FlationCoding { - struct Lookup - { - const jschar *chars; - size_t length; - Lookup(const jschar *chars, size_t length) : chars(chars), length(length) {} - }; - - static HashNumber hash(const Lookup &l) { - return HashChars(l.chars, l.length); - } - - static bool match(AtomEntryType entry, const Lookup &lookup) { - JS_ASSERT(entry); - JSAtom *key = AtomEntryToKey(entry); - if (key->length() != lookup.length) - return false; - return PodEqual(key->chars(), lookup.chars, lookup.length); - } + NormalEncoding, + CESU8Encoding }; -typedef HashSet AtomSet; +class PropertyName; } /* namespace js */ @@ -321,171 +258,188 @@ struct JSAtomState { js::AtomSet atoms; -#ifdef JS_THREADSAFE - JSThinLock lock; -#endif - /* * From this point until the end of struct definition the struct must - * contain only JSAtom fields. We use this to access the storage occupied - * by the common atoms in js_FinishCommonAtoms. + * contain only js::PropertyName fields. We use this to access the storage + * occupied by the common atoms in js_FinishCommonAtoms. * - * js_common_atom_names defined in jsatom.c contains C strings for atoms + * js_common_atom_names defined in jsatom.cpp contains C strings for atoms * in the order of atom fields here. Therefore you must update that array * if you change member order here. */ /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ - JSAtom *emptyAtom; + js::PropertyName *emptyAtom; /* * Literal value and type names. * NB: booleanAtoms must come right before typeAtoms! */ - JSAtom *booleanAtoms[2]; - JSAtom *typeAtoms[JSTYPE_LIMIT]; - JSAtom *nullAtom; + js::PropertyName *booleanAtoms[2]; + js::PropertyName *typeAtoms[JSTYPE_LIMIT]; + js::PropertyName *nullAtom; /* Standard class constructor or prototype names. */ - JSAtom *classAtoms[JSProto_LIMIT]; + js::PropertyName *classAtoms[JSProto_LIMIT]; /* Various built-in or commonly-used atoms, pinned on first context. */ - JSAtom *anonymousAtom; - JSAtom *applyAtom; - JSAtom *argumentsAtom; - JSAtom *arityAtom; - JSAtom *callAtom; - JSAtom *calleeAtom; - JSAtom *callerAtom; - JSAtom *classPrototypeAtom; - JSAtom *constructorAtom; - JSAtom *eachAtom; - JSAtom *evalAtom; - JSAtom *fileNameAtom; - JSAtom *getAtom; - JSAtom *globalAtom; - JSAtom *ignoreCaseAtom; - JSAtom *indexAtom; - JSAtom *inputAtom; - JSAtom *toISOStringAtom; - JSAtom *iteratorAtom; - JSAtom *joinAtom; - JSAtom *lastIndexAtom; - JSAtom *lengthAtom; - JSAtom *lineNumberAtom; - JSAtom *messageAtom; - JSAtom *multilineAtom; - JSAtom *nameAtom; - JSAtom *nextAtom; - JSAtom *noSuchMethodAtom; - JSAtom *objectNullAtom; - JSAtom *objectUndefinedAtom; - JSAtom *protoAtom; - JSAtom *setAtom; - JSAtom *sourceAtom; - JSAtom *stackAtom; - JSAtom *stickyAtom; - JSAtom *toGMTStringAtom; - JSAtom *toLocaleStringAtom; - JSAtom *toSourceAtom; - JSAtom *toStringAtom; - JSAtom *toUTCStringAtom; - JSAtom *valueOfAtom; - JSAtom *toJSONAtom; - JSAtom *void0Atom; - JSAtom *enumerableAtom; - JSAtom *configurableAtom; - JSAtom *writableAtom; - JSAtom *valueAtom; - JSAtom *testAtom; - JSAtom *useStrictAtom; - JSAtom *locAtom; - JSAtom *lineAtom; - JSAtom *InfinityAtom; - JSAtom *NaNAtom; - JSAtom *builderAtom; + js::PropertyName *anonymousAtom; + js::PropertyName *applyAtom; + js::PropertyName *argumentsAtom; + js::PropertyName *arityAtom; + js::PropertyName *BYTES_PER_ELEMENTAtom; + js::PropertyName *callAtom; + js::PropertyName *calleeAtom; + js::PropertyName *callerAtom; + js::PropertyName *classPrototypeAtom; + js::PropertyName *constructorAtom; + js::PropertyName *eachAtom; + js::PropertyName *evalAtom; + js::PropertyName *fileNameAtom; + js::PropertyName *getAtom; + js::PropertyName *globalAtom; + js::PropertyName *ignoreCaseAtom; + js::PropertyName *indexAtom; + js::PropertyName *inputAtom; + js::PropertyName *toISOStringAtom; + js::PropertyName *iteratorAtom; + js::PropertyName *joinAtom; + js::PropertyName *lastIndexAtom; + js::PropertyName *lengthAtom; + js::PropertyName *lineNumberAtom; + js::PropertyName *messageAtom; + js::PropertyName *multilineAtom; + js::PropertyName *nameAtom; + js::PropertyName *nextAtom; + js::PropertyName *noSuchMethodAtom; + js::PropertyName *objectNullAtom; + js::PropertyName *objectUndefinedAtom; + js::PropertyName *ofAtom; + js::PropertyName *protoAtom; + js::PropertyName *setAtom; + js::PropertyName *sourceAtom; + js::PropertyName *stackAtom; + js::PropertyName *stickyAtom; + js::PropertyName *toGMTStringAtom; + js::PropertyName *toLocaleStringAtom; + js::PropertyName *toSourceAtom; + js::PropertyName *toStringAtom; + js::PropertyName *toUTCStringAtom; + js::PropertyName *valueOfAtom; + js::PropertyName *toJSONAtom; + js::PropertyName *void0Atom; + js::PropertyName *enumerableAtom; + js::PropertyName *configurableAtom; + js::PropertyName *writableAtom; + js::PropertyName *valueAtom; + js::PropertyName *testAtom; + js::PropertyName *useStrictAtom; + js::PropertyName *locAtom; + js::PropertyName *lineAtom; + js::PropertyName *InfinityAtom; + js::PropertyName *NaNAtom; + js::PropertyName *builderAtom; #if JS_HAS_XML_SUPPORT - JSAtom *etagoAtom; - JSAtom *namespaceAtom; - JSAtom *ptagcAtom; - JSAtom *qualifierAtom; - JSAtom *spaceAtom; - JSAtom *stagoAtom; - JSAtom *starAtom; - JSAtom *starQualifierAtom; - JSAtom *tagcAtom; - JSAtom *xmlAtom; + js::PropertyName *etagoAtom; + js::PropertyName *namespaceAtom; + js::PropertyName *ptagcAtom; + js::PropertyName *qualifierAtom; + js::PropertyName *spaceAtom; + js::PropertyName *stagoAtom; + js::PropertyName *starAtom; + js::PropertyName *starQualifierAtom; + js::PropertyName *tagcAtom; + js::PropertyName *xmlAtom; /* Represents an invalid URI, for internal use only. */ - JSAtom *functionNamespaceURIAtom; + js::PropertyName *functionNamespaceURIAtom; #endif - JSAtom *ProxyAtom; + js::PropertyName *ProxyAtom; + + js::PropertyName *getOwnPropertyDescriptorAtom; + js::PropertyName *getPropertyDescriptorAtom; + js::PropertyName *definePropertyAtom; + js::PropertyName *deleteAtom; + js::PropertyName *getOwnPropertyNamesAtom; + js::PropertyName *enumerateAtom; + js::PropertyName *fixAtom; - JSAtom *getOwnPropertyDescriptorAtom; - JSAtom *getPropertyDescriptorAtom; - JSAtom *definePropertyAtom; - JSAtom *deleteAtom; - JSAtom *getOwnPropertyNamesAtom; - JSAtom *enumerateAtom; - JSAtom *fixAtom; + js::PropertyName *hasAtom; + js::PropertyName *hasOwnAtom; + js::PropertyName *keysAtom; + js::PropertyName *iterateAtom; - JSAtom *hasAtom; - JSAtom *hasOwnAtom; - JSAtom *keysAtom; - JSAtom *iterateAtom; + js::PropertyName *WeakMapAtom; - JSAtom *WeakMapAtom; + js::PropertyName *byteLengthAtom; + + js::PropertyName *returnAtom; + js::PropertyName *throwAtom; /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { - JSAtom *XMLListAtom; - JSAtom *decodeURIAtom; - JSAtom *decodeURIComponentAtom; - JSAtom *defineGetterAtom; - JSAtom *defineSetterAtom; - JSAtom *encodeURIAtom; - JSAtom *encodeURIComponentAtom; - JSAtom *escapeAtom; - JSAtom *hasOwnPropertyAtom; - JSAtom *isFiniteAtom; - JSAtom *isNaNAtom; - JSAtom *isPrototypeOfAtom; - JSAtom *isXMLNameAtom; - JSAtom *lookupGetterAtom; - JSAtom *lookupSetterAtom; - JSAtom *parseFloatAtom; - JSAtom *parseIntAtom; - JSAtom *propertyIsEnumerableAtom; - JSAtom *unescapeAtom; - JSAtom *unevalAtom; - JSAtom *unwatchAtom; - JSAtom *watchAtom; + js::PropertyName *XMLListAtom; + js::PropertyName *decodeURIAtom; + js::PropertyName *decodeURIComponentAtom; + js::PropertyName *defineGetterAtom; + js::PropertyName *defineSetterAtom; + js::PropertyName *encodeURIAtom; + js::PropertyName *encodeURIComponentAtom; + js::PropertyName *escapeAtom; + js::PropertyName *hasOwnPropertyAtom; + js::PropertyName *isFiniteAtom; + js::PropertyName *isNaNAtom; + js::PropertyName *isPrototypeOfAtom; + js::PropertyName *isXMLNameAtom; + js::PropertyName *lookupGetterAtom; + js::PropertyName *lookupSetterAtom; + js::PropertyName *parseFloatAtom; + js::PropertyName *parseIntAtom; + js::PropertyName *propertyIsEnumerableAtom; + js::PropertyName *unescapeAtom; + js::PropertyName *unevalAtom; + js::PropertyName *unwatchAtom; + js::PropertyName *watchAtom; } lazy; + + static const size_t commonAtomsOffset; + static const size_t lazyAtomsOffset; + + void clearLazyAtoms() { + memset(&lazy, 0, sizeof(lazy)); + } + + void junkAtoms() { +#ifdef DEBUG + memset(commonAtomsStart(), JS_FREE_PATTERN, sizeof(*this) - commonAtomsOffset); +#endif + } + + JSAtom **commonAtomsStart() { + return reinterpret_cast(&emptyAtom); + } + + void checkStaticInvariants(); }; -#define ATOM(name) cx->runtime->atomState.name##Atom +extern bool +AtomIsInterned(JSContext *cx, JSAtom *atom); -#define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom) -#define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy) -#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState)) +#define ATOM(name) cx->runtime->atomState.name##Atom -#define COMMON_ATOMS_START(state) \ - ((JSAtom **)((uint8 *)(state) + ATOM_OFFSET_START)) #define COMMON_ATOM_INDEX(name) \ - ((offsetof(JSAtomState, name##Atom) - ATOM_OFFSET_START) \ + ((offsetof(JSAtomState, name##Atom) - JSAtomState::commonAtomsOffset) \ / sizeof(JSAtom*)) #define COMMON_TYPE_ATOM_INDEX(type) \ - ((offsetof(JSAtomState, typeAtoms[type]) - ATOM_OFFSET_START) \ + ((offsetof(JSAtomState, typeAtoms[type]) - JSAtomState::commonAtomsOffset)\ / sizeof(JSAtom*)) #define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) #define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) -#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) - -#define CLASS_ATOM(cx,name) \ - ((cx)->runtime->atomState.classAtoms[JSProto_##name]) +#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState, classAtoms[JSProto_##name]) +#define CLASS_ATOM(cx,name) ((cx)->runtime->atomState.classAtoms[JSProto_##name]) extern const char *const js_common_atom_names[]; extern const size_t js_common_atom_count; @@ -505,6 +459,7 @@ extern const char js_anonymous_str[]; extern const char js_apply_str[]; extern const char js_arguments_str[]; extern const char js_arity_str[]; +extern const char js_BYTES_PER_ELEMENT_str[]; extern const char js_call_str[]; extern const char js_callee_str[]; extern const char js_caller_str[]; @@ -586,26 +541,36 @@ extern void js_TraceAtomState(JSTracer *trc); extern void -js_SweepAtomState(JSContext *cx); +js_SweepAtomState(JSRuntime *rt); -extern JSBool +extern bool js_InitCommonAtoms(JSContext *cx); extern void js_FinishCommonAtoms(JSContext *cx); -/* - * Find or create the atom for a string. Return null on failure to allocate - * memory. - */ +namespace js { + +/* N.B. must correspond to boolean tagging behavior. */ +enum InternBehavior +{ + DoNotInternAtom = false, + InternAtom = true +}; + +} /* namespace js */ + extern JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags); +js_Atomize(JSContext *cx, const char *bytes, size_t length, + js::InternBehavior ib = js::DoNotInternAtom, + js::FlationCoding fc = js::NormalEncoding); extern JSAtom * -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags, bool useCESU8 = false); +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, + js::InternBehavior ib = js::DoNotInternAtom); extern JSAtom * -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); +js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom); /* * Return an existing atom for the given char array or null if the char @@ -633,12 +598,13 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval, inline bool js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval, jsid *idp, js::Value *vp); + /* * For all unmapped atoms recorded in al, add a mapping from the atom's index * to its address. map->length must already be set to the number of atoms in * the list and map->vector must point to pre-allocated memory. */ extern void -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); +js_InitAtomMap(JSContext *cx, js::AtomIndexMap *indices, JSAtom **atoms); #endif /* jsatom_h___ */ diff --git a/deps/mozjs/js/src/jsatominlines.h b/deps/mozjs/js/src/jsatominlines.h index ee1d10e0c89..a63ba028b92 100644 --- a/deps/mozjs/js/src/jsatominlines.h +++ b/deps/mozjs/js/src/jsatominlines.h @@ -42,16 +42,30 @@ #include "jsatom.h" #include "jsnum.h" +#include "jsobj.h" +#include "jsstr.h" + +#include "mozilla/RangedPtr.h" +#include "vm/String.h" + +inline JSAtom * +js::AtomStateEntry::asPtr() const +{ + JS_ASSERT(bits != 0); + JSAtom *atom = reinterpret_cast(bits & NO_TAG_MASK); + JSString::readBarrier(atom); + return atom; +} inline bool js_ValueToAtom(JSContext *cx, const js::Value &v, JSAtom **atomp) { if (!v.isString()) { - JSString *str = js_ValueToString(cx, v); + JSString *str = js::ToStringSlow(cx, v); if (!str) return false; JS::Anchor anchor(str); - *atomp = js_AtomizeString(cx, str, 0); + *atomp = js_AtomizeString(cx, str); return !!*atomp; } @@ -61,7 +75,7 @@ js_ValueToAtom(JSContext *cx, const js::Value &v, JSAtom **atomp) return true; } - *atomp = js_AtomizeString(cx, str, 0); + *atomp = js_AtomizeString(cx, str); return !!*atomp; } @@ -116,7 +130,7 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval, } inline bool -js_Int32ToId(JSContext* cx, int32 index, jsid* id) +js_Int32ToId(JSContext* cx, int32_t index, jsid* id) { if (INT_FITS_IN_JSID(index)) { *id = INT_TO_JSID(index); @@ -130,4 +144,82 @@ js_Int32ToId(JSContext* cx, int32 index, jsid* id) return js_ValueToStringId(cx, js::StringValue(str), id); } +namespace js { + +/* + * Write out character representing |index| to the memory just before |end|. + * Thus |*end| is not touched, but |end[-1]| and earlier are modified as + * appropriate. There must be at least js::UINT32_CHAR_BUFFER_LENGTH elements + * before |end| to avoid buffer underflow. The start of the characters written + * is returned and is necessarily before |end|. + */ +template +inline mozilla::RangedPtr +BackfillIndexInCharBuffer(uint32_t index, mozilla::RangedPtr end) +{ +#ifdef DEBUG + /* + * Assert that the buffer we're filling will hold as many characters as we + * could write out, by dereferencing the index that would hold the most + * significant digit. + */ + (void) *(end - UINT32_CHAR_BUFFER_LENGTH); +#endif + + do { + uint32_t next = index / 10, digit = index % 10; + *--end = '0' + digit; + index = next; + } while (index > 0); + + return end; +} + +inline bool +IndexToId(JSContext *cx, uint32_t index, jsid *idp) +{ + if (index <= JSID_INT_MAX) { + *idp = INT_TO_JSID(index); + return true; + } + + extern bool IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp); + return IndexToIdSlow(cx, index, idp); +} + +static JS_ALWAYS_INLINE JSFlatString * +IdToString(JSContext *cx, jsid id) +{ + if (JSID_IS_STRING(id)) + return JSID_TO_ATOM(id); + + JSString *str; + if (JS_LIKELY(JSID_IS_INT(id))) + str = js_IntToString(cx, JSID_TO_INT(id)); + else + str = ToStringSlow(cx, IdToValue(id)); + + if (!str) + return NULL; + return str->ensureFlat(cx); +} + +inline +AtomHasher::Lookup::Lookup(const JSAtom *atom) + : chars(atom->chars()), length(atom->length()), atom(atom) +{} + +inline bool +AtomHasher::match(const AtomStateEntry &entry, const Lookup &lookup) +{ + JSAtom *key = entry.asPtr(); + if (lookup.atom) + return lookup.atom == key; + if (key->length() != lookup.length) + return false; + return PodEqual(key->chars(), lookup.chars, lookup.length); +} + +} // namespace js + #endif /* jsatominlines_h___ */ diff --git a/deps/mozjs/js/src/jsbit.h b/deps/mozjs/js/src/jsbit.h deleted file mode 100644 index de1e2912e2d..00000000000 --- a/deps/mozjs/js/src/jsbit.h +++ /dev/null @@ -1,305 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsbit_h___ -#define jsbit_h___ - -#include "jstypes.h" -#include "jscompat.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* -** A jsbitmap_t is a long integer that can be used for bitmaps -*/ -typedef jsuword jsbitmap_t; /* NSPR name, a la Unix system types */ -typedef jsbitmap_t jsbitmap; /* JS-style scalar typedef name */ - -#define JS_BITMAP_SIZE(bits) (JS_HOWMANY(bits, JS_BITS_PER_WORD) * \ - sizeof(jsbitmap)) - -#define JS_TEST_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & \ - ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) -#define JS_SET_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= \ - ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) -#define JS_CLEAR_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= \ - ~((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) - -/* -** Compute the log of the least power of 2 greater than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); - -/* -** Compute the log of the greatest power of 2 less than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); - -/* - * Replace bit-scanning code sequences with CPU-specific instructions to - * speedup calculations of ceiling/floor log2. - * - * With GCC 3.4 or later we can use __builtin_clz for that, see bug 327129. - * - * SWS: Added MSVC intrinsic bitscan support. See bugs 349364 and 356856. - */ -#if defined(_WIN32) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) - -unsigned char _BitScanForward(unsigned long * Index, unsigned long Mask); -unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); -# pragma intrinsic(_BitScanForward,_BitScanReverse) - -__forceinline static int -__BitScanForward32(unsigned int val) -{ - unsigned long idx; - - _BitScanForward(&idx, (unsigned long)val); - return (int)idx; -} -__forceinline static int -__BitScanReverse32(unsigned int val) -{ - unsigned long idx; - - _BitScanReverse(&idx, (unsigned long)val); - return (int)(31-idx); -} -# define js_bitscan_ctz32(val) __BitScanForward32(val) -# define js_bitscan_clz32(val) __BitScanReverse32(val) -# define JS_HAS_BUILTIN_BITSCAN32 - -#if defined(_M_AMD64) || defined(_M_X64) -unsigned char _BitScanForward64(unsigned long * Index, unsigned __int64 Mask); -unsigned char _BitScanReverse64(unsigned long * Index, unsigned __int64 Mask); -# pragma intrinsic(_BitScanForward64,_BitScanReverse64) - -__forceinline static int -__BitScanForward64(unsigned __int64 val) -{ - unsigned long idx; - - _BitScanForward64(&idx, val); - return (int)idx; -} -__forceinline static int -__BitScanReverse64(unsigned __int64 val) -{ - unsigned long idx; - - _BitScanReverse64(&idx, val); - return (int)(63-idx); -} -# define js_bitscan_ctz64(val) __BitScanForward64(val) -# define js_bitscan_clz64(val) __BitScanReverse64(val) -# define JS_HAS_BUILTIN_BITSCAN64 -#endif -#elif (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - -# define js_bitscan_ctz32(val) __builtin_ctz(val) -# define js_bitscan_clz32(val) __builtin_clz(val) -# define JS_HAS_BUILTIN_BITSCAN32 -# if (JS_BYTES_PER_WORD == 8) -# define js_bitscan_ctz64(val) __builtin_ctzll(val) -# define js_bitscan_clz64(val) __builtin_clzll(val) -# define JS_HAS_BUILTIN_BITSCAN64 -# endif - -#endif - -/* -** Macro version of JS_CeilingLog2: Compute the log of the least power of -** 2 greater than or equal to _n. The result is returned in _log2. -*/ -#ifdef JS_HAS_BUILTIN_BITSCAN32 -/* - * Use intrinsic function or count-leading-zeros to calculate ceil(log2(_n)). - * The macro checks for "n <= 1" and not "n != 0" as js_bitscan_clz32(0) is - * undefined. - */ -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - unsigned int j_ = (unsigned int)(_n); \ - (_log2) = (j_ <= 1 ? 0 : 32 - js_bitscan_clz32(j_ - 1)); \ - JS_END_MACRO -#else -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) & ((j_)-1)) \ - (_log2) += 1; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* -** Macro version of JS_FloorLog2: Compute the log of the greatest power of -** 2 less than or equal to _n. The result is returned in _log2. -** -** This is equivalent to finding the highest set bit in the word. -*/ -#ifdef JS_HAS_BUILTIN_BITSCAN32 -/* - * Use js_bitscan_clz32 or count-leading-zeros to calculate floor(log2(_n)). - * Since js_bitscan_clz32(0) is undefined, the macro set the loweset bit to 1 - * to ensure 0 result when _n == 0. - */ -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - (_log2) = 31 - js_bitscan_clz32(((unsigned int)(_n)) | 1); \ - JS_END_MACRO -#else -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* - * Internal function. - * Compute the log of the least power of 2 greater than or equal to n. This is - * a version of JS_CeilingLog2 that operates on unsigned integers with - * CPU-dependant size. - */ -#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) - -/* - * Internal function. - * Compute the log of the greatest power of 2 less than or equal to n. - * This is a version of JS_FloorLog2 that operates on unsigned integers with - * CPU-dependant size and requires that n != 0. - */ -#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) - -#if JS_BYTES_PER_WORD == 4 - -# ifdef JS_HAS_BUILTIN_BITSCAN32 -# define js_FloorLog2wImpl(n) \ - ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n))) -# else -# define js_FloorLog2wImpl(n) ((size_t)JS_FloorLog2(n)) -#endif - -#elif JS_BYTES_PER_WORD == 8 - -# ifdef JS_HAS_BUILTIN_BITSCAN64 -# define js_FloorLog2wImpl(n) \ - ((size_t)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n))) -# else -extern size_t js_FloorLog2wImpl(size_t n); -# endif - -#else - -# error "NOT SUPPORTED" - -#endif - -namespace js { - -inline size_t -CountTrailingZeros(size_t n) -{ - JS_ASSERT(n != 0); -#if JS_BYTES_PER_WORD != 4 && JS_BYTES_PER_WORD != 8 -# error "NOT SUPPORTED" -#endif - -#if JS_BYTES_PER_WORD == 4 && defined JS_HAS_BUILTIN_BITSCAN32 - return js_bitscan_ctz32(n); -#elif JS_BYTES_PER_WORD == 8 && defined JS_HAS_BUILTIN_BITSCAN64 - return js_bitscan_ctz64(n); -#else - size_t count = 0; -# if JS_BYTES_PER_WORD == 8 - if (!(n & size_t(0xFFFFFFFFU))) { count += 32; n >>= 32; } -# endif - if (!(n & 0xFFFF)) { count += 16; n >>= 16; } - if (!(n & 0xFF)) { count += 8; n >>= 8; } - if (!(n & 0xF)) { count += 4; n >>= 4; } - if (!(n & 0x3)) { count += 2; n >>= 2; } - if (!(n & 0x1)) { count += 1; n >>= 1; } - return count + 1 - (n & 0x1); -#endif -} - -} - -/* - * Macros for rotate left. There is no rotate operation in the C Language so - * the construct (a << 4) | (a >> 28) is used instead. Most compilers convert - * this to a rotate instruction but some versions of MSVC don't without a - * little help. To get MSVC to generate a rotate instruction, we have to use - * the _rotl intrinsic and use a pragma to make _rotl inline. - * - * MSVC in VS2005 will do an inline rotate instruction on the above construct. - */ - -#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \ - defined(_M_X64)) -#include -#pragma intrinsic(_rotl) -#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits) -#else -#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) -#endif - -JS_END_EXTERN_C -#endif /* jsbit_h___ */ diff --git a/deps/mozjs/js/src/jsbool.cpp b/deps/mozjs/js/src/jsbool.cpp index 264bb833e95..c1fdd201ea0 100644 --- a/deps/mozjs/js/src/jsbool.cpp +++ b/deps/mozjs/js/src/jsbool.cpp @@ -41,54 +41,57 @@ * JS boolean implementation. */ #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jsapi.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" +#include "jsinfer.h" #include "jsversion.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" -#include "jsvector.h" -#include "jsinterpinlines.h" +#include "vm/BooleanObject-inl.h" +#include "vm/GlobalObject.h" + +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" using namespace js; +using namespace js::types; -Class js_BooleanClass = { +Class js::BooleanClass = { "Boolean", - JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub }; #if JS_HAS_TOSOURCE -#include "jsprf.h" - static JSBool bool_toSource(JSContext *cx, uintN argc, Value *vp) { - bool b; - if (!GetPrimitiveThis(cx, vp, &b)) + CallArgs args = CallArgsFromVp(argc, vp); + + bool b, ok; + if (!BoxedPrimitiveMethodGuard(cx, args, bool_toSource, &b, &ok)) + return ok; + + StringBuffer sb(cx); + if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(cx, b, sb) || !sb.append("))")) return false; - char buf[32]; - JS_snprintf(buf, sizeof buf, "(new Boolean(%s))", JS_BOOLEAN_STR(b)); - JSString *str = JS_NewStringCopyZ(cx, buf); + JSString *str = sb.finishString(); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } #endif @@ -96,27 +99,27 @@ bool_toSource(JSContext *cx, uintN argc, Value *vp) static JSBool bool_toString(JSContext *cx, uintN argc, Value *vp) { - bool b; - if (!GetPrimitiveThis(cx, vp, &b)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - JSAtom *atom = cx->runtime->atomState.booleanAtoms[b ? 1 : 0]; - JSString *str = atom; - if (!str) - return JS_FALSE; - vp->setString(str); - return JS_TRUE; + bool b, ok; + if (!BoxedPrimitiveMethodGuard(cx, args, bool_toString, &b, &ok)) + return ok; + + args.rval().setString(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); + return true; } static JSBool bool_valueOf(JSContext *cx, uintN argc, Value *vp) { - bool b; - if (!GetPrimitiveThis(cx, vp, &b)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - vp->setBoolean(b); - return JS_TRUE; + bool b, ok; + if (!BoxedPrimitiveMethodGuard(cx, args, bool_valueOf, &b, &ok)) + return ok; + + args.rval().setBoolean(b); + return true; } static JSFunctionSpec boolean_methods[] = { @@ -125,24 +128,21 @@ static JSFunctionSpec boolean_methods[] = { #endif JS_FN(js_toString_str, bool_toString, 0, 0), JS_FN(js_valueOf_str, bool_valueOf, 0, 0), - JS_FN(js_toJSON_str, bool_valueOf, 0, 0), JS_FS_END }; static JSBool Boolean(JSContext *cx, uintN argc, Value *vp) { - Value *argv = vp + 2; - bool b = argc != 0 ? js_ValueToBoolean(argv[0]) : false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool b = args.length() != 0 ? js_ValueToBoolean(args[0]) : false; if (IsConstructing(vp)) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_BooleanClass); - if (!obj) - return false; - obj->setPrimitiveThis(BooleanValue(b)); - vp->setObject(*obj); + JSObject *obj = BooleanObject::create(cx, b); + args.rval().setObject(*obj); } else { - vp->setBoolean(b); + args.rval().setBoolean(b); } return true; } @@ -150,14 +150,30 @@ Boolean(JSContext *cx, uintN argc, Value *vp) JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj) { - JSObject *proto; + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *booleanProto = global->createBlankPrototype(cx, &BooleanClass); + if (!booleanProto) + return NULL; + booleanProto->setPrimitiveThis(BooleanValue(false)); - proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, - NULL, boolean_methods, NULL, NULL); - if (!proto) + JSFunction *ctor = global->createConstructor(cx, Boolean, &BooleanClass, + CLASS_ATOM(cx, Boolean), 1); + if (!ctor) return NULL; - proto->setPrimitiveThis(BooleanValue(false)); - return proto; + + if (!LinkConstructorAndPrototype(cx, ctor, booleanProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, booleanProto, NULL, boolean_methods)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_Boolean, ctor, booleanProto)) + return NULL; + + return booleanProto; } JSString * @@ -173,6 +189,33 @@ js::BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb) return b ? sb.append("true") : sb.append("false"); } +namespace js { + +bool +BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp) +{ + JS_ASSERT(ObjectClassIs(obj, ESClass_Boolean, cx)); + JS_ASSERT(obj.isProxy()); + + /* + * To respect the proxy abstraction, we can't simply unwrap and call + * getPrimitiveThis on the wrapped object. All we know is that obj says + * its [[Class]] is "Boolean". Boolean.prototype.valueOf is specified to + * return the [[PrimitiveValue]] internal property, so call that instead. + */ + InvokeArgsGuard ag; + if (!cx->stack.pushInvokeArgs(cx, 0, &ag)) + return false; + ag.calleev().setUndefined(); + ag.thisv().setObject(obj); + if (!GetProxyHandler(&obj)->nativeCall(cx, &obj, &BooleanClass, bool_valueOf, ag)) + return false; + *vp = ag.rval(); + return true; +} + +} /* namespace js */ + JSBool js_ValueToBoolean(const Value &v) { diff --git a/deps/mozjs/js/src/jsbool.h b/deps/mozjs/js/src/jsbool.h index 477b4093a4e..c5e857fcecb 100644 --- a/deps/mozjs/js/src/jsbool.h +++ b/deps/mozjs/js/src/jsbool.h @@ -45,15 +45,6 @@ #include "jsapi.h" #include "jsobj.h" -#include "jsstr.h" - -extern js::Class js_BooleanClass; - -inline bool -JSObject::isBoolean() const -{ - return getClass() == &js_BooleanClass; -} extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); @@ -66,6 +57,9 @@ namespace js { extern bool BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb); +inline bool +BooleanGetPrimitiveValue(JSContext *cx, JSObject &obj, Value *vp); + } /* namespace js */ extern JSBool diff --git a/deps/mozjs/js/src/jsboolinlines.h b/deps/mozjs/js/src/jsboolinlines.h new file mode 100644 index 00000000000..c9f17259346 --- /dev/null +++ b/deps/mozjs/js/src/jsboolinlines.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jeff Walden (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsboolinlines_h___ +#define jsboolinlines_h___ + +#include "jsobjinlines.h" + +namespace js { + +inline bool +BooleanGetPrimitiveValue(JSContext *cx, JSObject &obj, Value *vp) +{ + if (obj.isBoolean()) { + *vp = obj.getPrimitiveThis(); + return true; + } + + extern bool BooleanGetPrimitiveValueSlow(JSContext *, JSObject &, Value *); + return BooleanGetPrimitiveValueSlow(cx, obj, vp); +} + +} /* namespace js */ + +#endif /* jsboolinlines_h___ */ diff --git a/deps/mozjs/js/src/jsbuiltins.cpp b/deps/mozjs/js/src/jsbuiltins.cpp deleted file mode 100644 index a0778f38ef1..00000000000 --- a/deps/mozjs/js/src/jsbuiltins.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; -*- - * vim: set ts=4 sw=4 et tw=99: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * May 28, 2008. - * - * The Initial Developer of the Original Code is - * Andreas Gal - * - * Contributor(s): - * Brendan Eich - * Mike Shaver - * David Anderson - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include - -#include "jsapi.h" -#include "jsstdint.h" -#include "jsarray.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsgc.h" -#include "jsiter.h" -#include "jsnum.h" -#include "jslibmath.h" -#include "jsmath.h" -#include "jsnum.h" -#include "prmjtime.h" -#include "jsdate.h" -#include "jsscope.h" -#include "jsstr.h" -#include "jsbuiltins.h" -#include "jstracer.h" -#include "jsvector.h" - -#include "jsatominlines.h" -#include "jsobjinlines.h" -#include "jsscopeinlines.h" -#include "jscntxtinlines.h" - -using namespace avmplus; -using namespace nanojit; -using namespace js; - -JS_FRIEND_API(void) -js_SetTraceableNativeFailed(JSContext *cx) -{ - /* - * We might not be on trace (we might have deep bailed) so we hope - * cx->compartment is correct. - */ - SetBuiltinError(JS_TRACE_MONITOR_FROM_CONTEXT(cx)); -} - -/* - * NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. - * Do not use bool FASTCALL, use JSBool only! - */ - -jsdouble FASTCALL -js_dmod(jsdouble a, jsdouble b) -{ - if (b == 0.0) { - jsdpun u; - u.s.hi = JSDOUBLE_HI32_NAN; - u.s.lo = JSDOUBLE_LO32_NAN; - return u.d; - } - return js_fmod(a, b); -} -JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_dmod, DOUBLE, DOUBLE, 1, ACCSET_NONE) - -int32 FASTCALL -js_imod(int32 a, int32 b) -{ - if (a < 0 || b <= 0) - return -1; - int r = a % b; - return r; -} -JS_DEFINE_CALLINFO_2(extern, INT32, js_imod, INT32, INT32, 1, ACCSET_NONE) - -#if JS_BITS_PER_WORD == 32 - -jsdouble FASTCALL -js_UnboxDouble(uint32 tag, uint32 payload) -{ - if (tag == JSVAL_TAG_INT32) - return (double)(int32)payload; - - jsval_layout l; - l.s.tag = (JSValueTag)tag; - l.s.payload.u32 = payload; - return l.asDouble; -} -JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_UnboxDouble, UINT32, UINT32, 1, ACCSET_NONE) - -int32 FASTCALL -js_UnboxInt32(uint32 tag, uint32 payload) -{ - if (tag == JSVAL_TAG_INT32) - return (int32)payload; - - jsval_layout l; - l.s.tag = (JSValueTag)tag; - l.s.payload.u32 = payload; - return js_DoubleToECMAInt32(l.asDouble); -} -JS_DEFINE_CALLINFO_2(extern, INT32, js_UnboxInt32, UINT32, UINT32, 1, ACCSET_NONE) - -#elif JS_BITS_PER_WORD == 64 - -jsdouble FASTCALL -js_UnboxDouble(Value v) -{ - if (v.isInt32()) - return (jsdouble)v.toInt32(); - return v.toDouble(); -} -JS_DEFINE_CALLINFO_1(extern, DOUBLE, js_UnboxDouble, JSVAL, 1, ACCSET_NONE) - -int32 FASTCALL -js_UnboxInt32(Value v) -{ - if (v.isInt32()) - return v.toInt32(); - return js_DoubleToECMAInt32(v.toDouble()); -} -JS_DEFINE_CALLINFO_1(extern, INT32, js_UnboxInt32, VALUE, 1, ACCSET_NONE) - -#endif - -int32 FASTCALL -js_DoubleToInt32(jsdouble d) -{ - return js_DoubleToECMAInt32(d); -} -JS_DEFINE_CALLINFO_1(extern, INT32, js_DoubleToInt32, DOUBLE, 1, ACCSET_NONE) - -uint32 FASTCALL -js_DoubleToUint32(jsdouble d) -{ - return js_DoubleToECMAUint32(d); -} -JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE) - -/* - * js_StringToNumber and js_StringToInt32 store into their second argument, so - * they need to be annotated accordingly. To be future-proof, we use - * ACCSET_STORE_ANY so that new callers don't have to remember to update the - * annotation. - */ -jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok) -{ - double out = 0; /* silence warnings. */ - *ok = StringToNumberType(cx, str, &out); - return out; -} -JS_DEFINE_CALLINFO_3(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, BOOLPTR, - 0, ACCSET_STORE_ANY) - -int32 FASTCALL -js_StringToInt32(JSContext* cx, JSString* str, JSBool *ok) -{ - int32 out = 0; /* silence warnings. */ - *ok = StringToNumberType(cx, str, &out); - return out; -} -JS_DEFINE_CALLINFO_3(extern, INT32, js_StringToInt32, CONTEXT, STRING, BOOLPTR, - 0, ACCSET_STORE_ANY) - -/* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */ -static inline JSBool -AddPropertyHelper(JSContext* cx, JSObject* obj, Shape* shape, bool isDefinitelyAtom) -{ - JS_ASSERT(shape->previous() == obj->lastProperty()); - - if (obj->nativeEmpty()) { - if (!obj->ensureClassReservedSlotsForEmptyObject(cx)) - return false; - } - - uint32 slot; - slot = shape->slot; - JS_ASSERT(slot == obj->slotSpan()); - - if (slot < obj->numSlots()) { - JS_ASSERT(obj->getSlot(slot).isUndefined()); - } else { - if (!obj->allocSlot(cx, &slot)) - return false; - JS_ASSERT(slot == shape->slot); - } - - obj->extend(cx, shape, isDefinitelyAtom); - return !js_IsPropertyCacheDisabled(cx); -} - -JSBool FASTCALL -js_AddProperty(JSContext* cx, JSObject* obj, Shape* shape) -{ - return AddPropertyHelper(cx, obj, shape, /* isDefinitelyAtom = */false); -} -JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SHAPE, 0, ACCSET_STORE_ANY) - -JSBool FASTCALL -js_AddAtomProperty(JSContext* cx, JSObject* obj, Shape* shape) -{ - return AddPropertyHelper(cx, obj, shape, /* isDefinitelyAtom = */true); -} -JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddAtomProperty, CONTEXT, OBJECT, SHAPE, 0, ACCSET_STORE_ANY) - -static JSBool -HasProperty(JSContext* cx, JSObject* obj, jsid id) -{ - // Check that we know how the lookup op will behave. - for (JSObject* pobj = obj; pobj; pobj = pobj->getProto()) { - if (pobj->getOps()->lookupProperty) - return JS_NEITHER; - Class* clasp = pobj->getClass(); - if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass) - return JS_NEITHER; - } - - JSObject* obj2; - JSProperty* prop; - if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0) - return JS_NEITHER; - return prop != NULL; -} - -JSBool FASTCALL -js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr) -{ - JSAtom *atom = js_AtomizeString(cx, idstr, 0); - if (!atom) - return JS_NEITHER; - - return HasProperty(cx, obj, ATOM_TO_JSID(atom)); -} -JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, - 0, ACCSET_STORE_ANY) - -JSBool FASTCALL -js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index) -{ - jsid id; - if (!js_Int32ToId(cx, index, &id)) - return JS_NEITHER; - - return HasProperty(cx, obj, id); -} -JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, - 0, ACCSET_STORE_ANY) - -JSString* FASTCALL -js_TypeOfObject(JSContext* cx, JSObject* obj) -{ - JS_ASSERT(obj); - return cx->runtime->atomState.typeAtoms[obj->typeOf(cx)]; -} -JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, ACCSET_NONE) - -JSString* FASTCALL -js_BooleanIntToString(JSContext *cx, int32 unboxed) -{ - JS_ASSERT(uint32(unboxed) <= 1); - return cx->runtime->atomState.booleanAtoms[unboxed]; -} -JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanIntToString, CONTEXT, INT32, 1, ACCSET_NONE) - -JSObject* FASTCALL -js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) -{ - JS_ASSERT(funobj->isFunction()); - JS_ASSERT(proto->isFunction()); - JS_ASSERT(JS_ON_TRACE(cx)); - - JSFunction *fun = (JSFunction*) funobj; - JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); - - JSObject* closure = js_NewGCObject(cx, gc::FINALIZE_OBJECT2); - if (!closure) - return NULL; - - if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent, - fun, gc::FINALIZE_OBJECT2)) { - return NULL; - } - return closure; -} -JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, - 0, ACCSET_STORE_ANY) - diff --git a/deps/mozjs/js/src/jsbuiltins.h b/deps/mozjs/js/src/jsbuiltins.h deleted file mode 100644 index fe7243b70fe..00000000000 --- a/deps/mozjs/js/src/jsbuiltins.h +++ /dev/null @@ -1,635 +0,0 @@ -/* -*- 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 ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * May 28, 2008. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * - * Contributor(s): - * Jason Orendorff - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsbuiltins_h___ -#define jsbuiltins_h___ - -#ifdef JS_TRACER - -#include "nanojit/nanojit.h" -#include "jsvalue.h" - -#ifdef THIS -#undef THIS -#endif - -enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_NEITHER }; -enum { - JSTN_ERRTYPE_MASK = 0x07, - JSTN_UNBOX_AFTER = 0x08, - JSTN_MORE = 0x10, - JSTN_CONSTRUCTOR = 0x20, - JSTN_RETURN_NULLABLE_STR = 0x40, - JSTN_RETURN_NULLABLE_OBJ = 0x80 -}; - -#define JSTN_ERRTYPE(jstn) ((jstn)->flags & JSTN_ERRTYPE_MASK) - -/* - * Type describing a type specialization of a js::Native. - * - * |prefix| and |argtypes| declare what arguments should be passed to the - * native function. |prefix| can contain the following characters: - * - * 'C': a JSContext* argument - * 'T': |this| as a JSObject* argument (bails if |this| is not an object) - * 'S': |this| as a JSString* argument (bails if |this| is not a string) - * 'R': a JSRuntime* argument - * 'P': the pc as a jsbytecode* - * 'D': |this| as a number (jsdouble) - * 'f': the function being called, as a JSObject* - * 'p': the .prototype of the function, as a JSObject* - * - * The corresponding things will get passed as arguments to the builtin in - * reverse order (so TC means JSContext* as the first arg, and the - * JSObject* for |this| as the second arg). - * - * |argtypes| can contain the following characters: - * 'd': a number (double) argument - * 'i': an integer argument - * 's': a JSString* argument - * 'o': a JSObject* argument - * 'r': a JSObject* argument that is of class js_RegExpClass - * 'f': a JSObject* argument that is of class js_FunctionClass - * 'v': a value argument: on 32-bit, a Value*, on 64-bit, a jsval - */ -struct JSSpecializedNative { - const nanojit::CallInfo *builtin; - const char *prefix; - const char *argtypes; - uintN flags; /* JSTNErrType | JSTN_UNBOX_AFTER | JSTN_MORE | - JSTN_CONSTRUCTOR */ -}; - -/* - * Type holding extra trace-specific information about a fast native. - * - * 'specializations' points to a static array of available specializations - * terminated by the lack of having the JSTN_MORE flag set. - */ -struct JSNativeTraceInfo { - js::Native native; - JSSpecializedNative *specializations; -}; - -/* Macros used by JS_DEFINE_CALLINFOn. */ -#ifdef DEBUG -#define _JS_CI_NAME(op) ,#op -#else -#define _JS_CI_NAME(op) -#endif - -#define _JS_I32_ARGTYPE nanojit::ARGTYPE_I -#define _JS_I32_RETTYPE nanojit::ARGTYPE_I -#define _JS_U64_ARGTYPE nanojit::ARGTYPE_Q -#define _JS_U64_RETTYPE nanojit::ARGTYPE_Q -#define _JS_F64_ARGTYPE nanojit::ARGTYPE_D -#define _JS_F64_RETTYPE nanojit::ARGTYPE_D -#define _JS_PTR_ARGTYPE nanojit::ARGTYPE_P -#define _JS_PTR_RETTYPE nanojit::ARGTYPE_P - -struct ClosureVarInfo; - -/* - * Supported types for builtin functions. - * - * Types with -- for the two string fields are not permitted as argument types - * in JS_DEFINE_TRCINFO. - * - * There are three kinds of traceable-native error handling. - * - * - If a traceable native's return type ends with _FAIL, it always runs to - * completion. It can either succeed or fail with an error or exception; - * on success, it may or may not stay on trace. There may be side effects - * in any case. If the call succeeds but bails off trace, we resume in the - * interpreter at the next opcode. - * - * _FAIL builtins indicate failure or bailing off trace by setting bits in - * cx->interpState->builtinStatus. - * - * - If a traceable native's return type contains _RETRY, it can either - * succeed, fail with a JS exception, or tell the caller to bail off trace - * and retry the call from the interpreter. The last case happens if the - * builtin discovers that it can't do its job without examining the JS - * stack, reentering the interpreter, accessing properties of the global - * object, etc. - * - * The builtin must detect the need to retry before committing any side - * effects. If a builtin can't do this, it must use a _FAIL return type - * instead of _RETRY. - * - * _RETRY builtins indicate failure with a special return value that - * depends on the return type: - * - * BOOL_RETRY: JS_NEITHER - * INT32_RETRY: any negative value - * STRING_RETRY: NULL - * OBJECT_RETRY_NULL: NULL - * - * _RETRY function calls are faster than _FAIL calls. Each _RETRY call - * saves two writes to tm->bailExit and a read from state->builtinStatus. - * - * - All other traceable natives are infallible (e.g. Date.now, Math.log). - * - * Special builtins known to the tracer can have their own idiosyncratic - * error codes. - * - * When a traceable native returns a value indicating failure, we fall off - * trace. If an exception is pending, it is thrown; otherwise, we assume the - * builtin had no side effects and retry the current bytecode in the - * interpreter. - * - * So a builtin must not return a value indicating failure after causing side - * effects (such as reporting an error), without setting an exception pending. - * The operation would be retried, despite the first attempt's observable - * effects. - */ -#define _JS_CTYPE(ctype, size, pch, ach, flags) (ctype, size, pch, ach, flags) - -#define _JS_CTYPE_CONTEXT _JS_CTYPE(JSContext *, _JS_PTR,"C", "", INFALLIBLE) -#define _JS_CTYPE_RUNTIME _JS_CTYPE(JSRuntime *, _JS_PTR,"R", "", INFALLIBLE) -#define _JS_CTYPE_MATHCACHE _JS_CTYPE(js::MathCache *, _JS_PTR,"M", "", INFALLIBLE) -#define _JS_CTYPE_THIS _JS_CTYPE(JSObject *, _JS_PTR,"T", "", INFALLIBLE) -#define _JS_CTYPE_THIS_DOUBLE _JS_CTYPE(jsdouble, _JS_F64,"D", "", INFALLIBLE) -#define _JS_CTYPE_THIS_STRING _JS_CTYPE(JSString *, _JS_PTR,"S", "", INFALLIBLE) -#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f", "", INFALLIBLE) -#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p", "", INFALLIBLE) -#define _JS_CTYPE_FUNCTION _JS_CTYPE(JSFunction *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_PC _JS_CTYPE(jsbytecode *, _JS_PTR,"P", "", INFALLIBLE) -#define _JS_CTYPE_VALUEPTR _JS_CTYPE(js::Value *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_CVALUEPTR _JS_CTYPE(const js::Value *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_JSID _JS_CTYPE(jsid, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_JSVAL _JS_CTYPE(js::Value, _JS_U64, --, --, INFALLIBLE) -#define _JS_CTYPE_BOOL _JS_CTYPE(JSBool, _JS_I32, "","i", INFALLIBLE) -#define _JS_CTYPE_BOOL_RETRY _JS_CTYPE(JSBool, _JS_I32, --, --, FAIL_NEITHER) -#define _JS_CTYPE_BOOL_FAIL _JS_CTYPE(JSBool, _JS_I32, --, --, FAIL_STATUS) -#define _JS_CTYPE_BOOLPTR _JS_CTYPE(JSBool *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_INT32 _JS_CTYPE(int32, _JS_I32, "","i", INFALLIBLE) -#define _JS_CTYPE_INT32_RETRY _JS_CTYPE(int32, _JS_I32, --, --, FAIL_NEG) -#define _JS_CTYPE_INT32_FAIL _JS_CTYPE(int32, _JS_I32, --, --, FAIL_STATUS) -#define _JS_CTYPE_INT32PTR _JS_CTYPE(int32 *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_UINT32 _JS_CTYPE(uint32, _JS_I32, "","i", INFALLIBLE) -#define _JS_CTYPE_UINT32_RETRY _JS_CTYPE(uint32, _JS_I32, --, --, FAIL_NEG) -#define _JS_CTYPE_UINT32_FAIL _JS_CTYPE(uint32, _JS_I32, --, --, FAIL_STATUS) -#define _JS_CTYPE_DOUBLE _JS_CTYPE(jsdouble, _JS_F64, "","d", INFALLIBLE) -#define _JS_CTYPE_DOUBLE_FAIL _JS_CTYPE(jsdouble, _JS_F64, --, --, FAIL_STATUS) -#define _JS_CTYPE_STRING _JS_CTYPE(JSString *, _JS_PTR, "","s", INFALLIBLE) -#define _JS_CTYPE_STRING_RETRY _JS_CTYPE(JSString *, _JS_PTR, --, --, FAIL_NULL) -#define _JS_CTYPE_STRING_FAIL _JS_CTYPE(JSString *, _JS_PTR, --, --, FAIL_STATUS) -#define _JS_CTYPE_STRING_OR_NULL_FAIL _JS_CTYPE(JSString *, _JS_PTR, --, --, FAIL_STATUS | \ - JSTN_RETURN_NULLABLE_STR) -#define _JS_CTYPE_STRINGPTR _JS_CTYPE(JSString **, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_OBJECT _JS_CTYPE(JSObject *, _JS_PTR, "","o", INFALLIBLE) -#define _JS_CTYPE_OBJECT_RETRY _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_NULL) -#define _JS_CTYPE_OBJECT_FAIL _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_STATUS) -#define _JS_CTYPE_OBJECT_OR_NULL_FAIL _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_STATUS | \ - JSTN_RETURN_NULLABLE_OBJ) -#define _JS_CTYPE_OBJECTPTR _JS_CTYPE(JSObject **, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_CONSTRUCTOR_RETRY _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_NULL | \ - JSTN_CONSTRUCTOR) -#define _JS_CTYPE_REGEXP _JS_CTYPE(JSObject *, _JS_PTR, "","r", INFALLIBLE) -#define _JS_CTYPE_SHAPE _JS_CTYPE(js::Shape *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_TRACERSTATE _JS_CTYPE(TracerState *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_FRAGMENT _JS_CTYPE(nanojit::Fragment *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_CLASS _JS_CTYPE(js::Class *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_DOUBLEPTR _JS_CTYPE(double *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_CHARPTR _JS_CTYPE(char *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_CVIPTR _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_FRAMEINFO _JS_CTYPE(FrameInfo *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_PICTABLE _JS_CTYPE(PICTable *, _JS_PTR, --, --, INFALLIBLE) -#define _JS_CTYPE_UINTN _JS_CTYPE(uintN, _JS_PTR, --, --, INFALLIBLE) - -/* - * The "VALUE" type is used to indicate that a native takes a js::Value - * parameter by value. Unfortunately, for technical reasons, we can't simply - * have the parameter type be js::Value. Furthermore, the right thing to pass - * differs based on word size. Thus, a native that declares a parameter of type - * VALUE should have the corresponding argument type be: - * - on 32-bit: const Value* - * - on 64-bit: jsval (which is a uint64) - * - * To write code that just does the right thing, use the pattern: - * void foo(js::ValueArgType arg) { - * const js::Value &v = js::ValueArgToConstRef(arg); - */ -#if JS_BITS_PER_WORD == 32 -# define _JS_CTYPE_VALUE _JS_CTYPE(js::ValueArgType, _JS_PTR, "","v", INFALLIBLE) -#elif JS_BITS_PER_WORD == 64 -# define _JS_CTYPE_VALUE _JS_CTYPE(js::ValueArgType, _JS_U64, "","v", INFALLIBLE) -#endif - -#define _JS_EXPAND(tokens) tokens - -#define _JS_CTYPE_TYPE2(t,s,p,a,f) t -#define _JS_CTYPE_TYPE(tyname) _JS_EXPAND(_JS_CTYPE_TYPE2 _JS_CTYPE_##tyname) -#define _JS_CTYPE_RETTYPE2(t,s,p,a,f) s##_RETTYPE -#define _JS_CTYPE_RETTYPE(tyname) _JS_EXPAND(_JS_CTYPE_RETTYPE2 _JS_CTYPE_##tyname) -#define _JS_CTYPE_ARGTYPE2(t,s,p,a,f) s##_ARGTYPE -#define _JS_CTYPE_ARGTYPE(tyname) _JS_EXPAND(_JS_CTYPE_ARGTYPE2 _JS_CTYPE_##tyname) -#define _JS_CTYPE_PCH2(t,s,p,a,f) p -#define _JS_CTYPE_PCH(tyname) _JS_EXPAND(_JS_CTYPE_PCH2 _JS_CTYPE_##tyname) -#define _JS_CTYPE_ACH2(t,s,p,a,f) a -#define _JS_CTYPE_ACH(tyname) _JS_EXPAND(_JS_CTYPE_ACH2 _JS_CTYPE_##tyname) -#define _JS_CTYPE_FLAGS2(t,s,p,a,f) f -#define _JS_CTYPE_FLAGS(tyname) _JS_EXPAND(_JS_CTYPE_FLAGS2 _JS_CTYPE_##tyname) - -#define _JS_static_TN(t) static t -#define _JS_static_CI static -#define _JS_extern_TN(t) extern t -#define _JS_extern_CI -#define _JS_FRIEND_TN(t) extern JS_FRIEND_API(t) -#define _JS_FRIEND_CI -#define _JS_TN_LINKAGE(linkage, t) _JS_##linkage##_TN(t) -#define _JS_CI_LINKAGE(linkage) _JS_##linkage##_CI - -#define _JS_CALLINFO(name) name##_ci - -#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) -#define _JS_DEFINE_CALLINFO(linkage, name, crtype, cargtypes, argtypes, isPure, storeAccSet) \ - _JS_TN_LINKAGE(linkage, crtype) name cargtypes; \ - _JS_CI_LINKAGE(linkage) const nanojit::CallInfo _JS_CALLINFO(name) = \ - { (intptr_t) &name, argtypes, nanojit::ABI_CDECL, isPure, storeAccSet _JS_CI_NAME(name) };\ - JS_STATIC_ASSERT_IF(isPure, (storeAccSet) == nanojit::ACCSET_NONE); - -#else -#define _JS_DEFINE_CALLINFO(linkage, name, crtype, cargtypes, argtypes, isPure, storeAccSet) \ - _JS_TN_LINKAGE(linkage, crtype) FASTCALL name cargtypes; \ - _JS_CI_LINKAGE(linkage) const nanojit::CallInfo _JS_CALLINFO(name) = \ - { (intptr_t) &name, argtypes, nanojit::ABI_FASTCALL, isPure, storeAccSet _JS_CI_NAME(name) }; \ - JS_STATIC_ASSERT_IF(isPure, (storeAccSet) == nanojit::ACCSET_NONE); -#endif - -/* - * This macro is used for builtin functions that can be called from JITted - * code. It declares a C function named and a CallInfo struct named - * _ci so the tracer can call it. The in JS_DEFINE_CALLINFO_ is - * the number of arguments the builtin takes. Builtins with no arguments - * are not supported. Using a macro is clunky but ensures that the types - * for each C function matches those for the corresponding CallInfo struct; - * mismatched types can cause subtle problems. - * - * The macro arguments are: - * - * - The linkage for the function and the associated CallInfo global. It - * can be extern, static, or FRIEND, which specifies JS_FRIEND_API linkage - * for the function. - * - * - The return type. This identifier must name one of the _JS_TYPEINFO_* - * macros defined in jsbuiltins.h. - * - * - The builtin name. - * - * - The parameter types. - * - * - The isPure flag. Set to 1 if: - * (a) the function's return value is determined solely by its arguments - * (ie. no hidden state, no implicit inputs used such as global - * variables or the result of an I/O operation); and - * (b) the function causes no observable side-effects (ie. no writes to - * global variables, no I/O output). - * Multiple calls to a pure function can be merged during CSE. - * - * - The storeAccSet. This indicates which memory access regions the function - * accesses. It must be ACCSET_NONE if the function is pure; use - * ACCSET_STORE_ANY if you're not sure. Used to determine if each call site - * of the function aliases any loads. - */ -#define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0)), \ - nanojit::CallInfo::typeSig1(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1)), \ - nanojit::CallInfo::typeSig2(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2)), \ - nanojit::CallInfo::typeSig3(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2), \ - _JS_CTYPE_TYPE(at3)), \ - nanojit::CallInfo::typeSig4(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2), \ - _JS_CTYPE_ARGTYPE(at3)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2), \ - _JS_CTYPE_TYPE(at3), \ - _JS_CTYPE_TYPE(at4)), \ - nanojit::CallInfo::typeSig5(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2), \ - _JS_CTYPE_ARGTYPE(at3), \ - _JS_CTYPE_ARGTYPE(at4)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, isPure, storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2), \ - _JS_CTYPE_TYPE(at3), \ - _JS_CTYPE_TYPE(at4), \ - _JS_CTYPE_TYPE(at5)), \ - nanojit::CallInfo::typeSig6(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2), \ - _JS_CTYPE_ARGTYPE(at3), \ - _JS_CTYPE_ARGTYPE(at4), \ - _JS_CTYPE_ARGTYPE(at5)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_7(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, isPure, \ - storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2), \ - _JS_CTYPE_TYPE(at3), \ - _JS_CTYPE_TYPE(at4), \ - _JS_CTYPE_TYPE(at5), \ - _JS_CTYPE_TYPE(at6)), \ - nanojit::CallInfo::typeSig7(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2), \ - _JS_CTYPE_ARGTYPE(at3), \ - _JS_CTYPE_ARGTYPE(at4), \ - _JS_CTYPE_ARGTYPE(at5), \ - _JS_CTYPE_ARGTYPE(at6)), \ - isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_8(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, at7, isPure, \ - storeAccSet) \ - _JS_DEFINE_CALLINFO(linkage, op, \ - _JS_CTYPE_TYPE(rt), \ - (_JS_CTYPE_TYPE(at0), \ - _JS_CTYPE_TYPE(at1), \ - _JS_CTYPE_TYPE(at2), \ - _JS_CTYPE_TYPE(at3), \ - _JS_CTYPE_TYPE(at4), \ - _JS_CTYPE_TYPE(at5), \ - _JS_CTYPE_TYPE(at6), \ - _JS_CTYPE_TYPE(at7)), \ - nanojit::CallInfo::typeSig8(_JS_CTYPE_RETTYPE(rt), \ - _JS_CTYPE_ARGTYPE(at0), \ - _JS_CTYPE_ARGTYPE(at1), \ - _JS_CTYPE_ARGTYPE(at2), \ - _JS_CTYPE_ARGTYPE(at3), \ - _JS_CTYPE_ARGTYPE(at4), \ - _JS_CTYPE_ARGTYPE(at5), \ - _JS_CTYPE_ARGTYPE(at6), \ - _JS_CTYPE_ARGTYPE(at7)), \ - isPure, storeAccSet) - -#define JS_DECLARE_CALLINFO(name) extern const nanojit::CallInfo _JS_CALLINFO(name); - -#define _JS_TN_INIT_HELPER_n(n, args) _JS_TN_INIT_HELPER_##n args - -#define _JS_TN_INIT_HELPER_1(linkage, rt, op, at0, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_2(linkage, rt, op, at0, at1, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_3(linkage, rt, op, at0, at1, at2, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_4(linkage, rt, op, at0, at1, at2, at3, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_5(linkage, rt, op, at0, at1, at2, at3, at4, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at4) _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) \ - _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at4) _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) \ - _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at5) _JS_CTYPE_PCH(at4) _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) \ - _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at5) _JS_CTYPE_ACH(at4) _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) \ - _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_7(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at6) _JS_CTYPE_PCH(at5) _JS_CTYPE_PCH(at4) _JS_CTYPE_PCH(at3) \ - _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at6) _JS_CTYPE_ACH(at5) _JS_CTYPE_ACH(at4) _JS_CTYPE_ACH(at3) \ - _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define _JS_TN_INIT_HELPER_8(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, at7, isPure, storeAccSet) \ - &_JS_CALLINFO(op), \ - _JS_CTYPE_PCH(at7) _JS_CTYPE_PCH(at6) _JS_CTYPE_PCH(at5) _JS_CTYPE_PCH(at4) \ - _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ - _JS_CTYPE_ACH(at7) _JS_CTYPE_ACH(at6) _JS_CTYPE_ACH(at5) _JS_CTYPE_ACH(at4) \ - _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ - _JS_CTYPE_FLAGS(rt) - -#define JS_DEFINE_TRCINFO_1(name, tn0) \ - _JS_DEFINE_CALLINFO_n tn0 \ - JSSpecializedNative name##_sns[] = { \ - { _JS_TN_INIT_HELPER_n tn0 } \ - }; \ - JSNativeTraceInfo name##_trcinfo = { JS_VALUEIFY_NATIVE(name), name##_sns }; - -#define JS_DEFINE_TRCINFO_2(name, tn0, tn1) \ - _JS_DEFINE_CALLINFO_n tn0 \ - _JS_DEFINE_CALLINFO_n tn1 \ - JSSpecializedNative name##_sns[] = { \ - { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn1 } \ - }; \ - JSNativeTraceInfo name##_trcinfo = { JS_VALUEIFY_NATIVE(name), name##_sns }; - -#define JS_DEFINE_TRCINFO_3(name, tn0, tn1, tn2) \ - _JS_DEFINE_CALLINFO_n tn0 \ - _JS_DEFINE_CALLINFO_n tn1 \ - _JS_DEFINE_CALLINFO_n tn2 \ - JSSpecializedNative name##_sns[] = { \ - { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn1 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn2 } \ - }; \ - JSNativeTraceInfo name##_trcinfo = { JS_VALUEIFY_NATIVE(name), name##_sns }; - -#define JS_DEFINE_TRCINFO_4(name, tn0, tn1, tn2, tn3) \ - _JS_DEFINE_CALLINFO_n tn0 \ - _JS_DEFINE_CALLINFO_n tn1 \ - _JS_DEFINE_CALLINFO_n tn2 \ - _JS_DEFINE_CALLINFO_n tn3 \ - JSSpecializedNative name##_sns[] = { \ - { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn1 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn2 | JSTN_MORE }, \ - { _JS_TN_INIT_HELPER_n tn3 } \ - }; \ - JSNativeTraceInfo name##_trcinfo = { JS_VALUEIFY_NATIVE(name), name##_sns }; - -#define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args - -jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok); - -/* Extern version of SetBuiltinError. */ -extern JS_FRIEND_API(void) -js_SetTraceableNativeFailed(JSContext *cx); - -extern jsdouble FASTCALL -js_dmod(jsdouble a, jsdouble b); - -#else - -#define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_7(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, isPure, storeAccSet) -#define JS_DEFINE_CALLINFO_8(linkage, rt, op, at0, at1, at2, at3, at4, at5, at6, at7, isPure, storeAccSet) -#define JS_DECLARE_CALLINFO(name) -#define JS_DEFINE_TRCINFO_1(name, tn0) -#define JS_DEFINE_TRCINFO_2(name, tn0, tn1) -#define JS_DEFINE_TRCINFO_3(name, tn0, tn1, tn2) -#define JS_DEFINE_TRCINFO_4(name, tn0, tn1, tn2, tn3) - -#endif /* !JS_TRACER */ - -/* Defined in jsarray.cpp. */ -namespace js { -JS_DECLARE_CALLINFO(NewDenseEmptyArray) -JS_DECLARE_CALLINFO(NewDenseAllocatedArray) -JS_DECLARE_CALLINFO(NewDenseUnallocatedArray) -} -JS_DECLARE_CALLINFO(js_ArrayCompPush_tn) -JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity) - -/* Defined in jsbuiltins.cpp. */ -JS_DECLARE_CALLINFO(js_UnboxDouble) -JS_DECLARE_CALLINFO(js_UnboxInt32) -JS_DECLARE_CALLINFO(js_dmod) -JS_DECLARE_CALLINFO(js_imod) -JS_DECLARE_CALLINFO(js_DoubleToInt32) -JS_DECLARE_CALLINFO(js_DoubleToUint32) -JS_DECLARE_CALLINFO(js_StringToNumber) -JS_DECLARE_CALLINFO(js_StringToInt32) -JS_DECLARE_CALLINFO(js_AddProperty) -JS_DECLARE_CALLINFO(js_AddAtomProperty) -JS_DECLARE_CALLINFO(js_HasNamedProperty) -JS_DECLARE_CALLINFO(js_HasNamedPropertyInt32) -JS_DECLARE_CALLINFO(js_TypeOfObject) -JS_DECLARE_CALLINFO(js_BooleanIntToString) -JS_DECLARE_CALLINFO(js_NewNullClosure) - -/* Defined in jsfun.cpp. */ -JS_DECLARE_CALLINFO(js_AllocFlatClosure) -JS_DECLARE_CALLINFO(js_PutArgumentsOnTrace) -JS_DECLARE_CALLINFO(js_PutCallObjectOnTrace) -JS_DECLARE_CALLINFO(js_SetCallVar) -JS_DECLARE_CALLINFO(js_SetCallArg) -JS_DECLARE_CALLINFO(js_CloneFunctionObject) -JS_DECLARE_CALLINFO(js_CreateCallObjectOnTrace) -JS_DECLARE_CALLINFO(js_NewArgumentsOnTrace) - -/* Defined in jsnum.cpp. */ -JS_DECLARE_CALLINFO(js_NumberToString) - -/* Defined in jsobj.cpp. */ -JS_DECLARE_CALLINFO(js_Object_tn) -JS_DECLARE_CALLINFO(js_CreateThisFromTrace) -JS_DECLARE_CALLINFO(js_InitializerObject) - -/* Defined in jsregexp.cpp. */ -JS_DECLARE_CALLINFO(js_CloneRegExpObject) - -/* Defined in jsstr.cpp. */ -JS_DECLARE_CALLINFO(js_String_tn) -JS_DECLARE_CALLINFO(js_CompareStringsOnTrace) -JS_DECLARE_CALLINFO(js_ConcatStrings) -JS_DECLARE_CALLINFO(js_EqualStringsOnTrace) -JS_DECLARE_CALLINFO(js_FlattenOnTrace) - -/* Defined in jstypedarray.cpp. */ -JS_DECLARE_CALLINFO(js_TypedArray_uint8_clamp_double) - -#endif /* jsbuiltins_h___ */ diff --git a/deps/mozjs/js/src/jscell.h b/deps/mozjs/js/src/jscell.h index 5389a38a6c1..b7f931671d8 100644 --- a/deps/mozjs/js/src/jscell.h +++ b/deps/mozjs/js/src/jscell.h @@ -40,26 +40,58 @@ #ifndef jscell_h___ #define jscell_h___ +#include "jspubtd.h" + struct JSCompartment; namespace js { namespace gc { -template struct Arena; -struct ArenaBitmap; struct ArenaHeader; -struct MarkingDelay; struct Chunk; -struct FreeCell; + +/* The GC allocation kinds. */ +enum AllocKind { + FINALIZE_OBJECT0, + FINALIZE_OBJECT0_BACKGROUND, + FINALIZE_OBJECT2, + FINALIZE_OBJECT2_BACKGROUND, + FINALIZE_OBJECT4, + FINALIZE_OBJECT4_BACKGROUND, + FINALIZE_OBJECT8, + FINALIZE_OBJECT8_BACKGROUND, + FINALIZE_OBJECT12, + FINALIZE_OBJECT12_BACKGROUND, + FINALIZE_OBJECT16, + FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_SCRIPT, + FINALIZE_SHAPE, + FINALIZE_BASE_SHAPE, + FINALIZE_TYPE_OBJECT, +#if JS_HAS_XML_SUPPORT + FINALIZE_XML, +#endif + FINALIZE_SHORT_STRING, + FINALIZE_STRING, + FINALIZE_EXTERNAL_STRING, + FINALIZE_LAST = FINALIZE_EXTERNAL_STRING +}; + +static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1; +static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1; /* - * A GC cell is the base class for GC Things like JSObject, JSShortString, - * JSFunction, JSXML and for an empty cell called FreeCell. It helps avoiding - * casts from an Object to a Cell whenever we call GC related mark functions. - * Cell is not the base Class for JSString because static initialization - * (used for unitStringTables) does not work with inheritance. + * Live objects are marked black. How many other additional colors are available + * depends on the size of the GCThing. Objects marked gray are eligible for + * cycle collection. */ +static const uint32_t BLACK = 0; +static const uint32_t GRAY = 1; +/* + * A GC cell is the base class for all GC things. + */ struct Cell { static const size_t CellShift = 3; static const size_t CellSize = size_t(1) << CellShift; @@ -68,37 +100,19 @@ struct Cell { inline uintptr_t address() const; inline ArenaHeader *arenaHeader() const; inline Chunk *chunk() const; - inline ArenaBitmap *bitmap() const; - JS_ALWAYS_INLINE size_t cellIndex() const; + inline AllocKind getAllocKind() const; - JS_ALWAYS_INLINE bool isMarked(uint32 color) const; - JS_ALWAYS_INLINE bool markIfUnmarked(uint32 color) const; - JS_ALWAYS_INLINE void unmark(uint32 color) const; + JS_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const; + JS_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const; + JS_ALWAYS_INLINE void unmark(uint32_t color) const; inline JSCompartment *compartment() const; - JS_ALWAYS_INLINE js::gc::FreeCell *asFreeCell() { - return reinterpret_cast(this); - } - - JS_ALWAYS_INLINE const js::gc::FreeCell *asFreeCell() const { - return reinterpret_cast(this); - } - #ifdef DEBUG inline bool isAligned() const; #endif }; -struct FreeCell : Cell { - union { - FreeCell *link; - double data; - }; -}; - -JS_STATIC_ASSERT(sizeof(FreeCell) == Cell::CellSize); - } /* namespace gc */ } /* namespace js */ diff --git a/deps/mozjs/js/src/jsclass.h b/deps/mozjs/js/src/jsclass.h new file mode 100644 index 00000000000..37f911471df --- /dev/null +++ b/deps/mozjs/js/src/jsclass.h @@ -0,0 +1,431 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=79 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsclass_h__ +#define jsclass_h__ +/* + * A JSClass acts as a vtable for JS objects that allows JSAPI clients to + * control various aspects of the behavior of an object like property lookup. + * js::Class is an engine-private extension that allows more control over + * object behavior and, e.g., allows custom slow layout. + */ +#include "jsapi.h" +#include "jsprvtd.h" + +#ifdef __cplusplus + +namespace js { + +class PropertyName; +class SpecialId; + +static JS_ALWAYS_INLINE jsid +SPECIALID_TO_JSID(const SpecialId &sid); + +/* + * We partition the ways to refer to a property into three: by an index + * (uint32_t); by a string whose characters do not represent an index + * (PropertyName, see vm/String.h); and by various special values. + * + * Special values are encoded using SpecialId, which is layout-compatible but + * non-interconvertible with jsid. A SpecialId may be: an object (used by E4X + * and perhaps eventually by Harmony-proposed private names); JSID_VOID, which + * does not occur in JS scripts but may be used to indicate the absence of a + * valid identifier; or JS_DEFAULT_XML_NAMESPACE_ID, if E4X is enabled. + */ + +class SpecialId { + uintptr_t bits; + + /* Needs access to raw bits. */ + friend JS_ALWAYS_INLINE jsid SPECIALID_TO_JSID(const SpecialId &sid); + + static const uintptr_t TYPE_VOID = JSID_TYPE_VOID; + static const uintptr_t TYPE_OBJECT = JSID_TYPE_OBJECT; + static const uintptr_t TYPE_DEFAULT_XML_NAMESPACE = JSID_TYPE_DEFAULT_XML_NAMESPACE; + static const uintptr_t TYPE_MASK = JSID_TYPE_MASK; + + SpecialId(uintptr_t bits) : bits(bits) { } + + public: + SpecialId() : bits(TYPE_VOID) { } + + /* Object-valued */ + + SpecialId(JSObject &obj) + : bits(uintptr_t(&obj) | TYPE_OBJECT) + { + JS_ASSERT(&obj != NULL); + JS_ASSERT((uintptr_t(&obj) & TYPE_MASK) == 0); + } + + bool isObject() const { + return (bits & TYPE_MASK) == TYPE_OBJECT && bits != TYPE_OBJECT; + } + + JSObject *toObject() const { + JS_ASSERT(isObject()); + return reinterpret_cast(bits & ~TYPE_MASK); + } + + /* Empty */ + + static SpecialId empty() { + SpecialId sid(TYPE_OBJECT); + JS_ASSERT(sid.isEmpty()); + return sid; + } + + bool isEmpty() const { + return bits == TYPE_OBJECT; + } + + /* Void */ + + static SpecialId voidId() { + SpecialId sid(TYPE_VOID); + JS_ASSERT(sid.isVoid()); + return sid; + } + + bool isVoid() const { + return bits == TYPE_VOID; + } + + /* Default XML namespace */ + + static SpecialId defaultXMLNamespace() { + SpecialId sid(TYPE_DEFAULT_XML_NAMESPACE); + JS_ASSERT(sid.isDefaultXMLNamespace()); + return sid; + } + + bool isDefaultXMLNamespace() const { + return bits == TYPE_DEFAULT_XML_NAMESPACE; + } +}; + +static JS_ALWAYS_INLINE jsid +SPECIALID_TO_JSID(const SpecialId &sid) +{ + jsid id; + JSID_BITS(id) = sid.bits; + JS_ASSERT_IF(sid.isObject(), JSID_IS_OBJECT(id) && JSID_TO_OBJECT(id) == sid.toObject()); + JS_ASSERT_IF(sid.isVoid(), JSID_IS_VOID(id)); + JS_ASSERT_IF(sid.isEmpty(), JSID_IS_EMPTY(id)); + JS_ASSERT_IF(sid.isDefaultXMLNamespace(), JSID_IS_DEFAULT_XML_NAMESPACE(id)); + return id; +} + +static JS_ALWAYS_INLINE bool +JSID_IS_SPECIAL(jsid id) +{ + return JSID_IS_OBJECT(id) || JSID_IS_EMPTY(id) || JSID_IS_VOID(id) || + JSID_IS_DEFAULT_XML_NAMESPACE(id); +} + +static JS_ALWAYS_INLINE SpecialId +JSID_TO_SPECIALID(jsid id) +{ + JS_ASSERT(JSID_IS_SPECIAL(id)); + if (JSID_IS_OBJECT(id)) + return SpecialId(*JSID_TO_OBJECT(id)); + if (JSID_IS_EMPTY(id)) + return SpecialId::empty(); + if (JSID_IS_VOID(id)) + return SpecialId::voidId(); + JS_ASSERT(JSID_IS_DEFAULT_XML_NAMESPACE(id)); + return SpecialId::defaultXMLNamespace(); +} + +/* js::Class operation signatures. */ + +typedef JSBool +(* LookupGenericOp)(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); +typedef JSBool +(* LookupPropOp)(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, + JSProperty **propp); +typedef JSBool +(* LookupElementOp)(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, + JSProperty **propp); +typedef JSBool +(* LookupSpecialOp)(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, + JSProperty **propp); +typedef JSBool +(* DefineGenericOp)(JSContext *cx, JSObject *obj, jsid id, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); +typedef JSBool +(* DefinePropOp)(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); +typedef JSBool +(* DefineElementOp)(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); +typedef JSBool +(* DefineSpecialOp)(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); +typedef JSBool +(* GenericIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp); +typedef JSBool +(* PropertyIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp); +typedef JSBool +(* ElementIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp); +typedef JSBool +(* ElementIfPresentOp)(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp, bool* present); +typedef JSBool +(* SpecialIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp); +typedef JSBool +(* StrictGenericIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict); +typedef JSBool +(* StrictPropertyIdOp)(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict); +typedef JSBool +(* StrictElementIdOp)(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict); +typedef JSBool +(* StrictSpecialIdOp)(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict); +typedef JSBool +(* GenericAttributesOp)(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); +typedef JSBool +(* PropertyAttributesOp)(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp); +typedef JSBool +(* ElementAttributesOp)(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); +typedef JSBool +(* SpecialAttributesOp)(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp); +typedef JSBool +(* DeletePropertyOp)(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict); +typedef JSBool +(* DeleteElementOp)(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict); +typedef JSBool +(* DeleteSpecialOp)(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict); +typedef JSType +(* TypeOfOp)(JSContext *cx, JSObject *obj); + +/* + * Prepare to make |obj| non-extensible; in particular, fully resolve its properties. + * On error, return false. + * If |obj| is now ready to become non-extensible, set |*fixed| to true and return true. + * If |obj| refuses to become non-extensible, set |*fixed| to false and return true; the + * caller will throw an appropriate error. + */ +typedef JSBool +(* FixOp)(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props); + +typedef JSObject * +(* ObjectOp)(JSContext *cx, JSObject *obj); +typedef void +(* FinalizeOp)(JSContext *cx, JSObject *obj); + +#define JS_CLASS_MEMBERS \ + const char *name; \ + uint32_t flags; \ + \ + /* Mandatory non-null function pointer members. */ \ + JSPropertyOp addProperty; \ + JSPropertyOp delProperty; \ + JSPropertyOp getProperty; \ + JSStrictPropertyOp setProperty; \ + JSEnumerateOp enumerate; \ + JSResolveOp resolve; \ + JSConvertOp convert; \ + JSFinalizeOp finalize; \ + \ + /* Optionally non-null members start here. */ \ + JSClassInternal reserved0; \ + JSCheckAccessOp checkAccess; \ + JSNative call; \ + JSNative construct; \ + JSXDRObjectOp xdrObject; \ + JSHasInstanceOp hasInstance; \ + JSTraceOp trace + +/* + * The helper struct to measure the size of JS_CLASS_MEMBERS to know how much + * we have to padd js::Class to match the size of JSClass; + */ +struct ClassSizeMeasurement +{ + JS_CLASS_MEMBERS; +}; + +struct ClassExtension +{ + JSEqualityOp equality; + JSObjectOp outerObject; + JSObjectOp innerObject; + JSIteratorOp iteratorObject; + void *unused; + + /* + * isWrappedNative is true only if the class is an XPCWrappedNative. + * WeakMaps use this to override the wrapper disposal optimization. + */ + bool isWrappedNative; +}; + +#define JS_NULL_CLASS_EXT {NULL,NULL,NULL,NULL,NULL,false} + +struct ObjectOps +{ + LookupGenericOp lookupGeneric; + LookupPropOp lookupProperty; + LookupElementOp lookupElement; + LookupSpecialOp lookupSpecial; + DefineGenericOp defineGeneric; + DefinePropOp defineProperty; + DefineElementOp defineElement; + DefineSpecialOp defineSpecial; + GenericIdOp getGeneric; + PropertyIdOp getProperty; + ElementIdOp getElement; + ElementIfPresentOp getElementIfPresent; /* can be null */ + SpecialIdOp getSpecial; + StrictGenericIdOp setGeneric; + StrictPropertyIdOp setProperty; + StrictElementIdOp setElement; + StrictSpecialIdOp setSpecial; + GenericAttributesOp getGenericAttributes; + PropertyAttributesOp getPropertyAttributes; + ElementAttributesOp getElementAttributes; + SpecialAttributesOp getSpecialAttributes; + GenericAttributesOp setGenericAttributes; + PropertyAttributesOp setPropertyAttributes; + ElementAttributesOp setElementAttributes; + SpecialAttributesOp setSpecialAttributes; + DeletePropertyOp deleteProperty; + DeleteElementOp deleteElement; + DeleteSpecialOp deleteSpecial; + + JSNewEnumerateOp enumerate; + TypeOfOp typeOf; + FixOp fix; + ObjectOp thisObject; + FinalizeOp clear; +}; + +#define JS_NULL_OBJECT_OPS \ + {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, \ + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, \ + NULL,NULL,NULL,NULL,NULL,NULL} + +struct Class +{ + JS_CLASS_MEMBERS; + ClassExtension ext; + ObjectOps ops; + uint8_t pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) - + sizeof(ClassExtension) - sizeof(ObjectOps)]; + + /* Class is not native and its map is not a scope. */ + static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2; + + bool isNative() const { + return !(flags & NON_NATIVE); + } + + bool hasPrivate() const { + return !!(flags & JSCLASS_HAS_PRIVATE); + } +}; + +JS_STATIC_ASSERT(offsetof(JSClass, name) == offsetof(Class, name)); +JS_STATIC_ASSERT(offsetof(JSClass, flags) == offsetof(Class, flags)); +JS_STATIC_ASSERT(offsetof(JSClass, addProperty) == offsetof(Class, addProperty)); +JS_STATIC_ASSERT(offsetof(JSClass, delProperty) == offsetof(Class, delProperty)); +JS_STATIC_ASSERT(offsetof(JSClass, getProperty) == offsetof(Class, getProperty)); +JS_STATIC_ASSERT(offsetof(JSClass, setProperty) == offsetof(Class, setProperty)); +JS_STATIC_ASSERT(offsetof(JSClass, enumerate) == offsetof(Class, enumerate)); +JS_STATIC_ASSERT(offsetof(JSClass, resolve) == offsetof(Class, resolve)); +JS_STATIC_ASSERT(offsetof(JSClass, convert) == offsetof(Class, convert)); +JS_STATIC_ASSERT(offsetof(JSClass, finalize) == offsetof(Class, finalize)); +JS_STATIC_ASSERT(offsetof(JSClass, reserved0) == offsetof(Class, reserved0)); +JS_STATIC_ASSERT(offsetof(JSClass, checkAccess) == offsetof(Class, checkAccess)); +JS_STATIC_ASSERT(offsetof(JSClass, call) == offsetof(Class, call)); +JS_STATIC_ASSERT(offsetof(JSClass, construct) == offsetof(Class, construct)); +JS_STATIC_ASSERT(offsetof(JSClass, xdrObject) == offsetof(Class, xdrObject)); +JS_STATIC_ASSERT(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance)); +JS_STATIC_ASSERT(offsetof(JSClass, trace) == offsetof(Class, trace)); +JS_STATIC_ASSERT(sizeof(JSClass) == sizeof(Class)); + +static JS_ALWAYS_INLINE JSClass * +Jsvalify(Class *c) +{ + return (JSClass *)c; +} +static JS_ALWAYS_INLINE const JSClass * +Jsvalify(const Class *c) +{ + return (const JSClass *)c; +} + +static JS_ALWAYS_INLINE Class * +Valueify(JSClass *c) +{ + return (Class *)c; +} +static JS_ALWAYS_INLINE const Class * +Valueify(const JSClass *c) +{ + return (const Class *)c; +} + +/* + * Enumeration describing possible values of the [[Class]] internal property + * value of objects. + */ +enum ESClassValue { + ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp +}; + +/* + * Return whether the given object has the given [[Class]] internal property + * value. Beware, this query says nothing about the js::Class of the JSObject + * so the caller must not assume anything about obj's representation (e.g., obj + * may be a proxy). + */ +inline bool +ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx); + +/* Just a helper that checks v.isObject before calling ObjectClassIs. */ +inline bool +IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx); + +} /* namespace js */ + +#endif /* __cplusplus */ + +#endif /* jsclass_h__ */ diff --git a/deps/mozjs/js/src/jsclone.cpp b/deps/mozjs/js/src/jsclone.cpp index 54a62845fd2..ef93f5088d7 100644 --- a/deps/mozjs/js/src/jsclone.cpp +++ b/deps/mozjs/js/src/jsclone.cpp @@ -38,18 +38,25 @@ #include "jsclone.h" #include "jsdate.h" -#include "jsregexp.h" #include "jstypedarray.h" -#include "jsregexpinlines.h" +#include "jstypedarrayinlines.h" + +#include "vm/RegExpObject-inl.h" using namespace js; -namespace js +JS_FRIEND_API(uint64_t) +js_GetSCOffset(JSStructuredCloneWriter* writer) { + JS_ASSERT(writer); + return writer->output().count() * sizeof(uint64_t); +} + +namespace js { bool -WriteStructuredClone(JSContext *cx, const Value &v, uint64 **bufp, size_t *nbytesp, +WriteStructuredClone(JSContext *cx, const Value &v, uint64_t **bufp, size_t *nbytesp, const JSStructuredCloneCallbacks *cb, void *cbClosure) { SCOutput out(cx); @@ -66,7 +73,7 @@ ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *v return r.read(vp); } -} +} /* namespace js */ enum StructuredDataType { /* Structured data types provided by the engine */ @@ -84,6 +91,7 @@ enum StructuredDataType { SCTAG_BOOLEAN_OBJECT, SCTAG_STRING_OBJECT, SCTAG_NUMBER_OBJECT, + SCTAG_BACK_REFERENCE_OBJECT, SCTAG_TYPED_ARRAY_MIN = 0xFFFF0100, SCTAG_TYPED_ARRAY_MAX = SCTAG_TYPED_ARRAY_MIN + TypedArray::TYPE_MAX - 1, SCTAG_END_OF_BUILTIN_TYPES @@ -211,7 +219,7 @@ SCInput::readArray(T *p, size_t nelems) return eof(); if (sizeof(T) == 1) { - memcpy(p, point, nelems); + js_memcpy(p, point, nelems); } else { const T *q = (const T *) point; const T *qend = q + nelems; @@ -321,7 +329,7 @@ SCOutput::writeArray(const T *p, size_t nelems) T *q = (T *) &buf[start]; if (sizeof(T) == 1) { - memcpy(q, p, nelems); + js_memcpy(q, p, nelems); } else { const T *pend = p + nelems; while (p != pend) @@ -390,7 +398,6 @@ JSStructuredCloneWriter::checkStack() else JS_ASSERT(total <= ids.length()); - JS_ASSERT(memory.count() == objs.length()); size_t j = objs.length(); for (size_t i = 0; i < limit; i++) JS_ASSERT(memory.has(&objs[--j].toObject())); @@ -430,24 +437,24 @@ TagToArrayType(uint32_t tag) bool JSStructuredCloneWriter::writeTypedArray(JSObject *obj) { - TypedArray *arr = TypedArray::fromJSObject(obj); - if (!out.writePair(ArrayTypeToTag(arr->type), arr->length)) + JSObject *arr = TypedArray::getTypedArray(obj); + if (!out.writePair(ArrayTypeToTag(TypedArray::getType(arr)), TypedArray::getLength(arr))) return false; - switch (arr->type) { + switch (TypedArray::getType(arr)) { case TypedArray::TYPE_INT8: case TypedArray::TYPE_UINT8: case TypedArray::TYPE_UINT8_CLAMPED: - return out.writeArray((const uint8_t *) arr->data, arr->length); + return out.writeArray((const uint8_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr)); case TypedArray::TYPE_INT16: case TypedArray::TYPE_UINT16: - return out.writeArray((const uint16_t *) arr->data, arr->length); + return out.writeArray((const uint16_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr)); case TypedArray::TYPE_INT32: case TypedArray::TYPE_UINT32: case TypedArray::TYPE_FLOAT32: - return out.writeArray((const uint32_t *) arr->data, arr->length); + return out.writeArray((const uint32_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr)); case TypedArray::TYPE_FLOAT64: - return out.writeArray((const uint64_t *) arr->data, arr->length); + return out.writeArray((const uint64_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr)); default: JS_NOT_REACHED("unknown TypedArray type"); return false; @@ -457,9 +464,9 @@ JSStructuredCloneWriter::writeTypedArray(JSObject *obj) bool JSStructuredCloneWriter::writeArrayBuffer(JSObject *obj) { - ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj); - return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, abuf->byteLength) && - out.writeBytes(abuf->data, abuf->byteLength); + obj = ArrayBuffer::getArrayBuffer(obj); + return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, obj->arrayBufferByteLength()) && + out.writeBytes(obj->arrayBufferDataOffset(), obj->arrayBufferByteLength()); } bool @@ -467,18 +474,18 @@ JSStructuredCloneWriter::startObject(JSObject *obj) { JS_ASSERT(obj->isArray() || obj->isObject()); - /* Fail if obj is already on the stack. */ - MemorySet::AddPtr p = memory.lookupForAdd(obj); - if (p) { - JSContext *cx = context(); - if (callbacks && callbacks->reportError) - callbacks->reportError(cx, JS_SCERR_RECURSION); - else - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_RECURSION); + /* Handle cycles in the object graph. */ + CloneMemory::AddPtr p = memory.lookupForAdd(obj); + if (p) + return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value); + if (!memory.add(p, obj, memory.count())) return false; - } - if (!memory.add(p, obj)) + + if (memory.count() == UINT32_MAX) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_NEED_DIET, "object graph to serialize"); return false; + } /* * Get enumerable property ids and put them in reverse order so that they @@ -516,9 +523,9 @@ JSStructuredCloneWriter::startWrite(const js::Value &v) } else if (v.isObject()) { JSObject *obj = &v.toObject(); if (obj->isRegExp()) { - RegExp *re = RegExp::extractFrom(obj); - return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) && - writeString(SCTAG_STRING, re->getSource()); + RegExpObject &reobj = obj->asRegExp(); + return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) && + writeString(SCTAG_STRING, reobj.getSource()); } else if (obj->isDate()) { jsdouble d = js_DateGetMsecSinceEpoch(context(), obj); return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d); @@ -526,7 +533,7 @@ JSStructuredCloneWriter::startWrite(const js::Value &v) return startObject(obj); } else if (js_IsTypedArray(obj)) { return writeTypedArray(obj); - } else if (js_IsArrayBuffer(obj) && ArrayBuffer::fromJSObject(obj)) { + } else if (js_IsArrayBuffer(obj)) { return writeArrayBuffer(obj); } else if (obj->isBoolean()) { return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->getPrimitiveThis().toBoolean()); @@ -567,24 +574,28 @@ JSStructuredCloneWriter::write(const Value &v) */ JSObject *obj2; JSProperty *prop; - if (!js_HasOwnProperty(context(), obj->getOps()->lookupProperty, obj, id, + if (!js_HasOwnProperty(context(), obj->getOps()->lookupGeneric, obj, id, &obj2, &prop)) { return false; } if (prop) { Value val; - if (!writeId(id) || !obj->getProperty(context(), id, &val) || !startWrite(val)) + if (!writeId(id) || + !obj->getGeneric(context(), id, &val) || + !startWrite(val)) return false; } } } else { out.writePair(SCTAG_NULL, 0); - memory.remove(obj); objs.popBack(); counts.popBack(); } } + + memory.clear(); + return true; } @@ -593,7 +604,7 @@ JSStructuredCloneReader::checkDouble(jsdouble d) { jsval_layout l; l.asDouble = d; - if (!JSVAL_IS_DOUBLE(JSVAL_FROM_LAYOUT(l))) { + if (!JSVAL_IS_DOUBLE_IMPL(l)) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN"); return false; @@ -605,14 +616,13 @@ class Chars { JSContext *cx; jschar *p; public: - Chars() : p(NULL) {} + Chars(JSContext *cx) : cx(cx), p(NULL) {} ~Chars() { if (p) cx->free_(p); } - bool allocate(JSContext *cx, size_t len) { + bool allocate(size_t len) { JS_ASSERT(!p); // We're going to null-terminate! p = (jschar *) cx->malloc_((len + 1) * sizeof(jschar)); - this->cx = cx; if (p) { p[len] = jschar(0); return true; @@ -631,8 +641,8 @@ JSStructuredCloneReader::readString(uint32_t nchars) "string length"); return NULL; } - Chars chars; - if (!chars.allocate(context(), nchars) || !in.readChars(chars.get(), nchars)) + Chars chars(context()); + if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars)) return NULL; JSString *str = js_NewString(context(), chars.get(), nchars); if (str) @@ -649,23 +659,23 @@ JSStructuredCloneReader::readTypedArray(uint32_t tag, uint32_t nelems, Value *vp return false; vp->setObject(*obj); - TypedArray *arr = TypedArray::fromJSObject(obj); - JS_ASSERT(arr->length == nelems); - JS_ASSERT(arr->type == atype); + JSObject *arr = TypedArray::getTypedArray(obj); + JS_ASSERT(TypedArray::getLength(arr) == nelems); + JS_ASSERT(TypedArray::getType(arr) == atype); switch (atype) { case TypedArray::TYPE_INT8: case TypedArray::TYPE_UINT8: case TypedArray::TYPE_UINT8_CLAMPED: - return in.readArray((uint8_t *) arr->data, nelems); + return in.readArray((uint8_t *) TypedArray::getDataOffset(arr), nelems); case TypedArray::TYPE_INT16: case TypedArray::TYPE_UINT16: - return in.readArray((uint16_t *) arr->data, nelems); + return in.readArray((uint16_t *) TypedArray::getDataOffset(arr), nelems); case TypedArray::TYPE_INT32: case TypedArray::TYPE_UINT32: case TypedArray::TYPE_FLOAT32: - return in.readArray((uint32_t *) arr->data, nelems); + return in.readArray((uint32_t *) TypedArray::getDataOffset(arr), nelems); case TypedArray::TYPE_FLOAT64: - return in.readArray((uint64_t *) arr->data, nelems); + return in.readArray((uint64_t *) TypedArray::getDataOffset(arr), nelems); default: JS_NOT_REACHED("unknown TypedArray type"); return false; @@ -679,9 +689,8 @@ JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp) if (!obj) return false; vp->setObject(*obj); - ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj); - JS_ASSERT(abuf->byteLength == nbytes); - return in.readArray((uint8_t *) abuf->data, nbytes); + JS_ASSERT(obj->arrayBufferByteLength() == nbytes); + return in.readArray(obj->arrayBufferDataOffset(), nbytes); } bool @@ -745,6 +754,7 @@ JSStructuredCloneReader::startRead(Value *vp) } case SCTAG_REGEXP_OBJECT: { + RegExpFlag flags = RegExpFlag(data); uint32_t tag2, nchars; if (!in.readPair(&tag2, &nchars)) return false; @@ -760,10 +770,11 @@ JSStructuredCloneReader::startRead(Value *vp) const jschar *chars = str->getChars(context()); if (!chars) return false; - JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data); - if (!obj) + + RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars, length, flags, NULL); + if (!reobj) return false; - vp->setObject(*obj); + vp->setObject(*reobj); break; } @@ -771,13 +782,24 @@ JSStructuredCloneReader::startRead(Value *vp) case SCTAG_OBJECT_OBJECT: { JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) ? NewDenseEmptyArray(context()) - : NewBuiltinClassInstance(context(), &js_ObjectClass); - if (!obj || !objs.append(ObjectValue(*obj))) + : NewBuiltinClassInstance(context(), &ObjectClass); + if (!obj || !objs.append(ObjectValue(*obj)) || + !allObjs.append(ObjectValue(*obj))) return false; vp->setObject(*obj); break; } + case SCTAG_BACK_REFERENCE_OBJECT: { + if (data >= allObjs.length()) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, + "invalid input"); + } + *vp = allObjs[data]; + break; + } + case SCTAG_ARRAY_BUFFER_OBJECT: return readArrayBuffer(data, vp); @@ -822,7 +844,7 @@ JSStructuredCloneReader::readId(jsid *idp) JSString *str = readString(data); if (!str) return false; - JSAtom *atom = js_AtomizeString(context(), str, 0); + JSAtom *atom = js_AtomizeString(context(), str); if (!atom) return false; *idp = ATOM_TO_JSID(atom); @@ -853,9 +875,12 @@ JSStructuredCloneReader::read(Value *vp) objs.popBack(); } else { Value v; - if (!startRead(&v) || !obj->defineProperty(context(), id, v)) + if (!startRead(&v) || !obj->defineGeneric(context(), id, v)) return false; } } + + allObjs.clear(); + return true; } diff --git a/deps/mozjs/js/src/jsclone.h b/deps/mozjs/js/src/jsclone.h index 00ab2c1d4f5..7d202eaab93 100644 --- a/deps/mozjs/js/src/jsclone.h +++ b/deps/mozjs/js/src/jsclone.h @@ -41,10 +41,9 @@ #include "jsapi.h" #include "jscntxt.h" -#include "jshashtable.h" -#include "jsstdint.h" -#include "jsvector.h" -#include "jsvalue.h" + +#include "js/HashTable.h" +#include "js/Vector.h" namespace js { @@ -73,6 +72,8 @@ struct SCOutput { bool extractBuffer(uint64_t **datap, size_t *sizep); + uint64_t count() { return buf.length(); } + private: JSContext *cx; js::Vector buf; @@ -113,7 +114,8 @@ struct JSStructuredCloneReader { public: explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, void *cbClosure) - : in(in), objs(in.context()), callbacks(cb), closure(cbClosure) { } + : in(in), objs(in.context()), allObjs(in.context()), + callbacks(cb), closure(cbClosure) { } js::SCInput &input() { return in; } bool read(js::Value *vp); @@ -133,6 +135,9 @@ struct JSStructuredCloneReader { // Stack of objects with properties remaining to be read. js::AutoValueVector objs; + // Stack of all objects read during this deserialization + js::AutoValueVector allObjs; + // The user defined callbacks that will be used for cloning. const JSStructuredCloneCallbacks *callbacks; @@ -167,7 +172,7 @@ struct JSStructuredCloneWriter { js::SCOutput &out; - // Stack of objects with properties remaining to be written. + // Vector of objects with properties remaining to be written. js::AutoValueVector objs; // counts[i] is the number of properties of objs[i] remaining to be written. @@ -178,9 +183,10 @@ struct JSStructuredCloneWriter { js::AutoIdVector ids; // The "memory" list described in the HTML5 internal structured cloning algorithm. - // memory has the same elements as objs. - typedef js::HashSet MemorySet; - MemorySet memory; + // memory is a superset of objs; items are never removed from Memory + // until a serialization operation is finished + typedef js::HashMap CloneMemory; + CloneMemory memory; // The user defined callbacks that will be used for cloning. const JSStructuredCloneCallbacks *callbacks; diff --git a/deps/mozjs/js/src/jscntxt.cpp b/deps/mozjs/js/src/jscntxt.cpp index b9b8070d40e..e9a0b8d60d8 100644 --- a/deps/mozjs/js/src/jscntxt.cpp +++ b/deps/mozjs/js/src/jscntxt.cpp @@ -41,6 +41,9 @@ /* * JS execution context. */ + +#include /* make sure that is included and we can use + __GLIBC__ to detect glibc presence */ #include #include #include @@ -51,10 +54,7 @@ # include #endif // ANDROID -#include "jsstdint.h" - #include "jstypes.h" -#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsprf.h" @@ -69,22 +69,23 @@ #include "jsiter.h" #include "jslock.h" #include "jsmath.h" -#include "jsnativestack.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jspubtd.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" #include "jsstr.h" -#include "jstracer.h" #ifdef JS_METHODJIT # include "assembler/assembler/MacroAssembler.h" +# include "methodjit/MethodJIT.h" #endif +#include "frontend/TokenStream.h" +#include "frontend/ParseMaps.h" +#include "yarr/BumpPointerAllocator.h" +#include "jsatominlines.h" #include "jscntxtinlines.h" #include "jscompartment.h" #include "jsobjinlines.h" @@ -92,284 +93,105 @@ using namespace js; using namespace js::gc; -namespace js { - -ThreadData::ThreadData() - : interruptFlags(0), -#ifdef JS_THREADSAFE - requestDepth(0), -#endif -#ifdef JS_TRACER - onTraceCompartment(NULL), - recordingCompartment(NULL), - profilingCompartment(NULL), - maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE), -#endif - waiveGCQuota(false), - dtoaState(NULL), - nativeStackBase(GetNativeStackBase()), - pendingProxyOperation(NULL) -{ -} - -ThreadData::~ThreadData() -{ - if (dtoaState) - js_DestroyDtoaState(dtoaState); -} - -bool -ThreadData::init() -{ - return stackSpace.init() && !!(dtoaState = js_NewDtoaState()); -} - void -ThreadData::triggerOperationCallback(JSRuntime *rt) +JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, + size_t *regexpCode, size_t *stackCommitted) { - /* - * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures - * the write will become immediately visible to other processors polling - * the flag. Note that we only care about visibility here, not read/write - * ordering: this field can only be written with the GC lock held. - */ - if (interruptFlags) - return; - JS_ATOMIC_SET(&interruptFlags, 1); + if (normal) + *normal = mallocSizeOf(dtoaState); + + if (temporary) + *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); + + if (regexpCode) { + size_t method = 0, regexp = 0, unused = 0; + if (execAlloc_) + execAlloc_->sizeOfCode(&method, ®exp, &unused); + JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ + *regexpCode = regexp + unused; + } -#ifdef JS_THREADSAFE - /* rt->interruptCounter does not reflect suspended threads. */ - if (requestDepth != 0) - JS_ATOMIC_INCREMENT(&rt->interruptCounter); -#endif + if (stackCommitted) + *stackCommitted = stackSpace.sizeOfCommitted(); } -} /* namespace js */ - -#ifdef JS_THREADSAFE - -JSThread * -js_CurrentThread(JSRuntime *rt) +JS_FRIEND_API(void) +JSRuntime::triggerOperationCallback() { - void *id = js_CurrentThreadId(); - JS_LOCK_GC(rt); - /* - * We must not race with a GC that accesses cx->thread for JSContext - * instances on all threads, see bug 476934. + * Use JS_ATOMIC_SET in the hope that it ensures the write will become + * immediately visible to other processors polling the flag. */ - js_WaitForGC(rt); - - JSThread *thread; - JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id); - if (p) { - thread = p->value; - - /* - * If thread has no contexts, it might be left over from a previous - * thread with the same id but a different stack address. - */ - if (JS_CLIST_IS_EMPTY(&thread->contextList)) - thread->data.nativeStackBase = GetNativeStackBase(); - } else { - JS_UNLOCK_GC(rt); - - thread = OffTheBooks::new_(id); - if (!thread || !thread->init()) { - Foreground::delete_(thread); - return NULL; - } - JS_LOCK_GC(rt); - js_WaitForGC(rt); - if (!rt->threads.relookupOrAdd(p, id, thread)) { - JS_UNLOCK_GC(rt); - Foreground::delete_(thread); - return NULL; - } - - /* Another thread cannot add an entry for the current thread id. */ - JS_ASSERT(p->value == thread); - } - JS_ASSERT(thread->id == id); - -#ifdef DEBUG - char* gnsb = (char*) GetNativeStackBase(); - JS_ASSERT(gnsb + 0 == (char*) thread->data.nativeStackBase || - /* Work around apparent glibc bug; see bug 608526. */ - gnsb + 0x1000 == (char*) thread->data.nativeStackBase || - gnsb + 0x2000 == (char*) thread->data.nativeStackBase || - gnsb + 0x3000 == (char*) thread->data.nativeStackBase); -#endif - - return thread; -} - -JSBool -js_InitContextThread(JSContext *cx) -{ - JSThread *thread = js_CurrentThread(cx->runtime); - if (!thread) - return false; - - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - cx->setThread(thread); - return true; + JS_ATOMIC_SET(&interrupt, 1); } void -JSContext::setThread(JSThread *thread) +JSRuntime::setJitHardening(bool enabled) { - thread_ = thread; - stack.threadReset(); + jitHardening = enabled; + if (execAlloc_) + execAlloc_->setRandomize(enabled); } -void -js_ClearContextThread(JSContext *cx) +JSC::ExecutableAllocator * +JSRuntime::createExecutableAllocator(JSContext *cx) { - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); - cx->setThread(NULL); -} - -#endif /* JS_THREADSAFE */ + JS_ASSERT(!execAlloc_); + JS_ASSERT(cx->runtime == this); -ThreadData * -js_CurrentThreadData(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - JSThread *thread = js_CurrentThread(rt); - if (!thread) - return NULL; - - return &thread->data; -#else - return &rt->threadData; -#endif + JSC::AllocationBehavior randomize = + jitHardening ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic; + execAlloc_ = new_(randomize); + if (!execAlloc_) + js_ReportOutOfMemory(cx); + return execAlloc_; } -JSBool -js_InitThreads(JSRuntime *rt) +WTF::BumpPointerAllocator * +JSRuntime::createBumpPointerAllocator(JSContext *cx) { -#ifdef JS_THREADSAFE - return rt->threads.init(4); -#else - return rt->threadData.init(); -#endif -} + JS_ASSERT(!bumpAlloc_); + JS_ASSERT(cx->runtime == this); -void -js_FinishThreads(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - if (!rt->threads.initialized()) - return; - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - Foreground::delete_(thread); - } - rt->threads.clear(); -#endif + bumpAlloc_ = new_(); + if (!bumpAlloc_) + js_ReportOutOfMemory(cx); + return bumpAlloc_; } -void -js_PurgeThreads(JSContext *cx) +JSScript * +js_GetCurrentScript(JSContext *cx) { -#ifdef JS_THREADSAFE - for (JSThread::Map::Enum e(cx->runtime->threads); - !e.empty(); - e.popFront()) { - JSThread *thread = e.front().value; - - if (JS_CLIST_IS_EMPTY(&thread->contextList)) { - JS_ASSERT(cx->thread() != thread); - Foreground::delete_(thread); - e.removeFront(); - } else { - thread->data.purge(cx); - } - } -#else - cx->runtime->threadData.purge(cx); -#endif + return cx->hasfp() ? cx->fp()->maybeScript() : NULL; } -static const size_t ARENA_HEADER_SIZE_HACK = 40; -static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK; - JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { - JSContext *cx; - JSBool ok, first; - JSContextCallback cxCallback; + JS_AbortIfWrongThread(rt); /* * We need to initialize the new context fully before adding it to the * runtime list. After that it can be accessed from another thread via * js_ContextIterator. */ - void *mem = OffTheBooks::calloc_(sizeof *cx); - if (!mem) + JSContext *cx = OffTheBooks::new_(rt); + if (!cx) return NULL; - cx = new (mem) JSContext(rt); - cx->debugHooks = &rt->globalDebugHooks; -#if JS_STACK_GROWTH_DIRECTION > 0 - cx->stackLimit = (jsuword) -1; -#endif - cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; - JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT); - VOUCH_DOES_NOT_REQUIRE_STACK(); - - JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble), - &cx->scriptStackQuota); - JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int), - &cx->scriptStackQuota); - - JS_ASSERT(cx->resolveFlags == 0); if (!cx->busyArrays.init()) { Foreground::delete_(cx); return NULL; } -#ifdef JS_THREADSAFE - if (!js_InitContextThread(cx)) { - Foreground::delete_(cx); - return NULL; - } -#endif - /* - * Here the GC lock is still held after js_InitContextThread took it and + * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and * the GC is not running on another thread. */ - for (;;) { - if (rt->state == JSRTS_UP) { - JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList)); - first = JS_FALSE; - break; - } - if (rt->state == JSRTS_DOWN) { - JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList)); - first = JS_TRUE; - rt->state = JSRTS_LAUNCHING; - break; - } - JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); - - /* - * During the above wait after we are notified about the state change - * but before we wake up, another thread could enter the GC from - * js_DestroyContext, bug 478336. So we must wait here to ensure that - * when we exit the loop with the first flag set to true, that GC is - * finished. - */ - js_WaitForGC(rt); - } + bool first = JS_CLIST_IS_EMPTY(&rt->contextList); JS_APPEND_LINK(&cx->link, &rt->contextList); - JS_UNLOCK_GC(rt); js_InitRandom(cx); @@ -385,17 +207,9 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) #ifdef JS_THREADSAFE JS_BeginRequest(cx); #endif - ok = js_InitCommonAtoms(cx); - - /* - * scriptFilenameTable may be left over from a previous episode of - * non-zero contexts alive in rt, so don't re-init the table if it's - * not necessary. - */ - if (ok && !rt->scriptFilenameTable) - ok = js_InitRuntimeScriptState(rt); + bool ok = rt->staticStrings.init(cx); if (ok) - ok = js_InitRuntimeNumberState(cx); + ok = js_InitCommonAtoms(cx); #ifdef JS_THREADSAFE JS_EndRequest(cx); @@ -404,13 +218,9 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) js_DestroyContext(cx, JSDCM_NEW_FAILED); return NULL; } - - AutoLockGC lock(rt); - rt->state = JSRTS_UP; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); } - cxCallback = rt->cxCallback; + JSContextCallback cxCallback = rt->cxCallback; if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { js_DestroyContext(cx, JSDCM_NEW_FAILED); return NULL; @@ -419,153 +229,20 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) return cx; } -#if defined DEBUG && defined XP_UNIX -# include - -class JSAutoFile { -public: - JSAutoFile() : mFile(NULL) {} - - ~JSAutoFile() { - if (mFile) - fclose(mFile); - } - - FILE *open(const char *fname, const char *mode) { - return mFile = fopen(fname, mode); - } - operator FILE *() { - return mFile; - } - -private: - FILE *mFile; -}; - -static void -DumpEvalCacheMeter(JSContext *cx) -{ - if (const char *filename = getenv("JS_EVALCACHE_STATFILE")) { - struct { - const char *name; - ptrdiff_t offset; - } table[] = { -#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) } - EVAL_CACHE_METER_LIST(frob) -#undef frob - }; - JSEvalCacheMeter *ecm = &cx->compartment->evalCacheMeter; - - static JSAutoFile fp; - if (!fp && !fp.open(filename, "w")) - return; - - fprintf(fp, "eval cache meter (%p):\n", -#ifdef JS_THREADSAFE - (void *) cx->thread() -#else - (void *) cx->runtime -#endif - ); - for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) { - fprintf(fp, "%-8.8s %llu\n", - table[i].name, - (unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset)); - } - fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe); - fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe); - fflush(fp); - } -} -# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx) - -static void -DumpFunctionCountMap(const char *title, JSRuntime::FunctionCountMap &map, FILE *fp) -{ - fprintf(fp, "\n%s count map:\n", title); - - for (JSRuntime::FunctionCountMap::Range r = map.all(); !r.empty(); r.popFront()) { - JSFunction *fun = r.front().key; - int32 count = r.front().value; - - fprintf(fp, "%10d %s:%u\n", count, fun->u.i.script->filename, fun->u.i.script->lineno); - } -} - -static void -DumpFunctionMeter(JSContext *cx) -{ - if (const char *filename = cx->runtime->functionMeterFilename) { - struct { - const char *name; - ptrdiff_t offset; - } table[] = { -#define frob(x) { #x, offsetof(JSFunctionMeter, x) } - FUNCTION_KIND_METER_LIST(frob) -#undef frob - }; - JSFunctionMeter *fm = &cx->runtime->functionMeter; - - static JSAutoFile fp; - if (!fp && !fp.open(filename, "w")) - return; - - fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename); - for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) - fprintf(fp, "%-19.19s %d\n", table[i].name, *(int32 *)((uint8 *)fm + table[i].offset)); - - DumpFunctionCountMap("method read barrier", cx->runtime->methodReadBarrierCountMap, fp); - DumpFunctionCountMap("unjoined function", cx->runtime->unjoinedFunctionCountMap, fp); - - putc('\n', fp); - fflush(fp); - } -} - -# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx) - -#endif /* DEBUG && XP_UNIX */ - -#ifndef DUMP_EVAL_CACHE_METER -# define DUMP_EVAL_CACHE_METER(cx) ((void) 0) -#endif - -#ifndef DUMP_FUNCTION_METER -# define DUMP_FUNCTION_METER(cx) ((void) 0) -#endif - void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) { - JSRuntime *rt; - JSContextCallback cxCallback; - JSBool last; + JSRuntime *rt = cx->runtime; + JS_AbortIfWrongThread(rt); JS_ASSERT(!cx->enumerators); - rt = cx->runtime; #ifdef JS_THREADSAFE - /* - * For API compatibility we allow to destroy contexts without a thread in - * optimized builds. We assume that the embedding knows that an OOM error - * cannot happen in JS_SetContextThread. - */ - JS_ASSERT(cx->thread() && CURRENT_THREAD_IS_ME(cx->thread())); - if (!cx->thread()) - JS_SetContextThread(cx); - - /* - * For API compatibility we support destroying contexts with non-zero - * cx->outstandingRequests but we assume that all JS_BeginRequest calls - * on this cx contributes to cx->thread->data.requestDepth and there is no - * JS_SuspendRequest calls that set aside the counter. - */ - JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth); + JS_ASSERT(cx->outstandingRequests == 0); #endif if (mode != JSDCM_NEW_FAILED) { - cxCallback = rt->cxCallback; - if (cxCallback) { + if (JSContextCallback cxCallback = rt->cxCallback) { /* * JSCONTEXT_DESTROY callback is not allowed to fail and must * return true. @@ -576,101 +253,48 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } JS_LOCK_GC(rt); - JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); -#ifdef JS_THREADSAFE - /* - * Typically we are called outside a request, so ensure that the GC is not - * running before removing the context from rt->contextList, see bug 477021. - */ - if (cx->thread()->data.requestDepth == 0) - js_WaitForGC(rt); -#endif JS_REMOVE_LINK(&cx->link); - last = (rt->contextList.next == &rt->contextList); - if (last) - rt->state = JSRTS_LANDING; - if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC -#ifdef JS_THREADSAFE - || cx->outstandingRequests != 0 -#endif - ) { + bool last = !rt->hasContexts(); + if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC) { JS_ASSERT(!rt->gcRunning); - JS_UNLOCK_GC(rt); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + JS_UNLOCK_GC(rt); if (last) { -#ifdef JS_THREADSAFE /* - * If this thread is not in a request already, begin one now so - * that we wait for any racing GC started on a not-last context to - * finish, before we plow ahead and unpin atoms. Note that even - * though we begin a request here if necessary, we end all - * thread's requests before forcing a final GC. This lets any - * not-last context destruction racing in another thread try to - * force or maybe run the GC, but by that point, rt->state will - * not be JSRTS_UP, and that GC attempt will return early. + * Dump remaining type inference results first. This printing + * depends on atoms still existing. */ - if (cx->thread()->data.requestDepth == 0) - JS_BeginRequest(cx); -#endif - - js_FinishRuntimeNumberState(cx); + { + AutoLockGC lock(rt); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->types.print(cx, false); + } /* Unpin all common atoms before final GC. */ js_FinishCommonAtoms(cx); /* Clear debugging state to remove GC roots. */ - JS_ClearAllTraps(cx); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->clearTraps(cx); JS_ClearAllWatchPoints(cx); - } -#ifdef JS_THREADSAFE - /* - * Destroying a context implicitly calls JS_EndRequest(). Also, we must - * end our request here in case we are "last" -- in that event, another - * js_DestroyContext that was not last might be waiting in the GC for our - * request to end. We'll let it run below, just before we do the truly - * final GC and then free atom state. - */ - while (cx->outstandingRequests != 0) - JS_EndRequest(cx); -#endif + js_GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); - if (last) { - js_GC(cx, NULL, GC_LAST_CONTEXT); - DUMP_EVAL_CACHE_METER(cx); - DUMP_FUNCTION_METER(cx); - - /* Take the runtime down, now that it has no contexts or atoms. */ - JS_LOCK_GC(rt); - rt->state = JSRTS_DOWN; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - } else { - if (mode == JSDCM_FORCE_GC) - js_GC(cx, NULL, GC_NORMAL); - else if (mode == JSDCM_MAYBE_GC) - JS_MaybeGC(cx); - JS_LOCK_GC(rt); - js_WaitForGC(rt); + } else if (mode == JSDCM_FORCE_GC) { + js_GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); + } else if (mode == JSDCM_MAYBE_GC) { + JS_MaybeGC(cx); } + JS_LOCK_GC(rt); } #ifdef JS_THREADSAFE -#ifdef DEBUG - JSThread *t = cx->thread(); -#endif - js_ClearContextThread(cx); - JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); -#endif -#ifdef JS_METER_DST_OFFSET_CACHING - cx->dstOffsetCache.dumpStats(); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif JS_UNLOCK_GC(rt); -#ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); -#endif Foreground::delete_(cx); } @@ -680,30 +304,15 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) JSContext *cx = *iterp; Maybe lockIf; - if (unlocked) + if (unlocked) lockIf.construct(rt); - cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next); + cx = JSContext::fromLinkField(cx ? cx->link.next : rt->contextList.next); if (&cx->link == &rt->contextList) cx = NULL; *iterp = cx; return cx; } -JS_FRIEND_API(JSContext *) -js_NextActiveContext(JSRuntime *rt, JSContext *cx) -{ - JSContext *iter = cx; -#ifdef JS_THREADSAFE - while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (cx->outstandingRequests && cx->thread()->data.requestDepth) - break; - } - return cx; -#else - return js_ContextIterator(rt, JS_FALSE, &iter); -#endif -} - namespace js { bool @@ -741,7 +350,7 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp, * * If an exception was raised, then we call the debugErrorHook * (if present) to give it a chance to see the error before it - * propagates out of scope. This is needed for compatability + * propagates out of scope. This is needed for compatibility * with the old scheme. */ if (!JS_IsRunning(cx) || @@ -755,7 +364,10 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp, } } -/* The report must be initially zeroed. */ +/* + * The given JSErrorReport object have been zeroed and must not outlive + * cx->fp() (otherwise report->originPrincipals may become invalid). + */ static void PopulateReportBlame(JSContext *cx, JSErrorReport *report) { @@ -763,10 +375,11 @@ PopulateReportBlame(JSContext *cx, JSErrorReport *report) * Walk stack until we find a frame that is associated with some script * rather than a native frame. */ - for (StackFrame *fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) { - if (fp->pc(cx)) { - report->filename = fp->script()->filename; - report->lineno = js_FramePCToLineNumber(cx, fp); + for (FrameRegsIter iter(cx); !iter.done(); ++iter) { + if (iter.fp()->isScriptFrame()) { + report->filename = iter.fp()->script()->filename; + report->lineno = js_PCToLineNumber(cx, iter.fp()->script(), iter.pc()); + report->originPrincipals = iter.fp()->script()->originPrincipals; break; } } @@ -782,14 +395,7 @@ PopulateReportBlame(JSContext *cx, JSErrorReport *report) void js_ReportOutOfMemory(JSContext *cx) { -#ifdef JS_TRACER - /* - * If we are in a builtin called directly from trace, don't report an - * error. We will retry in the interpreter instead. - */ - if (JS_ON_TRACE(cx) && !JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit) - return; -#endif + cx->runtime->hadOutOfMemory = true; JSErrorReport report; JSErrorReporter onError = cx->errorReporter; @@ -820,20 +426,26 @@ js_ReportOutOfMemory(JSContext *cx) } } - if (onError) + if (onError) { + AutoAtomicIncrement incr(&cx->runtime->inOOMReport); onError(cx, msg, &report); -} - -void -js_ReportOutOfScriptQuota(JSContext *maybecx) -{ - if (maybecx) - JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_SCRIPT_STACK_QUOTA); + } } JS_FRIEND_API(void) js_ReportOverRecursed(JSContext *maybecx) { +#ifdef JS_MORE_DETERMINISTIC + /* + * We cannot make stack depth deterministic across different + * implementations (e.g. JIT vs. interpreter will differ in + * their maximum stack depth). + * However, we can detect externally when we hit the maximum + * stack depth which is useful for external testing programs + * like fuzzers. + */ + fprintf(stderr, "js_ReportOverRecursed called\n"); +#endif if (maybecx) JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); } @@ -860,8 +472,8 @@ checkReportFlags(JSContext *cx, uintN *flags) * We assume that if the top frame is a native, then it is strict if * the nearest scripted frame is strict, see bug 536306. */ - StackFrame *fp = js_GetScriptedCaller(cx, NULL); - if (fp && fp->script()->strictModeCode) + JSScript *script = cx->stack.currentScript(); + if (script && script->strictModeCode) *flags &= ~JSREPORT_WARNING; else if (cx->hasStrictOption()) *flags |= JSREPORT_WARNING; @@ -900,7 +512,7 @@ js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) PodZero(&report); report.flags = flags; report.errorNumber = JSMSG_USER_DEFINED_ERROR; - report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); + report.ucmessage = ucmessage = InflateString(cx, message, &messagelen); PopulateReportBlame(cx, &report); warning = JSREPORT_IS_WARNING(report.flags); @@ -960,8 +572,7 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, if (charArgs) { char *charArg = va_arg(ap, char *); size_t charArgLength = strlen(charArg); - reportp->messageArgs[i] - = js_InflateString(cx, charArg, &charArgLength); + reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength); if (!reportp->messageArgs[i]) goto error; } else { @@ -984,7 +595,7 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, size_t expandedLength; size_t len = strlen(efs->format); - buffer = fmt = js_InflateString (cx, efs->format, &len); + buffer = fmt = InflateString(cx, efs->format, &len); if (!buffer) goto error; expandedLength = len @@ -1019,9 +630,8 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, JS_ASSERT(expandedArgs == argCount); *out = 0; cx->free_(buffer); - *messagep = - js_DeflateString(cx, reportp->ucmessage, - (size_t)(out - reportp->ucmessage)); + *messagep = DeflateString(cx, reportp->ucmessage, + size_t(out - reportp->ucmessage)); if (!*messagep) goto error; } @@ -1036,7 +646,7 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, if (!*messagep) goto error; len = strlen(*messagep); - reportp->ucmessage = js_InflateString(cx, *messagep, &len); + reportp->ucmessage = InflateString(cx, *messagep, &len); if (!reportp->ucmessage) goto error; } @@ -1202,7 +812,7 @@ js_ReportMissingArg(JSContext *cx, const Value &v, uintN arg) JS_snprintf(argbuf, sizeof argbuf, "%u", arg); bytes = NULL; if (IsFunctionObject(v)) { - atom = GET_FUNCTION_PRIVATE(cx, &v.toObject())->atom; + atom = v.toObject().toFunction()->atom; bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, atom); if (!bytes) @@ -1234,12 +844,6 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber, return ok; } -#if defined DEBUG && defined XP_UNIX -/* For gdb usage. */ -void js_logon(JSContext *cx) { cx->logfp = stderr; cx->logPrevPc = NULL; } -void js_logoff(JSContext *cx) { cx->logfp = NULL; } -#endif - JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { #define MSG_DEF(name, number, count, exception, format) \ { format, count, exception } , @@ -1255,58 +859,23 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) return NULL; } -bool -checkOutOfMemory(JSRuntime *rt) -{ - AutoLockGC lock(rt); - return rt->gcBytes > rt->gcMaxBytes; -} - JSBool js_InvokeOperationCallback(JSContext *cx) { - JSRuntime *rt = cx->runtime; - ThreadData *td = JS_THREAD_DATA(cx); - JS_ASSERT_REQUEST_DEPTH(cx); - JS_ASSERT(td->interruptFlags != 0); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->interrupt != 0); /* * Reset the callback counter first, then run GC and yield. If another * thread is racing us here we will accumulate another callback request * which will be serviced at the next opportunity. */ - JS_LOCK_GC(rt); - td->interruptFlags = 0; -#ifdef JS_THREADSAFE - JS_ATOMIC_DECREMENT(&rt->interruptCounter); -#endif - JS_UNLOCK_GC(rt); + JS_ATOMIC_SET(&rt->interrupt, 0); - if (rt->gcIsNeeded) { - js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL); - - /* - * On trace we can exceed the GC quota, see comments in NewGCArena. So - * we check the quota and report OOM here when we are off trace. - */ - if (checkOutOfMemory(rt)) { -#ifdef JS_THREADSAFE - /* - * We have to wait until the background thread is done in order - * to get a correct answer. - */ - rt->gcHelperThread.waitBackgroundSweepEnd(rt); - if (checkOutOfMemory(rt)) { - js_ReportOutOfMemory(cx); - return false; - } -#else - js_ReportOutOfMemory(cx); - return false; -#endif - } - } + if (rt->gcIsNeeded) + js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); #ifdef JS_THREADSAFE /* @@ -1337,48 +906,16 @@ JSBool js_HandleExecutionInterrupt(JSContext *cx) { JSBool result = JS_TRUE; - if (JS_THREAD_DATA(cx)->interruptFlags) + if (cx->runtime->interrupt) result = js_InvokeOperationCallback(cx) && result; return result; } -namespace js { - -void -TriggerOperationCallback(JSContext *cx) -{ - /* - * We allow for cx to come from another thread. Thus we must deal with - * possible JS_ClearContextThread calls when accessing cx->thread. But we - * assume that the calling thread is in a request so JSThread cannot be - * GC-ed. - */ - ThreadData *td; -#ifdef JS_THREADSAFE - JSThread *thread = cx->thread(); - if (!thread) - return; - td = &thread->data; -#else - td = JS_THREAD_DATA(cx); -#endif - td->triggerOperationCallback(cx->runtime); -} - -void -TriggerAllOperationCallbacks(JSRuntime *rt) -{ - for (ThreadDataIter i(rt); !i.empty(); i.popFront()) - i.threadData()->triggerOperationCallback(rt); -} - -} /* namespace js */ - StackFrame * js_GetScriptedCaller(JSContext *cx, StackFrame *fp) { if (!fp) - fp = js_GetTopStackFrame(cx); + fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); while (fp && fp->isDummyFrame()) fp = fp->prev(); JS_ASSERT_IF(fp, fp->isScriptFrame()); @@ -1388,41 +925,7 @@ js_GetScriptedCaller(JSContext *cx, StackFrame *fp) jsbytecode* js_GetCurrentBytecodePC(JSContext* cx) { - jsbytecode *pc, *imacpc; - -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) { - pc = JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->pc; - imacpc = JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->imacpc; - } else -#endif - { - JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */ - pc = cx->running() ? cx->regs().pc : NULL; - if (!pc) - return NULL; - imacpc = cx->fp()->maybeImacropc(); - } - - /* - * If we are inside GetProperty_tn or similar, return a pointer to the - * current instruction in the script, not the CALL instruction in the - * imacro, for the benefit of callers doing bytecode inspection. - */ - return (*pc == JSOP_CALL && imacpc) ? imacpc : pc; -} - -bool -js_CurrentPCIsInImacro(JSContext *cx) -{ -#ifdef JS_TRACER - VOUCH_DOES_NOT_REQUIRE_STACK(); - if (JS_ON_TRACE(cx)) - return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->imacpc != NULL; - return cx->fp()->hasImacropc(); -#else - return false; -#endif + return cx->hasfp() ? cx->regs().pc : NULL; } void @@ -1438,15 +941,6 @@ DSTOffsetCache::purge() oldOffsetMilliseconds = 0; oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN; -#ifdef JS_METER_DST_OFFSET_CACHING - totalCalculations = 0; - hit = 0; - missIncreasing = missDecreasing = 0; - missIncreasingOffsetChangeExpand = missIncreasingOffsetChangeUpper = 0; - missDecreasingOffsetChangeExpand = missDecreasingOffsetChangeLower = 0; - missLargeIncrease = missLargeDecrease = 0; -#endif - sanityCheck(); } @@ -1462,23 +956,68 @@ DSTOffsetCache::DSTOffsetCache() } JSContext::JSContext(JSRuntime *rt) - : hasVersionOverride(false), - runtime(rt), + : ContextFriendFields(rt), + defaultVersion(JSVERSION_DEFAULT), + hasVersionOverride(false), + throwing(false), + exception(UndefinedValue()), + runOptions(0), + reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY), + localeCallbacks(NULL), + resolvingList(NULL), + generatingError(false), compartment(NULL), - stack(thisDuringConstruction()), - busyArrays() -{} - -JSContext::~JSContext() + stack(thisDuringConstruction()), /* depends on cx->thread_ */ + parseMapPool_(NULL), + globalObject(NULL), + argumentFormatMap(NULL), + lastMessage(NULL), + errorReporter(NULL), + operationCallback(NULL), + data(NULL), + data2(NULL), +#ifdef JS_THREADSAFE + outstandingRequests(0), +#endif + autoGCRooters(NULL), + debugHooks(&rt->globalDebugHooks), + securityCallbacks(NULL), + resolveFlags(0), + rngSeed(0), + iterValue(MagicValue(JS_NO_ITER_VALUE)), +#ifdef JS_METHODJIT + methodJitEnabled(false), +#endif + inferenceEnabled(false), +#ifdef MOZ_TRACE_JSCALLS + functionCallback(NULL), +#endif + enumerators(NULL), +#ifdef JS_THREADSAFE + gcBackgroundFree(NULL), +#endif + activeCompilations(0) +#ifdef DEBUG + , stackIterAssertionEnabled(true) +#endif { + PodZero(&link); #ifdef JS_THREADSAFE - JS_ASSERT(!thread_); + PodZero(&threadLinks); #endif +#ifdef JSGC_ROOT_ANALYSIS + PodArrayZero(thingGCRooters); +#ifdef DEBUG + checkGCRooters = NULL; +#endif +#endif +} +JSContext::~JSContext() +{ /* Free the stuff hanging off of cx. */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - JS_FinishArenaPool(&tempPool); - JS_FinishArenaPool(®ExpPool); + if (parseMapPool_) + Foreground::delete_(parseMapPool_); if (lastMessage) Foreground::free_(lastMessage); @@ -1498,7 +1037,7 @@ void JSContext::resetCompartment() { JSObject *scopeobj; - if (stack.running()) { + if (stack.hasfp()) { scopeobj = &fp()->scopeChain(); } else { scopeobj = globalObject; @@ -1515,9 +1054,11 @@ JSContext::resetCompartment() } compartment = scopeobj->compartment(); + inferenceEnabled = compartment->types.inferenceEnabled; if (isExceptionPending()) wrapPendingException(); + updateJITEnabled(); return; error: @@ -1546,7 +1087,8 @@ JSContext::wrapPendingException() JSGenerator * JSContext::generatorFor(StackFrame *fp) const { - JS_ASSERT(stack.contains(fp) && fp->isGeneratorFrame()); + JS_ASSERT(stack.containsSlow(fp)); + JS_ASSERT(fp->isGeneratorFrame()); JS_ASSERT(!fp->isFloatingGenerator()); JS_ASSERT(!genStack.empty()); @@ -1562,26 +1104,45 @@ JSContext::generatorFor(StackFrame *fp) const return NULL; } +bool +JSContext::runningWithTrustedPrincipals() const +{ + return !compartment || compartment->principals == runtime->trustedPrincipals(); +} + +void +JSRuntime::updateMallocCounter(JSContext *cx, size_t nbytes) +{ + /* We tolerate any thread races when updating gcMallocBytes. */ + ptrdiff_t oldCount = gcMallocBytes; + ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); + gcMallocBytes = newCount; + if (JS_UNLIKELY(newCount <= 0 && oldCount > 0)) + onTooMuchMalloc(); + else if (cx && cx->compartment) + cx->compartment->updateMallocCounter(nbytes); +} + JS_FRIEND_API(void) JSRuntime::onTooMuchMalloc() { -#ifdef JS_THREADSAFE - AutoLockGC lock(this); - - /* - * We can be called outside a request and can race against a GC that - * mutates the JSThread set during the sweeping phase. - */ - js_WaitForGC(this); -#endif - TriggerGC(this); + TriggerGC(this, gcreason::TOO_MUCH_MALLOC); } JS_FRIEND_API(void *) JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) { + /* + * Retry when we are done with the background sweeping and have stopped + * all the allocations and released the empty GC chunks. + */ + ShrinkGCBuffers(this); #ifdef JS_THREADSAFE - gcHelperThread.waitBackgroundSweepEnd(this); + { + AutoLockGC lock(this); + gcHelperThread.waitBackgroundSweepOrAllocEnd(); + } +#endif if (!p) p = OffTheBooks::malloc_(nbytes); else if (p == reinterpret_cast(1)) @@ -1590,37 +1151,35 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) p = OffTheBooks::realloc_(p, nbytes); if (p) return p; -#endif if (cx) js_ReportOutOfMemory(cx); return NULL; } -/* - * Release pool's arenas if the stackPool has existed for longer than the - * limit specified by gcEmptyArenaPoolLifespan. - */ -inline void -FreeOldArenas(JSRuntime *rt, JSArenaPool *pool) +void +JSRuntime::purge(JSContext *cx) { - JSArena *a = pool->current; - if (a == pool->first.next && a->avail == a->base + sizeof(int64)) { - int64 age = JS_Now() - *(int64 *) a->base; - if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000) - JS_FreeArenaPool(pool); - } + tempLifoAlloc.freeUnused(); + gsnCache.purge(); + + /* FIXME: bug 506341 */ + propertyCache.purge(cx); } void JSContext::purge() { - FreeOldArenas(runtime, ®ExpPool); + if (!activeCompilations) { + Foreground::delete_(parseMapPool_); + parseMapPool_ = NULL; + } } +#if defined(JS_METHODJIT) static bool ComputeIsJITBroken() { -#ifndef ANDROID +#if !defined(ANDROID) || defined(GONK) return false; #else // ANDROID if (getenv("JS_IGNORE_JIT_BROKENNESS")) { @@ -1687,40 +1246,48 @@ IsJITBrokenHere() } return isBroken; } +#endif void JSContext::updateJITEnabled() { -#ifdef JS_TRACER - traceJitEnabled = ((runOptions & JSOPTION_JIT) && - !IsJITBrokenHere() && - (debugHooks == &js_NullDebugHooks || - (debugHooks == &runtime->globalDebugHooks && - !runtime->debuggerInhibitsJIT()))); -#endif #ifdef JS_METHODJIT - methodJitEnabled = (runOptions & JSOPTION_METHODJIT) && - !IsJITBrokenHere() -# if defined JS_CPU_X86 || defined JS_CPU_X64 - && JSC::MacroAssemblerX86Common::getSSEState() >= - JSC::MacroAssemblerX86Common::HasSSE2 -# endif - ; -#ifdef JS_TRACER - profilingEnabled = (runOptions & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled; -#endif + // This allocator randomization is actually a compartment-wide option. + if (compartment && compartment->hasJaegerCompartment()) + compartment->jaegerCompartment()->execAlloc()->setRandomize(runtime->getJitHardening()); + methodJitEnabled = (runOptions & JSOPTION_METHODJIT) && !IsJITBrokenHere(); #endif } -namespace js { +size_t +JSContext::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const +{ + /* + * There are other JSContext members that could be measured; the following + * ones have been found by DMD to be worth measuring. More stuff may be + * added later. + */ + return mallocSizeOf(this) + busyArrays.sizeOfExcludingThis(mallocSizeOf); +} -JS_FORCES_STACK JS_FRIEND_API(void) -LeaveTrace(JSContext *cx) +namespace JS { + +#if defined JS_THREADSAFE && defined DEBUG + +AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx) + : cx(cx) { -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) - DeepBail(cx); -#endif + JS_ASSERT(cx->runtime->requestDepth || cx->runtime->gcRunning); + JS_ASSERT(cx->runtime->onOwnerThread()); + cx->runtime->checkRequestDepth++; } -} /* namespace js */ +AutoCheckRequestDepth::~AutoCheckRequestDepth() +{ + JS_ASSERT(cx->runtime->checkRequestDepth != 0); + cx->runtime->checkRequestDepth--; +} + +#endif + +} // namespace JS diff --git a/deps/mozjs/js/src/jscntxt.h b/deps/mozjs/js/src/jscntxt.h index b068f125144..8e6cd4fe7d2 100644 --- a/deps/mozjs/js/src/jscntxt.h +++ b/deps/mozjs/js/src/jscntxt.h @@ -38,35 +38,31 @@ * * ***** END LICENSE BLOCK ***** */ +/* JS execution context. */ + #ifndef jscntxt_h___ #define jscntxt_h___ -/* - * JS execution context. - */ + +#include "mozilla/Attributes.h" + #include +#include "jsapi.h" +#include "jsfriendapi.h" #include "jsprvtd.h" -#include "jsarena.h" -#include "jsclist.h" -#include "jslong.h" #include "jsatom.h" +#include "jsclist.h" #include "jsdhash.h" -#include "jsdtoa.h" -#include "jsfun.h" #include "jsgc.h" -#include "jsgcchunk.h" -#include "jshashtable.h" -#include "jsinterp.h" -#include "jsmath.h" -#include "jsobj.h" #include "jspropertycache.h" #include "jspropertytree.h" -#include "jsstaticcheck.h" #include "jsutil.h" -#include "jsarray.h" -#include "jsvector.h" #include "prmjtime.h" +#include "ds/LifoAlloc.h" +#include "gc/Statistics.h" +#include "js/HashTable.h" +#include "js/Vector.h" #include "vm/Stack.h" #ifdef _MSC_VER @@ -76,47 +72,30 @@ #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */ #endif -/* Forward declarations of nanojit types. */ -namespace nanojit { +JS_BEGIN_EXTERN_C +struct DtoaState; +JS_END_EXTERN_C -class Assembler; -class CodeAlloc; -class Fragment; -template struct DefaultHash; -template class HashMap; -template class Seq; +struct JSSharpObjectMap { + jsrefcount depth; + uint32_t sharpgen; + JSHashTable *table; -} /* namespace nanojit */ + JSSharpObjectMap() : depth(0), sharpgen(0), table(NULL) {} +}; namespace js { -/* Tracer constants. */ -static const size_t MONITOR_N_GLOBAL_STATES = 4; -static const size_t FRAGMENT_TABLE_SIZE = 512; -static const size_t MAX_GLOBAL_SLOTS = 4096; -static const size_t GLOBAL_SLOTS_BUFFER_SIZE = MAX_GLOBAL_SLOTS + 1; - -/* Forward declarations of tracer types. */ -class VMAllocator; -class FrameInfoCache; -struct FrameInfo; -struct VMSideExit; -struct TreeFragment; -struct TracerState; -template class Queue; -typedef Queue SlotList; -class TypeMap; -class LoopProfile; - -#if defined(JS_JIT_SPEW) || defined(DEBUG) -struct FragPI; -typedef nanojit::HashMap > FragStatsMap; -#endif - namespace mjit { class JaegerCompartment; } +class WeakMapBase; +class InterpreterFrames; + +class ScriptOpcodeCounts; +struct ScriptOpcodeCountsPair; + /* * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a * given pc in a script. We use the script->code pointer to tag the cache, @@ -131,24 +110,12 @@ struct GSNCache { jsbytecode *code; Map map; -#ifdef JS_GSNMETER - struct Stats { - uint32 hits; - uint32 misses; - uint32 fills; - uint32 purges; - - Stats() : hits(0), misses(0), fills(0), purges(0) { } - }; - - Stats stats; -#endif GSNCache() : code(NULL) { } void purge(); }; - + inline GSNCache * GetGSNCache(JSContext *cx); @@ -157,279 +124,192 @@ struct PendingProxyOperation { JSObject *object; }; -struct ThreadData { - /* - * If non-zero, we were been asked to call the operation callback as soon - * as possible. If the thread has an active request, this contributes - * towards rt->interruptCounter. - */ - volatile int32 interruptFlags; - -#ifdef JS_THREADSAFE - /* The request depth for this thread. */ - unsigned requestDepth; -#endif +typedef Vector ScriptOpcodeCountsVector; -#ifdef JS_TRACER +struct ConservativeGCData +{ /* - * During trace execution (or during trace recording or - * profiling), these fields point to the compartment doing the - * execution on this thread. At other times, they are NULL. If a - * thread tries to execute/record/profile one trace while another - * is still running, the initial one will abort. Therefore, we - * only need to track one at a time. + * The GC scans conservatively between ThreadData::nativeStackBase and + * nativeStackTop unless the latter is NULL. */ - JSCompartment *onTraceCompartment; - JSCompartment *recordingCompartment; - JSCompartment *profilingCompartment; + uintptr_t *nativeStackTop; - /* Maximum size of the tracer's code cache before we start flushing. */ - uint32 maxCodeCacheBytes; - - static const uint32 DEFAULT_JIT_CACHE_SIZE = 16 * 1024 * 1024; -#endif - - /* Keeper of the contiguous stack used by all contexts in this thread. */ - StackSpace stackSpace; - - /* - * Flag indicating that we are waiving any soft limits on the GC heap - * because we want allocations to be infallible (except when we hit OOM). - */ - bool waiveGCQuota; + union { + jmp_buf jmpbuf; + uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; + } registerSnapshot; /* - * The GSN cache is per thread since even multi-cx-per-thread embeddings - * do not interleave js_GetSrcNote calls. + * Cycle collector uses this to communicate that the native stack of the + * GC thread should be scanned only if the thread have more than the given + * threshold of requests. */ - GSNCache gsnCache; - - /* Property cache for faster call/get/set invocation. */ - PropertyCache propertyCache; - - /* State used by dtoa.c. */ - DtoaState *dtoaState; + unsigned requestThreshold; - /* Base address of the native stack for the current thread. */ - jsuword *nativeStackBase; - - /* List of currently pending operations on proxies. */ - PendingProxyOperation *pendingProxyOperation; - - ConservativeGCThreadData conservativeGC; - - ThreadData(); - ~ThreadData(); + ConservativeGCData() + : nativeStackTop(NULL), requestThreshold(0) + {} - bool init(); - - void mark(JSTracer *trc) { - stackSpace.mark(trc); + ~ConservativeGCData() { +#ifdef JS_THREADSAFE + /* + * The conservative GC scanner should be disabled when the thread leaves + * the last request. + */ + JS_ASSERT(!hasStackToScan()); +#endif } - void purge(JSContext *cx) { - gsnCache.purge(); + JS_NEVER_INLINE void recordStackTop(); - /* FIXME: bug 506341. */ - propertyCache.purge(cx); +#ifdef JS_THREADSAFE + void updateForRequestEnd(unsigned suspendCount) { + if (suspendCount) + recordStackTop(); + else + nativeStackTop = NULL; } +#endif - /* This must be called with the GC lock held. */ - void triggerOperationCallback(JSRuntime *rt); + bool hasStackToScan() const { + return !!nativeStackTop; + } }; } /* namespace js */ -#ifdef JS_THREADSAFE - -/* - * Structure uniquely representing a thread. It holds thread-private data - * that can be accessed without a global lock. - */ -struct JSThread { - typedef js::HashMap, - js::SystemAllocPolicy> Map; +struct JSRuntime : js::RuntimeFriendFields +{ + /* Default compartment. */ + JSCompartment *atomsCompartment; - /* Linked list of all contexts in use on this thread. */ - JSCList contextList; + /* List of compartments (protected by the GC lock). */ + js::CompartmentVector compartments; - /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - void *id; + /* See comment for JS_AbortIfWrongThread in jsapi.h. */ +#ifdef JS_THREADSAFE + public: + void *ownerThread() const { return ownerThread_; } + void clearOwnerThread(); + void setOwnerThread(); + JS_FRIEND_API(bool) onOwnerThread() const; + private: + void *ownerThread_; + public: +#else + public: + bool onOwnerThread() const { return true; } +#endif - /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */ - unsigned suspendCount; + /* Keeper of the contiguous stack used by all contexts in this thread. */ + js::StackSpace stackSpace; -# ifdef DEBUG - unsigned checkRequestDepth; -# endif + /* Temporary arena pool used while compiling and decompiling. */ + static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; + js::LifoAlloc tempLifoAlloc; - /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ - js::ThreadData data; + private: + /* + * Both of these allocators are used for regular expression code which is shared at the + * thread-data level. + */ + JSC::ExecutableAllocator *execAlloc_; + WTF::BumpPointerAllocator *bumpAlloc_; - JSThread(void *id) - : id(id), - suspendCount(0) -# ifdef DEBUG - , checkRequestDepth(0) -# endif - { - JS_INIT_CLIST(&contextList); - } + JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); + WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); - ~JSThread() { - /* The thread must have zero contexts. */ - JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList)); + public: + JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) { + return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx); } - - bool init() { - return data.init(); + WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) { + return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx); } -}; - -#define JS_THREAD_DATA(cx) (&(cx)->thread()->data) - -extern JSThread * -js_CurrentThread(JSRuntime *rt); - -/* - * The function takes the GC lock and does not release in successful return. - * On error (out of memory) the function releases the lock but delegates - * the error reporting to the caller. - */ -extern JSBool -js_InitContextThread(JSContext *cx); - -/* - * On entrance the GC lock must be held and it will be held on exit. - */ -extern void -js_ClearContextThread(JSContext *cx); -#endif /* JS_THREADSAFE */ - -#ifdef DEBUG -# define FUNCTION_KIND_METER_LIST(_) \ - _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \ - _(flat), _(badfunarg), \ - _(joinedsetmethod), _(joinedinitmethod), \ - _(joinedreplace), _(joinedsort), _(joinedmodulepat), \ - _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \ - _(unjoined), _(indynamicscope) -# define identity(x) x - -struct JSFunctionMeter { - int32 FUNCTION_KIND_METER_LIST(identity); -}; - -# undef identity - -# define JS_FUNCTION_METER(cx,x) JS_RUNTIME_METER((cx)->runtime, functionMeter.x) -#else -# define JS_FUNCTION_METER(cx,x) ((void)0) -#endif - -typedef enum JSDestroyContextMode { - JSDCM_NO_GC, - JSDCM_MAYBE_GC, - JSDCM_FORCE_GC, - JSDCM_NEW_FAILED -} JSDestroyContextMode; - -typedef enum JSRuntimeState { - JSRTS_DOWN, - JSRTS_LAUNCHING, - JSRTS_UP, - JSRTS_LANDING -} JSRuntimeState; + /* Base address of the native stack for the current thread. */ + uintptr_t nativeStackBase; -typedef struct JSPropertyTreeEntry { - JSDHashEntryHdr hdr; - js::Shape *child; -} JSPropertyTreeEntry; + /* The native stack size limit that runtime should not exceed. */ + size_t nativeStackQuota; -typedef void -(* JSActivityCallback)(void *arg, JSBool active); + /* + * Frames currently running in js::Interpret. See InterpreterFrames for + * details. + */ + js::InterpreterFrames *interpreterFrames; -namespace js { + /* Context create/destroy callback. */ + JSContextCallback cxCallback; -typedef js::Vector CompartmentVector; + /* Compartment create/destroy callback. */ + JSCompartmentCallback compartmentCallback; -} + js::ActivityCallback activityCallback; + void *activityCallbackArg; -struct JSRuntime { - /* Default compartment. */ - JSCompartment *atomsCompartment; #ifdef JS_THREADSAFE - bool atomsCompartmentIsLocked; -#endif - - /* List of compartments (protected by the GC lock). */ - js::CompartmentVector compartments; + /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */ + unsigned suspendCount; - /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ - JSRuntimeState state; + /* The request depth for this thread. */ + unsigned requestDepth; - /* Context create/destroy callback. */ - JSContextCallback cxCallback; +# ifdef DEBUG + unsigned checkRequestDepth; +# endif +#endif - /* Compartment create/destroy callback. */ - JSCompartmentCallback compartmentCallback; + /* Garbage collector state, used by jsgc.c. */ /* - * Sets a callback that is run whenever the runtime goes idle - the - * last active request ceases - and begins activity - when it was - * idle and a request begins. Note: The callback is called under the - * GC lock. + * Set of all GC chunks with at least one allocated thing. The + * conservative GC uses it to quickly check if a possible GC thing points + * into an allocated chunk. */ - void setActivityCallback(JSActivityCallback cb, void *arg) { - activityCallback = cb; - activityCallbackArg = arg; - } - - JSActivityCallback activityCallback; - void *activityCallbackArg; + js::GCChunkSet gcChunkSet; /* - * Shape regenerated whenever a prototype implicated by an "add property" - * property cache fill and induced trace guard has a readonly property or a - * setter defined on it. This number proxies for the shapes of all objects - * along the prototype chain of all objects in the runtime on which such an - * add-property result has been cached/traced. - * - * See bug 492355 for more details. - * - * This comes early in JSRuntime to minimize the immediate format used by - * trace-JITted code that reads it. + * Doubly-linked lists of chunks from user and system compartments. The GC + * allocates its arenas from the corresponding list and when all arenas + * in the list head are taken, then the chunk is removed from the list. + * During the GC when all arenas in a chunk become free, that chunk is + * removed from the list and scheduled for release. */ - uint32 protoHazardShape; - - /* Garbage collector state, used by jsgc.c. */ - js::GCChunkSet gcChunkSet; + js::gc::Chunk *gcSystemAvailableChunkListHead; + js::gc::Chunk *gcUserAvailableChunkListHead; + js::gc::ChunkPool gcChunkPool; js::RootedValueMap gcRootsHash; js::GCLocks gcLocksHash; jsrefcount gcKeepAtoms; - uint32 gcBytes; - uint32 gcTriggerBytes; - size_t gcLastBytes; + size_t gcBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; - size_t gcChunksWaitingToExpire; - uint32 gcEmptyArenaPoolLifespan; - uint32 gcNumber; - js::GCMarker *gcMarkingTracer; - uint32 gcTriggerFactor; - int64 gcJitReleaseTime; + + /* + * Number of the committed arenas in all GC chunks including empty chunks. + * The counter is volatile as it is read without the GC lock, see comments + * in MaybeGC. + */ + volatile uint32_t gcNumArenasFreeCommitted; + uint32_t gcNumber; + js::GCMarker *gcIncrementalTracer; + void *gcVerifyData; + bool gcChunkAllocationSinceLastGC; + int64_t gcNextFullGCTime; + int64_t gcJitReleaseTime; JSGCMode gcMode; - volatile bool gcIsNeeded; - JSObject *gcWeakMapList; + volatile uintptr_t gcBarrierFailed; + volatile uintptr_t gcIsNeeded; + js::WeakMapBase *gcWeakMapList; + js::gcstats::Statistics gcStats; + + /* The reason that an interrupt-triggered GC should be called. */ + js::gcreason::Reason gcTriggerReason; - /* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */ - void *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE / sizeof(void *)]; - void *gcMarkStackXMLs[js::XML_MARK_STACK_SIZE / sizeof(void *)]; - void *gcMarkStackLarges[js::LARGE_MARK_STACK_SIZE / sizeof(void *)]; + /* Pre-allocated space for the GC mark stack. */ + uintptr_t gcMarkStackArray[js::MARK_STACK_LENGTH]; /* * Compartment that triggered GC. If more than one Compatment need GC, @@ -440,6 +320,12 @@ struct JSRuntime { /* Compartment that is currently involved in per-compartment GC */ JSCompartment *gcCurrentCompartment; + /* + * If this is non-NULL, all marked objects must belong to this compartment. + * This is used to look for compartment bugs. + */ + JSCompartment *gcCheckCompartment; + /* * We can pack these flags as only the GC thread writes to them. Atomic * updates to packed bytes are not guaranteed, so stores issued by one @@ -449,13 +335,51 @@ struct JSRuntime { bool gcPoke; bool gcMarkAndSweep; bool gcRunning; - bool gcRegenShapes; + /* + * These options control the zealousness of the GC. The fundamental values + * are gcNextScheduled and gcDebugCompartmentGC. At every allocation, + * gcNextScheduled is decremented. When it reaches zero, we do either a + * full or a compartmental GC, based on gcDebugCompartmentGC. + * + * At this point, if gcZeal_ >= 2 then gcNextScheduled is reset to the + * value of gcZealFrequency. Otherwise, no additional GCs take place. + * + * You can control these values in several ways: + * - Pass the -Z flag to the shell (see the usage info for details) + * - Call gczeal() or schedulegc() from inside shell-executed JS code + * (see the help for details) + * + * Additionally, if gzZeal_ == 1 then we perform GCs in select places + * (during MaybeGC and whenever a GC poke happens). This option is mainly + * useful to embedders. + * + * We use gcZeal_ == 4 to enable write barrier verification. See the comment + * in jsgc.cpp for more information about this. + */ #ifdef JS_GC_ZEAL - jsrefcount gcZeal; + int gcZeal_; + int gcZealFrequency; + int gcNextScheduled; + bool gcDebugCompartmentGC; + + int gcZeal() { return gcZeal_; } + + bool needZealousGC() { + if (gcNextScheduled > 0 && --gcNextScheduled == 0) { + if (gcZeal() >= js::gc::ZealAllocThreshold && gcZeal() < js::gc::ZealVerifierThreshold) + gcNextScheduled = gcZealFrequency; + return true; + } + return false; + } +#else + int gcZeal() { return 0; } + bool needZealousGC() { return false; } #endif JSGCCallback gcCallback; + JSGCFinishedCallback gcFinishedCallback; private: /* @@ -465,50 +389,51 @@ struct JSRuntime { volatile ptrdiff_t gcMallocBytes; public: - js::GCChunkAllocator *gcChunkAllocator; - - void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) { - JS_ASSERT(allocator); - JS_ASSERT(state == JSRTS_DOWN); - gcChunkAllocator = allocator; - } - /* - * The trace operation and its data argument to trace embedding-specific - * GC roots. + * The trace operations to trace embedding-specific GC roots. One is for + * tracing through black roots and the other is for tracing through gray + * roots. The black/gray distinction is only relevant to the cycle + * collector. */ - JSTraceDataOp gcExtraRootsTraceOp; - void *gcExtraRootsData; + JSTraceDataOp gcBlackRootsTraceOp; + void *gcBlackRootsData; + JSTraceDataOp gcGrayRootsTraceOp; + void *gcGrayRootsData; + + /* Strong references on scripts held for PCCount profiling API. */ + js::ScriptOpcodeCountsVector *scriptPCCounters; /* Well-known numbers held for use by this runtime's contexts. */ js::Value NaNValue; js::Value negativeInfinityValue; js::Value positiveInfinityValue; - JSFlatString *emptyString; + JSAtom *emptyString; - /* List of active contexts sharing this runtime; protected by gcLock. */ + /* List of active contexts sharing this runtime. */ JSCList contextList; + bool hasContexts() const { + return !JS_CLIST_IS_EMPTY(&contextList); + } + /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; - /* - * Right now, we only support runtime-wide debugging. - */ - JSBool debugMode; + /* If true, new compartments are initially in debug mode. */ + bool debugMode; -#ifdef JS_TRACER - /* True if any debug hooks not supported by the JIT are enabled. */ - bool debuggerInhibitsJIT() const { - return (globalDebugHooks.interruptHook || - globalDebugHooks.callHook); - } -#endif + /* If true, new scripts must be created with PC counter information. */ + bool profilingScripts; + + /* Had an out-of-memory error which did not populate an exception. */ + JSBool hadOutOfMemory; - /* More debugging state, see jsdbgapi.c. */ - JSCList trapList; - JSCList watchPointList; + /* + * Linked list of all js::Debugger objects. This may be accessed by the GC + * thread, if any, or a thread that is in a request and holds gcLock. + */ + JSCList debuggerList; /* Client opaque pointers */ void *data; @@ -516,33 +441,11 @@ struct JSRuntime { #ifdef JS_THREADSAFE /* These combine to interlock the GC and new requests. */ PRLock *gcLock; - PRCondVar *gcDone; - PRCondVar *requestDone; - uint32 requestCount; - JSThread *gcThread; js::GCHelperThread gcHelperThread; - - /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ - PRLock *rtLock; -#ifdef DEBUG - void * rtLockOwner; -#endif - - /* Used to synchronize down/up state change; protected by gcLock. */ - PRCondVar *stateChange; - - /* - * Lock serializing trapList and watchPointList accesses, and count of all - * mutations to trapList and watchPointList made by debugger threads. To - * keep the code simple, we define debuggerMutations for the thread-unsafe - * case too. - */ - PRLock *debuggerLock; - - JSThread::Map threads; #endif /* JS_THREADSAFE */ - uint32 debuggerMutations; + + uint32_t debuggerMutations; /* * Security callbacks set on the runtime are used by each context unless @@ -553,18 +456,15 @@ struct JSRuntime { /* Structured data callbacks are runtime-wide. */ const JSStructuredCloneCallbacks *structuredCloneCallbacks; + /* Call this to accumulate telemetry data. */ + JSAccumulateTelemetryDataCallback telemetryCallback; + /* * The propertyRemovals counter is incremented for every JSObject::clear, * and for each JSObject::remove method call that frees a slot in the given * object. See js_NativeGet and js_NativeSet in jsobj.cpp. */ - int32 propertyRemovals; - - /* Script filename table. */ - struct JSHashTable *scriptFilenameTable; -#ifdef JS_THREADSAFE - PRLock *scriptFilenameTableLock; -#endif + int32_t propertyRemovals; /* Number localization, used by jsnum.c */ const char *thousandsSeparator; @@ -581,164 +481,88 @@ struct JSRuntime { JSObject *anynameObject; JSObject *functionNamespaceObject; -#ifdef JS_THREADSAFE - /* Number of threads with active requests and unhandled interrupts. */ - volatile int32 interruptCounter; -#else - js::ThreadData threadData; - -#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData) -#endif - /* - * Object shape (property cache structural type) identifier generator. - * - * Type 0 stands for the empty scope, and must not be regenerated due to - * uint32 wrap-around. Since js_GenerateShape (in jsinterp.cpp) uses - * atomic pre-increment, the initial value for the first typed non-empty - * scope will be 1. - * - * If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the - * cache is disabled, to avoid aliasing two different types. It stays - * disabled until a triggered GC at some later moment compresses live - * types, minimizing rt->shapeGen in the process. + * Flag indicating that we are waiving any soft limits on the GC heap + * because we want allocations to be infallible (except when we hit OOM). */ - volatile uint32 shapeGen; - - /* Literal table maintained by jsatom.c functions. */ - JSAtomState atomState; + bool waiveGCQuota; /* - * Various metering fields are defined at the end of JSRuntime. In this - * way there is no need to recompile all the code that refers to other - * fields of JSRuntime after enabling the corresponding metering macro. + * The GSN cache is per thread since even multi-cx-per-thread embeddings + * do not interleave js_GetSrcNote calls. */ -#ifdef JS_DUMP_ENUM_CACHE_STATS - int32 nativeEnumProbes; - int32 nativeEnumMisses; -# define ENUM_CACHE_METER(name) JS_ATOMIC_INCREMENT(&cx->runtime->name) -#else -# define ENUM_CACHE_METER(name) ((void) 0) -#endif + js::GSNCache gsnCache; -#ifdef DEBUG - /* Function invocation metering. */ - jsrefcount inlineCalls; - jsrefcount nativeCalls; - jsrefcount nonInlineCalls; - jsrefcount constructs; + /* Property cache for faster call/get/set invocation. */ + js::PropertyCache propertyCache; - /* - * NB: emptyShapes (in JSCompartment) is init'ed iff at least one - * of these envars is set: - * - * JS_PROPTREE_STATFILE statistics on the property tree forest - * JS_PROPTREE_DUMPFILE all paths in the property tree forest - */ - const char *propTreeStatFilename; - const char *propTreeDumpFilename; - - bool meterEmptyShapes() const { return propTreeStatFilename || propTreeDumpFilename; } - - /* String instrumentation. */ - jsrefcount liveStrings; - jsrefcount totalStrings; - jsrefcount liveDependentStrings; - jsrefcount totalDependentStrings; - jsrefcount badUndependStrings; - double lengthSum; - double lengthSquaredSum; - double strdepLengthSum; - double strdepLengthSquaredSum; - - /* Script instrumentation. */ - jsrefcount liveScripts; - jsrefcount totalScripts; - jsrefcount liveEmptyScripts; - jsrefcount totalEmptyScripts; - jsrefcount highWaterLiveScripts; -#endif /* DEBUG */ - -#ifdef JS_SCOPE_DEPTH_METER - /* - * Stats on runtime prototype chain lookups and scope chain depths, i.e., - * counts of objects traversed on a chain until the wanted id is found. - */ - JSBasicStats protoLookupDepthStats; - JSBasicStats scopeSearchDepthStats; + /* State used by jsdtoa.cpp. */ + DtoaState *dtoaState; - /* - * Stats on compile-time host environment and lexical scope chain lengths - * (maximum depths). - */ - JSBasicStats hostenvScopeDepthStats; - JSBasicStats lexicalScopeDepthStats; -#endif + /* List of currently pending operations on proxies. */ + js::PendingProxyOperation *pendingProxyOperation; -#ifdef JS_GCMETER - js::gc::JSGCStats gcStats; - js::gc::JSGCArenaStats globalArenaStats[js::gc::FINALIZE_LIMIT]; -#endif + js::ConservativeGCData conservativeGC; -#ifdef DEBUG - /* - * If functionMeterFilename, set from an envariable in JSRuntime's ctor, is - * null, the remaining members in this ifdef'ed group are not initialized. - */ - const char *functionMeterFilename; - JSFunctionMeter functionMeter; - char lastScriptFilename[1024]; + private: + JSPrincipals *trustedPrincipals_; + public: + void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; } + JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; } - typedef js::HashMap, - js::SystemAllocPolicy> FunctionCountMap; + /* Literal table maintained by jsatom.c functions. */ + JSAtomState atomState; - FunctionCountMap methodReadBarrierCountMap; - FunctionCountMap unjoinedFunctionCountMap; -#endif + /* Tables of strings that are pre-allocated in the atomsCompartment. */ + js::StaticStrings staticStrings; JSWrapObjectCallback wrapObjectCallback; JSPreWrapCallback preWrapObjectCallback; + js::PreserveWrapperCallback preserveWrapperCallback; -#ifdef JS_METHODJIT - /* This measures the size of JITScripts, native maps and IC structs. */ - size_t mjitDataSize; +#ifdef DEBUG + size_t noGCOrAllocationCheck; #endif - size_t stringMemoryUsed; + + /* + * To ensure that cx->malloc does not cause a GC, we set this flag during + * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while + * reporting the OOM, we ignore it. + */ + int32_t inOOMReport; + + bool jitHardening; JSRuntime(); ~JSRuntime(); - bool init(uint32 maxbytes); + bool init(uint32_t maxbytes); - void setGCTriggerFactor(uint32 factor); - void setGCLastBytes(size_t lastBytes); - void reduceGCTriggerBytes(uint32 amount); + JSRuntime *thisFromCtor() { return this; } /* * Call the system malloc while checking for GC memory pressure and - * reporting OOM error when cx is not null. + * reporting OOM error when cx is not null. We will not GC from here. */ void* malloc_(size_t bytes, JSContext *cx = NULL) { - updateMallocCounter(bytes); + updateMallocCounter(cx, bytes); void *p = ::js_malloc(bytes); return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx); } /* * Call the system calloc while checking for GC memory pressure and - * reporting OOM error when cx is not null. + * reporting OOM error when cx is not null. We will not GC from here. */ void* calloc_(size_t bytes, JSContext *cx = NULL) { - updateMallocCounter(bytes); + updateMallocCounter(cx, bytes); void *p = ::js_calloc(bytes); return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast(1), bytes, cx); } void* realloc_(void* p, size_t oldBytes, size_t newBytes, JSContext *cx = NULL) { JS_ASSERT(oldBytes < newBytes); - updateMallocCounter(newBytes - oldBytes); + updateMallocCounter(cx, newBytes - oldBytes); void *p2 = ::js_realloc(p, newBytes); return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx); } @@ -749,7 +573,7 @@ struct JSRuntime { * previously allocated memory. */ if (!p) - updateMallocCounter(bytes); + updateMallocCounter(cx, bytes); void *p2 = ::js_realloc(p, bytes); return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx); } @@ -783,13 +607,7 @@ struct JSRuntime { * The function must be called outside the GC lock and in case of OOM error * the caller must ensure that no deadlock possible during OOM reporting. */ - void updateMallocCounter(size_t nbytes) { - /* We tolerate any thread races when updating gcMallocBytes. */ - ptrdiff_t newCount = gcMallocBytes - ptrdiff_t(nbytes); - gcMallocBytes = newCount; - if (JS_UNLIKELY(newCount <= 0)) - onTooMuchMalloc(); - } + void updateMallocCounter(JSContext *cx, size_t nbytes); /* * The function must be called outside the GC lock. @@ -805,21 +623,25 @@ struct JSRuntime { * The function must be called outside the GC lock. */ JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); -}; -/* Common macros to access thread-local caches in JSThread or JSRuntime. */ -#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) + JS_FRIEND_API(void) triggerOperationCallback(); -#ifdef DEBUG -# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) -# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) -#else -# define JS_RUNTIME_METER(rt, which) /* nothing */ -# define JS_RUNTIME_UNMETER(rt, which) /* nothing */ -#endif + void setJitHardening(bool enabled); + bool getJitHardening() const { + return jitHardening; + } + + void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, + size_t *regexpCode, size_t *stackCommitted); + + void purge(JSContext *cx); +}; -#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); -#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); +/* Common macros to access thread-local caches in JSRuntime. */ +#define JS_PROPERTY_CACHE(cx) (cx->runtime->propertyCache) + +#define JS_KEEP_ATOMS(rt) (rt)->gcKeepAtoms++; +#define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--; #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* @@ -839,25 +661,21 @@ extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */ namespace js { -class AutoGCRooter; +template class Root; +class CheckRoot; + struct AutoResolving; static inline bool -OptionsHasXML(uint32 options) +OptionsHasXML(uint32_t options) { return !!(options & JSOPTION_XML); } static inline bool -OptionsHasAnonFunFix(uint32 options) -{ - return !!(options & JSOPTION_ANONFUNFIX); -} - -static inline bool -OptionsSameVersionFlags(uint32 self, uint32 other) +OptionsSameVersionFlags(uint32_t self, uint32_t other) { - static const uint32 mask = JSOPTION_XML | JSOPTION_ANONFUNFIX; + static const uint32_t mask = JSOPTION_XML; return !((self & mask) ^ (other & mask)); } @@ -872,14 +690,13 @@ OptionsSameVersionFlags(uint32 self, uint32 other) namespace VersionFlags { static const uintN MASK = 0x0FFF; /* see JSVersion in jspubtd.h */ static const uintN HAS_XML = 0x1000; /* flag induced by XML option */ -static const uintN ANONFUNFIX = 0x2000; /* see jsapi.h comment on JSOPTION_ANONFUNFIX */ static const uintN FULL_MASK = 0x3FFF; } static inline JSVersion VersionNumber(JSVersion version) { - return JSVersion(uint32(version) & VersionFlags::MASK); + return JSVersion(uint32_t(version) & VersionFlags::MASK); } static inline bool @@ -895,34 +712,10 @@ VersionShouldParseXML(JSVersion version) return VersionHasXML(version) || VersionNumber(version) >= JSVERSION_1_6; } -static inline bool -VersionHasAnonFunFix(JSVersion version) -{ - return !!(version & VersionFlags::ANONFUNFIX); -} - -static inline void -VersionSetXML(JSVersion *version, bool enable) -{ - if (enable) - *version = JSVersion(uint32(*version) | VersionFlags::HAS_XML); - else - *version = JSVersion(uint32(*version) & ~VersionFlags::HAS_XML); -} - -static inline void -VersionSetAnonFunFix(JSVersion *version, bool enable) -{ - if (enable) - *version = JSVersion(uint32(*version) | VersionFlags::ANONFUNFIX); - else - *version = JSVersion(uint32(*version) & ~VersionFlags::ANONFUNFIX); -} - static inline JSVersion VersionExtractFlags(JSVersion version) { - return JSVersion(uint32(version) & ~VersionFlags::MASK); + return JSVersion(uint32_t(version) & ~VersionFlags::MASK); } static inline void @@ -940,8 +733,7 @@ VersionHasFlags(JSVersion version) static inline uintN VersionFlagsToOptions(JSVersion version) { - uintN copts = (VersionHasXML(version) ? JSOPTION_XML : 0) | - (VersionHasAnonFunFix(version) ? JSOPTION_ANONFUNFIX : 0); + uintN copts = VersionHasXML(version) ? JSOPTION_XML : 0; JS_ASSERT((copts & JSCOMPILEOPTION_MASK) == copts); return copts; } @@ -949,9 +741,7 @@ VersionFlagsToOptions(JSVersion version) static inline JSVersion OptionFlagsToVersion(uintN options, JSVersion version) { - VersionSetXML(&version, OptionsHasXML(options)); - VersionSetAnonFunFix(&version, OptionsHasAnonFunFix(options)); - return version; + return VersionSetXML(version, OptionsHasXML(options)); } static inline bool @@ -966,7 +756,7 @@ typedef HashSetcompartment based on the current scope chain. */ void resetCompartment(); @@ -1028,12 +813,11 @@ struct JSContext /* Wrap cx->exception for the current compartment. */ void wrapPendingException(); - /* Temporary arena pool used while compiling and decompiling. */ - JSArenaPool tempPool; - - /* Temporary arena pool used while evaluate regular expressions. */ - JSArenaPool regExpPool; + private: + /* Lazily initialized pool of maps used during parse/emit. */ + js::ParseMapPool *parseMapPool_; + public: /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; @@ -1046,10 +830,6 @@ struct JSContext /* Last message string and log file for debugging. */ char *lastMessage; -#ifdef DEBUG - void *logfp; - jsbytecode *logPrevPc; -#endif /* Per-context optional error reporter. */ JSErrorReporter errorReporter; @@ -1064,20 +844,21 @@ struct JSContext inline js::RegExpStatics *regExpStatics(); public: + js::ParseMapPool &parseMapPool() { + JS_ASSERT(parseMapPool_); + return *parseMapPool_; + } + + inline bool ensureParseMapPool(); + /* * The default script compilation version can be set iff there is no code running. * This typically occurs via the JSAPI right after a context is constructed. */ - bool canSetDefaultVersion() const { - return !stack.running() && !hasVersionOverride; - } + inline bool canSetDefaultVersion() const; /* Force a version for future script compilation. */ - void overrideVersion(JSVersion newVersion) { - JS_ASSERT(!canSetDefaultVersion()); - versionOverride = newVersion; - hasVersionOverride = true; - } + inline void overrideVersion(JSVersion newVersion); /* Set the default script compilation version. */ void setDefaultVersion(JSVersion version) { @@ -1097,49 +878,29 @@ struct JSContext * Set the default version if possible; otherwise, force the version. * Return whether an override occurred. */ - bool maybeOverrideVersion(JSVersion newVersion) { - if (canSetDefaultVersion()) { - setDefaultVersion(newVersion); - return false; - } - overrideVersion(newVersion); - return true; - } + inline bool maybeOverrideVersion(JSVersion newVersion); /* * If there is no code on the stack, turn the override version into the * default version. */ void maybeMigrateVersionOverride() { - if (JS_LIKELY(!isVersionOverridden() && stack.empty())) - return; - defaultVersion = versionOverride; - clearVersionOverride(); + JS_ASSERT(stack.empty()); + if (JS_UNLIKELY(isVersionOverridden())) { + defaultVersion = versionOverride; + clearVersionOverride(); + } } /* * Return: * - The override version, if there is an override version. * - The newest scripted frame's version, if there is such a frame. - * - The default verion. + * - The default version. * * Note: if this ever shows up in a profile, just add caching! */ - JSVersion findVersion() const { - if (hasVersionOverride) - return versionOverride; - - if (stack.running()) { - /* There may be a scripted function somewhere on the stack! */ - js::StackFrame *f = fp(); - while (f && !f->isScriptFrame()) - f = f->prev(); - if (f) - return f->script()->getVersion(); - } - - return defaultVersion; - } + inline JSVersion findVersion() const; void setRunOptions(uintN ropts) { JS_ASSERT((ropts & JSRUNOPTION_MASK) == ropts); @@ -1147,18 +908,11 @@ struct JSContext } /* Note: may override the version. */ - void setCompileOptions(uintN newcopts) { - JS_ASSERT((newcopts & JSCOMPILEOPTION_MASK) == newcopts); - if (JS_LIKELY(getCompileOptions() == newcopts)) - return; - JSVersion version = findVersion(); - JSVersion newVersion = js::OptionFlagsToVersion(newcopts, version); - maybeOverrideVersion(newVersion); - } + inline void setCompileOptions(uintN newcopts); uintN getRunOptions() const { return runOptions; } - uintN getCompileOptions() const { return js::VersionFlagsToOptions(findVersion()); } - uintN allOptions() const { return getRunOptions() | getCompileOptions(); } + inline uintN getCompileOptions() const; + inline uintN allOptions() const; bool hasRunOption(uintN ropt) const { JS_ASSERT((ropt & JSRUNOPTION_MASK) == ropt); @@ -1169,27 +923,41 @@ struct JSContext bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); } bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); } -#ifdef JS_THREADSAFE - private: - JSThread *thread_; - public: - JSThread *thread() const { return thread_; } - - void setThread(JSThread *thread); - static const size_t threadOffset() { return offsetof(JSContext, thread_); } + js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; } + inline js::LifoAlloc &typeLifoAlloc(); +#ifdef JS_THREADSAFE unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ JSCList threadLinks; /* JSThread contextList linkage */ - -#define CX_FROM_THREAD_LINKS(tl) \ - ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif /* Stack of thread-stack-allocated GC roots. */ js::AutoGCRooter *autoGCRooters; +#ifdef JSGC_ROOT_ANALYSIS + + /* + * Stack allocated GC roots for stack GC heap pointers, which may be + * overwritten if moved during a GC. + */ + js::Root *thingGCRooters[js::THING_ROOT_COUNT]; + +#ifdef DEBUG + /* + * Stack allocated list of stack locations which hold non-relocatable + * GC heap pointers (where the target is rooted somewhere else) or integer + * values which may be confused for GC heap pointers. These are used to + * suppress false positives which occur when a rooting analysis treats the + * location as holding a relocatable pointer, but have no other effect on + * GC behavior. + */ + js::CheckRoot *checkGCRooters; +#endif + +#endif /* JSGC_ROOT_ANALYSIS */ + /* Debug hooks associated with the current context. */ const JSDebugHooks *debugHooks; @@ -1200,32 +968,21 @@ struct JSContext uintN resolveFlags; /* Random number generator state, used by jsmath.cpp. */ - int64 rngSeed; + int64_t rngSeed; - /* Location to stash the iteration value between JSOP_MOREITER and JSOP_FOR*. */ + /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ js::Value iterValue; -#ifdef JS_TRACER - /* - * True if traces may be executed. Invariant: The value of traceJitenabled - * is always equal to the expression in updateJITEnabled below. - * - * This flag and the fields accessed by updateJITEnabled are written only - * in runtime->gcLock, to avoid race conditions that would leave the wrong - * value in traceJitEnabled. (But the interpreter reads this without - * locking. That can race against another thread setting debug hooks, but - * we always read cx->debugHooks without locking anyway.) - */ - bool traceJitEnabled; -#endif - #ifdef JS_METHODJIT bool methodJitEnabled; - bool profilingEnabled; inline js::mjit::JaegerCompartment *jaegerCompartment(); #endif + bool inferenceEnabled; + + bool typeInferenceEnabled() { return inferenceEnabled; } + /* Caller must be holding runtime->gcLock. */ void updateJITEnabled(); @@ -1317,14 +1074,8 @@ struct JSContext void purge(); -#ifdef DEBUG - void assertValidStackDepth(uintN depth) { - JS_ASSERT(0 <= regs().sp - fp()->base()); - JS_ASSERT(depth <= uintptr_t(regs().sp - fp()->base())); - } -#else - void assertValidStackDepth(uintN /*depth*/) {} -#endif + /* For DEBUG. */ + inline void assertValidStackDepth(uintN depth); bool isExceptionPending() { return throwing; @@ -1342,6 +1093,34 @@ struct JSContext this->exception.setUndefined(); } + /* + * Count of currently active compilations. + * When there are compilations active for the context, the GC must not + * purge the ParseMapPool. + */ + uintN activeCompilations; + +#ifdef DEBUG + /* + * Controls whether a quadratic-complexity assertion is performed during + * stack iteration, defaults to true. + */ + bool stackIterAssertionEnabled; +#endif + + /* + * See JS_SetTrustedPrincipals in jsapi.h. + * Note: !cx->compartment is treated as trusted. + */ + bool runningWithTrustedPrincipals() const; + + JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; + + static inline JSContext *fromLinkField(JSCList *link) { + JS_ASSERT(link); + return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, link)); + } + private: /* * The allocation code calls the function to indicate either OOM failure @@ -1354,48 +1133,6 @@ struct JSContext namespace js { -#ifdef JS_THREADSAFE -# define JS_THREAD_ID(cx) ((cx)->thread() ? (cx)->thread()->id : 0) -#endif - -#if defined JS_THREADSAFE && defined DEBUG - -class AutoCheckRequestDepth { - JSContext *cx; - public: - AutoCheckRequestDepth(JSContext *cx) : cx(cx) { cx->thread()->checkRequestDepth++; } - - ~AutoCheckRequestDepth() { - JS_ASSERT(cx->thread()->checkRequestDepth != 0); - cx->thread()->checkRequestDepth--; - } -}; - -# define CHECK_REQUEST(cx) \ - JS_ASSERT((cx)->thread()); \ - JS_ASSERT((cx)->thread()->data.requestDepth || (cx)->thread() == (cx)->runtime->gcThread); \ - AutoCheckRequestDepth _autoCheckRequestDepth(cx); - -#else -# define CHECK_REQUEST(cx) ((void) 0) -# define CHECK_REQUEST_THREAD(cx) ((void) 0) -#endif - -static inline uintN -FramePCOffset(JSContext *cx, js::StackFrame* fp) -{ - jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); - return uintN(pc - fp->script()->code); -} - -static inline JSAtom ** -FrameAtomBase(JSContext *cx, js::StackFrame *fp) -{ - return fp->hasImacropc() - ? COMMON_ATOMS_START(&cx->runtime->atomState) - : fp->script()->atomMap.vector; -} - struct AutoResolving { public: enum Kind { @@ -1432,768 +1169,155 @@ struct AutoResolving { JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class AutoGCRooter { - public: - AutoGCRooter(JSContext *cx, ptrdiff_t tag) - : down(cx->autoGCRooters), tag(tag), context(cx) - { - JS_ASSERT(this != cx->autoGCRooters); - CHECK_REQUEST(cx); - cx->autoGCRooters = this; - } - - ~AutoGCRooter() { - JS_ASSERT(this == context->autoGCRooters); - CHECK_REQUEST(context); - context->autoGCRooters = down; - } - - /* Implemented in jsgc.cpp. */ - inline void trace(JSTracer *trc); - -#ifdef __GNUC__ -# pragma GCC visibility push(default) -#endif - friend JS_FRIEND_API(void) MarkContext(JSTracer *trc, JSContext *acx); - friend void MarkRuntime(JSTracer *trc); -#ifdef __GNUC__ -# pragma GCC visibility pop -#endif - - protected: - AutoGCRooter * const down; - - /* - * Discriminates actual subclass of this being used. If non-negative, the - * subclass roots an array of values of the length stored in this field. - * If negative, meaning is indicated by the corresponding value in the enum - * below. Any other negative value indicates some deeper problem such as - * memory corruption. - */ - ptrdiff_t tag; - - JSContext * const context; - - enum { - JSVAL = -1, /* js::AutoValueRooter */ - SHAPE = -2, /* js::AutoShapeRooter */ - PARSER = -3, /* js::Parser */ - SCRIPT = -4, /* js::AutoScriptRooter */ - ENUMERATOR = -5, /* js::AutoEnumStateRooter */ - IDARRAY = -6, /* js::AutoIdArray */ - DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */ - NAMESPACES = -8, /* js::AutoNamespaceArray */ - XML = -9, /* js::AutoXMLRooter */ - OBJECT = -10, /* js::AutoObjectRooter */ - ID = -11, /* js::AutoIdRooter */ - VALVECTOR = -12, /* js::AutoValueVector */ - DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ - STRING = -14, /* js::AutoStringRooter */ - IDVECTOR = -15, /* js::AutoIdVector */ - BINDINGS = -16, /* js::Bindings */ - SHAPEVECTOR = -17 /* js::AutoShapeVector */ - }; - - private: - /* No copy or assignment semantics. */ - AutoGCRooter(AutoGCRooter &ida); - void operator=(AutoGCRooter &ida); -}; - -/* FIXME(bug 332648): Move this into a public header. */ -class AutoValueRooter : private AutoGCRooter -{ +#ifdef JS_HAS_XML_SUPPORT +class AutoXMLRooter : private AutoGCRooter { public: - explicit AutoValueRooter(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, JSVAL), val(js::NullValue()) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - AutoValueRooter(JSContext *cx, const Value &v - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, JSVAL), val(v) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - AutoValueRooter(JSContext *cx, jsval v - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, JSVAL), val(js::Valueify(v)) + AutoXMLRooter(JSContext *cx, JSXML *xml + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, XML), xml(xml) { JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - /* - * If you are looking for Object* overloads, use AutoObjectRooter instead; - * rooting Object*s as a js::Value requires discerning whether or not it is - * a function object. Also, AutoObjectRooter is smaller. - */ - - void set(Value v) { - JS_ASSERT(tag == JSVAL); - val = v; - } - - void set(jsval v) { - JS_ASSERT(tag == JSVAL); - val = js::Valueify(v); - } - - const Value &value() const { - JS_ASSERT(tag == JSVAL); - return val; - } - - Value *addr() { - JS_ASSERT(tag == JSVAL); - return &val; - } - - const jsval &jsval_value() const { - JS_ASSERT(tag == JSVAL); - return Jsvalify(val); - } - - jsval *jsval_addr() { - JS_ASSERT(tag == JSVAL); - return Jsvalify(&val); + JS_ASSERT(xml); } friend void AutoGCRooter::trace(JSTracer *trc); - friend void MarkRuntime(JSTracer *trc); private: - Value val; + JSXML * const xml; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; +#endif /* JS_HAS_XML_SUPPORT */ -class AutoObjectRooter : private AutoGCRooter { - public: - AutoObjectRooter(JSContext *cx, JSObject *obj = NULL - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, OBJECT), obj(obj) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - void setObject(JSObject *obj) { - this->obj = obj; - } - - JSObject * object() const { - return obj; - } - - JSObject ** addr() { - return &obj; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - friend void MarkRuntime(JSTracer *trc); +#ifdef JS_THREADSAFE +# define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock) +# define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock) +#else +# define JS_LOCK_GC(rt) +# define JS_UNLOCK_GC(rt) +#endif +class AutoUnlockGC { private: - JSObject *obj; + JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; -class AutoStringRooter : private AutoGCRooter { public: - AutoStringRooter(JSContext *cx, JSString *str = NULL - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, STRING), str(str) + explicit AutoUnlockGC(JSRuntime *rt + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_UNLOCK_GC(rt); } + ~AutoUnlockGC() { JS_LOCK_GC(rt); } +}; - void setString(JSString *str) { - this->str = str; - } - - JSString * string() const { - return str; - } - - JSString ** addr() { - return &str; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - - private: - JSString *str; +class AutoKeepAtoms { + JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; -class AutoArrayRooter : private AutoGCRooter { public: - AutoArrayRooter(JSContext *cx, size_t len, Value *vec - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, len), array(vec) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ASSERT(tag >= 0); - } - - AutoArrayRooter(JSContext *cx, size_t len, jsval *vec - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, len), array(Valueify(vec)) + explicit AutoKeepAtoms(JSRuntime *rt + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ASSERT(tag >= 0); - } - - void changeLength(size_t newLength) { - tag = ptrdiff_t(newLength); - JS_ASSERT(tag >= 0); - } - - void changeArray(Value *newArray, size_t newLength) { - changeLength(newLength); - array = newArray; + JS_KEEP_ATOMS(rt); } + ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } +}; - Value *array; - - friend void AutoGCRooter::trace(JSTracer *trc); - - private: +class AutoReleasePtr { + JSContext *cx; + void *ptr; JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; -class AutoShapeRooter : private AutoGCRooter { + AutoReleasePtr(const AutoReleasePtr &other) MOZ_DELETE; + AutoReleasePtr operator=(const AutoReleasePtr &other) MOZ_DELETE; + public: - AutoShapeRooter(JSContext *cx, const js::Shape *shape - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, SHAPE), shape(shape) + explicit AutoReleasePtr(JSContext *cx, void *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), ptr(ptr) { JS_GUARD_OBJECT_NOTIFIER_INIT; } + ~AutoReleasePtr() { cx->free_(ptr); } +}; - friend void AutoGCRooter::trace(JSTracer *trc); - friend void MarkRuntime(JSTracer *trc); - - private: - const js::Shape * const shape; +/* + * FIXME: bug 602774: cleaner API for AutoReleaseNullablePtr + */ +class AutoReleaseNullablePtr { + JSContext *cx; + void *ptr; JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; -class AutoScriptRooter : private AutoGCRooter { + AutoReleaseNullablePtr(const AutoReleaseNullablePtr &other) MOZ_DELETE; + AutoReleaseNullablePtr operator=(const AutoReleaseNullablePtr &other) MOZ_DELETE; + public: - AutoScriptRooter(JSContext *cx, JSScript *script - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, SCRIPT), script(script) + explicit AutoReleaseNullablePtr(JSContext *cx, void *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), ptr(ptr) { JS_GUARD_OBJECT_NOTIFIER_INIT; } - - void setScript(JSScript *script) { - this->script = script; + void reset(void *ptr2) { + if (ptr) + cx->free_(ptr); + ptr = ptr2; } - - friend void AutoGCRooter::trace(JSTracer *trc); - - private: - JSScript *script; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER + ~AutoReleaseNullablePtr() { if (ptr) cx->free_(ptr); } }; -class AutoIdRooter : private AutoGCRooter +} /* namespace js */ + +class JSAutoResolveFlags { public: - explicit AutoIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0) - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, ID), id_(id) + JSAutoResolveFlags(JSContext *cx, uintN flags + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx), mSaved(cx->resolveFlags) { JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->resolveFlags = flags; } - jsid id() { - return id_; - } - - jsid * addr() { - return &id_; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - friend void MarkRuntime(JSTracer *trc); + ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } private: - jsid id_; + JSContext *mContext; + uintN mSaved; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class AutoIdArray : private AutoGCRooter { - public: - AutoIdArray(JSContext *cx, JSIdArray *ida JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, IDARRAY), idArray(ida) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - ~AutoIdArray() { - if (idArray) - JS_DestroyIdArray(context, idArray); - } - bool operator!() { - return idArray == NULL; - } - jsid operator[](size_t i) const { - JS_ASSERT(idArray); - JS_ASSERT(i < size_t(idArray->length)); - return idArray->vector[i]; - } - size_t length() const { - return idArray->length; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - - JSIdArray *steal() { - JSIdArray *copy = idArray; - idArray = NULL; - return copy; - } - - protected: - inline void trace(JSTracer *trc); - - private: - JSIdArray * idArray; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - /* No copy or assignment semantics. */ - AutoIdArray(AutoIdArray &ida); - void operator=(AutoIdArray &ida); -}; - -/* The auto-root for enumeration object and its state. */ -class AutoEnumStateRooter : private AutoGCRooter -{ - public: - AutoEnumStateRooter(JSContext *cx, JSObject *obj - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, ENUMERATOR), obj(obj), stateValue() - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ASSERT(obj); - } - - ~AutoEnumStateRooter() { - if (!stateValue.isNull()) { - DebugOnly ok = - obj->enumerate(context, JSENUMERATE_DESTROY, &stateValue, 0); - JS_ASSERT(ok); - } - } - - friend void AutoGCRooter::trace(JSTracer *trc); - - const Value &state() const { return stateValue; } - Value *addr() { return &stateValue; } - - protected: - void trace(JSTracer *trc); - - JSObject * const obj; - - private: - Value stateValue; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -#ifdef JS_HAS_XML_SUPPORT -class AutoXMLRooter : private AutoGCRooter { - public: - AutoXMLRooter(JSContext *cx, JSXML *xml - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, XML), xml(xml) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_ASSERT(xml); - } - - friend void AutoGCRooter::trace(JSTracer *trc); - friend void MarkRuntime(JSTracer *trc); - - private: - JSXML * const xml; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; -#endif /* JS_HAS_XML_SUPPORT */ - -class AutoBindingsRooter : private AutoGCRooter { - public: - AutoBindingsRooter(JSContext *cx, Bindings &bindings - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, BINDINGS), bindings(bindings) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - - private: - Bindings &bindings; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoLockGC { - public: - explicit AutoLockGC(JSRuntime *rt - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : rt(rt) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_LOCK_GC(rt); - } - ~AutoLockGC() { JS_UNLOCK_GC(rt); } - - private: - JSRuntime *rt; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoUnlockGC { - private: - JSRuntime *rt; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit AutoUnlockGC(JSRuntime *rt - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : rt(rt) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_UNLOCK_GC(rt); - } - ~AutoUnlockGC() { JS_LOCK_GC(rt); } -}; - -class AutoLockAtomsCompartment { - private: - JSContext *cx; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoLockAtomsCompartment(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_LOCK(cx, &cx->runtime->atomState.lock); -#ifdef JS_THREADSAFE - cx->runtime->atomsCompartmentIsLocked = true; -#endif - } - ~AutoLockAtomsCompartment() { -#ifdef JS_THREADSAFE - cx->runtime->atomsCompartmentIsLocked = false; -#endif - JS_UNLOCK(cx, &cx->runtime->atomState.lock); - } -}; - -class AutoUnlockAtomsCompartment { - JSContext *cx; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoUnlockAtomsCompartment(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; -#ifdef JS_THREADSAFE - cx->runtime->atomsCompartmentIsLocked = false; -#endif - JS_UNLOCK(cx, &cx->runtime->atomState.lock); - } - ~AutoUnlockAtomsCompartment() { - JS_LOCK(cx, &cx->runtime->atomState.lock); -#ifdef JS_THREADSAFE - cx->runtime->atomsCompartmentIsLocked = true; -#endif - } -}; - -class AutoKeepAtoms { - JSRuntime *rt; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit AutoKeepAtoms(JSRuntime *rt - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : rt(rt) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_KEEP_ATOMS(rt); - } - ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } -}; - -class AutoArenaAllocator { - JSArenaPool *pool; - void *mark; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit AutoArenaAllocator(JSArenaPool *pool - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : pool(pool), mark(JS_ARENA_MARK(pool)) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - ~AutoArenaAllocator() { JS_ARENA_RELEASE(pool, mark); } - - template - T *alloc(size_t elems) { - void *ptr; - JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T)); - return static_cast(ptr); - } -}; - -class AutoReleasePtr { - JSContext *cx; - void *ptr; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - AutoReleasePtr operator=(const AutoReleasePtr &other); - - public: - explicit AutoReleasePtr(JSContext *cx, void *ptr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx), ptr(ptr) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - ~AutoReleasePtr() { cx->free_(ptr); } -}; +namespace js { /* - * FIXME: bug 602774: cleaner API for AutoReleaseNullablePtr + * Enumerate all contexts in a runtime that are in the same thread as a given + * context. */ -class AutoReleaseNullablePtr { - JSContext *cx; - void *ptr; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - AutoReleaseNullablePtr operator=(const AutoReleaseNullablePtr &other); +class ThreadContextRange { + JSCList *begin; + JSCList *end; - public: - explicit AutoReleaseNullablePtr(JSContext *cx, void *ptr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx), ptr(ptr) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; +public: + explicit ThreadContextRange(JSContext *cx) { + end = &cx->runtime->contextList; + begin = end->next; } - void reset(void *ptr2) { - if (ptr) - cx->free_(ptr); - ptr = ptr2; - } - ~AutoReleaseNullablePtr() { if (ptr) cx->free_(ptr); } -}; -class AutoLocalNameArray { - public: - explicit AutoLocalNameArray(JSContext *cx, JSFunction *fun - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : context(cx), - mark(JS_ARENA_MARK(&cx->tempPool)), - names(fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool)), - count(fun->script()->bindings.countLocalNames()) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } + bool empty() const { return begin == end; } + void popFront() { JS_ASSERT(!empty()); begin = begin->next; } - ~AutoLocalNameArray() { - JS_ARENA_RELEASE(&context->tempPool, mark); + JSContext *front() const { + return JSContext::fromLinkField(begin); } - - operator bool() const { return !!names; } - - uint32 length() const { return count; } - - const jsuword &operator [](unsigned i) const { return names[i]; } - - private: - JSContext *context; - void *mark; - jsuword *names; - uint32 count; - - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -template -class AlreadyIncRefed -{ - typedef RefCountable *****ConvertibleToBool; - - RefCountable *obj; - - public: - explicit AlreadyIncRefed(RefCountable *obj) : obj(obj) {} - - bool null() const { return obj == NULL; } - operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } - - RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } - RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } - RefCountable *get() const { return obj; } -}; - -template -class NeedsIncRef -{ - typedef RefCountable *****ConvertibleToBool; - - RefCountable *obj; - - public: - explicit NeedsIncRef(RefCountable *obj) : obj(obj) {} - - bool null() const { return obj == NULL; } - operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } - - RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } - RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } - RefCountable *get() const { return obj; } -}; - -template -class AutoRefCount -{ - typedef RefCountable *****ConvertibleToBool; - - JSContext *const cx; - RefCountable *obj; - - AutoRefCount(const AutoRefCount &); - void operator=(const AutoRefCount &); - - public: - explicit AutoRefCount(JSContext *cx) - : cx(cx), obj(NULL) - {} - - AutoRefCount(JSContext *cx, NeedsIncRef aobj) - : cx(cx), obj(aobj.get()) - { - if (obj) - obj->incref(cx); - } - - AutoRefCount(JSContext *cx, AlreadyIncRefed aobj) - : cx(cx), obj(aobj.get()) - {} - - ~AutoRefCount() { - if (obj) - obj->decref(cx); - } - - void reset(NeedsIncRef aobj) { - if (obj) - obj->decref(cx); - obj = aobj.get(); - if (obj) - obj->incref(cx); - } - - void reset(AlreadyIncRefed aobj) { - if (obj) - obj->decref(cx); - obj = aobj.get(); - } - - bool null() const { return obj == NULL; } - operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } - - RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } - RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } - RefCountable *get() const { return obj; } }; } /* namespace js */ -class JSAutoResolveFlags -{ - public: - JSAutoResolveFlags(JSContext *cx, uintN flags - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : mContext(cx), mSaved(cx->resolveFlags) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - cx->resolveFlags = flags; - } - - ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } - - private: - JSContext *mContext; - uintN mSaved; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -extern js::ThreadData * -js_CurrentThreadData(JSRuntime *rt); - -extern JSBool -js_InitThreads(JSRuntime *rt); - -extern void -js_FinishThreads(JSRuntime *rt); - -extern void -js_PurgeThreads(JSContext *cx); - -namespace js { - -#ifdef JS_THREADSAFE - -/* Iterator over ThreadData from all JSThread instances. */ -class ThreadDataIter : public JSThread::Map::Range -{ - public: - ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {} - - ThreadData *threadData() const { - return &front().value->data; - } -}; - -#else /* !JS_THREADSAFE */ - -class ThreadDataIter -{ - JSRuntime *runtime; - bool done; - public: - ThreadDataIter(JSRuntime *rt) : runtime(rt), done(false) {} - - bool empty() const { - return done; - } - - void popFront() { - JS_ASSERT(!done); - done = true; - } - - ThreadData *threadData() const { - JS_ASSERT(!done); - return &runtime->threadData; - } -}; - -#endif /* !JS_THREADSAFE */ - -} /* namespace js */ - /* * Create and destroy functions for JSContext, which is manually allocated * and exclusively owned. @@ -2201,16 +1325,16 @@ class ThreadDataIter extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); +typedef enum JSDestroyContextMode { + JSDCM_NO_GC, + JSDCM_MAYBE_GC, + JSDCM_FORCE_GC, + JSDCM_NEW_FAILED +} JSDestroyContextMode; + extern void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); -static JS_INLINE JSContext * -js_ContextFromLinkField(JSCList *link) -{ - JS_ASSERT(link); - return (JSContext *) ((uint8 *) link - offsetof(JSContext, link)); -} - /* * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. @@ -2218,29 +1342,6 @@ js_ContextFromLinkField(JSCList *link) extern JSContext * js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); -/* - * Iterate through contexts with active requests. The caller must be holding - * rt->gcLock in case of a thread-safe build, or otherwise guarantee that the - * context list is not alternated asynchroniously. - */ -extern JS_FRIEND_API(JSContext *) -js_NextActiveContext(JSRuntime *, JSContext *); - -/* - * Report an exception, which is currently realized as a printf-style format - * string and its arguments. - */ -typedef enum JSErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "js.msg" -#undef MSG_DEF - JSErr_Limit -} JSErrNum; - -extern JS_FRIEND_API(const JSErrorFormatString *) -js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); - #ifdef va_start extern JSBool js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); @@ -2260,29 +1361,9 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, extern void js_ReportOutOfMemory(JSContext *cx); -/* - * Report that cx->scriptStackQuota is exhausted. - */ -void -js_ReportOutOfScriptQuota(JSContext *maybecx); - -/* JS_CHECK_RECURSION is used outside JS, so JS_FRIEND_API. */ -JS_FRIEND_API(void) -js_ReportOverRecursed(JSContext *maybecx); - extern JS_FRIEND_API(void) js_ReportAllocationOverflow(JSContext *cx); -#define JS_CHECK_RECURSION(cx, onerror) \ - JS_BEGIN_MACRO \ - int stackDummy_; \ - \ - if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy_)) { \ - js_ReportOverRecursed(cx); \ - onerror; \ - } \ - JS_END_MACRO - /* * Report an exception using a previously composed JSErrorReport. * XXXbe remove from "friend" API @@ -2328,8 +1409,7 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber, extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; #ifdef JS_THREADSAFE -# define JS_ASSERT_REQUEST_DEPTH(cx) (JS_ASSERT((cx)->thread()), \ - JS_ASSERT((cx)->thread()->data.requestDepth >= 1)) +# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime->requestDepth >= 1) #else # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) #endif @@ -2341,7 +1421,7 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; */ #define JS_CHECK_OPERATION_LIMIT(cx) \ (JS_ASSERT_REQUEST_DEPTH(cx), \ - (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx))) + (!cx->runtime->interrupt || js_InvokeOperationCallback(cx))) /* * Invoke the operation callback and return false if the current execution @@ -2353,185 +1433,187 @@ js_InvokeOperationCallback(JSContext *cx); extern JSBool js_HandleExecutionInterrupt(JSContext *cx); -namespace js { - -/* These must be called with GC lock taken. */ - -JS_FRIEND_API(void) -TriggerOperationCallback(JSContext *cx); - -void -TriggerAllOperationCallbacks(JSRuntime *rt); - -} /* namespace js */ - +/* + * Get the topmost scripted frame in a context. Note: if the topmost frame is + * in the middle of an inline call, that call will be expanded. To avoid this, + * use cx->stack.currentScript or cx->stack.currentScriptedScopeChain. + */ extern js::StackFrame * js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp); extern jsbytecode* js_GetCurrentBytecodePC(JSContext* cx); -extern bool -js_CurrentPCIsInImacro(JSContext *cx); +extern JSScript * +js_GetCurrentScript(JSContext* cx); namespace js { -class RegExpStatics; - -extern JS_FORCES_STACK JS_FRIEND_API(void) -LeaveTrace(JSContext *cx); +#ifdef JS_METHODJIT +namespace mjit { + void ExpandInlineFrames(JSCompartment *compartment); +} +#endif } /* namespace js */ -/* - * Get the current frame, first lazily instantiating stack frames if needed. - * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.) - * - * Defined in jstracer.cpp if JS_TRACER is defined. - */ -static JS_FORCES_STACK JS_INLINE js::StackFrame * -js_GetTopStackFrame(JSContext *cx) +/* How much expansion of inlined frames to do when inspecting the stack. */ +enum FrameExpandKind { + FRAME_EXPAND_NONE = 0, + FRAME_EXPAND_ALL = 1 +}; + +namespace js { + +/************************************************************************/ + +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(Value *vec, size_t len) { - js::LeaveTrace(cx); - return cx->maybefp(); + PodZero(vec, len); } -static JS_INLINE JSBool -js_IsPropertyCacheDisabled(JSContext *cx) +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(Value *beg, Value *end) { - return cx->runtime->shapeGen >= js::SHAPE_OVERFLOW_BIT; + PodZero(beg, end - beg); } -static JS_INLINE uint32 -js_RegenerateShapeForGC(JSRuntime *rt) +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(jsid *beg, jsid *end) { - JS_ASSERT(rt->gcRunning); - JS_ASSERT(rt->gcRegenShapes); - - /* - * Under the GC, compared with js_GenerateShape, we don't need to use - * atomic increments but we still must make sure that after an overflow - * the shape stays such. - */ - uint32 shape = rt->shapeGen; - shape = (shape + 1) | (shape & js::SHAPE_OVERFLOW_BIT); - rt->shapeGen = shape; - return shape; + for (jsid *id = beg; id != end; ++id) + *id = INT_TO_JSID(0); } -namespace js { - -template -class AutoVectorRooter : protected AutoGCRooter +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(jsid *vec, size_t len) { - public: - explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, tag), vector(cx) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } + MakeRangeGCSafe(vec, vec + len); +} - size_t length() const { return vector.length(); } +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(const Shape **beg, const Shape **end) +{ + PodZero(beg, end - beg); +} - bool append(const T &v) { return vector.append(v); } +static JS_ALWAYS_INLINE void +MakeRangeGCSafe(const Shape **vec, size_t len) +{ + PodZero(vec, len); +} - /* For use when space has already been reserved. */ - void infallibleAppend(const T &v) { vector.infallibleAppend(v); } +static JS_ALWAYS_INLINE void +SetValueRangeToUndefined(Value *beg, Value *end) +{ + for (Value *v = beg; v != end; ++v) + v->setUndefined(); +} - void popBack() { vector.popBack(); } - T popCopy() { return vector.popCopy(); } +static JS_ALWAYS_INLINE void +SetValueRangeToUndefined(Value *vec, size_t len) +{ + SetValueRangeToUndefined(vec, vec + len); +} - bool growBy(size_t inc) { - size_t oldLength = vector.length(); - if (!vector.growByUninitialized(inc)) - return false; - MakeRangeGCSafe(vector.begin() + oldLength, vector.end()); - return true; - } +static JS_ALWAYS_INLINE void +SetValueRangeToNull(Value *beg, Value *end) +{ + for (Value *v = beg; v != end; ++v) + v->setNull(); +} - bool resize(size_t newLength) { - size_t oldLength = vector.length(); - if (newLength <= oldLength) { - vector.shrinkBy(oldLength - newLength); - return true; - } - if (!vector.growByUninitialized(newLength - oldLength)) - return false; - MakeRangeGCSafe(vector.begin() + oldLength, vector.end()); - return true; - } +static JS_ALWAYS_INLINE void +SetValueRangeToNull(Value *vec, size_t len) +{ + SetValueRangeToNull(vec, vec + len); +} - bool reserve(size_t newLength) { - return vector.reserve(newLength); +class AutoObjectVector : public AutoVectorRooter +{ + public: + explicit AutoObjectVector(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooter(cx, OBJVECTOR) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; } - T &operator[](size_t i) { return vector[i]; } - const T &operator[](size_t i) const { return vector[i]; } - - const T *begin() const { return vector.begin(); } - T *begin() { return vector.begin(); } - - const T *end() const { return vector.end(); } - T *end() { return vector.end(); } - - const T &back() const { return vector.back(); } - - friend void AutoGCRooter::trace(JSTracer *trc); - - private: - typedef Vector VectorImpl; - VectorImpl vector; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class AutoValueVector : public AutoVectorRooter +class AutoShapeVector : public AutoVectorRooter { public: - explicit AutoValueVector(JSContext *cx + explicit AutoShapeVector(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, VALVECTOR) + : AutoVectorRooter(cx, SHAPEVECTOR) { JS_GUARD_OBJECT_NOTIFIER_INIT; } - const jsval *jsval_begin() const { return Jsvalify(begin()); } - jsval *jsval_begin() { return Jsvalify(begin()); } - - const jsval *jsval_end() const { return Jsvalify(end()); } - jsval *jsval_end() { return Jsvalify(end()); } - JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class AutoIdVector : public AutoVectorRooter +class AutoValueArray : public AutoGCRooter { + const js::Value *start_; + unsigned length_; + public: - explicit AutoIdVector(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, IDVECTOR) + AutoValueArray(JSContext *cx, const js::Value *start, unsigned length + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, VALARRAY), start_(start), length_(length) { JS_GUARD_OBJECT_NOTIFIER_INIT; } + const Value *start() const { return start_; } + unsigned length() const { return length_; } + JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class AutoShapeVector : public AutoVectorRooter +/* + * Allocation policy that uses JSRuntime::malloc_ and friends, so that + * memory pressure is properly accounted for. This is suitable for + * long-lived objects owned by the JSRuntime. + * + * Since it doesn't hold a JSContext (those may not live long enough), it + * can't report out-of-memory conditions itself; the caller must check for + * OOM and take the appropriate action. + * + * FIXME bug 647103 - replace these *AllocPolicy names. + */ +class RuntimeAllocPolicy { - public: - explicit AutoShapeVector(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, SHAPEVECTOR) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } + JSRuntime *const runtime; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER + public: + RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {} + RuntimeAllocPolicy(JSContext *cx) : runtime(cx->runtime) {} + void *malloc_(size_t bytes) { return runtime->malloc_(bytes); } + void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); } + void free_(void *p) { runtime->free_(p); } + void reportAllocOverflow() const {} }; -JSIdArray * -NewIdArray(JSContext *cx, jsint length); +/* + * FIXME bug 647103 - replace these *AllocPolicy names. + */ +class ContextAllocPolicy +{ + JSContext *const cx; + + public: + ContextAllocPolicy(JSContext *cx) : cx(cx) {} + JSContext *context() const { return cx; } + void *malloc_(size_t bytes) { return cx->malloc_(bytes); } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx->realloc_(p, oldBytes, bytes); } + void free_(void *p) { cx->free_(p); } + void reportAllocOverflow() const { js_ReportAllocationOverflow(cx); } +}; } /* namespace js */ diff --git a/deps/mozjs/js/src/jscntxtinlines.h b/deps/mozjs/js/src/jscntxtinlines.h index fb00563431b..9ca6a86219d 100644 --- a/deps/mozjs/js/src/jscntxtinlines.h +++ b/deps/mozjs/js/src/jscntxtinlines.h @@ -43,42 +43,51 @@ #include "jscntxt.h" #include "jscompartment.h" -#include "jsstaticcheck.h" +#include "jsfriendapi.h" +#include "jsinterp.h" #include "jsxml.h" -#include "jsregexp.h" #include "jsgc.h" +#include "frontend/ParseMaps.h" +#include "vm/RegExpObject.h" + namespace js { +struct PreserveRegsGuard +{ + PreserveRegsGuard(JSContext *cx, FrameRegs ®s) + : prevContextRegs(cx->maybeRegs()), cx(cx), regs_(regs) { + cx->stack.repointRegs(®s_); + } + ~PreserveRegsGuard() { + JS_ASSERT(cx->maybeRegs() == ®s_); + *prevContextRegs = regs_; + cx->stack.repointRegs(prevContextRegs); + } + + FrameRegs *prevContextRegs; + + private: + JSContext *cx; + FrameRegs ®s_; +}; + static inline GlobalObject * GetGlobalForScopeChain(JSContext *cx) { - /* - * This is essentially GetScopeChain(cx)->getGlobal(), but without - * falling off trace. - * - * This use of cx->fp, possibly on trace, is deliberate: - * cx->fp->scopeChain->getGlobal() returns the same object whether we're on - * trace or not, since we do not trace calls across global objects. - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - if (cx->running()) - return cx->fp()->scopeChain().getGlobal(); + if (cx->hasfp()) + return &cx->fp()->scopeChain().global(); - JSObject *scope = cx->globalObject; - if (!scope) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + JSObject *scope = JS_ObjectToInnerObject(cx, cx->globalObject); + if (!scope) return NULL; - } - OBJ_TO_INNER_OBJECT(cx, scope); - return scope->asGlobal(); + return &scope->asGlobal(); } inline GSNCache * GetGSNCache(JSContext *cx) { - return &JS_THREAD_DATA(cx)->gsnCache; + return &cx->runtime->gsnCache; } class AutoNamespaceArray : protected AutoGCRooter { @@ -91,25 +100,48 @@ class AutoNamespaceArray : protected AutoGCRooter { array.finish(context); } - uint32 length() const { return array.length; } + uint32_t length() const { return array.length; } public: friend void AutoGCRooter::trace(JSTracer *trc); - JSXMLArray array; + JSXMLArray array; +}; + +template +class AutoPtr +{ + JSContext *cx; + T *value; + + AutoPtr(const AutoPtr &other) MOZ_DELETE; + + public: + explicit AutoPtr(JSContext *cx) : cx(cx), value(NULL) {} + ~AutoPtr() { + cx->delete_(value); + } + + void operator=(T *ptr) { value = ptr; } + + typedef void ***** ConvertibleToBool; + operator ConvertibleToBool() const { return (ConvertibleToBool) value; } + + const T *operator->() const { return value; } + T *operator->() { return value; } + + T *get() { return value; } }; #ifdef DEBUG class CompartmentChecker { - private: JSContext *context; JSCompartment *compartment; public: explicit CompartmentChecker(JSContext *cx) : context(cx), compartment(cx->compartment) { - check(cx->running() ? JS_GetGlobalForScopeChain(cx) : cx->globalObject); - VOUCH_DOES_NOT_REQUIRE_STACK(); + check(cx->hasfp() ? JS_GetGlobalForScopeChain(cx) : cx->globalObject); } /* @@ -157,10 +189,6 @@ class CompartmentChecker check(v.toString()); } - void check(jsval v) { - check(Valueify(v)); - } - void check(const ValueArray &arr) { for (size_t i = 0; i < arr.length; i++) check(arr.array[i]); @@ -171,6 +199,11 @@ class CompartmentChecker check(arr.array[i]); } + void check(const CallArgs &args) { + for (Value *p = args.base(); p != args.end(); ++p) + check(*p); + } + void check(jsid id) { if (JSID_IS_OBJECT(id)) check(JSID_TO_OBJECT(id)); @@ -187,14 +220,15 @@ class CompartmentChecker void check(JSScript *script) { if (script) { - check(script->compartment); - if (script->u.object) - check(script->u.object); + check(script->compartment()); + if (!script->isCachedEval && script->globalObject) + check(script->globalObject); } } void check(StackFrame *fp) { - check(&fp->scopeChain()); + if (fp) + check(&fp->scopeChain()); } }; @@ -266,17 +300,17 @@ assertSameCompartment(JSContext *cx, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) #undef START_ASSERT_SAME_COMPARTMENT -STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2) +STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc) JS_ALWAYS_INLINE bool -CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) +CallJSNative(JSContext *cx, Native native, const CallArgs &args) { #ifdef DEBUG JSBool alreadyThrowing = cx->isExceptionPending(); #endif - assertSameCompartment(cx, ValueArray(vp, argc + 2)); - JSBool ok = native(cx, argc, vp); + assertSameCompartment(cx, args); + bool ok = native(cx, args.length(), args.base()); if (ok) { - assertSameCompartment(cx, vp[0]); + assertSameCompartment(cx, args.rval()); JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending()); } return ok; @@ -284,16 +318,16 @@ CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) extern JSBool CallOrConstructBoundFunction(JSContext *, uintN, js::Value *); -STATIC_PRECONDITION(ubound(vp) >= argc + 2) +STATIC_PRECONDITION(ubound(args.argv_) >= argc) JS_ALWAYS_INLINE bool -CallJSNativeConstructor(JSContext *cx, js::Native native, uintN argc, js::Value *vp) +CallJSNativeConstructor(JSContext *cx, Native native, const CallArgs &args) { #ifdef DEBUG - JSObject *callee = &vp[0].toObject(); + JSObject &callee = args.callee(); #endif - JS_ASSERT(vp[1].isMagic()); - if (!CallJSNative(cx, native, argc, vp)) + JS_ASSERT(args.thisv().isMagic()); + if (!CallJSNative(cx, native, args)) return false; /* @@ -311,16 +345,17 @@ CallJSNativeConstructor(JSContext *cx, js::Native native, uintN argc, js::Value * * (new Object(Object)) returns the callee. */ - extern JSBool proxy_Construct(JSContext *, uintN, Value *); - JS_ASSERT_IF(native != proxy_Construct && native != js::CallOrConstructBoundFunction && - (!callee->isFunction() || callee->getFunctionPrivate()->u.n.clasp != &js_ObjectClass), - !vp->isPrimitive() && callee != &vp[0].toObject()); + JS_ASSERT_IF(native != FunctionProxyClass.construct && + native != CallableObjectClass.construct && + native != js::CallOrConstructBoundFunction && + (!callee.isFunction() || callee.toFunction()->u.n.clasp != &ObjectClass), + !args.rval().isPrimitive() && callee != args.rval().toObject()); return true; } JS_ALWAYS_INLINE bool -CallJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *receiver, jsid id, js::Value *vp) +CallJSPropertyOp(JSContext *cx, PropertyOp op, JSObject *receiver, jsid id, Value *vp) { assertSameCompartment(cx, receiver, id, *vp); JSBool ok = op(cx, receiver, id, vp); @@ -330,19 +365,19 @@ CallJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *receiver, jsid id, } JS_ALWAYS_INLINE bool -CallJSPropertyOpSetter(JSContext *cx, js::StrictPropertyOp op, JSObject *obj, jsid id, - JSBool strict, js::Value *vp) +CallJSPropertyOpSetter(JSContext *cx, StrictPropertyOp op, JSObject *obj, jsid id, + JSBool strict, Value *vp) { assertSameCompartment(cx, obj, id, *vp); return op(cx, obj, id, strict, vp); } inline bool -CallSetter(JSContext *cx, JSObject *obj, jsid id, js::StrictPropertyOp op, uintN attrs, - uintN shortid, JSBool strict, js::Value *vp) +CallSetter(JSContext *cx, JSObject *obj, jsid id, StrictPropertyOp op, uintN attrs, + uintN shortid, JSBool strict, Value *vp) { if (attrs & JSPROP_SETTER) - return ExternalGetOrSet(cx, obj, id, CastAsObjectJsval(op), JSACC_WRITE, 1, vp, vp); + return InvokeGetterOrSetter(cx, obj, CastAsObjectJsval(op), 1, vp, vp); if (attrs & JSPROP_GETTER) return js_ReportGetterOnlyAssignment(cx); @@ -352,52 +387,96 @@ CallSetter(JSContext *cx, JSObject *obj, jsid id, js::StrictPropertyOp op, uintN return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp); } -#ifdef JS_TRACER -/* - * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a - * _FAIL builtin from trace on cx or another context on the same thread. The - * machine code for the trace remains on the C stack when js_DeepBail returns. - * - * Implemented in jstracer.cpp. - */ -JS_FORCES_STACK JS_FRIEND_API(void) -DeepBail(JSContext *cx); -#endif +static inline JSAtom ** +FrameAtomBase(JSContext *cx, js::StackFrame *fp) +{ + return fp->script()->atoms; +} + +} /* namespace js */ + +inline JSVersion +JSContext::findVersion() const +{ + if (hasVersionOverride) + return versionOverride; + + if (stack.hasfp()) { + /* There may be a scripted function somewhere on the stack! */ + js::StackFrame *f = fp(); + while (f && !f->isScriptFrame()) + f = f->prev(); + if (f) + return f->script()->getVersion(); + } -static JS_INLINE void -LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj) + return defaultVersion; +} + +inline bool +JSContext::canSetDefaultVersion() const { - if (!obj->parent) - LeaveTrace(cx); + return !stack.hasfp() && !hasVersionOverride; } -static JS_INLINE void -LeaveTraceIfArgumentsObject(JSContext *cx, JSObject *obj) +inline void +JSContext::overrideVersion(JSVersion newVersion) { - if (obj->isArguments()) - LeaveTrace(cx); + JS_ASSERT(!canSetDefaultVersion()); + versionOverride = newVersion; + hasVersionOverride = true; } -static JS_INLINE JSBool -CanLeaveTrace(JSContext *cx) +inline bool +JSContext::maybeOverrideVersion(JSVersion newVersion) { - JS_ASSERT(JS_ON_TRACE(cx)); -#ifdef JS_TRACER - return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit != NULL; -#else - return JS_FALSE; -#endif + if (canSetDefaultVersion()) { + setDefaultVersion(newVersion); + return false; + } + overrideVersion(newVersion); + return true; } -} /* namespace js */ +inline uintN +JSContext::getCompileOptions() const { return js::VersionFlagsToOptions(findVersion()); } + +inline uintN +JSContext::allOptions() const { return getRunOptions() | getCompileOptions(); } + +inline void +JSContext::setCompileOptions(uintN newcopts) +{ + JS_ASSERT((newcopts & JSCOMPILEOPTION_MASK) == newcopts); + if (JS_LIKELY(getCompileOptions() == newcopts)) + return; + JSVersion version = findVersion(); + JSVersion newVersion = js::OptionFlagsToVersion(newcopts, version); + maybeOverrideVersion(newVersion); +} + +inline void +JSContext::assertValidStackDepth(uintN depth) +{ +#ifdef DEBUG + JS_ASSERT(0 <= regs().sp - fp()->base()); + JS_ASSERT(depth <= uintptr_t(regs().sp - fp()->base())); +#endif +} #ifdef JS_METHODJIT inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() { - return compartment->jaegerCompartment; + return compartment->jaegerCompartment(); } #endif +inline js::LifoAlloc & +JSContext::typeLifoAlloc() +{ + return compartment->typeLifoAlloc; +} + inline bool JSContext::ensureGeneratorStackSpace() { @@ -407,17 +486,32 @@ JSContext::ensureGeneratorStackSpace() return ok; } -inline js::RegExpStatics * -JSContext::regExpStatics() -{ - return js::RegExpStatics::extractFrom(js::GetGlobalForScopeChain(this)); -} - inline void JSContext::setPendingException(js::Value v) { this->throwing = true; this->exception = v; - assertSameCompartment(this, v); + js::assertSameCompartment(this, v); +} + +inline bool +JSContext::ensureParseMapPool() +{ + if (parseMapPool_) + return true; + parseMapPool_ = js::OffTheBooks::new_(this); + return parseMapPool_; +} + +/* Get the current frame, first lazily instantiating stack frames if needed. */ +static inline js::StackFrame * +js_GetTopStackFrame(JSContext *cx, FrameExpandKind expand) +{ +#ifdef JS_METHODJIT + if (expand) + js::mjit::ExpandInlineFrames(cx->compartment); +#endif + + return cx->maybefp(); } #endif /* jscntxtinlines_h___ */ diff --git a/deps/mozjs/js/src/jscompartment.cpp b/deps/mozjs/js/src/jscompartment.cpp index b6335a01397..b87b948697b 100644 --- a/deps/mozjs/js/src/jscompartment.cpp +++ b/deps/mozjs/js/src/jscompartment.cpp @@ -43,152 +43,125 @@ #include "jsgc.h" #include "jsgcmark.h" #include "jsiter.h" +#include "jsmath.h" #include "jsproxy.h" #include "jsscope.h" -#include "jstracer.h" +#include "jswatchpoint.h" #include "jswrapper.h" + #include "assembler/wtf/Platform.h" +#include "js/MemoryMetrics.h" #include "methodjit/MethodJIT.h" #include "methodjit/PolyIC.h" #include "methodjit/MonoIC.h" +#include "vm/Debugger.h" +#include "yarr/BumpPointerAllocator.h" #include "jsgcinlines.h" +#include "jsobjinlines.h" #include "jsscopeinlines.h" #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" #endif +using namespace mozilla; using namespace js; using namespace js::gc; JSCompartment::JSCompartment(JSRuntime *rt) : rt(rt), principals(NULL), + needsBarrier_(false), + gcIncrementalTracer(NULL), gcBytes(0), gcTriggerBytes(0), gcLastBytes(0), hold(false), + typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), data(NULL), active(false), + hasDebugModeCodeToDrop(false), #ifdef JS_METHODJIT - jaegerCompartment(NULL), + jaegerCompartment_(NULL), #endif + regExps(rt), propertyTree(thisForCtor()), - emptyArgumentsShape(NULL), - emptyBlockShape(NULL), - emptyCallShape(NULL), - emptyDeclEnvShape(NULL), - emptyEnumeratorShape(NULL), - emptyWithShape(NULL), - initialRegExpShape(NULL), - initialStringShape(NULL), - debugMode(rt->debugMode), -#if ENABLE_YARR_JIT - regExpAllocator(NULL), -#endif - mathCache(NULL) + emptyTypeObject(NULL), + debugModeBits(rt->debugMode ? DebugFromC : 0), + mathCache(NULL), + watchpointMap(NULL) { - JS_INIT_CLIST(&scripts); - -#ifdef JS_TRACER - /* InitJIT expects this area to be zero'd. */ - PodZero(&traceMonitor); -#endif - - PodArrayZero(scriptsToGC); + PodArrayZero(evalCache); + setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9); } JSCompartment::~JSCompartment() { -#if ENABLE_YARR_JIT - Foreground::delete_(regExpAllocator); -#endif - -#if defined JS_TRACER - FinishJIT(&traceMonitor); -#endif - #ifdef JS_METHODJIT - Foreground::delete_(jaegerCompartment); + Foreground::delete_(jaegerCompartment_); #endif Foreground::delete_(mathCache); + Foreground::delete_(watchpointMap); #ifdef DEBUG - for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) - JS_ASSERT(!scriptsToGC[i]); + for (size_t i = 0; i < ArrayLength(evalCache); ++i) + JS_ASSERT(!evalCache[i]); #endif } bool -JSCompartment::init() -{ - chunk = NULL; - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].init(); - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - freeLists.finalizables[i] = NULL; -#ifdef JS_GCMETER - memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT); -#endif - if (!crossCompartmentWrappers.init()) - return false; +JSCompartment::init(JSContext *cx) +{ + activeAnalysis = activeInference = false; + types.init(cx); -#ifdef DEBUG - if (rt->meterEmptyShapes()) { - if (!emptyShapes.init()) - return false; - } -#endif + newObjectCache.reset(); -#ifdef JS_TRACER - if (!InitJIT(&traceMonitor, rt)) + if (!crossCompartmentWrappers.init()) return false; -#endif -#if ENABLE_YARR_JIT - regExpAllocator = rt->new_(); - if (!regExpAllocator) + if (!regExps.init(cx)) return false; -#endif - if (!backEdgeTable.init()) + if (!scriptFilenameTable.init()) return false; + return debuggees.init(); +} + #ifdef JS_METHODJIT - if (!(jaegerCompartment = rt->new_())) +bool +JSCompartment::ensureJaegerCompartmentExists(JSContext *cx) +{ + if (jaegerCompartment_) + return true; + + mjit::JaegerCompartment *jc = cx->new_(); + if (!jc) + return false; + if (!jc->Initialize(cx)) { + cx->delete_(jc); return false; - return jaegerCompartment->Initialize(); -#else + } + jaegerCompartment_ = jc; return true; -#endif } -#ifdef JS_METHODJIT size_t -JSCompartment::getMjitCodeSize() const +JSCompartment::sizeOfMjitCode() const { - return jaegerCompartment->execAlloc()->getCodeSize(); -} -#endif + if (!jaegerCompartment_) + return 0; -bool -JSCompartment::arenaListsAreEmpty() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (!arenas[i].isEmpty() || arenas[i].hasToBeFinalized) - return false; - } - return true; + size_t method, regexp, unused; + jaegerCompartment_->execAlloc()->sizeOfCode(&method, ®exp, &unused); + JS_ASSERT(regexp == 0); + return method + unused; } -static bool -IsCrossCompartmentWrapper(JSObject *wrapper) -{ - return wrapper->isWrapper() && - !!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT); -} +#endif bool JSCompartment::wrap(JSContext *cx, Value *vp) @@ -206,10 +179,6 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (vp->isString()) { JSString *str = vp->toString(); - /* Static atoms do not have to be wrapped. */ - if (str->isStaticAtom()) - return true; - /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; @@ -224,17 +193,15 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL - * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead -, + * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; - if (cx->running()) { - global = cx->fp()->scopeChain().getGlobal(); + if (cx->hasfp()) { + global = &cx->fp()->scopeChain().global(); } else { - global = cx->globalObject; - OBJ_TO_INNER_OBJECT(cx, global); + global = JS_ObjectToInnerObject(cx, cx->globalObject); if (!global) return false; } @@ -248,14 +215,14 @@ JSCompartment::wrap(JSContext *cx, Value *vp) return true; /* Translate StopIteration singleton. */ - if (obj->getClass() == &js_StopIterationClass) + if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { - obj = vp->toObject().unwrap(&flags); + obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); - if (obj->getCompartment() == this) + if (obj->compartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { @@ -265,7 +232,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) } vp->setObject(*obj); - if (obj->getCompartment() == this) + if (obj->compartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { @@ -292,12 +259,13 @@ JSCompartment::wrap(JSContext *cx, Value *vp) *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); - JS_ASSERT(IsCrossCompartmentWrapper(obj)); - if (obj->getParent() != global) { + JS_ASSERT(obj->isCrossCompartmentWrapper()); + if (global->getClass() != &dummy_class && obj->getParent() != global) { do { - obj->setParent(global); + if (!obj->setParent(cx, global)) + return false; obj = obj->getProto(); - } while (obj && IsCrossCompartmentWrapper(obj)); + } while (obj && obj->isCrossCompartmentWrapper()); } } return true; @@ -343,11 +311,14 @@ JSCompartment::wrap(JSContext *cx, Value *vp) vp->setObject(*wrapper); - wrapper->setProto(proto); - if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) + if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; - wrapper->setParent(global); + if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp)) + return false; + + if (!wrapper->setParent(cx, global)) + return false; return true; } @@ -361,6 +332,16 @@ JSCompartment::wrap(JSContext *cx, JSString **strp) return true; } +bool +JSCompartment::wrap(JSContext *cx, HeapPtrString *strp) +{ + AutoValueRooter tvr(cx, StringValue(*strp)); + if (!wrap(cx, tvr.addr())) + return false; + *strp = tvr.value().toString(); + return true; +} + bool JSCompartment::wrap(JSContext *cx, JSObject **objp) { @@ -425,33 +406,6 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props) return true; } -#if defined JS_METHODJIT && defined JS_MONOIC -/* - * Check if the pool containing the code for jit should be destroyed, per the - * heuristics in JSCompartment::sweep. - */ -static inline bool -ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit, - uint32 releaseInterval, uint32 &counter) -{ - JSC::ExecutablePool *pool = jit->code.m_executablePool; - if (pool->m_gcNumber != cx->runtime->gcNumber) { - /* - * The m_destroy flag may have been set in a previous GC for a pool which had - * references we did not remove (e.g. from the compartment's ExecutableAllocator) - * and is still around. Forget we tried to destroy it in such cases. - */ - pool->m_destroy = false; - pool->m_gcNumber = cx->runtime->gcNumber; - if (--counter == 0) { - pool->m_destroy = true; - counter = releaseInterval; - } - } - return pool->m_destroy; -} -#endif - /* * This method marks pointers that cross compartment boundaries. It should be * called only for per-compartment GCs, since full GCs naturally follow pointers @@ -460,82 +414,137 @@ ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit, void JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) { - JS_ASSERT(trc->context->runtime->gcCurrentCompartment); + JS_ASSERT(trc->runtime->gcCurrentCompartment); for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) - MarkValue(trc, e.front().key, "cross-compartment wrapper"); + MarkValueRoot(trc, e.front().key, "cross-compartment wrapper"); } void -JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) +JSCompartment::markTypes(JSTracer *trc) { - chunk = NULL; + /* + * Mark all scripts, type objects and singleton JS objects in the + * compartment. These can be referred to directly by type sets, which we + * cannot modify while code which depends on these type sets is active. + */ + JS_ASSERT(activeAnalysis); + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + MarkScriptRoot(trc, script, "mark_types_script"); + } + + for (size_t thingKind = FINALIZE_OBJECT0; + thingKind < FINALIZE_OBJECT_LIMIT; + thingKind++) { + for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) { + JSObject *object = i.get(); + if (object->hasSingletonType()) + MarkObjectRoot(trc, object, "mark_types_singleton"); + } + } + for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) + MarkTypeObjectRoot(trc, i.get(), "mark_types_scan"); +} + +void +JSCompartment::sweep(JSContext *cx, bool releaseTypes) +{ /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { - JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) && - !IsAboutToBeFinalized(cx, e.front().value.toGCThing()), + JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) && + !IsAboutToBeFinalized(e.front().value), e.front().key.isString()); - if (IsAboutToBeFinalized(cx, e.front().key.toGCThing()) || - IsAboutToBeFinalized(cx, e.front().value.toGCThing())) { + if (IsAboutToBeFinalized(e.front().key) || + IsAboutToBeFinalized(e.front().value)) { e.removeFront(); } } - /* Remove dead empty shapes. */ - if (emptyArgumentsShape && IsAboutToBeFinalized(cx, emptyArgumentsShape)) - emptyArgumentsShape = NULL; - if (emptyBlockShape && IsAboutToBeFinalized(cx, emptyBlockShape)) - emptyBlockShape = NULL; - if (emptyCallShape && IsAboutToBeFinalized(cx, emptyCallShape)) - emptyCallShape = NULL; - if (emptyDeclEnvShape && IsAboutToBeFinalized(cx, emptyDeclEnvShape)) - emptyDeclEnvShape = NULL; - if (emptyEnumeratorShape && IsAboutToBeFinalized(cx, emptyEnumeratorShape)) - emptyEnumeratorShape = NULL; - if (emptyWithShape && IsAboutToBeFinalized(cx, emptyWithShape)) - emptyWithShape = NULL; - - if (initialRegExpShape && IsAboutToBeFinalized(cx, initialRegExpShape)) - initialRegExpShape = NULL; - if (initialStringShape && IsAboutToBeFinalized(cx, initialStringShape)) - initialStringShape = NULL; - -#ifdef JS_TRACER - traceMonitor.sweep(cx); + /* Remove dead references held weakly by the compartment. */ + + sweepBaseShapeTable(cx); + sweepInitialShapeTable(cx); + sweepNewTypeObjectTable(cx, newTypeObjects); + sweepNewTypeObjectTable(cx, lazyTypeObjects); + + if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject)) + emptyTypeObject = NULL; + + newObjectCache.reset(); + + sweepBreakpoints(cx); + + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE); + + /* + * Kick all frames on the stack into the interpreter, and release all JIT + * code in the compartment. + */ +#ifdef JS_METHODJIT + mjit::ClearAllFrames(this); + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + mjit::ReleaseScriptCode(cx, script); + + /* + * Use counts for scripts are reset on GC. After discarding code we + * need to let it warm back up to get information like which opcodes + * are setting array holes or accessing getter properties. + */ + script->resetUseCount(); + } #endif + } -#if defined JS_METHODJIT && defined JS_MONOIC + if (!activeAnalysis) { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); - /* - * The release interval is the frequency with which we should try to destroy - * executable pools by releasing all JIT code in them, zero to never destroy pools. - * Initialize counter so that the first pool will be destroyed, and eventually drive - * the amount of JIT code in never-used compartments to zero. Don't discard anything - * for compartments which currently have active stack frames. - */ - uint32 counter = 1; - bool discardScripts = !active && releaseInterval != 0; - - for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { - JSScript *script = reinterpret_cast(cursor); - if (script->hasJITCode()) { - mjit::ic::SweepCallICs(cx, script, discardScripts); - if (discardScripts) { - if (script->jitNormal && - ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) { - mjit::ReleaseScriptCode(cx, script); - continue; - } - if (script->jitCtor && - ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) { - mjit::ReleaseScriptCode(cx, script); + /* + * Clear the analysis pool, but don't release its data yet. While + * sweeping types any live data will be allocated into the pool. + */ + LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); + oldAlloc.steal(&typeLifoAlloc); + + /* + * Periodically release observed types for all scripts. This is safe to + * do when there are no frames for the compartment on the stack. + */ + if (active) + releaseTypes = false; + + /* + * Sweep analysis information and everything depending on it from the + * compartment, including all remaining mjit code if inference is + * enabled in the compartment. + */ + if (types.inferenceEnabled) { + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->types) { + types::TypeScript::Sweep(cx, script); + + if (releaseTypes) { + script->types->destroy(); + script->types = NULL; + script->typesPurged = true; + } } } } - } -#endif /* JS_METHODJIT && JS_MONOIC */ + types.sweep(cx); + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + script->clearAnalysis(); + } + } active = false; } @@ -543,45 +552,53 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) void JSCompartment::purge(JSContext *cx) { - freeLists.purge(); + arenas.purge(); + regExps.purge(); dtoaCache.purge(); - /* Destroy eval'ed scripts. */ - js_DestroyScriptsToGC(cx, this); + /* + * Clear the hash and reset all evalHashLink to null before the GC. This + * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is + * not null when we have script owned by an object and not from the eval + * cache. + */ + for (size_t i = 0; i < ArrayLength(evalCache); ++i) { + for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) { + JSScript *script = *listHeadp; + JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT); + *listHeadp = NULL; + listHeadp = &script->evalHashLink(); + } + } nativeIterCache.purge(); toSourceCache.destroyIfConstructed(); +} + +void +JSCompartment::resetGCMallocBytes() +{ + gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); +} -#ifdef JS_TRACER +void +JSCompartment::setGCMaxMallocBytes(size_t value) +{ /* - * If we are about to regenerate shapes, we have to flush the JIT cache, - * which will eventually abort any current recording. + * For compatibility treat any value that exceeds PTRDIFF_T_MAX to + * mean that value. */ - if (cx->runtime->gcRegenShapes) - traceMonitor.needFlush = JS_TRUE; -#endif + gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; + resetGCMallocBytes(); +} -#ifdef JS_METHODJIT - for (JSScript *script = (JSScript *)scripts.next; - &script->links != &scripts; - script = (JSScript *)script->links.next) { - if (script->hasJITCode()) { -# if defined JS_POLYIC - mjit::ic::PurgePICs(cx, script); -# endif -# if defined JS_MONOIC - /* - * MICs do not refer to data which can be GC'ed and do not generate stubs - * which might need to be discarded, but are sensitive to shape regeneration. - */ - if (cx->runtime->gcRegenShapes) - mjit::ic::PurgeMICs(cx, script); -# endif - } - } -#endif +void +JSCompartment::onTooMuchMalloc() +{ + TriggerCompartmentGC(this, gcreason::TOO_MUCH_MALLOC); } + MathCache * JSCompartment::allocMathCache(JSContext *cx) { @@ -592,20 +609,176 @@ JSCompartment::allocMathCache(JSContext *cx) return mathCache; } -size_t -JSCompartment::backEdgeCount(jsbytecode *pc) const +bool +JSCompartment::hasScriptsOnStack(JSContext *cx) { - if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc)) - return p->value; + for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) { + JSScript *script = i.fp()->maybeScript(); + if (script && script->compartment() == this) + return true; + } + return false; +} - return 0; +bool +JSCompartment::setDebugModeFromC(JSContext *cx, bool b) +{ + bool enabledBefore = debugMode(); + bool enabledAfter = (debugModeBits & ~uintN(DebugFromC)) || b; + + // Debug mode can be enabled only when no scripts from the target + // compartment are on the stack. It would even be incorrect to discard just + // the non-live scripts' JITScripts because they might share ICs with live + // scripts (bug 632343). + // + // We do allow disabling debug mode while scripts are on the stack. In + // that case the debug-mode code for those scripts remains, so subsequently + // hooks may be called erroneously, even though debug mode is supposedly + // off, and we have to live with it. + // + bool onStack = false; + if (enabledBefore != enabledAfter) { + onStack = hasScriptsOnStack(cx); + if (b && onStack) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE); + return false; + } + } + + debugModeBits = (debugModeBits & ~uintN(DebugFromC)) | (b ? DebugFromC : 0); + JS_ASSERT(debugMode() == enabledAfter); + if (enabledBefore != enabledAfter) + updateForDebugMode(cx); + return true; } -size_t -JSCompartment::incBackEdgeCount(jsbytecode *pc) +void +JSCompartment::updateForDebugMode(JSContext *cx) { - if (BackEdgeMap::Ptr p = backEdgeTable.lookupWithDefault(pc, 0)) - return ++p->value; - return 1; /* oom not reported by backEdgeTable, so ignore. */ + for (ThreadContextRange r(cx); !r.empty(); r.popFront()) { + JSContext *cx = r.front(); + if (cx->compartment == this) + cx->updateJITEnabled(); + } + +#ifdef JS_METHODJIT + bool enabled = debugMode(); + + if (enabled) { + JS_ASSERT(!hasScriptsOnStack(cx)); + } else if (hasScriptsOnStack(cx)) { + hasDebugModeCodeToDrop = true; + return; + } + + /* + * Discard JIT code and bytecode analyses for any scripts that change + * debugMode. This assumes that 'comp' is in the same thread as 'cx'. + */ + for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->debugMode != enabled) { + mjit::ReleaseScriptCode(cx, script); + script->clearAnalysis(); + script->debugMode = enabled; + } + } + hasDebugModeCodeToDrop = false; +#endif +} + +bool +JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global) +{ + bool wasEnabled = debugMode(); + if (!debuggees.put(global)) { + js_ReportOutOfMemory(cx); + return false; + } + debugModeBits |= DebugFromJS; + if (!wasEnabled) + updateForDebugMode(cx); + return true; +} + +void +JSCompartment::removeDebuggee(JSContext *cx, + js::GlobalObject *global, + js::GlobalObjectSet::Enum *debuggeesEnum) +{ + bool wasEnabled = debugMode(); + JS_ASSERT(debuggees.has(global)); + if (debuggeesEnum) + debuggeesEnum->removeFront(); + else + debuggees.remove(global); + + if (debuggees.empty()) { + debugModeBits &= ~DebugFromJS; + if (wasEnabled && !debugMode()) + updateForDebugMode(cx); + } } +void +JSCompartment::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler) +{ + for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasAnyBreakpointsOrStepMode()) + script->clearBreakpointsIn(cx, dbg, handler); + } +} + +void +JSCompartment::clearTraps(JSContext *cx) +{ + for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasAnyBreakpointsOrStepMode()) + script->clearTraps(cx); + } +} + +void +JSCompartment::sweepBreakpoints(JSContext *cx) +{ + if (JS_CLIST_IS_EMPTY(&cx->runtime->debuggerList)) + return; + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (!script->hasAnyBreakpointsOrStepMode()) + continue; + bool scriptGone = IsAboutToBeFinalized(script); + for (unsigned i = 0; i < script->length; i++) { + BreakpointSite *site = script->getBreakpointSite(script->code + i); + if (!site) + continue; + // nextbp is necessary here to avoid possibly reading *bp after + // destroying it. + Breakpoint *nextbp; + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { + nextbp = bp->nextInSite(); + if (scriptGone || IsAboutToBeFinalized(bp->debugger->toJSObject())) + bp->destroy(cx); + } + } + } +} + +GCMarker * +JSCompartment::createBarrierTracer() +{ + JS_ASSERT(!gcIncrementalTracer); + return NULL; +} + +size_t +JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf) +{ + return baseShapes.sizeOfExcludingThis(mallocSizeOf) + + initialShapes.sizeOfExcludingThis(mallocSizeOf) + + newTypeObjects.sizeOfExcludingThis(mallocSizeOf) + + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf); +} diff --git a/deps/mozjs/js/src/jscompartment.h b/deps/mozjs/js/src/jscompartment.h index 9b9c1dceb84..5c0edb1f5e2 100644 --- a/deps/mozjs/js/src/jscompartment.h +++ b/deps/mozjs/js/src/jscompartment.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -40,278 +40,45 @@ #ifndef jscompartment_h___ #define jscompartment_h___ +#include "mozilla/Attributes.h" + +#include "jsclist.h" #include "jscntxt.h" -#include "jsgc.h" -#include "jsmath.h" -#include "jsobj.h" #include "jsfun.h" +#include "jsgc.h" #include "jsgcstats.h" -#include "jsclist.h" -#include "jsxml.h" +#include "jsobj.h" +#include "jsscope.h" +#include "vm/GlobalObject.h" +#include "vm/RegExpObject.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4251) /* Silence warning about JS_FRIEND_API and data members. */ #endif -namespace JSC { - -class ExecutableAllocator; - -} - namespace js { -/* Holds the number of recording attemps for an address. */ -typedef HashMap, - SystemAllocPolicy> RecordAttemptMap; - -/* Holds the profile data for loops. */ -typedef HashMap, - SystemAllocPolicy> LoopProfileMap; - -class Oracle; - -typedef HashSet, - SystemAllocPolicy> TracedScriptSet; - typedef HashMap, SystemAllocPolicy> ToSourceCache; -struct TraceMonitor; - -/* Holds the execution state during trace execution. */ -struct TracerState -{ - JSContext* cx; // current VM context handle - TraceMonitor* traceMonitor; // current TM - double* stackBase; // native stack base - double* sp; // native stack pointer, stack[0] is spbase[0] - double* eos; // first unusable word after the native stack / begin of globals - FrameInfo** callstackBase; // call stack base - void* sor; // start of rp stack - FrameInfo** rp; // call stack pointer - void* eor; // first unusable word after the call stack - VMSideExit* lastTreeExitGuard; // guard we exited on during a tree call - VMSideExit* lastTreeCallGuard; // guard we want to grow from if the tree - // call exit guard mismatched - void* rpAtLastTreeCall; // value of rp at innermost tree call guard - VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree - TreeFragment* outermostTree; // the outermost tree we initially invoked - uintN* inlineCallCountp; // inline call count counter - VMSideExit** innermostNestedGuardp; - VMSideExit* innermost; - uint64 startTime; - TracerState* prev; - - // Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the - // JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit - // if an error or exception occurred. - uint32 builtinStatus; - - // Used to communicate the location of the return value in case of a deep bail. - double* deepBailSp; - - // Used when calling natives from trace to root the vp vector. - uintN nativeVpLen; - js::Value* nativeVp; - - TracerState(JSContext *cx, TraceMonitor *tm, TreeFragment *ti, - uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp); - ~TracerState(); -}; - -/* - * Storage for the execution state and store during trace execution. Generated - * code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS| - * doubles after the stack begins. Thus, on trace, |TracerState::eos| holds a - * pointer to the first global. - */ -struct TraceNativeStorage -{ - /* Max number of stack slots/frame that may need to be restored in LeaveTree. */ - static const size_t MAX_NATIVE_STACK_SLOTS = 4096; - static const size_t MAX_CALL_STACK_ENTRIES = 500; - - double stack_global_buf[MAX_NATIVE_STACK_SLOTS + GLOBAL_SLOTS_BUFFER_SIZE]; - FrameInfo *callstack_buf[MAX_CALL_STACK_ENTRIES]; - - double *stack() { return stack_global_buf; } - double *global() { return stack_global_buf + MAX_NATIVE_STACK_SLOTS; } - FrameInfo **callstack() { return callstack_buf; } -}; - -/* Holds data to track a single globa. */ -struct GlobalState { - JSObject* globalObj; - uint32 globalShape; - SlotList* globalSlots; -}; - -/* - * Trace monitor. Every JSCompartment has an associated trace monitor - * that keeps track of loop frequencies for all JavaScript code loaded - * into that runtime. - */ -struct TraceMonitor { - /* - * The context currently executing JIT-compiled code in this compartment, or - * NULL if none. Among other things, this can in certain cases prevent - * last-ditch GC and suppress calls to JS_ReportOutOfMemory. - * - * !tracecx && !recorder: not on trace - * !tracecx && recorder: recording - * tracecx && !recorder: executing a trace - * tracecx && recorder: executing inner loop, recording outer loop - */ - JSContext *tracecx; - - /* - * State for the current tree execution. bailExit is valid if the tree has - * called back into native code via a _FAIL builtin and has not yet bailed, - * else garbage (NULL in debug builds). - */ - js::TracerState *tracerState; - js::VMSideExit *bailExit; - - /* Counts the number of iterations run by the currently executing trace. */ - unsigned iterationCounter; - - /* - * Cached storage to use when executing on trace. While we may enter nested - * traces, we always reuse the outer trace's storage, so never need more - * than of these. - */ - TraceNativeStorage *storage; - - /* - * There are 4 allocators here. This might seem like overkill, but they - * have different lifecycles, and by keeping them separate we keep the - * amount of retained memory down significantly. They are flushed (ie. - * all the allocated memory is freed) periodically. - * - * - dataAlloc has the lifecycle of the monitor. It's flushed only when - * the monitor is flushed. It's used for fragments. - * - * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is - * also *marked* when a recording starts and rewinds to the mark point - * if recording aborts. So you can put things in it that are only - * reachable on a successful record/compile cycle like GuardRecords and - * SideExits. - * - * - tempAlloc is flushed after each recording, successful or not. It's - * used to store LIR code and for all other elements in the LIR - * pipeline. - * - * - codeAlloc has the same lifetime as dataAlloc, but its API is - * different (CodeAlloc vs. VMAllocator). It's used for native code. - * It's also a good idea to keep code and data separate to avoid I-cache - * vs. D-cache issues. - */ - VMAllocator* dataAlloc; - VMAllocator* traceAlloc; - VMAllocator* tempAlloc; - nanojit::CodeAlloc* codeAlloc; - nanojit::Assembler* assembler; - FrameInfoCache* frameCache; - - /* This gets incremented every time the monitor is flushed. */ - uintN flushEpoch; - - Oracle* oracle; - TraceRecorder* recorder; - - /* If we are profiling a loop, this tracks the current profile. Otherwise NULL. */ - LoopProfile* profile; - - GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; - TreeFragment *vmfragments[FRAGMENT_TABLE_SIZE]; - RecordAttemptMap* recordAttempts; - - /* A hashtable mapping PC values to loop profiles for those loops. */ - LoopProfileMap* loopProfiles; - - /* - * Maximum size of the code cache before we start flushing. 1/16 of this - * size is used as threshold for the regular expression code cache. - */ - uint32 maxCodeCacheBytes; - - /* - * If nonzero, do not flush the JIT cache after a deep bail. That would - * free JITted code pages that we will later return to. Instead, set the - * needFlush flag so that it can be flushed later. - */ - JSBool needFlush; - - // Cached temporary typemap to avoid realloc'ing every time we create one. - // This must be used in only one place at a given time. It must be cleared - // before use. - TypeMap* cachedTempTypeMap; - - /* Scripts with recorded fragments. */ - TracedScriptSet tracedScripts; - -#ifdef DEBUG - /* Fields needed for fragment/guard profiling. */ - nanojit::Seq* branches; - uint32 lastFragID; - /* - * profAlloc has a lifetime which spans exactly from InitJIT to - * FinishJIT. - */ - VMAllocator* profAlloc; - FragStatsMap* profTab; -#endif - - bool ontrace() const { - return !!tracecx; - } - - /* Flush the JIT cache. */ - void flush(); - - /* Sweep any cache entry pointing to dead GC things. */ - void sweep(JSContext *cx); - - /* Mark any tracer stacks that are active. */ - void mark(JSTracer *trc); - - bool outOfMemory() const; - - JS_FRIEND_API(void) getCodeAllocStats(size_t &total, size_t &frag_size, size_t &free_size) const; - JS_FRIEND_API(size_t) getVMAllocatorsMainSize() const; - JS_FRIEND_API(size_t) getVMAllocatorsReserveSize() const; -}; - namespace mjit { class JaegerCompartment; } -} -/* Number of potentially reusable scriptsToGC to search for the eval cache. */ +/* Defined in jsapi.cpp */ +extern Class dummy_class; + +} /* namespace js */ + #ifndef JS_EVAL_CACHE_SHIFT # define JS_EVAL_CACHE_SHIFT 6 #endif -#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT) -#ifdef DEBUG -# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope) -# define identity(x) x - -struct JSEvalCacheMeter { - uint64 EVAL_CACHE_METER_LIST(identity); -}; - -# undef identity -#endif +/* Number of buckets in the hash of eval scripts. */ +#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT) namespace js { @@ -321,7 +88,7 @@ class NativeIterCache { /* Cached native iterators. */ JSObject *data[SIZE]; - static size_t getIndex(uint32 key) { + static size_t getIndex(uint32_t key) { return size_t(key) % SIZE; } @@ -339,15 +106,17 @@ class NativeIterCache { last = NULL; } - JSObject *get(uint32 key) const { + JSObject *get(uint32_t key) const { return data[getIndex(key)]; } - void set(uint32 key, JSObject *iterobj) { + void set(uint32_t key, JSObject *iterobj) { data[getIndex(key)] = iterobj; } }; +class MathCache; + /* * A single-entry cache for some base-10 double-to-string conversions. This * helps date-format-xparb.js. It also avoids skewing the results for @@ -375,53 +144,113 @@ class DtoaCache { }; +struct ScriptFilenameEntry +{ + bool marked; + char filename[1]; +}; + +struct ScriptFilenameHasher +{ + typedef const char *Lookup; + static HashNumber hash(const char *l) { return JS_HashString(l); } + static bool match(const ScriptFilenameEntry *e, const char *l) { + return strcmp(e->filename, l) == 0; + } +}; + +typedef HashSet ScriptFilenameTable; + } /* namespace js */ -struct JS_FRIEND_API(JSCompartment) { +namespace JS { +struct TypeInferenceSizes; +} + +struct JSCompartment +{ JSRuntime *rt; JSPrincipals *principals; - js::gc::Chunk *chunk; - js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT]; - js::gc::FreeLists freeLists; + js::gc::ArenaLists arenas; + + bool needsBarrier_; + js::GCMarker *gcIncrementalTracer; + + bool needsBarrier() { + return needsBarrier_; + } + + js::GCMarker *barrierTracer() { + JS_ASSERT(needsBarrier_); + if (gcIncrementalTracer) + return gcIncrementalTracer; + return createBarrierTracer(); + } - uint32 gcBytes; - uint32 gcTriggerBytes; + size_t gcBytes; + size_t gcTriggerBytes; size_t gcLastBytes; + size_t gcMaxMallocBytes; bool hold; + bool isSystemCompartment; -#ifdef JS_GCMETER - js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT]; -#endif + /* + * Pool for analysis and intermediate type information in this compartment. + * Cleared on every GC, unless the GC happens during analysis (indicated + * by activeAnalysis, which is implied by activeInference). + */ + static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 128 * 1024; + js::LifoAlloc typeLifoAlloc; + bool activeAnalysis; + bool activeInference; -#ifdef JS_TRACER - /* Trace-tree JIT recorder/interpreter state. */ - js::TraceMonitor traceMonitor; -#endif + /* Type information about the scripts and objects in this compartment. */ + js::types::TypeCompartment types; + public: /* Hashed lists of scripts created by eval to garbage-collect. */ - JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; - -#ifdef DEBUG - JSEvalCacheMeter evalCacheMeter; -#endif + JSScript *evalCache[JS_EVAL_CACHE_SIZE]; void *data; bool active; // GC flag, whether there are active frames + bool hasDebugModeCodeToDrop; js::WrapperMap crossCompartmentWrappers; #ifdef JS_METHODJIT - js::mjit::JaegerCompartment *jaegerCompartment; + private: + /* This is created lazily because many compartments don't need it. */ + js::mjit::JaegerCompartment *jaegerCompartment_; /* * This function is here so that xpconnect/src/xpcjsruntime.cpp doesn't * need to see the declaration of JaegerCompartment, which would require * #including MethodJIT.h into xpconnect/src/xpcjsruntime.cpp, which is * difficult due to reasons explained in bug 483677. */ - size_t getMjitCodeSize() const; + public: + bool hasJaegerCompartment() { + return !!jaegerCompartment_; + } + + js::mjit::JaegerCompartment *jaegerCompartment() const { + JS_ASSERT(jaegerCompartment_); + return jaegerCompartment_; + } + + bool ensureJaegerCompartmentExists(JSContext *cx); + + size_t sizeOfMjitCode() const; #endif + js::RegExpCompartment regExps; + + size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf); + void sizeOfTypeInferenceData(JSContext *cx, JS::TypeInferenceSizes *stats, + JSMallocSizeOfFun mallocSizeOf); + /* * Shared scope property tree, and arena-pool for allocating its nodes. */ @@ -435,55 +264,59 @@ struct JS_FRIEND_API(JSCompartment) { jsrefcount liveDictModeNodes; #endif - /* - * Runtime-shared empty scopes for well-known built-in objects that lack - * class prototypes (the usual locus of an emptyShape). Mnemonic: ABCDEW - */ - js::EmptyShape *emptyArgumentsShape; - js::EmptyShape *emptyBlockShape; - js::EmptyShape *emptyCallShape; - js::EmptyShape *emptyDeclEnvShape; - js::EmptyShape *emptyEnumeratorShape; - js::EmptyShape *emptyWithShape; + /* Set of all unowned base shapes in the compartment. */ + js::BaseShapeSet baseShapes; + void sweepBaseShapeTable(JSContext *cx); - typedef js::HashSet, - js::SystemAllocPolicy> EmptyShapeSet; + /* Set of initial shapes in the compartment. */ + js::InitialShapeSet initialShapes; + void sweepInitialShapeTable(JSContext *cx); - EmptyShapeSet emptyShapes; + /* Set of default 'new' or lazy types in the compartment. */ + js::types::TypeObjectSet newTypeObjects; + js::types::TypeObjectSet lazyTypeObjects; + void sweepNewTypeObjectTable(JSContext *cx, js::types::TypeObjectSet &table); - /* - * Initial shapes given to RegExp and String objects, encoding the initial - * sets of built-in instance properties and the fixed slots where they must - * be stored (see JSObject::JSSLOT_(REGEXP|STRING)_*). Later property - * additions may cause these shapes to not be used by a RegExp or String - * (even along the entire shape parent chain, should the object go into - * dictionary mode). But because all the initial properties are - * non-configurable, they will always map to fixed slots. - */ - const js::Shape *initialRegExpShape; - const js::Shape *initialStringShape; + js::types::TypeObject *emptyTypeObject; + + /* Get the default 'new' type for objects with a NULL prototype. */ + inline js::types::TypeObject *getEmptyType(JSContext *cx); - bool debugMode; // true iff debug mode on - JSCList scripts; // scripts in this compartment + js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto); - JSC::ExecutableAllocator *regExpAllocator; + /* Cache to speed up object creation. */ + js::NewObjectCache newObjectCache; + private: + enum { DebugFromC = 1, DebugFromJS = 2 }; + + uintN debugModeBits; // see debugMode() below + + /* + * Malloc counter to measure memory pressure for GC scheduling. It runs + * from gcMaxMallocBytes down to zero. + */ + volatile ptrdiff_t gcMallocBytes; + + public: js::NativeIterCache nativeIterCache; typedef js::Maybe LazyToSourceCache; LazyToSourceCache toSourceCache; + js::ScriptFilenameTable scriptFilenameTable; + JSCompartment(JSRuntime *rt); ~JSCompartment(); - bool init(); + bool init(JSContext *cx); /* Mark cross-compartment wrappers. */ void markCrossCompartmentWrappers(JSTracer *trc); bool wrap(JSContext *cx, js::Value *vp); bool wrap(JSContext *cx, JSString **strp); + bool wrap(JSContext *cx, js::HeapPtrString *strp); bool wrap(JSContext *cx, JSObject **objp); bool wrapId(JSContext *cx, jsid *idp); bool wrap(JSContext *cx, js::PropertyOp *op); @@ -491,16 +324,24 @@ struct JS_FRIEND_API(JSCompartment) { bool wrap(JSContext *cx, js::PropertyDescriptor *desc); bool wrap(JSContext *cx, js::AutoIdVector &props); - void sweep(JSContext *cx, uint32 releaseInterval); + void markTypes(JSTracer *trc); + void sweep(JSContext *cx, bool releaseTypes); void purge(JSContext *cx); - void finishArenaLists(); - void finalizeObjectArenaLists(JSContext *cx, JSGCInvocationKind gckind); - void finalizeStringArenaLists(JSContext *cx, JSGCInvocationKind gckind); - void finalizeShapeArenaLists(JSContext *cx, JSGCInvocationKind gckind); - bool arenaListsAreEmpty(); - void setGCLastBytes(size_t lastBytes); - void reduceGCTriggerBytes(uint32 amount); + void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); + void reduceGCTriggerBytes(size_t amount); + + void resetGCMallocBytes(); + void setGCMaxMallocBytes(size_t value); + void updateMallocCounter(size_t nbytes) { + ptrdiff_t oldCount = gcMallocBytes; + ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); + gcMallocBytes = newCount; + if (JS_UNLIKELY(newCount <= 0 && oldCount > 0)) + onTooMuchMalloc(); + } + + void onTooMuchMalloc(); js::DtoaCache dtoaCache; @@ -509,86 +350,60 @@ struct JS_FRIEND_API(JSCompartment) { js::MathCache *allocMathCache(JSContext *cx); - typedef js::HashMap, - js::SystemAllocPolicy> BackEdgeMap; - - BackEdgeMap backEdgeTable; + /* + * Weak reference to each global in this compartment that is a debuggee. + * Each global has its own list of debuggers. + */ + js::GlobalObjectSet debuggees; + private: JSCompartment *thisForCtor() { return this; } + public: js::MathCache *getMathCache(JSContext *cx) { return mathCache ? mathCache : allocMathCache(cx); } - size_t backEdgeCount(jsbytecode *pc) const; - size_t incBackEdgeCount(jsbytecode *pc); -}; + /* + * There are dueling APIs for debug mode. It can be enabled or disabled via + * JS_SetDebugModeForCompartment. It is automatically enabled and disabled + * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set + * if the C API wants debug mode and the DebugFromJS bit set if debuggees + * is non-empty. + */ + bool debugMode() const { return !!debugModeBits; } -#define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC) -#define JS_PROPERTY_TREE(cx) ((cx)->compartment->propertyTree) + /* + * True if any scripts from this compartment are on the JS stack in the + * calling thread. cx is a context in the calling thread, and it is assumed + * that no other thread is using this compartment. + */ + bool hasScriptsOnStack(JSContext *cx); -#ifdef DEBUG -#define JS_COMPARTMENT_METER(x) x -#else -#define JS_COMPARTMENT_METER(x) -#endif + private: + /* This is called only when debugMode() has just toggled. */ + void updateForDebugMode(JSContext *cx); -/* - * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current - * thread, regardless of whether cx is the context in which that trace is - * executing. cx must be a context on the current thread. - */ -static inline bool -JS_ON_TRACE(JSContext *cx) -{ -#ifdef JS_TRACER - if (JS_THREAD_DATA(cx)->onTraceCompartment) - return JS_THREAD_DATA(cx)->onTraceCompartment->traceMonitor.ontrace(); -#endif - return false; -} + public: + js::GlobalObjectSet &getDebuggees() { return debuggees; } + bool addDebuggee(JSContext *cx, js::GlobalObject *global); + void removeDebuggee(JSContext *cx, js::GlobalObject *global, + js::GlobalObjectSet::Enum *debuggeesEnum = NULL); + bool setDebugModeFromC(JSContext *cx, bool b); -#ifdef JS_TRACER -static inline js::TraceMonitor * -JS_TRACE_MONITOR_ON_TRACE(JSContext *cx) -{ - JS_ASSERT(JS_ON_TRACE(cx)); - return &JS_THREAD_DATA(cx)->onTraceCompartment->traceMonitor; -} + void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler); + void clearTraps(JSContext *cx); -/* - * Only call this directly from the interpreter loop or the method jit. - * Otherwise, we may get the wrong compartment, and thus the wrong - * TraceMonitor. - */ -static inline js::TraceMonitor * -JS_TRACE_MONITOR_FROM_CONTEXT(JSContext *cx) -{ - return &cx->compartment->traceMonitor; -} -#endif + private: + void sweepBreakpoints(JSContext *cx); -static inline js::TraceRecorder * -TRACE_RECORDER(JSContext *cx) -{ -#ifdef JS_TRACER - if (JS_THREAD_DATA(cx)->recordingCompartment) - return JS_THREAD_DATA(cx)->recordingCompartment->traceMonitor.recorder; -#endif - return NULL; -} + js::GCMarker *createBarrierTracer(); -static inline js::LoopProfile * -TRACE_PROFILER(JSContext *cx) -{ -#ifdef JS_TRACER - if (JS_THREAD_DATA(cx)->profilingCompartment) - return JS_THREAD_DATA(cx)->profilingCompartment->traceMonitor.profile; -#endif - return NULL; -} + public: + js::WatchpointMap *watchpointMap; +}; + +#define JS_PROPERTY_TREE(cx) ((cx)->compartment->propertyTree) namespace js { static inline MathCache * @@ -598,11 +413,12 @@ GetMathCache(JSContext *cx) } } -#ifdef DEBUG -# define EVAL_CACHE_METER(x) (cx->compartment->evalCacheMeter.x++) -#else -# define EVAL_CACHE_METER(x) ((void) 0) -#endif +inline void +JSContext::setCompartment(JSCompartment *compartment) +{ + this->compartment = compartment; + this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false; +} #ifdef _MSC_VER #pragma warning(pop) @@ -615,15 +431,19 @@ class PreserveCompartment { JSContext *cx; private: JSCompartment *oldCompartment; + bool oldInferenceEnabled; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; oldCompartment = cx->compartment; + oldInferenceEnabled = cx->inferenceEnabled; } ~PreserveCompartment() { + /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */ cx->compartment = oldCompartment; + cx->inferenceEnabled = oldInferenceEnabled; } }; @@ -634,14 +454,14 @@ class SwitchToCompartment : public PreserveCompartment { : PreserveCompartment(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; - cx->compartment = newCompartment; + cx->setCompartment(newCompartment); } SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM) : PreserveCompartment(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; - cx->compartment = target->getCompartment(); + cx->setCompartment(target->compartment()); } JS_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -663,6 +483,72 @@ class AssertCompartmentUnchanged { } }; -} +class AutoCompartment +{ + public: + JSContext * const context; + JSCompartment * const origin; + JSObject * const target; + JSCompartment * const destination; + private: + Maybe frame; + bool entered; + + public: + AutoCompartment(JSContext *cx, JSObject *target); + ~AutoCompartment(); + + bool enter(); + void leave(); + + private: + AutoCompartment(const AutoCompartment &) MOZ_DELETE; + AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE; +}; + +/* + * Use this to change the behavior of an AutoCompartment slightly on error. If + * the exception happens to be an Error object, copy it to the origin compartment + * instead of wrapping it. + */ +class ErrorCopier +{ + AutoCompartment ∾ + JSObject *scope; + + public: + ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(scope) { + JS_ASSERT(scope->compartment() == ac.origin); + } + ~ErrorCopier(); +}; + +class CompartmentsIter { + private: + JSCompartment **it, **end; + + public: + CompartmentsIter(JSRuntime *rt) { + it = rt->compartments.begin(); + end = rt->compartments.end(); + } + + bool done() const { return it == end; } + + void next() { + JS_ASSERT(!done()); + it++; + } + + JSCompartment *get() const { + JS_ASSERT(!done()); + return *it; + } + + operator JSCompartment *() const { return get(); } + JSCompartment *operator->() const { return get(); } +}; + +} /* namespace js */ #endif /* jscompartment_h___ */ diff --git a/deps/mozjs/js/src/jscompat.h b/deps/mozjs/js/src/jscompat.h index 5ecda6e8134..bee2977dc05 100644 --- a/deps/mozjs/js/src/jscompat.h +++ b/deps/mozjs/js/src/jscompat.h @@ -45,11 +45,7 @@ * the NSPR typedef names may be. */ #include "jstypes.h" -#include "jslong.h" typedef JSIntn intN; typedef JSUintn uintN; -typedef JSUword jsuword; -typedef JSWord jsword; -typedef float float32; #endif /* jscompat_h___ */ diff --git a/deps/mozjs/js/src/jsconfig.mk b/deps/mozjs/js/src/jsconfig.mk deleted file mode 100644 index 0ee3b7473ee..00000000000 --- a/deps/mozjs/js/src/jsconfig.mk +++ /dev/null @@ -1,169 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998-1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -ifndef OBJDIR - ifdef OBJDIR_NAME - OBJDIR = $(OBJDIR_NAME) - endif -endif - -NSPR_VERSION = v4.0 -NSPR_LIBSUFFIX = 4 - -NSPR_LOCAL = $(MOZ_DEPTH)/dist/$(OBJDIR)/nspr -NSPR_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) -NSPR_OBJDIR = $(OBJDIR) -ifeq ($(OS_ARCH), SunOS) - NSPR_OBJDIR := $(subst _sparc,,$(NSPR_OBJDIR)) -endif -ifeq ($(OS_ARCH), Linux) - LINUX_REL := $(shell uname -r) - ifneq (,$(findstring 2.0,$(LINUX_REL))) - NSPR_OBJDIR := $(subst _All,2.0_x86_glibc_PTH,$(NSPR_OBJDIR)) - else - NSPR_OBJDIR := $(subst _All,2.2_x86_glibc_PTH,$(NSPR_OBJDIR)) - endif -endif -ifeq ($(OS_ARCH), AIX) - NSPR_OBJDIR := $(subst 4.1,4.2,$(NSPR_OBJDIR)) -endif -ifeq ($(OS_CONFIG), IRIX6.2) - NSPR_OBJDIR := $(subst 6.2,6.2_n32_PTH,$(NSPR_OBJDIR)) -endif -ifeq ($(OS_CONFIG), IRIX6.5) - NSPR_OBJDIR := $(subst 6.5,6.5_n32_PTH,$(NSPR_OBJDIR)) -endif -ifeq ($(OS_ARCH), WINNT) - ifeq ($(OBJDIR), WIN32_D.OBJ) - NSPR_OBJDIR = WINNT4.0_DBG.OBJ - endif - ifeq ($(OBJDIR), WIN32_O.OBJ) - NSPR_OBJDIR = WINNT4.0_OPT.OBJ - endif -endif -NSPR_SHARED = /share/builds/components/nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) -ifeq ($(OS_ARCH), WINNT) - NSPR_SHARED = nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) -endif -NSPR_VERSIONFILE = $(NSPR_LOCAL)/Version -NSPR_CURVERSION := $(shell cat $(NSPR_VERSIONFILE) 2>/dev/null) - -get_nspr: - @echo "Grabbing NSPR component..." -ifeq ($(NSPR_VERSION), $(NSPR_CURVERSION)) - @echo "No need, NSPR is up to date in this tree (ver=$(NSPR_VERSION))." -else - mkdir -p $(NSPR_LOCAL) - mkdir -p $(NSPR_DIST) - ifneq ($(OS_ARCH), WINNT) - cp $(NSPR_SHARED)/*.jar $(NSPR_LOCAL) - else - sh $(MOZ_DEPTH)/../reltools/compftp.sh $(NSPR_SHARED) $(NSPR_LOCAL) *.jar - endif - unzip -o $(NSPR_LOCAL)/mdbinary.jar -d $(NSPR_DIST) - mkdir -p $(NSPR_DIST)/include - unzip -o $(NSPR_LOCAL)/mdheader.jar -d $(NSPR_DIST)/include - rm -rf $(NSPR_DIST)/META-INF - rm -rf $(NSPR_DIST)/include/META-INF - echo $(NSPR_VERSION) > $(NSPR_VERSIONFILE) -endif - -SHIP_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) -SHIP_DIR = $(SHIP_DIST)/SHIP - -SHIP_LIBS = libjs.$(SO_SUFFIX) libjs.a -ifeq ($(OS_ARCH), WINNT) - SHIP_LIBS = js32.dll js32.lib -endif -SHIP_LIBS += $(LCJAR) -SHIP_LIBS := $(addprefix $(SHIP_DIST)/lib/, $(SHIP_LIBS)) - -SHIP_INCS = js*.h prmjtime.h resource.h *.msg *.tbl -SHIP_INCS := $(addprefix $(SHIP_DIST)/include/, $(SHIP_INCS)) - -SHIP_BINS = js -ifeq ($(OS_ARCH), WINNT) - SHIP_BINS := $(addsuffix .exe, $(SHIP_BINS)) -endif -SHIP_BINS := $(addprefix $(SHIP_DIST)/bin/, $(SHIP_BINS)) - -ifdef BUILD_OPT - JSREFJAR = jsref_opt.jar -else -ifdef BUILD_IDG - JSREFJAR = jsref_idg.jar -else - JSREFJAR = jsref_dbg.jar -endif -endif - -ship: - mkdir -p $(SHIP_DIR)/$(LIBDIR) - mkdir -p $(SHIP_DIR)/include - mkdir -p $(SHIP_DIR)/bin - cp $(SHIP_LIBS) $(SHIP_DIR)/$(LIBDIR) - cp $(SHIP_INCS) $(SHIP_DIR)/include - cp $(SHIP_BINS) $(SHIP_DIR)/bin - cd $(SHIP_DIR); \ - zip -r $(JSREFJAR) bin lib include -ifdef BUILD_SHIP - cp $(SHIP_DIR)/$(JSREFJAR) $(BUILD_SHIP) -endif - -CWD = $(shell pwd) -shipSource: $(SHIP_DIR)/jsref_src.lst .FORCE - mkdir -p $(SHIP_DIR) - cd $(MOZ_DEPTH)/.. ; \ - zip $(CWD)/$(SHIP_DIR)/jsref_src.jar -@ < $(CWD)/$(SHIP_DIR)/jsref_src.lst -ifdef BUILD_SHIP - cp $(SHIP_DIR)/jsref_src.jar $(BUILD_SHIP) -endif - -JSREFSRCDIRS := $(shell cat $(DEPTH)/SpiderMonkey.rsp) -$(SHIP_DIR)/jsref_src.lst: .FORCE - mkdir -p $(SHIP_DIR) - rm -f $@ - touch $@ - for d in $(JSREFSRCDIRS); do \ - cd $(MOZ_DEPTH)/..; \ - ls -1 -d $$d | grep -v CVS | grep -v \.OBJ >> $(CWD)/$@; \ - cd $(CWD); \ - done - -.FORCE: diff --git a/deps/mozjs/js/src/jscpucfg.cpp b/deps/mozjs/js/src/jscpucfg.cpp deleted file mode 100644 index 028def676f5..00000000000 --- a/deps/mozjs/js/src/jscpucfg.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Roland Mainz - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Generate CPU-specific bit-size and similar #defines. - */ -#include -#include - -#if defined(CROSS_COMPILE) && !defined(FORCE_BIG_ENDIAN) && !defined(FORCE_LITTLE_ENDIAN) -#include -#endif - -/************************************************************************/ - -int main(int argc, char **argv) -{ - printf("#ifndef js_cpucfg___\n"); - printf("#define js_cpucfg___\n\n"); - - printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); - -#ifdef CROSS_COMPILE -#if defined(__APPLE__) && !defined(FORCE_BIG_ENDIAN) && !defined(FORCE_LITTLE_ENDIAN) - /* - * Darwin NSPR uses the same MDCPUCFG (_darwin.cfg) for multiple - * processors, and determines which processor to configure for based - * on compiler predefined macros. We do the same thing here. - */ - printf("#ifdef __LITTLE_ENDIAN__\n"); - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n"); - printf("#else\n"); - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n"); - printf("#endif\n\n"); -#elif defined(IS_LITTLE_ENDIAN) || defined(FORCE_LITTLE_ENDIAN) - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); -#elif defined(IS_BIG_ENDIAN) || defined(FORCE_BIG_ENDIAN) - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); -#else -#error "Endianess not defined." -#endif - -#else - - /* - * We don't handle PDP-endian or similar orders: if a short is big-endian, - * so must int and long be big-endian for us to generate the IS_BIG_ENDIAN - * #define and the IS_LITTLE_ENDIAN #undef. - */ - { - int big_endian = 0, little_endian = 0, ntests = 0; - - if (sizeof(short) == 2) { - /* force |volatile| here to get rid of any compiler optimisations - * (var in register etc.) which may be appiled to |auto| vars - - * even those in |union|s... - * (|static| is used to get the same functionality for compilers - * which do not honor |volatile|...). - */ - volatile static union { - short i; - char c[2]; - } u; - - u.i = 0x0102; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02); - little_endian += (u.c[0] == 0x02 && u.c[1] == 0x01); - ntests++; - } - - if (sizeof(int) == 4) { - /* force |volatile| here ... */ - volatile static union { - int i; - char c[4]; - } u; - - u.i = 0x01020304; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04); - little_endian += (u.c[0] == 0x04 && u.c[1] == 0x03 && - u.c[2] == 0x02 && u.c[3] == 0x01); - ntests++; - } - - if (sizeof(long) == 8) { - /* force |volatile| here ... */ - volatile static union { - long i; - char c[8]; - } u; - - /* - * Write this as portably as possible: avoid 0x0102030405060708L - * and <<= 32. - */ - u.i = 0x01020304; - u.i <<= 16, u.i <<= 16; - u.i |= 0x05060708; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04 && - u.c[4] == 0x05 && u.c[5] == 0x06 && - u.c[6] == 0x07 && u.c[7] == 0x08); - little_endian += (u.c[0] == 0x08 && u.c[1] == 0x07 && - u.c[2] == 0x06 && u.c[3] == 0x05 && - u.c[4] == 0x04 && u.c[5] == 0x03 && - u.c[6] == 0x02 && u.c[7] == 0x01); - ntests++; - } - - if (big_endian && big_endian == ntests) { - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); - } else if (little_endian && little_endian == ntests) { - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); - } else { - fprintf(stderr, "%s: unknown byte order" - "(big_endian=%d, little_endian=%d, ntests=%d)!\n", - argv[0], big_endian, little_endian, ntests); - return EXIT_FAILURE; - } - } - -#endif /* CROSS_COMPILE */ - - // PA-RISC is the only platform we try to support on which the stack - // grows towards higher addresses. Trying to detect it here has - // historically led to portability problems, which aren't worth it - // given the near consensus on stack growth direction. - printf("#ifdef __hppa\n" - "# define JS_STACK_GROWTH_DIRECTION (1)\n" - "#else\n" - "# define JS_STACK_GROWTH_DIRECTION (-1)\n" - "#endif\n"); - - printf("#endif /* js_cpucfg___ */\n"); - - return EXIT_SUCCESS; -} - diff --git a/deps/mozjs/js/src/jscpucfg.h b/deps/mozjs/js/src/jscpucfg.h index fa5ad51b8cb..5fd8415bdd3 100644 --- a/deps/mozjs/js/src/jscpucfg.h +++ b/deps/mozjs/js/src/jscpucfg.h @@ -42,50 +42,113 @@ #define JS_HAVE_LONG_LONG -#if defined(XP_WIN) || defined(XP_OS2) - #if defined(_WIN64) -#if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 8L -#define JS_BITS_PER_WORD_LOG2 6 -#define JS_ALIGN_OF_POINTER 8L -#else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ -#error "CPU type is unknown" -#endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ +# if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# define JS_BYTES_PER_DOUBLE 8 +# define JS_BYTES_PER_WORD 8 +# define JS_BITS_PER_WORD_LOG2 6 +# define JS_ALIGN_OF_POINTER 8 +# else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ +# error "CPU type is unknown" +# endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ #elif defined(_WIN32) || defined(XP_OS2) -#ifdef __WATCOMC__ -#define HAVE_VA_LIST_AS_ARRAY 1 -#endif - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 4L -#define JS_BITS_PER_WORD_LOG2 5 -#define JS_ALIGN_OF_POINTER 4L - -#endif /* _WIN32 || XP_OS2 */ - -#elif defined(XP_UNIX) - -#error "This file is supposed to be auto-generated on UNIX platforms, but the" -#error "static version for Mac and Windows platforms is being used." -#error "Something's probably wrong with paths/headers/dependencies/Makefiles." - -#else - -#error "Must define one of XP_OS2, XP_WIN, or XP_UNIX" - +# ifdef __WATCOMC__ +# define HAVE_VA_LIST_AS_ARRAY 1 +# endif + +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# define JS_BYTES_PER_DOUBLE 8 +# define JS_BYTES_PER_WORD 4 +# define JS_BITS_PER_WORD_LOG2 5 +# define JS_ALIGN_OF_POINTER 4 + +#elif defined(__APPLE__) +# if __LITTLE_ENDIAN__ +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# elif __BIG_ENDIAN__ +# undef IS_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 1 +# endif + +#elif defined(JS_HAVE_ENDIAN_H) +# include + +# if defined(__BYTE_ORDER) +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# elif __BYTE_ORDER == __BIG_ENDIAN +# undef IS_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 1 +# endif +# else /* !defined(__BYTE_ORDER) */ +# error "endian.h does not define __BYTE_ORDER. Cannot determine endianness." +# endif + +/* BSDs */ +#elif defined(JS_HAVE_MACHINE_ENDIAN_H) +# include +# include + +# if defined(_BYTE_ORDER) +# if _BYTE_ORDER == _LITTLE_ENDIAN +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# elif _BYTE_ORDER == _BIG_ENDIAN +# undef IS_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 1 +# endif +# else /* !defined(_BYTE_ORDER) */ +# error "machine/endian.h does not define _BYTE_ORDER. Cannot determine endianness." +# endif + +#elif defined(JS_HAVE_SYS_ISA_DEFS_H) +# include + +# if defined(_BIG_ENDIAN) +# undef IS_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 1 +# elif defined(_LITTLE_ENDIAN) +# define IS_LITTLE_ENDIAN 1 +# undef IS_BIG_ENDIAN +# else /* !defined(_LITTLE_ENDIAN) */ +# error "sys/isa_defs.h does not define _BIG_ENDIAN or _LITTLE_ENDIAN. Cannot determine endianness." +# endif +# if !defined(JS_STACK_GROWTH_DIRECTION) +# if defined(_STACK_GROWS_UPWARD) +# define JS_STACK_GROWTH_DIRECTION (1) +# elif defined(_STACK_GROWS_DOWNWARD) +# define JS_STACK_GROWTH_DIRECTION (-1) +# endif +# endif + +#elif defined(__sparc) || defined(__sparc__) || \ + defined(_POWER) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__hppa) || \ + defined(_MIPSEB) || defined(_BIG_ENDIAN) +/* IA64 running HP-UX will have _BIG_ENDIAN defined. + * IA64 running Linux will have endian.h and be handled above. + */ +# undef IS_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 1 + +#else /* !defined(__sparc) && !defined(__sparc__) && ... */ +# error "Cannot determine endianness of your platform. Please add support to jscpucfg.h." #endif #ifndef JS_STACK_GROWTH_DIRECTION -#define JS_STACK_GROWTH_DIRECTION (-1) +# ifdef __hppa +# define JS_STACK_GROWTH_DIRECTION (1) +# else +# define JS_STACK_GROWTH_DIRECTION (-1) +# endif #endif #endif /* js_cpucfg___ */ diff --git a/deps/mozjs/js/src/jscrashformat.h b/deps/mozjs/js/src/jscrashformat.h new file mode 100644 index 00000000000..6bd586febd9 --- /dev/null +++ b/deps/mozjs/js/src/jscrashformat.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jscrashformat_h___ +#define jscrashformat_h___ + +#include + +namespace js { +namespace crash { + +const static int crash_cookie_len = 16; +const static char crash_cookie[crash_cookie_len] = "*J*S*CRASHDATA*"; + +/* These values are used for CrashHeader::id. */ +enum { + JS_CRASH_STACK_GC = 0x400, + JS_CRASH_STACK_ERROR = 0x401, + JS_CRASH_RING = 0x800 +}; + +/* + * All the data here will be stored directly in the minidump, so we use + * platform-independent types. We also ensure that the size of every field is a + * multiple of 8 bytes, to guarantee that they won't be padded. + */ + +struct CrashHeader +{ + char cookie[crash_cookie_len]; + + /* id of the crash data, chosen from the enum above. */ + uint64_t id; + + CrashHeader(uint64_t id) : id(id) { memcpy(cookie, crash_cookie, crash_cookie_len); } +}; + +struct CrashRegisters +{ + uint64_t ip, sp, bp; +}; + +const static int crash_buffer_size = 32 * 1024; + +struct CrashStack +{ + CrashStack(uint64_t id) : header(id) {} + + CrashHeader header; + uint64_t snaptime; /* Unix time when the stack was snapshotted. */ + CrashRegisters regs; /* Register contents for the snapshot. */ + uint64_t stack_base; /* Base address of stack at the time of snapshot. */ + uint64_t stack_len; /* Extent of the stack. */ + char stack[crash_buffer_size]; /* Contents of the stack. */ +}; + +struct CrashRing +{ + CrashRing(uint64_t id) : header(id), offset(0) { memset(buffer, 0, sizeof(buffer)); } + + CrashHeader header; + uint64_t offset; /* Next byte to be written in the buffer. */ + char buffer[crash_buffer_size]; +}; + +/* These are the tag values for each entry in the CrashRing. */ +enum { + JS_CRASH_TAG_GC = 0x200 +}; + +} /* namespace crash */ +} /* namespace js */ + +#endif diff --git a/deps/mozjs/js/src/jscrashreport.cpp b/deps/mozjs/js/src/jscrashreport.cpp new file mode 100644 index 00000000000..d5148d62f27 --- /dev/null +++ b/deps/mozjs/js/src/jscrashreport.cpp @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jscntxt.h" +#include "jscrashreport.h" +#include "jscrashformat.h" + +#include + +namespace js { +namespace crash { + +const static int stack_snapshot_max_size = 32768; + +#if defined(XP_WIN) + +#include + +static bool +GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) +{ + /* Try to figure out how big the stack is. */ + char dummy; + MEMORY_BASIC_INFORMATION info; + if (VirtualQuery(reinterpret_cast(&dummy), &info, sizeof(info)) == 0) + return false; + if (info.State != MEM_COMMIT) + return false; + + /* 256 is a fudge factor to account for the rest of GetStack's frame. */ + uint64_t p = uint64_t(&dummy) - 256; + uint64_t len = stack_snapshot_max_size; + + if (p + len > uint64_t(info.BaseAddress) + info.RegionSize) + len = uint64_t(info.BaseAddress) + info.RegionSize - p; + + if (len > size) + len = size; + + *stack = p; + *stack_len = len; + + /* Get the register state. */ +#if defined(_MSC_VER) && JS_BITS_PER_WORD == 32 + /* ASM version for win2k that doesn't support RtlCaptureContext */ + uint32_t vip, vsp, vbp; + __asm { + Label: + mov [vbp], ebp; + mov [vsp], esp; + mov eax, [Label]; + mov [vip], eax; + } + regs->ip = vip; + regs->sp = vsp; + regs->bp = vbp; +#else + CONTEXT context; + RtlCaptureContext(&context); +#if JS_BITS_PER_WORD == 32 + regs->ip = context.Eip; + regs->sp = context.Esp; + regs->bp = context.Ebp; +#else + regs->ip = context.Rip; + regs->sp = context.Rsp; + regs->bp = context.Rbp; +#endif +#endif + + js_memcpy(buffer, (void *)p, len); + + return true; +} + +#elif 0 + +#include +#include +#include + +static bool +GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) +{ + /* 256 is a fudge factor to account for the rest of GetStack's frame. */ + char dummy; + uint64_t p = uint64_t(&dummy) - 256; + uint64_t pgsz = getpagesize(); + uint64_t len = stack_snapshot_max_size; + p &= ~(pgsz - 1); + + /* Try to figure out how big the stack is. */ + while (len > 0) { + if (mlock((const void *)p, len) == 0) { + munlock((const void *)p, len); + break; + } + len -= pgsz; + } + + if (len > size) + len = size; + + *stack = p; + *stack_len = len; + + /* Get the register state. */ + ucontext_t context; + if (getcontext(&context) != 0) + return false; + +#if JS_BITS_PER_WORD == 64 + regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_RSP]; + regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_RBP]; + regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_RIP]; +#elif JS_BITS_PER_WORD == 32 + regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_ESP]; + regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_EBP]; + regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_EIP]; +#endif + + js_memcpy(buffer, (void *)p, len); + + return true; +} + +#else + +static bool +GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) +{ + return false; +} + +#endif + +class Stack : private CrashStack +{ +public: + Stack(uint64_t id); + + bool snapshot(); +}; + +Stack::Stack(uint64_t id) + : CrashStack(id) +{ +} + +bool +Stack::snapshot() +{ + snaptime = time(NULL); + return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack)); +} + +class Ring : private CrashRing +{ +public: + Ring(uint64_t id); + + void push(uint64_t tag, void *data, size_t size); + +private: + size_t bufferSize() { return crash_buffer_size; } + void copyBytes(void *data, size_t size); +}; + +Ring::Ring(uint64_t id) + : CrashRing(id) +{ +} + +void +Ring::push(uint64_t tag, void *data, size_t size) +{ + uint64_t t = time(NULL); + + copyBytes(&tag, sizeof(uint64_t)); + copyBytes(&t, sizeof(uint64_t)); + copyBytes(data, size); + uint64_t mysize = size; + copyBytes(&mysize, sizeof(uint64_t)); +} + +void +Ring::copyBytes(void *data, size_t size) +{ + if (size >= bufferSize()) + size = bufferSize(); + + if (offset + size > bufferSize()) { + size_t first = bufferSize() - offset; + size_t second = size - first; + js_memcpy(&buffer[offset], data, first); + js_memcpy(buffer, (char *)data + first, second); + offset = second; + } else { + js_memcpy(&buffer[offset], data, size); + offset += size; + } +} + +static bool gInitialized; + +static Stack gGCStack(JS_CRASH_STACK_GC); +static Stack gErrorStack(JS_CRASH_STACK_ERROR); +static Ring gRingBuffer(JS_CRASH_RING); + +void +SnapshotGCStack() +{ + if (gInitialized) + gGCStack.snapshot(); +} + +void +SnapshotErrorStack() +{ + if (gInitialized) + gErrorStack.snapshot(); +} + +void +SaveCrashData(uint64_t tag, void *ptr, size_t size) +{ + if (gInitialized) + gRingBuffer.push(tag, ptr, size); +} + +} /* namespace crash */ +} /* namespace js */ + +using namespace js; +using namespace js::crash; + +JS_PUBLIC_API(void) +JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) +{ +#ifdef JS_CRASH_DIAGNOSTICS + if (!gInitialized) { + gInitialized = true; + (*callback)(&gGCStack, sizeof(gGCStack)); + (*callback)(&gErrorStack, sizeof(gErrorStack)); + (*callback)(&gRingBuffer, sizeof(gRingBuffer)); + } +#endif +} + diff --git a/deps/mozjs/js/src/jscrashreport.h b/deps/mozjs/js/src/jscrashreport.h new file mode 100644 index 00000000000..e01852241ae --- /dev/null +++ b/deps/mozjs/js/src/jscrashreport.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jscrashreport_h___ +#define jscrashreport_h___ + +#include "jstypes.h" +#include "jsutil.h" + +namespace js { +namespace crash { + +void +SnapshotGCStack(); + +void +SnapshotErrorStack(); + +void +SaveCrashData(uint64_t tag, void *ptr, size_t size); + +template +class StackBuffer { + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + volatile unsigned char buffer[size + 4]; + + public: + StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + + buffer[0] = marker; + buffer[1] = '['; + + for (size_t i = 0; i < size; i++) { + if (data) + buffer[i + 2] = ((unsigned char *)data)[i]; + else + buffer[i + 2] = 0; + } + + buffer[size - 2] = ']'; + buffer[size - 1] = marker; + } +}; + +} /* namespace crash */ +} /* namespace js */ + +#endif /* jscrashreport_h___ */ diff --git a/deps/mozjs/js/src/jsdate.cpp b/deps/mozjs/js/src/jsdate.cpp index 8ea1188ed5f..8d00b9952a0 100644 --- a/deps/mozjs/js/src/jsdate.cpp +++ b/deps/mozjs/js/src/jsdate.cpp @@ -56,26 +56,34 @@ #include #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsprf.h" #include "prmjtime.h" #include "jsutil.h" #include "jsapi.h" #include "jsversion.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsdate.h" #include "jsinterp.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" +#include "jslibmath.h" +#include "vm/GlobalObject.h" + +#include "jsinferinlines.h" #include "jsobjinlines.h" +#include "jsstrinlines.h" #include "vm/Stack-inl.h" +using namespace mozilla; using namespace js; +using namespace js::types; /* * The JS 'Date' object is patterned after the Java 'Date' object. @@ -422,8 +430,8 @@ DaylightSavingTA(jsdouble t, JSContext *cx) t = MakeDate(day, TimeWithinDay(t)); } - int64 timeMilliseconds = static_cast(t); - int64 offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx); + int64_t timeMilliseconds = static_cast(t); + int64_t offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx); return static_cast(offsetMilliseconds); } @@ -487,21 +495,30 @@ msFromTime(jsdouble t) * end of ECMA 'support' functions */ +static JSBool +date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp) +{ + JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); + JS_ASSERT(obj->isDate()); + + return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp); +} + /* * Other Support routines and definitions */ -Class js_DateClass = { +Class js::DateClass = { js_Date_str, JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + date_convert }; /* for use by date_parse */ @@ -540,9 +557,8 @@ date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, while (count > 0 && s1[s1off] && s2[s2off]) { if (ignoreCase) { - if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { + if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off])) break; - } } else { if ((jschar)s1[s1off] != s2[s2off]) { break; @@ -579,16 +595,16 @@ date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, #define MAXARGS 7 static JSBool -date_msecFromArgs(JSContext *cx, uintN argc, Value *argv, jsdouble *rval) +date_msecFromArgs(JSContext *cx, CallArgs args, double *rval) { uintN loop; jsdouble array[MAXARGS]; jsdouble msec_time; for (loop = 0; loop < MAXARGS; loop++) { - if (loop < argc) { + if (loop < args.length()) { jsdouble d; - if (!ValueToNumber(cx, argv[loop], &d)) + if (!ToNumber(cx, args[loop], &d)) return JS_FALSE; /* return NaN if any arg is not finite */ if (!JSDOUBLE_IS_FINITE(d)) { @@ -621,14 +637,15 @@ date_msecFromArgs(JSContext *cx, uintN argc, Value *argv, jsdouble *rval) static JSBool date_UTC(JSContext *cx, uintN argc, Value *vp) { - jsdouble msec_time; + CallArgs args = CallArgsFromVp(argc, vp); - if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time)) + jsdouble msec_time; + if (!date_msecFromArgs(cx, args, &msec_time)) return JS_FALSE; msec_time = TIMECLIP(msec_time); - vp->setNumber(msec_time); + args.rval().setNumber(msec_time); return JS_TRUE; } @@ -833,7 +850,12 @@ date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx) tzMul = -1; ++i; NEED_NDIGITS(2, tzHour); - NEED(':'); + /* + * Non-standard extension to the ISO date format (permitted by ES5): + * allow "-0700" as a time zone offset, not just "-07:00". + */ + if (PEEK(':')) + ++i; NEED_NDIGITS(2, tzMin); } else { isLocalTime = JS_TRUE; @@ -1019,7 +1041,7 @@ date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx) } if (i <= st + 1) goto syntax; - for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;) + for (k = ArrayLength(wtb); --k >= 0;) if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { int action = ttb[k]; if (action != 0) { @@ -1167,7 +1189,7 @@ date_parse(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return true; } - str = js_ValueToString(cx, vp[2]); + str = ToString(cx, vp[2]); if (!str) return JS_FALSE; vp[2].setString(str); @@ -1198,30 +1220,6 @@ date_now(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } -#ifdef JS_TRACER -static jsdouble FASTCALL -date_now_tn(JSContext*) -{ - return NowAsMillis(); -} -#endif - -/* - * Get UTC time from the date object. Returns false if the object is not - * Date type. - */ -static JSBool -GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp) -{ - if (!obj->isDate()) { - if (vp) - ReportIncompatibleMethod(cx, vp, &js_DateClass); - return false; - } - *dp = obj->getDateUTCTime().toNumber(); - return true; -} - /* * Set UTC time to a given time and invalidate cached local time. */ @@ -1230,9 +1228,11 @@ SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL) { JS_ASSERT(obj->isDate()); - size_t slotCap = JS_MIN(obj->numSlots(), JSObject::DATE_CLASS_RESERVED_SLOTS); - for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < slotCap; ind++) - obj->getSlotRef(ind).setUndefined(); + for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; + ind < JSObject::DATE_CLASS_RESERVED_SLOTS; + ind++) { + obj->setSlot(ind, UndefinedValue()); + } obj->setDateUTCTime(DoubleValue(t)); if (vp) @@ -1259,12 +1259,6 @@ FillLocalTimes(JSContext *cx, JSObject *obj) jsdouble utcTime = obj->getDateUTCTime().toNumber(); - /* Make sure there are slots to store the cached information. */ - if (obj->numSlots() < JSObject::DATE_CLASS_RESERVED_SLOTS) { - if (!obj->growSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS)) - return false; - } - if (!JSDOUBLE_IS_FINITE(utcTime)) { for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < JSObject::DATE_CLASS_RESERVED_SLOTS; @@ -1299,8 +1293,8 @@ FillLocalTimes(JSContext *cx, JSObject *obj) obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year)); - uint64 yearTime = uint64(localTime - yearStartTime); - jsint yearSeconds = uint32(yearTime / 1000); + uint64_t yearTime = uint64_t(localTime - yearStartTime); + jsint yearSeconds = uint32_t(yearTime / 1000); jsint day = yearSeconds / jsint(SecondsPerDay); @@ -1390,36 +1384,27 @@ FillLocalTimes(JSContext *cx, JSObject *obj) } /* Cache the local times in obj, if necessary. */ -static inline JSBool -GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *time = NULL) +static inline bool +GetAndCacheLocalTime(JSContext *cx, JSObject *obj) { - if (!obj) - return false; - if (!obj->isDate()) { - if (vp) - ReportIncompatibleMethod(cx, vp, &js_DateClass); - return false; - } + JS_ASSERT(obj->isDate()); /* If the local time is undefined, we need to fill in the cached values. */ if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) { if (!FillLocalTimes(cx, obj)) return false; } - - if (time) - *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble(); - return true; } static inline bool -GetThisUTCTime(JSContext *cx, Value *vp, jsdouble *dp) +GetAndCacheLocalTime(JSContext *cx, JSObject *obj, double *time) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) + if (!obj || !GetAndCacheLocalTime(cx, obj)) return false; - return GetUTCTime(cx, obj, vp, dp); + + *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble(); + return true; } /* @@ -1428,30 +1413,37 @@ GetThisUTCTime(JSContext *cx, Value *vp, jsdouble *dp) static JSBool date_getTime(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; - vp->setNumber(result); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getTime, &DateClass, &ok); + if (!obj) + return ok; + + args.rval() = obj->getDateUTCTime(); return true; } static JSBool date_getYear(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getYear, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR); if (yearVal.isInt32()) { /* Follow ECMA-262 to the letter, contrary to IE JScript. */ jsint year = yearVal.toInt32() - 1900; - vp->setInt32(year); + args.rval().setInt32(year); } else { - *vp = yearVal; + args.rval() = yearVal; } return true; @@ -1460,168 +1452,210 @@ date_getYear(JSContext *cx, uintN argc, Value *vp) static JSBool date_getFullYear(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getFullYear, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) - return JS_FALSE; + if (!GetAndCacheLocalTime(cx, obj)) + return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR); - return JS_TRUE; + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR); + return true; } static JSBool date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCFullYear, &DateClass, &ok); + if (!obj) + return ok; + + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = YearFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } static JSBool date_getMonth(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getMonth, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH); return true; } static JSBool date_getUTCMonth(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMonth, &DateClass, &ok); + if (!obj) + return ok; + + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = MonthFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } static JSBool date_getDate(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getDate, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE); return true; } static JSBool date_getUTCDate(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDate, &DateClass, &ok); + if (!obj) + return ok; + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = DateFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } static JSBool date_getDay(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getDay, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY); return true; } static JSBool date_getUTCDay(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDay, &DateClass, &ok); + if (!obj) + return ok; + jsdouble result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = WeekDay(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } static JSBool date_getHours(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getHours, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS); return true; } static JSBool date_getUTCHours(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCHours, &DateClass, &ok); + if (!obj) + return ok; + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = HourFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return JS_TRUE; } static JSBool date_getMinutes(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getMinutes, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES); return true; } static JSBool date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMinutes, &DateClass, &ok); + if (!obj) + return ok; + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = MinFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } @@ -1630,14 +1664,17 @@ date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp) static JSBool date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCSeconds, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetAndCacheLocalTime(cx, obj, vp)) + if (!GetAndCacheLocalTime(cx, obj)) return false; - *vp = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS); + args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS); return true; } @@ -1646,30 +1683,35 @@ date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp) static JSBool date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp) { - jsdouble result; - if (!GetThisUTCTime(cx, vp, &result)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMilliseconds, &DateClass, &ok); + if (!obj) + return ok; + double result = obj->getDateUTCTime().toNumber(); if (JSDOUBLE_IS_FINITE(result)) result = msFromTime(result); - vp->setNumber(result); + args.rval().setNumber(result); return true; } static JSBool date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_getTimezoneOffset, &DateClass, &ok); if (!obj) - return false; + return ok; - jsdouble utctime; - if (!GetUTCTime(cx, obj, vp, &utctime)) - return false; + double utctime = obj->getDateUTCTime().toNumber(); - jsdouble localtime; - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime)) + double localtime; + if (!GetAndCacheLocalTime(cx, obj, &localtime)) return false; /* @@ -1677,57 +1719,48 @@ date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp) * appropriate for this time. This value would be a constant except for * daylight savings time. */ - jsdouble result = (utctime - localtime) / msPerMinute; - vp->setNumber(result); + double result = (utctime - localtime) / msPerMinute; + args.rval().setNumber(result); return true; } static JSBool date_setTime(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - if (!obj->isDate()) { - ReportIncompatibleMethod(cx, vp, &js_DateClass); - return false; - } + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok); + if (!obj) + return ok; - if (argc == 0) { - SetDateToNaN(cx, obj, vp); + if (args.length() == 0) { + SetDateToNaN(cx, obj, &args.rval()); return true; } jsdouble result; - if (!ValueToNumber(cx, vp[2], &result)) + if (!ToNumber(cx, args[0], &result)) return false; - return SetUTCTime(cx, obj, TIMECLIP(result), vp); + return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval()); } static JSBool -date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp) +date_makeTime(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp) { - Value *argv; - uintN i; - jsdouble args[4], *argp, *stop; - jsdouble hour, min, sec, msec; - jsdouble lorutime; /* Local or UTC version of *date */ + CallArgs args = CallArgsFromVp(argc, vp); - jsdouble msec_time; - jsdouble result; - - JSObject *obj = ToObject(cx, &vp[1]); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetUTCTime(cx, obj, vp, &result)) - return false; + double result = obj->getDateUTCTime().toNumber(); /* just return NaN if the date is already NaN */ if (!JSDOUBLE_IS_FINITE(result)) { - vp->setNumber(result); + args.rval().setNumber(result); return true; } @@ -1740,155 +1773,152 @@ date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp) * if it's not given. This means that "d = new Date(); * d.setMilliseconds()" returns NaN. Blech. */ - if (argc == 0) { - SetDateToNaN(cx, obj, vp); + if (args.length() == 0) { + SetDateToNaN(cx, obj, &args.rval()); return true; } - if (argc > maxargs) - argc = maxargs; /* clamp argc */ - JS_ASSERT(argc <= 4); - argv = vp + 2; - for (i = 0; i < argc; i++) { - if (!ValueToNumber(cx, argv[i], &args[i])) + uintN numNums = Min(args.length(), maxargs); + JS_ASSERT(numNums <= 4); + double nums[4]; + for (uintN i = 0; i < numNums; i++) { + if (!ToNumber(cx, args[i], &nums[i])) return false; - if (!JSDOUBLE_IS_FINITE(args[i])) { - SetDateToNaN(cx, obj, vp); + if (!JSDOUBLE_IS_FINITE(nums[i])) { + SetDateToNaN(cx, obj, &args.rval()); return true; } - args[i] = js_DoubleToInteger(args[i]); + nums[i] = js_DoubleToInteger(nums[i]); } + double lorutime; /* Local or UTC version of *date */ if (local) lorutime = LocalTime(result, cx); else lorutime = result; - argp = args; - stop = argp + argc; + double *argp = nums; + double *stop = argp + numNums; + double hour; if (maxargs >= 4 && argp < stop) hour = *argp++; else hour = HourFromTime(lorutime); + double min; if (maxargs >= 3 && argp < stop) min = *argp++; else min = MinFromTime(lorutime); + double sec; if (maxargs >= 2 && argp < stop) sec = *argp++; else sec = SecFromTime(lorutime); + double msec; if (maxargs >= 1 && argp < stop) msec = *argp; else msec = msFromTime(lorutime); - msec_time = MakeTime(hour, min, sec, msec); + double msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(Day(lorutime), msec_time); -/* fprintf(stderr, "%f\n", result); */ - if (local) result = UTC(result, cx); -/* fprintf(stderr, "%f\n", result); */ - - return SetUTCTime(cx, obj, TIMECLIP(result), vp); + return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval()); } static JSBool date_setMilliseconds(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 1, JS_TRUE, argc, vp); + return date_makeTime(cx, date_setMilliseconds, 1, JS_TRUE, argc, vp); } static JSBool date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 1, JS_FALSE, argc, vp); + return date_makeTime(cx, date_setUTCMilliseconds, 1, JS_FALSE, argc, vp); } static JSBool date_setSeconds(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 2, JS_TRUE, argc, vp); + return date_makeTime(cx, date_setSeconds, 2, JS_TRUE, argc, vp); } static JSBool date_setUTCSeconds(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 2, JS_FALSE, argc, vp); + return date_makeTime(cx, date_setUTCSeconds, 2, JS_FALSE, argc, vp); } static JSBool date_setMinutes(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 3, JS_TRUE, argc, vp); + return date_makeTime(cx, date_setMinutes, 3, JS_TRUE, argc, vp); } static JSBool date_setUTCMinutes(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 3, JS_FALSE, argc, vp); + return date_makeTime(cx, date_setUTCMinutes, 3, JS_FALSE, argc, vp); } static JSBool date_setHours(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 4, JS_TRUE, argc, vp); + return date_makeTime(cx, date_setHours, 4, JS_TRUE, argc, vp); } static JSBool date_setUTCHours(JSContext *cx, uintN argc, Value *vp) { - return date_makeTime(cx, 4, JS_FALSE, argc, vp); + return date_makeTime(cx, date_setUTCHours, 4, JS_FALSE, argc, vp); } static JSBool -date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp) +date_makeDate(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp) { - Value *argv; - uintN i; - jsdouble lorutime; /* local or UTC version of *date */ - jsdouble args[3], *argp, *stop; - jsdouble year, month, day; - jsdouble result; + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok); if (!obj) - return false; + return ok; - if (!GetUTCTime(cx, obj, vp, &result)) - return false; + double result = obj->getDateUTCTime().toNumber(); /* see complaint about ECMA in date_MakeTime */ - if (argc == 0) { - SetDateToNaN(cx, obj, vp); + if (args.length() == 0) { + SetDateToNaN(cx, obj, &args.rval()); return true; } - if (argc > maxargs) - argc = maxargs; /* clamp argc */ - JS_ASSERT(1 <= argc && argc <= 3); - argv = vp + 2; - for (i = 0; i < argc; i++) { - if (!ValueToNumber(cx, argv[i], &args[i])) + uintN numNums = Min(args.length(), maxargs); + JS_ASSERT(1 <= numNums && numNums <= 3); + double nums[3]; + for (uintN i = 0; i < numNums; i++) { + if (!ToNumber(cx, args[i], &nums[i])) return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(args[i])) { - SetDateToNaN(cx, obj, vp); + if (!JSDOUBLE_IS_FINITE(nums[i])) { + SetDateToNaN(cx, obj, &args.rval()); return true; } - args[i] = js_DoubleToInteger(args[i]); + nums[i] = js_DoubleToInteger(nums[i]); } - /* return NaN if date is NaN and we're not setting the year, - * If we are, use 0 as the time. */ + /* + * return NaN if date is NaN and we're not setting the year, If we are, use + * 0 as the time. + */ + double lorutime; /* local or UTC version of *date */ if (!(JSDOUBLE_IS_FINITE(result))) { if (maxargs < 3) { - vp->setDouble(result); + args.rval().setDouble(result); return true; } lorutime = +0.; @@ -1896,18 +1926,21 @@ date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp) lorutime = local ? LocalTime(result, cx) : result; } - argp = args; - stop = argp + argc; + double *argp = nums; + double *stop = argp + numNums; + double year; if (maxargs >= 3 && argp < stop) year = *argp++; else year = YearFromTime(lorutime); + double month; if (maxargs >= 2 && argp < stop) month = *argp++; else month = MonthFromTime(lorutime); + double day; if (maxargs >= 1 && argp < stop) day = *argp++; else @@ -1919,79 +1952,80 @@ date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp) if (local) result = UTC(result, cx); - return SetUTCTime(cx, obj, TIMECLIP(result), vp); + return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval()); } static JSBool date_setDate(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 1, JS_TRUE, argc, vp); + return date_makeDate(cx, date_setDate, 1, JS_TRUE, argc, vp); } static JSBool date_setUTCDate(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 1, JS_FALSE, argc, vp); + return date_makeDate(cx, date_setUTCDate, 1, JS_FALSE, argc, vp); } static JSBool date_setMonth(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 2, JS_TRUE, argc, vp); + return date_makeDate(cx, date_setMonth, 2, JS_TRUE, argc, vp); } static JSBool date_setUTCMonth(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 2, JS_FALSE, argc, vp); + return date_makeDate(cx, date_setUTCMonth, 2, JS_FALSE, argc, vp); } static JSBool date_setFullYear(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 3, JS_TRUE, argc, vp); + return date_makeDate(cx, date_setFullYear, 3, JS_TRUE, argc, vp); } static JSBool date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp) { - return date_makeDate(cx, 3, JS_FALSE, argc, vp); + return date_makeDate(cx, date_setUTCFullYear, 3, JS_FALSE, argc, vp); } static JSBool date_setYear(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - jsdouble result; - if (!GetUTCTime(cx, obj, vp, &result)) - return false; + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok); + if (!obj) + return ok; - if (argc == 0) { - /* Call this only after GetUTCTime has verified that obj is Date. */ - SetDateToNaN(cx, obj, vp); + if (args.length() == 0) { + /* Call this only after verifying that obj.[[Class]] = "Date". */ + SetDateToNaN(cx, obj, &args.rval()); return true; } - jsdouble year; - if (!ValueToNumber(cx, vp[2], &year)) + double result = obj->getDateUTCTime().toNumber(); + + double year; + if (!ToNumber(cx, args[0], &year)) return false; if (!JSDOUBLE_IS_FINITE(year)) { - SetDateToNaN(cx, obj, vp); + SetDateToNaN(cx, obj, &args.rval()); return true; } year = js_DoubleToInteger(year); if (year >= 0 && year <= 99) year += 1900; - jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0; - jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + double t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0; + double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); result = MakeDate(day, TimeWithinDay(t)); result = UTC(result, cx); - return SetUTCTime(cx, obj, TIMECLIP(result), vp); + return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval()); } /* constants for toString, toUTCString */ @@ -2035,12 +2069,15 @@ print_iso_string(char* buf, size_t size, jsdouble utctime) } static JSBool -date_utc_format(JSContext *cx, Value *vp, +date_utc_format(JSContext *cx, Native native, CallArgs args, void (*printFunc)(char*, size_t, jsdouble)) { - jsdouble utctime; - if (!GetThisUTCTime(cx, vp, &utctime)) - return false; + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok); + if (!obj) + return ok; + + double utctime = obj->getDateUTCTime().toNumber(); char buf[100]; if (!JSDOUBLE_IS_FINITE(utctime)) { @@ -2057,20 +2094,20 @@ date_utc_format(JSContext *cx, Value *vp, JSString *str = JS_NewStringCopyZ(cx, buf); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } static JSBool date_toGMTString(JSContext *cx, uintN argc, Value *vp) { - return date_utc_format(cx, vp, print_gmt_string); + return date_utc_format(cx, date_toGMTString, CallArgsFromVp(argc, vp), print_gmt_string); } static JSBool date_toISOString(JSContext *cx, uintN argc, Value *vp) { - return date_utc_format(cx, vp, print_iso_string); + return date_utc_format(cx, date_toISOString, CallArgsFromVp(argc, vp), print_iso_string); } /* ES5 15.9.5.44. */ @@ -2083,8 +2120,8 @@ date_toJSON(JSContext *cx, uintN argc, Value *vp) return false; /* Step 2. */ - Value &tv = vp[0]; - if (!DefaultValue(cx, obj, JSTYPE_NUMBER, &tv)) + Value tv = ObjectValue(*obj); + if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv)) return false; /* Step 3. */ @@ -2095,7 +2132,7 @@ date_toJSON(JSContext *cx, uintN argc, Value *vp) /* Step 4. */ Value &toISO = vp[0]; - if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.toISOStringAtom), &toISO)) + if (!obj->getProperty(cx, cx->runtime->atomState.toISOStringAtom, &toISO)) return false; /* Step 5. */ @@ -2106,7 +2143,6 @@ date_toJSON(JSContext *cx, uintN argc, Value *vp) } /* Step 6. */ - LeaveTrace(cx); InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 0, &args)) return false; @@ -2127,15 +2163,15 @@ new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx) { jsint year = YearFromTime(timeval); - split->tm_usec = (int32) msFromTime(timeval) * 1000; - split->tm_sec = (int8) SecFromTime(timeval); - split->tm_min = (int8) MinFromTime(timeval); - split->tm_hour = (int8) HourFromTime(timeval); - split->tm_mday = (int8) DateFromTime(timeval); - split->tm_mon = (int8) MonthFromTime(timeval); - split->tm_wday = (int8) WeekDay(timeval); + split->tm_usec = int32_t(msFromTime(timeval)) * 1000; + split->tm_sec = int8_t(SecFromTime(timeval)); + split->tm_min = int8_t(MinFromTime(timeval)); + split->tm_hour = int8_t(HourFromTime(timeval)); + split->tm_mday = int8_t(DateFromTime(timeval)); + split->tm_mon = int8_t(MonthFromTime(timeval)); + split->tm_wday = int8_t(WeekDay(timeval)); split->tm_year = year; - split->tm_yday = (int16) DayWithinYear(timeval, year); + split->tm_yday = int16_t(DayWithinYear(timeval, year)); /* not sure how this affects things, but it doesn't seem to matter. */ @@ -2148,7 +2184,7 @@ typedef enum formatspec { /* helper function */ static JSBool -date_format(JSContext *cx, jsdouble date, formatspec format, Value *rval) +date_format(JSContext *cx, jsdouble date, formatspec format, CallReceiver call) { char buf[100]; JSString *str; @@ -2256,26 +2292,22 @@ date_format(JSContext *cx, jsdouble date, formatspec format, Value *rval) str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; - rval->setString(str); + call.rval().setString(str); return JS_TRUE; } -static JSBool -date_toLocaleHelper(JSContext *cx, JSObject *obj, const char *format, Value *vp) +static bool +ToLocaleHelper(JSContext *cx, CallReceiver call, JSObject *obj, const char *format) { - char buf[100]; - JSString *str; - PRMJTime split; - jsdouble utctime; - - if (!GetUTCTime(cx, obj, vp, &utctime)) - return false; + double utctime = obj->getDateUTCTime().toNumber(); + char buf[100]; if (!JSDOUBLE_IS_FINITE(utctime)) { JS_snprintf(buf, sizeof buf, js_NaN_date_str); } else { intN result_len; jsdouble local = LocalTime(utctime, cx); + PRMJTime split; new_explode(local, &split, cx); /* let PRMJTime format it. */ @@ -2283,7 +2315,7 @@ date_toLocaleHelper(JSContext *cx, JSObject *obj, const char *format, Value *vp) /* If it failed, default to toString. */ if (result_len == 0) - return date_format(cx, utctime, FORMATSPEC_FULL, vp); + return date_format(cx, utctime, FORMATSPEC_FULL, call); /* Hacked check against undesired 2-digit year 00/00/00 form. */ if (strcmp(format, "%x") == 0 && result_len >= 6 && @@ -2301,133 +2333,149 @@ date_toLocaleHelper(JSContext *cx, JSObject *obj, const char *format, Value *vp) } if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, Jsvalify(vp)); + return cx->localeCallbacks->localeToUnicode(cx, buf, &call.rval()); - str = JS_NewStringCopyZ(cx, buf); + JSString *str = JS_NewStringCopyZ(cx, buf); if (!str) return false; - vp->setString(str); + call.rval().setString(str); return true; } +/* + * NB: Because of NonGenericMethodGuard, the calling native return immediately + * after calling date_toLocaleHelper, even if it returns 'true'. + */ static JSBool -date_toLocaleString(JSContext *cx, uintN argc, Value *vp) +date_toLocaleHelper(JSContext *cx, uintN argc, Value *vp, Native native, const char *format) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok); if (!obj) - return false; + return ok; + + return ToLocaleHelper(cx, args, obj, format); +} +static JSBool +date_toLocaleStringHelper(JSContext *cx, Native native, uintN argc, Value *vp) +{ /* * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k * with msvc; '%#c' requests that a full year be used in the result string. */ - return date_toLocaleHelper(cx, obj, + return date_toLocaleHelper(cx, argc, vp, native, #if defined(_WIN32) && !defined(__MWERKS__) "%#c" #else "%c" #endif - , vp); + ); } static JSBool -date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp) +date_toLocaleString(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; + return date_toLocaleStringHelper(cx, date_toLocaleString, argc, vp); +} +static JSBool +date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp) +{ /* * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k * with msvc; '%#x' requests that a full year be used in the result string. */ - return date_toLocaleHelper(cx, obj, + return date_toLocaleHelper(cx, argc, vp, date_toLocaleDateString, #if defined(_WIN32) && !defined(__MWERKS__) "%#x" #else "%x" #endif - , vp); + ); } static JSBool date_toLocaleTimeString(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; - - return date_toLocaleHelper(cx, obj, "%X", vp); + return date_toLocaleHelper(cx, argc, vp, date_toLocaleTimeString, "%X"); } static JSBool date_toLocaleFormat(JSContext *cx, uintN argc, Value *vp) { if (argc == 0) - return date_toLocaleString(cx, argc, vp); + return date_toLocaleStringHelper(cx, date_toLocaleFormat, argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_toLocaleFormat, &DateClass, &ok); if (!obj) - return false; + return ok; - JSString *fmt = js_ValueToString(cx, vp[2]); + JSString *fmt = ToString(cx, args[0]); if (!fmt) return false; - vp[2].setString(fmt); + + args[0].setString(fmt); JSAutoByteString fmtbytes(cx, fmt); if (!fmtbytes) return false; - return date_toLocaleHelper(cx, obj, fmtbytes.ptr(), vp); + return ToLocaleHelper(cx, args, obj, fmtbytes.ptr()); } static JSBool date_toTimeString(JSContext *cx, uintN argc, Value *vp) { - jsdouble utctime; - if (!GetThisUTCTime(cx, vp, &utctime)) - return false; - return date_format(cx, utctime, FORMATSPEC_TIME, vp); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_toTimeString, &DateClass, &ok); + if (!obj) + return ok; + + return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_TIME, args); } static JSBool date_toDateString(JSContext *cx, uintN argc, Value *vp) { - jsdouble utctime; - if (!GetThisUTCTime(cx, vp, &utctime)) - return false; - return date_format(cx, utctime, FORMATSPEC_DATE, vp); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_toDateString, &DateClass, &ok); + if (!obj) + return ok; + + return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_DATE, args); } #if JS_HAS_TOSOURCE -#include -#include "jsnum.h" - static JSBool date_toSource(JSContext *cx, uintN argc, Value *vp) { - jsdouble utctime; - if (!GetThisUTCTime(cx, vp, &utctime)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - ToCStringBuf cbuf; - char *numStr = NumberToCString(cx, &cbuf, utctime); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return false; - } + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_toSource, &DateClass, &ok); + if (!obj) + return ok; - char *bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); - if (!bytes) { - JS_ReportOutOfMemory(cx); + StringBuffer sb(cx); + if (!sb.append("(new Date(") || !NumberValueToStringBuffer(cx, obj->getDateUTCTime(), sb) || + !sb.append("))")) + { return false; } - JSString *str = JS_NewStringCopyZ(cx, bytes); - cx->free_(bytes); + JSString *str = sb.finishString(); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } #endif @@ -2435,52 +2483,51 @@ date_toSource(JSContext *cx, uintN argc, Value *vp) static JSBool date_toString(JSContext *cx, uintN argc, Value *vp) { - jsdouble utctime; - if (!GetThisUTCTime(cx, vp, &utctime)) - return false; + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_toString, &DateClass, &ok); + if (!obj) + return ok; - return date_format(cx, utctime, FORMATSPEC_FULL, vp); + return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args); } static JSBool date_valueOf(JSContext *cx, uintN argc, Value *vp) { - /* - * It is an error to call date_valueOf on a non-date object, but we don't - * need to check for that explicitly here because every path calls - * GetUTCTime, which does the check. - */ - - /* If called directly with no arguments, convert to a time number. */ - if (argc == 0) - return date_getTime(cx, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); - /* Verify this before extracting a string from the first argument. */ - JSObject *obj = ToObject(cx, &vp[1]); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, date_valueOf, &DateClass, &ok); if (!obj) - return false; + return ok; + + /* If called directly with no arguments, convert to a time number. */ + if (argc == 0) { + args.rval() = obj->getDateUTCTime(); + return true; + } /* Convert to number only if the hint was given, otherwise favor string. */ - JSString *str = js_ValueToString(cx, vp[2]); + JSString *str = ToString(cx, args[0]); if (!str) return false; JSLinearString *linear_str = str->ensureLinear(cx); if (!linear_str) return false; JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]; - if (EqualStrings(linear_str, number_str)) - return date_getTime(cx, argc, vp); - return date_toString(cx, argc, vp); + if (EqualStrings(linear_str, number_str)) { + args.rval() = obj->getDateUTCTime(); + return true; + } + return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args); } -// Don't really need an argument here, but we don't support arg-less builtins -JS_DEFINE_TRCINFO_1(date_now, - (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, nanojit::ACCSET_STORE_ANY))) - static JSFunctionSpec date_static_methods[] = { JS_FN("UTC", date_UTC, MAXARGS,0), JS_FN("parse", date_parse, 1,0), - JS_TN("now", date_now, 0,0, &date_now_trcinfo), + JS_FN("now", date_now, 0,0), JS_FS_END }; @@ -2540,28 +2587,28 @@ static JSFunctionSpec date_methods[] = { JSBool js_Date(JSContext *cx, uintN argc, Value *vp) { - /* Date called as function. */ - if (!IsConstructing(vp)) - return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, vp); + CallArgs args = CallArgsFromVp(argc, vp); - Value *argv = vp + 2; + /* Date called as function. */ + if (!IsConstructing(args)) + return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args); /* Date called as constructor. */ jsdouble d; - if (argc == 0) { + if (args.length() == 0) { d = NowAsMillis(); - } else if (argc == 1) { - if (!argv[0].isString()) { + } else if (args.length() == 1) { + if (!args[0].isString()) { /* the argument is a millisecond number */ - if (!ValueToNumber(cx, argv[0], &d)) + if (!ToNumber(cx, args[0], &d)) return false; d = TIMECLIP(d); } else { /* the argument is a string; parse it. */ - JSString *str = js_ValueToString(cx, argv[0]); + JSString *str = ToString(cx, args[0]); if (!str) return false; - argv[0].setString(str); + args[0].setString(str); JSLinearString *linearStr = str->ensureLinear(cx); if (!linearStr) return false; @@ -2573,7 +2620,7 @@ js_Date(JSContext *cx, uintN argc, Value *vp) } } else { jsdouble msec_time; - if (!date_msecFromArgs(cx, argc, argv, &msec_time)) + if (!date_msecFromArgs(cx, args, &msec_time)) return false; if (JSDOUBLE_IS_FINITE(msec_time)) { @@ -2586,49 +2633,65 @@ js_Date(JSContext *cx, uintN argc, Value *vp) JSObject *obj = js_NewDateObjectMsec(cx, d); if (!obj) return false; - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } JSObject * js_InitDateClass(JSContext *cx, JSObject *obj) { - /* set static LocalTZA */ + JS_ASSERT(obj->isNative()); + + /* Set the static LocalTZA. */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS, - NULL, date_methods, NULL, date_static_methods); - if (!proto) + + GlobalObject *global = &obj->asGlobal(); + + JSObject *dateProto = global->createBlankPrototype(cx, &DateClass); + if (!dateProto) + return NULL; + SetDateToNaN(cx, dateProto); + + JSFunction *ctor = global->createConstructor(cx, js_Date, &DateClass, + CLASS_ATOM(cx, Date), MAXARGS); + if (!ctor) return NULL; - AutoObjectRooter tvr(cx, proto); + if (!LinkConstructorAndPrototype(cx, ctor, dateProto)) + return NULL; - SetDateToNaN(cx, proto); + if (!DefinePropertiesAndBrand(cx, ctor, NULL, date_static_methods)) + return NULL; /* - * ES5 B.2.6: - * The Function object that is the initial value of - * Date.prototype.toGMTString is the same Function - * object that is the initial value of - * Date.prototype.toUTCString. + * Define all Date.prototype.* functions, then brand for trace-jitted code. + * Date.prototype.toGMTString has the same initial value as + * Date.prototype.toUTCString. */ - AutoValueRooter toUTCStringFun(cx); + if (!JS_DefineFunctions(cx, dateProto, date_methods)) + return NULL; + Value toUTCStringFun; jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom); jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom); - if (!js_GetProperty(cx, proto, toUTCStringId, toUTCStringFun.addr()) || - !js_DefineProperty(cx, proto, toGMTStringId, toUTCStringFun.addr(), - PropertyStub, StrictPropertyStub, 0)) { + if (!js_GetProperty(cx, dateProto, toUTCStringId, &toUTCStringFun) || + !js_DefineProperty(cx, dateProto, toGMTStringId, &toUTCStringFun, + JS_PropertyStub, JS_StrictPropertyStub, 0)) + { return NULL; } - return proto; + if (!DefineConstructorAndPrototype(cx, global, JSProto_Date, ctor, dateProto)) + return NULL; + + return dateProto; } JS_FRIEND_API(JSObject *) js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass); - if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS)) + JSObject *obj = NewBuiltinClassInstance(cx, &DateClass); + if (!obj) return NULL; if (!SetUTCTime(cx, obj, msec_time)) return NULL; @@ -2651,8 +2714,7 @@ js_NewDateObject(JSContext* cx, int year, int mon, int mday, JS_FRIEND_API(JSBool) js_DateIsValid(JSContext *cx, JSObject* obj) { - jsdouble utctime; - return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime); + return obj->isDate() && !JSDOUBLE_IS_NaN(obj->getDateUTCTime().toNumber()); } JS_FRIEND_API(int) @@ -2661,7 +2723,7 @@ js_DateGetYear(JSContext *cx, JSObject* obj) jsdouble localtime; /* Preserve legacy API behavior of returning 0 for invalid dates. */ - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + if (!GetAndCacheLocalTime(cx, obj, &localtime) || JSDOUBLE_IS_NaN(localtime)) { return 0; } @@ -2674,7 +2736,7 @@ js_DateGetMonth(JSContext *cx, JSObject* obj) { jsdouble localtime; - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + if (!GetAndCacheLocalTime(cx, obj, &localtime) || JSDOUBLE_IS_NaN(localtime)) { return 0; } @@ -2687,7 +2749,7 @@ js_DateGetDate(JSContext *cx, JSObject* obj) { jsdouble localtime; - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + if (!GetAndCacheLocalTime(cx, obj, &localtime) || JSDOUBLE_IS_NaN(localtime)) { return 0; } @@ -2700,7 +2762,7 @@ js_DateGetHours(JSContext *cx, JSObject* obj) { jsdouble localtime; - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + if (!GetAndCacheLocalTime(cx, obj, &localtime) || JSDOUBLE_IS_NaN(localtime)) { return 0; } @@ -2713,7 +2775,7 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj) { jsdouble localtime; - if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + if (!GetAndCacheLocalTime(cx, obj, &localtime) || JSDOUBLE_IS_NaN(localtime)) { return 0; } @@ -2724,37 +2786,35 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj) JS_FRIEND_API(int) js_DateGetSeconds(JSContext *cx, JSObject* obj) { - jsdouble utctime; - - if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime)) + if (!obj->isDate()) + return 0; + + double utctime = obj->getDateUTCTime().toNumber(); + if (JSDOUBLE_IS_NaN(utctime)) return 0; - return (int) SecFromTime(utctime); } JS_FRIEND_API(jsdouble) js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) { - jsdouble utctime; - if (!GetUTCTime(cx, obj, NULL, &utctime)) - return 0; - return utctime; + return obj->isDate() ? obj->getDateUTCTime().toNumber() : 0; } #ifdef JS_THREADSAFE #include "prinrval.h" -JS_FRIEND_API(uint32) +JS_FRIEND_API(uint32_t) js_IntervalNow() { - return uint32(PR_IntervalToMilliseconds(PR_IntervalNow())); + return uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow())); } #else /* !JS_THREADSAFE */ -JS_FRIEND_API(uint32) +JS_FRIEND_API(uint32_t) js_IntervalNow() { - return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC); + return uint32_t(PRMJ_Now() / PRMJ_USEC_PER_MSEC); } #endif diff --git a/deps/mozjs/js/src/jsdate.h b/deps/mozjs/js/src/jsdate.h index ba97ddd855c..4d3545b4797 100644 --- a/deps/mozjs/js/src/jsdate.h +++ b/deps/mozjs/js/src/jsdate.h @@ -44,15 +44,7 @@ #ifndef jsdate_h___ #define jsdate_h___ -#include "jsobj.h" - -extern js::Class js_DateClass; - -inline bool -JSObject::isDate() const -{ - return getClass() == &js_DateClass; -} +#include "jscntxt.h" #define HalfTimeDomain 8.64e15 @@ -85,13 +77,6 @@ extern JS_FRIEND_API(JSObject*) js_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec); -/* - * Detect whether the internal date value is NaN. (Because failure is - * out-of-band for js_DateGet*) - */ -extern JS_FRIEND_API(JSBool) -js_DateIsValid(JSContext *cx, JSObject* obj); - extern JS_FRIEND_API(int) js_DateGetYear(JSContext *cx, JSObject* obj); @@ -110,10 +95,7 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetSeconds(JSContext *cx, JSObject* obj); -extern JS_FRIEND_API(jsdouble) -js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); - -typedef uint32 JSIntervalTime; +typedef uint32_t JSIntervalTime; extern JS_FRIEND_API(JSIntervalTime) js_IntervalNow(); diff --git a/deps/mozjs/js/src/jsdbgapi.cpp b/deps/mozjs/js/src/jsdbgapi.cpp index 8dc7d577f4a..142dd00ff7b 100644 --- a/deps/mozjs/js/src/jsdbgapi.cpp +++ b/deps/mozjs/js/src/jsdbgapi.cpp @@ -23,6 +23,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Nick Fitzgerald * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -42,17 +43,15 @@ * JS debugging API. */ #include +#include #include "jsprvtd.h" #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jsclist.h" -#include "jshashtable.h" #include "jsapi.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" -#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" @@ -60,15 +59,18 @@ #include "jslock.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsparse.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" #include "jsstr.h" +#include "jswatchpoint.h" #include "jswrapper.h" +#include "frontend/BytecodeEmitter.h" +#include "frontend/Parser.h" +#include "vm/Debugger.h" + #include "jsatominlines.h" -#include "jsdbgapiinlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsinterpinlines.h" #include "jsscopeinlines.h" @@ -78,29 +80,17 @@ #include "jsautooplen.h" -#include "methodjit/MethodJIT.h" -#include "methodjit/Retcon.h" +#ifdef __APPLE__ +#include "sharkctl.h" +#endif using namespace js; using namespace js::gc; -typedef struct JSTrap { - JSCList links; - JSScript *script; - jsbytecode *pc; - JSOp op; - JSTrapHandler handler; - jsval closure; -} JSTrap; - -#define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock) -#define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock) -#define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt)) - JS_PUBLIC_API(JSBool) JS_GetDebugMode(JSContext *cx) { - return cx->compartment->debugMode; + return cx->compartment->debugMode(); } JS_PUBLIC_API(JSBool) @@ -112,14 +102,16 @@ JS_SetDebugMode(JSContext *cx, JSBool debug) JS_PUBLIC_API(void) JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug) { - rt->debugMode = debug; + rt->debugMode = !!debug; } namespace js { -void +JSTrapStatus ScriptDebugPrologue(JSContext *cx, StackFrame *fp) { + JS_ASSERT(fp == cx->fp()); + if (fp->isFramePushedByExecute()) { if (JSInterpreterHook hook = cx->debugHooks->executeHook) fp->setHookData(hook(cx, Jsvalify(fp), true, 0, cx->debugHooks->executeHookData)); @@ -128,16 +120,32 @@ ScriptDebugPrologue(JSContext *cx, StackFrame *fp) fp->setHookData(hook(cx, Jsvalify(fp), true, 0, cx->debugHooks->callHookData)); } - Probes::enterJSFun(cx, fp->maybeFun(), fp->script()); + Value rval; + JSTrapStatus status = Debugger::onEnterFrame(cx, &rval); + switch (status) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_THROW: + cx->setPendingException(rval); + break; + case JSTRAP_ERROR: + cx->clearPendingException(); + break; + case JSTRAP_RETURN: + fp->setReturnValue(rval); + break; + default: + JS_NOT_REACHED("bad Debugger::onEnterFrame JSTrapStatus value"); + } + return status; } bool ScriptDebugEpilogue(JSContext *cx, StackFrame *fp, bool okArg) { + JS_ASSERT(fp == cx->fp()); JSBool ok = okArg; - Probes::exitJSFun(cx, fp->maybeFun(), fp->script()); - if (void *hookData = fp->maybeHookData()) { if (fp->isFramePushedByExecute()) { if (JSInterpreterHook hook = cx->debugHooks->executeHook) @@ -147,112 +155,17 @@ ScriptDebugEpilogue(JSContext *cx, StackFrame *fp, bool okArg) hook(cx, Jsvalify(fp), false, &ok, hookData); } } + Debugger::onLeaveFrame(cx); return ok; } } /* namespace js */ -#ifdef DEBUG -static bool -CompartmentHasLiveScripts(JSCompartment *comp) -{ -#if defined(JS_METHODJIT) && defined(JS_THREADSAFE) - jsword currentThreadId = reinterpret_cast(js_CurrentThreadId()); -#endif - - // Unsynchronized context iteration is technically a race; but this is only - // for debug asserts where such a race would be rare - JSContext *iter = NULL; - JSContext *icx; - while ((icx = JS_ContextIterator(comp->rt, &iter))) { -#if defined(JS_METHODJIT) && defined(JS_THREADSAFE) - if (JS_GetContextThread(icx) != currentThreadId) - continue; -#endif - for (AllFramesIter i(icx); !i.done(); ++i) { - JSScript *script = i.fp()->maybeScript(); - if (script && script->compartment == comp) - return JS_TRUE; - } - } - - return JS_FALSE; -} -#endif - JS_FRIEND_API(JSBool) JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug) { - if (comp->debugMode == !!debug) - return JS_TRUE; - - // This should only be called when no scripts are live. It would even be - // incorrect to discard just the non-live scripts' JITScripts because they - // might share ICs with live scripts (bug 632343). - JS_ASSERT(!CompartmentHasLiveScripts(comp)); - - // All scripts compiled from this point on should be in the requested debugMode. - comp->debugMode = !!debug; - - // Discard JIT code for any scripts that change debugMode. This function - // assumes that 'comp' is in the same thread as 'cx'. - -#ifdef JS_METHODJIT - JS::AutoEnterScriptCompartment ac; - - for (JSScript *script = (JSScript *)comp->scripts.next; - &script->links != &comp->scripts; - script = (JSScript *)script->links.next) - { - if (!script->debugMode == !debug) - continue; - - /* - * If compartment entry fails, debug mode is left partially on, leading - * to a small performance overhead but no loss of correctness. We set - * the debug flags to false so that the caller will not later attempt - * to use debugging features. - */ - if (!ac.entered() && !ac.enter(cx, script)) { - comp->debugMode = JS_FALSE; - return JS_FALSE; - } - - mjit::ReleaseScriptCode(cx, script); - script->debugMode = !!debug; - } -#endif - - return JS_TRUE; -} - -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; -#endif - - JS_ASSERT_IF(singleStep, cx->compartment->debugMode); - -#ifdef JS_METHODJIT - /* request the next recompile to inject single step interrupts */ - script->singleStepMode = !!singleStep; - - js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor; - if (jit && script->singleStepMode != jit->singleStepMode) { - js::mjit::Recompiler recompiler(cx, script); - if (!recompiler.recompile()) { - script->singleStepMode = !singleStep; - return JS_FALSE; - } - } -#endif - return JS_TRUE; + return comp->setDebugModeFromC(cx, !!debug); } static JSBool @@ -278,782 +191,78 @@ JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep) if (!CheckDebugMode(cx)) return JS_FALSE; - return js_SetSingleStepMode(cx, script, singleStep); -} - -/* - * NB: FindTrap must be called with rt->debuggerLock acquired. - */ -static JSTrap * -FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) -{ - JSTrap *trap; - - for (trap = (JSTrap *)rt->trapList.next; - &trap->links != &rt->trapList; - trap = (JSTrap *)trap->links.next) { - if (trap->script == script && trap->pc == pc) - return trap; - } - return NULL; -} - -jsbytecode * -js_UntrapScriptCode(JSContext *cx, JSScript *script) -{ - jsbytecode *code; - JSRuntime *rt; - JSTrap *trap; - - code = script->code; - rt = cx->runtime; - DBG_LOCK(rt); - for (trap = (JSTrap *)rt->trapList.next; - &trap->links != - &rt->trapList; - trap = (JSTrap *)trap->links.next) { - if (trap->script == script && - (size_t)(trap->pc - script->code) < script->length) { - if (code == script->code) { - jssrcnote *sn, *notes; - size_t nbytes; - - nbytes = script->length * sizeof(jsbytecode); - notes = script->notes(); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nbytes += (sn - notes + 1) * sizeof *sn; - - code = (jsbytecode *) cx->malloc_(nbytes); - if (!code) - break; - memcpy(code, script->code, nbytes); - GetGSNCache(cx)->purge(); - } - code[trap->pc - script->code] = trap->op; - } - } - DBG_UNLOCK(rt); - return code; + return script->setStepModeFlag(cx, singleStep); } JS_PUBLIC_API(JSBool) -JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler handler, jsval closure) +JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, jsval closure) { - JSTrap *junk, *trap, *twin; - JSRuntime *rt; - uint32 sample; + assertSameCompartment(cx, script, closure); if (!CheckDebugMode(cx)) - return JS_FALSE; - - JS_ASSERT((JSOp) *pc != JSOP_TRAP); - junk = NULL; - rt = cx->runtime; - DBG_LOCK(rt); - trap = FindTrap(rt, script, pc); - if (trap) { - JS_ASSERT(trap->script == script && trap->pc == pc); - JS_ASSERT(*pc == JSOP_TRAP); - } else { - sample = rt->debuggerMutations; - DBG_UNLOCK(rt); - trap = (JSTrap *) cx->malloc_(sizeof *trap); - if (!trap) - return JS_FALSE; - trap->closure = JSVAL_NULL; - DBG_LOCK(rt); - twin = (rt->debuggerMutations != sample) - ? FindTrap(rt, script, pc) - : NULL; - if (twin) { - junk = trap; - trap = twin; - } else { - JS_APPEND_LINK(&trap->links, &rt->trapList); - ++rt->debuggerMutations; - trap->script = script; - trap->pc = pc; - trap->op = (JSOp)*pc; - *pc = JSOP_TRAP; - } - } - trap->handler = handler; - trap->closure = closure; - DBG_UNLOCK(rt); - if (junk) - cx->free_(junk); - -#ifdef JS_METHODJIT - if (script->hasJITCode()) { - js::mjit::Recompiler recompiler(cx, script); - if (!recompiler.recompile()) - return JS_FALSE; - } -#endif - - return JS_TRUE; -} - -JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSRuntime *rt; - JSTrap *trap; - JSOp op; - - rt = cx->runtime; - DBG_LOCK(rt); - trap = FindTrap(rt, script, pc); - op = trap ? trap->op : (JSOp) *pc; - DBG_UNLOCK(rt); - return op; -} + return false; -static void -DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap) -{ - ++cx->runtime->debuggerMutations; - JS_REMOVE_LINK(&trap->links); - *trap->pc = (jsbytecode)trap->op; - DBG_UNLOCK(cx->runtime); - cx->free_(trap); + BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, NULL); + if (!site) + return false; + site->setTrap(cx, handler, closure); + return true; } JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, jsval *closurep) { - JSTrap *trap; - - DBG_LOCK(cx->runtime); - trap = FindTrap(cx->runtime, script, pc); - if (handlerp) - *handlerp = trap ? trap->handler : NULL; - if (closurep) - *closurep = trap ? trap->closure : JSVAL_NULL; - if (trap) - DestroyTrapAndUnlock(cx, trap); - else - DBG_UNLOCK(cx->runtime); - -#ifdef JS_METHODJIT - if (script->hasJITCode()) { - mjit::Recompiler recompiler(cx, script); - recompiler.recompile(); + if (BreakpointSite *site = script->getBreakpointSite(pc)) { + site->clearTrap(cx, handlerp, closurep); + } else { + if (handlerp) + *handlerp = NULL; + if (closurep) + *closurep = JSVAL_VOID; } -#endif } JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script) { - JSRuntime *rt; - JSTrap *trap, *next; - uint32 sample; - - rt = cx->runtime; - DBG_LOCK(rt); - for (trap = (JSTrap *)rt->trapList.next; - &trap->links != &rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - if (trap->script == script) { - sample = rt->debuggerMutations; - DestroyTrapAndUnlock(cx, trap); - DBG_LOCK(rt); - if (rt->debuggerMutations != sample + 1) - next = (JSTrap *)rt->trapList.next; - } - } - DBG_UNLOCK(rt); + script->clearTraps(cx); } JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx) -{ - JSRuntime *rt; - JSTrap *trap, *next; - uint32 sample; - - rt = cx->runtime; - DBG_LOCK(rt); - for (trap = (JSTrap *)rt->trapList.next; - &trap->links != &rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - sample = rt->debuggerMutations; - DestroyTrapAndUnlock(cx, trap); - DBG_LOCK(rt); - if (rt->debuggerMutations != sample + 1) - next = (JSTrap *)rt->trapList.next; - } - DBG_UNLOCK(rt); -} - -/* - * NB: js_MarkTraps 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). - */ -void -js_MarkTraps(JSTracer *trc) +JS_ClearAllTrapsForCompartment(JSContext *cx) { - JSRuntime *rt = trc->context->runtime; - - for (JSTrap *trap = (JSTrap *) rt->trapList.next; - &trap->links != &rt->trapList; - trap = (JSTrap *) trap->links.next) { - MarkValue(trc, Valueify(trap->closure), "trap->closure"); - } + cx->compartment->clearTraps(cx); } -JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) -{ - JSTrap *trap; - jsint op; - JSTrapStatus status; - - assertSameCompartment(cx, script); - DBG_LOCK(cx->runtime); - trap = FindTrap(cx->runtime, script, pc); - JS_ASSERT(!trap || trap->handler); - if (!trap) { - op = (JSOp) *pc; - DBG_UNLOCK(cx->runtime); - - /* Defend against "pc for wrong script" API usage error. */ - JS_ASSERT(op != JSOP_TRAP); - -#ifdef JS_THREADSAFE - /* If the API was abused, we must fail for want of the real op. */ - if (op == JSOP_TRAP) - return JSTRAP_ERROR; - - /* Assume a race with a debugger thread and try to carry on. */ - *rval = INT_TO_JSVAL(op); - return JSTRAP_CONTINUE; -#else - /* Always fail if single-threaded (must be an API usage error). */ - return JSTRAP_ERROR; -#endif - } - DBG_UNLOCK(cx->runtime); - - /* - * It's important that we not use 'trap->' after calling the callback -- - * the callback might remove the trap! - */ - op = (jsint)trap->op; - status = trap->handler(cx, script, pc, rval, trap->closure); - if (status == JSTRAP_CONTINUE) { - /* By convention, return the true op to the interpreter in rval. */ - *rval = INT_TO_JSVAL(op); - } - return status; -} - -#ifdef JS_TRACER -static void -JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited) -{ - if (wasInhibited) { - if (!rt->debuggerInhibitsJIT()) { - for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) - js_ContextFromLinkField(cl)->updateJITEnabled(); - } - } else if (rt->debuggerInhibitsJIT()) { - for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) - js_ContextFromLinkField(cl)->traceJitEnabled = false; - } -} -#endif - JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure) { -#ifdef JS_TRACER - { - AutoLockGC lock(rt); - bool wasInhibited = rt->debuggerInhibitsJIT(); -#endif - rt->globalDebugHooks.interruptHook = hook; - rt->globalDebugHooks.interruptHookData = closure; -#ifdef JS_TRACER - JITInhibitingHookChange(rt, wasInhibited); - } -#endif + rt->globalDebugHooks.interruptHook = hook; + rt->globalDebugHooks.interruptHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep) { -#ifdef JS_TRACER - AutoLockGC lock(rt); - bool wasInhibited = rt->debuggerInhibitsJIT(); -#endif if (hoop) *hoop = rt->globalDebugHooks.interruptHook; if (closurep) *closurep = rt->globalDebugHooks.interruptHookData; rt->globalDebugHooks.interruptHook = 0; rt->globalDebugHooks.interruptHookData = 0; -#ifdef JS_TRACER - JITInhibitingHookChange(rt, wasInhibited); -#endif return JS_TRUE; } /************************************************************************/ -struct JSWatchPoint { - JSCList links; - JSObject *object; /* weak link, see js_SweepWatchPoints */ - const Shape *shape; - StrictPropertyOp setter; - JSWatchPointHandler handler; - JSObject *closure; - uintN flags; -}; - -#define JSWP_LIVE 0x1 /* live because set and not cleared */ -#define JSWP_HELD 0x2 /* held while running handler/setter */ - -/* - * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases. - * The sweeping parameter is true if the watchpoint and its object are about to - * be finalized, in which case we don't need to changeProperty. - */ -static JSBool -DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag, bool sweeping) -{ - bool ok = true; - JSRuntime *rt = cx->runtime; - - wp->flags &= ~flag; - if (wp->flags != 0) { - DBG_UNLOCK(rt); - return ok; - } - - /* Remove wp from the list, then restore wp->shape->setter from wp. */ - ++rt->debuggerMutations; - JS_REMOVE_LINK(&wp->links); - DBG_UNLOCK(rt); - - /* - * If the property isn't found on wp->object, then someone else must have deleted it, - * and we don't need to change the property attributes. - */ - if (!sweeping) { - const Shape *shape = wp->shape; - const Shape *wprop = wp->object->nativeLookup(shape->id); - if (wprop && - wprop->hasSetterValue() == shape->hasSetterValue() && - IsWatchedProperty(cx, wprop)) { - shape = wp->object->changeProperty(cx, wprop, 0, wprop->attributes(), - wprop->getter(), wp->setter); - if (!shape) - ok = false; - } - } - - cx->free_(wp); - return ok; -} - -/* - * 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). 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. - */ -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->isMarked()) { - if (!wp->shape->isMarked()) { - modified = true; - MarkShape(trc, wp->shape, "shape"); - } - 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 -js_SweepWatchPoints(JSContext *cx) -{ - JSRuntime *rt; - JSWatchPoint *wp, *next; - uint32 sample; - - rt = cx->runtime; - DBG_LOCK(rt); - for (wp = (JSWatchPoint *)rt->watchPointList.next; - &wp->links != &rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (IsAboutToBeFinalized(cx, wp->object)) { - sample = rt->debuggerMutations; - - /* Ignore failures. */ - DropWatchPointAndUnlock(cx, wp, JSWP_LIVE, true); - DBG_LOCK(rt); - if (rt->debuggerMutations != sample + 1) - next = (JSWatchPoint *)rt->watchPointList.next; - } - } - DBG_UNLOCK(rt); -} - - - -/* - * NB: LockedFindWatchPoint must be called with rt->debuggerLock acquired. - */ -static JSWatchPoint * -LockedFindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id) -{ - JSWatchPoint *wp; - - for (wp = (JSWatchPoint *)rt->watchPointList.next; - &wp->links != &rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == obj && wp->shape->id == id) - return wp; - } - return NULL; -} - -static JSWatchPoint * -FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id) -{ - JSWatchPoint *wp; - - DBG_LOCK(rt); - wp = LockedFindWatchPoint(rt, obj, id); - DBG_UNLOCK(rt); - return wp; -} - -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; - &wp->links != &rt->watchPointList; - 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)); - - /* Determine the property's old value. */ - 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 - * is a method; or (2) the watched property was deleted and is - * now in the middle of being re-added via JSOP_SETMETHOD. In - * both cases we must trip the method read barrier in order to - * avoid passing an uncloned function object to the handler. - * - * Case 2 is especially hairy. js_watch_set, uniquely, gets - * called in the middle of creating a method property, after - * shape is in obj but before the slot has been set. So in this - * case we must finish initializing the half-finished method - * property before triggering the method read barrier. - * - * Bonus weirdness: because this changes obj's shape, - * js_NativeSet (which is our caller) will not write to the - * slot, as it will appear the property was deleted and a new - * property added. We must write the slot ourselves -- however - * we must do it after calling the watchpoint handler. So set - * needMethodSlotWrite here and use it to write to the slot - * below, if the handler does not tinker with the property - * further. - */ - JS_ASSERT(!wp->setter); - Value method = ObjectValue(shape->methodObject()); - if (old.isUndefined()) - obj->nativeSetSlot(slot, method); - ok = obj->methodReadBarrier(cx, *shape, &method); - if (!ok) - goto out; - wp->shape = shape = needMethodSlotWrite = obj->nativeLookup(propid); - JS_ASSERT(shape->isDataDescriptor()); - JS_ASSERT(!shape->isMethod()); - if (old.isUndefined()) - obj->nativeSetSlot(shape->slot, old); - else - old = method; - } - - { - Maybe tvr; - if (needMethodSlotWrite) - tvr.construct(cx, needMethodSlotWrite); - - /* - * Call the handler. This invalidates shape, so re-lookup the shape. - * NB: wp is held, so we can safely dereference it still. - */ - ok = wp->handler(cx, obj, propid, Jsvalify(old), Jsvalify(vp), wp->closure); - if (!ok) - goto out; - shape = obj->nativeLookup(propid); - - if (!shape) { - ok = true; - } else if (wp->setter) { - /* - * Pass the output of the handler to the setter. Security wrappers - * prevent any funny business between watchpoints and setters. - */ - ok = shape->hasSetterValue() - ? ExternalInvoke(cx, ObjectValue(*obj), - ObjectValue(*CastAsObject(wp->setter)), - 1, vp, 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); - ok = true; - } else { - /* - * A property with the default setter might be either a method - * or an ordinary function-valued data property subject to the - * method write barrier. - * - * It is not the setter's job to call methodWriteBarrier, - * but js_watch_set must do so, because the caller will be - * fooled into not doing it: shape does *not* have the - * default setter and therefore seems not to be a method. - */ - ok = obj->methodWriteBarrier(cx, *shape, *vp) != NULL; - } - } - - out: - DBG_LOCK(rt); - return DropWatchPointAndUnlock(cx, wp, JSWP_HELD, false) && ok; - } - } - DBG_UNLOCK(rt); - return true; -} - -static JSBool -js_watch_set_wrapper(JSContext *cx, uintN argc, Value *vp) -{ - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; - - JSObject &funobj = JS_CALLEE(cx, vp).toObject(); - JSFunction *wrapper = funobj.getFunctionPrivate(); - jsid userid = ATOM_TO_JSID(wrapper->atom); - - JS_SET_RVAL(cx, vp, argc ? JS_ARGV(cx, vp)[0] : UndefinedValue()); - /* - * The strictness we pass here doesn't matter, since we know that it's - * a JS setter, which can't depend on the assigning code's strictness. - */ - return js_watch_set(cx, obj, userid, false, vp); -} - -namespace js { - -bool -IsWatchedProperty(JSContext *cx, const Shape *shape) -{ - if (shape->hasSetterValue()) { - JSObject *funobj = shape->setterObject(); - if (!funobj || !funobj->isFunction()) - return false; - - JSFunction *fun = funobj->getFunctionPrivate(); - return fun->maybeNative() == js_watch_set_wrapper; - } - return shape->setterOp() == js_watch_set; -} - -} - -/* - * Return an appropriate setter to substitute for |setter| on a property - * with attributes |attrs|, to implement a watchpoint on the property named - * |id|. - */ -static StrictPropertyOp -WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, StrictPropertyOp setter) -{ - JSAtom *atom; - JSFunction *wrapper; - - /* Wrap a C++ setter simply by returning our own C++ setter. */ - if (!(attrs & JSPROP_SETTER)) - return &js_watch_set; /* & to silence schoolmarmish MSVC */ - - /* - * Wrap a JSObject * setter by constructing our own JSFunction * that saves the - * property id as the function name, and calls js_watch_set. - */ - if (JSID_IS_ATOM(id)) { - atom = JSID_TO_ATOM(id); - } else if (JSID_IS_INT(id)) { - if (!js_ValueToStringId(cx, IdToValue(id), &id)) - return NULL; - atom = JSID_TO_ATOM(id); - } else { - atom = NULL; - } - - wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, - setter ? CastAsObject(setter)->getParent() : NULL, atom); - if (!wrapper) - return NULL; - return CastAsStrictPropertyOp(FUN_OBJECT(wrapper)); -} - -static const Shape * -UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape) -{ - JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id); - JS_ASSERT(!IsWatchedProperty(cx, newShape)); - - /* Create a watching setter we can substitute for the new shape's setter. */ - StrictPropertyOp watchingSetter = - WrapWatchedSetter(cx, newShape->id, newShape->attributes(), newShape->setter()); - if (!watchingSetter) - return NULL; - - /* - * Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will - * return a new shape, or mutate this one. - */ - StrictPropertyOp originalSetter = newShape->setter(); - - /* - * Drop the watching setter into the object, in place of newShape. Note that a single - * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape: we - * wrap all (JSPropertyOp, not JSObject *) setters with js_watch_set, so shapes that - * differ only in their setter may all get wrapped to the same shape. - */ - const Shape *watchingShape = - js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(), - newShape->getter(), watchingSetter); - if (!watchingShape) - return NULL; - - /* Update the watchpoint with the new shape and its original setter. */ - wp->setter = originalSetter; - wp->shape = watchingShape; - - return watchingShape; -} - -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 - * watchpoint code, just as they do user-level changes. So if this change is - * installing a watchpoint-aware shape, it's something we asked for ourselves, and can - * proceed without interference. - */ - if (IsWatchedProperty(cx, newShape)) - return newShape; - - JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id); - if (!wp) - return newShape; - - return UpdateWatchpointShape(cx, wp, newShape); -} - -/* - * Return the underlying setter for |shape| on |obj|, seeing through any - * watchpoint-wrapping. Note that we need |obj| to disambiguate, since a single - * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape; see the - * comments in UpdateWatchpointShape. - */ -static StrictPropertyOp -UnwrapSetter(JSContext *cx, JSObject *obj, const Shape *shape) -{ - /* If it's not a watched property, its setter is not wrapped. */ - if (!IsWatchedProperty(cx, shape)) - return shape->setter(); - - /* Look up the watchpoint, from which we can retrieve the underlying setter. */ - JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, shape->id); - - /* - * Since we know |shape| is watched, we *must* find a watchpoint: we should never - * leave wrapped setters lying around in shapes after removing a watchpoint. - */ - JS_ASSERT(wp); - - return wp->setter; -} - JS_PUBLIC_API(JSBool) JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id, JSWatchPointHandler handler, JSObject *closure) { assertSameCompartment(cx, obj); + id = js_CheckForStringIndex(id); JSObject *origobj; Value v; @@ -1068,6 +277,9 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id, AutoValueRooter idroot(cx); if (JSID_IS_INT(id)) { propid = id; + } else if (JSID_IS_OBJECT(id)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH_PROP); + return false; } else { if (!js_ValueToStringId(cx, IdToValue(id), &propid)) return false; @@ -1085,147 +297,33 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id, if (!obj->isNative()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, obj->getClass()->name); - return false; - } - - JSObject *pobj; - JSProperty *prop; - if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) - return false; - const Shape *shape = (Shape *) prop; - JSRuntime *rt = cx->runtime; - if (!shape) { - /* Check for a deleted symbol watchpoint, which holds its property. */ - JSWatchPoint *wp = FindWatchPoint(rt, obj, propid); - if (!wp) { - /* 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 false; - } - shape = (Shape *) prop; - } - } else if (pobj != obj) { - /* Clone the prototype property so we can watch the right object. */ - AutoValueRooter valroot(cx); - PropertyOp getter; - StrictPropertyOp setter; - uintN attrs, flags; - 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()); - getter = shape->getter(); - setter = UnwrapSetter(cx, pobj, shape); - attrs = shape->attributes(); - flags = shape->getFlags(); - shortid = shape->shortid; - } else { - if (!pobj->getProperty(cx, propid, valroot.addr()) || - !pobj->getAttributes(cx, propid, &attrs)) { - return false; - } - getter = NULL; - setter = NULL; - flags = 0; - shortid = 0; - } - - /* Recall that obj is native, whether or not pobj is native. */ - if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(), - getter, setter, attrs, flags, - shortid, &prop)) { - return false; - } - shape = (Shape *) prop; - } - - /* - * At this point, prop/shape exists in obj, obj is locked, and we must - * unlock the object before returning. - */ - DBG_LOCK(rt); - JSWatchPoint *wp = LockedFindWatchPoint(rt, obj, propid); - if (!wp) { - DBG_UNLOCK(rt); - wp = (JSWatchPoint *) cx->malloc_(sizeof *wp); - if (!wp) - return false; - wp->handler = NULL; - wp->closure = NULL; - wp->object = obj; - wp->shape = NULL; - wp->flags = JSWP_LIVE; - - /* XXXbe nest in obj lock here */ - if (!UpdateWatchpointShape(cx, wp, shape)) { - /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */ - JS_INIT_CLIST(&wp->links); - DBG_LOCK(rt); - DropWatchPointAndUnlock(cx, wp, JSWP_LIVE, false); - return false; - } - - /* - * Now that wp is fully initialized, append it to rt's wp list. - * Because obj is locked we know that no other thread could have added - * a watchpoint for (obj, propid). - */ - DBG_LOCK(rt); - JS_ASSERT(!LockedFindWatchPoint(rt, obj, propid)); - JS_APPEND_LINK(&wp->links, &rt->watchPointList); - ++rt->debuggerMutations; + return false; } - /* - * Ensure that an object with watchpoints never has the same shape as an - * object without them, even if the watched properties are deleted. - */ - obj->watchpointOwnShapeChange(cx); + types::MarkTypePropertyConfigured(cx, obj, propid); - wp->handler = handler; - wp->closure = reinterpret_cast(closure); - DBG_UNLOCK(rt); - return true; + WatchpointMap *wpmap = cx->compartment->watchpointMap; + if (!wpmap) { + wpmap = cx->runtime->new_(); + if (!wpmap || !wpmap->init()) { + js_ReportOutOfMemory(cx); + return false; + } + cx->compartment->watchpointMap = wpmap; + } + return wpmap->watch(cx, obj, propid, handler, closure); } JS_PUBLIC_API(JSBool) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id, JSWatchPointHandler *handlerp, JSObject **closurep) { - assertSameCompartment(cx, obj); + assertSameCompartment(cx, obj, id); - JSRuntime *rt; - JSWatchPoint *wp; - - rt = cx->runtime; - DBG_LOCK(rt); - for (wp = (JSWatchPoint *)rt->watchPointList.next; - &wp->links != &rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == obj && SHAPE_USERID(wp->shape) == id) { - if (handlerp) - *handlerp = wp->handler; - if (closurep) - *closurep = wp->closure; - return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE, false); - } - } - DBG_UNLOCK(rt); - if (handlerp) - *handlerp = NULL; - if (closurep) - *closurep = NULL; - return JS_TRUE; + id = js_CheckForStringIndex(id); + if (WatchpointMap *wpmap = cx->compartment->watchpointMap) + wpmap->unwatch(obj, id, handlerp, closurep); + return true; } JS_PUBLIC_API(JSBool) @@ -1233,53 +331,19 @@ JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) { assertSameCompartment(cx, obj); - JSRuntime *rt; - JSWatchPoint *wp, *next; - uint32 sample; - - rt = cx->runtime; - DBG_LOCK(rt); - for (wp = (JSWatchPoint *)rt->watchPointList.next; - &wp->links != &rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (wp->object == obj) { - sample = rt->debuggerMutations; - if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE, false)) - return JS_FALSE; - DBG_LOCK(rt); - if (rt->debuggerMutations != sample + 1) - next = (JSWatchPoint *)rt->watchPointList.next; - } - } - DBG_UNLOCK(rt); - return JS_TRUE; + if (WatchpointMap *wpmap = cx->compartment->watchpointMap) + wpmap->unwatchObject(obj); + return true; } JS_PUBLIC_API(JSBool) JS_ClearAllWatchPoints(JSContext *cx) { - JSRuntime *rt; - JSWatchPoint *wp, *next; - uint32 sample; - - rt = cx->runtime; - DBG_LOCK(rt); - for (wp = (JSWatchPoint *)rt->watchPointList.next; - &wp->links != &rt->watchPointList; - wp = next) { - SwitchToCompartment sc(cx, wp->object); - - next = (JSWatchPoint *)wp->links.next; - sample = rt->debuggerMutations; - if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE, false)) - return JS_FALSE; - DBG_LOCK(rt); - if (rt->debuggerMutations != sample + 1) - next = (JSWatchPoint *)rt->watchPointList.next; + if (JSCompartment *comp = cx->compartment) { + if (WatchpointMap *wpmap = comp->watchpointMap) + wpmap->clear(); } - DBG_UNLOCK(rt); - return JS_TRUE; + return true; } /************************************************************************/ @@ -1302,6 +366,59 @@ JS_EndPC(JSContext *cx, JSScript *script) return script->code + script->length; } +JS_PUBLIC_API(JSBool) +JS_GetLinePCs(JSContext *cx, JSScript *script, + uintN startLine, uintN maxLines, + uintN* count, uintN** retLines, jsbytecode*** retPCs) +{ + uintN* lines; + jsbytecode** pcs; + size_t len = (script->length > maxLines ? maxLines : script->length); + lines = (uintN*) cx->malloc_(len * sizeof(uintN)); + if (!lines) + return JS_FALSE; + + pcs = (jsbytecode**) cx->malloc_(len * sizeof(jsbytecode*)); + if (!pcs) { + cx->free_(lines); + return JS_FALSE; + } + + uintN lineno = script->lineno; + uintN offset = 0; + uintN i = 0; + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + offset += SN_DELTA(sn); + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE || type == SRC_NEWLINE) { + if (type == SRC_SETLINE) + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + else + lineno++; + + if (lineno >= startLine) { + lines[i] = lineno; + pcs[i] = script->code + offset; + if (++i >= maxLines) + break; + } + } + } + + *count = i; + if (retLines) + *retLines = lines; + else + cx->free_(lines); + + if (retPCs) + *retPCs = pcs; + else + cx->free_(pcs); + + return JS_TRUE; +} + JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun) { @@ -1314,15 +431,29 @@ JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun) return fun->script()->bindings.hasLocalNames(); } -extern JS_PUBLIC_API(jsuword *) +extern JS_PUBLIC_API(uintptr_t *) JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp) { - *markp = JS_ARENA_MARK(&cx->tempPool); - return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool); + Vector localNames(cx); + if (!fun->script()->bindings.getLocalNameArray(cx, &localNames)) + return NULL; + + /* Munge data into the API this method implements. Avert your eyes! */ + *markp = cx->tempLifoAlloc().mark(); + + uintptr_t *names = cx->tempLifoAlloc().newArray(localNames.length()); + if (!names) { + js_ReportOutOfMemory(cx); + return NULL; + } + + JS_ASSERT(sizeof(*names) == sizeof(*localNames.begin())); + js_memcpy(names, localNames.begin(), localNames.length() * sizeof(*names)); + return names; } extern JS_PUBLIC_API(JSAtom *) -JS_LocalNameToAtom(jsuword w) +JS_LocalNameToAtom(uintptr_t w) { return JS_LOCAL_NAME_TO_ATOM(w); } @@ -1336,19 +467,19 @@ JS_AtomKey(JSAtom *atom) extern JS_PUBLIC_API(void) JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + cx->tempLifoAlloc().release(mark); } JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun) { - return FUN_SCRIPT(fun); + return fun->maybeScript(); } JS_PUBLIC_API(JSNative) JS_GetFunctionNative(JSContext *cx, JSFunction *fun) { - return Jsvalify(fun->maybeNative()); + return fun->maybeNative(); } JS_PUBLIC_API(JSPrincipals *) @@ -1357,6 +488,12 @@ JS_GetScriptPrincipals(JSContext *cx, JSScript *script) return script->principals; } +JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script) +{ + return script->originPrincipals; +} + /************************************************************************/ /* @@ -1366,7 +503,7 @@ JS_PUBLIC_API(JSStackFrame *) JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) { StackFrame *fp = Valueify(*iteratorp); - *iteratorp = Jsvalify((fp == NULL) ? js_GetTopStackFrame(cx) : fp->prev()); + *iteratorp = Jsvalify((fp == NULL) ? js_GetTopStackFrame(cx, FRAME_EXPAND_ALL) : fp->prev()); return *iteratorp; } @@ -1379,7 +516,7 @@ JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(jsbytecode *) JS_GetFramePC(JSContext *cx, JSStackFrame *fp) { - return Valueify(fp)->pc(cx); + return Valueify(fp)->pcQuadratic(cx->stack); } JS_PUBLIC_API(JSStackFrame *) @@ -1395,7 +532,7 @@ JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fpArg) if (fp->annotation() && fp->isScriptFrame()) { JSPrincipals *principals = fp->scopeChain().principals(cx); - if (principals && principals->globalPrivilegesEnabled(cx, principals)) { + if (principals) { /* * Give out an annotation only if privileges have not been revoked * or disabled globally. @@ -1413,35 +550,17 @@ JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) Valueify(fp)->setAnnotation(annotation); } -JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) -{ - JSPrincipals *principals; - - principals = Valueify(fp)->scopeChain().principals(cx); - if (!principals) - return NULL; - return principals->getPrincipalArray(cx, principals); -} - JS_PUBLIC_API(JSBool) JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp) { return !Valueify(fp)->isDummyFrame(); } -/* this is deprecated, use JS_GetFrameScopeChain instead */ -JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) -{ - return &Valueify(fp)->scopeChain(); -} - JS_PUBLIC_API(JSObject *) JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg) { StackFrame *fp = Valueify(fpArg); - JS_ASSERT(cx->stack.contains(fp)); + JS_ASSERT(cx->stack.containsSlow(fp)); js::AutoCompartment ac(cx, &fp->scopeChain()); if (!ac.enter()) @@ -1456,7 +575,7 @@ JS_PUBLIC_API(JSObject *) JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg) { StackFrame *fp = Valueify(fpArg); - JS_ASSERT(cx->stack.contains(fp)); + JS_ASSERT(cx->stack.containsSlow(fp)); if (!fp->isFunctionFrame()) return NULL; @@ -1487,14 +606,14 @@ JS_GetFrameThis(JSContext *cx, JSStackFrame *fpArg, jsval *thisv) if (!ComputeThis(cx, fp)) return false; - *thisv = Jsvalify(fp->thisValue()); + *thisv = fp->thisValue(); return true; } JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) { - return Valueify(fp)->maybeFun(); + return Valueify(fp)->maybeScriptFunction(); } JS_PUBLIC_API(JSObject *) @@ -1505,10 +624,21 @@ JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fpArg) return NULL; JS_ASSERT(fp->callee().isFunction()); - JS_ASSERT(fp->callee().getPrivate() == fp->fun()); return &fp->callee(); } +JS_PUBLIC_API(JSFunction *) +JS_GetScriptFunction(JSContext *cx, JSScript *script) +{ + return script->function(); +} + +JS_PUBLIC_API(JSObject *) +JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj) +{ + return obj->enclosingScope(); +} + JS_PUBLIC_API(JSBool) JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) { @@ -1518,7 +648,7 @@ JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(JSObject *) JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) { - return Valueify(fp)->maybeCallee(); + return Valueify(fp)->maybeCalleev().toObjectOrNull(); } JS_PUBLIC_API(JSBool) @@ -1528,7 +658,7 @@ JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp) if (!Valueify(fp)->getValidCalleeObject(cx, &v)) return false; - *vp = Jsvalify(v); + *vp = v.isObject() ? v : JSVAL_VOID; return true; } @@ -1538,10 +668,16 @@ JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) return Valueify(fp)->isDebuggerFrame(); } +JS_PUBLIC_API(JSBool) +JS_IsGlobalFrame(JSContext *cx, JSStackFrame *fp) +{ + return Valueify(fp)->isGlobalFrame(); +} + JS_PUBLIC_API(jsval) JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp) { - return Jsvalify(Valueify(fp)->returnValue()); + return Valueify(fp)->returnValue(); } JS_PUBLIC_API(void) @@ -1552,7 +688,7 @@ JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fpArg, jsval rval) JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode); #endif assertSameCompartment(cx, fp, rval); - fp->setReturnValue(Valueify(rval)); + fp->setReturnValue(rval); } /************************************************************************/ @@ -1563,6 +699,12 @@ JS_GetScriptFilename(JSContext *cx, JSScript *script) return script->filename; } +JS_PUBLIC_API(const jschar *) +JS_GetScriptSourceMap(JSContext *cx, JSScript *script) +{ + return script->sourceMap; +} + JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) { @@ -1606,39 +748,19 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fpArg, const char *filename, uintN lineno, jsval *rval) { - JS_ASSERT_NOT_ON_TRACE(cx); - if (!CheckDebugMode(cx)) return false; - JSObject *scobj = JS_GetFrameScopeChain(cx, fpArg); - if (!scobj) + Env *env = JS_GetFrameScopeChain(cx, fpArg); + if (!env) return false; - js::AutoCompartment ac(cx, scobj); + js::AutoCompartment ac(cx, env); if (!ac.enter()) return false; - /* - * NB: This function breaks the assumption that the compiler can see all - * calls and properly compute a static level. In order to get around this, - * we use a static level that will cause us not to attempt to optimize - * variable references made by this frame. - */ StackFrame *fp = Valueify(fpArg); - JSScript *script = Compiler::compileScript(cx, scobj, fp, fp->scopeChain().principals(cx), - TCF_COMPILE_N_GO, chars, length, - filename, lineno, cx->findVersion(), - NULL, UpvarCookie::UPVAR_LEVEL_LIMIT); - - if (!script) - return false; - - uintN evalFlags = StackFrame::DEBUGGER | StackFrame::EVAL; - bool ok = Execute(cx, *scobj, script, fp, evalFlags, Valueify(rval)); - - js_DestroyScript(cx, script); - return ok; + return EvaluateInEnv(cx, env, fp, chars, length, filename, lineno, rval); } JS_PUBLIC_API(JSBool) @@ -1650,11 +772,11 @@ JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, jschar *chars; JSBool ok; size_t len = length; - + if (!CheckDebugMode(cx)) return JS_FALSE; - chars = js_InflateString(cx, bytes, &len); + chars = InflateString(cx, bytes, &len); if (!chars) return JS_FALSE; length = (uintN) len; @@ -1676,14 +798,14 @@ JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) /* The caller passes null in *iteratorp to get things started. */ shape = (Shape *) *iteratorp; - if (!shape) { + if (!shape) shape = obj->lastProperty(); - } else { + else shape = shape->previous(); - if (!shape->previous()) { - JS_ASSERT(JSID_IS_EMPTY(shape->id)); - shape = NULL; - } + + if (!shape->previous()) { + JS_ASSERT(shape->isEmptyShape()); + shape = NULL; } return *iteratorp = reinterpret_cast(const_cast(shape)); @@ -1695,7 +817,7 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, { assertSameCompartment(cx, obj); Shape *shape = (Shape *) sprop; - pd->id = IdToJsval(shape->id); + pd->id = IdToJsval(shape->propid()); JSBool wasThrowing = cx->isExceptionPending(); Value lastException = UndefinedValue(); @@ -1703,13 +825,13 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, lastException = cx->getPendingException(); cx->clearPendingException(); - if (!js_GetProperty(cx, obj, shape->id, Valueify(&pd->value))) { + if (!js_GetProperty(cx, obj, shape->propid(), &pd->value)) { if (!cx->isExceptionPending()) { pd->flags = JSPD_ERROR; pd->value = JSVAL_VOID; } else { pd->flags = JSPD_EXCEPTION; - pd->value = Jsvalify(cx->getPendingException()); + pd->value = cx->getPendingException(); } } else { pd->flags = 0; @@ -1723,25 +845,16 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, | (!shape->configurable() ? JSPD_PERMANENT : 0); pd->spare = 0; if (shape->getter() == GetCallArg) { - pd->slot = shape->shortid; + pd->slot = shape->shortid(); pd->flags |= JSPD_ARGUMENT; } else if (shape->getter() == GetCallVar) { - pd->slot = shape->shortid; + pd->slot = shape->shortid(); pd->flags |= JSPD_VARIABLE; } else { pd->slot = 0; } pd->alias = JSVAL_VOID; - if (obj->containsSlot(shape->slot)) { - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - const Shape &aprop = r.front(); - if (&aprop != shape && aprop.slot == shape->slot) { - pd->alias = IdToJsval(aprop.id); - break; - } - } - } return JS_TRUE; } @@ -1765,20 +878,20 @@ JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) return JS_TRUE; } - uint32 n = obj->propertyCount(); + uint32_t n = obj->propertyCount(); JSPropertyDesc *pd = (JSPropertyDesc *) cx->malloc_(size_t(n) * sizeof(JSPropertyDesc)); if (!pd) return JS_FALSE; - uint32 i = 0; + uint32_t i = 0; for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL)) + if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; - if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL)) + if (!js_AddRoot(cx, &pd[i].value, NULL)) goto bad; Shape *shape = const_cast(&r.front()); if (!JS_GetPropertyDesc(cx, obj, reinterpret_cast(shape), &pd[i])) goto bad; - if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL)) + if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == n) break; @@ -1798,7 +911,7 @@ JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) { JSPropertyDesc *pd; - uint32 i; + uint32_t i; pd = pda->array; for (i = 0; i < pda->length; i++) { @@ -1839,17 +952,8 @@ JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) JS_PUBLIC_API(JSBool) JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) { -#ifdef JS_TRACER - { - AutoLockGC lock(rt); - bool wasInhibited = rt->debuggerInhibitsJIT(); -#endif - rt->globalDebugHooks.callHook = hook; - rt->globalDebugHooks.callHookData = closure; -#ifdef JS_TRACER - JITInhibitingHookChange(rt, wasInhibited); - } -#endif + rt->globalDebugHooks.callHook = hook; + rt->globalDebugHooks.callHookData = closure; return JS_TRUE; } @@ -1874,7 +978,7 @@ JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) JS_PUBLIC_API(size_t) JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) { - return obj->slotsAndStructSize(); + return obj->computedSizeOfThisSlotsElements(); } static size_t @@ -1894,33 +998,27 @@ JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) size_t nbytes; nbytes = sizeof *fun; - nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun)); - if (FUN_INTERPRETED(fun)) - nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); + nbytes += JS_GetObjectTotalSize(cx, fun); + if (fun->isInterpreted()) + nbytes += JS_GetScriptTotalSize(cx, fun->script()); if (fun->atom) nbytes += GetAtomTotalSize(cx, fun->atom); return nbytes; } -#include "jsemit.h" - JS_PUBLIC_API(size_t) JS_GetScriptTotalSize(JSContext *cx, JSScript *script) { size_t nbytes, pbytes; - jsatomid i; jssrcnote *sn, *notes; JSObjectArray *objarray; JSPrincipals *principals; nbytes = sizeof *script; - if (script->u.object) - nbytes += JS_GetObjectTotalSize(cx, script->u.object); - nbytes += script->length * sizeof script->code[0]; - nbytes += script->atomMap.length * sizeof script->atomMap.vector[0]; - for (i = 0; i < script->atomMap.length; i++) - nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]); + nbytes += script->natoms * sizeof script->atoms[0]; + for (size_t i = 0; i < script->natoms; i++) + nbytes += GetAtomTotalSize(cx, script->atoms[i]); if (script->filename) nbytes += strlen(script->filename) + 1; @@ -1932,7 +1030,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script) if (JSScript::isValidOffset(script->objectsOffset)) { objarray = script->objects(); - i = objarray->length; + size_t i = objarray->length; nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; do { nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]); @@ -1941,7 +1039,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script) if (JSScript::isValidOffset(script->regexpsOffset)) { objarray = script->regexps(); - i = objarray->length; + size_t i = objarray->length; nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; do { nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]); @@ -1974,8 +1072,7 @@ JS_IsSystemObject(JSContext *cx, JSObject *obj) JS_PUBLIC_API(JSBool) JS_MakeSystemObject(JSContext *cx, JSObject *obj) { - obj->setSystem(); - return true; + return obj->setSystem(cx); } /************************************************************************/ @@ -1986,87 +1083,377 @@ js_RevertVersion(JSContext *cx) cx->clearVersionOverride(); } -JS_PUBLIC_API(const JSDebugHooks *) -JS_GetGlobalDebugHooks(JSRuntime *rt) -{ - return &rt->globalDebugHooks; +JS_PUBLIC_API(const JSDebugHooks *) +JS_GetGlobalDebugHooks(JSRuntime *rt) +{ + return &rt->globalDebugHooks; +} + +const JSDebugHooks js_NullDebugHooks = {}; + +JS_PUBLIC_API(JSDebugHooks *) +JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks) +{ + JS_ASSERT(hooks); + + JSDebugHooks *old = const_cast(cx->debugHooks); + cx->debugHooks = hooks; + return old; +} + +JS_PUBLIC_API(JSDebugHooks *) +JS_ClearContextDebugHooks(JSContext *cx) +{ + return JS_SetContextDebugHooks(cx, &js_NullDebugHooks); +} + +/************************************************************************/ + +/* Profiling-related API */ + +/* Thread-unsafe error management */ + +static char gLastError[2000]; + +static void +#ifdef __GNUC__ +__attribute__((unused,format(printf,1,2))) +#endif +UnsafeError(const char *format, ...) +{ + va_list args; + va_start(args, format); + (void) vsnprintf(gLastError, sizeof(gLastError), format, args); + va_end(args); + + gLastError[sizeof(gLastError) - 1] = '\0'; +} + +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError() +{ + return gLastError; +} + +JS_PUBLIC_API(JSBool) +JS_StartProfiling(const char *profileName) +{ + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark for %s", profileName); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (!js_StartVtune(profileName)) + ok = JS_FALSE; +#endif + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName) +{ + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_VTUNE + if (!js_StopVtune()) + ok = JS_FALSE; +#endif + return ok; +} + +/* + * Start or stop whatever platform- and configuration-specific profiling + * backends are available. + */ +static JSBool +ControlProfilers(bool toState) +{ + JSBool ok = JS_TRUE; + + if (! Probes::ProfilingActive && toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_CALLGRIND + if (! js_StartCallgrind()) { + UnsafeError("Failed to start Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_ResumeVtune()) + ok = JS_FALSE; +#endif + } else if (Probes::ProfilingActive && ! toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_CALLGRIND + if (! js_StopCallgrind()) { + UnsafeError("failed to stop Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_PauseVtune()) + ok = JS_FALSE; +#endif + } + + Probes::ProfilingActive = toState; + + return ok; +} + +/* + * Pause/resume whatever profiling mechanism is currently compiled + * in, if applicable. This will not affect things like dtrace. + * + * Do not mix calls to these APIs with calls to the individual + * profilers' pause/resume functions, because only overall state is + * tracked, not the state of each profiler. + */ +JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName) +{ + return ControlProfilers(false); +} + +JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName) +{ + return ControlProfilers(true); +} + +JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName) +{ + JSBool ok = JS_TRUE; +#ifdef MOZ_CALLGRIND + js_DumpCallgrind(outfile); +#endif + return ok; +} + +#ifdef MOZ_PROFILING + +struct RequiredStringArg { + JSContext *mCx; + char *mBytes; + RequiredStringArg(JSContext *cx, uintN argc, jsval *vp, size_t argi, const char *caller) + : mCx(cx), mBytes(NULL) + { + if (argc <= argi) { + JS_ReportError(cx, "%s: not enough arguments", caller); + } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) { + JS_ReportError(cx, "%s: invalid arguments (string expected)", caller); + } else { + mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi])); + } + } + operator void*() { + return (void*) mBytes; + } + ~RequiredStringArg() { + if (mBytes) + mCx->free_(mBytes); + } +}; + +static JSBool +StartProfiling(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +StopProfiling(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +PauseProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +ResumeProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes))); + return JS_TRUE; +} + +/* Usage: DumpProfile([filename[, profileName]]) */ +static JSBool +DumpProfile(JSContext *cx, uintN argc, jsval *vp) +{ + bool ret; + if (argc == 0) { + ret = JS_DumpProfile(NULL, NULL); + } else { + RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile"); + if (!filename) + return JS_FALSE; + + if (argc == 1) { + ret = JS_DumpProfile(filename.mBytes, NULL); + } else { + RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile"); + if (!profileName) + return JS_FALSE; + + ret = JS_DumpProfile(filename.mBytes, profileName.mBytes); + } + } + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret)); + return true; } -const JSDebugHooks js_NullDebugHooks = {}; +#ifdef MOZ_SHARK -JS_PUBLIC_API(JSDebugHooks *) -JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks) +static JSBool +IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp) { - JS_ASSERT(hooks); - if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks) - LeaveTrace(cx); + JS_SET_RVAL(cx, vp, JSVAL_TRUE); + return true; +} -#ifdef JS_TRACER - AutoLockGC lock(cx->runtime); -#endif - JSDebugHooks *old = const_cast(cx->debugHooks); - cx->debugHooks = hooks; -#ifdef JS_TRACER - cx->updateJITEnabled(); #endif - return old; -} -JS_PUBLIC_API(JSDebugHooks *) -JS_ClearContextDebugHooks(JSContext *cx) +#ifdef MOZ_CALLGRIND +static JSBool +StartCallgrind(JSContext *cx, uintN argc, jsval *vp) { - return JS_SetContextDebugHooks(cx, &js_NullDebugHooks); + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind())); + return JS_TRUE; } -JS_PUBLIC_API(JSBool) -JS_StartProfiling() +static JSBool +StopCallgrind(JSContext *cx, uintN argc, jsval *vp) { - return Probes::startProfiling(); + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind())); + return JS_TRUE; } -JS_PUBLIC_API(void) -JS_StopProfiling() +static JSBool +DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) { - Probes::stopProfiling(); -} + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL))); + return JS_TRUE; + } -#ifdef MOZ_PROFILING + RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind"); + if (!outFile) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes))); + return JS_TRUE; +} +#endif +#ifdef MOZ_VTUNE static JSBool -StartProfiling(JSContext *cx, uintN argc, jsval *vp) +StartVtune(JSContext *cx, uintN argc, jsval *vp) { - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling())); - return true; + RequiredStringArg profileName(cx, argc, vp, 0, "startVtune"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes))); + return JS_TRUE; } static JSBool -StopProfiling(JSContext *cx, uintN argc, jsval *vp) +StopVtune(JSContext *cx, uintN argc, jsval *vp) { - JS_StopProfiling(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune())); + return JS_TRUE; } -#ifdef MOZ_SHARK - static JSBool -IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp) +PauseVtune(JSContext *cx, uintN argc, jsval *vp) { - JS_SET_RVAL(cx, vp, JSVAL_TRUE); - return true; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune())); + return JS_TRUE; } +static JSBool +ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune())); + return JS_TRUE; +} #endif static JSFunctionSpec profiling_functions[] = { - JS_FN("startProfiling", StartProfiling, 0,0), - JS_FN("stopProfiling", StopProfiling, 0,0), + JS_FN("startProfiling", StartProfiling, 1,0), + JS_FN("stopProfiling", StopProfiling, 1,0), + JS_FN("pauseProfilers", PauseProfilers, 1,0), + JS_FN("resumeProfilers", ResumeProfilers, 1,0), + JS_FN("dumpProfile", DumpProfile, 2,0), #ifdef MOZ_SHARK /* Keep users of the old shark API happy. */ JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), JS_FN("startShark", StartProfiling, 0,0), JS_FN("stopShark", StopProfiling, 0,0), +#endif +#ifdef MOZ_CALLGRIND + JS_FN("startCallgrind", StartCallgrind, 0,0), + JS_FN("stopCallgrind", StopCallgrind, 0,0), + JS_FN("dumpCallgrind", DumpCallgrind, 1,0), +#endif +#ifdef MOZ_VTUNE + JS_FN("startVtune", js_StartVtune, 1,0), + JS_FN("stopVtune", js_StopVtune, 0,0), + JS_FN("pauseVtune", js_PauseVtune, 0,0), + JS_FN("resumeVtune", js_ResumeVtune, 0,0), #endif JS_FS_END }; @@ -2089,40 +1476,30 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj) #include JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StartCallgrind() { - CALLGRIND_START_INSTRUMENTATION; - CALLGRIND_ZERO_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION); + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS); + return true; } JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StopCallgrind() { - CALLGRIND_STOP_INSTRUMENTATION; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION); + return true; } JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_DumpCallgrind(const char *outfile) { - JSString *str; - - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - JSAutoByteString bytes(cx, str); - if (!!bytes) { - CALLGRIND_DUMP_STATS_AT(bytes.ptr()); - return JS_TRUE; - } + if (outfile) { + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile)); + } else { + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS); } - CALLGRIND_DUMP_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } #endif /* MOZ_CALLGRIND */ @@ -2157,8 +1534,8 @@ static const char *vtuneErrorMessages[] = { }; -JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StartVtune(const char *profileName) { VTUNE_EVENT events[] = { { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, @@ -2185,528 +1562,122 @@ js_StartVtune(JSContext *cx, uintN argc, jsval *vp) default_filename, }; - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - params.tb5Filename = js_DeflateString(cx, str->chars(), str->length()); + if (profileName) { + char filename[strlen(profileName) + strlen("-vtune.tb5") + 1]; + snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName); + params.tb5Filename = filename; } status = VTStartSampling(¶ms); if (params.tb5Filename != default_filename) - cx->free_(params.tb5Filename); + Foreground::free_(params.tb5Filename); if (status != 0) { if (status == VTAPI_MULTIPLE_RUNS) VTStopSampling(0); if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune setup error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune setup error: %d", - status); + UnsafeError("Vtune setup error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StopVtune() { U32 status = VTStopSampling(1); if (status) { if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune shutdown error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune shutdown error: %d", - status); + UnsafeError("Vtune shutdown error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_PauseVtune() { VTPause(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_ResumeVtune() { VTResume(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } #endif /* MOZ_VTUNE */ -#ifdef MOZ_TRACEVIS -/* - * Ethogram - Javascript wrapper for TraceVis state - * - * ethology: The scientific study of animal behavior, - * especially as it occurs in a natural environment. - * ethogram: A pictorial catalog of the behavioral patterns of - * an organism or a species. - * - */ -#if defined(XP_WIN) -#include "jswin.h" -#else -#include -#endif - -#define ETHOGRAM_BUF_SIZE 65536 - -static JSBool -ethogram_construct(JSContext *cx, uintN argc, jsval *vp); -static void -ethogram_finalize(JSContext *cx, JSObject *obj); - -static JSClass ethogram_class = { - "Ethogram", - JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -struct EthogramEvent { - TraceVisState s; - TraceVisExitReason r; - int ts; - int tus; - JSString *filename; - int lineno; -}; - -static int -compare_strings(const void *k1, const void *k2) +JS_PUBLIC_API(void) +JS_DumpBytecode(JSContext *cx, JSScript *script) { - return strcmp((const char *) k1, (const char *) k2) == 0; -} - -class EthogramEventBuffer { -private: - EthogramEvent mBuf[ETHOGRAM_BUF_SIZE]; - int mReadPos; - int mWritePos; - JSObject *mFilenames; - int mStartSecond; - - struct EthogramScriptEntry { - char *filename; - JSString *jsfilename; +#if defined(DEBUG) + Sprinter sprinter(cx); + if (!sprinter.init()) + return; - EthogramScriptEntry *next; - }; - EthogramScriptEntry *mScripts; - -public: - friend JSBool - ethogram_construct(JSContext *cx, uintN argc, jsval *vp); - - inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) { - mBuf[mWritePos].s = s; - mBuf[mWritePos].r = r; -#if defined(XP_WIN) - FILETIME now; - GetSystemTimeAsFileTime(&now); - unsigned long long raw_us = 0.1 * - (((unsigned long long) now.dwHighDateTime << 32ULL) | - (unsigned long long) now.dwLowDateTime); - unsigned int sec = raw_us / 1000000L; - unsigned int usec = raw_us % 1000000L; - mBuf[mWritePos].ts = sec - mStartSecond; - mBuf[mWritePos].tus = usec; -#else - struct timeval tv; - gettimeofday(&tv, NULL); - mBuf[mWritePos].ts = tv.tv_sec - mStartSecond; - mBuf[mWritePos].tus = tv.tv_usec; + fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); + js_Disassemble(cx, script, true, &sprinter); + fputs(sprinter.string(), stdout); + fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif - - JSString *jsfilename = findScript(filename); - mBuf[mWritePos].filename = jsfilename; - mBuf[mWritePos].lineno = lineno; - - mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE; - if (mWritePos == mReadPos) { - mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE; - } - } - - inline EthogramEvent *pop() { - EthogramEvent *e = &mBuf[mReadPos]; - mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE; - return e; - } - - bool isEmpty() { - return (mReadPos == mWritePos); - } - - EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) { - JSHashNumber hash = JS_HashString(filename); - JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename); - if (*hep != NULL) - return NULL; - - JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this); - - EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry)); - if (entry == NULL) - return NULL; - - entry->next = mScripts; - mScripts = entry; - entry->filename = filename; - entry->jsfilename = jsfilename; - - return mScripts; - } - - void removeScripts(JSContext *cx) { - EthogramScriptEntry *se = mScripts; - while (se != NULL) { - char *filename = se->filename; - - JSHashNumber hash = JS_HashString(filename); - JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename); - JSHashEntry *he = *hep; - if (he) { - /* we hardly knew he */ - JS_HashTableRawRemove(traceVisScriptTable, hep, he); - } - - EthogramScriptEntry *se_head = se; - se = se->next; - JS_free(cx, se_head); - } - } - - JSString *findScript(char *filename) { - EthogramScriptEntry *se = mScripts; - while (se != NULL) { - if (compare_strings(se->filename, filename)) - return (se->jsfilename); - se = se->next; - } - return NULL; - } - - JSObject *filenames() { - return mFilenames; - } - - int length() { - if (mWritePos < mReadPos) - return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos; - else - return mWritePos - mReadPos; - } -}; - -static char jstv_empty[] = ""; - -inline char * -jstv_Filename(JSStackFrame *fp) -{ - while (fp && !fp->isScriptFrame()) - fp = fp->prev(); - return (fp && fp->maybeScript() && fp->script()->filename) - ? (char *)fp->script()->filename - : jstv_empty; -} -inline uintN -jstv_Lineno(JSContext *cx, JSStackFrame *fp) -{ - while (fp && fp->pc(cx) == NULL) - fp = fp->prev(); - return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0; -} - -/* Collect states here and distribute to a matching buffer, if any */ -JS_FRIEND_API(void) -js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) -{ - StackFrame *fp = cx->fp(); - - char *script_file = jstv_Filename(fp); - JSHashNumber hash = JS_HashString(script_file); - - JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file); - /* update event buffer, flag if overflowed */ - JSHashEntry *he = *hep; - if (he) { - EthogramEventBuffer *p; - p = (EthogramEventBuffer *) he->value; - - p->push(s, r, script_file, jstv_Lineno(cx, fp)); - } } -static JSBool -ethogram_construct(JSContext *cx, uintN argc, jsval *vp) +extern JS_PUBLIC_API(void) +JS_DumpPCCounts(JSContext *cx, JSScript *script) { - EthogramEventBuffer *p; +#if defined(DEBUG) + JS_ASSERT(script->pcCounters); - p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer)); - if (!p) - return JS_FALSE; + Sprinter sprinter(cx); + if (!sprinter.init()) + return; - p->mReadPos = p->mWritePos = 0; - p->mScripts = NULL; - p->mFilenames = JS_NewArrayObject(cx, 0, NULL); - -#if defined(XP_WIN) - FILETIME now; - GetSystemTimeAsFileTime(&now); - unsigned long long raw_us = 0.1 * - (((unsigned long long) now.dwHighDateTime << 32ULL) | - (unsigned long long) now.dwLowDateTime); - unsigned int s = raw_us / 1000000L; - p->mStartSecond = s; -#else - struct timeval tv; - gettimeofday(&tv, NULL); - p->mStartSecond = tv.tv_sec; + fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); + js_DumpPCCounts(cx, script, &sprinter); + fputs(sprinter.string(), stdout); + fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif - JSObject *obj; - if (JS_IsConstructing(cx, vp)) { - obj = JS_NewObject(cx, ðogram_class, NULL, NULL); - if (!obj) - return JS_FALSE; - } else { - obj = JS_THIS_OBJECT(cx, vp); - } - - jsval filenames = OBJECT_TO_JSVAL(p->filenames()); - if (!JS_DefineProperty(cx, obj, "filenames", filenames, - NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT)) - return JS_FALSE; - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); - JS_SetPrivate(cx, obj, p); - return JS_TRUE; } static void -ethogram_finalize(JSContext *cx, JSObject *obj) -{ - EthogramEventBuffer *p; - p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, NULL); - if (!p) - return; - - p->removeScripts(cx); - - JS_free(cx, p); -} - -static JSBool -ethogram_addScript(JSContext *cx, uintN argc, jsval *vp) -{ - JSString *str; - char *filename = NULL; - jsval *argv = JS_ARGV(cx, vp); - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return false; - if (argc < 1) { - /* silently ignore no args */ - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; - } - if (JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - filename = js_DeflateString(cx, str->chars(), str->length()); - if (!filename) - return false; - } - - EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); - - p->addScript(cx, obj, filename, str); - JS_SET_RVAL(cx, vp, JSVAL_VOID); - jsval dummy; - JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, &dummy); - return true; -} - -static JSBool -ethogram_getAllEvents(JSContext *cx, uintN argc, jsval *vp) -{ - EthogramEventBuffer *p; - jsval *argv = JS_ARGV(cx, vp); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); - if (!p) - return JS_FALSE; - - if (p->isEmpty()) { - JS_SET_RVAL(cx, vp, JSVAL_NULL); - return JS_TRUE; - } - - JSObject *rarray = JS_NewArrayObject(cx, 0, NULL); - if (rarray == NULL) { - JS_SET_RVAL(cx, vp, JSVAL_NULL); - return JS_TRUE; - } - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rarray)); - - for (int i = 0; !p->isEmpty(); i++) { - - JSObject *x = JS_NewObject(cx, NULL, NULL, NULL); - if (x == NULL) - return JS_FALSE; - - EthogramEvent *e = p->pop(); - - jsval state = INT_TO_JSVAL(e->s); - jsval reason = INT_TO_JSVAL(e->r); - jsval ts = INT_TO_JSVAL(e->ts); - jsval tus = INT_TO_JSVAL(e->tus); - - jsval filename = STRING_TO_JSVAL(e->filename); - jsval lineno = INT_TO_JSVAL(e->lineno); - - if (!JS_SetProperty(cx, x, "state", &state)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "reason", &reason)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "ts", &ts)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "tus", &tus)) - return JS_FALSE; - - if (!JS_SetProperty(cx, x, "filename", &filename)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "lineno", &lineno)) - return JS_FALSE; - - jsval element = OBJECT_TO_JSVAL(x); - JS_SetElement(cx, rarray, i, &element); - } - - return JS_TRUE; -} - -static JSBool -ethogram_getNextEvent(JSContext *cx, uintN argc, jsval *vp) -{ - EthogramEventBuffer *p; - jsval *argv = JS_ARGV(cx, vp); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); - if (!p) - return JS_FALSE; - - JSObject *x = JS_NewObject(cx, NULL, NULL, NULL); - if (x == NULL) - return JS_FALSE; - - if (p->isEmpty()) { - JS_SET_RVAL(cx, vp, JSVAL_NULL); - return JS_TRUE; - } - - EthogramEvent *e = p->pop(); - jsval state = INT_TO_JSVAL(e->s); - jsval reason = INT_TO_JSVAL(e->r); - jsval ts = INT_TO_JSVAL(e->ts); - jsval tus = INT_TO_JSVAL(e->tus); - - jsval filename = STRING_TO_JSVAL(e->filename); - jsval lineno = INT_TO_JSVAL(e->lineno); - - if (!JS_SetProperty(cx, x, "state", &state)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "reason", &reason)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "ts", &ts)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "tus", &tus)) - return JS_FALSE; - if (!JS_SetProperty(cx, x, "filename", &filename)) - return JS_FALSE; - - if (!JS_SetProperty(cx, x, "lineno", &lineno)) - return JS_FALSE; - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(x)); - - return JS_TRUE; -} - -static JSFunctionSpec ethogram_methods[] = { - JS_FN("addScript", ethogram_addScript, 1,0), - JS_FN("getAllEvents", ethogram_getAllEvents, 0,0), - JS_FN("getNextEvent", ethogram_getNextEvent, 0,0), - JS_FS_END -}; - -/* - * An |Ethogram| organizes the output of a collection of files that should be - * monitored together. A single object gets events for the group. - */ -JS_FRIEND_API(JSBool) -js_InitEthogram(JSContext *cx, uintN argc, jsval *vp) +DumpBytecodeScriptCallback(JSContext *cx, void *data, void *thing, + JSGCTraceKind traceKind, size_t thingSize) { - if (!traceVisScriptTable) { - traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings, - NULL, NULL, NULL); - } - - JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, ðogram_class, - ethogram_construct, 0, NULL, ethogram_methods, - NULL, NULL); - - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; + JS_ASSERT(traceKind == JSTRACE_SCRIPT); + JSScript *script = static_cast(thing); + reinterpret_cast *>(data)->append(script); } -JS_FRIEND_API(JSBool) -js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp) +JS_PUBLIC_API(void) +JS_DumpCompartmentBytecode(JSContext *cx) { - if (traceVisScriptTable) - JS_HashTableDestroy(traceVisScriptTable); + Vector scripts(cx); + IterateCells(cx, cx->compartment, gc::FINALIZE_SCRIPT, &scripts, DumpBytecodeScriptCallback); - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; + for (size_t i = 0; i < scripts.length(); i++) + JS_DumpBytecode(cx, scripts[i]); } -#endif /* MOZ_TRACEVIS */ - -#ifdef MOZ_TRACE_JSCALLS - JS_PUBLIC_API(void) -JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb) +JS_DumpCompartmentPCCounts(JSContext *cx) { - cx->functionCallback = fcb; + for (CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->pcCounters) + JS_DumpPCCounts(cx, script); + } } -JS_PUBLIC_API(JSFunctionCallback) -JS_GetFunctionCallback(JSContext *cx) +JS_PUBLIC_API(JSObject *) +JS_UnwrapObject(JSObject *obj) { - return cx->functionCallback; + return UnwrapObject(obj); } - -#endif /* MOZ_TRACE_JSCALLS */ - diff --git a/deps/mozjs/js/src/jsdbgapi.h b/deps/mozjs/js/src/jsdbgapi.h index d82cb574824..6b5c8bb6b68 100644 --- a/deps/mozjs/js/src/jsdbgapi.h +++ b/deps/mozjs/js/src/jsdbgapi.h @@ -23,6 +23,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Nick Fitzgerald * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -44,7 +45,6 @@ * JS debugger API. */ #include "jsapi.h" -#include "jsopcode.h" #include "jsprvtd.h" JS_BEGIN_EXTERN_C @@ -52,6 +52,9 @@ JS_BEGIN_EXTERN_C extern JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target); +extern JS_PUBLIC_API(JSCrossCompartmentCall *) +JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target); + #ifdef __cplusplus JS_END_EXTERN_C @@ -59,6 +62,7 @@ namespace JS { class JS_PUBLIC_API(AutoEnterScriptCompartment) { + protected: JSCrossCompartmentCall *call; public: @@ -74,13 +78,22 @@ class JS_PUBLIC_API(AutoEnterScriptCompartment) } }; +class JS_PUBLIC_API(AutoEnterFrameCompartment) : public AutoEnterScriptCompartment +{ + public: + bool enter(JSContext *cx, JSStackFrame *target); +}; + } /* namespace JS */ -JS_BEGIN_EXTERN_C +#ifdef DEBUG +JS_FRIEND_API(void) js_DumpValue(const js::Value &val); +JS_FRIEND_API(void) js_DumpId(jsid id); +JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL); #endif -extern JS_PUBLIC_API(JSScript *) -JS_GetScriptFromObject(JSObject *scriptObject); +JS_BEGIN_EXTERN_C +#endif extern JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent); @@ -107,9 +120,9 @@ extern JS_PUBLIC_API(JSBool) JS_GetDebugMode(JSContext *cx); /* - * Turn on/off debugging mode for a single compartment. This must be - * called from the main thread and the compartment must be associated - * with the main thread. + * Turn on/off debugging mode for a single compartment. This should only be + * used when no code from this compartment is running or on the stack in any + * thread. */ JS_FRIEND_API(JSBool) JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug); @@ -120,30 +133,15 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug); JS_FRIEND_API(JSBool) JS_SetDebugMode(JSContext *cx, JSBool debug); -/* Turn on single step mode. Requires debug mode. */ -extern JS_FRIEND_API(JSBool) -js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); - /* Turn on single step mode. */ extern JS_PUBLIC_API(JSBool) JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep); -/* - * Unexported library-private helper used to unpatch all traps in a script. - * Returns script->code if script has no traps, else a JS_malloc'ed copy of - * script->code which the caller must JS_free, or null on JS_malloc OOM. - */ -extern jsbytecode * -js_UntrapScriptCode(JSContext *cx, JSScript *script); - /* The closure argument will be marked. */ extern JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, jsval closure); -extern JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); - extern JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, jsval *closurep); @@ -152,10 +150,7 @@ extern JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx); - -extern JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); +JS_ClearAllTrapsForCompartment(JSContext *cx); extern JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSInterruptHook handler, void *closure); @@ -179,33 +174,6 @@ JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_ClearAllWatchPoints(JSContext *cx); -#ifdef JS_HAS_OBJ_WATCHPOINT -/* - * Hide these non-API function prototypes by testing whether the internal - * header file "jsversion.h" has been included. - */ -extern JSBool -js_TraceWatchPoints(JSTracer *trc); - -extern void -js_SweepWatchPoints(JSContext *cx); - -#ifdef __cplusplus - -extern JSBool -js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp); - -namespace js { - -bool -IsWatchedProperty(JSContext *cx, const Shape *shape); - -} - -#endif - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - /************************************************************************/ extern JS_PUBLIC_API(uintN) @@ -217,6 +185,11 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); extern JS_PUBLIC_API(jsbytecode *) JS_EndPC(JSContext *cx, JSScript *script); +extern JS_PUBLIC_API(JSBool) +JS_GetLinePCs(JSContext *cx, JSScript *script, + uintN startLine, uintN maxLines, + uintN* count, uintN** lines, jsbytecode*** pcs); + extern JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun); @@ -228,11 +201,11 @@ JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun); * to call JS_ReleaseFunctionLocalNameArray in a LIFO manner (wrt to any other * call that may use the temp pool. */ -extern JS_PUBLIC_API(jsuword *) +extern JS_PUBLIC_API(uintptr_t *) JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp); extern JS_PUBLIC_API(JSAtom *) -JS_LocalNameToAtom(jsuword w); +JS_LocalNameToAtom(uintptr_t w); extern JS_PUBLIC_API(JSString *) JS_AtomKey(JSAtom *atom); @@ -249,6 +222,9 @@ JS_GetFunctionNative(JSContext *cx, JSFunction *fun); extern JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script); +extern JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script); + /* * Stack Frame Iterator * @@ -277,16 +253,9 @@ JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(void) JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); -extern JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); - extern JS_PUBLIC_API(JSBool) JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp); -/* this is deprecated, use JS_GetFrameScopeChain instead */ -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); - extern JS_PUBLIC_API(JSObject *) JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); @@ -302,6 +271,12 @@ JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); +JS_PUBLIC_API(JSFunction *) +JS_GetScriptFunction(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSObject *) +JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj); + /* XXXrginda Initially published with typo */ #define JS_IsContructorFrame JS_IsConstructorFrame extern JS_PUBLIC_API(JSBool) @@ -310,6 +285,9 @@ JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSBool) JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); +extern JS_PUBLIC_API(JSBool) +JS_IsGlobalFrame(JSContext *cx, JSStackFrame *fp); + extern JS_PUBLIC_API(jsval) JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); @@ -358,6 +336,9 @@ JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp); extern JS_PUBLIC_API(const char *) JS_GetScriptFilename(JSContext *cx, JSScript *script); +extern JS_PUBLIC_API(const jschar *) +JS_GetScriptSourceMap(JSContext *cx, JSScript *script); + extern JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); @@ -403,9 +384,9 @@ JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, typedef struct JSPropertyDesc { jsval id; /* primary id, atomized string, or int */ jsval value; /* property value */ - uint8 flags; /* flags, see below */ - uint8 spare; /* unused */ - uint16 slot; /* argument/variable slot */ + uint8_t flags; /* flags, see below */ + uint8_t spare; /* unused */ + uint16_t slot; /* argument/variable slot */ jsval alias; /* alias id if JSPD_ALIAS flag */ } JSPropertyDesc; @@ -421,7 +402,7 @@ typedef struct JSPropertyDesc { /* throwing an exception */ typedef struct JSPropertyDescArray { - uint32 length; /* number of elements in array */ + uint32_t length; /* number of elements in array */ JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ } JSPropertyDescArray; @@ -475,9 +456,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script); * Return true if obj is a "system" object, that is, one created by * JS_NewSystemObject with the system flag set and not JS_NewObject. * - * What "system" means is up to the API client, but it can be used to implement - * access control policies based on script filenames and their prefixes, using - * JS_FlagScriptFilenamePrefix and JS_GetTopScriptFilenameFlags. + * What "system" means is up to the API client. */ extern JS_PUBLIC_API(JSBool) JS_IsSystemObject(JSContext *cx, JSObject *obj); @@ -505,72 +484,109 @@ JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks); extern JS_PUBLIC_API(JSDebugHooks *) JS_ClearContextDebugHooks(JSContext *cx); +/** + * Start any profilers that are available and have been configured on for this + * platform. This is NOT thread safe. + * + * The profileName is used by some profilers to describe the current profiling + * run. It may be used for part of the filename of the output, but the + * specifics depend on the profiler. Many profilers will ignore it. Passing in + * NULL is legal; some profilers may use it to output to stdout or similar. + * + * Returns true if no profilers fail to start. + */ extern JS_PUBLIC_API(JSBool) -JS_StartProfiling(); +JS_StartProfiling(const char *profileName); -extern JS_PUBLIC_API(void) -JS_StopProfiling(); +/** + * Stop any profilers that were previously started with JS_StartProfiling. + * Returns true if no profilers fail to stop. + */ +extern JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName); + +/** + * Write the current profile data to the given file, if applicable to whatever + * profiler is being used. + */ +extern JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName); + +/** + * Pause currently active profilers (only supported by some profilers). Returns + * whether any profilers failed to pause. (Profilers that do not support + * pause/resume do not count.) + */ +extern JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName); + +/** + * Resume suspended profilers + */ +extern JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName); +/** + * Add various profiling-related functions as properties of the given object. + */ extern JS_PUBLIC_API(JSBool) JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj); +/* Defined in vm/Debugger.cpp. */ +extern JS_PUBLIC_API(JSBool) +JS_DefineDebuggerObject(JSContext *cx, JSObject *obj); + +/** + * The profiling API calls are not able to report errors, so they use a + * thread-unsafe global memory buffer to hold the last error encountered. This + * should only be called after something returns false. + */ +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError(); + #ifdef MOZ_CALLGRIND extern JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StopCallgrind(); extern JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StartCallgrind(); extern JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_DumpCallgrind(const char *outfile); #endif /* MOZ_CALLGRIND */ #ifdef MOZ_VTUNE -extern JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StartVtune(const char *profileName); -extern JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StopVtune(); -extern JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_PauseVtune(); -extern JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_ResumeVtune(); #endif /* MOZ_VTUNE */ -#ifdef MOZ_TRACEVIS -extern JS_FRIEND_API(JSBool) -js_InitEthogram(JSContext *cx, uintN argc, jsval *vp); -extern JS_FRIEND_API(JSBool) -js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp); -#endif /* MOZ_TRACEVIS */ +extern JS_PUBLIC_API(void) +JS_DumpBytecode(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(void) +JS_DumpCompartmentBytecode(JSContext *cx); -#ifdef MOZ_TRACE_JSCALLS -typedef void (*JSFunctionCallback)(const JSFunction *fun, - const JSScript *scr, - const JSContext *cx, - int entering); +extern JS_PUBLIC_API(void) +JS_DumpPCCounts(JSContext *cx, JSScript *script); -/* - * 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); +JS_DumpCompartmentPCCounts(JSContext *cx); -extern JS_PUBLIC_API(JSFunctionCallback) -JS_GetFunctionCallback(JSContext *cx); -#endif /* MOZ_TRACE_JSCALLS */ +extern JS_PUBLIC_API(JSObject *) +JS_UnwrapObject(JSObject *obj); JS_END_EXTERN_C diff --git a/deps/mozjs/js/src/jsdbgapiinlines.h b/deps/mozjs/js/src/jsdbgapiinlines.h deleted file mode 100644 index b469ee2fd29..00000000000 --- a/deps/mozjs/js/src/jsdbgapiinlines.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey code. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jim Blandy (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsdbgapiinlines_h___ -#define jsdbgapiinlines_h___ - -#include "jsdbgapi.h" -#include "jscntxt.h" - -#if defined(JS_HAS_OBJ_WATCHPOINT) && defined(__cplusplus) - -extern const js::Shape * -js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape); - -/* - * Update any watchpoints on |obj| on |newShape->id| to use |newShape|. Property-manipulating - * functions must call this any time it takes on a new shape to represent a potentially - * watched property, or when it mutates a shape's attributes/setter/getter. - */ -static inline const js::Shape * -js_UpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape) -{ - if (JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList)) - return newShape; - - return js_SlowPathUpdateWatchpointsForShape(cx, obj, newShape); -} - -#endif /* JS_HAS_OBJ_WATCHPOINT && __cplusplus */ - -#endif /* jsdbgapiinlines_h__ */ diff --git a/deps/mozjs/js/src/jsdhash.cpp b/deps/mozjs/js/src/jsdhash.cpp index d3e3bb33cb0..3088d76ab14 100644 --- a/deps/mozjs/js/src/jsdhash.cpp +++ b/deps/mozjs/js/src/jsdhash.cpp @@ -40,12 +40,12 @@ /* * Double hashing implementation. + * + * Try to keep this file in sync with xpcom/glue/pldhash.cpp. */ #include #include #include -#include "jsstdint.h" -#include "jsbit.h" #include "jsdhash.h" #include "jsutil.h" @@ -71,9 +71,9 @@ using namespace js; #ifdef DEBUG #define JSDHASH_ONELINE_ASSERT JS_ASSERT -#define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ - JS_DHASH_TABLE_SIZE(table_) * \ - table_->entrySize)) +#define RECURSION_LEVEL(table_) (*(uint32_t*)(table_->entryStore + \ + JS_DHASH_TABLE_SIZE(table_) * \ + table_->entrySize)) /* * Most callers that assert about the recursion level don't care about * this magical value because they are asserting that mutation is @@ -82,13 +82,13 @@ using namespace js; * * Only PL_DHashTableFinish needs to allow this special value. */ -#define IMMUTABLE_RECURSION_LEVEL ((uint32)-1) +#define IMMUTABLE_RECURSION_LEVEL UINT32_MAX #define RECURSION_LEVEL_SAFE_TO_FINISH(table_) \ (RECURSION_LEVEL(table_) == 0 || \ RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL) -#define ENTRY_STORE_EXTRA sizeof(uint32) +#define ENTRY_STORE_EXTRA sizeof(uint32_t) #define INCREMENT_RECURSION_LEVEL(table_) \ JS_BEGIN_MACRO \ if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) \ @@ -97,7 +97,7 @@ using namespace js; #define DECREMENT_RECURSION_LEVEL(table_) \ JS_BEGIN_MACRO \ if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) { \ - JSDHASH_ONELINE_ASSERT(RECURSION_LEVEL(table_) > 0); \ + JS_ASSERT(RECURSION_LEVEL(table_) > 0); \ --RECURSION_LEVEL(table_); \ } \ JS_END_MACRO @@ -111,7 +111,7 @@ using namespace js; #endif /* defined(DEBUG) */ JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) +JS_DHashAllocTable(JSDHashTable *table, uint32_t nbytes) { return OffTheBooks::malloc_(nbytes); } @@ -168,7 +168,7 @@ JS_DHashMoveEntryStub(JSDHashTable *table, const JSDHashEntryHdr *from, JSDHashEntryHdr *to) { - memcpy(to, from, table->entrySize); + js_memcpy(to, from, table->entrySize); } JS_PUBLIC_API(void) @@ -209,8 +209,8 @@ JS_DHashGetStubOps(void) } JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity) +JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32_t entrySize, + uint32_t capacity) { JSDHashTable *table; @@ -233,10 +233,10 @@ JS_DHashTableDestroy(JSDHashTable *table) JS_PUBLIC_API(JSBool) JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity) + uint32_t entrySize, uint32_t capacity) { int log2; - uint32 nbytes; + uint32_t nbytes; #ifdef DEBUG if (entrySize > 10 * sizeof(void *)) { @@ -260,8 +260,8 @@ JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, if (capacity >= JS_DHASH_SIZE_LIMIT) return JS_FALSE; table->hashShift = JS_DHASH_BITS - log2; - table->maxAlphaFrac = (uint8)(0x100 * JS_DHASH_DEFAULT_MAX_ALPHA); - table->minAlphaFrac = (uint8)(0x100 * JS_DHASH_DEFAULT_MIN_ALPHA); + table->maxAlphaFrac = (uint8_t)(0x100 * JS_DHASH_DEFAULT_MAX_ALPHA); + table->minAlphaFrac = (uint8_t)(0x100 * JS_DHASH_DEFAULT_MIN_ALPHA); table->entrySize = entrySize; table->entryCount = table->removedCount = 0; table->generation = 0; @@ -292,7 +292,7 @@ JS_DHashTableSetAlphaBounds(JSDHashTable *table, float maxAlpha, float minAlpha) { - uint32 size; + uint32_t size; /* * Reject obviously insane bounds, rather than trying to guess what the @@ -325,8 +325,8 @@ JS_DHashTableSetAlphaBounds(JSDHashTable *table, minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); } - table->maxAlphaFrac = (uint8)(maxAlpha * 256); - table->minAlphaFrac = (uint8)(minAlpha * 256); + table->maxAlphaFrac = (uint8_t)(maxAlpha * 256); + table->minAlphaFrac = (uint8_t)(minAlpha * 256); } /* @@ -367,7 +367,7 @@ JS_PUBLIC_API(void) JS_DHashTableFinish(JSDHashTable *table) { char *entryAddr, *entryLimit; - uint32 entrySize; + uint32_t entrySize; JSDHashEntryHdr *entry; #ifdef DEBUG_XXXbrendan @@ -415,7 +415,7 @@ SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, int hashShift, sizeLog2; JSDHashEntryHdr *entry, *firstRemoved; JSDHashMatchEntry matchEntry; - uint32 sizeMask; + uint32_t sizeMask; METER(table->stats.searches++); JS_ASSERT(!(keyHash & COLLISION_FLAG)); @@ -492,7 +492,7 @@ FindFreeEntry(JSDHashTable *table, JSDHashNumber keyHash) JSDHashNumber hash1, hash2; int hashShift, sizeLog2; JSDHashEntryHdr *entry; - uint32 sizeMask; + uint32_t sizeMask; METER(table->stats.searches++); JS_ASSERT(!(keyHash & COLLISION_FLAG)); @@ -536,13 +536,13 @@ static JSBool ChangeTable(JSDHashTable *table, int deltaLog2) { int oldLog2, newLog2; - uint32 oldCapacity, newCapacity; + uint32_t oldCapacity, newCapacity; char *newEntryStore, *oldEntryStore, *oldEntryAddr; - uint32 entrySize, i, nbytes; + uint32_t entrySize, i, nbytes; JSDHashEntryHdr *oldEntry, *newEntry; JSDHashMoveEntry moveEntry; #ifdef DEBUG - uint32 recursionLevel; + uint32_t recursionLevel; #endif /* Look, but don't touch, until we succeed in getting new entry store. */ @@ -599,7 +599,7 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) { JSDHashNumber keyHash; JSDHashEntryHdr *entry; - uint32 size; + uint32_t size; int deltaLog2; JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); @@ -722,11 +722,11 @@ JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) table->entryCount--; } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) { char *entryAddr, *entryLimit; - uint32 i, capacity, entrySize, ceiling; + uint32_t i, capacity, entrySize, ceiling; JSBool didRemove; JSDHashEntryHdr *entry; JSDHashOperator op; @@ -784,6 +784,51 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) return i; } +struct SizeOfEntryExcludingThisArg +{ + size_t total; + JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis; + JSMallocSizeOfFun mallocSizeOf; + void *arg; // the arg passed by the user +}; + +static JSDHashOperator +SizeOfEntryExcludingThisEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32_t number, void *arg) +{ + SizeOfEntryExcludingThisArg *e = (SizeOfEntryExcludingThisArg *)arg; + e->total += e->sizeOfEntryExcludingThis(hdr, e->mallocSizeOf, e->arg); + return JS_DHASH_NEXT; +} + +extern JS_PUBLIC_API(size_t) +JS_DHashTableSizeOfExcludingThis(const JSDHashTable *table, + JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis, + JSMallocSizeOfFun mallocSizeOf, + void *arg /* = NULL */) +{ + size_t n = 0; + n += mallocSizeOf(table->entryStore); + if (sizeOfEntryExcludingThis) { + SizeOfEntryExcludingThisArg arg2 = { 0, sizeOfEntryExcludingThis, mallocSizeOf, arg }; + JS_DHashTableEnumerate(const_cast(table), + SizeOfEntryExcludingThisEnumerator, &arg2); + n += arg2.total; + } + return n; +} + +extern JS_PUBLIC_API(size_t) +JS_DHashTableSizeOfIncludingThis(const JSDHashTable *table, + JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis, + JSMallocSizeOfFun mallocSizeOf, + void *arg /* = NULL */) +{ + return mallocSizeOf(table) + + JS_DHashTableSizeOfExcludingThis(table, sizeOfEntryExcludingThis, + mallocSizeOf, arg); +} + #ifdef DEBUG JS_PUBLIC_API(void) JS_DHashMarkTableImmutable(JSDHashTable *table) @@ -799,9 +844,9 @@ JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) { char *entryAddr; - uint32 entrySize, entryCount; + uint32_t entrySize, entryCount; int hashShift, sizeLog2; - uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; + uint32_t i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; double sqsum, mean, variance, sigma; JSDHashEntryHdr *entry, *probe; diff --git a/deps/mozjs/js/src/jsdhash.h b/deps/mozjs/js/src/jsdhash.h index 13ab2210568..ec11b83120e 100644 --- a/deps/mozjs/js/src/jsdhash.h +++ b/deps/mozjs/js/src/jsdhash.h @@ -38,10 +38,14 @@ #ifndef jsdhash_h___ #define jsdhash_h___ + /* * Double hashing, a la Knuth 6. + * + * Try to keep this file in sync with xpcom/glue/pldhash.h. */ #include "jstypes.h" +#include "jsutil.h" JS_BEGIN_EXTERN_C @@ -76,7 +80,7 @@ JS_BEGIN_EXTERN_C #define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U /* Primitive and forward-struct typedefs. */ -typedef uint32 JSDHashNumber; +typedef uint32_t JSDHashNumber; typedef struct JSDHashEntryHdr JSDHashEntryHdr; typedef struct JSDHashEntryStub JSDHashEntryStub; typedef struct JSDHashTable JSDHashTable; @@ -194,33 +198,33 @@ struct JSDHashEntryHdr { struct JSDHashTable { const JSDHashTableOps *ops; /* virtual operations, see below */ void *data; /* ops- and instance-specific data */ - int16 hashShift; /* multiplicative hash shift */ - uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ - uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ - uint32 entrySize; /* number of bytes in an entry */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - uint32 generation; /* entry storage generation number */ + int16_t hashShift; /* multiplicative hash shift */ + uint8_t maxAlphaFrac; /* 8-bit fixed point max alpha */ + uint8_t minAlphaFrac; /* 8-bit fixed point min alpha */ + uint32_t entrySize; /* number of bytes in an entry */ + uint32_t entryCount; /* number of entries in table */ + uint32_t removedCount; /* removed entry sentinels in table */ + uint32_t generation; /* entry storage generation number */ char *entryStore; /* entry storage */ #ifdef JS_DHASHMETER struct JSDHashStats { - uint32 searches; /* total number of table searches */ - uint32 steps; /* hash chain links traversed */ - uint32 hits; /* searches that found key */ - uint32 misses; /* searches that didn't find key */ - uint32 lookups; /* number of JS_DHASH_LOOKUPs */ - uint32 addMisses; /* adds that miss, and do work */ - uint32 addOverRemoved; /* adds that recycled a removed entry */ - uint32 addHits; /* adds that hit an existing entry */ - uint32 addFailures; /* out-of-memory during add growth */ - uint32 removeHits; /* removes that hit, and do work */ - uint32 removeMisses; /* useless removes that miss */ - uint32 removeFrees; /* removes that freed entry directly */ - uint32 removeEnums; /* removes done by Enumerate */ - uint32 grows; /* table expansions */ - uint32 shrinks; /* table contractions */ - uint32 compresses; /* table compressions */ - uint32 enumShrinks; /* contractions after Enumerate */ + uint32_t searches; /* total number of table searches */ + uint32_t steps; /* hash chain links traversed */ + uint32_t hits; /* searches that found key */ + uint32_t misses; /* searches that didn't find key */ + uint32_t lookups; /* number of JS_DHASH_LOOKUPs */ + uint32_t addMisses; /* adds that miss, and do work */ + uint32_t addOverRemoved; /* adds that recycled a removed entry */ + uint32_t addHits; /* adds that hit an existing entry */ + uint32_t addFailures; /* out-of-memory during add growth */ + uint32_t removeHits; /* removes that hit, and do work */ + uint32_t removeMisses; /* useless removes that miss */ + uint32_t removeFrees; /* removes that freed entry directly */ + uint32_t removeEnums; /* removes done by Enumerate */ + uint32_t grows; /* table expansions */ + uint32_t shrinks; /* table contractions */ + uint32_t compresses; /* table compressions */ + uint32_t enumShrinks; /* contractions after Enumerate */ } stats; #endif }; @@ -238,7 +242,7 @@ struct JSDHashTable { * equal to 0; but note that jsdhash.c code will never call with 0 nbytes). */ typedef void * -(* JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); +(* JSDHashAllocTable)(JSDHashTable *table, uint32_t nbytes); typedef void (* JSDHashFreeTable) (JSDHashTable *table, void *ptr); @@ -340,7 +344,7 @@ struct JSDHashTableOps { * Default implementations for the above ops. */ extern JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); +JS_DHashAllocTable(JSDHashTable *table, uint32_t nbytes); extern JS_PUBLIC_API(void) JS_DHashFreeTable(JSDHashTable *table, void *ptr); @@ -396,8 +400,8 @@ JS_DHashGetStubOps(void); * the ops->allocTable callback. */ extern JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity); +JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32_t entrySize, + uint32_t capacity); /* * Finalize table's data, free its entry storage (via table->ops->freeTable), @@ -414,7 +418,7 @@ JS_DHashTableDestroy(JSDHashTable *table); */ extern JS_PUBLIC_API(JSBool) JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity); + uint32_t entrySize, uint32_t capacity); /* * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. @@ -452,11 +456,11 @@ JS_DHashTableSetAlphaBounds(JSDHashTable *table, #define JS_DHASH_DEFAULT_MIN_ALPHA 0.25 #define JS_DHASH_CAP(entryCount, maxAlpha) \ - ((uint32)((double)(entryCount) / (maxAlpha))) + ((uint32_t)((double)(entryCount) / (maxAlpha))) #define JS_DHASH_CAPACITY(entryCount, maxAlpha) \ (JS_DHASH_CAP(entryCount, maxAlpha) + \ - (((JS_DHASH_CAP(entryCount, maxAlpha) * (uint8)(0x100 * (maxAlpha))) \ + (((JS_DHASH_CAP(entryCount, maxAlpha) * (uint8_t)(0x100 * (maxAlpha))) \ >> 8) < (entryCount))) #define JS_DHASH_DEFAULT_CAPACITY(entryCount) \ @@ -570,12 +574,37 @@ JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); * the entry being enumerated, rather than returning JS_DHASH_REMOVE. */ typedef JSDHashOperator -(* JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg); +(* JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32_t number, void *arg); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); +typedef size_t +(* JSDHashSizeOfEntryExcludingThisFun)(JSDHashEntryHdr *hdr, + JSMallocSizeOfFun mallocSizeOf, + void *arg); + +/** + * Measure the size of the table's entry storage, and if + * |sizeOfEntryExcludingThis| is non-NULL, measure the size of things pointed + * to by entries. Doesn't measure |ops| because it's often shared between + * tables, nor |data| because it's opaque. + */ +extern JS_PUBLIC_API(size_t) +JS_DHashTableSizeOfExcludingThis(const JSDHashTable *table, + JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis, + JSMallocSizeOfFun mallocSizeOf, + void *arg = NULL); + +/** + * Like JS_DHashTableSizeOfExcludingThis, but includes sizeof(*this). + */ +extern JS_PUBLIC_API(size_t) +JS_DHashTableSizeOfIncludingThis(const JSDHashTable *table, + JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis, + JSMallocSizeOfFun mallocSizeOf, + void *arg = NULL); + #ifdef DEBUG /** * Mark a table as immutable for the remainder of its lifetime. This diff --git a/deps/mozjs/js/src/jsdtoa.cpp b/deps/mozjs/js/src/jsdtoa.cpp index 3031af56cf8..fb7295c8ebf 100644 --- a/deps/mozjs/js/src/jsdtoa.cpp +++ b/deps/mozjs/js/src/jsdtoa.cpp @@ -41,13 +41,11 @@ * Portable double to alphanumeric string and back converters. */ #include "jstypes.h" -#include "jsstdint.h" #include "jsdtoa.h" #include "jsprf.h" #include "jsapi.h" #include "jsprvtd.h" #include "jsnum.h" -#include "jsbit.h" #include "jslibmath.h" #include "jscntxt.h" @@ -62,20 +60,20 @@ using namespace js; #endif #ifndef Long -#define Long int32 +#define Long int32_t #endif #ifndef ULong -#define ULong uint32 +#define ULong uint32_t #endif /* #ifndef Llong -#define Llong JSInt64 +#define Llong int64_t #endif #ifndef ULlong -#define ULlong JSUint64 +#define ULlong uint64_t #endif */ @@ -92,7 +90,7 @@ inline void dtoa_free(void* p) { return UnwantedForeground::free_(p); } #include "dtoa.c" /* Mapping of JSDToStrMode -> js_dtoa mode */ -static const uint8 dtoaModes[] = { +static const uint8_t dtoaModes[] = { 0, /* DTOSTR_STANDARD */ 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ 3, /* DTOSTR_FIXED, */ @@ -143,7 +141,7 @@ js_dtostr(DtoaState *state, char *buffer, size_t bufferSize, JSDToStrMode mode, return NULL; } - memcpy(buffer + 2, numBegin, nDigits); + js_memcpy(buffer + 2, numBegin, nDigits); freedtoa(PASS_STATE numBegin); numBegin = buffer + 2; /* +2 leaves space for sign and/or decimal point */ numEnd = numBegin + nDigits; @@ -247,11 +245,11 @@ js_dtostr(DtoaState *state, char *buffer, size_t bufferSize, JSDToStrMode mode, /* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. * divisor must be between 1 and 65536. * This function cannot run out of memory. */ -static uint32 -divrem(Bigint *b, uint32 divisor) +static uint32_t +divrem(Bigint *b, uint32_t divisor) { - int32 n = b->wds; - uint32 remainder = 0; + int32_t n = b->wds; + uint32_t remainder = 0; ULong *bx; ULong *bp; @@ -282,13 +280,13 @@ divrem(Bigint *b, uint32 divisor) } /* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ -static uint32 quorem2(Bigint *b, int32 k) +static uint32_t quorem2(Bigint *b, int32_t k) { ULong mask; ULong result; ULong *bx, *bxe; - int32 w; - int32 n = k >> 5; + int32_t w; + int32_t n = k >> 5; k &= 0x1F; mask = (1<wds); Bfree(PASS_STATE b); @@ -402,7 +400,7 @@ js_dtobasestr(DtoaState *state, int base, double dinput) if (dval(df) != 0.0) { /* We have a fraction. */ int e, bbits; - int32 s2, done; + int32_t s2, done; Bigint *b, *s, *mlo, *mhi; b = s = mlo = mhi = NULL; @@ -422,7 +420,7 @@ js_dtobasestr(DtoaState *state, int base, double dinput) JS_ASSERT(e < 0); /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ - s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); + s2 = -(int32_t)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); #ifndef Sudden_Underflow if (!s2) s2 = -1; @@ -463,7 +461,7 @@ js_dtobasestr(DtoaState *state, int base, double dinput) done = JS_FALSE; do { - int32 j, j1; + int32_t j, j1; Bigint *delta; b = multadd(PASS_STATE b, base, 0); @@ -522,7 +520,7 @@ js_dtobasestr(DtoaState *state, int base, double dinput) digit++; done = JS_TRUE; } - JS_ASSERT(digit < (uint32)base); + JS_ASSERT(digit < (uint32_t)base); *p++ = BASEDIGIT(digit); } while (!done); Bfree(PASS_STATE b); diff --git a/deps/mozjs/js/src/jsemit.cpp b/deps/mozjs/js/src/jsemit.cpp deleted file mode 100644 index c26289658f4..00000000000 --- a/deps/mozjs/js/src/jsemit.cpp +++ /dev/null @@ -1,7748 +0,0 @@ -/* -*- 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 ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS bytecode generation. - */ -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include -#include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" -#include "jsutil.h" -#include "jsbit.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsversion.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsautooplen.h" // generated headers last -#include "jsstaticcheck.h" - -#include "jsatominlines.h" -#include "jsobjinlines.h" -#include "jsscopeinlines.h" -#include "jsscriptinlines.h" - -/* Allocation chunk counts, must be powers of two in general. */ -#define BYTECODE_CHUNK 256 /* code allocation increment */ -#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ -#define TRYNOTE_CHUNK 64 /* trynote allocation increment */ - -/* Macros to compute byte sizes from typed element counts. */ -#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) -#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) -#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) - -using namespace js; -using namespace js::gc; - -static JSBool -NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, - uintN stackDepth, size_t start, size_t end); - -static JSBool -EmitIndexOp(JSContext *cx, JSOp op, uintN index, JSCodeGenerator *cg); - -static JSBool -EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op, JSObjectBox *box); - -void -JSTreeContext::trace(JSTracer *trc) -{ - bindings.trace(trc); -} - -JSCodeGenerator::JSCodeGenerator(Parser *parser, - JSArenaPool *cpool, JSArenaPool *npool, - uintN lineno) - : JSTreeContext(parser), - codePool(cpool), notePool(npool), - codeMark(JS_ARENA_MARK(cpool)), noteMark(JS_ARENA_MARK(npool)), - stackDepth(0), maxStackDepth(0), - ntrynotes(0), lastTryNode(NULL), - spanDeps(NULL), jumpTargets(NULL), jtFreeList(NULL), - numSpanDeps(0), numJumpTargets(0), spanDepTodo(0), - arrayCompDepth(0), - emitLevel(0), - constMap(parser->context), - constList(parser->context), - globalUses(ContextAllocPolicy(parser->context)), - closedArgs(ContextAllocPolicy(parser->context)), - closedVars(ContextAllocPolicy(parser->context)), - traceIndex(0) -{ - flags = TCF_COMPILING; - memset(&prolog, 0, sizeof prolog); - memset(&main, 0, sizeof main); - current = &main; - firstLine = prolog.currentLine = main.currentLine = lineno; - prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1; - memset(&upvarMap, 0, sizeof upvarMap); -} - -bool JSCodeGenerator::init() -{ - return constMap.init(); -} - -JSCodeGenerator::~JSCodeGenerator() -{ - JS_ARENA_RELEASE(codePool, codeMark); - JS_ARENA_RELEASE(notePool, noteMark); - - /* NB: non-null only after OOM. */ - if (spanDeps) - parser->context->free_(spanDeps); - - if (upvarMap.vector) - parser->context->free_(upvarMap.vector); -} - -static ptrdiff_t -EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) -{ - jsbytecode *base, *limit, *next; - ptrdiff_t offset, length; - size_t incr, size; - - base = CG_BASE(cg); - next = CG_NEXT(cg); - limit = CG_LIMIT(cg); - offset = next - base; - if (next + delta > limit) { - length = offset + delta; - length = (length <= BYTECODE_CHUNK) - ? BYTECODE_CHUNK - : JS_BIT(JS_CeilingLog2(length)); - incr = BYTECODE_SIZE(length); - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); - } else { - size = BYTECODE_SIZE(limit - base); - incr -= size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - } - if (!base) { - js_ReportOutOfScriptQuota(cx); - return -1; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = base + length; - CG_NEXT(cg) = base + offset; - } - return offset; -} - -static void -UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) -{ - jsbytecode *pc; - JSOp op; - const JSCodeSpec *cs; - uintN extra, depth, nuses; - intN ndefs; - - pc = CG_CODE(cg, target); - op = (JSOp) *pc; - cs = &js_CodeSpec[op]; -#ifdef JS_TRACER - extern uint8 js_opcode2extra[]; - extra = js_opcode2extra[op]; -#else - extra = 0; -#endif - if ((cs->format & JOF_TMPSLOT_MASK) || extra) { - depth = (uintN) cg->stackDepth + - ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT) + - extra; - /* :TODO: hack - remove later. */ - switch (op) { - case JSOP_PROPINC: - case JSOP_PROPDEC: - depth += 1; - break; - case JSOP_NAMEINC: - case JSOP_NAMEDEC: - case JSOP_INCNAME: - case JSOP_DECNAME: - case JSOP_GNAMEINC: - case JSOP_GNAMEDEC: - case JSOP_INCGNAME: - case JSOP_DECGNAME: - depth += 2; - break; - default: - break; - } - if (depth > cg->maxStackDepth) - cg->maxStackDepth = depth; - } - - nuses = js_GetStackUses(cs, op, pc); - cg->stackDepth -= nuses; - JS_ASSERT(cg->stackDepth >= 0); - if (cg->stackDepth < 0) { - char numBuf[12]; - TokenStream *ts; - - JS_snprintf(numBuf, sizeof numBuf, "%d", target); - ts = &cg->parser->tokenStream; - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, - js_GetErrorMessage, NULL, - JSMSG_STACK_UNDERFLOW, - ts->getFilename() ? ts->getFilename() : "stdin", - numBuf); - } - ndefs = cs->ndefs; - if (ndefs < 0) { - JSObject *blockObj; - - /* We just executed IndexParsedObject */ - JS_ASSERT(op == JSOP_ENTERBLOCK); - JS_ASSERT(nuses == 0); - blockObj = cg->objectList.lastbox->object; - JS_ASSERT(blockObj->isStaticBlock()); - JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined()); - - OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth); - ndefs = OBJ_BLOCK_COUNT(cx, blockObj); - } - cg->stackDepth += ndefs; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; -} - -ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 1); - - if (offset >= 0) { - *CG_NEXT(cg)++ = (jsbytecode)op; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 2); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - CG_NEXT(cg) = next + 2; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 3); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - next[2] = op2; - CG_NEXT(cg) = next + 3; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit5(JSContext *cx, JSCodeGenerator *cg, JSOp op, uint16 op1, uint16 op2) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 5); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = UINT16_HI(op1); - next[2] = UINT16_LO(op1); - next[3] = UINT16_HI(op2); - next[4] = UINT16_LO(op2); - CG_NEXT(cg) = next + 5; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) -{ - ptrdiff_t length = 1 + (ptrdiff_t)extra; - ptrdiff_t offset = EmitCheck(cx, cg, op, length); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - *next = (jsbytecode)op; - memset(next + 1, 0, BYTECODE_SIZE(extra)); - CG_NEXT(cg) = next + length; - - /* - * Don't UpdateDepth if op's use-count comes from the immediate - * operand yet to be stored in the extra bytes after op. - */ - if (js_CodeSpec[op].nuses >= 0) - UpdateDepth(cx, cg, offset); - } - return offset; -} - -/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ -const char js_with_statement_str[] = "with statement"; -const char js_finally_block_str[] = "finally block"; -const char js_script_str[] = "script"; - -static const char *statementName[] = { - "label statement", /* LABEL */ - "if statement", /* IF */ - "else statement", /* ELSE */ - "destructuring body", /* BODY */ - "switch statement", /* SWITCH */ - "block", /* BLOCK */ - js_with_statement_str, /* WITH */ - "catch block", /* CATCH */ - "try block", /* TRY */ - js_finally_block_str, /* FINALLY */ - js_finally_block_str, /* SUBROUTINE */ - "do loop", /* DO_LOOP */ - "for loop", /* FOR_LOOP */ - "for/in loop", /* FOR_IN_LOOP */ - "while loop", /* WHILE_LOOP */ -}; - -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT); - -static const char * -StatementName(JSCodeGenerator *cg) -{ - if (!cg->topStmt) - return js_script_str; - return statementName[cg->topStmt->type]; -} - -static void -ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, - StatementName(cg)); -} - -/** - Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) - and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided - into unconditional (gotos and gosubs), and conditional jumps or branches - (which pop a value, test it, and jump depending on its value). Most jumps - have just one immediate operand, a signed offset from the jump opcode's pc - to the target bytecode. The lookup and table switch opcodes may contain - many jump offsets. - - Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was - fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is - suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for - the extended form of the JSOP_OR branch opcode). The unextended or short - formats have 16-bit signed immediate offset operands, the extended or long - formats have 32-bit signed immediates. The span-dependency problem consists - of selecting as few long instructions as possible, or about as few -- since - jumps can span other jumps, extending one jump may cause another to need to - be extended. - - Most JS scripts are short, so need no extended jumps. We optimize for this - case by generating short jumps until we know a long jump is needed. After - that point, we keep generating short jumps, but each jump's 16-bit immediate - offset operand is actually an unsigned index into cg->spanDeps, an array of - JSSpanDep structs. Each struct tells the top offset in the script of the - opcode, the "before" offset of the jump (which will be the same as top for - simplex jumps, but which will index further into the bytecode array for a - non-initial jump offset in a lookup or table switch), the after "offset" - adjusted during span-dependent instruction selection (initially the same - value as the "before" offset), and the jump target (more below). - - Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must - ensure that all bytecode generated so far can be inspected to discover where - the jump offset immediate operands lie within CG_CODE(cg). But the bonus is - that we generate span-dependency records sorted by their offsets, so we can - binary-search when trying to find a JSSpanDep for a given bytecode offset, - or the nearest JSSpanDep at or above a given pc. - - To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows - 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This - tells us that we need to binary-search for the cg->spanDeps entry by the - jump opcode's bytecode offset (sd->before). - - Jump targets need to be maintained in a data structure that lets us look - up an already-known target by its address (jumps may have a common target), - and that also lets us update the addresses (script-relative, a.k.a. absolute - offsets) of targets that come after a jump target (for when a jump below - that target needs to be extended). We use an AVL tree, implemented using - recursion, but with some tricky optimizations to its height-balancing code - (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). - - A final wrinkle: backpatch chains are linked by jump-to-jump offsets with - positive sign, even though they link "backward" (i.e., toward lower bytecode - address). We don't want to waste space and search time in the AVL tree for - such temporary backpatch deltas, so we use a single-bit wildcard scheme to - tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas - in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known - target, or is still awaiting backpatching. - - Note that backpatch chains would present a problem for BuildSpanDepTable, - which inspects bytecode to build cg->spanDeps on demand, when the first - short jump offset overflows. To solve this temporary problem, we emit a - proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose - nuses/ndefs counts help keep the stack balanced, but whose opcode format - distinguishes its backpatch delta immediate operand from a normal jump - offset. - */ -static int -BalanceJumpTargets(JSJumpTarget **jtp) -{ - JSJumpTarget *jt, *jt2, *root; - int dir, otherDir, heightChanged; - JSBool doubleRotate; - - jt = *jtp; - JS_ASSERT(jt->balance != 0); - - if (jt->balance < -1) { - dir = JT_RIGHT; - doubleRotate = (jt->kids[JT_LEFT]->balance > 0); - } else if (jt->balance > 1) { - dir = JT_LEFT; - doubleRotate = (jt->kids[JT_RIGHT]->balance < 0); - } else { - return 0; - } - - otherDir = JT_OTHER_DIR(dir); - if (doubleRotate) { - jt2 = jt->kids[otherDir]; - *jtp = root = jt2->kids[dir]; - - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - jt2->kids[dir] = root->kids[otherDir]; - root->kids[otherDir] = jt2; - - heightChanged = 1; - root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0); - root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0); - root->balance = 0; - } else { - *jtp = root = jt->kids[otherDir]; - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - heightChanged = (root->balance != 0); - jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance); - } - - return heightChanged; -} - -typedef struct AddJumpTargetArgs { - JSContext *cx; - JSCodeGenerator *cg; - ptrdiff_t offset; - JSJumpTarget *node; -} AddJumpTargetArgs; - -static int -AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) -{ - JSJumpTarget *jt; - int balanceDelta; - - jt = *jtp; - if (!jt) { - JSCodeGenerator *cg = args->cg; - - jt = cg->jtFreeList; - if (jt) { - cg->jtFreeList = jt->kids[JT_LEFT]; - } else { - JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, - sizeof *jt); - if (!jt) { - js_ReportOutOfScriptQuota(args->cx); - return 0; - } - } - jt->offset = args->offset; - jt->balance = 0; - jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL; - cg->numJumpTargets++; - args->node = jt; - *jtp = jt; - return 1; - } - - if (jt->offset == args->offset) { - args->node = jt; - return 0; - } - - if (args->offset < jt->offset) - balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]); - else - balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]); - if (!args->node) - return 0; - - jt->balance += balanceDelta; - return (balanceDelta && jt->balance) - ? 1 - BalanceJumpTargets(jtp) - : 0; -} - -#ifdef DEBUG_brendan -static int AVLCheck(JSJumpTarget *jt) -{ - int lh, rh; - - if (!jt) return 0; - JS_ASSERT(-1 <= jt->balance && jt->balance <= 1); - lh = AVLCheck(jt->kids[JT_LEFT]); - rh = AVLCheck(jt->kids[JT_RIGHT]); - JS_ASSERT(jt->balance == rh - lh); - return 1 + JS_MAX(lh, rh); -} -#endif - -static JSBool -SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd, - ptrdiff_t off) -{ - AddJumpTargetArgs args; - - if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - args.cx = cx; - args.cg = cg; - args.offset = sd->top + off; - args.node = NULL; - AddJumpTarget(&args, &cg->jumpTargets); - if (!args.node) - return JS_FALSE; - -#ifdef DEBUG_brendan - AVLCheck(cg->jumpTargets); -#endif - - SD_SET_TARGET(sd, args.node); - return JS_TRUE; -} - -#define SPANDEPS_MIN 256 -#define SPANDEPS_SIZE(n) ((n) * sizeof(JSSpanDep)) -#define SPANDEPS_SIZE_MIN SPANDEPS_SIZE(SPANDEPS_MIN) - -static JSBool -AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2, - ptrdiff_t off) -{ - uintN index; - JSSpanDep *sdbase, *sd; - size_t size; - - index = cg->numSpanDeps; - if (index + 1 == 0) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if ((index & (index - 1)) == 0 && - (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) { - size = sdbase ? SPANDEPS_SIZE(index) : SPANDEPS_SIZE_MIN / 2; - sdbase = (JSSpanDep *) cx->realloc_(sdbase, size + size); - if (!sdbase) - return JS_FALSE; - cg->spanDeps = sdbase; - } - - cg->numSpanDeps = index + 1; - sd = cg->spanDeps + index; - sd->top = pc - CG_BASE(cg); - sd->offset = sd->before = pc2 - CG_BASE(cg); - - if (js_CodeSpec[*pc].format & JOF_BACKPATCH) { - /* Jump offset will be backpatched if off is a non-zero "bpdelta". */ - if (off != 0) { - JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN); - if (off > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - } - SD_SET_BPDELTA(sd, off); - } else if (off == 0) { - /* Jump offset will be patched directly, without backpatch chaining. */ - SD_SET_TARGET(sd, 0); - } else { - /* The jump offset in off is non-zero, therefore it's already known. */ - if (!SetSpanDepTarget(cx, cg, sd, off)) - return JS_FALSE; - } - - if (index > SPANDEP_INDEX_MAX) - index = SPANDEP_INDEX_HUGE; - SET_SPANDEP_INDEX(pc2, index); - return JS_TRUE; -} - -static jsbytecode * -AddSwitchSpanDeps(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc) -{ - JSOp op; - jsbytecode *pc2; - ptrdiff_t off; - jsint low, high; - uintN njumps, indexlen; - - op = (JSOp) *pc; - JS_ASSERT(op == JSOP_TABLESWITCH || op == JSOP_LOOKUPSWITCH); - pc2 = pc; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return NULL; - pc2 += JUMP_OFFSET_LEN; - if (op == JSOP_TABLESWITCH) { - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - njumps = (uintN) (high - low + 1); - indexlen = 0; - } else { - njumps = GET_UINT16(pc2); - pc2 += UINT16_LEN; - indexlen = INDEX_LEN; - } - while (njumps) { - --njumps; - pc2 += indexlen; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return NULL; - pc2 += JUMP_OFFSET_LEN; - } - return 1 + pc2; -} - -static JSBool -BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *end; - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t off; - - pc = CG_BASE(cg) + cg->spanDepTodo; - end = CG_NEXT(cg); - while (pc != end) { - JS_ASSERT(pc < end); - op = (JSOp)*pc; - cs = &js_CodeSpec[op]; - - switch (JOF_TYPE(cs->format)) { - case JOF_TABLESWITCH: - case JOF_LOOKUPSWITCH: - pc = AddSwitchSpanDeps(cx, cg, pc); - if (!pc) - return JS_FALSE; - break; - - case JOF_JUMP: - off = GET_JUMP_OFFSET(pc); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return JS_FALSE; - /* FALL THROUGH */ - default: - pc += cs->length; - break; - } - } - - return JS_TRUE; -} - -static JSSpanDep * -GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc) -{ - uintN index; - ptrdiff_t offset; - int lo, hi, mid; - JSSpanDep *sd; - - index = GET_SPANDEP_INDEX(pc); - if (index != SPANDEP_INDEX_HUGE) - return cg->spanDeps + index; - - offset = pc - CG_BASE(cg); - lo = 0; - hi = cg->numSpanDeps - 1; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = cg->spanDeps + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - - JS_ASSERT(0); - return NULL; -} - -static JSBool -SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t delta) -{ - JSSpanDep *sd; - - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, delta); - return JS_TRUE; - } - - if (delta > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - - sd = GetSpanDep(cg, pc); - JS_ASSERT(SD_GET_BPDELTA(sd) == 0); - SD_SET_BPDELTA(sd, delta); - return JS_TRUE; -} - -static void -UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta) -{ - if (jt->offset > pivot) { - jt->offset += delta; - if (jt->kids[JT_LEFT]) - UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta); - } - if (jt->kids[JT_RIGHT]) - UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta); -} - -static JSSpanDep * -FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo, - JSSpanDep *guard) -{ - int num, hi, mid; - JSSpanDep *sdbase, *sd; - - num = cg->numSpanDeps; - JS_ASSERT(num > 0); - hi = num - 1; - sdbase = cg->spanDeps; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = sdbase + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - if (lo == num) - return guard; - sd = sdbase + lo; - JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset)); - return sd; -} - -static void -FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt) -{ - if (jt->kids[JT_LEFT]) - FreeJumpTargets(cg, jt->kids[JT_LEFT]); - if (jt->kids[JT_RIGHT]) - FreeJumpTargets(cg, jt->kids[JT_RIGHT]); - jt->kids[JT_LEFT] = cg->jtFreeList; - cg->jtFreeList = jt; -} - -static JSBool -OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *oldpc, *base, *limit, *next; - JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard; - ptrdiff_t offset, growth, delta, top, pivot, span, length, target; - JSBool done; - JSOp op; - uint32 type; - size_t size, incr; - jssrcnote *sn, *snlimit; - JSSrcNoteSpec *spec; - uintN i, n, noteIndex; - JSTryNode *tryNode; - DebugOnly passes = 0; - - base = CG_BASE(cg); - sdbase = cg->spanDeps; - sdlimit = sdbase + cg->numSpanDeps; - offset = CG_OFFSET(cg); - growth = 0; - - do { - done = JS_TRUE; - delta = 0; - top = pivot = -1; - sdtop = NULL; - pc = NULL; - op = JSOP_NOP; - type = 0; -#ifdef DEBUG_brendan - passes++; -#endif - - for (sd = sdbase; sd < sdlimit; sd++) { - JS_ASSERT(JT_HAS_TAG(sd->target)); - sd->offset += delta; - - if (sd->top != top) { - sdtop = sd; - top = sd->top; - JS_ASSERT(top == sd->before); - pivot = sd->offset; - pc = base + top; - op = (JSOp) *pc; - type = JOF_OPTYPE(op); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - /* - * We already extended all the jump offset operands for - * the opcode at sd->top. Jumps and branches have only - * one jump offset operand, but switches have many, all - * of which are adjacent in cg->spanDeps. - */ - continue; - } - - JS_ASSERT(type == JOF_JUMP || - type == JOF_TABLESWITCH || - type == JOF_LOOKUPSWITCH); - } - - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = SD_SPAN(sd, pivot); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - ptrdiff_t deltaFromTop = 0; - - done = JS_FALSE; - - switch (op) { - case JSOP_GOTO: op = JSOP_GOTOX; break; - case JSOP_IFEQ: op = JSOP_IFEQX; break; - case JSOP_IFNE: op = JSOP_IFNEX; break; - case JSOP_OR: op = JSOP_ORX; break; - case JSOP_AND: op = JSOP_ANDX; break; - case JSOP_GOSUB: op = JSOP_GOSUBX; break; - case JSOP_CASE: op = JSOP_CASEX; break; - case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; - case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; - case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; - default: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - *pc = (jsbytecode) op; - - for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) { - if (sd2 <= sd) { - /* - * sd2->offset already includes delta as it stood - * before we entered this loop, but it must also - * include the delta relative to top due to all the - * extended jump offset immediates for the opcode - * starting at top, which we extend in this loop. - * - * If there is only one extended jump offset, then - * sd2->offset won't change and this for loop will - * iterate once only. - */ - sd2->offset += deltaFromTop; - deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - } else { - /* - * sd2 comes after sd, and won't be revisited by - * the outer for loop, so we have to increase its - * offset by delta, not merely by deltaFromTop. - */ - sd2->offset += delta; - } - - delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - UpdateJumpTargets(cg->jumpTargets, sd2->offset, - JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - } - sd = sd2 - 1; - } - } - } - - growth += delta; - } while (!done); - - if (growth) { -#ifdef DEBUG_brendan - TokenStream *ts = &cg->parser->tokenStream; - - printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", - ts->filename ? ts->filename : "stdin", cg->firstLine, - growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps, - passes, offset + growth, offset, growth); -#endif - - /* - * Ensure that we have room for the extended jumps, but don't round up - * to a power of two -- we're done generating code, so we cut to fit. - */ - limit = CG_LIMIT(cg); - length = offset + growth; - next = base + length; - if (next > limit) { - JS_ASSERT(length > BYTECODE_CHUNK); - size = BYTECODE_SIZE(limit - base); - incr = BYTECODE_SIZE(length) - size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - if (!base) { - js_ReportOutOfScriptQuota(cx); - return JS_FALSE; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = next = base + length; - } - CG_NEXT(cg) = next; - - /* - * Set up a fake span dependency record to guard the end of the code - * being generated. This guard record is returned as a fencepost by - * FindNearestSpanDep if there is no real spandep at or above a given - * unextended code offset. - */ - guard.top = -1; - guard.offset = offset + growth; - guard.before = offset; - guard.target = NULL; - } - - /* - * Now work backwards through the span dependencies, copying chunks of - * bytecode between each extended jump toward the end of the grown code - * space, and restoring immediate offset operands for all jump bytecodes. - * The first chunk of bytecodes, starting at base and ending at the first - * extended jump offset (NB: this chunk includes the operation bytecode - * just before that immediate jump offset), doesn't need to be copied. - */ - JS_ASSERT(sd == sdlimit); - top = -1; - while (--sd >= sdbase) { - if (sd->top != top) { - top = sd->top; - op = (JSOp) base[top]; - type = JOF_OPTYPE(op); - - for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--) - continue; - sd2++; - pivot = sd2->offset; - JS_ASSERT(top == sd2->before); - } - - oldpc = base + sd->before; - span = SD_SPAN(sd, pivot); - - /* - * If this jump didn't need to be extended, restore its span immediate - * offset operand now, overwriting the index of sd within cg->spanDeps - * that was stored temporarily after *pc when BuildSpanDepTable ran. - * - * Note that span might fit in 16 bits even for an extended jump op, - * if the op has multiple span operands, not all of which overflowed - * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in - * range for a short jump, but others are not). - */ - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX); - SET_JUMP_OFFSET(oldpc, span); - continue; - } - - /* - * Set up parameters needed to copy the next run of bytecode starting - * at offset (which is a cursor into the unextended, original bytecode - * vector), down to sd->before (a cursor of the same scale as offset, - * it's the index of the original jump pc). Reuse delta to count the - * nominal number of bytes to copy. - */ - pc = base + sd->offset; - delta = offset - sd->before; - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - - /* - * Don't bother copying the jump offset we're about to reset, but do - * copy the bytecode at oldpc (which comes just before its immediate - * jump offset operand), on the next iteration through the loop, by - * including it in offset's new value. - */ - offset = sd->before + 1; - size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); - if (size) { - memmove(pc + 1 + JUMPX_OFFSET_LEN, - oldpc + 1 + JUMP_OFFSET_LEN, - size); - } - - SET_JUMPX_OFFSET(pc, span); - } - - if (growth) { - /* - * Fix source note deltas. Don't hardwire the delta fixup adjustment, - * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN - * at each sd that moved. The future may bring different offset sizes - * for span-dependent instruction operands. However, we fix only main - * notes here, not prolog notes -- we know that prolog opcodes are not - * span-dependent, and aren't likely ever to be. - */ - offset = growth = 0; - sd = sdbase; - for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount; - sn < snlimit; - sn = SN_NEXT(sn)) { - /* - * Recall that the offset of a given note includes its delta, and - * tells the offset of the annotated bytecode from the main entry - * point of the script. - */ - offset += SN_DELTA(sn); - while (sd < sdlimit && sd->before < offset) { - /* - * To compute the delta to add to sn, we need to look at the - * spandep after sd, whose offset - (before + growth) tells by - * how many bytes sd's instruction grew. - */ - sd2 = sd + 1; - if (sd2 == sdlimit) - sd2 = &guard; - delta = sd2->offset - (sd2->before + growth); - if (delta > 0) { - JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - sn = js_AddToSrcNoteDelta(cx, cg, sn, delta); - if (!sn) - return JS_FALSE; - snlimit = cg->main.notes + cg->main.noteCount; - growth += delta; - } - sd++; - } - - /* - * If sn has span-dependent offset operands, check whether each - * covers further span-dependencies, and increase those operands - * accordingly. Some source notes measure offset not from the - * annotated pc, but from that pc plus some small bias. NB: we - * assume that spec->offsetBias can't itself span span-dependent - * instructions! - */ - spec = &js_SrcNoteSpec[SN_TYPE(sn)]; - if (spec->isSpanDep) { - pivot = offset + spec->offsetBias; - n = spec->arity; - for (i = 0; i < n; i++) { - span = js_GetSrcNoteOffset(sn, i); - if (span == 0) - continue; - target = pivot + span * spec->isSpanDep; - sd2 = FindNearestSpanDep(cg, target, - (target >= pivot) - ? sd - sdbase - : 0, - &guard); - - /* - * Increase target by sd2's before-vs-after offset delta, - * which is absolute (i.e., relative to start of script, - * as is target). Recompute the span by subtracting its - * adjusted pivot from target. - */ - target += sd2->offset - sd2->before; - span = target - (pivot + growth); - span *= spec->isSpanDep; - noteIndex = sn - cg->main.notes; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span)) - return JS_FALSE; - sn = cg->main.notes + noteIndex; - snlimit = cg->main.notes + cg->main.noteCount; - } - } - } - cg->main.lastNoteOffset += growth; - - /* - * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's - * not clear how we can beat that). - */ - for (tryNode = cg->lastTryNode; tryNode; tryNode = tryNode->prev) { - /* - * First, look for the nearest span dependency at/above tn->start. - * There may not be any such spandep, in which case the guard will - * be returned. - */ - offset = tryNode->note.start; - sd = FindNearestSpanDep(cg, offset, 0, &guard); - delta = sd->offset - sd->before; - tryNode->note.start = offset + delta; - - /* - * Next, find the nearest spandep at/above tn->start + tn->length. - * Use its delta minus tn->start's delta to increase tn->length. - */ - length = tryNode->note.length; - sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); - if (sd2 != sd) { - tryNode->note.length = - length + sd2->offset - sd2->before - delta; - } - } - } - -#ifdef DEBUG_brendan - { - uintN bigspans = 0; - top = -1; - for (sd = sdbase; sd < sdlimit; sd++) { - offset = sd->offset; - - /* NB: sd->top cursors into the original, unextended bytecode vector. */ - if (sd->top != top) { - JS_ASSERT(top == -1 || - !JOF_TYPE_IS_EXTENDED_JUMP(type) || - bigspans != 0); - bigspans = 0; - top = sd->top; - JS_ASSERT(top == sd->before); - op = (JSOp) base[offset]; - type = JOF_OPTYPE(op); - JS_ASSERT(type == JOF_JUMP || - type == JOF_JUMPX || - type == JOF_TABLESWITCH || - type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCH || - type == JOF_LOOKUPSWITCHX); - pivot = offset; - } - - pc = base + offset; - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = GET_JUMPX_OFFSET(pc); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - bigspans++; - } else { - JS_ASSERT(type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCHX); - } - } else { - span = GET_JUMP_OFFSET(pc); - } - JS_ASSERT(SD_SPAN(sd, pivot) == span); - } - JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); - } -#endif - - /* - * Reset so we optimize at most once -- cg may be used for further code - * generation of successive, independent, top-level statements. No jump - * can span top-level statements, because JS lacks goto. - */ - size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); - cx->free_(cg->spanDeps); - cg->spanDeps = NULL; - FreeJumpTargets(cg, cg->jumpTargets); - cg->jumpTargets = NULL; - cg->numSpanDeps = cg->numJumpTargets = 0; - cg->spanDepTodo = CG_OFFSET(cg); - return JS_TRUE; -} - -static ptrdiff_t -EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) -{ - JSBool extend; - ptrdiff_t jmp; - jsbytecode *pc; - - extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; - if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return -1; - - jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); - if (jmp >= 0 && (extend || cg->spanDeps)) { - pc = CG_CODE(cg, jmp); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return -1; - } - return jmp; -} - -static ptrdiff_t -GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc) -{ - JSSpanDep *sd; - JSJumpTarget *jt; - ptrdiff_t top; - - if (!cg->spanDeps) - return GET_JUMP_OFFSET(pc); - - sd = GetSpanDep(cg, pc); - jt = sd->target; - if (!JT_HAS_TAG(jt)) - return JT_TO_BPDELTA(jt); - - top = sd->top; - while (--sd >= cg->spanDeps && sd->top == top) - continue; - sd++; - return JT_CLR_TAG(jt)->offset - sd->offset; -} - -JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off) -{ - if (!cg->spanDeps) { - if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, off); - return JS_TRUE; - } - - if (!BuildSpanDepTable(cx, cg)) - return JS_FALSE; - } - - return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); -} - -bool -JSTreeContext::inStatement(JSStmtType type) -{ - for (JSStmtInfo *stmt = topStmt; stmt; stmt = stmt->down) { - if (stmt->type == type) - return true; - } - return false; -} - -bool -JSTreeContext::ensureSharpSlots() -{ -#if JS_HAS_SHARP_VARS - JS_STATIC_ASSERT(SHARP_NSLOTS == 2); - - if (sharpSlotBase >= 0) { - JS_ASSERT(flags & TCF_HAS_SHARPS); - return true; - } - - JS_ASSERT(!(flags & TCF_HAS_SHARPS)); - if (inFunction()) { - JSContext *cx = parser->context; - JSAtom *sharpArrayAtom = js_Atomize(cx, "#array", 6, 0); - JSAtom *sharpDepthAtom = js_Atomize(cx, "#depth", 6, 0); - if (!sharpArrayAtom || !sharpDepthAtom) - return false; - - sharpSlotBase = bindings.countVars(); - if (!bindings.addVariable(cx, sharpArrayAtom)) - return false; - if (!bindings.addVariable(cx, sharpDepthAtom)) - return false; - } else { - /* - * Compiler::compileScript will rebase immediate operands indexing - * the sharp slots to come at the end of the global script's |nfixed| - * slots storage, after gvars and regexps. - */ - sharpSlotBase = 0; - } - flags |= TCF_HAS_SHARPS; -#endif - return true; -} - -bool -JSTreeContext::skipSpansGenerator(unsigned skip) -{ - JSTreeContext *tc = this; - for (unsigned i = 0; i < skip; ++i, tc = tc->parent) { - if (!tc) - return false; - if (tc->flags & TCF_FUN_IS_GENERATOR) - return true; - } - return false; -} - -void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top) -{ - stmt->type = type; - stmt->flags = 0; - stmt->blockid = tc->blockid(); - SET_STATEMENT_TOP(stmt, top); - stmt->label = NULL; - JS_ASSERT(!stmt->blockBox); - stmt->down = tc->topStmt; - tc->topStmt = stmt; - if (STMT_LINKS_SCOPE(stmt)) { - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - } else { - stmt->downScope = NULL; - } -} - -void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObjectBox *blockBox, - ptrdiff_t top) -{ - js_PushStatement(tc, stmt, STMT_BLOCK, top); - stmt->flags |= SIF_SCOPE; - blockBox->parent = tc->blockChainBox; - blockBox->object->setParent(tc->blockChain()); - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - tc->blockChainBox = blockBox; - stmt->blockBox = blockBox; -} - -/* - * Emit a backpatch op with offset pointing to the previous jump of this type, - * so that we can walk back up the chain fixing up the op and jump offset. - */ -static ptrdiff_t -EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) -{ - ptrdiff_t offset, delta; - - offset = CG_OFFSET(cg); - delta = offset - *lastp; - *lastp = offset; - JS_ASSERT(delta > 0); - return EmitJump(cx, cg, op, delta); -} - -static ptrdiff_t -EmitTraceOp(JSContext *cx, JSCodeGenerator *cg) -{ - uint32 index = cg->traceIndex; - if (index < UINT16_MAX) - cg->traceIndex++; - return js_Emit3(cx, cg, JSOP_TRACE, UINT16_HI(index), UINT16_LO(index)); -} - -/* - * Macro to emit a bytecode followed by a uint16 immediate operand stored in - * big-endian order, used for arg and var numbers as well as for atomIndexes. - * NB: We use cx and cg from our caller's lexical environment, and return - * false on error. - */ -#define EMIT_UINT16_IMM_OP(op, i) \ - JS_BEGIN_MACRO \ - if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ - return JS_FALSE; \ - JS_END_MACRO - -#define EMIT_UINT16PAIR_IMM_OP(op, i, j) \ - JS_BEGIN_MACRO \ - ptrdiff_t off_ = js_EmitN(cx, cg, op, 2 * UINT16_LEN); \ - if (off_ < 0) \ - return JS_FALSE; \ - jsbytecode *pc_ = CG_CODE(cg, off_); \ - SET_UINT16(pc_, i); \ - pc_ += UINT16_LEN; \ - SET_UINT16(pc_, j); \ - JS_END_MACRO - -#define EMIT_UINT16_IN_PLACE(offset, op, i) \ - JS_BEGIN_MACRO \ - CG_CODE(cg, offset)[0] = op; \ - CG_CODE(cg, offset)[1] = UINT16_HI(i); \ - CG_CODE(cg, offset)[2] = UINT16_LO(i); \ - JS_END_MACRO - -static JSBool -FlushPops(JSContext *cx, JSCodeGenerator *cg, intN *npops) -{ - JS_ASSERT(*npops != 0); - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - EMIT_UINT16_IMM_OP(JSOP_POPN, *npops); - *npops = 0; - return JS_TRUE; -} - -/* - * Emit additional bytecode(s) for non-local jumps. - */ -static JSBool -EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt) -{ - intN depth, npops; - JSStmtInfo *stmt; - - /* - * The non-local jump fixup we emit will unbalance cg->stackDepth, because - * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the - * end of a with statement, so we save cg->stackDepth here and restore it - * just before a successful return. - */ - depth = cg->stackDepth; - npops = 0; - -#define FLUSH_POPS() if (npops && !FlushPops(cx, cg, &npops)) return JS_FALSE - - for (stmt = cg->topStmt; stmt != toStmt; stmt = stmt->down) { - switch (stmt->type) { - case STMT_FINALLY: - FLUSH_POPS(); - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)) < 0) - return JS_FALSE; - break; - - case STMT_WITH: - /* There's a With object on the stack that we need to pop. */ - FLUSH_POPS(); - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - break; - - case STMT_FOR_IN_LOOP: - /* - * The iterator and the object being iterated need to be popped. - */ - FLUSH_POPS(); - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) - return JS_FALSE; - break; - - case STMT_SUBROUTINE: - /* - * There's a [exception or hole, retsub pc-index] pair on the - * stack that we need to pop. - */ - npops += 2; - break; - - default:; - } - - if (stmt->flags & SIF_SCOPE) { - /* There is a Block object with locals on the stack to pop. */ - FLUSH_POPS(); - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (!EmitLeaveBlock(cx, cg, JSOP_LEAVEBLOCK, stmt->blockBox)) - return JS_FALSE; - } - } - - FLUSH_POPS(); - cg->stackDepth = depth; - return JS_TRUE; - -#undef FLUSH_POPS -} - -static JSBool -EmitKnownBlockChain(JSContext *cx, JSCodeGenerator *cg, JSObjectBox *box) -{ - if (box) - return EmitIndexOp(cx, JSOP_BLOCKCHAIN, box->index, cg); - return js_Emit1(cx, cg, JSOP_NULLBLOCKCHAIN) >= 0; -} - -static JSBool -EmitBlockChain(JSContext *cx, JSCodeGenerator *cg) -{ - return EmitKnownBlockChain(cx, cg, cg->blockChainBox); -} - -static ptrdiff_t -EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) -{ - intN index; - - if (!EmitNonLocalJumpFixup(cx, cg, toStmt)) - return -1; - - if (label) - index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); - else if (noteType != SRC_NULL) - index = js_NewSrcNote(cx, cg, noteType); - else - index = 0; - if (index < 0) - return -1; - - ptrdiff_t result = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); - if (result < 0) - return result; - - if (!EmitBlockChain(cx, cg)) - return -1; - - return result; -} - -static JSBool -BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, - jsbytecode *target, jsbytecode op) -{ - jsbytecode *pc, *stop; - ptrdiff_t delta, span; - - pc = CG_CODE(cg, last); - stop = CG_CODE(cg, -1); - while (pc != stop) { - delta = GetJumpOffset(cg, pc); - span = target - pc; - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); - - /* - * Set *pc after jump offset in case bpdelta didn't overflow, but span - * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable - * and need to see the JSOP_BACKPATCH* op at *pc). - */ - *pc = op; - pc -= delta; - } - return JS_TRUE; -} - -void -js_PopStatement(JSTreeContext *tc) -{ - JSStmtInfo *stmt; - - stmt = tc->topStmt; - tc->topStmt = stmt->down; - if (STMT_LINKS_SCOPE(stmt)) { - tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) { - tc->blockChainBox = stmt->blockBox->parent; - JS_SCOPE_DEPTH_METERING(--tc->scopeDepth); - } - } -} - -JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) -{ - JSStmtInfo *stmt; - - stmt = cg->topStmt; - if (!STMT_IS_TRYING(stmt) && - (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || - !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), - JSOP_GOTO))) { - return JS_FALSE; - } - js_PopStatement(cg); - return JS_TRUE; -} - -JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn) -{ - /* XXX just do numbers for now */ - if (pn->pn_type == TOK_NUMBER) { - if (!cg->constMap.put(atom, NumberValue(pn->pn_dval))) - return JS_FALSE; - } - return JS_TRUE; -} - -JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt) -{ - if (!stmt) - stmt = tc->topScopeStmt; - for (; stmt; stmt = stmt->downScope) { - if (stmt->type == STMT_WITH) - break; - - /* Skip "maybe scope" statements that don't contain let bindings. */ - if (!(stmt->flags & SIF_SCOPE)) - continue; - - JSObject *obj = stmt->blockBox->object; - JS_ASSERT(obj->isStaticBlock()); - - const Shape *shape = obj->nativeLookup(ATOM_TO_JSID(atom)); - if (shape) { - JS_ASSERT(shape->hasShortID()); - - if (slotp) { - JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32()); - *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid; - } - return stmt; - } - } - - if (slotp) - *slotp = -1; - return stmt; -} - -/* - * The function sets vp to NO_CONSTANT when the atom does not corresponds to a - * name defining a constant. - */ -static JSBool -LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - Value *constp) -{ - JSStmtInfo *stmt; - JSObject *obj; - - /* - * Chase down the cg stack, but only until we reach the outermost cg. - * This enables propagating consts from top-level into switch cases in a - * function compiled along with the top-level script. - */ - constp->setMagic(JS_NO_CONSTANT); - do { - if (cg->inFunction() || cg->compileAndGo()) { - /* XXX this will need revising if 'const' becomes block-scoped. */ - stmt = js_LexicalLookup(cg, atom, NULL); - if (stmt) - return JS_TRUE; - - if (JSCodeGenerator::ConstMap::Ptr p = cg->constMap.lookup(atom)) { - JS_ASSERT(!p->value.isMagic(JS_NO_CONSTANT)); - *constp = p->value; - return JS_TRUE; - } - - /* - * Try looking in the variable object for a direct property that - * is readonly and permanent. We know such a property can't be - * shadowed by another property on obj's prototype chain, or a - * with object or catch variable; nor can prop's value be changed, - * nor can prop be deleted. - */ - if (cg->inFunction()) { - if (cg->bindings.hasBinding(cx, atom)) - break; - } else { - JS_ASSERT(cg->compileAndGo()); - obj = cg->scopeChain(); - - const Shape *shape = obj->nativeLookup(ATOM_TO_JSID(atom)); - if (shape) { - /* - * We're compiling code that will be executed immediately, - * not re-executed against a different scope chain and/or - * variable object. Therefore we can get constant values - * from our variable object here. - */ - if (!shape->writable() && !shape->configurable() && - shape->hasDefaultGetter() && obj->containsSlot(shape->slot)) { - *constp = obj->getSlot(shape->slot); - } - } - - if (shape) - break; - } - } - } while (cg->parent && (cg = cg->parent->asCodeGenerator())); - return JS_TRUE; -} - -static inline bool -FitsWithoutBigIndex(uintN index) -{ - return index < JS_BIT(16); -} - -/* - * Return JSOP_NOP to indicate that index fits 2 bytes and no index segment - * reset instruction is necessary, JSOP_FALSE to indicate an error or either - * JSOP_RESETBASE0 or JSOP_RESETBASE1 to indicate the reset bytecode to issue - * after the main bytecode sequence. - */ -static JSOp -EmitBigIndexPrefix(JSContext *cx, JSCodeGenerator *cg, uintN index) -{ - uintN indexBase; - - /* - * We have max 3 bytes for indexes and check for INDEX_LIMIT overflow only - * for big indexes. - */ - JS_STATIC_ASSERT(INDEX_LIMIT <= JS_BIT(24)); - JS_STATIC_ASSERT(INDEX_LIMIT >= - (JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 2) << 16); - - if (FitsWithoutBigIndex(index)) - return JSOP_NOP; - indexBase = index >> 16; - if (indexBase <= JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 1) { - if (js_Emit1(cx, cg, (JSOp)(JSOP_INDEXBASE1 + indexBase - 1)) < 0) - return JSOP_FALSE; - return JSOP_RESETBASE0; - } - - if (index >= INDEX_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LITERALS); - return JSOP_FALSE; - } - - if (js_Emit2(cx, cg, JSOP_INDEXBASE, (JSOp)indexBase) < 0) - return JSOP_FALSE; - return JSOP_RESETBASE; -} - -/* - * Emit a bytecode and its 2-byte constant index immediate operand. If the - * index requires more than 2 bytes, emit a prefix op whose 8-bit immediate - * operand effectively extends the 16-bit immediate of the prefixed opcode, - * by changing index "segment" (see jsinterp.c). We optimize segments 1-3 - * with single-byte JSOP_INDEXBASE[123] codes. - * - * Such prefixing currently requires a suffix to restore the "zero segment" - * register setting, but this could be optimized further. - */ -static JSBool -EmitIndexOp(JSContext *cx, JSOp op, uintN index, JSCodeGenerator *cg) -{ - JSOp bigSuffix; - - bigSuffix = EmitBigIndexPrefix(cx, cg, index); - if (bigSuffix == JSOP_FALSE) - return JS_FALSE; - EMIT_UINT16_IMM_OP(op, index); - return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; -} - -/* - * Slight sugar for EmitIndexOp, again accessing cx and cg from the macro - * caller's lexical environment, and embedding a false return on error. - */ -#define EMIT_INDEX_OP(op, index) \ - JS_BEGIN_MACRO \ - if (!EmitIndexOp(cx, op, index, cg)) \ - return JS_FALSE; \ - JS_END_MACRO - -static JSBool -EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSAtomListElement *ale; - - JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - if (op == JSOP_GETPROP && - pn->pn_atom == cx->runtime->atomState.lengthAtom) { - return js_Emit1(cx, cg, JSOP_LENGTH) >= 0; - } - ale = cg->atomList.add(cg->parser, pn->pn_atom); - if (!ale) - return JS_FALSE; - return EmitIndexOp(cx, op, ALE_INDEX(ale), cg); -} - -static JSBool -EmitObjectOp(JSContext *cx, JSObjectBox *objbox, JSOp op, - JSCodeGenerator *cg) -{ - JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); - return EmitIndexOp(cx, op, cg->objectList.index(objbox), cg); -} - -/* - * What good are ARGNO_LEN and SLOTNO_LEN, you ask? The answer is that, apart - * from EmitSlotIndexOp, they abstract out the detail that both are 2, and in - * other parts of the code there's no necessary relationship between the two. - * The abstraction cracks here in order to share EmitSlotIndexOp code among - * the JSOP_DEFLOCALFUN and JSOP_GET{ARG,VAR,LOCAL}PROP cases. - */ -JS_STATIC_ASSERT(ARGNO_LEN == 2); -JS_STATIC_ASSERT(SLOTNO_LEN == 2); - -static JSBool -EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, - JSCodeGenerator *cg) -{ - JSOp bigSuffix; - ptrdiff_t off; - jsbytecode *pc; - - JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM || - JOF_OPTYPE(op) == JOF_SLOTOBJECT); - bigSuffix = EmitBigIndexPrefix(cx, cg, index); - if (bigSuffix == JSOP_FALSE) - return JS_FALSE; - - /* Emit [op, slot, index]. */ - off = js_EmitN(cx, cg, op, 2 + INDEX_LEN); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_UINT16(pc, slot); - pc += 2; - SET_INDEX(pc, index); - return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; -} - -bool -JSCodeGenerator::shouldNoteClosedName(JSParseNode *pn) -{ - return !callsEval() && pn->pn_defn && pn->isClosed(); -} - -/* - * Adjust the slot for a block local to account for the number of variables - * that share the same index space with locals. Due to the incremental code - * generation for top-level script, we do the adjustment via code patching in - * Compiler::compileScript; see comments there. - * - * The function returns -1 on failures. - */ -static jsint -AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot) -{ - JS_ASSERT((jsuint) slot < cg->maxStackDepth); - if (cg->inFunction()) { - slot += cg->bindings.countVars(); - if ((uintN) slot >= SLOTNO_LIMIT) { - ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS); - slot = -1; - } - } - return slot; -} - -static bool -EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) -{ - JS_ASSERT(PN_TYPE(pn) == TOK_LEXICALSCOPE); - if (!EmitObjectOp(cx, pn->pn_objbox, JSOP_ENTERBLOCK, cg)) - return false; - - JSObject *blockObj = pn->pn_objbox->object; - jsint depth = AdjustBlockSlot(cx, cg, OBJ_BLOCK_DEPTH(cx, blockObj)); - if (depth < 0) - return false; - - uintN base = JSSLOT_FREE(&js_BlockClass); - for (uintN slot = base, limit = base + OBJ_BLOCK_COUNT(cx, blockObj); slot < limit; slot++) { - const Value &v = blockObj->getSlot(slot); - - /* Beware the empty destructuring dummy. */ - if (v.isUndefined()) { - JS_ASSERT(slot + 1 <= limit); - continue; - } - - JSDefinition *dn = (JSDefinition *) v.toPrivate(); - JS_ASSERT(dn->pn_defn); - JS_ASSERT(uintN(dn->frameSlot() + depth) < JS_BIT(16)); - dn->pn_cookie.set(dn->pn_cookie.level(), uint16(dn->frameSlot() + depth)); -#ifdef DEBUG - for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { - JS_ASSERT(pnu->pn_lexdef == dn); - JS_ASSERT(!(pnu->pn_dflags & PND_BOUND)); - JS_ASSERT(pnu->pn_cookie.isFree()); - } -#endif - - /* - * If this variable is closed over, and |eval| is not present, then - * then set a bit in dslots so the Method JIT can deoptimize this - * slot. - */ - bool isClosed = cg->shouldNoteClosedName(dn); - 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; -} - -static JSBool -EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op, - JSObjectBox *box) -{ - JSOp bigSuffix; - uintN count = OBJ_BLOCK_COUNT(cx, box->object); - - bigSuffix = EmitBigIndexPrefix(cx, cg, box->index); - if (bigSuffix == JSOP_FALSE) - return JS_FALSE; - if (js_Emit5(cx, cg, op, count, box->index) < 0) - return JS_FALSE; - return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; -} - -/* - * Try to convert a *NAME op to a *GNAME op, which optimizes access to - * undeclared globals. Return true if a conversion was made. - * - * This conversion is not made if we are in strict mode. In eval code nested - * within (strict mode) eval code, access to an undeclared "global" might - * merely be to a binding local to that outer eval: - * - * "use strict"; - * var x = "global"; - * eval('var x = "eval"; eval("x");'); // 'eval', not 'global' - * - * Outside eval code, access to an undeclared global is a strict mode error: - * - * "use strict"; - * function foo() - * { - * undeclared = 17; // throws ReferenceError - * } - * foo(); - */ -static bool -TryConvertToGname(JSCodeGenerator *cg, JSParseNode *pn, JSOp *op) -{ - if (cg->compileAndGo() && - cg->compiler()->globalScope->globalObj && - !cg->mightAliasLocals() && - !pn->isDeoptimized() && - !(cg->flags & TCF_STRICT_MODE_CODE)) { - switch (*op) { - case JSOP_NAME: *op = JSOP_GETGNAME; break; - case JSOP_SETNAME: *op = JSOP_SETGNAME; break; - case JSOP_INCNAME: *op = JSOP_INCGNAME; break; - case JSOP_NAMEINC: *op = JSOP_GNAMEINC; break; - case JSOP_DECNAME: *op = JSOP_DECGNAME; break; - case JSOP_NAMEDEC: *op = JSOP_GNAMEDEC; break; - case JSOP_FORNAME: *op = JSOP_FORGNAME; break; - case JSOP_SETCONST: - case JSOP_DELNAME: - /* Not supported. */ - return false; - default: JS_NOT_REACHED("gname"); - } - return true; - } - return false; -} - -// Binds a global, given a |dn| that is known to have the PND_GVAR bit, and a pn -// that is |dn| or whose definition is |dn|. |pn->pn_cookie| is an outparam -// that will be free (meaning no binding), or a slot number. -static bool -BindKnownGlobal(JSContext *cx, JSCodeGenerator *cg, JSParseNode *dn, JSParseNode *pn, JSAtom *atom) -{ - // Cookie is an outparam; make sure caller knew to clear it. - JS_ASSERT(pn->pn_cookie.isFree()); - - if (cg->mightAliasLocals()) - return true; - - GlobalScope *globalScope = cg->compiler()->globalScope; - - uint32 index; - if (dn->pn_cookie.isFree()) { - // The definition wasn't bound, so find its atom's index in the - // mapping of defined globals. - JSAtomListElement *ale = globalScope->names.lookup(atom); - index = ALE_INDEX(ale); - } else { - JSCodeGenerator *globalcg = globalScope->cg; - - // If the definition is bound, and we're in the same cg, we can re-use - // its cookie. - if (globalcg == cg) { - pn->pn_cookie = dn->pn_cookie; - pn->pn_dflags |= PND_BOUND; - return true; - } - - // Otherwise, find the atom's index by using the originating cg's - // global use table. - index = globalcg->globalUses[dn->pn_cookie.asInteger()].slot; - } - - if (!cg->addGlobalUse(atom, index, &pn->pn_cookie)) - return false; - - if (!pn->pn_cookie.isFree()) - pn->pn_dflags |= PND_BOUND; - - return true; -} - -// See BindKnownGlobal()'s comment. -static bool -BindGlobal(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, JSAtom *atom) -{ - pn->pn_cookie.makeFree(); - - JSDefinition *dn; - if (pn->pn_used) { - dn = pn->pn_lexdef; - } else { - if (!pn->pn_defn) - return true; - dn = (JSDefinition *)pn; - } - - // Only optimize for defined globals. - if (!dn->isGlobal()) - return true; - - return BindKnownGlobal(cx, cg, dn, pn, atom); -} - -/* - * BindNameToSlot attempts to optimize name gets and sets to stack slot loads - * and stores, given the compile-time information in cg and a TOK_NAME node pn. - * It returns false on error, true on success. - * - * The caller can inspect pn->pn_cookie for FREE_UPVAR_COOKIE to tell whether - * optimization occurred, in which case BindNameToSlot also updated pn->pn_op. - * If pn->pn_cookie is still FREE_UPVAR_COOKIE on return, pn->pn_op still may - * have been optimized, e.g., from JSOP_NAME to JSOP_CALLEE. Whether or not - * pn->pn_op was modified, if this function finds an argument or local variable - * name, PND_CONST will be set in pn_dflags for read-only properties after a - * successful return. - * - * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget - * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases - * in js_EmitTree. - */ -static JSBool -BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - JSDefinition *dn; - JSOp op; - JSAtom *atom; - JSDefinition::Kind dn_kind; - JSAtomListElement *ale; - uintN index; - - JS_ASSERT(pn->pn_type == TOK_NAME); - - /* Idempotency tests come first, since we may be called more than once. */ - if (pn->pn_dflags & PND_BOUND) - return JS_TRUE; - - /* No cookie initialized for these two, they're pre-bound by definition. */ - JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE); - - /* - * The parser linked all uses (including forward references) to their - * definitions, unless a with statement or direct eval intervened. - */ - if (pn->pn_used) { - JS_ASSERT(pn->pn_cookie.isFree()); - dn = pn->pn_lexdef; - JS_ASSERT(dn->pn_defn); - if (pn->isDeoptimized()) - return JS_TRUE; - pn->pn_dflags |= (dn->pn_dflags & PND_CONST); - } else { - if (!pn->pn_defn) - return JS_TRUE; - dn = (JSDefinition *) pn; - } - - op = PN_OP(pn); - if (op == JSOP_NOP) - return JS_TRUE; - - JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - atom = pn->pn_atom; - UpvarCookie cookie = dn->pn_cookie; - dn_kind = dn->kind(); - - /* - * Turn attempts to mutate const-declared bindings into get ops (for - * pre-increment and pre-decrement ops, our caller will have to emit - * JSOP_POS, JSOP_ONE, and JSOP_ADD as well). - * - * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared - * bindings visible to the compiler are permanent in JS unless the - * declaration originates at top level in eval code. - */ - switch (op) { - case JSOP_NAME: - case JSOP_SETCONST: - break; - case JSOP_DELNAME: - if (dn_kind != JSDefinition::UNKNOWN) { - if (cg->parser->callerFrame && dn->isTopLevel()) - JS_ASSERT(cg->compileAndGo()); - else - pn->pn_op = JSOP_FALSE; - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; - } - break; - default: - if (pn->isConst()) - pn->pn_op = op = JSOP_NAME; - } - - if (dn->isGlobal()) { - if (op == JSOP_NAME) { - /* - * If the definition is a defined global, not potentially aliased - * by a local variable, and not mutating the variable, try and - * optimize to a fast, unguarded global access. - */ - if (!BindKnownGlobal(cx, cg, dn, pn, atom)) - return JS_FALSE; - if (!pn->pn_cookie.isFree()) { - pn->pn_op = JSOP_GETGLOBAL; - return JS_TRUE; - } - } - - /* - * The locally stored cookie here should really come from |pn|, not - * |dn|. For example, we could have a SETGNAME op's lexdef be a - * GETGLOBAL op, and their cookies have very different meanings. As - * a workaround, just make the cookie free. - */ - cookie.makeFree(); - } - - if (cookie.isFree()) { - StackFrame *caller = cg->parser->callerFrame; - if (caller) { - JS_ASSERT(cg->compileAndGo()); - - /* - * Don't generate upvars on the left side of a for loop. See - * bug 470758. - */ - if (cg->flags & TCF_IN_FOR_INIT) - return JS_TRUE; - - JS_ASSERT(caller->isScriptFrame()); - - /* - * If this is an eval in the global scope, then unbound variables - * must be globals, so try to use GNAME ops. - */ - if (caller->isGlobalFrame() && TryConvertToGname(cg, pn, &op)) { - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - - pn->pn_op = op; - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; - } - - /* - * Out of tricks, so we must rely on PICs to optimize named - * accesses from direct eval called from function code. - */ - return JS_TRUE; - } - - /* Optimize accesses to undeclared globals. */ - if (!cg->mightAliasLocals() && !TryConvertToGname(cg, pn, &op)) - return JS_TRUE; - - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - - pn->pn_op = op; - pn->pn_dflags |= PND_BOUND; - - return JS_TRUE; - } - - uint16 level = cookie.level(); - JS_ASSERT(cg->staticLevel >= level); - - const uintN skip = cg->staticLevel - level; - if (skip != 0) { - JS_ASSERT(cg->inFunction()); - JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, cg->lexdeps.lookup(atom)); - JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - JS_ASSERT(cg->fun()->u.i.skipmin <= skip); - - /* - * If op is a mutating opcode, this upvar's lookup skips too many levels, - * or the function is heavyweight, we fall back on JSOP_*NAME*. - */ - if (op != JSOP_NAME) - return JS_TRUE; - if (level >= UpvarCookie::UPVAR_LEVEL_LIMIT) - return JS_TRUE; - if (cg->flags & TCF_FUN_HEAVYWEIGHT) - return JS_TRUE; - - if (!cg->fun()->isFlatClosure()) - return JS_TRUE; - - ale = cg->upvarList.lookup(atom); - if (ale) { - index = ALE_INDEX(ale); - } else { - if (!cg->bindings.addUpvar(cx, atom)) - return JS_FALSE; - - ale = cg->upvarList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - index = ALE_INDEX(ale); - JS_ASSERT(index == cg->upvarList.count - 1); - - UpvarCookie *vector = cg->upvarMap.vector; - uint32 length = cg->lexdeps.count; - if (!vector || cg->upvarMap.length != length) { - vector = (UpvarCookie *) cx->realloc_(vector, length * sizeof *vector); - if (!vector) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - cg->upvarMap.vector = vector; - cg->upvarMap.length = length; - } - - uintN slot = cookie.slot(); - if (slot != UpvarCookie::CALLEE_SLOT && dn_kind != JSDefinition::ARG) { - JSTreeContext *tc = cg; - do { - tc = tc->parent; - } while (tc->staticLevel != level); - if (tc->inFunction()) - slot += tc->fun()->nargs; - } - - JS_ASSERT(index < cg->upvarMap.length); - vector[index].set(skip, slot); - } - - pn->pn_op = JSOP_GETFCSLOT; - JS_ASSERT((index & JS_BITMASK(16)) == index); - pn->pn_cookie.set(0, index); - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; - } - - /* - * We are compiling a function body and may be able to optimize name - * to stack slot. Look for an argument or variable in the function and - * rewrite pn_op and update pn accordingly. - */ - switch (dn_kind) { - case JSDefinition::UNKNOWN: - return JS_TRUE; - - case JSDefinition::LET: - switch (op) { - case JSOP_NAME: op = JSOP_GETLOCAL; break; - case JSOP_SETNAME: op = JSOP_SETLOCAL; break; - case JSOP_INCNAME: op = JSOP_INCLOCAL; break; - case JSOP_NAMEINC: op = JSOP_LOCALINC; break; - case JSOP_DECNAME: op = JSOP_DECLOCAL; break; - case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; - case JSOP_FORNAME: op = JSOP_FORLOCAL; break; - default: JS_NOT_REACHED("let"); - } - break; - - case JSDefinition::ARG: - switch (op) { - case JSOP_NAME: op = JSOP_GETARG; break; - case JSOP_SETNAME: op = JSOP_SETARG; break; - case JSOP_INCNAME: op = JSOP_INCARG; break; - case JSOP_NAMEINC: op = JSOP_ARGINC; break; - case JSOP_DECNAME: op = JSOP_DECARG; break; - case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; - case JSOP_FORNAME: op = JSOP_FORARG; break; - default: JS_NOT_REACHED("arg"); - } - JS_ASSERT(!pn->isConst()); - break; - - case JSDefinition::VAR: - if (PN_OP(dn) == JSOP_CALLEE) { - JS_ASSERT(op != JSOP_CALLEE); - JS_ASSERT((cg->fun()->flags & JSFUN_LAMBDA) && atom == cg->fun()->atom); - - /* - * Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight to - * address two cases: a new binding introduced by eval, and - * assignment to the name in strict mode. - * - * var fun = (function f(s) { eval(s); return f; }); - * assertEq(fun("var f = 42"), 42); - * - * ECMAScript specifies that a function expression's name is bound - * in a lexical environment distinct from that used to bind its - * named parameters, the arguments object, and its variables. The - * new binding for "var f = 42" shadows the binding for the - * function itself, so the name of the function will not refer to - * the function. - * - * (function f() { "use strict"; f = 12; })(); - * - * Outside strict mode, assignment to a function expression's name - * has no effect. But in strict mode, this attempt to mutate an - * immutable binding must throw a TypeError. We implement this by - * not optimizing such assignments and by marking such functions as - * heavyweight, ensuring that the function name is represented in - * the scope chain so that assignment will throw a TypeError. - */ - JS_ASSERT(op != JSOP_DELNAME); - if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) { - op = JSOP_CALLEE; - pn->pn_dflags |= PND_CONST; - } - - pn->pn_op = op; - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; - } - /* FALL THROUGH */ - - default: - JS_ASSERT_IF(dn_kind != JSDefinition::FUNCTION, - dn_kind == JSDefinition::VAR || - dn_kind == JSDefinition::CONST); - switch (op) { - case JSOP_NAME: op = JSOP_GETLOCAL; break; - case JSOP_SETNAME: op = JSOP_SETLOCAL; break; - case JSOP_SETCONST: op = JSOP_SETLOCAL; break; - case JSOP_INCNAME: op = JSOP_INCLOCAL; break; - case JSOP_NAMEINC: op = JSOP_LOCALINC; break; - case JSOP_DECNAME: op = JSOP_DECLOCAL; break; - case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; - case JSOP_FORNAME: op = JSOP_FORLOCAL; break; - default: JS_NOT_REACHED("local"); - } - JS_ASSERT_IF(dn_kind == JSDefinition::CONST, pn->pn_dflags & PND_CONST); - break; - } - - JS_ASSERT(op != PN_OP(pn)); - pn->pn_op = op; - pn->pn_cookie.set(0, cookie.slot()); - pn->pn_dflags |= PND_BOUND; - return JS_TRUE; -} - -bool -JSCodeGenerator::addGlobalUse(JSAtom *atom, uint32 slot, UpvarCookie *cookie) -{ - JSAtomListElement *ale = globalMap.lookup(atom); - if (ale) { - cookie->set(0, uint16(ALE_INDEX(ale))); - return true; - } - - /* Don't bother encoding indexes >= uint16 */ - if (globalUses.length() >= UINT16_LIMIT) { - cookie->makeFree(); - return true; - } - - /* Find or add an existing atom table entry. */ - ale = atomList.add(parser, atom); - if (!ale) - return false; - - cookie->set(0, globalUses.length()); - - GlobalSlotArray::Entry entry = { ALE_INDEX(ale), slot }; - if (!globalUses.append(entry)) - return false; - - ale = globalMap.add(parser, atom); - if (!ale) - return false; - - ALE_SET_INDEX(ale, cookie->asInteger()); - return true; -} - -/* - * If pn contains a useful expression, return true with *answer set to true. - * If pn contains a useless expression, return true with *answer set to false. - * Return false on error. - * - * The caller should initialize *answer to false and invoke this function on - * an expression statement or similar subtree to decide whether the tree could - * produce code that has any side effects. For an expression statement, we - * define useless code as code with no side effects, because the main effect, - * the value left on the stack after the code executes, will be discarded by a - * pop bytecode. - */ -static JSBool -CheckSideEffects(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool *answer) -{ - JSBool ok; - JSParseNode *pn2; - - ok = JS_TRUE; - if (!pn || *answer) - return ok; - - switch (pn->pn_arity) { - case PN_FUNC: - /* - * A named function, contrary to ES3, is no longer useful, because we - * bind its name lexically (using JSOP_CALLEE) instead of creating an - * Object instance and binding a readonly, permanent property in it - * (the object and binding can be detected and hijacked or captured). - * This is a bug fix to ES3; it is fixed in ES3.1 drafts. - */ - *answer = JS_FALSE; - break; - - case PN_LIST: - if (pn->pn_op == JSOP_NOP || - pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || - pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { - /* - * Non-operators along with ||, &&, ===, and !== never invoke - * toString or valueOf. - */ - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) - ok &= CheckSideEffects(cx, cg, pn2, answer); - } else { - /* - * All invocation operations (construct: TOK_NEW, call: TOK_LP) - * are presumed to be useful, because they may have side effects - * even if their main effect (their return value) is discarded. - * - * TOK_LB binary trees of 3 or more nodes are flattened into lists - * to avoid too much recursion. All such lists must be presumed - * to be useful because each index operation could invoke a getter - * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, - * does not apply here: arguments[i][j] might invoke a getter). - * - * Likewise, array and object initialisers may call prototype - * setters (the __defineSetter__ built-in, and writable __proto__ - * on Array.prototype create this hazard). Initialiser list nodes - * have JSOP_NEWINIT in their pn_op. - */ - *answer = JS_TRUE; - } - break; - - case PN_TERNARY: - ok = CheckSideEffects(cx, cg, pn->pn_kid1, answer) && - CheckSideEffects(cx, cg, pn->pn_kid2, answer) && - CheckSideEffects(cx, cg, pn->pn_kid3, answer); - break; - - case PN_BINARY: - if (pn->pn_type == TOK_ASSIGN) { - /* - * Assignment is presumed to be useful, even if the next operation - * is another assignment overwriting this one's ostensible effect, - * because the left operand may be a property with a setter that - * has side effects. - * - * The only exception is assignment of a useless value to a const - * declared in the function currently being compiled. - */ - pn2 = pn->pn_left; - if (pn2->pn_type != TOK_NAME) { - *answer = JS_TRUE; - } else { - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (!CheckSideEffects(cx, cg, pn->pn_right, answer)) - return JS_FALSE; - if (!*answer && (pn->pn_op != JSOP_NOP || !pn2->isConst())) - *answer = JS_TRUE; - } - } else { - if (pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || - pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { - /* - * ||, &&, ===, and !== do not convert their operands via - * toString or valueOf method calls. - */ - ok = CheckSideEffects(cx, cg, pn->pn_left, answer) && - CheckSideEffects(cx, cg, pn->pn_right, answer); - } else { - /* - * We can't easily prove that neither operand ever denotes an - * object with a toString or valueOf method. - */ - *answer = JS_TRUE; - } - } - break; - - case PN_UNARY: - switch (pn->pn_type) { - case TOK_DELETE: - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (pn2->isConst()) { - *answer = JS_FALSE; - break; - } - /* FALL THROUGH */ - case TOK_DOT: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif - case TOK_LP: - case TOK_LB: - /* All these delete addressing modes have effects too. */ - *answer = JS_TRUE; - break; - default: - ok = CheckSideEffects(cx, cg, pn2, answer); - break; - } - break; - - case TOK_UNARYOP: - if (pn->pn_op == JSOP_NOT) { - /* ! does not convert its operand via toString or valueOf. */ - ok = CheckSideEffects(cx, cg, pn->pn_kid, answer); - break; - } - /* FALL THROUGH */ - - default: - /* - * All of TOK_INC, TOK_DEC, TOK_THROW, TOK_YIELD, and TOK_DEFSHARP - * have direct effects. Of the remaining unary-arity node types, - * we can't easily prove that the operand never denotes an object - * with a toString or valueOf method. - */ - *answer = JS_TRUE; - break; - } - break; - - case PN_NAME: - /* - * Take care to avoid trying to bind a label name (labels, both for - * statements and property values in object initialisers, have pn_op - * defaulted to JSOP_NOP). - */ - if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { - if (!BindNameToSlot(cx, cg, pn)) - return JS_FALSE; - if (pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE && - pn->pn_cookie.isFree()) { - /* - * Not an argument or local variable use, and not a use of a - * unshadowed named function expression's given name, so this - * expression could invoke a getter that has side effects. - */ - *answer = JS_TRUE; - } - } - pn2 = pn->maybeExpr(); - if (pn->pn_type == TOK_DOT) { - if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (!(pn2->pn_op == JSOP_ARGUMENTS && - pn->pn_atom == cx->runtime->atomState.lengthAtom)) { - /* - * Any dotted property reference could call a getter, except - * for arguments.length where arguments is unambiguous. - */ - *answer = JS_TRUE; - } - } - ok = CheckSideEffects(cx, cg, pn2, answer); - break; - - case PN_NAMESET: - ok = CheckSideEffects(cx, cg, pn->pn_tree, answer); - break; - - case PN_NULLARY: - if (pn->pn_type == TOK_DEBUGGER) - *answer = JS_TRUE; - break; - } - return ok; -} - -static JSBool -EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool callContext) -{ - JSOp op; - - if (!BindNameToSlot(cx, cg, pn)) - return JS_FALSE; - op = PN_OP(pn); - - if (callContext) { - switch (op) { - case JSOP_NAME: - op = JSOP_CALLNAME; - break; - case JSOP_GETGNAME: - op = JSOP_CALLGNAME; - break; - case JSOP_GETGLOBAL: - op = JSOP_CALLGLOBAL; - break; - case JSOP_GETARG: - op = JSOP_CALLARG; - break; - case JSOP_GETLOCAL: - op = JSOP_CALLLOCAL; - break; - case JSOP_GETFCSLOT: - op = JSOP_CALLFCSLOT; - break; - default: - JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE); - break; - } - } - - if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - if (callContext && js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - if (!pn->pn_cookie.isFree()) { - EMIT_UINT16_IMM_OP(op, pn->pn_cookie.asInteger()); - } else { - if (!EmitAtomOp(cx, pn, op, cg)) - return JS_FALSE; - } - } - - return JS_TRUE; -} - -#if JS_HAS_XML_SUPPORT -static JSBool -EmitXMLName(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSParseNode *pn2; - uintN oldflags; - - JS_ASSERT(pn->pn_type == TOK_UNARYOP); - JS_ASSERT(pn->pn_op == JSOP_XMLNAME); - JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME); - - pn2 = pn->pn_kid; - oldflags = cg->flags; - cg->flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->flags |= oldflags & TCF_IN_FOR_INIT; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - - return js_Emit1(cx, cg, op) >= 0; -} -#endif - -static JSBool -EmitSpecialPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - /* - * Special case for obj.__proto__ to deoptimize away from fast paths in the - * interpreter and trace recorder, which skip dense array instances by - * going up to Array.prototype before looking up the property name. - */ - JSAtomListElement *ale = cg->atomList.add(cg->parser, pn->pn_atom); - if (!ale) - return JS_FALSE; - if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg, - JSBool callContext) -{ - JSParseNode *pn2, *pndot, *pnup, *pndown; - ptrdiff_t top; - - JS_ASSERT(pn->pn_arity == PN_NAME); - pn2 = pn->maybeExpr(); - - /* Special case deoptimization for __proto__. */ - if ((op == JSOP_GETPROP || op == JSOP_CALLPROP) && - pn->pn_atom == cx->runtime->atomState.protoAtom) { - if (pn2 && !js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - return EmitSpecialPropOp(cx, pn, callContext ? JSOP_CALLELEM : JSOP_GETELEM, cg); - } - - if (callContext) { - JS_ASSERT(pn->pn_type == TOK_DOT); - JS_ASSERT(op == JSOP_GETPROP); - op = JSOP_CALLPROP; - } else if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT) { - if (pn2->pn_op == JSOP_THIS) { - if (pn->pn_atom != cx->runtime->atomState.lengthAtom) { - /* Fast path for gets of |this.foo|. */ - return EmitAtomOp(cx, pn, JSOP_GETTHISPROP, cg); - } - } else if (pn2->pn_type == TOK_NAME) { - /* - * Try to optimize: - * - arguments.length into JSOP_ARGCNT - * - argname.prop into JSOP_GETARGPROP - * - localname.prop into JSOP_GETLOCALPROP - * but don't do this if the property is 'length' -- prefer to emit - * JSOP_GETARG, etc., and then JSOP_LENGTH. - */ - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (pn->pn_atom == cx->runtime->atomState.lengthAtom) { - if (pn2->pn_op == JSOP_ARGUMENTS) - return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; - } else { - switch (pn2->pn_op) { - case JSOP_GETARG: - op = JSOP_GETARGPROP; - goto do_indexconst; - case JSOP_GETLOCAL: - op = JSOP_GETLOCALPROP; - do_indexconst: { - JSAtomListElement *ale; - jsatomid atomIndex; - - ale = cg->atomList.add(cg->parser, pn->pn_atom); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - return EmitSlotIndexOp(cx, op, pn2->pn_cookie.asInteger(), atomIndex, cg); - } - - default:; - } - } - } - } - - /* - * If the object operand is also a dotted property reference, reverse the - * list linked via pn_expr temporarily so we can iterate over it from the - * bottom up (reversing again as we go), to avoid excessive recursion. - */ - if (pn2->pn_type == TOK_DOT) { - pndot = pn2; - pnup = NULL; - top = CG_OFFSET(cg); - for (;;) { - /* Reverse pndot->pn_expr to point up, not down. */ - pndot->pn_offset = top; - JS_ASSERT(!pndot->pn_used); - pndown = pndot->pn_expr; - pndot->pn_expr = pnup; - if (pndown->pn_type != TOK_DOT) - break; - pnup = pndot; - pndot = pndown; - } - - /* pndown is a primary expression, not a dotted property reference. */ - if (!js_EmitTree(cx, cg, pndown)) - return JS_FALSE; - - do { - /* Walk back up the list, emitting annotated name ops. */ - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pndown->pn_offset) < 0) { - return JS_FALSE; - } - - /* Special case deoptimization on __proto__, as above. */ - if (pndot->pn_arity == PN_NAME && pndot->pn_atom == cx->runtime->atomState.protoAtom) { - if (!EmitSpecialPropOp(cx, pndot, JSOP_GETELEM, cg)) - return JS_FALSE; - } else if (!EmitAtomOp(cx, pndot, PN_OP(pndot), cg)) { - return JS_FALSE; - } - - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; - pndown = pndot; - } while ((pndot = pnup) != NULL); - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - - return EmitAtomOp(cx, pn, op, cg); -} - -static JSBool -EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - ptrdiff_t top; - JSParseNode *left, *right, *next, ltmp, rtmp; - int32_t slot; - - top = CG_OFFSET(cg); - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain to avoid too much recursion. */ - JS_ASSERT(pn->pn_op == JSOP_GETELEM); - JS_ASSERT(pn->pn_count >= 3); - left = pn->pn_head; - right = pn->last(); - next = left->pn_next; - JS_ASSERT(next != right); - - /* - * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by - * one or more index expression and JSOP_GETELEM op pairs. - */ - if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, cg, left)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT32(next->pn_dval, &slot) && - jsuint(slot) < JS_BIT(16) && - (!cg->inStrictMode() || - (!cg->mutatesParameter() && !cg->callsEval()))) { - /* - * arguments[i]() requires arguments object as "this". - * Check that we never generates list for that usage. - */ - JS_ASSERT(op != JSOP_CALLELEM || next->pn_next); - left->pn_offset = next->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - left = next; - next = left->pn_next; - } - } - - /* - * Check whether we generated JSOP_ARGSUB, just above, and have only - * one more index expression to emit. Given arguments[0][j], we must - * skip the while loop altogether, falling through to emit code for j - * (in the subtree referenced by right), followed by the annotated op, - * at the bottom of this function. - */ - JS_ASSERT(next != right || pn->pn_count == 3); - if (left == pn->pn_head) { - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - while (next != right) { - if (!js_EmitTree(cx, cg, next)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - next = next->pn_next; - } - } else { - if (pn->pn_arity == PN_NAME) { - /* - * Set left and right so pn appears to be a TOK_LB node, instead - * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and - * EmitDestructuringOps nearer below. In the destructuring case, - * the base expression (pn_expr) of the name may be null, which - * means we have to emit a JSOP_BINDNAME. - */ - left = pn->maybeExpr(); - if (!left) { - left = <mp; - left->pn_type = TOK_STRING; - left->pn_op = JSOP_BINDNAME; - left->pn_arity = PN_NULLARY; - left->pn_pos = pn->pn_pos; - left->pn_atom = pn->pn_atom; - } - right = &rtmp; - right->pn_type = TOK_STRING; - right->pn_op = js_IsIdentifier(pn->pn_atom) - ? JSOP_QNAMEPART - : JSOP_STRING; - right->pn_arity = PN_NULLARY; - right->pn_pos = pn->pn_pos; - right->pn_atom = pn->pn_atom; - } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - left = pn->pn_left; - right = pn->pn_right; - } - - /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ - if (op == JSOP_GETELEM && - left->pn_type == TOK_NAME && - right->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, cg, left)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT32(right->pn_dval, &slot) && - jsuint(slot) < JS_BIT(16) && - (!cg->inStrictMode() || - (!cg->mutatesParameter() && !cg->callsEval()))) { - left->pn_offset = right->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - return JS_TRUE; - } - } - - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - - /* The right side of the descendant operator is implicitly quoted. */ - JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || - right->pn_op == JSOP_QNAMEPART); - if (!js_EmitTree(cx, cg, right)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - return js_Emit1(cx, cg, op) >= 0; -} - -static JSBool -EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) -{ - int32_t ival; - uint32 u; - ptrdiff_t off; - jsbytecode *pc; - - if (JSDOUBLE_IS_INT32(dval, &ival)) { - if (ival == 0) - return js_Emit1(cx, cg, JSOP_ZERO) >= 0; - if (ival == 1) - return js_Emit1(cx, cg, JSOP_ONE) >= 0; - if ((jsint)(int8)ival == ival) - return js_Emit2(cx, cg, JSOP_INT8, (jsbytecode)(int8)ival) >= 0; - - u = (uint32)ival; - if (u < JS_BIT(16)) { - EMIT_UINT16_IMM_OP(JSOP_UINT16, u); - } else if (u < JS_BIT(24)) { - off = js_EmitN(cx, cg, JSOP_UINT24, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_UINT24(pc, u); - } else { - off = js_EmitN(cx, cg, JSOP_INT32, 4); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_INT32(pc, ival); - } - return JS_TRUE; - } - - if (!cg->constList.append(DoubleValue(dval))) - return JS_FALSE; - - return EmitIndexOp(cx, JSOP_DOUBLE, cg->constList.length() - 1, cg); -} - -/* - * To avoid bloating all parse nodes for the special case of switch, values are - * allocated in the temp pool and pointed to by the parse node. These values - * are not currently recycled (like parse nodes) and the temp pool is only - * flushed at the end of compiling a script, so these values are technically - * leaked. This would only be a problem for scripts containing a large number - * of large switches, which seems unlikely. - */ -static Value * -AllocateSwitchConstant(JSContext *cx) -{ - Value *pv; - JS_ARENA_ALLOCATE_TYPE(pv, Value, &cx->tempPool); - return pv; -} - -static JSBool -EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSStmtInfo *stmtInfo) -{ - JSOp switchOp; - JSBool ok, hasDefault, constPropagated; - ptrdiff_t top, off, defaultOffset; - JSParseNode *pn2, *pn3, *pn4; - uint32 caseCount, tableLength; - JSParseNode **table; - int32_t i, low, high; - JSAtomListElement *ale; - intN noteIndex; - size_t switchSize, tableSize; - jsbytecode *pc, *savepc; -#if JS_HAS_BLOCK_SCOPE - JSObjectBox *box; -#endif - - /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ - switchOp = JSOP_TABLESWITCH; - ok = JS_TRUE; - hasDefault = constPropagated = JS_FALSE; - defaultOffset = -1; - - /* - * If the switch contains let variables scoped by its body, model the - * resulting block on the stack first, before emitting the discriminant's - * bytecode (in case the discriminant contains a stack-model dependency - * such as a let expression). - */ - pn2 = pn->pn_right; -#if JS_HAS_BLOCK_SCOPE - if (pn2->pn_type == TOK_LEXICALSCOPE) { - /* - * Push the body's block scope before discriminant code-gen for proper - * static block scope linkage in case the discriminant contains a let - * expression. The block's locals must lie under the discriminant on - * the stack so that case-dispatch bytecodes can find the discriminant - * on top of stack. - */ - box = pn2->pn_objbox; - js_PushBlockScope(cg, stmtInfo, box, -1); - stmtInfo->type = STMT_SWITCH; - - /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ - if (!EmitEnterBlock(cx, pn2, cg)) - return JS_FALSE; - - /* - * Pop the switch's statement info around discriminant code-gen. Note - * how this leaves cg->blockChain referencing the switch's - * block scope object, which is necessary for correct block parenting - * in the case where the discriminant contains a let expression. - */ - cg->topStmt = stmtInfo->down; - cg->topScopeStmt = stmtInfo->downScope; - } -#ifdef __GNUC__ - else { - box = NULL; - } -#endif -#endif - - /* - * Emit code for the discriminant first (or nearly first, in the case of a - * switch whose body is a block scope). - */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Switch bytecodes run from here till end of final case. */ - top = CG_OFFSET(cg); -#if !JS_HAS_BLOCK_SCOPE - js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); -#else - if (pn2->pn_type == TOK_LC) { - js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); - } else { - /* Re-push the switch's statement info record. */ - cg->topStmt = cg->topScopeStmt = stmtInfo; - cg->blockChainBox = stmtInfo->blockBox; - - /* Set the statement info record's idea of top. */ - stmtInfo->update = top; - - /* Advance pn2 to refer to the switch case list. */ - pn2 = pn2->expr(); - } -#endif - - caseCount = pn2->pn_count; - tableLength = 0; - table = NULL; - - if (caseCount == 0 || - (caseCount == 1 && - (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { - caseCount = 0; - low = 0; - high = -1; - } else { -#define INTMAP_LENGTH 256 - jsbitmap intmap_space[INTMAP_LENGTH]; - jsbitmap *intmap = NULL; - int32 intmap_bitlen = 0; - - low = JSVAL_INT_MAX; - high = JSVAL_INT_MIN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) { - hasDefault = JS_TRUE; - caseCount--; /* one of the "cases" was the default */ - continue; - } - - JS_ASSERT(pn3->pn_type == TOK_CASE); - if (switchOp == JSOP_CONDSWITCH) - continue; - - pn4 = pn3->pn_left; - while (pn4->pn_type == TOK_RP) - pn4 = pn4->pn_kid; - - Value constVal; - switch (pn4->pn_type) { - case TOK_NUMBER: - constVal.setNumber(pn4->pn_dval); - break; - case TOK_STRING: - constVal.setString(pn4->pn_atom); - break; - case TOK_NAME: - if (!pn4->maybeExpr()) { - ok = LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &constVal); - if (!ok) - goto release; - if (!constVal.isMagic(JS_NO_CONSTANT)) { - if (constVal.isObject()) { - /* - * XXX JSOP_LOOKUPSWITCH does not support const- - * propagated object values, see bug 407186. - */ - switchOp = JSOP_CONDSWITCH; - continue; - } - constPropagated = JS_TRUE; - break; - } - } - /* FALL THROUGH */ - case TOK_PRIMARY: - if (pn4->pn_op == JSOP_TRUE) { - constVal.setBoolean(true); - break; - } - if (pn4->pn_op == JSOP_FALSE) { - constVal.setBoolean(false); - break; - } - if (pn4->pn_op == JSOP_NULL) { - constVal.setNull(); - break; - } - /* FALL THROUGH */ - default: - switchOp = JSOP_CONDSWITCH; - continue; - } - JS_ASSERT(constVal.isPrimitive()); - - pn3->pn_pval = AllocateSwitchConstant(cx); - if (!pn3->pn_pval) { - ok = JS_FALSE; - goto release; - } - - *pn3->pn_pval = constVal; - - if (switchOp != JSOP_TABLESWITCH) - continue; - if (!pn3->pn_pval->isInt32()) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - i = pn3->pn_pval->toInt32(); - if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - if (i < low) - low = i; - if (high < i) - high = i; - - /* - * Check for duplicates, which require a JSOP_LOOKUPSWITCH. - * We bias i by 65536 if it's negative, and hope that's a rare - * case (because it requires a malloc'd bitmap). - */ - if (i < 0) - i += JS_BIT(16); - if (i >= intmap_bitlen) { - if (!intmap && - i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) { - intmap = intmap_space; - intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2; - } else { - /* Just grab 8K for the worst-case bitmap. */ - intmap_bitlen = JS_BIT(16); - intmap = (jsbitmap *) - cx->malloc_((JS_BIT(16) >> JS_BITS_PER_WORD_LOG2) - * sizeof(jsbitmap)); - if (!intmap) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2); - } - if (JS_TEST_BIT(intmap, i)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - JS_SET_BIT(intmap, i); - } - - release: - if (intmap && intmap != intmap_space) - cx->free_(intmap); - if (!ok) - return JS_FALSE; - - /* - * Compute table length and select lookup instead if overlarge or - * more than half-sparse. - */ - if (switchOp == JSOP_TABLESWITCH) { - tableLength = (uint32)(high - low + 1); - if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) - switchOp = JSOP_LOOKUPSWITCH; - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* - * Lookup switch supports only atom indexes below 64K limit. - * Conservatively estimate the maximum possible index during - * switch generation and use conditional switch if it exceeds - * the limit. - */ - if (caseCount + cg->constList.length() > JS_BIT(16)) - switchOp = JSOP_CONDSWITCH; - } - } - - /* - * Emit a note with two offsets: first tells total switch code length, - * second tells offset to first JSOP_CASE if condswitch. - */ - noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0); - if (noteIndex < 0) - return JS_FALSE; - - if (switchOp == JSOP_CONDSWITCH) { - /* - * 0 bytes of immediate for unoptimized ECMAv2 switch. - */ - switchSize = 0; - } else if (switchOp == JSOP_TABLESWITCH) { - /* - * 3 offsets (len, low, high) before the table, 1 per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength)); - } else { - /* - * JSOP_LOOKUPSWITCH: - * 1 offset (len) and 1 atom index (npairs) before the table, - * 1 atom index and 1 jump offset per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN + INDEX_LEN + - (INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); - } - - /* - * Emit switchOp followed by switchSize bytes of jump or lookup table. - * - * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial - * to emit the immediate operand(s) by which bytecode readers such as - * BuildSpanDepTable discover the length of the switch opcode *before* - * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's - * also important to zero all unknown jump offset immediate operands, - * so they can be converted to span dependencies with null targets to - * be computed later (js_EmitN zeros switchSize bytes after switchOp). - */ - if (js_EmitN(cx, cg, switchOp, switchSize) < 0) - return JS_FALSE; - - off = -1; - if (switchOp == JSOP_CONDSWITCH) { - intN caseNoteIndex = -1; - JSBool beforeCases = JS_TRUE; - - /* Emit code for evaluating cases and jumping to case statements. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && !js_EmitTree(cx, cg, pn4)) - return JS_FALSE; - if (caseNoteIndex >= 0) { - /* off is the previous JSOP_CASE's bytecode offset. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - } - if (!pn4) { - JS_ASSERT(pn3->pn_type == TOK_DEFAULT); - continue; - } - caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (caseNoteIndex < 0) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_CASE, 0); - if (off < 0) - return JS_FALSE; - pn3->pn_offset = off; - if (beforeCases) { - uintN noteCount, noteCountDelta; - - /* Switch note's second offset is to first JSOP_CASE. */ - noteCount = CG_NOTE_COUNT(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - off - top)) { - return JS_FALSE; - } - noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; - if (noteCountDelta != 0) - caseNoteIndex += noteCountDelta; - beforeCases = JS_FALSE; - } - } - - /* - * If we didn't have an explicit default (which could fall in between - * cases, preventing us from fusing this js_SetSrcNoteOffset with the - * call in the loop above), link the last case to the implicit default - * for the decompiler. - */ - if (!hasDefault && - caseNoteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - - /* Emit default even if no explicit default statement. */ - defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); - if (defaultOffset < 0) - return JS_FALSE; - } else { - pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); - - if (switchOp == JSOP_TABLESWITCH) { - /* Fill in switch bounds, which we know fit in 16-bit offsets. */ - SET_JUMP_OFFSET(pc, low); - pc += JUMP_OFFSET_LEN; - SET_JUMP_OFFSET(pc, high); - pc += JUMP_OFFSET_LEN; - - /* - * Use malloc to avoid arena bloat for programs with many switches. - * We free table if non-null at label out, so all control flow must - * exit this function through goto out or goto bad. - */ - if (tableLength != 0) { - tableSize = (size_t)tableLength * sizeof *table; - table = (JSParseNode **) cx->malloc_(tableSize); - if (!table) - return JS_FALSE; - memset(table, 0, tableSize); - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - i = pn3->pn_pval->toInt32(); - i -= low; - JS_ASSERT((uint32)i < tableLength); - table[i] = pn3; - } - } - } else { - JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); - - /* Fill in the number of cases. */ - SET_INDEX(pc, caseCount); - pc += INDEX_LEN; - } - - /* - * After this point, all control flow involving JSOP_TABLESWITCH - * must set ok and goto out to exit this function. To keep things - * simple, all switchOp cases exit that way. - */ - MUST_FLOW_THROUGH("out"); - if (cg->spanDeps) { - /* - * We have already generated at least one big jump so we must - * explicitly add span dependencies for the switch jumps. When - * called below, js_SetJumpOffset can only do it when patching - * the first big jump or when cg->spanDeps is null. - */ - if (!AddSwitchSpanDeps(cx, cg, CG_CODE(cg, top))) - goto bad; - } - - if (constPropagated) { - /* - * Skip switchOp, as we are not setting jump offsets in the two - * for loops below. We'll restore CG_NEXT(cg) from savepc after, - * unless there was an error. - */ - savepc = CG_NEXT(cg); - CG_NEXT(cg) = pc + 1; - if (switchOp == JSOP_TABLESWITCH) { - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - if (pn3 && - (pn4 = pn3->pn_left) != NULL && - pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->maybeExpr()); - ale = cg->atomList.add(cg->parser, pn4->pn_atom); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += JUMP_OFFSET_LEN; - } - } else { - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->maybeExpr()); - ale = cg->atomList.add(cg->parser, pn4->pn_atom); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += INDEX_LEN + JUMP_OFFSET_LEN; - } - } - CG_NEXT(cg) = savepc; - } - } - - /* Emit code for each case's statements, copying pn_offset up to pn3. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) - CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx, cg, pn3->pn_offset, goto bad); - pn4 = pn3->pn_right; - ok = js_EmitTree(cx, cg, pn4); - if (!ok) - goto out; - pn3->pn_offset = pn4->pn_offset; - if (pn3->pn_type == TOK_DEFAULT) - off = pn3->pn_offset - top; - } - - if (!hasDefault) { - /* If no default case, offset for default is to end of switch. */ - off = CG_OFFSET(cg) - top; - } - - /* We better have set "off" by now. */ - JS_ASSERT(off != -1); - - /* Set the default offset (to end of switch if no default). */ - if (switchOp == JSOP_CONDSWITCH) { - pc = NULL; - JS_ASSERT(defaultOffset != -1); - ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), - off - (defaultOffset - top)); - if (!ok) - goto out; - } else { - pc = CG_CODE(cg, top); - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - - /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ - off = CG_OFFSET(cg) - top; - ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); - if (!ok) - goto out; - - if (switchOp == JSOP_TABLESWITCH) { - /* Skip over the already-initialized switch bounds. */ - pc += 2 * JUMP_OFFSET_LEN; - - /* Fill in the jump table, if there is one. */ - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - off = pn3 ? pn3->pn_offset - top : 0; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* Skip over the already-initialized number of cases. */ - pc += INDEX_LEN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - if (!cg->constList.append(*pn3->pn_pval)) - goto bad; - SET_INDEX(pc, cg->constList.length() - 1); - pc += INDEX_LEN; - - off = pn3->pn_offset - top; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } - -out: - if (table) - cx->free_(table); - if (ok) { - ok = js_PopStatementCG(cx, cg); - -#if JS_HAS_BLOCK_SCOPE - if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) - ok = EmitLeaveBlock(cx, cg, JSOP_LEAVEBLOCK, box); -#endif - } - return ok; - -bad: - ok = JS_FALSE; - goto out; -} - -JSBool -js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) -{ - /* - * The decompiler has assumptions about what may occur immediately after - * script->main (e.g., in the case of destructuring params). Thus, put the - * following ops into the range [script->code, script->main). Note: - * execution starts from script->code, so this has no semantic effect. - */ - - if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ - CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); - if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) - return false; - CG_SWITCH_TO_MAIN(cg); - } - - /* - * Strict mode functions' arguments objects copy initial parameter values. - * We create arguments objects lazily -- but that doesn't work for strict - * mode functions where a parameter might be modified and arguments might - * be accessed. For such functions we synthesize an access to arguments to - * initialize it with the original parameter values. - */ - if (cg->needsEagerArguments()) { - CG_SWITCH_TO_PROLOG(cg); - if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return false; - CG_SWITCH_TO_MAIN(cg); - } - - if (cg->flags & TCF_FUN_UNBRAND_THIS) { - CG_SWITCH_TO_PROLOG(cg); - if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0) - return false; - CG_SWITCH_TO_MAIN(cg); - } - - return js_EmitTree(cx, cg, body) && - js_Emit1(cx, cg, JSOP_STOP) >= 0 && - JSScript::NewScriptFromCG(cx, cg); -} - -/* A macro for inlining at the top of js_EmitTree (whence it came). */ -#define UPDATE_LINE_NUMBER_NOTES(cx, cg, line) \ - JS_BEGIN_MACRO \ - uintN line_ = (line); \ - uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ - if (delta_ != 0) { \ - /* \ - * Encode any change in the current source line number by using \ - * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \ - * whichever consumes less space. \ - * \ - * NB: We handle backward line number deltas (possible with for \ - * loops where the update part is emitted after the body, but its \ - * line number is <= any line number in the body) here by letting \ - * unsigned delta_ wrap to a very large number, which triggers a \ - * SRC_SETLINE. \ - */ \ - CG_CURRENT_LINE(cg) = line_; \ - if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \ - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\ - return JS_FALSE; \ - } else { \ - do { \ - if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ - return JS_FALSE; \ - } while (--delta_ != 0); \ - } \ - } \ - JS_END_MACRO - -/* A function, so that we avoid macro-bloating all the other callsites. */ -static JSBool -UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, uintN line) -{ - UPDATE_LINE_NUMBER_NOTES(cx, cg, line); - return JS_TRUE; -} - -static JSBool -MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn, jsatomid *result) -{ - jsatomid atomIndex; - JSAtomListElement *ale; - - if (!pn->pn_cookie.isFree()) { - atomIndex = (jsatomid) pn->pn_cookie.slot(); - } else { - ale = cg->atomList.add(cg->parser, pn->pn_atom); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - } - - if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM && - (!cg->inFunction() || (cg->flags & TCF_FUN_HEAVYWEIGHT)) && - !(pn->pn_dflags & PND_GVAR)) - { - CG_SWITCH_TO_PROLOG(cg); - if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno)) - return JS_FALSE; - EMIT_INDEX_OP(prologOp, atomIndex); - CG_SWITCH_TO_MAIN(cg); - } - - if (cg->inFunction() && - JOF_OPTYPE(pn->pn_op) == JOF_LOCAL && - pn->pn_cookie.slot() < cg->bindings.countVars() && - cg->shouldNoteClosedName(pn)) - { - if (!cg->closedVars.append(pn->pn_cookie.slot())) - return JS_FALSE; - } - - if (result) - *result = atomIndex; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING - -typedef JSBool -(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn); - -static JSBool -EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JS_ASSERT(pn->pn_type == TOK_NAME); - if (!BindNameToSlot(cx, cg, pn)) - return JS_FALSE; - - JS_ASSERT(PN_OP(pn) != JSOP_ARGUMENTS && PN_OP(pn) != JSOP_CALLEE); - return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); -} - -static JSBool -EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - DestructuringDeclEmitter emitter; - - if (pn->pn_type == TOK_RB) { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_COMMA) - continue; - emitter = (pn2->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn2)) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pn3 = pn2->pn_right; - emitter = (pn3->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn3)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -static JSBool -EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - /* - * Now emit the lvalue opcode sequence. If the lvalue is a nested - * destructuring initialiser-form, call ourselves to handle it, then - * pop the matched value. Otherwise emit an lvalue bytecode sequence - * ending with a JSOP_ENUMELEM or equivalent op. - */ - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (!EmitDestructuringOpsHelper(cx, cg, pn)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (pn->pn_type == TOK_NAME) { - if (!BindNameToSlot(cx, cg, pn)) - return JS_FALSE; - if (pn->isConst() && !pn->isInitialized()) - return js_Emit1(cx, cg, JSOP_POP) >= 0; - } - - switch (pn->pn_op) { - case JSOP_SETNAME: - case JSOP_SETGNAME: - /* - * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, - * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. - * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. - */ - if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETCONST: - if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETLOCAL: - { - jsuint slot = pn->pn_cookie.asInteger(); - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); - break; - } - - case JSOP_SETARG: - { - jsuint slot = pn->pn_cookie.asInteger(); - EMIT_UINT16_IMM_OP(PN_OP(pn), slot); - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - } - - default: - { - ptrdiff_t top; - - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - break; - } - - case JSOP_ENUMELEM: - JS_ASSERT(0); - } - } - - return JS_TRUE; -} - -/* - * Recursive helper for EmitDestructuringOps. - * - * Given a value to destructure on the stack, walk over an object or array - * initialiser at pn, emitting bytecodes to match property values and store - * them in the lvalues identified by the matched property names. - */ -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - jsuint index; - JSParseNode *pn2, *pn3; - JSBool doElemOp; - -#ifdef DEBUG - intN stackDepth = cg->stackDepth; - JS_ASSERT(stackDepth != 0); - JS_ASSERT(pn->pn_arity == PN_LIST); - JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); -#endif - - if (pn->pn_count == 0) { - /* Emit a DUP;POP sequence for the decompiler. */ - return js_Emit1(cx, cg, JSOP_DUP) >= 0 && - js_Emit1(cx, cg, JSOP_POP) >= 0; - } - - index = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Duplicate the value being destructured to use as a reference base. - * If dup is not the first one, annotate it for the decompiler. - */ - if (pn2 != pn->pn_head && js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - - /* - * Now push the property name currently being matched, which is either - * the array initialiser's current index, or the current property name - * "label" on the left of a colon in the object initialiser. Set pn3 - * to the lvalue node, which is in the value-initializing position. - */ - doElemOp = JS_TRUE; - if (pn->pn_type == TOK_RB) { - if (!EmitNumberOp(cx, index, cg)) - return JS_FALSE; - pn3 = pn2; - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - JS_ASSERT(pn2->pn_type == TOK_COLON); - pn3 = pn2->pn_left; - if (pn3->pn_type == TOK_NUMBER) { - /* - * If we are emitting an object destructuring initialiser, - * annotate the index op with SRC_INITPROP so we know we are - * not decompiling an array initialiser. - */ - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - } else { - JS_ASSERT(pn3->pn_type == TOK_STRING || - pn3->pn_type == TOK_NAME); - if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) - return JS_FALSE; - doElemOp = JS_FALSE; - } - pn3 = pn2->pn_right; - } - - if (doElemOp) { - /* - * Ok, get the value of the matching property name. This leaves - * that value on top of the value being destructured, so the stack - * is one deeper than when we started. - */ - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == stackDepth + 1); - } - - /* Nullary comma node makes a hole in the array destructurer. */ - if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { - JS_ASSERT(pn->pn_type == TOK_RB); - JS_ASSERT(pn2 == pn3); - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn3)) - return JS_FALSE; - } - - JS_ASSERT(cg->stackDepth == stackDepth); - ++index; - } - - return JS_TRUE; -} - -static ptrdiff_t -OpToDeclType(JSOp op) -{ - switch (op) { - case JSOP_NOP: - return SRC_DECL_LET; - case JSOP_DEFCONST: - return SRC_DECL_CONST; - case JSOP_DEFVAR: - return SRC_DECL_VAR; - default: - return SRC_DECL_NONE; - } -} - -static JSBool -EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - /* - * If we're called from a variable declaration, help the decompiler by - * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. - * If the destructuring initialiser is empty, our helper will emit a - * JSOP_DUP followed by a JSOP_POP for the decompiler. - */ - if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(prologOp)) < 0) - return JS_FALSE; - - /* - * Call our recursive helper to emit the destructuring assignments and - * related stack manipulations. - */ - return EmitDestructuringOpsHelper(cx, cg, pn); -} - -static JSBool -EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *lhs, JSParseNode *rhs) -{ - jsuint depth, limit, i, nslots; - JSParseNode *pn; - - depth = limit = (uintN) cg->stackDepth; - for (pn = rhs->pn_head; pn; pn = pn->pn_next) { - if (limit == JS_BIT(16)) { - ReportCompileErrorNumber(cx, CG_TS(cg), rhs, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG); - return JS_FALSE; - } - - /* MaybeEmitGroupAssignment won't call us if rhs is holey. */ - JS_ASSERT(!(pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY)); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - ++limit; - } - - if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(prologOp)) < 0) - return JS_FALSE; - - i = depth; - for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) { - /* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */ - JS_ASSERT(i < limit); - jsint slot = AdjustBlockSlot(cx, cg, i); - if (slot < 0) - return JS_FALSE; - EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); - - if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn)) - return JS_FALSE; - } - } - - nslots = limit - depth; - EMIT_UINT16_IMM_OP(JSOP_POPN, nslots); - cg->stackDepth = (uintN) depth; - return JS_TRUE; -} - -/* - * Helper called with pop out param initialized to a JSOP_POP* opcode. If we - * can emit a group assignment sequence, which results in 0 stack depth delta, - * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. - */ -static JSBool -MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn, JSOp *pop) -{ - JSParseNode *lhs, *rhs; - - JS_ASSERT(pn->pn_type == TOK_ASSIGN); - JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); - lhs = pn->pn_left; - rhs = pn->pn_right; - if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && - !(rhs->pn_xflags & PNX_HOLEY) && - lhs->pn_count <= rhs->pn_count) { - if (!EmitGroupAssignment(cx, cg, prologOp, lhs, rhs)) - return JS_FALSE; - *pop = JSOP_NOP; - } - return JS_TRUE; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -static JSBool -EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool inLetHead, ptrdiff_t *headNoteIndex) -{ - bool let, forInVar, first; -#if JS_HAS_BLOCK_SCOPE - bool forInLet, popScope; - JSStmtInfo *stmt, *scopeStmt; -#endif - ptrdiff_t off, noteIndex, tmp; - JSParseNode *pn2, *pn3, *next; - JSOp op; - jsatomid atomIndex; - uintN oldflags; - - /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ - *headNoteIndex = -1; - - /* - * Let blocks and expressions have a parenthesized head in which the new - * scope is not yet open. Initializer evaluation uses the parent node's - * lexical scope. If popScope is true below, then we hide the top lexical - * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that - * it won't find any names in the new let block. - * - * The same goes for let declarations in the head of any kind of for loop. - * Unlike a let declaration 'let x = i' within a block, where x is hoisted - * to the start of the block, a 'for (let x = i...) ...' loop evaluates i - * in the containing scope, and puts x in the loop body's scope. - */ - let = (pn->pn_op == JSOP_NOP); - forInVar = (pn->pn_xflags & PNX_FORINVAR) != 0; -#if JS_HAS_BLOCK_SCOPE - forInLet = let && forInVar; - popScope = (inLetHead || (let && (cg->flags & TCF_IN_FOR_INIT))); - if (popScope) { - stmt = cg->topStmt; - scopeStmt = cg->topScopeStmt; - } -# ifdef __GNUC__ - else stmt = scopeStmt = NULL; /* quell GCC overwarning */ -# endif - JS_ASSERT(!popScope || let); -#endif - - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = next) { - first = pn2 == pn->pn_head; - next = pn2->pn_next; - - if (pn2->pn_type != TOK_NAME) { -#if JS_HAS_DESTRUCTURING - if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { - /* - * Emit variable binding ops, but not destructuring ops. - * The parser (see Variables, jsparse.c) has ensured that - * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, - * and that case will emit the destructuring code only after - * emitting an enumerating opcode and a branch that tests - * whether the enumeration ended. - */ - JS_ASSERT(forInVar); - JS_ASSERT(pn->pn_count == 1); - if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn2)) - return JS_FALSE; - break; - } -#endif - - /* - * A destructuring initialiser assignment preceded by var will - * never occur to the left of 'in' in a for-in loop. As with 'for - * (var x = i in o)...', this will cause the entire 'var [a, b] = - * i' to be hoisted out of the loop. - */ - JS_ASSERT(pn2->pn_type == TOK_ASSIGN); - JS_ASSERT(!forInVar); - - /* - * To allow the front end to rewrite var f = x; as f = x; when a - * function f(){} precedes the var, detect simple name assignment - * here and initialize the name. - */ -#if !JS_HAS_DESTRUCTURING - JS_ASSERT(pn2->pn_left->pn_type == TOK_NAME); -#else - if (pn2->pn_left->pn_type == TOK_NAME) -#endif - { - pn3 = pn2->pn_right; - pn2 = pn2->pn_left; - goto do_name; - } - -#if JS_HAS_DESTRUCTURING - if (pn->pn_count == 1) { - /* - * If this is the only destructuring assignment in the list, - * try to optimize to a group assignment. If we're in a let - * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP - * in pn->pn_op, to suppress a second (and misplaced) 'let'. - */ - JS_ASSERT(noteIndex < 0 && !pn2->pn_next); - op = JSOP_POP; - if (!MaybeEmitGroupAssignment(cx, cg, - inLetHead ? JSOP_POP : PN_OP(pn), - pn2, &op)) { - return JS_FALSE; - } - if (op == JSOP_NOP) { - pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT; - break; - } - } - - pn3 = pn2->pn_left; - if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn3)) - return JS_FALSE; - - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - - /* - * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT - * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that - * we will emit at the bottom of this function. - */ - if (!EmitDestructuringOps(cx, cg, - inLetHead ? JSOP_POP : PN_OP(pn), - pn3)) { - return JS_FALSE; - } - goto emit_note_pop; -#endif - } - - /* - * Load initializer early to share code above that jumps to do_name. - * NB: if this var redeclares an existing binding, then pn2 is linked - * on its definition's use-chain and pn_expr has been overlayed with - * pn_lexdef. - */ - pn3 = pn2->maybeExpr(); - - do_name: - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - - op = PN_OP(pn2); - if (op == JSOP_ARGUMENTS) { - /* JSOP_ARGUMENTS => no initializer */ - JS_ASSERT(!pn3 && !let); - pn3 = NULL; -#ifdef __GNUC__ - atomIndex = 0; /* quell GCC overwarning */ -#endif - } else { - JS_ASSERT(op != JSOP_CALLEE); - JS_ASSERT(!pn2->pn_cookie.isFree() || !let); - if (!MaybeEmitVarDecl(cx, cg, PN_OP(pn), pn2, &atomIndex)) - return JS_FALSE; - - if (pn3) { - JS_ASSERT(!forInVar); - if (op == JSOP_SETNAME) { - JS_ASSERT(!let); - EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); - } else if (op == JSOP_SETGNAME) { - JS_ASSERT(!let); - EMIT_INDEX_OP(JSOP_BINDGNAME, atomIndex); - } - if (pn->pn_op == JSOP_DEFCONST && - !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, pn3)) { - return JS_FALSE; - } - -#if JS_HAS_BLOCK_SCOPE - /* Evaluate expr in the outer lexical scope if requested. */ - if (popScope) { - cg->topStmt = stmt->down; - cg->topScopeStmt = scopeStmt->downScope; - } -#endif - - oldflags = cg->flags; - cg->flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->flags |= oldflags & TCF_IN_FOR_INIT; - -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - cg->topStmt = stmt; - cg->topScopeStmt = scopeStmt; - JS_ASSERT(cg->blockChainBox == scopeStmt->blockBox); - } -#endif - } - } - - /* - * The parser rewrites 'for (var x = i in o)' to hoist 'var x = i' -- - * likewise 'for (let x = i in o)' becomes 'i; for (let x in o)' using - * a TOK_SEQ node to make the two statements appear as one. Therefore - * if this declaration is part of a for-in loop head, we do not need to - * emit op or any source note. Our caller, the TOK_FOR/TOK_IN case in - * js_EmitTree, will annotate appropriately. - */ - JS_ASSERT_IF(pn2->pn_defn, pn3 == pn2->pn_expr); - if (forInVar) { - JS_ASSERT(pn->pn_count == 1); - JS_ASSERT(!pn3); - break; - } - - if (first && - !inLetHead && - js_NewSrcNote2(cx, cg, SRC_DECL, - (pn->pn_op == JSOP_DEFCONST) - ? SRC_DECL_CONST - : (pn->pn_op == JSOP_DEFVAR) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else if (!pn2->pn_cookie.isFree()) { - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - EMIT_INDEX_OP(op, atomIndex); - } - -#if JS_HAS_DESTRUCTURING - emit_note_pop: -#endif - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - - /* If this is a let head, emit and return a srcnote on the pop. */ - if (inLetHead) { - *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); - if (*headNoteIndex < 0) - return JS_FALSE; - if (!(pn->pn_xflags & PNX_POPVAR)) - return js_Emit1(cx, cg, JSOP_NOP) >= 0; - } - - return !(pn->pn_xflags & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; -} - -#if defined DEBUG_brendan || defined DEBUG_mrbkap -static JSBool -GettableNoteForNextOp(JSCodeGenerator *cg) -{ - ptrdiff_t offset, target; - jssrcnote *sn, *end; - - offset = 0; - target = CG_OFFSET(cg); - for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; - sn = SN_NEXT(sn)) { - if (offset == target && SN_IS_GETTABLE(sn)) - return JS_TRUE; - offset += SN_DELTA(sn); - } - return JS_FALSE; -} -#endif - -/* Top-level named functions need a nop for decompilation. */ -static JSBool -EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index) -{ - return js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 && - js_Emit1(cx, cg, JSOP_NOP) >= 0; -} - -static bool -EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum) -{ - if (js_Emit3(cx, cg, JSOP_NEWINIT, (jsbytecode) key, 0) < 0) - return false; -#if JS_HAS_SHARP_VARS - if (cg->hasSharps()) { - if (pn->pn_count != 0) - EMIT_UINT16_IMM_OP(JSOP_SHARPINIT, cg->sharpSlotBase); - if (sharpnum >= 0) - EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, cg->sharpSlotBase, sharpnum); - } else { - JS_ASSERT(sharpnum < 0); - } -#endif - return true; -} - -static bool -EmitEndInit(JSContext *cx, JSCodeGenerator *cg, uint32 count) -{ -#if JS_HAS_SHARP_VARS - /* Emit an op for sharp array cleanup and decompilation. */ - if (cg->hasSharps() && count != 0) - EMIT_UINT16_IMM_OP(JSOP_SHARPINIT, cg->sharpSlotBase); -#endif - return js_Emit1(cx, cg, JSOP_ENDINIT) >= 0; -} - -bool -JSParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp) -{ - switch (pn_type) { - case TOK_NUMBER: - vp->setNumber(pn_dval); - return true; - case TOK_STRING: - vp->setString(pn_atom); - return true; - case TOK_PRIMARY: - switch (pn_op) { - case JSOP_NULL: - vp->setNull(); - return true; - case JSOP_FALSE: - vp->setBoolean(false); - return true; - case JSOP_TRUE: - vp->setBoolean(true); - return true; - default: - JS_NOT_REACHED("Unexpected node"); - return false; - } - case TOK_RB: { - JS_ASSERT((pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); - - JSObject *obj = NewDenseAllocatedArray(cx, pn_count); - if (!obj || !obj->ensureSlots(cx, pn_count)) - return false; - - unsigned idx = 0; - for (JSParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) { - Value value; - if (!pn->getConstantValue(cx, strictChecks, &value)) - return false; - obj->setDenseArrayElement(idx, value); - } - JS_ASSERT(idx == pn_count); - - vp->setObject(*obj); - return true; - } - case TOK_RC: { - JS_ASSERT((pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); - - gc::FinalizeKind kind = GuessObjectGCKind(pn_count, false); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); - if (!obj) - return false; - - for (JSParseNode *pn = pn_head; pn; pn = pn->pn_next) { - Value value; - if (!pn->pn_right->getConstantValue(cx, strictChecks, &value)) - return false; - - JSParseNode *pnid = pn->pn_left; - if (pnid->pn_type == TOK_NUMBER) { - Value idvalue = NumberValue(pnid->pn_dval); - jsid id; - if (idvalue.isInt32() && INT_FITS_IN_JSID(idvalue.toInt32())) - id = INT_TO_JSID(idvalue.toInt32()); - else if (!js_InternNonIntElementId(cx, obj, idvalue, &id)) - return false; - if (!obj->defineProperty(cx, id, value, NULL, NULL, JSPROP_ENUMERATE)) - return false; - } else { - JS_ASSERT(pnid->pn_type == TOK_NAME || - pnid->pn_type == TOK_STRING); - jsid id = ATOM_TO_JSID(pnid->pn_atom); - if (!((pnid->pn_atom == cx->runtime->atomState.protoAtom) - ? js_SetPropertyHelper(cx, obj, id, 0, &value, strictChecks) - : js_DefineNativeProperty(cx, obj, id, value, NULL, NULL, - JSPROP_ENUMERATE, 0, 0, NULL, 0))) { - return false; - } - } - } - - vp->setObject(*obj); - return true; - } - default: - JS_NOT_REACHED("Unexpected node"); - } - return false; -} - -static bool -EmitSingletonInitialiser(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - Value value; - if (!pn->getConstantValue(cx, cg->needStrictChecks(), &value)) - return false; - - JS_ASSERT(value.isObject()); - JSObjectBox *objbox = cg->parser->newObjectBox(&value.toObject()); - if (!objbox) - return false; - - return EmitObjectOp(cx, objbox, JSOP_OBJECT, cg); -} - -/* See the SRC_FOR source note offsetBias comments later in this file. */ -JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1); -JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1); - -class EmitLevelManager -{ -private: - JSCodeGenerator *cg; - -public: - EmitLevelManager(JSCodeGenerator *cg) : cg(cg) { cg->emitLevel++; } - - ~EmitLevelManager() { cg->emitLevel--; } -}; - -JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - JSBool ok, useful, wantval; - JSStmtInfo *stmt, stmtInfo; - ptrdiff_t top, off, tmp, beq, jmp, tmp2, tmp3; - JSParseNode *pn2, *pn3; - JSAtom *atom; - JSAtomListElement *ale; - jsatomid atomIndex; - uintN index; - ptrdiff_t noteIndex, noteIndex2; - JSSrcNoteType noteType; - jsbytecode *pc; - JSOp op; - TokenKind type; - uint32 argc; - EmitLevelManager elm(cg); -#if JS_HAS_SHARP_VARS - jsint sharpnum; -#endif - - JS_CHECK_RECURSION(cx, return JS_FALSE); - - ok = JS_TRUE; - pn->pn_offset = top = CG_OFFSET(cg); - - /* Emit notes to tell the current bytecode's source line number. */ - UPDATE_LINE_NUMBER_NOTES(cx, cg, pn->pn_pos.begin.lineno); - - switch (pn->pn_type) { - case TOK_FUNCTION: - { - JSFunction *fun; - uintN slot; - -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) - return JS_FALSE; - break; - } -#endif - - fun = pn->pn_funbox->function(); - JS_ASSERT(FUN_INTERPRETED(fun)); - if (fun->u.i.script) { - /* - * This second pass is needed to emit JSOP_NOP with a source note - * for the already-emitted function definition prolog opcode. See - * comments in the TOK_LC case. - */ - JS_ASSERT(pn->pn_op == JSOP_NOP); - JS_ASSERT(cg->inFunction()); - if (!EmitFunctionDefNop(cx, cg, pn->pn_index)) - return JS_FALSE; - break; - } - - JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT, - FUN_KIND(fun) == JSFUN_INTERPRETED); - - /* Generate code for the function's body. */ - void *cg2mark = JS_ARENA_MARK(cg->codePool); - void *cg2space; - JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool); - if (!cg2space) { - js_ReportOutOfScriptQuota(cx); - return JS_FALSE; - } - JSCodeGenerator *cg2 = - new (cg2space) JSCodeGenerator(cg->parser, - cg->codePool, cg->notePool, - pn->pn_pos.begin.lineno); - - if (!cg2->init()) - return JS_FALSE; - - cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION | - (cg->flags & TCF_FUN_MIGHT_ALIAS_LOCALS); - cg2->bindings.transfer(cx, &pn->pn_funbox->bindings); -#if JS_HAS_SHARP_VARS - if (cg2->flags & TCF_HAS_SHARPS) { - cg2->sharpSlotBase = cg2->bindings.sharpSlotBase(cx); - if (cg2->sharpSlotBase < 0) - return JS_FALSE; - } -#endif - cg2->setFunction(fun); - cg2->funbox = pn->pn_funbox; - cg2->parent = cg; - - /* - * jsparse.cpp:SetStaticLevel limited static nesting depth to fit in 16 - * bits and to reserve the all-ones value, thereby reserving the magic - * FREE_UPVAR_COOKIE value. Note the cg2->staticLevel assignment below. - */ - JS_ASSERT(cg->staticLevel < JS_BITMASK(16) - 1); - cg2->staticLevel = cg->staticLevel + 1; - - /* We measured the max scope depth when we parsed the function. */ - JS_SCOPE_DEPTH_METERING(cg2->maxScopeDepth = uint16(-1)); - if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) - pn = NULL; - - cg2->~JSCodeGenerator(); - JS_ARENA_RELEASE(cg->codePool, cg2mark); - cg2 = NULL; - if (!pn) - return JS_FALSE; - - /* Make the function object a literal in the outer script's pool. */ - index = cg->objectList.index(pn->pn_funbox); - - /* Emit a bytecode pointing to the closure object in its immediate. */ - op = PN_OP(pn); - if (op != JSOP_NOP) { - if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) && - js_NewSrcNote(cx, cg, SRC_GENEXP) < 0) { - return JS_FALSE; - } - EMIT_INDEX_OP(op, index); - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - break; - } - - /* - * For a script we emit the code as we parse. Thus the bytecode for - * top-level functions should go in the prolog to predefine their - * names in the variable object before the already-generated main code - * is executed. This extra work for top-level scripts is not necessary - * when we emit the code for a function. It is fully parsed prior to - * invocation of the emitter and calls to js_EmitTree for function - * definitions can be scheduled before generating the rest of code. - */ - if (!cg->inFunction()) { - JS_ASSERT(!cg->topStmt); - if (!BindGlobal(cx, cg, pn, fun->atom)) - return false; - if (pn->pn_cookie.isFree()) { - CG_SWITCH_TO_PROLOG(cg); - op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN; - EMIT_INDEX_OP(op, index); - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - CG_SWITCH_TO_MAIN(cg); - } - - /* Emit NOP for the decompiler. */ - if (!EmitFunctionDefNop(cx, cg, index)) - return JS_FALSE; - } else { - DebugOnly kind = cg->bindings.lookup(cx, fun->atom, &slot); - JS_ASSERT(kind == VARIABLE || kind == CONSTANT); - JS_ASSERT(index < JS_BIT(20)); - pn->pn_index = index; - op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN; - if (pn->isClosed() && - !cg->callsEval() && - !cg->closedVars.append(pn->pn_cookie.slot())) { - return JS_FALSE; - } - if (!EmitSlotIndexOp(cx, op, slot, index, cg)) - return JS_FALSE; - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - } - break; - } - - case TOK_ARGSBODY: - { - JSParseNode *pnlast = pn->last(); - for (JSParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) { - if (!pn2->pn_defn) - continue; - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (JOF_OPTYPE(pn2->pn_op) == JOF_QARG && cg->shouldNoteClosedName(pn2)) { - if (!cg->closedArgs.append(pn2->pn_cookie.slot())) - return JS_FALSE; - } - } - ok = js_EmitTree(cx, cg, pnlast); - break; - } - - case TOK_UPVARS: - JS_ASSERT(cg->lexdeps.count == 0); - JS_ASSERT(pn->pn_names.count != 0); - cg->lexdeps = pn->pn_names; - ok = js_EmitTree(cx, cg, pn->pn_tree); - break; - - case TOK_IF: - /* Initialize so we can detect else-if chains and avoid recursion. */ - stmtInfo.type = STMT_IF; - beq = jmp = -1; - noteIndex = -1; - - if_again: - /* Emit code for the condition before pushing stmtInfo. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - top = CG_OFFSET(cg); - if (stmtInfo.type == STMT_IF) { - js_PushStatement(cg, &stmtInfo, STMT_IF, top); - } else { - /* - * We came here from the goto further below that detects else-if - * chains, so we must mutate stmtInfo back into a STMT_IF record. - * Also (see below for why) we need a note offset for SRC_IF_ELSE - * to help the decompiler. Actually, we need two offsets, one for - * decompiling any else clause and the second for decompiling an - * else-if chain without bracing, overindenting, or incorrectly - * scoping let declarations. - */ - JS_ASSERT(stmtInfo.type == STMT_ELSE); - stmtInfo.type = STMT_IF; - stmtInfo.update = top; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - beq)) - return JS_FALSE; - } - - /* Emit an annotated branch-if-false around the then part. */ - pn3 = pn->pn_kid3; - noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - - /* Emit code for the then and optional else parts. */ - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (pn3) { - /* Modify stmtInfo so we know we're in the else part. */ - stmtInfo.type = STMT_ELSE; - - /* - * Emit a JSOP_BACKPATCH op to jump from the end of our then part - * around the else part. The js_PopStatementCG call at the bottom - * of this switch case will fix up the backpatch chain linked from - * stmtInfo.breaks. - */ - jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL); - if (jmp < 0) - return JS_FALSE; - - /* Ensure the branch-if-false comes here, then emit the else. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (pn3->pn_type == TOK_IF) { - pn = pn3; - goto if_again; - } - - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* - * Annotate SRC_IF_ELSE with the offset from branch to jump, for - * the decompiler's benefit. We can't just "back up" from the pc - * of the else clause, because we don't know whether an extended - * jump was required to leap from the end of the then clause over - * the else clause. - */ - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - } else { - /* No else part, fixup the branch-if-false to come here. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SWITCH: - /* Out of line to avoid bloating js_EmitTree's stack frame size. */ - ok = EmitSwitch(cx, cg, pn, &stmtInfo); - break; - - case TOK_WHILE: - /* - * Minimize bytecodes issued for one or more iterations by jumping to - * the condition below the body and closing the loop if the condition - * is true with a backward branch. For iteration count i: - * - * i test at the top test at the bottom - * = =============== ================== - * 0 ifeq-pass goto; ifne-fail - * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail - * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail - * . . . - * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail - * - * SpiderMonkey, pre-mozilla.org, emitted while parsing and so used - * test at the top. When JSParseNode trees were added during the ES3 - * work (1998-9), the code generation scheme was not optimized, and - * the decompiler continued to take advantage of the branch and jump - * that bracketed the body. But given the SRC_WHILE note, it is easy - * to support the more efficient scheme. - */ - js_PushStatement(cg, &stmtInfo, STMT_WHILE_LOOP, top); - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - noteIndex2 = js_NewSrcNote(cx, cg, SRC_TRACE); - if (noteIndex2 < 0) - return JS_FALSE; - top = EmitTraceOp(cx, cg); - if (top < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); - if (beq < 0) - return JS_FALSE; - /* - * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex - * note gets bigger. - */ - if (!js_SetSrcNoteOffset(cx, cg, noteIndex2, 0, beq - top)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, beq - jmp)) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_DO: - /* Emit an annotated nop so we know to decompile a 'do' keyword. */ - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; - - noteIndex2 = js_NewSrcNote(cx, cg, SRC_TRACE); - if (noteIndex2 < 0) - return JS_FALSE; - - /* Compile the loop body. */ - top = EmitTraceOp(cx, cg); - if (top < 0) - return JS_FALSE; - js_PushStatement(cg, &stmtInfo, STMT_DO_LOOP, top); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Set loop and enclosing label update offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* Compile the loop condition, now that continues know where to go. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* - * Since we use JSOP_IFNE for other purposes as well as for do-while - * loops, we must store 1 + (beq - top) in the SRC_WHILE note offset, - * and the decompiler must get that delta and decompile recursively. - */ - beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); - if (beq < 0) - return JS_FALSE; - /* - * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex - * note gets bigger. - */ - if (!js_SetSrcNoteOffset(cx, cg, noteIndex2, 0, beq - top)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, 1 + (beq - top))) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_FOR: - beq = 0; /* suppress gcc warnings */ - jmp = -1; - pn2 = pn->pn_left; - js_PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top); - - if (pn2->pn_type == TOK_IN) { - /* Set stmtInfo type for later testing. */ - stmtInfo.type = STMT_FOR_IN_LOOP; - - /* - * If the left part is 'var x', emit code to define x if necessary - * using a prolog opcode, but do not emit a pop. If the left part - * is 'var x = i', emit prolog code to define x if necessary; then - * emit code to evaluate i, assign the result to x, and pop the - * result off the stack. - * - * All the logic to do this is implemented in the outer switch's - * TOK_VAR case, conditioned on pn_xflags flags set by the parser. - * - * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) - * called here will generate the proper note for the assignment - * op that sets x = i, hoisting the initialized var declaration - * out of the loop: 'var x = i; for (x in o) ...'. - * - * In the 'for (var x in o) ...' case, nothing but the prolog op - * (if needed) should be generated here, we must emit the note - * just before the JSOP_FOR* opcode in the switch on pn3->pn_type - * a bit below, so nothing is hoisted: 'for (var x in o) ...'. - * - * A 'for (let x = i in o)' loop must not be hoisted, since in - * this form the let variable is scoped by the loop body (but not - * the head). The initializer expression i must be evaluated for - * any side effects. So we hoist only i in the let case. - */ - pn3 = pn2->pn_left; - type = PN_TYPE(pn3); - cg->flags |= TCF_IN_FOR_INIT; - if (TokenKindIsDecl(type) && !js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->flags &= ~TCF_IN_FOR_INIT; - - /* Compile the object expression to the right of 'in'. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - - /* - * Emit a bytecode to convert top of stack value to the iterator - * object depending on the loop variant (for-in, for-each-in, or - * destructuring for-in). - */ - JS_ASSERT(pn->pn_op == JSOP_ITER); - if (js_Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0) - return JS_FALSE; - - /* Annotate so the decompiler can find the loop-closing jump. */ - noteIndex = js_NewSrcNote(cx, cg, SRC_FOR_IN); - if (noteIndex < 0) - return JS_FALSE; - - /* - * Jump down to the loop condition to minimize overhead assuming at - * least one iteration, as the other loop forms do. - */ - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - - noteIndex2 = js_NewSrcNote(cx, cg, SRC_TRACE); - if (noteIndex2 < 0) - return JS_FALSE; - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - if (EmitTraceOp(cx, cg) < 0) - return JS_FALSE; - -#ifdef DEBUG - intN loopDepth = cg->stackDepth; -#endif - - /* - * Compile a JSOP_FOR* bytecode based on the left hand side. - * - * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| - * or similar, to signify assignment, rather than declaration, to - * the decompiler. EmitDestructuringOps takes a prolog bytecode - * parameter and emits the appropriate source note, defaulting to - * assignment, so JSOP_SETNAME is not critical here; many similar - * ops could be used -- just not JSOP_NOP (which means 'let'). - */ - op = JSOP_SETNAME; - switch (type) { -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: -#endif - case TOK_VAR: - JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); - pn3 = pn3->pn_head; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN) { - pn3 = pn3->pn_left; - JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); - } - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - op = PN_OP(pn2->pn_left); - goto destructuring_for; - } -#else - JS_ASSERT(pn3->pn_type == TOK_NAME); -#endif - /* FALL THROUGH */ - - case TOK_NAME: - { - /* - * Always annotate JSOP_FORLOCAL if given input of the form - * 'for (let x in * o)' -- the decompiler must not hoist the - * 'let x' out of the loop head, or x will be bound in the - * wrong scope. Likewise, but in this case only for the sake - * of higher decompilation fidelity only, do not hoist 'var x' - * when given 'for (var x in o)'. - */ - if (( -#if JS_HAS_BLOCK_SCOPE - type == TOK_LET || -#endif - (type == TOK_VAR && !pn3->maybeExpr())) && - js_NewSrcNote2(cx, cg, SRC_DECL, - (type == TOK_VAR) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - UpvarCookie cookie = pn3->pn_cookie; - if (!cookie.isFree()) { - op = PN_OP(pn3); - switch (op) { - case JSOP_GETARG: - case JSOP_SETARG: - op = JSOP_FORARG; - break; - case JSOP_GETLOCAL: - case JSOP_SETLOCAL: - op = JSOP_FORLOCAL; - break; - case JSOP_GETGLOBAL: - op = JSOP_FORGNAME; - cookie.makeFree(); - break; - default: - JS_NOT_REACHED("unexpected opcode"); - } - } else { - pn3->pn_op = JSOP_FORNAME; - if (!BindNameToSlot(cx, cg, pn3)) - return JS_FALSE; - op = PN_OP(pn3); - cookie = pn3->pn_cookie; - } - if (pn3->isConst()) { - ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return JS_FALSE; - } - if (!cookie.isFree()) { - atomIndex = (jsatomid) cookie.asInteger(); - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - if (!EmitAtomOp(cx, pn3, op, cg)) - return JS_FALSE; - } - break; - } - - case TOK_DOT: - /* - * 'for (o.p in q)' can use JSOP_FORPROP only if evaluating 'o' - * has no side effects. - */ - useful = JS_FALSE; - if (!CheckSideEffects(cx, cg, pn3->expr(), &useful)) - return JS_FALSE; - if (!useful) { - if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) - return JS_FALSE; - break; - } - /* FALL THROUGH */ - -#if JS_HAS_DESTRUCTURING - destructuring_for: -#endif - default: - if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth >= 2); - -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - if (!EmitDestructuringOps(cx, cg, op, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else -#endif - if (pn3->pn_type == TOK_LP) { - JS_ASSERT(pn3->pn_xflags & PNX_SETCALL); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - } else -#if JS_HAS_XML_SUPPORT - if (pn3->pn_type == TOK_UNARYOP) { - JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - } else -#endif - if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - } - - /* The stack should be balanced around the JSOP_FOR* opcode sequence. */ - JS_ASSERT(cg->stackDepth == loopDepth); - - tmp2 = CG_OFFSET(cg); - - /* Emit code for the loop body. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* - * Fixup the goto that starts the loop to jump down to JSOP_MOREITER. - */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (js_Emit1(cx, cg, JSOP_MOREITER) < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); - if (beq < 0) - return JS_FALSE; - - /* - * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex - * note gets bigger. - */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, beq - top)) - return JS_FALSE; - /* Set the first srcnote offset so we can find the start of the loop body. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp2 - jmp)) - return JS_FALSE; - /* Set the second srcnote offset so we can find the closing jump. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp)) - return JS_FALSE; - } else { - /* C-style for (init; cond; update) ... loop. */ - op = JSOP_POP; - pn3 = pn2->pn_kid1; - if (!pn3) { - /* No initializer: emit an annotated nop for the decompiler. */ - op = JSOP_NOP; - } else { - cg->flags |= TCF_IN_FOR_INIT; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (TokenKindIsDecl(PN_TYPE(pn3))) { - /* - * Check whether a destructuring-initialized var decl - * was optimized to a group assignment. If so, we do - * not need to emit a pop below, so switch to a nop, - * just for the decompiler. - */ - JS_ASSERT(pn3->pn_arity == PN_LIST); - if (pn3->pn_xflags & PNX_GROUPINIT) - op = JSOP_NOP; - } - } - cg->flags &= ~TCF_IN_FOR_INIT; - } - - /* - * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH). - * Use tmp to hold the biased srcnote "top" offset, which differs - * from the top local variable by the length of the JSOP_GOTO{,X} - * emitted in between tmp and top if this loop has a condition. - */ - noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); - if (noteIndex < 0 || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - tmp = CG_OFFSET(cg); - - if (pn2->pn_kid2) { - /* Goto the loop condition, which branches back to iterate. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - } - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - - noteIndex2 = js_NewSrcNote(cx, cg, SRC_TRACE); - if (noteIndex2 < 0) - return JS_FALSE; - - /* Emit code for the loop body. */ - if (EmitTraceOp(cx, cg) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* Set the second note offset so we can find the update part. */ - JS_ASSERT(noteIndex != -1); - tmp2 = CG_OFFSET(cg); - - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* Check for update code to do before the condition (if any). */ - pn3 = pn2->pn_kid3; - if (pn3) { - op = JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP && !js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* Always emit the POP or NOP, to help the decompiler. */ - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - - /* Restore the absolute line number for source note readers. */ - off = (ptrdiff_t) pn->pn_pos.end.lineno; - if (CG_CURRENT_LINE(cg) != (uintN) off) { - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) - return JS_FALSE; - CG_CURRENT_LINE(cg) = (uintN) off; - } - } - - tmp3 = CG_OFFSET(cg); - - if (pn2->pn_kid2) { - /* Fix up the goto from top to target the loop condition. */ - JS_ASSERT(jmp >= 0); - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - - if (!js_EmitTree(cx, cg, pn2->pn_kid2)) - return JS_FALSE; - } - - /* - * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex - * note gets bigger. - */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - /* Set the first note offset so we can find the loop condition. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - tmp3 - tmp)) { - return JS_FALSE; - } - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - tmp2 - tmp)) { - return JS_FALSE; - } - /* The third note offset helps us find the loop-closing jump. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - - if (pn2->pn_kid2) { - beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); - if (beq < 0) - return JS_FALSE; - } else { - /* No loop condition -- emit the loop-closing jump. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); - if (jmp < 0) - return JS_FALSE; - } - } - - /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (pn2->pn_type == TOK_IN) { - if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) || - js_Emit1(cx, cg, JSOP_ENDITER) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_BREAK: - stmt = cg->topStmt; - atom = pn->pn_atom; - if (atom) { - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->label != atom) - stmt = stmt->down; - noteType = SRC_BREAK2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) - stmt = stmt->down; - noteType = (stmt->type == STMT_SWITCH) ? SRC_NULL : SRC_BREAK; - } - - if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_CONTINUE: - stmt = cg->topStmt; - atom = pn->pn_atom; - if (atom) { - /* Find the loop statement enclosed by the matching label. */ - JSStmtInfo *loop = NULL; - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->label != atom) { - if (STMT_IS_LOOP(stmt)) - loop = stmt; - stmt = stmt->down; - } - stmt = loop; - noteType = SRC_CONT2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt)) - stmt = stmt->down; - noteType = SRC_CONTINUE; - } - - if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_WITH: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - js_PushStatement(cg, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); - if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) - return JS_FALSE; - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_TRY: - { - ptrdiff_t tryStart, tryEnd, catchJump, finallyStart; - intN depth; - JSParseNode *lastCatch; - - catchJump = -1; - - /* - * Push stmtInfo to track jumps-over-catches and gosubs-to-finally - * for later fixup. - * - * When a finally block is active (STMT_FINALLY in our tree context), - * non-local jumps (including jumps-over-catches) result in a GOSUB - * being written into the bytecode stream and fixed-up later (c.f. - * EmitBackPatchOp and BackPatch). - */ - js_PushStatement(cg, &stmtInfo, - pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, - CG_OFFSET(cg)); - - /* - * Since an exception can be thrown at any place inside the try block, - * we need to restore the stack and the scope chain before we transfer - * the control to the exception handler. - * - * For that we store in a try note associated with the catch or - * finally block the stack depth upon the try entry. The interpreter - * uses this depth to properly unwind the stack and the scope chain. - */ - depth = cg->stackDepth; - - /* Mark try location for decompilation, then emit try block. */ - if (js_Emit1(cx, cg, JSOP_TRY) < 0) - return JS_FALSE; - tryStart = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - JS_ASSERT(depth == cg->stackDepth); - - /* GOSUB to finally, if present. */ - if (pn->pn_kid3) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - } - - /* Emit (hidden) jump over catch and/or finally. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - tryEnd = CG_OFFSET(cg); - - JSObjectBox *prevBox = NULL; - /* If this try has a catch block, emit it. */ - pn2 = pn->pn_kid2; - lastCatch = NULL; - if (pn2) { - uintN count = 0; /* previous catch block's population */ - - /* - * The emitted code for a catch block looks like: - * - * blockchain - * [throwing] only if 2nd+ catch block - * [leaveblock] only if 2nd+ catch block - * enterblock with SRC_CATCH - * exception - * [dup] only if catchguard - * setlocalpop or destructuring code - * [< catchguard code >] if there's a catchguard - * [ifeq ] " " - * [pop] only if catchguard - * < catch block contents > - * leaveblock - * goto non-local; finally applies - * - * If there's no catch block without a catchguard, the last - * points to rethrow code. This - * code will [gosub] to the finally code if appropriate, and is - * also used for the catch-all trynote for capturing exceptions - * thrown from catch{} blocks. - */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - ptrdiff_t guardJump, catchNote; - - JS_ASSERT(cg->stackDepth == depth); - guardJump = GUARDJUMP(stmtInfo); - if (guardJump != -1) { - if (EmitKnownBlockChain(cx, cg, prevBox) < 0) - return JS_FALSE; - - /* Fix up and clean up previous catch block. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); - - /* - * Account for JSOP_ENTERBLOCK (whose block object count - * is saved below) and pushed exception object that we - * still have after the jumping from the previous guard. - */ - cg->stackDepth = depth + count + 1; - - /* - * Move exception back to cx->exception to prepare for - * the next catch. We hide [throwing] from the decompiler - * since it compensates for the hidden JSOP_DUP at the - * start of the previous guarded catch. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROWING) < 0) { - return JS_FALSE; - } - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (!EmitLeaveBlock(cx, cg, JSOP_LEAVEBLOCK, prevBox)) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == depth); - } - - /* - * Annotate the JSOP_ENTERBLOCK that's about to be generated - * by the call to js_EmitTree immediately below. Save this - * source note's index in stmtInfo for use by the TOK_CATCH: - * case, where the length of the catch guard is set as the - * note's offset. - */ - catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); - if (catchNote < 0) - return JS_FALSE; - CATCHNOTE(stmtInfo) = catchNote; - - /* - * Emit the lexical scope and catch body. Save the catch's - * block object population via count, for use when targeting - * guardJump at the next catch (the guard mismatch case). - */ - JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); - count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object); - prevBox = pn3->pn_objbox; - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* gosub , if required */ - if (pn->pn_kid3) { - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == depth); - } - - /* - * Jump over the remaining catch blocks. This will get fixed - * up to jump to after catch/finally. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - /* - * Save a pointer to the last catch node to handle try-finally - * and try-catch(guard)-finally special cases. - */ - lastCatch = pn3->expr(); - } - } - - /* - * Last catch guard jumps to the rethrow code sequence if none of the - * guards match. Target guardJump at the beginning of the rethrow - * sequence, just in case a guard expression throws and leaves the - * stack unbalanced. - */ - if (lastCatch && lastCatch->pn_kid2) { - if (EmitKnownBlockChain(cx, cg, prevBox) < 0) - return JS_FALSE; - - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); - - /* Sync the stack to take into account pushed exception. */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Rethrow the exception, delegating executing of finally if any - * to the exception handler. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROW) < 0) { - return JS_FALSE; - } - - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - } - - JS_ASSERT(cg->stackDepth == depth); - - /* Emit finally handler if any. */ - finallyStart = 0; /* to quell GCC uninitialized warnings */ - if (pn->pn_kid3) { - /* - * Fix up the gosubs that might have been emitted before non-local - * jumps to the finally code. - */ - if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) - return JS_FALSE; - - finallyStart = CG_OFFSET(cg); - - /* Indicate that we're emitting a subroutine body. */ - stmtInfo.type = STMT_SUBROUTINE; - if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3->pn_pos.begin.lineno)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || - !js_EmitTree(cx, cg, pn->pn_kid3) || - js_Emit1(cx, cg, JSOP_RETSUB) < 0) { - return JS_FALSE; - } - JS_ASSERT(cg->stackDepth == depth); - } - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Fix up the end-of-try/catch jumps to come here. */ - if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) - return JS_FALSE; - - /* - * Add the try note last, to let post-order give us the right ordering - * (first to last for a given nesting level, inner to outer by level). - */ - if (pn->pn_kid2 && - !NewTryNote(cx, cg, JSTRY_CATCH, depth, tryStart, tryEnd)) { - return JS_FALSE; - } - - /* - * If we've got a finally, mark try+catch region with additional - * trynote to catch exceptions (re)thrown from a catch block or - * for the try{}finally{} case. - */ - if (pn->pn_kid3 && - !NewTryNote(cx, cg, JSTRY_FINALLY, depth, tryStart, finallyStart)) { - return JS_FALSE; - } - break; - } - - case TOK_CATCH: - { - ptrdiff_t catchStart, guardJump; - JSObject *blockObj; - - /* - * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, - * and save the block object atom. - */ - stmt = cg->topStmt; - JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); - stmt->type = STMT_CATCH; - catchStart = stmt->update; - blockObj = stmt->blockBox->object; - - /* Go up one statement info record to the TRY or FINALLY record. */ - stmt = stmt->down; - JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); - - /* Pick up the pending exception and bind it to the catch variable. */ - if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) - return JS_FALSE; - - /* - * Dup the exception object if there is a guard for rethrowing to use - * it later when rethrowing or in other catches. - */ - if (pn->pn_kid2 && js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - - pn2 = pn->pn_kid1; - switch (pn2->pn_type) { -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; -#endif - - case TOK_NAME: - /* Inline and specialize BindNameToSlot for pn2. */ - JS_ASSERT(!pn2->pn_cookie.isFree()); - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie.asInteger()); - break; - - default: - JS_ASSERT(0); - } - - /* Emit the guard expression, if there is one. */ - if (pn->pn_kid2) { - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, - CG_OFFSET(cg) - catchStart)) { - return JS_FALSE; - } - /* ifeq */ - guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (guardJump < 0) - return JS_FALSE; - GUARDJUMP(*stmt) = guardJump; - - /* Pop duplicated exception object as we no longer need it. */ - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - - /* Emit the catch body. */ - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - - /* - * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via - * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. - */ - off = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) - return JS_FALSE; - break; - } - - case TOK_VAR: - if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) - return JS_FALSE; - break; - - case TOK_RETURN: - /* Push a return value */ - pn2 = pn->pn_kid; - if (pn2) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - - /* - * EmitNonLocalJumpFixup may add fixup bytecode to close open try - * blocks having finally clauses and to exit intermingled let blocks. - * We can't simply transfer control flow to our caller in that case, - * because we must gosub to those finally clauses from inner to outer, - * with the correct stack pointer (i.e., after popping any with, - * for/in, etc., slots nested inside the finally's try). - * - * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an - * extra JSOP_RETRVAL after the fixups. - */ - top = CG_OFFSET(cg); - if (js_Emit1(cx, cg, JSOP_RETURN) < 0) - return JS_FALSE; - if (!EmitNonLocalJumpFixup(cx, cg, NULL)) - return JS_FALSE; - if (top + JSOP_RETURN_LENGTH != CG_OFFSET(cg)) { - CG_BASE(cg)[top] = JSOP_SETRVAL; - if (js_Emit1(cx, cg, JSOP_RETRVAL) < 0) - return JS_FALSE; - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - } - break; - -#if JS_HAS_GENERATORS - case TOK_YIELD: - if (!cg->inFunction()) { - ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, - JSMSG_BAD_RETURN_OR_YIELD, - js_yield_str); - return JS_FALSE; - } - if (pn->pn_kid) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (pn->pn_hidden && js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_YIELD) < 0) - return JS_FALSE; - break; -#endif - - case TOK_LC: - { -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_UNARY) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, PN_OP(pn)) < 0) - return JS_FALSE; - break; - } -#endif - - JS_ASSERT(pn->pn_arity == PN_LIST); - - noteIndex = -1; - tmp = CG_OFFSET(cg); - if (pn->pn_xflags & PNX_NEEDBRACES) { - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; - } - - js_PushStatement(cg, &stmtInfo, STMT_BLOCK, top); - - JSParseNode *pnchild = pn->pn_head; - if (pn->pn_xflags & PNX_FUNCDEFS) { - /* - * This block contains top-level function definitions. To ensure - * that we emit the bytecode defining them before the rest of code - * in the block we use a separate pass over functions. During the - * main pass later the emitter will add JSOP_NOP with source notes - * for the function to preserve the original functions position - * when decompiling. - * - * Currently this is used only for functions, as compile-as-we go - * mode for scripts does not allow separate emitter passes. - */ - JS_ASSERT(cg->inFunction()); - if (pn->pn_xflags & PNX_DESTRUCT) { - /* - * Assign the destructuring arguments before defining any - * functions, see bug 419662. - */ - JS_ASSERT(pnchild->pn_type == TOK_SEMI); - JS_ASSERT(pnchild->pn_kid->pn_type == TOK_VAR); - if (!js_EmitTree(cx, cg, pnchild)) - return JS_FALSE; - pnchild = pnchild->pn_next; - } - - for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_FUNCTION) { - if (pn2->pn_op == JSOP_NOP) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } else { - /* - * JSOP_DEFFUN in a top-level block with function - * definitions appears, for example, when "if (true)" - * is optimized away from "if (true) function x() {}". - * See bug 428424. - */ - JS_ASSERT(pn2->pn_op == JSOP_DEFFUN); - } - } - } - } - for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - - ok = js_PopStatementCG(cx, cg); - break; - } - - case TOK_SEQ: - JS_ASSERT(pn->pn_arity == PN_LIST); - js_PushStatement(cg, &stmtInfo, STMT_SEQ, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SEMI: - pn2 = pn->pn_kid; - if (pn2) { - /* - * Top-level or called-from-a-native JS_Execute/EvaluateScript, - * debugger, and eval frames may need the value of the ultimate - * expression statement as the script's result, despite the fact - * that it appears useless to the compiler. - * - * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when - * calling JS_Compile* to suppress JSOP_POPV. - */ - useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL)); - - /* Don't eliminate expressions with side effects. */ - if (!useful) { - if (!CheckSideEffects(cx, cg, pn2, &useful)) - return JS_FALSE; - } - - /* - * Don't eliminate apparently useless expressions if they are - * labeled expression statements. The tc->topStmt->update test - * catches the case where we are nesting in js_EmitTree for a - * labeled compound statement. - */ - if (!useful && - cg->topStmt && - cg->topStmt->type == STMT_LABEL && - cg->topStmt->update >= CG_OFFSET(cg)) { - useful = true; - } - - if (!useful) { - /* Don't complain about directive prologue members; just don't emit their code. */ - if (!pn->isDirectivePrologueMember()) { - CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; - if (!ReportCompileErrorNumber(cx, CG_TS(cg), pn2, - JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_USELESS_EXPR)) { - return JS_FALSE; - } - } - } else { - op = wantval ? JSOP_POPV : JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (!wantval && - pn2->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { - return JS_FALSE; - } -#endif - if (op != JSOP_NOP) { - /* - * Specialize JSOP_SETPROP to JSOP_SETMETHOD to defer or - * avoid null closure cloning. Do this only for assignment - * statements that are not completion values wanted by a - * script evaluator, to ensure that the joined function - * can't escape directly. - */ - if (!wantval && - PN_TYPE(pn2) == TOK_ASSIGN && - PN_OP(pn2) == JSOP_NOP && - PN_OP(pn2->pn_left) == JSOP_SETPROP && - PN_OP(pn2->pn_right) == JSOP_LAMBDA && - pn2->pn_right->pn_funbox->joinable()) { - pn2->pn_left->pn_op = JSOP_SETMETHOD; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } - } - break; - - case TOK_COLON: - /* Emit an annotated nop so we know to decompile a label. */ - atom = pn->pn_atom; - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - pn2 = pn->expr(); - noteType = (pn2->pn_type == TOK_LC || - (pn2->pn_type == TOK_LEXICALSCOPE && - pn2->expr()->pn_type == TOK_LC)) - ? SRC_LABELBRACE - : SRC_LABEL; - noteIndex = js_NewSrcNote2(cx, cg, noteType, - (ptrdiff_t) ALE_INDEX(ale)); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Emit code for the labeled statement. */ - js_PushStatement(cg, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); - stmtInfo.label = atom; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - /* If the statement was compound, emit a note for the end brace. */ - if (noteType == SRC_LABELBRACE) { - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_COMMA: - /* - * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. - * These notes help the decompiler bracket the bytecodes generated - * from each sub-expression that follows a comma. - */ - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_ASSIGN: - /* - * Check left operand type and generate specialized code for it. - * Specialize to avoid ECMA "reference type" values on the operand - * stack, which impose pervasive runtime "GetValue" costs. - */ - pn2 = pn->pn_left; - atomIndex = (jsatomid) -1; /* quell GCC overwarning */ - switch (PN_TYPE(pn2)) { - case TOK_NAME: - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - if (!pn2->pn_cookie.isFree()) { - atomIndex = (jsatomid) pn2->pn_cookie.asInteger(); - } else { - ale = cg->atomList.add(cg->parser, pn2->pn_atom); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - if (!pn2->isConst()) { - JSOp op = PN_OP(pn2) == JSOP_SETGNAME ? JSOP_BINDGNAME : JSOP_BINDNAME; - EMIT_INDEX_OP(op, atomIndex); - } - } - break; - case TOK_DOT: - if (!js_EmitTree(cx, cg, pn2->expr())) - return JS_FALSE; - ale = cg->atomList.add(cg->parser, pn2->pn_atom); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - break; - case TOK_LB: - JS_ASSERT(pn2->pn_arity == PN_BINARY); - if (!js_EmitTree(cx, cg, pn2->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - break; -#endif - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - break; -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - - op = PN_OP(pn); - if (op != JSOP_NOP) { - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->isConst()) { - if (PN_OP(pn2) == JSOP_CALLEE) { - if (js_Emit1(cx, cg, JSOP_CALLEE) < 0) - return JS_FALSE; - } else { - EMIT_INDEX_OP(PN_OP(pn2), atomIndex); - } - } else if (PN_OP(pn2) == JSOP_SETNAME) { - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); - } else if (PN_OP(pn2) == JSOP_SETGNAME) { - if (!BindGlobal(cx, cg, pn2, pn2->pn_atom)) - return JS_FALSE; - if (pn2->pn_cookie.isFree()) - EmitAtomOp(cx, pn2, JSOP_GETGNAME, cg); - else - EMIT_UINT16_IMM_OP(JSOP_GETGLOBAL, pn2->pn_cookie.asInteger()); - } else { - EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETARG) - ? JSOP_GETARG - : JSOP_GETLOCAL, - atomIndex); - } - break; - case TOK_DOT: - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) { - if (js_Emit1(cx, cg, JSOP_LENGTH) < 0) - return JS_FALSE; - } else if (pn2->pn_atom == cx->runtime->atomState.protoAtom) { - if (!EmitIndexOp(cx, JSOP_QNAMEPART, atomIndex, cg)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - } else { - EMIT_INDEX_OP(JSOP_GETPROP, atomIndex); - } - break; - case TOK_LB: - case TOK_LP: -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: -#endif - if (js_Emit1(cx, cg, JSOP_DUP2) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - break; - default:; - } - } - - /* Now emit the right operand (it may affect the namespace). */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* If += etc., emit the binary operator with a decompiler note. */ - if (op != JSOP_NOP) { - /* - * Take care to avoid SRC_ASSIGNOP if the left-hand side is a const - * declared in the current compilation unit, as in this case (just - * a bit further below) we will avoid emitting the assignment op. - */ - if (pn2->pn_type != TOK_NAME || !pn2->isConst()) { - if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Left parts such as a.b.c and a[b].c need a decompiler note. */ - if (pn2->pn_type != TOK_NAME && -#if JS_HAS_DESTRUCTURING - pn2->pn_type != TOK_RB && - pn2->pn_type != TOK_RC && -#endif - js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) { - return JS_FALSE; - } - - /* Finally, emit the specialized assignment bytecode. */ - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->isConst()) - break; - /* FALL THROUGH */ - case TOK_DOT: - EMIT_INDEX_OP(PN_OP(pn2), atomIndex); - break; - case TOK_LB: - case TOK_LP: - if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - break; - - case TOK_HOOK: - /* Emit the condition, then branch if false to the else part. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - noteIndex = js_NewSrcNote(cx, cg, SRC_COND); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - - /* Jump around else, fixup the branch, emit else, fixup jump. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - - /* - * Because each branch pushes a single value, but our stack budgeting - * analysis ignores branches, we now have to adjust cg->stackDepth to - * ignore the value pushed by the first branch. Execution will follow - * only one path, so we must decrement cg->stackDepth. - * - * Failing to do this will foil code, such as the try/catch/finally - * exception handling code generator, that samples cg->stackDepth for - * use at runtime (JSOP_SETSP), or in let expression and block code - * generation, which must use the stack depth to compute local stack - * indexes correctly. - */ - JS_ASSERT(cg->stackDepth > 0); - cg->stackDepth--; - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - break; - - case TOK_OR: - case TOK_AND: - /* - * JSOP_OR converts the operand on the stack to boolean, and if true, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the next bytecode, which evaluates the right - * operand. The jump goes around the right operand evaluation. - * - * JSOP_AND converts the operand on the stack to boolean, and if false, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the right operand's bytecode. - */ - if (pn->pn_arity == PN_BINARY) { - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (top < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - off = CG_OFFSET(cg); - pc = CG_CODE(cg, top); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); - *pc = pn->pn_op; - } else { - JS_ASSERT(pn->pn_arity == PN_LIST); - JS_ASSERT(pn->pn_head->pn_next->pn_next); - - /* Left-associative operator chain: avoid too much recursion. */ - pn2 = pn->pn_head; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (top < 0) - return JS_FALSE; - - /* Emit nodes between the head and the tail. */ - jmp = top; - while ((pn2 = pn2->pn_next)->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (off < 0) - return JS_FALSE; - if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp)) - return JS_FALSE; - jmp = off; - - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - pn2 = pn->pn_head; - off = CG_OFFSET(cg); - do { - pc = CG_CODE(cg, top); - tmp = GetJumpOffset(cg, pc); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); - *pc = pn->pn_op; - top += tmp; - } while ((pn2 = pn2->pn_next)->pn_next); - } - break; - - case TOK_PLUS: - case TOK_BITOR: - case TOK_BITXOR: - case TOK_BITAND: - case TOK_EQOP: - case TOK_RELOP: - case TOK_IN: - case TOK_INSTANCEOF: - case TOK_SHOP: - case TOK_MINUS: - case TOK_STAR: - case TOK_DIVOP: - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain: avoid too much recursion. */ - pn2 = pn->pn_head; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - op = PN_OP(pn); - while ((pn2 = pn2->pn_next) != NULL) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } else { -#if JS_HAS_XML_SUPPORT - uintN oldflags; - - case TOK_DBLCOLON: - if (pn->pn_arity == PN_NAME) { - if (!js_EmitTree(cx, cg, pn->expr())) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, PN_OP(pn), cg)) - return JS_FALSE; - break; - } - - /* - * Binary :: has a right operand that brackets arbitrary code, - * possibly including a let (a = b) ... expression. We must clear - * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. - */ - oldflags = cg->flags; - cg->flags &= ~TCF_IN_FOR_INIT; -#endif - - /* Binary operators that evaluate both operands unconditionally. */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; -#if JS_HAS_XML_SUPPORT - cg->flags |= oldflags & TCF_IN_FOR_INIT; -#endif - if (js_Emit1(cx, cg, PN_OP(pn)) < 0) - return JS_FALSE; - } - break; - - case TOK_THROW: -#if JS_HAS_XML_SUPPORT - case TOK_AT: - case TOK_DEFAULT: - JS_ASSERT(pn->pn_arity == PN_UNARY); - /* FALL THROUGH */ -#endif - case TOK_UNARYOP: - { - uintN oldflags; - - /* Unary op, including unary +/-. */ - op = PN_OP(pn); -#if JS_HAS_XML_SUPPORT - if (op == JSOP_XMLNAME) { - if (!EmitXMLName(cx, pn, op, cg)) - return JS_FALSE; - break; - } -#endif - pn2 = pn->pn_kid; - - if (op == JSOP_TYPEOF && pn2->pn_type != TOK_NAME) - op = JSOP_TYPEOFEXPR; - - oldflags = cg->flags; - cg->flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->flags |= oldflags & TCF_IN_FOR_INIT; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - } - - case TOK_INC: - case TOK_DEC: - /* Emit lvalue-specialized code for ++/-- operators. */ - pn2 = pn->pn_kid; - JS_ASSERT(pn2->pn_type != TOK_RP); - op = PN_OP(pn); - switch (pn2->pn_type) { - default: - JS_ASSERT(pn2->pn_type == TOK_NAME); - pn2->pn_op = op; - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - op = PN_OP(pn2); - if (op == JSOP_CALLEE) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else if (!pn2->pn_cookie.isFree()) { - atomIndex = (jsatomid) pn2->pn_cookie.asInteger(); - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - break; - } - if (pn2->isConst()) { - if (js_Emit1(cx, cg, JSOP_POS) < 0) - return JS_FALSE; - op = PN_OP(pn); - if (!(js_CodeSpec[op].format & JOF_POST)) { - if (js_Emit1(cx, cg, JSOP_ONE) < 0) - return JS_FALSE; - op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, op, cg, JS_FALSE)) - return JS_FALSE; - break; - case TOK_LB: - if (!EmitElemOp(cx, pn2, op, cg)) - return JS_FALSE; - break; - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#endif - } - break; - - case TOK_DELETE: - /* - * Under ECMA 3, deleting a non-reference returns true -- but alas we - * must evaluate the operand if it appears it might have side effects. - */ - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - if (!BindNameToSlot(cx, cg, pn2)) - return JS_FALSE; - op = PN_OP(pn2); - if (op == JSOP_FALSE) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else { - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg, JS_FALSE)) - return JS_FALSE; - break; -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: - if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) - return JS_FALSE; - break; -#endif - case TOK_LB: - if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) - return JS_FALSE; - break; - default: - /* - * If useless, just emit JSOP_TRUE; otherwise convert delete foo() - * to foo(), true (a comma expression, requiring SRC_PCDELTA). - */ - useful = JS_FALSE; - if (!CheckSideEffects(cx, cg, pn2, &useful)) - return JS_FALSE; - if (!useful) { - off = noteIndex = -1; - } else { - JS_ASSERT_IF(pn2->pn_type == TOK_LP, !(pn2->pn_xflags & PNX_SETCALL)); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = CG_OFFSET(cg); - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_TRUE) < 0) - return JS_FALSE; - if (noteIndex >= 0) { - tmp = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_FILTER: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - jmp = EmitJump(cx, cg, JSOP_FILTER, 0); - if (jmp < 0) - return JS_FALSE; - top = EmitTraceOp(cx, cg); - if (top < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (EmitJump(cx, cg, JSOP_ENDFILTER, top - CG_OFFSET(cg)) < 0) - return JS_FALSE; - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - break; -#endif - - case TOK_DOT: - /* - * Pop a stack operand, convert it to object, get a property named by - * this bytecode's immediate-indexed atom operand, and push its value - * (not a reference to it). - */ - ok = EmitPropOp(cx, pn, PN_OP(pn), cg, JS_FALSE); - break; - - case TOK_LB: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif - /* - * Pop two operands, convert the left one to object and the right one - * to property name (atom or tagged int), get the named property, and - * push its value. Set the "obj" register to the result of ToObject - * on the left operand. - */ - ok = EmitElemOp(cx, pn, PN_OP(pn), cg); - break; - - case TOK_NEW: - case TOK_LP: - { - bool callop = (PN_TYPE(pn) == TOK_LP); - - /* - * Emit callable invocation or operator new (constructor call) code. - * First, emit code for the left operand to evaluate the callable or - * constructable object expression. - * - * For operator new applied to other expressions than E4X ones, we emit - * JSOP_GETPROP instead of JSOP_CALLPROP, etc. This is necessary to - * interpose the lambda-initialized method read barrier -- see the code - * in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP. - * - * Then (or in a call case that has no explicit reference-base - * object) we emit JSOP_PUSH to produce the |this| slot required - * for calls (which non-strict mode functions will box into the - * global object). - */ - pn2 = pn->pn_head; - switch (pn2->pn_type) { - case TOK_NAME: - if (!EmitNameOp(cx, cg, pn2, callop)) - return JS_FALSE; - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, PN_OP(pn2), cg, callop)) - return JS_FALSE; - break; - case TOK_LB: - JS_ASSERT(pn2->pn_op == JSOP_GETELEM); - if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, cg)) - return JS_FALSE; - break; - case TOK_UNARYOP: -#if JS_HAS_XML_SUPPORT - if (pn2->pn_op == JSOP_XMLNAME) { - if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, cg)) - return JS_FALSE; - callop = true; /* suppress JSOP_PUSH after */ - break; - } -#endif - /* FALL THROUGH */ - default: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - callop = false; /* trigger JSOP_PUSH after */ - break; - } - if (!callop && js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - - /* Remember start of callable-object bytecode for decompilation hint. */ - off = top; - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ - uintN oldflags = cg->flags; - cg->flags &= ~TCF_IN_FOR_INIT; - for (pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - } - cg->flags |= oldflags & TCF_IN_FOR_INIT; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) - return JS_FALSE; - - argc = pn->pn_count - 1; - if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; - if (PN_OP(pn) == JSOP_EVAL) { - EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); - if (EmitBlockChain(cx, cg) < 0) - return JS_FALSE; - } - if (pn->pn_xflags & PNX_SETCALL) { - if (js_Emit1(cx, cg, JSOP_SETCALL) < 0) - return JS_FALSE; - } - break; - } - - case TOK_LEXICALSCOPE: - { - JSObjectBox *objbox; - - objbox = pn->pn_objbox; - js_PushBlockScope(cg, &stmtInfo, objbox, CG_OFFSET(cg)); - - /* - * If this lexical scope is not for a catch block, let block or let - * expression, or any kind of for loop (where the scope starts in the - * head after the first part if for (;;), else in the body if for-in); - * and if our container is top-level but not a function body, or else - * a block statement; then emit a SRC_BRACE note. All other container - * statements get braces by default from the decompiler. - */ - noteIndex = -1; - type = PN_TYPE(pn->expr()); - if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && - (!(stmt = stmtInfo.down) - ? !cg->inFunction() - : stmt->type == STMT_BLOCK)) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap - /* There must be no source note already output for the next op. */ - JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || - CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || - !GettableNoteForNextOp(cg)); -#endif - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0) - return JS_FALSE; - } - - JS_ASSERT(CG_OFFSET(cg) == top); - if (!EmitEnterBlock(cx, pn, cg)) - return JS_FALSE; - - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - - op = PN_OP(pn); - if (op == JSOP_LEAVEBLOCKEXPR) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - } else { - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - if (!EmitLeaveBlock(cx, cg, op, objbox)) - return JS_FALSE; - - ok = js_PopStatementCG(cx, cg); - break; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - /* Let statements have their variable declarations on the left. */ - if (pn->pn_arity == PN_BINARY) { - pn2 = pn->pn_right; - pn = pn->pn_left; - } else { - pn2 = NULL; - } - - /* Non-null pn2 means that pn is the variable list from a let head. */ - JS_ASSERT(pn->pn_arity == PN_LIST); - if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) - return JS_FALSE; - - /* Thus non-null pn2 is the body of the let block or expression. */ - tmp = CG_OFFSET(cg); - if (pn2 && !js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - break; -#endif /* JS_HAS_BLOCK_SCOPE */ - -#if JS_HAS_GENERATORS - case TOK_ARRAYPUSH: { - jsint slot; - - /* - * The array object's stack index is in cg->arrayCompDepth. See below - * under the array initialiser code generator for array comprehension - * special casing. - */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - slot = AdjustBlockSlot(cx, cg, cg->arrayCompDepth); - if (slot < 0) - return JS_FALSE; - EMIT_UINT16_IMM_OP(PN_OP(pn), slot); - break; - } -#endif - - case TOK_RB: -#if JS_HAS_GENERATORS - case TOK_ARRAYCOMP: -#endif - /* - * Emit code for [a, b, c] that is equivalent to constructing a new - * array and in source order evaluating each element value and adding - * it to the array, without invoking latent setters. We use the - * JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to - * avoid dup'ing and popping the array as each element is added, as - * JSOP_SETELEM/JSOP_SETPROP would do. - */ -#if JS_HAS_SHARP_VARS - sharpnum = -1; - do_emit_array: -#endif - -#if JS_HAS_GENERATORS - if (pn->pn_type == TOK_ARRAYCOMP) { - uintN saveDepth; - - if (!EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum)) - return JS_FALSE; - - /* - * Pass the new array's stack index to the TOK_ARRAYPUSH case via - * cg->arrayCompDepth, then simply traverse the TOK_FOR node and - * its kids under pn2 to generate this comprehension. - */ - JS_ASSERT(cg->stackDepth > 0); - saveDepth = cg->arrayCompDepth; - cg->arrayCompDepth = (uint32) (cg->stackDepth - 1); - if (!js_EmitTree(cx, cg, pn->pn_head)) - return JS_FALSE; - cg->arrayCompDepth = saveDepth; - - /* Emit the usual op needed for decompilation. */ - if (!EmitEndInit(cx, cg, 1)) - return JS_FALSE; - break; - } -#endif /* JS_HAS_GENERATORS */ - - if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && cg->checkSingletonContext()) { - if (!EmitSingletonInitialiser(cx, cg, pn)) - return JS_FALSE; - break; - } - - /* Use the slower NEWINIT for arrays in scripts containing sharps. */ - if (cg->hasSharps()) { - if (!EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum)) - return JS_FALSE; - } else { - ptrdiff_t off = js_EmitN(cx, cg, JSOP_NEWARRAY, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_UINT24(pc, pn->pn_count); - } - - pn2 = pn->pn_head; - for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { - if (!EmitNumberOp(cx, atomIndex, cg)) - return JS_FALSE; - if (pn2->pn_type == TOK_COMMA && pn2->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_HOLE) < 0) - return JS_FALSE; - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } - JS_ASSERT(atomIndex == pn->pn_count); - - if (pn->pn_xflags & PNX_ENDCOMMA) { - /* Emit a source note so we know to decompile an extra comma. */ - if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) - return JS_FALSE; - } - - /* - * Emit an op to finish the array and, secondarily, to aid in sharp - * array cleanup (if JS_HAS_SHARP_VARS) and decompilation. - */ - if (!EmitEndInit(cx, cg, atomIndex)) - return JS_FALSE; - break; - - case TOK_RC: { -#if JS_HAS_SHARP_VARS - sharpnum = -1; - do_emit_object: -#endif -#if JS_HAS_DESTRUCTURING_SHORTHAND - if (pn->pn_xflags & PNX_DESTRUCT) { - ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT); - return JS_FALSE; - } -#endif - - if (!cg->hasSharps() && !(pn->pn_xflags & PNX_NONCONST) && cg->checkSingletonContext()) { - if (!EmitSingletonInitialiser(cx, cg, pn)) - return JS_FALSE; - break; - } - - /* - * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing - * a new object and in source order evaluating each property value and - * adding the property to the object, without invoking latent setters. - * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to - * ignore setters and to avoid dup'ing and popping the object as each - * property is added, as JSOP_SETELEM/JSOP_SETPROP would do. - */ - ptrdiff_t offset = CG_NEXT(cg) - CG_BASE(cg); - if (!EmitNewInit(cx, cg, JSProto_Object, pn, sharpnum)) - return JS_FALSE; - - /* - * Try to construct the shape of the object as we go, so we can emit a - * JSOP_NEWOBJECT with the final shape instead. - */ - JSObject *obj = NULL; - if (!cg->hasSharps() && cg->compileAndGo()) { - gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); - if (!obj) - return JS_FALSE; - } - - uintN methodInits = 0, slowMethodInits = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */ - pn3 = pn2->pn_left; - if (pn3->pn_type == TOK_NUMBER) { - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - } - - /* Emit code for the property initializer. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - - op = PN_OP(pn2); - if (op == JSOP_GETTER || op == JSOP_SETTER) { - obj = NULL; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ - if (pn3->pn_type == TOK_NUMBER) { - obj = NULL; - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } else { - JS_ASSERT(pn3->pn_type == TOK_NAME || - pn3->pn_type == TOK_STRING); - ale = cg->atomList.add(cg->parser, pn3->pn_atom); - if (!ale) - return JS_FALSE; - - /* Check whether we can optimize to JSOP_INITMETHOD. */ - JSParseNode *init = pn2->pn_right; - bool lambda = PN_OP(init) == JSOP_LAMBDA; - if (lambda) - ++methodInits; - if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) { - obj = NULL; - op = JSOP_INITMETHOD; - pn2->pn_op = uint8(op); - } else { - /* - * Disable NEWOBJECT on initializers that set __proto__, which has - * a non-standard setter on objects. - */ - if (pn3->pn_atom == cx->runtime->atomState.protoAtom) - obj = NULL; - op = JSOP_INITPROP; - if (lambda) - ++slowMethodInits; - } - - if (obj) { - JS_ASSERT(!obj->inDictionaryMode()); - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(pn3->pn_atom), - UndefinedValue(), NULL, NULL, - JSPROP_ENUMERATE, 0, 0, NULL)) { - return JS_FALSE; - } - if (obj->inDictionaryMode()) - obj = NULL; - } - - EMIT_INDEX_OP(op, ALE_INDEX(ale)); - } - } - - if (cg->funbox && cg->funbox->shouldUnbrand(methodInits, slowMethodInits)) { - obj = NULL; - if (js_Emit1(cx, cg, JSOP_UNBRAND) < 0) - return JS_FALSE; - } - if (!EmitEndInit(cx, cg, pn->pn_count)) - return JS_FALSE; - - if (obj) { - /* - * The object survived and has a predictable shape. Update the original bytecode, - * as long as we can do so without using a big index prefix/suffix. - */ - JSObjectBox *objbox = cg->parser->newObjectBox(obj); - if (!objbox) - return JS_FALSE; - unsigned index = cg->objectList.index(objbox); - if (FitsWithoutBigIndex(index)) - EMIT_UINT16_IN_PLACE(offset, JSOP_NEWOBJECT, uint16(index)); - } - - break; - } - -#if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - JS_ASSERT(cg->hasSharps()); - sharpnum = pn->pn_num; - pn = pn->pn_kid; - if (pn->pn_type == TOK_RB) - goto do_emit_array; -# if JS_HAS_GENERATORS - if (pn->pn_type == TOK_ARRAYCOMP) - goto do_emit_array; -# endif - if (pn->pn_type == TOK_RC) - goto do_emit_object; - - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, cg->sharpSlotBase, (jsatomid) sharpnum); - break; - - case TOK_USESHARP: - JS_ASSERT(cg->hasSharps()); - EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, cg->sharpSlotBase, (jsatomid) pn->pn_num); - break; -#endif /* JS_HAS_SHARP_VARS */ - - case TOK_NAME: - /* - * Cope with a left-over function definition that was replaced by a use - * of a later function definition of the same name. See FunctionDef and - * MakeDefIntoUse in jsparse.cpp. - */ - if (pn->pn_op == JSOP_NOP) - break; - if (!EmitNameOp(cx, cg, pn, JS_FALSE)) - return JS_FALSE; - break; - -#if JS_HAS_XML_SUPPORT - case TOK_XMLATTR: - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: -#endif - case TOK_STRING: - ok = EmitAtomOp(cx, pn, PN_OP(pn), cg); - break; - - case TOK_NUMBER: - ok = EmitNumberOp(cx, pn->pn_dval, cg); - break; - - case TOK_REGEXP: { - JS_ASSERT(pn->pn_op == JSOP_REGEXP); - ok = EmitIndexOp(cx, JSOP_REGEXP, - cg->regexpList.index(pn->pn_objbox), - cg); - break; - } - -#if JS_HAS_XML_SUPPORT - case TOK_ANYNAME: -#endif - case TOK_PRIMARY: - if (js_Emit1(cx, cg, PN_OP(pn)) < 0) - return JS_FALSE; - break; - - case TOK_DEBUGGER: - if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0) - return JS_FALSE; - break; - -#if JS_HAS_XML_SUPPORT - case TOK_XMLELEM: - case TOK_XMLLIST: - JS_ASSERT(PN_TYPE(pn) == TOK_XMLLIST || pn->pn_count != 0); - switch (pn->pn_head ? PN_TYPE(pn->pn_head) : TOK_XMLLIST) { - case TOK_XMLETAGO: - JS_ASSERT(0); - /* FALL THROUGH */ - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - break; - default: - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - } - - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - - if (pn->pn_xflags & PNX_XMLROOT) { - if (pn->pn_count == 0) { - JS_ASSERT(pn->pn_type == TOK_XMLLIST); - atom = cx->runtime->atomState.emptyAtom; - ale = cg->atomList.add(cg->parser, atom); - if (!ale) - return JS_FALSE; - EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - } - if (js_Emit1(cx, cg, PN_OP(pn)) < 0) - return JS_FALSE; - } -#ifdef DEBUG - else - JS_ASSERT(pn->pn_count != 0); -#endif - break; - - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - case TOK_XMLETAGO: - { - uint32 i; - - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - - ale = cg->atomList.add(cg->parser, - (pn->pn_type == TOK_XMLETAGO) - ? cx->runtime->atomState.etagoAtom - : cx->runtime->atomState.stagoAtom); - if (!ale) - return JS_FALSE; - EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - - JS_ASSERT(pn->pn_count != 0); - pn2 = pn->pn_head; - if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if ((i & 1) && pn2->pn_type == TOK_LC) { - if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, - (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { - return JS_FALSE; - } - } - - ale = cg->atomList.add(cg->parser, - (pn->pn_type == TOK_XMLPTAGC) - ? cx->runtime->atomState.ptagcAtom - : cx->runtime->atomState.tagcAtom); - if (!ale) - return JS_FALSE; - EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - if ((pn->pn_xflags & PNX_XMLROOT) && js_Emit1(cx, cg, PN_OP(pn)) < 0) - return JS_FALSE; - break; - } - - case TOK_XMLNAME: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_count != 0); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_arity == PN_NULLARY); - ok = (pn->pn_op == JSOP_OBJECT) - ? EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg) - : EmitAtomOp(cx, pn, PN_OP(pn), cg); - } - break; - - case TOK_XMLPI: - ale = cg->atomList.add(cg->parser, pn->pn_atom2); - if (!ale) - return JS_FALSE; - if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) - return JS_FALSE; - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default: - JS_ASSERT(0); - } - - /* cg->emitLevel == 1 means we're last on the stack, so finish up. */ - if (ok && cg->emitLevel == 1) { - if (cg->spanDeps) - ok = OptimizeSpanDeps(cx, cg); - if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.end.lineno)) - return JS_FALSE; - } - - return ok; -} - -/* - * We should try to get rid of offsetBias (always 0 or 1, where 1 is - * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL. - */ -JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { - {"null", 0, 0, 0}, - {"if", 0, 0, 0}, - {"if-else", 2, 0, 1}, - {"for", 3, 1, 1}, - {"while", 1, 0, 1}, - {"continue", 0, 0, 0}, - {"decl", 1, 1, 1}, - {"pcdelta", 1, 0, 1}, - {"assignop", 0, 0, 0}, - {"cond", 1, 0, 1}, - {"brace", 1, 0, 1}, - {"hidden", 0, 0, 0}, - {"pcbase", 1, 0, -1}, - {"label", 1, 0, 0}, - {"labelbrace", 1, 0, 0}, - {"endbrace", 0, 0, 0}, - {"break2label", 1, 0, 0}, - {"cont2label", 1, 0, 0}, - {"switch", 2, 0, 1}, - {"funcdef", 1, 0, 0}, - {"catch", 1, 0, 1}, - {"extended", -1, 0, 0}, - {"newline", 0, 0, 0}, - {"setline", 1, 0, 0}, - {"xdelta", 0, 0, 0}, -}; - -static intN -AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) -{ - intN index; - JSArenaPool *pool; - size_t size; - - index = CG_NOTE_COUNT(cg); - if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - if (!CG_NOTES(cg)) { - /* Allocate the first note array lazily; leave noteMask alone. */ - JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); - } else { - /* Grow by doubling note array size; update noteMask on success. */ - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (CG_NOTES(cg)) - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - } - if (!CG_NOTES(cg)) { - js_ReportOutOfScriptQuota(cx); - return -1; - } - } - - CG_NOTE_COUNT(cg) = index + 1; - return index; -} - -intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) -{ - intN index, n; - jssrcnote *sn; - ptrdiff_t offset, delta, xdelta; - - /* - * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then - * incrementing CG_NOTE_COUNT(cg). - */ - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - - /* - * Compute delta from the last annotated bytecode's offset. If it's too - * big to fit in sn, allocate one or more xdelta notes and reset sn. - */ - offset = CG_OFFSET(cg); - delta = offset - CG_LAST_NOTE_OFFSET(cg); - CG_LAST_NOTE_OFFSET(cg) = offset; - if (delta >= SN_DELTA_LIMIT) { - do { - xdelta = JS_MIN(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(sn, xdelta); - delta -= xdelta; - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - } while (delta >= SN_DELTA_LIMIT); - } - - /* - * Initialize type and delta, then allocate the minimum number of notes - * needed for type's arity. Usually, we won't need more, but if an offset - * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg). - */ - SN_MAKE_NOTE(sn, type, delta); - for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) { - if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) - return -1; - } - return index; -} - -intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset)) - return -1; - } - return index; -} - -intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1)) - return -1; - if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2)) - return -1; - } - return index; -} - -static JSBool -GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) -{ - JSArenaPool *pool; - size_t size; - - /* Grow by doubling note array size; update noteMask on success. */ - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (!CG_NOTES(cg)) { - js_ReportOutOfScriptQuota(cx); - return JS_FALSE; - } - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - return JS_TRUE; -} - -jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta) -{ - ptrdiff_t base, limit, newdelta, diff; - intN index; - - /* - * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to - * main script note deltas, and only by a small positive amount. - */ - JS_ASSERT(cg->current == &cg->main); - JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); - - base = SN_DELTA(sn); - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - newdelta = base + delta; - if (newdelta < limit) { - SN_SET_DELTA(sn, newdelta); - } else { - index = sn - cg->main.notes; - if ((cg->main.noteCount & cg->main.noteMask) == 0) { - if (!GrowSrcNotes(cx, cg)) - return NULL; - sn = cg->main.notes + index; - } - diff = cg->main.noteCount - index; - cg->main.noteCount++; - memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); - SN_MAKE_XDELTA(sn, delta); - sn++; - } - return sn; -} - -JS_FRIEND_API(uintN) -js_SrcNoteLength(jssrcnote *sn) -{ - uintN arity; - jssrcnote *base; - - arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity; - for (base = sn++; arity; sn++, arity--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - return sn - base; -} - -JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which) -{ - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT((intN) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - if (*sn & SN_3BYTE_OFFSET_FLAG) { - return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16) - | (sn[1] << 8) - | sn[2]); - } - return (ptrdiff_t)*sn; -} - -JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset) -{ - jssrcnote *sn; - ptrdiff_t diff; - - if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - sn = &CG_NOTES(cg)[index]; - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT((intN) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - - /* See if the new offset requires three bytes. */ - if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { - /* Maybe this offset was already set to a three-byte value. */ - if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { - /* Losing, need to insert another two bytes for this offset. */ - index = sn - CG_NOTES(cg); - - /* - * Simultaneously test to see if the source note array must grow to - * accommodate either the first or second byte of additional storage - * required by this 3-byte offset. - */ - if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { - if (!GrowSrcNotes(cx, cg)) - return JS_FALSE; - sn = CG_NOTES(cg) + index; - } - CG_NOTE_COUNT(cg) += 2; - - diff = CG_NOTE_COUNT(cg) - (index + 3); - JS_ASSERT(diff >= 0); - if (diff > 0) - memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); - } - *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); - *sn++ = (jssrcnote)(offset >> 8); - } - *sn = (jssrcnote)offset; - return JS_TRUE; -} - -#ifdef DEBUG_notme -#define DEBUG_srcnotesize -#endif - -#ifdef DEBUG_srcnotesize -#define NBINS 10 -static uint32 hist[NBINS]; - -void DumpSrcNoteSizeHist() -{ - static FILE *fp; - int i, n; - - if (!fp) { - fp = fopen("/tmp/srcnotes.hist", "w"); - if (!fp) - return; - setvbuf(fp, NULL, _IONBF, 0); - } - fprintf(fp, "SrcNote size histogram:\n"); - for (i = 0; i < NBINS; i++) { - fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]); - for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n) - fputc('*', fp); - fputc('\n', fp); - } - fputc('\n', fp); -} -#endif - -/* - * Fill in the storage at notes with prolog and main srcnotes; the space at - * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h. - * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's - * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES! - */ -JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) -{ - uintN prologCount, mainCount, totalCount; - ptrdiff_t offset, delta; - jssrcnote *sn; - - JS_ASSERT(cg->current == &cg->main); - - prologCount = cg->prolog.noteCount; - if (prologCount && cg->prolog.currentLine != cg->firstLine) { - CG_SWITCH_TO_PROLOG(cg); - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0) - return JS_FALSE; - prologCount = cg->prolog.noteCount; - CG_SWITCH_TO_MAIN(cg); - } else { - /* - * Either no prolog srcnotes, or no line number change over prolog. - * We don't need a SRC_SETLINE, but we may need to adjust the offset - * of the first main note, by adding to its delta and possibly even - * prepending SRC_XDELTA notes to it to account for prolog bytecodes - * that came at and after the last annotated bytecode. - */ - offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; - JS_ASSERT(offset >= 0); - if (offset > 0 && cg->main.noteCount != 0) { - /* NB: Use as much of the first main note's delta as we can. */ - sn = cg->main.notes; - delta = SN_IS_XDELTA(sn) - ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) - : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); - if (offset < delta) - delta = offset; - for (;;) { - if (!js_AddToSrcNoteDelta(cx, cg, sn, delta)) - return JS_FALSE; - offset -= delta; - if (offset == 0) - break; - delta = JS_MIN(offset, SN_XDELTA_MASK); - sn = cg->main.notes; - } - } - } - - mainCount = cg->main.noteCount; - totalCount = prologCount + mainCount; - if (prologCount) - memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount)); - memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); - SN_MAKE_TERMINATOR(¬es[totalCount]); - -#ifdef DEBUG_notme - { int bin = JS_CeilingLog2(totalCount); - if (bin >= NBINS) - bin = NBINS - 1; - ++hist[bin]; - } -#endif - return JS_TRUE; -} - -static JSBool -NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, - uintN stackDepth, size_t start, size_t end) -{ - JSTryNode *tryNode; - - JS_ASSERT((uintN)(uint16)stackDepth == stackDepth); - JS_ASSERT(start <= end); - JS_ASSERT((size_t)(uint32)start == start); - JS_ASSERT((size_t)(uint32)end == end); - - JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool); - if (!tryNode) { - js_ReportOutOfScriptQuota(cx); - return JS_FALSE; - } - - tryNode->note.kind = kind; - tryNode->note.stackDepth = (uint16)stackDepth; - tryNode->note.start = (uint32)start; - tryNode->note.length = (uint32)(end - start); - tryNode->prev = cg->lastTryNode; - cg->lastTryNode = tryNode; - cg->ntrynotes++; - return JS_TRUE; -} - -void -js_FinishTakingTryNotes(JSCodeGenerator *cg, JSTryNoteArray *array) -{ - JSTryNode *tryNode; - JSTryNote *tn; - - JS_ASSERT(array->length > 0 && array->length == cg->ntrynotes); - tn = array->vector + array->length; - tryNode = cg->lastTryNode; - do { - *--tn = tryNode->note; - } while ((tryNode = tryNode->prev) != NULL); - JS_ASSERT(tn == array->vector); -} - -/* - * Find the index of the given object for code generator. - * - * Since the emitter refers to each parsed object only once, for the index we - * use the number of already indexes objects. We also add the object to a list - * to convert the list to a fixed-size array when we complete code generation, - * see JSCGObjectList::finish below. - * - * Most of the objects go to JSCodeGenerator.objectList but for regexp we use a - * separated JSCodeGenerator.regexpList. In this way the emitted index can be - * directly used to store and fetch a reference to a cloned RegExp object that - * shares the same JSRegExp private data created for the object literal in - * objbox. We need a cloned object to hold lastIndex and other direct properties - * that should not be shared among threads sharing a precompiled function or - * script. - * - * If the code being compiled is function code, allocate a reserved slot in - * the cloned function object that shares its precompiled script with other - * cloned function objects and with the compiler-created clone-parent. There - * are nregexps = script->regexps()->length such reserved slots in each - * function object cloned from fun->object. NB: during compilation, a funobj - * slots element must never be allocated, because JSObject::allocSlot could - * hand out one of the slots that should be given to a regexp clone. - * - * If the code being compiled is global code, the cloned regexp are stored in - * fp->vars slot and to protect regexp slots from GC we set fp->nvars to - * nregexps. - * - * The slots initially contain undefined or null. We populate them lazily when - * JSOP_REGEXP is executed for the first time. - * - * Why clone regexp objects? ECMA specifies that when a regular expression - * literal is scanned, a RegExp object is created. In the spec, compilation - * and execution happen indivisibly, but in this implementation and many of - * its embeddings, code is precompiled early and re-executed in multiple - * threads, or using multiple global objects, or both, for efficiency. - * - * In such cases, naively following ECMA leads to wrongful sharing of RegExp - * objects, which makes for collisions on the lastIndex property (especially - * for global regexps) and on any ad-hoc properties. Also, __proto__ refers to - * the pre-compilation prototype, a pigeon-hole problem for instanceof tests. - */ -uintN -JSCGObjectList::index(JSObjectBox *objbox) -{ - JS_ASSERT(!objbox->emitLink); - objbox->emitLink = lastbox; - lastbox = objbox; - objbox->index = length++; - return objbox->index; -} - -void -JSCGObjectList::finish(JSObjectArray *array) -{ - JSObject **cursor; - JSObjectBox *objbox; - - JS_ASSERT(length <= INDEX_LIMIT); - JS_ASSERT(length == array->length); - - cursor = array->vector + array->length; - objbox = lastbox; - do { - --cursor; - JS_ASSERT(!*cursor); - *cursor = objbox->object; - } while ((objbox = objbox->emitLink) != NULL); - JS_ASSERT(cursor == array->vector); -} - -void -JSGCConstList::finish(JSConstArray *array) -{ - JS_ASSERT(array->length == list.length()); - Value *src = list.begin(), *srcend = list.end(); - Value *dst = array->vector; - for (; src != srcend; ++src, ++dst) - *dst = *src; -} diff --git a/deps/mozjs/js/src/jsemit.h b/deps/mozjs/js/src/jsemit.h deleted file mode 100644 index c5a1f1b8ec6..00000000000 --- a/deps/mozjs/js/src/jsemit.h +++ /dev/null @@ -1,1091 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=79: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsemit_h___ -#define jsemit_h___ -/* - * JS bytecode generation. - */ -#include "jstypes.h" -#include "jsatom.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscript.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * NB: If you add enumerators for scope statements, add them between STMT_WITH - * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add - * non-looping statement enumerators, add them before STMT_DO_LOOP or you will - * break the STMT_TYPE_IS_LOOP macro. - * - * Also remember to keep the statementName array in jsemit.c in sync. - */ -typedef enum JSStmtType { - STMT_LABEL, /* labeled statement: L: s */ - STMT_IF, /* if (then) statement */ - STMT_ELSE, /* else clause of if statement */ - STMT_SEQ, /* synthetic sequence of statements */ - STMT_BLOCK, /* compound statement: { s1[;... sN] } */ - STMT_SWITCH, /* switch statement */ - STMT_WITH, /* with statement */ - STMT_CATCH, /* catch block */ - STMT_TRY, /* try block */ - STMT_FINALLY, /* finally block */ - STMT_SUBROUTINE, /* gosub-target subroutine body */ - STMT_DO_LOOP, /* do/while loop statement */ - STMT_FOR_LOOP, /* for loop statement */ - STMT_FOR_IN_LOOP, /* for/in loop statement */ - STMT_WHILE_LOOP, /* while loop statement */ - STMT_LIMIT -} JSStmtType; - -#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) - -/* - * A comment on the encoding of the JSStmtType enum and type-testing macros: - * - * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may - * become, a lexical scope. It therefore includes block and switch (the two - * low-numbered "maybe" scope types) and excludes with (with has dynamic scope - * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally - * types, which are high-numbered maybe-scope types. - * - * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly - * links to other scoping statement info records. It excludes the two early - * "maybe" types, block and switch, as well as the try and both finally types, - * since try and the other trailing maybe-scope types don't need block scope - * unless they contain let declarations. - * - * We treat WITH as a static scope because it prevents lexical binding from - * continuing further up the static scope chain. With the lost "reformed with" - * proposal for ES4, we would be able to model it statically, too. - */ -#define STMT_TYPE_MAYBE_SCOPE(type) \ - (type != STMT_WITH && \ - STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) - -#define STMT_TYPE_LINKS_SCOPE(type) \ - STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) - -#define STMT_TYPE_IS_TRYING(type) \ - STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) - -#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) - -#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) -#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ - ((stmt)->flags & SIF_SCOPE)) -#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) -#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) - -typedef struct JSStmtInfo JSStmtInfo; - -struct JSStmtInfo { - uint16 type; /* statement type */ - uint16 flags; /* flags, see below */ - uint32 blockid; /* for simplified dominance computation */ - ptrdiff_t update; /* loop update offset (top if none) */ - ptrdiff_t breaks; /* offset of last break in loop */ - ptrdiff_t continues; /* offset of last continue in loop */ - union { - JSAtom *label; /* name of LABEL */ - JSObjectBox *blockBox; /* block scope object */ - }; - JSStmtInfo *down; /* info for enclosing statement */ - JSStmtInfo *downScope; /* next enclosing lexical scope */ -}; - -#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ -#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ -#define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ - -/* - * To reuse space in JSStmtInfo, rename breaks and continues for use during - * try/catch/finally code generation and backpatching. To match most common - * use cases, the macro argument is a struct, not a struct pointer. Only a - * loop, switch, or label statement info record can have breaks and continues, - * and only a for loop has an update backpatch chain, so it's safe to overlay - * these for the "trying" JSStmtTypes. - */ -#define CATCHNOTE(stmt) ((stmt).update) -#define GOSUBS(stmt) ((stmt).breaks) -#define GUARDJUMP(stmt) ((stmt).continues) - -#define SET_STATEMENT_TOP(stmt, top) \ - ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) - -#ifdef JS_SCOPE_DEPTH_METER -# define JS_SCOPE_DEPTH_METERING(code) ((void) (code)) -# define JS_SCOPE_DEPTH_METERING_IF(cond, code) ((cond) ? (void) (code) : (void) 0) -#else -# define JS_SCOPE_DEPTH_METERING(code) ((void) 0) -# define JS_SCOPE_DEPTH_METERING_IF(code, x) ((void) 0) -#endif - -#define TCF_COMPILING 0x01 /* JSTreeContext is JSCodeGenerator */ -#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ -#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ -#define TCF_RETURN_VOID 0x08 /* function has 'return;' */ -#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ -#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */ -#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */ -#define TCF_FUN_USES_ARGUMENTS 0x80 /* function uses arguments except as a - parameter name */ -#define TCF_FUN_HEAVYWEIGHT 0x100 /* function needs Call object per call */ -#define TCF_FUN_IS_GENERATOR 0x200 /* parsed yield statement in function */ -#define TCF_FUN_USES_OWN_NAME 0x400 /* named function expression that uses its - own name */ -#define TCF_HAS_FUNCTION_STMT 0x800 /* block contains a function statement */ -#define TCF_GENEXP_LAMBDA 0x1000 /* flag lambda from generator expression */ -#define TCF_COMPILE_N_GO 0x2000 /* compile-and-go mode of script, can - optimize name references based on scope - chain */ -#define TCF_NO_SCRIPT_RVAL 0x4000 /* API caller does not want result value - from global script */ -#define TCF_HAS_SHARPS 0x8000 /* source contains sharp defs or uses */ - -/* - * Set when parsing a declaration-like destructuring pattern. This - * flag causes PrimaryExpr to create PN_NAME parse nodes for variable - * references which are not hooked into any definition's use chain, - * added to any tree context's AtomList, etc. etc. CheckDestructuring - * will do that work later. - * - * The comments atop CheckDestructuring explain the distinction - * between assignment-like and declaration-like destructuring - * patterns, and why they need to be treated differently. - */ -#define TCF_DECL_DESTRUCTURING 0x10000 - -/* - * A request flag passed to Compiler::compileScript and then down via - * JSCodeGenerator to js_NewScriptFromCG, from script_compile_sub and any - * kindred functions that need to make mutable scripts (even empty ones; - * i.e., they can't share the const JSScript::emptyScript() singleton). - */ -#define TCF_NEED_MUTABLE_SCRIPT 0x20000 - -/* - * This function/global/eval code body contained a Use Strict Directive. Treat - * certain strict warnings as errors, and forbid the use of 'with'. See also - * TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and JSREPORT_STRICT_ERROR. - */ -#define TCF_STRICT_MODE_CODE 0x40000 - -/* bit 0x80000 is unused */ - -/* - * Flag signifying that the current function seems to be a constructor that - * sets this.foo to define "methods", at least one of which can't be a null - * closure, so we should avoid over-specializing property cache entries and - * trace inlining guards to method function object identity, which will vary - * per instance. - */ -#define TCF_FUN_UNBRAND_THIS 0x100000 - -/* - * "Module pattern", i.e., a lambda that is immediately applied and the whole - * of an expression statement. - */ -#define TCF_FUN_MODULE_PATTERN 0x200000 - -/* - * Flag to prevent a non-escaping function from being optimized into a null - * closure (i.e., a closure that needs only its global object for free variable - * resolution), because this function contains a closure that needs one or more - * scope objects surrounding it (i.e., a Call object for an outer heavyweight - * function). See bug 560234. - */ -#define TCF_FUN_ENTRAINS_SCOPES 0x400000 - -/* The function calls 'eval'. */ -#define TCF_FUN_CALLS_EVAL 0x800000 - -/* The function mutates a positional (non-destructuring) parameter. */ -#define TCF_FUN_MUTATES_PARAMETER 0x1000000 - -/* - * Compiling an eval() script. - */ -#define TCF_COMPILE_FOR_EVAL 0x2000000 - -/* - * The function or a function that encloses it may define new local names - * at runtime through means other than calling eval. - */ -#define TCF_FUN_MIGHT_ALIAS_LOCALS 0x4000000 - -/* - * The script contains singleton initialiser JSOP_OBJECT. - */ -#define TCF_HAS_SINGLETONS 0x8000000 - -/* - * Some enclosing scope is a with-statement or E4X filter-expression. - */ -#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. - */ -#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID) - -/* - * Sticky deoptimization flags to propagate from FunctionBody. - */ -#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \ - TCF_FUN_USES_ARGUMENTS | \ - TCF_FUN_PARAM_ARGUMENTS | \ - TCF_FUN_HEAVYWEIGHT | \ - TCF_FUN_IS_GENERATOR | \ - TCF_FUN_USES_OWN_NAME | \ - TCF_HAS_SHARPS | \ - TCF_FUN_CALLS_EVAL | \ - TCF_FUN_MIGHT_ALIAS_LOCALS | \ - TCF_FUN_MUTATES_PARAMETER | \ - TCF_STRICT_MODE_CODE | \ - TCF_FUN_EXTENSIBLE_SCOPE) - -struct JSTreeContext { /* tree context for semantic checks */ - uint32 flags; /* statement state flags, see above */ - uint32 bodyid; /* block number of program/function body */ - uint32 blockidGen; /* preincremented block number generator */ - JSStmtInfo *topStmt; /* top of statement info stack */ - JSStmtInfo *topScopeStmt; /* top lexical scope statement */ - JSObjectBox *blockChainBox; /* compile time block scope chain (NB: one - deeper than the topScopeStmt/downScope - chain when in head of let block/expr) */ - JSParseNode *blockNode; /* parse node for a block with let declarations - (block with its own lexical scope) */ - JSAtomList decls; /* function, const, and var declarations */ - js::Parser *parser; /* ptr to common parsing and lexing data */ - - private: - union { - JSFunction *fun_; /* function to store argument and variable - names when flags & TCF_IN_FUNCTION */ - JSObject *scopeChain_; /* scope chain object for the script */ - }; - - public: - JSFunction *fun() const { - JS_ASSERT(inFunction()); - return fun_; - } - void setFunction(JSFunction *fun) { - JS_ASSERT(inFunction()); - fun_ = fun; - } - JSObject *scopeChain() const { - JS_ASSERT(!inFunction()); - return scopeChain_; - } - void setScopeChain(JSObject *scopeChain) { - JS_ASSERT(!inFunction()); - scopeChain_ = scopeChain; - } - - JSAtomList lexdeps; /* unresolved lexical name dependencies */ - JSTreeContext *parent; /* enclosing function or global context */ - uintN staticLevel; /* static compilation unit nesting level */ - - JSFunctionBox *funbox; /* null or box for function we're compiling - if (flags & TCF_IN_FUNCTION) and not in - Compiler::compileFunctionBody */ - JSFunctionBox *functionList; - - JSParseNode *innermostWith; /* innermost WITH parse node */ - - js::Bindings bindings; /* bindings in this code, including - arguments if we're compiling a function */ - -#ifdef JS_SCOPE_DEPTH_METER - uint16 scopeDepth; /* current lexical scope chain depth */ - uint16 maxScopeDepth; /* maximum lexical scope chain depth */ -#endif - - void trace(JSTracer *trc); - - 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, prs->emptyCallShape), - sharpSlotBase(-1) - { - prs->tc = this; - JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0); - } - - /* - * For functions the tree context is constructed and destructed a second - * time during code generation. To avoid a redundant stats update in such - * cases, we store uint16(-1) in maxScopeDepth. - */ - ~JSTreeContext() { - parser->tc = this->parent; - JS_SCOPE_DEPTH_METERING_IF((maxScopeDepth != uint16(-1)), - JS_BASIC_STATS_ACCUM(&parser - ->context - ->runtime - ->lexicalScopeDepthStats, - maxScopeDepth)); - } - - uintN blockid() { return topStmt ? topStmt->blockid : bodyid; } - - JSObject *blockChain() { - return blockChainBox ? blockChainBox->object : NULL; - } - - /* - * True if we are at the topmost level of a entire script or function body. - * For example, while parsing this code we would encounter f1 and f2 at - * body level, but we would not encounter f3 or f4 at body level: - * - * function f1() { function f2() { } } - * if (cond) { function f3() { if (cond) { function f4() { } } } } - */ - bool atBodyLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); } - - /* Test whether we're in a statement of given type. */ - bool inStatement(JSStmtType type); - - bool inStrictMode() const { - return flags & TCF_STRICT_MODE_CODE; - } - - inline bool needStrictChecks(); - - /* - * sharpSlotBase is -1 or first slot of pair for [sharpArray, sharpDepth]. - * The parser calls ensureSharpSlots to allocate these two stack locals. - */ - int sharpSlotBase; - bool ensureSharpSlots(); - - js::Compiler *compiler() { return (js::Compiler *)parser; } - - // Return true there is a generator function within |skip| lexical scopes - // (going upward) from this context's lexical scope. Always return true if - // this context is itself a generator. - bool skipSpansGenerator(unsigned skip); - - bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; } - bool inFunction() const { return flags & TCF_IN_FUNCTION; } - - bool compiling() const { return flags & TCF_COMPILING; } - inline JSCodeGenerator *asCodeGenerator(); - - bool usesArguments() const { - return flags & TCF_FUN_USES_ARGUMENTS; - } - - void noteCallsEval() { - flags |= TCF_FUN_CALLS_EVAL; - } - - bool callsEval() const { - return flags & TCF_FUN_CALLS_EVAL; - } - - void noteMightAliasLocals() { - flags |= TCF_FUN_MIGHT_ALIAS_LOCALS; - } - - bool mightAliasLocals() const { - return flags & TCF_FUN_MIGHT_ALIAS_LOCALS; - } - - void noteParameterMutation() { - JS_ASSERT(inFunction()); - flags |= TCF_FUN_MUTATES_PARAMETER; - } - - bool mutatesParameter() const { - JS_ASSERT(inFunction()); - return flags & TCF_FUN_MUTATES_PARAMETER; - } - - void noteArgumentsUse() { - JS_ASSERT(inFunction()); - flags |= TCF_FUN_USES_ARGUMENTS; - if (funbox) - funbox->node->pn_dflags |= PND_FUNARG; - } - - bool needsEagerArguments() const { - return inStrictMode() && ((usesArguments() && mutatesParameter()) || callsEval()); - } - - void noteHasExtensibleScope() { - flags |= TCF_FUN_EXTENSIBLE_SCOPE; - } - - bool hasExtensibleScope() const { - return flags & TCF_FUN_EXTENSIBLE_SCOPE; - } -}; - -/* - * Return true if we need to check for conditions that elicit - * JSOPTION_STRICT warnings or strict mode errors. - */ -inline bool JSTreeContext::needStrictChecks() { - return parser->context->hasStrictOption() || inStrictMode(); -} - -/* - * Span-dependent instructions are jumps whose span (from the jump bytecode to - * the jump target) may require 2 or 4 bytes of immediate operand. - */ -typedef struct JSSpanDep JSSpanDep; -typedef struct JSJumpTarget JSJumpTarget; - -struct JSSpanDep { - ptrdiff_t top; /* offset of first bytecode in an opcode */ - ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ - ptrdiff_t before; /* original offset - 1 of jump operand */ - JSJumpTarget *target; /* tagged target pointer or backpatch delta */ -}; - -/* - * Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets - * sorted by offset from left to right, so that targets after a span-dependent - * instruction whose jump offset operand must be extended can be found quickly - * and adjusted upward (toward higher offsets). - */ -struct JSJumpTarget { - ptrdiff_t offset; /* offset of span-dependent jump target */ - int balance; /* AVL tree balance number */ - JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ -}; - -#define JT_LEFT 0 -#define JT_RIGHT 1 -#define JT_OTHER_DIR(dir) (1 - (dir)) -#define JT_IMBALANCE(dir) (((dir) << 1) - 1) -#define JT_DIR(imbalance) (((imbalance) + 1) >> 1) - -/* - * Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, - * so we can maintain backpatch chains when using span dependency records to - * hold jump offsets that overflow 16 bits. - */ -#define JT_TAG_BIT ((jsword) 1) -#define JT_UNTAG_SHIFT 1 -#define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) -#define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) -#define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) - -#define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) -#define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) -#define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) -#define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) -#define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) - -#define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) -#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ - JT_CLR_TAG((sd)->target)) -#define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) -#define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ - JT_TO_BPDELTA((sd)->target)) - -/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ -#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ - ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ - : 0) - -typedef struct JSTryNode JSTryNode; - -struct JSTryNode { - JSTryNote note; - JSTryNode *prev; -}; - -struct JSCGObjectList { - uint32 length; /* number of emitted so far objects */ - JSObjectBox *lastbox; /* last emitted object */ - - JSCGObjectList() : length(0), lastbox(NULL) {} - - uintN index(JSObjectBox *objbox); - void finish(JSObjectArray *array); -}; - -class JSGCConstList { - js::Vector list; - public: - JSGCConstList(JSContext *cx) : list(cx) {} - bool append(js::Value v) { return list.append(v); } - size_t length() const { return list.length(); } - void finish(JSConstArray *array); - -}; - -struct JSCodeGenerator : public JSTreeContext -{ - JSArenaPool *codePool; /* pointer to thread code arena pool */ - JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ - void *codeMark; /* low watermark in cg->codePool */ - void *noteMark; /* low watermark in cg->notePool */ - - struct { - jsbytecode *base; /* base of JS bytecode vector */ - jsbytecode *limit; /* one byte beyond end of bytecode */ - jsbytecode *next; /* pointer to next free bytecode */ - jssrcnote *notes; /* source notes, see below */ - uintN noteCount; /* number of source notes so far */ - uintN noteMask; /* growth increment for notes */ - ptrdiff_t lastNoteOffset; /* code offset for last source note */ - uintN currentLine; /* line number for tree-based srcnote gen */ - } prolog, main, *current; - - JSAtomList atomList; /* literals indexed for mapping */ - uintN firstLine; /* first line, for js_NewScriptFromCG */ - - intN stackDepth; /* current stack depth in script frame */ - uintN maxStackDepth; /* maximum stack depth so far */ - - uintN ntrynotes; /* number of allocated so far try notes */ - JSTryNode *lastTryNode; /* the last allocated try node */ - - JSSpanDep *spanDeps; /* span dependent instruction records */ - JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ - JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ - uintN numSpanDeps; /* number of span dependencies */ - uintN numJumpTargets; /* number of jump targets */ - ptrdiff_t spanDepTodo; /* offset from main.base of potentially - unoptimized spandeps */ - - uintN arrayCompDepth; /* stack depth of array in comprehension */ - - uintN emitLevel; /* js_EmitTree recursion level */ - - typedef js::HashMap ConstMap; - ConstMap constMap; /* compile time constants */ - - JSGCConstList constList; /* constants to be included with the script */ - - JSCGObjectList objectList; /* list of emitted objects */ - JSCGObjectList regexpList; /* list of emitted regexp that will be - cloned during execution */ - - JSAtomList upvarList; /* map of atoms to upvar indexes */ - JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */ - - typedef js::Vector GlobalUseVector; - - GlobalUseVector globalUses; /* per-script global uses */ - JSAtomList globalMap; /* per-script map of global name to globalUses vector */ - - /* Vectors of pn_cookie slot values. */ - typedef js::Vector SlotVector; - SlotVector closedArgs; - SlotVector closedVars; - - uint16 traceIndex; /* index for the next JSOP_TRACE instruction */ - - /* - * Initialize cg to allocate bytecode space from codePool, source note - * space from notePool, and all other arena-allocated temporaries from - * parser->context->tempPool. - */ - JSCodeGenerator(js::Parser *parser, - JSArenaPool *codePool, JSArenaPool *notePool, - uintN lineno); - bool init(); - - /* - * Release cg->codePool, cg->notePool, and parser->context->tempPool to - * marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own - * the arena pool "tops-of-stack" space above their codeMark, noteMark, and - * tempMark points. This means you cannot alloc from tempPool and save the - * pointer beyond the next JSCodeGenerator destructor call. - */ - ~JSCodeGenerator(); - - /* - * Adds a use of a variable that is statically known to exist on the - * global object. - * - * The actual slot of the variable on the global object is not known - * until after compilation. Properties must be resolved before being - * added, to avoid aliasing properties that should be resolved. This makes - * slot prediction based on the global object's free slot impossible. So, - * we use the slot to index into cg->globalScope->defs, and perform a - * fixup of the script at the very end of compilation. - * - * If the global use can be cached, |cookie| will be set to |slot|. - * Otherwise, |cookie| is set to the free cookie value. - */ - bool addGlobalUse(JSAtom *atom, uint32 slot, js::UpvarCookie *cookie); - - bool hasSharps() const { - bool rv = !!(flags & TCF_HAS_SHARPS); - JS_ASSERT((sharpSlotBase >= 0) == rv); - return rv; - } - - uintN sharpSlots() const { - return hasSharps() ? SHARP_NSLOTS : 0; - } - - bool compilingForEval() const { return !!(flags & TCF_COMPILE_FOR_EVAL); } - JSVersion version() const { return parser->versionWithFlags(); } - - bool shouldNoteClosedName(JSParseNode *pn); - - bool checkSingletonContext() { - if (!compileAndGo() || inFunction()) - return false; - for (JSStmtInfo *stmt = topStmt; stmt; stmt = stmt->down) { - if (STMT_IS_LOOP(stmt)) - return false; - } - flags |= TCF_HAS_SINGLETONS; - return true; - } -}; - -#define CG_TS(cg) TS((cg)->parser) - -#define CG_BASE(cg) ((cg)->current->base) -#define CG_LIMIT(cg) ((cg)->current->limit) -#define CG_NEXT(cg) ((cg)->current->next) -#define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) -#define CG_OFFSET(cg) (CG_NEXT(cg) - CG_BASE(cg)) - -#define CG_NOTES(cg) ((cg)->current->notes) -#define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) -#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) -#define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) -#define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) - -#define CG_PROLOG_BASE(cg) ((cg)->prolog.base) -#define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) -#define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) -#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) -#define CG_PROLOG_OFFSET(cg) (CG_PROLOG_NEXT(cg) - CG_PROLOG_BASE(cg)) - -#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) -#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) - -inline JSCodeGenerator * -JSTreeContext::asCodeGenerator() -{ - JS_ASSERT(compiling()); - return static_cast(this); -} - -/* - * Emit one bytecode. - */ -extern ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); - -/* - * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). - */ -extern ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); - -/* - * Emit three bytecodes, an opcode with two bytes of immediate operands. - */ -extern ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2); - -/* - * Emit five bytecodes, an opcode with two 16-bit immediates. - */ -extern ptrdiff_t -js_Emit5(JSContext *cx, JSCodeGenerator *cg, JSOp op, uint16 op1, - uint16 op2); - -/* - * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. - */ -extern ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); - -/* - * Unsafe macro to call js_SetJumpOffset and return false if it does. - */ -#define CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,BAD_EXIT) \ - JS_BEGIN_MACRO \ - if (!js_SetJumpOffset(cx, cg, pc, off)) { \ - BAD_EXIT; \ - } \ - JS_END_MACRO - -#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ - CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,return JS_FALSE) - -#define CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx,cg,off,BAD_EXIT) \ - CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx, cg, CG_CODE(cg,off), \ - CG_OFFSET(cg) - (off), BAD_EXIT) - -#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ - CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx, cg, off, return JS_FALSE) - -extern JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off); - -/* - * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. - */ -extern void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top); - -/* - * Push a block scope statement and link blockObj into tc->blockChain. To pop - * this statement info record, use js_PopStatement as usual, or if appropriate - * (if generating code), js_PopStatementCG. - */ -extern void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObjectBox *blockBox, - ptrdiff_t top); - -/* - * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it - * is up to the caller to free it. - */ -extern void -js_PopStatement(JSTreeContext *tc); - -/* - * Like js_PopStatement(cg), also patch breaks and continues unless the top - * statement info record represents a try-catch-finally suite. May fail if a - * jump offset overflows. - */ -extern JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); - -/* - * Define and lookup a primitive jsval associated with the const named by atom. - * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn - * and saves the const's value in cg->constList, if it can be used at compile - * time. It returns true unless an error occurred. - * - * If the initializer's value could not be saved, js_DefineCompileTimeConstant - * calls will return the undefined value. js_DefineCompileTimeConstant tries - * to find a const value memorized for atom, returning true with *vp set to a - * value other than undefined if the constant was found, true with *vp set to - * JSVAL_VOID if not found, and false on error. - */ -extern JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn); - -/* - * Find a lexically scoped variable (one declared by let, catch, or an array - * comprehension) named by atom, looking in tc's compile-time scopes. - * - * If a WITH statement is reached along the scope stack, return its statement - * info record, so callers can tell that atom is ambiguous. If slotp is not - * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. - * This means that if slotp is not null, all the block objects on the lexical - * scope chain must have had their depth slots computed by the code generator, - * so the caller must be under js_EmitTree. - * - * In any event, directly return the statement info record in which atom was - * found. Otherwise return null. - */ -extern JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, - JSStmtInfo *stmt = NULL); - -/* - * Emit code into cg for the tree rooted at pn. - */ -extern JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -/* - * Emit function code using cg for the tree rooted at body. - */ -extern JSBool -js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); - -/* - * Source notes generated along with bytecode for decompiling and debugging. - * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of - * the previous note. If 3 bits of offset aren't enough, extended delta notes - * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits - * are emitted before the next note. Some notes have operand offsets encoded - * immediately after them, in note bytes or byte-triples. - * - * Source Note Extended Delta - * +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ - * |note-type|delta| |1 1| ext-delta | - * +---------+-----+ +---+-----------+ - * - * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, - * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. - * - * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its - * initializers need to match the order here. - * - * Note on adding new source notes: every pair of bytecodes (A, B) where A and - * B have disjoint sets of source notes that could apply to each bytecode may - * reuse the same note type value for two notes (snA, snB) that have the same - * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is - * why SRC_IF and SRC_INITPROP have the same value below. For bad historical - * reasons, some bytecodes below that could be overlayed have not been, but - * before using SRC_EXTENDED, consider compressing the existing note types. - * - * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such - * incompatible source note or other bytecode changes. - */ -typedef enum JSSrcNoteType { - SRC_NULL = 0, /* terminates a note vector */ - SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ - SRC_BREAK = 1, /* JSOP_GOTO is a break */ - SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or - to an index label in a regular (structuring) - or a destructuring object initialiser */ - SRC_GENEXP = 1, /* JSOP_LAMBDA from generator expression */ - SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ - SRC_FOR_IN = 2, /* JSOP_GOTO to for-in loop condition from - before loop (same arity as SRC_IF_ELSE) */ - SRC_FOR = 3, /* JSOP_NOP or JSOP_POP in for(;;) loop head */ - SRC_WHILE = 4, /* JSOP_GOTO to for or while loop condition - from before loop, else JSOP_NOP at top of - do-while loop */ - SRC_TRACE = 4, /* For JSOP_TRACE; includes distance to loop end */ - SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; - also used on JSOP_ENDINIT if extra comma - at end of array literal: [1,2,,]; - JSOP_DUP continuing destructuring pattern */ - SRC_DECL = 6, /* type of a declaration (var, const, let*) */ - SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment - operation, with SRC_DECL_* offset operand */ - SRC_PCDELTA = 7, /* distance forward from comma-operator to - next POP, or from CONDSWITCH to first CASE - opcode, etc. -- always a forward delta */ - SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ - SRC_ASSIGNOP = 8, /* += or another assign-op follows */ - SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ - SRC_BRACE = 10, /* mandatory brace, for scope or to avoid - dangling else */ - SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ - SRC_PCBASE = 12, /* distance back from annotated getprop or - setprop op to left-most obj.prop.subprop - bytecode -- always a backward delta */ - SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ - SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ - SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ - SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ - SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ - SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, - 2nd off to first JSOP_CASE if condswitch */ - SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ - SRC_CATCH = 20, /* catch block has guard */ - SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ - SRC_NEWLINE = 22, /* bytecode follows a source newline */ - SRC_SETLINE = 23, /* a file-absolute source line number note */ - SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ -} JSSrcNoteType; - -/* - * Constants for the SRC_DECL source note. Note that span-dependent bytecode - * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need - * to be adjusted, but these "offsets" are too small to span a span-dependent - * instruction, so can be used to denote distinct declaration syntaxes to the - * decompiler. - * - * NB: the var_prefix array in jsopcode.c depends on these dense indexes from - * SRC_DECL_VAR through SRC_DECL_LET. - */ -#define SRC_DECL_VAR 0 -#define SRC_DECL_CONST 1 -#define SRC_DECL_LET 2 -#define SRC_DECL_NONE 3 - -#define SN_TYPE_BITS 5 -#define SN_DELTA_BITS 3 -#define SN_XDELTA_BITS 6 -#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) -#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) -#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) - -#define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ - (((t) << SN_DELTA_BITS) \ - | ((d) & SN_DELTA_MASK))) -#define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ - ((SRC_XDELTA << SN_DELTA_BITS) \ - | ((d) & SN_XDELTA_MASK))) - -#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) -#define SN_TYPE(sn) ((JSSrcNoteType)(SN_IS_XDELTA(sn) \ - ? SRC_XDELTA \ - : *(sn) >> SN_DELTA_BITS)) -#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) -#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) - -#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ - ? *(sn) & SN_XDELTA_MASK \ - : *(sn) & SN_DELTA_MASK)) -#define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ - ? SN_MAKE_XDELTA(sn, delta) \ - : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) - -#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) -#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) - -/* - * Offset fields follow certain notes and are frequency-encoded: an offset in - * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and - * the high bit of the first byte is set. - */ -#define SN_3BYTE_OFFSET_FLAG 0x80 -#define SN_3BYTE_OFFSET_MASK 0x7f - -typedef struct JSSrcNoteSpec { - const char *name; /* name for disassembly/debugging output */ - int8 arity; /* number of offset operands */ - uint8 offsetBias; /* bias of offset(s) from annotated pc */ - int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, - 0 otherwise; sign tells span direction */ -} JSSrcNoteSpec; - -extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; -extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); - -#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ - : js_SrcNoteLength(sn)) -#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) - -/* A source note array is terminated by an all-zero element. */ -#define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) -#define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) - -/* - * Append a new source note of the given type (and therefore size) to cg's - * notes dynamic array, updating cg->noteCount. Return the new note's index - * within the array pointed at by cg->current->notes. Return -1 if out of - * memory. - */ -extern intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); - -extern intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset); - -extern intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2); - -/* - * NB: this function can add at most one extra extended delta note. - */ -extern jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta); - -/* - * Get and set the offset operand identified by which (0 for the first, etc.). - */ -extern JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which); - -extern JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset); - -/* - * Finish taking source notes in cx's notePool, copying final notes to the new - * stable store allocated by the caller and passed in via notes. Return false - * on malloc failure, which means this function reported an error. - * - * To compute the number of jssrcnotes to allocate and pass in via notes, use - * the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of - * js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes - * FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! - */ -#define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ - JS_BEGIN_MACRO \ - ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ - cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ - if ((cg)->prolog.noteCount && \ - (cg)->prolog.currentLine != (cg)->firstLine) { \ - if (diff_ > SN_DELTA_MASK) \ - cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ - cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ - } else if (diff_ > 0) { \ - if (cg->main.noteCount) { \ - jssrcnote *sn_ = (cg)->main.notes; \ - diff_ -= SN_IS_XDELTA(sn_) \ - ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ - : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ - } \ - if (diff_ > 0) \ - cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ - } \ - JS_END_MACRO - -extern JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); - -extern void -js_FinishTakingTryNotes(JSCodeGenerator *cg, JSTryNoteArray *array); - -JS_END_EXTERN_C - -#endif /* jsemit_h___ */ diff --git a/deps/mozjs/js/src/jsexn.cpp b/deps/mozjs/js/src/jsexn.cpp index efa9da82413..5ee3aaf47a9 100644 --- a/deps/mozjs/js/src/jsexn.cpp +++ b/deps/mozjs/js/src/jsexn.cpp @@ -43,9 +43,10 @@ */ #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" @@ -61,17 +62,23 @@ #include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" #include "jswrapper.h" +#include "vm/GlobalObject.h" + +#include "jsinferinlines.h" #include "jsobjinlines.h" +#include "jsstrinlines.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; -/* Forward declarations for js_ErrorClass's initializer. */ +/* Forward declarations for ErrorClass's initializer. */ static JSBool Exception(JSContext *cx, uintN argc, Value *vp); @@ -85,17 +92,17 @@ static JSBool exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp); -Class js_ErrorClass = { +Class js::ErrorClass = { js_Error_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Error), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, (JSResolveOp)exn_resolve, - ConvertStub, + JS_ConvertStub, exn_finalize, NULL, /* reserved0 */ NULL, /* checkAccess */ @@ -107,7 +114,7 @@ Class js_ErrorClass = { }; typedef struct JSStackTraceElem { - JSString *funName; + js::HeapPtrString funName; size_t argc; const char *filename; uintN ulineno; @@ -116,10 +123,11 @@ typedef struct JSStackTraceElem { typedef struct JSExnPrivate { /* A copy of the JSErrorReport originally generated. */ JSErrorReport *errorReport; - JSString *message; - JSString *filename; + js::HeapPtrString message; + js::HeapPtrString filename; uintN lineno; size_t stackDepth; + intN exnType; JSStackTraceElem stackElems[1]; } JSExnPrivate; @@ -152,7 +160,7 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) size_t i, argsArraySize, argsCopySize, argSize; size_t mallocSize; JSErrorReport *copy; - uint8 *cursor; + uint8_t *cursor; #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) @@ -180,7 +188,7 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) */ mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + ucmessageSize + uclinebufSize + linebufSize + filenameSize; - cursor = (uint8 *)cx->malloc_(mallocSize); + cursor = (uint8_t *)cx->malloc_(mallocSize); if (!cursor) return NULL; @@ -194,22 +202,22 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) for (i = 0; report->messageArgs[i]; ++i) { copy->messageArgs[i] = (const jschar *)cursor; argSize = JS_CHARS_SIZE(report->messageArgs[i]); - memcpy(cursor, report->messageArgs[i], argSize); + js_memcpy(cursor, report->messageArgs[i], argSize); cursor += argSize; } copy->messageArgs[i] = NULL; - JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); + JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize); } if (report->ucmessage) { copy->ucmessage = (const jschar *)cursor; - memcpy(cursor, report->ucmessage, ucmessageSize); + js_memcpy(cursor, report->ucmessage, ucmessageSize); cursor += ucmessageSize; } if (report->uclinebuf) { copy->uclinebuf = (const jschar *)cursor; - memcpy(cursor, report->uclinebuf, uclinebufSize); + js_memcpy(cursor, report->uclinebuf, uclinebufSize); cursor += uclinebufSize; if (report->uctokenptr) { copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - @@ -219,7 +227,7 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) if (report->linebuf) { copy->linebuf = (const char *)cursor; - memcpy(cursor, report->linebuf, linebufSize); + js_memcpy(cursor, report->linebuf, linebufSize); cursor += linebufSize; if (report->tokenptr) { copy->tokenptr = copy->linebuf + (report->tokenptr - @@ -229,9 +237,12 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) if (report->filename) { copy->filename = (const char *)cursor; - memcpy(cursor, report->filename, filenameSize); + js_memcpy(cursor, report->filename, filenameSize); } - JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); + JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize); + + /* HOLD called by the destination error object. */ + copy->originPrincipals = report->originPrincipals; /* Copy non-pointer members. */ copy->lineno = report->lineno; @@ -244,7 +255,7 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) return copy; } -static jsval * +static HeapValue * GetStackTraceValueBuffer(JSExnPrivate *priv) { /* @@ -255,114 +266,106 @@ GetStackTraceValueBuffer(JSExnPrivate *priv) */ JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); - return (jsval *)(priv->stackElems + priv->stackDepth); + return reinterpret_cast(priv->stackElems + priv->stackDepth); } -static JSBool +struct SuppressErrorsGuard +{ + JSContext *cx; + JSErrorReporter prevReporter; + JSExceptionState *prevState; + + SuppressErrorsGuard(JSContext *cx) + : cx(cx), + prevReporter(JS_SetErrorReporter(cx, NULL)), + prevState(JS_SaveExceptionState(cx)) + {} + + ~SuppressErrorsGuard() + { + JS_RestoreExceptionState(cx, prevState); + JS_SetErrorReporter(cx, prevReporter); + } +}; + +struct AppendArg { + Vector &values; + AppendArg(Vector &values) : values(values) {} + bool operator()(uintN, Value *vp) { + return values.append(*vp); + } +}; + +static void +SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv); + +static bool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, - JSString *filename, uintN lineno, JSErrorReport *report) + JSString *filename, uintN lineno, JSErrorReport *report, intN exnType) { - JSSecurityCallbacks *callbacks; - CheckAccessOp checkAccess; - JSErrorReporter older; - JSExceptionState *state; - jsid callerid; - StackFrame *fp, *fpstop; - size_t stackDepth, valueCount, size; - JSBool overflow; - JSExnPrivate *priv; - JSStackTraceElem *elem; - jsval *values; + JS_ASSERT(exnObject->isError()); + JS_ASSERT(!exnObject->getPrivate()); - JS_ASSERT(exnObject->getClass() == &js_ErrorClass); + JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); + JSCheckAccessOp checkAccess = callbacks ? callbacks->checkObjectAccess : NULL; - /* - * Prepare stack trace data. - * - * Set aside any error reporter for cx and save its exception state - * so we can suppress any checkAccess failures. Such failures should stop - * the backtrace procedure, not result in a failure of this constructor. - */ - callbacks = JS_GetSecurityCallbacks(cx); - checkAccess = callbacks - ? Valueify(callbacks->checkObjectAccess) - : NULL; - older = JS_SetErrorReporter(cx, NULL); - state = JS_SaveExceptionState(cx); - - callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom); - stackDepth = 0; - valueCount = 0; - for (fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) { - if (fp->compartment() != cx->compartment) - break; - if (fp->isNonEvalFunctionFrame()) { - Value v = NullValue(); - if (checkAccess && - !checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) { + Vector frames(cx); + Vector values(cx); + { + SuppressErrorsGuard seg(cx); + for (FrameRegsIter i(cx); !i.done(); ++i) { + /* + * An exception object stores stack values from 'fp' which may be + * in a different compartment from 'exnObject'. Engine compartment + * invariants require such values to be wrapped. A simpler solution + * is to just cut off the backtrace at compartment boundaries. + * Also, avoid exposing values from different security principals. + */ + StackFrame *fp = i.fp(); + if (fp->compartment() != cx->compartment) break; + if (checkAccess && fp->isNonEvalFunctionFrame()) { + Value v = NullValue(); + jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom); + if (!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) + break; + } + + if (!frames.growBy(1)) + return false; + JSStackTraceElem &frame = frames.back(); + if (fp->isNonEvalFunctionFrame()) { + frame.funName.init(fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString); + frame.argc = fp->numActualArgs(); + if (!fp->forEachCanonicalActualArg(AppendArg(values))) + return false; + } else { + frame.funName.init(NULL); + frame.argc = 0; + } + if (fp->isScriptFrame()) { + frame.filename = fp->script()->filename; + frame.ulineno = js_PCToLineNumber(cx, fp->script(), i.pc()); + } else { + frame.ulineno = 0; + frame.filename = NULL; } - valueCount += fp->numActualArgs(); } - ++stackDepth; } - JS_RestoreExceptionState(cx, state); - JS_SetErrorReporter(cx, older); - fpstop = fp; - - size = offsetof(JSExnPrivate, stackElems); - overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); - size += stackDepth * sizeof(JSStackTraceElem); - overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); - size += valueCount * sizeof(jsval); - if (overflow) { - js_ReportAllocationOverflow(cx); - return JS_FALSE; - } - priv = (JSExnPrivate *)cx->malloc_(size); - if (!priv) - return JS_FALSE; - /* - * We initialize errorReport with a copy of report after setting the - * private slot, to prevent GC accessing a junk value we clear the field - * here. - */ - priv->errorReport = NULL; - priv->message = message; - priv->filename = filename; - priv->lineno = lineno; - priv->stackDepth = stackDepth; + /* Do not need overflow check: the vm stack is already bigger. */ + JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame)); - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (fp = js_GetTopStackFrame(cx); fp != fpstop; fp = fp->prev()) { - if (fp->compartment() != cx->compartment) - break; - if (!fp->isFunctionFrame() || fp->isEvalFrame()) { - elem->funName = NULL; - elem->argc = 0; - } else { - elem->funName = fp->fun()->atom - ? fp->fun()->atom - : cx->runtime->emptyString; - elem->argc = fp->numActualArgs(); - fp->forEachCanonicalActualArg(CopyTo(Valueify(values))); - values += elem->argc; - } - elem->ulineno = 0; - elem->filename = NULL; - if (fp->isScriptFrame()) { - elem->filename = fp->script()->filename; - if (fp->pc(cx)) - elem->ulineno = js_FramePCToLineNumber(cx, fp); - } - ++elem; - } - JS_ASSERT(priv->stackElems + stackDepth == elem); - JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); + size_t nbytes = offsetof(JSExnPrivate, stackElems) + + frames.length() * sizeof(JSStackTraceElem) + + values.length() * sizeof(HeapValue); - exnObject->setPrivate(priv); + JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes); + if (!priv) + return false; + + /* Initialize to zero so that write barriers don't witness undefined values. */ + memset(priv, 0, nbytes); if (report) { /* @@ -373,17 +376,35 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { - /* The finalizer realeases priv since it is in the private slot. */ - return JS_FALSE; + cx->free_(priv); + return false; } + } else { + priv->errorReport = NULL; } - return JS_TRUE; + priv->message.init(message); + priv->filename.init(filename); + priv->lineno = lineno; + priv->stackDepth = frames.length(); + priv->exnType = exnType; + + JSStackTraceElem *framesDest = priv->stackElems; + HeapValue *valuesDest = reinterpret_cast(framesDest + frames.length()); + JS_ASSERT(valuesDest == GetStackTraceValueBuffer(priv)); + + PodCopy(framesDest, frames.begin(), frames.length()); + for (size_t i = 0; i < values.length(); ++i) + valuesDest[i].init(cx->compartment, values[i]); + + SetExnPrivate(cx, exnObject, priv); + return true; } static inline JSExnPrivate * -GetExnPrivate(JSContext *cx, JSObject *obj) +GetExnPrivate(JSObject *obj) { + JS_ASSERT(obj->isError()); return (JSExnPrivate *) obj->getPrivate(); } @@ -393,9 +414,9 @@ exn_trace(JSTracer *trc, JSObject *obj) JSExnPrivate *priv; JSStackTraceElem *elem; size_t vcount, i; - jsval *vp, v; + HeapValue *vp; - priv = GetExnPrivate(trc->context, obj); + priv = GetExnPrivate(obj); if (priv) { if (priv->message) MarkString(trc, priv->message, "exception message"); @@ -412,21 +433,34 @@ exn_trace(JSTracer *trc, JSObject *obj) } vp = GetStackTraceValueBuffer(priv); for (i = 0; i != vcount; ++i, ++vp) { - v = *vp; - JS_CALL_VALUE_TRACER(trc, v, "stack trace argument"); + MarkValue(trc, *vp, "stack trace argument"); } } } +/* NB: An error object's private must be set through this function. */ static void -exn_finalize(JSContext *cx, JSObject *obj) +SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv) { - JSExnPrivate *priv; + JS_ASSERT(!exnObject->getPrivate()); + JS_ASSERT(exnObject->isError()); + if (JSErrorReport *report = priv->errorReport) { + if (JSPrincipals *prin = report->originPrincipals) + JSPRINCIPALS_HOLD(cx, prin); + } + exnObject->setPrivate(priv); +} - priv = GetExnPrivate(cx, obj); - if (priv) { - if (priv->errorReport) - cx->free_(priv->errorReport); +static void +exn_finalize(JSContext *cx, JSObject *obj) +{ + if (JSExnPrivate *priv = GetExnPrivate(obj)) { + if (JSErrorReport *report = priv->errorReport) { + /* HOLD called by SetExnPrivate. */ + if (JSPrincipals *prin = report->originPrincipals) + JSPRINCIPALS_DROP(cx, prin); + cx->free_(report); + } cx->free_(priv); } } @@ -444,7 +478,7 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, uintN attrs; *objp = NULL; - priv = GetExnPrivate(cx, obj); + priv = GetExnPrivate(obj); if (priv && JSID_IS_ATOM(id)) { str = JSID_TO_STRING(id); @@ -487,8 +521,6 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!stack) return false; - /* Allow to GC all things that were used to build stack trace. */ - priv->stackDepth = 0; prop = js_stack_str; v = STRING_TO_JSVAL(stack); attrs = JSPROP_ENUMERATE; @@ -513,33 +545,34 @@ js_ErrorFromException(JSContext *cx, jsval exn) if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); - if (obj->getClass() != &js_ErrorClass) + if (!obj->isError()) return NULL; - priv = GetExnPrivate(cx, obj); + priv = GetExnPrivate(obj); if (!priv) return NULL; return priv->errorReport; } static JSString * -ValueToShortSource(JSContext *cx, jsval v) +ValueToShortSource(JSContext *cx, const Value &v) { JSString *str; /* Avoid toSource bloat and fallibility for object types. */ - if (JSVAL_IS_PRIMITIVE(v)) - return js_ValueToSource(cx, Valueify(v)); + if (!v.isObject()) + return js_ValueToSource(cx, v); - AutoCompartment ac(cx, JSVAL_TO_OBJECT(v)); + JSObject *obj = &v.toObject(); + AutoCompartment ac(cx, obj); if (!ac.enter()) return NULL; - if (VALUE_IS_FUNCTION(cx, v)) { + if (obj->isFunction()) { /* * XXX Avoid function decompilation bloat for now. */ - str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); - if (!str && !(str = js_ValueToSource(cx, Valueify(v)))) { + str = JS_GetFunctionId(obj->toFunction()); + if (!str && !(str = js_ValueToSource(cx, v))) { /* * Continue to soldier on if the function couldn't be * converted into a string. @@ -553,8 +586,7 @@ ValueToShortSource(JSContext *cx, jsval v) * memory, for too many classes (see Mozilla bug 166743). */ char buf[100]; - JS_snprintf(buf, sizeof buf, "[object %s]", - JSVAL_TO_OBJECT(v)->getClass()->name); + JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name); str = JS_NewStringCopyZ(cx, buf); } @@ -571,7 +603,7 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) jschar *stackbuf; size_t stacklen, stackmax; JSStackTraceElem *elem, *endElem; - jsval *values; + HeapValue *values; size_t i; JSString *str; const char *cp; @@ -613,8 +645,8 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) length_ >= STACK_LENGTH_LIMIT - stacklen) { \ goto done; \ } \ - stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ - ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar)); \ + stackmax = RoundUpPow2(stacklen + length_); \ + ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) \ goto bad; \ stackbuf = (jschar *) ptr_; \ @@ -692,8 +724,7 @@ FilenameToString(JSContext *cx, const char *filename) static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { - JSString *message, *filename; - StackFrame *fp; + CallArgs args = CallArgsFromVp(argc, vp); /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when @@ -702,52 +733,48 @@ Exception(JSContext *cx, uintN argc, Value *vp) * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ - JSObject &callee = vp[0].toObject(); Value protov; - if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) - return JS_FALSE; + if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) + return false; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); - return JS_FALSE; + return false; } JSObject *errProto = &protov.toObject(); - JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); + JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL); if (!obj) - return JS_FALSE; - - /* - * If it's a new object of class Exception, then null out the private - * data so that the finalizer doesn't attempt to free it. - */ - if (obj->getClass() == &js_ErrorClass) - obj->setPrivate(NULL); + return false; /* Set the 'message' property. */ - Value *argv = vp + 2; - if (argc != 0 && !argv[0].isUndefined()) { - message = js_ValueToString(cx, argv[0]); + JSString *message; + if (args.length() != 0 && !args[0].isUndefined()) { + message = ToString(cx, args[0]); if (!message) - return JS_FALSE; - argv[0].setString(message); + return false; + args[0].setString(message); } else { message = NULL; } + /* Find the scripted caller. */ + FrameRegsIter iter(cx); + while (!iter.done() && !iter.fp()->isScriptFrame()) + ++iter; + /* Set the 'fileName' property. */ - if (argc > 1) { - filename = js_ValueToString(cx, argv[1]); + JSString *filename; + if (args.length() > 1) { + filename = ToString(cx, args[1]); if (!filename) - return JS_FALSE; - argv[1].setString(filename); - fp = NULL; + return false; + args[1].setString(filename); } else { - fp = js_GetScriptedCaller(cx, NULL); - if (fp) { - filename = FilenameToString(cx, fp->script()->filename); + if (!iter.done()) { + filename = FilenameToString(cx, iter.fp()->script()->filename); if (!filename) - return JS_FALSE; + return false; } else { filename = cx->runtime->emptyString; } @@ -755,86 +782,95 @@ Exception(JSContext *cx, uintN argc, Value *vp) /* Set the 'lineNumber' property. */ uint32_t lineno; - if (argc > 2) { - if (!ValueToECMAUint32(cx, argv[2], &lineno)) - return JS_FALSE; + if (args.length() > 2) { + if (!ToUint32(cx, args[2], &lineno)) + return false; } else { - if (!fp) - fp = js_GetScriptedCaller(cx, NULL); - lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0; + lineno = iter.done() ? 0 : js_PCToLineNumber(cx, iter.fp()->script(), iter.pc()); } - if (obj->getClass() == &js_ErrorClass && - !InitExnPrivate(cx, obj, message, filename, lineno, NULL)) { - return JS_FALSE; - } + intN exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32(); + if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType)) + return false; - vp->setObject(*obj); - return JS_TRUE; + args.rval().setObject(*obj); + return true; } -/* - * Convert to string. - * - * This method only uses JavaScript-modifiable properties name, message. It - * is left to the host to check for private data and report filename and line - * number information along with this message. - */ +/* ES5 15.11.4.4 (NB: with subsequent errata). */ static JSBool exn_toString(JSContext *cx, uintN argc, Value *vp) { - jsval v; - JSString *name, *message, *result; - jschar *chars, *cp; - size_t name_length, message_length, length; + JS_CHECK_RECURSION(cx, return false); + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return JS_FALSE; - if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), Valueify(&v))) - return JS_FALSE; - name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; - vp->setString(name); + /* Step 2. */ + if (!args.thisv().isObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); + return false; + } - if (!JS_GetProperty(cx, obj, js_message_str, &v)) - return JS_FALSE; - message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) - : cx->runtime->emptyString; - - if (message->length() != 0) { - name_length = name->length(); - message_length = message->length(); - length = (name_length ? name_length + 2 : 0) + message_length; - cp = chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - if (name_length) { - const jschar *name_chars = name->getChars(cx); - if (!name_chars) - return JS_FALSE; - js_strncpy(cp, name_chars, name_length); - cp += name_length; - *cp++ = ':'; *cp++ = ' '; - } - const jschar *message_chars = message->getChars(cx); - if (!message_chars) - return JS_FALSE; - js_strncpy(cp, message_chars, message_length); - cp += message_length; - *cp = 0; - - result = js_NewString(cx, chars, length); - if (!result) { - cx->free_(chars); - return JS_FALSE; - } + /* Step 1. */ + JSObject &obj = args.thisv().toObject(); + + /* Step 3. */ + Value nameVal; + if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal)) + return false; + + /* Step 4. */ + JSString *name; + if (nameVal.isUndefined()) { + name = CLASS_ATOM(cx, Error); + } else { + name = ToString(cx, nameVal); + if (!name) + return false; + } + + /* Step 5. */ + Value msgVal; + if (!obj.getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal)) + return false; + + /* Step 6. */ + JSString *message; + if (msgVal.isUndefined()) { + message = cx->runtime->emptyString; } else { - result = name; + message = ToString(cx, msgVal); + if (!message) + return false; + } + + /* Step 7. */ + if (name->empty() && message->empty()) { + args.rval().setString(CLASS_ATOM(cx, Error)); + return true; + } + + /* Step 8. */ + if (name->empty()) { + args.rval().setString(message); + return true; } - vp->setString(result); - return JS_TRUE; + /* Step 9. */ + if (message->empty()) { + args.rval().setString(name); + return true; + } + + /* Step 10. */ + StringBuffer sb(cx); + if (!sb.append(name) || !sb.append(": ") || !sb.append(message)) + return false; + + JSString *str = sb.finishString(); + if (!str) + return false; + args.rval().setString(str); + return true; } #if JS_HAS_TOSOURCE @@ -844,134 +880,76 @@ exn_toString(JSContext *cx, uintN argc, Value *vp) static JSBool exn_toSource(JSContext *cx, uintN argc, Value *vp) { - JSString *name, *message, *filename, *lineno_as_str, *result; - jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; - size_t lineno_length, name_length, message_length, filename_length, length; - jschar *chars, *cp; + JS_CHECK_RECURSION(cx, return false); + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), vp)) - return false; - name = js_ValueToString(cx, *vp); - if (!name) - return false; - vp->setString(name); + Value nameVal; + JSString *name; + if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal) || + !(name = ToString(cx, nameVal))) { - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroots), Valueify(localroots)); + return false; + } -#ifdef __GNUC__ - message = filename = NULL; -#endif - if (!JS_GetProperty(cx, obj, js_message_str, &localroots[0]) || - !(message = js_ValueToSource(cx, Valueify(localroots[0])))) { - return false; - } - localroots[0] = STRING_TO_JSVAL(message); + Value messageVal; + JSString *message; + if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &messageVal) || + !(message = js_ValueToSource(cx, messageVal))) + { + return false; + } - if (!JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) || - !(filename = js_ValueToSource(cx, Valueify(localroots[1])))) { - return false; - } - localroots[1] = STRING_TO_JSVAL(filename); + Value filenameVal; + JSString *filename; + if (!obj->getProperty(cx, cx->runtime->atomState.fileNameAtom, &filenameVal) || + !(filename = js_ValueToSource(cx, filenameVal))) + { + return false; + } - if (!JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2])) - return false; - uint32_t lineno; - if (!ValueToECMAUint32(cx, Valueify(localroots[2]), &lineno)) - return false; + Value linenoVal; + uint32_t lineno; + if (!obj->getProperty(cx, cx->runtime->atomState.lineNumberAtom, &linenoVal) || + !ToUint32(cx, linenoVal, &lineno)) + { + return false; + } - if (lineno != 0) { - lineno_as_str = js_ValueToString(cx, Valueify(localroots[2])); - if (!lineno_as_str) - return false; - lineno_length = lineno_as_str->length(); - } else { - lineno_as_str = NULL; - lineno_length = 0; - } + StringBuffer sb(cx); + if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) + return false; - /* Magic 8, for the characters in ``(new ())''. */ - name_length = name->length(); - message_length = message->length(); - length = 8 + name_length + message_length; - - filename_length = filename->length(); - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - length += 2 + filename_length; - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - length += 2 + lineno_length; - } - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - length += 6 + lineno_length; - } - } + if (!sb.append(message)) + return false; - cp = chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar)); - if (!chars) + if (!filename->empty()) { + if (!sb.append(", ") || !sb.append(filename)) return false; + } + if (lineno != 0) { + /* We have a line, but no filename, add empty string */ + if (filename->empty() && !sb.append(", \"\"")) + return false; - *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; - const jschar *name_chars = name->getChars(cx); - if (!name_chars) + JSString *linenumber = ToString(cx, linenoVal); + if (!linenumber) return false; - js_strncpy(cp, name_chars, name_length); - cp += name_length; - *cp++ = '('; - const jschar *message_chars = message->getChars(cx); - if (!message_chars) + if (!sb.append(", ") || !sb.append(linenumber)) return false; - if (message_length != 0) { - js_strncpy(cp, message_chars, message_length); - cp += message_length; - } - - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - *cp++ = ','; *cp++ = ' '; - const jschar *filename_chars = filename->getChars(cx); - if (!filename_chars) - return false; - js_strncpy(cp, filename_chars, filename_length); - cp += filename_length; - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; - } - } - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - *cp++ = ','; *cp++ = ' '; - const jschar *lineno_chars = lineno_as_str->getChars(cx); - if (!lineno_chars) - return false; - js_strncpy(cp, lineno_chars, lineno_length); - cp += lineno_length; - } + } - *cp++ = ')'; *cp++ = ')'; *cp = 0; + if (!sb.append("))")) + return false; - result = js_NewString(cx, chars, length); - if (!result) { - cx->free_(chars); - return false; - } - vp->setString(result); - return true; - } + JSString *str = sb.finishString(); + if (!str) + return false; + args.rval().setString(str); + return true; } #endif @@ -993,73 +971,78 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError); -static JS_INLINE JSProtoKey -GetExceptionProtoKey(intN exn) -{ - JS_ASSERT(JSEXN_ERR <= exn); - JS_ASSERT(exn < JSEXN_LIMIT); - return (JSProtoKey) (JSProto_Error + exn); -} - -JSObject * -js_InitExceptionClasses(JSContext *cx, JSObject *obj) +static JSObject * +InitErrorClass(JSContext *cx, GlobalObject *global, intN type, JSObject &proto) { - /* - * If lazy class initialization occurs for any Error subclass, then all - * classes are initialized, starting with Error. To avoid reentry and - * redundant initialization, we must not pass a null proto parameter to - * NewNonFunction below, when called for the Error superclass. We need to - * ensure that Object.prototype is the proto of Error.prototype. - * - * See the equivalent code to ensure that parent_proto is non-null when - * js_InitClass calls NewObject, in jsobj.cpp. - */ - JSObject *obj_proto; - if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto)) + JSProtoKey key = GetExceptionProtoKey(type); + JSAtom *name = cx->runtime->atomState.classAtoms[key]; + JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto); + if (!errorProto) return NULL; - /* Define all error constructors. */ Value empty = StringValue(cx->runtime->emptyString); jsid nameId = ATOM_TO_JSID(cx->runtime->atomState.nameAtom); jsid messageId = ATOM_TO_JSID(cx->runtime->atomState.messageAtom); jsid fileNameId = ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom); jsid lineNumberId = ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom); - JSObject *error_proto = NULL; - for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) { - JSProtoKey protoKey = GetExceptionProtoKey(i); - JSAtom *atom = cx->runtime->atomState.classAtoms[protoKey]; - JSObject *proto = - DefineConstructorAndPrototype(cx, obj, protoKey, atom, - (i == JSEXN_ERR) ? obj_proto : error_proto, - &js_ErrorClass, Exception, 1, - NULL, (i == JSEXN_ERR) ? exception_methods : NULL, - NULL, NULL); - if (!proto) - return NULL; - JS_ASSERT(proto->privateData == NULL); - - if (i == JSEXN_ERR) - error_proto = proto; - - /* Add properties to the prototype. */ - JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); - if (!js_DefineNativeProperty(cx, proto, nameId, StringValue(atom), - PropertyStub, StrictPropertyStub, - 0, 0, 0, NULL) || - !js_DefineNativeProperty(cx, proto, messageId, empty, - PropertyStub, StrictPropertyStub, - 0, 0, 0, NULL) || - !js_DefineNativeProperty(cx, proto, fileNameId, empty, - PropertyStub, StrictPropertyStub, - JSPROP_ENUMERATE, 0, 0, NULL) || - !js_DefineNativeProperty(cx, proto, lineNumberId, Valueify(JSVAL_ZERO), - PropertyStub, StrictPropertyStub, - JSPROP_ENUMERATE, 0, 0, NULL)) { + if (!DefineNativeProperty(cx, errorProto, nameId, StringValue(name), + JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) || + !DefineNativeProperty(cx, errorProto, messageId, empty, + JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) || + !DefineNativeProperty(cx, errorProto, fileNameId, empty, + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) || + !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) + { + return NULL; + } + + /* Create the corresponding constructor. */ + JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1, + JSFunction::ExtendedFinalizeKind); + if (!ctor) + return NULL; + ctor->setExtendedSlot(0, Int32Value(int32_t(type))); + + if (!LinkConstructorAndPrototype(cx, ctor, errorProto)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto)) + return NULL; + + JS_ASSERT(!errorProto->getPrivate()); + + return errorProto; +} + +JSObject * +js_InitExceptionClasses(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj->isGlobal()); + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *objectProto = global->getOrCreateObjectPrototype(cx); + if (!objectProto) + return NULL; + + /* Initialize the base Error class first. */ + JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto); + if (!errorProto) + return NULL; + + /* |Error.prototype| alone has method properties. */ + if (!DefinePropertiesAndBrand(cx, errorProto, NULL, exception_methods)) + return NULL; + + /* Define all remaining *Error constructors. */ + for (intN i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) { + if (!InitErrorClass(cx, global, i, *errorProto)) return NULL; - } } - return error_proto; + return errorProto; } const JSErrorFormatString* @@ -1143,7 +1126,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, /* Protect the newly-created strings below from nesting GCs. */ PodArrayZero(tv); - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(tv), Valueify(tv)); + AutoArrayRooter tvr(cx, ArrayLength(tv), tv); /* * Try to get an appropriate prototype by looking up the corresponding @@ -1155,7 +1138,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, goto out; tv[0] = OBJECT_TO_JSVAL(errProto); - errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); + errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL); if (!errObject) { ok = JS_FALSE; goto out; @@ -1177,7 +1160,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, tv[3] = STRING_TO_JSVAL(filenameStr); ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, - reportp->lineno, reportp); + reportp->lineno, reportp, exn); if (!ok) goto out; @@ -1208,13 +1191,13 @@ js_ReportUncaughtException(JSContext *cx) return false; PodArrayZero(roots); - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots)); + AutoArrayRooter tvr(cx, ArrayLength(roots), roots); /* - * Because js_ValueToString below could error and an exception object - * could become unrooted, we must root exnObject. Later, if exnObject is - * non-null, we need to root other intermediates, so allocate an operand - * stack segment to protect all of these values. + * Because ToString below could error and an exception object could become + * unrooted, we must root exnObject. Later, if exnObject is non-null, we + * need to root other intermediates, so allocate an operand stack segment + * to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; @@ -1226,20 +1209,20 @@ js_ReportUncaughtException(JSContext *cx) JS_ClearPendingException(cx); reportp = js_ErrorFromException(cx, exn); - /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ - str = js_ValueToString(cx, Valueify(exn)); + /* XXX L10N angels cry once again. see also everywhere else */ + str = ToString(cx, exn); JSAutoByteString bytesStorage; if (!str) { bytes = "unknown (can't convert to string)"; } else { - roots[1] = STRING_TO_JSVAL(str); + roots[1] = StringValue(str); if (!bytesStorage.encode(cx, str)) return false; bytes = bytesStorage.ptr(); } JSAutoByteString filename; - if (!reportp && exnObject && exnObject->getClass() == &js_ErrorClass) { + if (!reportp && exnObject && exnObject->isError()) { if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2])) return false; if (JSVAL_IS_STRING(roots[2])) { @@ -1251,14 +1234,14 @@ js_ReportUncaughtException(JSContext *cx) if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3])) return false; - str = js_ValueToString(cx, Valueify(roots[3])); + str = ToString(cx, roots[3]); if (!str || !filename.encode(cx, str)) return false; if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4])) return false; uint32_t lineno; - if (!ValueToECMAUint32(cx, Valueify(roots[4]), &lineno)) + if (!ToUint32(cx, roots[4], &lineno)) return false; reportp = &report; @@ -1288,3 +1271,63 @@ js_ReportUncaughtException(JSContext *cx) return true; } + +extern JSObject * +js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope) +{ + assertSameCompartment(cx, scope); + JSExnPrivate *priv = GetExnPrivate(errobj); + + uint32_t stackDepth = priv->stackDepth; + size_t valueCount = 0; + for (uint32_t i = 0; i < stackDepth; i++) + valueCount += priv->stackElems[i].argc; + + size_t size = offsetof(JSExnPrivate, stackElems) + + stackDepth * sizeof(JSStackTraceElem) + + valueCount * sizeof(jsval); + + JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size); + if (!copy) + return NULL; + + struct AutoFree { + JSContext *cx; + JSExnPrivate *p; + ~AutoFree() { + if (p) { + cx->free_(p->errorReport); + cx->free_(p); + } + } + } autoFree = {cx, copy}; + + // Copy each field. Don't bother copying the stack elements. + if (priv->errorReport) { + copy->errorReport = CopyErrorReport(cx, priv->errorReport); + if (!copy->errorReport) + return NULL; + } else { + copy->errorReport = NULL; + } + copy->message.init(priv->message); + if (!cx->compartment->wrap(cx, ©->message)) + return NULL; + JS::Anchor messageAnchor(copy->message); + copy->filename.init(priv->filename); + if (!cx->compartment->wrap(cx, ©->filename)) + return NULL; + JS::Anchor filenameAnchor(copy->filename); + copy->lineno = priv->lineno; + copy->stackDepth = 0; + copy->exnType = priv->exnType; + + // Create the Error object. + JSObject *proto = scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType); + if (!proto) + return NULL; + JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL); + SetExnPrivate(cx, copyobj, copy); + autoFree.p = NULL; + return copyobj; +} diff --git a/deps/mozjs/js/src/jsexn.h b/deps/mozjs/js/src/jsexn.h index 1d08b34aa4e..4e47d3062d3 100644 --- a/deps/mozjs/js/src/jsexn.h +++ b/deps/mozjs/js/src/jsexn.h @@ -44,7 +44,7 @@ #ifndef jsexn_h___ #define jsexn_h___ -extern js::Class js_ErrorClass; +#include "jsobj.h" /* * Initialize the exception constructor/prototype hierarchy. @@ -90,4 +90,23 @@ extern const JSErrorFormatString * js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber); +/* + * Make a copy of errobj parented to scope. + * + * cx must be in the same compartment as scope. errobj may be in a different + * compartment, but it must be an Error object (not a wrapper of one) and it + * must not be one of the prototype objects created by js_InitExceptionClasses + * (errobj->getPrivate() must not be NULL). + */ +extern JSObject * +js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope); + +static JS_INLINE JSProtoKey +GetExceptionProtoKey(intN exn) +{ + JS_ASSERT(JSEXN_ERR <= exn); + JS_ASSERT(exn < JSEXN_LIMIT); + return JSProtoKey(JSProto_Error + exn); +} + #endif /* jsexn_h___ */ diff --git a/deps/mozjs/js/src/jsfriendapi.cpp b/deps/mozjs/js/src/jsfriendapi.cpp index 902d4362672..93d98bc54ed 100644 --- a/deps/mozjs/js/src/jsfriendapi.cpp +++ b/deps/mozjs/js/src/jsfriendapi.cpp @@ -37,16 +37,32 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/GuardObjects.h" +#include "mozilla/StdInt.h" + #include "jscntxt.h" #include "jscompartment.h" #include "jsfriendapi.h" +#include "jswrapper.h" +#include "jsweakmap.h" +#include "jswatchpoint.h" + +#include "jsobjinlines.h" using namespace js; +using namespace JS; + +JS_FRIEND_API(void) +JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) +{ + rt->gcGrayRootsTraceOp = traceOp; + rt->gcGrayRootsData = data; +} JS_FRIEND_API(JSString *) JS_GetAnonymousString(JSRuntime *rt) { - JS_ASSERT(rt->state == JSRTS_UP); + JS_ASSERT(rt->hasContexts()); return rt->atomState.anonymousAtom; } @@ -58,7 +74,7 @@ JS_FindCompilationScope(JSContext *cx, JSObject *obj) * asked of us. */ if (obj->isWrapper()) - obj = obj->unwrap(); + obj = UnwrapObject(obj); /* * Innerize the target_obj so that we compile in the correct (inner) @@ -69,14 +85,638 @@ JS_FindCompilationScope(JSContext *cx, JSObject *obj) return obj; } +JS_FRIEND_API(JSFunction *) +JS_GetObjectFunction(JSObject *obj) +{ + if (obj->isFunction()) + return obj->toFunction(); + return NULL; +} + JS_FRIEND_API(JSObject *) -JS_UnwrapObject(JSObject *obj) +JS_GetGlobalForFrame(JSStackFrame *fp) { - return obj->unwrap(); + return &Valueify(fp)->scopeChain().global(); +} + +JS_FRIEND_API(JSBool) +JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto) +{ + /* + * Change the prototype of an object which hasn't been used anywhere + * and does not share its type with another object. Unlike JS_SetPrototype, + * does not nuke type information for the object. + */ + CHECK_REQUEST(cx); + + if (!obj->hasSingletonType()) { + /* + * We can see non-singleton objects when trying to splice prototypes + * due to mutable __proto__ (ugh). + */ + return JS_SetPrototype(cx, obj, proto); + } + + return obj->splicePrototype(cx, proto); } JS_FRIEND_API(JSObject *) -JS_GetFrameScopeChainRaw(JSStackFrame *fp) +JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) +{ + JSObject *obj = JS_NewObject(cx, clasp, proto, parent); + if (!obj || !obj->setSingletonType(cx)) + return NULL; + return obj; +} + +JS_FRIEND_API(void) +js::GCForReason(JSContext *cx, gcreason::Reason reason) +{ + js_GC(cx, NULL, GC_NORMAL, reason); +} + +JS_FRIEND_API(void) +js::CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reason) +{ + /* We cannot GC the atoms compartment alone; use a full GC instead. */ + JS_ASSERT(comp != cx->runtime->atomsCompartment); + + js_GC(cx, comp, GC_NORMAL, reason); +} + +JS_FRIEND_API(void) +js::ShrinkingGC(JSContext *cx, gcreason::Reason reason) +{ + js_GC(cx, NULL, GC_SHRINK, reason); +} + +JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt) +{ + ShrinkGCBuffers(rt); +} + +JS_FRIEND_API(JSPrincipals *) +JS_GetCompartmentPrincipals(JSCompartment *compartment) +{ + return compartment->principals; +} + +JS_FRIEND_API(JSBool) +JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc) +{ + return cx->compartment->wrap(cx, desc); +} + +JS_FRIEND_API(void) +JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape) +{ + MarkCycleCollectorChildren(trc, (const Shape *)shape); +} + +AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; +} + +AutoPreserveCompartment::~AutoPreserveCompartment() +{ + /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */ + cx->compartment = oldCompartment; +} + +AutoSwitchCompartment::AutoSwitchCompartment(JSContext *cx, JSCompartment *newCompartment + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->setCompartment(newCompartment); +} + +AutoSwitchCompartment::AutoSwitchCompartment(JSContext *cx, JSObject *target + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->setCompartment(target->compartment()); +} + +AutoSwitchCompartment::~AutoSwitchCompartment() +{ + /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */ + cx->compartment = oldCompartment; +} + +JS_FRIEND_API(bool) +js::IsSystemCompartment(const JSCompartment *c) +{ + return c->isSystemCompartment; +} + +JS_FRIEND_API(bool) +js::IsAtomsCompartmentFor(const JSContext *cx, const JSCompartment *c) +{ + return c == cx->runtime->atomsCompartment; +} + +JS_FRIEND_API(bool) +js::IsScopeObject(JSObject *obj) +{ + return obj->isScope(); +} + +JS_FRIEND_API(JSObject *) +js::GetObjectParentMaybeScope(JSObject *obj) +{ + return obj->enclosingScope(); +} + +JS_FRIEND_API(JSObject *) +js::GetGlobalForObjectCrossCompartment(JSObject *obj) +{ + return &obj->global(); +} + +JS_FRIEND_API(uint32_t) +js::GetObjectSlotSpan(JSObject *obj) +{ + return obj->slotSpan(); +} + +JS_FRIEND_API(bool) +js::IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx) +{ + return obj->compartment() == cx->compartment; +} + +JS_FRIEND_API(bool) +js::IsOriginalScriptFunction(JSFunction *fun) +{ + return fun->script()->function() == fun; +} + +JS_FRIEND_API(JSFunction *) +js::DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs) +{ + RootObject objRoot(cx, &obj); + + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + JSAtom *atom = js_Atomize(cx, name, strlen(name)); + if (!atom) + return NULL; + return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs, + JSFunction::ExtendedFinalizeKind); +} + +JS_FRIEND_API(JSFunction *) +js::NewFunctionWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags, + JSObject *parent, const char *name) +{ + RootObject parentRoot(cx, &parent); + + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + JSAtom *atom; + + CHECK_REQUEST(cx); + assertSameCompartment(cx, parent); + + if (!name) { + atom = NULL; + } else { + atom = js_Atomize(cx, name, strlen(name)); + if (!atom) + return NULL; + } + + return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, atom, + JSFunction::ExtendedFinalizeKind); +} + +JS_FRIEND_API(JSFunction *) +js::NewFunctionByIdWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent, + jsid id) +{ + RootObject parentRoot(cx, &parent); + + JS_ASSERT(JSID_IS_STRING(id)); + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + CHECK_REQUEST(cx); + assertSameCompartment(cx, parent); + + return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, JSID_TO_ATOM(id), + JSFunction::ExtendedFinalizeKind); +} + +JS_FRIEND_API(JSObject *) +js::InitClassWithReserved(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs) +{ + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj, parent_proto); + RootObject objRoot(cx, &obj); + return js_InitClass(cx, objRoot, parent_proto, Valueify(clasp), constructor, + nargs, ps, fs, static_ps, static_fs, NULL, + JSFunction::ExtendedFinalizeKind); +} + +JS_FRIEND_API(const Value &) +js::GetFunctionNativeReserved(JSObject *fun, size_t which) +{ + JS_ASSERT(fun->toFunction()->isNative()); + return fun->toFunction()->getExtendedSlot(which); +} + +JS_FRIEND_API(void) +js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val) +{ + JS_ASSERT(fun->toFunction()->isNative()); + fun->toFunction()->setExtendedSlot(which, val); +} + +JS_FRIEND_API(void) +js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value) +{ + obj->setSlot(slot, value); +} + +void +js::SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback) +{ + rt->preserveWrapperCallback = callback; +} + +/* + * The below code is for temporary telemetry use. It can be removed when + * sufficient data has been harvested. + */ + +extern size_t sE4XObjectsCreated; + +JS_FRIEND_API(size_t) +JS_GetE4XObjectsCreated(JSContext *) +{ + return sE4XObjectsCreated; +} + +extern size_t sSetProtoCalled; + +JS_FRIEND_API(size_t) +JS_SetProtoCalled(JSContext *) +{ + return sSetProtoCalled; +} + +extern size_t sCustomIteratorCount; + +JS_FRIEND_API(size_t) +JS_GetCustomIteratorCount(JSContext *cx) +{ + return sCustomIteratorCount; +} + +void +js::TraceWeakMaps(WeakMapTracer *trc) +{ + WeakMapBase::traceAllMappings(trc); + WatchpointMap::traceAll(trc); +} + +JS_FRIEND_API(bool) +js::GCThingIsMarkedGray(void *thing) +{ + JS_ASSERT(thing); + return reinterpret_cast(thing)->isMarked(gc::GRAY); +} + +JS_FRIEND_API(void) +JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) +{ + rt->telemetryCallback = callback; +} + +JS_FRIEND_API(void) +JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback) +{ + rt->gcFinishedCallback = callback; +} + +#ifdef DEBUG +JS_FRIEND_API(void) +js_DumpString(JSString *str) +{ + str->dump(); +} + +JS_FRIEND_API(void) +js_DumpAtom(JSAtom *atom) +{ + atom->dump(); +} + +extern void +DumpChars(const jschar *s, size_t n) +{ + if (n == SIZE_MAX) { + n = 0; + while (s[n]) + n++; + } + + fputc('"', stderr); + for (size_t i = 0; i < n; i++) { + if (s[i] == '\n') + fprintf(stderr, "\\n"); + else if (s[i] == '\t') + fprintf(stderr, "\\t"); + else if (s[i] >= 32 && s[i] < 127) + fputc(s[i], stderr); + else if (s[i] <= 255) + fprintf(stderr, "\\x%02x", (unsigned int) s[i]); + else + fprintf(stderr, "\\u%04x", (unsigned int) s[i]); + } + fputc('"', stderr); +} + +JS_FRIEND_API(void) +js_DumpChars(const jschar *s, size_t n) { - return &Valueify(fp)->scopeChain(); + fprintf(stderr, "jschar * (%p) = ", (void *) s); + DumpChars(s, n); + fputc('\n', stderr); } + +JS_FRIEND_API(void) +js_DumpObject(JSObject *obj) +{ + obj->dump(); +} + +struct DumpingChildInfo { + void *node; + JSGCTraceKind kind; + + DumpingChildInfo (void *n, JSGCTraceKind k) + : node(n), kind(k) + {} +}; + +typedef HashSet, ContextAllocPolicy> PtrSet; + +struct JSDumpHeapTracer : public JSTracer { + PtrSet visited; + FILE *output; + Vector nodes; + char buffer[200]; + bool rootTracing; + + JSDumpHeapTracer(JSContext *cx, FILE *fp) + : visited(cx), output(fp), nodes(cx) + {} +}; + +static void +DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind); + +static void +DumpHeapPushIfNew(JSTracer *trc, void **thingp, JSGCTraceKind kind) +{ + JS_ASSERT(trc->callback == DumpHeapPushIfNew || + trc->callback == DumpHeapVisitChild); + void *thing = *thingp; + JSDumpHeapTracer *dtrc = static_cast(trc); + + /* + * If we're tracing roots, print root information. Do this even if we've + * already seen thing, for complete root information. + */ + if (dtrc->rootTracing) { + fprintf(dtrc->output, "%p %s\n", thing, + JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer))); + } + + PtrSet::AddPtr ptrEntry = dtrc->visited.lookupForAdd(thing); + if (ptrEntry || !dtrc->visited.add(ptrEntry, thing)) + return; + + dtrc->nodes.append(DumpingChildInfo(thing, kind)); +} + +static void +DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) +{ + JS_ASSERT(trc->callback == DumpHeapVisitChild); + JSDumpHeapTracer *dtrc = static_cast(trc); + const char *edgeName = JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer)); + fprintf(dtrc->output, "> %p %s\n", *thingp, edgeName); + DumpHeapPushIfNew(dtrc, thingp, kind); +} + +void +js::DumpHeapComplete(JSContext *cx, FILE *fp) +{ + JSDumpHeapTracer dtrc(cx, fp); + JS_TracerInit(&dtrc, cx, DumpHeapPushIfNew); + if (!dtrc.visited.init(10000)) + return; + + /* Store and log the root information. */ + dtrc.rootTracing = true; + TraceRuntime(&dtrc); + fprintf(dtrc.output, "==========\n"); + + /* Log the graph. */ + dtrc.rootTracing = false; + dtrc.callback = DumpHeapVisitChild; + + while (!dtrc.nodes.empty()) { + DumpingChildInfo dci = dtrc.nodes.popCopy(); + JS_PrintTraceThingInfo(dtrc.buffer, sizeof(dtrc.buffer), + &dtrc, dci.node, dci.kind, JS_TRUE); + fprintf(fp, "%p %s\n", dci.node, dtrc.buffer); + JS_TraceChildren(&dtrc, dci.node, dci.kind); + } + + dtrc.visited.finish(); +} + +#endif + +namespace js { + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt) +{ + return !!rt->gcIncrementalTracer && !rt->gcRunning; +} + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx) +{ + return IsIncrementalBarrierNeeded(cx->runtime); +} + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr) +{ + if (!ptr) + return; + JS_ASSERT(!static_cast(ptr)->compartment()->rt->gcRunning); + uint32_t kind = gc::GetGCThingTraceKind(ptr); + if (kind == JSTRACE_OBJECT) + JSObject::writeBarrierPre((JSObject *) ptr); + else if (kind == JSTRACE_STRING) + JSString::writeBarrierPre((JSString *) ptr); + else + JS_NOT_REACHED("invalid trace kind"); +} + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v) +{ + HeapValue::writeBarrierPre(v); +} + +/* static */ void +AutoLockGC::LockGC(JSRuntime *rt) +{ + JS_ASSERT(rt); + JS_LOCK_GC(rt); +} + +/* static */ void +AutoLockGC::UnlockGC(JSRuntime *rt) +{ + JS_ASSERT(rt); + JS_UNLOCK_GC(rt); +} + +void +AutoLockGC::lock(JSRuntime *rt) +{ + JS_ASSERT(rt); + JS_ASSERT(!runtime); + runtime = rt; + JS_LOCK_GC(rt); +} + +JS_FRIEND_API(const JSStructuredCloneCallbacks *) +GetContextStructuredCloneCallbacks(JSContext *cx) +{ + return cx->runtime->structuredCloneCallbacks; +} + +JS_FRIEND_API(JSVersion) +VersionSetXML(JSVersion version, bool enable) +{ + return enable ? JSVersion(uint32_t(version) | VersionFlags::HAS_XML) + : JSVersion(uint32_t(version) & ~VersionFlags::HAS_XML); +} + +JS_FRIEND_API(bool) +CanCallContextDebugHandler(JSContext *cx) +{ + return cx->debugHooks && cx->debugHooks->debuggerHandler; +} + +JS_FRIEND_API(JSTrapStatus) +CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval) +{ + if (!CanCallContextDebugHandler(cx)) + return JSTRAP_RETURN; + + return cx->debugHooks->debuggerHandler(cx, script, bc, rval, + cx->debugHooks->debuggerHandlerData); +} + +#ifdef JS_THREADSAFE +void * +GetOwnerThread(const JSContext *cx) +{ + return cx->runtime->ownerThread(); +} + +JS_FRIEND_API(unsigned) +GetContextOutstandingRequests(const JSContext *cx) +{ + return cx->outstandingRequests; +} + +JS_FRIEND_API(PRLock *) +GetRuntimeGCLock(const JSRuntime *rt) +{ + return rt->gcLock; +} + +AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) + : context(cx) +{ + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + JSRuntime *rt = context->runtime; + JS_ASSERT(rt->requestDepth >= 1); + JS_ASSERT(!rt->conservativeGC.requestThreshold); + if (rt->requestDepth == 1) + rt->conservativeGC.requestThreshold = 1; +} + +AutoSkipConservativeScan::~AutoSkipConservativeScan() +{ + JSRuntime *rt = context->runtime; + if (rt->requestDepth == 1) + rt->conservativeGC.requestThreshold = 0; +} +#endif + +JS_FRIEND_API(JSCompartment *) +GetContextCompartment(const JSContext *cx) +{ + return cx->compartment; +} + +JS_FRIEND_API(bool) +HasUnrootedGlobal(const JSContext *cx) +{ + return cx->hasRunOption(JSOPTION_UNROOTED_GLOBAL); +} + +JS_FRIEND_API(void) +SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg) +{ + rt->activityCallback = cb; + rt->activityCallbackArg = arg; +} + +JS_FRIEND_API(bool) +IsContextRunningJS(JSContext *cx) +{ + return !cx->stack.empty(); +} + +JS_FRIEND_API(void) +TriggerOperationCallback(JSRuntime *rt) +{ + rt->triggerOperationCallback(); +} + +JS_FRIEND_API(const CompartmentVector&) +GetRuntimeCompartments(JSRuntime *rt) +{ + return rt->compartments; +} + +JS_FRIEND_API(size_t) +SizeOfJSContext() +{ + return sizeof(JSContext); +} + +} // namespace js diff --git a/deps/mozjs/js/src/jsfriendapi.h b/deps/mozjs/js/src/jsfriendapi.h index a2179e5983f..495b4006817 100644 --- a/deps/mozjs/js/src/jsfriendapi.h +++ b/deps/mozjs/js/src/jsfriendapi.h @@ -40,23 +40,755 @@ #ifndef jsfriendapi_h___ #define jsfriendapi_h___ +#include "jsclass.h" #include "jspubtd.h" #include "jsprvtd.h" +#include "mozilla/GuardObjects.h" + JS_BEGIN_EXTERN_C +extern JS_FRIEND_API(void) +JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); + extern JS_FRIEND_API(JSString *) JS_GetAnonymousString(JSRuntime *rt); extern JS_FRIEND_API(JSObject *) JS_FindCompilationScope(JSContext *cx, JSObject *obj); +extern JS_FRIEND_API(JSFunction *) +JS_GetObjectFunction(JSObject *obj); + +extern JS_FRIEND_API(JSObject *) +JS_GetGlobalForFrame(JSStackFrame *fp); + +extern JS_FRIEND_API(JSBool) +JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto); + +extern JS_FRIEND_API(JSObject *) +JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); + +extern JS_FRIEND_API(uint32_t) +JS_ObjectCountDynamicSlots(JSObject *obj); + +extern JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt); + +extern JS_FRIEND_API(size_t) +JS_GetE4XObjectsCreated(JSContext *cx); + +extern JS_FRIEND_API(size_t) +JS_SetProtoCalled(JSContext *cx); + +extern JS_FRIEND_API(size_t) +JS_GetCustomIteratorCount(JSContext *cx); + +extern JS_FRIEND_API(JSBool) +JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret); + +/* + * Used by the cycle collector to trace through the shape and all + * shapes it reaches, marking all non-shape children found in the + * process. Uses bounded stack space. + */ +extern JS_FRIEND_API(void) +JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape); + +enum { + JS_TELEMETRY_GC_REASON, + JS_TELEMETRY_GC_IS_COMPARTMENTAL, + JS_TELEMETRY_GC_MS, + JS_TELEMETRY_GC_MARK_MS, + JS_TELEMETRY_GC_SWEEP_MS +}; + +typedef void +(* JSAccumulateTelemetryDataCallback)(int id, uint32_t sample); + +extern JS_FRIEND_API(void) +JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); + +typedef void +(* JSGCFinishedCallback)(JSRuntime *rt, JSCompartment *comp, const char *description); + +extern JS_FRIEND_API(void) +JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback); + +extern JS_FRIEND_API(JSPrincipals *) +JS_GetCompartmentPrincipals(JSCompartment *compartment); + +/* Safe to call with input obj == NULL. Returns non-NULL iff obj != NULL. */ extern JS_FRIEND_API(JSObject *) -JS_UnwrapObject(JSObject *obj); +JS_ObjectToInnerObject(JSContext *cx, JSObject *obj); +/* Requires obj != NULL. */ extern JS_FRIEND_API(JSObject *) -JS_GetFrameScopeChainRaw(JSStackFrame *fp); +JS_ObjectToOuterObject(JSContext *cx, JSObject *obj); + +extern JS_FRIEND_API(JSObject *) +JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent); + +extern JS_FRIEND_API(JSBool) +js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp); + +JS_FRIEND_API(void) +js_ReportOverRecursed(JSContext *maybecx); + +#ifdef DEBUG + +/* + * Routines to print out values during debugging. These are FRIEND_API to help + * the debugger find them and to support temporarily hacking js_Dump* calls + * into other code. + */ + +extern JS_FRIEND_API(void) +js_DumpString(JSString *str); + +extern JS_FRIEND_API(void) +js_DumpAtom(JSAtom *atom); + +extern JS_FRIEND_API(void) +js_DumpObject(JSObject *obj); + +extern JS_FRIEND_API(void) +js_DumpChars(const jschar *s, size_t n); +#endif + +#ifdef __cplusplus + +extern JS_FRIEND_API(bool) +JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj); + +extern JS_FRIEND_API(JSBool) +JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc); + +extern JS_FRIEND_API(JSBool) +JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, js::Value *statep, jsid *idp); + +#endif JS_END_EXTERN_C +#ifdef __cplusplus + +struct PRLock; + +namespace js { + +struct ContextFriendFields { + JSRuntime *const runtime; + + ContextFriendFields(JSRuntime *rt) + : runtime(rt) { } + + static const ContextFriendFields *get(const JSContext *cx) { + return reinterpret_cast(cx); + } +}; + +struct RuntimeFriendFields { + /* + * If non-zero, we were been asked to call the operation callback as soon + * as possible. + */ + volatile int32_t interrupt; + + /* Limit pointer for checking native stack consumption. */ + uintptr_t nativeStackLimit; + + RuntimeFriendFields() + : interrupt(0), + nativeStackLimit(0) { } + + static const RuntimeFriendFields *get(const JSRuntime *rt) { + return reinterpret_cast(rt); + } +}; + +inline JSRuntime * +GetRuntime(const JSContext *cx) +{ + return ContextFriendFields::get(cx)->runtime; +} + +typedef bool +(* PreserveWrapperCallback)(JSContext *cx, JSObject *obj); + +#ifdef DEBUG + /* + * DEBUG-only method to dump the complete object graph of heap-allocated things. + * fp is the file for the dump output. + */ +extern JS_FRIEND_API(void) +DumpHeapComplete(JSContext *cx, FILE *fp); + +#endif + +class JS_FRIEND_API(AutoPreserveCompartment) { + private: + JSContext *cx; + JSCompartment *oldCompartment; + public: + AutoPreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoPreserveCompartment(); + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class JS_FRIEND_API(AutoSwitchCompartment) { + private: + JSContext *cx; + JSCompartment *oldCompartment; + public: + AutoSwitchCompartment(JSContext *cx, JSCompartment *newCompartment + JS_GUARD_OBJECT_NOTIFIER_PARAM); + AutoSwitchCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoSwitchCompartment(); + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +#ifdef OLD_GETTER_SETTER_METHODS +JS_FRIEND_API(JSBool) obj_defineGetter(JSContext *cx, uintN argc, js::Value *vp); +JS_FRIEND_API(JSBool) obj_defineSetter(JSContext *cx, uintN argc, js::Value *vp); +#endif + +extern JS_FRIEND_API(bool) +IsSystemCompartment(const JSCompartment *compartment); + +extern JS_FRIEND_API(bool) +IsAtomsCompartmentFor(const JSContext *cx, const JSCompartment *c); + +/* + * Check whether it is OK to assign an undeclared property with name + * propname of the global object in the current script on cx. Reports + * an error if one needs to be reported (in particular in all cases + * when it returns false). + */ +extern JS_FRIEND_API(bool) +CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname); + +struct WeakMapTracer; + +/* + * Weak map tracer callback, called once for every binding of every + * weak map that was live at the time of the last garbage collection. + * + * m will be NULL if the weak map is not contained in a JS Object. + */ +typedef void +(* WeakMapTraceCallback)(WeakMapTracer *trc, JSObject *m, + void *k, JSGCTraceKind kkind, + void *v, JSGCTraceKind vkind); + +struct WeakMapTracer { + JSRuntime *runtime; + WeakMapTraceCallback callback; + + WeakMapTracer(JSRuntime *rt, WeakMapTraceCallback cb) + : runtime(rt), callback(cb) {} +}; + +extern JS_FRIEND_API(void) +TraceWeakMaps(WeakMapTracer *trc); + +extern JS_FRIEND_API(bool) +GCThingIsMarkedGray(void *thing); + + +/* + * Shadow declarations of JS internal structures, for access by inline access + * functions below. Do not use these structures in any other way. When adding + * new fields for access by inline methods, make sure to add static asserts to + * the original header file to ensure that offsets are consistent. + */ +namespace shadow { + +struct TypeObject { + JSObject *proto; +}; + +struct BaseShape { + js::Class *clasp; + JSObject *parent; +}; + +struct Shape { + BaseShape *base; + jsid _1; + uint32_t slotInfo; + + static const uint32_t FIXED_SLOTS_SHIFT = 27; +}; + +struct Object { + Shape *shape; + TypeObject *type; + js::Value *slots; + js::Value *_1; + + size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; } + Value *fixedSlots() const { + return (Value *)(uintptr_t(this) + sizeof(shadow::Object)); + } + + js::Value &slotRef(size_t slot) const { + size_t nfixed = numFixedSlots(); + if (slot < nfixed) + return fixedSlots()[slot]; + return slots[slot - nfixed]; + } +}; + +struct Atom { + size_t _; + const jschar *chars; +}; + +} /* namespace shadow */ + +extern JS_FRIEND_DATA(js::Class) AnyNameClass; +extern JS_FRIEND_DATA(js::Class) AttributeNameClass; +extern JS_FRIEND_DATA(js::Class) CallClass; +extern JS_FRIEND_DATA(js::Class) DeclEnvClass; +extern JS_FRIEND_DATA(js::Class) FunctionClass; +extern JS_FRIEND_DATA(js::Class) FunctionProxyClass; +extern JS_FRIEND_DATA(js::Class) NamespaceClass; +extern JS_FRIEND_DATA(js::Class) OuterWindowProxyClass; +extern JS_FRIEND_DATA(js::Class) ObjectProxyClass; +extern JS_FRIEND_DATA(js::Class) QNameClass; +extern JS_FRIEND_DATA(js::Class) XMLClass; +extern JS_FRIEND_DATA(js::Class) ObjectClass; + +inline js::Class * +GetObjectClass(const JSObject *obj) +{ + return reinterpret_cast(obj)->shape->base->clasp; +} + +inline JSClass * +GetObjectJSClass(const JSObject *obj) +{ + return js::Jsvalify(GetObjectClass(obj)); +} + +JS_FRIEND_API(bool) +IsScopeObject(JSObject *obj); + +inline JSObject * +GetObjectParent(JSObject *obj) +{ + JS_ASSERT(!IsScopeObject(obj)); + return reinterpret_cast(obj)->shape->base->parent; +} + +JS_FRIEND_API(JSObject *) +GetObjectParentMaybeScope(JSObject *obj); + +JS_FRIEND_API(JSObject *) +GetGlobalForObjectCrossCompartment(JSObject *obj); + +JS_FRIEND_API(bool) +IsOriginalScriptFunction(JSFunction *fun); + +JS_FRIEND_API(JSFunction *) +DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs); + +JS_FRIEND_API(JSFunction *) +NewFunctionWithReserved(JSContext *cx, JSNative call, uintN nargs, uintN flags, + JSObject *parent, const char *name); + +JS_FRIEND_API(JSFunction *) +NewFunctionByIdWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags, + JSObject *parent, jsid id); + +JS_FRIEND_API(JSObject *) +InitClassWithReserved(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs); + +JS_FRIEND_API(const Value &) +GetFunctionNativeReserved(JSObject *fun, size_t which); + +JS_FRIEND_API(void) +SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val); + +inline JSObject * +GetObjectProto(JSObject *obj) +{ + return reinterpret_cast(obj)->type->proto; +} + +inline void * +GetObjectPrivate(JSObject *obj) +{ + const shadow::Object *nobj = reinterpret_cast(obj); + void **addr = reinterpret_cast(&nobj->fixedSlots()[nobj->numFixedSlots()]); + return *addr; +} + +/* + * Get a slot that is both reserved for object's clasp *and* is fixed (fits + * within the maximum capacity for the object's fixed slots). + */ +inline const Value & +GetReservedSlot(const JSObject *obj, size_t slot) +{ + JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); + return reinterpret_cast(obj)->slotRef(slot); +} + +JS_FRIEND_API(void) +SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const Value &value); + +inline void +SetReservedSlot(JSObject *obj, size_t slot, const Value &value) +{ + JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); + shadow::Object *sobj = reinterpret_cast(obj); + if (sobj->slotRef(slot).isMarkable()) + SetReservedSlotWithBarrier(obj, slot, value); + else + sobj->slotRef(slot) = value; +} + +JS_FRIEND_API(uint32_t) +GetObjectSlotSpan(JSObject *obj); + +inline const Value & +GetObjectSlot(JSObject *obj, size_t slot) +{ + JS_ASSERT(slot < GetObjectSlotSpan(obj)); + return reinterpret_cast(obj)->slotRef(slot); +} + +inline Shape * +GetObjectShape(JSObject *obj) +{ + shadow::Shape *shape = reinterpret_cast(obj)->shape; + return reinterpret_cast(shape); +} + +inline const jschar * +GetAtomChars(JSAtom *atom) +{ + return reinterpret_cast(atom)->chars; +} + +inline JSLinearString * +AtomToLinearString(JSAtom *atom) +{ + return reinterpret_cast(atom); +} + +static inline js::PropertyOp +CastAsJSPropertyOp(JSObject *object) +{ + return JS_DATA_TO_FUNC_PTR(js::PropertyOp, object); +} + +static inline js::StrictPropertyOp +CastAsJSStrictPropertyOp(JSObject *object) +{ + return JS_DATA_TO_FUNC_PTR(js::StrictPropertyOp, object); +} + +JS_FRIEND_API(bool) +GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector *props); + +JS_FRIEND_API(bool) +StringIsArrayIndex(JSLinearString *str, jsuint *indexp); + +JS_FRIEND_API(void) +SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback); + +JS_FRIEND_API(bool) +IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx); + +/* + * NB: these flag bits are encoded into the bytecode stream in the immediate + * operand of JSOP_ITER, so don't change them without advancing jsxdrapi.h's + * JSXDR_BYTECODE_VERSION. + */ +#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ +#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ +#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ +#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */ +#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ +#define JSITER_FOR_OF 0x20 /* harmony for-of loop */ + +inline uintptr_t +GetContextStackLimit(const JSContext *cx) +{ + return RuntimeFriendFields::get(GetRuntime(cx))->nativeStackLimit; +} + +#define JS_CHECK_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE(js::GetContextStackLimit(cx), &stackDummy_)) { \ + js_ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +JS_FRIEND_API(void) +StartPCCountProfiling(JSContext *cx); + +JS_FRIEND_API(void) +StopPCCountProfiling(JSContext *cx); + +JS_FRIEND_API(void) +PurgePCCounts(JSContext *cx); + +JS_FRIEND_API(size_t) +GetPCCountScriptCount(JSContext *cx); + +JS_FRIEND_API(JSString *) +GetPCCountScriptSummary(JSContext *cx, size_t script); + +JS_FRIEND_API(JSString *) +GetPCCountScriptContents(JSContext *cx, size_t script); + +#ifdef JS_THREADSAFE +JS_FRIEND_API(void *) +GetOwnerThread(const JSContext *cx); + +JS_FRIEND_API(unsigned) +GetContextOutstandingRequests(const JSContext *cx); + +JS_FRIEND_API(PRLock *) +GetRuntimeGCLock(const JSRuntime *rt); + +class JS_FRIEND_API(AutoSkipConservativeScan) +{ + public: + AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoSkipConservativeScan(); + + private: + JSContext *context; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; +#endif + +JS_FRIEND_API(JSCompartment *) +GetContextCompartment(const JSContext *cx); + +JS_FRIEND_API(bool) +HasUnrootedGlobal(const JSContext *cx); + +typedef void +(* ActivityCallback)(void *arg, JSBool active); + +/* + * Sets a callback that is run whenever the runtime goes idle - the + * last active request ceases - and begins activity - when it was + * idle and a request begins. Note: The callback is called under the + * GC lock. + */ +JS_FRIEND_API(void) +SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg); + +class JS_FRIEND_API(AutoLockGC) +{ + public: + explicit AutoLockGC(JSRuntime *rt = NULL + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : runtime(rt) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (rt) + LockGC(rt); + } + + ~AutoLockGC() + { + if (runtime) + UnlockGC(runtime); + } + + bool locked() const { + return !!runtime; + } + void lock(JSRuntime *rt); + + private: + static void LockGC(JSRuntime *rt); + static void UnlockGC(JSRuntime *rt); + + JSRuntime *runtime; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +extern JS_FRIEND_API(const JSStructuredCloneCallbacks *) +GetContextStructuredCloneCallbacks(JSContext *cx); + +extern JS_FRIEND_API(JSVersion) +VersionSetXML(JSVersion version, bool enable); + +extern JS_FRIEND_API(bool) +CanCallContextDebugHandler(JSContext *cx); + +extern JS_FRIEND_API(JSTrapStatus) +CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval); + +extern JS_FRIEND_API(bool) +IsContextRunningJS(JSContext *cx); + +/* Must be called with GC lock taken. */ +extern JS_FRIEND_API(void) +TriggerOperationCallback(JSRuntime *rt); + +class SystemAllocPolicy; +typedef Vector CompartmentVector; +extern JS_FRIEND_API(const CompartmentVector&) +GetRuntimeCompartments(JSRuntime *rt); + +extern JS_FRIEND_API(size_t) +SizeOfJSContext(); + +#define GCREASONS(D) \ + /* Reasons internal to the JS engine */ \ + D(API) \ + D(MAYBEGC) \ + D(LAST_CONTEXT) \ + D(DESTROY_CONTEXT) \ + D(LAST_DITCH) \ + D(TOO_MUCH_MALLOC) \ + D(ALLOC_TRIGGER) \ + D(UNUSED1) /* was CHUNK */ \ + D(UNUSED2) /* was SHAPE */ \ + D(UNUSED3) /* was REFILL */ \ + \ + /* Reasons from Firefox */ \ + D(DOM_WINDOW_UTILS) \ + D(COMPONENT_UTILS) \ + D(MEM_PRESSURE) \ + D(CC_WAITING) \ + D(CC_FORCED) \ + D(LOAD_END) \ + D(POST_COMPARTMENT) \ + D(PAGE_HIDE) \ + D(NSJSCONTEXT_DESTROY) \ + D(SET_NEW_DOCUMENT) \ + D(SET_DOC_SHELL) \ + D(DOM_UTILS) \ + D(DOM_IPC) \ + D(DOM_WORKER) \ + D(INTER_SLICE_GC) \ + D(REFRESH_FRAME) + +namespace gcreason { + +/* GCReasons will end up looking like JSGC_MAYBEGC */ +enum Reason { +#define MAKE_REASON(name) name, + GCREASONS(MAKE_REASON) +#undef MAKE_REASON + NO_REASON, + NUM_REASONS +}; + +} /* namespace gcreason */ + +extern JS_FRIEND_API(void) +GCForReason(JSContext *cx, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +ShrinkingGC(JSContext *cx, gcreason::Reason reason); + +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx); + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr); + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v); + +class ObjectPtr +{ + JSObject *value; + + public: + ObjectPtr() : value(NULL) {} + + ObjectPtr(JSObject *obj) : value(obj) {} + + /* Always call finalize before the destructor. */ + ~ObjectPtr() { JS_ASSERT(!value); } + + void finalize(JSRuntime *rt) { + if (IsIncrementalBarrierNeeded(rt)) + IncrementalReferenceBarrier(value); + value = NULL; + } + void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); } + + void init(JSObject *obj) { value = obj; } + + JSObject *get() const { return value; } + + void writeBarrierPre(JSRuntime *rt) { + IncrementalReferenceBarrier(value); + } + + ObjectPtr &operator=(JSObject *obj) { + IncrementalReferenceBarrier(value); + value = obj; + return *this; + } + + JSObject &operator*() const { return *value; } + JSObject *operator->() const { return value; } + operator JSObject *() const { return value; } +}; + +} /* namespace js */ + +#endif + +/* Implemented in jsdate.cpp. */ + +/* + * Detect whether the internal date value is NaN. (Because failure is + * out-of-band for js_DateGet*) + */ +extern JS_FRIEND_API(JSBool) +js_DateIsValid(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(double) +js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); + +/* Implemented in jscntxt.cpp. */ + +/* + * Report an exception, which is currently realized as a printf-style format + * string and its arguments. + */ +typedef enum JSErrNum { +#define MSG_DEF(name, number, count, exception, format) \ + name = number, +#include "js.msg" +#undef MSG_DEF + JSErr_Limit +} JSErrNum; + +extern JS_FRIEND_API(const JSErrorFormatString *) +js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); + +/* Implemented in jsclone.cpp. */ + +extern JS_FRIEND_API(uint64_t) +js_GetSCOffset(JSStructuredCloneWriter* writer); + #endif /* jsfriendapi_h___ */ diff --git a/deps/mozjs/js/src/jsfun.cpp b/deps/mozjs/js/src/jsfun.cpp index cf3f44c517d..1c57cb9b42a 100644 --- a/deps/mozjs/js/src/jsfun.cpp +++ b/deps/mozjs/js/src/jsfun.cpp @@ -42,18 +42,17 @@ * JS function support. */ #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" #include "jsutil.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" -#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" @@ -62,16 +61,18 @@ #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsparse.h" #include "jspropertytree.h" #include "jsproxy.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsexn.h" -#include "jsstaticcheck.h" -#include "jstracer.h" + +#include "frontend/BytecodeCompiler.h" +#include "frontend/BytecodeEmitter.h" +#include "frontend/TokenStream.h" +#include "vm/ScopeObject.h" +#include "vm/Debugger.h" #if JS_HAS_GENERATORS # include "jsiter.h" @@ -87,29 +88,31 @@ #include "jsatominlines.h" #include "jsfuninlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" - +#include "vm/ScopeObject-inl.h" +#include "vm/ArgumentsObject-inl.h" #include "vm/Stack-inl.h" +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; inline JSObject * JSObject::getThrowTypeError() const { - return getGlobal()->getThrowTypeError(); + return global().getThrowTypeError(); } JSBool js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp) { JSObject *argsobj; - if (fp->hasOverriddenArgs()) { JS_ASSERT(fp->hasCallObj()); - jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); - return fp->callObj().getProperty(cx, id, vp); + return fp->callObj().getProperty(cx, cx->runtime->atomState.argumentsAtom, vp); } argsobj = js_GetArgsObject(cx, fp); if (!argsobj) @@ -118,115 +121,67 @@ js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp) return JS_TRUE; } -JSBool -js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp) +js::ArgumentsObject * +ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee) { - JS_ASSERT(fp->isFunctionFrame()); + JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX); - if (fp->hasOverriddenArgs()) { - JS_ASSERT(fp->hasCallObj()); - - jsid argumentsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); - Value v; - if (!fp->callObj().getProperty(cx, argumentsid, &v)) - return false; + JSObject *proto = callee.global().getOrCreateObjectPrototype(cx); + if (!proto) + return NULL; - JSObject *obj; - if (v.isPrimitive()) { - obj = js_ValueToNonNullObject(cx, v); - if (!obj) - return false; - } else { - obj = &v.toObject(); - } - return obj->getProperty(cx, id, vp); - } + RootedVarTypeObject type(cx); - vp->setUndefined(); - if (JSID_IS_INT(id)) { - uint32 arg = uint32(JSID_TO_INT(id)); - JSObject *argsobj = fp->maybeArgsObj(); - if (arg < fp->numActualArgs()) { - if (argsobj) { - const Value &v = argsobj->getArgsElement(arg); - if (v.isMagic(JS_ARGS_HOLE)) - return argsobj->getProperty(cx, id, vp); - if (fp->functionScript()->strictModeCode) { - *vp = v; - return true; - } - } - *vp = fp->canonicalActualArg(arg); - } else { - /* - * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share - * storage between the formal parameter and arguments[k] for all - * fp->argc <= k && k < fp->fun->nargs. For example, in - * - * function f(x) { x = 42; return arguments[0]; } - * f(); - * - * the call to f should return undefined, not 42. If fp->argsobj - * is null at this point, as it would be in the example, return - * undefined in *vp. - */ - if (argsobj) - return argsobj->getProperty(cx, id, vp); - } - } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - JSObject *argsobj = fp->maybeArgsObj(); - if (argsobj && argsobj->isArgsLengthOverridden()) - return argsobj->getProperty(cx, id, vp); - vp->setInt32(fp->numActualArgs()); - } - return true; -} - -static JSObject * -NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) -{ - JSObject *proto; - if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) + type = proto->getNewType(cx); + if (!type) return NULL; - JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2); - JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2); - if (!argsobj) - return NULL; + bool strict = callee.toFunction()->inStrictMode(); + Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass; - EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx); + RootedVarShape emptyArgumentsShape(cx); + emptyArgumentsShape = + EmptyShape::getInitialShape(cx, clasp, proto, + proto->getParent(), FINALIZE_KIND, + BaseShape::INDEXED); if (!emptyArgumentsShape) return NULL; - AutoShapeRooter shapeRoot(cx, emptyArgumentsShape); ArgumentsData *data = (ArgumentsData *) cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value)); if (!data) return NULL; - SetValueRangeToUndefined(data->slots, argc); - /* Can't fail from here on, so initialize everything in argsobj. */ - argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode() - ? &StrictArgumentsClass - : &js_ArgumentsClass, - proto, parent, NULL, false); + data->callee.init(ObjectValue(callee)); + InitValueRange(data->slots, argc, false); - argsobj->setMap(emptyArgumentsShape); + /* We have everything needed to fill in the object, so make the object. */ + JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL); + if (!obj) + return NULL; - argsobj->setArgsLength(argc); - argsobj->setArgsData(data); - data->callee.setObject(callee); + ArgumentsObject &argsobj = obj->asArguments(); - return argsobj; + JS_ASSERT(UINT32_MAX > (uint64_t(argc) << PACKED_BITS_COUNT)); + argsobj.initInitialLength(argc); + argsobj.initData(data); + argsobj.setStackFrame(NULL); + + JS_ASSERT(argsobj.numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS); + JS_ASSERT(argsobj.numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS); + + return &argsobj; } struct STATIC_SKIP_INFERENCE PutArg { - PutArg(Value *dst) : dst(dst) {} - Value *dst; + PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {} + HeapValue *dst; + JSCompartment *compartment; bool operator()(uintN, Value *src) { + JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined()); if (!dst->isMagic(JS_ARGS_HOLE)) - *dst = *src; + dst->set(compartment, *src); ++dst; return true; } @@ -236,21 +191,26 @@ JSObject * js_GetArgsObject(JSContext *cx, StackFrame *fp) { /* - * We must be in a function activation; the function must be lightweight - * or else fp must have a variable object. + * Arguments and Call objects are owned by the enclosing non-eval function + * frame, thus any eval frames must be skipped before testing hasArgsObj. */ - JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj()); - - while (fp->isEvalOrDebuggerFrame()) + JS_ASSERT(fp->isFunctionFrame()); + while (fp->isEvalInFunction()) fp = fp->prev(); + /* + * Mark all functions which have ever had arguments objects constructed, + * which will prevent lazy arguments optimizations in the method JIT. + */ + if (!fp->script()->createdArgs) + types::MarkArgumentsCreated(cx, fp->script()); + /* Create an arguments object for fp only if it lacks one. */ + JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj()); if (fp->hasArgsObj()) return &fp->argsObj(); - /* Compute the arguments object's parent slot from fp's scope chain. */ - JSObject *global = fp->scopeChain().getGlobal(); - JSObject *argsobj = NewArguments(cx, global, fp->numActualArgs(), fp->callee()); + ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee()); if (!argsobj) return argsobj; @@ -264,9 +224,9 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp) * retrieve up-to-date parameter values. */ if (argsobj->isStrictArguments()) - fp->forEachCanonicalActualArg(PutArg(argsobj->getArgsData()->slots)); + fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots)); else - argsobj->setPrivate(fp); + argsobj->setStackFrame(fp); fp->setArgsObj(*argsobj); return argsobj; @@ -275,284 +235,61 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp) void js_PutArgsObject(StackFrame *fp) { - JSObject &argsobj = fp->argsObj(); + ArgumentsObject &argsobj = fp->argsObj(); if (argsobj.isNormalArguments()) { - JS_ASSERT(argsobj.getPrivate() == fp); - fp->forEachCanonicalActualArg(PutArg(argsobj.getArgsData()->slots)); - argsobj.setPrivate(NULL); - } else { - JS_ASSERT(!argsobj.getPrivate()); - } -} - -#ifdef JS_TRACER - -/* - * Traced versions of js_GetArgsObject and js_PutArgsObject. - */ -JSObject * JS_FASTCALL -js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee) -{ - JSObject *argsobj = NewArguments(cx, parent, argc, *callee); - if (!argsobj) - return NULL; - - if (argsobj->isStrictArguments()) { - /* - * Strict mode callers must copy arguments into the created arguments - * object. The trace-JITting code is in TraceRecorder::newArguments. - */ - JS_ASSERT(!argsobj->getPrivate()); + JS_ASSERT(argsobj.maybeStackFrame() == fp); + JSCompartment *comp = fp->scopeChain().compartment(); + fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots)); + argsobj.setStackFrame(NULL); } else { - argsobj->setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE); - } - - return argsobj; -} -JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, OBJECT, UINT32, OBJECT, - 0, nanojit::ACCSET_STORE_ANY) - -/* FIXME change the return type to void. */ -JSBool JS_FASTCALL -js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args) -{ - JS_ASSERT(argsobj->isNormalArguments()); - JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE); - - /* - * TraceRecorder::putActivationObjects builds a single, contiguous array of - * the arguments, regardless of whether #actuals > #formals so there is no - * need to worry about actual vs. formal arguments. - */ - Value *srcend = args + argsobj->getArgsInitialLength(); - Value *dst = argsobj->getArgsData()->slots; - for (Value *src = args; src != srcend; ++src, ++dst) { - if (!dst->isMagic(JS_ARGS_HOLE)) - *dst = *src; + JS_ASSERT(!argsobj.maybeStackFrame()); } - - argsobj->setPrivate(NULL); - return true; } -JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALUEPTR, 0, - nanojit::ACCSET_STORE_ANY) - -#endif /* JS_TRACER */ static JSBool args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - JS_ASSERT(obj->isArguments()); - + ArgumentsObject &argsobj = obj->asArguments(); if (JSID_IS_INT(id)) { uintN arg = uintN(JSID_TO_INT(id)); - if (arg < obj->getArgsInitialLength()) - obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE)); + if (arg < argsobj.initialLength()) + argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE)); } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - obj->setArgsLengthOverridden(); + argsobj.markLengthOverridden(); } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) { - obj->setArgsCallee(MagicValue(JS_ARGS_HOLE)); + argsobj.asNormalArguments().clearCallee(); } return true; } -static JS_REQUIRES_STACK JSObject * -WrapEscapingClosure(JSContext *cx, StackFrame *fp, JSFunction *fun) -{ - JS_ASSERT(fun->optimizedClosure()); - JS_ASSERT(!fun->u.i.wrapper); - - /* - * We do not attempt to reify Call and Block objects on demand for outer - * scopes. This could be done (see the "v8" patch in bug 494235) but it is - * fragile in the face of ongoing compile-time optimization. Instead, the - * _DBG* opcodes used by wrappers created here must cope with unresolved - * upvars and throw them as reference errors. Caveat debuggers! - */ - JSObject *scopeChain = GetScopeChain(cx, fp); - if (!scopeChain) - return NULL; - - JSObject *wfunobj = NewFunction(cx, scopeChain); - if (!wfunobj) - return NULL; - AutoObjectRooter tvr(cx, wfunobj); - - JSFunction *wfun = (JSFunction *) wfunobj; - wfunobj->setPrivate(wfun); - wfun->nargs = fun->nargs; - wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT; - wfun->u.i.skipmin = fun->u.i.skipmin; - wfun->u.i.wrapper = true; - wfun->u.i.script = NULL; - wfun->atom = fun->atom; - - JSScript *script = fun->script(); - jssrcnote *snbase = script->notes(); - jssrcnote *sn = snbase; - while (!SN_IS_TERMINATOR(sn)) - sn = SN_NEXT(sn); - uintN nsrcnotes = (sn - snbase) + 1; - - /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */ - JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes, - script->atomMap.length, - JSScript::isValidOffset(script->objectsOffset) - ? script->objects()->length - : 0, - script->bindings.countUpvars(), - JSScript::isValidOffset(script->regexpsOffset) - ? script->regexps()->length - : 0, - JSScript::isValidOffset(script->trynotesOffset) - ? script->trynotes()->length - : 0, - JSScript::isValidOffset(script->constOffset) - ? script->consts()->length - : 0, - JSScript::isValidOffset(script->globalsOffset) - ? script->globals()->length - : 0, - script->nClosedArgs, - script->nClosedVars, - script->getVersion()); - if (!wscript) - return NULL; - - memcpy(wscript->code, script->code, script->length); - wscript->main = wscript->code + (script->main - script->code); - - memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote)); - memcpy(wscript->atomMap.vector, script->atomMap.vector, - wscript->atomMap.length * sizeof(JSAtom *)); - if (JSScript::isValidOffset(script->objectsOffset)) { - memcpy(wscript->objects()->vector, script->objects()->vector, - wscript->objects()->length * sizeof(JSObject *)); - } - if (JSScript::isValidOffset(script->regexpsOffset)) { - memcpy(wscript->regexps()->vector, script->regexps()->vector, - wscript->regexps()->length * sizeof(JSObject *)); - } - if (JSScript::isValidOffset(script->trynotesOffset)) { - memcpy(wscript->trynotes()->vector, script->trynotes()->vector, - wscript->trynotes()->length * sizeof(JSTryNote)); - } - if (JSScript::isValidOffset(script->globalsOffset)) { - memcpy(wscript->globals()->vector, script->globals()->vector, - wscript->globals()->length * sizeof(GlobalSlotArray::Entry)); - } - if (script->nClosedArgs + script->nClosedVars != 0) - script->copyClosedSlotsTo(wscript); - - if (script->bindings.hasUpvars()) { - JS_ASSERT(script->bindings.countUpvars() == wscript->upvars()->length); - memcpy(wscript->upvars()->vector, script->upvars()->vector, - script->bindings.countUpvars() * sizeof(uint32)); - } - - jsbytecode *pc = wscript->code; - while (*pc != JSOP_STOP) { - /* FIXME should copy JSOP_TRAP? */ - JSOp op = js_GetOpcode(cx, wscript, pc); - const JSCodeSpec *cs = &js_CodeSpec[op]; - ptrdiff_t oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(pc); - - /* - * Rewrite JSOP_{GET,CALL}FCSLOT as JSOP_{GET,CALL}UPVAR_DBG for the - * case where fun is an escaping flat closure. This works because the - * UPVAR and FCSLOT ops by design have the same format: an upvar index - * immediate operand. - */ - switch (op) { - case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break; - case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break; - case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break; - case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break; - case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break; - default:; - } - pc += oplen; - } - - /* - * Fill in the rest of wscript. This means if you add members to JSScript - * you must update this code. FIXME: factor into JSScript::clone method. - */ - JS_ASSERT(wscript->getVersion() == script->getVersion()); - wscript->nfixed = script->nfixed; - wscript->filename = script->filename; - wscript->lineno = script->lineno; - wscript->nslots = script->nslots; - wscript->staticLevel = script->staticLevel; - wscript->principals = script->principals; - wscript->noScriptRval = script->noScriptRval; - wscript->savedCallerFun = script->savedCallerFun; - wscript->hasSharps = script->hasSharps; - wscript->strictModeCode = script->strictModeCode; - wscript->compileAndGo = script->compileAndGo; - wscript->usesEval = script->usesEval; - wscript->usesArguments = script->usesArguments; - wscript->warnedAboutTwoArgumentEval = script->warnedAboutTwoArgumentEval; - if (wscript->principals) - JSPRINCIPALS_HOLD(cx, wscript->principals); -#ifdef CHECK_SCRIPT_OWNER - wscript->owner = script->owner; -#endif - - wscript->bindings.clone(cx, &script->bindings); - - /* 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; -} - static JSBool ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - LeaveTrace(cx); - if (!obj->isNormalArguments()) return true; + NormalArgumentsObject &argsobj = obj->asNormalArguments(); if (JSID_IS_INT(id)) { /* * arg can exceed the number of arguments if a script changed the * prototype to point to another Arguments object with a bigger argc. */ uintN arg = uintN(JSID_TO_INT(id)); - if (arg < obj->getArgsInitialLength()) { - JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)); - if (StackFrame *fp = (StackFrame *) obj->getPrivate()) + if (arg < argsobj.initialLength()) { + JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE)); + if (StackFrame *fp = argsobj.maybeStackFrame()) *vp = fp->canonicalActualArg(arg); else - *vp = obj->getArgsElement(arg); + *vp = argsobj.element(arg); } } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (!obj->isArgsLengthOverridden()) - vp->setInt32(obj->getArgsInitialLength()); + if (!argsobj.hasOverriddenLength()) + vp->setInt32(argsobj.initialLength()); } else { JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)); - const Value &v = obj->getArgsCallee(); - if (!v.isMagic(JS_ARGS_HOLE)) { - /* - * If this function or one in it needs upvars that reach above it - * in the scope chain, it must not be a null closure (it could be a - * flat closure, or an unoptimized closure -- the latter itself not - * necessarily heavyweight). Rather than wrap here, we simply throw - * to reduce code size and tell debugger users the truth instead of - * passing off a fibbing wrapper. - */ - if (GET_FUNCTION_PRIVATE(cx, &v.toObject())->needsWrapper()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OPTIMIZED_CLOSURE_LEAK); - return false; - } + const Value &v = argsobj.callee(); + if (!v.isMagic(JS_ARGS_HOLE)) *vp = v; - } } return true; } @@ -560,26 +297,21 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) static JSBool ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { -#ifdef JS_TRACER - // To be able to set a property here on trace, we would have to make - // sure any updates also get written back to the trace native stack. - // For simplicity, we just leave trace, since this is presumably not - // a common operation. - LeaveTrace(cx); -#endif - - if (!obj->isNormalArguments()) return true; + NormalArgumentsObject &argsobj = obj->asNormalArguments(); + if (JSID_IS_INT(id)) { uintN arg = uintN(JSID_TO_INT(id)); - if (arg < obj->getArgsInitialLength()) { - StackFrame *fp = (StackFrame *) obj->getPrivate(); - if (fp) { + if (arg < argsobj.initialLength()) { + if (StackFrame *fp = argsobj.maybeStackFrame()) { JSScript *script = fp->functionScript(); - if (script->usesArguments) + if (script->usesArguments) { + if (arg < fp->numFormalArgs()) + TypeScript::SetArgument(cx, script, arg, *vp); fp->canonicalActualArg(arg) = *vp; + } return true; } } @@ -597,54 +329,54 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) * that has a setter for this id. */ AutoValueRooter tvr(cx); - return js_DeleteProperty(cx, obj, id, tvr.addr(), false) && - js_DefineProperty(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE); + return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), false) && + js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE); } static JSBool args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { - JS_ASSERT(obj->isNormalArguments()); - *objp = NULL; + NormalArgumentsObject &argsobj = obj->asNormalArguments(); + uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; if (JSID_IS_INT(id)) { - uint32 arg = uint32(JSID_TO_INT(id)); - if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) + uint32_t arg = uint32_t(JSID_TO_INT(id)); + if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE)) return true; attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (obj->isArgsLengthOverridden()) + if (argsobj.hasOverriddenLength()) return true; } else { if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) return true; - if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE)) + if (argsobj.callee().isMagic(JS_ARGS_HOLE)) return true; } Value undef = UndefinedValue(); - if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs)) + if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs)) return JS_FALSE; - *objp = obj; + *objp = &argsobj; return true; } static JSBool args_enumerate(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->isNormalArguments()); + NormalArgumentsObject &argsobj = obj->asNormalArguments(); /* * Trigger reflection in args_resolve using a series of js_LookupProperty * calls. */ - int argc = int(obj->getArgsInitialLength()); + int argc = int(argsobj.initialLength()); for (int i = -2; i != argc; i++) { jsid id = (i == -2) ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) @@ -654,7 +386,7 @@ args_enumerate(JSContext *cx, JSObject *obj) JSObject *pobj; JSProperty *prop; - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop)) return false; } return true; @@ -663,41 +395,42 @@ args_enumerate(JSContext *cx, JSObject *obj) static JSBool StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - LeaveTrace(cx); - if (!obj->isStrictArguments()) return true; + StrictArgumentsObject &argsobj = obj->asStrictArguments(); + if (JSID_IS_INT(id)) { /* * arg can exceed the number of arguments if a script changed the * prototype to point to another Arguments object with a bigger argc. */ uintN arg = uintN(JSID_TO_INT(id)); - if (arg < obj->getArgsInitialLength()) { - const Value &v = obj->getArgsElement(arg); + if (arg < argsobj.initialLength()) { + const Value &v = argsobj.element(arg); if (!v.isMagic(JS_ARGS_HOLE)) *vp = v; } } else { JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)); - if (!obj->isArgsLengthOverridden()) - vp->setInt32(obj->getArgsInitialLength()); + if (!argsobj.hasOverriddenLength()) + vp->setInt32(argsobj.initialLength()); } return true; } static JSBool - StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { if (!obj->isStrictArguments()) return true; + StrictArgumentsObject &argsobj = obj->asStrictArguments(); + if (JSID_IS_INT(id)) { uintN arg = uintN(JSID_TO_INT(id)); - if (arg < obj->getArgsInitialLength()) { - obj->setArgsElement(arg, *vp); + if (arg < argsobj.initialLength()) { + argsobj.setElement(arg, *vp); return true; } } else { @@ -711,29 +444,29 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) * collect its value. */ AutoValueRooter tvr(cx); - return js_DeleteProperty(cx, obj, id, tvr.addr(), strict) && - js_SetProperty(cx, obj, id, vp, strict); + return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), strict) && + js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict); } static JSBool strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { - JS_ASSERT(obj->isStrictArguments()); - *objp = NULL; + StrictArgumentsObject &argsobj = obj->asStrictArguments(); + uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; PropertyOp getter = StrictArgGetter; StrictPropertyOp setter = StrictArgSetter; if (JSID_IS_INT(id)) { - uint32 arg = uint32(JSID_TO_INT(id)); - if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) + uint32_t arg = uint32_t(JSID_TO_INT(id)); + if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE)) return true; attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (obj->isArgsLengthOverridden()) + if (argsobj.hasOverriddenLength()) return true; } else { if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) && @@ -742,22 +475,22 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject } attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; - getter = CastAsPropertyOp(obj->getThrowTypeError()); - setter = CastAsStrictPropertyOp(obj->getThrowTypeError()); + getter = CastAsPropertyOp(argsobj.getThrowTypeError()); + setter = CastAsStrictPropertyOp(argsobj.getThrowTypeError()); } Value undef = UndefinedValue(); - if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs)) + if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs)) return false; - *objp = obj; + *objp = &argsobj; return true; } static JSBool strictargs_enumerate(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->isStrictArguments()); + StrictArgumentsObject *argsobj = &obj->asStrictArguments(); /* * Trigger reflection in strictargs_resolve using a series of @@ -767,19 +500,19 @@ strictargs_enumerate(JSContext *cx, JSObject *obj) JSProperty *prop; // length - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop)) + if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop)) return false; // callee - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop)) + if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop)) return false; // caller - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop)) + if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop)) return false; - for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) { - if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop)) + for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) { + if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop)) return false; } @@ -789,228 +522,109 @@ strictargs_enumerate(JSContext *cx, JSObject *obj) static void args_finalize(JSContext *cx, JSObject *obj) { - cx->free_((void *) obj->getArgsData()); -} - -/* - * If a generator's arguments or call object escapes, and the generator frame - * is not executing, the generator object needs to be marked because it is not - * otherwise reachable. An executing generator is rooted by its invocation. To - * distinguish the two cases (which imply different access paths to the - * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only - * set on the StackFrame kept in the generator object's JSGenerator. - */ -static inline void -MaybeMarkGenerator(JSTracer *trc, JSObject *obj) -{ -#if JS_HAS_GENERATORS - StackFrame *fp = (StackFrame *) obj->getPrivate(); - if (fp && fp->isFloatingGenerator()) { - JSObject *genobj = js_FloatingFrameToGenerator(fp)->obj; - MarkObject(trc, *genobj, "generator object"); - } -#endif + cx->free_(reinterpret_cast(obj->asArguments().data())); } static void args_trace(JSTracer *trc, JSObject *obj) { - JS_ASSERT(obj->isArguments()); - if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) { - JS_ASSERT(!obj->isStrictArguments()); - return; - } - - ArgumentsData *data = obj->getArgsData(); - if (data->callee.isObject()) - MarkObject(trc, data->callee.toObject(), js_callee_str); - MarkValueRange(trc, obj->getArgsInitialLength(), data->slots, js_arguments_str); + ArgumentsObject &argsobj = obj->asArguments(); + ArgumentsData *data = argsobj.data(); + MarkValue(trc, data->callee, js_callee_str); + MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str); - MaybeMarkGenerator(trc, obj); + /* + * If a generator's arguments or call object escapes, and the generator + * frame is not executing, the generator object needs to be marked because + * it is not otherwise reachable. An executing generator is rooted by its + * invocation. To distinguish the two cases (which imply different access + * paths to the generator object), we use the JSFRAME_FLOATING_GENERATOR + * flag, which is only set on the StackFrame kept in the generator object's + * JSGenerator. + */ +#if JS_HAS_GENERATORS + StackFrame *fp = argsobj.maybeStackFrame(); + if (fp && fp->isFloatingGenerator()) + MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); +#endif } /* - * The Arguments classes aren't initialized via js_InitClass, because arguments - * objects have the initial value of Object.prototype as their [[Prototype]]. - * However, Object.prototype.toString.call(arguments) === "[object Arguments]" - * per ES5 (although not ES3), so the class name is "Arguments" rather than - * "Object". - * - * The JSClass functions below collaborate to lazily reflect and synchronize - * actual argument values, argument count, and callee function object stored - * in a StackFrame with their corresponding property values in the frame's + * The classes below collaborate to lazily reflect and synchronize actual + * argument values, argument count, and callee function object stored in a + * StackFrame with their corresponding property values in the frame's * arguments object. */ -Class js_ArgumentsClass = { +Class js::NormalArgumentsObjectClass = { "Arguments", - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - PropertyStub, /* addProperty */ + JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, + JS_PropertyStub, /* addProperty */ args_delProperty, - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ args_enumerate, - (JSResolveOp) args_resolve, - ConvertStub, - args_finalize, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - args_trace + reinterpret_cast(args_resolve), + JS_ConvertStub, + args_finalize, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + args_trace, + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ + } }; -namespace js { - /* * Strict mode arguments is significantly less magical than non-strict mode * arguments, so it is represented by a different class while sharing some * functionality. */ -Class StrictArgumentsClass = { +Class js::StrictArgumentsObjectClass = { "Arguments", - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - PropertyStub, /* addProperty */ + JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, + JS_PropertyStub, /* addProperty */ args_delProperty, - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ strictargs_enumerate, reinterpret_cast(strictargs_resolve), - ConvertStub, - args_finalize, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - args_trace -}; - -} - -/* - * A Declarative Environment object stores its active StackFrame pointer in - * its private slot, just as Call and Arguments objects do. - */ -Class js_DeclEnvClass = { - js_Object_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub -}; - -static JSBool -CheckForEscapingClosure(JSContext *cx, JSObject *obj, Value *vp) -{ - JS_ASSERT(obj->isCall() || obj->getClass() == &js_DeclEnvClass); - - const Value &v = *vp; - - JSObject *funobj; - if (IsFunctionObject(v, &funobj)) { - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); - - /* - * Any escaping null or flat closure that reaches above itself or - * contains nested functions that reach above it must be wrapped. - * We can wrap only when this Call or Declarative Environment obj - * still has an active stack frame associated with it. - */ - if (fun->needsWrapper()) { - LeaveTrace(cx); - - StackFrame *fp = (StackFrame *) obj->getPrivate(); - if (fp) { - JSObject *wrapper = WrapEscapingClosure(cx, fp, fun); - if (!wrapper) - return false; - vp->setObject(*wrapper); - return true; - } - - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OPTIMIZED_CLOSURE_LEAK); - return false; - } + JS_ConvertStub, + args_finalize, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + args_trace, + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ } - return true; -} - -static JSBool -CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) -{ - return CheckForEscapingClosure(cx, obj, vp); -} - -/* - * Construct a call object for the given bindings. If this is a call object - * for a function invocation, callee should be the function being called. - * Otherwise it must be a call object for eval of strict mode code, and callee - * must be null. - */ -static JSObject * -NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee) -{ - Bindings &bindings = script->bindings; - size_t argsVars = bindings.countArgsAndVars(); - size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars; - gc::FinalizeKind kind = gc::GetGCObjectKind(slots); - - JSObject *callobj = js_NewGCObject(cx, kind); - if (!callobj) - return NULL; - - /* Init immediately to avoid GC seeing a half-init'ed object. */ - callobj->initCall(cx, bindings, &scopeChain); - - /* This must come after callobj->lastProp has been set. */ - if (!callobj->ensureInstanceReservedSlots(cx, argsVars)) - return NULL; - -#ifdef DEBUG - for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) { - const Shape &s = r.front(); - if (s.slot != SHAPE_INVALID_SLOT) { - JS_ASSERT(s.slot + 1 == callobj->slotSpan()); - break; - } - } -#endif - - callobj->setCallObjCallee(callee); - return callobj; -} - -static inline JSObject * -NewDeclEnvObject(JSContext *cx, StackFrame *fp) -{ - JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2); - 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(emptyDeclEnvShape); - return envobj; -} +}; namespace js { -JSObject * +CallObject * CreateFunCallObject(JSContext *cx, StackFrame *fp) { JS_ASSERT(fp->isNonEvalFunctionFrame()); @@ -1024,99 +638,74 @@ CreateFunCallObject(JSContext *cx, StackFrame *fp) * For a named function expression Call's parent points to an environment * object holding function's name. */ - if (JSAtom *lambdaName = (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL) { - scopeChain = NewDeclEnvObject(cx, fp); + if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) { + scopeChain = DeclEnvObject::create(cx, fp); if (!scopeChain) return NULL; - if (!js_DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName), - ObjectValue(fp->callee()), - CalleeGetter, NULL, - JSPROP_PERMANENT | JSPROP_READONLY, - 0, 0, NULL)) { + if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName), + ObjectValue(fp->callee()), NULL, NULL, + JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) { return NULL; } } - JSObject *callobj = NewCallObject(cx, fp->script(), *scopeChain, &fp->callee()); + CallObject *callobj = CallObject::create(cx, fp->script(), *scopeChain, &fp->callee()); if (!callobj) return NULL; - callobj->setPrivate(fp); + callobj->setStackFrame(fp); fp->setScopeChainWithOwnCallObj(*callobj); return callobj; } -JSObject * +CallObject * CreateEvalCallObject(JSContext *cx, StackFrame *fp) { - JSObject *callobj = NewCallObject(cx, fp->script(), fp->scopeChain(), NULL); + CallObject *callobj = CallObject::create(cx, fp->script(), fp->scopeChain(), NULL); if (!callobj) return NULL; - callobj->setPrivate(fp); + callobj->setStackFrame(fp); fp->setScopeChainWithOwnCallObj(*callobj); return callobj; } } // namespace js -JSObject * JS_FASTCALL -js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain) -{ - JS_ASSERT(!js_IsNamedLambda(fun)); - JS_ASSERT(scopeChain); - JS_ASSERT(callee); - return NewCallObject(cx, fun->script(), *scopeChain, callee); -} - -JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT, - 0, nanojit::ACCSET_STORE_ANY) - -inline static void -CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots) -{ - JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars); - Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS; - memcpy(base, argv, nargs * sizeof(Value)); - memcpy(base + nargs, slots, nvars * sizeof(Value)); -} - void js_PutCallObject(StackFrame *fp) { - JSObject &callobj = fp->callObj(); - JS_ASSERT(callobj.getPrivate() == fp); + CallObject &callobj = fp->callObj().asCall(); + JS_ASSERT(callobj.maybeStackFrame() == fp); JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame()); - JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval()); + JS_ASSERT(fp->isEvalFrame() == callobj.isForEval()); /* Get the arguments object to snapshot fp's actual argument values. */ if (fp->hasArgsObj()) { if (!fp->hasOverriddenArgs()) - callobj.setCallObjArguments(ObjectValue(fp->argsObj())); + callobj.initArguments(ObjectValue(fp->argsObj())); js_PutArgsObject(fp); } JSScript *script = fp->script(); Bindings &bindings = script->bindings; - if (callobj.callIsForEval()) { + if (callobj.isForEval()) { JS_ASSERT(script->strictModeCode); JS_ASSERT(bindings.countArgs() == 0); /* This could be optimized as below, but keep it simple for now. */ - CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots()); + callobj.copyValues(0, NULL, bindings.countVars(), fp->slots()); } else { JSFunction *fun = fp->fun(); - JS_ASSERT(fun == callobj.getCallObjCalleeFunction()); + JS_ASSERT(script == callobj.getCalleeFunction()->script()); JS_ASSERT(script == fun->script()); uintN n = bindings.countArgsAndVars(); if (n > 0) { - JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots()); - - uint32 nvars = bindings.countVars(); - uint32 nargs = bindings.countArgs(); + uint32_t nvars = bindings.countVars(); + uint32_t nargs = bindings.countArgs(); JS_ASSERT(fun->nargs == nargs); JS_ASSERT(nvars + nargs == n); @@ -1126,69 +715,73 @@ js_PutCallObject(StackFrame *fp) || script->debugMode #endif ) { - CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots()); + callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots()); } else { /* * For each arg & var that is closed over, copy it from the stack - * into the call object. + * into the call object. We use initArg/VarUnchecked because, + * when you call a getter on a call object, js_NativeGetInline + * caches the return value in the slot, so we can't assert that + * it's undefined. */ - uint32 nclosed = script->nClosedArgs; - for (uint32 i = 0; i < nclosed; i++) { - uint32 e = script->getClosedArg(i); - callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e)); + uint32_t nclosed = script->nClosedArgs; + for (uint32_t i = 0; i < nclosed; i++) { + uint32_t e = script->getClosedArg(i); +#ifdef JS_GC_ZEAL + callobj.setArg(e, fp->formalArg(e)); +#else + callobj.initArgUnchecked(e, fp->formalArg(e)); +#endif } nclosed = script->nClosedVars; - for (uint32 i = 0; i < nclosed; i++) { - uint32 e = script->getClosedVar(i); - callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]); + for (uint32_t i = 0; i < nclosed; i++) { + uint32_t e = script->getClosedVar(i); +#ifdef JS_GC_ZEAL + callobj.setVar(e, fp->slots()[e]); +#else + callobj.initVarUnchecked(e, fp->slots()[e]); +#endif } } + + /* + * Update the args and vars for the active call if this is an outer + * function in a script nesting. + */ + types::TypeScriptNesting *nesting = script->nesting(); + if (nesting && script->isOuterFunction) { + nesting->argArray = callobj.argArray(); + nesting->varArray = callobj.varArray(); + } } - /* Clear private pointers to fp, which is about to go away (js_Invoke). */ + /* Clear private pointers to fp, which is about to go away. */ if (js_IsNamedLambda(fun)) { - JSObject *env = callobj.getParent(); - - JS_ASSERT(env->getClass() == &js_DeclEnvClass); - JS_ASSERT(env->getPrivate() == fp); - env->setPrivate(NULL); + JSObject &env = callobj.enclosingScope(); + JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp); + env.setPrivate(NULL); } } - callobj.setPrivate(NULL); -} - -JSBool JS_FASTCALL -js_PutCallObjectOnTrace(JSObject *callobj, uint32 nargs, Value *argv, - uint32 nvars, Value *slots) -{ - JS_ASSERT(callobj->isCall()); - JS_ASSERT(!callobj->getPrivate()); - - uintN n = nargs + nvars; - if (n != 0) - CopyValuesToCallObject(*callobj, nargs, argv, nvars, slots); - - return true; + callobj.setStackFrame(NULL); } -JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutCallObjectOnTrace, OBJECT, UINT32, VALUEPTR, - UINT32, VALUEPTR, 0, nanojit::ACCSET_STORE_ANY) - namespace js { static JSBool GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - StackFrame *fp = obj->maybeCallObjStackFrame(); + CallObject &callobj = obj->asCall(); + + StackFrame *fp = callobj.maybeStackFrame(); if (fp && !fp->hasOverriddenArgs()) { JSObject *argsobj = js_GetArgsObject(cx, fp); if (!argsobj) return false; vp->setObject(*argsobj); } else { - *vp = obj->getCallObjArguments(); + *vp = callobj.getArguments(); } return true; } @@ -1196,158 +789,123 @@ GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp) static JSBool SetCallArguments(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { - if (StackFrame *fp = obj->maybeCallObjStackFrame()) + CallObject &callobj = obj->asCall(); + + if (StackFrame *fp = callobj.maybeStackFrame()) fp->setOverriddenArgs(); - obj->setCallObjArguments(*vp); + callobj.setArguments(*vp); return true; } JSBool GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); + CallObject &callobj = obj->asCall(); + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - if (StackFrame *fp = obj->maybeCallObjStackFrame()) + if (StackFrame *fp = callobj.maybeStackFrame()) *vp = fp->formalArg(i); else - *vp = obj->callObjArg(i); + *vp = callobj.arg(i); return true; } JSBool SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); + CallObject &callobj = obj->asCall(); + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - Value *argp; - if (StackFrame *fp = obj->maybeCallObjStackFrame()) - argp = &fp->formalArg(i); + if (StackFrame *fp = callobj.maybeStackFrame()) + fp->formalArg(i) = *vp; else - argp = &obj->callObjArg(i); + callobj.setArg(i, *vp); + + JSFunction *fun = callobj.getCalleeFunction(); + JSScript *script = fun->script(); + if (!script->ensureHasTypes(cx)) + return false; + + TypeScript::SetArgument(cx, script, i, *vp); - GC_POKE(cx, *argp); - *argp = *vp; return true; } JSBool GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); + CallObject &callobj = obj->asCall(); + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i); + *vp = callobj.getCallee()->toFunction()->getFlatClosureUpvar(i); return true; } JSBool SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); - - Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i); + CallObject &callobj = obj->asCall(); + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - GC_POKE(cx, *up); - *up = *vp; + callobj.getCallee()->toFunction()->setFlatClosureUpvar(i, *vp); return true; } JSBool GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); + CallObject &callobj = obj->asCall(); + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - if (StackFrame *fp = obj->maybeCallObjStackFrame()) + if (StackFrame *fp = callobj.maybeStackFrame()) *vp = fp->varSlot(i); else - *vp = obj->callObjVar(i); - + *vp = callobj.var(i); return true; } -JSBool -GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, Value *vp) -{ - if (!GetCallVar(cx, obj, id, vp)) - return false; - - return CheckForEscapingClosure(cx, obj, vp); -} - JSBool SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { - JS_ASSERT(obj->isCall()); + CallObject &callobj = obj->asCall(); - JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); - uintN i = (uint16) JSID_TO_INT(id); - - /* - * As documented in TraceRecorder::attemptTreeCall(), when recording an - * inner tree call, the recorder assumes the inner tree does not mutate - * any tracked upvars. The abort here is a pessimistic precaution against - * bug 620662, where an inner tree setting a closed stack variable in an - * outer tree is illegal, and runtime would fall off trace. - */ -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) { - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - if (tm->recorder && tm->tracecx) - AbortRecording(cx, "upvar write in nested tree"); - } -#endif + JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id)); + uintN i = (uint16_t) JSID_TO_INT(id); - Value *varp; - if (StackFrame *fp = obj->maybeCallObjStackFrame()) - varp = &fp->varSlot(i); + if (StackFrame *fp = callobj.maybeStackFrame()) + fp->varSlot(i) = *vp; else - varp = &obj->callObjVar(i); + callobj.setVar(i, *vp); - GC_POKE(cx, *varp); - *varp = *vp; - return true; -} + JSFunction *fun = callobj.getCalleeFunction(); + JSScript *script = fun->script(); + if (!script->ensureHasTypes(cx)) + return false; -} // namespace js + TypeScript::SetLocal(cx, script, i, *vp); -#if JS_TRACER -JSBool JS_FASTCALL -js_SetCallArg(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg) -{ - Value argcopy = ValueArgToConstRef(arg); - return SetCallArg(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy); + return true; } -JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallArg, CONTEXT, OBJECT, JSID, VALUE, 0, - nanojit::ACCSET_STORE_ANY) -JSBool JS_FASTCALL -js_SetCallVar(JSContext *cx, JSObject *obj, jsid slotid, ValueArgType arg) -{ - Value argcopy = ValueArgToConstRef(arg); - return SetCallVar(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy); -} -JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, 0, - nanojit::ACCSET_STORE_ANY) -#endif +} // namespace js static JSBool -call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) +call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { - JS_ASSERT(obj->isCall()); JS_ASSERT(!obj->getProto()); if (!JSID_IS_ATOM(id)) return true; - JSObject *callee = obj->getCallObjCallee(); + JSObject *callee = obj->asCall().getCallee(); #ifdef DEBUG if (callee) { - JSScript *script = callee->getFunctionPrivate()->script(); + JSScript *script = callee->toFunction()->script(); JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id))); } #endif @@ -1361,10 +919,10 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, * rebinding-Call-property logic. */ if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) { - if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), - GetCallArguments, SetCallArguments, - JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE, - 0, 0, NULL, JSDNP_DONT_PURGE)) { + if (!DefineNativeProperty(cx, obj, id, UndefinedValue(), + GetCallArguments, SetCallArguments, + JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE, + 0, 0, DNP_DONT_PURGE)) { return false; } *objp = obj; @@ -1379,43 +937,34 @@ static void call_trace(JSTracer *trc, JSObject *obj) { JS_ASSERT(obj->isCall()); - if (StackFrame *fp = obj->maybeCallObjStackFrame()) { - /* - * FIXME: Hide copies of stack values rooted by fp from the Cycle - * Collector, which currently lacks a non-stub Unlink implementation - * for JS objects (including Call objects), so is unable to collect - * cycles involving Call objects whose frames are active without this - * hiding hack. - */ - uintN first = JSObject::CALL_RESERVED_SLOTS; - uintN count = fp->script()->bindings.countArgsAndVars(); - JS_ASSERT(obj->numSlots() >= first + count); - SetValueRangeToUndefined(obj->getSlots() + first, count); - } - - MaybeMarkGenerator(trc, obj); + /* Mark any generator frame, as for arguments objects. */ +#if JS_HAS_GENERATORS + StackFrame *fp = (StackFrame *) obj->getPrivate(); + if (fp && fp->isFloatingGenerator()) + MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); +#endif } -JS_PUBLIC_DATA(Class) js_CallClass = { +JS_PUBLIC_DATA(Class) js::CallClass = { "Call", JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) | + JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ JS_EnumerateStub, (JSResolveOp)call_resolve, - NULL, /* convert: Leave it NULL so we notice if calls ever escape */ - NULL, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ + NULL, /* convert: Leave it NULL so we notice if calls ever escape */ + NULL, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ call_trace }; @@ -1423,28 +972,12 @@ bool StackFrame::getValidCalleeObject(JSContext *cx, Value *vp) { if (!isFunctionFrame()) { - vp->setUndefined(); - return true; - } - - JSFunction *fun = this->fun(); - - /* - * See the equivalent condition in ArgGetter for the 'callee' id case, but - * note that here we do not want to throw, since this escape can happen via - * a foo.caller reference alone, without any debugger or indirect eval. And - * alas, it seems foo.caller is still used on the Web. - */ - if (fun->needsWrapper()) { - JSObject *wrapper = WrapEscapingClosure(cx, this, fun); - if (!wrapper) - return false; - vp->setObject(*wrapper); + vp->setNull(); return true; } - JSObject &funobj = callee(); - vp->setObject(funobj); + JSFunction *fun = this->callee().toFunction(); + vp->setObject(*fun); /* * Check for an escape attempt by a joined function object, which must go @@ -1452,224 +985,208 @@ StackFrame::getValidCalleeObject(JSContext *cx, Value *vp) * atom by which it was uniquely associated with a property. */ const Value &thisv = functionThis(); - if (thisv.isObject()) { - JS_ASSERT(funobj.getFunctionPrivate() == fun); + if (thisv.isObject() && fun->methodAtom() && !fun->isClonedMethod()) { + JSObject *thisp = &thisv.toObject(); + JSObject *first_barriered_thisp = NULL; - if (fun->compiledFunObj() == funobj && fun->methodAtom()) { - JSObject *thisp = &thisv.toObject(); - JSObject *first_barriered_thisp = NULL; + do { + /* + * While a non-native object is responsible for handling its + * entire prototype chain, notable non-natives including dense + * and typed arrays have native prototypes, so keep going. + */ + if (!thisp->isNative()) + continue; - do { + const Shape *shape = thisp->nativeLookup(cx, ATOM_TO_JSID(fun->methodAtom())); + if (shape) { /* - * While a non-native object is responsible for handling its - * entire prototype chain, notable non-natives including dense - * and typed arrays have native prototypes, so keep going. + * Two cases follow: the method barrier was not crossed + * yet, so we cross it here; the method barrier *was* + * crossed but after the call, in which case we fetch + * and validate the cloned (unjoined) funobj from the + * method property's slot. + * + * In either case we must allow for the method property + * to have been replaced, or its value overwritten. */ - if (!thisp->isNative()) - continue; + if (shape->isMethod() && thisp->nativeGetMethod(shape) == fun) { + if (!thisp->methodReadBarrier(cx, *shape, vp)) + return false; + overwriteCallee(vp->toObject()); + return true; + } - if (thisp->hasMethodBarrier()) { - const Shape *shape = thisp->nativeLookup(ATOM_TO_JSID(fun->methodAtom())); - if (shape) { + if (shape->hasSlot()) { + Value v = thisp->getSlot(shape->slot()); + JSFunction *clone; + + if (IsFunctionObject(v, &clone) && + clone->isInterpreted() && + clone->script() == fun->script() && + clone->methodObj() == thisp) { /* - * Two cases follow: the method barrier was not crossed - * yet, so we cross it here; the method barrier *was* - * crossed but after the call, in which case we fetch - * and validate the cloned (unjoined) funobj from the - * method property's slot. - * - * In either case we must allow for the method property - * to have been replaced, or its value overwritten. + * N.B. If the method barrier was on a function + * with singleton type, then while crossing the + * method barrier CloneFunctionObject will have + * ignored the attempt to clone the function. */ - if (shape->isMethod() && shape->methodObject() == funobj) { - if (!thisp->methodReadBarrier(cx, *shape, vp)) - return false; - calleev().setObject(vp->toObject()); - return true; - } - - if (shape->hasSlot()) { - Value v = thisp->getSlot(shape->slot); - JSObject *clone; - - if (IsFunctionObject(v, &clone) && - GET_FUNCTION_PRIVATE(cx, clone) == fun && - clone->hasMethodObj(*thisp)) { - JS_ASSERT(clone != &funobj); - *vp = v; - calleev().setObject(*clone); - return true; - } - } + JS_ASSERT_IF(!clone->hasSingletonType(), clone != fun); + *vp = v; + overwriteCallee(*clone); + return true; } - - if (!first_barriered_thisp) - first_barriered_thisp = thisp; } - } while ((thisp = thisp->getProto()) != NULL); + } if (!first_barriered_thisp) - return true; + first_barriered_thisp = thisp; + } while ((thisp = thisp->getProto()) != NULL); - /* - * At this point, we couldn't find an already-existing clone (or - * force to exist a fresh clone) created via thisp's method read - * barrier, so we must clone fun and store it in fp's callee to - * avoid re-cloning upon repeated foo.caller access. - * - * This must mean the code in js_DeleteProperty could not find this - * stack frame on the stack when the method was deleted. We've lost - * track of the method, so we associate it with the first barriered - * object found starting from thisp on the prototype chain. - */ - JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent()); - if (!newfunobj) - return false; - newfunobj->setMethodObj(*first_barriered_thisp); - calleev().setObject(*newfunobj); - vp->setObject(*newfunobj); + if (!first_barriered_thisp) return true; - } + + /* + * At this point, we couldn't find an already-existing clone (or + * force to exist a fresh clone) created via thisp's method read + * barrier, so we must clone fun and store it in fp's callee to + * avoid re-cloning upon repeated foo.caller access. + * + * This must mean the code in js_DeleteGeneric could not find this + * stack frame on the stack when the method was deleted. We've lost + * track of the method, so we associate it with the first barriered + * object found starting from thisp on the prototype chain. + */ + JSFunction *newfunobj = CloneFunctionObject(cx, fun); + if (!newfunobj) + return false; + newfunobj->setMethodObj(*first_barriered_thisp); + overwriteCallee(*newfunobj); + vp->setObject(*newfunobj); + return true; } return true; } -/* Generic function tinyids. */ -enum { - FUN_ARGUMENTS = -1, /* predefined arguments local variable */ - FUN_LENGTH = -2, /* number of actual args, arity if inactive */ - FUN_ARITY = -3, /* number of formal parameters; desired argc */ - FUN_NAME = -4, /* function name, "" if anonymous */ - FUN_CALLER = -5 /* Function.prototype.caller, backward compat */ -}; - static JSBool fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - if (!JSID_IS_INT(id)) - return true; - - jsint slot = JSID_TO_INT(id); - - /* - * Loop because getter and setter can be delegated from another class, - * but loop only for FUN_LENGTH because we must pretend that f.length - * is in each function instance f, per ECMA-262, instead of only in the - * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED - * to make it appear so). - * - * This code couples tightly to the attributes for lazyFunctionDataProps[] - * and poisonPillProps[] initializers below, and to js_SetProperty and - * js_HasOwnProperty. - * - * It's important to allow delegating objects, even though they inherit - * this getter (fun_getProperty), to override arguments, arity, caller, - * and name. If we didn't return early for slot != FUN_LENGTH, we would - * clobber *vp with the native property value, instead of letting script - * override that value in delegating objects. - * - * Note how that clobbering is what simulates JSPROP_READONLY for all of - * the non-standard properties when the directly addressed object (obj) - * is a function object (i.e., when this loop does not iterate). - */ - while (!obj->isFunction()) { - if (slot != FUN_LENGTH) - return true; obj = obj->getProto(); if (!obj) return true; } - JSFunction *fun = obj->getFunctionPrivate(); + JSFunction *fun = obj->toFunction(); - /* Find fun's top-most activation record. */ - StackFrame *fp; - for (fp = js_GetTopStackFrame(cx); - fp && (fp->maybeFun() != fun || fp->isEvalOrDebuggerFrame()); - fp = fp->prev()) { - continue; + /* + * Mark the function's script as uninlineable, to expand any of its + * frames on the stack before we go looking for them. This allows the + * below walk to only check each explicit frame rather than needing to + * check any calls that were inlined. + */ + if (fun->isInterpreted()) { + fun->script()->uninlineable = true; + MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE); } - switch (slot) { - case FUN_ARGUMENTS: - /* Warn if strict about f.arguments or equivalent unqualified uses. */ - if (!JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - js_arguments_str)) { + /* Set to early to null in case of error */ + vp->setNull(); + + /* Find fun's top-most activation record. */ + StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); + for (; fp; fp = fp->prev()) { + if (!fp->isFunctionFrame() || fp->isEvalFrame()) + continue; + Value callee; + if (!fp->getValidCalleeObject(cx, &callee)) return false; + if (&callee.toObject() == fun) + break; + } + if (!fp) + return true; + +#ifdef JS_METHODJIT + if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) { + /* + * If the frame was called from within an inlined frame, mark the + * innermost function as uninlineable to expand its frame and allow us + * to recover its callee object. + */ + JSInlinedSite *inlined; + jsbytecode *prevpc = fp->prev()->pcQuadratic(cx->stack, fp, &inlined); + if (inlined) { + mjit::JITChunk *chunk = fp->prev()->jit()->chunk(prevpc); + JSFunction *fun = chunk->inlineFrames()[inlined->inlineIndex].fun; + fun->script()->uninlineable = true; + MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE); } - if (fp) { - if (!js_GetArgsValue(cx, fp, vp)) - return false; - } else { - vp->setNull(); + } +#endif + + if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) { + /* Warn if strict about f.arguments or equivalent unqualified uses. */ + if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, + NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) { + return false; } - break; - case FUN_LENGTH: - case FUN_ARITY: - vp->setInt32(fun->nargs); - break; + /* + * Purposefully disconnect the returned arguments object from the frame + * by always creating a new copy that does not alias formal parameters. + * This allows function-local analysis to determine that formals are + * not aliased and generally simplifies arguments objects. + */ + ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee()); + if (!argsobj) + return false; + + fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots)); + *vp = ObjectValue(*argsobj); + return true; + } - case FUN_NAME: - vp->setString(fun->atom ? fun->atom - : cx->runtime->emptyString); - break; + if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) { + if (!fp->prev()) + return true; - case FUN_CALLER: - vp->setNull(); - if (fp && fp->prev() && !fp->prev()->getValidCalleeObject(cx, vp)) + StackFrame *frame = js_GetScriptedCaller(cx, fp->prev()); + if (frame && !frame->getValidCalleeObject(cx, vp)) return false; - if (vp->isObject()) { - JSObject &caller = vp->toObject(); - - /* Censor the caller if it is from another compartment. */ - if (caller.getCompartment() != cx->compartment) { - vp->setNull(); - } else if (caller.isFunction()) { - JSFunction *callerFun = caller.getFunctionPrivate(); - if (callerFun->isInterpreted() && callerFun->inStrictMode()) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, - JSMSG_CALLER_IS_STRICT); - return false; - } + if (!vp->isObject()) { + JS_ASSERT(vp->isNull()); + return true; + } + + /* Censor the caller if it is from another compartment. */ + JSObject &caller = vp->toObject(); + if (caller.compartment() != cx->compartment) { + vp->setNull(); + } else if (caller.isFunction()) { + JSFunction *callerFun = caller.toFunction(); + if (callerFun->isInterpreted() && callerFun->inStrictMode()) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, + JSMSG_CALLER_IS_STRICT); + return false; } } - break; - default: - JS_NOT_REACHED("fun_getProperty"); + return true; } - return true; + JS_NOT_REACHED("fun_getProperty"); + return false; } -struct LazyFunctionDataProp { - uint16 atomOffset; - int8 tinyid; - uint8 attrs; -}; - -struct PoisonPillProp { - uint16 atomOffset; - int8 tinyid; -}; - -/* NB: no sentinels at ends -- use JS_ARRAY_LENGTH to bound loops. */ -static const LazyFunctionDataProp lazyFunctionDataProps[] = { - {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT|JSPROP_READONLY}, - {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT|JSPROP_READONLY}, -}; -/* Properties censored into [[ThrowTypeError]] in strict mode. */ -static const PoisonPillProp poisonPillProps[] = { - {ATOM_OFFSET(arguments), FUN_ARGUMENTS }, - {ATOM_OFFSET(caller), FUN_CALLER }, +/* NB: no sentinels at ends -- use ArrayLength to bound loops. + * Properties censored into [[ThrowTypeError]] in strict mode. */ +static const uint16_t poisonPillProps[] = { + ATOM_OFFSET(arguments), + ATOM_OFFSET(caller), }; static JSBool @@ -1677,6 +1194,8 @@ fun_enumerate(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isFunction()); + RootObject root(cx, &obj); + jsid id; bool found; @@ -1689,17 +1208,14 @@ fun_enumerate(JSContext *cx, JSObject *obj) id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED)) return false; + + id = ATOM_TO_JSID(cx->runtime->atomState.nameAtom); + if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED)) + return false; - for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) { - const LazyFunctionDataProp &lfp = lazyFunctionDataProps[i]; - id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lfp.atomOffset)); - if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED)) - return false; - } - - for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) { - const PoisonPillProp &p = poisonPillProps[i]; - id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, p.atomOffset)); + for (uintN i = 0; i < ArrayLength(poisonPillProps); i++) { + const uint16_t offset = poisonPillProps[i]; + id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, offset)); if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED)) return false; } @@ -1711,7 +1227,7 @@ static JSObject * ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) { #ifdef DEBUG - JSFunction *fun = obj->getFunctionPrivate(); + JSFunction *fun = obj->toFunction(); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->isFunctionPrototype()); #endif @@ -1728,24 +1244,28 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) * Make the prototype object an instance of Object with the same parent * as the function object itself. */ - JSObject *parent = obj->getParent(); - JSObject *proto; - if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) + JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx); + if (!objProto) return NULL; - proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent); - if (!proto) + JSObject *proto = NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL); + if (!proto || !proto->setSingletonType(cx)) return NULL; /* - * ECMA (15.3.5.2) says that a user-defined function's .prototype property - * is non-configurable, non-enumerable, and (initially) writable. Hence - * JSPROP_PERMANENT below. By contrast, the built-in constructors, such as - * Object (15.2.3.1) and Function (15.3.3.1), have non-writable - * .prototype properties. Those are eagerly defined, with attributes - * JSPROP_PERMANENT | JSPROP_READONLY, in js_InitClass. + * Per ES5 15.3.5.2 a user-defined function's .prototype property is + * initially non-configurable, non-enumerable, and writable. Per ES5 13.2 + * the prototype's .constructor property is configurable, non-enumerable, + * and writable. */ - if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) - return NULL; + if (!obj->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom, + ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT) || + !proto->defineProperty(cx, cx->runtime->atomState.constructorAtom, + ObjectValue(*obj), JS_PropertyStub, JS_StrictPropertyStub, 0)) + { + return NULL; + } + return proto; } @@ -1756,17 +1276,17 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!JSID_IS_ATOM(id)) return true; - JSFunction *fun = obj->getFunctionPrivate(); + RootedVarFunction fun(cx); + fun = obj->toFunction(); if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) { /* * Native or "built-in" functions do not have a .prototype property per - * ECMA-262 (all editions). Built-in constructor functions, e.g. Object - * and Function to name two conspicuous examples, do have a .prototype - * property, but it is created eagerly by js_InitClass (jsobj.cpp). + * ECMA-262, or (Object.prototype, Function.prototype, etc.) have that + * property created eagerly. * * ES5 15.3.4: the non-native function object named Function.prototype - * must not have a .prototype property. + * does not have a .prototype property. * * ES5 15.3.4.5: bound functions don't have a prototype property. The * isNative() test covers this case because bound functions are native @@ -1775,67 +1295,55 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (fun->isNative() || fun->isFunctionPrototype()) return true; - if (!ResolveInterpretedFunctionPrototype(cx, obj)) + if (!ResolveInterpretedFunctionPrototype(cx, fun)) return false; - *objp = obj; + *objp = fun; return true; } - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) || + JSID_IS_ATOM(id, cx->runtime->atomState.nameAtom)) { JS_ASSERT(!IsInternalFunctionObject(obj)); - if (!js_DefineNativeProperty(cx, obj, id, Int32Value(fun->nargs), - PropertyStub, StrictPropertyStub, - JSPROP_PERMANENT | JSPROP_READONLY, 0, 0, NULL)) { + + Value v; + if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) + v.setInt32(fun->nargs); + else + v.setString(fun->atom ? fun->atom : cx->runtime->emptyString); + + if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) { return false; } - *objp = obj; + *objp = fun; return true; } - for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) { - const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i]; - - if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset))) { - JS_ASSERT(!IsInternalFunctionObject(obj)); - - if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), - fun_getProperty, StrictPropertyStub, - lfp->attrs, Shape::HAS_SHORTID, - lfp->tinyid, NULL)) { - return false; - } - *objp = obj; - return true; - } - } - - for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) { - const PoisonPillProp &p = poisonPillProps[i]; + for (uintN i = 0; i < ArrayLength(poisonPillProps); i++) { + const uint16_t offset = poisonPillProps[i]; - if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, p.atomOffset))) { - JS_ASSERT(!IsInternalFunctionObject(obj)); + if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, offset))) { + JS_ASSERT(!IsInternalFunctionObject(fun)); PropertyOp getter; StrictPropertyOp setter; uintN attrs = JSPROP_PERMANENT; - if (fun->isInterpreted() ? fun->inStrictMode() : obj->isBoundFunction()) { - JSObject *throwTypeError = obj->getThrowTypeError(); + if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) { + JSObject *throwTypeError = fun->getThrowTypeError(); getter = CastAsPropertyOp(throwTypeError); setter = CastAsStrictPropertyOp(throwTypeError); attrs |= JSPROP_GETTER | JSPROP_SETTER; } else { getter = fun_getProperty; - setter = StrictPropertyStub; + setter = JS_StrictPropertyStub; } - if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), - getter, setter, - attrs, Shape::HAS_SHORTID, - p.tinyid, NULL)) { + if (!DefineNativeProperty(cx, fun, id, UndefinedValue(), getter, setter, + attrs, 0, 0)) { return false; } - *objp = obj; + *objp = fun; return true; } } @@ -1851,15 +1359,16 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; JSFunction *fun; - uint32 firstword; /* flag telling whether fun->atom is non-null, + uint32_t firstword; /* flag telling whether fun->atom is non-null, plus for fun->u.i.skipmin, fun->u.i.wrapper, and 14 bits reserved for future use */ - uint32 flagsword; /* word for argument count and fun->flags */ + uint32_t flagsword; /* word for argument count and fun->flags */ cx = xdr->cx; + JSScript *script; if (xdr->mode == JSXDR_ENCODE) { - fun = GET_FUNCTION_PRIVATE(cx, *objp); - if (!FUN_INTERPRETED(fun)) { + fun = (*objp)->toFunction(); + if (!fun->isInterpreted()) { JSAutoByteString funNameBytes; if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION, @@ -1867,25 +1376,21 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) } return false; } - if (fun->u.i.wrapper) { - JSAutoByteString funNameBytes; - if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XDR_CLOSURE_WRAPPER, name); - return false; - } - JS_ASSERT((fun->u.i.wrapper & ~1U) == 0); - firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom; + firstword = !!fun->atom; flagsword = (fun->nargs << 16) | fun->flags; + script = fun->script(); } else { - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); + RootedVarObject parent(cx, NULL); + fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL); if (!fun) return false; - FUN_OBJECT(fun)->clearParent(); - FUN_OBJECT(fun)->clearProto(); + if (!fun->clearParent(cx)) + return false; + if (!fun->clearType(cx)) + return false; + script = NULL; } - AutoObjectRooter tvr(cx, FUN_OBJECT(fun)); - if (!JS_XDRUint32(xdr, &firstword)) return false; if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom)) @@ -1893,24 +1398,19 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) if (!JS_XDRUint32(xdr, &flagsword)) return false; - if (xdr->mode == JSXDR_DECODE) { - fun->nargs = flagsword >> 16; - JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); - fun->flags = uint16(flagsword); - fun->u.i.skipmin = uint16(firstword >> 2); - fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1); - } - - if (!js_XDRScript(xdr, &fun->u.i.script)) + if (!js_XDRScript(xdr, &script)) return false; if (xdr->mode == JSXDR_DECODE) { - *objp = FUN_OBJECT(fun); -#ifdef CHECK_SCRIPT_OWNER - fun->script()->owner = NULL; -#endif + fun->nargs = flagsword >> 16; + JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); + fun->flags = uint16_t(flagsword); + fun->setScript(script); + if (!script->typeSetFunction(cx, fun)) + return false; JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs()); js_CallNewScriptHook(cx, fun->script(), fun); + *objp = fun; } return true; @@ -1933,12 +1433,11 @@ fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) while (obj->isFunction()) { if (!obj->isBoundFunction()) break; - obj = obj->getBoundFunctionTarget(); + obj = obj->toFunction()->getBoundFunctionTarget(); } - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); Value pval; - if (!obj->getProperty(cx, id, &pval)) + if (!obj->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &pval)) return JS_FALSE; if (pval.isPrimitive()) { @@ -1954,53 +1453,49 @@ fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) return JS_TRUE; } -static void -fun_trace(JSTracer *trc, JSObject *obj) +inline void +JSFunction::trace(JSTracer *trc) { - /* A newborn function object may have a not yet initialized private slot. */ - JSFunction *fun = (JSFunction *) obj->getPrivate(); - if (!fun) - return; - - if (fun != obj) { - /* obj is a cloned function object, trace the clone-parent, fun. */ - MarkObject(trc, *fun, "private"); + if (isFlatClosure() && hasFlatClosureUpvars()) { + if (HeapValue *upvars = getFlatClosureUpvars()) + MarkValueRange(trc, script()->bindings.countUpvars(), upvars, "upvars"); + } - /* The function could be a flat closure with upvar copies in the clone. */ - if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) { - MarkValueRange(trc, fun->script()->bindings.countUpvars(), - obj->getFlatClosureUpvars(), "upvars"); - } - return; + if (isExtended()) { + MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots), + toExtended()->extendedSlots, "nativeReserved"); } - if (fun->atom) - MarkString(trc, fun->atom, "atom"); + if (atom) + MarkStringUnbarriered(trc, atom, "atom"); + + if (isInterpreted()) { + if (script()) + MarkScript(trc, script(), "script"); + if (environment()) + MarkObjectUnbarriered(trc, environment(), "fun_callscope"); + } +} - if (fun->isInterpreted() && fun->script()) - js_TraceScript(trc, fun->script()); +static void +fun_trace(JSTracer *trc, JSObject *obj) +{ + obj->toFunction()->trace(trc); } static void fun_finalize(JSContext *cx, JSObject *obj) { - /* Ignore newborn function objects. */ - JSFunction *fun = obj->getFunctionPrivate(); - if (!fun) - return; - - /* Cloned function objects may be flat closures with upvars to free. */ - if (fun != obj) { - if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) - cx->free_((void *) obj->getFlatClosureUpvars()); - return; - } + if (obj->toFunction()->isFlatClosure()) + obj->toFunction()->finalizeUpvars(); +} - /* - * Null-check fun->script() because the parser sets interpreted very early. - */ - if (fun->isInterpreted() && fun->script()) - js_DestroyScriptFromGC(cx, fun->script()); +size_t +JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const +{ + return (isFlatClosure() && hasFlatClosureUpvars()) ? + mallocSizeOf(getFlatClosureUpvars()) : + 0; } /* @@ -2008,23 +1503,22 @@ fun_finalize(JSContext *cx, JSObject *obj) * does not bloat every instance, only those on which reserved slots are set, * and those on which ad-hoc properties are defined. */ -JS_PUBLIC_DATA(Class) js_FunctionClass = { +JS_FRIEND_DATA(Class) js::FunctionClass = { js_Function_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) | + JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ fun_enumerate, (JSResolveOp)fun_resolve, - ConvertStub, + JS_ConvertStub, fun_finalize, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ NULL, fun_hasInstance, fun_trace @@ -2034,8 +1528,8 @@ JSString * fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent) { if (!obj->isFunction()) { - if (obj->isFunctionProxy()) - return JSProxy::fun_toString(cx, obj, indent); + if (IsFunctionProxy(obj)) + return Proxy::fun_toString(cx, obj, indent); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, js_toString_str, @@ -2043,7 +1537,7 @@ fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent) return NULL; } - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj); + JSFunction *fun = obj->toFunction(); if (!fun) return NULL; @@ -2079,7 +1573,7 @@ fun_toString(JSContext *cx, uintN argc, Value *vp) JS_ASSERT(IsFunctionObject(vp[0])); uint32_t indent = 0; - if (argc != 0 && !ValueToECMAUint32(cx, vp[2], &indent)) + if (argc != 0 && !ToUint32(cx, vp[2], &indent)) return false; JSObject *obj = ToObject(cx, &vp[1]); @@ -2116,11 +1610,10 @@ fun_toSource(JSContext *cx, uintN argc, Value *vp) JSBool js_fun_call(JSContext *cx, uintN argc, Value *vp) { - LeaveTrace(cx); Value fval = vp[1]; if (!js_IsCallable(fval)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass); return false; } @@ -2143,7 +1636,7 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp) /* Push fval, thisv, and the args. */ args.calleev() = fval; args.thisv() = thisv; - memcpy(args.argv(), argv, argc * sizeof *argv); + PodCopy(args.array(), argv, argc); bool ok = Invoke(cx, args); *vp = args.rval(); @@ -2157,7 +1650,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) /* Step 1. */ Value fval = vp[1]; if (!js_IsCallable(fval)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass); return false; } @@ -2182,13 +1675,14 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) if (!js_GetLengthProperty(cx, aobj, &length)) return false; - LeaveTrace(cx); - /* Step 6. */ - uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX)); + if (length > StackSpace::ARGS_LENGTH_MAX) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS); + return false; + } InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, n, &args)) + if (!cx->stack.pushInvokeArgs(cx, length, &args)) return false; /* Push fval, obj, and aobj's elements as args. */ @@ -2196,7 +1690,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) args.thisv() = vp[2]; /* Steps 7-8. */ - if (!GetElements(cx, aobj, n, args.argv())) + if (!GetElements(cx, aobj, length, args.array())) return false; /* Step 9. */ @@ -2213,35 +1707,41 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp); } +static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 0; +static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1; + +static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 2; + inline bool -JSObject::initBoundFunction(JSContext *cx, const Value &thisArg, - const Value *args, uintN argslen) +JSFunction::initBoundFunction(JSContext *cx, const Value &thisArg, + const Value *args, uintN argslen) { JS_ASSERT(isFunction()); - flags |= JSObject::BOUND_FUNCTION; - getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg; - getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen); - if (argslen != 0) { - /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */ - EmptyShape *empty = EmptyShape::create(cx, clasp); - if (!empty) - return false; + /* + * Convert to a dictionary to set the BOUND_FUNCTION flag and increase + * the slot span to cover the arguments and additional slots for the 'this' + * value and arguments count. + */ + if (!toDictionaryMode(cx)) + return false; - empty->slotSpan += argslen; - setMap(empty); + if (!setFlag(cx, BaseShape::BOUND_FUNCTION)) + return false; - if (!ensureInstanceReservedSlots(cx, argslen)) - return false; + if (!setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen)) + return false; + + setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg); + setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen)); + + initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen); - JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS); - memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value)); - } return true; } inline JSObject * -JSObject::getBoundFunctionTarget() const +JSFunction::getBoundFunctionTarget() const { JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); @@ -2251,7 +1751,7 @@ JSObject::getBoundFunctionTarget() const } inline const js::Value & -JSObject::getBoundFunctionThis() const +JSFunction::getBoundFunctionThis() const { JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); @@ -2259,16 +1759,23 @@ JSObject::getBoundFunctionThis() const return getSlot(JSSLOT_BOUND_FUNCTION_THIS); } -inline const js::Value * -JSObject::getBoundFunctionArguments(uintN &argslen) const +inline const js::Value & +JSFunction::getBoundFunctionArgument(uintN which) const { JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); + JS_ASSERT(which < getBoundFunctionArgumentCount()); - argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); - JS_ASSERT_IF(argslen > 0, numSlots() >= argslen); + return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which); +} + +inline size_t +JSFunction::getBoundFunctionArgumentCount() const +{ + JS_ASSERT(isFunction()); + JS_ASSERT(isBoundFunction()); - return getSlots() + FUN_CLASS_RESERVED_SLOTS; + return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); } namespace js { @@ -2277,36 +1784,33 @@ namespace js { JSBool CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = &vp[0].toObject(); - JS_ASSERT(obj->isFunction()); - JS_ASSERT(obj->isBoundFunction()); - - LeaveTrace(cx); + JSFunction *fun = vp[0].toObject().toFunction(); + JS_ASSERT(fun->isBoundFunction()); bool constructing = IsConstructing(vp); /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */ - uintN argslen; - const Value *boundArgs = obj->getBoundFunctionArguments(argslen); + uintN argslen = fun->getBoundFunctionArgumentCount(); - if (argc + argslen > JS_ARGS_LENGTH_MAX) { + if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) { js_ReportAllocationOverflow(cx); return false; } /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */ - JSObject *target = obj->getBoundFunctionTarget(); + JSObject *target = fun->getBoundFunctionTarget(); /* 15.3.4.5.1 step 2. */ - const Value &boundThis = obj->getBoundFunctionThis(); + const Value &boundThis = fun->getBoundFunctionThis(); InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args)) return false; /* 15.3.4.5.1, 15.3.4.5.2 step 4. */ - memcpy(args.argv(), boundArgs, argslen * sizeof(Value)); - memcpy(args.argv() + argslen, vp + 2, argc * sizeof(Value)); + for (uintN i = 0; i < argslen; i++) + args[i] = fun->getBoundFunctionArgument(i); + PodCopy(args.array() + argslen, vp + 2, argc); /* 15.3.4.5.1, 15.3.4.5.2 step 5. */ args.calleev().setObject(*target); @@ -2327,17 +1831,15 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp) static JSBool fun_isGenerator(JSContext *cx, uintN argc, Value *vp) { - JSObject *funobj; - if (!IsFunctionObject(vp[1], &funobj)) { + JSFunction *fun; + if (!IsFunctionObject(vp[1], &fun)) { JS_SET_RVAL(cx, vp, BooleanValue(false)); return true; } - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); - bool result = false; if (fun->isInterpreted()) { - JSScript *script = fun->u.i.script; + JSScript *script = fun->script(); JS_ASSERT(script->length != 0); result = script->code[0] == JSOP_GENERATOR; } @@ -2351,70 +1853,62 @@ fun_isGenerator(JSContext *cx, uintN argc, Value *vp) static JSBool fun_bind(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* Step 1. */ - Value &thisv = vp[1]; + Value &thisv = args.thisv(); /* Step 2. */ if (!js_IsCallable(thisv)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, args, &FunctionClass); return false; } - JSObject *target = &thisv.toObject(); + RootedVarObject target(cx); + target = &thisv.toObject(); /* Step 3. */ - Value *args = NULL; + Value *boundArgs = NULL; uintN argslen = 0; - if (argc > 1) { - args = vp + 3; - argslen = argc - 1; + if (args.length() > 1) { + boundArgs = args.array() + 1; + argslen = args.length() - 1; } /* Steps 15-16. */ uintN length = 0; if (target->isFunction()) { - uintN nargs = target->getFunctionPrivate()->nargs; + uintN nargs = target->toFunction()->nargs; if (nargs > argslen) length = nargs - argslen; } /* Step 4-6, 10-11. */ - JSAtom *name = target->isFunction() ? target->getFunctionPrivate()->atom : NULL; + JSAtom *name = target->isFunction() ? target->toFunction()->atom : NULL; - /* NB: Bound functions abuse |parent| to store their target. */ JSObject *funobj = js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length, JSFUN_CONSTRUCTOR, target, name); if (!funobj) return false; + /* NB: Bound functions abuse |parent| to store their target. */ + if (!funobj->setParent(cx, target)) + return false; + /* Steps 7-9. */ - Value thisArg = argc >= 1 ? vp[2] : UndefinedValue(); - if (!funobj->initBoundFunction(cx, thisArg, args, argslen)) + Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue(); + if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen)) return false; /* Steps 17, 19-21 are handled by fun_resolve. */ /* Step 18 is the default for new functions. */ /* Step 22. */ - vp->setObject(*funobj); + args.rval().setObject(*funobj); return true; } -static JSFunctionSpec function_methods[] = { -#if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, fun_toSource, 0,0), -#endif - JS_FN(js_toString_str, fun_toString, 0,0), - JS_FN(js_apply_str, js_fun_apply, 2,0), - JS_FN(js_call_str, js_fun_call, 1,0), - JS_FN("bind", fun_bind, 1,0), -#if JS_HAS_GENERATORS - JS_FN("isGenerator", fun_isGenerator,0,0), -#endif - JS_FS_END -}; - /* * Report "malformed formal parameter" iff no illegal char or similar scanner * error was already reported. @@ -2429,55 +1923,44 @@ OnBadFormal(JSContext *cx, TokenKind tt) return false; } -static JSBool -Function(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs call = CallArgsFromVp(argc, vp); - - JS::Anchor obj(NewFunction(cx, NULL)); - if (!obj.get()) - return false; +namespace js { - JSObject &calleeParent = *call.callee().getParent(); +JSFunctionSpec function_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, fun_toSource, 0,0), +#endif + JS_FN(js_toString_str, fun_toString, 0,0), + JS_FN(js_apply_str, js_fun_apply, 2,0), + JS_FN(js_call_str, js_fun_call, 1,0), + JS_FN("bind", fun_bind, 1,0), +#if JS_HAS_GENERATORS + JS_FN("isGenerator", fun_isGenerator,0,0), +#endif + JS_FS_END +}; - /* - * NB: (new Function) is not lexically closed by its caller, it's just an - * anonymous function in the top-level scope that its constructor inhabits. - * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, - * and so would a call to f from another top-level's script or function. - * - * In older versions, before call objects, a new Function was adopted by - * its running context's globalObject, which might be different from the - * top-level reachable from scopeChain (in HTML frames, e.g.). - */ - JSFunction *fun = js_NewFunction(cx, obj.get(), NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, - &calleeParent, cx->runtime->atomState.anonymousAtom); - if (!fun) - return false; +JSBool +Function(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); - /* - * CSP check: whether new Function() is allowed at all. - * Report errors via CSP is done in the script security manager. - * js_CheckContentSecurityPolicy is defined in jsobj.cpp - */ - if (!js_CheckContentSecurityPolicy(cx, &calleeParent)) { + /* Block this call if security callbacks forbid it. */ + RootedVar global(cx); + global = &args.callee().global(); + if (!global->isRuntimeCodeGenEnabled(cx)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION); return false; } - EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx); - if (!emptyCallShape) - return false; - AutoShapeRooter shapeRoot(cx, emptyCallShape); - - Bindings bindings(cx, emptyCallShape); - AutoBindingsRooter root(cx, bindings); + Bindings bindings(cx); + const char *filename; uintN lineno; - const char *filename = CurrentScriptFileAndLine(cx, &lineno); + JSPrincipals *originPrincipals; + CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals); + JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); - Value *argv = call.argv(); - uintN n = argc ? argc - 1 : 0; + uintN n = args.length() ? args.length() - 1 : 0; if (n > 0) { /* * Collect the function-argument arguments into one string, separated @@ -2492,10 +1975,10 @@ Function(JSContext *cx, uintN argc, Value *vp) size_t args_length = 0; for (uintN i = 0; i < n; i++) { /* Collect the lengths for all the function-argument arguments. */ - JSString *arg = js_ValueToString(cx, argv[i]); + JSString *arg = ToString(cx, args[i]); if (!arg) return false; - argv[i].setString(arg); + args[i].setString(arg); /* * Check for overflow. The < test works because the maximum @@ -2520,13 +2003,13 @@ Function(JSContext *cx, uintN argc, Value *vp) /* * Allocate a string to hold the concatenated arguments, including room - * for a terminating 0. Mark cx->tempPool for later release, to free - * collected_args and its tokenstream in one swoop. + * for a terminating 0. Mark cx->tempLifeAlloc for later release, to + * free collected_args and its tokenstream in one swoop. */ - AutoArenaAllocator aaa(&cx->tempPool); - jschar *cp = aaa.alloc(args_length + 1); + LifoAllocScope las(&cx->tempLifoAlloc()); + jschar *cp = cx->tempLifoAlloc().newArray(args_length + 1); if (!cp) { - js_ReportOutOfScriptQuota(cx); + js_ReportOutOfMemory(cx); return false; } jschar *collected_args = cp; @@ -2535,7 +2018,7 @@ Function(JSContext *cx, uintN argc, Value *vp) * Concatenate the arguments into the new string, separated by commas. */ for (uintN i = 0; i < n; i++) { - JSString *arg = argv[i].toString(); + JSString *arg = args[i].toString(); size_t arg_length = arg->length(); const jschar *arg_chars = arg->getChars(cx); if (!arg_chars) @@ -2548,7 +2031,7 @@ Function(JSContext *cx, uintN argc, Value *vp) } /* Initialize a tokenstream that reads from the given string. */ - TokenStream ts(cx); + TokenStream ts(cx, principals, originPrincipals); if (!ts.init(collected_args, args_length, filename, lineno, cx->findVersion())) return false; @@ -2563,27 +2046,22 @@ Function(JSContext *cx, uintN argc, Value *vp) if (tt != TOK_NAME) return OnBadFormal(cx, tt); - /* - * Get the atom corresponding to the name from the token - * stream; we're assured at this point that it's a valid - * identifier. - */ - JSAtom *atom = ts.currentToken().t_atom; - /* Check for a duplicate parameter name. */ - if (bindings.hasBinding(cx, atom)) { - JSAutoByteString name; - if (!js_AtomToPrintableString(cx, atom, &name)) + PropertyName *name = ts.currentToken().name(); + if (bindings.hasBinding(cx, name)) { + JSAutoByteString bytes; + if (!js_AtomToPrintableString(cx, name, &bytes)) return false; if (!ReportCompileErrorNumber(cx, &ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_DUPLICATE_FORMAL, name.ptr())) { + JSMSG_DUPLICATE_FORMAL, bytes.ptr())) + { return false; } } - uint16 dummy; - if (!bindings.addArgument(cx, atom, &dummy)) + uint16_t dummy; + if (!bindings.addArgument(cx, name, &dummy)) return false; /* @@ -2604,8 +2082,8 @@ Function(JSContext *cx, uintN argc, Value *vp) const jschar *chars; size_t length; - if (argc) { - JSString *str = js_ValueToString(cx, argv[argc - 1]); + if (args.length()) { + JSString *str = ToString(cx, args[args.length() - 1]); if (!str) return false; strAnchor.set(str); @@ -2616,16 +2094,24 @@ Function(JSContext *cx, uintN argc, Value *vp) length = 0; } - JSPrincipals *principals = PrincipalsForCompiledCode(call, cx); - bool ok = Compiler::compileFunctionBody(cx, fun, principals, &bindings, - chars, length, filename, lineno, + /* + * NB: (new Function) is not lexically closed by its caller, it's just an + * anonymous function in the top-level scope that its constructor inhabits. + * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, + * and so would a call to f from another top-level's script or function. + */ + JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, + global, cx->runtime->atomState.anonymousAtom); + if (!fun) + return false; + + bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals, + &bindings, chars, length, filename, lineno, cx->findVersion()); - call.rval().setObject(obj); + args.rval().setObject(*fun); return ok; } -namespace js { - bool IsBuiltinFunctionConstructor(JSFunction *fun) { @@ -2636,18 +2122,18 @@ const Shape * LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj) { #ifdef DEBUG - JSFunction *fun = funobj->getFunctionPrivate(); + JSFunction *fun = funobj->toFunction(); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->isFunctionPrototype()); JS_ASSERT(!funobj->isBoundFunction()); #endif jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - const Shape *shape = funobj->nativeLookup(id); + const Shape *shape = funobj->nativeLookup(cx, id); if (!shape) { if (!ResolveInterpretedFunctionPrototype(cx, funobj)) return NULL; - shape = funobj->nativeLookup(id); + shape = funobj->nativeLookup(cx, id); } JS_ASSERT(!shape->configurable()); JS_ASSERT(shape->isDataDescriptor()); @@ -2658,163 +2144,121 @@ LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj) } /* namespace js */ -static JSBool -ThrowTypeError(JSContext *cx, uintN argc, Value *vp) -{ - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, - JSMSG_THROW_TYPE_ERROR); - return false; -} - -JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, - NULL, function_methods, NULL, NULL); - if (!proto) - return NULL; - - JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL); - if (!fun) - return NULL; - fun->flags |= JSFUN_PROTOTYPE; - - JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT); - if (!script) - return NULL; - script->noScriptRval = true; - script->code[0] = JSOP_STOP; - script->code[1] = SRC_NULL; -#ifdef CHECK_SCRIPT_OWNER - 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. */ - JSFunction *throwTypeError = - js_NewFunction(cx, NULL, reinterpret_cast(ThrowTypeError), 0, - 0, obj, NULL); - if (!throwTypeError) - return NULL; - - obj->asGlobal()->setThrowTypeError(throwTypeError); - } - - return proto; -} - JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom) + uintN flags, HandleObject parent, JSAtom *atom, js::gc::AllocKind kind) { + JS_ASSERT(kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); + JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind)); + JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind)); + JSFunction *fun; if (funobj) { JS_ASSERT(funobj->isFunction()); - funobj->setParent(parent); + JS_ASSERT(funobj->getParent() == parent); } else { - funobj = NewFunction(cx, parent); + funobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind); if (!funobj) return NULL; } - JS_ASSERT(!funobj->getPrivate()); - fun = (JSFunction *) funobj; + fun = static_cast(funobj); /* Initialize all function members. */ - fun->nargs = uint16(nargs); - fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO); + fun->nargs = uint16_t(nargs); + fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK); if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) { JS_ASSERT(!native); - JS_ASSERT(nargs == 0); - fun->u.i.skipmin = 0; - fun->u.i.wrapper = false; - fun->u.i.script = NULL; + fun->script().init(NULL); + fun->initEnvironment(parent); } else { fun->u.n.clasp = NULL; - if (flags & JSFUN_TRCINFO) { -#ifdef JS_TRACER - JSNativeTraceInfo *trcinfo = - JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, native); - fun->u.n.native = (js::Native) trcinfo->native; - fun->u.n.trcinfo = trcinfo; -#else - fun->u.n.trcinfo = NULL; -#endif - } else { - fun->u.n.native = native; - fun->u.n.trcinfo = NULL; - } + fun->u.n.native = native; JS_ASSERT(fun->u.n.native); } + if (kind == JSFunction::ExtendedFinalizeKind) { + fun->flags |= JSFUN_EXTENDED; + fun->initializeExtended(); + } fun->atom = atom; - /* Set private to self to indicate non-cloned fully initialized function. */ - FUN_OBJECT(fun)->setPrivate(fun); + if (native && !fun->setSingletonType(cx)) + return NULL; + return fun; } -JSObject * JS_FASTCALL +JSFunction * JS_FASTCALL js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, - JSObject *proto) + JSObject *proto, gc::AllocKind kind) { JS_ASSERT(parent); JS_ASSERT(proto); - JSObject *clone; + JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind); + if (!cloneobj) + return NULL; + JSFunction *clone = static_cast(cloneobj); + + clone->nargs = fun->nargs; + clone->flags = fun->flags & ~JSFUN_EXTENDED; + if (fun->isInterpreted()) { + clone->initScript(fun->script()); + clone->initEnvironment(parent); + } else { + clone->u.n = fun->u.n; + } + clone->atom = fun->atom; + + if (kind == JSFunction::ExtendedFinalizeKind) { + clone->flags |= JSFUN_EXTENDED; + clone->initializeExtended(); + } + if (cx->compartment == fun->compartment()) { /* - * The cloned function object does not need the extra JSFunction members - * beyond JSObject as it points to fun via the private slot. + * We can use the same type as the original function provided that (a) + * its prototype is correct, and (b) its type is not a singleton. The + * first case will hold in all compileAndGo code, and the second case + * will have been caught by CloneFunctionObject coming from function + * definitions or read barriers, so will not get here. */ - clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent); - if (!clone) - return NULL; - clone->setPrivate(fun); + if (fun->getProto() == proto && !fun->hasSingletonType()) + clone->setType(fun->type()); } else { /* - * Across compartments we have to deep copy JSFunction and clone the - * script (for interpreted functions). + * Across compartments we have to clone the script for interpreted + * functions. */ - clone = NewFunction(cx, parent); - if (!clone) - return NULL; - JSFunction *cfun = (JSFunction *) clone; - cfun->nargs = fun->nargs; - cfun->flags = fun->flags; - cfun->u = fun->getFunctionPrivate()->u; - cfun->atom = fun->atom; - clone->setPrivate(cfun); - if (cfun->isInterpreted()) { - JSScript *script = cfun->u.i.script; + if (clone->isInterpreted()) { + JSScript *script = clone->script(); JS_ASSERT(script); - JS_ASSERT(script->compartment == fun->compartment()); - JS_ASSERT(script->compartment != cx->compartment); + JS_ASSERT(script->compartment() == fun->compartment()); + JS_ASSERT(script->compartment() != cx->compartment); - cfun->u.i.script = js_CloneScript(cx, script); - if (!cfun->u.i.script) + clone->script().init(NULL); + JSScript *cscript = js_CloneScript(cx, script); + if (!cscript) return NULL; -#ifdef CHECK_SCRIPT_OWNER - cfun->script()->owner = NULL; -#endif - js_CallNewScriptHook(cx, cfun->script(), cfun); + + cscript->globalObject = &clone->global(); + clone->setScript(cscript); + if (!cscript->typeSetFunction(cx, clone)) + return NULL; + + js_CallNewScriptHook(cx, clone->script(), clone); + Debugger::onNewScript(cx, clone->script(), NULL); } } return clone; } -#ifdef JS_TRACER -JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0, - nanojit::ACCSET_STORE_ANY) -#endif - /* * Create a new flat closure, but don't initialize the imported upvar * values. The tracer calls this function and then initializes the upvar * slots on trace. */ -JSObject * JS_FASTCALL +JSFunction * JS_FASTCALL js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) { JS_ASSERT(fun->isFlatClosure()); @@ -2823,71 +2267,55 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset), fun->script()->upvars()->length == fun->script()->bindings.countUpvars()); - JSObject *closure = CloneFunctionObject(cx, fun, scopeChain); + JSFunction *closure = CloneFunctionObject(cx, fun, scopeChain, JSFunction::ExtendedFinalizeKind); if (!closure) return closure; - uint32 nslots = fun->script()->bindings.countUpvars(); + uint32_t nslots = fun->script()->bindings.countUpvars(); if (nslots == 0) return closure; - Value *upvars = (Value *) cx->malloc_(nslots * sizeof(Value)); - if (!upvars) + HeapValue *data = (HeapValue *) cx->malloc_(nslots * sizeof(HeapValue)); + if (!data) return NULL; - closure->setFlatClosureUpvars(upvars); + closure->setExtendedSlot(JSFunction::FLAT_CLOSURE_UPVARS_SLOT, PrivateValue(data)); return closure; } -JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure, - CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY) - -JSObject * -js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen) +JSFunction * +js_NewFlatClosure(JSContext *cx, JSFunction *fun) { /* * Flat closures cannot yet be partial, that is, all upvars must be copied, * or the closure won't be flattened. Therefore they do not need to search * enclosing scope objects via JSOP_NAME, etc. - * - * FIXME: bug 545759 proposes to enable partial flat closures. Fixing this - * bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK - * annotations on this function's prototype and definition. */ - VOUCH_DOES_NOT_REQUIRE_STACK(); JSObject *scopeChain = &cx->fp()->scopeChain(); - JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain); + JSFunction *closure = js_AllocFlatClosure(cx, fun, scopeChain); if (!closure || !fun->script()->bindings.hasUpvars()) return closure; - Value *upvars = closure->getFlatClosureUpvars(); - uintN level = fun->u.i.script->staticLevel; + uintN level = fun->script()->staticLevel; JSUpvarArray *uva = fun->script()->upvars(); - for (uint32 i = 0, n = uva->length; i < n; i++) - upvars[i] = GetUpvar(cx, level, uva->vector[i]); + for (uint32_t i = 0, n = uva->length; i < n; i++) + closure->initFlatClosureUpvar(i, GetUpvar(cx, level, uva->vector[i])); return closure; } -JSObject * -js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun) -{ - JS_ASSERT(cx->fp()->fun()->flags & JSFUN_HEAVYWEIGHT); - JS_ASSERT(!cx->fp()->fun()->optimizedClosure()); - JS_ASSERT(FUN_FLAT_CLOSURE(fun)); - - return WrapEscapingClosure(cx, cx->fp(), fun); -} - JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native, - uintN nargs, uintN attrs) +js_DefineFunction(JSContext *cx, HandleObject obj, jsid id, Native native, + uintN nargs, uintN attrs, AllocKind kind) { + RootId idRoot(cx, &id); + PropertyOp gop; StrictPropertyOp sop; - JSFunction *fun; + + RootedVarFunction fun(cx); if (attrs & JSFUN_STUB_GSOPS) { /* @@ -2897,67 +2325,24 @@ js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native, * for more on this. */ attrs &= ~JSFUN_STUB_GSOPS; - gop = PropertyStub; - sop = StrictPropertyStub; + gop = JS_PropertyStub; + sop = JS_StrictPropertyStub; } else { gop = NULL; sop = NULL; } - /* - * Historically, all objects have had a parent member as intrinsic scope - * chain link. We want to move away from this universal parent, but JS - * requires that function objects have something like parent (ES3 and ES5 - * call it the [[Scope]] internal property), to bake a particular static - * scope environment into each function object. - * - * All function objects thus have parent, including all native functions. - * All native functions defined by the JS_DefineFunction* APIs are created - * via the call below to js_NewFunction, which passes obj as the parent - * parameter, and so binds fun's parent to obj using JSObject::setParent, - * under js_NewFunction (in JSObject::init, called from NewObject -- see - * jsobjinlines.h). - * - * But JSObject::setParent sets the DELEGATE object flag on its receiver, - * to mark the object as a proto or parent of another object. Such objects - * may intervene in property lookups and scope chain searches, so require - * special handling when caching lookup and search results (since such - * intervening objects can in general grow shadowing properties later). - * - * Thus using setParent prematurely flags certain objects, notably class - * prototypes, so that defining native methods on them, where the method's - * name (e.g., toString) is already bound on Object.prototype, triggers - * shadowingShapeChange events and gratuitous shape regeneration. - * - * To fix this longstanding bug, we set check whether obj is already a - * delegate, and if not, then if js_NewFunction flagged obj as a delegate, - * we clear the flag. - * - * We thus rely on the fact that native functions (including indirect eval) - * do not use the property cache or equivalent JIT techniques that require - * this bit to be set on their parent-linked scope chain objects. - * - * Note: we keep API compatibility by setting parent to obj for all native - * function objects, even if obj->getGlobal() would suffice. This should be - * revisited when parent is narrowed to exist only for function objects and - * possibly a few prehistoric scope objects (e.g. event targets). - * - * FIXME: bug 611190. - */ - bool wasDelegate = obj->isDelegate(); - fun = js_NewFunction(cx, NULL, native, nargs, - attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO), + attrs & (JSFUN_FLAGS_MASK), obj, - JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL); + JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL, + kind); if (!fun) return NULL; - if (!wasDelegate && obj->isDelegate()) - obj->clearDelegate(); - - if (!obj->defineProperty(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK)) + if (!obj->defineGeneric(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK)) return NULL; + return fun; } @@ -2966,24 +2351,12 @@ JS_STATIC_ASSERT((JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) == 0); JSFunction * js_ValueToFunction(JSContext *cx, const Value *vp, uintN flags) { - JSObject *funobj; - if (!IsFunctionObject(*vp, &funobj)) { - js_ReportIsNotFunction(cx, vp, flags); - return NULL; - } - return GET_FUNCTION_PRIVATE(cx, funobj); -} - -JSObject * -js_ValueToFunctionObject(JSContext *cx, Value *vp, uintN flags) -{ - JSObject *funobj; - if (!IsFunctionObject(*vp, &funobj)) { + JSFunction *fun; + if (!IsFunctionObject(*vp, &fun)) { js_ReportIsNotFunction(cx, vp, flags); return NULL; } - - return funobj; + return fun; } JSObject * @@ -3005,7 +2378,6 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags) const char *name = NULL, *source = NULL; AutoValueRooter tvr(cx); uintN error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION; - LeaveTrace(cx); /* * We try to the print the code that produced vp if vp is a value in the @@ -3022,9 +2394,6 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags) ptrdiff_t spindex = 0; FrameRegsIter i(cx); - while (!i.done() && !i.pc()) - ++i; - if (!i.done()) { uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc()); Value *simsp = i.fp()->base() + depth; diff --git a/deps/mozjs/js/src/jsfun.h b/deps/mozjs/js/src/jsfun.h index 176627b0739..d194686da16 100644 --- a/deps/mozjs/js/src/jsfun.h +++ b/deps/mozjs/js/src/jsfun.h @@ -48,7 +48,8 @@ #include "jsatom.h" #include "jsscript.h" #include "jsstr.h" -#include "jsopcode.h" + +#include "gc/Barrier.h" /* * The high two bits of JSFunction.flags encode whether the function is native @@ -60,14 +61,14 @@ * 10 interpreted, flat closure * 11 interpreted, null closure * - * FUN_FLAT_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset != 0. - * FUN_NULL_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset == 0. + * isFlatClosure() implies isInterpreted() and u.i.script->upvarsOffset != 0. + * isNullClosure() implies isInterpreted() and u.i.script->upvarsOffset == 0. * - * FUN_INTERPRETED but not FUN_FLAT_CLOSURE and u.i.script->upvarsOffset != 0 + * isInterpreted() but not isFlatClosure() and u.i.script->upvarsOffset != 0 * is an Algol-like function expression or nested function, i.e., a function * that never escapes upward or downward (heapward), and is only ever called. * - * Finally, FUN_INTERPRETED and u.i.script->upvarsOffset == 0 could be either + * Finally, isInterpreted() and u.i.script->upvarsOffset == 0 could be either * a non-closure (a global function definition, or any function that uses no * outer names), or a closure of an escaping function that uses outer names * whose values can't be snapshot (because the outer names could be reassigned @@ -80,8 +81,8 @@ * NB: JSFUN_EXPR_CLOSURE reuses JSFUN_STUB_GSOPS, which is an API request flag * bit only, never stored in fun->flags. * - * If we need more bits in the future, all flags for FUN_INTERPRETED functions - * can move to u.i.script->flags. For now we use function flag bits to minimize + * If we need more bits in the future, all flags for interpreted functions can + * move to u.i.script->flags. For now we use function flag bits to minimize * pointer-chasing. */ #define JSFUN_JOINABLE 0x0001 /* function is null closure that does not @@ -92,80 +93,65 @@ global object */ #define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */ -#define JSFUN_TRCINFO 0x2000 /* when set, u.n.trcinfo is non-null, - JSFunctionSpec::call points to a - JSNativeTraceInfo. */ +#define JSFUN_EXTENDED 0x2000 /* structure is FunctionExtended */ #define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */ #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 */ -#define FUN_OBJECT(fun) (static_cast(fun)) -#define FUN_KIND(fun) ((fun)->flags & JSFUN_KINDMASK) -#define FUN_SET_KIND(fun,k) ((fun)->flags = ((fun)->flags & ~JSFUN_KINDMASK) | (k)) -#define FUN_INTERPRETED(fun) (FUN_KIND(fun) >= JSFUN_INTERPRETED) -#define FUN_FLAT_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_FLAT_CLOSURE) -#define FUN_NULL_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_NULL_CLOSURE) -#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) -#define FUN_CLASP(fun) (JS_ASSERT(!FUN_INTERPRETED(fun)), \ - fun->u.n.clasp) -#define FUN_TRCINFO(fun) (JS_ASSERT(!FUN_INTERPRETED(fun)), \ - JS_ASSERT((fun)->flags & JSFUN_TRCINFO), \ - fun->u.n.trcinfo) - -struct JSFunction : public JSObject_Slots2 -{ - /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */ +namespace js { class FunctionExtended; } - uint16 nargs; /* maximum number of specified arguments, +struct JSFunction : public JSObject +{ + uint16_t nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ - uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */ + uint16_t flags; /* flags, see JSFUN_* below and in jsapi.h */ union U { - struct { + struct Native { js::Native native; /* native method pointer or null */ js::Class *clasp; /* class of objects constructed by this function */ - JSNativeTraceInfo *trcinfo; } n; struct Scripted { - JSScript *script; /* interpreted bytecode descriptor or null */ - uint16 skipmin; /* net skip amount up (toward zero) from - script->staticLevel to nearest upvar, - including upvars in nested functions */ - JSPackedBool wrapper; /* true if this function is a wrapper that - rewrites bytecode optimized for a function - judged non-escaping by the compiler, which - then escaped via the debugger or a rogue - indirect eval; if true, then this function - object's proto is the wrapped object */ - js::Shape *names; /* argument and variable names */ + JSScript *script_; /* interpreted bytecode descriptor or null; + use the accessor! */ + JSObject *env_; /* environment for new activations; + use the accessor! */ } i; void *nativeOrScript; } u; JSAtom *atom; /* name for diagnostics and decompiling */ - bool optimizedClosure() const { return FUN_KIND(this) > JSFUN_INTERPRETED; } - bool needsWrapper() const { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; } - bool isInterpreted() const { return FUN_INTERPRETED(this); } - bool isNative() const { return !FUN_INTERPRETED(this); } - bool isConstructor() const { return flags & JSFUN_CONSTRUCTOR; } + bool optimizedClosure() const { return kind() > JSFUN_INTERPRETED; } + bool isInterpreted() const { return kind() >= JSFUN_INTERPRETED; } + bool isNative() const { return !isInterpreted(); } + bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; } bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); } - bool isFlatClosure() const { return FUN_KIND(this) == JSFUN_FLAT_CLOSURE; } + bool isNullClosure() const { return kind() == JSFUN_NULL_CLOSURE; } + bool isFlatClosure() const { return kind() == JSFUN_FLAT_CLOSURE; } bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; } bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); } + + uint16_t kind() const { return flags & JSFUN_KINDMASK; } + void setKind(uint16_t k) { + JS_ASSERT(!(k & ~JSFUN_KINDMASK)); + flags = (flags & ~JSFUN_KINDMASK) | k; + } + /* Returns the strictness of this function, which must be interpreted. */ inline bool inStrictMode() const; - void setArgCount(uint16 nargs) { + + void setArgCount(uint16_t nargs) { JS_ASSERT(this->nargs == 0); this->nargs = nargs; } - /* uint16 representation bounds number of call object dynamic slots. */ + /* uint16_t representation bounds number of call object dynamic slots. */ enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) }; -#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~(jsuword) 1)) -#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & (jsuword) 1)) != 0) +#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~uintptr_t(1))) +#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & uintptr_t(1))) != 0) bool mightEscape() const { return isInterpreted() && (isFlatClosure() || !script()->bindings.hasUpvars()); @@ -175,351 +161,211 @@ struct JSFunction : public JSObject_Slots2 return flags & JSFUN_JOINABLE; } - JSObject &compiledFunObj() { - return *this; - } - - private: /* - * js_FunctionClass reserves two slots, which are free in JSObject::fslots - * without requiring dslots allocation. Null closures that can be joined to - * a compiler-created function object use the first one to hold a mutable - * methodAtom() state variable, needed for correct foo.caller handling. + * For an interpreted function, accessors for the initial scope object of + * activations (stack frames) of the function. */ - enum { - METHOD_ATOM_SLOT = JSSLOT_FUN_METHOD_ATOM - }; + inline JSObject *environment() const; + inline void setEnvironment(JSObject *obj); + inline void initEnvironment(JSObject *obj); - public: - void setJoinable() { - JS_ASSERT(FUN_INTERPRETED(this)); - getSlotRef(METHOD_ATOM_SLOT).setNull(); - flags |= JSFUN_JOINABLE; - } + static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); } - /* - * Method name imputed from property uniquely assigned to or initialized, - * where the function does not need to be cloned to carry a scope chain or - * flattened upvars. - */ - JSAtom *methodAtom() const { - return (joinable() && getSlot(METHOD_ATOM_SLOT).isString()) - ? &getSlot(METHOD_ATOM_SLOT).toString()->asAtom() - : NULL; + inline void setJoinable(); + + js::HeapPtrScript &script() const { + JS_ASSERT(isInterpreted()); + return *(js::HeapPtrScript *)&u.i.script_; } - void setMethodAtom(JSAtom *atom) { - JS_ASSERT(joinable()); - getSlotRef(METHOD_ATOM_SLOT).setString(atom); + inline void setScript(JSScript *script_); + inline void initScript(JSScript *script_); + + JSScript *maybeScript() const { + return isInterpreted() ? script().get() : NULL; } - js::Native maybeNative() const { - return isInterpreted() ? NULL : u.n.native; + JSNative native() const { + JS_ASSERT(isNative()); + return u.n.native; } - JSScript *script() const { - JS_ASSERT(isInterpreted()); - return u.i.script; + JSNative maybeNative() const { + return isInterpreted() ? NULL : native(); } static uintN offsetOfNativeOrScript() { - JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script)); + JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_)); JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript)); return offsetof(JSFunction, u.nativeOrScript); } - /* Number of extra fixed function object slots. */ - static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS; -}; + js::Class *getConstructorClass() const { + JS_ASSERT(isNative()); + return u.n.clasp; + } -/* - * Trace-annotated native. This expands to a JSFunctionSpec initializer (like - * JS_FN in jsapi.h). fastcall is a FastNative; trcinfo is a - * JSNativeTraceInfo*. - */ -#ifdef JS_TRACER -/* MSVC demands the intermediate (void *) cast here. */ -# define JS_TN(name,fastcall,nargs,flags,trcinfo) \ - JS_FN(name, JS_DATA_TO_FUNC_PTR(Native, trcinfo), nargs, \ - (flags) | JSFUN_STUB_GSOPS | JSFUN_TRCINFO) + void setConstructorClass(js::Class *clasp) { + JS_ASSERT(isNative()); + u.n.clasp = clasp; + } + +#if JS_BITS_PER_WORD == 32 + static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2; + static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4; #else -# define JS_TN(name,fastcall,nargs,flags,trcinfo) \ - JS_FN(name, fastcall, nargs, flags) + static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4; + static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8; #endif -/* - * NB: the Arguments classes are uninitialized internal classes that masquerade - * (according to Object.prototype.toString.call(arguments)) as "Arguments", - * while having Object.getPrototypeOf(arguments) === Object.prototype. - * - * WARNING (to alert embedders reading this private .h file): arguments objects - * are *not* thread-safe and should not be used concurrently -- they should be - * used by only one thread at a time, preferably by only one thread over their - * lifetime (a JS worker that migrates from one OS thread to another but shares - * nothing is ok). - * - * Yes, this is an incompatible change, which prefigures the impending move to - * single-threaded objects and GC heaps. - */ -extern js::Class js_ArgumentsClass; + inline void trace(JSTracer *trc); -namespace js { + /* Bound function accessors. */ -extern Class StrictArgumentsClass; + inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg, + const js::Value *args, uintN argslen); -struct ArgumentsData { - js::Value callee; - js::Value slots[1]; -}; + inline JSObject *getBoundFunctionTarget() const; + inline const js::Value &getBoundFunctionThis() const; + inline const js::Value &getBoundFunctionArgument(uintN which) const; + inline size_t getBoundFunctionArgumentCount() const; -} + private: + inline js::FunctionExtended *toExtended(); + inline const js::FunctionExtended *toExtended() const; -inline bool -JSObject::isNormalArguments() const -{ - return getClass() == &js_ArgumentsClass; -} + inline bool isExtended() const { + JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind); + JS_ASSERT(!!(flags & JSFUN_EXTENDED) == (getAllocKind() == ExtendedFinalizeKind)); + return !!(flags & JSFUN_EXTENDED); + } -inline bool -JSObject::isStrictArguments() const -{ - return getClass() == &js::StrictArgumentsClass; -} + public: + /* Accessors for data stored in extended functions. */ -inline bool -JSObject::isArguments() const -{ - return isNormalArguments() || isStrictArguments(); -} + inline void initializeExtended(); -#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126) + inline void setExtendedSlot(size_t which, const js::Value &val); + inline const js::Value &getExtendedSlot(size_t which) const; -extern JS_PUBLIC_DATA(js::Class) js_CallClass; -extern JS_PUBLIC_DATA(js::Class) js_FunctionClass; -extern js::Class js_DeclEnvClass; + /* + * Flat closures with one or more upvars snapshot the upvars' values + * into a vector of js::Values referenced from here. This is a private + * pointer but is set only at creation and does not need to be barriered. + */ + static const uint32_t FLAT_CLOSURE_UPVARS_SLOT = 0; -inline bool -JSObject::isCall() const -{ - return getClass() == &js_CallClass; -} + static inline size_t getFlatClosureUpvarsOffset(); -inline bool -JSObject::isFunction() const -{ - return getClass() == &js_FunctionClass; -} + inline js::Value getFlatClosureUpvar(uint32_t i) const; + inline void setFlatClosureUpvar(uint32_t i, const js::Value &v); + inline void initFlatClosureUpvar(uint32_t i, const js::Value &v); -inline JSFunction * -JSObject::getFunctionPrivate() const -{ - JS_ASSERT(isFunction()); - return reinterpret_cast(getPrivate()); -} + private: + inline bool hasFlatClosureUpvars() const; + inline js::HeapValue *getFlatClosureUpvars() const; + public: -namespace js { + /* See comments in fun_finalize. */ + inline void finalizeUpvars(); -/* - * NB: jsapi.h and jsobj.h must be included before any call to this macro. - */ -#define VALUE_IS_FUNCTION(cx, v) \ - (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isFunction()) + /* Slot holding associated method property, needed for foo.caller handling. */ + static const uint32_t METHOD_PROPERTY_SLOT = 0; -static JS_ALWAYS_INLINE bool -IsFunctionObject(const js::Value &v) -{ - return v.isObject() && v.toObject().isFunction(); -} + /* For cloned methods, slot holding the object this was cloned as a property from. */ + static const uint32_t METHOD_OBJECT_SLOT = 1; -static JS_ALWAYS_INLINE bool -IsFunctionObject(const js::Value &v, JSObject **funobj) -{ - return v.isObject() && (*funobj = &v.toObject())->isFunction(); -} + /* Whether this is a function cloned from a method. */ + inline bool isClonedMethod() const; -static JS_ALWAYS_INLINE bool -IsFunctionObject(const js::Value &v, JSFunction **fun) -{ - JSObject *funobj; - bool b = IsFunctionObject(v, &funobj); - if (b) - *fun = funobj->getFunctionPrivate(); - return b; -} + /* For a cloned method, pointer to the object the method was cloned for. */ + inline JSObject *methodObj() const; + inline void setMethodObj(JSObject& obj); -extern JS_ALWAYS_INLINE bool -SameTraceType(const Value &lhs, const Value &rhs) -{ - return SameType(lhs, rhs) && - (lhs.isPrimitive() || - lhs.toObject().isFunction() == rhs.toObject().isFunction()); -} + /* + * Method name imputed from property uniquely assigned to or initialized, + * where the function does not need to be cloned to carry a scope chain or + * flattened upvars. This is set on both the original and cloned function. + */ + inline JSAtom *methodAtom() const; + inline void setMethodAtom(JSAtom *atom); -/* - * Macro to access the private slot of the function object after the slot is - * initialized. - */ -#define GET_FUNCTION_PRIVATE(cx, funobj) \ - (JS_ASSERT((funobj)->isFunction()), \ - (JSFunction *) (funobj)->getPrivate()) + /* + * Measures things hanging off this JSFunction that are counted by the + * |miscSize| argument in JSObject::sizeOfExcludingThis(). + */ + size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const; -/* - * Return true if this is a compiler-created internal function accessed by - * its own object. Such a function object must not be accessible to script - * or embedding code. - */ -inline bool -IsInternalFunctionObject(JSObject *funobj) -{ - JS_ASSERT(funobj->isFunction()); - JSFunction *fun = (JSFunction *) funobj->getPrivate(); - return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent(); -} - -/* Valueified JS_IsConstructing. */ -static JS_ALWAYS_INLINE bool -IsConstructing(const Value *vp) -{ -#ifdef DEBUG - JSObject *callee = &JS_CALLEE(cx, vp).toObject(); - if (callee->isFunction()) { - JSFunction *fun = callee->getFunctionPrivate(); - JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0); - } else { - JS_ASSERT(callee->getClass()->construct != NULL); - } -#endif - return vp[1].isMagic(); -} + private: + /* + * These member functions are inherited from JSObject, but should never be applied to + * a value statically known to be a JSFunction. + */ + inline JSFunction *toFunction() MOZ_DELETE; + inline const JSFunction *toFunction() const MOZ_DELETE; +}; -static JS_ALWAYS_INLINE bool -IsConstructing_PossiblyWithGivenThisObject(const Value *vp, JSObject **ctorThis) +inline JSFunction * +JSObject::toFunction() { -#ifdef DEBUG - JSObject *callee = &JS_CALLEE(cx, vp).toObject(); - if (callee->isFunction()) { - JSFunction *fun = callee->getFunctionPrivate(); - JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0); - } else { - JS_ASSERT(callee->getClass()->construct != NULL); - } -#endif - bool isCtor = vp[1].isMagic(); - if (isCtor) - *ctorThis = vp[1].getMagicObjectOrNullPayload(); - return isCtor; + JS_ASSERT(JS_ObjectIsFunction(NULL, this)); + return static_cast(this); } -inline const char * -GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes) +inline const JSFunction * +JSObject::toFunction() const { - if (fun->atom) - return bytes->encode(cx, fun->atom); - return js_anonymous_str; + JS_ASSERT(JS_ObjectIsFunction(NULL, const_cast(this))); + return static_cast(this); } -extern bool -IsBuiltinFunctionConstructor(JSFunction *fun); - -/* - * Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() && - * !funobj->isBoundFunction(). This is sufficient to establish that funobj has - * a non-configurable non-method .prototype data property, thought it might not - * have been resolved yet, and its value could be anything. - * - * Return the shape of the .prototype property of funobj, resolving it if - * needed. On error, return NULL. - * - * This is not safe to call on trace because it defines properties, which can - * trigger lookups that could reenter. - */ -const Shape * -LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj); - -} /* namespace js */ - extern JSString * fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent); extern JSFunction * -js_NewFunction(JSContext *cx, JSObject *funobj, js::Native native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom); - -extern JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitArgumentsClass(JSContext *cx, JSObject *obj); - -extern void -js_TraceFunction(JSTracer *trc, JSFunction *fun); - -extern void -js_FinalizeFunction(JSContext *cx, JSFunction *fun); +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, + uintN flags, js::HandleObject parent, JSAtom *atom, + js::gc::AllocKind kind = JSFunction::FinalizeKind); -extern JSObject * JS_FASTCALL -js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, - JSObject *proto); +extern JSFunction * JS_FASTCALL +js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto, + js::gc::AllocKind kind = JSFunction::FinalizeKind); -inline JSObject * -CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent) -{ - JS_ASSERT(parent); - JSObject *proto; - if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto)) - return NULL; - return js_CloneFunctionObject(cx, fun, parent, proto); -} - -extern JSObject * JS_FASTCALL +extern JSFunction * JS_FASTCALL js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain); -extern JSObject * -js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen); - -extern JS_REQUIRES_STACK JSObject * -js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun); +extern JSFunction * +js_NewFlatClosure(JSContext *cx, JSFunction *fun); extern JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, js::Native native, - uintN nargs, uintN flags); +js_DefineFunction(JSContext *cx, js::HandleObject obj, jsid id, JSNative native, + uintN nargs, uintN flags, + js::gc::AllocKind kind = JSFunction::FinalizeKind); /* - * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the - * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that - * with #if/#error in jsfun.c. + * Flags for js_ValueToFunction and js_ReportIsNotFunction. */ -#define JSV2F_CONSTRUCT ((uintN)js::INVOKE_CONSTRUCTOR) +#define JSV2F_CONSTRUCT INITIAL_CONSTRUCT #define JSV2F_SEARCH_STACK 0x10000 extern JSFunction * js_ValueToFunction(JSContext *cx, const js::Value *vp, uintN flags); -extern JSObject * -js_ValueToFunctionObject(JSContext *cx, js::Value *vp, uintN flags); - extern JSObject * js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags); extern void js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags); -extern JSObject * JS_FASTCALL -js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain); - extern void js_PutCallObject(js::StackFrame *fp); -extern JSBool JS_FASTCALL -js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv, - uint32 nvars, js::Value *slots); - namespace js { -JSObject * +CallObject * CreateFunCallObject(JSContext *cx, StackFrame *fp); -JSObject * +CallObject * CreateEvalCallObject(JSContext *cx, StackFrame *fp); extern JSBool @@ -528,13 +374,6 @@ GetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); extern JSBool GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); -/* - * Slower version of js_GetCallVar used when call_resolve detects an attempt to - * leak an optimized closure via indirect or debugger eval. - */ -extern JSBool -GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); - extern JSBool GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); @@ -547,13 +386,37 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp); extern JSBool SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp); +/* + * Function extended with reserved slots for use by various kinds of functions. + * Most functions do not have these extensions, but enough are that efficient + * storage is required (no malloc'ed reserved slots). + */ +class FunctionExtended : public JSFunction +{ + friend struct JSFunction; + + /* Reserved slots available for storage by particular native functions. */ + HeapValue extendedSlots[2]; +}; + } // namespace js -extern JSBool -js_GetArgsValue(JSContext *cx, js::StackFrame *fp, js::Value *vp); +inline js::FunctionExtended * +JSFunction::toExtended() +{ + JS_ASSERT(isExtended()); + return static_cast(this); +} + +inline const js::FunctionExtended * +JSFunction::toExtended() const +{ + JS_ASSERT(isExtended()); + return static_cast(this); +} extern JSBool -js_GetArgsProperty(JSContext *cx, js::StackFrame *fp, jsid id, js::Value *vp); +js_GetArgsValue(JSContext *cx, js::StackFrame *fp, js::Value *vp); /* * Get the arguments object for the given frame. If the frame is strict mode @@ -574,25 +437,6 @@ js_PutArgsObject(js::StackFrame *fp); inline bool js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; } -/* - * Maximum supported value of arguments.length. It bounds the maximum number of - * arguments that can be supplied via the second (so-called |argArray|) param - * to Function.prototype.apply. This value also bounds the number of elements - * parsed in an array initialiser. - * - * The thread's stack is the limiting factor for this number. It is currently - * 2MB, which fits a little less than 2^19 arguments (once the stack frame, - * callstack, etc. are included). Pick a max args length that is a little less. - */ -const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024; - -/* - * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as an Int32 - * Value. Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must be less than JSVAL_INT_MAX. - */ -JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30)); -JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX); - extern JSBool js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); diff --git a/deps/mozjs/js/src/jsfuninlines.h b/deps/mozjs/js/src/jsfuninlines.h index 4cbb744b236..0ae91f846c7 100644 --- a/deps/mozjs/js/src/jsfuninlines.h +++ b/deps/mozjs/js/src/jsfuninlines.h @@ -43,10 +43,368 @@ #include "jsfun.h" #include "jsscript.h" +#include "vm/GlobalObject.h" + +#include "vm/ScopeObject-inl.h" + inline bool JSFunction::inStrictMode() const { return script()->strictModeCode; } +inline JSObject * +JSFunction::environment() const +{ + JS_ASSERT(isInterpreted()); + return u.i.env_; +} + +inline void +JSFunction::setEnvironment(JSObject *obj) +{ + JS_ASSERT(isInterpreted()); + *(js::HeapPtrObject *)&u.i.env_ = obj; +} + +inline void +JSFunction::initEnvironment(JSObject *obj) +{ + JS_ASSERT(isInterpreted()); + ((js::HeapPtrObject *)&u.i.env_)->init(obj); +} + +inline void +JSFunction::initializeExtended() +{ + JS_ASSERT(isExtended()); + + JS_ASSERT(js::ArrayLength(toExtended()->extendedSlots) == 2); + toExtended()->extendedSlots[0].init(js::UndefinedValue()); + toExtended()->extendedSlots[1].init(js::UndefinedValue()); +} + +inline void +JSFunction::setJoinable() +{ + JS_ASSERT(isInterpreted()); + flags |= JSFUN_JOINABLE; +} + +inline bool +JSFunction::isClonedMethod() const +{ + return joinable() && isExtended() && getExtendedSlot(METHOD_OBJECT_SLOT).isObject(); +} + +inline JSAtom * +JSFunction::methodAtom() const +{ + return (joinable() && isExtended() && getExtendedSlot(METHOD_PROPERTY_SLOT).isString()) + ? (JSAtom *) getExtendedSlot(METHOD_PROPERTY_SLOT).toString() + : NULL; +} + +inline void +JSFunction::setMethodAtom(JSAtom *atom) +{ + JS_ASSERT(joinable()); + setExtendedSlot(METHOD_PROPERTY_SLOT, js::StringValue(atom)); +} + +inline JSObject * +JSFunction::methodObj() const +{ + JS_ASSERT(joinable()); + return isClonedMethod() ? &getExtendedSlot(METHOD_OBJECT_SLOT).toObject() : NULL; +} + +inline void +JSFunction::setMethodObj(JSObject& obj) +{ + JS_ASSERT(joinable()); + setExtendedSlot(METHOD_OBJECT_SLOT, js::ObjectValue(obj)); +} + +inline void +JSFunction::setExtendedSlot(size_t which, const js::Value &val) +{ + JS_ASSERT(which < js::ArrayLength(toExtended()->extendedSlots)); + toExtended()->extendedSlots[which] = val; +} + +inline const js::Value & +JSFunction::getExtendedSlot(size_t which) const +{ + JS_ASSERT(which < js::ArrayLength(toExtended()->extendedSlots)); + return toExtended()->extendedSlots[which]; +} + +inline bool +JSFunction::hasFlatClosureUpvars() const +{ + JS_ASSERT(isFlatClosure()); + return isExtended() && !getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).isUndefined(); +} + +inline js::HeapValue * +JSFunction::getFlatClosureUpvars() const +{ + JS_ASSERT(hasFlatClosureUpvars()); + return (js::HeapValue *) getExtendedSlot(FLAT_CLOSURE_UPVARS_SLOT).toPrivate(); +} + +inline void +JSFunction::finalizeUpvars() +{ + /* + * Cloned function objects may be flat closures with upvars to free. + * + * We must not access JSScript here that is stored in JSFunction. The + * script can be finalized before the function or closure instances. So we + * just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded + * as a double. We must also ignore newborn closures that do not have the + * private pointer set. + * + * FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it + * here explicitly. + */ + if (hasFlatClosureUpvars()) { + js::HeapValue *upvars = getFlatClosureUpvars(); + js::Foreground::free_(upvars); + } +} + +inline js::Value +JSFunction::getFlatClosureUpvar(uint32_t i) const +{ + JS_ASSERT(hasFlatClosureUpvars()); + JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length); + JS_ASSERT(i < script()->bindings.countUpvars()); + return getFlatClosureUpvars()[i]; +} + +inline void +JSFunction::setFlatClosureUpvar(uint32_t i, const js::Value &v) +{ + JS_ASSERT(isFlatClosure()); + JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length); + JS_ASSERT(i < script()->bindings.countUpvars()); + getFlatClosureUpvars()[i] = v; +} + +inline void +JSFunction::initFlatClosureUpvar(uint32_t i, const js::Value &v) +{ + JS_ASSERT(isFlatClosure()); + JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length); + JS_ASSERT(i < script()->bindings.countUpvars()); + getFlatClosureUpvars()[i].init(v); +} + +/* static */ inline size_t +JSFunction::getFlatClosureUpvarsOffset() +{ + return offsetof(js::FunctionExtended, extendedSlots[FLAT_CLOSURE_UPVARS_SLOT]); +} + +namespace js { + +static JS_ALWAYS_INLINE bool +IsFunctionObject(const js::Value &v) +{ + return v.isObject() && v.toObject().isFunction(); +} + +static JS_ALWAYS_INLINE bool +IsFunctionObject(const js::Value &v, JSFunction **fun) +{ + if (v.isObject() && v.toObject().isFunction()) { + *fun = v.toObject().toFunction(); + return true; + } + return false; +} + +static JS_ALWAYS_INLINE bool +IsNativeFunction(const js::Value &v) +{ + JSFunction *fun; + return IsFunctionObject(v, &fun) && fun->isNative(); +} + +static JS_ALWAYS_INLINE bool +IsNativeFunction(const js::Value &v, JSFunction **fun) +{ + return IsFunctionObject(v, fun) && (*fun)->isNative(); +} + +static JS_ALWAYS_INLINE bool +IsNativeFunction(const js::Value &v, JSNative native) +{ + JSFunction *fun; + return IsFunctionObject(v, &fun) && fun->maybeNative() == native; +} + +/* + * When we have an object of a builtin class, we don't quite know what its + * valueOf/toString methods are, since these methods may have been overwritten + * or shadowed. However, we can still do better than the general case by + * hard-coding the necessary properties for us to find the native we expect. + * + * TODO: a per-thread shape-based cache would be faster and simpler. + */ +static JS_ALWAYS_INLINE bool +ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid, JSNative native) +{ + JS_ASSERT(obj->getClass() == clasp); + + Value v; + if (!HasDataProperty(cx, obj, methodid, &v)) { + JSObject *proto = obj->getProto(); + if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v)) + return false; + } + + return js::IsNativeFunction(v, native); +} + +extern JS_ALWAYS_INLINE bool +SameTraceType(const Value &lhs, const Value &rhs) +{ + return SameType(lhs, rhs) && + (lhs.isPrimitive() || + lhs.toObject().isFunction() == rhs.toObject().isFunction()); +} + +/* Valueified JS_IsConstructing. */ +static JS_ALWAYS_INLINE bool +IsConstructing(const Value *vp) +{ +#ifdef DEBUG + JSObject *callee = &JS_CALLEE(cx, vp).toObject(); + if (callee->isFunction()) { + JSFunction *fun = callee->toFunction(); + JS_ASSERT((fun->flags & JSFUN_CONSTRUCTOR) != 0); + } else { + JS_ASSERT(callee->getClass()->construct != NULL); + } +#endif + return vp[1].isMagic(); +} + +inline bool +IsConstructing(CallReceiver call) +{ + return IsConstructing(call.base()); +} + +inline const char * +GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes) +{ + if (fun->atom) + return bytes->encode(cx, fun->atom); + return js_anonymous_str; +} + +extern JSFunctionSpec function_methods[]; + +extern JSBool +Function(JSContext *cx, uintN argc, Value *vp); + +extern bool +IsBuiltinFunctionConstructor(JSFunction *fun); + +/* + * Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() && + * !funobj->isBoundFunction(). This is sufficient to establish that funobj has + * a non-configurable non-method .prototype data property, thought it might not + * have been resolved yet, and its value could be anything. + * + * Return the shape of the .prototype property of funobj, resolving it if + * needed. On error, return NULL. + * + * This is not safe to call on trace because it defines properties, which can + * trigger lookups that could reenter. + */ +const Shape * +LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj); + +static inline JSObject * +SkipScopeParent(JSObject *parent) +{ + if (!parent) + return NULL; + while (parent->isScope()) + parent = &parent->asScope().enclosingScope(); + return parent; +} + +inline JSFunction * +CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, + gc::AllocKind kind = JSFunction::FinalizeKind) +{ + JS_ASSERT(parent); + JSObject *proto = parent->global().getOrCreateFunctionPrototype(cx); + if (!proto) + return NULL; + + return js_CloneFunctionObject(cx, fun, parent, proto, kind); +} + +inline JSFunction * +CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent) +{ + /* + * For attempts to clone functions at a function definition opcode or from + * a method barrier, don't perform the clone if the function has singleton + * type. This was called pessimistically, and we need to preserve the + * type's property that if it is singleton there is only a single object + * with its type in existence. + */ + if (fun->hasSingletonType()) { + if (!fun->setParent(cx, SkipScopeParent(parent))) + return NULL; + fun->setEnvironment(parent); + return fun; + } + + return CloneFunctionObject(cx, fun, parent); +} + +inline JSFunction * +CloneFunctionObject(JSContext *cx, JSFunction *fun) +{ + /* + * Variant which makes an exact clone of fun, preserving parent and proto. + * Calling the above version CloneFunctionObject(cx, fun, fun->getParent()) + * is not equivalent: API clients, including XPConnect, can reparent + * objects so that fun->global() != fun->getProto()->global(). + * See ReparentWrapperIfFound. + */ + JS_ASSERT(fun->getParent() && fun->getProto()); + + if (fun->hasSingletonType()) + return fun; + + return js_CloneFunctionObject(cx, fun, fun->environment(), fun->getProto(), + JSFunction::ExtendedFinalizeKind); +} + +} /* namespace js */ + +inline void +JSFunction::setScript(JSScript *script_) +{ + JS_ASSERT(isInterpreted()); + script() = script_; +} + +inline void +JSFunction::initScript(JSScript *script_) +{ + JS_ASSERT(isInterpreted()); + script().init(script_); +} + #endif /* jsfuninlines_h___ */ diff --git a/deps/mozjs/js/src/jsgc.cpp b/deps/mozjs/js/src/jsgc.cpp index d290da82fa7..f92f1e5bfc6 100644 --- a/deps/mozjs/js/src/jsgc.cpp +++ b/deps/mozjs/js/src/jsgc.cpp @@ -38,9 +38,12 @@ * * ***** END LICENSE BLOCK ***** */ +/* JS Mark-and-Sweep Garbage Collector. */ + +#include "mozilla/Attributes.h" +#include "mozilla/Util.h" + /* - * JS Mark-and-Sweep Garbage Collector. - * * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see * jsgc.h). It allocates from a special GC arena pool with each arena allocated * using malloc. It uses an ideally parallel array of flag bytes to hold the @@ -50,47 +53,50 @@ */ #include #include /* for memset used when DEBUG */ + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jshash.h" -#include "jsbit.h" #include "jsclist.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" +#include "jscompartment.h" +#include "jscrashreport.h" +#include "jscrashformat.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" -#include "jsgcchunk.h" #include "jsgcmark.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsparse.h" +#include "jsprobes.h" #include "jsproxy.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" -#include "jsstr.h" -#include "methodjit/MethodJIT.h" - +#include "jswatchpoint.h" +#include "jsweakmap.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif -#include "jsprobes.h" +#include "frontend/Parser.h" +#include "gc/Memory.h" +#include "methodjit/MethodJIT.h" +#include "vm/Debugger.h" +#include "vm/String.h" + +#include "jsinterpinlines.h" #include "jsobjinlines.h" -#include "jshashtable.h" -#include "jsweakmap.h" -#include "jsstrinlines.h" -#include "jscompartment.h" +#include "vm/ScopeObject-inl.h" +#include "vm/String-inl.h" #ifdef MOZ_VALGRIND # define JS_VALGRIND @@ -99,39 +105,29 @@ # include #endif -using namespace js; -using namespace js::gc; - -/* - * Check that JSTRACE_XML follows JSTRACE_OBJECT and JSTRACE_STRING. - */ -JS_STATIC_ASSERT(JSTRACE_OBJECT == 0); -JS_STATIC_ASSERT(JSTRACE_STRING == 1); -JS_STATIC_ASSERT(JSTRACE_SHAPE == 2); -JS_STATIC_ASSERT(JSTRACE_XML == 3); - -/* - * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_SHAPE is the last non-xml - * trace kind when JS_HAS_XML_SUPPORT is false. - */ -JS_STATIC_ASSERT(JSTRACE_SHAPE + 1 == JSTRACE_XML); - -#ifdef JS_GCMETER -# define METER(x) ((void) (x)) -# define METER_IF(condition, x) ((void) ((condition) && (x))) +#ifdef XP_WIN +# include "jswin.h" #else -# define METER(x) ((void) 0) -# define METER_IF(condition, x) ((void) 0) +# include #endif -# define METER_UPDATE_MAX(maxLval, rval) \ - METER_IF((maxLval) < (rval), (maxLval) = (rval)) +using namespace mozilla; +using namespace js; +using namespace js::gc; namespace js { namespace gc { +#ifdef JS_GC_ZEAL +static void +StartVerifyBarriers(JSContext *cx); + +static void +EndVerifyBarriers(JSContext *cx); +#endif + /* This array should be const, but that doesn't link right under GCC. */ -FinalizeKind slotsToThingKind[] = { +AllocKind slotsToThingKind[] = { /* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4, /* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8, /* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12, @@ -141,444 +137,737 @@ FinalizeKind slotsToThingKind[] = { JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT); -#ifdef DEBUG -const uint8 GCThingSizeMap[] = { +const uint32_t Arena::ThingSizes[] = { sizeof(JSObject), /* FINALIZE_OBJECT0 */ sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ - sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */ - sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2_BACKGROUND */ - sizeof(JSObject_Slots4), /* FINALIZE_OBJECT4 */ - sizeof(JSObject_Slots4), /* FINALIZE_OBJECT4_BACKGROUND */ - sizeof(JSObject_Slots8), /* FINALIZE_OBJECT8 */ - sizeof(JSObject_Slots8), /* FINALIZE_OBJECT8_BACKGROUND */ + sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */ + sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2_BACKGROUND */ + sizeof(JSObject_Slots4), /* FINALIZE_OBJECT4 */ + sizeof(JSObject_Slots4), /* FINALIZE_OBJECT4_BACKGROUND */ + sizeof(JSObject_Slots8), /* FINALIZE_OBJECT8 */ + sizeof(JSObject_Slots8), /* FINALIZE_OBJECT8_BACKGROUND */ sizeof(JSObject_Slots12), /* FINALIZE_OBJECT12 */ sizeof(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */ sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ - sizeof(JSFunction), /* FINALIZE_FUNCTION */ + sizeof(JSScript), /* FINALIZE_SCRIPT */ sizeof(Shape), /* FINALIZE_SHAPE */ + sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */ + sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ #if JS_HAS_XML_SUPPORT sizeof(JSXML), /* FINALIZE_XML */ #endif sizeof(JSShortString), /* FINALIZE_SHORT_STRING */ sizeof(JSString), /* FINALIZE_STRING */ - sizeof(JSString), /* FINALIZE_EXTERNAL_STRING */ + sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ }; -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GCThingSizeMap) == FINALIZE_LIMIT); +#define OFFSET(type) uint32_t(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type)) + +const uint32_t Arena::FirstThingOffsets[] = { + OFFSET(JSObject), /* FINALIZE_OBJECT0 */ + OFFSET(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2 */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2_BACKGROUND */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4 */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4_BACKGROUND */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8 */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8_BACKGROUND */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12 */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ + OFFSET(JSScript), /* FINALIZE_SCRIPT */ + OFFSET(Shape), /* FINALIZE_SHAPE */ + OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */ + OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ +#if JS_HAS_XML_SUPPORT + OFFSET(JSXML), /* FINALIZE_XML */ +#endif + OFFSET(JSShortString), /* FINALIZE_SHORT_STRING */ + OFFSET(JSString), /* FINALIZE_STRING */ + OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ +}; + +#undef OFFSET + +class GCCompartmentsIter { + private: + JSCompartment **it, **end; + + public: + GCCompartmentsIter(JSRuntime *rt) { + if (rt->gcCurrentCompartment) { + it = &rt->gcCurrentCompartment; + end = &rt->gcCurrentCompartment + 1; + } else { + it = rt->compartments.begin(); + end = rt->compartments.end(); + } + } + + bool done() const { return it == end; } + + void next() { + JS_ASSERT(!done()); + it++; + } + + JSCompartment *get() const { + JS_ASSERT(!done()); + return *it; + } -JS_FRIEND_API(size_t) -ArenaHeader::getThingSize() const + operator JSCompartment *() const { return get(); } + JSCompartment *operator->() const { return get(); } +}; + +#ifdef DEBUG +void +ArenaHeader::checkSynchronizedWithFreeList() const { - return GCThingSizeMap[getThingKind()]; + /* + * Do not allow to access the free list when its real head is still stored + * in FreeLists and is not synchronized with this one. + */ + JS_ASSERT(allocated()); + + /* + * We can be called from the background finalization thread when the free + * list in the compartment can mutate at any moment. We cannot do any + * checks in this case. + */ + if (!compartment->rt->gcRunning) + return; + + FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); + if (firstSpan.isEmpty()) + return; + const FreeSpan *list = compartment->arenas.getFreeList(getAllocKind()); + if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress()) + return; + + /* + * Here this arena has free things, FreeList::lists[thingKind] is not + * empty and also points to this arena. Thus they must the same. + */ + JS_ASSERT(firstSpan.isSameNonEmptySpan(list)); } #endif -/* Initialize the arena and setup the free list. */ -template -inline FreeCell * -Arena::buildFreeList() +/* static */ void +Arena::staticAsserts() { - T *first = &t.things[0]; - T *last = &t.things[JS_ARRAY_LENGTH(t.things) - 1]; - for (T *thing = first; thing != last;) { - T *following = thing + 1; - thing->asFreeCell()->link = following->asFreeCell(); - thing = following; - } - last->asFreeCell()->link = NULL; - return first->asFreeCell(); + JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT); } template inline bool -Arena::finalize(JSContext *cx) +Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background) { - JS_ASSERT(aheader.compartment); - JS_ASSERT(!aheader.getMarkingDelay()->link); - - FreeCell *nextFree = aheader.freeList; - FreeCell *freeList = NULL; - FreeCell **tailp = &freeList; - bool allClear = true; + /* Enforce requirements on size of T. */ + JS_ASSERT(thingSize % Cell::CellSize == 0); + JS_ASSERT(thingSize <= 255); - T *thingsEnd = &t.things[ThingsPerArena-1]; - T *thing = &t.things[0]; - thingsEnd++; + JS_ASSERT(aheader.allocated()); + JS_ASSERT(thingKind == aheader.getAllocKind()); + JS_ASSERT(thingSize == aheader.getThingSize()); + JS_ASSERT(!aheader.hasDelayedMarking); - if (!nextFree) { - nextFree = thingsEnd->asFreeCell(); - } else { - JS_ASSERT(thing->asFreeCell() <= nextFree); - JS_ASSERT(nextFree < thingsEnd->asFreeCell()); - } + uintptr_t thing = thingsStart(thingKind); + uintptr_t lastByte = thingsEnd() - 1; - for (;; thing++) { - if (thing->asFreeCell() == nextFree) { - if (thing == thingsEnd) + FreeSpan nextFree(aheader.getFirstFreeSpan()); + nextFree.checkSpan(); + + FreeSpan newListHead; + FreeSpan *newListTail = &newListHead; + uintptr_t newFreeSpanStart = 0; + bool allClear = true; + DebugOnly nmarked = 0; + for (;; thing += thingSize) { + JS_ASSERT(thing <= lastByte + 1); + if (thing == nextFree.first) { + JS_ASSERT(nextFree.last <= lastByte); + if (nextFree.last == lastByte) break; - nextFree = nextFree->link; - if (!nextFree) { - nextFree = thingsEnd->asFreeCell(); + JS_ASSERT(Arena::isAligned(nextFree.last, thingSize)); + if (!newFreeSpanStart) + newFreeSpanStart = thing; + thing = nextFree.last; + nextFree = *nextFree.nextSpan(); + nextFree.checkSpan(); + } else { + T *t = reinterpret_cast(thing); + if (t->isMarked()) { + allClear = false; + nmarked++; + if (newFreeSpanStart) { + JS_ASSERT(thing >= thingsStart(thingKind) + thingSize); + newListTail->first = newFreeSpanStart; + newListTail->last = thing - thingSize; + newListTail = newListTail->nextSpanUnchecked(thingSize); + newFreeSpanStart = 0; + } } else { - JS_ASSERT(thing->asFreeCell() < nextFree); - JS_ASSERT(nextFree < thingsEnd->asFreeCell()); + if (!newFreeSpanStart) + newFreeSpanStart = thing; + t->finalize(cx, background); + JS_POISON(t, JS_FREE_PATTERN, thingSize); } - } else if (thing->asFreeCell()->isMarked()) { - allClear = false; - continue; - } else { - thing->finalize(cx); -#ifdef DEBUG - memset(thing, JS_FREE_PATTERN, sizeof(T)); -#endif } - FreeCell *t = thing->asFreeCell(); - *tailp = t; - tailp = &t->link; } -#ifdef DEBUG - /* Check that the free list is consistent. */ - unsigned nfree = 0; - if (freeList) { - JS_ASSERT(tailp != &freeList); - FreeCell *t = freeList; - for (;;) { - ++nfree; - if (&t->link == tailp) - break; - JS_ASSERT(t < t->link); - t = t->link; - } - } if (allClear) { - JS_ASSERT(nfree == ThingsPerArena); - JS_ASSERT(freeList == static_cast(&t.things[0])); - JS_ASSERT(tailp == &t.things[ThingsPerArena-1].asFreeCell()->link); - } else { - JS_ASSERT(nfree < ThingsPerArena); + JS_ASSERT(newListTail == &newListHead); + JS_ASSERT(newFreeSpanStart == thingsStart(thingKind)); + return true; } -#endif - *tailp = NULL; - aheader.freeList = freeList; - return allClear; -} + + newListTail->first = newFreeSpanStart ? newFreeSpanStart : nextFree.first; + JS_ASSERT(Arena::isAligned(newListTail->first, thingSize)); + newListTail->last = lastByte; #ifdef DEBUG -bool -checkArenaListAllUnmarked(JSCompartment *comp) -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (comp->arenas[i].markedThingsInArenaList()) - return false; + size_t nfree = 0; + for (const FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) { + span->checkSpan(); + JS_ASSERT(Arena::isAligned(span->first, thingSize)); + JS_ASSERT(Arena::isAligned(span->last, thingSize)); + nfree += (span->last - span->first) / thingSize + 1; + JS_ASSERT(nfree + nmarked <= thingsPerArena(thingSize)); } - return true; -} + nfree += (newListTail->last + 1 - newListTail->first) / thingSize; + JS_ASSERT(nfree + nmarked == thingsPerArena(thingSize)); #endif + aheader.setFirstFreeSpan(&newListHead); -} /* namespace gc */ -} /* namespace js */ - -void -JSCompartment::finishArenaLists() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].releaseAll(i); + return false; } -void -Chunk::clearMarkBitmap() +template +inline void +FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background) { - PodZero(&bitmaps[0], ArenasPerChunk); + /* + * Release empty arenas and move non-full arenas with some free things into + * a separated list that we append to al after the loop to ensure that any + * arena before al->cursor is full. + */ + JS_ASSERT_IF(!al->head, al->cursor == &al->head); + ArenaLists::ArenaList available; + ArenaHeader **ap = &al->head; + size_t thingSize = Arena::thingSize(thingKind); + while (ArenaHeader *aheader = *ap) { + bool allClear = aheader->getArena()->finalize(cx, thingKind, thingSize, background); + if (allClear) { + *ap = aheader->next; + aheader->chunk()->releaseArena(aheader); + } else if (aheader->hasFreeThings()) { + *ap = aheader->next; + *available.cursor = aheader; + available.cursor = &aheader->next; + } else { + ap = &aheader->next; + } + } + + /* Terminate the available list and append it to al. */ + *available.cursor = NULL; + *ap = available.head; + al->cursor = ap; + JS_ASSERT_IF(!al->head, al->cursor == &al->head); } -bool -Chunk::init(JSRuntime *rt) +/* + * Finalize the list. On return al->cursor points to the first non-empty arena + * after the al->head. + */ +static void +FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background) { - info.runtime = rt; - info.age = 0; - info.emptyArenaLists.init(); - info.emptyArenaLists.cellFreeList = &arenas[0].aheader; -#ifdef JS_THREADSAFE - info.chunkLock = JS_NEW_LOCK(); - if (!info.chunkLock) - return false; + switch(thingKind) { + case FINALIZE_OBJECT0: + case FINALIZE_OBJECT0_BACKGROUND: + case FINALIZE_OBJECT2: + case FINALIZE_OBJECT2_BACKGROUND: + case FINALIZE_OBJECT4: + case FINALIZE_OBJECT4_BACKGROUND: + case FINALIZE_OBJECT8: + case FINALIZE_OBJECT8_BACKGROUND: + case FINALIZE_OBJECT12: + case FINALIZE_OBJECT12_BACKGROUND: + case FINALIZE_OBJECT16: + case FINALIZE_OBJECT16_BACKGROUND: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_SCRIPT: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_SHAPE: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_BASE_SHAPE: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_TYPE_OBJECT: + FinalizeTypedArenas(cx, al, thingKind, background); + break; +#if JS_HAS_XML_SUPPORT + case FINALIZE_XML: + FinalizeTypedArenas(cx, al, thingKind, background); + break; #endif - ArenaHeader *aheader = &arenas[0].aheader; - ArenaHeader *last = &arenas[JS_ARRAY_LENGTH(arenas) - 1].aheader; - while (aheader < last) { - ArenaHeader *following = reinterpret_cast(aheader->address() + ArenaSize); - aheader->next = following; - aheader->compartment = NULL; - aheader = following; - } - last->next = NULL; - last->compartment = NULL; - info.numFree = ArenasPerChunk; - for (size_t i = 0; i != JS_ARRAY_LENGTH(markingDelay); ++i) - markingDelay[i].init(); - return true; + case FINALIZE_STRING: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_SHORT_STRING: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + case FINALIZE_EXTERNAL_STRING: + FinalizeTypedArenas(cx, al, thingKind, background); + break; + } } -bool -Chunk::unused() -{ - return info.numFree == ArenasPerChunk; +static inline Chunk * +AllocChunk() { + return static_cast(MapAlignedPages(ChunkSize, ChunkSize)); } -bool -Chunk::hasAvailableArenas() -{ - return info.numFree > 0; +static inline void +FreeChunk(Chunk *p) { + UnmapPages(static_cast(p), ChunkSize); } -bool -Chunk::withinArenasRange(Cell *cell) +#ifdef JS_THREADSAFE +inline bool +ChunkPool::wantBackgroundAllocation(JSRuntime *rt) const { - uintptr_t addr = uintptr_t(cell); - if (addr >= uintptr_t(&arenas[0]) && addr < uintptr_t(&arenas[ArenasPerChunk])) - return true; - return false; + /* + * To minimize memory waste we do not want to run the background chunk + * allocation if we have empty chunks or when the runtime needs just few + * of them. + */ + return rt->gcHelperThread.canBackgroundAllocate() && + emptyCount == 0 && + rt->gcChunkSet.count() >= 4; } +#endif -template -ArenaHeader * -Chunk::allocateArena(JSContext *cx, unsigned thingKind) +/* Must be called with the GC lock taken. */ +inline Chunk * +ChunkPool::get(JSRuntime *rt) { + JS_ASSERT(this == &rt->gcChunkPool); + + Chunk *chunk = emptyChunkListHead; + if (chunk) { + JS_ASSERT(emptyCount); + emptyChunkListHead = chunk->info.next; + --emptyCount; + } else { + JS_ASSERT(!emptyCount); + chunk = Chunk::allocate(rt); + if (!chunk) + return NULL; + JS_ASSERT(chunk->info.numArenasFreeCommitted == ArenasPerChunk); + rt->gcNumArenasFreeCommitted += ArenasPerChunk; + } + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + #ifdef JS_THREADSAFE - Maybe maybeLock; - if (cx->runtime->gcHelperThread.sweeping) - maybeLock.construct(info.chunkLock); + if (wantBackgroundAllocation(rt)) + rt->gcHelperThread.startBackgroundAllocationIfIdle(); #endif - JSCompartment *comp = cx->compartment; - JS_ASSERT(hasAvailableArenas()); - ArenaHeader *aheader = info.emptyArenaLists.getTypedFreeList(thingKind); - if (!aheader) { - aheader = info.emptyArenaLists.getOtherArena(); - aheader->freeList = aheader->getArena()->buildFreeList(); - } - JS_ASSERT(!aheader->compartment); - JS_ASSERT(!aheader->getMarkingDelay()->link); - aheader->compartment = comp; - aheader->setThingKind(thingKind); - --info.numFree; - JSRuntime *rt = info.runtime; - - JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize); - JS_ATOMIC_ADD(&comp->gcBytes, ArenaSize); - METER(JS_ATOMIC_INCREMENT(&rt->gcStats.nallarenas)); - if (comp->gcBytes >= comp->gcTriggerBytes) - TriggerCompartmentGC(comp); - return aheader; + return chunk; } -void -Chunk::releaseArena(ArenaHeader *aheader) +/* Must be called either during the GC or with the GC lock taken. */ +inline void +ChunkPool::put(Chunk *chunk) { - JSRuntime *rt = info.runtime; -#ifdef JS_THREADSAFE - Maybe maybeLock; - if (rt->gcHelperThread.sweeping) - maybeLock.construct(info.chunkLock); -#endif - JSCompartment *comp = aheader->compartment; - METER(rt->gcStats.afree++); - JS_ASSERT(rt->gcStats.nallarenas != 0); - METER(JS_ATOMIC_DECREMENT(&rt->gcStats.nallarenas)); + chunk->info.age = 0; + chunk->info.next = emptyChunkListHead; + emptyChunkListHead = chunk; + emptyCount++; +} - JS_ASSERT(size_t(rt->gcBytes) >= ArenaSize); - JS_ASSERT(size_t(comp->gcBytes) >= ArenaSize); -#ifdef JS_THREADSAFE - if (rt->gcHelperThread.sweeping) { - rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); - comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); +/* Must be called either during the GC or with the GC lock taken. */ +Chunk * +ChunkPool::expire(JSRuntime *rt, bool releaseAll) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + /* + * Return old empty chunks to the system while preserving the order of + * other chunks in the list. This way, if the GC runs several times + * without emptying the list, the older chunks will stay at the tail + * and are more likely to reach the max age. + */ + Chunk *freeList = NULL; + for (Chunk **chunkp = &emptyChunkListHead; *chunkp; ) { + JS_ASSERT(emptyCount); + Chunk *chunk = *chunkp; + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); + if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { + *chunkp = chunk->info.next; + --emptyCount; + chunk->prepareToBeFreed(rt); + chunk->info.next = freeList; + freeList = chunk; + } else { + /* Keep the chunk but increase its age. */ + ++chunk->info.age; + chunkp = &chunk->info.next; + } + } + JS_ASSERT_IF(releaseAll, !emptyCount); + return freeList; +} + +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); } -#endif - JS_ATOMIC_ADD(&rt->gcBytes, -ArenaSize); - JS_ATOMIC_ADD(&comp->gcBytes, -ArenaSize); - info.emptyArenaLists.insert(aheader); - aheader->compartment = NULL; - ++info.numFree; - if (unused()) - info.age = 0; } -JSRuntime * -Chunk::getRuntime() +void +ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) { - return info.runtime; + FreeChunkList(expire(rt, releaseAll)); } -inline jsuword -GetGCChunk(JSRuntime *rt) +JS_FRIEND_API(int64_t) +ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { - void *p = rt->gcChunkAllocator->alloc(); -#ifdef MOZ_GCTIMER - if (p) - JS_ATOMIC_INCREMENT(&newChunkCount); -#endif - METER_IF(p, rt->gcStats.nchunks++); - METER_UPDATE_MAX(rt->gcStats.maxnchunks, rt->gcStats.nchunks); - return reinterpret_cast(p); + JS_ASSERT(this == &rt->gcChunkPool); + + int64_t numDecommitted = 0; + Chunk *chunk = emptyChunkListHead; + while (chunk) { + for (uint32_t i = 0; i < ArenasPerChunk; ++i) + if (chunk->decommittedArenas.get(i)) + ++numDecommitted; + chunk = chunk->info.next; + } + return numDecommitted; } -inline void -ReleaseGCChunk(JSRuntime *rt, jsuword chunk) +/* static */ Chunk * +Chunk::allocate(JSRuntime *rt) { - void *p = reinterpret_cast(chunk); - JS_ASSERT(p); -#ifdef MOZ_GCTIMER - JS_ATOMIC_INCREMENT(&destroyChunkCount); -#endif -#ifdef JS_THREADSAFE - JS_DESTROY_LOCK(((Chunk *)chunk)->info.chunkLock); -#endif - JS_ASSERT(rt->gcStats.nchunks != 0); - METER(rt->gcStats.nchunks--); - rt->gcChunkAllocator->free_(p); + Chunk *chunk = static_cast(AllocChunk()); + if (!chunk) + return NULL; + chunk->init(); + rt->gcStats.count(gcstats::STAT_NEW_CHUNK); + return chunk; } -inline Chunk * -AllocateGCChunk(JSRuntime *rt) +/* Must be called with the GC lock taken. */ +/* static */ inline void +Chunk::release(JSRuntime *rt, Chunk *chunk) { - Chunk *p = (Chunk *)rt->gcChunkAllocator->alloc(); -#ifdef MOZ_GCTIMER - if (p) - JS_ATOMIC_INCREMENT(&newChunkCount); -#endif - METER_IF(p, rt->gcStats.nchunks++); - return p; + JS_ASSERT(chunk); + chunk->prepareToBeFreed(rt); + FreeChunk(chunk); } inline void -ReleaseGCChunk(JSRuntime *rt, Chunk *p) +Chunk::prepareToBeFreed(JSRuntime *rt) { - JS_ASSERT(p); -#ifdef MOZ_GCTIMER - JS_ATOMIC_INCREMENT(&destroyChunkCount); -#endif -#ifdef JS_THREADSAFE - JS_DESTROY_LOCK(p->info.chunkLock); + JS_ASSERT(rt->gcNumArenasFreeCommitted >= info.numArenasFreeCommitted); + rt->gcNumArenasFreeCommitted -= info.numArenasFreeCommitted; + rt->gcStats.count(gcstats::STAT_DESTROY_CHUNK); + +#ifdef DEBUG + /* + * Let FreeChunkList detect a missing prepareToBeFreed call before it + * frees chunk. + */ + info.numArenasFreeCommitted = 0; #endif - JS_ASSERT(rt->gcStats.nchunks != 0); - METER(rt->gcStats.nchunks--); - rt->gcChunkAllocator->free_(p); } -static Chunk * -PickChunk(JSRuntime *rt) +void +Chunk::init() { - Chunk *chunk; - for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) { - if (r.front()->hasAvailableArenas()) - return r.front(); - } - - chunk = AllocateGCChunk(rt); - if (!chunk) - return NULL; + JS_POISON(this, JS_FREE_PATTERN, ChunkSize); /* - * FIXME bug 583732 - chunk is newly allocated and cannot be present in - * the table so using ordinary lookupForAdd is suboptimal here. + * We clear the bitmap to guard against xpc_IsGrayGCThing being called on + * uninitialized data, which would happen before the first GC cycle. */ - GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk); - JS_ASSERT(!p); - if (!rt->gcChunkSet.add(p, chunk)) { - ReleaseGCChunk(rt, chunk); - return NULL; - } + bitmap.clear(); - if (!chunk->init(rt)) { - ReleaseGCChunk(rt, chunk); - return NULL; + /* Initialize the arena tracking bitmap. */ + decommittedArenas.clear(false); + + /* Initialize the chunk info. */ + info.freeArenasHead = &arenas[0].aheader; + info.lastDecommittedArenaOffset = 0; + info.numArenasFree = ArenasPerChunk; + info.numArenasFreeCommitted = ArenasPerChunk; + info.age = 0; + + /* Initialize the arena header state. */ + for (jsuint i = 0; i < ArenasPerChunk; i++) { + arenas[i].aheader.setAsNotAllocated(); + arenas[i].aheader.next = (i + 1 < ArenasPerChunk) + ? &arenas[i + 1].aheader + : NULL; } - return chunk; + /* The rest of info fields are initialized in PickChunk. */ } -static void -ExpireGCChunks(JSRuntime *rt) +inline Chunk ** +GetAvailableChunkList(JSCompartment *comp) { - static const size_t MaxAge = 3; + JSRuntime *rt = comp->rt; + return comp->isSystemCompartment + ? &rt->gcSystemAvailableChunkListHead + : &rt->gcUserAvailableChunkListHead; +} - /* Remove unused chunks. */ - AutoLockGC lock(rt); +inline void +Chunk::addToAvailableList(JSCompartment *comp) +{ + insertToAvailableList(GetAvailableChunkList(comp)); +} - rt->gcChunksWaitingToExpire = 0; - for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) { - Chunk *chunk = e.front(); - JS_ASSERT(chunk->info.runtime == rt); - if (chunk->unused()) { - if (chunk->info.age++ > MaxAge) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; - } - rt->gcChunksWaitingToExpire++; - } +inline void +Chunk::insertToAvailableList(Chunk **insertPoint) +{ + JS_ASSERT(hasAvailableArenas()); + JS_ASSERT(!info.prevp); + JS_ASSERT(!info.next); + info.prevp = insertPoint; + Chunk *insertBefore = *insertPoint; + if (insertBefore) { + JS_ASSERT(insertBefore->info.prevp == insertPoint); + insertBefore->info.prevp = &info.next; } + info.next = insertBefore; + *insertPoint = this; } -template -static ArenaHeader * -AllocateArena(JSContext *cx, unsigned thingKind) +inline void +Chunk::removeFromAvailableList() { - JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); - Chunk *chunk = cx->compartment->chunk; - if (!chunk || !chunk->hasAvailableArenas()) { - chunk = PickChunk(rt); - if (!chunk) { - TriggerGC(rt); - return NULL; - } - cx->compartment->chunk = chunk; + JS_ASSERT(info.prevp); + *info.prevp = info.next; + if (info.next) { + JS_ASSERT(info.next->info.prevp == &info.next); + info.next->info.prevp = info.prevp; } - return chunk->allocateArena(cx, thingKind); + info.prevp = NULL; + info.next = NULL; } -JS_FRIEND_API(bool) -IsAboutToBeFinalized(JSContext *cx, const void *thing) +/* + * Search for and return the next decommitted Arena. Our goal is to keep + * lastDecommittedArenaOffset "close" to a free arena. We do this by setting + * it to the most recently freed arena when we free, and forcing it to + * the last alloc + 1 when we allocate. + */ +jsuint +Chunk::findDecommittedArenaOffset() { - if (JSAtom::isStatic(thing)) - return false; - JS_ASSERT(cx); + /* Note: lastFreeArenaOffset can be past the end of the list. */ + for (jsuint i = info.lastDecommittedArenaOffset; i < ArenasPerChunk; i++) + if (decommittedArenas.get(i)) + return i; + for (jsuint i = 0; i < info.lastDecommittedArenaOffset; i++) + if (decommittedArenas.get(i)) + return i; + JS_NOT_REACHED("No decommitted arenas found."); + return -1; +} - JSCompartment *thingCompartment = reinterpret_cast(thing)->compartment(); - JSRuntime *rt = cx->runtime; - JS_ASSERT(rt == thingCompartment->rt); +ArenaHeader * +Chunk::fetchNextDecommittedArena() +{ + JS_ASSERT(info.numArenasFreeCommitted == 0); + JS_ASSERT(info.numArenasFree > 0); + + jsuint offset = findDecommittedArenaOffset(); + info.lastDecommittedArenaOffset = offset + 1; + --info.numArenasFree; + decommittedArenas.unset(offset); + + Arena *arena = &arenas[offset]; + MarkPagesInUse(arena, ArenaSize); + arena->aheader.setAsNotAllocated(); + + return &arena->aheader; +} + +inline ArenaHeader * +Chunk::fetchNextFreeArena(JSRuntime *rt) +{ + JS_ASSERT(info.numArenasFreeCommitted > 0); + JS_ASSERT(info.numArenasFreeCommitted <= info.numArenasFree); + JS_ASSERT(info.numArenasFreeCommitted <= rt->gcNumArenasFreeCommitted); + + ArenaHeader *aheader = info.freeArenasHead; + info.freeArenasHead = aheader->next; + --info.numArenasFreeCommitted; + --info.numArenasFree; + --rt->gcNumArenasFreeCommitted; + + return aheader; +} + +ArenaHeader * +Chunk::allocateArena(JSCompartment *comp, AllocKind thingKind) +{ + JS_ASSERT(hasAvailableArenas()); + + JSRuntime *rt = comp->rt; + JS_ASSERT(rt->gcBytes <= rt->gcMaxBytes); + if (rt->gcMaxBytes - rt->gcBytes < ArenaSize) + return NULL; + + ArenaHeader *aheader = JS_LIKELY(info.numArenasFreeCommitted > 0) + ? fetchNextFreeArena(rt) + : fetchNextDecommittedArena(); + aheader->init(comp, thingKind); + if (JS_UNLIKELY(!hasAvailableArenas())) + removeFromAvailableList(); + + Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize); + rt->gcBytes += ArenaSize; + comp->gcBytes += ArenaSize; + if (comp->gcBytes >= comp->gcTriggerBytes) + TriggerCompartmentGC(comp, gcreason::ALLOC_TRIGGER); + + return aheader; +} + +inline void +Chunk::addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader) +{ + JS_ASSERT(!aheader->allocated()); + aheader->next = info.freeArenasHead; + info.freeArenasHead = aheader; + ++info.numArenasFreeCommitted; + ++info.numArenasFree; + ++rt->gcNumArenasFreeCommitted; +} + +void +Chunk::releaseArena(ArenaHeader *aheader) +{ + JS_ASSERT(aheader->allocated()); + JS_ASSERT(!aheader->hasDelayedMarking); + JSCompartment *comp = aheader->compartment; + JSRuntime *rt = comp->rt; +#ifdef JS_THREADSAFE + AutoLockGC maybeLock; + if (rt->gcHelperThread.sweeping()) + maybeLock.lock(rt); +#endif + + Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes - ArenaSize); + JS_ASSERT(rt->gcBytes >= ArenaSize); + JS_ASSERT(comp->gcBytes >= ArenaSize); +#ifdef JS_THREADSAFE + if (rt->gcHelperThread.sweeping()) + comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); +#endif + rt->gcBytes -= ArenaSize; + comp->gcBytes -= ArenaSize; + + aheader->setAsNotAllocated(); + addArenaToFreeList(rt, aheader); + + if (info.numArenasFree == 1) { + JS_ASSERT(!info.prevp); + JS_ASSERT(!info.next); + addToAvailableList(comp); + } else if (!unused()) { + JS_ASSERT(info.prevp); + } else { + rt->gcChunkSet.remove(this); + removeFromAvailableList(); + rt->gcChunkPool.put(this); + } +} + +} /* namespace gc */ +} /* namespace js */ + +/* The caller must hold the GC lock. */ +static Chunk * +PickChunk(JSCompartment *comp) +{ + JSRuntime *rt = comp->rt; + Chunk **listHeadp = GetAvailableChunkList(comp); + Chunk *chunk = *listHeadp; + if (chunk) + return chunk; + + chunk = rt->gcChunkPool.get(rt); + if (!chunk) + return NULL; + + rt->gcChunkAllocationSinceLastGC = true; + + /* + * FIXME bug 583732 - chunk is newly allocated and cannot be present in + * the table so using ordinary lookupForAdd is suboptimal here. + */ + GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk); + JS_ASSERT(!p); + if (!rt->gcChunkSet.add(p, chunk)) { + Chunk::release(rt, chunk); + return NULL; + } + + chunk->info.prevp = NULL; + chunk->info.next = NULL; + chunk->addToAvailableList(comp); + + return chunk; +} + +JS_FRIEND_API(bool) +IsAboutToBeFinalized(const Cell *thing) +{ + JSCompartment *thingCompartment = reinterpret_cast(thing)->compartment(); + JSRuntime *rt = thingCompartment->rt; if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != thingCompartment) return false; return !reinterpret_cast(thing)->isMarked(); } -JS_FRIEND_API(bool) -js_GCThingIsMarked(void *thing, uintN color = BLACK) +bool +IsAboutToBeFinalized(const Value &v) { - JS_ASSERT(thing); - AssertValidColor(thing, color); - return reinterpret_cast(thing)->isMarked(color); + JS_ASSERT(v.isMarkable()); + return IsAboutToBeFinalized((Cell *)v.toGCThing()); } -/* - * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all - * JIT code is discarded in inactive compartments, regardless of how often that - * code runs. - */ -static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000; +/* Lifetime for type sets attached to scripts containing observed types. */ +static const int64_t JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000; JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes) +js_InitGC(JSRuntime *rt, uint32_t maxbytes) { - /* - * Make room for at least 16 chunks so the table would not grow before - * the browser starts up. - */ - if (!rt->gcChunkSet.init(16)) + if (!rt->gcChunkSet.init(INITIAL_CHUNK_CAPACITY)) return false; if (!rt->gcRootsHash.init(256)) @@ -588,16 +877,10 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) return false; #ifdef JS_THREADSAFE - rt->gcLock = JS_NEW_LOCK(); + rt->gcLock = PR_NewLock(); if (!rt->gcLock) return false; - rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->gcDone) - return false; - rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->requestDone) - return false; - if (!rt->gcHelperThread.init(rt)) + if (!rt->gcHelperThread.init()) return false; #endif @@ -608,80 +891,48 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) rt->gcMaxBytes = maxbytes; rt->setGCMaxMallocBytes(maxbytes); - rt->gcEmptyArenaPoolLifespan = 30000; - - rt->gcTriggerFactor = uint32(100.0f * GC_HEAP_GROWTH_FACTOR); - - /* - * The assigned value prevents GC from running when GC memory is too low - * (during JS engine start). - */ - rt->setGCLastBytes(8192); - - rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME; - - METER(PodZero(&rt->gcStats)); + rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; return true; } namespace js { inline bool -InFreeList(ArenaHeader *aheader, void *thing) -{ - for (FreeCell *cursor = aheader->freeList; cursor; cursor = cursor->link) { - JS_ASSERT(!cursor->isMarked()); - JS_ASSERT_IF(cursor->link, cursor < cursor->link); - - /* If the cursor moves past the thing, it's not in the freelist. */ - if (thing < cursor) - break; - - /* If we find it on the freelist, it's dead. */ - if (thing == cursor) - return true; - } - return false; -} - -template -inline ConservativeGCTest -MarkArenaPtrConservatively(JSTracer *trc, ArenaHeader *aheader, uintptr_t addr) +InFreeList(ArenaHeader *aheader, uintptr_t addr) { - JS_ASSERT(aheader->compartment); - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - - uintptr_t offset = (addr & ArenaMask) - Arena::FirstThingOffset; - if (offset >= Arena::ThingsSpan) - return CGCT_NOTARENA; - - /* addr can point inside the thing so we must align the address. */ - uintptr_t shift = offset % sizeof(T); - T *thing = reinterpret_cast(addr - shift); + if (!aheader->hasFreeThings()) + return false; - if (InFreeList(aheader, thing)) - return CGCT_NOTLIVE; + FreeSpan firstSpan(aheader->getFirstFreeSpan()); - MarkRoot(trc, thing, "machine stack"); + for (const FreeSpan *span = &firstSpan;;) { + /* If the thing comes fore the current span, it's not free. */ + if (addr < span->first) + return false; -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - if (IS_GC_MARKING_TRACER(trc) && static_cast(trc)->conservativeDumpFileName) - static_cast(trc)->conservativeRoots.append(thing); -#endif + /* + * If we find it inside the span, it's dead. We use here "<=" and not + * "<" even for the last span as we know that thing is inside the + * arena. Thus for the last span thing < span->end. + */ + if (addr <= span->last) + return true; -#if defined JS_DUMP_CONSERVATIVE_GC_ROOTS || defined JS_GCMETER - if (IS_GC_MARKING_TRACER(trc) && shift) - static_cast(trc)->conservativeStats.unaligned++; -#endif - return CGCT_VALID; + /* + * The last possible empty span is an the end of the arena. Here + * span->end < thing < thingsEnd and so we must have more spans. + */ + span = span->nextSpan(); + } } /* - * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets - * thingKind accordingly. Otherwise returns the reason for rejection. + * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and + * details about the thing if so. On failure, returns the reason for rejection. */ inline ConservativeGCTest -MarkIfGCThingWord(JSTracer *trc, jsuword w) +IsAddressableGCThing(JSRuntime *rt, uintptr_t w, + gc::AllocKind *thingKindPtr, ArenaHeader **arenaHeader, void **thing) { /* * We assume that the compiler never uses sub-word alignment to store @@ -698,16 +949,16 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) * An object jsid has its low bits tagged. In the value representation on * 64-bit, the high bits are tagged. */ - const jsuword JSID_PAYLOAD_MASK = ~jsuword(JSID_TYPE_MASK); + const uintptr_t JSID_PAYLOAD_MASK = ~uintptr_t(JSID_TYPE_MASK); #if JS_BITS_PER_WORD == 32 - jsuword addr = w & JSID_PAYLOAD_MASK; + uintptr_t addr = w & JSID_PAYLOAD_MASK; #elif JS_BITS_PER_WORD == 64 - jsuword addr = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK; + uintptr_t addr = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK; #endif Chunk *chunk = Chunk::fromAddress(addr); - if (!trc->context->runtime->gcChunkSet.has(chunk)) + if (!rt->gcChunkSet.has(chunk)) return CGCT_NOTCHUNK; /* @@ -718,69 +969,84 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) if (!Chunk::withinArenasRange(addr)) return CGCT_NOTARENA; - ArenaHeader *aheader = &chunk->arenas[Chunk::arenaIndex(addr)].aheader; + /* If the arena is not currently allocated, don't access the header. */ + size_t arenaOffset = Chunk::arenaIndex(addr); + if (chunk->decommittedArenas.get(arenaOffset)) + return CGCT_FREEARENA; + + ArenaHeader *aheader = &chunk->arenas[arenaOffset].aheader; - if (!aheader->compartment) + if (!aheader->allocated()) return CGCT_FREEARENA; - ConservativeGCTest test; - unsigned thingKind = aheader->getThingKind(); + JSCompartment *curComp = rt->gcCurrentCompartment; + if (curComp && curComp != aheader->compartment) + return CGCT_OTHERCOMPARTMENT; - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_EXTERNAL_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHORT_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_FUNCTION: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHAPE: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; + AllocKind thingKind = aheader->getAllocKind(); + uintptr_t offset = addr & ArenaMask; + uintptr_t minOffset = Arena::firstThingOffset(thingKind); + if (offset < minOffset) + return CGCT_NOTARENA; + + /* addr can point inside the thing so we must align the address. */ + uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); + addr -= shift; + + if (thing) + *thing = reinterpret_cast(addr); + if (arenaHeader) + *arenaHeader = aheader; + if (thingKindPtr) + *thingKindPtr = thingKind; + return CGCT_VALID; +} + +/* + * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets + * thingKind accordingly. Otherwise returns the reason for rejection. + */ +inline ConservativeGCTest +MarkIfGCThingWord(JSTracer *trc, uintptr_t w) +{ + void *thing; + ArenaHeader *aheader; + AllocKind thingKind; + ConservativeGCTest status = IsAddressableGCThing(trc->runtime, w, &thingKind, &aheader, &thing); + if (status != CGCT_VALID) + return status; + + /* + * Check if the thing is free. We must use the list of free spans as at + * this point we no longer have the mark bits from the previous GC run and + * we must account for newly allocated things. + */ + if (InFreeList(aheader, uintptr_t(thing))) + return CGCT_NOTLIVE; + +#ifdef DEBUG + const char pattern[] = "machine_stack %p"; + char nameBuf[sizeof(pattern) - 2 + sizeof(thing) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), pattern, thing); + JS_SET_TRACING_NAME(trc, nameBuf); #endif - default: - test = CGCT_WRONGTAG; - JS_NOT_REACHED("wrong tag"); + MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); + +#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS + if (IS_GC_MARKING_TRACER(trc)) { + GCMarker *marker = static_cast(trc); + if (marker->conservativeDumpFileName) + marker->conservativeRoots.append(thing); + if (uintptr_t(thing) != w) + marker->conservativeStats.unaligned++; } +#endif - return test; + return CGCT_VALID; } static void -MarkWordConservatively(JSTracer *trc, jsuword w) +MarkWordConservatively(JSTracer *trc, uintptr_t w) { /* * The conservative scanner may access words that valgrind considers as @@ -789,87 +1055,93 @@ MarkWordConservatively(JSTracer *trc, jsuword w) * original word. See bug 572678. */ #ifdef JS_VALGRIND - VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w)); + JS_SILENCE_UNUSED_VALUE_IN_EXPR(VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w))); #endif MarkIfGCThingWord(trc, w); } static void -MarkRangeConservatively(JSTracer *trc, const jsuword *begin, const jsuword *end) +MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end) { JS_ASSERT(begin <= end); - for (const jsuword *i = begin; i != end; ++i) + for (const uintptr_t *i = begin; i < end; ++i) MarkWordConservatively(trc, *i); } -static void -MarkThreadDataConservatively(JSTracer *trc, ThreadData *td) +static JS_NEVER_INLINE void +MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt) { - ConservativeGCThreadData *ctd = &td->conservativeGC; - JS_ASSERT(ctd->hasStackToScan()); - jsuword *stackMin, *stackEnd; + ConservativeGCData *cgcd = &rt->conservativeGC; + if (!cgcd->hasStackToScan()) { +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->suspendCount); + JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold); +#endif + return; + } + + uintptr_t *stackMin, *stackEnd; #if JS_STACK_GROWTH_DIRECTION > 0 - stackMin = td->nativeStackBase; - stackEnd = ctd->nativeStackTop; + stackMin = rt->nativeStackBase; + stackEnd = cgcd->nativeStackTop; #else - stackMin = ctd->nativeStackTop + 1; - stackEnd = td->nativeStackBase; + stackMin = cgcd->nativeStackTop + 1; + stackEnd = reinterpret_cast(rt->nativeStackBase); #endif + JS_ASSERT(stackMin <= stackEnd); MarkRangeConservatively(trc, stackMin, stackEnd); - MarkRangeConservatively(trc, ctd->registerSnapshot.words, - JS_ARRAY_END(ctd->registerSnapshot.words)); + MarkRangeConservatively(trc, cgcd->registerSnapshot.words, + ArrayEnd(cgcd->registerSnapshot.words)); } void MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv) { - const jsuword *begin = beginv->payloadWord(); - const jsuword *end = endv->payloadWord();; + /* + * Normally, the drainMarkStack phase of marking will never trace outside + * of the compartment currently being collected. However, conservative + * scanning during drainMarkStack (as is done for generators) can break + * this invariant. So we disable the compartment assertions in this + * situation. + */ + struct AutoSkipChecking { + JSRuntime *runtime; + JSCompartment *savedCompartment; + + AutoSkipChecking(JSRuntime *rt) + : runtime(rt), savedCompartment(rt->gcCheckCompartment) { + rt->gcCheckCompartment = NULL; + } + ~AutoSkipChecking() { runtime->gcCheckCompartment = savedCompartment; } + } as(trc->runtime); + + const uintptr_t *begin = beginv->payloadWord(); + const uintptr_t *end = endv->payloadWord(); #ifdef JS_NUNBOX32 /* * With 64-bit jsvals on 32-bit systems, we can optimize a bit by * scanning only the payloads. */ JS_ASSERT(begin <= end); - for (const jsuword *i = begin; i != end; i += sizeof(Value)/sizeof(jsuword)) + for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t)) MarkWordConservatively(trc, *i); #else MarkRangeConservatively(trc, begin, end); #endif } -void -MarkConservativeStackRoots(JSTracer *trc) -{ -#ifdef JS_THREADSAFE - for (JSThread::Map::Range r = trc->context->runtime->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - ConservativeGCThreadData *ctd = &thread->data.conservativeGC; - if (ctd->hasStackToScan()) { - JS_ASSERT_IF(!thread->data.requestDepth, thread->suspendCount); - MarkThreadDataConservatively(trc, &thread->data); - } else { - JS_ASSERT(!thread->suspendCount); - JS_ASSERT(thread->data.requestDepth <= ctd->requestThreshold); - } - } -#else - MarkThreadDataConservatively(trc, &trc->context->runtime->threadData); -#endif -} - JS_NEVER_INLINE void -ConservativeGCThreadData::recordStackTop() +ConservativeGCData::recordStackTop() { /* Update the native stack pointer if it points to a bigger stack. */ - jsuword dummy; + uintptr_t dummy; nativeStackTop = &dummy; /* - * To record and update the register snapshot for the conservative - * scanning with the latest values we use setjmp. + * To record and update the register snapshot for the conservative scanning + * with the latest values we use setjmp. */ #if defined(_MSC_VER) # pragma warning(push) @@ -881,22 +1153,28 @@ ConservativeGCThreadData::recordStackTop() #endif } -static inline void +void RecordNativeStackTopForGC(JSContext *cx) { - ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC; + ConservativeGCData *cgcd = &cx->runtime->conservativeGC; #ifdef JS_THREADSAFE /* Record the stack top here only if we are called from a request. */ - JS_ASSERT(cx->thread()->data.requestDepth >= ctd->requestThreshold); - if (cx->thread()->data.requestDepth == ctd->requestThreshold) + JS_ASSERT(cx->runtime->requestDepth >= cgcd->requestThreshold); + if (cx->runtime->requestDepth == cgcd->requestThreshold) return; #endif - ctd->recordStackTop(); + cgcd->recordStackTop(); } } /* namespace js */ +bool +js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, gc::AllocKind *thingKind, void **thing) +{ + return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID; +} + #ifdef DEBUG static void CheckLeakedRoots(JSRuntime *rt); @@ -905,32 +1183,27 @@ CheckLeakedRoots(JSRuntime *rt); void js_FinishGC(JSRuntime *rt) { -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif -#ifdef JS_GCMETER - if (JS_WANT_GC_METER_PRINT) - js_DumpGCStats(rt, stdout); + /* + * Wait until the background finalization stops and the helper thread + * shuts down before we forcefully release any remaining GC memory. + */ +#ifdef JS_THREADSAFE + rt->gcHelperThread.finish(); #endif /* Delete all remaining Compartments. */ - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { - JSCompartment *comp = *c; - comp->finishArenaLists(); - Foreground::delete_(comp); - } + for (CompartmentsIter c(rt); !c.done(); c.next()) + Foreground::delete_(c.get()); rt->compartments.clear(); rt->atomsCompartment = NULL; - rt->gcWeakMapList = NULL; - + rt->gcSystemAvailableChunkListHead = NULL; + rt->gcUserAvailableChunkListHead = NULL; for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) - ReleaseGCChunk(rt, r.front()); + Chunk::release(rt, r.front()); rt->gcChunkSet.clear(); -#ifdef JS_THREADSAFE - rt->gcHelperThread.finish(rt); -#endif + rt->gcChunkPool.expireAndFree(rt, true); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -943,7 +1216,7 @@ js_FinishGC(JSRuntime *rt) JSBool js_AddRoot(JSContext *cx, Value *vp, const char *name) { - JSBool ok = js_AddRootRT(cx->runtime, Jsvalify(vp), name); + JSBool ok = js_AddRootRT(cx->runtime, vp, name); if (!ok) JS_ReportOutOfMemory(cx); return ok; @@ -969,7 +1242,6 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) * rt->gcLock across the mark phase (including the root hashtable mark). */ AutoLockGC lock(rt); - js_WaitForGC(rt); return !!rt->gcRootsHash.put((void *)vp, RootInfo(name, JS_GC_ROOT_VALUE_PTR)); @@ -986,7 +1258,6 @@ js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) * rt->gcLock across the mark phase (including the root hashtable mark). */ AutoLockGC lock(rt); - js_WaitForGC(rt); return !!rt->gcRootsHash.put((void *)rp, RootInfo(name, JS_GC_ROOT_GCTHING_PTR)); @@ -1000,7 +1271,6 @@ js_RemoveRoot(JSRuntime *rt, void *rp) * Same synchronization drill as above in js_AddRoot. */ AutoLockGC lock(rt); - js_WaitForGC(rt); rt->gcRootsHash.remove(rp); rt->gcPoke = JS_TRUE; return JS_TRUE; @@ -1015,7 +1285,7 @@ typedef RootedValueMap::Enum RootEnum; static void CheckLeakedRoots(JSRuntime *rt) { - uint32 leakedroots = 0; + uint32_t leakedroots = 0; /* Warn (but don't assert) debug builds of any remaining roots. */ for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) { @@ -1057,7 +1327,7 @@ js_DumpNamedRoots(JSRuntime *rt, #endif /* DEBUG */ -uint32 +uint32_t js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { AutoLockGC lock(rt); @@ -1078,242 +1348,342 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) } void -JSRuntime::setGCTriggerFactor(uint32 factor) -{ - JS_ASSERT(factor >= 100); - - gcTriggerFactor = factor; - setGCLastBytes(gcLastBytes); - - for (JSCompartment **c = compartments.begin(); c != compartments.end(); ++c) - (*c)->setGCLastBytes(gcLastBytes); -} - -void -JSRuntime::setGCLastBytes(size_t lastBytes) +JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) { gcLastBytes = lastBytes; - /* FIXME bug 603916 - we should unify the triggers here. */ - float trigger1 = float(lastBytes) * float(gcTriggerFactor) / 100.0f; - float trigger2 = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * - GC_HEAP_GROWTH_FACTOR; - float maxtrigger = Max(trigger1, trigger2); - gcTriggerBytes = (float(gcMaxBytes) < maxtrigger) ? gcMaxBytes : size_t(maxtrigger); -} - -void -JSRuntime::reduceGCTriggerBytes(uint32 amount) { - JS_ASSERT(amount > 0); - JS_ASSERT((gcTriggerBytes - amount) > 0); - if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) - return; - gcTriggerBytes -= amount; + size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD); + float trigger = float(base) * GC_HEAP_GROWTH_FACTOR; + gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger)); } void -JSCompartment::setGCLastBytes(size_t lastBytes) +JSCompartment::reduceGCTriggerBytes(size_t amount) { - gcLastBytes = lastBytes; - - /* FIXME bug 603916 - we should unify the triggers here. */ - float trigger1 = float(lastBytes) * float(rt->gcTriggerFactor) / 100.0f; - float trigger2 = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * - GC_HEAP_GROWTH_FACTOR; - float maxtrigger = Max(trigger1, trigger2); - gcTriggerBytes = (float(rt->gcMaxBytes) < maxtrigger) ? rt->gcMaxBytes : size_t(maxtrigger); -} - -void -JSCompartment::reduceGCTriggerBytes(uint32 amount) { JS_ASSERT(amount > 0); - JS_ASSERT((gcTriggerBytes - amount) > 0); - if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) + JS_ASSERT(gcTriggerBytes - amount >= 0); + if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; } -void -FreeLists::purge() +namespace js { +namespace gc { + +inline void * +ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) { - /* - * Return the free list back to the arena so the GC finalization will not - * run the finalizers over unitialized bytes from free things. - */ - for (FreeCell ***p = finalizables; p != JS_ARRAY_END(finalizables); ++p) - *p = NULL; -} + Chunk *chunk = NULL; -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind < FINALIZE_LIMIT); - return &c->arenas[thingKind]; -} + ArenaList *al = &arenaLists[thingKind]; + AutoLockGC maybeLock; -#ifdef DEBUG -bool -CheckAllocation(JSContext *cx) -{ #ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()); -#endif - JS_ASSERT(!cx->runtime->gcRunning); - return true; -} -#endif + volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind]; + if (*bfs != BFS_DONE) { + /* + * We cannot search the arena list for free things while the + * background finalization runs and can modify head or cursor at any + * moment. So we always allocate a new arena in that case. + */ + maybeLock.lock(comp->rt); + if (*bfs == BFS_RUN) { + JS_ASSERT(!*al->cursor); + chunk = PickChunk(comp); + if (!chunk) { + /* + * Let the caller to wait for the background allocation to + * finish and restart the allocation attempt. + */ + return NULL; + } + } else if (*bfs == BFS_JUST_FINISHED) { + /* See comments before BackgroundFinalizeState definition. */ + *bfs = BFS_DONE; + } else { + JS_ASSERT(*bfs == BFS_DONE); + } + } +#endif /* JS_THREADSAFE */ -inline bool -NeedLastDitchGC(JSContext *cx) + if (!chunk) { + if (ArenaHeader *aheader = *al->cursor) { + JS_ASSERT(aheader->hasFreeThings()); + + /* + * The empty arenas are returned to the chunk and should not present on + * the list. + */ + JS_ASSERT(!aheader->isEmpty()); + al->cursor = &aheader->next; + + /* + * Move the free span stored in the arena to the free list and + * allocate from it. + */ + freeLists[thingKind] = aheader->getFirstFreeSpan(); + aheader->setAsFullyUsed(); + return freeLists[thingKind].infallibleAllocate(Arena::thingSize(thingKind)); + } + + /* Make sure we hold the GC lock before we call PickChunk. */ + if (!maybeLock.locked()) + maybeLock.lock(comp->rt); + chunk = PickChunk(comp); + if (!chunk) + return NULL; + } + + /* + * While we still hold the GC lock get an arena from some chunk, mark it + * as full as its single free span is moved to the free lits, and insert + * it to the list as a fully allocated arena. + * + * We add the arena before the the head, not after the tail pointed by the + * cursor, so after the GC the most recently added arena will be used first + * for allocations improving cache locality. + */ + JS_ASSERT(!*al->cursor); + ArenaHeader *aheader = chunk->allocateArena(comp, thingKind); + if (!aheader) + return NULL; + + aheader->next = al->head; + if (!al->head) { + JS_ASSERT(al->cursor == &al->head); + al->cursor = &aheader->next; + } + al->head = aheader; + + /* See comments before allocateFromNewArena about this assert. */ + JS_ASSERT(!aheader->hasFreeThings()); + uintptr_t arenaAddr = aheader->arenaAddress(); + return freeLists[thingKind].allocateFromNewArena(arenaAddr, + Arena::firstThingOffset(thingKind), + Arena::thingSize(thingKind)); +} + +void +ArenaLists::finalizeNow(JSContext *cx, AllocKind thingKind) { - JSRuntime *rt = cx->runtime; -#ifdef JS_GC_ZEAL - if (rt->gcZeal >= 1) - return true; +#ifdef JS_THREADSAFE + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); #endif - return rt->gcIsNeeded; + FinalizeArenas(cx, &arenaLists[thingKind], thingKind, false); } -/* - * Return false only if the GC run but could not bring its memory usage under - * JSRuntime::gcMaxBytes. - */ -static bool -RunLastDitchGC(JSContext *cx) +inline void +ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind) { - JSRuntime *rt = cx->runtime; - METER(rt->gcStats.lastditch++); + JS_ASSERT(thingKind == FINALIZE_OBJECT0_BACKGROUND || + thingKind == FINALIZE_OBJECT2_BACKGROUND || + thingKind == FINALIZE_OBJECT4_BACKGROUND || + thingKind == FINALIZE_OBJECT8_BACKGROUND || + thingKind == FINALIZE_OBJECT12_BACKGROUND || + thingKind == FINALIZE_OBJECT16_BACKGROUND || + thingKind == FINALIZE_SHORT_STRING || + thingKind == FINALIZE_STRING); + #ifdef JS_THREADSAFE - Maybe maybeUnlockAtomsCompartment; - if (cx->compartment == rt->atomsCompartment && rt->atomsCompartmentIsLocked) - maybeUnlockAtomsCompartment.construct(cx); + JS_ASSERT(!cx->runtime->gcHelperThread.sweeping()); + + ArenaList *al = &arenaLists[thingKind]; + if (!al->head) { + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); + JS_ASSERT(al->cursor == &al->head); + return; + } + + /* + * The state can be just-finished if we have not allocated any GC things + * from the arena list after the previous background finalization. + */ + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE || + backgroundFinalizeState[thingKind] == BFS_JUST_FINISHED); + + if (cx->gcBackgroundFree) { + /* + * To ensure the finalization order even during the background GC we + * must use infallibleAppend so arenas scheduled for background + * finalization would not be finalized now if the append fails. + */ + cx->gcBackgroundFree->finalizeVector.infallibleAppend(al->head); + al->clear(); + backgroundFinalizeState[thingKind] = BFS_RUN; + } else { + FinalizeArenas(cx, al, thingKind, false); + backgroundFinalizeState[thingKind] = BFS_DONE; + } + +#else /* !JS_THREADSAFE */ + + finalizeNow(cx, thingKind); + #endif - /* The last ditch GC preserves all atoms. */ - AutoKeepAtoms keep(rt); - js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL); +} #ifdef JS_THREADSAFE - if (rt->gcBytes >= rt->gcMaxBytes) - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); -#endif +/*static*/ void +ArenaLists::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) +{ + JS_ASSERT(listHead); + AllocKind thingKind = listHead->getAllocKind(); + JSCompartment *comp = listHead->compartment; + ArenaList finalized; + finalized.head = listHead; + FinalizeArenas(cx, &finalized, thingKind, true); + + /* + * After we finish the finalization al->cursor must point to the end of + * the head list as we emptied the list before the background finalization + * and the allocation adds new arenas before the cursor. + */ + ArenaLists *lists = &comp->arenas; + ArenaList *al = &lists->arenaLists[thingKind]; - return rt->gcBytes < rt->gcMaxBytes; + AutoLockGC lock(cx->runtime); + JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN); + JS_ASSERT(!*al->cursor); + + /* + * We must set the state to BFS_JUST_FINISHED if we touch arenaList list, + * even if we add to the list only fully allocated arenas without any free + * things. It ensures that the allocation thread takes the GC lock and all + * writes to the free list elements are propagated. As we always take the + * GC lock when allocating new arenas from the chunks we can set the state + * to BFS_DONE if we have released all finalized arenas back to their + * chunks. + */ + if (finalized.head) { + *al->cursor = finalized.head; + if (finalized.cursor != &finalized.head) + al->cursor = finalized.cursor; + lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED; + } else { + lists->backgroundFinalizeState[thingKind] = BFS_DONE; + } } +#endif /* JS_THREADSAFE */ -template -inline bool -RefillTypedFreeList(JSContext *cx, unsigned thingKind) +void +ArenaLists::finalizeObjects(JSContext *cx) { - JSCompartment *compartment = cx->compartment; - JS_ASSERT_IF(compartment->freeLists.finalizables[thingKind], - !*compartment->freeLists.finalizables[thingKind]); + finalizeNow(cx, FINALIZE_OBJECT0); + finalizeNow(cx, FINALIZE_OBJECT2); + finalizeNow(cx, FINALIZE_OBJECT4); + finalizeNow(cx, FINALIZE_OBJECT8); + finalizeNow(cx, FINALIZE_OBJECT12); + finalizeNow(cx, FINALIZE_OBJECT16); - JS_ASSERT(!cx->runtime->gcRunning); - if (cx->runtime->gcRunning) - return false; - - bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; #ifdef JS_THREADSAFE - bool waited = false; + finalizeLater(cx, FINALIZE_OBJECT0_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT2_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT4_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT8_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT12_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND); #endif - do { - if (canGC && JS_UNLIKELY(NeedLastDitchGC(cx))) { - if (!RunLastDitchGC(cx)) - break; +#if JS_HAS_XML_SUPPORT + finalizeNow(cx, FINALIZE_XML); +#endif +} + +void +ArenaLists::finalizeStrings(JSContext *cx) +{ + finalizeLater(cx, FINALIZE_SHORT_STRING); + finalizeLater(cx, FINALIZE_STRING); + + finalizeNow(cx, FINALIZE_EXTERNAL_STRING); +} + +void +ArenaLists::finalizeShapes(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SHAPE); + finalizeNow(cx, FINALIZE_BASE_SHAPE); + finalizeNow(cx, FINALIZE_TYPE_OBJECT); +} + +void +ArenaLists::finalizeScripts(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SCRIPT); +} + +static void +RunLastDitchGC(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + /* The last ditch GC preserves all atoms. */ + AutoKeepAtoms keep(rt); + js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH); +} + +/* static */ void * +ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) +{ + JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty()); + + JSCompartment *comp = cx->compartment; + JSRuntime *rt = comp->rt; + JS_ASSERT(!rt->gcRunning); + + bool runGC = !!rt->gcIsNeeded; + for (;;) { + if (JS_UNLIKELY(runGC)) { + RunLastDitchGC(cx); /* * The JSGC_END callback can legitimately allocate new GC * things and populate the free list. If that happens, just * return that list head. */ - if (compartment->freeLists.finalizables[thingKind]) - return true; - canGC = false; + size_t thingSize = Arena::thingSize(thingKind); + if (void *thing = comp->arenas.allocateFromFreeList(thingKind, thingSize)) + return thing; } - ArenaList *arenaList = GetFinalizableArenaList(compartment, thingKind); + /* + * allocateFromArena may fail while the background finalization still + * run. In that case we want to wait for it to finish and restart. + * However, checking for that is racy as the background finalization + * could free some things after allocateFromArena decided to fail but + * at this point it may have already stopped. To avoid this race we + * always try to allocate twice. + */ + for (bool secondAttempt = false; ; secondAttempt = true) { + void *thing = comp->arenas.allocateFromArena(comp, thingKind); + if (JS_LIKELY(!!thing)) + return thing; + if (secondAttempt) + break; + + AutoLockGC lock(rt); #ifdef JS_THREADSAFE -try_again: + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif - ArenaHeader *aheader = NULL; - if (!arenaList->hasToBeFinalized) { - aheader = arenaList->getNextWithFreeList(); - if (aheader) { - JS_ASSERT(aheader->freeList); - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - compartment->freeLists.populate(aheader, thingKind); - return true; - } } /* - * If the allocation fails rt->gcIsNeeded will be set and we will run - * the GC on the next loop iteration if the last ditch GC is allowed. + * We failed to allocate. Run the GC if we haven't done it already. + * Otherwise report OOM. */ - aheader = AllocateArena(cx, thingKind); - if (aheader) { - compartment->freeLists.populate(aheader, thingKind); - arenaList->insert(aheader); - return true; - } -#ifdef JS_THREADSAFE - if (!waited) { - /* The background thread can still free arenas during the finalization phase. */ - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); - waited = true; - goto try_again; - } -#endif - } while (canGC); + if (runGC) + break; + runGC = true; + } - METER(cx->runtime->gcStats.fail++); js_ReportOutOfMemory(cx); - return false; + return NULL; } -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) -{ - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_EXTERNAL_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHORT_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_FUNCTION: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHAPE: - return RefillTypedFreeList(cx, thingKind); -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - return RefillTypedFreeList(cx, thingKind); -#endif - default: - JS_NOT_REACHED("bad finalize kind"); - return false; - } -} +} /* namespace gc */ +} /* namespace js */ -uint32 +JSGCTraceKind js_GetGCThingTraceKind(void *thing) { return GetGCThingTraceKind(thing); @@ -1326,13 +1696,12 @@ js_LockGCThingRT(JSRuntime *rt, void *thing) return true; AutoLockGC lock(rt); - if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) + if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) { p->value++; - else - return false; + return true; + } - METER(rt->gcStats.lock++); - return true; + return false; } void @@ -1348,8 +1717,6 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing) rt->gcPoke = true; if (--p->value == 0) rt->gcLocksHash.remove(p); - - METER(rt->gcStats.unlock++); } } @@ -1362,7 +1729,7 @@ namespace js { * * To implement such delayed marking of the children with minimal overhead for * the normal case of sufficient native stack, the code adds a field per - * arena. The field marlingdelay->link links all arenas with delayed things + * arena. The field markingDelay->link links all arenas with delayed things * into a stack list with the pointer to stack top in * GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds * arenas to the stack as necessary while markDelayedChildren pops the arenas @@ -1370,20 +1737,22 @@ namespace js { */ GCMarker::GCMarker(JSContext *cx) - : color(0), - unmarkedArenaStackTop(MarkingDelay::stackBottom()), - objStack(cx->runtime->gcMarkStackObjs, sizeof(cx->runtime->gcMarkStackObjs)), - xmlStack(cx->runtime->gcMarkStackXMLs, sizeof(cx->runtime->gcMarkStackXMLs)), - largeStack(cx->runtime->gcMarkStackLarges, sizeof(cx->runtime->gcMarkStackLarges)) + : color(BLACK), + unmarkedArenaStackTop(NULL), + stack(cx->runtime->gcMarkStackArray) { - JS_TRACER_INIT(this, cx, NULL); -#ifdef DEBUG + JS_TracerInit(this, cx, NULL); markLaterArenas = 0; -#endif #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS"); memset(&conservativeStats, 0, sizeof(conservativeStats)); #endif + + /* + * The GC is recomputing the liveness of WeakMap entries, so we + * delay visting entries. + */ + eagerlyTraceWeakMaps = JS_FALSE; } GCMarker::~GCMarker() @@ -1391,10 +1760,6 @@ GCMarker::~GCMarker() #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS dumpConservativeRoots(); #endif -#ifdef JS_GCMETER - /* Update total stats. */ - context->runtime->gcStats.conservative.add(conservativeStats); -#endif } void @@ -1402,95 +1767,47 @@ GCMarker::delayMarkingChildren(const void *thing) { const Cell *cell = reinterpret_cast(thing); ArenaHeader *aheader = cell->arenaHeader(); - if (aheader->getMarkingDelay()->link) { + if (aheader->hasDelayedMarking) { /* Arena already scheduled to be marked later */ return; } - aheader->getMarkingDelay()->link = unmarkedArenaStackTop; - unmarkedArenaStackTop = aheader; - METER(markLaterArenas++); - METER_UPDATE_MAX(cell->compartment()->rt->gcStats.maxunmarked, markLaterArenas); + aheader->setNextDelayedMarking(unmarkedArenaStackTop); + unmarkedArenaStackTop = aheader->getArena(); + markLaterArenas++; } -template static void -MarkDelayedChilderen(JSTracer *trc, ArenaHeader *aheader) +MarkDelayedChildren(GCMarker *trc, Arena *a) { - Arena *a = aheader->getArena(); - T *end = &a->t.things[Arena::ThingsPerArena]; - for (T* thing = &a->t.things[0]; thing != end; ++thing) { - if (thing->isMarked()) - js::gc::MarkChildren(trc, thing); + AllocKind allocKind = a->aheader.getAllocKind(); + JSGCTraceKind traceKind = MapAllocToTraceKind(allocKind); + size_t thingSize = Arena::thingSize(allocKind); + uintptr_t end = a->thingsEnd(); + for (uintptr_t thing = a->thingsStart(allocKind); thing != end; thing += thingSize) { + Cell *t = reinterpret_cast(thing); + if (t->isMarked()) + JS_TraceChildren(trc, t, traceKind); } } void GCMarker::markDelayedChildren() { - while (unmarkedArenaStackTop != MarkingDelay::stackBottom()) { + JS_ASSERT(unmarkedArenaStackTop); + do { /* * If marking gets delayed at the same arena again, we must repeat * marking of its things. For that we pop arena from the stack and - * clear its nextDelayedMarking before we begin the marking. + * clear its hasDelayedMarking flag before we begin the marking. */ - ArenaHeader *aheader = unmarkedArenaStackTop; - unmarkedArenaStackTop = aheader->getMarkingDelay()->link; - JS_ASSERT(unmarkedArenaStackTop); - aheader->getMarkingDelay()->link = NULL; -#ifdef DEBUG + Arena *a = unmarkedArenaStackTop; + JS_ASSERT(a->aheader.hasDelayedMarking); JS_ASSERT(markLaterArenas); + unmarkedArenaStackTop = a->aheader.getNextDelayedMarking(); + a->aheader.hasDelayedMarking = 0; markLaterArenas--; -#endif - - switch (aheader->getThingKind()) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_STRING: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_EXTERNAL_STRING: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_SHORT_STRING: - JS_NOT_REACHED("no delayed marking"); - break; - case FINALIZE_FUNCTION: - MarkDelayedChilderen(this, aheader); - break; - case FINALIZE_SHAPE: - MarkDelayedChilderen(this, aheader); - break; -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - MarkDelayedChilderen(this, aheader); - break; -#endif - default: - JS_NOT_REACHED("wrong thingkind"); - } - } + MarkDelayedChildren(this, a); + } while (unmarkedArenaStackTop); JS_ASSERT(!markLaterArenas); } @@ -1498,7 +1815,7 @@ GCMarker::markDelayedChildren() #ifdef DEBUG static void -EmptyMarkCallback(JSTracer *trc, void *thing, uint32 kind) +EmptyMarkCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind) { } #endif @@ -1515,48 +1832,37 @@ gc_root_traversal(JSTracer *trc, const RootEntry &entry) ptr = vp->isGCThing() ? vp->toGCThing() : NULL; } - if (ptr) { - if (!JSAtom::isStatic(ptr)) { - /* Use conservative machinery to find if ptr is a valid GC thing. */ - JSTracer checker; - JS_TRACER_INIT(&checker, trc->context, EmptyMarkCallback); - ConservativeGCTest test = MarkIfGCThingWord(&checker, reinterpret_cast(ptr)); - if (test != CGCT_VALID && entry.value.name) { - fprintf(stderr, + if (ptr && !trc->runtime->gcCurrentCompartment) { + /* + * Use conservative machinery to find if ptr is a valid GC thing. + * We only do this during global GCs, to preserve the invariant + * that mark callbacks are not in place during compartment GCs. + */ + JSTracer checker; + JS_TracerInit(&checker, trc->context, EmptyMarkCallback); + ConservativeGCTest test = MarkIfGCThingWord(&checker, reinterpret_cast(ptr)); + if (test != CGCT_VALID && entry.value.name) { + fprintf(stderr, "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" "invalid gcthing. This is usually caused by a missing call to JS_RemoveRoot.\n" "The root's name is \"%s\".\n", - entry.value.name); - } - JS_ASSERT(test == CGCT_VALID); + entry.value.name); } + JS_ASSERT(test == CGCT_VALID); } #endif - JS_SET_TRACING_NAME(trc, entry.value.name ? entry.value.name : "root"); + const char *name = entry.value.name ? entry.value.name : "root"; if (entry.value.type == JS_GC_ROOT_GCTHING_PTR) - MarkGCThing(trc, *reinterpret_cast(entry.key)); + MarkGCThingRoot(trc, *reinterpret_cast(entry.key), name); else - MarkValueRaw(trc, *reinterpret_cast(entry.key)); + MarkValueRoot(trc, *reinterpret_cast(entry.key), name); } static void gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc) { JS_ASSERT(entry.value >= 1); - MarkGCThing(trc, entry.key, "locked object"); -} - -void -js_TraceStackFrame(JSTracer *trc, StackFrame *fp) -{ - MarkObject(trc, fp->scopeChain(), "scope chain"); - if (fp->isDummyFrame()) - return; - if (fp->hasArgsObj()) - MarkObject(trc, fp->argsObj(), "arguments"); - js_TraceScript(trc, fp->script()); - fp->script()->compartment->active = true; - MarkValue(trc, fp->returnValue(), "rval"); + MarkGCThingRoot(trc, entry.key, "locked object"); } void @@ -1569,7 +1875,7 @@ AutoIdArray::trace(JSTracer *trc) void AutoEnumStateRooter::trace(JSTracer *trc) { - gc::MarkObject(trc, *obj, "js::AutoEnumStateRooter.obj"); + gc::MarkObjectRoot(trc, obj, "JS::AutoEnumStateRooter.obj"); } inline void @@ -1577,29 +1883,20 @@ AutoGCRooter::trace(JSTracer *trc) { switch (tag) { case JSVAL: - MarkValue(trc, static_cast(this)->val, "js::AutoValueRooter.val"); - return; - - case SHAPE: - MarkShape(trc, static_cast(this)->shape, "js::AutoShapeRooter.val"); + MarkValueRoot(trc, static_cast(this)->val, "JS::AutoValueRooter.val"); return; case PARSER: static_cast(this)->trace(trc); return; - case SCRIPT: - if (JSScript *script = static_cast(this)->script) - js_TraceScript(trc, script); - return; - case ENUMERATOR: static_cast(this)->trace(trc); return; case IDARRAY: { JSIdArray *ida = static_cast(this)->idArray; - MarkIdRange(trc, ida->length, ida->vector, "js::AutoIdArray.idArray"); + MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray"); return; } @@ -1608,10 +1905,10 @@ AutoGCRooter::trace(JSTracer *trc) static_cast(this)->descriptors; for (size_t i = 0, len = descriptors.length(); i < len; i++) { PropDesc &desc = descriptors[i]; - MarkValue(trc, desc.pd, "PropDesc::pd"); - MarkValue(trc, desc.value, "PropDesc::value"); - MarkValue(trc, desc.get, "PropDesc::get"); - MarkValue(trc, desc.set, "PropDesc::set"); + MarkValueRoot(trc, desc.pd, "PropDesc::pd"); + MarkValueRoot(trc, desc.value, "PropDesc::value"); + MarkValueRoot(trc, desc.get, "PropDesc::get"); + MarkValueRoot(trc, desc.set, "PropDesc::set"); } return; } @@ -1619,20 +1916,19 @@ AutoGCRooter::trace(JSTracer *trc) case DESCRIPTOR : { PropertyDescriptor &desc = *static_cast(this); if (desc.obj) - MarkObject(trc, *desc.obj, "Descriptor::obj"); - MarkValue(trc, desc.value, "Descriptor::value"); + MarkObjectRoot(trc, desc.obj, "Descriptor::obj"); + MarkValueRoot(trc, desc.value, "Descriptor::value"); if ((desc.attrs & JSPROP_GETTER) && desc.getter) - MarkObject(trc, *CastAsObject(desc.getter), "Descriptor::get"); + MarkObjectRoot(trc, CastAsObject(desc.getter), "Descriptor::get"); if (desc.attrs & JSPROP_SETTER && desc.setter) - MarkObject(trc, *CastAsObject(desc.setter), "Descriptor::set"); + MarkObjectRoot(trc, CastAsObject(desc.setter), "Descriptor::set"); return; } case NAMESPACES: { - JSXMLArray &array = static_cast(this)->array; - MarkObjectRange(trc, array.length, reinterpret_cast(array.vector), - "JSXMLArray.vector"); - array.cursors->trace(trc); + JSXMLArray &array = static_cast(this)->array; + MarkObjectRange(trc, array.length, array.vector, "JSXMLArray.vector"); + js_XMLArrayCursorTrace(trc, array.cursors); return; } @@ -1642,44 +1938,60 @@ AutoGCRooter::trace(JSTracer *trc) case OBJECT: if (JSObject *obj = static_cast(this)->obj) - MarkObject(trc, *obj, "js::AutoObjectRooter.obj"); + MarkObjectRoot(trc, obj, "JS::AutoObjectRooter.obj"); return; case ID: - MarkId(trc, static_cast(this)->id_, "js::AutoIdRooter.val"); + MarkIdRoot(trc, static_cast(this)->id_, "JS::AutoIdRooter.id_"); return; case VALVECTOR: { AutoValueVector::VectorImpl &vector = static_cast(this)->vector; - MarkValueRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector"); + MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector"); return; } case STRING: if (JSString *str = static_cast(this)->str) - MarkString(trc, str, "js::AutoStringRooter.str"); + MarkStringRoot(trc, str, "JS::AutoStringRooter.str"); return; case IDVECTOR: { AutoIdVector::VectorImpl &vector = static_cast(this)->vector; - MarkIdRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector"); + MarkIdRootRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector"); return; } case SHAPEVECTOR: { AutoShapeVector::VectorImpl &vector = static_cast(this)->vector; - MarkShapeRange(trc, vector.length(), vector.begin(), "js::AutoShapeVector.vector"); + MarkShapeRootRange(trc, vector.length(), const_cast(vector.begin()), + "js::AutoShapeVector.vector"); return; } - case BINDINGS: { - static_cast(this)->bindings.trace(trc); + case OBJVECTOR: { + AutoObjectVector::VectorImpl &vector = static_cast(this)->vector; + MarkObjectRootRange(trc, vector.length(), vector.begin(), "js::AutoObjectVector.vector"); + return; + } + + case VALARRAY: { + AutoValueArray *array = static_cast(this); + MarkValueRootRange(trc, array->length(), array->start(), "js::AutoValueArray"); return; } } JS_ASSERT(tag >= 0); - MarkValueRange(trc, tag, static_cast(this)->array, "js::AutoArrayRooter.array"); + MarkValueRootRange(trc, tag, static_cast(this)->array, + "JS::AutoArrayRooter.array"); +} + +void +AutoGCRooter::traceAll(JSTracer *trc) +{ + for (js::AutoGCRooter *gcr = this; gcr; gcr = gcr->down) + gcr->trace(trc); } namespace js { @@ -1691,26 +2003,38 @@ MarkContext(JSTracer *trc, JSContext *acx) /* Mark other roots-by-definition in acx. */ if (acx->globalObject && !acx->hasRunOption(JSOPTION_UNROOTED_GLOBAL)) - MarkObject(trc, *acx->globalObject, "global object"); + MarkObjectRoot(trc, acx->globalObject, "global object"); if (acx->isExceptionPending()) - MarkValue(trc, acx->getPendingException(), "exception"); + MarkValueRoot(trc, acx->getPendingException(), "exception"); - for (js::AutoGCRooter *gcr = acx->autoGCRooters; gcr; gcr = gcr->down) - gcr->trace(trc); + if (acx->autoGCRooters) + acx->autoGCRooters->traceAll(trc); if (acx->sharpObjectMap.depth > 0) js_TraceSharpMap(trc, &acx->sharpObjectMap); - MarkValue(trc, acx->iterValue, "iterValue"); + MarkValueRoot(trc, acx->iterValue, "iterValue"); +} + +void +MarkWeakReferences(GCMarker *gcmarker) +{ + JS_ASSERT(gcmarker->isMarkStackEmpty()); + while (WatchpointMap::markAllIteratively(gcmarker) || + WeakMapBase::markAllIteratively(gcmarker) || + Debugger::markAllIteratively(gcmarker)) { + gcmarker->drainMarkStack(); + } + JS_ASSERT(gcmarker->isMarkStackEmpty()); } -JS_REQUIRES_STACK void +static void MarkRuntime(JSTracer *trc) { - JSRuntime *rt = trc->context->runtime; + JSRuntime *rt = trc->runtime; - if (rt->state != JSRTS_LANDING) - MarkConservativeStackRoots(trc); + if (rt->hasContexts()) + MarkConservativeStackRoots(trc, rt); for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) gc_root_traversal(trc, r.front()); @@ -1718,77 +2042,90 @@ MarkRuntime(JSTracer *trc) for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront()) gc_lock_traversal(r.front(), trc); + if (rt->scriptPCCounters) { + const ScriptOpcodeCountsVector &vec = *rt->scriptPCCounters; + for (size_t i = 0; i < vec.length(); i++) + MarkScriptRoot(trc, vec[i].script, "scriptPCCounters"); + } + js_TraceAtomState(trc); - js_MarkTraps(trc); + rt->staticStrings.trace(trc); JSContext *iter = NULL; while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) MarkContext(trc, acx); -#ifdef JS_TRACER - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->traceMonitor.mark(trc); -#endif - - for (ThreadDataIter i(rt); !i.empty(); i.popFront()) - i.threadData()->mark(trc); + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + if (c->activeAnalysis) + c->markTypes(trc); - /* - * We mark extra roots at the last thing so it can use use additional - * colors to implement cycle collection. - */ - if (rt->gcExtraRootsTraceOp) - rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); + /* During a GC, these are treated as weak pointers. */ + if (!IS_GC_MARKING_TRACER(trc)) { + if (c->watchpointMap) + c->watchpointMap->markAll(trc); + } -#ifdef DEBUG - if (rt->functionMeterFilename) { - for (int k = 0; k < 2; k++) { - typedef JSRuntime::FunctionCountMap HM; - HM &h = (k == 0) ? rt->methodReadBarrierCountMap : rt->unjoinedFunctionCountMap; - for (HM::Range r = h.all(); !r.empty(); r.popFront()) { - JSFunction *fun = r.front().key; - JS_CALL_OBJECT_TRACER(trc, fun, "FunctionCountMap key"); + /* Do not discard scripts with counters while profiling. */ + if (rt->profilingScripts) { + for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->pcCounters) + MarkScriptRoot(trc, script, "profilingScripts"); } } } + +#ifdef JS_METHODJIT + /* We need to expand inline frames before stack scanning. */ + for (CompartmentsIter c(rt); !c.done(); c.next()) + mjit::ExpandInlineFrames(c); #endif + + rt->stackSpace.mark(trc); + + /* The embedding can register additional roots here. */ + if (JSTraceDataOp op = rt->gcBlackRootsTraceOp) + (*op)(trc, rt->gcBlackRootsData); + + if (!IS_GC_MARKING_TRACER(trc)) { + /* We don't want to miss these when called from TraceRuntime. */ + if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) + (*op)(trc, rt->gcGrayRootsData); + } } void -TriggerGC(JSRuntime *rt) +TriggerGC(JSRuntime *rt, gcreason::Reason reason) { - JS_ASSERT(!rt->gcRunning); - if (rt->gcIsNeeded) + JS_ASSERT(rt->onOwnerThread()); + + if (rt->gcRunning || rt->gcIsNeeded) return; - /* - * Trigger the GC when it is safe to call an operation callback on any - * thread. - */ + /* Trigger the GC when it is safe to call an operation callback. */ rt->gcIsNeeded = true; rt->gcTriggerCompartment = NULL; - TriggerAllOperationCallbacks(rt); + rt->gcTriggerReason = reason; + rt->triggerOperationCallback(); } void -TriggerCompartmentGC(JSCompartment *comp) +TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason) { JSRuntime *rt = comp->rt; JS_ASSERT(!rt->gcRunning); -#ifdef JS_GC_ZEAL - if (rt->gcZeal >= 1) { - TriggerGC(rt); + if (rt->gcZeal()) { + TriggerGC(rt, reason); return; } -#endif if (rt->gcMode != JSGC_MODE_COMPARTMENT || comp == rt->atomsCompartment) { /* We can't do a compartmental GC of the default compartment. */ - TriggerGC(rt); + TriggerGC(rt, reason); return; } - + if (rt->gcIsNeeded) { /* If we need to GC more than one compartment, run a full GC. */ if (rt->gcTriggerCompartment != comp) @@ -1796,285 +2133,235 @@ TriggerCompartmentGC(JSCompartment *comp) return; } - if (rt->gcBytes > 8192 && rt->gcBytes >= 3 * (rt->gcTriggerBytes / 2)) { - /* If we're using significantly more than our quota, do a full GC. */ - TriggerGC(rt); - return; - } - /* * Trigger the GC when it is safe to call an operation callback on any * thread. */ rt->gcIsNeeded = true; rt->gcTriggerCompartment = comp; - TriggerAllOperationCallbacks(comp->rt); + rt->gcTriggerReason = reason; + comp->rt->triggerOperationCallback(); } void MaybeGC(JSContext *cx) { JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); -#ifdef JS_GC_ZEAL - if (rt->gcZeal > 0) { - js_GC(cx, NULL, GC_NORMAL); + if (rt->gcZeal()) { + js_GC(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); return; } -#endif JSCompartment *comp = cx->compartment; if (rt->gcIsNeeded) { - js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL); + js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); return; } - if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) - js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL); -} - -} /* namespace js */ - -void -js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp) -{ - JSScript **listp, *script; - - for (size_t i = 0; i != JS_ARRAY_LENGTH(comp->scriptsToGC); ++i) { - listp = &comp->scriptsToGC[i]; - while ((script = *listp) != NULL) { - *listp = script->u.nextToGC; - script->u.nextToGC = NULL; - js_DestroyCachedScript(cx, script); - } + if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) { + js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); + return; } -} - -template -static void -FinalizeArenaList(JSCompartment *comp, JSContext *cx, JSGCInvocationKind gckind, unsigned thingKind) -{ - JS_STATIC_ASSERT(!(sizeof(T) & Cell::CellMask)); - ArenaList *arenaList = GetFinalizableArenaList(comp, thingKind); - ArenaHeader **ap = &arenaList->head; -#ifdef JS_GCMETER - uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0; -#endif - while (ArenaHeader *aheader = *ap) { - JS_ASSERT(aheader->getThingKind() == thingKind); - JS_ASSERT(aheader->getThingSize() == sizeof(T)); - bool allClear = aheader->getArena()->finalize(cx); - if (allClear) { - *ap = aheader->next; - aheader->chunk()->releaseArena(aheader); - METER(nkilledarenas++); + /* + * Access to the counters and, on 32 bit, setting gcNextFullGCTime below + * is not atomic and a race condition could trigger or suppress the GC. We + * tolerate this. + */ + int64_t now = PRMJ_Now(); + if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) { + if (rt->gcChunkAllocationSinceLastGC || + rt->gcNumArenasFreeCommitted > FreeCommittedArenasThreshold) + { + js_GC(cx, NULL, GC_SHRINK, gcreason::MAYBEGC); } else { - ap = &aheader->next; - METER(nlivearenas++); + rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN; } } - arenaList->cursor = arenaList->head; - METER(UpdateCompartmentStats(comp, thingKind, nlivearenas, nkilledarenas, nthings)); } -template static void -FinalizeArenaListLater(JSContext *cx, ArenaList *arenaList, ArenaHeader *listHead) +DecommitArenasFromAvailableList(JSRuntime *rt, Chunk **availableListHeadp) { - JS_STATIC_ASSERT(!(sizeof(T) & Cell::CellMask)); - JS_ASSERT(arenaList->hasToBeFinalized); - ArenaHeader **ap = &listHead; - ArenaHeader *aheader = *ap; - JS_ASSERT(aheader); -#ifdef DEBUG - int thingKind = listHead->getThingKind(); - JSCompartment *comp = listHead->compartment; -#endif - JS_ASSERT(sizeof(T) == listHead->getThingSize()); + Chunk *chunk = *availableListHeadp; + if (!chunk) + return; + + /* + * Decommit is expensive so we avoid holding the GC lock while calling it. + * + * We decommit from the tail of the list to minimize interference with the + * main thread that may start to allocate things at this point. + * + * The arena that is been decommitted outside the GC lock must not be + * available for allocations either via the free list or via the + * decommittedArenas bitmap. For that we just fetch the arena from the + * free list before the decommit pretending as it was allocated. If this + * arena also is the single free arena in the chunk, then we must remove + * from the available list before we release the lock so the allocation + * thread would not see chunks with no free arenas on the available list. + * + * After we retake the lock, we mark the arena as free and decommitted if + * the decommit was successful. We must also add the chunk back to the + * available list if we removed it previously or when the main thread + * have allocated all remaining free arenas in the chunk. + * + * We also must make sure that the aheader is not accessed again after we + * decommit the arena. + */ + JS_ASSERT(chunk->info.prevp == availableListHeadp); + while (Chunk *next = chunk->info.next) { + JS_ASSERT(next->info.prevp == &chunk->info.next); + chunk = next; + } -#ifdef JS_GCMETER - uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0; -#endif for (;;) { - bool allClear = aheader->getArena()->finalize(cx); + while (chunk->info.numArenasFreeCommitted != 0) { + ArenaHeader *aheader = chunk->fetchNextFreeArena(rt); + + Chunk **savedPrevp = chunk->info.prevp; + if (!chunk->hasAvailableArenas()) + chunk->removeFromAvailableList(); + + size_t arenaIndex = Chunk::arenaIndex(aheader->arenaAddress()); + bool ok; + { + /* + * If the main thread waits for the decommit to finish, skip + * potentially expensive unlock/lock pair on the contested + * lock. + */ + Maybe maybeUnlock; + if (!rt->gcRunning) + maybeUnlock.construct(rt); + ok = MarkPagesUnused(aheader->getArena(), ArenaSize); + } + + if (ok) { + ++chunk->info.numArenasFree; + chunk->decommittedArenas.set(arenaIndex); + } else { + chunk->addArenaToFreeList(rt, aheader); + } + JS_ASSERT(chunk->hasAvailableArenas()); + JS_ASSERT(!chunk->unused()); + if (chunk->info.numArenasFree == 1) { + /* + * Put the chunk back to the available list either at the + * point where it was before to preserve the available list + * that we enumerate, or, when the allocation thread has fully + * used all the previous chunks, at the beginning of the + * available list. + */ + Chunk **insertPoint = savedPrevp; + if (savedPrevp != availableListHeadp) { + Chunk *prev = Chunk::fromPointerToNext(savedPrevp); + if (!prev->hasAvailableArenas()) + insertPoint = availableListHeadp; + } + chunk->insertToAvailableList(insertPoint); + } else { + JS_ASSERT(chunk->info.prevp); + } + + if (rt->gcChunkAllocationSinceLastGC) { + /* + * The allocator thread has started to get new chunks. We should stop + * to avoid decommitting arenas in just allocated chunks. + */ + return; + } + } /* - * We don't delete the head because the next allocated arena has to - * link to it. + * chunk->info.prevp becomes null when the allocator thread consumed + * all chunks from the available list. */ - if (allClear && (aheader != listHead)) { - *ap = aheader->next; - aheader->chunk()->releaseArena(aheader); - METER(nkilledarenas++); - } else { - ap = &aheader->next; - METER(nlivearenas++); - } - if (!(aheader = *ap)) + JS_ASSERT_IF(chunk->info.prevp, *chunk->info.prevp == chunk); + if (chunk->info.prevp == availableListHeadp || !chunk->info.prevp) break; - } - arenaList->cursor = listHead; - arenaList->hasToBeFinalized = false; - METER(UpdateCompartmentStats(comp, thingKind, nlivearenas, nkilledarenas, nthings)); -} - -void -FinalizeArenaList(JSContext *cx, ArenaList *list, ArenaHeader *listHead) -{ - JS_ASSERT(list->head); - JS_ASSERT(listHead); - FinalizeKind kind = js::gc::FinalizeKind(listHead->getThingKind()); - switch (kind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT16: - case FINALIZE_FUNCTION: - case FINALIZE_SHAPE: - case FINALIZE_EXTERNAL_STRING: - JS_NOT_REACHED("no background finalization"); - break; - case FINALIZE_OBJECT0_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_OBJECT2_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_OBJECT4_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_OBJECT8_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_OBJECT12_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_OBJECT16_BACKGROUND: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_STRING: - FinalizeArenaListLater(cx, list, listHead); - break; - case FINALIZE_SHORT_STRING: - FinalizeArenaListLater(cx, list, listHead); - break; - #if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - JS_NOT_REACHED("no background finalization"); - break; -#endif - default: - JS_NOT_REACHED("wrong kind"); + /* + * prevp exists and is not the list head. It must point to the next + * field of the previous chunk. + */ + chunk = chunk->getPrevious(); } } -#ifdef JS_THREADSAFE -template -void BackgroundFinalize(JSCompartment *comp, JSContext *cx, JSGCInvocationKind gckind, unsigned thingKind) +static void +DecommitArenas(JSRuntime *rt) { - ArenaList *list = GetFinalizableArenaList(comp, thingKind); - if (!(list->head && list->head->next && cx->gcBackgroundFree->finalizeLater(list))) - FinalizeArenaList(comp, cx, gckind, thingKind); + DecommitArenasFromAvailableList(rt, &rt->gcSystemAvailableChunkListHead); + DecommitArenasFromAvailableList(rt, &rt->gcUserAvailableChunkListHead); } -#endif -void -JSCompartment::finalizeObjectArenaLists(JSContext *cx, JSGCInvocationKind gckind) +/* Must be called with the GC lock taken. */ +static void +ExpireChunksAndArenas(JSRuntime *rt, bool shouldShrink) { - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT0); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT2); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT4); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT8); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT12); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT16); - FinalizeArenaList(this, cx, gckind, FINALIZE_FUNCTION); - -#ifdef JS_THREADSAFE - if (cx->gcBackgroundFree && gckind != GC_LAST_CONTEXT && cx->runtime->state != JSRTS_LANDING) { - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT0_BACKGROUND); - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT2_BACKGROUND); - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT4_BACKGROUND); - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT8_BACKGROUND); - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT12_BACKGROUND); - BackgroundFinalize(this, cx, gckind, FINALIZE_OBJECT16_BACKGROUND); - } else { - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT0_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT2_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT4_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT8_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT12_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT16_BACKGROUND); + if (Chunk *toFree = rt->gcChunkPool.expire(rt, shouldShrink)) { + AutoUnlockGC unlock(rt); + FreeChunkList(toFree); } -#else - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT0_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT2_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT4_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT8_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT12_BACKGROUND); - FinalizeArenaList(this, cx, gckind, FINALIZE_OBJECT16_BACKGROUND); -#endif -#if JS_HAS_XML_SUPPORT - FinalizeArenaList(this, cx, gckind, FINALIZE_XML); -#endif + if (shouldShrink) + DecommitArenas(rt); } -void -JSCompartment::finalizeStringArenaLists(JSContext *cx, JSGCInvocationKind gckind) -{ #ifdef JS_THREADSAFE - if (cx->gcBackgroundFree && gckind != GC_LAST_CONTEXT && cx->runtime->state != JSRTS_LANDING) { - BackgroundFinalize(this, cx, gckind, FINALIZE_SHORT_STRING); - BackgroundFinalize(this, cx, gckind, FINALIZE_STRING); - } else { - FinalizeArenaList(this, cx, gckind, FINALIZE_SHORT_STRING); - FinalizeArenaList(this, cx, gckind, FINALIZE_STRING); - } - FinalizeArenaList(this, cx, gckind, FINALIZE_EXTERNAL_STRING); -#else - FinalizeArenaList(this, cx, gckind, FINALIZE_SHORT_STRING); - FinalizeArenaList(this, cx, gckind, FINALIZE_STRING); - FinalizeArenaList(this, cx, gckind, FINALIZE_EXTERNAL_STRING); -#endif -} -void -JSCompartment::finalizeShapeArenaLists(JSContext *cx, JSGCInvocationKind gckind) +static unsigned +GetCPUCount() { - FinalizeArenaList(this, cx, gckind, FINALIZE_SHAPE); + static unsigned ncpus = 0; + if (ncpus == 0) { +# ifdef XP_WIN + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpus = unsigned(sysinfo.dwNumberOfProcessors); +# else + long n = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = (n > 0) ? unsigned(n) : 1; +# endif + } + return ncpus; } -#ifdef JS_THREADSAFE - -namespace js { - bool -GCHelperThread::init(JSRuntime *rt) +GCHelperThread::init() { if (!(wakeup = PR_NewCondVar(rt->gcLock))) return false; - if (!(sweepingDone = PR_NewCondVar(rt->gcLock))) + if (!(done = PR_NewCondVar(rt->gcLock))) return false; - thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL, + thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - return !!thread; + if (!thread) + return false; + backgroundAllocation = (GetCPUCount() >= 2); + return true; } void -GCHelperThread::finish(JSRuntime *rt) +GCHelperThread::finish() { PRThread *join = NULL; { AutoLockGC lock(rt); - if (thread && !shutdown) { - shutdown = true; - PR_NotifyCondVar(wakeup); + if (thread && state != SHUTDOWN) { + /* + * We cannot be in the ALLOCATING or CANCEL_ALLOCATION states as + * the allocations should have been stopped during the last GC. + */ + JS_ASSERT(state == IDLE || state == SWEEPING); + if (state == IDLE) + PR_NotifyCondVar(wakeup); + state = SHUTDOWN; join = thread; } } @@ -2084,54 +2371,140 @@ GCHelperThread::finish(JSRuntime *rt) } if (wakeup) PR_DestroyCondVar(wakeup); - if (sweepingDone) - PR_DestroyCondVar(sweepingDone); + if (done) + PR_DestroyCondVar(done); } /* static */ void GCHelperThread::threadMain(void *arg) { - JSRuntime *rt = static_cast(arg); - rt->gcHelperThread.threadLoop(rt); + static_cast(arg)->threadLoop(); } void -GCHelperThread::threadLoop(JSRuntime *rt) +GCHelperThread::threadLoop() { AutoLockGC lock(rt); - while (!shutdown) { - /* - * Sweeping can be true here on the first iteration if a GC and the - * corresponding startBackgroundSweep call happen before this thread - * has a chance to run. - */ - if (!sweeping) + + /* + * Even on the first iteration the state can be SHUTDOWN or SWEEPING if + * the stop request or the GC and the corresponding startBackgroundSweep call + * happen before this thread has a chance to run. + */ + for (;;) { + switch (state) { + case SHUTDOWN: + return; + case IDLE: PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT); - if (sweeping) { - AutoUnlockGC unlock(rt); + break; + case SWEEPING: doSweep(); + if (state == SWEEPING) + state = IDLE; + PR_NotifyAllCondVar(done); + break; + case ALLOCATING: + do { + Chunk *chunk; + { + AutoUnlockGC unlock(rt); + chunk = Chunk::allocate(rt); + } + + /* OOM stops the background allocation. */ + if (!chunk) + break; + JS_ASSERT(chunk->info.numArenasFreeCommitted == ArenasPerChunk); + rt->gcNumArenasFreeCommitted += ArenasPerChunk; + rt->gcChunkPool.put(chunk); + } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt)); + if (state == ALLOCATING) + state = IDLE; + break; + case CANCEL_ALLOCATION: + state = IDLE; + PR_NotifyAllCondVar(done); + break; } - sweeping = false; - PR_NotifyAllCondVar(sweepingDone); } } +bool +GCHelperThread::prepareForBackgroundSweep() +{ + JS_ASSERT(state == IDLE); + size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length(); + return finalizeVector.reserve(maxArenaLists); +} + +/* Must be called with the GC lock taken. */ void -GCHelperThread::startBackgroundSweep(JSRuntime *rt) +GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink) { /* The caller takes the GC lock. */ - JS_ASSERT(!sweeping); - sweeping = true; + JS_ASSERT(state == IDLE); + JS_ASSERT(cx); + JS_ASSERT(!finalizationContext); + finalizationContext = cx; + shrinkFlag = shouldShrink; + state = SWEEPING; PR_NotifyCondVar(wakeup); } +/* Must be called with the GC lock taken. */ +void +GCHelperThread::startBackgroundShrink() +{ + switch (state) { + case IDLE: + JS_ASSERT(!finalizationContext); + shrinkFlag = true; + state = SWEEPING; + PR_NotifyCondVar(wakeup); + break; + case SWEEPING: + shrinkFlag = true; + break; + case ALLOCATING: + case CANCEL_ALLOCATION: + /* + * If we have started background allocation there is nothing to + * shrink. + */ + break; + case SHUTDOWN: + JS_NOT_REACHED("No shrink on shutdown"); + } +} + +/* Must be called with the GC lock taken. */ void -GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt) +GCHelperThread::waitBackgroundSweepEnd() { - AutoLockGC lock(rt); - while (sweeping) - PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT); + while (state == SWEEPING) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +void +GCHelperThread::waitBackgroundSweepOrAllocEnd() +{ + if (state == ALLOCATING) + state = CANCEL_ALLOCATION; + while (state == SWEEPING || state == CANCEL_ALLOCATION) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +inline void +GCHelperThread::startBackgroundAllocationIfIdle() +{ + if (state == IDLE) { + state = ALLOCATING; + PR_NotifyCondVar(wakeup); + } } JS_FRIEND_API(void) @@ -2153,62 +2526,67 @@ GCHelperThread::replenishAndFreeLater(void *ptr) Foreground::free_(ptr); } +/* Must be called with the GC lock taken. */ void GCHelperThread::doSweep() { - JS_ASSERT(cx); - for (FinalizeListAndHead *i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - FinalizeArenaList(cx, i->list, i->head); - finalizeVector.resize(0); - cx = NULL; - - if (freeCursor) { - void **array = freeCursorEnd - FREE_ARRAY_LENGTH; - freeElementsAndArray(array, freeCursor); - freeCursor = freeCursorEnd = NULL; - } else { - JS_ASSERT(!freeCursorEnd); - } - for (void ***iter = freeVector.begin(); iter != freeVector.end(); ++iter) { - void **array = *iter; - freeElementsAndArray(array, array + FREE_ARRAY_LENGTH); + if (JSContext *cx = finalizationContext) { + finalizationContext = NULL; + AutoUnlockGC unlock(rt); + + /* + * We must finalize in the insert order, see comments in + * finalizeObjects. + */ + for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) + ArenaLists::backgroundFinalize(cx, *i); + finalizeVector.resize(0); + + if (freeCursor) { + void **array = freeCursorEnd - FREE_ARRAY_LENGTH; + freeElementsAndArray(array, freeCursor); + freeCursor = freeCursorEnd = NULL; + } else { + JS_ASSERT(!freeCursorEnd); + } + for (void ***iter = freeVector.begin(); iter != freeVector.end(); ++iter) { + void **array = *iter; + freeElementsAndArray(array, array + FREE_ARRAY_LENGTH); + } + freeVector.resize(0); } - freeVector.resize(0); -} + bool shrinking = shrinkFlag; + ExpireChunksAndArenas(rt, shrinking); + + /* + * The main thread may have called ShrinkGCBuffers while + * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag + * afterwards. + */ + if (!shrinking && shrinkFlag) { + shrinkFlag = false; + ExpireChunksAndArenas(rt, true); + } } #endif /* JS_THREADSAFE */ -static void -SweepCrossCompartmentWrappers(JSContext *cx) +} /* namespace js */ + +static bool +ReleaseObservedTypes(JSContext *cx) { JSRuntime *rt = cx->runtime; - /* - * Figure out how much JIT code should be released from inactive compartments. - * If multiple eighth-lives have passed, compound the release interval linearly; - * if enough time has passed, all inactive JIT code will be released. - */ - uint32 releaseInterval = 0; - int64 now = PRMJ_Now(); + + bool releaseTypes = false; + int64_t now = PRMJ_Now(); if (now >= rt->gcJitReleaseTime) { - releaseInterval = 8; - while (now >= rt->gcJitReleaseTime) { - if (--releaseInterval == 1) - rt->gcJitReleaseTime = now; - rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME; - } + releaseTypes = true; + rt->gcJitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; } - /* - * Sweep the compartment: - * (1) Remove dead wrappers from the compartment map. - * (2) Finalize any unused empty shapes. - * (3) Sweep the trace JIT of unused code. - * (4) Sweep the method JIT ICs and release infrequently used JIT code. - */ - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->sweep(cx, releaseInterval); + return releaseTypes; } static void @@ -2228,11 +2606,11 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) JSCompartment *compartment = *read++; if (!compartment->hold && - (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) + (compartment->arenas.arenaListsAreEmpty() || !rt->hasContexts())) { - JS_ASSERT(compartment->freeLists.isEmpty()); + compartment->arenas.checkEmptyFreeLists(); if (callback) - (void) callback(cx, compartment, JSCOMPARTMENT_DESTROY); + JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); if (compartment->principals) JSPRINCIPALS_DROP(cx, compartment->principals); cx->delete_(compartment); @@ -2243,134 +2621,77 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) rt->compartments.resize(write - rt->compartments.begin()); } -/* - * Common cache invalidation and so forth that must be done before GC. Even if - * GCUntilDone calls GC several times, this work needs to be done only once. - */ static void -PreGCCleanup(JSContext *cx, JSGCInvocationKind gckind) +BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; - /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ - rt->gcIsNeeded = false; - rt->gcTriggerCompartment = NULL; + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->purge(cx); - /* Reset malloc counter. */ - rt->resetGCMallocBytes(); + rt->purge(cx); -#ifdef JS_DUMP_SCOPE_METERS { - extern void js_DumpScopeMeters(JSRuntime *rt); - js_DumpScopeMeters(rt); + JSContext *iter = NULL; + while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) + acx->purge(); } -#endif /* - * Reset the property cache's type id generator so we can compress ids. - * Same for the protoHazardShape proxy-shape standing in for all object - * prototypes having readonly or setter properties. + * Mark phase. */ - if (rt->shapeGen & SHAPE_OVERFLOW_BIT -#ifdef JS_GC_ZEAL - || rt->gcZeal >= 1 -#endif - ) { - rt->gcRegenShapes = true; - rt->shapeGen = 0; - rt->protoHazardShape = 0; - } + rt->gcStats.beginPhase(gcstats::PHASE_MARK); + + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) + r.front()->bitmap.clear(); if (rt->gcCurrentCompartment) { - rt->gcCurrentCompartment->purge(cx); - } else { - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->purge(cx); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->markCrossCompartmentWrappers(gcmarker); + Debugger::markCrossCompartmentDebuggerObjectReferents(gcmarker); } - js_PurgeThreads(cx); - { - JSContext *iter = NULL; - while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) - acx->purge(); - } + MarkRuntime(gcmarker); } -/* - * Perform mark-and-sweep GC. - * - * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each - * other thread must be either outside all requests or blocked waiting for GC - * to finish. Note that the caller does not hold rt->gcLock. - * If comp is set, we perform a single-compartment GC. - */ static void -MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_PARAM) +EndMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; - rt->gcNumber++; - JS_ASSERT_IF(comp, !rt->gcRegenShapes); - JS_ASSERT_IF(comp, gckind != GC_LAST_CONTEXT); - JS_ASSERT_IF(comp, comp != rt->atomsCompartment); - JS_ASSERT_IF(comp, comp->rt->gcMode == JSGC_MODE_COMPARTMENT); - /* - * Mark phase. - */ - GCMarker gcmarker(cx); - JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker)); - JS_ASSERT(gcmarker.getMarkColor() == BLACK); - rt->gcMarkingTracer = &gcmarker; -#ifdef JS_THREADSAFE - /* - * cx->gcBackgroundFree is set if we need several mark-and-sweep loops to - * finish the GC. - */ - if (!cx->gcBackgroundFree) { - /* Wait until the sweeping from the previois GC finishes. */ - rt->gcHelperThread.waitBackgroundSweepEnd(rt); - cx->gcBackgroundFree = &rt->gcHelperThread; - } else { - rt->gcHelperThread.waitBackgroundSweepEnd(rt); - } - JS_ASSERT(!rt->gcHelperThread.sweeping); - cx->gcBackgroundFree->setContext(cx); -#endif - for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) - r.front()->clearMarkBitmap(); + JS_ASSERT(gcmarker->isMarkStackEmpty()); + MarkWeakReferences(gcmarker); - if (comp) { - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->markCrossCompartmentWrappers(&gcmarker); - } else { - js_MarkScriptFilenames(rt); + if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) { + gcmarker->setMarkColorGray(); + (*op)(gcmarker, rt->gcGrayRootsData); + gcmarker->drainMarkStack(); + MarkWeakReferences(gcmarker); } - MarkRuntime(&gcmarker); - - gcmarker.drainMarkStack(); - - /* - * Mark weak roots. - */ - while (true) { - if (!js_TraceWatchPoints(&gcmarker) && !WeakMap::markIteratively(&gcmarker)) - break; - gcmarker.drainMarkStack(); - } + JS_ASSERT(gcmarker->isMarkStackEmpty()); + rt->gcIncrementalTracer = NULL; - rt->gcMarkingTracer = NULL; + rt->gcStats.endPhase(gcstats::PHASE_MARK); if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_MARK_END); #ifdef DEBUG /* Make sure that we didn't mark an object in another compartment */ - if (comp) { - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, checkArenaListAllUnmarked(*c)); + if (rt->gcCurrentCompartment) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { + JS_ASSERT_IF(c != rt->gcCurrentCompartment && c != rt->atomsCompartment, + c->arenas.checkArenaListAllUnmarked()); + } } #endif +} + +static void +SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) +{ + JSRuntime *rt = cx->runtime; /* * Sweep phase. @@ -2386,56 +2707,58 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM * unique. This works since the atomization API must not be called during * the GC. */ - TIMESTAMP(startSweep); + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP); /* Finalize unreachable (key,value) pairs in all weak maps. */ - WeakMap::sweep(cx); + WeakMapBase::sweepAll(gcmarker); - js_SweepAtomState(cx); + js_SweepAtomState(rt); - /* Finalize watch points associated with unreachable objects. */ - js_SweepWatchPoints(cx); + /* Collect watch points associated with unreachable objects. */ + WatchpointMap::sweepAll(rt); - /* - * We finalize objects before other GC things to ensure that object's finalizer - * can access them even if they will be freed. Sweep the runtime's property trees - * after finalizing objects, in case any had watchpoints referencing tree nodes. - * Do this before sweeping compartments, so that we sweep all shapes in - * unreachable compartments. - */ - if (comp) { - comp->sweep(cx, 0); - comp->finalizeObjectArenaLists(cx, gckind); - TIMESTAMP(sweepObjectEnd); - comp->finalizeStringArenaLists(cx, gckind); - TIMESTAMP(sweepStringEnd); - comp->finalizeShapeArenaLists(cx, gckind); - TIMESTAMP(sweepShapeEnd); - } else { - SweepCrossCompartmentWrappers(cx); - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - (*c)->finalizeObjectArenaLists(cx, gckind); + if (!rt->gcCurrentCompartment) + Debugger::sweepAll(cx); - TIMESTAMP(sweepObjectEnd); + bool releaseTypes = !rt->gcCurrentCompartment && ReleaseObservedTypes(cx); + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->sweep(cx, releaseTypes); - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - (*c)->finalizeStringArenaLists(cx, gckind); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_OBJECT); - TIMESTAMP(sweepStringEnd); + /* + * We finalize objects before other GC things to ensure that the object's + * finalizer can access the other things even if they will be freed. + */ + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.finalizeObjects(cx); + } - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - (*c)->finalizeShapeArenaLists(cx, gckind); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_STRING); + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.finalizeStrings(cx); + } - TIMESTAMP(sweepShapeEnd); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_SCRIPT); + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.finalizeScripts(cx); + } - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->propertyTree.dumpShapeStats(); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_SHAPE); + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.finalizeShapes(cx); } - PropertyTree::dumpShapes(cx); +#ifdef DEBUG + PropertyTree::dumpShapes(cx); +#endif - if (!comp) { - SweepCompartments(cx, gckind); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DESTROY); /* * Sweep script filenames after sweeping functions in the generic loop @@ -2443,101 +2766,72 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM * script and calls rt->destroyScriptHook, the hook can still access the * script's filename. See bug 323267. */ - js_SweepScriptFilenames(rt); - } + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + js_SweepScriptFilenames(c); - /* - * Destroy arenas after we finished the sweeping so finalizers can safely - * use IsAboutToBeFinalized(). - */ - ExpireGCChunks(rt); - TIMESTAMP(sweepDestroyEnd); + /* + * This removes compartments from rt->compartment, so we do it last to make + * sure we don't miss sweeping any compartments. + */ + if (!rt->gcCurrentCompartment) + SweepCompartments(cx, gckind); - if (rt->gcCallback) - (void) rt->gcCallback(cx, JSGC_FINALIZE_END); -#ifdef DEBUG_srcnotesize - { extern void DumpSrcNoteSizeHist(); - DumpSrcNoteSizeHist(); - printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); - } +#ifndef JS_THREADSAFE + /* + * Destroy arenas after we finished the sweeping so finalizers can safely + * use IsAboutToBeFinalized(). + * This is done on the GCHelperThread if JS_THREADSAFE is defined. + */ + ExpireChunksAndArenas(rt, gckind == GC_SHRINK); #endif -} - -#ifdef JS_THREADSAFE + } -/* - * If the GC is running and we're called on another thread, wait for this GC - * activation to finish. We can safely wait here without fear of deadlock (in - * the case where we are called within a request on another thread's context) - * because the GC doesn't set rt->gcRunning until after it has waited for all - * active requests to end. - * - * We call here js_CurrentThreadId() after checking for rt->gcState to avoid - * an expensive call when the GC is not running. - */ -void -js_WaitForGC(JSRuntime *rt) -{ - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcRunning); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT); + if (rt->gcCallback) + (void) rt->gcCallback(cx, JSGC_FINALIZE_END); } } -/* - * GC is running on another thread. Temporarily suspend all requests running - * on the current thread and wait until the GC is done. - */ +/* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */ static void -LetOtherGCFinish(JSContext *cx) +MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; - JS_ASSERT(rt->gcThread); - JS_ASSERT(cx->thread() != rt->gcThread); + rt->gcNumber++; - size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0; - JS_ASSERT(requestDebit <= rt->requestCount); -#ifdef JS_TRACER - JS_ASSERT_IF(requestDebit == 0, !JS_ON_TRACE(cx)); -#endif - if (requestDebit != 0) { -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) { - /* - * Leave trace before we decrease rt->requestCount and notify the - * GC. Otherwise the GC may start immediately after we unlock while - * this thread is still on trace. - */ - AutoUnlockGC unlock(rt); - LeaveTrace(cx); - } -#endif - rt->requestCount -= requestDebit; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); + /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ + rt->gcIsNeeded = false; + rt->gcTriggerCompartment = NULL; + + /* Clear gcMallocBytes for all compartments */ + JSCompartment **read = rt->compartments.begin(); + JSCompartment **end = rt->compartments.end(); + JS_ASSERT(rt->compartments.length() >= 1); + + while (read < end) { + JSCompartment *compartment = *read++; + compartment->resetGCMallocBytes(); } - /* - * Check that we did not release the GC lock above and let the GC to - * finish before we wait. - */ - JS_ASSERT(rt->gcThread); + /* Reset weak map list. */ + WeakMapBase::resetWeakMapList(rt); - /* - * Wait for GC to finish on the other thread, even if requestDebit is 0 - * and even if GC has not started yet because the gcThread is waiting in - * AutoGCSession. This ensures that js_GC never returns without a full GC - * cycle happening. - */ - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcThread); + /* Reset malloc counter. */ + rt->resetGCMallocBytes(); - rt->requestCount += requestDebit; -} + AutoUnlockGC unlock(rt); -#endif + GCMarker gcmarker(cx); + JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker)); + JS_ASSERT(gcmarker.getMarkColor() == BLACK); + rt->gcIncrementalTracer = &gcmarker; + + BeginMarkPhase(cx, &gcmarker, gckind); + gcmarker.drainMarkStack(); + EndMarkPhase(cx, &gcmarker, gckind); + SweepPhase(cx, &gcmarker, gckind); +} class AutoGCSession { public: @@ -2547,114 +2841,50 @@ class AutoGCSession { private: JSContext *context; - /* Disable copy constructor or assignments */ - AutoGCSession(const AutoGCSession&); - void operator=(const AutoGCSession&); + AutoGCSession(const AutoGCSession&) MOZ_DELETE; + void operator=(const AutoGCSession&) MOZ_DELETE; }; -/* - * Start a new GC session. Together with LetOtherGCFinish this function - * contains the rendezvous algorithm by which we stop the world for GC. - * - * This thread becomes the GC thread. Wait for all other threads to quiesce. - * Then set rt->gcRunning and return. - */ +/* Start a new GC session. */ AutoGCSession::AutoGCSession(JSContext *cx) : context(cx) { + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); JSRuntime *rt = cx->runtime; - -#ifdef JS_THREADSAFE - if (rt->gcThread && rt->gcThread != cx->thread()) - LetOtherGCFinish(cx); -#endif - JS_ASSERT(!rt->gcRunning); - -#ifdef JS_THREADSAFE - /* No other thread is in GC, so indicate that we're now in GC. */ - JS_ASSERT(!rt->gcThread); - rt->gcThread = cx->thread(); - - /* - * Notify operation callbacks on other threads, which will give them a - * chance to yield their requests. Threads without requests perform their - * callback at some later point, which then will be unnecessary, but - * harmless. - */ - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - if (thread != cx->thread()) - thread->data.triggerOperationCallback(rt); - } - - /* - * Discount the request on the current thread from contributing to - * rt->requestCount before we wait for all other requests to finish. - * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on - * rt->requestCount transitions to 0. - */ - size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0; - JS_ASSERT(requestDebit <= rt->requestCount); - if (requestDebit != rt->requestCount) { - rt->requestCount -= requestDebit; - - do { - JS_AWAIT_REQUEST_DONE(rt); - } while (rt->requestCount > 0); - rt->requestCount += requestDebit; - } - -#endif /* JS_THREADSAFE */ - - /* - * Set rt->gcRunning here within the GC lock, and after waiting for any - * active requests to end. This way js_WaitForGC called outside a request - * would not block on the GC that is waiting for other requests to finish - * with rt->gcThread set while JS_BeginRequest would do such wait. - */ rt->gcRunning = true; } -/* End the current GC session and allow other threads to proceed. */ AutoGCSession::~AutoGCSession() { JSRuntime *rt = context->runtime; rt->gcRunning = false; -#ifdef JS_THREADSAFE - JS_ASSERT(rt->gcThread == context->thread()); - rt->gcThread = NULL; - JS_NOTIFY_GC_DONE(rt); -#endif } /* * GC, repeatedly if necessary, until we think we have not created any new - * garbage and no other threads are demanding more GC. + * garbage. We disable inlining to ensure that the bottom of the stack with + * possible GC roots recorded in js_GC excludes any pointers we use during the + * marking implementation. */ -static void -GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_PARAM) +static JS_NEVER_INLINE void +GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) { - if (JS_ON_TRACE(cx)) - return; - JSRuntime *rt = cx->runtime; - /* Recursive GC or a call from another thread restarts the GC cycle. */ - if (rt->gcMarkAndSweep) { - rt->gcPoke = true; -#ifdef JS_THREADSAFE - JS_ASSERT(rt->gcThread); - if (rt->gcThread != cx->thread()) { - /* We do not return until another GC finishes. */ - LetOtherGCFinish(cx); - } -#endif + JS_ASSERT_IF(comp, comp != rt->atomsCompartment); + JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT); + + /* Recursive GC is no-op. */ + if (rt->gcMarkAndSweep) return; - } AutoGCSession gcsession(cx); + /* Don't GC if we are reporting an OOM. */ + if (rt->inOOMReport) + return; + /* * We should not be depending on cx->compartment in the GC, so set it to * NULL to look for violations. @@ -2664,84 +2894,58 @@ GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM JS_ASSERT(!rt->gcCurrentCompartment); rt->gcCurrentCompartment = comp; - METER(rt->gcStats.poke++); - - bool firstRun = true; rt->gcMarkAndSweep = true; + #ifdef JS_THREADSAFE + /* + * As we about to purge caches and clear the mark bits we must wait for + * any background finalization to finish. We must also wait for the + * background allocation to finish so we can avoid taking the GC lock + * when manipulating the chunks during the GC. + */ JS_ASSERT(!cx->gcBackgroundFree); + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); + if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep()) + cx->gcBackgroundFree = &rt->gcHelperThread; #endif - do { - rt->gcPoke = false; - - AutoUnlockGC unlock(rt); - if (firstRun) { - PreGCCleanup(cx, gckind); - TIMESTAMP(startMark); - firstRun = false; - } - MarkAndSweep(cx, comp, gckind GCTIMER_ARG); + MarkAndSweep(cx, gckind); #ifdef JS_THREADSAFE + if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); - if (rt->gcPoke) { - AutoLockGC lock(rt); - cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(rt); - } -#endif - - // GC again if: - // - another thread, not in a request, called js_GC - // - js_GC was called recursively - // - a finalizer called js_RemoveRoot or js_UnlockGCThingRT. - } while (rt->gcPoke); - -#ifdef JS_THREADSAFE - JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); - cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(rt); + cx->gcBackgroundFree = NULL; + rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); + } #endif rt->gcMarkAndSweep = false; - rt->gcRegenShapes = false; - rt->setGCLastBytes(rt->gcBytes); rt->gcCurrentCompartment = NULL; - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->setGCLastBytes((*c)->gcBytes); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->setGCLastBytes(c->gcBytes, gckind); } void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) +js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason) { JSRuntime *rt = cx->runtime; + JS_AbortIfWrongThread(rt); - /* - * Don't collect garbage if the runtime isn't up, and cx is not the last - * context in the runtime. The last context must force a GC, and nothing - * should suppress that final collection or there may be shutdown leaks, - * or runtime bloat until the next context is created. - */ - if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) - return; +#ifdef JS_GC_ZEAL + struct AutoVerifyBarriers { + JSContext *cx; + bool inVerify; + AutoVerifyBarriers(JSContext *cx) : cx(cx), inVerify(cx->runtime->gcVerifyData) { + if (inVerify) EndVerifyBarriers(cx); + } + ~AutoVerifyBarriers() { if (inVerify) StartVerifyBarriers(cx); } + } av(cx); +#endif RecordNativeStackTopForGC(cx); -#ifdef DEBUG - int stackDummy; -# if JS_STACK_GROWTH_DIRECTION > 0 - /* cx->stackLimit is set to jsuword(-1) by default. */ - JS_ASSERT_IF(cx->stackLimit != jsuword(-1), - JS_CHECK_STACK_SIZE(cx->stackLimit + (1 << 14), &stackDummy)); -# else - /* -16k because it is possible to perform a GC during an overrecursion report. */ - JS_ASSERT_IF(cx->stackLimit, JS_CHECK_STACK_SIZE(cx->stackLimit - (1 << 14), &stackDummy)); -# endif -#endif - - GCTIMER_BEGIN(); + gcstats::AutoGC agc(rt->gcStats, comp, reason); do { /* @@ -2751,18 +2955,15 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) * on another thread. */ if (JSGCCallback callback = rt->gcCallback) { - if (!callback(cx, JSGC_BEGIN) && gckind != GC_LAST_CONTEXT) + if (!callback(cx, JSGC_BEGIN) && rt->hasContexts()) return; } { -#ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); -#endif /* Lock out other GC allocator and collector invocations. */ AutoLockGC lock(rt); - - GCUntilDone(cx, comp, gckind GCTIMER_ARG); + rt->gcPoke = false; + GCCycle(cx, comp, gckind); } /* We re-sample the callback again as the finalizers can change it. */ @@ -2770,125 +2971,824 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) (void) callback(cx, JSGC_END); /* - * On shutdown, iterate until the JSGC_END callback stops creating - * garbage. + * On shutdown, iterate until finalizers or the JSGC_END callback + * stop creating garbage. */ - } while (gckind == GC_LAST_CONTEXT && rt->gcPoke); -#ifdef JS_GCMETER - js_DumpGCStats(cx->runtime, stderr); -#endif - GCTIMER_END(gckind == GC_LAST_CONTEXT); + } while (!rt->hasContexts() && rt->gcPoke); + + rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN; + + rt->gcChunkAllocationSinceLastGC = false; } namespace js { -namespace gc { -bool -SetProtoCheckingForCycles(JSContext *cx, JSObject *obj, JSObject *proto) +void +ShrinkGCBuffers(JSRuntime *rt) { - /* - * This function cannot be called during the GC and always requires a - * request. - */ + AutoLockGC lock(rt); + JS_ASSERT(!rt->gcRunning); +#ifndef JS_THREADSAFE + ExpireChunksAndArenas(rt, true); +#else + rt->gcHelperThread.startBackgroundShrink(); +#endif +} + +class AutoCopyFreeListToArenas { + JSRuntime *rt; + + public: + AutoCopyFreeListToArenas(JSRuntime *rt) + : rt(rt) { + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.copyFreeListsToArenas(); + } + + ~AutoCopyFreeListToArenas() { + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.clearFreeListsInArenas(); + } +}; + +void +TraceRuntime(JSTracer *trc) +{ + JS_ASSERT(!IS_GC_MARKING_TRACER(trc)); + #ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()->data.requestDepth); + { + JSContext *cx = trc->context; + JSRuntime *rt = cx->runtime; + if (!rt->gcRunning) { + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); + + rt->gcHelperThread.waitBackgroundSweepEnd(); + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + RecordNativeStackTopForGC(trc->context); + MarkRuntime(trc); + return; + } + } +#else + AutoCopyFreeListToArenas copy(trc->runtime); + RecordNativeStackTopForGC(trc->context); +#endif /* - * This is only necessary if AutoGCSession below would wait for GC to - * finish on another thread, but to capture the minimal stack space and - * for code simplicity we do it here unconditionally. + * Calls from inside a normal GC or a recursive calls are OK and do not + * require session setup. */ - RecordNativeStackTopForGC(cx); + MarkRuntime(trc); +} + +struct IterateArenaCallbackOp +{ + JSContext *cx; + void *data; + IterateArenaCallback callback; + JSGCTraceKind traceKind; + size_t thingSize; + IterateArenaCallbackOp(JSContext *cx, void *data, IterateArenaCallback callback, + JSGCTraceKind traceKind, size_t thingSize) + : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize) + {} + void operator()(Arena *arena) { (*callback)(cx, data, arena, traceKind, thingSize); } +}; + +struct IterateCellCallbackOp +{ + JSContext *cx; + void *data; + IterateCellCallback callback; + JSGCTraceKind traceKind; + size_t thingSize; + IterateCellCallbackOp(JSContext *cx, void *data, IterateCellCallback callback, + JSGCTraceKind traceKind, size_t thingSize) + : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize) + {} + void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); } +}; + +void +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback) +{ + CHECK_REQUEST(cx); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + for (CompartmentsIter c(rt); !c.done(); c.next()) { + (*compartmentCallback)(cx, data, c); + } +} + +void +IterateCompartmentsArenasCells(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) +{ + CHECK_REQUEST(cx); JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + AutoLockGC lock(rt); AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); +#endif AutoUnlockGC unlock(rt); - bool cycle = false; - for (JSObject *obj2 = proto; obj2;) { - if (obj2 == obj) { - cycle = true; - break; + AutoCopyFreeListToArenas copy(rt); + for (CompartmentsIter c(rt); !c.done(); c.next()) { + (*compartmentCallback)(cx, data, c); + + for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { + JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind)); + size_t thingSize = Arena::thingSize(AllocKind(thingKind)); + IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize); + IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize); + ForEachArenaAndCell(c, AllocKind(thingKind), arenaOp, cellOp); } - obj2 = obj2->getProto(); } - if (!cycle) - obj->setProto(proto); +} - return !cycle; +void +IterateChunks(JSContext *cx, void *data, IterateChunkCallback chunkCallback) +{ + /* :XXX: Any way to common this preamble with IterateCompartmentsArenasCells? */ + CHECK_REQUEST(cx); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); +#endif + AutoUnlockGC unlock(rt); + + for (js::GCChunkSet::Range r = rt->gcChunkSet.all(); !r.empty(); r.popFront()) + chunkCallback(cx, data, r.front()); } +void +IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, + void *data, IterateCellCallback cellCallback) +{ + /* :XXX: Any way to common this preamble with IterateCompartmentsArenasCells? */ + CHECK_REQUEST(cx); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); +#endif + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); + size_t thingSize = Arena::thingSize(thingKind); + + if (compartment) { + for (CellIterUnderGC i(compartment, thingKind); !i.done(); i.next()) + cellCallback(cx, data, i.getCell(), traceKind, thingSize); + } else { + for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CellIterUnderGC i(c, thingKind); !i.done(); i.next()) + cellCallback(cx, data, i.getCell(), traceKind, thingSize); + } + } +} + +namespace gc { + JSCompartment * NewCompartment(JSContext *cx, JSPrincipals *principals) { JSRuntime *rt = cx->runtime; + JS_AbortIfWrongThread(rt); + JSCompartment *compartment = cx->new_(rt); - if (!compartment || !compartment->init()) { - Foreground::delete_(compartment); - JS_ReportOutOfMemory(cx); - return NULL; + if (compartment && compartment->init(cx)) { + // Any compartment with the trusted principals -- and there can be + // multiple -- is a system compartment. + compartment->isSystemCompartment = principals && rt->trustedPrincipals() == principals; + if (principals) { + compartment->principals = principals; + JSPRINCIPALS_HOLD(cx, principals); + } + + compartment->setGCLastBytes(8192, GC_NORMAL); + + /* + * Before reporting the OOM condition, |lock| needs to be cleaned up, + * hence the scoping. + */ + { + AutoLockGC lock(rt); + if (rt->compartments.append(compartment)) + return compartment; + } + + js_ReportOutOfMemory(cx); } + Foreground::delete_(compartment); + return NULL; +} - if (principals) { - compartment->principals = principals; - JSPRINCIPALS_HOLD(cx, principals); +void +RunDebugGC(JSContext *cx) +{ +#ifdef JS_GC_ZEAL + JSRuntime *rt = cx->runtime; + + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; + + RunLastDitchGC(cx); +#endif +} + +#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) + +static void +CheckStackRoot(JSTracer *trc, uintptr_t *w) +{ + /* Mark memory as defined for valgrind, as in MarkWordConservatively. */ +#ifdef JS_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w)); +#endif + + ConservativeGCTest test = MarkIfGCThingWord(trc, *w, DONT_MARK_THING); + + if (test == CGCT_VALID) { + JSContext *iter = NULL; + bool matched = false; + JSRuntime *rt = trc->context->runtime; + while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) { + for (unsigned i = 0; i < THING_ROOT_COUNT; i++) { + Root *rooter = acx->thingGCRooters[i]; + while (rooter) { + if (rooter->address() == (Cell **) w) + matched = true; + rooter = rooter->previous(); + } + } + CheckRoot *check = acx->checkGCRooters; + while (check) { + if (check->contains(static_cast(w), sizeof(w))) + matched = true; + check = check->previous(); + } + } + if (!matched) { + /* + * Only poison the last byte in the word. It is easy to get + * accidental collisions when a value that does not occupy a full + * word is used to overwrite a now-dead GC thing pointer. In this + * case we want to avoid damaging the smaller value. + */ + PoisonPtr(w); + } } +} - compartment->setGCLastBytes(8192); +static void +CheckStackRootsRange(JSTracer *trc, uintptr_t *begin, uintptr_t *end) +{ + JS_ASSERT(begin <= end); + for (uintptr_t *i = begin; i != end; ++i) + CheckStackRoot(trc, i); +} - { - AutoLockGC lock(rt); +void +CheckStackRoots(JSContext *cx) +{ + AutoCopyFreeListToArenas copy(cx->runtime); - if (!rt->compartments.append(compartment)) { - AutoUnlockGC unlock(rt); - Foreground::delete_(compartment); - JS_ReportOutOfMemory(cx); + JSTracer checker; + JS_TRACER_INIT(&checker, cx, EmptyMarkCallback); + + ThreadData *td = JS_THREAD_DATA(cx); + + ConservativeGCThreadData *ctd = &td->conservativeGC; + ctd->recordStackTop(); + + JS_ASSERT(ctd->hasStackToScan()); + uintptr_t *stackMin, *stackEnd; +#if JS_STACK_GROWTH_DIRECTION > 0 + stackMin = td->nativeStackBase; + stackEnd = ctd->nativeStackTop; +#else + stackMin = ctd->nativeStackTop + 1; + stackEnd = td->nativeStackBase; +#endif + + JS_ASSERT(stackMin <= stackEnd); + CheckStackRootsRange(&checker, stackMin, stackEnd); + CheckStackRootsRange(&checker, ctd->registerSnapshot.words, + ArrayEnd(ctd->registerSnapshot.words)); +} + +#endif /* DEBUG && JSGC_ROOT_ANALYSIS && !JS_THREADSAFE */ + +#ifdef JS_GC_ZEAL + +/* + * Write barrier verification + * + * The next few functions are for incremental write barrier verification. When + * StartVerifyBarriers is called, a snapshot is taken of all objects in the GC + * heap and saved in an explicit graph data structure. Later, EndVerifyBarriers + * traverses the heap again. Any pointer values that were in the snapshot and + * are no longer found must be marked; otherwise an assertion triggers. Note + * that we must not GC in between starting and finishing a verification phase. + * + * The VerifyBarriers function is a shorthand. It checks if a verification phase + * is currently running. If not, it starts one. Otherwise, it ends the current + * phase and starts a new one. + * + * The user can adjust the frequency of verifications, which causes + * VerifyBarriers to be a no-op all but one out of N calls. However, if the + * |always| parameter is true, it starts a new phase no matter what. + */ + +struct EdgeValue +{ + void *thing; + JSGCTraceKind kind; + char *label; +}; + +struct VerifyNode +{ + void *thing; + JSGCTraceKind kind; + uint32_t count; + EdgeValue edges[1]; +}; + +typedef HashMap NodeMap; + +/* + * The verifier data structures are simple. The entire graph is stored in a + * single block of memory. At the beginning is a VerifyNode for the root + * node. It is followed by a sequence of EdgeValues--the exact number is given + * in the node. After the edges come more nodes and their edges. + * + * The edgeptr and term fields are used to allocate out of the block of memory + * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term), + * we just abandon the verification. + * + * The nodemap field is a hashtable that maps from the address of the GC thing + * to the VerifyNode that represents it. + */ +struct VerifyTracer : JSTracer { + /* The gcNumber when the verification began. */ + uint32_t number; + + /* This counts up to JS_VERIFIER_FREQ to decide whether to verify. */ + uint32_t count; + + /* This graph represents the initial GC "snapshot". */ + VerifyNode *curnode; + VerifyNode *root; + char *edgeptr; + char *term; + NodeMap nodemap; + + /* A dummy marker used for the write barriers; stored in gcMarkingTracer. */ + GCMarker gcmarker; + + VerifyTracer(JSContext *cx) : nodemap(cx), gcmarker(cx) {} +}; + +/* + * This function builds up the heap snapshot by adding edges to the current + * node. + */ +static void +AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + VerifyTracer *trc = (VerifyTracer *)jstrc; + + trc->edgeptr += sizeof(EdgeValue); + if (trc->edgeptr >= trc->term) { + trc->edgeptr = trc->term; + return; + } + + VerifyNode *node = trc->curnode; + uint32_t i = node->count; + + node->edges[i].thing = *thingp; + node->edges[i].kind = kind; + node->edges[i].label = trc->debugPrinter ? NULL : (char *)trc->debugPrintArg; + node->count++; +} + +static VerifyNode * +MakeNode(VerifyTracer *trc, void *thing, JSGCTraceKind kind) +{ + NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing); + if (!p) { + VerifyNode *node = (VerifyNode *)trc->edgeptr; + trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue); + if (trc->edgeptr >= trc->term) { + trc->edgeptr = trc->term; return NULL; } + + node->thing = thing; + node->count = 0; + node->kind = kind; + trc->nodemap.add(p, thing, node); + return node; } + return NULL; +} - JSCompartmentCallback callback = rt->compartmentCallback; - if (callback && !callback(cx, compartment, JSCOMPARTMENT_NEW)) { - AutoLockGC lock(rt); - rt->compartments.popBack(); - Foreground::delete_(compartment); - return NULL; +static +VerifyNode * +NextNode(VerifyNode *node) +{ + if (node->count == 0) + return (VerifyNode *)((char *)node + sizeof(VerifyNode) - sizeof(EdgeValue)); + else + return (VerifyNode *)((char *)node + sizeof(VerifyNode) + + sizeof(EdgeValue)*(node->count - 1)); +} + +static void +StartVerifyBarriers(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (rt->gcVerifyData) + return; + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); + +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + RecordNativeStackTopForGC(cx); + + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) + r.front()->bitmap.clear(); + + /* + * Kick all frames on the stack into the interpreter, and release all JIT + * code in the compartment. + */ +#ifdef JS_METHODJIT + for (CompartmentsIter c(rt); !c.done(); c.next()) { + mjit::ClearAllFrames(c); + + for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + mjit::ReleaseScriptCode(cx, script); + + /* + * Use counts for scripts are reset on GC. After discarding code we + * need to let it warm back up to get information like which opcodes + * are setting array holes or accessing getter properties. + */ + script->resetUseCount(); + } } - return compartment; +#endif + + VerifyTracer *trc = new (js_malloc(sizeof(VerifyTracer))) VerifyTracer(cx); + + rt->gcNumber++; + trc->number = rt->gcNumber; + trc->count = 0; + + JS_TracerInit(trc, cx, AccumulateEdge); + + const size_t size = 64 * 1024 * 1024; + trc->root = (VerifyNode *)js_malloc(size); + JS_ASSERT(trc->root); + trc->edgeptr = (char *)trc->root; + trc->term = trc->edgeptr + size; + + trc->nodemap.init(); + + /* Create the root node. */ + trc->curnode = MakeNode(trc, NULL, JSGCTraceKind(0)); + + /* Make all the roots be edges emanating from the root node. */ + MarkRuntime(trc); + + VerifyNode *node = trc->curnode; + if (trc->edgeptr == trc->term) + goto oom; + + /* For each edge, make a node for it if one doesn't already exist. */ + while ((char *)node < trc->edgeptr) { + for (uint32_t i = 0; i < node->count; i++) { + EdgeValue &e = node->edges[i]; + VerifyNode *child = MakeNode(trc, e.thing, e.kind); + if (child) { + trc->curnode = child; + JS_TraceChildren(trc, e.thing, e.kind); + } + if (trc->edgeptr == trc->term) + goto oom; + } + + node = NextNode(node); + } + + rt->gcVerifyData = trc; + rt->gcIncrementalTracer = &trc->gcmarker; + for (CompartmentsIter c(rt); !c.done(); c.next()) { + c->gcIncrementalTracer = &trc->gcmarker; + c->needsBarrier_ = true; + } + + return; + +oom: + js_free(trc->root); + trc->~VerifyTracer(); + js_free(trc); } -} /* namespace gc */ +static void +CheckAutorooter(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + static_cast(*thingp)->markIfUnmarked(); +} -void -TraceRuntime(JSTracer *trc) +/* + * This function is called by EndVerifyBarriers for every heap edge. If the edge + * already existed in the original snapshot, we "cancel it out" by overwriting + * it with NULL. EndVerifyBarriers later asserts that the remaining non-NULL + * edges (i.e., the ones from the original snapshot that must have been + * modified) must point to marked objects. + */ +static void +CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) { - LeaveTrace(trc->context); + VerifyTracer *trc = (VerifyTracer *)jstrc; + VerifyNode *node = trc->curnode; + + for (uint32_t i = 0; i < node->count; i++) { + if (node->edges[i].thing == *thingp) { + JS_ASSERT(node->edges[i].kind == kind); + node->edges[i].thing = NULL; + return; + } + } +} + +static void +EndVerifyBarriers(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); #ifdef JS_THREADSAFE - { - JSContext *cx = trc->context; - JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif - if (rt->gcThread != cx->thread()) { - AutoGCSession gcsession(cx); - AutoUnlockGC unlock(rt); - RecordNativeStackTopForGC(trc->context); - MarkRuntime(trc); + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + RecordNativeStackTopForGC(cx); + + VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData; + + if (!trc) + return; + + JS_ASSERT(trc->number == rt->gcNumber); + + for (CompartmentsIter c(rt); !c.done(); c.next()) { + c->gcIncrementalTracer = NULL; + c->needsBarrier_ = false; + } + + if (rt->gcIncrementalTracer->hasDelayedChildren()) + rt->gcIncrementalTracer->markDelayedChildren(); + + rt->gcVerifyData = NULL; + rt->gcIncrementalTracer = NULL; + + JS_TracerInit(trc, cx, CheckAutorooter); + + JSContext *iter = NULL; + while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) { + if (acx->autoGCRooters) + acx->autoGCRooters->traceAll(trc); + } + + JS_TracerInit(trc, cx, CheckEdge); + + /* Start after the roots. */ + VerifyNode *node = NextNode(trc->root); + int count = 0; + + while ((char *)node < trc->edgeptr) { + trc->curnode = node; + JS_TraceChildren(trc, node->thing, node->kind); + + for (uint32_t i = 0; i < node->count; i++) { + void *thing = node->edges[i].thing; + JS_ASSERT_IF(thing, static_cast(thing)->isMarked()); + } + + count++; + node = NextNode(node); + } + + js_free(trc->root); + trc->~VerifyTracer(); + js_free(trc); +} + +void +VerifyBarriers(JSContext *cx, bool always) +{ + if (cx->runtime->gcZeal() < ZealVerifierThreshold) + return; + + uint32_t freq = cx->runtime->gcZealFrequency; + + JSRuntime *rt = cx->runtime; + if (VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData) { + if (++trc->count < freq && !always) return; + + EndVerifyBarriers(cx); + } + StartVerifyBarriers(cx); +} + +#endif /* JS_GC_ZEAL */ + +} /* namespace gc */ + +static void ReleaseAllJITCode(JSContext *cx) +{ +#ifdef JS_METHODJIT + for (GCCompartmentsIter c(cx->runtime); !c.done(); c.next()) { + mjit::ClearAllFrames(c); + for (CellIter i(cx, c, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + mjit::ReleaseScriptCode(cx, script); } } -#else - RecordNativeStackTopForGC(trc->context); #endif +} - /* - * Calls from inside a normal GC or a recursive calls are OK and do not - * require session setup. - */ - MarkRuntime(trc); +/* + * There are three possible PCCount profiling states: + * + * 1. None: Neither scripts nor the runtime have counter information. + * 2. Profile: Active scripts have counter information, the runtime does not. + * 3. Query: Scripts do not have counter information, the runtime does. + * + * When starting to profile scripts, counting begins immediately, with all JIT + * code discarded and recompiled with counters as necessary. Active interpreter + * frames will not begin profiling until they begin executing another script + * (via a call or return). + * + * The below API functions manage transitions to new states, according + * to the table below. + * + * Old State + * ------------------------- + * Function None Profile Query + * -------- + * StartPCCountProfiling Profile Profile Profile + * StopPCCountProfiling None Query Query + * PurgePCCounts None None None + */ + +static void +ReleaseScriptPCCounters(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->scriptPCCounters); + + ScriptOpcodeCountsVector &vec = *rt->scriptPCCounters; + + for (size_t i = 0; i < vec.length(); i++) + vec[i].counters.destroy(cx); + + cx->delete_(rt->scriptPCCounters); + rt->scriptPCCounters = NULL; +} + +JS_FRIEND_API(void) +StartPCCountProfiling(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + AutoLockGC lock(rt); + + if (rt->profilingScripts) + return; + + if (rt->scriptPCCounters) + ReleaseScriptPCCounters(cx); + + ReleaseAllJITCode(cx); + + rt->profilingScripts = true; +} + +JS_FRIEND_API(void) +StopPCCountProfiling(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + AutoLockGC lock(rt); + + if (!rt->profilingScripts) + return; + JS_ASSERT(!rt->scriptPCCounters); + + ReleaseAllJITCode(cx); + + ScriptOpcodeCountsVector *vec = cx->new_(SystemAllocPolicy()); + if (!vec) + return; + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + for (CellIter i(cx, c, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->pcCounters && script->types) { + ScriptOpcodeCountsPair info; + info.script = script; + info.counters.steal(script->pcCounters); + if (!vec->append(info)) + info.counters.destroy(cx); + } + } + } + + rt->profilingScripts = false; + rt->scriptPCCounters = vec; +} + +JS_FRIEND_API(void) +PurgePCCounts(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + AutoLockGC lock(rt); + + if (!rt->scriptPCCounters) + return; + JS_ASSERT(!rt->profilingScripts); + + ReleaseScriptPCCounters(cx); } } /* namespace js */ + +#if JS_HAS_XML_SUPPORT +extern size_t sE4XObjectsCreated; + +JSXML * +js_NewGCXML(JSContext *cx) +{ + if (!cx->runningWithTrustedPrincipals()) + ++sE4XObjectsCreated; + + return NewGCThing(cx, js::gc::FINALIZE_XML, sizeof(JSXML)); +} +#endif diff --git a/deps/mozjs/js/src/jsgc.h b/deps/mozjs/js/src/jsgc.h index 4f2e024a961..1b38a6ef640 100644 --- a/deps/mozjs/js/src/jsgc.h +++ b/deps/mozjs/js/src/jsgc.h @@ -45,208 +45,624 @@ */ #include +#include "mozilla/Util.h" + +#include "jsalloc.h" #include "jstypes.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsdhash.h" -#include "jsbit.h" -#include "jsgcchunk.h" +#include "jslock.h" #include "jsutil.h" -#include "jsvector.h" #include "jsversion.h" -#include "jsobj.h" -#include "jsfun.h" #include "jsgcstats.h" #include "jscell.h" +#include "ds/BitArray.h" +#include "gc/Statistics.h" +#include "js/HashTable.h" +#include "js/Vector.h" +#include "js/TemplateLib.h" + struct JSCompartment; extern "C" void js_TraceXML(JSTracer *trc, JSXML* thing); #if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(limit, lval) ((jsuword)(lval) < limit) +# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) < limit) #else -# define JS_CHECK_STACK_SIZE(limit, lval) ((jsuword)(lval) > limit) +# define JS_CHECK_STACK_SIZE(limit, lval) ((uintptr_t)(lval) > limit) #endif namespace js { +class GCHelperThread; struct Shape; namespace gc { -/* The kind of GC thing with a finalizer. */ -enum FinalizeKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_FUNCTION, - FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, - FINALIZE_SHAPE, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, +struct Arena; + +/* + * This must be an upper bound, but we do not need the least upper bound, so + * we just exclude non-background objects. + */ +const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - FINALIZE_OBJECT_LIMIT / 2; + +/* + * Page size is 4096 by default, except for SPARC, where it is 8192. + * Note: Do not use JS_CPU_SPARC here, this header is used outside JS. + * Bug 692267: Move page size definition to gc/Memory.h and include it + * directly once jsgc.h is no longer an installed header. + */ +#if defined(SOLARIS) && (defined(__sparc) || defined(__sparcv9)) +const size_t PageShift = 13; +#else +const size_t PageShift = 12; #endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_LIMIT -}; +const size_t PageSize = size_t(1) << PageShift; + +const size_t ChunkShift = 20; +const size_t ChunkSize = size_t(1) << ChunkShift; +const size_t ChunkMask = ChunkSize - 1; -const size_t ArenaShift = 12; -const size_t ArenaSize = size_t(1) << ArenaShift; +const size_t ArenaShift = PageShift; +const size_t ArenaSize = PageSize; const size_t ArenaMask = ArenaSize - 1; -template struct Arena; +/* + * This is the maximum number of arenas we allow in the FreeCommitted state + * before we trigger a GC_SHRINK to release free arenas to the OS. + */ +const static uint32_t FreeCommittedArenasThreshold = (32 << 20) / ArenaSize; + +/* + * The mark bitmap has one bit per each GC cell. For multi-cell GC things this + * wastes space but allows to avoid expensive devisions by thing's size when + * accessing the bitmap. In addition this allows to use some bits for colored + * marking during the cycle GC. + */ +const size_t ArenaCellCount = size_t(1) << (ArenaShift - Cell::CellShift); +const size_t ArenaBitmapBits = ArenaCellCount; +const size_t ArenaBitmapBytes = ArenaBitmapBits / 8; +const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD; + +/* + * A FreeSpan represents a contiguous sequence of free cells in an Arena. + * |first| is the address of the first free cell in the span. |last| is the + * address of the last free cell in the span. This last cell holds a FreeSpan + * data structure for the next span unless this is the last span on the list + * of spans in the arena. For this last span |last| points to the last byte of + * the last thing in the arena and no linkage is stored there, so + * |last| == arenaStart + ArenaSize - 1. If the space at the arena end is + * fully used this last span is empty and |first| == |last + 1|. + * + * Thus |first| < |last| implies that we have either the last span with at least + * one element or that the span is not the last and contains at least 2 + * elements. In both cases to allocate a thing from this span we need simply + * to increment |first| by the allocation size. + * + * |first| == |last| implies that we have a one element span that records the + * next span. So to allocate from it we need to update the span list head + * with a copy of the span stored at |last| address so the following + * allocations will use that span. + * + * |first| > |last| implies that we have an empty last span and the arena is + * fully used. + * + * Also only for the last span (|last| & 1)! = 0 as all allocation sizes are + * multiples of Cell::CellSize. + */ +struct FreeSpan { + uintptr_t first; + uintptr_t last; + + public: + FreeSpan() {} + + FreeSpan(uintptr_t first, uintptr_t last) + : first(first), last(last) { + checkSpan(); + } + + /* + * To minimize the size of the arena header the first span is encoded + * there as offsets from the arena start. + */ + static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { + /* Check that we can pack the offsets into uint16. */ + JS_STATIC_ASSERT(ArenaShift < 16); + JS_ASSERT(firstOffset <= ArenaSize); + JS_ASSERT(lastOffset < ArenaSize); + JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1))); + return firstOffset | (lastOffset << 16); + } + + /* + * Encoded offsets for a full arena when its first span is the last one + * and empty. + */ + static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); + + static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + + size_t firstOffset = offsets & 0xFFFF; + size_t lastOffset = offsets >> 16; + JS_ASSERT(firstOffset <= ArenaSize); + JS_ASSERT(lastOffset < ArenaSize); + + /* + * We must not use | when calculating first as firstOffset is + * ArenaMask + 1 for the empty span. + */ + return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); + } + + void initAsEmpty(uintptr_t arenaAddr = 0) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + first = arenaAddr + ArenaSize; + last = arenaAddr | (ArenaSize - 1); + JS_ASSERT(isEmpty()); + } + + bool isEmpty() const { + checkSpan(); + return first > last; + } + + bool hasNext() const { + checkSpan(); + return !(last & uintptr_t(1)); + } + + const FreeSpan *nextSpan() const { + JS_ASSERT(hasNext()); + return reinterpret_cast(last); + } + + FreeSpan *nextSpanUnchecked(size_t thingSize) const { +#ifdef DEBUG + uintptr_t lastOffset = last & ArenaMask; + JS_ASSERT(!(lastOffset & 1)); + JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0); +#endif + return reinterpret_cast(last); + } + + uintptr_t arenaAddressUnchecked() const { + return last & ~ArenaMask; + } + + uintptr_t arenaAddress() const { + checkSpan(); + return arenaAddressUnchecked(); + } + + ArenaHeader *arenaHeader() const { + return reinterpret_cast(arenaAddress()); + } + + bool isSameNonEmptySpan(const FreeSpan *another) const { + JS_ASSERT(!isEmpty()); + JS_ASSERT(!another->isEmpty()); + return first == another->first && last == another->last; + } + + bool isWithinArena(uintptr_t arenaAddr) const { + JS_ASSERT(!(arenaAddr & ArenaMask)); + + /* Return true for the last empty span as well. */ + return arenaAddress() == arenaAddr; + } + + size_t encodeAsOffsets() const { + /* + * We must use first - arenaAddress(), not first & ArenaMask as + * first == ArenaMask + 1 for an empty span. + */ + uintptr_t arenaAddr = arenaAddress(); + return encodeOffsets(first - arenaAddr, last & ArenaMask); + } + + /* See comments before FreeSpan for details. */ + JS_ALWAYS_INLINE void *allocate(size_t thingSize) { + JS_ASSERT(thingSize % Cell::CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + /* Bump-allocate from the current span. */ + first = thing + thingSize; + } else if (JS_LIKELY(thing == last)) { + /* + * Move to the next span. We use JS_LIKELY as without PGO + * compilers mis-predict == here as unlikely to succeed. + */ + *this = *reinterpret_cast(thing); + } else { + return NULL; + } + checkSpan(); + return reinterpret_cast(thing); + } + + /* A version of allocate when we know that the span is not empty. */ + JS_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { + JS_ASSERT(thingSize % Cell::CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + first = thing + thingSize; + } else { + JS_ASSERT(thing == last); + *this = *reinterpret_cast(thing); + } + checkSpan(); + return reinterpret_cast(thing); + } + + /* + * Allocate from a newly allocated arena. We do not move the free list + * from the arena. Rather we set the arena up as fully used during the + * initialization so to allocate we simply return the first thing in the + * arena and set the free list to point to the second. + */ + JS_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, + size_t thingSize) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + uintptr_t thing = arenaAddr | firstThingOffset; + first = thing + thingSize; + last = arenaAddr | ArenaMask; + checkSpan(); + return reinterpret_cast(thing); + } + + void checkSpan() const { +#ifdef DEBUG + /* We do not allow spans at the end of the address space. */ + JS_ASSERT(last != uintptr_t(-1)); + JS_ASSERT(first); + JS_ASSERT(last); + JS_ASSERT(first - 1 <= last); + uintptr_t arenaAddr = arenaAddressUnchecked(); + if (last & 1) { + /* The span is the last. */ + JS_ASSERT((last & ArenaMask) == ArenaMask); + + if (first - 1 == last) { + /* The span is last and empty. The above start != 0 check + * implies that we are not at the end of the address space. + */ + return; + } + size_t spanLength = last - first + 1; + JS_ASSERT(spanLength % Cell::CellSize == 0); + + /* Start and end must belong to the same arena. */ + JS_ASSERT((first & ~ArenaMask) == arenaAddr); + return; + } + + /* The span is not the last and we have more spans to follow. */ + JS_ASSERT(first <= last); + size_t spanLengthWithoutOneThing = last - first; + JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0); + + JS_ASSERT((first & ~ArenaMask) == arenaAddr); + + /* + * If there is not enough space before the arena end to allocate one + * more thing, then the span must be marked as the last one to avoid + * storing useless empty span reference. + */ + size_t beforeTail = ArenaSize - (last & ArenaMask); + JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize); + + FreeSpan *next = reinterpret_cast(last); + + /* + * The GC things on the list of free spans come from one arena + * and the spans are linked in ascending address order with + * at least one non-free thing between spans. + */ + JS_ASSERT(last < next->first); + JS_ASSERT(arenaAddr == next->arenaAddressUnchecked()); + + if (next->first > next->last) { + /* + * The next span is the empty span that terminates the list for + * arenas that do not have any free things at the end. + */ + JS_ASSERT(next->first - 1 == next->last); + JS_ASSERT(arenaAddr + ArenaSize == next->first); + } +#endif + } + +}; /* Every arena has a header. */ struct ArenaHeader { + friend struct FreeLists; + JSCompartment *compartment; + + /* + * ArenaHeader::next has two purposes: when unallocated, it points to the + * next available Arena's header. When allocated, it points to the next + * arena of the same size class and compartment. + */ ArenaHeader *next; - FreeCell *freeList; private: - unsigned thingKind; + /* + * The first span of free things in the arena. We encode it as the start + * and end offsets within the arena, not as FreeSpan structure, to + * minimize the header size. + */ + size_t firstFreeSpanOffsets; + /* + * One of AllocKind constants or FINALIZE_LIMIT when the arena does not + * contain any GC things and is on the list of empty arenas in the GC + * chunk. The latter allows to quickly check if the arena is allocated + * during the conservative GC scanning without searching the arena in the + * list. + */ + size_t allocKind : 8; + + /* + * When recursive marking uses too much stack the marking is delayed and + * the corresponding arenas are put into a stack using the following field + * as a linkage. To distinguish the bottom of the stack from the arenas + * not present in the stack we use an extra flag to tag arenas on the + * stack. + * + * To minimize the ArenaHeader size we record the next delayed marking + * linkage as arenaAddress() >> ArenaShift and pack it with the allocKind + * field and hasDelayedMarking flag. We use 8 bits for the allocKind, not + * ArenaShift - 1, so the compiler can use byte-level memory instructions + * to access it. + */ public: + size_t hasDelayedMarking : 1; + size_t nextDelayedMarking : JS_BITS_PER_WORD - 8 - 1; + + static void staticAsserts() { + /* We must be able to fit the allockind into uint8_t. */ + JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); + + /* + * nextDelayedMarkingpacking assumes that ArenaShift has enough bits + * to cover allocKind and hasDelayedMarking. + */ + JS_STATIC_ASSERT(ArenaShift >= 8 + 1); + } + inline uintptr_t address() const; inline Chunk *chunk() const; - inline ArenaBitmap *bitmap() const; - template - Arena *getArena() { - return reinterpret_cast *>(address()); + bool allocated() const { + JS_ASSERT(allocKind <= size_t(FINALIZE_LIMIT)); + return allocKind < size_t(FINALIZE_LIMIT); + } + + void init(JSCompartment *comp, AllocKind kind) { + JS_ASSERT(!allocated()); + JS_ASSERT(!hasDelayedMarking); + compartment = comp; + + JS_STATIC_ASSERT(FINALIZE_LIMIT <= 255); + allocKind = size_t(kind); + + /* See comments in FreeSpan::allocateFromNewArena. */ + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; + } + + void setAsNotAllocated() { + allocKind = size_t(FINALIZE_LIMIT); + hasDelayedMarking = 0; + nextDelayedMarking = 0; + } + + uintptr_t arenaAddress() const { + return address(); } - unsigned getThingKind() const { - return thingKind; + Arena *getArena() { + return reinterpret_cast(arenaAddress()); } - void setThingKind(unsigned kind) { - thingKind = kind; + AllocKind getAllocKind() const { + JS_ASSERT(allocated()); + return AllocKind(allocKind); } - inline MarkingDelay *getMarkingDelay() const; + inline size_t getThingSize() const; + bool hasFreeThings() const { + return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; + } + + inline bool isEmpty() const; + + void setAsFullyUsed() { + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; + } + + FreeSpan getFirstFreeSpan() const { #ifdef DEBUG - JS_FRIEND_API(size_t) getThingSize() const; + checkSynchronizedWithFreeList(); #endif -}; - -template -struct Things { - char filler1[R1]; - T things[N]; - char filler[R2]; -}; + return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); + } -template -struct Things { - char filler1[R1]; - T things[N]; -}; + void setFirstFreeSpan(const FreeSpan *span) { + JS_ASSERT(span->isWithinArena(arenaAddress())); + firstFreeSpanOffsets = span->encodeAsOffsets(); + } -template -struct Things { - T things[N]; - char filler2[R2]; -}; +#ifdef DEBUG + void checkSynchronizedWithFreeList() const; +#endif -template -struct Things { - T things[N]; + inline Arena *getNextDelayedMarking() const; + inline void setNextDelayedMarking(Arena *arena); }; -template struct Arena { /* * Layout of an arena: - * An arena is 4K. We want it to have a header followed by a list of T - * objects. However, each object should be aligned to a sizeof(T)-boundary. - * To achieve this, we pad before and after the object array. + * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader + * descriptor followed by some pad bytes. The remainder of the arena is + * filled with the array of T things. The pad bytes ensure that the thing + * array ends exactly at the end of the arena. * - * +-------------+-----+----+----+-----+----+-----+ - * | ArenaHeader | pad | T0 | T1 | ... | Tn | pad | - * +-------------+-----+----+----+-----+----+-----+ + * +-------------+-----+----+----+-----+----+ + * | ArenaHeader | pad | T0 | T1 | ... | Tn | + * +-------------+-----+----+----+-----+----+ * - * <----------------------------------------------> = 4096 bytes - * <-----> = Filler1Size - * <-------------------> = HeaderSize - * <--------------------------> = SpaceAfterHeader - * <-----> = Filler2Size + * <----------------------------------------> = ArenaSize bytes + * <-------------------> = first thing offset */ - static const size_t Filler1Size = - tl::If< sizeof(ArenaHeader) % sizeof(T) == 0, size_t, - 0, - sizeof(T) - sizeof(ArenaHeader) % sizeof(T) >::result; - static const size_t HeaderSize = sizeof(ArenaHeader) + Filler1Size; - static const size_t SpaceAfterHeader = ArenaSize - HeaderSize; - static const size_t Filler2Size = SpaceAfterHeader % sizeof(T); - static const size_t ThingsPerArena = SpaceAfterHeader / sizeof(T); - static const size_t FirstThingOffset = HeaderSize; - static const size_t ThingsSpan = ThingsPerArena * sizeof(T); - ArenaHeader aheader; - Things t; + uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - static void staticAsserts() { - /* - * Everything we store in the heap must be a multiple of the cell - * size. - */ - JS_STATIC_ASSERT(sizeof(T) % Cell::CellSize == 0); - JS_STATIC_ASSERT(offsetof(Arena, t.things) % sizeof(T) == 0); - JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + private: + static JS_FRIEND_DATA(const uint32_t) ThingSizes[]; + static JS_FRIEND_DATA(const uint32_t) FirstThingOffsets[]; + + public: + static void staticAsserts(); + + static size_t thingSize(AllocKind kind) { + return ThingSizes[kind]; + } + + static size_t firstThingOffset(AllocKind kind) { + return FirstThingOffsets[kind]; } - inline FreeCell *buildFreeList(); + static size_t thingsPerArena(size_t thingSize) { + JS_ASSERT(thingSize % Cell::CellSize == 0); - bool finalize(JSContext *cx); + /* We should be able to fit FreeSpan in any GC thing. */ + JS_ASSERT(thingSize >= sizeof(FreeSpan)); + + return (ArenaSize - sizeof(ArenaHeader)) / thingSize; + } + + static size_t thingsSpan(size_t thingSize) { + return thingsPerArena(thingSize) * thingSize; + } + + static bool isAligned(uintptr_t thing, size_t thingSize) { + /* Things ends at the arena end. */ + uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; + return tailOffset % thingSize == 0; + } + + uintptr_t address() const { + return aheader.address(); + } + + uintptr_t thingsStart(AllocKind thingKind) { + return address() | firstThingOffset(thingKind); + } + + uintptr_t thingsEnd() { + return address() + ArenaSize; + } + + template + bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background); }; -void FinalizeArena(ArenaHeader *aheader); +/* The chunk header (located at the end of the chunk to preserve arena alignment). */ +struct ChunkInfo { + Chunk *next; + Chunk **prevp; -/* - * Live objects are marked black. How many other additional colors are available - * depends on the size of the GCThing. - */ -static const uint32 BLACK = 0; + /* Free arenas are linked together with aheader.next. */ + ArenaHeader *freeArenasHead; + + /* + * Decommitted arenas are tracked by a bitmap in the chunk header. We use + * this offset to start our search iteration close to a decommitted arena + * that we can allocate. + */ + uint32_t lastDecommittedArenaOffset; -/* An arena bitmap contains enough mark bits for all the cells in an arena. */ -struct ArenaBitmap { - static const size_t BitCount = ArenaSize / Cell::CellSize; - static const size_t BitWords = BitCount / JS_BITS_PER_WORD; + /* Number of free arenas, either committed or decommitted. */ + uint32_t numArenasFree; - uintptr_t bitmap[BitWords]; + /* Number of free, committed arenas. */ + uint32_t numArenasFreeCommitted; + + /* Number of GC cycles this chunk has survived. */ + uint32_t age; +}; - JS_ALWAYS_INLINE bool isMarked(size_t bit, uint32 color) { - bit += color; - JS_ASSERT(bit < BitCount); - uintptr_t *word = &bitmap[bit / JS_BITS_PER_WORD]; - return *word & (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); +/* + * Calculating ArenasPerChunk: + * + * In order to figure out how many Arenas will fit in a chunk, we need to know + * how much extra space is available after we allocate the header data. This + * is a problem because the header size depends on the number of arenas in the + * chunk. The two dependent fields are bitmap and decommittedArenas. + * + * For the mark bitmap, we know that each arena will use a fixed number of full + * bytes: ArenaBitmapBytes. The full size of the header data is this number + * multiplied by the eventual number of arenas we have in the header. We, + * conceptually, distribute this header data among the individual arenas and do + * not include it in the header. This way we do not have to worry about its + * variable size: it gets attached to the variable number we are computing. + * + * For the decommitted arena bitmap, we only have 1 bit per arena, so this + * technique will not work. Instead, we observe that we do not have enough + * header info to fill 8 full arenas: it is currently 4 on 64bit, less on + * 32bit. Thus, with current numbers, we need 64 bytes for decommittedArenas. + * This will not become 63 bytes unless we double the data required in the + * header. Therefore, we just compute the number of bytes required to track + * every possible arena and do not worry about slop bits, since there are too + * few to usefully allocate. + * + * To actually compute the number of arenas we can allocate in a chunk, we + * divide the amount of available space less the header info (not including + * the mark bitmap which is distributed into the arena size) by the size of + * the arena (with the mark bitmap bytes it uses). + */ +const size_t BytesPerArenaWithHeader = ArenaSize + ArenaBitmapBytes; +const size_t ChunkDecommitBitmapBytes = ChunkSize / ArenaSize / JS_BITS_PER_BYTE; +const size_t ChunkBytesAvailable = ChunkSize - sizeof(ChunkInfo) - ChunkDecommitBitmapBytes; +const size_t ArenasPerChunk = ChunkBytesAvailable / BytesPerArenaWithHeader; + +/* A chunk bitmap contains enough mark bits for all the cells in a chunk. */ +struct ChunkBitmap { + uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk]; + + JS_ALWAYS_INLINE void getMarkWordAndMask(const Cell *cell, uint32_t color, + uintptr_t **wordp, uintptr_t *maskp); + + JS_ALWAYS_INLINE bool isMarked(const Cell *cell, uint32_t color) { + uintptr_t *word, mask; + getMarkWordAndMask(cell, color, &word, &mask); + return *word & mask; } - JS_ALWAYS_INLINE bool markIfUnmarked(size_t bit, uint32 color) { - JS_ASSERT(bit + color < BitCount); - uintptr_t *word = &bitmap[bit / JS_BITS_PER_WORD]; - uintptr_t mask = (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); + JS_ALWAYS_INLINE bool markIfUnmarked(const Cell *cell, uint32_t color) { + uintptr_t *word, mask; + getMarkWordAndMask(cell, BLACK, &word, &mask); if (*word & mask) return false; *word |= mask; if (color != BLACK) { - bit += color; - word = &bitmap[bit / JS_BITS_PER_WORD]; - mask = (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); + /* + * We use getMarkWordAndMask to recalculate both mask and word as + * doing just mask << color may overflow the mask. + */ + getMarkWordAndMask(cell, color, &word, &mask); if (*word & mask) return false; *word |= mask; @@ -254,17 +670,29 @@ struct ArenaBitmap { return true; } - JS_ALWAYS_INLINE void unmark(size_t bit, uint32 color) { - bit += color; - JS_ASSERT(bit < BitCount); - uintptr_t *word = &bitmap[bit / JS_BITS_PER_WORD]; - *word &= ~(uintptr_t(1) << (bit % JS_BITS_PER_WORD)); + JS_ALWAYS_INLINE void unmark(const Cell *cell, uint32_t color) { + uintptr_t *word, mask; + getMarkWordAndMask(cell, color, &word, &mask); + *word &= ~mask; + } + + void clear() { + PodArrayZero(bitmap); } #ifdef DEBUG - bool noBitsSet() { - for (unsigned i = 0; i < BitWords; i++) { - if (bitmap[i] != uintptr_t(0)) + bool noBitsSet(ArenaHeader *aheader) { + /* + * We assume that the part of the bitmap corresponding to the arena + * has the exact number of words so we do not need to deal with a word + * that covers bits from two arenas. + */ + JS_STATIC_ASSERT(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD); + + uintptr_t *word, unused; + getMarkWordAndMask(reinterpret_cast(aheader->address()), BLACK, &word, &unused); + for (size_t i = 0; i != ArenaBitmapWords; i++) { + if (word[i]) return false; } return true; @@ -272,134 +700,142 @@ struct ArenaBitmap { #endif }; -/* Ensure that bitmap covers the whole arena. */ -JS_STATIC_ASSERT(ArenaSize % Cell::CellSize == 0); -JS_STATIC_ASSERT(ArenaBitmap::BitCount % JS_BITS_PER_WORD == 0); +JS_STATIC_ASSERT(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap)); + +typedef BitArray PerArenaBitmap; + +const size_t ChunkPadSize = ChunkSize + - (sizeof(Arena) * ArenasPerChunk) + - sizeof(ChunkBitmap) + - sizeof(PerArenaBitmap) + - sizeof(ChunkInfo); +JS_STATIC_ASSERT(ChunkPadSize < BytesPerArenaWithHeader); /* - * When recursive marking uses too much stack the marking is delayed and - * the corresponding arenas are put into a stack using a linked via the - * following per arena structure. + * Chunks contain arenas and associated data structures (mark bitmap, delayed + * marking state). */ -struct MarkingDelay { - ArenaHeader *link; +struct Chunk { + Arena arenas[ArenasPerChunk]; - void init() - { - link = NULL; - } + /* Pad to full size to ensure cache alignment of ChunkInfo. */ + uint8_t padding[ChunkPadSize]; - /* - * To separate arenas without things to mark later from the arena at the - * marked delay stack bottom we use for the latter a special sentinel - * value. We set it to the header for the second arena in the chunk - * starting the 0 address. - */ - static ArenaHeader *stackBottom() { - return reinterpret_cast(ArenaSize); - } -}; + ChunkBitmap bitmap; + PerArenaBitmap decommittedArenas; + ChunkInfo info; -struct EmptyArenaLists { - /* Arenas with no internal freelist prepared. */ - ArenaHeader *cellFreeList; + static Chunk *fromAddress(uintptr_t addr) { + addr &= ~ChunkMask; + return reinterpret_cast(addr); + } - /* Arenas with internal freelists prepared for a given finalize kind. */ - ArenaHeader *freeLists[FINALIZE_LIMIT]; + static bool withinArenasRange(uintptr_t addr) { + uintptr_t offset = addr & ChunkMask; + return offset < ArenasPerChunk * ArenaSize; + } - void init() { - PodZero(this); + static size_t arenaIndex(uintptr_t addr) { + JS_ASSERT(withinArenasRange(addr)); + return (addr & ChunkMask) >> ArenaShift; } - ArenaHeader *getOtherArena() { - ArenaHeader *aheader = cellFreeList; - if (aheader) { - cellFreeList = aheader->next; - return aheader; - } - for (int i = 0; i < FINALIZE_LIMIT; i++) { - aheader = freeLists[i]; - if (aheader) { - freeLists[i] = aheader->next; - return aheader; - } - } - JS_NOT_REACHED("No arena"); - return NULL; + uintptr_t address() const { + uintptr_t addr = reinterpret_cast(this); + JS_ASSERT(!(addr & ChunkMask)); + return addr; } - ArenaHeader *getTypedFreeList(unsigned thingKind) { - JS_ASSERT(thingKind < FINALIZE_LIMIT); - ArenaHeader *aheader = freeLists[thingKind]; - if (aheader) - freeLists[thingKind] = aheader->next; - return aheader; + bool unused() const { + return info.numArenasFree == ArenasPerChunk; } - void insert(ArenaHeader *aheader) { - unsigned thingKind = aheader->getThingKind(); - aheader->next = freeLists[thingKind]; - freeLists[thingKind] = aheader; + bool hasAvailableArenas() const { + return info.numArenasFree != 0; } -}; -/* The chunk header (located at the end of the chunk to preserve arena alignment). */ -struct ChunkInfo { - Chunk *link; - JSRuntime *runtime; - EmptyArenaLists emptyArenaLists; - size_t age; - size_t numFree; -#ifdef JS_THREADSAFE - PRLock *chunkLock; -#endif -}; + inline void addToAvailableList(JSCompartment *compartment); + inline void insertToAvailableList(Chunk **insertPoint); + inline void removeFromAvailableList(); -/* Chunks contain arenas and associated data structures (mark bitmap, delayed marking state). */ -struct Chunk { - static const size_t BytesPerArena = ArenaSize + - sizeof(ArenaBitmap) + - sizeof(MarkingDelay); + ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); - static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena; + void releaseArena(ArenaHeader *aheader); - Arena arenas[ArenasPerChunk]; - ArenaBitmap bitmaps[ArenasPerChunk]; - MarkingDelay markingDelay[ArenasPerChunk]; + static Chunk *allocate(JSRuntime *rt); - ChunkInfo info; + /* Must be called with the GC lock taken. */ + static inline void release(JSRuntime *rt, Chunk *chunk); + static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead); - static Chunk *fromAddress(uintptr_t addr) { - addr &= ~GC_CHUNK_MASK; - return reinterpret_cast(addr); + /* Must be called with the GC lock taken. */ + inline void prepareToBeFreed(JSRuntime *rt); + + /* + * Assuming that the info.prevp points to the next field of the previous + * chunk in a doubly-linked list, get that chunk. + */ + Chunk *getPrevious() { + JS_ASSERT(info.prevp); + return fromPointerToNext(info.prevp); } - static bool withinArenasRange(uintptr_t addr) { - uintptr_t offset = addr & GC_CHUNK_MASK; - return offset < ArenasPerChunk * ArenaSize; + /* Get the chunk from a pointer to its info.next field. */ + static Chunk *fromPointerToNext(Chunk **nextFieldPtr) { + uintptr_t addr = reinterpret_cast(nextFieldPtr); + JS_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next)); + return reinterpret_cast(addr - offsetof(Chunk, info.next)); } - static size_t arenaIndex(uintptr_t addr) { - JS_ASSERT(withinArenasRange(addr)); - return (addr & GC_CHUNK_MASK) >> ArenaShift; + private: + inline void init(); + + /* Search for a decommitted arena to allocate. */ + jsuint findDecommittedArenaOffset(); + ArenaHeader* fetchNextDecommittedArena(); + + public: + /* Unlink and return the freeArenasHead. */ + inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt); + + inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader); +}; + +JS_STATIC_ASSERT(sizeof(Chunk) == ChunkSize); + +class ChunkPool { + Chunk *emptyChunkListHead; + size_t emptyCount; + + public: + ChunkPool() + : emptyChunkListHead(NULL), + emptyCount(0) { } + + size_t getEmptyCount() const { + return emptyCount; } - void clearMarkBitmap(); - bool init(JSRuntime *rt); + inline bool wantBackgroundAllocation(JSRuntime *rt) const; - bool unused(); - bool hasAvailableArenas(); - bool withinArenasRange(Cell *cell); + /* Must be called with the GC lock taken. */ + inline Chunk *get(JSRuntime *rt); - template - ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind); + /* Must be called either during the GC or with the GC lock taken. */ + inline void put(Chunk *chunk); - void releaseArena(ArenaHeader *aheader); + /* + * Return the list of chunks that can be released outside the GC lock. + * Must be called either during the GC or with the GC lock taken. + */ + Chunk *expire(JSRuntime *rt, bool releaseAll); + + /* Must be called with the GC lock taken. */ + void expireAndFree(JSRuntime *rt, bool releaseAll); - JSRuntime *getRuntime(); + /* Must be called either during the GC or with the GC lock taken. */ + JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; -JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); -JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE); inline uintptr_t Cell::address() const @@ -423,30 +859,21 @@ Cell::chunk() const { uintptr_t addr = uintptr_t(this); JS_ASSERT(addr % Cell::CellSize == 0); - addr &= ~(GC_CHUNK_SIZE - 1); + addr &= ~(ChunkSize - 1); return reinterpret_cast(addr); } -ArenaBitmap * -Cell::bitmap() const +AllocKind +Cell::getAllocKind() const { - return &chunk()->bitmaps[Chunk::arenaIndex(address())]; -} - -STATIC_POSTCONDITION_ASSUME(return < ArenaBitmap::BitCount) -size_t -Cell::cellIndex() const -{ - uintptr_t addr = address(); - return (addr & ArenaMask) >> Cell::CellShift; + return arenaHeader()->getAllocKind(); } #ifdef DEBUG inline bool Cell::isAligned() const { - uintptr_t offset = address() & ArenaMask; - return offset % arenaHeader()->getThingSize() == 0; + return Arena::isAligned(address(), arenaHeader()->getThingSize()); } #endif @@ -465,20 +892,49 @@ ArenaHeader::chunk() const return Chunk::fromAddress(address()); } -inline ArenaBitmap * -ArenaHeader::bitmap() const +inline bool +ArenaHeader::isEmpty() const { - return &chunk()->bitmaps[Chunk::arenaIndex(address())]; + /* Arena is empty if its first span covers the whole arena. */ + JS_ASSERT(allocated()); + size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); + return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); } -inline MarkingDelay * -ArenaHeader::getMarkingDelay() const +inline size_t +ArenaHeader::getThingSize() const { - return &chunk()->markingDelay[Chunk::arenaIndex(address())]; + JS_ASSERT(allocated()); + return Arena::thingSize(getAllocKind()); +} + +inline Arena * +ArenaHeader::getNextDelayedMarking() const +{ + return reinterpret_cast(nextDelayedMarking << ArenaShift); +} + +inline void +ArenaHeader::setNextDelayedMarking(Arena *arena) +{ + JS_ASSERT(!hasDelayedMarking); + hasDelayedMarking = 1; + nextDelayedMarking = arena->address() >> ArenaShift; +} + +JS_ALWAYS_INLINE void +ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, + uintptr_t **wordp, uintptr_t *maskp) +{ + JS_ASSERT(cell->chunk() == Chunk::fromAddress(reinterpret_cast(this))); + size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; + JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); + *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); + *wordp = &bitmap[bit / JS_BITS_PER_WORD]; } static void -AssertValidColor(const void *thing, uint32 color) +AssertValidColor(const void *thing, uint32_t color) { #ifdef DEBUG ArenaHeader *aheader = reinterpret_cast(thing)->arenaHeader(); @@ -487,25 +943,25 @@ AssertValidColor(const void *thing, uint32 color) } inline bool -Cell::isMarked(uint32 color = BLACK) const +Cell::isMarked(uint32_t color) const { AssertValidColor(this, color); - return bitmap()->isMarked(cellIndex(), color); + return chunk()->bitmap.isMarked(this, color); } bool -Cell::markIfUnmarked(uint32 color = BLACK) const +Cell::markIfUnmarked(uint32_t color) const { AssertValidColor(this, color); - return bitmap()->markIfUnmarked(cellIndex(), color); + return chunk()->bitmap.markIfUnmarked(this, color); } void -Cell::unmark(uint32 color) const +Cell::unmark(uint32_t color) const { JS_ASSERT(color != BLACK); AssertValidColor(this, color); - bitmap()->unmark(cellIndex(), color); + chunk()->bitmap.unmark(this, color); } JSCompartment * @@ -514,32 +970,25 @@ Cell::compartment() const return arenaHeader()->compartment; } -#define JSTRACE_XML 3 - -/* - * One past the maximum trace kind. - */ -#define JSTRACE_LIMIT 4 - /* * Lower limit after which we limit the heap growth */ -const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE; +const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024; /* - * A GC is triggered once the number of newly allocated arenas - * is GC_HEAP_GROWTH_FACTOR times the number of live arenas after - * the last GC starting after the lower limit of - * GC_ARENA_ALLOCATION_TRIGGER. + * A GC is triggered once the number of newly allocated arenas is + * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC + * starting after the lower limit of GC_ALLOCATION_THRESHOLD. */ const float GC_HEAP_GROWTH_FACTOR = 3.0f; -static inline size_t -GetFinalizableTraceKind(size_t thingKind) -{ - JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8); +/* Perform a Full GC every 20 seconds if MaybeGC is called */ +static const int64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000; - static const uint8 map[FINALIZE_LIMIT] = { +static inline JSGCTraceKind +MapAllocToTraceKind(AllocKind thingKind) +{ + static const JSGCTraceKind map[FINALIZE_LIMIT] = { JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ @@ -552,8 +1001,10 @@ GetFinalizableTraceKind(size_t thingKind) JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_FUNCTION */ + JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ JSTRACE_SHAPE, /* FINALIZE_SHAPE */ + JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ + JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ #if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ JSTRACE_XML, #endif @@ -561,108 +1012,277 @@ GetFinalizableTraceKind(size_t thingKind) JSTRACE_STRING, /* FINALIZE_STRING */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ }; + return map[thingKind]; +} + +inline JSGCTraceKind +GetGCThingTraceKind(const void *thing); + +struct ArenaLists { + + /* + * ArenaList::head points to the start of the list. Normally cursor points + * to the first arena in the list with some free things and all arenas + * before cursor are fully allocated. However, as the arena currently being + * allocated from is considered full while its list of free spans is moved + * into the freeList, during the GC or cell enumeration, when an + * unallocated freeList is moved back to the arena, we can see an arena + * with some free cells before the cursor. The cursor is an indirect + * pointer to allow for efficient list insertion at the cursor point and + * other list manipulations. + */ + struct ArenaList { + ArenaHeader *head; + ArenaHeader **cursor; + + ArenaList() { + clear(); + } + + void clear() { + head = NULL; + cursor = &head; + } + }; + + private: + /* + * For each arena kind its free list is represented as the first span with + * free things. Initially all the spans are initialized as empty. After we + * find a new arena with available things we move its first free span into + * the list and set the arena as fully allocated. way we do not need to + * update the arena header after the initial allocation. When starting the + * GC we only move the head of the of the list of spans back to the arena + * only for the arena that was not fully allocated. + */ + FreeSpan freeLists[FINALIZE_LIMIT]; + + ArenaList arenaLists[FINALIZE_LIMIT]; + +#ifdef JS_THREADSAFE + /* + * The background finalization adds the finalized arenas to the list at + * the *cursor position. backgroundFinalizeState controls the interaction + * between the GC lock and the access to the list from the allocation + * thread. + * + * BFS_DONE indicates that the finalizations is not running or cannot + * affect this arena list. The allocation thread can access the list + * outside the GC lock. + * + * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the + * lock. The former indicates that the finalization still runs. The latter + * signals that finalization just added to the list finalized arenas. In + * that case the lock effectively serves as a read barrier to ensure that + * the allocation thread see all the writes done during finalization. + */ + enum BackgroundFinalizeState { + BFS_DONE, + BFS_RUN, + BFS_JUST_FINISHED + }; - JS_ASSERT(thingKind < FINALIZE_LIMIT); - return map[thingKind]; -} + volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; +#endif -inline uint32 -GetGCThingTraceKind(const void *thing); + public: + ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + freeLists[i].initAsEmpty(); +#ifdef JS_THREADSAFE + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + backgroundFinalizeState[i] = BFS_DONE; +#endif + } -static inline JSRuntime * -GetGCThingRuntime(void *thing) -{ - return reinterpret_cast(thing)->chunk()->info.runtime; -} + ~ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { +#ifdef JS_THREADSAFE + /* + * We can only call this during the shutdown after the last GC when + * the background finalization is disabled. + */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); +#endif + ArenaHeader **headp = &arenaLists[i].head; + while (ArenaHeader *aheader = *headp) { + *headp = aheader->next; + aheader->chunk()->releaseArena(aheader); + } + } + } + + const FreeSpan *getFreeList(AllocKind thingKind) const { + return &freeLists[thingKind]; + } -/* The arenas in a list have uniform kind. */ -struct ArenaList { - ArenaHeader *head; /* list start */ - ArenaHeader *cursor; /* arena with free things */ - volatile bool hasToBeFinalized; - - inline void init() { - head = NULL; - cursor = NULL; - hasToBeFinalized = false; - } - - inline ArenaHeader *getNextWithFreeList() { - JS_ASSERT(!hasToBeFinalized); - while (cursor) { - ArenaHeader *aheader = cursor; - cursor = aheader->next; - if (aheader->freeList) - return aheader; + ArenaHeader *getFirstArena(AllocKind thingKind) const { + return arenaLists[thingKind].head; + } + + bool arenaListsAreEmpty() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { +#ifdef JS_THREADSAFE + /* + * The arena cannot be empty if the background finalization is not yet + * done. + */ + if (backgroundFinalizeState[i] != BFS_DONE) + return false; +#endif + if (arenaLists[i].head) + return false; } - return NULL; + return true; } #ifdef DEBUG - bool markedThingsInArenaList() { - for (ArenaHeader *aheader = head; aheader; aheader = aheader->next) { - if (!aheader->bitmap()->noBitsSet()) - return true; + bool checkArenaListAllUnmarked() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { +# ifdef JS_THREADSAFE + /* The background finalization must have stopped at this point. */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || + backgroundFinalizeState[i] == BFS_JUST_FINISHED); +# endif + for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { + if (!aheader->chunk()->bitmap.noBitsSet(aheader)) + return false; + } } - return false; + return true; } #endif - inline void insert(ArenaHeader *aheader) { - aheader->next = head; - head = aheader; +#ifdef JS_THREADSAFE + bool doneBackgroundFinalize(AllocKind kind) const { + return backgroundFinalizeState[kind] == BFS_DONE; } +#endif - void releaseAll(unsigned thingKind) { - while (head) { - ArenaHeader *next = head->next; - head->chunk()->releaseArena(head); - head = next; + /* + * Return the free list back to the arena so the GC finalization will not + * run the finalizers over unitialized bytes from free things. + */ + void purge() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { + FreeSpan *headSpan = &freeLists[i]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + JS_ASSERT(!aheader->hasFreeThings()); + aheader->setFirstFreeSpan(headSpan); + headSpan->initAsEmpty(); + } } - head = NULL; - cursor = NULL; } - inline bool isEmpty() const { - return !head; + /* + * Temporarily copy the free list heads to the arenas so the code can see + * the proper value in ArenaHeader::freeList when accessing the latter + * outside the GC. + */ + void copyFreeListsToArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + copyFreeListToArena(AllocKind(i)); } -}; -struct FreeLists { - FreeCell **finalizables[FINALIZE_LIMIT]; + void copyFreeListToArena(AllocKind thingKind) { + FreeSpan *headSpan = &freeLists[thingKind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + JS_ASSERT(!aheader->hasFreeThings()); + aheader->setFirstFreeSpan(headSpan); + } + } - void purge(); + /* + * Clear the free lists in arenas that were temporarily set there using + * copyToArenas. + */ + void clearFreeListsInArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + clearFreeListInArena(AllocKind(i)); + } - inline FreeCell *getNext(uint32 kind) { - FreeCell *top = NULL; - if (finalizables[kind]) { - top = *finalizables[kind]; - if (top) { - *finalizables[kind] = top->link; - } else { - finalizables[kind] = NULL; - } + + void clearFreeListInArena(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); + aheader->setAsFullyUsed(); + } + } + + /* + * Check that the free list is either empty or were synchronized with the + * arena using copyToArena(). + */ + bool isSynchronizedFreeList(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (headSpan->isEmpty()) + return true; + ArenaHeader *aheader = headSpan->arenaHeader(); + if (aheader->hasFreeThings()) { + /* + * If the arena has a free list, it must be the same as one in + * lists. + */ + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); + return true; } - return top; + return false; } - void populate(ArenaHeader *aheader, uint32 thingKind) { - finalizables[thingKind] = &aheader->freeList; + JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { + return freeLists[thingKind].allocate(thingSize); } + static void *refillFreeList(JSContext *cx, AllocKind thingKind); + + void checkEmptyFreeLists() { #ifdef DEBUG - bool isEmpty() const { - for (size_t i = 0; i != JS_ARRAY_LENGTH(finalizables); ++i) { - if (finalizables[i]) - return false; - } - return true; + for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i) + JS_ASSERT(freeLists[i].isEmpty()); +#endif + } + + void checkEmptyFreeList(AllocKind kind) { + JS_ASSERT(freeLists[kind].isEmpty()); } + + void finalizeObjects(JSContext *cx); + void finalizeStrings(JSContext *cx); + void finalizeShapes(JSContext *cx); + void finalizeScripts(JSContext *cx); + +#ifdef JS_THREADSAFE + static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead); #endif + + private: + inline void finalizeNow(JSContext *cx, AllocKind thingKind); + inline void finalizeLater(JSContext *cx, AllocKind thingKind); + + inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind); }; + +/* + * Initial allocation size for data structures holding chunks is set to hold + * chunks with total capacity of 16MB to avoid buffer resizes during browser + * startup. + */ +const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize; + +/* The number of GC cycles an empty chunk can survive before been released. */ +const size_t MAX_EMPTY_CHUNK_AGE = 4; + +inline Cell * +AsCell(JSObject *obj) +{ + return reinterpret_cast(obj); } -typedef Vector GCChunks; +} /* namespace gc */ struct GCPtrHasher { @@ -675,7 +1295,7 @@ struct GCPtrHasher static bool match(void *l, void *k) { return l == k; } }; -typedef HashMap GCLocks; +typedef HashMap GCLocks; struct RootInfo { RootInfo() {} @@ -697,8 +1317,8 @@ struct WrapperHasher typedef Value Lookup; static HashNumber hash(Value key) { - uint64 bits = JSVAL_BITS(Jsvalify(key)); - return (uint32)bits ^ (uint32)(bits >> 32); + uint64_t bits = JSVAL_TO_IMPL(key).asBits; + return uint32_t(bits) ^ uint32_t(bits >> 32); } static bool match(const Value &l, const Value &k) { return l == k; } @@ -706,45 +1326,13 @@ struct WrapperHasher typedef HashMap WrapperMap; -class AutoValueVector; -class AutoIdVector; -} - -static inline void -CheckGCFreeListLink(js::gc::FreeCell *cell) -{ - /* - * The GC things on the free lists come from one arena and the things on - * the free list are linked in ascending address order. - */ - JS_ASSERT_IF(cell->link, cell->arenaHeader() == cell->link->arenaHeader()); - JS_ASSERT_IF(cell->link, cell < cell->link); -} - -extern bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); - -#ifdef DEBUG -extern bool -CheckAllocation(JSContext *cx); -#endif +} /* namespace js */ -extern JS_FRIEND_API(uint32) +extern JS_FRIEND_API(JSGCTraceKind) js_GetGCThingTraceKind(void *thing); -#if 1 -/* - * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles - * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg - * ignored", etc. - */ -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE) -#else -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval)) -#endif - extern JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes); +js_InitGC(JSRuntime *rt, uint32_t maxbytes); extern void js_FinishGC(JSRuntime *rt); @@ -762,7 +1350,7 @@ js_DumpNamedRoots(JSRuntime *rt, void *data); #endif -extern uint32 +extern uint32_t js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); /* Table of pointers with count valid members. */ @@ -771,14 +1359,6 @@ typedef struct JSPtrTable { void **array; } JSPtrTable; -extern JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); - -#ifdef JS_TRACER -extern JSBool -js_ReserveObjects(JSContext *cx, size_t nobjects); -#endif - extern JSBool js_LockGCThingRT(JSRuntime *rt, void *thing); @@ -786,36 +1366,36 @@ extern void js_UnlockGCThingRT(JSRuntime *rt, void *thing); extern JS_FRIEND_API(bool) -IsAboutToBeFinalized(JSContext *cx, const void *thing); +IsAboutToBeFinalized(const js::gc::Cell *thing); -extern JS_FRIEND_API(bool) -js_GCThingIsMarked(void *thing, uintN color); +extern bool +IsAboutToBeFinalized(const js::Value &value); -extern void -js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp); +extern bool +js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing); namespace js { -extern JS_REQUIRES_STACK void -MarkRuntime(JSTracer *trc); - extern void TraceRuntime(JSTracer *trc); -extern JS_REQUIRES_STACK JS_FRIEND_API(void) +extern JS_FRIEND_API(void) MarkContext(JSTracer *trc, JSContext *acx); /* Must be called with GC lock taken. */ extern void -TriggerGC(JSRuntime *rt); +TriggerGC(JSRuntime *rt, js::gcreason::Reason reason); /* Must be called with GC lock taken. */ extern void -TriggerCompartmentGC(JSCompartment *comp); +TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); extern void MaybeGC(JSContext *cx); +extern void +ShrinkGCBuffers(JSRuntime *rt); + } /* namespace js */ /* @@ -825,74 +1405,58 @@ typedef enum JSGCInvocationKind { /* Normal invocation. */ GC_NORMAL = 0, - /* - * Called from js_DestroyContext for last JSContext in a JSRuntime, when - * it is imperative that rt->gcPoke gets cleared early in js_GC. - */ - GC_LAST_CONTEXT = 1 + /* Minimize GC triggers and release empty GC chunks right away. */ + GC_SHRINK = 1 } JSGCInvocationKind; /* Pass NULL for |comp| to get a full GC. */ extern void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind); - -#ifdef JS_THREADSAFE -/* - * This is a helper for code at can potentially run outside JS request to - * ensure that the GC is not running when the function returns. - * - * This function must be called with the GC lock held. - */ -extern void -js_WaitForGC(JSRuntime *rt); - -#else /* !JS_THREADSAFE */ - -# define js_WaitForGC(rt) ((void) 0) - -#endif - -extern void -js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp); - -extern void -FinalizeArenaList(JSContext *cx, js::gc::ArenaList *arenaList, js::gc::ArenaHeader *head); +js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason r); namespace js { #ifdef JS_THREADSAFE -/* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a separated - * thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as a - * simple vector, to avoid realloc calls during the vector growth and to not - * bloat the binary size of the inlined freeLater method. Any OOM during - * buffer growth results in the pointer being freed immediately. - */ class GCHelperThread { + enum State { + IDLE, + SWEEPING, + ALLOCATING, + CANCEL_ALLOCATION, + SHUTDOWN + }; + + /* + * During the finalization we do not free immediately. Rather we add the + * corresponding pointers to a buffer which we later release on a + * separated thread. + * + * The buffer is implemented as a vector of 64K arrays of pointers, not as + * a simple vector, to avoid realloc calls during the vector growth and to + * not bloat the binary size of the inlined freeLater method. Any OOM + * during buffer growth results in the pointer being freed immediately. + */ static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - JSContext *cx; - PRThread* thread; - PRCondVar* wakeup; - PRCondVar* sweepingDone; - bool shutdown; + JSRuntime *const rt; + PRThread *thread; + PRCondVar *wakeup; + PRCondVar *done; + volatile State state; + + JSContext *finalizationContext; + bool shrinkFlag; Vector freeVector; void **freeCursor; void **freeCursorEnd; - struct FinalizeListAndHead { - js::gc::ArenaList *list; - js::gc::ArenaHeader *head; + Vector finalizeVector; - }; - - Vector finalizeVector; + bool backgroundAllocation; + + friend struct js::gc::ArenaLists; JS_FRIEND_API(void) replenishAndFreeLater(void *ptr); @@ -905,51 +1469,78 @@ class GCHelperThread { } static void threadMain(void* arg); + void threadLoop(); - void threadLoop(JSRuntime *rt); + /* Must be called with the GC lock taken. */ void doSweep(); public: - GCHelperThread() - : thread(NULL), + GCHelperThread(JSRuntime *rt) + : rt(rt), + thread(NULL), wakeup(NULL), - sweepingDone(NULL), - shutdown(false), + done(NULL), + state(IDLE), + finalizationContext(NULL), + shrinkFlag(false), freeCursor(NULL), freeCursorEnd(NULL), - sweeping(false) { } + backgroundAllocation(true) + { } + + bool init(); + void finish(); + + /* Must be called with the GC lock taken. */ + void startBackgroundSweep(JSContext *cx, bool shouldShrink); + + /* Must be called with the GC lock taken. */ + void startBackgroundShrink(); + + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepEnd(); + + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepOrAllocEnd(); + + /* Must be called with the GC lock taken. */ + inline void startBackgroundAllocationIfIdle(); + + bool canBackgroundAllocate() const { + return backgroundAllocation; + } - volatile bool sweeping; - bool init(JSRuntime *rt); - void finish(JSRuntime *rt); + void disableBackgroundAllocation() { + backgroundAllocation = false; + } + + PRThread *getThread() const { + return thread; + } - /* Must be called with GC lock taken. */ - void startBackgroundSweep(JSRuntime *rt); + /* + * Outside the GC lock may give true answer when in fact the sweeping has + * been done. + */ + bool sweeping() const { + return state == SWEEPING; + } - /* Must be called outside the GC lock. */ - void waitBackgroundSweepEnd(JSRuntime *rt); + bool shouldShrink() const { + JS_ASSERT(sweeping()); + return shrinkFlag; + } void freeLater(void *ptr) { - JS_ASSERT(!sweeping); + JS_ASSERT(!sweeping()); if (freeCursor != freeCursorEnd) *freeCursor++ = ptr; else replenishAndFreeLater(ptr); } - bool finalizeLater(js::gc::ArenaList *list) { - JS_ASSERT(!sweeping); - JS_ASSERT(!list->hasToBeFinalized); - if (!list->head) - return true; - FinalizeListAndHead f = {list, list->head}; - if (!finalizeVector.append(f)) - return false; - list->hasToBeFinalized = true; - return true; - } - - void setContext(JSContext *context) { cx = context; } + /* Must be called with the GC lock taken. */ + bool prepareForBackgroundSweep(); }; #endif /* JS_THREADSAFE */ @@ -962,226 +1553,255 @@ struct GCChunkHasher { * ratio. */ static HashNumber hash(gc::Chunk *chunk) { - JS_ASSERT(!(jsuword(chunk) & GC_CHUNK_MASK)); - return HashNumber(jsuword(chunk) >> GC_CHUNK_SHIFT); + JS_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask)); + return HashNumber(uintptr_t(chunk) >> gc::ChunkShift); } static bool match(gc::Chunk *k, gc::Chunk *l) { - JS_ASSERT(!(jsuword(k) & GC_CHUNK_MASK)); - JS_ASSERT(!(jsuword(l) & GC_CHUNK_MASK)); + JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask)); + JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask)); return k == l; } }; typedef HashSet GCChunkSet; -struct ConservativeGCThreadData { - - /* - * The GC scans conservatively between ThreadData::nativeStackBase and - * nativeStackTop unless the latter is NULL. - */ - jsuword *nativeStackTop; - - union { - jmp_buf jmpbuf; - jsuword words[JS_HOWMANY(sizeof(jmp_buf), sizeof(jsuword))]; - } registerSnapshot; - - /* - * Cycle collector uses this to communicate that the native stack of the - * GC thread should be scanned only if the thread have more than the given - * threshold of requests. - */ - unsigned requestThreshold; - - ConservativeGCThreadData() - : nativeStackTop(NULL), requestThreshold(0) - { - } - - ~ConservativeGCThreadData() { -#ifdef JS_THREADSAFE - /* - * The conservative GC scanner should be disabled when the thread leaves - * the last request. - */ - JS_ASSERT(!hasStackToScan()); -#endif - } - - JS_NEVER_INLINE void recordStackTop(); - -#ifdef JS_THREADSAFE - void updateForRequestEnd(unsigned suspendCount) { - if (suspendCount) - recordStackTop(); - else - nativeStackTop = NULL; - } -#endif - - bool hasStackToScan() const { - return !!nativeStackTop; - } -}; - template struct MarkStack { T *stack; - uintN tos, limit; + T *tos; + T *limit; bool push(T item) { if (tos == limit) return false; - stack[tos++] = item; + *tos++ = item; return true; } - bool isEmpty() { return tos == 0; } + bool push(T item1, T item2, T item3) { + T *nextTos = tos + 3; + if (nextTos > limit) + return false; + tos[0] = item1; + tos[1] = item2; + tos[2] = item3; + tos = nextTos; + return true; + } - T pop() { - JS_ASSERT(!isEmpty()); - return stack[--tos]; + bool isEmpty() const { + return tos == stack; } - T &peek() { + T pop() { JS_ASSERT(!isEmpty()); - return stack[tos-1]; + return *--tos; } - MarkStack(void **buffer, size_t size) - { - tos = 0; - limit = size / sizeof(T) - 1; - stack = (T *)buffer; - } + template + MarkStack(T (&buffer)[N]) + : stack(buffer), + tos(buffer), + limit(buffer + N) { } }; -struct LargeMarkItem -{ - JSObject *obj; - uintN markpos; +static const size_t MARK_STACK_LENGTH = 32768; - LargeMarkItem(JSObject *obj) : obj(obj), markpos(0) {} -}; +struct GCMarker : public JSTracer { + /* + * We use a common mark stack to mark GC things of different types and use + * the explicit tags to distinguish them when it cannot be deduced from + * the context of push or pop operation. + * + * Currently we need only 4 tags. However that can be extended to 8 if + * necessary as we tag only GC things. + */ + enum StackTag { + ValueArrayTag, + ObjectTag, + TypeTag, + XmlTag, + LastTag = XmlTag + }; -static const size_t OBJECT_MARK_STACK_SIZE = 32768 * sizeof(JSObject *); -static const size_t XML_MARK_STACK_SIZE = 1024 * sizeof(JSXML *); -static const size_t LARGE_MARK_STACK_SIZE = 64 * sizeof(LargeMarkItem); + static const uintptr_t StackTagMask = 3; + + static void staticAsserts() { + JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); + JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); + } -struct GCMarker : public JSTracer { private: /* The color is only applied to objects, functions and xml. */ - uint32 color; + uint32_t color; public: - /* See comments before delayMarkingChildren is jsgc.cpp. */ - js::gc::ArenaHeader *unmarkedArenaStackTop; -#ifdef DEBUG - size_t markLaterArenas; -#endif - -#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER) - js::gc::ConservativeGCStats conservativeStats; -#endif + /* Pointer to the top of the stack of arenas we are delaying marking on. */ + js::gc::Arena *unmarkedArenaStackTop; + /* Count of arenas that are currently in the stack. */ + DebugOnly markLaterArenas; #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS + js::gc::ConservativeGCStats conservativeStats; Vector conservativeRoots; const char *conservativeDumpFileName; - void dumpConservativeRoots(); #endif - MarkStack objStack; - MarkStack xmlStack; - MarkStack largeStack; + MarkStack stack; public: explicit GCMarker(JSContext *cx); ~GCMarker(); - uint32 getMarkColor() const { + uint32_t getMarkColor() const { return color; } - void setMarkColor(uint32 newColor) { - /* We must process the mark stack here, otherwise we confuse colors. */ - drainMarkStack(); - color = newColor; + /* + * The only valid color transition during a GC is from black to gray. It is + * wrong to switch the mark color from gray to black. The reason is that the + * cycle collector depends on the invariant that there are no black to gray + * edges in the GC heap. This invariant lets the CC not trace through black + * objects. If this invariant is violated, the cycle collector may free + * objects that are still reachable. + */ + void setMarkColorGray() { + JS_ASSERT(color == gc::BLACK); + color = gc::GRAY; } void delayMarkingChildren(const void *thing); + bool hasDelayedChildren() const { + return !!unmarkedArenaStackTop; + } + void markDelayedChildren(); bool isMarkStackEmpty() { - return objStack.isEmpty() && xmlStack.isEmpty() && largeStack.isEmpty(); + return stack.isEmpty(); } - JS_FRIEND_API(void) drainMarkStack(); + void drainMarkStack(); + + inline void processMarkStackTop(); void pushObject(JSObject *obj) { - if (!objStack.push(obj)) - delayMarkingChildren(obj); + pushTaggedPtr(ObjectTag, obj); + } + + void pushType(types::TypeObject *type) { + pushTaggedPtr(TypeTag, type); } void pushXML(JSXML *xml) { - if (!xmlStack.push(xml)) - delayMarkingChildren(xml); + pushTaggedPtr(XmlTag, xml); + } + + void pushTaggedPtr(StackTag tag, void *ptr) { + uintptr_t addr = reinterpret_cast(ptr); + JS_ASSERT(!(addr & StackTagMask)); + if (!stack.push(addr | uintptr_t(tag))) + delayMarkingChildren(ptr); } }; void MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end); +typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment); +typedef void (*IterateChunkCallback)(JSContext *cx, void *data, gc::Chunk *chunk); +typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena, + JSGCTraceKind traceKind, size_t thingSize); +typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, + JSGCTraceKind traceKind, size_t thingSize); + +/* + * This function calls |compartmentCallback| on every compartment. + */ +extern JS_FRIEND_API(void) +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback); +/* + * This function calls |compartmentCallback| on every compartment, + * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use + * cell in the GC heap. + */ +extern JS_FRIEND_API(void) +IterateCompartmentsArenasCells(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback); + +/* + * Invoke chunkCallback on every in-use chunk. + */ +extern JS_FRIEND_API(void) +IterateChunks(JSContext *cx, void *data, IterateChunkCallback chunkCallback); + +/* + * Invoke cellCallback on every in-use object of the specified thing kind for + * the given compartment or for all compartments if it is null. + */ +extern JS_FRIEND_API(void) +IterateCells(JSContext *cx, JSCompartment *compartment, gc::AllocKind thingKind, + void *data, IterateCellCallback cellCallback); + } /* namespace js */ extern void js_FinalizeStringRT(JSRuntime *rt, JSString *str); /* - * This function is defined in jsdbgapi.cpp but is declared here to avoid - * polluting jsdbgapi.h, a public API header, with internal functions. + * Macro to test if a traversal is the marking phase of the GC. */ -extern void -js_MarkTraps(JSTracer *trc); +#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL) namespace js { namespace gc { -/* - * Macro to test if a traversal is the marking phase of GC to avoid exposing - * ScriptFilenameEntry to traversal implementations. - */ -#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL) +JSCompartment * +NewCompartment(JSContext *cx, JSPrincipals *principals); + +/* Tries to run a GC no matter what (used for GC zeal). */ +void +RunDebugGC(JSContext *cx); + +#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) && !defined(JS_THREADSAFE) +/* Overwrites stack references to GC things which have not been rooted. */ +void CheckStackRoots(JSContext *cx); -#if JS_HAS_XML_SUPPORT -# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) < JSTRACE_LIMIT) +inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); } #else -# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_SHAPE) +inline void MaybeCheckStackRoots(JSContext *cx) {} #endif -/* - * Set object's prototype while checking that doing so would not create - * a cycle in the proto chain. The cycle check and proto change are done - * only when all other requests are finished or suspended to ensure exclusive - * access to the chain. If there is a cycle, return false without reporting - * an error. Otherwise, set the proto and return true. - */ -extern bool -SetProtoCheckingForCycles(JSContext *cx, JSObject *obj, JSObject *proto); +const int ZealPokeThreshold = 1; +const int ZealAllocThreshold = 2; +const int ZealVerifierThreshold = 4; -JSCompartment * -NewCompartment(JSContext *cx, JSPrincipals *principals); +#ifdef JS_GC_ZEAL -} /* namespace js */ -} /* namespace gc */ +/* Check that write barriers have been used correctly. See jsgc.cpp. */ +void +VerifyBarriers(JSContext *cx, bool always = false); + +#else -inline JSCompartment * -JSObject::getCompartment() const +static inline void +VerifyBarriers(JSContext *cx, bool always = false) { - return compartment(); } +#endif + +} /* namespace gc */ + +static inline JSCompartment * +GetObjectCompartment(JSObject *obj) { return reinterpret_cast(obj)->compartment(); } + +} /* namespace js */ + #endif /* jsgc_h___ */ diff --git a/deps/mozjs/js/src/jsgcchunk.cpp b/deps/mozjs/js/src/jsgcchunk.cpp deleted file mode 100644 index 551f6b7ebfa..00000000000 --- a/deps/mozjs/js/src/jsgcchunk.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Copyright (C) 2006-2008 Jason Evans . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice(s), this list of conditions and the following disclaimer as - * the first lines of this file unmodified other than the possible - * addition of one or more copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ - -#include -#include "jstypes.h" -#include "jsstdint.h" -#include "jsgcchunk.h" - -#ifdef XP_WIN -# include "jswin.h" - -# ifdef _MSC_VER -# pragma warning( disable: 4267 4996 4146 ) -# endif - -#elif defined(XP_OS2) - -# define INCL_DOSMEMMGR -# include - -#elif defined(XP_MACOSX) || defined(DARWIN) - -# include -# include -# include -# include -# include - -#elif defined(XP_UNIX) - -# include -# include - -# ifndef MAP_NOSYNC -# define MAP_NOSYNC 0 -# endif - -#endif - -#ifdef XP_WIN - -static void * -MapPages(void *addr, size_t size) -{ - void *p = VirtualAlloc(addr, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); - JS_ASSERT_IF(p && addr, p == addr); - return p; -} - -static void -UnmapPages(void *addr, size_t size) -{ - JS_ALWAYS_TRUE(VirtualFree(addr, 0, MEM_RELEASE)); -} - -#elif defined(XP_OS2) - -#define JS_GC_HAS_MAP_ALIGN 1 -#define OS2_MAX_RECURSIONS 16 - -static void -UnmapPages(void *addr, size_t size) -{ - if (!DosFreeMem(addr)) - return; - - /* if DosFreeMem() failed, 'addr' is probably part of an "expensive" - * allocation, so calculate the base address and try again - */ - unsigned long cb = 2 * size; - unsigned long flags; - if (DosQueryMem(addr, &cb, &flags) || cb < size) - return; - - jsuword base = reinterpret_cast(addr) - ((2 * size) - cb); - DosFreeMem(reinterpret_cast(base)); - - return; -} - -static void * -MapAlignedPagesRecursively(size_t size, size_t alignment, int& recursions) -{ - if (++recursions >= OS2_MAX_RECURSIONS) - return NULL; - - void *tmp; - if (DosAllocMem(&tmp, size, - OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { - JS_ALWAYS_TRUE(DosAllocMem(&tmp, size, - PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); - } - size_t offset = reinterpret_cast(tmp) & (alignment - 1); - if (!offset) - return tmp; - - /* if there are 'filler' bytes of free space above 'tmp', free 'tmp', - * then reallocate it as a 'filler'-sized block; assuming we're not - * in a race with another thread, the next recursion should succeed - */ - size_t filler = size + alignment - offset; - unsigned long cb = filler; - unsigned long flags = 0; - unsigned long rc = DosQueryMem(&(static_cast(tmp))[size], - &cb, &flags); - if (!rc && (flags & PAG_FREE) && cb >= filler) { - UnmapPages(tmp, 0); - if (DosAllocMem(&tmp, filler, - OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { - JS_ALWAYS_TRUE(DosAllocMem(&tmp, filler, - PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); - } - } - - void *p = MapAlignedPagesRecursively(size, alignment, recursions); - UnmapPages(tmp, 0); - - return p; -} - -static void * -MapAlignedPages(size_t size, size_t alignment) -{ - int recursions = -1; - - /* make up to OS2_MAX_RECURSIONS attempts to get an aligned block - * of the right size by recursively allocating blocks of unaligned - * free memory until only an aligned allocation is possible - */ - void *p = MapAlignedPagesRecursively(size, alignment, recursions); - if (p) - return p; - - /* if memory is heavily fragmented, the recursive strategy may fail; - * instead, use the "expensive" strategy: allocate twice as much - * as requested and return an aligned address within this block - */ - if (DosAllocMem(&p, 2 * size, - OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { - JS_ALWAYS_TRUE(DosAllocMem(&p, 2 * size, - PAG_COMMIT | PAG_READ | PAG_WRITE) == 0); - } - - jsuword addr = reinterpret_cast(p); - addr = (addr + (alignment - 1)) & ~(alignment - 1); - - return reinterpret_cast(addr); -} - -#elif defined(XP_MACOSX) || defined(DARWIN) - -static void * -MapPages(void *addr, size_t size) -{ - vm_address_t p; - int flags; - if (addr) { - p = (vm_address_t) addr; - flags = 0; - } else { - flags = VM_FLAGS_ANYWHERE; - } - - kern_return_t err = vm_allocate((vm_map_t) mach_task_self(), - &p, (vm_size_t) size, flags); - if (err != KERN_SUCCESS) - return NULL; - - JS_ASSERT(p); - JS_ASSERT_IF(addr, p == (vm_address_t) addr); - return (void *) p; -} - -static void -UnmapPages(void *addr, size_t size) -{ - JS_ALWAYS_TRUE(vm_deallocate((vm_map_t) mach_task_self(), - (vm_address_t) addr, - (vm_size_t) size) - == KERN_SUCCESS); -} - -#elif defined(XP_UNIX) - -/* Required on Solaris 10. Might improve performance elsewhere. */ -# if defined(SOLARIS) && defined(MAP_ALIGN) -# define JS_GC_HAS_MAP_ALIGN - -static void * -MapAlignedPages(size_t size, size_t alignment) -{ - /* - * We don't use MAP_FIXED here, because it can cause the *replacement* - * of existing mappings, and we only want to create new mappings. - */ -#ifdef SOLARIS - void *p = mmap((caddr_t) alignment, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_NOSYNC | MAP_ALIGN | MAP_ANON, -1, 0); -#else - void *p = mmap((void *) alignment, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_NOSYNC | MAP_ALIGN | MAP_ANON, -1, 0); -#endif - if (p == MAP_FAILED) - return NULL; - return p; -} - -# else /* JS_GC_HAS_MAP_ALIGN */ - -static void * -MapPages(void *addr, size_t size) -{ - /* - * We don't use MAP_FIXED here, because it can cause the *replacement* - * of existing mappings, and we only want to create new mappings. - */ - void *p = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, - -1, 0); - if (p == MAP_FAILED) - return NULL; - if (addr && p != addr) { - /* We succeeded in mapping memory, but not in the right place. */ - JS_ALWAYS_TRUE(munmap(p, size) == 0); - return NULL; - } - return p; -} - -# endif /* !JS_GC_HAS_MAP_ALIGN */ - -static void -UnmapPages(void *addr, size_t size) -{ -#ifdef SOLARIS - JS_ALWAYS_TRUE(munmap((caddr_t) addr, size) == 0); -#else - JS_ALWAYS_TRUE(munmap(addr, size) == 0); -#endif -} - -#endif - -namespace js { - -GCChunkAllocator defaultGCChunkAllocator; - -inline void * -FindChunkStart(void *p) -{ - jsuword addr = reinterpret_cast(p); - addr = (addr + GC_CHUNK_MASK) & ~GC_CHUNK_MASK; - return reinterpret_cast(addr); -} - -JS_FRIEND_API(void *) -AllocGCChunk() -{ - void *p; - -#ifdef JS_GC_HAS_MAP_ALIGN - p = MapAlignedPages(GC_CHUNK_SIZE, GC_CHUNK_SIZE); - if (!p) - return NULL; -#else - /* - * Windows requires that there be a 1:1 mapping between VM allocation - * and deallocation operations. Therefore, take care here to acquire the - * final result via one mapping operation. This means unmapping any - * preliminary result that is not correctly aligned. - */ - p = MapPages(NULL, GC_CHUNK_SIZE); - if (!p) - return NULL; - - if (reinterpret_cast(p) & GC_CHUNK_MASK) { - UnmapPages(p, GC_CHUNK_SIZE); - p = MapPages(FindChunkStart(p), GC_CHUNK_SIZE); - while (!p) { - /* - * Over-allocate in order to map a memory region that is - * definitely large enough then deallocate and allocate again the - * correct size, within the over-sized mapping. - */ - p = MapPages(NULL, GC_CHUNK_SIZE * 2); - if (!p) - return 0; - UnmapPages(p, GC_CHUNK_SIZE * 2); - p = MapPages(FindChunkStart(p), GC_CHUNK_SIZE); - - /* - * Failure here indicates a race with another thread, so - * try again. - */ - } - } -#endif /* !JS_GC_HAS_MAP_ALIGN */ - - JS_ASSERT(!(reinterpret_cast(p) & GC_CHUNK_MASK)); - return p; -} - -JS_FRIEND_API(void) -FreeGCChunk(void *p) -{ - JS_ASSERT(p); - JS_ASSERT(!(reinterpret_cast(p) & GC_CHUNK_MASK)); - UnmapPages(p, GC_CHUNK_SIZE); -} - -} /* namespace js */ - diff --git a/deps/mozjs/js/src/jsgcchunk.h b/deps/mozjs/js/src/jsgcchunk.h deleted file mode 100644 index 94d37ae2d32..00000000000 --- a/deps/mozjs/js/src/jsgcchunk.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9.1 code, released - * June 30, 2009. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation - * - * Contributor(s): - * Igor Bukanov - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsgchunk_h__ -#define jsgchunk_h__ - -#include "jsprvtd.h" -#include "jsutil.h" - -namespace js { - -const size_t GC_CHUNK_SHIFT = 20; -const size_t GC_CHUNK_SIZE = size_t(1) << GC_CHUNK_SHIFT; -const size_t GC_CHUNK_MASK = GC_CHUNK_SIZE - 1; - -JS_FRIEND_API(void *) -AllocGCChunk(); - -JS_FRIEND_API(void) -FreeGCChunk(void *p); - -class GCChunkAllocator { - public: - GCChunkAllocator() {} - - void *alloc() { - void *chunk = doAlloc(); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - return chunk; - } - - void free_(void *chunk) { - JS_ASSERT(chunk); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - doFree(chunk); - } - - private: - virtual void *doAlloc() { - return AllocGCChunk(); - } - - virtual void doFree(void *chunk) { - FreeGCChunk(chunk); - } - - /* No copy or assignment semantics. */ - GCChunkAllocator(const GCChunkAllocator &); - void operator=(const GCChunkAllocator &); -}; - -extern GCChunkAllocator defaultGCChunkAllocator; - -} - -#endif /* jsgchunk_h__ */ diff --git a/deps/mozjs/js/src/jsgcinlines.h b/deps/mozjs/js/src/jsgcinlines.h index a09faca0eca..36627f39673 100644 --- a/deps/mozjs/js/src/jsgcinlines.h +++ b/deps/mozjs/js/src/jsgcinlines.h @@ -43,63 +43,11 @@ #include "jsgc.h" #include "jscntxt.h" #include "jscompartment.h" -#include "jsscope.h" - #include "jslock.h" -#include "jstl.h" - -#ifdef JS_GCMETER -# define METER(x) ((void) (x)) -# define METER_IF(condition, x) ((void) ((condition) && (x))) -#else -# define METER(x) ((void) 0) -# define METER_IF(condition, x) ((void) 0) -#endif - -inline bool -JSAtom::isUnitString(const void *ptr) -{ - jsuword delta = reinterpret_cast(ptr) - - reinterpret_cast(unitStaticTable); - if (delta >= UNIT_STATIC_LIMIT * sizeof(JSString)) - return false; - - /* If ptr points inside the static array, it must be well-aligned. */ - JS_ASSERT(delta % sizeof(JSString) == 0); - return true; -} - -inline bool -JSAtom::isLength2String(const void *ptr) -{ - jsuword delta = reinterpret_cast(ptr) - - reinterpret_cast(length2StaticTable); - if (delta >= NUM_SMALL_CHARS * NUM_SMALL_CHARS * sizeof(JSString)) - return false; - - /* If ptr points inside the static array, it must be well-aligned. */ - JS_ASSERT(delta % sizeof(JSString) == 0); - return true; -} - -inline bool -JSAtom::isHundredString(const void *ptr) -{ - jsuword delta = reinterpret_cast(ptr) - - reinterpret_cast(hundredStaticTable); - if (delta >= NUM_HUNDRED_STATICS * sizeof(JSString)) - return false; - - /* If ptr points inside the static array, it must be well-aligned. */ - JS_ASSERT(delta % sizeof(JSString) == 0); - return true; -} +#include "jsscope.h" +#include "jsxml.h" -inline bool -JSAtom::isStatic(const void *ptr) -{ - return isUnitString(ptr) || isLength2String(ptr) || isHundredString(ptr); -} +#include "js/TemplateLib.h" namespace js { @@ -107,33 +55,97 @@ struct Shape; namespace gc { -inline uint32 +inline JSGCTraceKind GetGCThingTraceKind(const void *thing) { JS_ASSERT(thing); - if (JSAtom::isStatic(thing)) - return JSTRACE_STRING; const Cell *cell = reinterpret_cast(thing); - return GetFinalizableTraceKind(cell->arenaHeader()->getThingKind()); + return MapAllocToTraceKind(cell->getAllocKind()); } /* Capacity for slotsToThingKind */ const size_t SLOTS_TO_THING_KIND_LIMIT = 17; /* Get the best kind to use when making an object with the given slot count. */ -static inline FinalizeKind +static inline AllocKind GetGCObjectKind(size_t numSlots) { - extern FinalizeKind slotsToThingKind[]; + extern AllocKind slotsToThingKind[]; if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) - return FINALIZE_OBJECT0; + return FINALIZE_OBJECT16; return slotsToThingKind[numSlots]; } +static inline AllocKind +GetGCObjectKind(Class *clasp) +{ + if (clasp == &FunctionClass) + return JSFunction::FinalizeKind; + uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp); + if (clasp->flags & JSCLASS_HAS_PRIVATE) + nslots++; + return GetGCObjectKind(nslots); +} + +/* As for GetGCObjectKind, but for dense array allocation. */ +static inline AllocKind +GetGCArrayKind(size_t numSlots) +{ + extern AllocKind slotsToThingKind[]; + + /* + * Dense arrays can use their fixed slots to hold their elements array + * (less two Values worth of ObjectElements header), but if more than the + * maximum number of fixed slots is needed then the fixed slots will be + * unused. + */ + JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2); + if (numSlots > JSObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT) + return FINALIZE_OBJECT2; + return slotsToThingKind[numSlots + 2]; +} + +static inline AllocKind +GetGCObjectFixedSlotsKind(size_t numFixedSlots) +{ + extern AllocKind slotsToThingKind[]; + + JS_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT); + return slotsToThingKind[numFixedSlots]; +} + +static inline bool +IsBackgroundAllocKind(AllocKind kind) +{ + JS_ASSERT(kind <= FINALIZE_OBJECT_LAST); + return kind % 2 == 1; +} + +static inline AllocKind +GetBackgroundAllocKind(AllocKind kind) +{ + JS_ASSERT(!IsBackgroundAllocKind(kind)); + return (AllocKind) (kind + 1); +} + +/* + * Try to get the next larger size for an object, keeping BACKGROUND + * consistent. + */ +static inline bool +TryIncrementAllocKind(AllocKind *kindp) +{ + size_t next = size_t(*kindp) + 2; + if (next >= size_t(FINALIZE_OBJECT_LIMIT)) + return false; + *kindp = AllocKind(next); + return true; +} + /* Get the number of fixed slots and initial capacity associated with a kind. */ static inline size_t -GetGCKindSlots(FinalizeKind thingKind) +GetGCKindSlots(AllocKind thingKind) { /* Using a switch in hopes that thingKind will usually be a compile-time constant. */ switch (thingKind) { @@ -161,8 +173,199 @@ GetGCKindSlots(FinalizeKind thingKind) } } -} /* namespace gc */ -} /* namespace js */ +static inline size_t +GetGCKindSlots(AllocKind thingKind, Class *clasp) +{ + size_t nslots = GetGCKindSlots(thingKind); + + /* An object's private data uses the space taken by its last fixed slot. */ + if (clasp->flags & JSCLASS_HAS_PRIVATE) { + JS_ASSERT(nslots > 0); + nslots--; + } + + /* + * Functions have a larger finalize kind than FINALIZE_OBJECT to reserve + * space for the extra fields in JSFunction, but have no fixed slots. + */ + if (clasp == &FunctionClass) + nslots = 0; + + return nslots; +} + +static inline void +GCPoke(JSRuntime *rt, Value oldval) +{ + /* + * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles + * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg + * ignored", etc. + */ +#if 1 + rt->gcPoke = true; +#else + rt->gcPoke = oldval.isGCThing(); +#endif + +#ifdef JS_GC_ZEAL + /* Schedule a GC to happen "soon" after a GC poke. */ + if (rt->gcZeal() >= js::gc::ZealPokeThreshold) + rt->gcNextScheduled = 1; +#endif +} + +/* + * Invoke ArenaOp and CellOp on every arena and cell in a compartment which + * have the specified thing kind. + */ +template +void +ForEachArenaAndCell(JSCompartment *compartment, AllocKind thingKind, + ArenaOp arenaOp, CellOp cellOp) +{ + size_t thingSize = Arena::thingSize(thingKind); + ArenaHeader *aheader = compartment->arenas.getFirstArena(thingKind); + + for (; aheader; aheader = aheader->next) { + Arena *arena = aheader->getArena(); + arenaOp(arena); + FreeSpan firstSpan(aheader->getFirstFreeSpan()); + const FreeSpan *span = &firstSpan; + + for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { + JS_ASSERT(thing <= arena->thingsEnd()); + if (thing == span->first) { + if (!span->hasNext()) + break; + thing = span->last; + span = span->nextSpan(); + } else { + Cell *t = reinterpret_cast(thing); + cellOp(t); + } + } + } +} + +class CellIterImpl +{ + size_t firstThingOffset; + size_t thingSize; + ArenaHeader *aheader; + FreeSpan firstSpan; + const FreeSpan *span; + uintptr_t thing; + Cell *cell; + + protected: + CellIterImpl() { + } + + void init(JSCompartment *comp, AllocKind kind) { + JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind)); + firstThingOffset = Arena::firstThingOffset(kind); + thingSize = Arena::thingSize(kind); + aheader = comp->arenas.getFirstArena(kind); + firstSpan.initAsEmpty(); + span = &firstSpan; + thing = span->first; + next(); + } + + public: + bool done() const { + return !cell; + } + + template T *get() const { + JS_ASSERT(!done()); + return static_cast(cell); + } + + Cell *getCell() const { + JS_ASSERT(!done()); + return cell; + } + + void next() { + for (;;) { + if (thing != span->first) + break; + if (JS_LIKELY(span->hasNext())) { + thing = span->last + thingSize; + span = span->nextSpan(); + break; + } + if (!aheader) { + cell = NULL; + return; + } + firstSpan = aheader->getFirstFreeSpan(); + span = &firstSpan; + thing = aheader->arenaAddress() | firstThingOffset; + aheader = aheader->next; + } + cell = reinterpret_cast(thing); + thing += thingSize; + } +}; + +class CellIterUnderGC : public CellIterImpl { + + public: + CellIterUnderGC(JSCompartment *comp, AllocKind kind) { + JS_ASSERT(comp->rt->gcRunning); + init(comp, kind); + } +}; + +/* + * When using the iterator outside the GC the caller must ensure that no GC or + * allocations of GC things are possible and that the background finalization + * for the given thing kind is not enabled or is done. + */ +class CellIter: public CellIterImpl +{ + ArenaLists *lists; + AllocKind kind; +#ifdef DEBUG + size_t *counter; +#endif + public: + CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind) + : lists(&comp->arenas), + kind(kind) { +#ifdef JS_THREADSAFE + JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind)); +#endif + if (lists->isSynchronizedFreeList(kind)) { + lists = NULL; + } else { + JS_ASSERT(!comp->rt->gcRunning); + lists->copyFreeListToArena(kind); + } +#ifdef DEBUG + counter = &cx->runtime->noGCOrAllocationCheck; + ++*counter; +#endif + init(comp, kind); + } + + ~CellIter() { +#ifdef DEBUG + JS_ASSERT(*counter > 0); + --*counter; +#endif + if (lists) + lists->clearFreeListInArena(kind); + } +}; + +/* Signatures for ArenaOp and CellOp above. */ + +inline void EmptyArenaOp(Arena *arena) {} +inline void EmptyCellOp(Cell *t) {} /* * Allocates a new GC thing. After a successful allocation the caller must @@ -173,86 +376,109 @@ GetGCKindSlots(FinalizeKind thingKind) template inline T * -NewFinalizableGCThing(JSContext *cx, unsigned thingKind) +NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) { - JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT); + JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); #ifdef JS_THREADSAFE JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), - (thingKind == js::gc::FINALIZE_STRING) || - (thingKind == js::gc::FINALIZE_SHORT_STRING)); + kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); #endif + JS_ASSERT(!cx->runtime->gcRunning); + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); - METER(cx->compartment->compartmentStats[thingKind].alloc++); - do { - js::gc::FreeCell *cell = cx->compartment->freeLists.getNext(thingKind); - if (cell) { - CheckGCFreeListLink(cell); - return (T *)cell; - } - if (!RefillFinalizableFreeList(cx, thingKind)) - return NULL; - } while (true); +#ifdef JS_GC_ZEAL + if (cx->runtime->needZealousGC()) + js::gc::RunDebugGC(cx); +#endif + + js::gc::MaybeCheckStackRoots(cx); + + JSCompartment *comp = cx->compartment; + void *t = comp->arenas.allocateFromFreeList(kind, thingSize); + if (!t) + t = js::gc::ArenaLists::refillFreeList(cx, kind); + return static_cast(t); } -#undef METER -#undef METER_IF +/* Alternate form which allocates a GC thing if doing so cannot trigger a GC. */ +template +inline T * +TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) +{ + JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); +#ifdef JS_THREADSAFE + JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), + kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); +#endif + JS_ASSERT(!cx->runtime->gcRunning); + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); + +#ifdef JS_GC_ZEAL + if (cx->runtime->needZealousGC()) + return NULL; +#endif + + void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); + return static_cast(t); +} + +} /* namespace gc */ +} /* namespace js */ inline JSObject * -js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind) +js_NewGCObject(JSContext *cx, js::gc::AllocKind kind) { JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); - JSObject *obj = NewFinalizableGCThing(cx, kind); - if (obj) { - obj->capacity = js::gc::GetGCKindSlots(kind); - obj->lastProp = NULL; /* Stops obj from being scanned until initializated. */ - } - return obj; + return js::gc::NewGCThing(cx, kind, js::gc::Arena::thingSize(kind)); +} + +inline JSObject * +js_TryNewGCObject(JSContext *cx, js::gc::AllocKind kind) +{ + JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); + return js::gc::TryNewGCThing(cx, kind, js::gc::Arena::thingSize(kind)); } inline JSString * js_NewGCString(JSContext *cx) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_STRING); + return js::gc::NewGCThing(cx, js::gc::FINALIZE_STRING, sizeof(JSString)); } inline JSShortString * js_NewGCShortString(JSContext *cx) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_SHORT_STRING); + return js::gc::NewGCThing(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString)); } inline JSExternalString * -js_NewGCExternalString(JSContext *cx, uintN type) +js_NewGCExternalString(JSContext *cx) { - JS_ASSERT(type < JSExternalString::TYPE_LIMIT); - JSExternalString *str = NewFinalizableGCThing(cx, js::gc::FINALIZE_EXTERNAL_STRING); - return str; + return js::gc::NewGCThing(cx, js::gc::FINALIZE_EXTERNAL_STRING, + sizeof(JSExternalString)); } -inline JSFunction* -js_NewGCFunction(JSContext *cx) +inline JSScript * +js_NewGCScript(JSContext *cx) { - JSFunction *fun = NewFinalizableGCThing(cx, js::gc::FINALIZE_FUNCTION); - if (fun) { - fun->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS; - fun->lastProp = NULL; /* Stops fun from being scanned until initializated. */ - } - return fun; + return js::gc::NewGCThing(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript)); } inline js::Shape * js_NewGCShape(JSContext *cx) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_SHAPE); + return js::gc::NewGCThing(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape)); } -#if JS_HAS_XML_SUPPORT -inline JSXML * -js_NewGCXML(JSContext *cx) +inline js::BaseShape * +js_NewGCBaseShape(JSContext *cx) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_XML); + return js::gc::NewGCThing(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape)); } -#endif +#if JS_HAS_XML_SUPPORT +extern JSXML * +js_NewGCXML(JSContext *cx); +#endif #endif /* jsgcinlines_h___ */ diff --git a/deps/mozjs/js/src/jsgcmark.cpp b/deps/mozjs/js/src/jsgcmark.cpp index 984a861d5d1..6b359195d29 100644 --- a/deps/mozjs/js/src/jsgcmark.cpp +++ b/deps/mozjs/js/src/jsgcmark.cpp @@ -1,41 +1,8 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey code. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jsgcmark.h" #include "jsprf.h" @@ -45,6 +12,39 @@ #include "jsobjinlines.h" #include "jsscopeinlines.h" +#include "vm/String-inl.h" +#include "methodjit/MethodJIT.h" + +/* + * There are two mostly separate mark paths. The first is a fast path used + * internally in the GC. The second is a slow path used for root marking and + * for API consumers like the cycle collector or Class::trace implementations. + * + * The fast path uses explicit stacks. The basic marking process during a GC is + * that all roots are pushed on to a mark stack, and then each item on the + * stack is scanned (possibly pushing more stuff) until the stack is empty. + * + * PushMarkStack pushes a GC thing onto the mark stack. In some cases (shapes + * or strings) it eagerly marks the object rather than pushing it. Popping and + * scanning is done by the processMarkStackTop method. For efficiency reasons + * like tail recursion elimination that method also implements the scanning of + * objects. For other GC things it uses helper methods. + * + * Most of the marking code outside jsgcmark uses functions like MarkObject, + * MarkString, etc. These functions check if an object is in the compartment + * currently being GCed. If it is, they call PushMarkStack. Roots are pushed + * this way as well as pointers traversed inside trace hooks (for things like + * IteratorClass). It it always valid to call a MarkX function instead of + * PushMarkStack, although it may be slower. + * + * The MarkX functions also handle non-GC object traversal. In this case, they + * call a callback for each object visited. This is a recursive process; the + * mark stacks are not involved. These callbacks may ask for the outgoing + * pointers to be visited. Eventually, this leads to the MarkChildren functions + * being called. These functions duplicate much of the functionality of + * scanning functions, but they don't push onto an explicit stack. + */ + using namespace js; using namespace js::gc; @@ -61,38 +61,58 @@ static inline void PushMarkStack(GCMarker *gcmarker, JSFunction *thing); static inline void -PushMarkStack(GCMarker *gcmarker, const Shape *thing); +PushMarkStack(GCMarker *gcmarker, JSScript *thing); static inline void -PushMarkStack(GCMarker *gcmarker, JSShortString *thing); +PushMarkStack(GCMarker *gcmarker, const Shape *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); +static inline void +PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing); + +/*** Object Marking ***/ + template -void -Mark(JSTracer *trc, T *thing) +static inline void +CheckMarkedThing(JSTracer *trc, T *thing) { + JS_ASSERT(trc); JS_ASSERT(thing); - JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing))); JS_ASSERT(trc->debugPrinter || trc->debugPrintArg); + JS_ASSERT_IF(trc->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc)); - JS_ASSERT(!JSAtom::isStatic(thing)); - JS_ASSERT(thing->asFreeCell()->isAligned()); + JS_ASSERT(thing->isAligned()); - JSRuntime *rt = trc->context->runtime; - JS_ASSERT(thing->arenaHeader()->compartment); - JS_ASSERT(thing->arenaHeader()->compartment->rt == rt); + JS_ASSERT(thing->compartment()); + JS_ASSERT(thing->compartment()->rt == trc->runtime); +} + +template +void +MarkInternal(JSTracer *trc, T *thing) +{ + CheckMarkedThing(trc, thing); + + JSRuntime *rt = trc->runtime; + + JS_ASSERT_IF(rt->gcCheckCompartment, + thing->compartment() == rt->gcCheckCompartment || + thing->compartment() == rt->atomsCompartment); /* * Don't mark things outside a compartment if we are in a per-compartment * GC. */ if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) { - if (IS_GC_MARKING_TRACER(trc)) + if (IS_GC_MARKING_TRACER(trc)) { PushMarkStack(static_cast(trc), thing); - else - trc->callback(trc, (void *)thing, GetGCThingTraceKind(thing)); + } else { + void *tmp = (void *)thing; + trc->callback(trc, &tmp, GetGCThingTraceKind(thing)); + JS_ASSERT(tmp == thing); + } } #ifdef DEBUG @@ -101,406 +121,365 @@ Mark(JSTracer *trc, T *thing) #endif } -void -MarkString(JSTracer *trc, JSString *str) +template +static void +MarkUnbarriered(JSTracer *trc, T *thing, const char *name) { - JS_ASSERT(str); - if (str->isStaticAtom()) - return; - Mark(trc, str); + JS_SET_TRACING_NAME(trc, name); + MarkInternal(trc, thing); } -void -MarkString(JSTracer *trc, JSString *str, const char *name) +template +static void +Mark(JSTracer *trc, const HeapPtr &thing, const char *name) { - JS_ASSERT(str); JS_SET_TRACING_NAME(trc, name); - MarkString(trc, str); + MarkInternal(trc, thing.get()); } -void -MarkObject(JSTracer *trc, JSObject &obj, const char *name) +template +static void +MarkRoot(JSTracer *trc, T *thing, const char *name) { - JS_ASSERT(trc); - JS_ASSERT(&obj); JS_SET_TRACING_NAME(trc, name); - Mark(trc, &obj); + MarkInternal(trc, thing); } -void -MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer, - const void *arg, size_t index) -{ - JS_ASSERT(trc); - JS_ASSERT(&obj); - JS_SET_TRACING_DETAILS(trc, printer, arg, index); - Mark(trc, &obj); +template +static void +MarkRange(JSTracer *trc, size_t len, HeapPtr *vec, const char *name) { + for (size_t i = 0; i < len; ++i) { + if (T *obj = vec[i]) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkInternal(trc, obj); + } + } } -void -MarkShape(JSTracer *trc, const Shape *shape, const char *name) -{ - JS_ASSERT(trc); - JS_ASSERT(shape); - JS_SET_TRACING_NAME(trc, name); - Mark(trc, shape); +template +static void +MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name) { + for (size_t i = 0; i < len; ++i) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkInternal(trc, vec[i]); + } } +#define DeclMarkerImpl(base, type) \ +void \ +Mark##base(JSTracer *trc, const HeapPtr &thing, const char *name) \ +{ \ + Mark(trc, thing, name); \ +} \ + \ +void \ +Mark##base##Root(JSTracer *trc, type *thing, const char *name) \ +{ \ + MarkRoot(trc, thing, name); \ +} \ + \ +void \ +Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name) \ +{ \ + MarkUnbarriered(trc, thing, name); \ +} \ + \ +void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr *vec, const char *name) \ +{ \ + MarkRange(trc, len, vec, name); \ +} \ + \ +void Mark##base##RootRange(JSTracer *trc, size_t len, type **vec, const char *name) \ +{ \ + MarkRootRange(trc, len, vec, name); \ +} \ + +DeclMarkerImpl(BaseShape, BaseShape) +DeclMarkerImpl(Object, ArgumentsObject) +DeclMarkerImpl(Object, GlobalObject) +DeclMarkerImpl(Object, JSObject) +DeclMarkerImpl(Object, JSFunction) +DeclMarkerImpl(Script, JSScript) +DeclMarkerImpl(Shape, Shape) +DeclMarkerImpl(String, JSAtom) +DeclMarkerImpl(String, JSString) +DeclMarkerImpl(String, JSFlatString) +DeclMarkerImpl(String, JSLinearString) +DeclMarkerImpl(TypeObject, types::TypeObject) #if JS_HAS_XML_SUPPORT -void -MarkXML(JSTracer *trc, JSXML *xml, const char *name) -{ - JS_ASSERT(trc); - JS_ASSERT(xml); - JS_SET_TRACING_NAME(trc, name); - Mark(trc, xml); -} +DeclMarkerImpl(XML, JSXML) #endif -void -PushMarkStack(GCMarker *gcmarker, JSXML *thing) -{ - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment); - - if (thing->markIfUnmarked(gcmarker->getMarkColor())) - gcmarker->pushXML(thing); -} +/*** Externally Typed Marking ***/ void -PushMarkStack(GCMarker *gcmarker, JSObject *thing) +MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind) { - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment); - - if (thing->markIfUnmarked(gcmarker->getMarkColor())) - gcmarker->pushObject(thing); + JS_ASSERT(thing); + JS_ASSERT(kind == GetGCThingTraceKind(thing)); + switch (kind) { + case JSTRACE_OBJECT: + MarkInternal(trc, reinterpret_cast(thing)); + break; + case JSTRACE_STRING: + MarkInternal(trc, reinterpret_cast(thing)); + break; + case JSTRACE_SCRIPT: + MarkInternal(trc, static_cast(thing)); + break; + case JSTRACE_SHAPE: + MarkInternal(trc, reinterpret_cast(thing)); + break; + case JSTRACE_BASE_SHAPE: + MarkInternal(trc, reinterpret_cast(thing)); + break; + case JSTRACE_TYPE_OBJECT: + MarkInternal(trc, reinterpret_cast(thing)); + break; +#if JS_HAS_XML_SUPPORT + case JSTRACE_XML: + MarkInternal(trc, static_cast(thing)); + break; +#endif + } } void -PushMarkStack(GCMarker *gcmarker, JSFunction *thing) +MarkGCThingRoot(JSTracer *trc, void *thing, const char *name) { - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment); - - if (thing->markIfUnmarked(gcmarker->getMarkColor())) - gcmarker->pushObject(thing); + JS_SET_TRACING_NAME(trc, name); + if (!thing) + return; + MarkKind(trc, thing, GetGCThingTraceKind(thing)); } -void -PushMarkStack(GCMarker *gcmarker, JSShortString *thing) -{ - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment); +/*** ID Marking ***/ - (void) thing->markIfUnmarked(gcmarker->getMarkColor()); +static inline void +MarkIdInternal(JSTracer *trc, const jsid &id) +{ + if (JSID_IS_STRING(id)) + MarkInternal(trc, JSID_TO_STRING(id)); + else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) + MarkInternal(trc, JSID_TO_OBJECT(id)); } -static void -ScanShape(GCMarker *gcmarker, const Shape *shape); - void -PushMarkStack(GCMarker *gcmarker, const Shape *thing) +MarkId(JSTracer *trc, const HeapId &id, const char *name) { - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment); - - /* We mark shapes directly rather than pushing on the stack. */ - if (thing->markIfUnmarked(gcmarker->getMarkColor())) - ScanShape(gcmarker, thing); + JS_SET_TRACING_NAME(trc, name); + MarkIdInternal(trc, id); } void -MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name) +MarkIdRoot(JSTracer *trc, const jsid &id, const char *name) { - for (uint32 i = 0; i < len; i++) { - if (JSAtom *atom = vec[i]) { - JS_SET_TRACING_INDEX(trc, name, i); - if (!atom->isStaticAtom()) - Mark(trc, atom); - } - } + JS_SET_TRACING_NAME(trc, name); + MarkIdInternal(trc, id); } void -MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name) +MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name) { - for (uint32 i = 0; i < len; i++) { - if (JSObject *obj = vec[i]) { - JS_SET_TRACING_INDEX(trc, name, i); - Mark(trc, obj); - } + for (size_t i = 0; i < len; ++i) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkIdInternal(trc, vec[i]); } } void -MarkXMLRange(JSTracer *trc, size_t len, JSXML **vec, const char *name) +MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name) { - for (size_t i = 0; i < len; i++) { - if (JSXML *xml = vec[i]) { - JS_SET_TRACING_INDEX(trc, "xml_vector", i); - Mark(trc, xml); - } + for (size_t i = 0; i < len; ++i) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkIdInternal(trc, vec[i]); } } -void -MarkId(JSTracer *trc, jsid id) -{ - if (JSID_IS_STRING(id)) { - JSString *str = JSID_TO_STRING(id); - if (!str->isStaticAtom()) - Mark(trc, str); - } else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) { - Mark(trc, JSID_TO_OBJECT(id)); - } -} +/*** Value Marking ***/ -void -MarkId(JSTracer *trc, jsid id, const char *name) +static inline void +MarkValueInternal(JSTracer *trc, const Value &v) { - JS_SET_TRACING_NAME(trc, name); - MarkId(trc, id); + if (v.isMarkable()) { + JS_ASSERT(v.toGCThing()); + return MarkKind(trc, v.toGCThing(), v.gcKind()); + } } void -MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name) +MarkValue(JSTracer *trc, const js::HeapValue &v, const char *name) { - for (jsid *idp = beg; idp != end; ++idp) { - JS_SET_TRACING_INDEX(trc, name, (idp - beg)); - MarkId(trc, *idp); - } + JS_SET_TRACING_NAME(trc, name); + MarkValueInternal(trc, v); } void -MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name) +MarkValueRoot(JSTracer *trc, const Value &v, const char *name) { - MarkIdRange(trc, vec, vec + len, name); + JS_SET_TRACING_NAME(trc, name); + MarkValueInternal(trc, v); } void -MarkKind(JSTracer *trc, void *thing, uint32 kind) +MarkValueRange(JSTracer *trc, size_t len, const HeapValue *vec, const char *name) { - JS_ASSERT(thing); - JS_ASSERT(kind == GetGCThingTraceKind(thing)); - switch (kind) { - case JSTRACE_OBJECT: - Mark(trc, reinterpret_cast(thing)); - break; - case JSTRACE_STRING: - MarkString(trc, reinterpret_cast(thing)); - break; - case JSTRACE_SHAPE: - Mark(trc, reinterpret_cast(thing)); - break; -#if JS_HAS_XML_SUPPORT - case JSTRACE_XML: - Mark(trc, reinterpret_cast(thing)); - break; -#endif - default: - JS_ASSERT(false); + for (size_t i = 0; i < len; ++i) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkValueInternal(trc, vec[i]); } } -/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */ void -MarkValueRaw(JSTracer *trc, const js::Value &v) +MarkValueRootRange(JSTracer *trc, size_t len, const Value *vec, const char *name) { - if (v.isMarkable()) { - JS_ASSERT(v.toGCThing()); - return MarkKind(trc, v.toGCThing(), v.gcKind()); + for (size_t i = 0; i < len; ++i) { + JS_SET_TRACING_INDEX(trc, name, i); + MarkValueInternal(trc, vec[i]); } } -void -MarkValue(JSTracer *trc, const js::Value &v, const char *name) +/*** Special Marking ***/ + +/* + * The unioned HeapPtr stored in script->globalObj needs special treatment to + * typecheck correctly. + */ +static void +MarkObject(JSTracer *trc, const HeapPtr &thing, const char *name) { JS_SET_TRACING_NAME(trc, name); - MarkValueRaw(trc, v); + MarkInternal(trc, thing.get()); } void -MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name) +MarkShape(JSTracer *trc, const HeapPtr &thing, const char *name) { - for (Value *vp = beg; vp < end; ++vp) { - JS_SET_TRACING_INDEX(trc, name, vp - beg); - MarkValueRaw(trc, *vp); - } + JS_SET_TRACING_NAME(trc, name); + MarkInternal(trc, const_cast(thing.get())); } void -MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name) +MarkValueUnbarriered(JSTracer *trc, const js::Value &v, const char *name) { - MarkValueRange(trc, vec, vec + len, name); + JS_SET_TRACING_NAME(trc, name); + MarkValueInternal(trc, v); } void -MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name) +MarkCrossCompartmentValue(JSTracer *trc, const js::HeapValue &v, const char *name) { - for (const Shape **sp = beg; sp < end; ++sp) { - JS_SET_TRACING_INDEX(trc, name, sp - beg); - MarkShape(trc, *sp, name); + if (v.isMarkable()) { + js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing(); + JSRuntime *rt = trc->runtime; + if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment) + return; + + MarkValue(trc, v, name); } } -void -MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name) -{ - MarkShapeRange(trc, vec, vec + len, name); -} +/*** Push Mark Stack ***/ -/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */ -void -MarkGCThing(JSTracer *trc, void *thing, uint32 kind) -{ - if (!thing) - return; +#define JS_COMPARTMENT_ASSERT(rt, thing) \ + JS_ASSERT_IF((rt)->gcCurrentCompartment, \ + (thing)->compartment() == (rt)->gcCurrentCompartment); - MarkKind(trc, thing, kind); -} +#define JS_COMPARTMENT_ASSERT_STR(rt, thing) \ + JS_ASSERT_IF((rt)->gcCurrentCompartment, \ + (thing)->compartment() == (rt)->gcCurrentCompartment || \ + (thing)->compartment() == (rt)->atomsCompartment); -void -MarkGCThing(JSTracer *trc, void *thing) +static void +PushMarkStack(GCMarker *gcmarker, JSXML *thing) { - if (!thing) - return; - MarkKind(trc, thing, GetGCThingTraceKind(thing)); -} + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); -void -MarkGCThing(JSTracer *trc, void *thing, const char *name) -{ - JS_SET_TRACING_NAME(trc, name); - MarkGCThing(trc, thing); + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + gcmarker->pushXML(thing); } -void -MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index) +static void +PushMarkStack(GCMarker *gcmarker, JSObject *thing) { - JS_SET_TRACING_INDEX(trc, name, index); - MarkGCThing(trc, thing); -} + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); -void -Mark(JSTracer *trc, void *thing, uint32 kind, const char *name) -{ - JS_ASSERT(thing); - JS_SET_TRACING_NAME(trc, name); - MarkKind(trc, thing, kind); + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + gcmarker->pushObject(thing); } -void -MarkRoot(JSTracer *trc, JSObject *thing, const char *name) +static void +PushMarkStack(GCMarker *gcmarker, JSFunction *thing) { - MarkObject(trc, *thing, name); -} + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); -void -MarkRoot(JSTracer *trc, JSString *thing, const char *name) -{ - MarkString(trc, thing, name); + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + gcmarker->pushObject(thing); } -void -MarkRoot(JSTracer *trc, const Shape *thing, const char *name) +static void +PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing) { - MarkShape(trc, thing, name); + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + gcmarker->pushType(thing); } -void -MarkRoot(JSTracer *trc, JSXML *thing, const char *name) +static void +MarkChildren(JSTracer *trc, JSScript *script); + +static void +PushMarkStack(GCMarker *gcmarker, JSScript *thing) { - MarkXML(trc, thing, name); + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + + /* + * We mark scripts directly rather than pushing on the stack as they can + * refer to other scripts only indirectly (like via nested functions) and + * we cannot get to deep recursion. + */ + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + MarkChildren(gcmarker, thing); } static void -PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) -{ - Shape *shape; - jsid id; - size_t n; - const char *name; - - JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter); - shape = (Shape *)trc->debugPrintArg; - id = shape->id; - JS_ASSERT(!JSID_IS_VOID(id)); - name = trc->debugPrintIndex ? js_setter_str : js_getter_str; - - if (JSID_IS_ATOM(id)) { - n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); - if (n < bufsize) - JS_snprintf(buf + n, bufsize - n, " %s", name); - } else if (JSID_IS_INT(shape->id)) { - JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); - } else { - JS_snprintf(buf, bufsize, " %s", name); - } -} +ScanShape(GCMarker *gcmarker, const Shape *shape); static void -PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize) +PushMarkStack(GCMarker *gcmarker, const Shape *thing) { - Shape *shape; - jsid id; - size_t n; + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); - JS_ASSERT(trc->debugPrinter == PrintPropertyMethod); - shape = (Shape *)trc->debugPrintArg; - id = shape->id; - JS_ASSERT(!JSID_IS_VOID(id)); - - JS_ASSERT(JSID_IS_ATOM(id)); - n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); - if (n < bufsize) - JS_snprintf(buf + n, bufsize - n, " method"); + /* We mark shapes directly rather than pushing on the stack. */ + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + ScanShape(gcmarker, thing); } static inline void -ScanValue(GCMarker *gcmarker, const Value &v) +ScanBaseShape(GCMarker *gcmarker, BaseShape *base); + +static void +PushMarkStack(GCMarker *gcmarker, BaseShape *thing) { - if (v.isMarkable()) { - switch (v.gcKind()) { - case JSTRACE_STRING: { - JSString *str = (JSString *)v.toGCThing(); - if (!str->isStaticAtom()) - PushMarkStack(gcmarker, str); - break; - } - case JSTRACE_OBJECT: - PushMarkStack(gcmarker, (JSObject *)v.toGCThing()); - break; - case JSTRACE_XML: - PushMarkStack(gcmarker, (JSXML *)v.toGCThing()); - break; - } - } + JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + + /* We mark base shapes directly rather than pushing on the stack. */ + if (thing->markIfUnmarked(gcmarker->getMarkColor())) + ScanBaseShape(gcmarker, thing); } static void ScanShape(GCMarker *gcmarker, const Shape *shape) { -restart: - JSRuntime *rt = gcmarker->context->runtime; - if (rt->gcRegenShapes) - shape->shape = js_RegenerateShapeForGC(rt); - - if (JSID_IS_STRING(shape->id)) { - JSString *str = JSID_TO_STRING(shape->id); - if (!str->isStaticAtom()) - PushMarkStack(gcmarker, str); - } else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->id))) { - PushMarkStack(gcmarker, JSID_TO_OBJECT(shape->id)); - } + restart: + PushMarkStack(gcmarker, shape->base()); - if (shape->hasGetterValue() && shape->getter()) - PushMarkStack(gcmarker, shape->getterObject()); - if (shape->hasSetterValue() && shape->setter()) - PushMarkStack(gcmarker, shape->setterObject()); - - if (shape->isMethod()) - PushMarkStack(gcmarker, &shape->methodObject()); + jsid id = shape->maybePropid(); + if (JSID_IS_STRING(id)) + PushMarkStack(gcmarker, JSID_TO_STRING(id)); + else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) + PushMarkStack(gcmarker, JSID_TO_OBJECT(id)); shape = shape->previous(); if (shape && shape->markIfUnmarked(gcmarker->getMarkColor())) @@ -508,181 +487,362 @@ ScanShape(GCMarker *gcmarker, const Shape *shape) } static inline void -PushMarkStack(GCMarker *gcmarker, JSString *str) +ScanBaseShape(GCMarker *gcmarker, BaseShape *base) { - JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment, - str->compartment() == gcmarker->context->runtime->gcCurrentCompartment - || str->compartment() == gcmarker->context->runtime->atomsCompartment); + base->assertConsistency(); - str->mark(gcmarker); -} + if (base->hasGetterObject()) + PushMarkStack(gcmarker, base->getterObject()); -static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048; + if (base->hasSetterObject()) + PushMarkStack(gcmarker, base->setterObject()); -static void -ScanObject(GCMarker *gcmarker, JSObject *obj) -{ - if (obj->isNewborn()) - return; - - if (JSObject *parent = obj->getParent()) + if (JSObject *parent = base->getObjectParent()) PushMarkStack(gcmarker, parent); - if (JSObject *proto = obj->getProto()) - PushMarkStack(gcmarker, proto); - - Class *clasp = obj->getClass(); - if (clasp->trace) { - if (obj->isDenseArray() && obj->getDenseArrayCapacity() > LARGE_OBJECT_CHUNK_SIZE) - gcmarker->largeStack.push(LargeMarkItem(obj)); - else - clasp->trace(gcmarker, obj); - } - if (obj->emptyShapes) { - int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; - for (int i = 0; i < count; i++) { - if (obj->emptyShapes[i]) - PushMarkStack(gcmarker, obj->emptyShapes[i]); - } + /* + * All children of the owned base shape are consistent with its + * unowned one, thus we do not need to trace through children of the + * unowned base shape. + */ + if (base->isOwned()) { + UnownedBaseShape *unowned = base->baseUnowned(); + JS_ASSERT(base->compartment() == unowned->compartment()); + unowned->markIfUnmarked(gcmarker->getMarkColor()); } +} - if (obj->isNative()) { -#ifdef JS_DUMP_SCOPE_METERS - js::MeterEntryCount(obj->propertyCount); -#endif +static inline void +ScanLinearString(GCMarker *gcmarker, JSLinearString *str) +{ + JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str); + JS_ASSERT(str->isMarked()); + + /* + * Add extra asserts to confirm the static type to detect incorrect string + * mutations. + */ + JS_ASSERT(str->JSString::isLinear()); + while (str->isDependent()) { + str = str->asDependent().base(); + JS_ASSERT(str->JSString::isLinear()); + JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str); + if (!str->markIfUnmarked()) + break; + } +} - js::Shape *shape = obj->lastProp; - PushMarkStack(gcmarker, shape); +/* + * The function tries to scan the whole rope tree using the marking stack as + * temporary storage. If that becomes full, the unscanned ropes are added to + * the delayed marking list. When the function returns, the marking stack is + * at the same depth as it was on entry. This way we avoid using tags when + * pushing ropes to the stack as ropes never leaks to other users of the + * stack. This also assumes that a rope can only point to other ropes or + * linear strings, it cannot refer to GC things of other types. + */ +static void +ScanRope(GCMarker *gcmarker, JSRope *rope) +{ + uintptr_t *savedTos = gcmarker->stack.tos; + for (;;) { + JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); + JS_ASSERT(rope->JSString::isRope()); + JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, rope); + JS_ASSERT(rope->isMarked()); + JSRope *next = NULL; + + JSString *right = rope->rightChild(); + if (right->markIfUnmarked()) { + if (right->isLinear()) + ScanLinearString(gcmarker, &right->asLinear()); + else + next = &right->asRope(); + } - if (gcmarker->context->runtime->gcRegenShapes) { - /* We need to regenerate our shape if hasOwnShape(). */ - uint32 newShape = shape->shape; - if (obj->hasOwnShape()) { - newShape = js_RegenerateShapeForGC(gcmarker->context->runtime); - JS_ASSERT(newShape != shape->shape); + JSString *left = rope->leftChild(); + if (left->markIfUnmarked()) { + if (left->isLinear()) { + ScanLinearString(gcmarker, &left->asLinear()); + } else { + /* + * When both children are ropes, set aside the right one to + * scan it later. + */ + if (next && !gcmarker->stack.push(reinterpret_cast(next))) + gcmarker->delayMarkingChildren(next); + next = &left->asRope(); } - obj->objShape = newShape; } - - uint32 nslots = obj->slotSpan(); - JS_ASSERT(obj->slotSpan() <= obj->numSlots()); - if (nslots > LARGE_OBJECT_CHUNK_SIZE) { - gcmarker->largeStack.push(LargeMarkItem(obj)); + if (next) { + rope = next; + } else if (savedTos != gcmarker->stack.tos) { + JS_ASSERT(savedTos < gcmarker->stack.tos); + rope = reinterpret_cast(gcmarker->stack.pop()); } else { - for (uint32 i = nslots; i > 0; i--) { - ScanValue(gcmarker, obj->getSlot(i-1)); - } + break; } } -} + JS_ASSERT(savedTos == gcmarker->stack.tos); + } -static bool -ScanLargeObject(GCMarker *gcmarker, LargeMarkItem &item) +static inline void +ScanString(GCMarker *gcmarker, JSString *str) { - JSObject *obj = item.obj; - - uint32 capacity; - if (obj->isDenseArray()) { - capacity = obj->getDenseArrayCapacity(); - } else { - JS_ASSERT(obj->isNative()); - capacity = obj->slotSpan(); - } - - uintN start = item.markpos; - uintN stop = JS_MIN(start + LARGE_OBJECT_CHUNK_SIZE, capacity); - for (uintN i=stop; i>start; i--) - ScanValue(gcmarker, obj->getSlot(i-1)); + if (str->isLinear()) + ScanLinearString(gcmarker, &str->asLinear()); + else + ScanRope(gcmarker, &str->asRope()); +} - if (stop == capacity) - return true; +static inline void +PushMarkStack(GCMarker *gcmarker, JSString *str) +{ + JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str); - item.markpos += LARGE_OBJECT_CHUNK_SIZE; - return false; + /* + * As string can only refer to other strings we fully scan its GC graph + * using the explicit stack when navigating the rope tree to avoid + * dealing with strings on the stack in drainMarkStack. + */ + if (str->markIfUnmarked()) + ScanString(gcmarker, str); } -static 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); +static inline void +PushValueArray(GCMarker *gcmarker, JSObject* obj, HeapValue *start, HeapValue *end) +{ + JS_ASSERT(start <= end); + uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; + uintptr_t startAddr = reinterpret_cast(start); + uintptr_t endAddr = reinterpret_cast(end); + + /* Push in the reverse order so obj will be on top. */ + if (!gcmarker->stack.push(endAddr, startAddr, tagged)) { + /* + * If we cannot push the array, we trigger delay marking for the whole + * object. + */ + gcmarker->delayMarkingChildren(obj); } } void MarkChildren(JSTracer *trc, JSObject *obj) { - /* If obj has no map, it must be a newborn. */ - if (obj->isNewborn()) - return; + MarkTypeObject(trc, obj->typeFromGC(), "type"); - /* Trace universal (ops-independent) members. */ - if (JSObject *proto = obj->getProto()) - MarkObject(trc, *proto, "proto"); - if (JSObject *parent = obj->getParent()) - MarkObject(trc, *parent, "parent"); - - if (obj->emptyShapes) { - int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; - for (int i = 0; i < count; i++) { - if (obj->emptyShapes[i]) - MarkShape(trc, obj->emptyShapes[i], "emptyShape"); - } - } + Shape *shape = obj->lastProperty(); + MarkShapeUnbarriered(trc, shape, "shape"); - Class *clasp = obj->getClass(); + Class *clasp = shape->getObjectClass(); if (clasp->trace) clasp->trace(trc, obj); - if (obj->isNative()) { -#ifdef JS_DUMP_SCOPE_METERS - js::MeterEntryCount(obj->propertyCount); -#endif - - MarkShape(trc, obj->lastProp, "shape"); - - if (obj->slotSpan() > 0) - MarkObjectSlots(trc, obj); + if (shape->isNative()) { + uint32_t nslots = obj->slotSpan(); + for (uint32_t i = 0; i < nslots; i++) { + JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); + MarkValueInternal(trc, obj->nativeGetSlot(i)); + } } } -void +static void MarkChildren(JSTracer *trc, JSString *str) { + /* + * We use custom barriers in JSString, so it's safe to use unbarriered + * marking here. + */ if (str->isDependent()) { - MarkString(trc, str->asDependent().base(), "base"); + MarkStringUnbarriered(trc, str->asDependent().base(), "base"); } else if (str->isRope()) { JSRope &rope = str->asRope(); - MarkString(trc, rope.leftChild(), "left child"); - MarkString(trc, rope.rightChild(), "right child"); + MarkStringUnbarriered(trc, rope.leftChild(), "left child"); + MarkStringUnbarriered(trc, rope.rightChild(), "right child"); } } -void +static void +MarkChildren(JSTracer *trc, JSScript *script) +{ + CheckScript(script, NULL); + + JS_ASSERT_IF(trc->runtime->gcCheckCompartment, + script->compartment() == trc->runtime->gcCheckCompartment); + + MarkStringRootRange(trc, script->natoms, script->atoms, "atoms"); + + if (JSScript::isValidOffset(script->objectsOffset)) { + JSObjectArray *objarray = script->objects(); + MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); + } + + if (JSScript::isValidOffset(script->regexpsOffset)) { + JSObjectArray *objarray = script->regexps(); + MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); + } + + if (JSScript::isValidOffset(script->constOffset)) { + JSConstArray *constarray = script->consts(); + MarkValueRange(trc, constarray->length, constarray->vector, "consts"); + } + + if (script->function()) + MarkObjectUnbarriered(trc, script->function(), "function"); + + if (!script->isCachedEval && script->globalObject) + MarkObject(trc, script->globalObject, "object"); + + if (IS_GC_MARKING_TRACER(trc) && script->filename) + js_MarkScriptFilename(script->filename); + + script->bindings.trace(trc); + + if (script->types) + script->types->trace(trc); + + if (script->hasAnyBreakpointsOrStepMode()) + script->markTrapClosures(trc); +} + +static void MarkChildren(JSTracer *trc, const Shape *shape) { -restart: - MarkId(trc, shape->id, "id"); + MarkBaseShapeUnbarriered(trc, shape->base(), "base"); + MarkId(trc, shape->maybePropid(), "propid"); + if (shape->previous()) + MarkShape(trc, shape->previous(), "parent"); +} - if (shape->hasGetterValue() && shape->getter()) - MarkObjectWithPrinter(trc, *shape->getterObject(), PrintPropertyGetterOrSetter, shape, 0); - if (shape->hasSetterValue() && shape->setter()) - MarkObjectWithPrinter(trc, *shape->setterObject(), PrintPropertyGetterOrSetter, shape, 1); +static inline void +MarkBaseShapeGetterSetter(JSTracer *trc, BaseShape *base) +{ + if (base->hasGetterObject()) + MarkObjectUnbarriered(trc, base->getterObject(), "getter"); + if (base->hasSetterObject()) + MarkObjectUnbarriered(trc, base->setterObject(), "setter"); +} - if (shape->isMethod()) - MarkObjectWithPrinter(trc, shape->methodObject(), PrintPropertyMethod, shape, 0); +static void +MarkChildren(JSTracer *trc, BaseShape *base) +{ + MarkBaseShapeGetterSetter(trc, base); + if (base->isOwned()) + MarkBaseShapeUnbarriered(trc, base->baseUnowned(), "base"); - shape = shape->previous(); - if (shape) - goto restart; + if (JSObject *parent = base->getObjectParent()) + MarkObjectUnbarriered(trc, parent, "parent"); } -#ifdef JS_HAS_XML_SUPPORT +/* + * This function is used by the cycle collector to trace through the + * children of a BaseShape (and its baseUnowned(), if any). The cycle + * collector does not directly care about BaseShapes, so only the + * getter, setter, and parent are marked. Furthermore, the parent is + * marked only if it isn't the same as prevParent, which will be + * updated to the current shape's parent. + */ +inline void +MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent) +{ + JS_ASSERT(base); + + /* + * The cycle collector does not need to trace unowned base shapes, + * as they have the same getter, setter and parent as the original + * base shape. + */ + base->assertConsistency(); + + MarkBaseShapeGetterSetter(trc, base); + + JSObject *parent = base->getObjectParent(); + if (parent && parent != *prevParent) { + MarkObjectUnbarriered(trc, parent, "parent"); + *prevParent = parent; + } +} + +/* + * This function is used by the cycle collector to trace through a + * shape. The cycle collector does not care about shapes or base + * shapes, so those are not marked. Instead, any shapes or base shapes + * that are encountered have their children marked. Stack space is + * bounded. If two shapes in a row have the same parent pointer, the + * parent pointer will only be marked once. + */ void +MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape) +{ + JSObject *prevParent = NULL; + do { + MarkCycleCollectorChildren(trc, shape->base(), &prevParent); + MarkId(trc, shape->maybePropid(), "propid"); + shape = shape->previous(); + } while (shape); +} + +static void +ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) +{ + if (!type->singleton) { + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop && JSID_IS_STRING(prop->id)) + PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); + } + } + + if (type->proto) + PushMarkStack(gcmarker, type->proto); + + if (type->newScript) { + PushMarkStack(gcmarker, type->newScript->fun); + PushMarkStack(gcmarker, type->newScript->shape); + } + + if (type->interpretedFunction) + PushMarkStack(gcmarker, type->interpretedFunction); + + if (type->singleton && !type->lazy()) + PushMarkStack(gcmarker, type->singleton); + + if (type->interpretedFunction) + PushMarkStack(gcmarker, type->interpretedFunction); +} + +static void +MarkChildren(JSTracer *trc, types::TypeObject *type) +{ + if (!type->singleton) { + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop) + MarkId(trc, prop->id, "type_prop"); + } + } + + if (type->proto) + MarkObject(trc, type->proto, "type_proto"); + + if (type->singleton && !type->lazy()) + MarkObject(trc, type->singleton, "type_singleton"); + + if (type->newScript) { + MarkObject(trc, type->newScript->fun, "type_new_function"); + MarkShape(trc, type->newScript->shape, "type_new_shape"); + } + + if (type->interpretedFunction) + MarkObject(trc, type->interpretedFunction, "type_function"); +} + +#ifdef JS_HAS_XML_SUPPORT +static void MarkChildren(JSTracer *trc, JSXML *xml) { js_TraceXML(trc, xml); @@ -691,54 +851,170 @@ MarkChildren(JSTracer *trc, JSXML *xml) } /* namespace gc */ -void -GCMarker::drainMarkStack() +inline void +GCMarker::processMarkStackTop() { - while (!isMarkStackEmpty()) { - while (!objStack.isEmpty()) - ScanObject(this, objStack.pop()); + /* + * The function uses explicit goto and implements the scanning of the + * object directly. It allows to eliminate the tail recursion and + * significantly improve the marking performance, see bug 641025. + */ + HeapValue *vp, *end; + JSObject *obj; + + uintptr_t addr = stack.pop(); + uintptr_t tag = addr & StackTagMask; + addr &= ~StackTagMask; + + if (tag == ValueArrayTag) { + JS_STATIC_ASSERT(ValueArrayTag == 0); + JS_ASSERT(!(addr & Cell::CellMask)); + obj = reinterpret_cast(addr); + uintptr_t addr2 = stack.pop(); + uintptr_t addr3 = stack.pop(); + JS_ASSERT(addr2 <= addr3); + JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0); + vp = reinterpret_cast(addr2); + end = reinterpret_cast(addr3); + goto scan_value_array; + } - while (!xmlStack.isEmpty()) - MarkChildren(this, xmlStack.pop()); + if (tag == ObjectTag) { + obj = reinterpret_cast(addr); + goto scan_obj; + } - if (!largeStack.isEmpty()) { - LargeMarkItem &item = largeStack.peek(); - if (ScanLargeObject(this, item)) - largeStack.pop(); + if (tag == TypeTag) { + ScanTypeObject(this, reinterpret_cast(addr)); + } else { + JS_ASSERT(tag == XmlTag); + MarkChildren(this, reinterpret_cast(addr)); + } + return; + + scan_value_array: + JS_ASSERT(vp <= end); + while (vp != end) { + const Value &v = *vp++; + if (v.isString()) { + JSString *str = v.toString(); + if (str->markIfUnmarked()) + ScanString(this, str); + } else if (v.isObject()) { + JSObject *obj2 = &v.toObject(); + if (obj2->markIfUnmarked(getMarkColor())) { + PushValueArray(this, obj, vp, end); + obj = obj2; + goto scan_obj; + } + } + } + return; + + scan_obj: + { + types::TypeObject *type = obj->typeFromGC(); + PushMarkStack(this, type); + + js::Shape *shape = obj->lastProperty(); + PushMarkStack(this, shape); + + /* Call the trace hook if necessary. */ + Class *clasp = shape->getObjectClass(); + if (clasp->trace) { + if (clasp == &ArrayClass) { + JS_ASSERT(!shape->isNative()); + vp = obj->getDenseArrayElements(); + end = vp + obj->getDenseArrayInitializedLength(); + goto scan_value_array; + } + clasp->trace(this, obj); } - if (isMarkStackEmpty()) { - /* - * Mark children of things that caused too deep recursion during the above - * tracing. Don't do this until we're done with everything else. - */ - markDelayedChildren(); + if (!shape->isNative()) + return; + + unsigned nslots = obj->slotSpan(); + vp = obj->fixedSlots(); + if (obj->slots) { + unsigned nfixed = obj->numFixedSlots(); + if (nslots > nfixed) { + PushValueArray(this, obj, vp, vp + nfixed); + vp = obj->slots; + end = vp + (nslots - nfixed); + goto scan_value_array; + } } + JS_ASSERT(nslots <= obj->numFixedSlots()); + end = vp + nslots; + goto scan_value_array; } } -} /* namespace js */ +void +GCMarker::drainMarkStack() +{ + JSRuntime *rt = runtime; + rt->gcCheckCompartment = rt->gcCurrentCompartment; + + for (;;) { + while (!stack.isEmpty()) + processMarkStackTop(); + if (!hasDelayedChildren()) + break; + + /* + * Mark children of things that caused too deep recursion during the + * above tracing. Don't do this until we're done with everything + * else. + */ + markDelayedChildren(); + } -JS_PUBLIC_API(void) -JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) + rt->gcCheckCompartment = NULL; +} + +void +TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind) { switch (kind) { case JSTRACE_OBJECT: - MarkChildren(trc, (JSObject *)thing); + MarkChildren(trc, static_cast(thing)); break; case JSTRACE_STRING: - MarkChildren(trc, (JSString *)thing); + MarkChildren(trc, static_cast(thing)); + break; + + case JSTRACE_SCRIPT: + MarkChildren(trc, static_cast(thing)); break; case JSTRACE_SHAPE: - MarkChildren(trc, (js::Shape *)thing); + MarkChildren(trc, static_cast(thing)); + break; + + case JSTRACE_BASE_SHAPE: + MarkChildren(trc, static_cast(thing)); + break; + + case JSTRACE_TYPE_OBJECT: + MarkChildren(trc, (types::TypeObject *)thing); break; #if JS_HAS_XML_SUPPORT case JSTRACE_XML: - MarkChildren(trc, (JSXML *)thing); + MarkChildren(trc, static_cast(thing)); break; #endif } } + +void +CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind) +{ + JS_ASSERT(thing); + MarkKind(trc, thing, kind); +} + +} /* namespace js */ diff --git a/deps/mozjs/js/src/jsgcmark.h b/deps/mozjs/js/src/jsgcmark.h index d5e77b4dd3c..57099af8218 100644 --- a/deps/mozjs/js/src/jsgcmark.h +++ b/deps/mozjs/js/src/jsgcmark.h @@ -1,41 +1,8 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey code. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jsgcmark_h___ #define jsgcmark_h___ @@ -43,118 +10,188 @@ #include "jsgc.h" #include "jscntxt.h" #include "jscompartment.h" - #include "jslock.h" -#include "jstl.h" + +#include "gc/Barrier.h" +#include "js/TemplateLib.h" namespace js { namespace gc { -template -void Mark(JSTracer *trc, T *thing); +/*** Object Marking ***/ -void -MarkString(JSTracer *trc, JSString *str); - -void -MarkString(JSTracer *trc, JSString *str, const char *name); +/* + * These functions expose marking functionality for all of the different GC + * thing kinds. For each GC thing, there are several variants. As an example, + * these are the variants generated for JSObject. They are listed from most to + * least desirable for use: + * + * MarkObject(JSTracer *trc, const HeapPtr &thing, const char *name); + * This function should be used for marking JSObjects, in preference to all + * others below. Use it when you have HeapPtr, which + * automatically implements write barriers. + * + * MarkObjectRoot(JSTracer *trc, JSObject *thing, const char *name); + * This function is only valid during the root marking phase of GC (i.e., + * when MarkRuntime is on the stack). + * + * MarkObjectUnbarriered(JSTracer *trc, JSObject *thing, const char *name); + * Like MarkObject, this function can be called at any time. It is more + * forgiving, since it doesn't demand a HeapPtr as an argument. Its use + * should always be accompanied by a comment explaining how write barriers + * are implemented for the given field. + * + * Additionally, the functions MarkObjectRange and MarkObjectRootRange are + * defined for marking arrays of object pointers. + */ +#define DeclMarker(base, type) \ +void Mark##base(JSTracer *trc, const HeapPtr &thing, const char *name); \ +void Mark##base##Root(JSTracer *trc, type *thing, const char *name); \ +void Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name); \ +void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr *thing, const char *name); \ +void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name); + +DeclMarker(BaseShape, BaseShape) +DeclMarker(Object, ArgumentsObject) +DeclMarker(Object, GlobalObject) +DeclMarker(Object, JSObject) +DeclMarker(Object, JSFunction) +DeclMarker(Script, JSScript) +DeclMarker(Shape, Shape) +DeclMarker(String, JSAtom) +DeclMarker(String, JSString) +DeclMarker(String, JSFlatString) +DeclMarker(String, JSLinearString) +DeclMarker(TypeObject, types::TypeObject) +#if JS_HAS_XML_SUPPORT +DeclMarker(XML, JSXML) +#endif -void -MarkObject(JSTracer *trc, JSObject &obj, const char *name); +/*** Externally Typed Marking ***/ +/* + * Note: this must only be called by the GC and only when we are tracing through + * MarkRoots. It is explicitly for ConservativeStackMarking and should go away + * after we transition to exact rooting. + */ void -MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer, - const void *arg, size_t index); +MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind); void -MarkShape(JSTracer *trc, const Shape *shape, const char *name); +MarkGCThingRoot(JSTracer *trc, void *thing, const char *name); -void -MarkXML(JSTracer *trc, JSXML *xml, const char *name); +/*** ID Marking ***/ void -MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name); +MarkId(JSTracer *trc, const HeapId &id, const char *name); void -MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name); +MarkIdRoot(JSTracer *trc, const jsid &id, const char *name); void -MarkXMLRange(JSTracer *trc, size_t len, JSXML **vec, const char *name); +MarkIdRange(JSTracer *trc, size_t len, js::HeapId *vec, const char *name); void -MarkId(JSTracer *trc, jsid id); +MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name); -void -MarkId(JSTracer *trc, jsid id, const char *name); +/*** Value Marking ***/ void -MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name); +MarkValue(JSTracer *trc, const js::HeapValue &v, const char *name); void -MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name); +MarkValueRange(JSTracer *trc, size_t len, const HeapValue *vec, const char *name); void -MarkKind(JSTracer *trc, void *thing, uint32 kind); +MarkValueRoot(JSTracer *trc, const Value &v, const char *name); void -MarkValueRaw(JSTracer *trc, const js::Value &v); +MarkValueRootRange(JSTracer *trc, size_t len, const Value *vec, const char *name); -void -MarkValue(JSTracer *trc, const js::Value &v, const char *name); - -void -MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name); - -void -MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name); +inline void +MarkValueRootRange(JSTracer *trc, const Value *begin, const Value *end, const char *name) +{ + MarkValueRootRange(trc, end - begin, begin, name); +} -void -MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name); +/*** Special Cases ***/ +/* TypeNewObject contains a HeapPtr that needs a unique cast. */ void -MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name); +MarkShape(JSTracer *trc, const HeapPtr &thing, const char *name); -/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */ +/* Direct value access used by the write barriers and the methodjit */ void -MarkGCThing(JSTracer *trc, void *thing, uint32 kind); +MarkValueUnbarriered(JSTracer *trc, const js::Value &v, const char *name); +/* + * Mark a value that may be in a different compartment from the compartment + * being GC'd. (Although it won't be marked if it's in the wrong compartment.) + */ void -MarkGCThing(JSTracer *trc, void *thing); +MarkCrossCompartmentValue(JSTracer *trc, const js::HeapValue &v, const char *name); +/* + * MarkChildren is exposed solely for preWriteBarrier on + * JSObject::TradeGuts. It should not be considered external interface. + */ void -MarkGCThing(JSTracer *trc, void *thing, const char *name); +MarkChildren(JSTracer *trc, JSObject *obj); +/* + * Trace through the shape and any shapes it contains to mark + * non-shape children. This is exposed to the JS API as + * JS_TraceShapeCycleCollectorChildren. + */ void -MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index); +MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape); -void -Mark(JSTracer *trc, void *thing, uint32 kind, const char *name); +/*** Generic ***/ +/* + * The Mark() functions interface should only be used by code that must be + * templated. Other uses should use the more specific, type-named functions. + */ -void -MarkRoot(JSTracer *trc, JSObject *thing, const char *name); +inline void +Mark(JSTracer *trc, const js::HeapValue &v, const char *name) +{ + MarkValue(trc, v, name); +} -void -MarkRoot(JSTracer *trc, JSString *thing, const char *name); +inline void +Mark(JSTracer *trc, const HeapPtr &o, const char *name) +{ + MarkObject(trc, o, name); +} -void -MarkRoot(JSTracer *trc, const Shape *thing, const char *name); +inline void +Mark(JSTracer *trc, const HeapPtr &xml, const char *name) +{ + MarkXML(trc, xml, name); +} -void -MarkRoot(JSTracer *trc, JSXML *thing, const char *name); +inline bool +IsMarked(const js::Value &v) +{ + if (v.isMarkable()) + return !IsAboutToBeFinalized(v); + return true; +} -void -MarkChildren(JSTracer *trc, JSObject *obj); +inline bool +IsMarked(Cell *cell) +{ + return !IsAboutToBeFinalized(cell); +} -void -MarkChildren(JSTracer *trc, JSString *str); +} /* namespace gc */ void -MarkChildren(JSTracer *trc, const Shape *shape); +TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind); void -MarkChildren(JSTracer *trc, JSXML *xml); +CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind); -} -} +} /* namespace js */ #endif diff --git a/deps/mozjs/js/src/jsgcstats.cpp b/deps/mozjs/js/src/jsgcstats.cpp index e02031e0480..ef56cd12d0f 100644 --- a/deps/mozjs/js/src/jsgcstats.cpp +++ b/deps/mozjs/js/src/jsgcstats.cpp @@ -36,16 +36,19 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/Util.h" + #include "jstypes.h" #include "jscntxt.h" #include "jsgcstats.h" #include "jsgc.h" #include "jsxml.h" -#include "jsbuiltins.h" #include "jscompartment.h" #include "jsgcinlines.h" +#include "jsobjinlines.h" +using namespace mozilla; using namespace js; using namespace js::gc; @@ -55,13 +58,13 @@ using namespace js::gc; namespace js { namespace gc { -#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER) +#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) void ConservativeGCStats::dump(FILE *fp) { size_t words = 0; - for (size_t i = 0; i != JS_ARRAY_LENGTH(counter); ++i) + for (size_t i = 0; i < ArrayLength(counter); ++i) words += counter[i]; #define ULSTAT(x) ((unsigned long)(x)) @@ -70,8 +73,8 @@ ConservativeGCStats::dump(FILE *fp) fprintf(fp, " excluded, low bit set: %lu\n", ULSTAT(counter[CGCT_LOWBITSET])); fprintf(fp, " not withing a chunk: %lu\n", ULSTAT(counter[CGCT_NOTCHUNK])); fprintf(fp, " not within arena range: %lu\n", ULSTAT(counter[CGCT_NOTARENA])); + fprintf(fp, " in another compartment: %lu\n", ULSTAT(counter[CGCT_OTHERCOMPARTMENT])); fprintf(fp, " points to free arena: %lu\n", ULSTAT(counter[CGCT_FREEARENA])); - fprintf(fp, " excluded, wrong tag: %lu\n", ULSTAT(counter[CGCT_WRONGTAG])); fprintf(fp, " excluded, not live: %lu\n", ULSTAT(counter[CGCT_NOTLIVE])); fprintf(fp, " valid GC things: %lu\n", ULSTAT(counter[CGCT_VALID])); fprintf(fp, " valid but not aligned: %lu\n", ULSTAT(unaligned)); @@ -79,230 +82,7 @@ ConservativeGCStats::dump(FILE *fp) } #endif -#ifdef JS_GCMETER -void -UpdateCompartmentStats(JSCompartment *comp, unsigned thingKind, uint32 nlivearenas, - uint32 nkilledArenas, uint32 nthings) -{ - size_t narenas = 0; - JSGCArenaStats *compSt = &comp->compartmentStats[thingKind]; - JSGCArenaStats *globSt = &comp->rt->globalArenaStats[thingKind]; - narenas = nlivearenas + nkilledArenas; - JS_ASSERT(narenas >= compSt->livearenas); - - compSt->newarenas = narenas - compSt->livearenas; - compSt->narenas = narenas; - compSt->livearenas = nlivearenas; - if (compSt->maxarenas < narenas) - compSt->maxarenas = narenas; - compSt->totalarenas += narenas; - - compSt->nthings = nthings; - if (compSt->maxthings < nthings) - compSt->maxthings = nthings; - compSt->totalthings += nthings; - globSt->newarenas += compSt->newarenas; - globSt->narenas += narenas; - globSt->livearenas += compSt->livearenas; - globSt->totalarenas += compSt->totalarenas; - globSt->nthings += compSt->nthings; - globSt->totalthings += compSt->totalthings; - if (globSt->maxarenas < compSt->maxarenas) - globSt->maxarenas = compSt->maxarenas; - if (globSt->maxthings < compSt->maxthings) - globSt->maxthings = compSt->maxthings; -} - -static const char *const GC_ARENA_NAMES[] = { - "object_0", - "object_0_background", - "object_2", - "object_2_background", - "object_4", - "object_4_background", - "object_8", - "object_8_background", - "object_12", - "object_12_background", - "object_16", - "object_16_background", - "function", - "shape", -#if JS_HAS_XML_SUPPORT - "xml", -#endif - "short string", - "string", - "external_string", -}; -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GC_ARENA_NAMES) == FINALIZE_LIMIT); - -template -static inline void -GetSizeAndThings(size_t &thingSize, size_t &thingsPerArena) -{ - thingSize = sizeof(T); - thingsPerArena = Arena::ThingsPerArena; -} - -void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena) -{ - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_EXTERNAL_STRING: - case FINALIZE_STRING: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_SHORT_STRING: - GetSizeAndThings(thingSize, thingsPerArena); - break; - case FINALIZE_FUNCTION: - GetSizeAndThings(thingSize, thingsPerArena); - break; -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - GetSizeAndThings(thingSize, thingsPerArena); - break; -#endif - default: - JS_NOT_REACHED("wrong kind"); - } -} - -void -DumpArenaStats(JSGCArenaStats *stp, FILE *fp) -{ - size_t sumArenas = 0, sumTotalArenas = 0, sumThings =0, sumMaxThings = 0; - size_t sumThingSize = 0, sumTotalThingSize = 0, sumArenaCapacity = 0; - size_t sumTotalArenaCapacity = 0, sumAlloc = 0, sumLocalAlloc = 0; - - for (int i = 0; i < (int) FINALIZE_LIMIT; i++) { - JSGCArenaStats *st = &stp[i]; - if (st->maxarenas == 0) - continue; - size_t thingSize = 0, thingsPerArena = 0; - GetSizeAndThingsPerArena(i, thingSize, thingsPerArena); - - fprintf(fp, "%s arenas (thing size %lu, %lu things per arena):\n", - GC_ARENA_NAMES[i], UL(thingSize), UL(thingsPerArena)); - fprintf(fp, " arenas before GC: %lu\n", UL(st->narenas)); - fprintf(fp, " arenas after GC: %lu (%.1f%%)\n", - UL(st->livearenas), PERCENT(st->livearenas, st->narenas)); - fprintf(fp, " max arenas: %lu\n", UL(st->maxarenas)); - fprintf(fp, " things: %lu\n", UL(st->nthings)); - fprintf(fp, " GC cell utilization: %.1f%%\n", - PERCENT(st->nthings, thingsPerArena * st->narenas)); - fprintf(fp, " average cell utilization: %.1f%%\n", - PERCENT(st->totalthings, thingsPerArena * st->totalarenas)); - fprintf(fp, " max things: %lu\n", UL(st->maxthings)); - fprintf(fp, " alloc attempts: %lu\n", UL(st->alloc)); - fprintf(fp, " alloc without locks: %lu (%.1f%%)\n", - UL(st->localalloc), PERCENT(st->localalloc, st->alloc)); - sumArenas += st->narenas; - sumTotalArenas += st->totalarenas; - sumThings += st->nthings; - sumMaxThings += st->maxthings; - sumThingSize += thingSize * st->nthings; - sumTotalThingSize += size_t(thingSize * st->totalthings); - sumArenaCapacity += thingSize * thingsPerArena * st->narenas; - sumTotalArenaCapacity += thingSize * thingsPerArena * st->totalarenas; - sumAlloc += st->alloc; - sumLocalAlloc += st->localalloc; - putc('\n', fp); - } - - fputs("Never used arenas:\n", fp); - for (int i = 0; i < (int) FINALIZE_LIMIT; i++) { - JSGCArenaStats *st = &stp[i]; - if (st->maxarenas != 0) - continue; - fprintf(fp, "%s\n", GC_ARENA_NAMES[i]); - } - fprintf(fp, "\nTOTAL STATS:\n"); - fprintf(fp, " total GC arenas: %lu\n", UL(sumArenas)); - fprintf(fp, " total GC things: %lu\n", UL(sumThings)); - fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings)); - fprintf(fp, " GC cell utilization: %.1f%%\n", - PERCENT(sumThingSize, sumArenaCapacity)); - fprintf(fp, " average cell utilization: %.1f%%\n", - PERCENT(sumTotalThingSize, sumTotalArenaCapacity)); - fprintf(fp, " alloc attempts: %lu\n", UL(sumAlloc)); - fprintf(fp, " alloc without locks: %lu (%.1f%%)\n", - UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc)); -} - -void -DumpCompartmentStats(JSCompartment *comp, FILE *fp) -{ - if (comp->rt->atomsCompartment == comp) - fprintf(fp, "\n**** AtomsCompartment Allocation Statistics: %p ****\n\n", (void *) comp); - else - fprintf(fp, "\n**** Compartment Allocation Statistics: %p ****\n\n", (void *) comp); - - DumpArenaStats(&comp->compartmentStats[0], fp); -} - -#endif - } //gc -} //js - -#ifdef JS_GCMETER - -JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp) -{ -#define ULSTAT(x) UL(rt->gcStats.x) - if (JS_WANT_GC_METER_PRINT) { - fprintf(fp, "\n**** Global Arena Allocation Statistics: ****\n"); - DumpArenaStats(&rt->globalArenaStats[0], fp); - fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes)); - fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); - fprintf(fp, " last ditch GC runs: %lu\n", ULSTAT(lastditch)); - fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); - fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); - fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(unmarked)); -#ifdef DEBUG - fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxunmarked)); -#endif - fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); - fprintf(fp, " thing arenas freed so far: %lu\n\n", ULSTAT(afree)); - } - - if (JS_WANT_GC_PER_COMPARTMENT_PRINT) - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - DumpCompartmentStats(*c, fp); - PodZero(&rt->globalArenaStats); - if (JS_WANT_CONSERVATIVE_GC_PRINT) - rt->gcStats.conservative.dump(fp); -#undef ULSTAT -} -#endif - -namespace js { #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS void @@ -328,20 +108,13 @@ GCMarker::dumpConservativeRoots() for (void **thingp = conservativeRoots.begin(); thingp != conservativeRoots.end(); ++thingp) { void *thing = thingp; fprintf(fp, " %p: ", thing); - - switch (GetGCThingTraceKind(thing)) { - default: - JS_NOT_REACHED("Unknown trace kind"); + switch (GetGCThingTraceKind(thing)) { case JSTRACE_OBJECT: { JSObject *obj = (JSObject *) thing; fprintf(fp, "object %s", obj->getClass()->name); break; } - case JSTRACE_SHAPE: { - fprintf(fp, "shape"); - break; - } case JSTRACE_STRING: { JSString *str = (JSString *) thing; if (str->isLinear()) { @@ -353,6 +126,22 @@ GCMarker::dumpConservativeRoots() } break; } + case JSTRACE_SCRIPT: { + fprintf(fp, "shape"); + break; + } + case JSTRACE_SHAPE: { + fprintf(fp, "shape"); + break; + } + case JSTRACE_BASE_SHAPE: { + fprintf(fp, "base_shape"); + break; + } + case JSTRACE_TYPE_OBJECT: { + fprintf(fp, "type_object"); + break; + } # if JS_HAS_XML_SUPPORT case JSTRACE_XML: { JSXML *xml = (JSXML *) thing; @@ -370,73 +159,4 @@ GCMarker::dumpConservativeRoots() } #endif /* JS_DUMP_CONSERVATIVE_GC_ROOTS */ -#ifdef MOZ_GCTIMER - -jsrefcount newChunkCount = 0; -jsrefcount destroyChunkCount = 0; - -GCTimer::GCTimer() { - getFirstEnter(); - memset(this, 0, sizeof(GCTimer)); - enter = PRMJ_Now(); -} - -uint64 -GCTimer::getFirstEnter() { - static uint64 firstEnter = PRMJ_Now(); - return firstEnter; -} - -#define TIMEDIFF(start, end) ((double)(end - start) / PRMJ_USEC_PER_MSEC) - -void -GCTimer::finish(bool lastGC) { - end = PRMJ_Now(); - - if (startMark > 0) { - if (JS_WANT_GC_SUITE_PRINT) { - fprintf(stderr, "%f %f %f\n", - TIMEDIFF(enter, end), - TIMEDIFF(startMark, startSweep), - TIMEDIFF(startSweep, sweepDestroyEnd)); - } else { - static FILE *gcFile; - - if (!gcFile) { - gcFile = fopen("gcTimer.dat", "a"); - - fprintf(gcFile, " AppTime, Total, Mark, Sweep, FinObj,"); - fprintf(gcFile, " FinStr, SwShapes, Destroy, +Chunks, -Chunks\n"); - } - JS_ASSERT(gcFile); - /* 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) { - fclose(gcFile); - gcFile = NULL; - } - } - } - newChunkCount = 0; - destroyChunkCount = 0; -} - -#undef TIMEDIFF - -#endif - -} //js - -#undef UL -#undef PERCENT +} /* namespace js */ diff --git a/deps/mozjs/js/src/jsgcstats.h b/deps/mozjs/js/src/jsgcstats.h index 6f661e53ec9..21560a18a29 100644 --- a/deps/mozjs/js/src/jsgcstats.h +++ b/deps/mozjs/js/src/jsgcstats.h @@ -39,139 +39,47 @@ #ifndef jsgcstats_h___ #define jsgcstats_h___ +#include "mozilla/Util.h" + #if !defined JS_DUMP_CONSERVATIVE_GC_ROOTS && defined DEBUG # define JS_DUMP_CONSERVATIVE_GC_ROOTS 1 #endif -/* Define JS_GCMETER here if wanted */ -#if defined JS_GCMETER -const bool JS_WANT_GC_METER_PRINT = true; -const bool JS_WANT_GC_PER_COMPARTMENT_PRINT = true; -const bool JS_WANT_CONSERVATIVE_GC_PRINT = true; -#elif defined DEBUG -# define JS_GCMETER 1 -const bool JS_WANT_GC_METER_PRINT = false; -const bool JS_WANT_GC_PER_COMPARTMENT_PRINT = false; -const bool JS_WANT_CONSERVATIVE_GC_PRINT = false; -#endif - namespace js { namespace gc { /* * The conservative GC test for a word shows that it is either a valid GC * thing or is not for one of the following reasons. */ -enum ConservativeGCTest { +enum ConservativeGCTest +{ CGCT_VALID, CGCT_LOWBITSET, /* excluded because one of the low bits was set */ CGCT_NOTARENA, /* not within arena range in a chunk */ + CGCT_OTHERCOMPARTMENT, /* in another compartment */ CGCT_NOTCHUNK, /* not within a valid chunk */ CGCT_FREEARENA, /* within arena containing only free things */ - CGCT_WRONGTAG, /* tagged pointer but wrong type */ CGCT_NOTLIVE, /* gcthing is not allocated */ CGCT_END }; -struct ConservativeGCStats { - uint32 counter[gc::CGCT_END]; /* ConservativeGCTest classification +struct ConservativeGCStats +{ + uint32_t counter[gc::CGCT_END]; /* ConservativeGCTest classification counters */ - uint32 unaligned; /* number of valid but not aligned on + uint32_t unaligned; /* number of valid but not aligned on thing start pointers */ void add(const ConservativeGCStats &another) { - for (size_t i = 0; i != JS_ARRAY_LENGTH(counter); ++i) + for (size_t i = 0; i < mozilla::ArrayLength(counter); ++i) counter[i] += another.counter[i]; } void dump(FILE *fp); }; -#ifdef JS_GCMETER -struct JSGCArenaStats { - uint32 alloc; /* allocation attempts */ - uint32 localalloc; /* allocations from local lists */ - uint32 nthings; /* live GC things */ - uint32 maxthings; /* maximum of live GC cells */ - double totalthings; /* live GC things the GC scanned so far */ - uint32 narenas; /* number of arena in list before the GC */ - uint32 newarenas; /* new arenas allocated before the last GC */ - uint32 livearenas; /* number of live arenas after the last GC */ - uint32 maxarenas; /* maximum of allocated arenas */ - uint32 totalarenas; /* total number of arenas with live things that - GC scanned so far */ -}; -#endif - -#ifdef JS_GCMETER - -struct JSGCStats { - uint32 lock; /* valid lock calls */ - uint32 unlock; /* valid unlock calls */ - uint32 unmarked; /* number of times marking of GC thing's children were - delayed due to a low C stack */ - uint32 lastditch; /* number of times the last ditch GC run */ - uint32 fail; /* allocation failures */ -#ifdef DEBUG - uint32 maxunmarked;/* maximum number of things with children to mark - later */ -#endif - uint32 poke; /* number of potentially useful GC calls */ - uint32 afree; /* thing arenas freed so far */ - uint32 nallarenas; /* number of all allocated arenas */ - uint32 maxnallarenas; /* maximum number of all allocated arenas */ - uint32 nchunks; /* number of allocated chunks */ - uint32 maxnchunks; /* maximum number of allocated chunks */ - - ConservativeGCStats conservative; -}; - -extern void -UpdateCompartmentStats(JSCompartment *comp, unsigned thingKind, uint32 nlivearenas, - uint32 nkilledArenas, uint32 nthings); -#endif /* JS_GCMETER */ - } //gc -#ifdef MOZ_GCTIMER - -const bool JS_WANT_GC_SUITE_PRINT = false; //false for gnuplot output - -extern jsrefcount newChunkCount; -extern jsrefcount destroyChunkCount; - -struct GCTimer { - uint64 enter; - uint64 startMark; - uint64 startSweep; - uint64 sweepObjectEnd; - uint64 sweepStringEnd; - uint64 sweepShapeEnd; - uint64 sweepDestroyEnd; - uint64 end; - - GCTimer(); - - uint64 getFirstEnter(); - - void finish(bool lastGC); -}; - -# define GCTIMER_PARAM , GCTimer &gcTimer -# define GCTIMER_ARG , gcTimer -# define TIMESTAMP(x) (gcTimer.x = PRMJ_Now()) -# define GCTIMER_BEGIN() GCTimer gcTimer -# define GCTIMER_END(last) (gcTimer.finish(last)) -#else -# define GCTIMER_PARAM -# define GCTIMER_ARG -# define TIMESTAMP(x) ((void) 0) -# define GCTIMER_BEGIN() ((void) 0) -# define GCTIMER_END(last) ((void) 0) -#endif - } //js -extern JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp); - #endif /* jsgcstats_h__ */ diff --git a/deps/mozjs/js/src/jshash.cpp b/deps/mozjs/js/src/jshash.cpp index ae2a3fcc17b..ff18dbde785 100644 --- a/deps/mozjs/js/src/jshash.cpp +++ b/deps/mozjs/js/src/jshash.cpp @@ -43,8 +43,6 @@ #include #include #include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" #include "jsutil.h" #include "jshash.h" @@ -97,7 +95,7 @@ static JSHashAllocOps defaultHashAllocOps = { }; JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, +JS_NewHashTable(uint32_t n, JSHashFunction keyHash, JSHashComparator keyCompare, JSHashComparator valueCompare, JSHashAllocOps *allocOps, void *allocPriv) { @@ -107,8 +105,8 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash, if (n <= MINBUCKETS) { n = MINBUCKETSLOG2; } else { - n = JS_CeilingLog2(n); - if ((int32)n < 0) + n = JS_CEILING_LOG2W(n); + if (int32_t(n) < 0) return NULL; } @@ -139,7 +137,7 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash, JS_PUBLIC_API(void) JS_HashTableDestroy(JSHashTable *ht) { - uint32 i, n; + uint32_t i, n; JSHashEntry *he, **hep; JSHashAllocOps *allocOps = ht->allocOps; void *allocPriv = ht->allocPriv; @@ -196,7 +194,7 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) } static JSBool -Resize(JSHashTable *ht, uint32 newshift) +Resize(JSHashTable *ht, uint32_t newshift) { size_t nb, nentries, i; JSHashEntry **oldbuckets, *he, *next, **hep; @@ -251,7 +249,7 @@ JS_PUBLIC_API(JSHashEntry *) JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, JSHashNumber keyHash, const void *key, void *value) { - uint32 n; + uint32_t n; JSHashEntry *he; /* Grow the table if it is overloaded */ @@ -303,7 +301,7 @@ JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) JS_PUBLIC_API(void) JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) { - uint32 n; + uint32_t n; *hep = he->next; ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); @@ -357,7 +355,7 @@ JS_PUBLIC_API(int) JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) { JSHashEntry *he, **hep, **bucket; - uint32 nlimit, n, nbuckets, newlog2; + uint32_t nlimit, n, nbuckets, newlog2; int rv; nlimit = ht->nentries; @@ -387,7 +385,7 @@ JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) JS_ASSERT(ht->nentries < nlimit); nbuckets = NBUCKETS(ht); if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { - newlog2 = JS_CeilingLog2(ht->nentries); + newlog2 = JS_CEILING_LOG2W(ht->nentries); if (newlog2 < MINBUCKETSLOG2) newlog2 = MINBUCKETSLOG2; @@ -406,8 +404,8 @@ JS_PUBLIC_API(void) JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) { double sqsum, mean, sigma; - uint32 nchains, nbuckets; - uint32 i, n, maxChain, maxChainLen; + uint32_t nchains, nbuckets; + uint32_t i, n, maxChain, maxChainLen; JSHashEntry *he; sqsum = 0; diff --git a/deps/mozjs/js/src/jshash.h b/deps/mozjs/js/src/jshash.h index a365a517866..e4df5217fe3 100644 --- a/deps/mozjs/js/src/jshash.h +++ b/deps/mozjs/js/src/jshash.h @@ -39,6 +39,7 @@ #ifndef jshash_h___ #define jshash_h___ + /* * API to portable hash table code. */ @@ -49,7 +50,7 @@ JS_BEGIN_EXTERN_C -typedef uint32 JSHashNumber; +typedef uint32_t JSHashNumber; typedef struct JSHashEntry JSHashEntry; typedef struct JSHashTable JSHashTable; @@ -84,18 +85,18 @@ struct JSHashEntry { struct JSHashTable { JSHashEntry **buckets; /* vector of hash buckets */ - uint32 nentries; /* number of entries in table */ - uint32 shift; /* multiplicative hash shift */ + uint32_t nentries; /* number of entries in table */ + uint32_t shift; /* multiplicative hash shift */ JSHashFunction keyHash; /* key hash function */ JSHashComparator keyCompare; /* key comparison function */ JSHashComparator valueCompare; /* value comparison function */ JSHashAllocOps *allocOps; /* allocation operations */ void *allocPriv; /* allocation private data */ #ifdef JS_HASHMETER - uint32 nlookups; /* total number of lookups */ - uint32 nsteps; /* number of hash chains traversed */ - uint32 ngrows; /* number of table expansions */ - uint32 nshrinks; /* number of table contractions */ + uint32_t nlookups; /* total number of lookups */ + uint32_t nsteps; /* number of hash chains traversed */ + uint32_t ngrows; /* number of table expansions */ + uint32_t nshrinks; /* number of table contractions */ #endif }; @@ -104,7 +105,7 @@ struct JSHashTable { * If allocOps is null, use default allocator ops built on top of malloc(). */ extern JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, +JS_NewHashTable(uint32_t n, JSHashFunction keyHash, JSHashComparator keyCompare, JSHashComparator valueCompare, JSHashAllocOps *allocOps, void *allocPriv); diff --git a/deps/mozjs/js/src/jshashtable.h b/deps/mozjs/js/src/jshashtable.h deleted file mode 100644 index feb603bc716..00000000000 --- a/deps/mozjs/js/src/jshashtable.h +++ /dev/null @@ -1,1220 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * November 13, 2009. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * - * Contributor(s): - * Brendan Eich (Original Author) - * Chris Waterson - * L. David Baron , Mozilla Corporation - * Luke Wagner - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jshashtable_h_ -#define jshashtable_h_ - -#include "jsalloc.h" -#include "jstl.h" - -namespace js { - -/* Integral types for all hash functions. */ -typedef uint32 HashNumber; - -/*****************************************************************************/ - -namespace detail { - -/* - * js::detail::HashTable is an implementation detail of the js::HashMap and - * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples, - * skip to the end of the detail namespace. - */ - -/* Reusable implementation of HashMap and HashSet. */ -template -class HashTable : private AllocPolicy -{ - typedef typename tl::StripConst::result NonConstT; - typedef typename HashPolicy::KeyType Key; - typedef typename HashPolicy::Lookup Lookup; - - /* - * T::operator= is a private operation for HashMap::Entry. HashMap::Entry - * makes HashTable a friend, but MSVC does not allow HashMap::Entry to make - * HashTable::Entry a friend. So do assignment here: - */ - static void assignT(NonConstT &dst, const T &src) { dst = src; } - - public: - class Entry { - HashNumber keyHash; - - public: - Entry() : keyHash(0), t() {} - void operator=(const Entry &rhs) { keyHash = rhs.keyHash; assignT(t, rhs.t); } - - NonConstT t; - - bool isFree() const { return keyHash == sFreeKey; } - void setFree() { keyHash = sFreeKey; assignT(t, T()); } - bool isRemoved() const { return keyHash == sRemovedKey; } - void setRemoved() { keyHash = sRemovedKey; assignT(t, T()); } - bool isLive() const { return isLiveHash(keyHash); } - void setLive(HashNumber hn) { JS_ASSERT(isLiveHash(hn)); keyHash = hn; } - - void setCollision() { JS_ASSERT(isLive()); keyHash |= sCollisionBit; } - void setCollision(HashNumber collisionBit) { - JS_ASSERT(isLive()); keyHash |= collisionBit; - } - void unsetCollision() { JS_ASSERT(isLive()); keyHash &= ~sCollisionBit; } - bool hasCollision() const { JS_ASSERT(isLive()); return keyHash & sCollisionBit; } - bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; } - HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; } - }; - - /* - * A nullable pointer to a hash table element. A Ptr |p| can be tested - * either explicitly |if (p.found()) p->...| or using boolean conversion - * |if (p) p->...|. Ptr objects must not be used after any mutating hash - * table operations unless |generation()| is tested. - */ - class Ptr - { - friend class HashTable; - typedef void (Ptr::* ConvertibleToBool)(); - void nonNull() {} - - Entry *entry; - - protected: - Ptr(Entry &entry) : entry(&entry) {} - - public: - /* Leaves Ptr uninitialized. */ - Ptr() { -#ifdef DEBUG - entry = (Entry *)0xbad; -#endif - } - - bool found() const { return entry->isLive(); } - operator ConvertibleToBool() const { return found() ? &Ptr::nonNull : 0; } - bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; } - bool operator!=(const Ptr &rhs) const { return !(*this == rhs); } - - T &operator*() const { return entry->t; } - T *operator->() const { return &entry->t; } - }; - - /* A Ptr that can be used to add a key after a failed lookup. */ - class AddPtr : public Ptr - { - friend class HashTable; - HashNumber keyHash; -#ifdef DEBUG - uint64 mutationCount; - - AddPtr(Entry &entry, HashNumber hn, uint64 mutationCount) - : Ptr(entry), keyHash(hn), mutationCount(mutationCount) {} -#else - AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {} -#endif - public: - /* Leaves AddPtr uninitialized. */ - AddPtr() {} - }; - - /* - * A collection of hash table entries. The collection is enumerated by - * calling |front()| followed by |popFront()| as long as |!empty()|. As - * with Ptr/AddPtr, Range objects must not be used after any mutating hash - * table operation unless the |generation()| is tested. - */ - class Range - { - protected: - friend class HashTable; - - Range(Entry *c, Entry *e) : cur(c), end(e) { - while (cur != end && !cur->isLive()) - ++cur; - } - - Entry *cur, *end; - - public: - bool empty() const { - return cur == end; - } - - T &front() const { - JS_ASSERT(!empty()); - return cur->t; - } - - void popFront() { - JS_ASSERT(!empty()); - while (++cur != end && !cur->isLive()); - } - }; - - /* - * A Range whose lifetime delimits a mutating enumeration of a hash table. - * Since rehashing when elements were removed during enumeration would be - * bad, it is postponed until |endEnumeration()| is called. If - * |endEnumeration()| is not called before an Enum's constructor, it will - * be called automatically. Since |endEnumeration()| touches the hash - * table, the user must ensure that the hash table is still alive when this - * happens. - */ - class Enum : public Range - { - friend class HashTable; - - HashTable &table; - bool removed; - - /* Not copyable. */ - Enum(const Enum &); - void operator=(const Enum &); - - public: - template explicit - Enum(Map &map) : Range(map.all()), table(map.impl), removed(false) {} - - /* - * Removes the |front()| element from the table, leaving |front()| - * invalid until the next call to |popFront()|. For example: - * - * HashSet s; - * for (HashSet::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - */ - void removeFront() { - table.remove(*this->cur); - removed = true; - } - - /* Potentially rehashes the table. */ - ~Enum() { - if (removed) - table.checkUnderloaded(); - } - - /* Can be used to end the enumeration before the destructor. */ - void endEnumeration() { - if (removed) { - table.checkUnderloaded(); - removed = false; - } - } - }; - - private: - uint32 hashShift; /* multiplicative hash shift */ - uint32 tableCapacity; /* = JS_BIT(sHashBits - hashShift) */ - uint32 entryCount; /* number of entries in table */ - uint32 gen; /* entry storage generation number */ - uint32 removedCount; /* removed entry sentinels in table */ - Entry *table; /* entry storage */ - - void setTableSizeLog2(unsigned sizeLog2) { - hashShift = sHashBits - sizeLog2; - tableCapacity = JS_BIT(sizeLog2); - } - -#ifdef DEBUG - mutable struct Stats { - uint32 searches; /* total number of table searches */ - uint32 steps; /* hash chain links traversed */ - uint32 hits; /* searches that found key */ - uint32 misses; /* searches that didn't find key */ - uint32 addOverRemoved; /* adds that recycled a removed entry */ - uint32 removes; /* calls to remove */ - uint32 removeFrees; /* calls to remove that freed the entry */ - uint32 grows; /* table expansions */ - uint32 shrinks; /* table contractions */ - uint32 compresses; /* table compressions */ - } stats; -# define METER(x) x -#else -# define METER(x) -#endif - -#ifdef DEBUG - friend class js::ReentrancyGuard; - mutable bool entered; - uint64 mutationCount; -#endif - - static const unsigned sMinSizeLog2 = 4; - static const unsigned sMinSize = 1 << sMinSizeLog2; - static const unsigned sSizeLimit = JS_BIT(24); - static const unsigned sHashBits = tl::BitSize::result; - static const uint8 sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */ - static const uint8 sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */ - static const uint8 sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */ - static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */ - static const HashNumber sCollisionBit = 1; - static const HashNumber sFreeKey = 0; - static const HashNumber sRemovedKey = 1; - - static bool isLiveHash(HashNumber hash) - { - return hash > sRemovedKey; - } - - static HashNumber prepareHash(const Lookup& l) - { - HashNumber keyHash = HashPolicy::hash(l); - - /* Improve keyHash distribution. */ - keyHash *= sGoldenRatio; - - /* Avoid reserved hash codes. */ - if (!isLiveHash(keyHash)) - keyHash -= (sRemovedKey + 1); - return keyHash & ~sCollisionBit; - } - - static Entry *createTable(AllocPolicy &alloc, uint32 capacity) - { - Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); - if (!newTable) - return NULL; - for (Entry *e = newTable, *end = e + capacity; e != end; ++e) - new(e) Entry(); - return newTable; - } - - static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32 capacity) - { - for (Entry *e = oldTable, *end = e + capacity; e != end; ++e) - e->~Entry(); - alloc.free_(oldTable); - } - - public: - HashTable(AllocPolicy ap) - : AllocPolicy(ap), - entryCount(0), - gen(0), - removedCount(0), - table(NULL) -#ifdef DEBUG - , entered(false), - mutationCount(0) -#endif - {} - - bool init(uint32 length) - { - /* Make sure that init isn't called twice. */ - JS_ASSERT(table == NULL); - - /* - * Correct for sMaxAlphaFrac such that the table will not resize - * when adding 'length' entries. - */ - JS_ASSERT(length < (uint32(1) << 23)); - uint32 capacity = (length * sInvMaxAlpha) >> 7; - - if (capacity < sMinSize) - capacity = sMinSize; - - /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */ - uint32 roundUp = sMinSize, roundUpLog2 = sMinSizeLog2; - while (roundUp < capacity) { - roundUp <<= 1; - ++roundUpLog2; - } - - capacity = roundUp; - if (capacity >= sSizeLimit) { - this->reportAllocOverflow(); - return false; - } - - table = createTable(*this, capacity); - if (!table) - return false; - - setTableSizeLog2(roundUpLog2); - METER(memset(&stats, 0, sizeof(stats))); - return true; - } - - bool initialized() const - { - return !!table; - } - - ~HashTable() - { - if (table) - destroyTable(*this, table, tableCapacity); - } - - private: - static HashNumber hash1(HashNumber hash0, uint32 shift) { - return hash0 >> shift; - } - - static HashNumber hash2(HashNumber hash0, uint32 log2, uint32 shift) { - return ((hash0 << log2) >> shift) | 1; - } - - bool overloaded() { - return entryCount + removedCount >= ((sMaxAlphaFrac * tableCapacity) >> 8); - } - - bool underloaded() { - return tableCapacity > sMinSize && - entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8); - } - - static bool match(Entry &e, const Lookup &l) { - return HashPolicy::match(HashPolicy::getKey(e.t), l); - } - - Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const - { - JS_ASSERT(isLiveHash(keyHash)); - JS_ASSERT(!(keyHash & sCollisionBit)); - JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit); - JS_ASSERT(table); - METER(stats.searches++); - - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); - Entry *entry = &table[h1]; - - /* Miss: return space for a new entry. */ - if (entry->isFree()) { - METER(stats.misses++); - return *entry; - } - - /* Hit: return entry. */ - if (entry->matchHash(keyHash) && match(*entry, l)) { - METER(stats.hits++); - return *entry; - } - - /* Collision: double hash. */ - unsigned sizeLog2 = sHashBits - hashShift; - HashNumber h2 = hash2(keyHash, sizeLog2, hashShift); - HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1; - - /* Save the first removed entry pointer so we can recycle later. */ - Entry *firstRemoved = NULL; - - while(true) { - if (JS_UNLIKELY(entry->isRemoved())) { - if (!firstRemoved) - firstRemoved = entry; - } else { - entry->setCollision(collisionBit); - } - - METER(stats.steps++); - h1 -= h2; - h1 &= sizeMask; - - entry = &table[h1]; - if (entry->isFree()) { - METER(stats.misses++); - return firstRemoved ? *firstRemoved : *entry; - } - - if (entry->matchHash(keyHash) && match(*entry, l)) { - METER(stats.hits++); - return *entry; - } - } - } - - /* - * This is a copy of lookup hardcoded to the assumptions: - * 1. the lookup is a lookupForAdd - * 2. the key, whose |keyHash| has been passed is not in the table, - * 3. no entries have been removed from the table. - * This specialized search avoids the need for recovering lookup values - * from entries, which allows more flexible Lookup/Key types. - */ - Entry &findFreeEntry(HashNumber keyHash) - { - METER(stats.searches++); - JS_ASSERT(!(keyHash & sCollisionBit)); - - /* N.B. the |keyHash| has already been distributed. */ - - /* Compute the primary hash address. */ - HashNumber h1 = hash1(keyHash, hashShift); - Entry *entry = &table[h1]; - - /* Miss: return space for a new entry. */ - if (entry->isFree()) { - METER(stats.misses++); - return *entry; - } - - /* Collision: double hash. */ - unsigned sizeLog2 = sHashBits - hashShift; - HashNumber h2 = hash2(keyHash, sizeLog2, hashShift); - HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1; - - while(true) { - JS_ASSERT(!entry->isRemoved()); - entry->setCollision(); - - METER(stats.steps++); - h1 -= h2; - h1 &= sizeMask; - - entry = &table[h1]; - if (entry->isFree()) { - METER(stats.misses++); - return *entry; - } - } - } - - bool changeTableSize(int deltaLog2) - { - /* Look, but don't touch, until we succeed in getting new entry store. */ - Entry *oldTable = table; - uint32 oldCap = tableCapacity; - uint32 newLog2 = sHashBits - hashShift + deltaLog2; - uint32 newCapacity = JS_BIT(newLog2); - if (newCapacity >= sSizeLimit) { - this->reportAllocOverflow(); - return false; - } - - Entry *newTable = createTable(*this, newCapacity); - if (!newTable) - return false; - - /* We can't fail from here on, so update table parameters. */ - setTableSizeLog2(newLog2); - removedCount = 0; - gen++; - table = newTable; - - /* Copy only live entries, leaving removed ones behind. */ - for (Entry *src = oldTable, *end = src + oldCap; src != end; ++src) { - if (src->isLive()) { - src->unsetCollision(); - findFreeEntry(src->getKeyHash()) = *src; - } - } - - destroyTable(*this, oldTable, oldCap); - return true; - } - - void remove(Entry &e) - { - METER(stats.removes++); - if (e.hasCollision()) { - e.setRemoved(); - removedCount++; - } else { - METER(stats.removeFrees++); - e.setFree(); - } - entryCount--; -#ifdef DEBUG - mutationCount++; -#endif - } - - void checkUnderloaded() - { - if (underloaded()) { - METER(stats.shrinks++); - (void) changeTableSize(-1); - } - } - - public: - void clear() - { - for (Entry *e = table, *end = table + tableCapacity; e != end; ++e) - *e = Entry(); - removedCount = 0; - entryCount = 0; -#ifdef DEBUG - mutationCount++; -#endif - } - - void finish() - { - JS_ASSERT(!entered); - - if (!table) - return; - - destroyTable(*this, table, tableCapacity); - table = NULL; - gen++; - entryCount = 0; - removedCount = 0; -#ifdef DEBUG - mutationCount++; -#endif - } - - Range all() const { - return Range(table, table + tableCapacity); - } - - bool empty() const { - return !entryCount; - } - - uint32 count() const{ - return entryCount; - } - - uint32 generation() const { - return gen; - } - - Ptr lookup(const Lookup &l) const { - ReentrancyGuard g(*this); - HashNumber keyHash = prepareHash(l); - return Ptr(lookup(l, keyHash, 0)); - } - - AddPtr lookupForAdd(const Lookup &l) const { - ReentrancyGuard g(*this); - HashNumber keyHash = prepareHash(l); - Entry &entry = lookup(l, keyHash, sCollisionBit); -#ifdef DEBUG - return AddPtr(entry, keyHash, mutationCount); -#else - return AddPtr(entry, keyHash); -#endif - } - - bool add(AddPtr &p) - { - ReentrancyGuard g(*this); - JS_ASSERT(mutationCount == p.mutationCount); - JS_ASSERT(table); - JS_ASSERT(!p.found()); - JS_ASSERT(!(p.keyHash & sCollisionBit)); - - /* - * Changing an entry from removed to live does not affect whether we - * are overloaded and can be handled separately. - */ - if (p.entry->isRemoved()) { - METER(stats.addOverRemoved++); - removedCount--; - p.keyHash |= sCollisionBit; - } else { - /* If alpha is >= .75, grow or compress the table. */ - if (overloaded()) { - /* Compress if a quarter or more of all entries are removed. */ - int deltaLog2; - if (removedCount >= (tableCapacity >> 2)) { - METER(stats.compresses++); - deltaLog2 = 0; - } else { - METER(stats.grows++); - deltaLog2 = 1; - } - - if (!changeTableSize(deltaLog2)) - return false; - - /* Preserve the validity of |p.entry|. */ - p.entry = &findFreeEntry(p.keyHash); - } - } - - p.entry->setLive(p.keyHash); - entryCount++; -#ifdef DEBUG - mutationCount++; -#endif - return true; - } - - /* - * There is an important contract between the caller and callee for this - * function: if add() returns true, the caller must assign the T value - * which produced p before using the hashtable again. - */ - bool add(AddPtr &p, T** pentry) - { - if (!add(p)) - return false; - *pentry = &p.entry->t; - return true; - } - - bool add(AddPtr &p, const T &t) - { - if (!add(p)) - return false; - p.entry->t = t; - return true; - } - - bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t) - { -#ifdef DEBUG - p.mutationCount = mutationCount; -#endif - { - ReentrancyGuard g(*this); - p.entry = &lookup(l, p.keyHash, sCollisionBit); - } - return p.found() || add(p, t); - } - - void remove(Ptr p) - { - ReentrancyGuard g(*this); - JS_ASSERT(p.found()); - remove(*p.entry); - checkUnderloaded(); - } -#undef METER -}; - -} /* namespace detail */ - -/*****************************************************************************/ - -/* - * Hash policy - * - * A hash policy P for a hash table with key-type Key must provide: - * - a type |P::Lookup| to use to lookup table entries; - * - a static member function |P::hash| with signature - * - * static js::HashNumber hash(Lookup) - * - * to use to hash the lookup type; and - * - a static member function |P::match| with signature - * - * static bool match(Key, Lookup) - * - * to use to test equality of key and lookup values. - * - * Normally, Lookup = Key. In general, though, different values and types of - * values can be used to lookup and store. If a Lookup value |l| is != to the - * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: - * - * js::HashSet::AddPtr p = h.lookup(l); - * if (!p) { - * assert(P::match(k, l)); // must hold - * h.add(p, k); - * } - */ - -/* Default hashing policies. */ -template -struct DefaultHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - /* Hash if can implicitly cast to hash number type. */ - return l; - } - static bool match(const Key &k, const Lookup &l) { - /* Use builtin or overloaded operator==. */ - return k == l; - } -}; - -/* - * Pointer hashing policy that strips the lowest zeroBits when calculating the - * hash to improve key distribution. - */ -template -struct PointerHasher -{ - typedef Key Lookup; - static HashNumber hash(const Lookup &l) { - size_t word = reinterpret_cast(l) >> zeroBits; - JS_STATIC_ASSERT(sizeof(HashNumber) == 4); -#if JS_BYTES_PER_WORD == 4 - return HashNumber(word); -#else - JS_STATIC_ASSERT(sizeof word == 8); - return HashNumber((word >> 32) ^ word); -#endif - } - static bool match(const Key &k, const Lookup &l) { - return k == l; - } -}; - -/* - * Specialized hashing policy for pointer types. It assumes that the type is - * at least word-aligned. For types with smaller size use PointerHasher. - */ -template -struct DefaultHasher: PointerHasher::result> { }; - -/* Looking for a hasher for jsid? Try the DefaultHasher in jsatom.h. */ - -/* - * JS-friendly, STL-like container providing a hash-based map from keys to - * values. In particular, HashMap calls constructors and destructors of all - * objects added so non-PODs may be used safely. - * - * Key/Value requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jstl.h (default js::ContextAllocPolicy) - * - * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members - * called by HashMap must not call back into the same HashMap object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template -class HashMap -{ - public: - typedef typename HashPolicy::Lookup Lookup; - - class Entry - { - template friend class detail::HashTable; - void operator=(const Entry &rhs) { - const_cast(key) = rhs.key; - value = rhs.value; - } - - public: - Entry() : key(), value() {} - Entry(const Key &k, const Value &v) : key(k), value(v) {} - - const Key key; - Value value; - }; - - private: - /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */ - struct MapHashPolicy : HashPolicy - { - typedef Key KeyType; - static const Key &getKey(Entry &e) { return e.key; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashMap(const HashMap &); - HashMap &operator=(const HashMap &); - - Impl impl; - - public: - /* - * HashMap construction is fallible (due to OOM); thus the user must call - * init after constructing a HashMap and check the return value. - */ - HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32 len = 0) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashMap HM; - * HM h; - * if (HM::Ptr p = h.lookup(3)) { - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * } - * - * Also see the definition of Ptr in HashTable above (with T = Entry). - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using - * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: - * - * typedef HashMap HM; - * HM h; - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; // p acts like a pointer to Entry - * assert(p->key == 3); // Entry contains the key - * char val = p->value; // and value - * - * Also see the definition of AddPtr in HashTable above (with T = Entry). - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HM::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 'a')) - * return false; - * } - * const HM::Entry &e = *p; - * assert(p->key == 3); - * char val = p->value; - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - bool add(AddPtr &p, const Key &k, const Value &v) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - pentry->value = v; - return true; - } - - bool add(AddPtr &p, const Key &k) { - Entry *pentry; - if (!impl.add(p, &pentry)) - return false; - const_cast(pentry->key) = k; - return true; - } - - bool relookupOrAdd(AddPtr &p, const Key &k, const Value &v) { - return impl.relookupOrAdd(p, k, Entry(k, v)); - } - - /* - * |all()| returns a Range containing |count()| elements. E.g.: - * - * typedef HashMap HM; - * HM h; - * for (HM::Range r = h.all(); !r.empty(); r.popFront()) - * char c = r.front().value; - * - * Also see the definition of Range in HashTable above (with T = Entry). - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - size_t count() const { return impl.count(); } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashMap HM; - * HM s; - * for (HM::Enum e(s); !e.empty(); e.popFront()) - * if (e.front().value == 'l') - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above (with T = Entry). - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The map must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashMap operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return NULL on oom. */ - Entry *put(const Key &k, const Value &v) { - AddPtr p = lookupForAdd(k); - if (p) { - p->value = v; - return &*p; - } - return add(p, k, v) ? &*p : NULL; - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const Key &k, const Value &v) { - AddPtr p = lookupForAdd(k); - JS_ASSERT(!p); - return add(p, k, v); - } - - /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */ - Ptr lookupWithDefault(const Key &k, const Value &defaultValue) { - AddPtr p = lookupForAdd(k); - if (p) - return p; - (void)add(p, k, defaultValue); /* p is left false-y on oom. */ - return p; - } - - /* Remove if present. */ - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } -}; - -/* - * JS-friendly, STL-like container providing a hash-based set of values. In - * particular, HashSet calls constructors and destructors of all objects added - * so non-PODs may be used safely. - * - * T requirements: - * - default constructible, copyable, destructible, assignable - * HashPolicy requirements: - * - see "Hash policy" above (default js::DefaultHasher) - * AllocPolicy: - * - see "Allocation policies" in jstl.h (default js::ContextAllocPolicy) - * - * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by - * HashSet must not call back into the same HashSet object. - * N.B: Due to the lack of exception handling, the user must call |init()|. - */ -template -class HashSet -{ - typedef typename HashPolicy::Lookup Lookup; - - /* Implement HashSet in terms of HashTable. */ - struct SetOps : HashPolicy { - typedef T KeyType; - static const KeyType &getKey(const T &t) { return t; } - }; - typedef detail::HashTable Impl; - - friend class Impl::Enum; - - /* Not implicitly copyable (expensive). May add explicit |clone| later. */ - HashSet(const HashSet &); - HashSet &operator=(const HashSet &); - - Impl impl; - - public: - /* - * HashSet construction is fallible (due to OOM); thus the user must call - * init after constructing a HashSet and check the return value. - */ - HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} - bool init(uint32 len = 0) { return impl.init(len); } - bool initialized() const { return impl.initialized(); } - - /* - * Return whether the given lookup value is present in the map. E.g.: - * - * typedef HashSet HS; - * HS h; - * if (HS::Ptr p = h.lookup(3)) { - * assert(*p == 3); // p acts like a pointer to int - * } - * - * Also see the definition of Ptr in HashTable above. - */ - typedef typename Impl::Ptr Ptr; - Ptr lookup(const Lookup &l) const { return impl.lookup(l); } - - /* Assuming |p.found()|, remove |*p|. */ - void remove(Ptr p) { impl.remove(p); } - - /* - * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient - * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using - * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.: - * - * typedef HashSet HS; - * HS h; - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * if (!h.add(p, 3)) - * return false; - * } - * assert(*p == 3); // p acts like a pointer to int - * - * Also see the definition of AddPtr in HashTable above. - * - * N.B. The caller must ensure that no mutating hash table operations - * occur between a pair of |lookupForAdd| and |add| calls. To avoid - * looking up the key a second time, the caller may use the more efficient - * relookupOrAdd method. This method reuses part of the hashing computation - * to more efficiently insert the key if it has not been added. For - * example, a mutation-handling version of the previous example: - * - * HS::AddPtr p = h.lookupForAdd(3); - * if (!p) { - * call_that_may_mutate_h(); - * if (!h.relookupOrAdd(p, 3, 3)) - * return false; - * } - * assert(*p == 3); - * - * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the - * entry t, where the caller ensures match(l,t). - */ - typedef typename Impl::AddPtr AddPtr; - AddPtr lookupForAdd(const Lookup &l) const { - return impl.lookupForAdd(l); - } - - bool add(AddPtr &p, const T &t) { - return impl.add(p, t); - } - - bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) { - return impl.relookupOrAdd(p, l, t); - } - - /* - * |all()| returns a Range containing |count()| elements: - * - * typedef HashSet HS; - * HS h; - * for (HS::Range r = h.all(); !r.empty(); r.popFront()) - * int i = r.front(); - * - * Also see the definition of Range in HashTable above. - */ - typedef typename Impl::Range Range; - Range all() const { return impl.all(); } - size_t count() const { return impl.count(); } - - /* - * Typedef for the enumeration class. An Enum may be used to examine and - * remove table entries: - * - * typedef HashSet HS; - * HS s; - * for (HS::Enum e(s); !e.empty(); e.popFront()) - * if (e.front() == 42) - * e.removeFront(); - * - * Table resize may occur in Enum's destructor. Also see the definition of - * Enum in HashTable above. - */ - typedef typename Impl::Enum Enum; - - /* - * Remove all entries. This does not shrink the table. For that consider - * using the finish() method. - */ - void clear() { impl.clear(); } - - /* - * Remove all the entries and release all internal buffers. The set must - * be initialized again before any use. - */ - void finish() { impl.finish(); } - - /* Does the table contain any entries? */ - bool empty() const { return impl.empty(); } - - /* - * If |generation()| is the same before and after a HashSet operation, - * pointers into the table remain valid. - */ - unsigned generation() const { return impl.generation(); } - - /* Shorthand operations: */ - - bool has(const Lookup &l) const { - return impl.lookup(l) != NULL; - } - - /* Overwrite existing value with v. Return NULL on oom. */ - const T *put(const T &t) { - AddPtr p = lookupForAdd(t); - return p ? &*p : (add(p, t) ? &*p : NULL); - } - - /* Like put, but assert that the given key is not already present. */ - bool putNew(const T &t) { - AddPtr p = lookupForAdd(t); - JS_ASSERT(!p); - return add(p, t); - } - - void remove(const Lookup &l) { - if (Ptr p = lookup(l)) - remove(p); - } -}; - -} /* namespace js */ - -#endif diff --git a/deps/mozjs/js/src/jshotloop.h b/deps/mozjs/js/src/jshotloop.h deleted file mode 100644 index a47ae76eb56..00000000000 --- a/deps/mozjs/js/src/jshotloop.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Mozilla SpiderMonkey JaegerMonkey implementation - * - * The Initial Developer of the Original Code is - * Mozilla Foundation - * Portions created by the Initial Developer are Copyright (C) 2002-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jshotloop_h___ -#define jshotloop_h___ - -#include "jscntxt.h" - -namespace js { - -uint32 -GetHotloop(JSContext *cx); - -} - -#endif diff --git a/deps/mozjs/js/src/jsify.pl b/deps/mozjs/js/src/jsify.pl deleted file mode 100644 index 0ce2186935d..00000000000 --- a/deps/mozjs/js/src/jsify.pl +++ /dev/null @@ -1,483 +0,0 @@ -#!/usr/local/bin/perl - -# This script modifies C code to use the hijacked NSPR routines that are -# now baked into the JavaScript engine rather than using the NSPR -# routines that they were based on, i.e. types like PRArenaPool are changed -# to JSArenaPool. -# -# This script was used in 9/98 to facilitate the incorporation of some NSPR -# code into the JS engine so as to minimize dependency on NSPR. -# - -# Command-line: jsify.pl [options] [filename]* -# -# Options: -# -r Reverse direction of transformation, i.e. JS ==> NSPR2 -# -outdir Directory in which to place output files - - -# NSPR2 symbols that will be modified to JS symbols, e.g. -# PRArena <==> JSArena - -@NSPR_symbols = ( -"PRArena", -"PRArenaPool", -"PRArenaStats", -"PR_ARENAMETER", -"PR_ARENA_", -"PR_ARENA_ALIGN", -"PR_ARENA_ALLOCATE", -"PR_ARENA_CONST_ALIGN_MASK", -"PR_ARENA_DEFAULT_ALIGN", -"PR_ARENA_DESTROY", -"PR_ARENA_GROW", -"PR_ARENA_MARK", -"PR_ARENA_RELEASE", - -"PR_smprintf", -"PR_smprintf_free", -"PR_snprintf", -"PR_sprintf_append", -"PR_sscanf", -"PR_sxprintf", -"PR_vsmprintf", -"PR_vsnprintf", -"PR_vsprintf_append", -"PR_vsxprintf", - -"PRCList", -"PRCListStr", -"PRCLists", - -"PRDestroyEventProc", -"PREvent", -"PREventFunProc", -"PREventQueue", -"PRHandleEventProc", -"PR_PostEvent", -"PR_PostSynchronousEvent", -"PR_ProcessPendingEvents", -"PR_CreateEventQueue", -"PR_DequeueEvent", -"PR_DestroyEvent", -"PR_DestroyEventQueue", -"PR_EventAvailable", -"PR_EventLoop", -"PR_GetEvent", -"PR_GetEventOwner", -"PR_GetEventQueueMonitor", -"PR_GetEventQueueSelectFD", -"PR_GetMainEventQueue", -"PR_HandleEvent", -"PR_InitEvent", -"PR_ENTER_EVENT_QUEUE_MONITOR", -"PR_EXIT_EVENT_QUEUE_MONITOR", -"PR_MapEvents", -"PR_RevokeEvents", - -"PR_cnvtf", -"PR_dtoa", -"PR_strtod", - -"PRFileDesc", - -"PR_HASH_BITS", -"PR_GOLDEN_RATIO", -"PRHashAllocOps", -"PRHashComparator", -"PRHashEntry", -"PRHashEnumerator", -"PRHashFunction", -"PRHashNumber", -"PRHashTable", -"PR_HashString", -"PR_HashTableAdd", -"PR_HashTableDestroy", -"PR_HashTableDump", -"PR_HashTableEnumerateEntries", -"PR_HashTableLookup", -"PR_HashTableRawAdd", -"PR_HashTableRawLookup", -"PR_HashTableRawRemove", -"PR_HashTableRemove", - -"PRBool", -"PRFloat64", -"PRInt16", -"PRInt32", -"PRInt64", -"PRInt8", -"PRIntn", -"PRUint16", -"PRUint32", -"PRUint64", -"PRUint8", -"PRUintn", -"PRPtrDiff", -"PRPtrdiff", -"PRUptrdiff", -"PRUword", -"PRWord", -"PRPackedBool", -"PRSize", -"PRStatus", -"pruword", -"prword", -"prword_t", - -"PR_ALIGN_OF_DOUBLE", -"PR_ALIGN_OF_FLOAT", -"PR_ALIGN_OF_INT", -"PR_ALIGN_OF_INT64", -"PR_ALIGN_OF_LONG", -"PR_ALIGN_OF_POINTER", -"PR_ALIGN_OF_SHORT", -"PR_ALIGN_OF_WORD", -"PR_BITS_PER_BYTE", -"PR_BITS_PER_BYTE_LOG2", -"PR_BITS_PER_DOUBLE", -"PR_BITS_PER_DOUBLE_LOG2", -"PR_BITS_PER_FLOAT", -"PR_BITS_PER_FLOAT_LOG2", -"PR_BITS_PER_INT", -"PR_BITS_PER_INT64", -"PR_BITS_PER_INT64_LOG2", -"PR_BITS_PER_INT_LOG2", -"PR_BITS_PER_LONG", -"PR_BITS_PER_LONG_LOG2", -"PR_BITS_PER_SHORT", -"PR_BITS_PER_SHORT_LOG2", -"PR_BITS_PER_WORD", -"PR_BITS_PER_WORD_LOG2", -"PR_BYTES_PER_BYTE", -"PR_BYTES_PER_DOUBLE", -"PR_BYTES_PER_DWORD", -"PR_BYTES_PER_DWORD_LOG2", -"PR_BYTES_PER_FLOAT", -"PR_BYTES_PER_INT", -"PR_BYTES_PER_INT64", -"PR_BYTES_PER_LONG", -"PR_BYTES_PER_SHORT", -"PR_BYTES_PER_WORD", -"PR_BYTES_PER_WORD_LOG2", - -"PRSegment", -"PRSegmentAccess", -"PRStuffFunc", -"PRThread", - -"PR_APPEND_LINK", - -"PR_ASSERT", - -"PR_ATOMIC_DWORD_LOAD", -"PR_ATOMIC_DWORD_STORE", - -"PR_Abort", - -"PR_ArenaAllocate", -"PR_ArenaCountAllocation", -"PR_ArenaCountGrowth", -"PR_ArenaCountInplaceGrowth", -"PR_ArenaCountRelease", -"PR_ArenaCountRetract", -"PR_ArenaFinish", -"PR_ArenaGrow", -"PR_ArenaRelease", -"PR_CompactArenaPool", -"PR_DumpArenaStats", -"PR_FinishArenaPool", -"PR_FreeArenaPool", -"PR_InitArenaPool", - -"PR_Assert", - -"PR_AttachThread", - -"PR_BEGIN_EXTERN_C", -"PR_BEGIN_MACRO", - -"PR_BIT", -"PR_BITMASK", - -"PR_BUFFER_OVERFLOW_ERROR", - -"PR_CALLBACK", -"PR_CALLBACK_DECL", -"PR_CALLOC", -"PR_CEILING_LOG2", -"PR_CLEAR_ARENA", -"PR_CLEAR_BIT", -"PR_CLEAR_UNUSED", -"PR_CLIST_IS_EMPTY", -"PR_COUNT_ARENA", -"PR_CURRENT_THREAD", - -"PR_GetSegmentAccess", -"PR_GetSegmentSize", -"PR_GetSegmentVaddr", -"PR_GrowSegment", -"PR_DestroySegment", -"PR_MapSegment", -"PR_NewSegment", -"PR_Segment", -"PR_Seg", -"PR_SEGMENT_NONE", -"PR_SEGMENT_RDONLY", -"PR_SEGMENT_RDWR", - -"PR_Calloc", -"PR_CeilingLog2", -"PR_CompareStrings", -"PR_CompareValues", -"PR_DELETE", -"PR_END_EXTERN_C", -"PR_END_MACRO", -"PR_ENUMERATE_STOP", -"PR_FAILURE", -"PR_FALSE", -"PR_FLOOR_LOG2", -"PR_FREEIF", -"PR_FREE_PATTERN", -"PR_FloorLog2", -"PR_FormatTime", -"PR_Free", - -"PR_GetEnv", -"PR_GetError", -"PR_INIT_ARENA_POOL", -"PR_INIT_CLIST", -"PR_INIT_STATIC_CLIST", -"PR_INLINE", -"PR_INSERT_AFTER", -"PR_INSERT_BEFORE", -"PR_INSERT_LINK", -"PR_INT32", -"PR_INTERVAL_NO_TIMEOUT", -"PR_INTERVAL_NO_WAIT", -"PR_Init", -"PR_LIST_HEAD", -"PR_LIST_TAIL", -"PR_LOG", -"PR_LOGGING", -"PR_LOG_ALWAYS", -"PR_LOG_BEGIN", -"PR_LOG_DEBUG", -"PR_LOG_DEFINE", -"PR_LOG_END", -"PR_LOG_ERROR", -"PR_LOG_MAX", -"PR_LOG_MIN", -"PR_LOG_NONE", -"PR_LOG_NOTICE", -"PR_LOG_TEST", -"PR_LOG_WARN", -"PR_LOG_WARNING", -"PR_LogFlush", -"PR_LogPrint", -"PR_MALLOC", -"PR_MAX", -"PR_MD_calloc", -"PR_MD_free", -"PR_MD_malloc", -"PR_MD_realloc", -"PR_MIN", -"PR_Malloc", -"PR_NEW", -"PR_NEWZAP", -"PR_NEXT_LINK", -"PR_NOT_REACHED", -"PR_NewCondVar", -"PR_NewHashTable", -"PR_NewLogModule", -"PR_PREV_LINK", -"PR_PUBLIC_API", -"PR_PUBLIC_DATA", -"PR_RANGE_ERROR", -"PR_REALLOC", -"PR_REMOVE_AND_INIT_LINK", -"PR_REMOVE_LINK", -"PR_ROUNDUP", -"PR_Realloc", - -"PR_SET_BIT", -"PR_STATIC_CALLBACK", -"PR_SUCCESS", -"PR_SetError", -"PR_SetLogBuffering", -"PR_SetLogFile", - -"PR_TEST_BIT", -"PR_TRUE", -"PR_UINT32", -"PR_UPTRDIFF", - -"prarena_h___", -"prbit_h___", -"prclist_h___", -"prdtoa_h___", -"prlog_h___", -"prlong_h___", -"prmacos_h___", -"prmem_h___", -"prprf_h___", -"prtypes_h___", - -"prarena", -"prbit", -"prbitmap_t", -"prclist", -"prcpucfg", -"prdtoa", -"prhash", -"plhash", -"prlong", -"prmacos", -"prmem", -"prosdep", -"protypes", -"prprf", -"prtypes" -); - -while ($ARGV[0] =~ /^-/) { - if ($ARGV[0] eq "-r") { - shift; - $reverse_conversion = 1; - } elsif ($ARGV[0] eq "-outdir") { - shift; - $outdir = shift; - } -} - -# Given an NSPR symbol compute the JS equivalent or -# vice-versa -sub subst { - local ($replacement); - local ($sym) = @_; - - $replacement = substr($sym,0,2) eq "pr" ? "js" : "JS"; - $replacement .= substr($sym, 2); - return $replacement; -} - -# Build the regular expression that will convert between the NSPR -# types and the JS types -if ($reverse_conversion) { - die "Not implemented yet"; -} else { - foreach $sym (@NSPR_symbols) { - $regexp .= $sym . "|" - } - # Get rid of the last "!" - chop $regexp; - - # Replace PR* with JS* and replace pr* with js* - $regexp = 's/(^|\\W)(' . $regexp . ')/$1 . &subst($2)/eg'; -# print $regexp; -} - -# Pre-compile a little subroutine to perform the regexp substitution -# between NSPR types and JS types -eval('sub convert_from_NSPR {($line) = @_; $line =~ ' . $regexp . ';}'); - -sub convert_mallocs { - ($line) = @_; - $line =~ s/PR_MALLOC/malloc/g; - $line =~ s/PR_REALLOC/realloc/g; - $line =~ s/PR_FREE/free/g; - return $line; -} - -sub convert_includes { - ($line) = @_; - if ($line !~ /include/) { - return $line; - } - - if ($line =~ /prlog\.h/) { - $line = '#include "jsutil.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /plhash\.h/) { - $line = '#include "jshash.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /plarena\.h/) { - $line = '#include "jsarena.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /prmem\.h/) { - $line = ""; - } elsif ($line =~ /jsmsg\.def/) { - $line = '#include "js.msg"' . "\n"; - } elsif ($line =~ /shellmsg\.def/) { - $line = '#include "jsshell.msg"' . "\n"; - } elsif ($line =~ /jsopcode\.def/) { - $line = '#include "jsopcode.tbl"' . "\n"; - } - return $line; -} - -sub convert_declarations { - ($line) = @_; - $line =~ s/PR_EXTERN/JS_EXTERN_API/g; - $line =~ s/PR_IMPLEMENT_DATA/JS_EXPORT_DATA/g; - $line =~ s/PR_IMPLEMENT/JS_EXPORT_API/g; - $line =~ s/PR_IMPORT/JS_IMPORT/g; - $line =~ s/PR_PUBLIC_API/JS_EXPORT_API/g; - $line =~ s/PR_PUBLIC_DATA/JS_EXPORT_DATA/g; - return $line; -} - -sub convert_long_long_macros { - ($line) = @_; - $line =~ s/\b(LL_)/JSLL_/g; - return $line; -} - -sub convert_asserts { - ($line) = @_; - $line =~ s/\bPR_ASSERT/JS_ASSERT/g; - return $line; -} - -while ($#ARGV >= 0) { - $infile = shift; - - # Change filename, e.g. prtime.h to jsprtime.h, except for legacy - # files that start with 'prmj', like prmjtime.h. - $outfile = $infile; - if ($infile !~ /^prmj/) { - $outfile =~ s/^pr/js/; - $outfile =~ s/^pl/js/; - } - - if ($outdir) { - $outfile = $outdir . '/' . $outfile; - } - - if ($infile eq $outfile) { - die "Error: refuse to overwrite $outfile, use -outdir option." - } - die "Can't open $infile" if !open(INFILE, "<$infile"); - die "Can't open $outfile for writing" if !open(OUTFILE, ">$outfile"); - - while () { - $line = $_; - - #Get rid of #include "prlog.h" - &convert_includes($line); - - # Rename PR_EXTERN, PR_IMPORT, etc. - &convert_declarations($line); - - # Convert from PR_MALLOC to malloc, etc. - &convert_mallocs($line); - - # Convert from PR_ASSERT to JS_ASSERT -# &convert_asserts($line); - - # Convert from, e.g. PRArena to JSPRArena - &convert_from_NSPR($line); - - # Change LL_* macros to JSLL_* - &convert_long_long_macros($line); - - print OUTFILE $line; - } -} diff --git a/deps/mozjs/js/src/jsinfer.cpp b/deps/mozjs/js/src/jsinfer.cpp new file mode 100644 index 00000000000..6b7c1fb075b --- /dev/null +++ b/deps/mozjs/js/src/jsinfer.cpp @@ -0,0 +1,6395 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */ +/* vim: set ts=40 sw=4 et tw=99: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SpiderMonkey bytecode type inference + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Hackett + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jsautooplen.h" +#include "jsbool.h" +#include "jsdate.h" +#include "jsexn.h" +#include "jsfriendapi.h" +#include "jsgc.h" +#include "jsgcmark.h" +#include "jsinfer.h" +#include "jsmath.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsscript.h" +#include "jscntxt.h" +#include "jsscope.h" +#include "jsstr.h" +#include "jsiter.h" + +#include "frontend/TokenStream.h" +#include "js/MemoryMetrics.h" +#include "methodjit/MethodJIT.h" +#include "methodjit/Retcon.h" +#ifdef JS_METHODJIT +# include "assembler/assembler/MacroAssembler.h" +#endif + +#include "jsatominlines.h" +#include "jsgcinlines.h" +#include "jsinferinlines.h" +#include "jsobjinlines.h" +#include "jsscriptinlines.h" +#include "vm/Stack-inl.h" + +#ifdef JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#ifdef __SUNPRO_CC +#include +#endif + +using namespace js; +using namespace js::types; +using namespace js::analyze; + +static inline jsid +id_prototype(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); +} + +static inline jsid +id_arguments(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); +} + +static inline jsid +id_length(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); +} + +static inline jsid +id___proto__(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.protoAtom); +} + +static inline jsid +id_constructor(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.constructorAtom); +} + +static inline jsid +id_caller(JSContext *cx) { + return ATOM_TO_JSID(cx->runtime->atomState.callerAtom); +} + +static inline jsid +id_toString(JSContext *cx) +{ + return ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); +} + +static inline jsid +id_toSource(JSContext *cx) +{ + return ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom); +} + +#ifdef DEBUG +const char * +types::TypeIdStringImpl(jsid id) +{ + if (JSID_IS_VOID(id)) + return "(index)"; + if (JSID_IS_EMPTY(id)) + return "(new)"; + static char bufs[4][100]; + static unsigned which = 0; + which = (which + 1) & 3; + PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0); + return bufs[which]; +} +#endif + +///////////////////////////////////////////////////////////////////// +// Logging +///////////////////////////////////////////////////////////////////// + +static bool InferSpewActive(SpewChannel channel) +{ + static bool active[SPEW_COUNT]; + static bool checked = false; + if (!checked) { + checked = true; + PodArrayZero(active); + const char *env = getenv("INFERFLAGS"); + if (!env) + return false; + if (strstr(env, "ops")) + active[ISpewOps] = true; + if (strstr(env, "result")) + active[ISpewResult] = true; + if (strstr(env, "full")) { + for (unsigned i = 0; i < SPEW_COUNT; i++) + active[i] = true; + } + } + return active[channel]; +} + +#ifdef DEBUG + +static bool InferSpewColorable() +{ + /* Only spew colors on xterm-color to not screw up emacs. */ + const char *env = getenv("TERM"); + if (!env) + return false; + return strcmp(env, "xterm-color") == 0; +} + +const char * +types::InferSpewColorReset() +{ + if (!InferSpewColorable()) + return ""; + return "\x1b[0m"; +} + +const char * +types::InferSpewColor(TypeConstraint *constraint) +{ + /* Type constraints are printed out using foreground colors. */ + static const char *colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m", + "\x1b[34m", "\x1b[35m", "\x1b[36m", + "\x1b[37m" }; + if (!InferSpewColorable()) + return ""; + return colors[DefaultHasher::hash(constraint) % 7]; +} + +const char * +types::InferSpewColor(TypeSet *types) +{ + /* Type sets are printed out using bold colors. */ + static const char *colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m", + "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m", + "\x1b[1;37m" }; + if (!InferSpewColorable()) + return ""; + return colors[DefaultHasher::hash(types) % 7]; +} + +const char * +types::TypeString(Type type) +{ + if (type.isPrimitive()) { + switch (type.primitive()) { + case JSVAL_TYPE_UNDEFINED: + return "void"; + case JSVAL_TYPE_NULL: + return "null"; + case JSVAL_TYPE_BOOLEAN: + return "bool"; + case JSVAL_TYPE_INT32: + return "int"; + case JSVAL_TYPE_DOUBLE: + return "float"; + case JSVAL_TYPE_STRING: + return "string"; + case JSVAL_TYPE_MAGIC: + return "lazyargs"; + default: + JS_NOT_REACHED("Bad type"); + return ""; + } + } + if (type.isUnknown()) + return "unknown"; + if (type.isAnyObject()) + return " object"; + + static char bufs[4][40]; + static unsigned which = 0; + which = (which + 1) & 3; + + if (type.isSingleObject()) + JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject()); + else + JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject()); + + return bufs[which]; +} + +const char * +types::TypeObjectString(TypeObject *type) +{ + return TypeString(Type::ObjectType(type)); +} + +unsigned JSScript::id() { + if (!id_) { + id_ = ++compartment()->types.scriptCount; + InferSpew(ISpewOps, "script #%u: %p %s:%d", + id_, this, filename ? filename : "", lineno); + } + return id_; +} + +void +types::InferSpew(SpewChannel channel, const char *fmt, ...) +{ + if (!InferSpewActive(channel)) + return; + + va_list ap; + va_start(ap, fmt); + fprintf(stdout, "[infer] "); + vfprintf(stdout, fmt, ap); + fprintf(stdout, "\n"); + va_end(ap); +} + +bool +types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value) +{ + /* + * Check the correctness of the type information in the object's property + * against an actual value. + */ + if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) { + id = MakeTypeId(cx, id); + + /* Watch for properties which inference does not monitor. */ + if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) + return true; + + /* + * If we called in here while resolving a type constraint, we may be in the + * middle of resolving a standard class and the type sets will not be updated + * until the outer TypeSet::add finishes. + */ + if (cx->compartment->types.pendingCount) + return true; + + Type type = GetValueType(cx, value); + + AutoEnterTypeInference enter(cx); + + /* + * We don't track types for properties inherited from prototypes which + * haven't yet been accessed during analysis of the inheriting object. + * Don't do the property instantiation now. + */ + TypeSet *types = obj->maybeGetProperty(cx, id); + if (!types) + return true; + + /* + * If the types inherited from prototypes are not being propagated into + * this set (because we haven't analyzed code which accesses the + * property), skip. + */ + if (!types->hasPropagatedProperty()) + return true; + + if (!types->hasType(type)) { + TypeFailure(cx, "Missing type in object %s %s: %s", + TypeObjectString(obj), TypeIdString(id), TypeString(type)); + } + } + return true; +} + +#endif + +void +types::TypeFailure(JSContext *cx, const char *fmt, ...) +{ + char msgbuf[1024]; /* Larger error messages will be truncated */ + char errbuf[1024]; + + va_list ap; + va_start(ap, fmt); + JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap); + va_end(ap); + + JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf); + + /* Dump type state, even if INFERFLAGS is unset. */ + cx->compartment->types.print(cx, true); + + /* Always active, even in release builds */ + MOZ_Assert(msgbuf, __FILE__, __LINE__); + + *((volatile int *)NULL) = 0; /* Should never be reached */ +} + +///////////////////////////////////////////////////////////////////// +// TypeSet +///////////////////////////////////////////////////////////////////// + +TypeSet * +TypeSet::make(JSContext *cx, const char *name) +{ + JS_ASSERT(cx->compartment->activeInference); + + TypeSet *res = cx->typeLifoAlloc().new_(); + if (!res) { + cx->compartment->types.setPendingNukeTypes(cx); + return NULL; + } + + InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s", + InferSpewColor(res), res, InferSpewColorReset(), + name); + + return res; +} + +inline void +TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting) +{ + if (!constraint) { + /* OOM failure while constructing the constraint. */ + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + JS_ASSERT(cx->compartment->activeInference); + + InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s", + InferSpewColor(this), this, InferSpewColorReset(), + InferSpewColor(constraint), constraint, InferSpewColorReset(), + constraint->kind()); + + JS_ASSERT(constraint->next == NULL); + constraint->next = constraintList; + constraintList = constraint; + + if (!callExisting) + return; + + /* If any type is possible, there's no need to worry about specifics. */ + if (flags & TYPE_FLAG_UNKNOWN) { + cx->compartment->types.addPending(cx, constraint, this, Type::UnknownType()); + } else { + /* Enqueue type set members stored as bits. */ + for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) { + if (flags & flag) { + Type type = Type::PrimitiveType(TypeFlagPrimitive(flag)); + cx->compartment->types.addPending(cx, constraint, this, type); + } + } + + /* If any object is possible, skip specifics. */ + if (flags & TYPE_FLAG_ANYOBJECT) { + cx->compartment->types.addPending(cx, constraint, this, Type::AnyObjectType()); + } else { + /* Enqueue specific object types. */ + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObjectKey *object = getObject(i); + if (object) + cx->compartment->types.addPending(cx, constraint, this, + Type::ObjectType(object)); + } + } + } + + cx->compartment->types.resolvePending(cx); +} + +void +TypeSet::print(JSContext *cx) +{ + if (flags & TYPE_FLAG_OWN_PROPERTY) + printf(" [own]"); + if (flags & TYPE_FLAG_CONFIGURED_PROPERTY) + printf(" [configured]"); + + if (isDefiniteProperty()) + printf(" [definite:%d]", definiteSlot()); + + if (baseFlags() == 0 && !baseObjectCount()) { + printf(" missing"); + return; + } + + if (flags & TYPE_FLAG_UNKNOWN) + printf(" unknown"); + if (flags & TYPE_FLAG_ANYOBJECT) + printf(" object"); + + if (flags & TYPE_FLAG_UNDEFINED) + printf(" void"); + if (flags & TYPE_FLAG_NULL) + printf(" null"); + if (flags & TYPE_FLAG_BOOLEAN) + printf(" bool"); + if (flags & TYPE_FLAG_INT32) + printf(" int"); + if (flags & TYPE_FLAG_DOUBLE) + printf(" float"); + if (flags & TYPE_FLAG_STRING) + printf(" string"); + if (flags & TYPE_FLAG_LAZYARGS) + printf(" lazyargs"); + + uint32_t objectCount = baseObjectCount(); + if (objectCount) { + printf(" object[%u]", objectCount); + + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObjectKey *object = getObject(i); + if (object) + printf(" %s", TypeString(Type::ObjectType(object))); + } + } +} + +bool +TypeSet::propertyNeedsBarrier(JSContext *cx, jsid id) +{ + id = MakeTypeId(cx, id); + + if (unknownObject()) + return true; + + for (unsigned i = 0; i < getObjectCount(); i++) { + if (getSingleObject(i)) + return true; + + if (types::TypeObject *otype = getTypeObject(i)) { + if (otype->unknownProperties()) + return true; + + if (types::TypeSet *propTypes = otype->maybeGetProperty(cx, id)) { + if (propTypes->needsBarrier(cx)) + return true; + } + } + } + + addFreeze(cx); + return false; +} + +///////////////////////////////////////////////////////////////////// +// TypeSet constraints +///////////////////////////////////////////////////////////////////// + +/* Standard subset constraint, propagate all types from one set to another. */ +class TypeConstraintSubset : public TypeConstraint +{ +public: + TypeSet *target; + + TypeConstraintSubset(TypeSet *target) + : TypeConstraint("subset"), target(target) + { + JS_ASSERT(target); + } + + void newType(JSContext *cx, TypeSet *source, Type type) + { + /* Basic subset constraint, move all types to the target. */ + target->addType(cx, type); + } +}; + +void +TypeSet::addSubset(JSContext *cx, TypeSet *target) +{ + add(cx, cx->typeLifoAlloc().new_(target)); +} + +/* Constraints for reads/writes on object properties. */ +class TypeConstraintProp : public TypeConstraint +{ +public: + JSScript *script; + jsbytecode *pc; + + /* + * If assign is true, the target is used to update a property of the object. + * If assign is false, the target is assigned the value of the property. + */ + bool assign; + TypeSet *target; + + /* Property being accessed. */ + jsid id; + + TypeConstraintProp(JSScript *script, jsbytecode *pc, + TypeSet *target, jsid id, bool assign) + : TypeConstraint("prop"), script(script), pc(pc), + assign(assign), target(target), id(id) + { + JS_ASSERT(script && pc && target); + } + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *target, jsid id) +{ + add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, false)); +} + +void +TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *target, jsid id) +{ + add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, true)); +} + +/* + * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM. + * These are derived from the types on the properties themselves, rather than + * those pushed in the 'this' slot at the call site, which allows us to retain + * correlations between the type of the 'this' object and the associated + * callee scripts at polymorphic call sites. + */ +class TypeConstraintCallProp : public TypeConstraint +{ +public: + JSScript *script; + jsbytecode *callpc; + + /* Property being accessed. */ + jsid id; + + TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id) + : TypeConstraint("callprop"), script(script), callpc(callpc), id(id) + { + JS_ASSERT(script && callpc); + } + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id) +{ + /* + * For calls which will go through JSOP_NEW, don't add any constraints to + * modify the 'this' types of callees. The initial 'this' value will be + * outright ignored. + */ + jsbytecode *callpc = script->analysis()->getCallPC(pc); + if (JSOp(*callpc) == JSOP_NEW) + return; + + add(cx, cx->typeLifoAlloc().new_(script, callpc, id)); +} + +/* + * Constraints for generating 'set' property constraints on a SETELEM only if + * the element type may be a number. For SETELEM we only account for integer + * indexes, and if the element cannot be an integer (e.g. it must be a string) + * then we lose precision by treating it like one. + */ +class TypeConstraintSetElement : public TypeConstraint +{ +public: + JSScript *script; + jsbytecode *pc; + + TypeSet *objectTypes; + TypeSet *valueTypes; + + TypeConstraintSetElement(JSScript *script, jsbytecode *pc, + TypeSet *objectTypes, TypeSet *valueTypes) + : TypeConstraint("setelement"), script(script), pc(pc), + objectTypes(objectTypes), valueTypes(valueTypes) + { + JS_ASSERT(script && pc); + } + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *objectTypes, TypeSet *valueTypes) +{ + add(cx, cx->typeLifoAlloc().new_(script, pc, objectTypes, + valueTypes)); +} + +/* + * Constraints for watching call edges as they are discovered and invoking native + * function handlers, adding constraints for arguments, receiver objects and the + * return value, and updating script foundOffsets. + */ +class TypeConstraintCall : public TypeConstraint +{ +public: + /* Call site being tracked. */ + TypeCallsite *callsite; + + TypeConstraintCall(TypeCallsite *callsite) + : TypeConstraint("call"), callsite(callsite) + {} + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addCall(JSContext *cx, TypeCallsite *site) +{ + add(cx, cx->typeLifoAlloc().new_(site)); +} + +/* Constraints for arithmetic operations. */ +class TypeConstraintArith : public TypeConstraint +{ +public: + /* Type set receiving the result of the arithmetic. */ + TypeSet *target; + + /* For addition operations, the other operand. */ + TypeSet *other; + + TypeConstraintArith(TypeSet *target, TypeSet *other) + : TypeConstraint("arith"), target(target), other(other) + { + JS_ASSERT(target); + } + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other) +{ + add(cx, cx->typeLifoAlloc().new_(target, other)); +} + +/* Subset constraint which transforms primitive values into appropriate objects. */ +class TypeConstraintTransformThis : public TypeConstraint +{ +public: + JSScript *script; + TypeSet *target; + + TypeConstraintTransformThis(JSScript *script, TypeSet *target) + : TypeConstraint("transformthis"), script(script), target(target) + {} + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target) +{ + add(cx, cx->typeLifoAlloc().new_(script, target)); +} + +/* + * Constraint which adds a particular type to the 'this' types of all + * discovered scripted functions. + */ +class TypeConstraintPropagateThis : public TypeConstraint +{ +public: + JSScript *script; + jsbytecode *callpc; + Type type; + TypeSet *types; + + TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types) + : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types) + {} + + void newType(JSContext *cx, TypeSet *source, Type type); +}; + +void +TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types) +{ + /* Don't add constraints when the call will be 'new' (see addCallProperty). */ + jsbytecode *callpc = script->analysis()->getCallPC(pc); + if (JSOp(*callpc) == JSOP_NEW) + return; + + add(cx, cx->typeLifoAlloc().new_(script, callpc, type, types)); +} + +/* Subset constraint which filters out primitive types. */ +class TypeConstraintFilterPrimitive : public TypeConstraint +{ +public: + TypeSet *target; + TypeSet::FilterKind filter; + + TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter) + : TypeConstraint("filter"), target(target), filter(filter) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) + { + switch (filter) { + case TypeSet::FILTER_ALL_PRIMITIVES: + if (type.isPrimitive()) + return; + break; + + case TypeSet::FILTER_NULL_VOID: + if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED)) + return; + break; + + case TypeSet::FILTER_VOID: + if (type.isPrimitive(JSVAL_TYPE_UNDEFINED)) + return; + break; + + default: + JS_NOT_REACHED("Bad filter"); + } + + target->addType(cx, type); + } +}; + +void +TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter) +{ + add(cx, cx->typeLifoAlloc().new_(target, filter)); +} + +/* If id is a normal slotful 'own' property of an object, get its shape. */ +static inline const Shape * +GetSingletonShape(JSContext *cx, JSObject *obj, jsid id) +{ + const Shape *shape = obj->nativeLookup(cx, id); + if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) + return shape; + return NULL; +} + +void +ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32_t offset) +{ + TypeBarrier **pbarrier = &getCode(offset).typeBarriers; + while (*pbarrier) { + TypeBarrier *barrier = *pbarrier; + if (barrier->target->hasType(barrier->type)) { + /* Barrier is now obsolete, it can be removed. */ + *pbarrier = barrier->next; + continue; + } + if (barrier->singleton) { + JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED)); + const Shape *shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId); + if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) { + /* + * When we analyzed the script the singleton had an 'own' + * property which was undefined (probably a 'var' variable + * added to a global object), but now it is defined. The only + * way it can become undefined again is if an explicit assign + * or deletion on the property occurs, which will update the + * type set for the property directly and trigger construction + * of a normal type barrier. + */ + *pbarrier = barrier->next; + continue; + } + } + pbarrier = &barrier->next; + } +} + +/* + * Cheesy limit on the number of objects we will tolerate in an observed type + * set before refusing to add new type barriers for objects. + * :FIXME: this heuristic sucks, and doesn't handle calls. + */ +static const uint32_t BARRIER_OBJECT_LIMIT = 10; + +void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32_t offset, bool all) +{ + pruneTypeBarriers(cx, offset); + + bool resetResolving = !cx->compartment->types.resolving; + if (resetResolving) + cx->compartment->types.resolving = true; + + TypeBarrier **pbarrier = &getCode(offset).typeBarriers; + while (*pbarrier) { + TypeBarrier *barrier = *pbarrier; + if (barrier->target->hasType(barrier->type) ) { + /* + * Barrier is now obsolete, it can be removed. This is not + * redundant with the pruneTypeBarriers() call above, as breaking + * previous type barriers may have modified the target type set. + */ + *pbarrier = barrier->next; + } else if (all) { + /* Force removal of the barrier. */ + barrier->target->addType(cx, barrier->type); + *pbarrier = barrier->next; + } else if (!barrier->type.isUnknown() && + !barrier->type.isAnyObject() && + barrier->type.isObject() && + barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) { + /* Maximum number of objects in the set exceeded. */ + barrier->target->addType(cx, barrier->type); + *pbarrier = barrier->next; + } else { + pbarrier = &barrier->next; + } + } + + if (resetResolving) { + cx->compartment->types.resolving = false; + cx->compartment->types.resolvePending(cx); + } +} + +void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v) +{ + if (v.kind() != SSAValue::PUSHED) + return; + + uint32_t offset = v.pushedOffset(); + if (JSOp(script->code[offset]) == JSOP_GETPROP) + breakTypeBarriersSSA(cx, poppedValue(offset, 0)); + + breakTypeBarriers(cx, offset, true); +} + +/* + * Subset constraint for property reads and argument passing which can add type + * barriers on the read instead of passing types along. + */ +class TypeConstraintSubsetBarrier : public TypeConstraint +{ +public: + JSScript *script; + jsbytecode *pc; + TypeSet *target; + + TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target) + : TypeConstraint("subsetBarrier"), script(script), pc(pc), target(target) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (!target->hasType(type)) + script->analysis()->addTypeBarrier(cx, pc, target, type); + } +}; + +void +TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) +{ + add(cx, cx->typeLifoAlloc().new_(script, pc, target)); +} + +/* + * Constraint which marks a pushed ARGUMENTS value as unknown if the script has + * an arguments object created in the future. + */ +class TypeConstraintLazyArguments : public TypeConstraint +{ +public: + TypeSet *target; + + TypeConstraintLazyArguments(TypeSet *target) + : TypeConstraint("lazyArgs"), target(target) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) {} + + void newObjectState(JSContext *cx, TypeObject *object, bool force) + { + if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) + target->addType(cx, Type::UnknownType()); + } +}; + +void +TypeSet::addLazyArguments(JSContext *cx, TypeSet *target) +{ + add(cx, cx->typeLifoAlloc().new_(target)); +} + +///////////////////////////////////////////////////////////////////// +// TypeConstraint +///////////////////////////////////////////////////////////////////// + +/* Get the object to use for a property access on type. */ +static inline TypeObject * +GetPropertyObject(JSContext *cx, JSScript *script, Type type) +{ + if (type.isTypeObject()) + return type.typeObject(); + + /* Force instantiation of lazy types for singleton objects. */ + if (type.isSingleObject()) + return type.singleObject()->getType(cx); + + /* + * Handle properties attached to primitive types, treating this access as a + * read on the primitive's new object. + */ + TypeObject *object = NULL; + switch (type.primitive()) { + + case JSVAL_TYPE_INT32: + case JSVAL_TYPE_DOUBLE: + object = TypeScript::StandardType(cx, script, JSProto_Number); + break; + + case JSVAL_TYPE_BOOLEAN: + object = TypeScript::StandardType(cx, script, JSProto_Boolean); + break; + + case JSVAL_TYPE_STRING: + object = TypeScript::StandardType(cx, script, JSProto_String); + break; + + default: + /* undefined, null and lazy arguments do not have properties. */ + return NULL; + } + + if (!object) + cx->compartment->types.setPendingNukeTypes(cx); + return object; +} + +static inline bool +UsePropertyTypeBarrier(jsbytecode *pc) +{ + /* + * At call opcodes, type barriers can only be added for the call bindings, + * which TypeConstraintCall will add barrier constraints for directly. + */ + uint32_t format = js_CodeSpec[*pc].format; + return (format & JOF_TYPESET) && !(format & JOF_INVOKE); +} + +static inline void +MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) +{ + if (UsePropertyTypeBarrier(pc)) + script->analysis()->addTypeBarrier(cx, pc, target, Type::UnknownType()); + else + target->addType(cx, Type::UnknownType()); +} + +/* + * Handle a property access on a specific object. All property accesses go through + * here, whether via x.f, x[f], or global name accesses. + */ +static inline void +PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object, + bool assign, TypeSet *target, jsid id) +{ + /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */ + if (object->unknownProperties()) { + if (!assign) + MarkPropertyAccessUnknown(cx, script, pc, target); + return; + } + + /* Capture the effects of a standard property access. */ + TypeSet *types = object->getProperty(cx, id, assign); + if (!types) + return; + if (assign) { + target->addSubset(cx, types); + } else { + if (!types->hasPropagatedProperty()) + object->getFromPrototypes(cx, id, types); + if (UsePropertyTypeBarrier(pc)) { + types->addSubsetBarrier(cx, script, pc, target); + if (object->singleton && !JSID_IS_VOID(id)) { + /* + * Add a singleton type barrier on the object if it has an + * 'own' property which is currently undefined. We'll be able + * to remove the barrier after the property becomes defined, + * even if no undefined value is ever observed at pc. + */ + const Shape *shape = GetSingletonShape(cx, object->singleton, id); + if (shape && object->singleton->nativeGetSlot(shape->slot()).isUndefined()) + script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id); + } + } else { + types->addSubset(cx, target); + } + } +} + +/* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */ +static inline bool +UnknownPropertyAccess(JSScript *script, Type type) +{ + return type.isUnknown() + || type.isAnyObject() + || (!type.isObject() && !script->hasGlobal()); +} + +void +TypeConstraintProp::newType(JSContext *cx, TypeSet *source, Type type) +{ + if (UnknownPropertyAccess(script, type)) { + /* + * Access on an unknown object. Reads produce an unknown result, writes + * need to be monitored. + */ + if (assign) + cx->compartment->types.monitorBytecode(cx, script, pc - script->code); + else + MarkPropertyAccessUnknown(cx, script, pc, target); + return; + } + + if (type.isPrimitive(JSVAL_TYPE_MAGIC)) { + /* Ignore cases which will be accounted for by the followEscapingArguments analysis. */ + if (assign || (id != JSID_VOID && id != id_length(cx))) + return; + + if (id == JSID_VOID) + MarkPropertyAccessUnknown(cx, script, pc, target); + else + target->addType(cx, Type::Int32Type()); + return; + } + + TypeObject *object = GetPropertyObject(cx, script, type); + if (object) + PropertyAccess(cx, script, pc, object, assign, target, id); +} + +void +TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type) +{ + /* + * For CALLPROP, we need to update not just the pushed types but also the + * 'this' types of possible callees. If we can't figure out that set of + * callees, monitor the call to make sure discovered callees get their + * 'this' types updated. + */ + + if (UnknownPropertyAccess(script, type)) { + cx->compartment->types.monitorBytecode(cx, script, callpc - script->code); + return; + } + + TypeObject *object = GetPropertyObject(cx, script, type); + if (object) { + if (object->unknownProperties()) { + cx->compartment->types.monitorBytecode(cx, script, callpc - script->code); + } else { + TypeSet *types = object->getProperty(cx, id, false); + if (!types) + return; + if (!types->hasPropagatedProperty()) + object->getFromPrototypes(cx, id, types); + /* Bypass addPropagateThis, we already have the callpc. */ + types->add(cx, cx->typeLifoAlloc().new_( + script, callpc, type, (TypeSet *) NULL)); + } + } +} + +void +TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type) +{ + if (type.isUnknown() || + type.isPrimitive(JSVAL_TYPE_INT32) || + type.isPrimitive(JSVAL_TYPE_DOUBLE)) { + objectTypes->addSetProperty(cx, script, pc, valueTypes, JSID_VOID); + } +} + +void +TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) +{ + JSScript *script = callsite->script; + jsbytecode *pc = callsite->pc; + + if (type.isUnknown() || type.isAnyObject()) { + /* Monitor calls on unknown functions. */ + cx->compartment->types.monitorBytecode(cx, script, pc - script->code); + return; + } + + JSFunction *callee = NULL; + + if (type.isSingleObject()) { + JSObject *obj = type.singleObject(); + + if (!obj->isFunction()) { + /* Calls on non-functions are dynamically monitored. */ + return; + } + + if (obj->toFunction()->isNative()) { + /* + * The return value and all side effects within native calls should + * be dynamically monitored, except when the compiler is generating + * specialized inline code or stub calls for a specific natives and + * knows about the behavior of that native. + */ + cx->compartment->types.monitorBytecode(cx, script, pc - script->code, true); + + /* + * Add type constraints capturing the possible behavior of + * specialized natives which operate on properties. :XXX: use + * better factoring for both this and the compiler code itself + * which specializes particular natives. + */ + + Native native = obj->toFunction()->native(); + + if (native == js::array_push) { + for (size_t i = 0; i < callsite->argumentCount; i++) { + callsite->thisTypes->addSetProperty(cx, script, pc, + callsite->argumentTypes[i], JSID_VOID); + } + } + + if (native == js::array_pop || native == js::array_shift) + callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID); + + if (native == js_Array) { + TypeObject *res = TypeScript::InitObject(cx, script, pc, JSProto_Array); + if (!res) + return; + + callsite->returnTypes->addType(cx, Type::ObjectType(res)); + + if (callsite->argumentCount >= 2) { + for (unsigned i = 0; i < callsite->argumentCount; i++) { + PropertyAccess(cx, script, pc, res, true, + callsite->argumentTypes[i], JSID_VOID); + } + } + } + + return; + } + + callee = obj->toFunction(); + } else if (type.isTypeObject()) { + callee = type.typeObject()->interpretedFunction; + if (!callee) + return; + } else { + /* Calls on non-objects are dynamically monitored. */ + return; + } + + if (!callee->script()->ensureHasTypes(cx)) + return; + + unsigned nargs = callee->nargs; + + /* Add bindings for the arguments of the call. */ + for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) { + TypeSet *argTypes = callsite->argumentTypes[i]; + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); + argTypes->addSubsetBarrier(cx, script, pc, types); + } + + /* Add void type for any formals in the callee not supplied at the call site. */ + for (unsigned i = callsite->argumentCount; i < nargs; i++) { + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); + types->addType(cx, Type::UndefinedType()); + } + + TypeSet *thisTypes = TypeScript::ThisTypes(callee->script()); + TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script()); + + if (callsite->isNew) { + /* + * If the script does not return a value then the pushed value is the + * new object (typical case). Note that we don't model construction of + * the new value, which is done dynamically; we don't keep track of the + * possible 'new' types for a given prototype type object. + */ + thisTypes->addSubset(cx, callsite->returnTypes); + returnTypes->addFilterPrimitives(cx, callsite->returnTypes, + TypeSet::FILTER_ALL_PRIMITIVES); + } else { + /* + * Add a binding for the return value of the call. We don't add a + * binding for the receiver object, as this is done with PropagateThis + * constraints added by the original JSOP_CALL* op. The type sets we + * manipulate here have lost any correlations between particular types + * in the 'this' and 'callee' sets, which we want to maintain for + * polymorphic JSOP_CALLPROP invocations. + */ + returnTypes->addSubset(cx, callsite->returnTypes); + } +} + +void +TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type) +{ + if (type.isUnknown() || type.isAnyObject()) { + /* + * The callee is unknown, make sure the call is monitored so we pick up + * possible this/callee correlations. This only comes into play for + * CALLPROP, for other calls we are past the type barrier and a + * TypeConstraintCall will also monitor the call. + */ + cx->compartment->types.monitorBytecode(cx, script, callpc - script->code); + return; + } + + /* Ignore calls to natives, these will be handled by TypeConstraintCall. */ + JSFunction *callee = NULL; + + if (type.isSingleObject()) { + JSObject *object = type.singleObject(); + if (!object->isFunction() || !object->toFunction()->isInterpreted()) + return; + callee = object->toFunction(); + } else if (type.isTypeObject()) { + TypeObject *object = type.typeObject(); + if (!object->interpretedFunction) + return; + callee = object->interpretedFunction; + } else { + /* Ignore calls to primitives, these will go through a stub. */ + return; + } + + if (!callee->script()->ensureHasTypes(cx)) + return; + + TypeSet *thisTypes = TypeScript::ThisTypes(callee->script()); + if (this->types) + this->types->addSubset(cx, thisTypes); + else + thisTypes->addType(cx, this->type); +} + +void +TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type) +{ + /* + * We only model a subset of the arithmetic behavior that is actually + * possible. The following need to be watched for at runtime: + * + * 1. Operations producing a double where no operand was a double. + * 2. Operations producing a string where no operand was a string (addition only). + * 3. Operations producing a value other than int/double/string. + */ + if (other) { + /* + * Addition operation, consider these cases: + * {int,bool} x {int,bool} -> int + * double x {int,bool,double} -> double + * string x any -> string + */ + if (type.isUnknown() || other->unknown()) { + target->addType(cx, Type::UnknownType()); + } else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) { + if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | + TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN | + TYPE_FLAG_ANYOBJECT) || + other->getObjectCount() != 0) { + target->addType(cx, Type::DoubleType()); + } + } else if (type.isPrimitive(JSVAL_TYPE_STRING)) { + target->addType(cx, Type::StringType()); + } else { + if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | + TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN | + TYPE_FLAG_ANYOBJECT) || + other->getObjectCount() != 0) { + target->addType(cx, Type::Int32Type()); + } + if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) + target->addType(cx, Type::DoubleType()); + } + } else { + if (type.isUnknown()) + target->addType(cx, Type::UnknownType()); + else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) + target->addType(cx, Type::DoubleType()); + else + target->addType(cx, Type::Int32Type()); + } +} + +void +TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, Type type) +{ + if (type.isUnknown() || type.isAnyObject() || type.isObject() || script->strictModeCode) { + target->addType(cx, type); + return; + } + + /* + * Note: if |this| is null or undefined, the pushed value is the outer window. We + * can't use script->getGlobalType() here because it refers to the inner window. + */ + if (!script->hasGlobal() || + type.isPrimitive(JSVAL_TYPE_NULL) || + type.isPrimitive(JSVAL_TYPE_UNDEFINED)) { + target->addType(cx, Type::UnknownType()); + return; + } + + TypeObject *object = NULL; + switch (type.primitive()) { + case JSVAL_TYPE_INT32: + case JSVAL_TYPE_DOUBLE: + object = TypeScript::StandardType(cx, script, JSProto_Number); + break; + case JSVAL_TYPE_BOOLEAN: + object = TypeScript::StandardType(cx, script, JSProto_Boolean); + break; + case JSVAL_TYPE_STRING: + object = TypeScript::StandardType(cx, script, JSProto_String); + break; + default: + return; + } + + if (!object) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + target->addType(cx, Type::ObjectType(object)); +} + +///////////////////////////////////////////////////////////////////// +// Freeze constraints +///////////////////////////////////////////////////////////////////// + +/* Constraint which triggers recompilation of a script if any type is added to a type set. */ +class TypeConstraintFreeze : public TypeConstraint +{ +public: + RecompileInfo info; + + /* Whether a new type has already been added, triggering recompilation. */ + bool typeAdded; + + TypeConstraintFreeze(RecompileInfo info) + : TypeConstraint("freeze"), info(info), typeAdded(false) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (typeAdded) + return; + + typeAdded = true; + cx->compartment->types.addPendingRecompile(cx, info); + } +}; + +void +TypeSet::addFreeze(JSContext *cx) +{ + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo), false); +} + +/* + * Constraint which triggers recompilation of a script if a possible new JSValueType + * tag is realized for a type set. + */ +class TypeConstraintFreezeTypeTag : public TypeConstraint +{ +public: + RecompileInfo info; + + /* + * Whether the type tag has been marked unknown due to a type change which + * occurred after this constraint was generated (and which triggered recompilation). + */ + bool typeUnknown; + + TypeConstraintFreezeTypeTag(RecompileInfo info) + : TypeConstraint("freezeTypeTag"), info(info), typeUnknown(false) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (typeUnknown) + return; + + if (!type.isUnknown() && !type.isAnyObject() && type.isObject()) { + /* Ignore new objects when the type set already has other objects. */ + if (source->getObjectCount() >= 2) + return; + } + + typeUnknown = true; + cx->compartment->types.addPendingRecompile(cx, info); + } +}; + +static inline JSValueType +GetValueTypeFromTypeFlags(TypeFlags flags) +{ + switch (flags) { + case TYPE_FLAG_UNDEFINED: + return JSVAL_TYPE_UNDEFINED; + case TYPE_FLAG_NULL: + return JSVAL_TYPE_NULL; + case TYPE_FLAG_BOOLEAN: + return JSVAL_TYPE_BOOLEAN; + case TYPE_FLAG_INT32: + return JSVAL_TYPE_INT32; + case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE): + return JSVAL_TYPE_DOUBLE; + case TYPE_FLAG_STRING: + return JSVAL_TYPE_STRING; + case TYPE_FLAG_LAZYARGS: + return JSVAL_TYPE_MAGIC; + case TYPE_FLAG_ANYOBJECT: + return JSVAL_TYPE_OBJECT; + default: + return JSVAL_TYPE_UNKNOWN; + } +} + +JSValueType +TypeSet::getKnownTypeTag(JSContext *cx) +{ + TypeFlags flags = baseFlags(); + JSValueType type; + + if (baseObjectCount()) + type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT; + else + type = GetValueTypeFromTypeFlags(flags); + + /* + * If the type set is totally empty then it will be treated as unknown, + * but we still need to record the dependency as adding a new type can give + * it a definite type tag. This is not needed if there are enough types + * that the exact tag is unknown, as it will stay unknown as more types are + * added to the set. + */ + bool empty = flags == 0 && baseObjectCount() == 0; + JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN); + + if (cx->compartment->types.compiledInfo.script && (empty || type != JSVAL_TYPE_UNKNOWN)) { + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo), false); + } + + return type; +} + +/* Constraint which triggers recompilation if an object acquires particular flags. */ +class TypeConstraintFreezeObjectFlags : public TypeConstraint +{ +public: + RecompileInfo info; + + /* Flags we are watching for on this object. */ + TypeObjectFlags flags; + + /* Whether the object has already been marked as having one of the flags. */ + bool *pmarked; + bool localMarked; + + TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags, bool *pmarked) + : TypeConstraint("freezeObjectFlags"), info(info), flags(flags), + pmarked(pmarked), localMarked(false) + {} + + TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags) + : TypeConstraint("freezeObjectFlags"), info(info), flags(flags), + pmarked(&localMarked), localMarked(false) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) {} + + void newObjectState(JSContext *cx, TypeObject *object, bool force) + { + if (object->hasAnyFlags(flags) && !*pmarked) { + *pmarked = true; + cx->compartment->types.addPendingRecompile(cx, info); + } else if (force) { + cx->compartment->types.addPendingRecompile(cx, info); + } + } +}; + +/* + * Constraint which triggers recompilation if any object in a type set acquire + * particular flags. + */ +class TypeConstraintFreezeObjectFlagsSet : public TypeConstraint +{ +public: + RecompileInfo info; + + TypeObjectFlags flags; + bool marked; + + TypeConstraintFreezeObjectFlagsSet(RecompileInfo info, TypeObjectFlags flags) + : TypeConstraint("freezeObjectKindSet"), info(info), flags(flags), marked(false) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (marked) { + /* Despecialized the kind we were interested in due to recompilation. */ + return; + } + + if (type.isUnknown() || type.isAnyObject()) { + /* Fallthrough and recompile. */ + } else if (type.isObject()) { + TypeObject *object = type.isSingleObject() + ? type.singleObject()->getType(cx) + : type.typeObject(); + if (!object->hasAnyFlags(flags)) { + /* + * Add a constraint on the the object to pick up changes in the + * object's properties. + */ + TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); + if (!types) + return; + types->add(cx, cx->typeLifoAlloc().new_( + info, flags, &marked), false); + return; + } + } else { + return; + } + + marked = true; + cx->compartment->types.addPendingRecompile(cx, info); + } +}; + +bool +TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags) +{ + if (unknownObject()) + return true; + + /* + * Treat type sets containing no objects as having all object flags, + * to spare callers from having to check this. + */ + if (baseObjectCount() == 0) + return true; + + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObject *object = getTypeObject(i); + if (!object) { + JSObject *obj = getSingleObject(i); + if (obj) + object = obj->getType(cx); + } + if (object && object->hasAnyFlags(flags)) + return true; + } + + /* + * Watch for new objects of different kind, and re-traverse existing types + * in this set to add any needed FreezeArray constraints. + */ + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo, flags)); + + return false; +} + +bool +TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags) +{ + if (object->hasAnyFlags(flags)) + return true; + + TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); + if (!types) + return true; + types->add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo, flags), false); + return false; +} + +void +types::MarkArgumentsCreated(JSContext *cx, JSScript *script) +{ + JS_ASSERT(!script->createdArgs); + + script->createdArgs = true; + script->uninlineable = true; + + MarkTypeObjectFlags(cx, script->function(), + OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE); + + if (!script->usedLazyArgs) + return; + + AutoEnterTypeInference enter(cx); + +#ifdef JS_METHODJIT + mjit::ExpandInlineFrames(cx->compartment); +#endif + + if (!script->ensureRanAnalysis(cx, NULL)) + return; + + ScriptAnalysis *analysis = script->analysis(); + + for (FrameRegsIter iter(cx); !iter.done(); ++iter) { + StackFrame *fp = iter.fp(); + if (fp->isScriptFrame() && fp->script() == script) { + /* + * Check locals and stack slots, assignment to individual arguments + * is treated as an escape on the arguments. + */ + Value *sp = fp->base() + analysis->getCode(iter.pc()).stackDepth; + for (Value *vp = fp->slots(); vp < sp; vp++) { + if (vp->isParticularMagic(JS_LAZY_ARGUMENTS)) { + if (!js_GetArgsValue(cx, fp, vp)) + vp->setNull(); + } + } + } + } +} + +static inline void +ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force) +{ + if (object->unknownProperties()) + return; + + /* All constraints listening to state changes are on the empty id. */ + TypeSet *types = object->maybeGetProperty(cx, JSID_EMPTY); + + /* Mark as unknown after getting the types, to avoid assertion. */ + if (markingUnknown) + object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES; + + if (types) { + TypeConstraint *constraint = types->constraintList; + while (constraint) { + constraint->newObjectState(cx, object, force); + constraint = constraint->next; + } + } +} + +void +TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj) +{ + JS_ASSERT(!obj->unknownProperties()); + TypeSet *types = obj->getProperty(cx, JSID_EMPTY, false); + if (!types) + return; + + /* + * Use a constraint which triggers recompilation when markStateChange is + * called, which will set 'force' to true. + */ + types->add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo, + 0)); +} + +class TypeConstraintFreezeOwnProperty : public TypeConstraint +{ +public: + RecompileInfo info; + + bool updated; + bool configurable; + + TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable) + : TypeConstraint("freezeOwnProperty"), + info(info), updated(false), configurable(configurable) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) {} + + void newPropertyState(JSContext *cx, TypeSet *source) + { + if (updated) + return; + if (source->isOwnProperty(configurable)) { + updated = true; + cx->compartment->types.addPendingRecompile(cx, info); + } + } +}; + +static void +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); + +bool +TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) +{ + /* + * Everywhere compiled code depends on definite properties associated with + * a type object's newScript, we need to make sure there are constraints + * in place which will mark those properties as configured should the + * definite properties be invalidated. + */ + if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { + if (object->newScript) { + CheckNewScriptProperties(cx, object, object->newScript->fun); + } else { + JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED); + object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; + } + } + + if (isOwnProperty(configurable)) + return true; + + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo, + configurable), false); + return false; +} + +bool +TypeSet::knownNonEmpty(JSContext *cx) +{ + if (baseFlags() != 0 || baseObjectCount() != 0) + return true; + + addFreeze(cx); + + return false; +} + +bool +TypeSet::knownSubset(JSContext *cx, TypeSet *other) +{ + if ((baseFlags() & other->baseFlags()) != baseFlags()) + return false; + + if (unknownObject()) { + JS_ASSERT(other->unknownObject()); + } else { + for (unsigned i = 0; i < getObjectCount(); i++) { + TypeObjectKey *obj = getObject(i); + if (!obj) + continue; + if (!other->hasType(Type::ObjectType(obj))) + return false; + } + } + + addFreeze(cx); + + return true; +} + +int +TypeSet::getTypedArrayType(JSContext *cx) +{ + int arrayType = TypedArray::TYPE_MAX; + unsigned count = getObjectCount(); + + for (unsigned i = 0; i < count; i++) { + JSObject *proto = NULL; + if (JSObject *object = getSingleObject(i)) { + proto = object->getProto(); + } else if (TypeObject *object = getTypeObject(i)) { + JS_ASSERT(!object->hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY)); + proto = object->proto; + } + if (!proto) + continue; + + int objArrayType = proto->getClass() - TypedArray::slowClasses; + JS_ASSERT(objArrayType >= 0 && objArrayType < TypedArray::TYPE_MAX); + + /* + * Set arrayType to the type of the first array. Return if there is an array + * of another type. + */ + if (arrayType == TypedArray::TYPE_MAX) + arrayType = objArrayType; + else if (arrayType != objArrayType) + return TypedArray::TYPE_MAX; + } + + /* + * Assume the caller checked that OBJECT_FLAG_NON_TYPED_ARRAY is not set. + * This means the set contains at least one object because sets with no + * objects have all object flags. + */ + JS_ASSERT(arrayType != TypedArray::TYPE_MAX); + + /* Recompile when another typed array is added to this set. */ + addFreeze(cx); + + return arrayType; +} + +JSObject * +TypeSet::getSingleton(JSContext *cx, bool freeze) +{ + if (baseFlags() != 0 || baseObjectCount() != 1) + return NULL; + + JSObject *obj = getSingleObject(0); + if (!obj) + return NULL; + + if (freeze) { + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo), false); + } + + return obj; +} + +static inline bool +TypeHasGlobal(Type type, JSObject *global) +{ + if (type.isUnknown() || type.isAnyObject()) + return false; + + if (type.isSingleObject()) + return &type.singleObject()->global() == global; + + if (type.isTypeObject()) + return type.typeObject()->getGlobal() == global; + + JS_ASSERT(type.isPrimitive()); + return true; +} + +class TypeConstraintFreezeGlobal : public TypeConstraint +{ +public: + RecompileInfo info; + JSObject *global; + + TypeConstraintFreezeGlobal(RecompileInfo info, JSObject *global) + : TypeConstraint("freezeGlobal"), info(info), global(global) + { + JS_ASSERT(global); + } + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (!global || TypeHasGlobal(type, global)) + return; + + global = NULL; + cx->compartment->types.addPendingRecompile(cx, info); + } +}; + +bool +TypeSet::hasGlobalObject(JSContext *cx, JSObject *global) +{ + if (unknownObject()) + return false; + + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObjectKey *object = getObject(i); + if (object && !TypeHasGlobal(Type::ObjectType(object), global)) + return false; + } + + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledInfo, global), false); + + return true; +} + +bool +TypeSet::needsBarrier(JSContext *cx) +{ + bool result = unknownObject() + || getObjectCount() > 0 + || hasAnyFlag(TYPE_FLAG_STRING); + if (!result) + addFreeze(cx); + return result; +} + +///////////////////////////////////////////////////////////////////// +// TypeCompartment +///////////////////////////////////////////////////////////////////// + +void +TypeCompartment::init(JSContext *cx) +{ + PodZero(this); + + if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE) { +#ifdef JS_METHODJIT + JSC::MacroAssembler masm; + if (masm.supportsFloatingPoint()) +#endif + inferenceEnabled = true; + } +} + +TypeObject * +TypeCompartment::newTypeObject(JSContext *cx, JSScript *script, + JSProtoKey key, JSObject *proto, bool unknown) +{ + RootObject root(cx, &proto); + + TypeObject *object = gc::NewGCThing(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject)); + if (!object) + return NULL; + new(object) TypeObject(proto, key == JSProto_Function, unknown); + + if (!cx->typeInferenceEnabled()) + object->flags |= OBJECT_FLAG_UNKNOWN_MASK; + else + object->setFlagsFromKey(cx, key); + + return object; +} + +TypeObject * +TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, const AllocationSiteKey &key) +{ + AutoEnterTypeInference enter(cx); + + if (!allocationSiteTable) { + allocationSiteTable = cx->new_(); + if (!allocationSiteTable || !allocationSiteTable->init()) { + cx->compartment->types.setPendingNukeTypes(cx); + return NULL; + } + } + + AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key); + JS_ASSERT(!p); + + JSObject *proto; + if (!js_GetClassPrototype(cx, key.script->global(), key.kind, &proto, NULL)) + return NULL; + + TypeObject *res = newTypeObject(cx, key.script, key.kind, proto); + if (!res) { + cx->compartment->types.setPendingNukeTypes(cx); + return NULL; + } + + jsbytecode *pc = key.script->code + key.offset; + + if (JSOp(*pc) == JSOP_NEWOBJECT) { + /* + * This object is always constructed the same way and will not be + * observed by other code before all properties have been added. Mark + * all the properties as definite properties of the object. + */ + JSObject *baseobj = key.script->getObject(GET_UINT32_INDEX(pc)); + + if (!res->addDefiniteProperties(cx, baseobj)) + return NULL; + } + + if (!allocationSiteTable->add(p, key, res)) { + cx->compartment->types.setPendingNukeTypes(cx); + return NULL; + } + + return res; +} + +static inline jsid +GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) +{ + unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset); + return MakeTypeId(cx, ATOM_TO_JSID(script->getAtom(index))); +} + +static inline const Value & +GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc) +{ + unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, 0); + return script->getConst(index); +} + +bool +types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + /* + * Make a heuristic guess at a use of JSOP_NEW that the constructed object + * should have a fresh type object. We do this when the NEW is immediately + * followed by a simple assignment to an object's .prototype field. + * This is designed to catch common patterns for subclassing in JS: + * + * function Super() { ... } + * function Sub1() { ... } + * function Sub2() { ... } + * + * Sub1.prototype = new Super(); + * Sub2.prototype = new Super(); + * + * Using distinct type objects for the particular prototypes of Sub1 and + * Sub2 lets us continue to distinguish the two subclasses and any extra + * properties added to those prototype objects. + */ + if (JSOp(*pc) != JSOP_NEW) + return false; + pc += JSOP_NEW_LENGTH; + if (JSOp(*pc) == JSOP_SETPROP) { + jsid id = GetAtomId(cx, script, pc, 0); + if (id == id_prototype(cx)) + return true; + } + + return false; +} + +bool +types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script) +{ + if (!cx->typeInferenceEnabled() || !script->hasGlobal()) + return true; + + JSObject *proto = script->global()->getOrCreateArrayPrototype(cx); + if (!proto) + return true; + + do { + TypeObject *type = proto->getType(cx); + if (type->unknownProperties()) + return true; + TypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false); + if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx)) + return true; + proto = proto->getProto(); + } while (proto); + + return false; +} + +bool +TypeCompartment::growPendingArray(JSContext *cx) +{ + unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2); + PendingWork *newArray = (PendingWork *) OffTheBooks::calloc_(newCapacity * sizeof(PendingWork)); + if (!newArray) { + cx->compartment->types.setPendingNukeTypes(cx); + return false; + } + + PodCopy(newArray, pendingArray, pendingCount); + cx->free_(pendingArray); + + pendingArray = newArray; + pendingCapacity = newCapacity; + + return true; +} + +void +TypeCompartment::processPendingRecompiles(JSContext *cx) +{ + /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */ + Vector *pending = pendingRecompiles; + pendingRecompiles = NULL; + + JS_ASSERT(!pending->empty()); + +#ifdef JS_METHODJIT + + mjit::ExpandInlineFrames(cx->compartment); + + for (unsigned i = 0; i < pending->length(); i++) { + const RecompileInfo &info = (*pending)[i]; + mjit::JITScript *jit = info.script->getJIT(info.constructing); + if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) + mjit::Recompiler::clearStackReferencesAndChunk(cx, info.script, jit, info.chunkIndex); + } + +#endif /* JS_METHODJIT */ + + cx->delete_(pending); +} + +void +TypeCompartment::setPendingNukeTypes(JSContext *cx) +{ + JS_ASSERT(compartment()->activeInference); + if (!pendingNukeTypes) { + if (cx->compartment) + js_ReportOutOfMemory(cx); + pendingNukeTypes = true; + } +} + +void +TypeCompartment::nukeTypes(JSContext *cx) +{ + JS_ASSERT(this == &cx->compartment->types); + + /* + * This is the usual response if we encounter an OOM while adding a type + * or resolving type constraints. Reset the compartment to not use type + * inference, and recompile all scripts. + * + * Because of the nature of constraint-based analysis (add constraints, and + * iterate them until reaching a fixpoint), we can't undo an add of a type set, + * and merely aborting the operation which triggered the add will not be + * sufficient for correct behavior as we will be leaving the types in an + * inconsistent state. + */ + JS_ASSERT(pendingNukeTypes); + if (pendingRecompiles) { + cx->free_(pendingRecompiles); + pendingRecompiles = NULL; + } + + /* + * We may or may not be under the GC. In either case don't allocate, and + * acquire the GC lock so we can update inferenceEnabled for all contexts. + */ + +#ifdef JS_THREADSAFE + AutoLockGC maybeLock; + if (!cx->runtime->gcMarkAndSweep) + maybeLock.lock(cx->runtime); +#endif + + inferenceEnabled = false; + + /* Update the cached inferenceEnabled bit in all contexts. */ + for (JSCList *cl = cx->runtime->contextList.next; + cl != &cx->runtime->contextList; + cl = cl->next) { + JSContext *cx = JSContext::fromLinkField(cl); + cx->setCompartment(cx->compartment); + } + +#ifdef JS_METHODJIT + + JSCompartment *compartment = cx->compartment; + mjit::ExpandInlineFrames(compartment); + mjit::ClearAllFrames(compartment); + + /* Throw away all JIT code in the compartment, but leave everything else alone. */ + + for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasJITCode()) + mjit::ReleaseScriptCode(cx, script); + } +#endif /* JS_METHODJIT */ + +} + +void +TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) +{ +#ifdef JS_METHODJIT + mjit::JITScript *jit = info.script->getJIT(info.constructing); + if (!jit || !jit->chunkDescriptor(info.chunkIndex).chunk) { + /* Scripts which haven't been compiled yet don't need to be recompiled. */ + return; + } + + if (!pendingRecompiles) { + pendingRecompiles = cx->new_< Vector >(cx); + if (!pendingRecompiles) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + } + + for (unsigned i = 0; i < pendingRecompiles->length(); i++) { + if (info == (*pendingRecompiles)[i]) + return; + } + + if (!pendingRecompiles->append(info)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } +#endif +} + +void +TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc) +{ +#ifdef JS_METHODJIT + RecompileInfo info; + info.script = script; + + if (script->jitNormal) { + info.constructing = false; + info.chunkIndex = script->jitNormal->chunkIndex(pc); + addPendingRecompile(cx, info); + } + + if (script->jitCtor) { + info.constructing = true; + info.chunkIndex = script->jitCtor->chunkIndex(pc); + addPendingRecompile(cx, info); + } +#endif +} + +void +TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset, + bool returnOnly) +{ + ScriptAnalysis *analysis = script->analysis(); + JS_ASSERT(analysis->ranInference()); + + jsbytecode *pc = script->code + offset; + + JS_ASSERT_IF(returnOnly, js_CodeSpec[*pc].format & JOF_INVOKE); + + Bytecode &code = analysis->getCode(pc); + + if (returnOnly ? code.monitoredTypesReturn : code.monitoredTypes) + return; + + InferSpew(ISpewOps, "addMonitorNeeded:%s #%u:%05u", + returnOnly ? " returnOnly" : "", script->id(), offset); + + /* Dynamically monitor this call to keep track of its result types. */ + if (js_CodeSpec[*pc].format & JOF_INVOKE) + code.monitoredTypesReturn = true; + + if (!returnOnly) + code.monitoredTypes = true; + + cx->compartment->types.addPendingRecompile(cx, script, pc); + + /* Trigger recompilation of any inline callers. */ + if (script->function() && !script->function()->hasLazyType()) + ObjectStateChange(cx, script->function()->type(), false, true); +} + +void +TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) +{ + JS_ASSERT(this == &cx->compartment->types); + JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); + JS_ASSERT(!target->singleton); + JS_ASSERT(target->unknownProperties()); + target->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN; + + AutoEnterTypeInference enter(cx); + + /* + * Mark both persistent and transient type sets which contain obj as having + * a generic object type. It is not sufficient to mark just the persistent + * sets, as analysis of individual opcodes can pull type objects from + * static information (like initializer objects at various offsets). + * + * We make a list of properties to update and fix them afterwards, as adding + * types can't be done while iterating over cells as it can potentially make + * new type objects as well or trigger GC. + */ + Vector pending(cx); + for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + TypeObject *object = i.get(); + + unsigned count = object->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = object->getProperty(i); + if (prop && prop->types.hasType(Type::ObjectType(target))) { + if (!pending.append(&prop->types)) + cx->compartment->types.setPendingNukeTypes(cx); + } + } + } + + for (unsigned i = 0; i < pending.length(); i++) + pending[i]->addType(cx, Type::AnyObjectType()); + + for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->types) { + unsigned count = TypeScript::NumTypeSets(script); + TypeSet *typeArray = script->types->typeArray(); + for (unsigned i = 0; i < count; i++) { + if (typeArray[i].hasType(Type::ObjectType(target))) + typeArray[i].addType(cx, Type::AnyObjectType()); + } + } + if (script->hasAnalysis() && script->analysis()->ranInference()) { + for (unsigned i = 0; i < script->length; i++) { + if (!script->analysis()->maybeCode(i)) + continue; + jsbytecode *pc = script->code + i; + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + continue; + unsigned defCount = GetDefCount(script, i); + if (ExtendedDef(pc)) + defCount++; + for (unsigned j = 0; j < defCount; j++) { + TypeSet *types = script->analysis()->pushedTypes(pc, j); + if (types->hasType(Type::ObjectType(target))) + types->addType(cx, Type::AnyObjectType()); + } + } + } + } +} + +void +ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, Type type) +{ + Bytecode &code = getCode(pc); + + if (!type.isUnknown() && !type.isAnyObject() && + type.isObject() && target->getObjectCount() >= BARRIER_OBJECT_LIMIT) { + /* Ignore this barrier, just add the type to the target. */ + target->addType(cx, type); + return; + } + + if (!code.typeBarriers) { + /* + * Adding type barriers at a bytecode which did not have them before + * will trigger recompilation. If there were already type barriers, + * however, do not trigger recompilation (the script will be recompiled + * if any of the barriers is ever violated). + */ + cx->compartment->types.addPendingRecompile(cx, script, const_cast(pc)); + + /* Trigger recompilation of any inline callers. */ + if (script->function() && !script->function()->hasLazyType()) + ObjectStateChange(cx, script->function()->type(), false, true); + } + + /* Ignore duplicate barriers. */ + TypeBarrier *barrier = code.typeBarriers; + while (barrier) { + if (barrier->target == target && barrier->type == type && !barrier->singleton) + return; + barrier = barrier->next; + } + + InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s", + script->id(), pc - script->code, + InferSpewColor(target), target, InferSpewColorReset(), + TypeString(type)); + + barrier = cx->typeLifoAlloc().new_(target, type, (JSObject *) NULL, JSID_VOID); + + barrier->next = code.typeBarriers; + code.typeBarriers = barrier; +} + +void +ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, JSObject *singleton, jsid singletonId) +{ + JS_ASSERT(singletonId == MakeTypeId(cx, singletonId) && !JSID_IS_VOID(singletonId)); + + Bytecode &code = getCode(pc); + + if (!code.typeBarriers) { + /* Trigger recompilation as for normal type barriers. */ + cx->compartment->types.addPendingRecompile(cx, script, const_cast(pc)); + if (script->function() && !script->function()->hasLazyType()) + ObjectStateChange(cx, script->function()->type(), false, true); + } + + InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s", + script->id(), pc - script->code, + InferSpewColor(target), target, InferSpewColorReset(), + (void *) singleton, TypeIdString(singletonId)); + + TypeBarrier *barrier = cx->typeLifoAlloc().new_(target, Type::UndefinedType(), + singleton, singletonId); + + barrier->next = code.typeBarriers; + code.typeBarriers = barrier; +} + +void +TypeCompartment::print(JSContext *cx, bool force) +{ + JSCompartment *compartment = this->compartment(); + AutoEnterAnalysis enter(compartment); + + if (!force && !InferSpewActive(ISpewResult)) + return; + + for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasAnalysis() && script->analysis()->ranInference()) + script->analysis()->printTypes(cx); + } + +#ifdef DEBUG + for (gc::CellIter i(cx, compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + TypeObject *object = i.get(); + object->print(cx); + } +#endif + + printf("Counts: "); + for (unsigned count = 0; count < TYPE_COUNT_LIMIT; count++) { + if (count) + printf("/"); + printf("%u", typeCounts[count]); + } + printf(" (%u over)\n", typeCountOver); + + printf("Recompilations: %u\n", recompilations); +} + +///////////////////////////////////////////////////////////////////// +// TypeCompartment tables +///////////////////////////////////////////////////////////////////// + +/* + * The arrayTypeTable and objectTypeTable are per-compartment tables for making + * common type objects to model the contents of large script singletons and + * JSON objects. These are vanilla Arrays and native Objects, so we distinguish + * the types of different ones by looking at the types of their properties. + * + * All singleton/JSON arrays which have the same prototype, are homogenous and + * of the same element type will share a type object. All singleton/JSON + * objects which have the same shape and property types will also share a type + * object. We don't try to collate arrays or objects that have type mismatches. + */ + +static inline bool +NumberTypes(Type a, Type b) +{ + return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) + && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); +} + +/* + * As for GetValueType, but requires object types to be non-singletons with + * their default prototype. These are the only values that should appear in + * arrays and objects whose type can be fixed. + */ +static inline Type +GetValueTypeForTable(JSContext *cx, const Value &v) +{ + Type type = GetValueType(cx, v); + JS_ASSERT(!type.isSingleObject()); + return type; +} + +struct types::ArrayTableKey +{ + Type type; + JSObject *proto; + + ArrayTableKey() + : type(Type::UndefinedType()), proto(NULL) + {} + + typedef ArrayTableKey Lookup; + + static inline uint32_t hash(const ArrayTableKey &v) { + return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); + } + + static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) { + return v1.type == v2.type && v1.proto == v2.proto; + } +}; + +void +TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj) +{ + AutoEnterTypeInference enter(cx); + + if (!arrayTypeTable) { + arrayTypeTable = cx->new_(); + if (!arrayTypeTable || !arrayTypeTable->init()) { + arrayTypeTable = NULL; + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + } + + /* + * If the array is of homogenous type, pick a type object which will be + * shared with all other singleton/JSON arrays of the same type. + * If the array is heterogenous, keep the existing type object, which has + * unknown properties. + */ + JS_ASSERT(obj->isDenseArray()); + + unsigned len = obj->getDenseArrayInitializedLength(); + if (len == 0) + return; + + Type type = GetValueTypeForTable(cx, obj->getDenseArrayElement(0)); + + for (unsigned i = 1; i < len; i++) { + Type ntype = GetValueTypeForTable(cx, obj->getDenseArrayElement(i)); + if (ntype != type) { + if (NumberTypes(type, ntype)) + type = Type::DoubleType(); + else + return; + } + } + + ArrayTableKey key; + key.type = type; + key.proto = obj->getProto(); + ArrayTypeTable::AddPtr p = arrayTypeTable->lookupForAdd(key); + + if (p) { + obj->setType(p->value); + } else { + /* Make a new type to use for future arrays with the same elements. */ + TypeObject *objType = newTypeObject(cx, NULL, JSProto_Array, obj->getProto()); + if (!objType) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + obj->setType(objType); + + if (!objType->unknownProperties()) + objType->addPropertyType(cx, JSID_VOID, type); + + if (!arrayTypeTable->relookupOrAdd(p, key, objType)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + } +} + +/* + * N.B. We could also use the initial shape of the object (before its type is + * fixed) as the key in the object table, but since all references in the table + * are weak the hash entries would usually be collected on GC even if objects + * with the new type/shape are still live. + */ +struct types::ObjectTableKey +{ + jsid *ids; + uint32_t nslots; + uint32_t nfixed; + JSObject *proto; + + typedef JSObject * Lookup; + + static inline uint32_t hash(JSObject *obj) { + return (uint32_t) (JSID_BITS(obj->lastProperty()->propid()) ^ + obj->slotSpan() ^ obj->numFixedSlots() ^ + ((uint32_t)(size_t)obj->getProto() >> 2)); + } + + static inline bool match(const ObjectTableKey &v, JSObject *obj) { + if (obj->slotSpan() != v.nslots || + obj->numFixedSlots() != v.nfixed || + obj->getProto() != v.proto) { + return false; + } + const Shape *shape = obj->lastProperty(); + while (!shape->isEmptyShape()) { + if (shape->propid() != v.ids[shape->slot()]) + return false; + shape = shape->previous(); + } + return true; + } +}; + +struct types::ObjectTableEntry +{ + ReadBarriered object; + Type *types; +}; + +void +TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj) +{ + AutoEnterTypeInference enter(cx); + + if (!objectTypeTable) { + objectTypeTable = cx->new_(); + if (!objectTypeTable || !objectTypeTable->init()) { + objectTypeTable = NULL; + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + } + + /* + * Use the same type object for all singleton/JSON arrays with the same + * base shape, i.e. the same fields written in the same order. If there + * is a type mismatch with previous objects of the same shape, use the + * generic unknown type. + */ + JS_ASSERT(obj->isObject()); + + if (obj->slotSpan() == 0 || obj->inDictionaryMode()) + return; + + ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj); + const Shape *baseShape = obj->lastProperty(); + + if (p) { + /* The lookup ensures the shape matches, now check that the types match. */ + Type *types = p->value.types; + for (unsigned i = 0; i < obj->slotSpan(); i++) { + Type ntype = GetValueTypeForTable(cx, obj->getSlot(i)); + if (ntype != types[i]) { + if (NumberTypes(ntype, types[i])) { + if (types[i].isPrimitive(JSVAL_TYPE_INT32)) { + types[i] = Type::DoubleType(); + const Shape *shape = baseShape; + while (!shape->isEmptyShape()) { + if (shape->slot() == i) { + Type type = Type::DoubleType(); + if (!p->value.object->unknownProperties()) { + jsid id = MakeTypeId(cx, shape->propid()); + p->value.object->addPropertyType(cx, id, type); + } + break; + } + shape = shape->previous(); + } + } + } else { + return; + } + } + } + + obj->setType(p->value.object); + } else { + /* Make a new type to use for the object and similar future ones. */ + TypeObject *objType = newTypeObject(cx, NULL, JSProto_Object, obj->getProto()); + if (!objType || !objType->addDefiniteProperties(cx, obj)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + jsid *ids = (jsid *) cx->calloc_(obj->slotSpan() * sizeof(jsid)); + if (!ids) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + Type *types = (Type *) cx->calloc_(obj->slotSpan() * sizeof(Type)); + if (!types) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + const Shape *shape = baseShape; + while (!shape->isEmptyShape()) { + ids[shape->slot()] = shape->propid(); + types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot())); + if (!objType->unknownProperties()) { + jsid id = MakeTypeId(cx, shape->propid()); + objType->addPropertyType(cx, id, types[shape->slot()]); + } + shape = shape->previous(); + } + + ObjectTableKey key; + key.ids = ids; + key.nslots = obj->slotSpan(); + key.nfixed = obj->numFixedSlots(); + key.proto = obj->getProto(); + JS_ASSERT(ObjectTableKey::match(key, obj)); + + ObjectTableEntry entry; + entry.object = objType; + entry.types = types; + + p = objectTypeTable->lookupForAdd(obj); + if (!objectTypeTable->add(p, key, entry)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + obj->setType(objType); + } +} + +///////////////////////////////////////////////////////////////////// +// TypeObject +///////////////////////////////////////////////////////////////////// + +void +TypeObject::getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force) +{ + if (!force && types->hasPropagatedProperty()) + return; + + types->setPropagatedProperty(); + + if (!proto) + return; + + if (proto->getType(cx)->unknownProperties()) { + types->addType(cx, Type::UnknownType()); + return; + } + + TypeSet *protoTypes = proto->type()->getProperty(cx, id, false); + if (!protoTypes) + return; + + protoTypes->addSubset(cx, types); + + proto->type()->getFromPrototypes(cx, id, protoTypes); +} + +static inline void +UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *shape, bool force) +{ + types->setOwnProperty(cx, false); + if (!shape->writable()) + types->setOwnProperty(cx, true); + + if (shape->hasGetterValue() || shape->hasSetterValue()) { + types->setOwnProperty(cx, true); + types->addType(cx, Type::UnknownType()); + } else if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) { + const Value &value = obj->nativeGetSlot(shape->slot()); + + /* + * Don't add initial undefined types for singleton properties that are + * not collated into the JSID_VOID property (see propertySet comment). + */ + if (force || !value.isUndefined()) { + Type type = GetValueType(cx, value); + types->addType(cx, type); + } + } +} + +bool +TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) +{ + JS_ASSERT(!*pprop); + Property *base = cx->typeLifoAlloc().new_(id); + if (!base) { + cx->compartment->types.setPendingNukeTypes(cx); + return false; + } + + if (singleton) { + /* + * Fill the property in with any type the object already has in an + * own property. We are only interested in plain native properties + * which don't go through a barrier when read by the VM or jitcode. + * We don't need to handle arrays or other JIT'ed non-natives as + * these are not (yet) singletons. + */ + + if (JSID_IS_VOID(id)) { + /* Go through all shapes on the object to get integer-valued properties. */ + const Shape *shape = singleton->lastProperty(); + while (!shape->isEmptyShape()) { + if (JSID_IS_VOID(MakeTypeId(cx, shape->propid()))) + UpdatePropertyType(cx, &base->types, singleton, shape, true); + shape = shape->previous(); + } + } else if (!JSID_IS_EMPTY(id)) { + const Shape *shape = singleton->nativeLookup(cx, id); + if (shape) + UpdatePropertyType(cx, &base->types, singleton, shape, false); + } + + if (singleton->watched()) { + /* + * Mark the property as configured, to inhibit optimizations on it + * and avoid bypassing the watchpoint handler. + */ + base->types.setOwnProperty(cx, true); + } + } + + *pprop = base; + + InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s", + InferSpewColor(&base->types), &base->types, InferSpewColorReset(), + TypeObjectString(this), TypeIdString(id)); + + return true; +} + +bool +TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj) +{ + if (unknownProperties()) + return true; + + /* Mark all properties of obj as definite properties of this type. */ + AutoEnterTypeInference enter(cx); + + const Shape *shape = obj->lastProperty(); + while (!shape->isEmptyShape()) { + jsid id = MakeTypeId(cx, shape->propid()); + if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) && + shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) { + TypeSet *types = getProperty(cx, id, true); + if (!types) + return false; + types->setDefinite(shape->slot()); + } + shape = shape->previous(); + } + + return true; +} + +bool +TypeObject::matchDefiniteProperties(JSObject *obj) +{ + unsigned count = getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = getProperty(i); + if (!prop) + continue; + if (prop->types.isDefiniteProperty()) { + unsigned slot = prop->types.definiteSlot(); + + bool found = false; + const Shape *shape = obj->lastProperty(); + while (!shape->isEmptyShape()) { + if (shape->slot() == slot && shape->propid() == prop->id) { + found = true; + break; + } + shape = shape->previous(); + } + if (!found) + return false; + } + } + + return true; +} + +inline void +InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type) +{ + JS_ASSERT(id == MakeTypeId(cx, id)); + + AutoEnterTypeInference enter(cx); + + TypeSet *types = obj->getProperty(cx, id, true); + if (!types || types->hasType(type)) + return; + + InferSpew(ISpewOps, "externalType: property %s %s: %s", + TypeObjectString(obj), TypeIdString(id), TypeString(type)); + types->addType(cx, type); +} + +void +TypeObject::addPropertyType(JSContext *cx, jsid id, Type type) +{ + InlineAddTypeProperty(cx, this, id, type); +} + +void +TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value) +{ + InlineAddTypeProperty(cx, this, id, GetValueType(cx, value)); +} + +void +TypeObject::addPropertyType(JSContext *cx, const char *name, Type type) +{ + jsid id = JSID_VOID; + if (name) { + JSAtom *atom = js_Atomize(cx, name, strlen(name)); + if (!atom) { + AutoEnterTypeInference enter(cx); + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + id = ATOM_TO_JSID(atom); + } + InlineAddTypeProperty(cx, this, id, type); +} + +void +TypeObject::addPropertyType(JSContext *cx, const char *name, const Value &value) +{ + addPropertyType(cx, name, GetValueType(cx, value)); +} + +void +TypeObject::markPropertyConfigured(JSContext *cx, jsid id) +{ + AutoEnterTypeInference enter(cx); + + id = MakeTypeId(cx, id); + + TypeSet *types = getProperty(cx, id, true); + if (types) + types->setOwnProperty(cx, true); +} + +void +TypeObject::markStateChange(JSContext *cx) +{ + if (unknownProperties()) + return; + + AutoEnterTypeInference enter(cx); + TypeSet *types = maybeGetProperty(cx, JSID_EMPTY); + if (types) { + TypeConstraint *constraint = types->constraintList; + while (constraint) { + constraint->newObjectState(cx, this, true); + constraint = constraint->next; + } + } +} + +void +TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) +{ + if ((this->flags & flags) == flags) + return; + + AutoEnterTypeInference enter(cx); + + if (singleton) { + /* Make sure flags are consistent with persistent object state. */ + JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS, + (flags & OBJECT_FLAG_UNINLINEABLE) && + interpretedFunction->script()->createdArgs); + JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, + interpretedFunction->script()->uninlineable); + JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION, + interpretedFunction->script()->reentrantOuterFunction); + JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, + singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); + } + + this->flags |= flags; + + InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); + + ObjectStateChange(cx, this, false, false); +} + +void +TypeObject::markUnknown(JSContext *cx) +{ + AutoEnterTypeInference enter(cx); + + JS_ASSERT(cx->compartment->activeInference); + JS_ASSERT(!unknownProperties()); + + if (!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) + clearNewScript(cx); + + InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); + + ObjectStateChange(cx, this, true, true); + + /* + * Existing constraints may have already been added to this object, which we need + * to do the right thing for. We can't ensure that we will mark all unknown + * objects before they have been accessed, as the __proto__ of a known object + * could be dynamically set to an unknown object, and we can decide to ignore + * properties of an object during analysis (i.e. hashmaps). Adding unknown for + * any properties accessed already accounts for possible values read from them. + */ + + unsigned count = getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = getProperty(i); + if (prop) { + prop->types.addType(cx, Type::UnknownType()); + prop->types.setOwnProperty(cx, true); + } + } +} + +void +TypeObject::clearNewScript(JSContext *cx) +{ + JS_ASSERT(!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)); + flags |= OBJECT_FLAG_NEW_SCRIPT_CLEARED; + + /* + * It is possible for the object to not have a new script yet but to have + * one added in the future. When analyzing properties of new scripts we mix + * in adding constraints to trigger clearNewScript with changes to the + * type sets themselves (from breakTypeBarriers). It is possible that we + * could trigger one of these constraints before AnalyzeNewScriptProperties + * has finished, in which case we want to make sure that call fails. + */ + if (!newScript) + return; + + AutoEnterTypeInference enter(cx); + + /* + * Any definite properties we added due to analysis of the new script when + * the type object was created are now invalid: objects with the same type + * can be created by using 'new' on a different script or through some + * other mechanism (e.g. Object.create). Rather than clear out the definite + * bits on the object's properties, just mark such properties as having + * been deleted/reconfigured, which will have the same effect on JITs + * wanting to use the definite bits to optimize property accesses. + */ + for (unsigned i = 0; i < getPropertyCount(); i++) { + Property *prop = getProperty(i); + if (!prop) + continue; + if (prop->types.isDefiniteProperty()) + prop->types.setOwnProperty(cx, true); + } + + /* + * If we cleared the new script while in the middle of initializing an + * object, it will still have the new script's shape and reflect the no + * longer correct state of the object once its initialization is completed. + * We can't really detect the possibility of this statically, but the new + * script keeps track of where each property is initialized so we can walk + * the stack and fix up any such objects. + */ + for (FrameRegsIter iter(cx); !iter.done(); ++iter) { + StackFrame *fp = iter.fp(); + if (fp->isScriptFrame() && fp->isConstructing() && + fp->fun() == newScript->fun && fp->thisValue().isObject() && + !fp->thisValue().toObject().hasLazyType() && + fp->thisValue().toObject().type() == this) { + JSObject *obj = &fp->thisValue().toObject(); + jsbytecode *pc = iter.pc(); + + /* Whether all identified 'new' properties have been initialized. */ + bool finished = false; + + /* If not finished, number of properties that have been added. */ + uint32_t numProperties = 0; + + /* + * If non-zero, we are scanning initializers in a call which has + * already finished. + */ + size_t depth = 0; + + for (TypeNewScript::Initializer *init = newScript->initializerList;; init++) { + uint32_t offset = uint32_t(pc - fp->script()->code); + if (init->kind == TypeNewScript::Initializer::SETPROP) { + if (!depth && init->offset > offset) { + /* Advanced past all properties which have been initialized. */ + break; + } + numProperties++; + } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) { + if (depth) { + depth++; + } else if (init->offset > offset) { + /* Advanced past all properties which have been initialized. */ + break; + } else if (init->offset == offset) { + StackSegment &seg = cx->stack.space().containingSegment(fp); + if (seg.maybefp() == fp) + break; + fp = seg.computeNextFrame(fp); + pc = fp->pcQuadratic(cx->stack); + } else { + /* This call has already finished. */ + depth = 1; + } + } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) { + if (depth) { + depth--; + } else { + /* This call has not finished yet. */ + break; + } + } else { + JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); + finished = true; + break; + } + } + + if (!finished) + obj->rollbackProperties(cx, numProperties); + } + } + + /* We NULL out newScript *before* freeing it so the write barrier works. */ + TypeNewScript *savedNewScript = newScript; + newScript = NULL; + cx->free_(savedNewScript); + + markStateChange(cx); +} + +void +TypeObject::print(JSContext *cx) +{ + printf("%s : %s", + TypeObjectString(this), + proto ? TypeString(Type::ObjectType(proto)) : "(null)"); + + if (unknownProperties()) { + printf(" unknown"); + } else { + if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED_ARRAY)) + printf(" packed"); + if (!hasAnyFlags(OBJECT_FLAG_NON_DENSE_ARRAY)) + printf(" dense"); + if (!hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY)) + printf(" typed"); + if (hasAnyFlags(OBJECT_FLAG_UNINLINEABLE)) + printf(" uninlineable"); + if (hasAnyFlags(OBJECT_FLAG_SPECIAL_EQUALITY)) + printf(" specialEquality"); + if (hasAnyFlags(OBJECT_FLAG_ITERATED)) + printf(" iterated"); + } + + unsigned count = getPropertyCount(); + + if (count == 0) { + printf(" {}\n"); + return; + } + + printf(" {"); + + for (unsigned i = 0; i < count; i++) { + Property *prop = getProperty(i); + if (prop) { + printf("\n %s:", TypeIdString(prop->id)); + prop->types.print(cx); + } + } + + printf("\n}\n"); +} + +///////////////////////////////////////////////////////////////////// +// Type Analysis +///////////////////////////////////////////////////////////////////// + +/* + * If the bytecode immediately following code/pc is a test of the value + * pushed by code, that value should be marked as possibly void. + */ +static inline bool +CheckNextTest(jsbytecode *pc) +{ + jsbytecode *next = pc + GetBytecodeLength(pc); + switch ((JSOp)*next) { + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_NOT: + case JSOP_OR: + case JSOP_AND: + case JSOP_TYPEOF: + case JSOP_TYPEOFEXPR: + return true; + default: + /* TRAP ok here */ + return false; + } +} + +static inline TypeObject * +GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + if (!script->hasGlobal()) + return NULL; + + JSOp op = JSOp(*pc); + JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT); + + bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array)); + return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object); +} + +/* + * Detach nesting state for script from its parent, removing it entirely if it + * has no children of its own. This happens when walking type information while + * initially resolving NAME accesses, thus will not invalidate any compiler + * dependencies. + */ +static void +DetachNestingParent(JSScript *script) +{ + TypeScriptNesting *nesting = script->nesting(); + + if (!nesting || !nesting->parent) + return; + + /* Remove from parent's list of children. */ + JSScript **pscript = &nesting->parent->nesting()->children; + while ((*pscript)->nesting() != nesting) + pscript = &(*pscript)->nesting()->next; + *pscript = nesting->next; + + nesting->parent = NULL; + + /* If this nesting can have no children of its own, destroy it. */ + if (!script->isOuterFunction) + script->clearNesting(); +} + +ScriptAnalysis::NameAccess +ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + NameAccess access; + PodZero(&access); + + if (!JSID_IS_ATOM(id)) + return access; + JSAtom *atom = JSID_TO_ATOM(id); + + JSScript *script = this->script; + while (script->function() && script->nesting()) { + if (!script->ensureRanInference(cx)) + return access; + + /* + * Don't resolve names in scripts which use 'let' or 'with'. New names + * bound here can mask variables of the script itself. + * + * Also, don't resolve names in scripts which are generators. Frame + * balancing works differently for generators and we do not maintain + * active frame counts for such scripts. + */ + if (script->analysis()->addsScopeObjects() || + JSOp(*script->code) == JSOP_GENERATOR) { + return access; + } + + /* Check if the script definitely binds the identifier. */ + uintN index; + BindingKind kind = script->bindings.lookup(cx, atom, &index); + if (kind == ARGUMENT || kind == VARIABLE) { + TypeObject *obj = script->function()->getType(cx); + + if (addDependency) { + /* + * Record the dependency which compiled code has on the outer + * function being non-reentrant. + */ + if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION)) + return access; + } + + access.script = script; + access.nesting = script->nesting(); + access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index); + access.arg = (kind == ARGUMENT); + access.index = index; + return access; + } else if (kind != NONE) { + return access; + } + + /* + * The script's bindings do not contain a name for the function itself, + * don't resolve name accesses on lambdas in DeclEnv objects on the + * scope chain. + */ + if (atom == CallObjectLambdaName(script->function())) + return access; + + if (!script->nesting()->parent) + return access; + script = script->nesting()->parent; + } + + return access; +} + +/* Analyze type information for a single bytecode. */ +bool +ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, + TypeInferenceState &state) +{ + jsbytecode *pc = script->code + offset; + JSOp op = (JSOp)*pc; + + Bytecode &code = getCode(offset); + JS_ASSERT(!code.pushedTypes); + + InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset); + + unsigned defCount = GetDefCount(script, offset); + if (ExtendedDef(pc)) + defCount++; + + TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized(defCount); + if (!pushed) + return false; + PodZero(pushed, defCount); + code.pushedTypes = pushed; + + /* + * Add phi nodes introduced at this point to the list of all phi nodes in + * the script. Types for these are not generated until after the script has + * been processed, as types can flow backwards into phi nodes and the + * source sets may not exist if we try to process these eagerly. + */ + if (code.newValues) { + SlotValue *newv = code.newValues; + while (newv->slot) { + if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) { + newv++; + continue; + } + + /* + * The phi nodes at join points should all be unique, and every phi + * node created should be in the phiValues list on some bytecode. + */ + if (!state.phiNodes.append(newv->value.phiNode())) + return false; + TypeSet &types = newv->value.phiNode()->types; + InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u", + InferSpewColor(&types), &types, InferSpewColorReset(), + script->id(), offset, newv->slot); + newv++; + } + } + + /* + * Treat decomposed ops as no-ops, we will analyze the decomposed version + * instead. (We do, however, need to look at introduced phi nodes). + */ + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + return true; + + for (unsigned i = 0; i < defCount; i++) { + InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u", + InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(), + i, script->id(), offset); + } + + /* Add type constraints for the various opcodes. */ + switch (op) { + + /* Nop bytecodes. */ + case JSOP_POP: + case JSOP_NOP: + case JSOP_LOOPHEAD: + case JSOP_LOOPENTRY: + case JSOP_GOTO: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_LINENO: + case JSOP_DEFCONST: + case JSOP_LEAVEWITH: + case JSOP_LEAVEBLOCK: + case JSOP_RETRVAL: + case JSOP_ENDITER: + case JSOP_THROWING: + case JSOP_GOSUB: + case JSOP_RETSUB: + case JSOP_CONDSWITCH: + case JSOP_DEFAULT: + case JSOP_POPN: + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + case JSOP_DEFXMLNS: + case JSOP_INDEXBASE: + case JSOP_INDEXBASE1: + case JSOP_INDEXBASE2: + case JSOP_INDEXBASE3: + case JSOP_RESETBASE: + case JSOP_RESETBASE0: + case JSOP_POPV: + case JSOP_DEBUGGER: + case JSOP_SETCALL: + case JSOP_TABLESWITCH: + case JSOP_LOOKUPSWITCH: + case JSOP_TRY: + case JSOP_LABEL: + break; + + /* Bytecodes pushing values of known type. */ + case JSOP_VOID: + case JSOP_UNDEFINED: + pushed[0].addType(cx, Type::UndefinedType()); + break; + case JSOP_ZERO: + case JSOP_ONE: + case JSOP_INT8: + case JSOP_INT32: + case JSOP_UINT16: + case JSOP_UINT24: + case JSOP_BITAND: + case JSOP_BITOR: + case JSOP_BITXOR: + case JSOP_BITNOT: + case JSOP_RSH: + case JSOP_LSH: + case JSOP_URSH: + pushed[0].addType(cx, Type::Int32Type()); + break; + case JSOP_FALSE: + case JSOP_TRUE: + case JSOP_EQ: + case JSOP_NE: + case JSOP_LT: + case JSOP_LE: + case JSOP_GT: + case JSOP_GE: + case JSOP_NOT: + case JSOP_STRICTEQ: + case JSOP_STRICTNE: + case JSOP_IN: + case JSOP_INSTANCEOF: + case JSOP_DELDESC: + pushed[0].addType(cx, Type::BooleanType()); + break; + case JSOP_DOUBLE: + pushed[0].addType(cx, Type::DoubleType()); + break; + case JSOP_STRING: + case JSOP_TYPEOF: + case JSOP_TYPEOFEXPR: + case JSOP_QNAMEPART: + case JSOP_XMLTAGEXPR: + case JSOP_TOATTRVAL: + case JSOP_ADDATTRNAME: + case JSOP_ADDATTRVAL: + case JSOP_XMLELTEXPR: + pushed[0].addType(cx, Type::StringType()); + break; + case JSOP_NULL: + pushed[0].addType(cx, Type::NullType()); + break; + + case JSOP_REGEXP: + if (script->hasGlobal()) { + TypeObject *object = TypeScript::StandardType(cx, script, JSProto_RegExp); + if (!object) + return false; + pushed[0].addType(cx, Type::ObjectType(object)); + } else { + pushed[0].addType(cx, Type::UnknownType()); + } + break; + + case JSOP_OBJECT: { + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); + pushed[0].addType(cx, Type::ObjectType(obj)); + break; + } + + case JSOP_STOP: + /* If a stop is reachable then the return type may be void. */ + if (script->function()) + TypeScript::ReturnTypes(script)->addType(cx, Type::UndefinedType()); + break; + + case JSOP_OR: + case JSOP_AND: + /* OR/AND push whichever operand determined the result. */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_DUP: + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + poppedTypes(pc, 0)->addSubset(cx, &pushed[1]); + break; + + case JSOP_DUP2: + poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); + poppedTypes(pc, 0)->addSubset(cx, &pushed[1]); + poppedTypes(pc, 1)->addSubset(cx, &pushed[2]); + poppedTypes(pc, 0)->addSubset(cx, &pushed[3]); + break; + + case JSOP_SWAP: + case JSOP_PICK: { + unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc)); + /* The last popped value is the last pushed. */ + poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]); + for (unsigned i = 0; i < pickedDepth; i++) + poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]); + break; + } + + case JSOP_GETGNAME: + case JSOP_CALLGNAME: { + jsid id = GetAtomId(cx, script, pc, 0); + + TypeSet *seen = bytecodeTypes(pc); + seen->addSubset(cx, &pushed[0]); + + /* + * Normally we rely on lazy standard class initialization to fill in + * the types of global properties the script can access. In a few cases + * the method JIT will bypass this, and we need to add the types direclty. + */ + if (id == ATOM_TO_JSID(cx->runtime->atomState.typeAtoms[JSTYPE_VOID])) + seen->addType(cx, Type::UndefinedType()); + if (id == ATOM_TO_JSID(cx->runtime->atomState.NaNAtom)) + seen->addType(cx, Type::DoubleType()); + if (id == ATOM_TO_JSID(cx->runtime->atomState.InfinityAtom)) + seen->addType(cx, Type::DoubleType()); + + /* Handle as a property access. */ + PropertyAccess(cx, script, pc, script->global()->getType(cx), false, seen, id); + + if (op == JSOP_CALLGNAME) + pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType()); + + if (CheckNextTest(pc)) + pushed[0].addType(cx, Type::UndefinedType()); + break; + } + + case JSOP_NAME: + case JSOP_CALLNAME: { + TypeSet *seen = bytecodeTypes(pc); + seen->addSubset(cx, &pushed[0]); + + /* + * Try to resolve this name by walking the function's scope nesting. + * If we succeed but the accessed script has had its TypeScript purged + * in the past, we still must use a type barrier: the name access can + * be on a call object which predated the purge, and whose types might + * not be reflected in the reconstructed information. + */ + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script && !access.script->typesPurged) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + types->addSubsetBarrier(cx, script, pc, seen); + } else { + addTypeBarrier(cx, pc, seen, Type::UnknownType()); + } + + if (op == JSOP_CALLNAME) + pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType()); + break; + } + + case JSOP_BINDGNAME: + case JSOP_BINDNAME: + break; + + case JSOP_SETGNAME: { + jsid id = GetAtomId(cx, script, pc, 0); + PropertyAccess(cx, script, pc, script->global()->getType(cx), + true, poppedTypes(pc, 0), id); + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + + case JSOP_SETNAME: { + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + poppedTypes(pc, 0)->addSubset(cx, types); + } else { + cx->compartment->types.monitorBytecode(cx, script, offset); + } + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + + case JSOP_SETCONST: + cx->compartment->types.monitorBytecode(cx, script, offset); + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_GETXPROP: + case JSOP_GETFCSLOT: + case JSOP_CALLFCSLOT: { + TypeSet *seen = bytecodeTypes(pc); + addTypeBarrier(cx, pc, seen, Type::UnknownType()); + seen->addSubset(cx, &pushed[0]); + if (op == JSOP_CALLFCSLOT) + pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType()); + break; + } + + case JSOP_GETARG: + case JSOP_CALLARG: + case JSOP_GETLOCAL: + case JSOP_CALLLOCAL: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + /* + * Normally these opcodes don't pop anything, but they are given + * an extended use holding the variable's SSA value before the + * access. Use the types from here. + */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + } else if (slot < TotalSlots(script)) { + TypeSet *types = TypeScript::SlotTypes(script, slot); + types->addSubset(cx, &pushed[0]); + } else { + /* Local 'let' variable. Punt on types for these, for now. */ + pushed[0].addType(cx, Type::UnknownType()); + } + if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL) + pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType()); + break; + } + + case JSOP_SETARG: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (!trackSlot(slot) && slot < TotalSlots(script)) { + TypeSet *types = TypeScript::SlotTypes(script, slot); + poppedTypes(pc, 0)->addSubset(cx, types); + } + + /* + * For assignments to non-escaping locals/args, we don't need to update + * the possible types of the var, as for each read of the var SSA gives + * us the writes that could have produced that read. + */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: { + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + poppedTypes(pc, 0)->addArith(cx, &pushed[0]); + } else if (slot < TotalSlots(script)) { + TypeSet *types = TypeScript::SlotTypes(script, slot); + types->addArith(cx, types); + types->addSubset(cx, &pushed[0]); + } else { + pushed[0].addType(cx, Type::UnknownType()); + } + break; + } + + case JSOP_ARGUMENTS: { + /* Compute a precise type only when we know the arguments won't escape. */ + TypeObject *funType = script->function()->getType(cx); + if (funType->unknownProperties() || funType->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) { + pushed[0].addType(cx, Type::UnknownType()); + break; + } + TypeSet *types = funType->getProperty(cx, JSID_EMPTY, false); + if (!types) + break; + types->addLazyArguments(cx, &pushed[0]); + pushed[0].addType(cx, Type::LazyArgsType()); + break; + } + + case JSOP_SETPROP: + case JSOP_SETMETHOD: { + jsid id = GetAtomId(cx, script, pc, 0); + poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id); + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + + case JSOP_LENGTH: + case JSOP_GETPROP: + case JSOP_CALLPROP: { + jsid id = GetAtomId(cx, script, pc, 0); + TypeSet *seen = script->analysis()->bytecodeTypes(pc); + + poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id); + if (op == JSOP_CALLPROP) + poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id); + + seen->addSubset(cx, &pushed[0]); + if (CheckNextTest(pc)) + pushed[0].addType(cx, Type::UndefinedType()); + break; + } + + /* + * We only consider ELEM accesses on integers below. Any element access + * which is accessing a non-integer property must be monitored. + */ + + case JSOP_GETELEM: + case JSOP_CALLELEM: { + TypeSet *seen = script->analysis()->bytecodeTypes(pc); + + poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID); + + seen->addSubset(cx, &pushed[0]); + if (op == JSOP_CALLELEM) + pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), poppedTypes(pc, 1)); + if (CheckNextTest(pc)) + pushed[0].addType(cx, Type::UndefinedType()); + break; + } + + case JSOP_SETELEM: + poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0)); + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_TOID: + /* + * This is only used for element inc/dec ops; any id produced which + * is not an integer must be monitored. + */ + pushed[0].addType(cx, Type::Int32Type()); + break; + + case JSOP_THIS: + TypeScript::ThisTypes(script)->addTransformThis(cx, script, &pushed[0]); + break; + + case JSOP_RETURN: + case JSOP_SETRVAL: + if (script->function()) + poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script)); + break; + + case JSOP_ADD: + poppedTypes(pc, 0)->addArith(cx, &pushed[0], poppedTypes(pc, 1)); + poppedTypes(pc, 1)->addArith(cx, &pushed[0], poppedTypes(pc, 0)); + break; + + case JSOP_SUB: + case JSOP_MUL: + case JSOP_MOD: + case JSOP_DIV: + poppedTypes(pc, 0)->addArith(cx, &pushed[0]); + poppedTypes(pc, 1)->addArith(cx, &pushed[0]); + break; + + case JSOP_NEG: + case JSOP_POS: + poppedTypes(pc, 0)->addArith(cx, &pushed[0]); + break; + + case JSOP_LAMBDA: + case JSOP_LAMBDA_FC: + case JSOP_DEFFUN: + case JSOP_DEFLOCALFUN: + case JSOP_DEFLOCALFUN_FC: { + unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0; + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + off)); + + TypeSet *res = NULL; + if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) { + res = &pushed[0]; + } else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) { + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + res = &pushed[0]; + } else { + /* Should not see 'let' vars here. */ + JS_ASSERT(slot < TotalSlots(script)); + res = TypeScript::SlotTypes(script, slot); + } + } + + if (res) { + if (script->hasGlobal()) + res->addType(cx, Type::ObjectType(obj)); + else + res->addType(cx, Type::UnknownType()); + } else { + cx->compartment->types.monitorBytecode(cx, script, offset); + } + break; + } + + case JSOP_DEFVAR: + break; + + case JSOP_CALL: + case JSOP_EVAL: + case JSOP_FUNCALL: + case JSOP_FUNAPPLY: + case JSOP_NEW: { + TypeSet *seen = script->analysis()->bytecodeTypes(pc); + seen->addSubset(cx, &pushed[0]); + + /* Construct the base call information about this site. */ + unsigned argCount = GetUseCount(script, offset) - 2; + TypeCallsite *callsite = cx->typeLifoAlloc().new_( + cx, script, pc, op == JSOP_NEW, argCount); + if (!callsite || (argCount && !callsite->argumentTypes)) { + cx->compartment->types.setPendingNukeTypes(cx); + break; + } + callsite->thisTypes = poppedTypes(pc, argCount); + callsite->returnTypes = seen; + + for (unsigned i = 0; i < argCount; i++) + callsite->argumentTypes[i] = poppedTypes(pc, argCount - 1 - i); + + /* + * Mark FUNCALL and FUNAPPLY sites as monitored. The method JIT may + * lower these into normal calls, and we need to make sure the + * callee's argument types are checked on entry. + */ + if (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY) + cx->compartment->types.monitorBytecode(cx, script, pc - script->code); + + poppedTypes(pc, argCount + 1)->addCall(cx, callsite); + break; + } + + case JSOP_NEWINIT: + case JSOP_NEWARRAY: + case JSOP_NEWOBJECT: { + TypeObject *initializer = GetInitializerType(cx, script, pc); + TypeSet *types = script->analysis()->bytecodeTypes(pc); + if (script->hasGlobal()) { + if (!initializer) + return false; + types->addType(cx, Type::ObjectType(initializer)); + } else { + JS_ASSERT(!initializer); + types->addType(cx, Type::UnknownType()); + } + types->addSubset(cx, &pushed[0]); + break; + } + + case JSOP_ENDINIT: + break; + + case JSOP_INITELEM: { + const SSAValue &objv = poppedValue(pc, 2); + jsbytecode *initpc = script->code + objv.pushedOffset(); + TypeObject *initializer = GetInitializerType(cx, script, initpc); + + if (initializer) { + pushed[0].addType(cx, Type::ObjectType(initializer)); + if (!initializer->unknownProperties()) { + /* + * Assume the initialized element is an integer. INITELEM can be used + * for doubles which don't map to the JSID_VOID property, which must + * be caught with dynamic monitoring. + */ + TypeSet *types = initializer->getProperty(cx, JSID_VOID, true); + if (!types) + return false; + if (state.hasGetSet) { + types->addType(cx, Type::UnknownType()); + } else if (state.hasHole) { + if (!initializer->unknownProperties()) + initializer->setFlags(cx, OBJECT_FLAG_NON_PACKED_ARRAY); + } else { + poppedTypes(pc, 0)->addSubset(cx, types); + } + } + } else { + pushed[0].addType(cx, Type::UnknownType()); + } + state.hasGetSet = false; + state.hasHole = false; + break; + } + + case JSOP_GETTER: + case JSOP_SETTER: + state.hasGetSet = true; + break; + + case JSOP_HOLE: + state.hasHole = true; + break; + + case JSOP_INITPROP: + case JSOP_INITMETHOD: { + const SSAValue &objv = poppedValue(pc, 1); + jsbytecode *initpc = script->code + objv.pushedOffset(); + TypeObject *initializer = GetInitializerType(cx, script, initpc); + + if (initializer) { + pushed[0].addType(cx, Type::ObjectType(initializer)); + if (!initializer->unknownProperties()) { + jsid id = GetAtomId(cx, script, pc, 0); + TypeSet *types = initializer->getProperty(cx, id, true); + if (!types) + return false; + if (id == id___proto__(cx) || id == id_prototype(cx)) + cx->compartment->types.monitorBytecode(cx, script, offset); + else if (state.hasGetSet) + types->addType(cx, Type::UnknownType()); + else + poppedTypes(pc, 0)->addSubset(cx, types); + } + } else { + pushed[0].addType(cx, Type::UnknownType()); + } + state.hasGetSet = false; + JS_ASSERT(!state.hasHole); + break; + } + + case JSOP_ENTERWITH: + case JSOP_ENTERBLOCK: + case JSOP_ENTERLET0: + /* + * Scope lookups can occur on the values being pushed here. We don't track + * the value or its properties, and just monitor all name opcodes in the + * script. + */ + break; + + case JSOP_ENTERLET1: + /* + * JSOP_ENTERLET1 enters a let block with an unrelated value on top of + * the stack (such as the condition to a switch) whose constraints must + * be propagated. The other values are ignored for the same reason as + * JSOP_ENTERLET0. + */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[defCount - 1]); + break; + + case JSOP_ITER: { + /* + * Use a per-script type set to unify the possible target types of all + * 'for in' or 'for each' loops in the script. We need to mark the + * value pushed by the ITERNEXT appropriately, but don't track the SSA + * information to connect that ITERNEXT with the appropriate ITER. + * This loses some precision when a script mixes 'for in' and + * 'for each' loops together, oh well. + */ + if (!state.forTypes) { + state.forTypes = TypeSet::make(cx, "forTypes"); + if (!state.forTypes) + return false; + } + + if (GET_UINT8(pc) & JSITER_FOREACH) + state.forTypes->addType(cx, Type::UnknownType()); + else + state.forTypes->addType(cx, Type::StringType()); + break; + } + + case JSOP_ITERNEXT: + state.forTypes->addSubset(cx, &pushed[0]); + break; + + case JSOP_MOREITER: + pushed[1].addType(cx, Type::BooleanType()); + break; + + case JSOP_ENUMELEM: + case JSOP_ENUMCONSTELEM: + case JSOP_ARRAYPUSH: + cx->compartment->types.monitorBytecode(cx, script, offset); + break; + + case JSOP_THROW: + /* There will be a monitor on the bytecode catching the exception. */ + break; + + case JSOP_FINALLY: + /* Pushes information about whether an exception was thrown. */ + break; + + case JSOP_IMPLICITTHIS: + case JSOP_EXCEPTION: + pushed[0].addType(cx, Type::UnknownType()); + break; + + case JSOP_DELPROP: + case JSOP_DELELEM: + case JSOP_DELNAME: + pushed[0].addType(cx, Type::BooleanType()); + break; + + case JSOP_LEAVEBLOCKEXPR: + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_LEAVEFORLETIN: + break; + + case JSOP_CASE: + poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); + break; + + case JSOP_GENERATOR: + if (script->function()) { + if (script->hasGlobal()) { + JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx); + if (!proto) + return false; + TypeObject *object = proto->getNewType(cx); + if (!object) + return false; + TypeScript::ReturnTypes(script)->addType(cx, Type::ObjectType(object)); + } else { + TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType()); + } + } + break; + + case JSOP_YIELD: + pushed[0].addType(cx, Type::UnknownType()); + break; + + case JSOP_CALLXMLNAME: + pushed[1].addType(cx, Type::UnknownType()); + /* FALLTHROUGH */ + + case JSOP_XMLNAME: + pushed[0].addType(cx, Type::UnknownType()); + break; + + case JSOP_SETXMLNAME: + cx->compartment->types.monitorBytecode(cx, script, offset); + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_BINDXMLNAME: + break; + + case JSOP_TOXML: + case JSOP_TOXMLLIST: + case JSOP_XMLPI: + case JSOP_XMLCDATA: + case JSOP_XMLCOMMENT: + case JSOP_DESCENDANTS: + case JSOP_TOATTRNAME: + case JSOP_QNAMECONST: + case JSOP_QNAME: + case JSOP_ANYNAME: + case JSOP_GETFUNNS: + pushed[0].addType(cx, Type::UnknownType()); + break; + + case JSOP_FILTER: + /* Note: the second value pushed by filter is a hole, and not modelled. */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + + case JSOP_ENDFILTER: + poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); + break; + + case JSOP_CALLEE: + if (script->hasGlobal()) + pushed[0].addType(cx, Type::ObjectType(script->function())); + else + pushed[0].addType(cx, Type::UnknownType()); + break; + + default: + /* Display fine-grained debug information first */ + fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script->id(), offset); + TypeFailure(cx, "Unknown bytecode %02x", op); + } + + return true; +} + +void +ScriptAnalysis::analyzeTypes(JSContext *cx) +{ + JS_ASSERT(!ranInference()); + + if (OOM()) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + /* + * Refuse to analyze the types in a script which is compileAndGo but is + * running against a global with a cleared scope. Per GlobalObject::clear, + * we won't be running anymore compileAndGo code against the global + * (moreover, after clearing our analysis results will be wrong for the + * script and trying to reanalyze here can cause reentrance problems if we + * try to reinitialize standard classes that were cleared). + */ + if (script->hasClearedGlobal()) + return; + + if (!ranSSA()) { + analyzeSSA(cx); + if (failed()) + return; + } + + /* + * Set this early to avoid reentrance. Any failures are OOMs, and will nuke + * all types in the compartment. + */ + ranInference_ = true; + + /* Make sure the initial type set of all local vars includes void. */ + for (unsigned i = 0; i < script->nfixed; i++) + TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType()); + + TypeScriptNesting *nesting = script->function() ? script->nesting() : NULL; + if (nesting && nesting->parent) { + /* + * Check whether NAME accesses can be resolved in parent scopes, and + * detach from the parent if so. Even if outdated activations of this + * function are live when the parent is called again, we do not need to + * consider this reentrance as no state in the parent will be used. + */ + if (!nesting->parent->ensureRanInference(cx)) + return; + + bool detached = false; + + /* Don't track for leaf scripts which have no free variables. */ + if (!usesScopeChain() && !script->isOuterFunction) { + DetachNestingParent(script); + detached = true; + } + + /* + * If the names bound by the script are extensible (DEFFUN, EVAL, ...), + * don't resolve NAME accesses into the parent. + */ + if (!detached && extendsScope()) { + DetachNestingParent(script); + detached = true; + } + + + if (!detached) { + /* + * Don't track for parents which add call objects or are generators, + * don't resolve NAME accesses into the parent. + */ + if (nesting->parent->analysis()->addsScopeObjects() || + JSOp(*nesting->parent->code) == JSOP_GENERATOR) + { + DetachNestingParent(script); + } + } + } + + TypeInferenceState state(cx); + + unsigned offset = 0; + while (offset < script->length) { + Bytecode *code = maybeCode(offset); + + jsbytecode *pc = script->code + offset; + + if (code && !analyzeTypesBytecode(cx, offset, state)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + offset += GetBytecodeLength(pc); + } + + for (unsigned i = 0; i < state.phiNodes.length(); i++) { + SSAPhiNode *node = state.phiNodes[i]; + for (unsigned j = 0; j < node->length; j++) { + const SSAValue &v = node->options[j]; + getValueTypes(v)->addSubset(cx, &node->types); + } + } + + /* + * Replay any dynamic type results which have been generated for the script + * either because we ran the interpreter some before analyzing or because + * we are reanalyzing after a GC. + */ + TypeResult *result = script->types->dynamicList; + while (result) { + if (result->offset != UINT32_MAX) { + pushedTypes(result->offset)->addType(cx, result->type); + } else { + /* Custom for-in loop iteration has happened in this script. */ + state.forTypes->addType(cx, Type::UnknownType()); + } + result = result->next; + } + + if (!script->usesArguments || script->createdArgs) + return; + + /* + * Do additional analysis to determine whether the arguments object in the + * script can escape. + */ + + /* + * Note: don't check for strict mode code here, even though arguments + * accesses in such scripts will always be deoptimized. These scripts can + * have a JSOP_ARGUMENTS in their prologue which the usesArguments check + * above does not account for. We filter in the interpreter and JITs + * themselves. + */ + if (script->function()->isHeavyweight() || cx->compartment->debugMode() || localsAliasStack()) { + MarkArgumentsCreated(cx, script); + return; + } + + offset = 0; + while (offset < script->length) { + Bytecode *code = maybeCode(offset); + jsbytecode *pc = script->code + offset; + + if (code && JSOp(*pc) == JSOP_ARGUMENTS) { + Vector seen(cx); + if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) { + MarkArgumentsCreated(cx, script); + return; + } + } + + offset += GetBytecodeLength(pc); + } + + /* + * The VM is now free to use the arguments in this script lazily. If we end + * up creating an arguments object for the script in the future or regard + * the arguments as escaping, we need to walk the stack and replace lazy + * arguments objects with actual arguments objects. + */ + script->usedLazyArgs = true; +} + +bool +ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector *seen) +{ + /* + * trackUseChain is false for initial values of variables, which + * cannot hold the script's arguments object. + */ + if (!trackUseChain(v)) + return true; + + for (unsigned i = 0; i < seen->length(); i++) { + if (v == (*seen)[i]) + return true; + } + if (!seen->append(v)) { + cx->compartment->types.setPendingNukeTypes(cx); + return false; + } + + SSAUseChain *use = useChain(v); + while (use) { + if (!followEscapingArguments(cx, use, seen)) + return false; + use = use->next; + } + + return true; +} + +bool +ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector *seen) +{ + if (!use->popped) + return followEscapingArguments(cx, SSAValue::PhiValue(use->offset, use->u.phi), seen); + + jsbytecode *pc = script->code + use->offset; + uint32_t which = use->u.which; + + JSOp op = JSOp(*pc); + + if (op == JSOP_POP || op == JSOP_POPN) + return true; + + /* Allow GETELEM and LENGTH on arguments objects that don't escape. */ + + /* + * Note: if the element index is not an integer we will mark the arguments + * as escaping at the access site. + */ + if (op == JSOP_GETELEM && which == 1) + return true; + + if (op == JSOP_LENGTH) + return true; + + /* Allow assignments to non-closed locals (but not arguments). */ + + if (op == JSOP_SETLOCAL) { + uint32_t slot = GetBytecodeSlot(script, pc); + if (!trackSlot(slot)) + return false; + if (!followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen)) + return false; + return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen); + } + + if (op == JSOP_GETLOCAL) + return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen); + + return false; +} + +bool +ScriptAnalysis::integerOperation(JSContext *cx, jsbytecode *pc) +{ + JS_ASSERT(uint32_t(pc - script->code) < script->length); + + switch (JSOp(*pc)) { + + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: { + if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + uint32_t slot = GetBytecodeSlot(script, pc); + if (trackSlot(slot)) { + if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + } + return true; + } + + case JSOP_ADD: + case JSOP_SUB: + case JSOP_MUL: + case JSOP_DIV: + if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + if (poppedTypes(pc, 1)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + return true; + + default: + return true; + } +} + +/* + * Persistent constraint clearing out newScript and definite properties from + * an object should a property on another object get a setter. + */ +class TypeConstraintClearDefiniteSetter : public TypeConstraint +{ +public: + TypeObject *object; + + TypeConstraintClearDefiniteSetter(TypeObject *object) + : TypeConstraint("clearDefiniteSetter"), object(object) + {} + + void newPropertyState(JSContext *cx, TypeSet *source) + { + if (!object->newScript) + return; + /* + * Clear out the newScript shape and definite property information from + * an object if the source type set could be a setter or could be + * non-writable, both of which are indicated by the source type set + * being marked as configured. + */ + if (!(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) && source->isOwnProperty(true)) + object->clearNewScript(cx); + } + + void newType(JSContext *cx, TypeSet *source, Type type) {} +}; + +/* + * Constraint which clears definite properties on an object should a type set + * contain any types other than a single object. + */ +class TypeConstraintClearDefiniteSingle : public TypeConstraint +{ +public: + TypeObject *object; + + TypeConstraintClearDefiniteSingle(TypeObject *object) + : TypeConstraint("clearDefiniteSingle"), object(object) + {} + + void newType(JSContext *cx, TypeSet *source, Type type) { + if (object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) + return; + + if (source->baseFlags() || source->getObjectCount() > 1) + object->clearNewScript(cx); + } +}; + +static bool +AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSObject **pbaseobj, + Vector *initializerList) +{ + /* + * When invoking 'new' on the specified script, try to find some properties + * which will definitely be added to the created object before it has a + * chance to escape and be accessed elsewhere. + * + * Returns true if the entire script was analyzed (pbaseobj has been + * preserved), false if we had to bail out part way through (pbaseobj may + * have been cleared). + */ + + if (initializerList->length() > 50) { + /* + * Bail out on really long initializer lists (far longer than maximum + * number of properties we can track), we may be recursing. + */ + return false; + } + + JSScript *script = fun->script(); + JS_ASSERT(!script->isInnerFunction); + + if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) { + *pbaseobj = NULL; + cx->compartment->types.setPendingNukeTypes(cx); + return false; + } + + if (script->hasClearedGlobal()) + return false; + + ScriptAnalysis *analysis = script->analysis(); + + /* + * Offset of the last bytecode which popped 'this' and which we have + * processed. For simplicity, we scan for places where 'this' is pushed + * and immediately analyze the place where that pushed value is popped. + * This runs the risk of doing things out of order, if the script looks + * something like 'this.f = (this.g = ...)', so we watch and bail out if + * a 'this' is pushed before the previous 'this' value was popped. + */ + uint32_t lastThisPopped = 0; + + unsigned nextOffset = 0; + while (nextOffset < script->length) { + unsigned offset = nextOffset; + jsbytecode *pc = script->code + offset; + + JSOp op = JSOp(*pc); + + nextOffset += GetBytecodeLength(pc); + + Bytecode *code = analysis->maybeCode(pc); + if (!code) + continue; + + /* + * End analysis after the first return statement from the script, + * returning success if the return is unconditional. + */ + if (op == JSOP_RETURN || op == JSOP_STOP || op == JSOP_RETRVAL) { + if (offset < lastThisPopped) { + *pbaseobj = NULL; + return false; + } + return code->unconditional; + } + + /* 'this' can escape through a call to eval. */ + if (op == JSOP_EVAL) { + if (offset < lastThisPopped) + *pbaseobj = NULL; + return false; + } + + /* + * We are only interested in places where 'this' is popped. The new + * 'this' value cannot escape and be accessed except through such uses. + */ + if (op != JSOP_THIS) + continue; + + /* Maintain ordering property on how 'this' is used, as described above. */ + if (offset < lastThisPopped) { + *pbaseobj = NULL; + return false; + } + + SSAValue thisv = SSAValue::PushedValue(offset, 0); + SSAUseChain *uses = analysis->useChain(thisv); + + JS_ASSERT(uses); + if (uses->next || !uses->popped) { + /* 'this' value popped in more than one place. */ + return false; + } + + lastThisPopped = uses->offset; + + /* Only handle 'this' values popped in unconditional code. */ + Bytecode *poppedCode = analysis->maybeCode(uses->offset); + if (!poppedCode || !poppedCode->unconditional) + return false; + + pc = script->code + uses->offset; + op = JSOp(*pc); + + JSObject *obj = *pbaseobj; + + if (op == JSOP_SETPROP && uses->u.which == 1) { + /* + * Don't use GetAtomId here, we need to watch for SETPROP on + * integer properties and bail out. We can't mark the aggregate + * JSID_VOID type property as being in a definite slot. + */ + unsigned index = js_GetIndexFromBytecode(script, pc, 0); + jsid id = ATOM_TO_JSID(script->getAtom(index)); + if (MakeTypeId(cx, id) != id) + return false; + if (id == id_prototype(cx) || id == id___proto__(cx) || id == id_constructor(cx)) + return false; + + /* + * Ensure that if the properties named here could have a setter or + * a permanent property in any transitive prototype, the definite + * properties get cleared from the shape. + */ + JSObject *parent = type->proto; + while (parent) { + TypeObject *parentObject = parent->getType(cx); + if (parentObject->unknownProperties()) + return false; + TypeSet *parentTypes = parentObject->getProperty(cx, id, false); + if (!parentTypes || parentTypes->isOwnProperty(true)) + return false; + parentTypes->add(cx, cx->typeLifoAlloc().new_(type)); + parent = parent->getProto(); + } + + unsigned slotSpan = obj->slotSpan(); + if (!DefineNativeProperty(cx, obj, id, UndefinedValue(), NULL, NULL, + JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) { + cx->compartment->types.setPendingNukeTypes(cx); + *pbaseobj = NULL; + return false; + } + + if (obj->inDictionaryMode()) { + *pbaseobj = NULL; + return false; + } + + if (obj->slotSpan() == slotSpan) { + /* Set a duplicate property. */ + return false; + } + + TypeNewScript::Initializer setprop(TypeNewScript::Initializer::SETPROP, uses->offset); + if (!initializerList->append(setprop)) { + cx->compartment->types.setPendingNukeTypes(cx); + *pbaseobj = NULL; + return false; + } + + if (obj->slotSpan() >= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) { + /* Maximum number of definite properties added. */ + return false; + } + } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) { + /* + * Passed as the first parameter to Function.call. Follow control + * into the callee, and add any definite properties it assigns to + * the object as well. :TODO: This is narrow pattern matching on + * the inheritance patterns seen in the v8-deltablue benchmark, and + * needs robustness against other ways initialization can cross + * script boundaries. + * + * Add constraints ensuring we are calling Function.call on a + * particular script, removing definite properties from the result + */ + + /* Callee/this must have been pushed by a CALLPROP. */ + SSAValue calleev = analysis->poppedValue(pc, GET_ARGC(pc) + 1); + if (calleev.kind() != SSAValue::PUSHED) + return false; + jsbytecode *calleepc = script->code + calleev.pushedOffset(); + if (JSOp(*calleepc) != JSOP_CALLPROP) + return false; + + /* + * This code may not have run yet, break any type barriers involved + * in performing the call (for the greater good!). + */ + analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0)); + analysis->breakTypeBarriers(cx, calleepc - script->code, true); + + TypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1); + TypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc)); + + /* Need to definitely be calling Function.call on a specific script. */ + JSObject *funcallObj = funcallTypes->getSingleton(cx, false); + JSObject *scriptObj = scriptTypes->getSingleton(cx, false); + if (!funcallObj || !scriptObj || !scriptObj->isFunction() || + !scriptObj->toFunction()->isInterpreted()) { + return false; + } + + JSFunction *function = scriptObj->toFunction(); + JS_ASSERT(!function->script()->isInnerFunction); + + /* + * Generate constraints to clear definite properties from the type + * should the Function.call or callee itself change in the future. + */ + funcallTypes->add(cx, + cx->typeLifoAlloc().new_(type)); + scriptTypes->add(cx, + cx->typeLifoAlloc().new_(type)); + + TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset); + if (!initializerList->append(pushframe)) { + cx->compartment->types.setPendingNukeTypes(cx); + *pbaseobj = NULL; + return false; + } + + if (!AnalyzeNewScriptProperties(cx, type, function, + pbaseobj, initializerList)) { + return false; + } + + TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0); + if (!initializerList->append(popframe)) { + cx->compartment->types.setPendingNukeTypes(cx); + *pbaseobj = NULL; + return false; + } + + /* + * The callee never lets the 'this' value escape, continue looking + * for definite properties in the remainder of this script. + */ + } else { + /* Unhandled use of 'this'. */ + return false; + } + } + + /* Will have hit a STOP or similar, unless the script always throws. */ + return true; +} + +/* + * Either make the newScript information for type when it is constructed + * by the specified script, or regenerate the constraints for an existing + * newScript on the type after they were cleared by a GC. + */ +static void +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) +{ + if (type->unknownProperties() || fun->script()->isInnerFunction) + return; + + /* Strawman object to add properties to and watch for duplicates. */ + JSObject *baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16); + if (!baseobj) { + if (type->newScript) + type->clearNewScript(cx); + return; + } + + Vector initializerList(cx); + AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList); + if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) { + if (type->newScript) + type->clearNewScript(cx); + return; + } + + /* + * If the type already has a new script, we are just regenerating the type + * constraints and don't need to make another TypeNewScript. Make sure that + * the properties added to baseobj match the type's definite properties. + */ + if (type->newScript) { + if (!type->matchDefiniteProperties(baseobj)) + type->clearNewScript(cx); + return; + } + + gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); + + /* We should not have overflowed the maximum number of fixed slots for an object. */ + JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan()); + + TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0); + + /* + * The base object may have been created with a different finalize kind + * than we will use for subsequent new objects. Generate an object with the + * appropriate final shape. + */ + baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, + baseobj->lastProperty()); + if (!baseobj || + !type->addDefiniteProperties(cx, baseobj) || + !initializerList.append(done)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + size_t numBytes = sizeof(TypeNewScript) + + (initializerList.length() * sizeof(TypeNewScript::Initializer)); + type->newScript = (TypeNewScript *) cx->calloc_(numBytes); + if (!type->newScript) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + type->newScript->fun = fun; + type->newScript->allocKind = kind; + type->newScript->shape = baseobj->lastProperty(); + + type->newScript->initializerList = (TypeNewScript::Initializer *) + ((char *) type->newScript.get() + sizeof(TypeNewScript)); + PodCopy(type->newScript->initializerList, initializerList.begin(), initializerList.length()); +} + +///////////////////////////////////////////////////////////////////// +// Printing +///////////////////////////////////////////////////////////////////// + +void +ScriptAnalysis::printTypes(JSContext *cx) +{ + AutoEnterAnalysis enter(script->compartment()); + TypeCompartment *compartment = &script->compartment()->types; + + /* + * Check if there are warnings for used values with unknown types, and build + * statistics about the size of type sets found for stack values. + */ + for (unsigned offset = 0; offset < script->length; offset++) { + if (!maybeCode(offset)) + continue; + + jsbytecode *pc = script->code + offset; + + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + continue; + + unsigned defCount = GetDefCount(script, offset); + if (!defCount) + continue; + + for (unsigned i = 0; i < defCount; i++) { + TypeSet *types = pushedTypes(offset, i); + + if (types->unknown()) { + compartment->typeCountOver++; + continue; + } + + unsigned typeCount = 0; + + if (types->hasAnyFlag(TYPE_FLAG_ANYOBJECT) || types->getObjectCount() != 0) + typeCount++; + for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) { + if (types->hasAnyFlag(flag)) + typeCount++; + } + + /* + * Adjust the type counts for floats: values marked as floats + * are also marked as ints by the inference, but for counting + * we don't consider these to be separate types. + */ + if (types->hasAnyFlag(TYPE_FLAG_DOUBLE)) { + JS_ASSERT(types->hasAnyFlag(TYPE_FLAG_INT32)); + typeCount--; + } + + if (typeCount > TypeCompartment::TYPE_COUNT_LIMIT) { + compartment->typeCountOver++; + } else if (typeCount == 0) { + /* Ignore values without types, this may be unreached code. */ + } else { + compartment->typeCounts[typeCount-1]++; + } + } + } + +#ifdef DEBUG + + if (script->function()) + printf("Function"); + else if (script->isCachedEval) + printf("Eval"); + else + printf("Main"); + printf(" #%u %s (line %d):\n", script->id(), script->filename, script->lineno); + + printf("locals:"); + printf("\n return:"); + TypeScript::ReturnTypes(script)->print(cx); + printf("\n this:"); + TypeScript::ThisTypes(script)->print(cx); + + for (unsigned i = 0; script->function() && i < script->function()->nargs; i++) { + printf("\n arg%u:", i); + TypeScript::ArgTypes(script, i)->print(cx); + } + for (unsigned i = 0; i < script->nfixed; i++) { + if (!trackSlot(LocalSlot(script, i))) { + printf("\n local%u:", i); + TypeScript::LocalTypes(script, i)->print(cx); + } + } + printf("\n"); + + for (unsigned offset = 0; offset < script->length; offset++) { + if (!maybeCode(offset)) + continue; + + jsbytecode *pc = script->code + offset; + + PrintBytecode(cx, script, pc); + + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + continue; + + if (js_CodeSpec[*pc].format & JOF_TYPESET) { + TypeSet *types = script->analysis()->bytecodeTypes(pc); + printf(" typeset %d:", (int) (types - script->types->typeArray())); + types->print(cx); + printf("\n"); + } + + unsigned defCount = GetDefCount(script, offset); + for (unsigned i = 0; i < defCount; i++) { + printf(" type %d:", i); + pushedTypes(offset, i)->print(cx); + printf("\n"); + } + + if (getCode(offset).monitoredTypes) + printf(" monitored\n"); + + TypeBarrier *barrier = getCode(offset).typeBarriers; + if (barrier != NULL) { + printf(" barrier:"); + while (barrier) { + printf(" %s", TypeString(barrier->type)); + barrier = barrier->next; + } + printf("\n"); + } + } + + printf("\n"); + +#endif /* DEBUG */ + +} + +///////////////////////////////////////////////////////////////////// +// Interface functions +///////////////////////////////////////////////////////////////////// + +namespace js { +namespace types { + +void +MarkIteratorUnknownSlow(JSContext *cx) +{ + /* Check whether we are actually at an ITER opcode. */ + + jsbytecode *pc; + JSScript *script = cx->stack.currentScript(&pc); + if (!script || !pc) + return; + + if (JSOp(*pc) != JSOP_ITER) + return; + + AutoEnterTypeInference enter(cx); + + /* + * This script is iterating over an actual Iterator or Generator object, or + * an object with a custom __iterator__ hook. In such cases 'for in' loops + * can produce values other than strings, and the types of the ITER opcodes + * in the script need to be updated. During analysis this is done with the + * forTypes in the analysis state, but we don't keep a pointer to this type + * set and need to scan the script to fix affected opcodes. + */ + + TypeResult *result = script->types->dynamicList; + while (result) { + if (result->offset == UINT32_MAX) { + /* Already know about custom iterators used in this script. */ + JS_ASSERT(result->type.isUnknown()); + return; + } + result = result->next; + } + + InferSpew(ISpewOps, "externalType: customIterator #%u", script->id()); + + result = cx->new_(UINT32_MAX, Type::UnknownType()); + if (!result) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + result->next = script->types->dynamicList; + script->types->dynamicList = result; + + if (!script->hasAnalysis() || !script->analysis()->ranInference()) + return; + + ScriptAnalysis *analysis = script->analysis(); + + for (unsigned i = 0; i < script->length; i++) { + jsbytecode *pc = script->code + i; + if (!analysis->maybeCode(pc)) + continue; + if (JSOp(*pc) == JSOP_ITERNEXT) + analysis->pushedTypes(pc, 0)->addType(cx, Type::UnknownType()); + } + + /* Trigger recompilation of any inline callers. */ + if (script->function() && !script->function()->hasLazyType()) + ObjectStateChange(cx, script->function()->type(), false, true); +} + +void +TypeMonitorCallSlow(JSContext *cx, JSObject *callee, + const CallArgs &args, bool constructing) +{ + unsigned nargs = callee->toFunction()->nargs; + JSScript *script = callee->toFunction()->script(); + + if (!constructing) + TypeScript::SetThis(cx, script, args.thisv()); + + /* + * Add constraints going up to the minimum of the actual and formal count. + * If there are more actuals than formals the later values can only be + * accessed through the arguments object, which is monitored. + */ + unsigned arg = 0; + for (; arg < args.length() && arg < nargs; arg++) + TypeScript::SetArgument(cx, script, arg, args[arg]); + + /* Watch for fewer actuals than formals to the call. */ + for (; arg < nargs; arg++) + TypeScript::SetArgument(cx, script, arg, UndefinedValue()); +} + +static inline bool +IsAboutToBeFinalized(TypeObjectKey *key) +{ + /* Mask out the low bit indicating whether this is a type or JS object. */ + return !reinterpret_cast(uintptr_t(key) & ~1)->isMarked(); +} + +void +TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + AutoEnterTypeInference enter(cx); + + /* Directly update associated type sets for applicable bytecodes. */ + if (js_CodeSpec[*pc].format & JOF_TYPESET) { + if (!script->ensureRanAnalysis(cx, NULL)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + TypeSet *types = script->analysis()->bytecodeTypes(pc); + if (!types->hasType(type)) { + InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s", + script->id(), pc - script->code, TypeString(type)); + types->addType(cx, type); + } + return; + } + + /* + * For inc/dec ops, we need to go back and reanalyze the affected opcode + * taking the overflow into account. We won't see an explicit adjustment + * of the type of the thing being inc/dec'ed, nor will adding TYPE_DOUBLE to + * the pushed value affect that type. + */ + JSOp op = JSOp(*pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + if (cs->format & (JOF_INC | JOF_DEC)) { + switch (op) { + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: { + /* + * Just mark the slot's type as holding the new type. This captures + * the effect if the slot is not being tracked, and if the slot + * doesn't escape we will update the pushed types below to capture + * the slot's value after this write. + */ + uint32_t slot = GetBytecodeSlot(script, pc); + if (slot < TotalSlots(script)) { + TypeSet *types = TypeScript::SlotTypes(script, slot); + types->addType(cx, type); + } + break; + } + + default:; + } + } + + if (script->hasAnalysis() && script->analysis()->ranInference()) { + /* + * If the pushed set already has this type, we don't need to ensure + * there is a TypeIntermediate. Either there already is one, or the + * type could be determined from the script's other input type sets. + */ + TypeSet *pushed = script->analysis()->pushedTypes(pc, 0); + if (pushed->hasType(type)) + return; + } else { + /* Scan all intermediate types on the script to check for a dupe. */ + TypeResult *result, **pstart = &script->types->dynamicList, **presult = pstart; + while (*presult) { + result = *presult; + if (result->offset == unsigned(pc - script->code) && result->type == type) { + if (presult != pstart) { + /* Move to the head of the list, maintain LRU order. */ + *presult = result->next; + result->next = *pstart; + *pstart = result; + } + return; + } + presult = &result->next; + } + } + + InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s", + script->id(), pc - script->code, TypeString(type)); + + TypeResult *result = cx->new_(pc - script->code, type); + if (!result) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + result->next = script->types->dynamicList; + script->types->dynamicList = result; + + if (script->hasAnalysis() && script->analysis()->ranInference()) { + TypeSet *pushed = script->analysis()->pushedTypes(pc, 0); + pushed->addType(cx, type); + } + + /* Trigger recompilation of any inline callers. */ + if (script->function() && !script->function()->hasLazyType()) + ObjectStateChange(cx, script->function()->type(), false, true); +} + +void +TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) +{ + /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */ + if (!(js_CodeSpec[*pc].format & JOF_TYPESET)) + return; + + AutoEnterTypeInference enter(cx); + + if (!script->ensureRanAnalysis(cx, NULL)) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + Type type = GetValueType(cx, rval); + TypeSet *types = script->analysis()->bytecodeTypes(pc); + if (types->hasType(type)) + return; + + InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s", + script->id(), pc - script->code, TypeString(type)); + types->addType(cx, type); +} + +bool +TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) +{ + JS_ASSERT(script->types && !script->types->hasScope()); + + Root scriptRoot(cx, &script); + RootObject scopeRoot(cx, &scope); + + JSFunction *fun = script->function(); + bool nullClosure = fun && fun->isNullClosure(); + + JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction); + JS_ASSERT_IF(!scope, fun && !script->isInnerFunction); + + /* + * The scope object must be the initial one for the script, before any call + * object has been created in the heavyweight case. + */ + JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(), + scope->asCall().getCalleeFunction() != fun); + + if (!script->compileAndGo) { + script->types->global = NULL; + return true; + } + + JS_ASSERT_IF(fun && scope, fun->global() == scope->global()); + script->types->global = fun ? &fun->global() : &scope->global(); + + /* + * Update the parent in the script's bindings. The bindings are created + * with a NULL parent, and fixing the parent now avoids the need to reshape + * every time a call object is created from the bindings. + */ + if (!script->bindings.setParent(cx, script->types->global)) + return false; + + if (!cx->typeInferenceEnabled()) + return true; + + if (!script->isInnerFunction || nullClosure) { + /* + * Outermost functions need nesting information if there are inner + * functions directly nested in them. + */ + if (script->isOuterFunction) { + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + } + return true; + } + + /* + * Walk the scope chain to the next call object, which will be the function + * the script is nested inside. + */ + while (!scope->isCall()) + scope = &scope->asScope().enclosingScope(); + + CallObject &call = scope->asCall(); + + /* The isInnerFunction test ensures there is no intervening strict eval call object. */ + JS_ASSERT(!call.isForEval()); + + /* Don't track non-heavyweight parents, NAME ops won't reach into them. */ + JSFunction *parentFun = call.getCalleeFunction(); + if (!parentFun || !parentFun->isHeavyweight()) + return true; + JSScript *parent = parentFun->script(); + JS_ASSERT(parent->isOuterFunction); + + /* + * We only need the nesting in the child if it has NAME accesses going + * into the parent. We won't know for sure whether this is the case until + * analyzing the script's types, which we don't want to do yet. The nesting + * info we make here may get pruned if/when we eventually do such analysis. + */ + + /* + * Scopes are set when scripts first execute, and the parent script must + * have executed first. It is still possible for the parent script to not + * have a scope, however, as we occasionally purge all TypeScripts from the + * compartment and there may be inner function objects parented to an + * activation of the outer function sticking around. In such cases, treat + * the parent's call object as the most recent one, so that it is not + * marked as reentrant. + */ + if (!parent->ensureHasTypes(cx)) + return false; + if (!parent->types->hasScope()) { + if (!SetScope(cx, parent, &call.enclosingScope())) + return false; + parent->nesting()->activeCall = &call; + parent->nesting()->argArray = Valueify(call.argArray()); + parent->nesting()->varArray = Valueify(call.varArray()); + } + + JS_ASSERT(!script->types->nesting); + + /* Construct and link nesting information for the two functions. */ + + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + + script->nesting()->parent = parent; + script->nesting()->next = parent->nesting()->children; + parent->nesting()->children = script; + + return true; +} + +TypeScriptNesting::~TypeScriptNesting() +{ + /* + * Unlink from any parent/child. Nesting info on a script does not keep + * either the parent or children live during GC. + */ + + if (parent) { + JSScript **pscript = &parent->nesting()->children; + while ((*pscript)->nesting() != this) + pscript = &(*pscript)->nesting()->next; + *pscript = next; + } + + while (children) { + TypeScriptNesting *child = children->nesting(); + children = child->next; + child->parent = NULL; + child->next = NULL; + } +} + +bool +ClearActiveNesting(JSScript *start) +{ + /* + * Clear active call information for script and any outer functions + * inner to it. Return false if an inner function has frames on the stack. + */ + + /* Traverse children, then parent, avoiding recursion. */ + JSScript *script = start; + bool traverseChildren = true; + while (true) { + TypeScriptNesting *nesting = script->nesting(); + if (nesting->children && traverseChildren) { + script = nesting->children; + continue; + } + if (nesting->activeFrames) + return false; + if (script->isOuterFunction) { + nesting->activeCall = NULL; + nesting->argArray = NULL; + nesting->varArray = NULL; + } + if (script == start) + break; + if (nesting->next) { + script = nesting->next; + traverseChildren = true; + } else { + script = nesting->parent; + traverseChildren = false; + } + } + + return true; +} + +/* + * For the specified scope and script with an outer function, check if the + * scope represents a reentrant activation on an inner function of the parent + * or any of its transitive parents. + */ +static void +CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script) +{ + restart: + JSScript *parent = script->nesting()->parent; + JS_ASSERT(parent); + + while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent) + scope = &scope->asScope().enclosingScope(); + + if (scope != parent->nesting()->activeCall) { + parent->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION); + + /* + * Continue checking parents to see if this is reentrant for them too. + * We don't need to check this in for non-reentrant calls on the outer + * function: when we entered any outer function to the immediate parent + * we cleared the active call for its transitive children, so a + * non-reentrant call on a child is also a non-reentrant call on the + * parent. + */ + if (parent->nesting()->parent) { + scope = &scope->asScope().enclosingScope(); + script = parent; + goto restart; + } + } +} + +void +NestingPrologue(JSContext *cx, StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + if (nesting->parent) + CheckNestingParent(cx, &fp->scopeChain(), script); + + if (script->isOuterFunction) { + /* + * Check the stack has no frames for this activation, any of its inner + * functions or any of their transitive inner functions. + */ + if (!ClearActiveNesting(script)) { + script->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION); + } + + nesting->activeCall = &fp->callObj(); + nesting->argArray = fp->formalArgs(); + nesting->varArray = fp->slots(); + } + + /* Maintain stack frame count for the function. */ + nesting->activeFrames++; +} + +void +NestingEpilogue(StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + JS_ASSERT(nesting->activeFrames != 0); + nesting->activeFrames--; +} + +} } /* namespace js::types */ + +///////////////////////////////////////////////////////////////////// +// TypeScript +///////////////////////////////////////////////////////////////////// + +/* + * Returns true if we don't expect to compute the correct types for some value + * pushed by the specified bytecode. + */ +static inline bool +IgnorePushed(const jsbytecode *pc, unsigned index) +{ + switch (JSOp(*pc)) { + /* We keep track of the scopes pushed by BINDNAME separately. */ + case JSOP_BINDNAME: + case JSOP_BINDGNAME: + case JSOP_BINDXMLNAME: + return true; + + /* Stack not consistent in TRY_BRANCH_AFTER_COND. */ + case JSOP_IN: + case JSOP_EQ: + case JSOP_NE: + case JSOP_LT: + case JSOP_LE: + case JSOP_GT: + case JSOP_GE: + return (index == 0); + + /* Value not determining result is not pushed by OR/AND. */ + case JSOP_OR: + case JSOP_AND: + return (index == 0); + + /* Holes tracked separately. */ + case JSOP_HOLE: + return (index == 0); + case JSOP_FILTER: + return (index == 1); + + /* Storage for 'with' and 'let' blocks not monitored. */ + case JSOP_ENTERWITH: + case JSOP_ENTERBLOCK: + case JSOP_ENTERLET0: + case JSOP_ENTERLET1: + return true; + + /* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */ + case JSOP_ITER: + case JSOP_ITERNEXT: + case JSOP_MOREITER: + case JSOP_ENDITER: + return true; + + /* Ops which can manipulate values pushed by opcodes we don't model. */ + case JSOP_DUP: + case JSOP_DUP2: + case JSOP_SWAP: + case JSOP_PICK: + return true; + + /* We don't keep track of state indicating whether there is a pending exception. */ + case JSOP_FINALLY: + return true; + + /* + * We don't treat GETLOCAL immediately followed by a pop as a use-before-def, + * and while the type will have been inferred correctly the method JIT + * may not have written the local's initial undefined value to the stack, + * leaving a stale value. + */ + case JSOP_GETLOCAL: + return JSOp(pc[JSOP_GETLOCAL_LENGTH]) == JSOP_POP; + + default: + return false; + } +} + +bool +JSScript::makeTypes(JSContext *cx) +{ + JS_ASSERT(!types); + + if (!cx->typeInferenceEnabled()) { + types = (TypeScript *) cx->calloc_(sizeof(TypeScript)); + if (!types) + return false; + new(types) TypeScript(); + return true; + } + + AutoEnterTypeInference enter(cx); + + unsigned count = TypeScript::NumTypeSets(this); + + types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count)); + if (!types) { + cx->compartment->types.setPendingNukeTypes(cx); + return false; + } + + new(types) TypeScript(); + +#ifdef DEBUG + TypeSet *typeArray = types->typeArray(); + for (unsigned i = 0; i < nTypeSets; i++) + InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", + InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), + i, id()); + TypeSet *returnTypes = TypeScript::ReturnTypes(this); + InferSpew(ISpewOps, "typeSet: %sT%p%s return #%u", + InferSpewColor(returnTypes), returnTypes, InferSpewColorReset(), + id()); + TypeSet *thisTypes = TypeScript::ThisTypes(this); + InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", + InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), + id()); + unsigned nargs = function() ? function()->nargs : 0; + for (unsigned i = 0; i < nargs; i++) { + TypeSet *types = TypeScript::ArgTypes(this, i); + InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u", + InferSpewColor(types), types, InferSpewColorReset(), + i, id()); + } + for (unsigned i = 0; i < nfixed; i++) { + TypeSet *types = TypeScript::LocalTypes(this, i); + InferSpew(ISpewOps, "typeSet: %sT%p%s local%u #%u", + InferSpewColor(types), types, InferSpewColorReset(), + i, id()); + } +#endif + + return true; +} + +bool +JSScript::makeAnalysis(JSContext *cx) +{ + JS_ASSERT(types && !types->analysis); + + AutoEnterAnalysis enter(cx); + + types->analysis = cx->typeLifoAlloc().new_(this); + + if (!types->analysis) + return false; + + types->analysis->analyzeBytecode(cx); + + if (types->analysis->OOM()) { + types->analysis = NULL; + return false; + } + + return true; +} + +bool +JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) +{ + function_ = fun; + + if (!cx->typeInferenceEnabled()) + return true; + + if (singleton) { + if (!fun->setSingletonType(cx)) + return false; + } else { + TypeObject *type = cx->compartment->types.newTypeObject(cx, this, + JSProto_Function, fun->getProto()); + if (!type) + return false; + + fun->setType(type); + type->interpretedFunction = fun; + } + + return true; +} + +#ifdef DEBUG + +/* static */ void +TypeScript::CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp) +{ + AutoEnterTypeInference enter(cx); + + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + return; + + if (!script->hasAnalysis() || !script->analysis()->ranInference()) + return; + ScriptAnalysis *analysis = script->analysis(); + + int defCount = GetDefCount(script, pc - script->code); + + for (int i = 0; i < defCount; i++) { + const js::Value &val = sp[-defCount + i]; + TypeSet *types = analysis->pushedTypes(pc, i); + if (IgnorePushed(pc, i)) + continue; + + Type type = GetValueType(cx, val); + + if (!types->hasType(type)) { + /* Display fine-grained debug information first */ + fprintf(stderr, "Missing type at #%u:%05u pushed %u: %s\n", + script->id(), unsigned(pc - script->code), i, TypeString(type)); + TypeFailure(cx, "Missing type pushed %u: %s", i, TypeString(type)); + } + } +} + +#endif + +///////////////////////////////////////////////////////////////////// +// JSObject +///////////////////////////////////////////////////////////////////// + +bool +JSObject::shouldSplicePrototype(JSContext *cx) +{ + /* + * During bootstrapping, if inference is enabled we need to make sure not + * to splice a new prototype in for Function.prototype or the global + * object if their __proto__ had previously been set to null, as this + * will change the prototype for all other objects with the same type. + * If inference is disabled we cannot determine from the object whether it + * has had its __proto__ set after creation. + */ + if (getProto() != NULL) + return false; + return !cx->typeInferenceEnabled() || hasSingletonType(); +} + +bool +JSObject::splicePrototype(JSContext *cx, JSObject *proto) +{ + /* + * For singleton types representing only a single JSObject, the proto + * can be rearranged as needed without destroying type information for + * the old or new types. Note that type constraints propagating properties + * from the old prototype are not removed. + */ + JS_ASSERT_IF(cx->typeInferenceEnabled(), hasSingletonType()); + + /* Inner objects may not appear on prototype chains. */ + JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject); + + /* + * Force type instantiation when splicing lazy types. This may fail, + * in which case inference will be disabled for the compartment. + */ + TypeObject *type = getType(cx); + TypeObject *protoType = NULL; + if (proto) { + protoType = proto->getType(cx); + if (!proto->getNewType(cx)) + return false; + } + + if (!cx->typeInferenceEnabled()) { + TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx); + if (!type) + return false; + type_ = type; + return true; + } + + type->proto = proto; + + AutoEnterTypeInference enter(cx); + + if (protoType && protoType->unknownProperties() && !type->unknownProperties()) { + type->markUnknown(cx); + return true; + } + + if (!type->unknownProperties()) { + /* Update properties on this type with any shared with the prototype. */ + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = type->getProperty(i); + if (prop && prop->types.hasPropagatedProperty()) + type->getFromPrototypes(cx, prop->id, &prop->types, true); + } + } + + return true; +} + +void +JSObject::makeLazyType(JSContext *cx) +{ + JS_ASSERT(cx->typeInferenceEnabled() && hasLazyType()); + AutoEnterTypeInference enter(cx); + + TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, + JSProto_Object, getProto()); + if (!type) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + + /* Fill in the type according to the state of this object. */ + + type->singleton = this; + + if (isFunction() && toFunction()->isInterpreted()) { + type->interpretedFunction = toFunction(); + JSScript *script = type->interpretedFunction->script(); + if (script->createdArgs) + type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS; + if (script->uninlineable) + type->flags |= OBJECT_FLAG_UNINLINEABLE; + if (script->reentrantOuterFunction) + type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION; + } + + if (lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) + type->flags |= OBJECT_FLAG_ITERATED; + +#if JS_HAS_XML_SUPPORT + /* + * XML objects do not have equality hooks but are treated special by EQ/NE + * ops. Just mark the type as totally unknown. + */ + if (isXML() && !type->unknownProperties()) + type->markUnknown(cx); +#endif + + if (getClass()->ext.equality) + type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY; + + if (type->unknownProperties()) { + type_ = type; + return; + } + + /* Not yet generating singleton arrays. */ + type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY + | OBJECT_FLAG_NON_PACKED_ARRAY + | OBJECT_FLAG_NON_TYPED_ARRAY; + + type_ = type; +} + +/* static */ inline HashNumber +TypeObjectEntry::hash(JSObject *proto) +{ + return PointerHasher::hash(proto); +} + +/* static */ inline bool +TypeObjectEntry::match(TypeObject *key, JSObject *lookup) +{ + return key->proto == lookup; +} + +#ifdef DEBUG +bool +JSObject::hasNewType(TypeObject *type) +{ + TypeObjectSet &table = compartment()->newTypeObjects; + + if (!table.initialized()) + return false; + + TypeObjectSet::Ptr p = table.lookup(this); + return p && *p == type; +} +#endif /* DEBUG */ + +bool +JSObject::setNewTypeUnknown(JSContext *cx) +{ + if (!setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN)) + return false; + + /* + * If the object already has a new type, mark that type as unknown. It will + * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set + * crawl if prototypes of the object change dynamically in the future. + */ + TypeObjectSet &table = cx->compartment->newTypeObjects; + if (table.initialized()) { + if (TypeObjectSet::Ptr p = table.lookup(this)) + MarkTypeObjectUnknownProperties(cx, *p); + } + + return true; +} + +TypeObject * +JSObject::getNewType(JSContext *cx, JSFunction *fun) +{ + TypeObjectSet &table = cx->compartment->newTypeObjects; + + if (!table.initialized() && !table.init()) + return NULL; + + TypeObjectSet::AddPtr p = table.lookupForAdd(this); + if (p) { + TypeObject *type = *p; + + /* + * If set, the type's newScript indicates the script used to create + * all objects in existence which have this type. If there are objects + * in existence which are not created by calling 'new' on newScript, + * we must clear the new script information from the type and will not + * be able to assume any definite properties for instances of the type. + * This case is rare, but can happen if, for example, two scripted + * functions have the same value for their 'prototype' property, or if + * Object.create is called with a prototype object that is also the + * 'prototype' property of some scripted function. + */ + if (type->newScript && type->newScript->fun != fun) + type->clearNewScript(cx); + + return type; + } + + RootedVarObject self(cx, this); + + if (!setDelegate(cx)) + return NULL; + + bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN); + + TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, + JSProto_Object, self, markUnknown); + if (!type) + return NULL; + + if (!table.relookupOrAdd(p, self, type)) + return NULL; + + if (!cx->typeInferenceEnabled()) + return type; + + AutoEnterTypeInference enter(cx); + + /* + * Set the special equality flag for types whose prototype also has the + * flag set. This is a hack, :XXX: need a real correspondence between + * types and the possible js::Class of objects with that type. + */ + if (self->hasSpecialEquality()) + type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY; + + if (fun) + CheckNewScriptProperties(cx, type, fun); + +#if JS_HAS_XML_SUPPORT + /* Special case for XML object equality, see makeLazyType(). */ + if (self->isXML() && !type->unknownProperties()) + type->flags |= OBJECT_FLAG_UNKNOWN_MASK; +#endif + + if (self->getClass()->ext.equality) + type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY; + + /* + * The new type is not present in any type sets, so mark the object as + * unknown in all type sets it appears in. This allows the prototype of + * such objects to mutate freely without triggering an expensive walk of + * the compartment's type sets. (While scripts normally don't mutate + * __proto__, the browser will for proxies and such, and we need to + * accommodate this behavior). + */ + if (type->unknownProperties()) + type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN; + + return type; +} + +TypeObject * +JSCompartment::getLazyType(JSContext *cx, JSObject *proto) +{ + gc::MaybeCheckStackRoots(cx); + + TypeObjectSet &table = cx->compartment->lazyTypeObjects; + + if (!table.initialized() && !table.init()) + return NULL; + + TypeObjectSet::AddPtr p = table.lookupForAdd(proto); + if (p) { + TypeObject *type = *p; + JS_ASSERT(type->lazy()); + + return type; + } + + TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, + JSProto_Object, proto, false); + if (!type) + return NULL; + + if (!table.relookupOrAdd(p, proto, type)) + return NULL; + + type->singleton = (JSObject *) TypeObject::LAZY_SINGLETON; + + return type; +} + +///////////////////////////////////////////////////////////////////// +// Tracing +///////////////////////////////////////////////////////////////////// + +void +TypeSet::sweep(JSContext *cx, JSCompartment *compartment) +{ + /* + * Purge references to type objects that are no longer live. Type sets hold + * only weak references. For type sets containing more than one object, + * live entries in the object hash need to be copied to the compartment's + * new arena. + */ + unsigned objectCount = baseObjectCount(); + if (objectCount >= 2) { + unsigned oldCapacity = HashSetCapacity(objectCount); + TypeObjectKey **oldArray = objectSet; + + clearObjects(); + objectCount = 0; + for (unsigned i = 0; i < oldCapacity; i++) { + TypeObjectKey *object = oldArray[i]; + if (object && !IsAboutToBeFinalized(object)) { + TypeObjectKey **pentry = + HashSetInsert + (compartment, objectSet, objectCount, object); + if (pentry) + *pentry = object; + else + compartment->types.setPendingNukeTypes(cx); + } + } + setBaseObjectCount(objectCount); + } else if (objectCount == 1) { + TypeObjectKey *object = (TypeObjectKey *) objectSet; + if (IsAboutToBeFinalized(object)) { + objectSet = NULL; + setBaseObjectCount(0); + } + } + + /* + * All constraints are wiped out on each GC, including those propagating + * into this type set from prototype properties. + */ + constraintList = NULL; + flags &= ~TYPE_FLAG_PROPAGATED_PROPERTY; +} + +inline void +TypeObject::clearProperties() +{ + setBasePropertyCount(0); + propertySet = NULL; +} + +/* + * Before sweeping the arenas themselves, scan all type objects in a + * compartment to fixup weak references: property type sets referencing dead + * JS and type objects, and singleton JS objects whose type is not referenced + * elsewhere. This also releases memory associated with dead type objects, + * so that type objects do not need later finalization. + */ +inline void +TypeObject::sweep(JSContext *cx) +{ + /* + * We may be regenerating existing type sets containing this object, + * so reset contributions on each GC to avoid tripping the limit. + */ + contribution = 0; + + if (singleton) { + JS_ASSERT(!newScript); + + /* + * All properties can be discarded. We will regenerate them as needed + * as code gets reanalyzed. + */ + clearProperties(); + + return; + } + + if (!isMarked()) { + if (newScript) + Foreground::free_(newScript); + return; + } + + JSCompartment *compartment = this->compartment(); + + /* + * Properties were allocated from the old arena, and need to be copied over + * to the new one. Don't hang onto properties without the OWN_PROPERTY + * flag; these were never directly assigned, and get any possible values + * from the object's prototype. + */ + unsigned propertyCount = basePropertyCount(); + if (propertyCount >= 2) { + unsigned oldCapacity = HashSetCapacity(propertyCount); + Property **oldArray = propertySet; + + clearProperties(); + propertyCount = 0; + for (unsigned i = 0; i < oldCapacity; i++) { + Property *prop = oldArray[i]; + if (prop && prop->types.isOwnProperty(false)) { + Property *newProp = compartment->typeLifoAlloc.new_(*prop); + if (newProp) { + Property **pentry = + HashSetInsert + (compartment, propertySet, propertyCount, prop->id); + if (pentry) { + *pentry = newProp; + newProp->types.sweep(cx, compartment); + } else { + compartment->types.setPendingNukeTypes(cx); + } + } else { + compartment->types.setPendingNukeTypes(cx); + } + } + } + setBasePropertyCount(propertyCount); + } else if (propertyCount == 1) { + Property *prop = (Property *) propertySet; + if (prop->types.isOwnProperty(false)) { + Property *newProp = compartment->typeLifoAlloc.new_(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(cx, compartment); + } else { + compartment->types.setPendingNukeTypes(cx); + } + } else { + propertySet = NULL; + setBasePropertyCount(0); + } + } + + if (basePropertyCount() <= SET_ARRAY_SIZE) { + for (unsigned i = 0; i < basePropertyCount(); i++) + JS_ASSERT(propertySet[i]); + } + + /* + * The GC will clear out the constraints ensuring the correctness of the + * newScript information, these constraints will need to be regenerated + * the next time we compile code which depends on this info. + */ + if (newScript) + flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE; +} + +struct SweepTypeObjectOp +{ + JSContext *cx; + SweepTypeObjectOp(JSContext *cx) : cx(cx) {} + void operator()(gc::Cell *cell) { + TypeObject *object = static_cast(cell); + object->sweep(cx); + } +}; + +void +SweepTypeObjects(JSContext *cx, JSCompartment *compartment) +{ + SweepTypeObjectOp op(cx); + gc::ForEachArenaAndCell(compartment, gc::FINALIZE_TYPE_OBJECT, gc::EmptyArenaOp, op); +} + +void +TypeCompartment::sweep(JSContext *cx) +{ + JSCompartment *compartment = this->compartment(); + + SweepTypeObjects(cx, compartment); + + /* + * Iterate through the array/object type tables and remove all entries + * referencing collected data. These tables only hold weak references. + */ + + if (arrayTypeTable) { + for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) { + const ArrayTableKey &key = e.front().key; + TypeObject *obj = e.front().value; + JS_ASSERT(obj->proto == key.proto); + JS_ASSERT(!key.type.isSingleObject()); + + bool remove = false; + if (key.type.isTypeObject() && !key.type.typeObject()->isMarked()) + remove = true; + if (!obj->isMarked()) + remove = true; + + if (remove) + e.removeFront(); + } + } + + if (objectTypeTable) { + for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { + const ObjectTableKey &key = e.front().key; + const ObjectTableEntry &entry = e.front().value; + JS_ASSERT(entry.object->proto == key.proto); + + bool remove = false; + if (!entry.object->isMarked()) + remove = true; + for (unsigned i = 0; !remove && i < key.nslots; i++) { + if (JSID_IS_STRING(key.ids[i])) { + JSString *str = JSID_TO_STRING(key.ids[i]); + if (!str->isMarked()) + remove = true; + } + JS_ASSERT(!entry.types[i].isSingleObject()); + if (entry.types[i].isTypeObject() && !entry.types[i].typeObject()->isMarked()) + remove = true; + } + + if (remove) { + Foreground::free_(key.ids); + Foreground::free_(entry.types); + e.removeFront(); + } + } + } + + if (allocationSiteTable) { + for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { + const AllocationSiteKey &key = e.front().key; + TypeObject *object = e.front().value; + + if (IsAboutToBeFinalized(key.script) || !object->isMarked()) + e.removeFront(); + } + } + + /* + * The pending array is reset on GC, it can grow large (75+ KB) and is easy + * to reallocate if the compartment becomes active again. + */ + if (pendingArray) + cx->free_(pendingArray); + + pendingArray = NULL; + pendingCapacity = 0; +} + +void +JSCompartment::sweepNewTypeObjectTable(JSContext *cx, TypeObjectSet &table) +{ + if (table.initialized()) { + for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) { + TypeObject *type = e.front(); + if (!type->isMarked()) + e.removeFront(); + } + } +} + +TypeCompartment::~TypeCompartment() +{ + if (pendingArray) + Foreground::free_(pendingArray); + + if (arrayTypeTable) + Foreground::delete_(arrayTypeTable); + + if (objectTypeTable) + Foreground::delete_(objectTypeTable); + + if (allocationSiteTable) + Foreground::delete_(allocationSiteTable); +} + +/* static */ void +TypeScript::Sweep(JSContext *cx, JSScript *script) +{ + JSCompartment *compartment = script->compartment(); + JS_ASSERT(compartment->types.inferenceEnabled); + + unsigned num = NumTypeSets(script); + TypeSet *typeArray = script->types->typeArray(); + + /* Remove constraints and references to dead objects from the persistent type sets. */ + for (unsigned i = 0; i < num; i++) + typeArray[i].sweep(cx, compartment); + + TypeResult **presult = &script->types->dynamicList; + while (*presult) { + TypeResult *result = *presult; + Type type = result->type; + + if (!type.isUnknown() && !type.isAnyObject() && type.isObject() && + IsAboutToBeFinalized(type.objectKey())) { + *presult = result->next; + cx->delete_(result); + } else { + presult = &result->next; + } + } + + /* + * If the script has nesting state with a most recent activation, we do not + * need either to mark the call object or clear it if not live. Even with + * a dead pointer in the nesting, we can't get a spurious match while + * testing for reentrancy: if previous activations are still live, they + * cannot alias the most recent one, and future activations will overwrite + * activeCall on creation. + */ +} + +void +TypeScript::destroy() +{ + while (dynamicList) { + TypeResult *next = dynamicList->next; + Foreground::delete_(dynamicList); + dynamicList = next; + } + + if (nesting) + Foreground::delete_(nesting); + + Foreground::free_(this); +} + +inline size_t +TypeSet::computedSizeOfExcludingThis() +{ + /* + * This memory is allocated within the temp pool (but accounted for + * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must + * compute its size analytically. + */ + uint32_t count = baseObjectCount(); + if (count >= 2) + return HashSetCapacity(count) * sizeof(TypeObject *); + return 0; +} + +inline size_t +TypeObject::computedSizeOfExcludingThis() +{ + /* + * This memory is allocated within the temp pool (but accounted for + * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must + * compute its size analytically. + */ + size_t bytes = 0; + + uint32_t count = basePropertyCount(); + if (count >= 2) + bytes += HashSetCapacity(count) * sizeof(TypeObject *); + + count = getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = getProperty(i); + if (prop) + bytes += sizeof(Property) + prop->types.computedSizeOfExcludingThis(); + } + + return bytes; +} + +static void +SizeOfScriptTypeInferenceData(JSScript *script, TypeInferenceSizes *sizes, + JSMallocSizeOfFun mallocSizeOf) +{ + TypeScript *typeScript = script->types; + if (!typeScript) + return; + + /* If TI is disabled, a single TypeScript is still present. */ + if (!script->compartment()->types.inferenceEnabled) { + sizes->scripts += mallocSizeOf(typeScript); + return; + } + + sizes->scripts += mallocSizeOf(typeScript->nesting); + + unsigned count = TypeScript::NumTypeSets(script); + sizes->scripts += mallocSizeOf(typeScript); + + TypeResult *result = typeScript->dynamicList; + while (result) { + sizes->scripts += mallocSizeOf(result); + result = result->next; + } + + /* + * This counts memory that is in the temp pool but gets attributed + * elsewhere. See JS::SizeOfCompartmentTypeInferenceData for more details. + */ + TypeSet *typeArray = typeScript->typeArray(); + for (unsigned i = 0; i < count; i++) { + size_t bytes = typeArray[i].computedSizeOfExcludingThis(); + sizes->scripts += bytes; + sizes->temporary -= bytes; + } +} + +void +JSCompartment::sizeOfTypeInferenceData(JSContext *cx, TypeInferenceSizes *sizes, + JSMallocSizeOfFun mallocSizeOf) +{ + /* + * Note: not all data in the pool is temporary, and some will survive GCs + * by being copied to the replacement pool. This memory will be counted + * elsewhere and deducted from the amount of temporary data. + */ + sizes->temporary += typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf); + + /* Pending arrays are cleared on GC along with the analysis pool. */ + sizes->temporary += mallocSizeOf(types.pendingArray); + + /* TypeCompartment::pendingRecompiles is non-NULL only while inference code is running. */ + JS_ASSERT(!types.pendingRecompiles); + + for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) + SizeOfScriptTypeInferenceData(i.get(), sizes, mallocSizeOf); + + if (types.allocationSiteTable) + sizes->tables += types.allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); + + if (types.arrayTypeTable) + sizes->tables += types.arrayTypeTable->sizeOfIncludingThis(mallocSizeOf); + + if (types.objectTypeTable) { + sizes->tables += types.objectTypeTable->sizeOfIncludingThis(mallocSizeOf); + + for (ObjectTypeTable::Enum e(*types.objectTypeTable); + !e.empty(); + e.popFront()) + { + const ObjectTableKey &key = e.front().key; + const ObjectTableEntry &value = e.front().value; + + /* key.ids and values.types have the same length. */ + sizes->tables += mallocSizeOf(key.ids) + mallocSizeOf(value.types); + } + } +} + +void +TypeObject::sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf) +{ + if (singleton) { + /* + * Properties and associated type sets for singletons are cleared on + * every GC. The type object is normally destroyed too, but we don't + * charge this to 'temporary' as this is not for GC heap values. + */ + JS_ASSERT(!newScript); + return; + } + + sizes->objects += mallocSizeOf(newScript); + + /* + * This counts memory that is in the temp pool but gets attributed + * elsewhere. See JSCompartment::sizeOfTypeInferenceData for more details. + */ + size_t bytes = computedSizeOfExcludingThis(); + sizes->objects += bytes; + sizes->temporary -= bytes; +} diff --git a/deps/mozjs/js/src/jsinfer.h b/deps/mozjs/js/src/jsinfer.h new file mode 100644 index 00000000000..8a7fc83ed81 --- /dev/null +++ b/deps/mozjs/js/src/jsinfer.h @@ -0,0 +1,1315 @@ +//* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */ +/* vim: set ts=40 sw=4 et tw=99: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SpiderMonkey bytecode type inference + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Hackett + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* Definitions related to javascript type inference. */ + +#ifndef jsinfer_h___ +#define jsinfer_h___ + +#include "jsalloc.h" +#include "jscell.h" +#include "jsfriendapi.h" +#include "jsprvtd.h" + +#include "ds/LifoAlloc.h" +#include "gc/Barrier.h" +#include "js/HashTable.h" + +namespace JS { +struct TypeInferenceSizes; +} + +namespace js { +namespace types { + +/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */ +struct TypeObjectKey { + static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } + static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; } +}; + +/* + * Information about a single concrete type. We pack this into a single word, + * where small values are particular primitive or other singleton types, and + * larger values are either specific JS objects or type objects. + */ +class Type +{ + uintptr_t data; + Type(uintptr_t data) : data(data) {} + + public: + + uintptr_t raw() const { return data; } + + bool isPrimitive() const { + return data < JSVAL_TYPE_OBJECT; + } + + bool isPrimitive(JSValueType type) const { + JS_ASSERT(type < JSVAL_TYPE_OBJECT); + return (uintptr_t) type == data; + } + + JSValueType primitive() const { + JS_ASSERT(isPrimitive()); + return (JSValueType) data; + } + + bool isAnyObject() const { + return data == JSVAL_TYPE_OBJECT; + } + + bool isUnknown() const { + return data == JSVAL_TYPE_UNKNOWN; + } + + /* Accessors for types that are either JSObject or TypeObject. */ + + bool isObject() const { + JS_ASSERT(!isAnyObject() && !isUnknown()); + return data > JSVAL_TYPE_UNKNOWN; + } + + TypeObjectKey *objectKey() const { + JS_ASSERT(isObject()); + return (TypeObjectKey *) data; + } + + /* Accessors for JSObject types */ + + bool isSingleObject() const { + return isObject() && !!(data & 1); + } + + JSObject *singleObject() const { + JS_ASSERT(isSingleObject()); + return (JSObject *) (data ^ 1); + } + + /* Accessors for TypeObject types */ + + bool isTypeObject() const { + return isObject() && !(data & 1); + } + + TypeObject *typeObject() const { + JS_ASSERT(isTypeObject()); + return (TypeObject *) data; + } + + bool operator == (Type o) const { return data == o.data; } + bool operator != (Type o) const { return data != o.data; } + + static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); } + static inline Type NullType() { return Type(JSVAL_TYPE_NULL); } + static inline Type BooleanType() { return Type(JSVAL_TYPE_BOOLEAN); } + static inline Type Int32Type() { return Type(JSVAL_TYPE_INT32); } + static inline Type DoubleType() { return Type(JSVAL_TYPE_DOUBLE); } + static inline Type StringType() { return Type(JSVAL_TYPE_STRING); } + static inline Type LazyArgsType() { return Type(JSVAL_TYPE_MAGIC); } + static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); } + static inline Type UnknownType() { return Type(JSVAL_TYPE_UNKNOWN); } + + static inline Type PrimitiveType(JSValueType type) { + JS_ASSERT(type < JSVAL_TYPE_UNKNOWN); + return Type(type); + } + + static inline Type ObjectType(JSObject *obj); + static inline Type ObjectType(TypeObject *obj); + static inline Type ObjectType(TypeObjectKey *obj); +}; + +/* Get the type of a jsval, or zero for an unknown special value. */ +inline Type GetValueType(JSContext *cx, const Value &val); + +/* + * Type inference memory management overview. + * + * Inference constructs a global web of constraints relating the contents of + * type sets particular to various scripts and type objects within a + * compartment. This data can consume a significant amount of memory, and to + * avoid this building up we try to clear it with some regularity. On each GC + * which occurs while we are not actively working with inference or other + * analysis information, we clear out all generated constraints, all type sets + * describing stack types within scripts, and (normally) all data describing + * type objects for particular JS objects (see the lazy type objects overview + * below). JIT code depends on this data and is cleared as well. + * + * All this data is allocated into compartment->pool. Some type inference data + * lives across GCs: type sets for scripts and non-singleton type objects, and + * propeties for such type objects. This data is also allocated into + * compartment->pool, but everything still live is copied to a new arena on GC. + */ + +/* + * A constraint which listens to additions to a type set and propagates those + * changes to other type sets. + */ +class TypeConstraint +{ +public: +#ifdef DEBUG + const char *kind_; + const char *kind() const { return kind_; } +#else + const char *kind() const { return NULL; } +#endif + + /* Next constraint listening to the same type set. */ + TypeConstraint *next; + + TypeConstraint(const char *kind) + : next(NULL) + { +#ifdef DEBUG + this->kind_ = kind; +#endif + } + + /* Register a new type for the set this constraint is listening to. */ + virtual void newType(JSContext *cx, TypeSet *source, Type type) = 0; + + /* + * For constraints attached to an object property's type set, mark the + * property as having been configured or received an own property. + */ + virtual void newPropertyState(JSContext *cx, TypeSet *source) {} + + /* + * For constraints attached to the JSID_EMPTY type set on an object, mark a + * change in one of the object's dynamic property flags. If force is set, + * recompilation is always triggered. + */ + virtual void newObjectState(JSContext *cx, TypeObject *object, bool force) {} +}; + +/* Flags and other state stored in TypeSet::flags */ +enum { + TYPE_FLAG_UNDEFINED = 0x1, + TYPE_FLAG_NULL = 0x2, + TYPE_FLAG_BOOLEAN = 0x4, + TYPE_FLAG_INT32 = 0x8, + TYPE_FLAG_DOUBLE = 0x10, + TYPE_FLAG_STRING = 0x20, + TYPE_FLAG_LAZYARGS = 0x40, + TYPE_FLAG_ANYOBJECT = 0x80, + + /* Mask/shift for the number of objects in objectSet */ + TYPE_FLAG_OBJECT_COUNT_MASK = 0xff00, + TYPE_FLAG_OBJECT_COUNT_SHIFT = 8, + TYPE_FLAG_OBJECT_COUNT_LIMIT = + TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT, + + /* Whether the contents of this type set are totally unknown. */ + TYPE_FLAG_UNKNOWN = 0x00010000, + + /* Mask of normal type flags on a type set. */ + TYPE_FLAG_BASE_MASK = 0x000100ff, + + /* Flags for type sets which are on object properties. */ + + /* + * Whether there are subset constraints propagating the possible types + * for this property inherited from the object's prototypes. Reset on GC. + */ + TYPE_FLAG_PROPAGATED_PROPERTY = 0x00020000, + + /* Whether this property has ever been directly written. */ + TYPE_FLAG_OWN_PROPERTY = 0x00040000, + + /* + * Whether the property has ever been deleted or reconfigured to behave + * differently from a normal native property (e.g. made non-writable or + * given a scripted getter or setter). + */ + TYPE_FLAG_CONFIGURED_PROPERTY = 0x00080000, + + /* + * Whether the property is definitely in a particular inline slot on all + * objects from which it has not been deleted or reconfigured. Implies + * OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change. + */ + TYPE_FLAG_DEFINITE_PROPERTY = 0x00100000, + + /* If the property is definite, mask and shift storing the slot. */ + TYPE_FLAG_DEFINITE_MASK = 0x0f000000, + TYPE_FLAG_DEFINITE_SHIFT = 24 +}; +typedef uint32_t TypeFlags; + +/* Flags and other state stored in TypeObject::flags */ +enum { + /* Objects with this type are functions. */ + OBJECT_FLAG_FUNCTION = 0x1, + + /* If set, newScript information should not be installed on this object. */ + OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x2, + + /* + * If set, type constraints covering the correctness of the newScript + * definite properties need to be regenerated before compiling any jitcode + * which depends on this information. + */ + OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4, + + /* + * Whether we have ensured all type sets in the compartment contain + * ANYOBJECT instead of this object. + */ + OBJECT_FLAG_SETS_MARKED_UNKNOWN = 0x8, + + /* Mask/shift for the number of properties in propertySet */ + OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff0, + OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 4, + OBJECT_FLAG_PROPERTY_COUNT_LIMIT = + OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, + + /* + * Some objects are not dense arrays, or are dense arrays whose length + * property does not fit in an int32_t. + */ + OBJECT_FLAG_NON_DENSE_ARRAY = 0x00010000, + + /* Whether any objects this represents are not packed arrays. */ + OBJECT_FLAG_NON_PACKED_ARRAY = 0x00020000, + + /* Whether any objects this represents are not typed arrays. */ + OBJECT_FLAG_NON_TYPED_ARRAY = 0x00040000, + + /* Whether any represented script has had arguments objects created. */ + OBJECT_FLAG_CREATED_ARGUMENTS = 0x00080000, + + /* Whether any represented script is considered uninlineable. */ + OBJECT_FLAG_UNINLINEABLE = 0x00100000, + + /* Whether any objects have an equality hook. */ + OBJECT_FLAG_SPECIAL_EQUALITY = 0x00200000, + + /* Whether any objects have been iterated over. */ + OBJECT_FLAG_ITERATED = 0x00400000, + + /* Outer function which has been marked reentrant. */ + OBJECT_FLAG_REENTRANT_FUNCTION = 0x00800000, + + /* For a global object, whether flags were set on the RegExpStatics. */ + OBJECT_FLAG_REGEXP_FLAGS_SET = 0x01000000, + + /* Flags which indicate dynamic properties of represented objects. */ + OBJECT_FLAG_DYNAMIC_MASK = 0x01ff0000, + + /* + * Whether all properties of this object are considered unknown. + * If set, all flags in DYNAMIC_MASK will also be set. + */ + OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x80000000, + + /* Mask for objects created with unknown properties. */ + OBJECT_FLAG_UNKNOWN_MASK = + OBJECT_FLAG_DYNAMIC_MASK + | OBJECT_FLAG_UNKNOWN_PROPERTIES + | OBJECT_FLAG_SETS_MARKED_UNKNOWN +}; +typedef uint32_t TypeObjectFlags; + +/* Information about the set of types associated with an lvalue. */ +class TypeSet +{ + /* Flags for this type set. */ + TypeFlags flags; + + /* Possible objects this type set can represent. */ + TypeObjectKey **objectSet; + + public: + + /* Chain of constraints which propagate changes out from this type set. */ + TypeConstraint *constraintList; + + TypeSet() + : flags(0), objectSet(NULL), constraintList(NULL) + {} + + void print(JSContext *cx); + + inline void sweep(JSContext *cx, JSCompartment *compartment); + inline size_t computedSizeOfExcludingThis(); + + /* Whether this set contains a specific type. */ + inline bool hasType(Type type); + + TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; } + bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); } + bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); } + + bool empty() const { return !baseFlags() && !baseObjectCount(); } + + bool hasAnyFlag(TypeFlags flags) const { + JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags); + return !!(baseFlags() & flags); + } + + bool isOwnProperty(bool configurable) const { + return flags & (configurable ? TYPE_FLAG_CONFIGURED_PROPERTY : TYPE_FLAG_OWN_PROPERTY); + } + bool isDefiniteProperty() const { return flags & TYPE_FLAG_DEFINITE_PROPERTY; } + unsigned definiteSlot() const { + JS_ASSERT(isDefiniteProperty()); + return flags >> TYPE_FLAG_DEFINITE_SHIFT; + } + + /* + * Add a type to this set, calling any constraint handlers if this is a new + * possible type. + */ + inline void addType(JSContext *cx, Type type); + + /* Mark this type set as representing an own property or configured property. */ + inline void setOwnProperty(JSContext *cx, bool configured); + + /* + * Iterate through the objects in this set. getObjectCount overapproximates + * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject + * may return NULL. + */ + inline unsigned getObjectCount(); + inline TypeObjectKey *getObject(unsigned i); + inline JSObject *getSingleObject(unsigned i); + inline TypeObject *getTypeObject(unsigned i); + + void setOwnProperty(bool configurable) { + flags |= TYPE_FLAG_OWN_PROPERTY; + if (configurable) + flags |= TYPE_FLAG_CONFIGURED_PROPERTY; + } + void setDefinite(unsigned slot) { + JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)); + flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT); + } + + bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); } + void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; } + + enum FilterKind { + FILTER_ALL_PRIMITIVES, + FILTER_NULL_VOID, + FILTER_VOID + }; + + /* Add specific kinds of constraints to this set. */ + inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true); + void addSubset(JSContext *cx, TypeSet *target); + void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *target, jsid id); + void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *target, jsid id); + void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id); + void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, + TypeSet *objectTypes, TypeSet *valueTypes); + void addCall(JSContext *cx, TypeCallsite *site); + void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL); + void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target); + void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, + Type type, TypeSet *types = NULL); + void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter); + void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target); + void addLazyArguments(JSContext *cx, TypeSet *target); + + /* + * Make an type set with the specified debugging name, not embedded in + * another structure. + */ + static TypeSet *make(JSContext *cx, const char *name); + + /* + * Methods for JIT compilation. If a script is currently being compiled + * (see AutoEnterCompilation) these will add constraints ensuring that if + * the return value change in the future due to new type information, the + * currently compiled script will be marked for recompilation. + */ + + /* Completely freeze the contents of this type set. */ + void addFreeze(JSContext *cx); + + /* Get any type tag which all values in this set must have. */ + JSValueType getKnownTypeTag(JSContext *cx); + + bool isLazyArguments(JSContext *cx) { return getKnownTypeTag(cx) == JSVAL_TYPE_MAGIC; } + + /* Whether the type set or a particular object has any of a set of flags. */ + bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags); + static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags); + + /* + * Watch for a generic object state change on a type object. This currently + * includes reallocations of slot pointers for global objects, and changes + * to newScript data on types. + */ + static void WatchObjectStateChange(JSContext *cx, TypeObject *object); + + /* + * For type sets on a property, return true if the property has any 'own' + * values assigned. If configurable is set, return 'true' if the property + * has additionally been reconfigured as non-configurable, non-enumerable + * or non-writable (this only applies to properties that have changed after + * having been created, not to e.g. properties non-writable on creation). + */ + bool isOwnProperty(JSContext *cx, TypeObject *object, bool configurable); + + /* Get whether this type set is non-empty. */ + bool knownNonEmpty(JSContext *cx); + + /* Get whether this type set is known to be a subset of other. */ + bool knownSubset(JSContext *cx, TypeSet *other); + + /* + * Get the typed array type of all objects in this set. Returns + * TypedArray::TYPE_MAX if the set contains different array types. + */ + int getTypedArrayType(JSContext *cx); + + /* Get the single value which can appear in this type set, otherwise NULL. */ + JSObject *getSingleton(JSContext *cx, bool freeze = true); + + /* Whether all objects in this set are parented to a particular global. */ + bool hasGlobalObject(JSContext *cx, JSObject *global); + + inline void clearObjects(); + + /* + * Whether a location with this TypeSet needs a write barrier (i.e., whether + * it can hold GC things). The type set is frozen if no barrier is needed. + */ + bool needsBarrier(JSContext *cx); + + /* The type set is frozen if no barrier is needed. */ + bool propertyNeedsBarrier(JSContext *cx, jsid id); + + private: + uint32_t baseObjectCount() const { + return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT; + } + inline void setBaseObjectCount(uint32_t count); +}; + +/* + * Handler which persists information about dynamic types pushed within a + * script which can affect its behavior and are not covered by JOF_TYPESET ops, + * such as integer operations which overflow to a double. These persist across + * GCs, and are used to re-seed script types when they are reanalyzed. + */ +struct TypeResult +{ + uint32_t offset; + Type type; + TypeResult *next; + + TypeResult(uint32_t offset, Type type) + : offset(offset), type(type), next(NULL) + {} +}; + +/* + * Type barriers overview. + * + * Type barriers are a technique for using dynamic type information to improve + * the inferred types within scripts. At certain opcodes --- those with the + * JOF_TYPESET format --- we will construct a type set storing the set of types + * which we have observed to be pushed at that opcode, and will only use those + * observed types when doing propagation downstream from the bytecode. For + * example, in the following script: + * + * function foo(x) { + * return x.f + 10; + * } + * + * Suppose we know the type of 'x' and that the type of its 'f' property is + * either an int or float. To account for all possible behaviors statically, + * we would mark the result of the 'x.f' access as an int or float, as well + * as the result of the addition and the return value of foo (and everywhere + * the result of 'foo' is used). When dealing with polymorphic code, this is + * undesirable behavior --- the type imprecision surrounding the polymorphism + * will tend to leak to many places in the program. + * + * Instead, we will keep track of the types that have been dynamically observed + * to have been produced by the 'x.f', and only use those observed types + * downstream from the access. If the 'x.f' has only ever produced integers, + * we will treat its result as an integer and mark the result of foo as an + * integer. + * + * The set of observed types will be a subset of the set of possible types, + * and if the two sets are different, a type barriers will be added at the + * bytecode which checks the dynamic result every time the bytecode executes + * and makes sure it is in the set of observed types. If it is not, that + * observed set is updated, and the new type information is automatically + * propagated along the already-generated type constraints to the places + * where the result of the bytecode is used. + * + * Observing new types at a bytecode removes type barriers at the bytecode + * (this removal happens lazily, see ScriptAnalysis::pruneTypeBarriers), and if + * all type barriers at a bytecode are removed --- the set of observed types + * grows to match the set of possible types --- then the result of the bytecode + * no longer needs to be dynamically checked (unless the set of possible types + * grows, triggering the generation of new type barriers). + * + * Barriers are only relevant for accesses on properties whose types inference + * actually tracks (see propertySet comment under TypeObject). Accesses on + * other properties may be able to produce additional unobserved types even + * without a barrier present, and can only be compiled to jitcode with special + * knowledge of the property in question (e.g. for lengths of arrays, or + * elements of typed arrays). + */ + +/* + * Barrier introduced at some bytecode. These are added when, during inference, + * we block a type from being propagated as would normally be done for a subset + * constraint. The propagation is technically possible, but we suspect it will + * not happen dynamically and this type needs to be watched for. These are only + * added at reads of properties and at scripted call sites. + */ +struct TypeBarrier +{ + /* Next barrier on the same bytecode. */ + TypeBarrier *next; + + /* Target type set into which propagation was blocked. */ + TypeSet *target; + + /* + * Type which was not added to the target. If target ends up containing the + * type somehow, this barrier can be removed. + */ + Type type; + + /* + * If specified, this barrier can be removed if object has a non-undefined + * value in property id. + */ + JSObject *singleton; + jsid singletonId; + + TypeBarrier(TypeSet *target, Type type, JSObject *singleton, jsid singletonId) + : next(NULL), target(target), type(type), + singleton(singleton), singletonId(singletonId) + {} +}; + +/* Type information about a property. */ +struct Property +{ + /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ + HeapId id; + + /* Possible types for this property, including types inherited from prototypes. */ + TypeSet types; + + inline Property(jsid id); + inline Property(const Property &o); + + static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } + static jsid getKey(Property *p) { return p->id; } +}; + +/* + * Information attached to a TypeObject if it is always constructed using 'new' + * on a particular script. This is used to manage state related to the definite + * properties on the type object: these definite properties depend on type + * information which could change as the script executes (e.g. a scripted + * setter is added to a prototype object), and we need to ensure both that the + * appropriate type constraints are in place when necessary, and that we can + * remove the definite property information and repair the JS stack if the + * constraints are violated. + */ +struct TypeNewScript +{ + HeapPtrFunction fun; + + /* Allocation kind to use for newly constructed objects. */ + gc::AllocKind allocKind; + + /* + * Shape to use for newly constructed objects. Reflects all definite + * properties the object will have. + */ + HeapPtr shape; + + /* + * Order in which properties become initialized. We need this in case a + * scripted setter is added to one of the object's prototypes while it is + * in the middle of being initialized, so we can walk the stack and fixup + * any objects which look for in-progress objects which were prematurely + * set with their final shape. Initialization can traverse stack frames, + * in which case FRAME_PUSH/FRAME_POP are used. + */ + struct Initializer { + enum Kind { + SETPROP, + FRAME_PUSH, + FRAME_POP, + DONE + } kind; + uint32_t offset; + Initializer(Kind kind, uint32_t offset) + : kind(kind), offset(offset) + {} + }; + Initializer *initializerList; + + static inline void writeBarrierPre(TypeNewScript *newScript); + static inline void writeBarrierPost(TypeNewScript *newScript, void *addr); +}; + +/* + * Lazy type objects overview. + * + * Type objects which represent at most one JS object are constructed lazily. + * These include types for native functions, standard classes, scripted + * functions defined at the top level of global/eval scripts, and in some + * other cases. Typical web workloads often create many windows (and many + * copies of standard natives) and many scripts, with comparatively few + * non-singleton types. + * + * We can recover the type information for the object from examining it, + * so don't normally track the possible types of its properties as it is + * updated. Property type sets for the object are only constructed when an + * analyzed script attaches constraints to it: the script is querying that + * property off the object or another which delegates to it, and the analysis + * information is sensitive to changes in the property's type. Future changes + * to the property (whether those uncovered by analysis or those occurring + * in the VM) will treat these properties like those of any other type object. + * + * When a GC occurs, we wipe out all analysis information for all the + * compartment's scripts, so can destroy all properties on singleton type + * objects at the same time. If there is no reference on the stack to the + * type object itself, the type object is also destroyed, and the JS object + * reverts to having a lazy type. + */ + +/* Type information about an object accessed by a script. */ +struct TypeObject : gc::Cell +{ + /* Prototype shared by objects using this type. */ + HeapPtrObject proto; + + /* + * Whether there is a singleton JS object with this type. That JS object + * must appear in type sets instead of this; we include the back reference + * here to allow reverting the JS object to a lazy type. + */ + HeapPtrObject singleton; + + /* + * Value held by singleton if this is a standin type for a singleton JS + * object whose type has not been constructed yet. + */ + static const size_t LAZY_SINGLETON = 1; + bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; } + + /* Flags for this object. */ + TypeObjectFlags flags; + + /* + * Estimate of the contribution of this object to the type sets it appears in. + * This is the sum of the sizes of those sets at the point when the object + * was added. + * + * When the contribution exceeds the CONTRIBUTION_LIMIT, any type sets the + * object is added to are instead marked as unknown. If we get to this point + * we are probably not adding types which will let us do meaningful optimization + * later, and we want to ensure in such cases that our time/space complexity + * is linear, not worst-case cubic as it would otherwise be. + */ + uint32_t contribution; + static const uint32_t CONTRIBUTION_LIMIT = 2000; + + /* + * If non-NULL, objects of this type have always been constructed using + * 'new' on the specified script, which adds some number of properties to + * the object in a definite order before the object escapes. + */ + HeapPtr newScript; + + /* + * Properties of this object. This may contain JSID_VOID, representing the + * types of all integer indexes of the object, and/or JSID_EMPTY, holding + * constraints listening to changes to the object's state. + * + * The type sets in the properties of a type object describe the possible + * values that can be read out of that property in actual JS objects. + * Properties only account for native properties (those with a slot and no + * specialized getter hook) and the elements of dense arrays. For accesses + * on such properties, the correspondence is as follows: + * + * 1. If the type has unknownProperties(), the possible properties and + * value types for associated JSObjects are unknown. + * + * 2. Otherwise, for any JSObject obj with TypeObject type, and any jsid id + * which is a property in obj, before obj->getProperty(id) the property + * in type for id must reflect the result of the getProperty. + * + * There is an exception for properties of singleton JS objects which + * are undefined at the point where the property was (lazily) generated. + * In such cases the property type set will remain empty, and the + * 'undefined' type will only be added after a subsequent assignment or + * deletion. After these properties have been assigned a defined value, + * the only way they can become undefined again is after such an assign + * or deletion. + * + * We establish these by using write barriers on calls to setProperty and + * defineProperty which are on native properties, and by using the inference + * analysis to determine the side effects of code which is JIT-compiled. + */ + Property **propertySet; + + /* If this is an interpreted function, the function object. */ + HeapPtrFunction interpretedFunction; + +#if JS_BITS_PER_WORD == 32 + void *padding; +#endif + + inline TypeObject(JSObject *proto, bool isFunction, bool unknown); + + bool isFunction() { return !!(flags & OBJECT_FLAG_FUNCTION); } + + bool hasAnyFlags(TypeObjectFlags flags) { + JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return !!(this->flags & flags); + } + bool hasAllFlags(TypeObjectFlags flags) { + JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return (this->flags & flags) == flags; + } + + bool unknownProperties() { + JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES, + hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); + return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES); + } + + /* + * Get or create a property of this object. Only call this for properties which + * a script accesses explicitly. 'assign' indicates whether this is for an + * assignment, and the own types of the property will be used instead of + * aggregate types. + */ + inline TypeSet *getProperty(JSContext *cx, jsid id, bool assign); + + /* Get a property only if it already exists. */ + inline TypeSet *maybeGetProperty(JSContext *cx, jsid id); + + inline unsigned getPropertyCount(); + inline Property *getProperty(unsigned i); + + /* Set flags on this object which are implied by the specified key. */ + inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind); + + /* + * Get the global object which all objects of this type are parented to, + * or NULL if there is none known. + */ + inline JSObject *getGlobal(); + + /* Helpers */ + + bool addProperty(JSContext *cx, jsid id, Property **pprop); + bool addDefiniteProperties(JSContext *cx, JSObject *obj); + bool matchDefiniteProperties(JSObject *obj); + void addPrototype(JSContext *cx, TypeObject *proto); + void addPropertyType(JSContext *cx, jsid id, Type type); + void addPropertyType(JSContext *cx, jsid id, const Value &value); + void addPropertyType(JSContext *cx, const char *name, Type type); + void addPropertyType(JSContext *cx, const char *name, const Value &value); + void markPropertyConfigured(JSContext *cx, jsid id); + void markStateChange(JSContext *cx); + void setFlags(JSContext *cx, TypeObjectFlags flags); + void markUnknown(JSContext *cx); + void clearNewScript(JSContext *cx); + void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false); + + void print(JSContext *cx); + + inline void clearProperties(); + inline void sweep(JSContext *cx); + + inline size_t computedSizeOfExcludingThis(); + + void sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf); + + /* + * Type objects don't have explicit finalizers. Memory owned by a type + * object pending deletion is released when weak references are sweeped + * from all the compartment's type objects. + */ + void finalize(JSContext *cx, bool background) {} + + static inline void writeBarrierPre(TypeObject *type); + static inline void writeBarrierPost(TypeObject *type, void *addr); + static inline void readBarrier(TypeObject *type); + + static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; } + + private: + inline uint32_t basePropertyCount() const; + inline void setBasePropertyCount(uint32_t count); + + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(TypeObject, proto) == offsetof(js::shadow::TypeObject, proto)); + } +}; + +/* + * Entries for the per-compartment set of type objects which are the default + * 'new' or the lazy types of some prototype. + */ +struct TypeObjectEntry +{ + typedef JSObject *Lookup; + + static inline HashNumber hash(JSObject *base); + static inline bool match(TypeObject *key, JSObject *lookup); +}; +typedef HashSet, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet; + +/* + * Call to mark a script's arguments as having been created, recompile any + * dependencies and walk the stack if necessary to fix any lazy arguments. + */ +extern void +MarkArgumentsCreated(JSContext *cx, JSScript *script); + +/* Whether to use a new type object when calling 'new' at script/pc. */ +bool +UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc); + +/* + * Whether Array.prototype, or an object on its proto chain, has an + * indexed property. + */ +bool +ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script); + +/* + * Type information about a callsite. this is separated from the bytecode + * information itself so we can handle higher order functions not called + * directly via a bytecode. + */ +struct TypeCallsite +{ + JSScript *script; + jsbytecode *pc; + + /* Whether this is a 'NEW' call. */ + bool isNew; + + /* Types of each argument to the call. */ + TypeSet **argumentTypes; + unsigned argumentCount; + + /* Types of the this variable. */ + TypeSet *thisTypes; + + /* Type set receiving the return value of this call. */ + TypeSet *returnTypes; + + inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc, + bool isNew, unsigned argumentCount); +}; + +/* + * Information attached to outer and inner function scripts nested in one + * another for tracking the reentrance state for outer functions. This state is + * used to generate fast accesses to the args and vars of the outer function. + * + * A function is non-reentrant if, at any point in time, only the most recent + * activation (i.e. call object) is live. An activation is live if either the + * activation is on the stack, or a transitive inner function parented to the + * activation is on the stack. + * + * Because inner functions can be (and, quite often, are) stored in object + * properties and it is difficult to build a fast and robust escape analysis + * to cope with such flow, we detect reentrance dynamically. For the outer + * function, we keep track of the call object for the most recent activation, + * and the number of frames for the function and its inner functions which are + * on the stack. + * + * If the outer function is called while frames associated with a previous + * activation are on the stack, the outer function is reentrant. If an inner + * function is called whose scope does not match the most recent activation, + * the outer function is reentrant. + * + * The situation gets trickier when there are several levels of nesting. + * + * function foo() { + * var a; + * function bar() { + * var b; + * function baz() { return a + b; } + * } + * } + * + * At calls to 'baz', we don't want to do the scope check for the activations + * of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to + * 'baz' which is a reentrant call on 'foo' must also be a reentrant call on + * 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'. + */ +struct TypeScriptNesting +{ + /* + * If this is an inner function, the outer function. If non-NULL, this will + * be the immediate nested parent of the script (even if that parent has + * been marked reentrant). May be NULL even if the script has a nested + * parent, if NAME accesses cannot be tracked into the parent (either the + * script extends its scope with eval() etc., or the parent can make new + * scope chain objects with 'let' or 'with'). + */ + JSScript *parent; + + /* If this is an outer function, list of inner functions. */ + JSScript *children; + + /* Link for children list of parent. */ + JSScript *next; + + /* If this is an outer function, the most recent activation. */ + JSObject *activeCall; + + /* + * If this is an outer function, pointers to the most recent activation's + * arguments and variables arrays. These could be referring either to stack + * values in activeCall's frame (if it has not finished yet) or to the + * internal slots of activeCall (if the frame has finished). Pointers to + * these fields can be embedded directly in JIT code (though remember to + * use 'addDependency == true' when calling resolveNameAccess). + */ + const Value *argArray; + const Value *varArray; + + /* Number of frames for this function on the stack. */ + uint32_t activeFrames; + + TypeScriptNesting() { PodZero(this); } + ~TypeScriptNesting(); +}; + +/* Construct nesting information for script wrt its parent. */ +bool CheckScriptNesting(JSContext *cx, JSScript *script); + +/* Track nesting state when calling or finishing an outer/inner function. */ +void NestingPrologue(JSContext *cx, StackFrame *fp); +void NestingEpilogue(StackFrame *fp); + +/* Persistent type information for a script, retained across GCs. */ +class TypeScript +{ + friend struct ::JSScript; + + /* Analysis information for the script, cleared on each GC. */ + analyze::ScriptAnalysis *analysis; + + /* + * Information about the scope in which a script executes. This information + * is not set until the script has executed at least once and SetScope + * called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE. + */ + static const size_t GLOBAL_MISSING_SCOPE = 0x1; + + /* Global object for the script, if compileAndGo. */ + HeapPtr global; + + public: + + /* Nesting state for outer or inner function scripts. */ + TypeScriptNesting *nesting; + + /* Dynamic types generated at points within this script. */ + TypeResult *dynamicList; + + inline TypeScript(); + + bool hasScope() { return size_t(global.get()) != GLOBAL_MISSING_SCOPE; } + + /* Array of type type sets for variables and JOF_TYPESET ops. */ + TypeSet *typeArray() { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); } + + static inline unsigned NumTypeSets(JSScript *script); + + static bool SetScope(JSContext *cx, JSScript *script, JSObject *scope); + + static inline TypeSet *ReturnTypes(JSScript *script); + static inline TypeSet *ThisTypes(JSScript *script); + static inline TypeSet *ArgTypes(JSScript *script, unsigned i); + static inline TypeSet *LocalTypes(JSScript *script, unsigned i); + + /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */ + static inline TypeSet *SlotTypes(JSScript *script, unsigned slot); + +#ifdef DEBUG + /* Check that correct types were inferred for the values pushed by this bytecode. */ + static void CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp); +#endif + + /* Get the default 'new' object for a given standard class, per the script's global. */ + static inline TypeObject *StandardType(JSContext *cx, JSScript *script, JSProtoKey kind); + + /* Get a type object for an allocation site in this script. */ + static inline TypeObject *InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind); + + /* + * Monitor a bytecode pushing a value which is not accounted for by the + * inference type constraints, such as integer overflow. + */ + static inline void MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc); + static inline void MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc); + static inline void MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc); + + static inline void GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc); + static inline void MonitorOverflow(JSContext *cx); + static inline void MonitorString(JSContext *cx); + static inline void MonitorUnknown(JSContext *cx); + + /* + * Monitor a bytecode pushing any value. This must be called for any opcode + * which is JOF_TYPESET, and where either the script has not been analyzed + * by type inference or where the pc has type barriers. For simplicity, we + * always monitor JOF_TYPESET opcodes in the interpreter and stub calls, + * and only look at barriers when generating JIT code for the script. + */ + static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, + const js::Value &val); + static inline void Monitor(JSContext *cx, const js::Value &rval); + + /* Monitor an assignment at a SETELEM on a non-integer identifier. */ + static inline void MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc, + JSObject *obj, jsid id, const js::Value &val); + + /* Add a type for a variable in a script. */ + static inline void SetThis(JSContext *cx, JSScript *script, Type type); + static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value); + static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type); + static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value); + static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type); + static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value); + + static void Sweep(JSContext *cx, JSScript *script); + inline void trace(JSTracer *trc); + void destroy(); +}; + +struct ArrayTableKey; +typedef HashMap,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable; + +struct ObjectTableKey; +struct ObjectTableEntry; +typedef HashMap ObjectTypeTable; + +struct AllocationSiteKey; +typedef HashMap,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable; + +struct RecompileInfo +{ + JSScript *script; + bool constructing:1; + uint32_t chunkIndex:31; + + bool operator == (const RecompileInfo &o) const { + return script == o.script && constructing == o.constructing && chunkIndex == o.chunkIndex; + } +}; + +/* Type information for a compartment. */ +struct TypeCompartment +{ + /* Whether type inference is enabled in this compartment. */ + bool inferenceEnabled; + + /* Number of scripts in this compartment. */ + unsigned scriptCount; + + /* + * Bit set if all current types must be marked as unknown, and all scripts + * recompiled. Caused by OOM failure within inference operations. + */ + bool pendingNukeTypes; + + /* Pending recompilations to perform before execution of JIT code can resume. */ + Vector *pendingRecompiles; + + /* + * Number of recompilation events and inline frame expansions that have + * occurred in this compartment. If these change, code should not count on + * compiled code or the current stack being intact. + */ + unsigned recompilations; + unsigned frameExpansions; + + /* + * Script currently being compiled. All constraints which look for type + * changes inducing recompilation are keyed to this script. Note: script + * compilation is not reentrant. + */ + RecompileInfo compiledInfo; + + /* Table for referencing types of objects keyed to an allocation site. */ + AllocationSiteTable *allocationSiteTable; + + /* Tables for determining types of singleton/JSON objects. */ + + ArrayTypeTable *arrayTypeTable; + ObjectTypeTable *objectTypeTable; + + void fixArrayType(JSContext *cx, JSObject *obj); + void fixObjectType(JSContext *cx, JSObject *obj); + + /* Constraint solving worklist structures. */ + + /* + * Worklist of types which need to be propagated to constraints. We use a + * worklist to avoid blowing the native stack. + */ + struct PendingWork + { + TypeConstraint *constraint; + TypeSet *source; + Type type; + }; + PendingWork *pendingArray; + unsigned pendingCount; + unsigned pendingCapacity; + + /* Whether we are currently resolving the pending worklist. */ + bool resolving; + + /* Logging fields */ + + /* Counts of stack type sets with some number of possible operand types. */ + static const unsigned TYPE_COUNT_LIMIT = 4; + unsigned typeCounts[TYPE_COUNT_LIMIT]; + unsigned typeCountOver; + + void init(JSContext *cx); + ~TypeCompartment(); + + inline JSCompartment *compartment(); + + /* Add a type to register with a list of constraints. */ + inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type); + bool growPendingArray(JSContext *cx); + + /* Resolve pending type registrations, excluding delayed ones. */ + inline void resolvePending(JSContext *cx); + + /* Prints results of this compartment if spew is enabled or force is set. */ + void print(JSContext *cx, bool force); + + /* + * Make a function or non-function object associated with an optional + * script. The 'key' parameter here may be an array, typed array, function + * or JSProto_Object to indicate a type whose class is unknown (not just + * js_ObjectClass). + */ + TypeObject *newTypeObject(JSContext *cx, JSScript *script, + JSProtoKey kind, JSObject *proto, bool unknown = false); + + /* Make an object for an allocation site. */ + TypeObject *newAllocationSiteTypeObject(JSContext *cx, const AllocationSiteKey &key); + + void nukeTypes(JSContext *cx); + void processPendingRecompiles(JSContext *cx); + + /* Mark all types as needing destruction once inference has 'finished'. */ + void setPendingNukeTypes(JSContext *cx); + + /* Mark a script as needing recompilation once inference has finished. */ + void addPendingRecompile(JSContext *cx, const RecompileInfo &info); + void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc); + + /* Monitor future effects on a bytecode. */ + void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset, + bool returnOnly = false); + + /* Mark any type set containing obj as having a generic object type. */ + void markSetsUnknown(JSContext *cx, TypeObject *obj); + + void sweep(JSContext *cx); + void finalizeObjects(); +}; + +enum SpewChannel { + ISpewOps, /* ops: New constraints and types. */ + ISpewResult, /* result: Final type sets. */ + SPEW_COUNT +}; + +#ifdef DEBUG + +const char * InferSpewColorReset(); +const char * InferSpewColor(TypeConstraint *constraint); +const char * InferSpewColor(TypeSet *types); + +void InferSpew(SpewChannel which, const char *fmt, ...); +const char * TypeString(Type type); +const char * TypeObjectString(TypeObject *type); + +/* Check that the type property for id in obj contains value. */ +bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value); + +#else + +inline const char * InferSpewColorReset() { return NULL; } +inline const char * InferSpewColor(TypeConstraint *constraint) { return NULL; } +inline const char * InferSpewColor(TypeSet *types) { return NULL; } +inline void InferSpew(SpewChannel which, const char *fmt, ...) {} +inline const char * TypeString(Type type) { return NULL; } +inline const char * TypeObjectString(TypeObject *type) { return NULL; } + +#endif + +/* Print a warning, dump state and abort the program. */ +void TypeFailure(JSContext *cx, const char *fmt, ...); + +} /* namespace types */ +} /* namespace js */ + +namespace JS { + template<> class AnchorPermitted { }; +} + +#endif // jsinfer_h___ diff --git a/deps/mozjs/js/src/jsinferinlines.h b/deps/mozjs/js/src/jsinferinlines.h new file mode 100644 index 00000000000..c936448c33c --- /dev/null +++ b/deps/mozjs/js/src/jsinferinlines.h @@ -0,0 +1,1444 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */ +/* vim: set ts=40 sw=4 et tw=99: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SpiderMonkey bytecode type inference + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Hackett + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* Inline members for javascript type inference. */ + +#include "jsarray.h" +#include "jsanalyze.h" +#include "jscompartment.h" +#include "jsgcmark.h" +#include "jsinfer.h" +#include "jsprf.h" +#include "vm/GlobalObject.h" + +#include "vm/Stack-inl.h" + +#ifndef jsinferinlines_h___ +#define jsinferinlines_h___ + +///////////////////////////////////////////////////////////////////// +// Types +///////////////////////////////////////////////////////////////////// + +namespace js { +namespace types { + +/* static */ inline Type +Type::ObjectType(JSObject *obj) +{ + if (obj->hasSingletonType()) + return Type(uintptr_t(obj) | 1); + return Type(uintptr_t(obj->type())); +} + +/* static */ inline Type +Type::ObjectType(TypeObject *obj) +{ + if (obj->singleton) + return Type(uintptr_t(obj->singleton.get()) | 1); + return Type(uintptr_t(obj)); +} + +/* static */ inline Type +Type::ObjectType(TypeObjectKey *obj) +{ + return Type(uintptr_t(obj)); +} + +inline Type +GetValueType(JSContext *cx, const Value &val) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + if (val.isDouble()) + return Type::DoubleType(); + if (val.isObject()) + return Type::ObjectType(&val.toObject()); + return Type::PrimitiveType(val.extractNonDoubleType()); +} + +inline TypeFlags +PrimitiveTypeFlag(JSValueType type) +{ + switch (type) { + case JSVAL_TYPE_UNDEFINED: + return TYPE_FLAG_UNDEFINED; + case JSVAL_TYPE_NULL: + return TYPE_FLAG_NULL; + case JSVAL_TYPE_BOOLEAN: + return TYPE_FLAG_BOOLEAN; + case JSVAL_TYPE_INT32: + return TYPE_FLAG_INT32; + case JSVAL_TYPE_DOUBLE: + return TYPE_FLAG_DOUBLE; + case JSVAL_TYPE_STRING: + return TYPE_FLAG_STRING; + case JSVAL_TYPE_MAGIC: + return TYPE_FLAG_LAZYARGS; + default: + JS_NOT_REACHED("Bad type"); + return 0; + } +} + +inline JSValueType +TypeFlagPrimitive(TypeFlags flags) +{ + switch (flags) { + case TYPE_FLAG_UNDEFINED: + return JSVAL_TYPE_UNDEFINED; + case TYPE_FLAG_NULL: + return JSVAL_TYPE_NULL; + case TYPE_FLAG_BOOLEAN: + return JSVAL_TYPE_BOOLEAN; + case TYPE_FLAG_INT32: + return JSVAL_TYPE_INT32; + case TYPE_FLAG_DOUBLE: + return JSVAL_TYPE_DOUBLE; + case TYPE_FLAG_STRING: + return JSVAL_TYPE_STRING; + case TYPE_FLAG_LAZYARGS: + return JSVAL_TYPE_MAGIC; + default: + JS_NOT_REACHED("Bad type"); + return (JSValueType) 0; + } +} + +/* + * Get the canonical representation of an id to use when doing inference. This + * maintains the constraint that if two different jsids map to the same property + * in JS (e.g. 3 and "3"), they have the same type representation. + */ +inline jsid +MakeTypeId(JSContext *cx, jsid id) +{ + JS_ASSERT(!JSID_IS_EMPTY(id)); + + /* + * All integers must map to the aggregate property for index types, including + * negative integers. + */ + if (JSID_IS_INT(id)) + return JSID_VOID; + + /* + * Check for numeric strings, as in js_StringIsIndex, but allow negative + * and overflowing integers. + */ + if (JSID_IS_STRING(id)) { + JSFlatString *str = JSID_TO_FLAT_STRING(id); + const jschar *cp = str->getCharsZ(cx); + if (JS7_ISDEC(*cp) || *cp == '-') { + cp++; + while (JS7_ISDEC(*cp)) + cp++; + if (*cp == 0) + return JSID_VOID; + } + return id; + } + + return JSID_VOID; +} + +const char * TypeIdStringImpl(jsid id); + +/* Convert an id for printing during debug. */ +static inline const char * +TypeIdString(jsid id) +{ +#ifdef DEBUG + return TypeIdStringImpl(id); +#else + return "(missing)"; +#endif +} + +/* + * Structure for type inference entry point functions. All functions which can + * change type information must use this, and functions which depend on + * intermediate types (i.e. JITs) can use this to ensure that intermediate + * information is not collected and does not change. + * + * Pins inference results so that intermediate type information, TypeObjects + * and JSScripts won't be collected during GC. Does additional sanity checking + * that inference is not reentrant and that recompilations occur properly. + */ +struct AutoEnterTypeInference +{ + JSContext *cx; + bool oldActiveAnalysis; + bool oldActiveInference; + + AutoEnterTypeInference(JSContext *cx, bool compiling = false) + : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis), + oldActiveInference(cx->compartment->activeInference) + { + JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled); + cx->compartment->activeAnalysis = true; + cx->compartment->activeInference = true; + } + + ~AutoEnterTypeInference() + { + cx->compartment->activeAnalysis = oldActiveAnalysis; + cx->compartment->activeInference = oldActiveInference; + + /* + * If there are no more type inference activations on the stack, + * process any triggered recompilations. Note that we should not be + * invoking any scripted code while type inference is running. + * :TODO: assert this. + */ + if (!cx->compartment->activeInference) { + TypeCompartment *types = &cx->compartment->types; + if (types->pendingNukeTypes) + types->nukeTypes(cx); + else if (types->pendingRecompiles) + types->processPendingRecompiles(cx); + } + } +}; + +/* + * Structure marking the currently compiled script, for constraints which can + * trigger recompilation. + */ +struct AutoEnterCompilation +{ + RecompileInfo &info; + + AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex) + : info(cx->compartment->types.compiledInfo) + { + JS_ASSERT(!info.script); + info.script = script; + info.constructing = constructing; + info.chunkIndex = chunkIndex; + } + + ~AutoEnterCompilation() + { + JS_ASSERT(info.script); + info.script = NULL; + info.constructing = false; + info.chunkIndex = 0; + } +}; + +///////////////////////////////////////////////////////////////////// +// Interface functions +///////////////////////////////////////////////////////////////////// + +/* + * These functions check whether inference is enabled before performing some + * action on the type state. To avoid checking cx->typeInferenceEnabled() + * everywhere, it is generally preferred to use one of these functions or + * a type function on JSScript to perform inference operations. + */ + +/* + * Get the default 'new' object for a given standard class, per the currently + * active global. + */ +inline TypeObject * +GetTypeNewObject(JSContext *cx, JSProtoKey key) +{ + JSObject *proto; + if (!js_GetClassPrototype(cx, NULL, key, &proto, NULL)) + return NULL; + return proto->getNewType(cx); +} + +/* Get a type object for the immediate allocation site within a native. */ +inline TypeObject * +GetTypeCallerInitObject(JSContext *cx, JSProtoKey key) +{ + if (cx->typeInferenceEnabled()) { + jsbytecode *pc; + JSScript *script = cx->stack.currentScript(&pc); + if (script) + return TypeScript::InitObject(cx, script, pc, key); + } + return GetTypeNewObject(cx, key); +} + +/* + * When using a custom iterator within the initialization of a 'for in' loop, + * mark the iterator values as unknown. + */ +inline void +MarkIteratorUnknown(JSContext *cx) +{ + extern void MarkIteratorUnknownSlow(JSContext *cx); + + if (cx->typeInferenceEnabled()) + MarkIteratorUnknownSlow(cx); +} + +/* + * Monitor a javascript call, either on entry to the interpreter or made + * from within the interpreter. + */ +inline void +TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing) +{ + extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, + const CallArgs &args, bool constructing); + + JSObject *callee = &args.callee(); + if (callee->isFunction()) { + JSFunction *fun = callee->toFunction(); + if (fun->isInterpreted()) { + JSScript *script = fun->script(); + if (!script->ensureRanAnalysis(cx, fun->environment())) + return; + if (cx->typeInferenceEnabled()) + TypeMonitorCallSlow(cx, callee, args, constructing); + } + } +} + +inline bool +TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id) +{ + if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties()) + return false; + + if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id)) + return false; + + return true; +} + +/* Add a possible type for a property of obj. */ +inline void +AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type) +{ + if (cx->typeInferenceEnabled()) + id = MakeTypeId(cx, id); + if (TrackPropertyTypes(cx, obj, id)) + obj->type()->addPropertyType(cx, id, type); +} + +inline void +AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value) +{ + if (cx->typeInferenceEnabled()) + id = MakeTypeId(cx, id); + if (TrackPropertyTypes(cx, obj, id)) + obj->type()->addPropertyType(cx, id, value); +} + +inline void +AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type) +{ + if (cx->typeInferenceEnabled() && !obj->unknownProperties()) + obj->addPropertyType(cx, name, type); +} + +inline void +AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value) +{ + if (cx->typeInferenceEnabled() && !obj->unknownProperties()) + obj->addPropertyType(cx, name, value); +} + +/* Set one or more dynamic flags on a type object. */ +inline void +MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags) +{ + if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) + obj->type()->setFlags(cx, flags); +} + +/* + * Mark all properties of a type object as unknown. If markSetsUnknown is set, + * scan the entire compartment and mark all type sets containing it as having + * an unknown object. This is needed for correctness in dealing with mutable + * __proto__, which can change the type of an object dynamically. + */ +inline void +MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj, + bool markSetsUnknown = false) +{ + if (cx->typeInferenceEnabled()) { + if (!obj->unknownProperties()) + obj->markUnknown(cx); + if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) + cx->compartment->types.markSetsUnknown(cx, obj); + } +} + +/* + * Mark any property which has been deleted or configured to be non-writable or + * have a getter/setter. + */ +inline void +MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id) +{ + if (cx->typeInferenceEnabled()) + id = MakeTypeId(cx, id); + if (TrackPropertyTypes(cx, obj, id)) + obj->type()->markPropertyConfigured(cx, id); +} + +/* Mark a state change on a particular object. */ +inline void +MarkObjectStateChange(JSContext *cx, JSObject *obj) +{ + if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties()) + obj->type()->markStateChange(cx); +} + +/* + * For an array or object which has not yet escaped and been referenced elsewhere, + * pick a new type based on the object's current contents. + */ + +inline void +FixArrayType(JSContext *cx, JSObject *obj) +{ + if (cx->typeInferenceEnabled()) + cx->compartment->types.fixArrayType(cx, obj); +} + +inline void +FixObjectType(JSContext *cx, JSObject *obj) +{ + if (cx->typeInferenceEnabled()) + cx->compartment->types.fixObjectType(cx, obj); +} + +/* Interface helpers for JSScript */ +extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval); +extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type); + +inline bool +UseNewTypeAtEntry(JSContext *cx, StackFrame *fp) +{ + return fp->isConstructing() && cx->typeInferenceEnabled() && + fp->prev() && fp->prev()->isScriptFrame() && + UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp)); +} + +///////////////////////////////////////////////////////////////////// +// Script interface functions +///////////////////////////////////////////////////////////////////// + +inline +TypeScript::TypeScript() +{ + this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE; +} + +/* static */ inline unsigned +TypeScript::NumTypeSets(JSScript *script) +{ + return script->nTypeSets + analyze::TotalSlots(script); +} + +/* static */ inline TypeSet * +TypeScript::ReturnTypes(JSScript *script) +{ + return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot(); +} + +/* static */ inline TypeSet * +TypeScript::ThisTypes(JSScript *script) +{ + return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot(); +} + +/* + * Note: for non-escaping arguments and locals, argTypes/localTypes reflect + * only the initial type of the variable (e.g. passed values for argTypes, + * or undefined for localTypes) and not types from subsequent assignments. + */ + +/* static */ inline TypeSet * +TypeScript::ArgTypes(JSScript *script, unsigned i) +{ + JS_ASSERT(i < script->function()->nargs); + return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i); +} + +/* static */ inline TypeSet * +TypeScript::LocalTypes(JSScript *script, unsigned i) +{ + JS_ASSERT(i < script->nfixed); + return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i); +} + +/* static */ inline TypeSet * +TypeScript::SlotTypes(JSScript *script, unsigned slot) +{ + JS_ASSERT(slot < js::analyze::TotalSlots(script)); + return script->types->typeArray() + script->nTypeSets + slot; +} + +/* static */ inline TypeObject * +TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key) +{ + JSObject *proto; + if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL)) + return NULL; + return proto->getNewType(cx); +} + +struct AllocationSiteKey { + JSScript *script; + + uint32_t offset : 24; + JSProtoKey kind : 8; + + static const uint32_t OFFSET_LIMIT = (1 << 23); + + AllocationSiteKey() { PodZero(this); } + + typedef AllocationSiteKey Lookup; + + static inline uint32_t hash(AllocationSiteKey key) { + return uint32_t(size_t(key.script->code + key.offset)) ^ key.kind; + } + + static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { + return a.script == b.script && a.offset == b.offset && a.kind == b.kind; + } +}; + +/* static */ inline TypeObject * +TypeScript::InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind) +{ + /* :XXX: Limit script->length so we don't need to check the offset up front? */ + uint32_t offset = pc - script->code; + + if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT) + return GetTypeNewObject(cx, kind); + + AllocationSiteKey key; + key.script = script; + key.offset = offset; + key.kind = kind; + + if (!cx->compartment->types.allocationSiteTable) + return cx->compartment->types.newAllocationSiteTypeObject(cx, key); + + AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key); + + if (p) + return p->value; + return cx->compartment->types.newAllocationSiteTypeObject(cx, key); +} + +/* static */ inline void +TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) +{ + if (cx->typeInferenceEnabled()) + TypeMonitorResult(cx, script, pc, rval); +} + +/* static */ inline void +TypeScript::MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + if (cx->typeInferenceEnabled()) + TypeDynamicResult(cx, script, pc, Type::DoubleType()); +} + +/* static */ inline void +TypeScript::MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + if (cx->typeInferenceEnabled()) + TypeDynamicResult(cx, script, pc, Type::StringType()); +} + +/* static */ inline void +TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + if (cx->typeInferenceEnabled()) + TypeDynamicResult(cx, script, pc, Type::UnknownType()); +} + +/* static */ inline void +TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc) +{ + *script = cx->fp()->script(); + *pc = cx->regs().pc; +} + +/* static */ inline void +TypeScript::MonitorOverflow(JSContext *cx) +{ + JSScript *script; + jsbytecode *pc; + GetPcScript(cx, &script, &pc); + MonitorOverflow(cx, script, pc); +} + +/* static */ inline void +TypeScript::MonitorString(JSContext *cx) +{ + JSScript *script; + jsbytecode *pc; + GetPcScript(cx, &script, &pc); + MonitorString(cx, script, pc); +} + +/* static */ inline void +TypeScript::MonitorUnknown(JSContext *cx) +{ + JSScript *script; + jsbytecode *pc; + GetPcScript(cx, &script, &pc); + MonitorUnknown(cx, script, pc); +} + +/* static */ inline void +TypeScript::Monitor(JSContext *cx, const js::Value &rval) +{ + JSScript *script; + jsbytecode *pc; + GetPcScript(cx, &script, &pc); + Monitor(cx, script, pc, rval); +} + +/* static */ inline void +TypeScript::MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc, + JSObject *obj, jsid id, const js::Value &rval) +{ + if (cx->typeInferenceEnabled() && !obj->hasSingletonType()) { + /* + * Mark as unknown any object which has had dynamic assignments to + * non-integer properties at SETELEM opcodes. This avoids making large + * numbers of type properties for hashmap-style objects. We don't need + * to do this for objects with singleton type, because type properties + * are only constructed for them when analyzed scripts depend on those + * specific properties. + */ + uint32_t i; + if (js_IdIsIndex(id, &i)) + return; + MarkTypeObjectUnknownProperties(cx, obj->type()); + } +} + +/* static */ inline void +TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) +{ + if (!cx->typeInferenceEnabled()) + return; + JS_ASSERT(script->types); + + /* Analyze the script regardless if -a was used. */ + bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS); + + if (!ThisTypes(script)->hasType(type) || analyze) { + AutoEnterTypeInference enter(cx); + + InferSpew(ISpewOps, "externalType: setThis #%u: %s", + script->id(), TypeString(type)); + ThisTypes(script)->addType(cx, type); + + if (analyze && script->types->hasScope()) + script->ensureRanInference(cx); + } +} + +/* static */ inline void +TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) +{ + if (cx->typeInferenceEnabled()) + SetThis(cx, script, GetValueType(cx, value)); +} + +/* static */ inline void +TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type) +{ + if (!cx->typeInferenceEnabled()) + return; + JS_ASSERT(script->types); + + if (!LocalTypes(script, local)->hasType(type)) { + AutoEnterTypeInference enter(cx); + + InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s", + script->id(), local, TypeString(type)); + LocalTypes(script, local)->addType(cx, type); + } +} + +/* static */ inline void +TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value) +{ + if (cx->typeInferenceEnabled()) { + Type type = GetValueType(cx, value); + SetLocal(cx, script, local, type); + } +} + +/* static */ inline void +TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) +{ + if (!cx->typeInferenceEnabled()) + return; + JS_ASSERT(script->types); + + if (!ArgTypes(script, arg)->hasType(type)) { + AutoEnterTypeInference enter(cx); + + InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", + script->id(), arg, TypeString(type)); + ArgTypes(script, arg)->addType(cx, type); + } +} + +/* static */ inline void +TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) +{ + if (cx->typeInferenceEnabled()) { + Type type = GetValueType(cx, value); + SetArgument(cx, script, arg, type); + } +} + +void +TypeScript::trace(JSTracer *trc) +{ + if (hasScope() && global) + gc::MarkObject(trc, global, "script_global"); + + /* Note: nesting does not keep anything alive. */ +} + +///////////////////////////////////////////////////////////////////// +// TypeCompartment +///////////////////////////////////////////////////////////////////// + +inline JSCompartment * +TypeCompartment::compartment() +{ + return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); +} + +inline void +TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type) +{ + JS_ASSERT(this == &cx->compartment->types); + JS_ASSERT(!cx->runtime->gcRunning); + + InferSpew(ISpewOps, "pending: %sC%p%s %s", + InferSpewColor(constraint), constraint, InferSpewColorReset(), + TypeString(type)); + + if ((pendingCount == pendingCapacity) && !growPendingArray(cx)) + return; + + PendingWork &pending = pendingArray[pendingCount++]; + pending.constraint = constraint; + pending.source = source; + pending.type = type; +} + +inline void +TypeCompartment::resolvePending(JSContext *cx) +{ + JS_ASSERT(this == &cx->compartment->types); + + if (resolving) { + /* There is an active call further up resolving the worklist. */ + return; + } + + resolving = true; + + /* Handle all pending type registrations. */ + while (pendingCount) { + const PendingWork &pending = pendingArray[--pendingCount]; + InferSpew(ISpewOps, "resolve: %sC%p%s %s", + InferSpewColor(pending.constraint), pending.constraint, + InferSpewColorReset(), TypeString(pending.type)); + pending.constraint->newType(cx, pending.source, pending.type); + } + + resolving = false; +} + +///////////////////////////////////////////////////////////////////// +// TypeSet +///////////////////////////////////////////////////////////////////// + +/* + * The sets of objects and scripts in a type set grow monotonically, are usually + * empty, almost always small, and sometimes big. For empty or singleton sets, + * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE, + * an array of this length is used to store the elements. For larger sets, a hash + * table filled to 25%-50% of capacity is used, with collisions resolved by linear + * probing. TODO: replace these with jshashtables. + */ +const unsigned SET_ARRAY_SIZE = 8; + +/* Get the capacity of a set with the given element count. */ +static inline unsigned +HashSetCapacity(unsigned count) +{ + JS_ASSERT(count >= 2); + + if (count <= SET_ARRAY_SIZE) + return SET_ARRAY_SIZE; + + unsigned log2; + JS_FLOOR_LOG2(log2, count); + return 1 << (log2 + 2); +} + +/* Compute the FNV hash for the low 32 bits of v. */ +template +static inline uint32_t +HashKey(T v) +{ + uint32_t nv = KEY::keyBits(v); + + uint32_t hash = 84696351 ^ (nv & 0xff); + hash = (hash * 16777619) ^ ((nv >> 8) & 0xff); + hash = (hash * 16777619) ^ ((nv >> 16) & 0xff); + return (hash * 16777619) ^ ((nv >> 24) & 0xff); +} + +/* + * Insert space for an element into the specified set and grow its capacity if needed. + * returned value is an existing or new entry (NULL if new). + */ +template +static U ** +HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key) +{ + unsigned capacity = HashSetCapacity(count); + unsigned insertpos = HashKey(key) & (capacity - 1); + + /* Whether we are converting from a fixed array to hashtable. */ + bool converting = (count == SET_ARRAY_SIZE); + + if (!converting) { + while (values[insertpos] != NULL) { + if (KEY::getKey(values[insertpos]) == key) + return &values[insertpos]; + insertpos = (insertpos + 1) & (capacity - 1); + } + } + + count++; + unsigned newCapacity = HashSetCapacity(count); + + if (newCapacity == capacity) { + JS_ASSERT(!converting); + return &values[insertpos]; + } + + U **newValues = compartment->typeLifoAlloc.newArray(newCapacity); + if (!newValues) + return NULL; + PodZero(newValues, newCapacity); + + for (unsigned i = 0; i < capacity; i++) { + if (values[i]) { + unsigned pos = HashKey(KEY::getKey(values[i])) & (newCapacity - 1); + while (newValues[pos] != NULL) + pos = (pos + 1) & (newCapacity - 1); + newValues[pos] = values[i]; + } + } + + values = newValues; + + insertpos = HashKey(key) & (newCapacity - 1); + while (values[insertpos] != NULL) + insertpos = (insertpos + 1) & (newCapacity - 1); + return &values[insertpos]; +} + +/* + * Insert an element into the specified set if it is not already there, returning + * an entry which is NULL if the element was not there. + */ +template +static inline U ** +HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key) +{ + if (count == 0) { + JS_ASSERT(values == NULL); + count++; + return (U **) &values; + } + + if (count == 1) { + U *oldData = (U*) values; + if (KEY::getKey(oldData) == key) + return (U **) &values; + + values = compartment->typeLifoAlloc.newArray(SET_ARRAY_SIZE); + if (!values) { + values = (U **) oldData; + return NULL; + } + PodZero(values, SET_ARRAY_SIZE); + count++; + + values[0] = oldData; + return &values[1]; + } + + if (count <= SET_ARRAY_SIZE) { + for (unsigned i = 0; i < count; i++) { + if (KEY::getKey(values[i]) == key) + return &values[i]; + } + + if (count < SET_ARRAY_SIZE) { + count++; + return &values[count - 1]; + } + } + + return HashSetInsertTry(compartment, values, count, key); +} + +/* Lookup an entry in a hash set, return NULL if it does not exist. */ +template +static inline U * +HashSetLookup(U **values, unsigned count, T key) +{ + if (count == 0) + return NULL; + + if (count == 1) + return (KEY::getKey((U *) values) == key) ? (U *) values : NULL; + + if (count <= SET_ARRAY_SIZE) { + for (unsigned i = 0; i < count; i++) { + if (KEY::getKey(values[i]) == key) + return values[i]; + } + return NULL; + } + + unsigned capacity = HashSetCapacity(count); + unsigned pos = HashKey(key) & (capacity - 1); + + while (values[pos] != NULL) { + if (KEY::getKey(values[pos]) == key) + return values[pos]; + pos = (pos + 1) & (capacity - 1); + } + + return NULL; +} + +inline bool +TypeSet::hasType(Type type) +{ + if (unknown()) + return true; + + if (type.isUnknown()) { + return false; + } else if (type.isPrimitive()) { + return !!(flags & PrimitiveTypeFlag(type.primitive())); + } else if (type.isAnyObject()) { + return !!(flags & TYPE_FLAG_ANYOBJECT); + } else { + return !!(flags & TYPE_FLAG_ANYOBJECT) || + HashSetLookup + (objectSet, baseObjectCount(), type.objectKey()) != NULL; + } +} + +inline void +TypeSet::setBaseObjectCount(uint32_t count) +{ + JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT); + flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK) + | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT); +} + +inline void +TypeSet::clearObjects() +{ + setBaseObjectCount(0); + objectSet = NULL; +} + +inline void +TypeSet::addType(JSContext *cx, Type type) +{ + JS_ASSERT(cx->compartment->activeInference); + + if (unknown()) + return; + + if (type.isUnknown()) { + flags |= TYPE_FLAG_BASE_MASK; + clearObjects(); + JS_ASSERT(unknown()); + } else if (type.isPrimitive()) { + TypeFlags flag = PrimitiveTypeFlag(type.primitive()); + if (flags & flag) + return; + + /* If we add float to a type set it is also considered to contain int. */ + if (flag == TYPE_FLAG_DOUBLE) + flag |= TYPE_FLAG_INT32; + + flags |= flag; + } else { + if (flags & TYPE_FLAG_ANYOBJECT) + return; + if (type.isAnyObject()) + goto unknownObject; + uint32_t objectCount = baseObjectCount(); + TypeObjectKey *object = type.objectKey(); + TypeObjectKey **pentry = HashSetInsert + (cx->compartment, objectSet, objectCount, object); + if (!pentry) { + cx->compartment->types.setPendingNukeTypes(cx); + return; + } + if (*pentry) + return; + *pentry = object; + + setBaseObjectCount(objectCount); + + if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) + goto unknownObject; + + if (type.isTypeObject()) { + TypeObject *nobject = type.typeObject(); + JS_ASSERT(!nobject->singleton); + if (nobject->unknownProperties()) + goto unknownObject; + if (objectCount > 1) { + nobject->contribution += (objectCount - 1) * (objectCount - 1); + if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) { + InferSpew(ISpewOps, "limitUnknown: %sT%p%s", + InferSpewColor(this), this, InferSpewColorReset()); + goto unknownObject; + } + } + } + } + + if (false) { + unknownObject: + type = Type::AnyObjectType(); + flags |= TYPE_FLAG_ANYOBJECT; + clearObjects(); + } + + InferSpew(ISpewOps, "addType: %sT%p%s %s", + InferSpewColor(this), this, InferSpewColorReset(), + TypeString(type)); + + /* Propagate the type to all constraints. */ + TypeConstraint *constraint = constraintList; + while (constraint) { + cx->compartment->types.addPending(cx, constraint, this, type); + constraint = constraint->next; + } + + cx->compartment->types.resolvePending(cx); +} + +inline void +TypeSet::setOwnProperty(JSContext *cx, bool configured) +{ + TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0); + + if ((flags & nflags) == nflags) + return; + + flags |= nflags; + + /* Propagate the change to all constraints. */ + TypeConstraint *constraint = constraintList; + while (constraint) { + constraint->newPropertyState(cx, this); + constraint = constraint->next; + } +} + +inline unsigned +TypeSet::getObjectCount() +{ + JS_ASSERT(!unknownObject()); + uint32_t count = baseObjectCount(); + if (count > SET_ARRAY_SIZE) + return HashSetCapacity(count); + return count; +} + +inline TypeObjectKey * +TypeSet::getObject(unsigned i) +{ + JS_ASSERT(i < getObjectCount()); + if (baseObjectCount() == 1) { + JS_ASSERT(i == 0); + return (TypeObjectKey *) objectSet; + } + return objectSet[i]; +} + +inline JSObject * +TypeSet::getSingleObject(unsigned i) +{ + TypeObjectKey *key = getObject(i); + return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL; +} + +inline TypeObject * +TypeSet::getTypeObject(unsigned i) +{ + TypeObjectKey *key = getObject(i); + return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL; +} + +///////////////////////////////////////////////////////////////////// +// TypeCallsite +///////////////////////////////////////////////////////////////////// + +inline +TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc, + bool isNew, unsigned argumentCount) + : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount), + thisTypes(NULL), returnTypes(NULL) +{ + /* Caller must check for failure. */ + argumentTypes = cx->typeLifoAlloc().newArray(argumentCount); +} + +///////////////////////////////////////////////////////////////////// +// TypeObject +///////////////////////////////////////////////////////////////////// + +inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown) +{ + PodZero(this); + + /* Inner objects may not appear on prototype chains. */ + JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject); + + this->proto = proto; + + if (function) + flags |= OBJECT_FLAG_FUNCTION; + if (unknown) + flags |= OBJECT_FLAG_UNKNOWN_MASK; + + InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); +} + +inline uint32_t +TypeObject::basePropertyCount() const +{ + return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; +} + +inline void +TypeObject::setBasePropertyCount(uint32_t count) +{ + JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); + flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) + | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); +} + +inline TypeSet * +TypeObject::getProperty(JSContext *cx, jsid id, bool assign) +{ + JS_ASSERT(cx->compartment->activeInference); + JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); + JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id)); + JS_ASSERT(!unknownProperties()); + + uint32_t propertyCount = basePropertyCount(); + Property **pprop = HashSetInsert + (cx->compartment, propertySet, propertyCount, id); + if (!pprop) { + cx->compartment->types.setPendingNukeTypes(cx); + return NULL; + } + + if (!*pprop) { + setBasePropertyCount(propertyCount); + if (!addProperty(cx, id, pprop)) + return NULL; + if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { + markUnknown(cx); + TypeSet *types = TypeSet::make(cx, "propertyOverflow"); + types->addType(cx, Type::UnknownType()); + return types; + } + } + + TypeSet *types = &(*pprop)->types; + + if (assign) + types->setOwnProperty(cx, false); + + return types; +} + +inline TypeSet * +TypeObject::maybeGetProperty(JSContext *cx, jsid id) +{ + JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); + JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id)); + JS_ASSERT(!unknownProperties()); + + Property *prop = HashSetLookup + (propertySet, basePropertyCount(), id); + + return prop ? &prop->types : NULL; +} + +inline unsigned +TypeObject::getPropertyCount() +{ + uint32_t count = basePropertyCount(); + if (count > SET_ARRAY_SIZE) + return HashSetCapacity(count); + return count; +} + +inline Property * +TypeObject::getProperty(unsigned i) +{ + JS_ASSERT(i < getPropertyCount()); + if (basePropertyCount() == 1) { + JS_ASSERT(i == 0); + return (Property *) propertySet; + } + return propertySet[i]; +} + +inline void +TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key) +{ + TypeObjectFlags flags = 0; + + switch (key) { + case JSProto_Function: + JS_ASSERT(isFunction()); + /* FALLTHROUGH */ + + case JSProto_Object: + flags = OBJECT_FLAG_NON_DENSE_ARRAY + | OBJECT_FLAG_NON_PACKED_ARRAY + | OBJECT_FLAG_NON_TYPED_ARRAY; + break; + + case JSProto_Array: + flags = OBJECT_FLAG_NON_TYPED_ARRAY; + break; + + default: + /* :XXX: abstract */ + JS_ASSERT(key == JSProto_Int8Array || + key == JSProto_Uint8Array || + key == JSProto_Int16Array || + key == JSProto_Uint16Array || + key == JSProto_Int32Array || + key == JSProto_Uint32Array || + key == JSProto_Float32Array || + key == JSProto_Float64Array || + key == JSProto_Uint8ClampedArray); + flags = OBJECT_FLAG_NON_DENSE_ARRAY + | OBJECT_FLAG_NON_PACKED_ARRAY; + break; + } + + if (!hasAllFlags(flags)) + setFlags(cx, flags); +} + +inline JSObject * +TypeObject::getGlobal() +{ + if (singleton) + return &singleton->global(); + if (interpretedFunction && interpretedFunction->script()->compileAndGo) + return &interpretedFunction->global(); + return NULL; +} + +inline void +TypeObject::writeBarrierPre(TypeObject *type) +{ +#ifdef JSGC_INCREMENTAL + if (!type) + return; + + JSCompartment *comp = type->compartment(); + if (comp->needsBarrier()) + MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "write barrier"); +#endif +} + +inline void +TypeObject::writeBarrierPost(TypeObject *type, void *addr) +{ +} + +inline void +TypeObject::readBarrier(TypeObject *type) +{ +#ifdef JSGC_INCREMENTAL + JSCompartment *comp = type->compartment(); + if (comp->needsBarrier()) + MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier"); +#endif +} + +inline void +TypeNewScript::writeBarrierPre(TypeNewScript *newScript) +{ +#ifdef JSGC_INCREMENTAL + if (!newScript) + return; + + JSCompartment *comp = newScript->fun->compartment(); + if (comp->needsBarrier()) { + MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier"); + MarkShape(comp->barrierTracer(), newScript->shape, "write barrier"); + } +#endif +} + +inline void +TypeNewScript::writeBarrierPost(TypeNewScript *newScript, void *addr) +{ +} + +inline +Property::Property(jsid id) + : id(id) +{ +} + +inline +Property::Property(const Property &o) + : id(o.id.get()), types(o.types) +{ +} + +} } /* namespace js::types */ + +inline bool +JSScript::ensureHasTypes(JSContext *cx) +{ + return types || makeTypes(cx); +} + +inline bool +JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope) +{ + JSScript *self = this; + + if (!self->ensureHasTypes(cx)) + return false; + if (!self->types->hasScope()) { + js::CheckRoot root(cx, &self); + js::RootObject objRoot(cx, &scope); + if (!js::types::TypeScript::SetScope(cx, self, scope)) + return false; + } + if (!self->hasAnalysis() && !self->makeAnalysis(cx)) + return false; + JS_ASSERT(self->analysis()->ranBytecode()); + return true; +} + +inline bool +JSScript::ensureRanInference(JSContext *cx) +{ + if (!ensureRanAnalysis(cx, NULL)) + return false; + if (!analysis()->ranInference()) { + js::types::AutoEnterTypeInference enter(cx); + analysis()->analyzeTypes(cx); + } + return !analysis()->OOM() && + !cx->compartment->types.pendingNukeTypes; +} + +inline bool +JSScript::hasAnalysis() +{ + return types && types->analysis; +} + +inline js::analyze::ScriptAnalysis * +JSScript::analysis() +{ + JS_ASSERT(hasAnalysis()); + return types->analysis; +} + +inline void +JSScript::clearAnalysis() +{ + if (types) + types->analysis = NULL; +} + +inline void +js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32_t offset, uint32_t which, + js::types::Type type) +{ + js::types::TypeSet *pushed = pushedTypes(offset, which); + pushed->addType(cx, type); +} + +inline js::types::TypeObject * +JSCompartment::getEmptyType(JSContext *cx) +{ + if (!emptyTypeObject) + emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, NULL, true); + return emptyTypeObject; +} + +#endif // jsinferinlines_h___ diff --git a/deps/mozjs/js/src/jsinterp.cpp b/deps/mozjs/js/src/jsinterp.cpp index fb2c1a5b856..794c480600f 100644 --- a/deps/mozjs/js/src/jsinterp.cpp +++ b/deps/mozjs/js/src/jsinterp.cpp @@ -45,8 +45,6 @@ #include #include #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" @@ -59,6 +57,7 @@ #include "jsdbgapi.h" #include "jsfun.h" #include "jsgc.h" +#include "jsgcmark.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" @@ -66,30 +65,31 @@ #include "jsobj.h" #include "jsopcode.h" #include "jspropertycache.h" -#include "jsemit.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#include "jsstaticcheck.h" -#include "jstracer.h" #include "jslibmath.h" -#include "jsvector.h" + +#include "frontend/BytecodeEmitter.h" #ifdef JS_METHODJIT #include "methodjit/MethodJIT.h" -#include "methodjit/MethodJIT-inl.h" #include "methodjit/Logging.h" #endif +#include "vm/Debugger.h" + #include "jsatominlines.h" +#include "jsinferinlines.h" #include "jsinterpinlines.h" #include "jsobjinlines.h" +#include "jsopcodeinlines.h" #include "jsprobes.h" #include "jspropertycacheinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" -#include "jsstrinlines.h" -#include "jsopcodeinlines.h" +#include "jstypedarrayinlines.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" @@ -103,112 +103,7 @@ using namespace js; using namespace js::gc; - -/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */ -#if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ - -JSObject * -js::GetScopeChain(JSContext *cx) -{ - StackFrame *fp = js_GetTopStackFrame(cx); - if (!fp) { - /* - * There is no code active on this context. In place of an actual - * scope chain, use the context's global object, which is set in - * js_InitFunctionAndObjectClasses, and which represents the default - * scope chain for the embedding. See also js_FindClassObject. - * - * For embeddings that use the inner and outer object hooks, the inner - * object represents the ultimate global object, with the outer object - * acting as a stand-in. - */ - JSObject *obj = cx->globalObject; - if (!obj) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - - OBJ_TO_INNER_OBJECT(cx, obj); - return obj; - } - return GetScopeChain(cx, fp); -} - -/* - * This computes the blockChain by iterating through the bytecode - * of the current script until it reaches the PC. Each time it sees - * an ENTERBLOCK or LEAVEBLOCK instruction, it records the new - * blockChain. A faster variant of this function that doesn't - * require bytecode scanning appears below. - */ -JSObject * -js::GetBlockChain(JSContext *cx, StackFrame *fp) -{ - if (!fp->isScriptFrame()) - return NULL; - - /* Assume that imacros don't affect blockChain */ - jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); - - JSScript *script = fp->script(); - jsbytecode *start = script->code; - JS_ASSERT(target >= start && target < start + script->length); - - JSObject *blockChain = NULL; - uintN indexBase = 0; - ptrdiff_t oplen; - for (jsbytecode *pc = start; pc < target; pc += oplen) { - JSOp op = js_GetOpcode(cx, script, pc); - const JSCodeSpec *cs = &js_CodeSpec[op]; - oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(pc); - - if (op == JSOP_INDEXBASE) - indexBase = GET_INDEXBASE(pc); - else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3) - indexBase = (op - JSOP_INDEXBASE1 + 1) << 16; - else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0) - indexBase = 0; - else if (op == JSOP_ENTERBLOCK) - blockChain = script->getObject(indexBase + GET_INDEX(pc)); - else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) - blockChain = blockChain->getParent(); - else if (op == JSOP_BLOCKCHAIN) - blockChain = script->getObject(indexBase + GET_INDEX(pc)); - else if (op == JSOP_NULLBLOCKCHAIN) - blockChain = NULL; - } - - return blockChain; -} - -/* - * This function computes the current blockChain, but only in - * the special case where a BLOCKCHAIN or NULLBLOCKCHAIN - * instruction appears immediately after the current PC. - * We ensure this happens for a few important ops like DEFFUN. - * |oplen| is the length of opcode at the current PC. - */ -JSObject * -js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) -{ - /* Assume that we're in a script frame. */ - jsbytecode *pc = fp->pc(cx); - JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op); - - pc += oplen; - op = JSOp(*pc); - JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op); - - /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */ - if (op == JSOP_NULLBLOCKCHAIN) - return NULL; - if (op == JSOP_BLOCKCHAIN) - return fp->script()->getObject(GET_INDEX(pc)); - - return GetBlockChain(cx, fp); -} +using namespace js::types; /* * We can't determine in advance which local variables can live on the stack and @@ -240,10 +135,10 @@ js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) * This lazy cloning is implemented in GetScopeChain, which is also used in * some other cases --- entering 'with' blocks, for example. */ -static JSObject * -GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) +JSObject * +js::GetScopeChain(JSContext *cx, StackFrame *fp) { - JSObject *sharedBlock = blockChain; + StaticBlockObject *sharedBlock = fp->maybeBlockChain(); if (!sharedBlock) { /* @@ -255,9 +150,6 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) return &fp->scopeChain(); } - /* We don't handle cloning blocks on trace. */ - LeaveTrace(cx); - /* * We have one or more lexical scopes to reflect into fp->scopeChain, so * make sure there's a call object at the current head of the scope chain, @@ -281,8 +173,8 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) * to, but not including, that prototype. */ limitClone = &fp->scopeChain(); - while (limitClone->getClass() == &js_WithClass) - limitClone = limitClone->getParent(); + while (limitClone->isWith()) + limitClone = &limitClone->asWith().enclosingScope(); JS_ASSERT(limitClone); /* @@ -314,36 +206,36 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) * Special-case cloning the innermost block; this doesn't have enough in * common with subsequent steps to include in the loop. * - * js_CloneBlockObject leaves the clone's parent slot uninitialized. We - * populate it below. + * create() leaves the clone's enclosingScope unset. We set it below. */ - JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp); + ClonedBlockObject *innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp); if (!innermostNewChild) return NULL; - AutoObjectRooter tvr(cx, innermostNewChild); /* * Clone our way towards outer scopes until we reach the innermost * enclosing function, or the innermost block we've already cloned. */ - JSObject *newChild = innermostNewChild; + ClonedBlockObject *newChild = innermostNewChild; for (;;) { JS_ASSERT(newChild->getProto() == sharedBlock); - sharedBlock = sharedBlock->getParent(); + sharedBlock = sharedBlock->enclosingBlock(); /* Sometimes limitBlock will be NULL, so check that first. */ if (sharedBlock == limitBlock || !sharedBlock) break; /* As in the call above, we don't know the real parent yet. */ - JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp); + ClonedBlockObject *clone = ClonedBlockObject::create(cx, *sharedBlock, fp); if (!clone) return NULL; - newChild->setParent(clone); + if (!newChild->setEnclosingScope(cx, *clone)) + return NULL; newChild = clone; } - newChild->setParent(&fp->scopeChain()); + if (!newChild->setEnclosingScope(cx, fp->scopeChain())) + return NULL; /* @@ -351,7 +243,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) * found it in blockChain. */ JS_ASSERT_IF(limitBlock && - limitBlock->isBlock() && + limitBlock->isClonedBlock() && limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp), sharedBlock); @@ -361,15 +253,34 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) } JSObject * -js::GetScopeChain(JSContext *cx, StackFrame *fp) +js::GetScopeChain(JSContext *cx) { - return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp)); -} + /* + * Note: we don't need to expand inline frames here, because frames are + * only inlined when the caller and callee share the same scope chain. + */ + StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); + if (!fp) { + /* + * There is no code active on this context. In place of an actual + * scope chain, use the context's global object, which is set in + * js_InitFunctionAndObjectClasses, and which represents the default + * scope chain for the embedding. See also js_FindClassObject. + * + * For embeddings that use the inner and outer object hooks, the inner + * object represents the ultimate global object, with the outer object + * acting as a stand-in. + */ + JSObject *obj = cx->globalObject; + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } -JSObject * -js::GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) -{ - return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen)); + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; + } + return GetScopeChain(cx, fp); } /* Some objects (e.g., With) delegate 'this' to another object. */ @@ -383,49 +294,6 @@ CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv) return thisp; } -namespace js { - -void -ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) -{ - Value &thisv = vp[1]; - -#ifdef DEBUG - if (thisv.isObject()) { - JS_ASSERT(thisv.toObject().getClass() != clasp); - } else if (thisv.isString()) { - JS_ASSERT(clasp != &js_StringClass); - } else if (thisv.isNumber()) { - JS_ASSERT(clasp != &js_NumberClass); - } else if (thisv.isBoolean()) { - JS_ASSERT(clasp != &js_BooleanClass); - } else { - JS_ASSERT(thisv.isUndefined() || thisv.isNull()); - } -#endif - - if (JSFunction *fun = js_ValueToFunction(cx, &vp[0], 0)) { - const char *name = thisv.isObject() - ? thisv.toObject().getClass()->name - : thisv.isString() - ? "string" - : thisv.isNumber() - ? "number" - : thisv.isBoolean() - ? "boolean" - : thisv.isNull() - ? js_null_str - : thisv.isUndefined() - ? js_undefined_str - : "value"; - JSAutoByteString funNameBytes; - if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, - clasp->name, funName, name); - } - } -} - /* * ECMA requires "the global object", but in embeddings such as the browser, * which have multiple top-level objects (windows, frames, etc. in the DOM), @@ -442,7 +310,7 @@ ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) * The alert should display "true". */ bool -BoxNonStrictThis(JSContext *cx, const CallReceiver &call) +js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call) { /* * Check for SynthesizeFrame poisoning and fast constructors which @@ -452,12 +320,12 @@ BoxNonStrictThis(JSContext *cx, const CallReceiver &call) JS_ASSERT(!thisv.isMagic()); #ifdef DEBUG - JSFunction *fun = call.callee().isFunction() ? call.callee().getFunctionPrivate() : NULL; + JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL; JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode()); #endif if (thisv.isNullOrUndefined()) { - JSObject *thisp = call.callee().getGlobal()->thisObject(cx); + JSObject *thisp = call.callee().global().thisObject(cx); if (!thisp) return false; call.thisv().setObject(*thisp); @@ -470,23 +338,16 @@ BoxNonStrictThis(JSContext *cx, const CallReceiver &call) return true; } -} - #if JS_HAS_NO_SUCH_METHOD -const uint32 JSSLOT_FOUND_FUNCTION = 0; -const uint32 JSSLOT_SAVED_ID = 1; +const uint32_t JSSLOT_FOUND_FUNCTION = 0; +const uint32_t JSSLOT_SAVED_ID = 1; Class js_NoSuchMethodClass = { "NoSuchMethod", JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, }; /* @@ -494,7 +355,7 @@ Class js_NoSuchMethodClass = { * the base object, we search for the __noSuchMethod__ method in the base. * If it exists, we store the method and the property's id into an object of * NoSuchMethod class and store this object into the callee's stack slot. - * Later, js_Invoke will recognise such an object and transfer control to + * Later, Invoke will recognise such an object and transfer control to * NoSuchMethod that invokes the method like: * * this.__noSuchMethod__(id, args) @@ -503,48 +364,39 @@ Class js_NoSuchMethodClass = { * call by name, and args is an Array containing this invocation's actual * parameters. */ -JSBool -js_OnUnknownMethod(JSContext *cx, Value *vp) +bool +js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp) { - JS_ASSERT(!vp[1].isPrimitive()); - - JSObject *obj = &vp[1].toObject(); jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); AutoValueRooter tvr(cx); if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr())) return false; + TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc); + if (tvr.value().isPrimitive()) { - vp[0] = tvr.value(); + *vp = tvr.value(); } else { #if JS_HAS_XML_SUPPORT /* Extract the function name from function::name qname. */ - if (vp[0].isObject()) { - obj = &vp[0].toObject(); - if (!js_IsFunctionQName(cx, obj, &id)) - return false; - if (!JSID_IS_VOID(id)) - vp[0] = IdToValue(id); + if (idval.isObject()) { + obj = &idval.toObject(); + if (js_GetLocalNameFromFunctionQName(obj, &id, cx)) + idval = IdToValue(id); } #endif - obj = js_NewGCObject(cx, FINALIZE_OBJECT2); + + obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL); if (!obj) return false; - /* - * Null map to cause prompt and safe crash if this object were to - * escape due to a bug. This will make the object appear to be a - * stillborn instance that needs no finalization, which is sound: - * NoSuchMethod helper objects own no manually allocated resources. - */ - obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false); obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value()); - obj->setSlot(JSSLOT_SAVED_ID, vp[0]); - vp[0].setObject(*obj); + obj->setSlot(JSSLOT_SAVED_ID, idval); + vp->setObject(*obj); } return true; } -static JS_REQUIRES_STACK JSBool +static JSBool NoSuchMethod(JSContext *cx, uintN argc, Value *vp) { InvokeArgsGuard args; @@ -570,10 +422,8 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp) #endif /* JS_HAS_NO_SUCH_METHOD */ -namespace js { - -JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, StackFrame *fp) +bool +js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp) { JS_ASSERT(script); JS_ASSERT(fp == cx->fp()); @@ -584,7 +434,7 @@ RunScript(JSContext *cx, JSScript *script, StackFrame *fp) /* FIXME: Once bug 470510 is fixed, make this an assert. */ if (script->compileAndGo) { - if (fp->scopeChain().getGlobal()->isCleared()) { + if (fp->scopeChain().global().isCleared()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE); return false; } @@ -592,12 +442,13 @@ RunScript(JSContext *cx, JSScript *script, StackFrame *fp) #ifdef JS_METHODJIT mjit::CompileStatus status; - status = mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter); + status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(), + mjit::CompileRequest_Interpreter); if (status == mjit::Compile_Error) return false; if (status == mjit::Compile_Okay) - return mjit::JaegerShot(cx); + return mjit::JaegerShot(cx, false); #endif return Interpret(cx, fp); @@ -609,16 +460,18 @@ RunScript(JSContext *cx, JSScript *script, StackFrame *fp) * required arguments, allocate declared local variables, and pop everything * when done. Then push the return value. */ -JS_REQUIRES_STACK bool -Invoke(JSContext *cx, const CallArgs &argsRef, ConstructOption option) +bool +js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct) { - /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */ + JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX); - CallArgs args = argsRef; - JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX); + JS_ASSERT(!cx->compartment->activeAnalysis); + + /* MaybeConstruct is a subset of InitialFrameFlags */ + InitialFrameFlags initial = (InitialFrameFlags) construct; if (args.calleev().isPrimitive()) { - js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(option)); + js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial)); return false; } @@ -626,170 +479,60 @@ Invoke(JSContext *cx, const CallArgs &argsRef, ConstructOption option) Class *clasp = callee.getClass(); /* Invoke non-functions. */ - if (JS_UNLIKELY(clasp != &js_FunctionClass)) { + if (JS_UNLIKELY(clasp != &FunctionClass)) { #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass)) - return NoSuchMethod(cx, args.argc(), args.base()); + return NoSuchMethod(cx, args.length(), args.base()); #endif - JS_ASSERT_IF(option == INVOKE_CONSTRUCTOR, !clasp->construct); + JS_ASSERT_IF(construct, !clasp->construct); if (!clasp->call) { - js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(option)); + js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial)); return false; } - return CallJSNative(cx, clasp->call, args.argc(), args.base()); + return CallJSNative(cx, clasp->call, args); } /* Invoke native functions. */ - JSFunction *fun = callee.getFunctionPrivate(); - JS_ASSERT_IF(option == INVOKE_CONSTRUCTOR, !fun->isConstructor()); + JSFunction *fun = callee.toFunction(); + JS_ASSERT_IF(construct, !fun->isNativeConstructor()); if (fun->isNative()) - return CallJSNative(cx, fun->u.n.native, args.argc(), args.base()); - - /* Handle the empty-script special case. */ - JSScript *script = fun->script(); - if (JS_UNLIKELY(script->isEmpty())) { - if (option == INVOKE_CONSTRUCTOR) { - JSObject *obj = js_CreateThisForFunction(cx, &callee); - if (!obj) - return false; - args.rval().setObject(*obj); - } else { - args.rval().setUndefined(); - } - return true; - } + return CallJSNative(cx, fun->u.n.native, args); + + TypeMonitorCall(cx, args, construct); /* Get pointer to new frame/slots, prepare arguments. */ - uint32 flags = ToFrameFlags(option); - InvokeFrameGuard frame; - StackFrame *fp = cx->stack.getInvokeFrame(cx, args, fun, script, &flags, &frame); - if (!fp) + InvokeFrameGuard ifg; + if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg)) return false; - /* Initialize frame, locals. */ - fp->initCallFrame(cx, callee, fun, args.argc(), flags); - SetValueRangeToUndefined(fp->slots(), script->nfixed); - - /* Officially push fp. frame's destructor pops. */ - cx->stack.pushInvokeFrame(args, &frame); - /* Now that the new frame is rooted, maybe create a call object. */ - if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp)) + StackFrame *fp = ifg.fp(); + if (!fp->functionPrologue(cx)) return false; /* Run function until JSOP_STOP, JSOP_RETURN or error. */ JSBool ok; { AutoPreserveEnumerators preserve(cx); - ok = RunScript(cx, script, fp); + ok = RunScript(cx, fun->script(), fp); } args.rval() = fp->returnValue(); - JS_ASSERT_IF(ok && option == INVOKE_CONSTRUCTOR, !args.rval().isPrimitive()); + JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive()); return ok; } bool -InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc) -{ -#ifdef JS_TRACER - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "attempt to reenter VM while recording"); - LeaveTrace(cx); -#endif - - /* Always push arguments, regardless of optimized/normal invoke. */ - ContextStack &stack = cx->stack; - if (!stack.pushInvokeArgs(cx, argc, &args_)) - return false; - - /* Callees may clobber 'this' or 'callee'. */ - savedCallee_ = args_.calleev() = calleev; - savedThis_ = args_.thisv() = thisv; - - do { - /* Hoist dynamic checks from scripted Invoke. */ - if (!calleev.isObject()) - break; - JSObject &callee = calleev.toObject(); - if (callee.getClass() != &js_FunctionClass) - break; - JSFunction *fun = callee.getFunctionPrivate(); - if (fun->isNative()) - break; - script_ = fun->script(); - if (fun->isHeavyweight() || script_->isEmpty()) - break; - - /* - * The frame will remain pushed even when the callee isn't active which - * will affect the observable current global, so avoid any change. - */ - if (callee.getGlobal() != GetGlobalForScopeChain(cx)) - break; - - /* Push the stack frame once for the session. */ - uint32 flags = 0; - if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_)) - return false; - StackFrame *fp = frame_.fp(); - fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags); - stack.pushInvokeFrame(args_, &frame_); - -#ifdef JS_METHODJIT - /* Hoist dynamic checks from RunScript. */ - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT); - if (status == mjit::Compile_Error) - return false; - if (status != mjit::Compile_Okay) - break; - /* Cannot also cache the raw code pointer; it can change. */ - - /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */ - JS_CHECK_RECURSION(cx, return false); - stackLimit_ = stack.space().getStackLimit(cx); - if (!stackLimit_) - return false; - - stop_ = script_->code + script_->length - 1; - JS_ASSERT(*stop_ == JSOP_STOP); -#endif - - /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */ - nformals_ = fp->numFormalArgs(); - formals_ = fp->formalArgs(); - actuals_ = args_.argv(); - JS_ASSERT(actuals_ == fp->actualArgs()); - return true; - } while (0); - - /* - * Use the normal invoke path. - * - * The callee slot gets overwritten during an unoptimized Invoke, so we - * cache it here and restore it before every Invoke call. The 'this' value - * does not get overwritten, so we can fill it here once. - */ - if (frame_.pushed()) - frame_.pop(); - formals_ = actuals_ = args_.argv(); - nformals_ = (unsigned)-1; - return true; -} - -bool -ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, - uintN argc, Value *argv, Value *rval) +js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, + Value *rval) { - LeaveTrace(cx); - InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return false; args.calleev() = fval; args.thisv() = thisv; - memcpy(args.argv(), argv, argc * sizeof(Value)); + PodCopy(args.array(), argv, argc); if (args.thisv().isObject()) { /* @@ -811,18 +554,54 @@ ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, } bool -ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, - Value *rval) +js::InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef) { - LeaveTrace(cx); + JS_ASSERT(!FunctionClass.construct); + CallArgs args = argsRef; + + args.thisv().setMagic(JS_IS_CONSTRUCTING); + + if (args.calleev().isObject()) { + JSObject *callee = &args.callee(); + Class *clasp = callee->getClass(); + if (clasp == &FunctionClass) { + JSFunction *fun = callee->toFunction(); + + if (fun->isNativeConstructor()) { + Probes::calloutBegin(cx, fun); + bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args); + Probes::calloutEnd(cx, fun); + return ok; + } + + if (!fun->isInterpretedConstructor()) + goto error; + + if (!InvokeKernel(cx, args, CONSTRUCT)) + return false; + + JS_ASSERT(args.rval().isObject()); + return true; + } + if (clasp->construct) + return CallJSNativeConstructor(cx, clasp->construct, args); + } + +error: + js_ReportIsNotFunction(cx, &args.calleev(), JSV2F_CONSTRUCT); + return false; +} +bool +js::InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval) +{ InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return false; args.calleev() = fval; args.thisv().setMagic(JS_THIS_POISON); - memcpy(args.argv(), argv, argc * sizeof(Value)); + PodCopy(args.array(), argv, argc); if (!InvokeConstructor(cx, args)) return false; @@ -832,55 +611,25 @@ ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *a } bool -ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval, - JSAccessMode mode, uintN argc, Value *argv, Value *rval) +js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, + Value *rval) { - LeaveTrace(cx); - /* - * ExternalInvoke could result in another try to get or set the same id - * again, see bug 355497. + * Invoke could result in another try to get or set the same id again, see + * bug 355497. */ JS_CHECK_RECURSION(cx, return false); - return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval); -} - -#if JS_HAS_SHARP_VARS -JS_STATIC_ASSERT(SHARP_NSLOTS == 2); - -static JS_NEVER_INLINE bool -InitSharpSlots(JSContext *cx, StackFrame *fp) -{ - StackFrame *prev = fp->prev(); - JSScript *script = fp->script(); - JS_ASSERT(script->nfixed >= SHARP_NSLOTS); - - Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS]; - if (prev && prev->script()->hasSharps) { - JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS); - int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame()) - ? prev->fun()->script()->bindings.sharpSlotBase(cx) - : prev->numFixed() - SHARP_NSLOTS; - if (base < 0) - return false; - sharps[0] = prev->slots()[base]; - sharps[1] = prev->slots()[base + 1]; - } else { - sharps[0].setUndefined(); - sharps[1].setUndefined(); - } - return true; + return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval); } -#endif bool -Execute(JSContext *cx, JSObject &chain, JSScript *script, - StackFrame *prev, uintN flags, Value *result) +js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, + ExecuteType type, StackFrame *evalInFrame, Value *result) { - JS_ASSERT_IF(prev, !prev->isDummyFrame()); - JS_ASSERT_IF(prev, prev->compartment() == cx->compartment); - JS_ASSERT(script->compartment == cx->compartment); + JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); + + Root scriptRoot(cx, &script); if (script->isEmpty()) { if (result) @@ -888,82 +637,29 @@ Execute(JSContext *cx, JSObject &chain, JSScript *script, return true; } - LeaveTrace(cx); - AutoScriptRooter root(cx, script); - - /* - * Get a pointer to new frame/slots. This memory is not "claimed", so the - * code before pushExecuteFrame must not reenter the interpreter or assume - * the frame is rooted. - */ - ExecuteFrameGuard frame; - if (!cx->stack.getExecuteFrame(cx, script, &frame)) + ExecuteFrameGuard efg; + if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg)) return false; - /* Initialize fixed slots (GVAR ops expect NULL). */ - SetValueRangeToNull(frame.fp()->slots(), script->nfixed); - - /* Initialize frame and locals. */ - JSObject *initialVarObj; - if (prev) { - JS_ASSERT(chain == prev->scopeChain()); - frame.fp()->initEvalFrame(cx, script, prev, flags); - - /* NB: prev may not be in cx->currentSegment. */ - initialVarObj = (prev == cx->maybefp()) - ? &cx->stack.currentVarObj() - : &cx->stack.space().varObjForFrame(prev); - } else { - /* The scope chain could be anything, so innerize just in case. */ - JSObject *innerizedChain = &chain; - OBJ_TO_INNER_OBJECT(cx, innerizedChain); - if (!innerizedChain) - return false; - - /* If we were handed a non-native object, complain bitterly. */ - if (!innerizedChain->isNative()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NON_NATIVE_SCOPE); - return false; - } - - frame.fp()->initGlobalFrame(script, *innerizedChain, flags); - - /* If scope chain is an inner window, outerize for 'this'. */ - JSObject *thisp = chain.thisObject(cx); - if (!thisp) - return false; - frame.fp()->globalThis().setObject(*thisp); - - initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX) - ? chain.getGlobal() - : &chain; - } - JS_ASSERT(!initialVarObj->getOps()->defineProperty); - - if (frame.fp()->isStrictEvalFrame()) { - /* Give strict mode eval its own fresh lexical environment. */ - initialVarObj = CreateEvalCallObject(cx, frame.fp()); - if (!initialVarObj) - return false; - JS_ASSERT(initialVarObj == &frame.fp()->scopeChain()); - } - - /* Officially push the frame. ~FrameGuard pops. */ - cx->stack.pushExecuteFrame(initialVarObj, &frame); + if (!script->ensureRanAnalysis(cx, &scopeChain)) + return false; -#if JS_HAS_SHARP_VARS - if (script->hasSharps && !InitSharpSlots(cx, frame.fp())) + /* Give strict mode eval its own fresh lexical environment. */ + StackFrame *fp = efg.fp(); + if (fp->isStrictEvalFrame() && !CreateEvalCallObject(cx, fp)) return false; -#endif Probes::startExecution(cx, script); - /* Run script until JSOP_STOP or error. */ + TypeScript::SetThis(cx, script, fp->thisValue()); + AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, frame.fp()); - if (result) - *result = frame.fp()->returnValue(); + JSBool ok = RunScript(cx, script, fp); + if (result && ok) + *result = fp->returnValue(); + + if (fp->isStrictEvalFrame()) + js_PutCallObject(fp); Probes::stopExecution(cx, script); @@ -971,78 +667,39 @@ Execute(JSContext *cx, JSObject &chain, JSScript *script, } bool -CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs) +js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval) { - JSObject *obj2; - JSProperty *prop; - uintN oldAttrs; - bool isFunction; - const char *type, *name; - - if (!obj->lookupProperty(cx, id, &obj2, &prop)) + /* The scope chain could be anything, so innerize just in case. */ + JSObject *scopeChain = &scopeChainArg; + OBJ_TO_INNER_OBJECT(cx, scopeChain); + if (!scopeChain) return false; - if (!prop) - return true; - if (obj2->isNative()) { - oldAttrs = ((Shape *) prop)->attributes(); - } else { - if (!obj2->getAttributes(cx, id, &oldAttrs)) - return false; - } - - /* We allow redeclaring some non-readonly properties. */ - if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { - /* Allow redeclaration of variables and functions. */ - if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) - return true; - - /* - * Allow adding a getter only if a property already has a setter - * but no getter and similarly for adding a setter. That is, we - * allow only the following transitions: - * - * no-property --> getter --> getter + setter - * no-property --> setter --> getter + setter - */ - if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) - return true; - /* - * Allow redeclaration of an impermanent property (in which case - * anyone could delete it and redefine it, willy-nilly). - */ - if (!(oldAttrs & JSPROP_PERMANENT)) - return true; + /* If we were handed a non-native object, complain bitterly. */ + if (!scopeChain->isNative()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_NATIVE_SCOPE); + return false; } + JS_ASSERT(!scopeChain->getOps()->defineProperty); - isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; - if (!isFunction) { - Value value; - if (!obj->getProperty(cx, id, &value)) - return JS_FALSE; - isFunction = IsFunctionObject(value); + /* The VAROBJFIX option makes varObj == globalObj in global code. */ + if (!cx->hasRunOption(JSOPTION_VAROBJFIX)) { + if (!scopeChain->setVarObj(cx)) + return false; } - type = (oldAttrs & attrs & JSPROP_GETTER) - ? js_getter_str - : (oldAttrs & attrs & JSPROP_SETTER) - ? js_setter_str - : (oldAttrs & JSPROP_READONLY) - ? js_const_str - : isFunction - ? js_function_str - : js_var_str; - JSAutoByteString bytes; - name = js_ValueToPrintable(cx, IdToValue(id), &bytes); - if (!name) + /* Use the scope chain as 'this', modulo outerization. */ + JSObject *thisObj = scopeChain->thisObject(cx); + if (!thisObj) return false; - JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, - JSMSG_REDECLARED_VAR, type, name)); - return false; + Value thisv = ObjectValue(*thisObj); + + return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL, + NULL /* evalInFrame */, rval); } JSBool -HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) +js::HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) { Class *clasp = obj->getClass(); if (clasp->hasInstance) @@ -1053,12 +710,16 @@ HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) } bool -LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result) +js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result) { #if JS_HAS_XML_SUPPORT if (JS_UNLIKELY(lval.isObject() && lval.toObject().isXML()) || (rval.isObject() && rval.toObject().isXML())) { - return js_TestXMLEquality(cx, lval, rval, result); + JSBool res; + if (!js_TestXMLEquality(cx, lval, rval, &res)) + return false; + *result = !!res; + return true; } #endif @@ -1071,17 +732,20 @@ LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result if (lval.isDouble()) { double l = lval.toDouble(), r = rval.toDouble(); - *result = JSDOUBLE_COMPARE(l, ==, r, false); + *result = (l == r); return true; } if (lval.isObject()) { JSObject *l = &lval.toObject(); JSObject *r = &rval.toObject(); - l->assertSpecialEqualitySynced(); - if (EqualityOp eq = l->getClass()->ext.equality) { - return eq(cx, l, &rval, result); + if (JSEqualityOp eq = l->getClass()->ext.equality) { + JSBool res; + if (!eq(cx, l, &rval, &res)) + return false; + *result = !!res; + return true; } *result = l == r; @@ -1105,10 +769,9 @@ LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result Value lvalue = lval; Value rvalue = rval; - if (lvalue.isObject() && !DefaultValue(cx, &lvalue.toObject(), JSTYPE_VOID, &lvalue)) + if (!ToPrimitive(cx, &lvalue)) return false; - - if (rvalue.isObject() && !DefaultValue(cx, &rvalue.toObject(), JSTYPE_VOID, &rvalue)) + if (!ToPrimitive(cx, &rvalue)) return false; if (lvalue.isString() && rvalue.isString()) { @@ -1118,23 +781,21 @@ LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result } double l, r; - if (!ValueToNumber(cx, lvalue, &l) || - !ValueToNumber(cx, rvalue, &r)) { + if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r)) return false; - } - *result = JSDOUBLE_COMPARE(l, ==, r, false); + *result = (l == r); return true; } bool -StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal) +js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal) { Value lval = lref, rval = rref; if (SameType(lval, rval)) { if (lval.isString()) return EqualStrings(cx, lval.toString(), rval.toString(), equal); if (lval.isDouble()) { - *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); + *equal = (lval.toDouble() == rval.toDouble()); return true; } if (lval.isObject()) { @@ -1152,13 +813,13 @@ StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal if (lval.isDouble() && rval.isInt32()) { double ld = lval.toDouble(); double rd = rval.toInt32(); - *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = (ld == rd); return true; } if (lval.isInt32() && rval.isDouble()) { double ld = lval.toInt32(); double rd = rval.toDouble(); - *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = (ld == rd); return true; } @@ -1179,7 +840,7 @@ IsNaN(const Value &v) } bool -SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) +js::SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same) { if (IsNegativeZero(v1)) { *same = IsNegativeZero(v2); @@ -1197,7 +858,7 @@ SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) } JSType -TypeOfValue(JSContext *cx, const Value &vref) +js::TypeOfValue(JSContext *cx, const Value &vref) { Value v = vref; if (v.isNumber()) @@ -1214,86 +875,14 @@ TypeOfValue(JSContext *cx, const Value &vref) return JSTYPE_BOOLEAN; } -JS_REQUIRES_STACK bool -InvokeConstructor(JSContext *cx, const CallArgs &argsRef) +bool +js::ValueToId(JSContext *cx, const Value &v, jsid *idp) { - JS_ASSERT(!js_FunctionClass.construct); - CallArgs args = argsRef; - - if (args.calleev().isObject()) { - JSObject *callee = &args.callee(); - Class *clasp = callee->getClass(); - if (clasp == &js_FunctionClass) { - JSFunction *fun = callee->getFunctionPrivate(); - - if (fun->isConstructor()) { - args.thisv().setMagicWithObjectOrNullPayload(NULL); - return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); - } - - if (!fun->isInterpretedConstructor()) - goto error; - - if (!Invoke(cx, args, INVOKE_CONSTRUCTOR)) - return false; - - JS_ASSERT(args.rval().isObject()); - JS_RUNTIME_METER(cx->runtime, constructs); - return true; - } - if (clasp->construct) { - args.thisv().setMagicWithObjectOrNullPayload(NULL); - return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); - } - } - -error: - js_ReportIsNotFunction(cx, &args.calleev(), JSV2F_CONSTRUCT); - return false; -} - -bool -InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval, - uintN argc, Value *argv, Value *rval) -{ - LeaveTrace(cx); - - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, argc, &args)) - return JS_FALSE; - - args.calleev() = fval; - /* Initialize args.thisv on all paths below. */ - memcpy(args.argv(), argv, argc * sizeof(Value)); - - /* Handle the fast-constructor cases before calling the general case. */ - JSObject &callee = fval.toObject(); - Class *clasp = callee.getClass(); - JSFunction *fun; - bool ok; - if (clasp == &js_FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) { - args.thisv().setMagicWithObjectOrNullPayload(thisobj); - ok = CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); - } else if (clasp->construct) { - args.thisv().setMagicWithObjectOrNullPayload(thisobj); - ok = CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); - } else { - args.thisv().setObjectOrNull(thisobj); - ok = Invoke(cx, args, INVOKE_CONSTRUCTOR); - } - - *rval = args.rval(); - return ok; -} - -bool -ValueToId(JSContext *cx, const Value &v, jsid *idp) -{ - int32_t i; - if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) { - *idp = INT_TO_JSID(i); - return true; - } + int32_t i; + if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) { + *idp = INT_TO_JSID(i); + return true; + } #if JS_HAS_XML_SUPPORT if (v.isObject()) { @@ -1308,14 +897,12 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp) return js_ValueToStringId(cx, v, idp); } -} /* namespace js */ - /* * Enter the new with scope using an object at sp[-1] and associate the depth * of the with block with sp + stackIndex. */ -JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) +static bool +EnterWith(JSContext *cx, jsint stackIndex) { StackFrame *fp = cx->fp(); Value *sp = cx->regs().sp; @@ -1332,16 +919,12 @@ js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) sp[-1].setObject(*obj); } - JSObject *parent = GetScopeChainFast(cx, fp, op, oplen); + JSObject *parent = GetScopeChain(cx, fp); if (!parent) return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - JSObject *withobj = js_NewWithObject(cx, obj, parent, - sp + stackIndex - fp->base()); + JSObject *withobj = WithObject::create(cx, fp, *obj, *parent, + sp + stackIndex - fp->base()); if (!withobj) return JS_FALSE; @@ -1349,68 +932,62 @@ js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) return JS_TRUE; } -JS_STATIC_INTERPRET JS_REQUIRES_STACK void -js_LeaveWith(JSContext *cx) +static void +LeaveWith(JSContext *cx) { - JSObject *withobj; - - withobj = &cx->fp()->scopeChain(); - JS_ASSERT(withobj->getClass() == &js_WithClass); - JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp())); - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); - withobj->setPrivate(NULL); - cx->fp()->setScopeChainNoCallObj(*withobj->getParent()); + WithObject &withobj = cx->fp()->scopeChain().asWith(); + JS_ASSERT(withobj.maybeStackFrame() == js_FloatingFrameIfGenerator(cx, cx->fp())); + JS_ASSERT(withobj.stackDepth() >= 0); + withobj.setStackFrame(NULL); + cx->fp()->setScopeChainNoCallObj(withobj.enclosingScope()); } -JS_REQUIRES_STACK Class * -js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth) +bool +js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth) { - Class *clasp; - - clasp = obj->getClass(); - if ((clasp == &js_WithClass || clasp == &js_BlockClass) && - obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) && - OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) { - return clasp; - } - return NULL; + return (obj.isWith() || obj.isBlock()) && + obj.getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) && + obj.asNestedScope().stackDepth() >= stackDepth; } -/* - * Unwind block and scope chains to match the given depth. The function sets - * fp->sp on return to stackDepth. - */ -JS_REQUIRES_STACK JSBool -js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind) +/* Unwind block and scope chains to match the given depth. */ +void +js::UnwindScope(JSContext *cx, uint32_t stackDepth) { - Class *clasp; - - JS_ASSERT(stackDepth >= 0); JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp); StackFrame *fp = cx->fp(); - for (;;) { - clasp = js_IsActiveWithOrBlock(cx, &fp->scopeChain(), stackDepth); - if (!clasp) + StaticBlockObject *block = fp->maybeBlockChain(); + while (block) { + if (block->stackDepth() < stackDepth) break; - if (clasp == &js_BlockClass) { - /* Don't fail until after we've updated all stacks. */ - normalUnwind &= js_PutBlockObject(cx, normalUnwind); - } else { - js_LeaveWith(cx); - } + block = block->enclosingBlock(); } + fp->setBlockChain(block); - cx->regs().sp = fp->base() + stackDepth; - return normalUnwind; + for (;;) { + JSObject &scopeChain = fp->scopeChain(); + if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth)) + break; + if (scopeChain.isClonedBlock()) + scopeChain.asClonedBlock().put(cx); + else + LeaveWith(cx); + } } -JS_STATIC_INTERPRET JSBool -js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) +/* + * Find the results of incrementing or decrementing *vp. For pre-increments, + * both *vp and *vp2 will contain the result on return. For post-increments, + * vp will contain the original value converted to a number and vp2 will get + * the result. Both vp and vp2 must be roots. + */ +static bool +DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) { if (cs->format & JOF_POST) { double d; - if (!ValueToNumber(cx, *vp, &d)) + if (!ToNumber(cx, *vp, &d)) return JS_FALSE; vp->setNumber(d); (cs->format & JOF_INC) ? ++d : --d; @@ -1419,7 +996,7 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) } double d; - if (!ValueToNumber(cx, *vp, &d)) + if (!ToNumber(cx, *vp, &d)) return JS_FALSE; (cs->format & JOF_INC) ? ++d : --d; vp->setNumber(d); @@ -1432,11 +1009,10 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie) { JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0); const uintN targetLevel = closureLevel - cookie.level(); - JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT); - StackFrame *fp = cx->stack.findFrameAtLevel(targetLevel); + StackFrame *fp = FindUpvarFrame(cx, targetLevel); uintN slot = cookie.slot(); - Value *vp; + const Value *vp; if (!fp->isFunctionFrame() || fp->isEvalFrame()) { vp = fp->slots() + fp->numFixed(); @@ -1454,412 +1030,21 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie) return vp[slot]; } -#ifdef DEBUG - -JS_STATIC_INTERPRET JS_REQUIRES_STACK void -js_LogOpcode(JSContext *cx) -{ - FILE *logfp; - StackFrame *fp; - FrameRegs *regs; - intN ndefs, n, nuses; - JSOp op; - - logfp = (FILE *) cx->logfp; - JS_ASSERT(logfp); - fp = cx->fp(); - regs = &cx->regs(); - - /* - * Operations in prologues don't produce interesting values, and - * js_DecompileValueGenerator isn't set up to handle them anyway. - */ - if (cx->logPrevPc && regs->pc >= fp->script()->main) { - JSOp logPrevOp = JSOp(*cx->logPrevPc); - ndefs = js_GetStackDefs(cx, &js_CodeSpec[logPrevOp], logPrevOp, - fp->script(), cx->logPrevPc); - - /* - * If there aren't that many elements on the stack, then we have - * probably entered a new frame, and printing output would just be - * misleading. - */ - if (ndefs != 0 && - ndefs < regs->sp - fp->slots()) { - for (n = -ndefs; n < 0; n++) { - char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL); - if (bytes) { - fprintf(logfp, "%s %s", - (n == -ndefs) ? " output:" : ",", - bytes); - cx->free_(bytes); - } else { - JS_ClearPendingException(cx); - } - } - fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base())); - } - fprintf(logfp, " stack: "); - for (Value *siter = fp->base(); siter < regs->sp; siter++) { - if (siter->isObject() && siter->toObject().getClass() == &js_CallClass) { - /* - * Call objects have NULL convert ops so that we catch cases - * where they escape. So js_ValueToString doesn't work on them. - */ - fputs("", logfp); - } else { - JSString *str = js_ValueToString(cx, *siter); - JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL; - if (!linearStr) { - fputs("", logfp); - JS_ClearPendingException(cx); - } else { - FileEscapedString(logfp, linearStr, 0); - } - } - fputc(' ', logfp); - } - fputc('\n', logfp); - } - - fprintf(logfp, "%4u: ", - js_PCToLineNumber(cx, fp->script(), - fp->hasImacropc() ? fp->imacropc() : regs->pc)); - - Sprinter sprinter; - void *mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - js_Disassemble1(cx, fp->script(), regs->pc, - regs->pc - fp->script()->code, - JS_FALSE, &sprinter); - fprintf(logfp, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); - - op = (JSOp) *regs->pc; - nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc); - if (nuses != 0) { - for (n = -nuses; n < 0; n++) { - char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL); - if (bytes) { - fprintf(logfp, "%s %s", - (n == -nuses) ? " inputs:" : ",", - bytes); - cx->free_(bytes); - } else { - JS_ClearPendingException(cx); - } - } - fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base())); - } - cx->logPrevPc = regs->pc; - - /* It's nice to have complete logs when debugging a crash. */ - fflush(logfp); -} - -#endif /* DEBUG */ - -#ifdef JS_OPMETER - -# include - -# define HIST_NSLOTS 8 - -/* - * The second dimension is hardcoded at 256 because we know that many bits fit - * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address - * any particular row. - */ -static uint32 succeeds[JSOP_LIMIT][256]; -static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS]; - -JS_STATIC_INTERPRET void -js_MeterOpcodePair(JSOp op1, JSOp op2) -{ - if (op1 != JSOP_STOP) - ++succeeds[op1][op2]; -} - -JS_STATIC_INTERPRET void -js_MeterSlotOpcode(JSOp op, uint32 slot) -{ - if (slot < HIST_NSLOTS) - ++slot_ops[op][slot]; -} - -typedef struct Edge { - const char *from; - const char *to; - uint32 count; -} Edge; - -static int -compare_edges(const void *a, const void *b) +extern StackFrame * +js::FindUpvarFrame(JSContext *cx, uintN targetLevel) { - const Edge *ea = (const Edge *) a; - const Edge *eb = (const Edge *) b; - - return (int32)eb->count - (int32)ea->count; -} - -void -js_DumpOpMeters() -{ - const char *name, *from, *style; - FILE *fp; - uint32 total, count; - uint32 i, j, nedges; - Edge *graph; - - name = getenv("JS_OPMETER_FILE"); - if (!name) - name = "/tmp/ops.dot"; - fp = fopen(name, "w"); - if (!fp) { - perror(name); - return; - } - - total = nedges = 0; - for (i = 0; i < JSOP_LIMIT; i++) { - for (j = 0; j < JSOP_LIMIT; j++) { - count = succeeds[i][j]; - if (count != 0) { - total += count; - ++nedges; - } - } - } - -# define SIGNIFICANT(count,total) (200. * (count) >= (total)) - - graph = (Edge *) OffTheBooks::calloc_(nedges * sizeof graph[0]); - if (!graph) - return; - for (i = nedges = 0; i < JSOP_LIMIT; i++) { - from = js_CodeName[i]; - for (j = 0; j < JSOP_LIMIT; j++) { - count = succeeds[i][j]; - if (count != 0 && SIGNIFICANT(count, total)) { - graph[nedges].from = from; - graph[nedges].to = js_CodeName[j]; - graph[nedges].count = count; - ++nedges; - } - } - } - qsort(graph, nedges, sizeof(Edge), compare_edges); - -# undef SIGNIFICANT - - fputs("digraph {\n", fp); - for (i = 0, style = NULL; i < nedges; i++) { - JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count); - if (!style || graph[i-1].count != graph[i].count) { - style = (i > nedges * .75) ? "dotted" : - (i > nedges * .50) ? "dashed" : - (i > nedges * .25) ? "solid" : "bold"; - } - fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n", - graph[i].from, graph[i].to, - (unsigned long)graph[i].count, style); - } - cx->free_(graph); - fputs("}\n", fp); - fclose(fp); - - name = getenv("JS_OPMETER_HIST"); - if (!name) - name = "/tmp/ops.hist"; - fp = fopen(name, "w"); - if (!fp) { - perror(name); - return; - } - fputs("bytecode", fp); - for (j = 0; j < HIST_NSLOTS; j++) - fprintf(fp, " slot %1u", (unsigned)j); - putc('\n', fp); - fputs("========", fp); - for (j = 0; j < HIST_NSLOTS; j++) - fputs(" =======", fp); - putc('\n', fp); - for (i = 0; i < JSOP_LIMIT; i++) { - for (j = 0; j < HIST_NSLOTS; j++) { - if (slot_ops[i][j] != 0) { - /* Reuse j in the next loop, since we break after. */ - fprintf(fp, "%-8.8s", js_CodeName[i]); - for (j = 0; j < HIST_NSLOTS; j++) - fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]); - putc('\n', fp); - break; - } - } - } - fclose(fp); -} - -#endif /* JS_OPSMETER */ - -#endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */ - -#ifndef jsinvoke_cpp___ - -#ifdef JS_REPRMETER -// jsval representation metering: this measures the kinds of jsvals that -// are used as inputs to each JSOp. -namespace reprmeter { - enum Repr { - NONE, - INT, - DOUBLE, - BOOLEAN_PROPER, - BOOLEAN_OTHER, - STRING, - OBJECT_NULL, - OBJECT_PLAIN, - FUNCTION_INTERPRETED, - FUNCTION_FASTNATIVE, - ARRAY_SLOW, - ARRAY_DENSE - }; - - // Return the |repr| value giving the representation of the given jsval. - static Repr - GetRepr(jsval v) - { - if (JSVAL_IS_INT(v)) - return INT; - if (JSVAL_IS_DOUBLE(v)) - return DOUBLE; - if (JSVAL_IS_SPECIAL(v)) { - return (v == JSVAL_TRUE || v == JSVAL_FALSE) - ? BOOLEAN_PROPER - : BOOLEAN_OTHER; - } - if (JSVAL_IS_STRING(v)) - return STRING; - - JS_ASSERT(JSVAL_IS_OBJECT(v)); - - JSObject *obj = JSVAL_TO_OBJECT(v); - if (VALUE_IS_FUNCTION(cx, v)) { - JSFunction *fun = obj->getFunctionPrivate(); - if (FUN_INTERPRETED(fun)) - return FUNCTION_INTERPRETED; - return FUNCTION_FASTNATIVE; - } - // This must come before the general array test, because that - // one subsumes this one. - if (!obj) - return OBJECT_NULL; - if (obj->isDenseArray()) - return ARRAY_DENSE; - if (obj->isArray()) - return ARRAY_SLOW; - return OBJECT_PLAIN; - } - - static const char *reprName[] = { "invalid", "int", "double", "bool", "special", - "string", "null", "object", - "fun:interp", "fun:native" - "array:slow", "array:dense" }; - - // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is - // the |repr| of the ith input to the JSOp. - struct OpInput { - enum { max_uses = 16 }; - - JSOp op; - Repr uses[max_uses]; - - OpInput() : op(JSOp(255)) { - for (int i = 0; i < max_uses; ++i) - uses[i] = NONE; - } - - OpInput(JSOp op) : op(op) { - for (int i = 0; i < max_uses; ++i) - uses[i] = NONE; - } - - // Hash function - operator uint32() const { - uint32 h = op; - for (int i = 0; i < max_uses; ++i) - h = h * 7 + uses[i] * 13; - return h; - } - - bool operator==(const OpInput &opinput) const { - if (op != opinput.op) - return false; - for (int i = 0; i < max_uses; ++i) { - if (uses[i] != opinput.uses[i]) - return false; - } - return true; - } - - OpInput &operator=(const OpInput &opinput) { - op = opinput.op; - for (int i = 0; i < max_uses; ++i) - uses[i] = opinput.uses[i]; - return *this; - } - }; - - typedef HashMap, SystemAllocPolicy> OpInputHistogram; - - OpInputHistogram opinputs; - bool opinputsInitialized = false; - - // Record an OpInput for the current op. This should be called just - // before executing the op. - static void - MeterRepr(JSContext *cx) - { - // Note that we simply ignore the possibility of errors (OOMs) - // using the hash map, since this is only metering code. - - if (!opinputsInitialized) { - opinputs.init(); - opinputsInitialized = true; - } - - JSOp op = JSOp(*cx->regs->pc); - int nuses = js_GetStackUses(&js_CodeSpec[op], op, cx->regs->pc); - - // Build the OpInput. - OpInput opinput(op); - for (int i = 0; i < nuses; ++i) { - jsval v = cx->regs->sp[-nuses+i]; - opinput.uses[i] = GetRepr(v); - } - - if (OpInputHistogram::Ptr p = opinputs.lookupWithDefault(opinput, 0)) - p->value++; - } - - void - js_DumpReprMeter() - { - FILE *f = fopen("/tmp/reprmeter.txt", "w"); - JS_ASSERT(f); - for (OpInputHistogram::Range r = opinputs.all(); !r.empty(); r.popFront()) { - const OpInput &o = r.front().key; - uint64 c = r.front().value; - fprintf(f, "%3d,%s", o.op, js_CodeName[o.op]); - for (int i = 0; i < OpInput::max_uses && o.uses[i] != NONE; ++i) - fprintf(f, ",%s", reprName[o.uses[i]]); - fprintf(f, ",%llu\n", c); - } - fclose(f); + StackFrame *fp = cx->fp(); + while (true) { + JS_ASSERT(fp && fp->isScriptFrame()); + if (fp->script()->staticLevel == targetLevel) + break; + fp = fp->prev(); } + return fp; } -#endif /* JS_REPRMETER */ #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0) +#define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v #define PUSH_NULL() regs.sp++->setNull() #define PUSH_UNDEFINED() regs.sp++->setUndefined() #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b) @@ -1872,7 +1057,7 @@ namespace reprmeter { #define POP_COPY_TO(v) v = *--regs.sp #define POP_RETURN_VALUE() regs.fp()->setReturnValue(*--regs.sp) -#define POP_BOOLEAN(cx, vp, b) \ +#define VALUE_TO_BOOLEAN(cx, vp, b) \ JS_BEGIN_MACRO \ vp = ®s.sp[-1]; \ if (vp->isNull()) { \ @@ -1882,9 +1067,10 @@ namespace reprmeter { } else { \ b = !!js_ValueToBoolean(*vp); \ } \ - regs.sp--; \ JS_END_MACRO +#define POP_BOOLEAN(cx, vp, b) do { VALUE_TO_BOOLEAN(cx, vp, b); regs.sp--; } while(0) + #define VALUE_TO_OBJECT(cx, vp, obj) \ JS_BEGIN_MACRO \ if ((vp)->isObject()) { \ @@ -1903,15 +1089,6 @@ namespace reprmeter { VALUE_TO_OBJECT(cx, vp_, obj); \ JS_END_MACRO -#define DEFAULT_VALUE(cx, n, hint, v) \ - JS_BEGIN_MACRO \ - JS_ASSERT(v.isObject()); \ - JS_ASSERT(v == regs.sp[n]); \ - if (!DefaultValue(cx, &v.toObject(), hint, ®s.sp[n])) \ - goto error; \ - v = regs.sp[n]; \ - JS_END_MACRO - /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */ static JS_ALWAYS_INLINE bool CanIncDecWithoutOverflow(int32_t i) @@ -1919,39 +1096,6 @@ CanIncDecWithoutOverflow(int32_t i) return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX); } -/* - * Define JS_OPMETER to instrument bytecode succession, generating a .dot file - * on shutdown that shows the graph of significant predecessor/successor pairs - * executed, where the edge labels give the succession counts. The .dot file - * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot. - * - * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops - * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts - * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist. - */ -#ifndef JS_OPMETER -# define METER_OP_INIT(op) /* nothing */ -# define METER_OP_PAIR(op1,op2) /* nothing */ -# define METER_SLOT_OP(op,slot) /* nothing */ -#else - -/* - * The second dimension is hardcoded at 256 because we know that many bits fit - * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address - * any particular row. - */ -# define METER_OP_INIT(op) ((op) = JSOP_STOP) -# define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2)) -# define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot)) - -#endif - -#ifdef JS_REPRMETER -# define METER_REPR(cx) (reprmeter::MeterRepr(cx)) -#else -# define METER_REPR(cx) ((void) 0) -#endif /* JS_REPRMETER */ - /* * Threaded interpretation via computed goto appears to be well-supported by * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., @@ -1970,100 +1114,74 @@ CanIncDecWithoutOverflow(int32_t i) # endif #endif -/* - * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on - * single-thread DEBUG js shell testing to verify property cache hits. - */ -#if defined DEBUG && !defined JS_THREADSAFE +template +class GenericInterruptEnabler : public InterpreterFrames::InterruptEnablerBase { + public: + GenericInterruptEnabler(T *variable, T value) : variable(variable), value(value) { } + void enableInterrupts() const { *variable = value; } -# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \ - JS_BEGIN_MACRO \ - if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \ - entry)) { \ - goto error; \ - } \ - JS_END_MACRO + private: + T *variable; + T value; +}; -static bool -AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, FrameRegs& regs, - ptrdiff_t pcoff, JSObject *start, JSObject *found, - PropertyCacheEntry *entry) +inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs, + const InterruptEnablerBase &enabler) + : context(cx), regs(regs), enabler(enabler) +{ + older = cx->runtime->interpreterFrames; + cx->runtime->interpreterFrames = this; +} + +inline InterpreterFrames::~InterpreterFrames() +{ + context->runtime->interpreterFrames = older; +} + +#if defined(DEBUG) && !defined(JS_THREADSAFE) +void +js::AssertValidPropertyCacheHit(JSContext *cx, + JSObject *start, JSObject *found, + PropertyCacheEntry *entry) { - uint32 sample = cx->runtime->gcNumber; + jsbytecode *pc; + cx->stack.currentScript(&pc); + + uint32_t sample = cx->runtime->gcNumber; PropertyCacheEntry savedEntry = *entry; - JSAtom *atom; - if (pcoff >= 0) - GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom); - else - atom = cx->runtime->atomState.lengthAtom; + PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]); JSObject *obj, *pobj; JSProperty *prop; JSBool ok; - if (JOF_OPMODE(*regs.pc) == JOF_NAME) { - ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop); - } else { - obj = start; - ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - } - if (!ok) - return false; + if (JOF_OPMODE(*pc) == JOF_NAME) + ok = FindProperty(cx, name, start, &obj, &pobj, &prop); + else + ok = LookupProperty(cx, start, name, &pobj, &prop); + JS_ASSERT(ok); + if (cx->runtime->gcNumber != sample) JS_PROPERTY_CACHE(cx).restore(&savedEntry); JS_ASSERT(prop); JS_ASSERT(pobj == found); const Shape *shape = (Shape *) prop; - if (entry->vword.isSlot()) { - JS_ASSERT(entry->vword.toSlot() == shape->slot); - JS_ASSERT(!shape->isMethod()); - } else if (entry->vword.isShape()) { - JS_ASSERT(entry->vword.toShape() == shape); - JS_ASSERT_IF(shape->isMethod(), - shape->methodObject() == pobj->nativeGetSlot(shape->slot).toObject()); - } else { - Value v; - JS_ASSERT(entry->vword.isFunObj()); - JS_ASSERT(!entry->vword.isNull()); - JS_ASSERT(pobj->brandedOrHasMethodBarrier()); - JS_ASSERT(shape->hasDefaultGetterOrIsMethod()); - JS_ASSERT(pobj->containsSlot(shape->slot)); - v = pobj->nativeGetSlot(shape->slot); - JS_ASSERT(entry->vword.toFunObj() == v.toObject()); - - if (shape->isMethod()) { - JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP); - JS_ASSERT(shape->methodObject() == v.toObject()); - } - } - - return true; + JS_ASSERT(entry->prop == shape); } - -#else -# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0) -#endif +#endif /* DEBUG && !JS_THREADSAFE */ /* * Ensure that the intrepreter switch can close call-bytecode cases in the * same way as non-call bytecodes. */ JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); -JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH); -JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH); JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH); JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH); -/* - * Same for debuggable flat closures defined at top level in another function - * or program fragment. - */ -JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH); - /* * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}. @@ -2081,16 +1199,6 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH); JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH); JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); -#ifdef JS_TRACER -# define ABORT_RECORDING(cx, reason) \ - JS_BEGIN_MACRO \ - if (TRACE_RECORDER(cx)) \ - AbortRecording(cx, reason); \ - JS_END_MACRO -#else -# define ABORT_RECORDING(cx, reason) ((void) 0) -#endif - /* * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle * all cases, but we inline the most frequently taken paths here. @@ -2098,8 +1206,8 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); static inline bool IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval) { - if (iterobj->getClass() == &js_IteratorClass) { - NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); + if (iterobj->isIterator()) { + NativeIterator *ni = iterobj->getNativeIterator(); if (ni->isKeyIter()) { *cond = (ni->props_cursor < ni->props_end); return true; @@ -2114,58 +1222,50 @@ IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval) static inline bool IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) { - if (iterobj->getClass() == &js_IteratorClass) { - NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); + if (iterobj->isIterator()) { + NativeIterator *ni = iterobj->getNativeIterator(); if (ni->isKeyIter()) { JS_ASSERT(ni->props_cursor < ni->props_end); - jsid id = *ni->current(); - if (JSID_IS_ATOM(id)) { - rval->setString(JSID_TO_STRING(id)); - ni->incCursor(); - return true; - } - /* Take the slow path if we have to stringify a numeric property name. */ + rval->setString(*ni->current()); + ni->incCursor(); + return true; } } return js_IteratorNext(cx, iterobj, rval); } -namespace js { +/* + * For bytecodes which push values and then fall through, make sure the + * types of the pushed values are consistent with type inference information. + */ +static inline void +TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const FrameRegs ®s) +{ +#ifdef DEBUG + if (cx->typeInferenceEnabled() && + n == GetBytecodeLength(regs.pc)) { + TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp); + } +#endif +} -JS_REQUIRES_STACK JS_NEVER_INLINE bool -Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMode interpMode) +JS_NEVER_INLINE bool +js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) { -#ifdef MOZ_TRACEVIS - TraceVisStateObj tvso(cx, S_INTERP); + JSAutoResolveFlags rf(cx, RESOLVE_INFER); + + gc::VerifyBarriers(cx, true); + + JS_ASSERT(!cx->compartment->activeAnalysis); + +#if JS_THREADED_INTERP +#define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->pcCounters, jumpTable == interruptJumpTable) +#else +#define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->pcCounters, switchMask == -1) #endif - JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); -# ifdef DEBUG /* - * We call this macro from BEGIN_CASE in threaded interpreters, - * and before entering the switch in non-threaded interpreters. - * However, reaching such points doesn't mean we've actually - * fetched an OP from the instruction stream: some opcodes use - * 'op=x; DO_OP()' to let another opcode's implementation finish - * their work, and many opcodes share entry points with a run of - * consecutive BEGIN_CASEs. - * - * Take care to trace OP only when it is the opcode fetched from - * the instruction stream, so the trace matches what one would - * expect from looking at the code. (We do omit POPs after SETs; - * unfortunate, but not worth fixing.) - */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ - (OP) == *regs.pc) \ - js_LogOpcode(cx); \ - JS_END_MACRO -# else -# define LOG_OPCODE(OP) ((void) 0) -# endif - - /* - * Macros for threaded interpreter loop + * Macros for threaded interpreter loop */ #if JS_THREADED_INTERP static void *const normalJumpTable[] = { @@ -2184,29 +1284,21 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo register void * const *jumpTable = normalJumpTable; - METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */ - -# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable)) - -# ifdef JS_TRACER -# define CHECK_RECORDER() \ - JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable) -# else -# define CHECK_RECORDER() ((void)0) -# endif + typedef GenericInterruptEnabler InterruptEnabler; + InterruptEnabler interruptEnabler(&jumpTable, interruptJumpTable); # define DO_OP() JS_BEGIN_MACRO \ - CHECK_RECORDER(); \ + CHECK_PCCOUNT_INTERRUPTS(); \ + js::gc::VerifyBarriers(cx); \ JS_EXTENSION_(goto *jumpTable[op]); \ JS_END_MACRO # define DO_NEXT_OP(n) JS_BEGIN_MACRO \ - METER_OP_PAIR(op, JSOp(regs.pc[n])); \ + TypeCheckNextBytecode(cx, script, n, regs); \ op = (JSOp) *(regs.pc += (n)); \ - METER_REPR(cx); \ DO_OP(); \ JS_END_MACRO -# define BEGIN_CASE(OP) L_##OP: LOG_OPCODE(OP); CHECK_RECORDER(); +# define BEGIN_CASE(OP) L_##OP: # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); # define END_VARLEN_CASE DO_NEXT_OP(len); # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \ @@ -2220,15 +1312,8 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo register intN switchMask = 0; intN switchOp; - -# define ENABLE_INTERRUPTS() ((void) (switchMask = -1)) - -# ifdef JS_TRACER -# define CHECK_RECORDER() \ - JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1) -# else -# define CHECK_RECORDER() ((void)0) -# endif + typedef GenericInterruptEnabler InterruptEnabler; + InterruptEnabler interruptEnabler(&switchMask, -1); # define DO_OP() goto do_op # define DO_NEXT_OP(n) JS_BEGIN_MACRO \ @@ -2236,7 +1321,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo goto advance_pc; \ JS_END_MACRO -# define BEGIN_CASE(OP) case OP: CHECK_RECORDER(); +# define BEGIN_CASE(OP) case OP: # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH) # define END_CASE_LEN(n) END_CASE_LENX(n) # define END_CASE_LENX(n) END_CASE_LEN##n @@ -2250,63 +1335,60 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo # define END_CASE_LEN3 len = 3; goto advance_pc; # define END_CASE_LEN4 len = 4; goto advance_pc; # define END_CASE_LEN5 len = 5; goto advance_pc; +# define END_CASE_LEN7 len = 7; goto advance_pc; # define END_VARLEN_CASE goto advance_pc; # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) # define END_EMPTY_CASES goto advance_pc_by_one; #endif /* !JS_THREADED_INTERP */ +#define ENABLE_INTERRUPTS() (interruptEnabler.enableInterrupts()) + #define LOAD_ATOM(PCOFF, atom) \ JS_BEGIN_MACRO \ - JS_ASSERT(regs.fp()->hasImacropc() \ - ? atoms == COMMON_ATOMS_START(&rt->atomState) && \ - GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \ - : (size_t)(atoms - script->atomMap.vector) < \ - (size_t)(script->atomMap.length - \ - GET_INDEX(regs.pc + PCOFF))); \ + JS_ASSERT((size_t)(atoms - script->atoms) < \ + (size_t)(script->natoms - GET_INDEX(regs.pc + PCOFF))); \ atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \ JS_END_MACRO -#define GET_FULL_INDEX(PCOFF) \ - (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF))) - -#define LOAD_OBJECT(PCOFF, obj) \ - (obj = script->getObject(GET_FULL_INDEX(PCOFF))) +#define LOAD_NAME(PCOFF, name) \ + JS_BEGIN_MACRO \ + JSAtom *atom; \ + LOAD_ATOM((PCOFF), atom); \ + name = atom->asPropertyName(); \ + JS_END_MACRO -#define LOAD_FUNCTION(PCOFF) \ - (fun = script->getFunction(GET_FULL_INDEX(PCOFF))) +#define GET_FULL_INDEX(PCOFF) \ + (atoms - script->atoms + GET_INDEX(regs.pc + (PCOFF))) #define LOAD_DOUBLE(PCOFF, dbl) \ (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble()) +#if defined(JS_METHODJIT) bool useMethodJIT = false; +#endif #ifdef JS_METHODJIT #define RESET_USE_METHODJIT() \ JS_BEGIN_MACRO \ useMethodJIT = cx->methodJitEnabled && \ - interpMode == JSINTERP_NORMAL && \ - script->getJITStatus(regs.fp()->isConstructing()) != JITScript_Invalid; \ + (interpMode == JSINTERP_NORMAL || \ + interpMode == JSINTERP_REJOIN || \ + interpMode == JSINTERP_SKIP_TRAP); \ JS_END_MACRO -#define MONITOR_BRANCH_METHODJIT() \ +#define CHECK_PARTIAL_METHODJIT(status) \ JS_BEGIN_MACRO \ - mjit::CompileStatus status = \ - mjit::CanMethodJITAtBranch(cx, script, regs.fp(), regs.pc); \ - if (status == mjit::Compile_Error) \ - goto error; \ - if (status == mjit::Compile_Okay) { \ - void *ncode = \ - script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);\ - interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \ - if (inlineCallCount) \ - goto jit_return; \ - regs.fp()->setFinishedInInterpreter(); \ - goto leave_on_safe_point; \ - } \ - if (status == mjit::Compile_Abort) { \ - useMethodJIT = false; \ + switch (status) { \ + case mjit::Jaeger_UnfinishedAtTrap: \ + interpMode = JSINTERP_SKIP_TRAP; \ + /* FALLTHROUGH */ \ + case mjit::Jaeger_Unfinished: \ + op = (JSOp) *regs.pc; \ + RESTORE_INTERP_VARS_CHECK_EXCEPTION(); \ + DO_OP(); \ + default:; \ } \ JS_END_MACRO @@ -2314,155 +1396,79 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo #define RESET_USE_METHODJIT() ((void) 0) -#define MONITOR_BRANCH_METHODJIT() ((void) 0) - -#endif - -#ifdef JS_TRACER - -#ifdef MOZ_TRACEVIS -#if JS_THREADED_INTERP -#define MONITOR_BRANCH_TRACEVIS \ - JS_BEGIN_MACRO \ - if (jumpTable != interruptJumpTable) \ - EnterTraceVisState(cx, S_RECORD, R_NONE); \ - JS_END_MACRO -#else /* !JS_THREADED_INTERP */ -#define MONITOR_BRANCH_TRACEVIS \ - JS_BEGIN_MACRO \ - EnterTraceVisState(cx, S_RECORD, R_NONE); \ - JS_END_MACRO -#endif -#else -#define MONITOR_BRANCH_TRACEVIS #endif #define RESTORE_INTERP_VARS() \ JS_BEGIN_MACRO \ - script = regs.fp()->script(); \ + SET_SCRIPT(regs.fp()->script()); \ argv = regs.fp()->maybeFormalArgs(); \ atoms = FrameAtomBase(cx, regs.fp()); \ JS_ASSERT(&cx->regs() == ®s); \ - if (cx->isExceptionPending()) \ - goto error; \ - JS_END_MACRO - -#define MONITOR_BRANCH() \ - JS_BEGIN_MACRO \ - if (TRACING_ENABLED(cx)) { \ - if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \ - MONITOR_BRANCH_METHODJIT(); \ - } else { \ - MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \ - if (r == MONITOR_RECORDING) { \ - JS_ASSERT(TRACE_RECORDER(cx)); \ - JS_ASSERT(!TRACE_PROFILER(cx)); \ - MONITOR_BRANCH_TRACEVIS; \ - ENABLE_INTERRUPTS(); \ - CLEAR_LEAVE_ON_TRACE_POINT(); \ - } \ - RESTORE_INTERP_VARS(); \ - JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \ - if (r == MONITOR_ERROR) \ - goto error; \ - } \ - } else { \ - MONITOR_BRANCH_METHODJIT(); \ - } \ JS_END_MACRO -#else /* !JS_TRACER */ - -#define MONITOR_BRANCH() \ +#define RESTORE_INTERP_VARS_CHECK_EXCEPTION() \ JS_BEGIN_MACRO \ - MONITOR_BRANCH_METHODJIT(); \ + RESTORE_INTERP_VARS(); \ + if (cx->isExceptionPending()) \ + goto error; \ + CHECK_INTERRUPT_HANDLER(); \ JS_END_MACRO -#endif /* !JS_TRACER */ - /* * Prepare to call a user-supplied branch handler, and abort the script * if it returns false. */ #define CHECK_BRANCH() \ JS_BEGIN_MACRO \ - if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \ + if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \ goto error; \ JS_END_MACRO -#if defined(JS_TRACER) && defined(JS_METHODJIT) -# define LEAVE_ON_SAFE_POINT() \ - do { \ - JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ - JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx)); \ - JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL); \ - if (leaveOnSafePoint && !regs.fp()->hasImacropc() && \ - script->maybeNativeCodeForPC(regs.fp()->isConstructing(), regs.pc)) { \ - JS_ASSERT(!TRACE_RECORDER(cx)); \ - interpReturnOK = true; \ - goto leave_on_safe_point; \ - } \ - } while (0) -#else -# define LEAVE_ON_SAFE_POINT() /* nop */ -#endif - #define BRANCH(n) \ JS_BEGIN_MACRO \ regs.pc += (n); \ op = (JSOp) *regs.pc; \ - if ((n) <= 0) { \ - CHECK_BRANCH(); \ - if (op == JSOP_NOTRACE) { \ - if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) { \ - MONITOR_BRANCH(); \ - op = (JSOp) *regs.pc; \ - } \ - } else if (op == JSOP_TRACE) { \ - MONITOR_BRANCH(); \ - op = (JSOp) *regs.pc; \ - } \ - } \ - LEAVE_ON_SAFE_POINT(); \ + if ((n) <= 0) \ + goto check_backedge; \ DO_OP(); \ JS_END_MACRO +#define SET_SCRIPT(s) \ + JS_BEGIN_MACRO \ + script = (s); \ + if (script->hasAnyBreakpointsOrStepMode()) \ + ENABLE_INTERRUPTS(); \ + if (script->pcCounters) \ + ENABLE_INTERRUPTS(); \ + JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP, \ + script->hasAnyBreakpointsOrStepMode()); \ + JS_END_MACRO + #define CHECK_INTERRUPT_HANDLER() \ JS_BEGIN_MACRO \ if (cx->debugHooks->interruptHook) \ ENABLE_INTERRUPTS(); \ JS_END_MACRO + /* Repoint cx->regs to a local variable for faster access. */ FrameRegs regs = cx->regs(); + PreserveRegsGuard interpGuard(cx, regs); - /* Repoint cx->regs to a local variable for faster access. */ - struct InterpExitGuard { - JSContext *cx; - const FrameRegs ®s; - FrameRegs *prevContextRegs; - InterpExitGuard(JSContext *cx, FrameRegs ®s) - : cx(cx), regs(regs), prevContextRegs(&cx->regs()) { - cx->stack.repointRegs(®s); - } - ~InterpExitGuard() { - JS_ASSERT(&cx->regs() == ®s); - *prevContextRegs = regs; - cx->stack.repointRegs(prevContextRegs); - } - } interpGuard(cx, regs); + /* + * Help Debugger find frames running scripts that it has put in + * single-step mode. + */ + InterpreterFrames interpreterFrame(cx, ®s, interruptEnabler); /* Copy in hot values that change infrequently. */ JSRuntime *const rt = cx->runtime; - JSScript *script = regs.fp()->script(); + JSScript *script; + SET_SCRIPT(regs.fp()->script()); Value *argv = regs.fp()->maybeFormalArgs(); CHECK_INTERRUPT_HANDLER(); -#if defined(JS_TRACER) && defined(JS_METHODJIT) - bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT); -# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false)) -#else -# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0) -#endif + if (rt->profilingScripts) + ENABLE_INTERRUPTS(); if (!entryFrame) entryFrame = regs.fp(); @@ -2471,14 +1477,13 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo * Initialize the index segment register used by LOAD_ATOM and * GET_FULL_INDEX macros below. As a register we use a pointer based on * the atom map to turn frequently executed LOAD_ATOM into simple array - * access. For less frequent object and regexp loads we have to recover - * the segment from atoms pointer first. + * access. For less frequent object loads we have to recover the segment + * from atoms pointer first. */ - JSAtom **atoms = script->atomMap.vector; + JSAtom **atoms = script->atoms; #if JS_HAS_GENERATORS if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) { - JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp())->regs); JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) <= StackDepth(script)); @@ -2491,41 +1496,40 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo } #endif -#ifdef JS_TRACER - /* - * The method JIT may have already initiated a recording, in which case - * there should already be a valid recorder. Otherwise... - * we cannot reenter the interpreter while recording. - */ - if (interpMode == JSINTERP_RECORD) { - JS_ASSERT(TRACE_RECORDER(cx)); - JS_ASSERT(!TRACE_PROFILER(cx)); - ENABLE_INTERRUPTS(); - } else if (interpMode == JSINTERP_PROFILE) { - ENABLE_INTERRUPTS(); - } else if (TRACE_RECORDER(cx)) { - AbortRecording(cx, "attempt to reenter interpreter while recording"); - } - - if (regs.fp()->hasImacropc()) - atoms = COMMON_ATOMS_START(&rt->atomState); -#endif + /* State communicated between non-local jumps: */ + JSBool interpReturnOK; /* Don't call the script prologue if executing between Method and Trace JIT. */ if (interpMode == JSINTERP_NORMAL) { - JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(), regs.pc == script->code); - if (!ScriptPrologueOrGeneratorResume(cx, regs.fp())) + StackFrame *fp = regs.fp(); + JS_ASSERT_IF(!fp->isGeneratorFrame(), regs.pc == script->code); + if (!ScriptPrologueOrGeneratorResume(cx, fp, UseNewTypeAtEntry(cx, fp))) goto error; + if (cx->compartment->debugMode()) { + JSTrapStatus status = ScriptDebugPrologue(cx, fp); + switch (status) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + interpReturnOK = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + case JSTRAP_ERROR: + goto error; + default: + JS_NOT_REACHED("bad ScriptDebugPrologue status"); + } + } } + /* The REJOIN mode acts like the normal mode, except the prologue is skipped. */ + if (interpMode == JSINTERP_REJOIN) + interpMode = JSINTERP_NORMAL; + CHECK_INTERRUPT_HANDLER(); RESET_USE_METHODJIT(); - /* State communicated between non-local jumps: */ - JSBool interpReturnOK; - JSAtom *atomNotDefined; - /* * It is important that "op" be initialized before calling DO_OP because * it is possible for "op" to be specially assigned during the normal @@ -2537,31 +1541,9 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo len = 0; /* Check for too deep of a native thread stack. */ -#ifdef JS_TRACER -#ifdef JS_METHODJIT - JS_CHECK_RECURSION(cx, do { - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "too much recursion"); - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); - goto error; - } while (0);); -#else - JS_CHECK_RECURSION(cx, do { - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "too much recursion"); - goto error; - } while (0);); -#endif -#else JS_CHECK_RECURSION(cx, goto error); -#endif -#if JS_THREADED_INTERP - DO_NEXT_OP(len); -#else DO_NEXT_OP(len); -#endif #if JS_THREADED_INTERP /* @@ -2583,8 +1565,8 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo op = (JSOp) *regs.pc; do_op: - CHECK_RECORDER(); - LOG_OPCODE(op); + CHECK_PCCOUNT_INTERRUPTS(); + js::gc::VerifyBarriers(cx); switchOp = intN(op) | switchMask; do_switch: switch (switchOp) { @@ -2598,19 +1580,28 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo #endif /* !JS_THREADED_INTERP */ { bool moreInterrupts = false; + + if (cx->runtime->profilingScripts) { + if (!script->pcCounters) + script->initCounts(cx); + moreInterrupts = true; + } + + if (script->pcCounters) { + OpcodeCounts counts = script->getCounts(regs.pc); + counts.get(OpcodeCounts::BASE_INTERP)++; + moreInterrupts = true; + } + JSInterruptHook hook = cx->debugHooks->interruptHook; - if (hook) { -#ifdef JS_TRACER - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "interrupt hook"); -#ifdef JS_METHODJIT - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); -#endif -#endif + if (hook || script->stepModeEnabled()) { Value rval; - switch (hook(cx, script, regs.pc, Jsvalify(&rval), - cx->debugHooks->interruptHookData)) { + JSTrapStatus status = JSTRAP_CONTINUE; + if (hook) + status = hook(cx, script, regs.pc, &rval, cx->debugHooks->interruptHookData); + if (status == JSTRAP_CONTINUE && script->stepModeEnabled()) + status = Debugger::onSingleStep(cx, &rval); + switch (status) { case JSTRAP_ERROR: goto error; case JSTRAP_CONTINUE: @@ -2627,78 +1618,33 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo moreInterrupts = true; } -#ifdef JS_TRACER -#ifdef JS_METHODJIT - if (TRACE_PROFILER(cx) && interpMode == JSINTERP_PROFILE) { - LoopProfile *prof = TRACE_PROFILER(cx); - JS_ASSERT(!TRACE_RECORDER(cx)); - LoopProfile::ProfileAction act = prof->profileOperation(cx, op); - switch (act) { - case LoopProfile::ProfComplete: - if (interpMode != JSINTERP_NORMAL) { - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); - } - break; - default: - moreInterrupts = true; - break; - } - } -#endif - if (TraceRecorder* tr = TRACE_RECORDER(cx)) { - JS_ASSERT(!TRACE_PROFILER(cx)); - AbortableRecordingStatus status = tr->monitorRecording(op); - JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR); - - if (interpMode != JSINTERP_NORMAL) { - JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT); - switch (status) { - case ARECORD_IMACRO_ABORTED: - case ARECORD_ABORTED: - case ARECORD_COMPLETED: - case ARECORD_STOP: -#ifdef JS_METHODJIT - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); -#endif - break; - default: - break; - } - } + if (script->hasAnyBreakpointsOrStepMode()) + moreInterrupts = true; + if (script->hasBreakpointsAt(regs.pc) && interpMode != JSINTERP_SKIP_TRAP) { + Value rval; + JSTrapStatus status = Debugger::onTrap(cx, &rval); switch (status) { - case ARECORD_CONTINUE: - moreInterrupts = true; - break; - case ARECORD_IMACRO: - case ARECORD_IMACRO_ABORTED: - atoms = COMMON_ATOMS_START(&rt->atomState); - op = JSOp(*regs.pc); - CLEAR_LEAVE_ON_TRACE_POINT(); - if (status == ARECORD_IMACRO) - DO_OP(); /* keep interrupting for op. */ - break; - case ARECORD_ERROR: - // The code at 'error:' aborts the recording. + case JSTRAP_ERROR: + goto error; + case JSTRAP_RETURN: + regs.fp()->setReturnValue(rval); + interpReturnOK = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->setPendingException(rval); goto error; - case ARECORD_ABORTED: - case ARECORD_COMPLETED: - break; - case ARECORD_STOP: - /* A 'stop' error should have already aborted recording. */ default: - JS_NOT_REACHED("Bad recording status"); + break; } + JS_ASSERT(status == JSTRAP_CONTINUE); + CHECK_INTERRUPT_HANDLER(); + JS_ASSERT(rval.isInt32() && rval.toInt32() == op); } -#endif /* !JS_TRACER */ + + interpMode = JSINTERP_NORMAL; #if JS_THREADED_INTERP -#ifdef MOZ_TRACEVIS - if (!moreInterrupts) - ExitTraceVisState(cx, R_ABORT); -#endif jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable; JS_EXTENSION_(goto *normalJumpTable[op]); #else @@ -2710,30 +1656,78 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo /* No-ops for ease of decompilation. */ ADD_EMPTY_CASE(JSOP_NOP) +ADD_EMPTY_CASE(JSOP_UNUSED0) +ADD_EMPTY_CASE(JSOP_UNUSED1) +ADD_EMPTY_CASE(JSOP_UNUSED2) +ADD_EMPTY_CASE(JSOP_UNUSED3) +ADD_EMPTY_CASE(JSOP_UNUSED4) +ADD_EMPTY_CASE(JSOP_UNUSED5) +ADD_EMPTY_CASE(JSOP_UNUSED6) +ADD_EMPTY_CASE(JSOP_UNUSED7) +ADD_EMPTY_CASE(JSOP_UNUSED8) +ADD_EMPTY_CASE(JSOP_UNUSED9) +ADD_EMPTY_CASE(JSOP_UNUSED10) +ADD_EMPTY_CASE(JSOP_UNUSED11) +ADD_EMPTY_CASE(JSOP_UNUSED12) +ADD_EMPTY_CASE(JSOP_UNUSED13) +ADD_EMPTY_CASE(JSOP_UNUSED14) +ADD_EMPTY_CASE(JSOP_UNUSED15) +ADD_EMPTY_CASE(JSOP_UNUSED16) +ADD_EMPTY_CASE(JSOP_UNUSED17) ADD_EMPTY_CASE(JSOP_CONDSWITCH) ADD_EMPTY_CASE(JSOP_TRY) #if JS_HAS_XML_SUPPORT ADD_EMPTY_CASE(JSOP_STARTXML) ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) #endif -ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN) +ADD_EMPTY_CASE(JSOP_LOOPHEAD) +ADD_EMPTY_CASE(JSOP_LOOPENTRY) END_EMPTY_CASES -BEGIN_CASE(JSOP_TRACE) -BEGIN_CASE(JSOP_NOTRACE) - LEAVE_ON_SAFE_POINT(); -END_CASE(JSOP_TRACE) +BEGIN_CASE(JSOP_LABEL) +END_CASE(JSOP_LABEL) + +check_backedge: +{ + CHECK_BRANCH(); + if (op != JSOP_LOOPHEAD) + DO_OP(); + +#ifdef JS_METHODJIT + if (!useMethodJIT) + DO_OP(); + mjit::CompileStatus status = + mjit::CanMethodJIT(cx, script, regs.pc, regs.fp()->isConstructing(), + mjit::CompileRequest_Interpreter); + if (status == mjit::Compile_Error) + goto error; + if (status == mjit::Compile_Okay) { + void *ncode = + script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc); + JS_ASSERT(ncode); + mjit::JaegerStatus status = + mjit::JaegerShotAtSafePoint(cx, ncode, true); + CHECK_PARTIAL_METHODJIT(status); + interpReturnOK = (status == mjit::Jaeger_Returned); + if (entryFrame != regs.fp()) + goto jit_return; + regs.fp()->setFinishedInInterpreter(); + goto leave_on_safe_point; + } + if (status == mjit::Compile_Abort) + useMethodJIT = false; +#endif /* JS_METHODJIT */ + + DO_OP(); +} /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */ BEGIN_CASE(JSOP_LINENO) END_CASE(JSOP_LINENO) -BEGIN_CASE(JSOP_BLOCKCHAIN) -END_CASE(JSOP_BLOCKCHAIN) - -BEGIN_CASE(JSOP_PUSH) +BEGIN_CASE(JSOP_UNDEFINED) PUSH_UNDEFINED(); -END_CASE(JSOP_PUSH) +END_CASE(JSOP_UNDEFINED) BEGIN_CASE(JSOP_POP) regs.sp--; @@ -2744,20 +1738,17 @@ BEGIN_CASE(JSOP_POPN) regs.sp -= GET_UINT16(regs.pc); #ifdef DEBUG JS_ASSERT(regs.fp()->base() <= regs.sp); - JSObject *obj = GetBlockChain(cx, regs.fp()); - JS_ASSERT_IF(obj, - OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) + StaticBlockObject *block = regs.fp()->maybeBlockChain(); + JS_ASSERT_IF(block, + block->stackDepth() + block->slotCount() <= (size_t) (regs.sp - regs.fp()->base())); - for (obj = ®s.fp()->scopeChain(); obj; obj = obj->getParent()) { - Class *clasp = obj->getClass(); - if (clasp != &js_BlockClass && clasp != &js_WithClass) + for (JSObject *obj = ®s.fp()->scopeChain(); obj; obj = obj->enclosingScope()) { + if (!obj->isBlock() || !obj->isWith()) continue; if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp())) break; - JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) - + ((clasp == &js_BlockClass) - ? OBJ_BLOCK_COUNT(cx, obj) - : 1) + JS_ASSERT(regs.fp()->base() + obj->asBlock().stackDepth() + + (obj->isBlock() ? obj->asBlock().slotCount() : 1) <= regs.sp); } #endif @@ -2770,7 +1761,7 @@ BEGIN_CASE(JSOP_POPV) END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_ENTERWITH) - if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH)) + if (!EnterWith(cx, -1)) goto error; /* @@ -2788,7 +1779,7 @@ END_CASE(JSOP_ENTERWITH) BEGIN_CASE(JSOP_LEAVEWITH) JS_ASSERT(regs.sp[-1].toObject() == regs.fp()->scopeChain()); regs.sp--; - js_LeaveWith(cx); + LeaveWith(cx); END_CASE(JSOP_LEAVEWITH) BEGIN_CASE(JSOP_RETURN) @@ -2804,55 +1795,49 @@ BEGIN_CASE(JSOP_STOP) */ CHECK_BRANCH(); -#ifdef JS_TRACER - if (regs.fp()->hasImacropc()) { - /* - * If we are at the end of an imacro, return to its caller in the - * current frame. - */ - JS_ASSERT(op == JSOP_STOP); - JS_ASSERT((uintN)(regs.sp - regs.fp()->slots()) <= script->nslots); - jsbytecode *imacpc = regs.fp()->imacropc(); - regs.pc = imacpc + js_CodeSpec[*imacpc].length; - regs.fp()->clearImacropc(); - LEAVE_ON_SAFE_POINT(); - atoms = script->atomMap.vector; - op = JSOp(*regs.pc); - DO_OP(); - } -#endif - interpReturnOK = true; if (entryFrame != regs.fp()) inline_return: { - JS_ASSERT(!regs.fp()->hasImacropc()); - JS_ASSERT(!js_IsActiveWithOrBlock(cx, ®s.fp()->scopeChain(), 0)); + JS_ASSERT(!regs.fp()->hasBlockChain()); + JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); + + if (cx->compartment->debugMode()) + interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK); + interpReturnOK = ScriptEpilogue(cx, regs.fp(), interpReturnOK); - CHECK_INTERRUPT_HANDLER(); /* The JIT inlines ScriptEpilogue. */ #ifdef JS_METHODJIT jit_return: #endif - cx->stack.popInlineFrame(); - /* Sync interpreter locals. */ - script = regs.fp()->script(); - argv = regs.fp()->maybeFormalArgs(); - atoms = FrameAtomBase(cx, regs.fp()); + /* The results of lowered call/apply frames need to be shifted. */ + bool shiftResult = regs.fp()->loweredCallOrApply(); + + cx->stack.popInlineFrame(regs); + + RESTORE_INTERP_VARS(); + + JS_ASSERT(*regs.pc == JSOP_NEW || *regs.pc == JSOP_CALL || + *regs.pc == JSOP_FUNCALL || *regs.pc == JSOP_FUNAPPLY); /* Resume execution in the calling frame. */ RESET_USE_METHODJIT(); - JS_ASSERT(inlineCallCount); - inlineCallCount--; if (JS_LIKELY(interpReturnOK)) { - JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length - == JSOP_CALL_LENGTH); - TRACE_0(LeaveFrame); + TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); + + if (shiftResult) { + regs.sp[-2] = regs.sp[-1]; + regs.sp--; + } + len = JSOP_CALL_LENGTH; DO_NEXT_OP(len); } + + /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */ + regs.pc += JSOP_CALL_LENGTH; goto error; } else { JS_ASSERT(regs.sp == regs.fp()->base()); @@ -2898,11 +1883,10 @@ END_CASE(JSOP_IFNE) BEGIN_CASE(JSOP_OR) { bool cond; - Value *vp; - POP_BOOLEAN(cx, vp, cond); + Value *_; + VALUE_TO_BOOLEAN(cx, _, cond); if (cond == true) { len = GET_JUMP_OFFSET(regs.pc); - PUSH_COPY(*vp); DO_NEXT_OP(len); } } @@ -2911,76 +1895,15 @@ END_CASE(JSOP_OR) BEGIN_CASE(JSOP_AND) { bool cond; - Value *vp; - POP_BOOLEAN(cx, vp, cond); + Value *_; + VALUE_TO_BOOLEAN(cx, _, cond); if (cond == false) { len = GET_JUMP_OFFSET(regs.pc); - PUSH_COPY(*vp); DO_NEXT_OP(len); } } END_CASE(JSOP_AND) -BEGIN_CASE(JSOP_DEFAULTX) - regs.sp--; - /* FALL THROUGH */ -BEGIN_CASE(JSOP_GOTOX) -{ - len = GET_JUMPX_OFFSET(regs.pc); - BRANCH(len); -} -END_CASE(JSOP_GOTOX); - -BEGIN_CASE(JSOP_IFEQX) -{ - bool cond; - Value *_; - POP_BOOLEAN(cx, _, cond); - if (cond == false) { - len = GET_JUMPX_OFFSET(regs.pc); - BRANCH(len); - } -} -END_CASE(JSOP_IFEQX) - -BEGIN_CASE(JSOP_IFNEX) -{ - bool cond; - Value *_; - POP_BOOLEAN(cx, _, cond); - if (cond != false) { - len = GET_JUMPX_OFFSET(regs.pc); - BRANCH(len); - } -} -END_CASE(JSOP_IFNEX) - -BEGIN_CASE(JSOP_ORX) -{ - bool cond; - Value *vp; - POP_BOOLEAN(cx, vp, cond); - if (cond == true) { - len = GET_JUMPX_OFFSET(regs.pc); - PUSH_COPY(*vp); - DO_NEXT_OP(len); - } -} -END_CASE(JSOP_ORX) - -BEGIN_CASE(JSOP_ANDX) -{ - bool cond; - Value *vp; - POP_BOOLEAN(cx, vp, cond); - if (cond == JS_FALSE) { - len = GET_JUMPX_OFFSET(regs.pc); - PUSH_COPY(*vp); - DO_NEXT_OP(len); - } -} -END_CASE(JSOP_ANDX) - /* * If the index value at sp[n] is not an int that fits in a jsval, it could * be an object (an XML QName, AttributeName, or AnyName), but only if we are @@ -3002,7 +1925,7 @@ END_CASE(JSOP_ANDX) #define TRY_BRANCH_AFTER_COND(cond,spdec) \ JS_BEGIN_MACRO \ JS_ASSERT(js_CodeSpec[op].length == 1); \ - uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \ + uintN diff_ = (uintN) GET_UINT8(regs.pc) - (uintN) JSOP_IFEQ; \ if (diff_ <= 1) { \ regs.sp -= spdec; \ if (cond == (diff_ != 0)) { \ @@ -3027,7 +1950,7 @@ BEGIN_CASE(JSOP_IN) FETCH_ELEMENT_ID(obj, -2, id); JSObject *obj2; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &obj2, &prop)) + if (!obj->lookupGeneric(cx, id, &obj2, &prop)) goto error; bool cond = prop != NULL; TRY_BRANCH_AFTER_COND(cond, 2); @@ -3039,7 +1962,7 @@ END_CASE(JSOP_IN) BEGIN_CASE(JSOP_ITER) { JS_ASSERT(regs.sp > regs.fp()->base()); - uintN flags = regs.pc[1]; + uint8_t flags = GET_UINT8(regs.pc); if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) goto error; CHECK_INTERRUPT_HANDLER(); @@ -3060,6 +1983,17 @@ BEGIN_CASE(JSOP_MOREITER) } END_CASE(JSOP_MOREITER) +BEGIN_CASE(JSOP_ITERNEXT) +{ + Value *itervp = regs.sp - GET_INT8(regs.pc); + JS_ASSERT(itervp >= regs.fp()->base()); + JS_ASSERT(itervp->isObject()); + PUSH_NULL(); + if (!IteratorNext(cx, &itervp->toObject(), ®s.sp[-1])) + goto error; +} +END_CASE(JSOP_ITERNEXT) + BEGIN_CASE(JSOP_ENDITER) { JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); @@ -3070,104 +2004,25 @@ BEGIN_CASE(JSOP_ENDITER) } END_CASE(JSOP_ENDITER) -BEGIN_CASE(JSOP_FORARG) +BEGIN_CASE(JSOP_DUP) { - JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); - uintN slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numFormalArgs()); - JS_ASSERT(regs.sp[-1].isObject()); - if (!IteratorNext(cx, ®s.sp[-1].toObject(), &argv[slot])) - goto error; + JS_ASSERT(regs.sp > regs.fp()->base()); + const Value &rref = regs.sp[-1]; + PUSH_COPY(rref); } -END_CASE(JSOP_FORARG) +END_CASE(JSOP_DUP) -BEGIN_CASE(JSOP_FORLOCAL) +BEGIN_CASE(JSOP_DUP2) { - JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); - uintN slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numSlots()); - JS_ASSERT(regs.sp[-1].isObject()); - if (!IteratorNext(cx, ®s.sp[-1].toObject(), ®s.fp()->slots()[slot])) - goto error; + JS_ASSERT(regs.sp - 2 >= regs.fp()->base()); + const Value &lref = regs.sp[-2]; + const Value &rref = regs.sp[-1]; + PUSH_COPY(lref); + PUSH_COPY(rref); } -END_CASE(JSOP_FORLOCAL) +END_CASE(JSOP_DUP2) -BEGIN_CASE(JSOP_FORNAME) -BEGIN_CASE(JSOP_FORGNAME) -{ - JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); - JSAtom *atom; - LOAD_ATOM(0, atom); - jsid id = ATOM_TO_JSID(atom); - JSObject *obj, *obj2; - JSProperty *prop; - if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) - goto error; - - { - AutoValueRooter tvr(cx); - JS_ASSERT(regs.sp[-1].isObject()); - if (!IteratorNext(cx, ®s.sp[-1].toObject(), tvr.addr())) - goto error; - if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode)) - goto error; - } -} -END_CASE(JSOP_FORNAME) - -BEGIN_CASE(JSOP_FORPROP) -{ - JS_ASSERT(regs.sp - 2 >= regs.fp()->base()); - JSAtom *atom; - LOAD_ATOM(0, atom); - jsid id = ATOM_TO_JSID(atom); - JSObject *obj; - FETCH_OBJECT(cx, -1, obj); - { - AutoValueRooter tvr(cx); - JS_ASSERT(regs.sp[-2].isObject()); - if (!IteratorNext(cx, ®s.sp[-2].toObject(), tvr.addr())) - goto error; - if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode)) - goto error; - } - regs.sp--; -} -END_CASE(JSOP_FORPROP) - -BEGIN_CASE(JSOP_FORELEM) - /* - * JSOP_FORELEM simply dups the property identifier at top of stack and - * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand - * side expression evaluation and assignment. This opcode exists solely to - * help the decompiler. - */ - JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); - JS_ASSERT(regs.sp[-1].isObject()); - PUSH_NULL(); - if (!IteratorNext(cx, ®s.sp[-2].toObject(), ®s.sp[-1])) - goto error; -END_CASE(JSOP_FORELEM) - -BEGIN_CASE(JSOP_DUP) -{ - JS_ASSERT(regs.sp > regs.fp()->base()); - const Value &rref = regs.sp[-1]; - PUSH_COPY(rref); -} -END_CASE(JSOP_DUP) - -BEGIN_CASE(JSOP_DUP2) -{ - JS_ASSERT(regs.sp - 2 >= regs.fp()->base()); - const Value &lref = regs.sp[-2]; - const Value &rref = regs.sp[-1]; - PUSH_COPY(lref); - PUSH_COPY(rref); -} -END_CASE(JSOP_DUP2) - -BEGIN_CASE(JSOP_SWAP) +BEGIN_CASE(JSOP_SWAP) { JS_ASSERT(regs.sp - 2 >= regs.fp()->base()); Value &lref = regs.sp[-2]; @@ -3178,88 +2033,27 @@ END_CASE(JSOP_SWAP) BEGIN_CASE(JSOP_PICK) { - jsint i = regs.pc[1]; - JS_ASSERT(regs.sp - (i+1) >= regs.fp()->base()); - Value lval = regs.sp[-(i+1)]; - memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i); + unsigned i = GET_UINT8(regs.pc); + JS_ASSERT(regs.sp - (i + 1) >= regs.fp()->base()); + Value lval = regs.sp[-int(i + 1)]; + memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i); regs.sp[-1] = lval; } END_CASE(JSOP_PICK) -#define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \ - JS_BEGIN_MACRO \ - if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \ - /* Fast path for Object instance properties. */ \ - JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \ - !shape->hasDefaultSetter()); \ - if (((shape)->slot != SHAPE_INVALID_SLOT)) \ - *(vp) = (pobj)->nativeGetSlot((shape)->slot); \ - else \ - (vp)->setUndefined(); \ - } else { \ - if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \ - goto error; \ - } \ - JS_END_MACRO - -#define NATIVE_SET(cx,obj,shape,entry,strict,vp) \ - JS_BEGIN_MACRO \ - if (shape->hasDefaultSetter() && \ - (shape)->slot != SHAPE_INVALID_SLOT && \ - !(obj)->brandedOrHasMethodBarrier()) { \ - /* Fast path for, e.g., plain Object instance properties. */ \ - (obj)->nativeSetSlot((shape)->slot, *vp); \ - } else { \ - if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \ - goto error; \ - } \ - JS_END_MACRO - -/* - * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is - * the constant length of the SET opcode sequence, and spdec is the constant - * by which to decrease the stack pointer to pop all of the SET op's operands. - * - * NB: unlike macros that could conceivably be replaced by functions (ignoring - * goto error), where a call should not have to be braced in order to expand - * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack - * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with - * nearby opcode code. - */ -#define SKIP_POP_AFTER_SET(oplen,spdec) \ - if (regs.pc[oplen] == JSOP_POP) { \ - regs.sp -= spdec; \ - regs.pc += oplen + JSOP_POP_LENGTH; \ - op = (JSOp) *regs.pc; \ - DO_OP(); \ - } - -#define END_SET_CASE(OP) \ - SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \ - END_CASE(OP) - -#define END_SET_CASE_STORE_RVAL(OP,spdec) \ - SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \ - { \ - Value *newsp = regs.sp - ((spdec) - 1); \ - newsp[-1] = regs.sp[-1]; \ - regs.sp = newsp; \ - } \ - END_CASE(OP) - BEGIN_CASE(JSOP_SETCONST) { - JSAtom *atom; - LOAD_ATOM(0, atom); - JSObject &obj = cx->stack.currentVarObj(); + PropertyName *name; + LOAD_NAME(0, name); + JSObject &obj = regs.fp()->varObj(); const Value &ref = regs.sp[-1]; - if (!obj.defineProperty(cx, ATOM_TO_JSID(atom), ref, - PropertyStub, StrictPropertyStub, + if (!obj.defineProperty(cx, name, ref, + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { goto error; } } -END_SET_CASE(JSOP_SETCONST); +END_CASE(JSOP_SETCONST); #if JS_HAS_DESTRUCTURING BEGIN_CASE(JSOP_ENUMCONSTELEM) @@ -3269,9 +2063,9 @@ BEGIN_CASE(JSOP_ENUMCONSTELEM) FETCH_OBJECT(cx, -2, obj); jsid id; FETCH_ELEMENT_ID(obj, -1, id); - if (!obj->defineProperty(cx, id, ref, - PropertyStub, StrictPropertyStub, - JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { + if (!obj->defineGeneric(cx, id, ref, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { goto error; } regs.sp -= 3; @@ -3280,7 +2074,7 @@ END_CASE(JSOP_ENUMCONSTELEM) #endif BEGIN_CASE(JSOP_BINDGNAME) - PUSH_OBJECT(*regs.fp()->scopeChain().getGlobal()); + PUSH_OBJECT(regs.fp()->scopeChain().global()); END_CASE(JSOP_BINDGNAME) BEGIN_CASE(JSOP_BINDNAME) @@ -3304,20 +2098,13 @@ BEGIN_CASE(JSOP_BINDNAME) * forms. */ obj = ®s.fp()->scopeChain(); - if (!obj->getParent()) + if (obj->isGlobal()) break; - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom); - if (!atom) { - ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); - break; - } + PropertyName *name; + LOAD_NAME(0, name); - jsid id = ATOM_TO_JSID(atom); - obj = js_FindIdentifierBase(cx, ®s.fp()->scopeChain(), id); + obj = FindIdentifierBase(cx, ®s.fp()->scopeChain(), name); if (!obj) goto error; } while (0); @@ -3325,17 +2112,12 @@ BEGIN_CASE(JSOP_BINDNAME) } END_CASE(JSOP_BINDNAME) -BEGIN_CASE(JSOP_IMACOP) - JS_ASSERT(JS_UPTRDIFF(regs.fp()->imacropc(), script->code) < script->length); - op = JSOp(*regs.fp()->imacropc()); - DO_OP(); - #define BITWISE_OP(OP) \ JS_BEGIN_MACRO \ int32_t i, j; \ - if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \ + if (!ToInt32(cx, regs.sp[-2], &i)) \ goto error; \ - if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \ + if (!ToInt32(cx, regs.sp[-1], &j)) \ goto error; \ i = i OP j; \ regs.sp--; \ @@ -3360,7 +2142,7 @@ END_CASE(JSOP_BITAND) JS_BEGIN_MACRO \ Value rval = regs.sp[-1]; \ Value lval = regs.sp[-2]; \ - JSBool cond; \ + bool cond; \ if (!LooselyEqual(cx, lval, rval, &cond)) \ goto error; \ cond = cond OP JS_TRUE; \ @@ -3383,7 +2165,7 @@ END_CASE(JSOP_NE) JS_BEGIN_MACRO \ const Value &rref = regs.sp[-1]; \ const Value &lref = regs.sp[-2]; \ - JSBool equal; \ + bool equal; \ if (!StrictlyEqual(cx, lref, rref, &equal)) \ goto error; \ COND = equal OP JS_TRUE; \ @@ -3418,77 +2200,66 @@ BEGIN_CASE(JSOP_CASE) } END_CASE(JSOP_CASE) -BEGIN_CASE(JSOP_CASEX) -{ - bool cond; - STRICT_EQUALITY_OP(==, cond); - if (cond) { - regs.sp--; - len = GET_JUMPX_OFFSET(regs.pc); - BRANCH(len); - } -} -END_CASE(JSOP_CASEX) - #undef STRICT_EQUALITY_OP -#define RELATIONAL_OP(OP) \ - JS_BEGIN_MACRO \ - Value rval = regs.sp[-1]; \ - Value lval = regs.sp[-2]; \ - bool cond; \ - /* Optimize for two int-tagged operands (typical loop control). */ \ - if (lval.isInt32() && rval.isInt32()) { \ - cond = lval.toInt32() OP rval.toInt32(); \ - } else { \ - if (lval.isObject()) \ - DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \ - if (rval.isObject()) \ - DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ - if (lval.isString() && rval.isString()) { \ - JSString *l = lval.toString(), *r = rval.toString(); \ - int32 result; \ - if (!CompareStrings(cx, l, r, &result)) \ - goto error; \ - cond = result OP 0; \ - } else { \ - double l, r; \ - if (!ValueToNumber(cx, lval, &l) || \ - !ValueToNumber(cx, rval, &r)) { \ - goto error; \ - } \ - cond = JSDOUBLE_COMPARE(l, OP, r, false); \ - } \ - } \ - TRY_BRANCH_AFTER_COND(cond, 2); \ - regs.sp--; \ - regs.sp[-1].setBoolean(cond); \ - JS_END_MACRO - BEGIN_CASE(JSOP_LT) - RELATIONAL_OP(<); +{ + bool cond; + const Value &lref = regs.sp[-2]; + const Value &rref = regs.sp[-1]; + if (!LessThanOperation(cx, lref, rref, &cond)) + goto error; + TRY_BRANCH_AFTER_COND(cond, 2); + regs.sp[-2].setBoolean(cond); + regs.sp--; +} END_CASE(JSOP_LT) BEGIN_CASE(JSOP_LE) - RELATIONAL_OP(<=); +{ + bool cond; + const Value &lref = regs.sp[-2]; + const Value &rref = regs.sp[-1]; + if (!LessThanOrEqualOperation(cx, lref, rref, &cond)) + goto error; + TRY_BRANCH_AFTER_COND(cond, 2); + regs.sp[-2].setBoolean(cond); + regs.sp--; +} END_CASE(JSOP_LE) BEGIN_CASE(JSOP_GT) - RELATIONAL_OP(>); +{ + bool cond; + const Value &lref = regs.sp[-2]; + const Value &rref = regs.sp[-1]; + if (!GreaterThanOperation(cx, lref, rref, &cond)) + goto error; + TRY_BRANCH_AFTER_COND(cond, 2); + regs.sp[-2].setBoolean(cond); + regs.sp--; +} END_CASE(JSOP_GT) BEGIN_CASE(JSOP_GE) - RELATIONAL_OP(>=); +{ + bool cond; + const Value &lref = regs.sp[-2]; + const Value &rref = regs.sp[-1]; + if (!GreaterThanOrEqualOperation(cx, lref, rref, &cond)) + goto error; + TRY_BRANCH_AFTER_COND(cond, 2); + regs.sp[-2].setBoolean(cond); + regs.sp--; +} END_CASE(JSOP_GE) -#undef RELATIONAL_OP - #define SIGNED_SHIFT_OP(OP) \ JS_BEGIN_MACRO \ int32_t i, j; \ - if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \ + if (!ToInt32(cx, regs.sp[-2], &i)) \ goto error; \ - if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \ + if (!ToInt32(cx, regs.sp[-1], &j)) \ goto error; \ i = i OP (j & 31); \ regs.sp--; \ @@ -3508,158 +2279,67 @@ END_CASE(JSOP_RSH) BEGIN_CASE(JSOP_URSH) { uint32_t u; - if (!ValueToECMAUint32(cx, regs.sp[-2], &u)) + if (!ToUint32(cx, regs.sp[-2], &u)) goto error; int32_t j; - if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) + if (!ToInt32(cx, regs.sp[-1], &j)) goto error; u >>= (j & 31); regs.sp--; - regs.sp[-1].setNumber(uint32(u)); + if (!regs.sp[-1].setNumber(uint32_t(u))) + TypeScript::MonitorOverflow(cx, script, regs.pc); } END_CASE(JSOP_URSH) BEGIN_CASE(JSOP_ADD) { - Value rval = regs.sp[-1]; Value lval = regs.sp[-2]; - - if (lval.isInt32() && rval.isInt32()) { - int32_t l = lval.toInt32(), r = rval.toInt32(); - int32_t sum = l + r; - regs.sp--; - if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) - regs.sp[-1].setDouble(double(l) + double(r)); - else - regs.sp[-1].setInt32(sum); - } else -#if JS_HAS_XML_SUPPORT - if (IsXML(lval) && IsXML(rval)) { - if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval)) - goto error; - regs.sp--; - regs.sp[-1] = rval; - } else -#endif - { - if (lval.isObject()) - DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); - if (rval.isObject()) - DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); - bool lIsString, rIsString; - if ((lIsString = lval.isString()) | (rIsString = rval.isString())) { - JSString *lstr, *rstr; - if (lIsString) { - lstr = lval.toString(); - } else { - lstr = js_ValueToString(cx, lval); - if (!lstr) - goto error; - regs.sp[-2].setString(lstr); - } - if (rIsString) { - rstr = rval.toString(); - } else { - rstr = js_ValueToString(cx, rval); - if (!rstr) - goto error; - regs.sp[-1].setString(rstr); - } - JSString *str = js_ConcatStrings(cx, lstr, rstr); - if (!str) - goto error; - regs.sp--; - regs.sp[-1].setString(str); - } else { - double l, r; - if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r)) - goto error; - l += r; - regs.sp--; - regs.sp[-1].setNumber(l); - } - } + Value rval = regs.sp[-1]; + if (!AddOperation(cx, lval, rval, ®s.sp[-2])) + goto error; + regs.sp--; } END_CASE(JSOP_ADD) -#define BINARY_OP(OP) \ - JS_BEGIN_MACRO \ - double d1, d2; \ - if (!ValueToNumber(cx, regs.sp[-2], &d1) || \ - !ValueToNumber(cx, regs.sp[-1], &d2)) { \ - goto error; \ - } \ - double d = d1 OP d2; \ - regs.sp--; \ - regs.sp[-1].setNumber(d); \ - JS_END_MACRO - BEGIN_CASE(JSOP_SUB) - BINARY_OP(-); +{ + Value lval = regs.sp[-2]; + Value rval = regs.sp[-1]; + if (!SubOperation(cx, lval, rval, ®s.sp[-2])) + goto error; + regs.sp--; +} END_CASE(JSOP_SUB) BEGIN_CASE(JSOP_MUL) - BINARY_OP(*); +{ + Value lval = regs.sp[-2]; + Value rval = regs.sp[-1]; + if (!MulOperation(cx, lval, rval, ®s.sp[-2])) + goto error; + regs.sp--; +} END_CASE(JSOP_MUL) -#undef BINARY_OP - BEGIN_CASE(JSOP_DIV) { - double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { + Value lval = regs.sp[-2]; + Value rval = regs.sp[-1]; + if (!DivOperation(cx, lval, rval, ®s.sp[-2])) goto error; - } regs.sp--; - if (d2 == 0) { - const Value *vp; -#ifdef XP_WIN - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - vp = &rt->NaNValue; - else -#endif - if (d1 == 0 || JSDOUBLE_IS_NaN(d1)) - vp = &rt->NaNValue; - else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2)) - vp = &rt->negativeInfinityValue; - else - vp = &rt->positiveInfinityValue; - regs.sp[-1] = *vp; - } else { - d1 /= d2; - regs.sp[-1].setNumber(d1); - } } END_CASE(JSOP_DIV) BEGIN_CASE(JSOP_MOD) { - Value &lref = regs.sp[-2]; - Value &rref = regs.sp[-1]; - int32_t l, r; - if (lref.isInt32() && rref.isInt32() && - (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) { - int32_t mod = l % r; - regs.sp--; - regs.sp[-1].setInt32(mod); - } else { - double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { - goto error; - } - regs.sp--; - if (d2 == 0) { - regs.sp[-1].setDouble(js_NaN); - } else { - d1 = js_fmod(d1, d2); - regs.sp[-1].setDouble(d1); - } - } + Value lval = regs.sp[-2]; + Value rval = regs.sp[-1]; + if (!ModOperation(cx, lval, rval, ®s.sp[-2])) + goto error; + regs.sp--; } END_CASE(JSOP_MOD) @@ -3675,7 +2355,7 @@ END_CASE(JSOP_NOT) BEGIN_CASE(JSOP_BITNOT) { int32_t i; - if (!ValueToECMAInt32(cx, regs.sp[-1], &i)) + if (!ToInt32(cx, regs.sp[-1], &i)) goto error; i = ~i; regs.sp[-1].setInt32(i); @@ -3689,34 +2369,36 @@ BEGIN_CASE(JSOP_NEG) * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the * results, -0.0 or INT32_MAX + 1, are jsdouble values. */ - const Value &ref = regs.sp[-1]; + Value ref = regs.sp[-1]; int32_t i; if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) { i = -i; regs.sp[-1].setInt32(i); } else { double d; - if (!ValueToNumber(cx, regs.sp[-1], &d)) + if (!ToNumber(cx, regs.sp[-1], &d)) goto error; d = -d; - regs.sp[-1].setDouble(d); + if (!regs.sp[-1].setNumber(d) && !ref.isDouble()) + TypeScript::MonitorOverflow(cx, script, regs.pc); } } END_CASE(JSOP_NEG) BEGIN_CASE(JSOP_POS) - if (!ValueToNumber(cx, ®s.sp[-1])) + if (!ToNumber(cx, ®s.sp[-1])) goto error; + if (!regs.sp[-1].isInt32()) + TypeScript::MonitorOverflow(cx, script, regs.pc); END_CASE(JSOP_POS) BEGIN_CASE(JSOP_DELNAME) { - JSAtom *atom; - LOAD_ATOM(0, atom); - jsid id = ATOM_TO_JSID(atom); + PropertyName *name; + LOAD_NAME(0, name); JSObject *obj, *obj2; JSProperty *prop; - if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) + if (!FindProperty(cx, name, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) goto error; /* Strict mode code should never contain JSOP_DELNAME opcodes. */ @@ -3725,7 +2407,7 @@ BEGIN_CASE(JSOP_DELNAME) /* ECMA says to return true if name is undefined or inherited. */ PUSH_BOOLEAN(true); if (prop) { - if (!obj->deleteProperty(cx, id, ®s.sp[-1], false)) + if (!obj->deleteProperty(cx, name, ®s.sp[-1], false)) goto error; } } @@ -3733,15 +2415,14 @@ END_CASE(JSOP_DELNAME) BEGIN_CASE(JSOP_DELPROP) { - JSAtom *atom; - LOAD_ATOM(0, atom); - jsid id = ATOM_TO_JSID(atom); + PropertyName *name; + LOAD_NAME(0, name); JSObject *obj; FETCH_OBJECT(cx, -1, obj); Value rval; - if (!obj->deleteProperty(cx, id, &rval, script->strictModeCode)) + if (!obj->deleteProperty(cx, name, &rval, script->strictModeCode)) goto error; regs.sp[-1] = rval; @@ -3754,25 +2435,37 @@ BEGIN_CASE(JSOP_DELELEM) JSObject *obj; FETCH_OBJECT(cx, -2, obj); - /* Fetch index and convert it to id suitable for use with obj. */ - jsid id; - FETCH_ELEMENT_ID(obj, -1, id); + const Value &propval = regs.sp[-1]; + Value &rval = regs.sp[-2]; - /* Get or set the element. */ - if (!obj->deleteProperty(cx, id, ®s.sp[-2], script->strictModeCode)) + if (!obj->deleteByValue(cx, propval, &rval, script->strictModeCode)) goto error; regs.sp--; } END_CASE(JSOP_DELELEM) +BEGIN_CASE(JSOP_TOID) +{ + /* + * Increment or decrement requires use to lookup the same property twice, but we need to avoid + * the oberservable stringification the second time. + * There must be an object value below the id, which will not be popped + * but is necessary in interning the id for XML. + */ + Value objval = regs.sp[-2]; + Value idval = regs.sp[-1]; + if (!ToIdOperation(cx, objval, idval, ®s.sp[-1])) + goto error; +} +END_CASE(JSOP_TOID) + BEGIN_CASE(JSOP_TYPEOFEXPR) BEGIN_CASE(JSOP_TYPEOF) { const Value &ref = regs.sp[-1]; - JSType type = JS_TypeOfValue(cx, Jsvalify(ref)); - JSAtom *atom = rt->atomState.typeAtoms[type]; - regs.sp[-1].setString(atom); + JSType type = JS_TypeOfValue(cx, ref); + regs.sp[-1].setString(rt->atomState.typeAtoms[type]); } END_CASE(JSOP_TYPEOF) @@ -3780,39 +2473,17 @@ BEGIN_CASE(JSOP_VOID) regs.sp[-1].setUndefined(); END_CASE(JSOP_VOID) -{ - JSObject *obj; - JSAtom *atom; - jsid id; - jsint i; - BEGIN_CASE(JSOP_INCELEM) BEGIN_CASE(JSOP_DECELEM) BEGIN_CASE(JSOP_ELEMINC) BEGIN_CASE(JSOP_ELEMDEC) - - /* - * Delay fetching of id until we have the object to ensure the proper - * evaluation order. See bug 372331. - */ - id = JSID_VOID; - i = -2; - goto fetch_incop_obj; + /* No-op */ +END_CASE(JSOP_INCELEM) BEGIN_CASE(JSOP_INCPROP) BEGIN_CASE(JSOP_DECPROP) BEGIN_CASE(JSOP_PROPINC) BEGIN_CASE(JSOP_PROPDEC) - LOAD_ATOM(0, atom); - id = ATOM_TO_JSID(atom); - i = -1; - - fetch_incop_obj: - FETCH_OBJECT(cx, i, obj); - if (JSID_IS_VOID(id)) - FETCH_ELEMENT_ID(obj, -1, id); - goto do_incop; - BEGIN_CASE(JSOP_INCNAME) BEGIN_CASE(JSOP_DECNAME) BEGIN_CASE(JSOP_NAMEINC) @@ -3821,111 +2492,12 @@ BEGIN_CASE(JSOP_INCGNAME) BEGIN_CASE(JSOP_DECGNAME) BEGIN_CASE(JSOP_GNAMEINC) BEGIN_CASE(JSOP_GNAMEDEC) -{ - obj = ®s.fp()->scopeChain(); - if (js_CodeSpec[op].format & JOF_GNAME) - obj = obj->getGlobal(); - - JSObject *obj2; - PropertyCacheEntry *entry; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom); - if (!atom) { - ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); - if (obj == obj2 && entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - Value &rref = obj->nativeGetSlotRef(slot); - int32_t tmp; - if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) { - int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1); - if (!(js_CodeSpec[op].format & JOF_POST)) - tmp = inc; - rref.getInt32Ref() = inc; - PUSH_INT32(tmp); - len = JSOP_INCNAME_LENGTH; - DO_NEXT_OP(len); - } - } - LOAD_ATOM(0, atom); - } - - id = ATOM_TO_JSID(atom); - JSProperty *prop; - if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) - goto error; - if (!prop) { - atomNotDefined = atom; - goto atom_not_defined; - } -} - -do_incop: -{ - /* - * We need a root to store the value to leave on the stack until - * we have done with obj->setProperty. - */ - PUSH_NULL(); - if (!obj->getProperty(cx, id, ®s.sp[-1])) - goto error; - - const JSCodeSpec *cs = &js_CodeSpec[op]; - JS_ASSERT(cs->ndefs == 1); - JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2); - - uint32 format = cs->format; - uint32 setPropFlags = (JOF_MODE(format) == JOF_NAME) - ? JSRESOLVE_ASSIGNING - : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED; - - Value &ref = regs.sp[-1]; - int32_t tmp; - if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) { - int incr = (format & JOF_INC) ? 1 : -1; - if (format & JOF_POST) - ref.getInt32Ref() = tmp + incr; - else - ref.getInt32Ref() = tmp += incr; - - { - JSAutoResolveFlags rf(cx, setPropFlags); - if (!obj->setProperty(cx, id, &ref, script->strictModeCode)) - goto error; - } - - /* - * We must set regs.sp[-1] to tmp for both post and pre increments - * as the setter overwrites regs.sp[-1]. - */ - ref.setInt32(tmp); - } else { - /* We need an extra root for the result. */ - PUSH_NULL(); - if (!js_DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1])) - goto error; - - { - JSAutoResolveFlags rf(cx, setPropFlags); - if (!obj->setProperty(cx, id, ®s.sp[-1], script->strictModeCode)) - goto error; - } - - regs.sp--; - } - - if (cs->nuses == 0) { - /* regs.sp[-1] already contains the result of name increment. */ - } else { - regs.sp[-1 - cs->nuses] = regs.sp[-1]; - regs.sp -= cs->nuses; - } - len = cs->length; - DO_NEXT_OP(len); -} -} + /* No-op */ +END_CASE(JSOP_INCPROP) { int incr, incr2; - uint32 slot; + uint32_t slot; Value *vp; /* Position cases so the most frequent i++ does not need a jump. */ @@ -3941,7 +2513,6 @@ BEGIN_CASE(JSOP_ARGINC) do_arg_incop: slot = GET_ARGNO(regs.pc); JS_ASSERT(slot < regs.fp()->numFormalArgs()); - METER_SLOT_OP(op, slot); vp = argv + slot; goto do_int_fast_incop; @@ -3962,7 +2533,6 @@ BEGIN_CASE(JSOP_LOCALINC) do_local_incop: slot = GET_SLOTNO(regs.pc); JS_ASSERT(slot < regs.fp()->numSlots()); - METER_SLOT_OP(op, slot); vp = regs.fp()->slots() + slot; do_int_fast_incop: @@ -3970,12 +2540,12 @@ BEGIN_CASE(JSOP_LOCALINC) if (JS_LIKELY(vp->isInt32() && CanIncDecWithoutOverflow(tmp = vp->toInt32()))) { vp->getInt32Ref() = tmp + incr; JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length); - SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0); PUSH_INT32(tmp + incr2); } else { PUSH_COPY(*vp); - if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) + if (!DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) goto error; + TypeScript::MonitorOverflow(cx, script, regs.pc); } len = JSOP_INCARG_LENGTH; JS_ASSERT(len == js_CodeSpec[op].length); @@ -3988,456 +2558,45 @@ BEGIN_CASE(JSOP_THIS) PUSH_COPY(regs.fp()->thisValue()); END_CASE(JSOP_THIS) -BEGIN_CASE(JSOP_UNBRANDTHIS) -{ - if (!ComputeThis(cx, regs.fp())) - goto error; - Value &thisv = regs.fp()->thisValue(); - if (thisv.isObject()) { - JSObject *obj = &thisv.toObject(); - if (obj->isNative()) - obj->unbrand(cx); - } -} -END_CASE(JSOP_UNBRANDTHIS) - -{ - JSObject *obj; - Value *vp; - jsint i; - -BEGIN_CASE(JSOP_GETTHISPROP) - if (!ComputeThis(cx, regs.fp())) - goto error; - i = 0; - PUSH_COPY(regs.fp()->thisValue()); - goto do_getprop_body; - -BEGIN_CASE(JSOP_GETARGPROP) -{ - i = ARGNO_LEN; - uint32 slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numFormalArgs()); - PUSH_COPY(argv[slot]); - goto do_getprop_body; -} - -BEGIN_CASE(JSOP_GETLOCALPROP) -{ - i = SLOTNO_LEN; - uint32 slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < script->nslots); - PUSH_COPY(regs.fp()->slots()[slot]); - goto do_getprop_body; -} - BEGIN_CASE(JSOP_GETPROP) BEGIN_CASE(JSOP_GETXPROP) - i = 0; - - do_getprop_body: - vp = ®s.sp[-1]; - - do_getprop_with_lval: - VALUE_TO_OBJECT(cx, vp, obj); - - { - Value rval; - do { - /* - * We do not impose the method read barrier if in an imacro, - * assuming any property gets it does (e.g., for 'toString' - * from JSOP_NEW) will not be leaked to the calling script. - */ - JSObject *aobj = js_GetProtoIfDenseArray(obj); - - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom); - if (!atom) { - ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry); - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - const Shape *shape = entry->vword.toShape(); - NATIVE_GET(cx, obj, obj2, shape, - regs.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER, - &rval); - } - break; - } - - jsid id = ATOM_TO_JSID(atom); - if (JS_LIKELY(!aobj->getOps()->getProperty) - ? !js_GetPropertyHelper(cx, obj, id, - (regs.fp()->hasImacropc() || - regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER, - &rval) - : !obj->getProperty(cx, id, &rval)) { - goto error; - } - } while (0); - - regs.sp[-1] = rval; - assertSameCompartment(cx, regs.sp[-1]); - JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length); - len = JSOP_GETPROP_LENGTH + i; - } -END_VARLEN_CASE - BEGIN_CASE(JSOP_LENGTH) - vp = ®s.sp[-1]; - if (vp->isString()) { - vp->setInt32(vp->toString()->length()); - } else if (vp->isObject()) { - JSObject *obj = &vp->toObject(); - if (obj->isArray()) { - jsuint length = obj->getArrayLength(); - regs.sp[-1].setNumber(length); - } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) { - uint32 length = obj->getArgsInitialLength(); - JS_ASSERT(length < INT32_MAX); - regs.sp[-1].setInt32(int32_t(length)); - } else { - i = -2; - goto do_getprop_with_lval; - } - } else { - i = -2; - goto do_getprop_with_lval; - } -END_CASE(JSOP_LENGTH) - -} - BEGIN_CASE(JSOP_CALLPROP) { - Value lval = regs.sp[-1]; - - Value objv; - if (lval.isObject()) { - objv = lval; - } else { - JSProtoKey protoKey; - if (lval.isString()) { - protoKey = JSProto_String; - } else if (lval.isNumber()) { - protoKey = JSProto_Number; - } else if (lval.isBoolean()) { - protoKey = JSProto_Boolean; - } else { - JS_ASSERT(lval.isNull() || lval.isUndefined()); - js_ReportIsNullOrUndefined(cx, -1, lval, NULL); - goto error; - } - JSObject *pobj; - if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) - goto error; - objv.setObject(*pobj); - } - - JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject()); Value rval; + if (!GetPropertyOperation(cx, regs.pc, regs.sp[-1], &rval)) + goto error; - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom); - if (!atom) { - ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry); - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - const Shape *shape = entry->vword.toShape(); - NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval); - } - regs.sp[-1] = rval; - assertSameCompartment(cx, regs.sp[-1]); - PUSH_COPY(lval); - } else { - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(atom); + TypeScript::Monitor(cx, script, regs.pc, rval); - PUSH_NULL(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!aobj->getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - goto error; - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; - assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); - } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - goto error; - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); - } - } -#if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { - LOAD_ATOM(0, atom); - regs.sp[-2].setString(atom); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) - goto error; - } -#endif + regs.sp[-1] = rval; + assertSameCompartment(cx, regs.sp[-1]); } -END_CASE(JSOP_CALLPROP) - -BEGIN_CASE(JSOP_UNBRAND) - JS_ASSERT(regs.sp - regs.fp()->slots() >= 1); - regs.sp[-1].toObject().unbrand(cx); -END_CASE(JSOP_UNBRAND) +END_CASE(JSOP_GETPROP) BEGIN_CASE(JSOP_SETGNAME) BEGIN_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_SETMETHOD) { - Value rval = regs.sp[-1]; - JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval)); - Value &lref = regs.sp[-2]; - JS_ASSERT_IF(op == JSOP_SETNAME, lref.isObject()); - JSObject *obj; - VALUE_TO_OBJECT(cx, &lref, obj); - - JS_ASSERT_IF(op == JSOP_SETGNAME, obj == regs.fp()->scopeChain().getGlobal()); + const Value &rval = regs.sp[-1]; + const Value &lval = regs.sp[-2]; - do { - PropertyCache *cache = &JS_PROPERTY_CACHE(cx); - - /* - * Probe the property cache, specializing for two important - * set-property cases. First: - * - * function f(a, b, c) { - * var o = {p:a, q:b, r:c}; - * return o; - * } - * - * or similar real-world cases, which evolve a newborn native - * object predicatably through some bounded number of property - * additions. And second: - * - * o.p = x; - * - * in a frequently executed method or loop body, where p will - * (possibly after the first iteration) always exist in native - * object o. - */ - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) { - /* - * Property cache hit, only partially confirmed by testForSet. We - * know that the entry applies to regs.pc and that obj's shape - * matches. - * - * The entry predicts either a new property to be added directly to - * obj by this set, or on an existing "own" property, or on a - * prototype property that has a setter. - */ - const Shape *shape = entry->vword.toShape(); - JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable()); - JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0); - - /* - * Fastest path: check whether obj already has the cached shape and - * call NATIVE_SET and break to get out of the do-while(0). But we - * can call NATIVE_SET only for a direct or proto-setter hit. - */ - if (!entry->adding()) { - if (entry->vcapTag() == 0 || - ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape())) - { -#ifdef DEBUG - if (entry->directHit()) { - JS_ASSERT(obj->nativeContains(*shape)); - } else { - JS_ASSERT(obj2->nativeContains(*shape)); - JS_ASSERT(entry->vcapTag() == 1); - JS_ASSERT(entry->kshape != entry->vshape()); - JS_ASSERT(!shape->hasSlot()); - } -#endif - - PCMETER(cache->pchits++); - PCMETER(cache->setpchits++); - NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval); - break; - } - } else { - JS_ASSERT(obj->isExtensible()); - - if (obj->nativeEmpty()) { - if (!obj->ensureClassReservedSlotsForEmptyObject(cx)) - goto error; - } - - uint32 slot; - if (shape->previous() == obj->lastProperty() && - entry->vshape() == rt->protoHazardShape && - shape->hasDefaultSetter()) { - slot = shape->slot; - JS_ASSERT(slot == obj->slotSpan()); - - /* - * Fast path: adding a plain old property that was once at - * the frontier of the property tree, whose slot is next to - * claim among the already-allocated slots in obj, where - * shape->table has not been created yet. - */ - PCMETER(cache->pchits++); - PCMETER(cache->addpchits++); - - if (slot < obj->numSlots()) { - JS_ASSERT(obj->getSlot(slot).isUndefined()); - } else { - if (!obj->allocSlot(cx, &slot)) - goto error; - JS_ASSERT(slot == shape->slot); - } - - /* Simply extend obj's property tree path with shape! */ - obj->extend(cx, shape); - - /* - * No method change check here because here we are adding a - * new property, not updating an existing slot's value that - * might contain a method of a branded shape. - */ - TRACE_1(AddProperty, obj); - obj->nativeSetSlot(slot, rval); - - /* - * Purge the property cache of the id we may have just - * shadowed in obj's scope and proto chains. - */ - js_PurgeScopeChain(cx, obj, shape->id); - break; - } - } - PCMETER(cache->setpcmisses++); - - LOAD_ATOM(0, atom); - } else { - JS_ASSERT(atom); - } + if (!SetPropertyOperation(cx, regs.pc, lval, rval)) + goto error; - jsid id = ATOM_TO_JSID(atom); - if (entry && JS_LIKELY(!obj->getOps()->setProperty)) { - uintN defineHow; - if (op == JSOP_SETMETHOD) - defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD; - else if (op == JSOP_SETNAME) - defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED; - else - defineHow = JSDNP_CACHE_RESULT; - if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)) - goto error; - } else { - if (!obj->setProperty(cx, id, &rval, script->strictModeCode)) - goto error; - ABORT_RECORDING(cx, "Non-native set"); - } - } while (0); + regs.sp[-2] = regs.sp[-1]; + regs.sp--; } -END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2); +END_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_GETELEM) { Value &lref = regs.sp[-2]; Value &rref = regs.sp[-1]; - if (lref.isString() && rref.isInt32()) { - JSString *str = lref.toString(); - int32_t i = rref.toInt32(); - if (size_t(i) < str->length()) { - str = JSAtom::getUnitStringForElement(cx, str, size_t(i)); - if (!str) - goto error; - regs.sp--; - regs.sp[-1].setString(str); - len = JSOP_GETELEM_LENGTH; - DO_NEXT_OP(len); - } - } - - JSObject *obj; - VALUE_TO_OBJECT(cx, &lref, obj); - - const Value *copyFrom; - Value rval; - jsid id; - if (rref.isInt32()) { - int32_t i = rref.toInt32(); - if (obj->isDenseArray()) { - jsuint idx = jsuint(i); - - if (idx < obj->getDenseArrayCapacity()) { - copyFrom = obj->addressOfDenseArrayElement(idx); - if (!copyFrom->isMagic()) - goto end_getelem; - } - } else if (obj->isArguments()) { - uint32 arg = uint32(i); - - if (arg < obj->getArgsInitialLength()) { - copyFrom = obj->addressOfArgsElement(arg); - if (!copyFrom->isMagic(JS_ARGS_HOLE)) { - if (StackFrame *afp = (StackFrame *) obj->getPrivate()) - copyFrom = &afp->canonicalActualArg(arg); - goto end_getelem; - } - } - } - if (JS_LIKELY(INT_FITS_IN_JSID(i))) - id = INT_TO_JSID(i); - else - goto intern_big_int; - } else { - int32_t i; - if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) { - id = INT_TO_JSID(i); - } else { - intern_big_int: - if (!js_InternNonIntElementId(cx, obj, rref, &id)) - goto error; - } - } - - if (!obj->getProperty(cx, id, &rval)) + if (!GetElementOperation(cx, lref, rref, ®s.sp[-2])) goto error; - copyFrom = &rval; - - end_getelem: regs.sp--; - regs.sp[-1] = *copyFrom; - assertSameCompartment(cx, regs.sp[-1]); } END_CASE(JSOP_GETELEM) @@ -4445,7 +2604,7 @@ BEGIN_CASE(JSOP_CALLELEM) { /* Find the object on which to look for |this|'s properties. */ Value thisv = regs.sp[-2]; - JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); + JSObject *thisObj = ValuePropertyBearer(cx, regs.fp(), thisv, -2); if (!thisObj) goto error; @@ -4459,16 +2618,14 @@ BEGIN_CASE(JSOP_CALLELEM) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) { - /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */ - regs.sp[-2] = regs.sp[-1]; - regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, &thisv.toObject(), regs.sp[-1], regs.sp - 2)) goto error; - } else -#endif - { - regs.sp[-1] = thisv; } +#endif + + regs.sp--; + + TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); } END_CASE(JSOP_CALLELEM) @@ -4478,213 +2635,139 @@ BEGIN_CASE(JSOP_SETELEM) FETCH_OBJECT(cx, -3, obj); jsid id; FETCH_ELEMENT_ID(obj, -2, id); - Value rval; - do { - if (obj->isDenseArray() && JSID_IS_INT(id)) { - jsuint length = obj->getDenseArrayCapacity(); - jsint i = JSID_TO_INT(id); - if ((jsuint)i < length) { - if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { - if (js_PrototypeHasIndexedProperties(cx, obj)) - break; - if ((jsuint)i >= obj->getArrayLength()) - obj->setArrayLength(i + 1); - } - obj->setDenseArrayElement(i, regs.sp[-1]); - goto end_setelem; - } - } - } while (0); - rval = regs.sp[-1]; - if (!obj->setProperty(cx, id, &rval, script->strictModeCode)) + Value &value = regs.sp[-1]; + if (!SetObjectElementOperation(cx, obj, id, value)) goto error; - end_setelem:; + regs.sp[-3] = value; + regs.sp -= 2; } -END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3) +END_CASE(JSOP_SETELEM) BEGIN_CASE(JSOP_ENUMELEM) { /* Funky: the value to set is under the [obj, id] pair. */ JSObject *obj; FETCH_OBJECT(cx, -2, obj); - jsid id; - FETCH_ELEMENT_ID(obj, -1, id); - Value rval = regs.sp[-3]; - if (!obj->setProperty(cx, id, &rval, script->strictModeCode)) - goto error; - regs.sp -= 3; -} -END_CASE(JSOP_ENUMELEM) - -{ // begin block around calling opcodes - JSFunction *newfun; - JSObject *callee; - uint32 flags; - uintN argc; - Value *vp; - -BEGIN_CASE(JSOP_NEW) -{ - /* Get immediate argc and find the constructor function. */ - argc = GET_ARGC(regs.pc); - vp = regs.sp - (2 + argc); - JS_ASSERT(vp >= regs.fp()->base()); - /* - * Assign lval, callee, and newfun exactly as the code at inline_call: expects to - * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor. - */ - if (IsFunctionObject(vp[0], &callee)) { - newfun = callee->getFunctionPrivate(); - if (newfun->isInterpretedConstructor()) { - if (newfun->u.i.script->isEmpty()) { - JSObject *obj2 = js_CreateThisForFunction(cx, callee); - if (!obj2) - goto error; - vp[0].setObject(*obj2); - regs.sp = vp + 1; - goto end_new; - } - - flags = StackFrame::CONSTRUCTING; - goto inline_call; - } - } - - if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + jsid id; + FETCH_ELEMENT_ID(obj, -1, id); + Value rval = regs.sp[-3]; + if (!obj->setGeneric(cx, id, &rval, script->strictModeCode)) goto error; - regs.sp = vp + 1; - CHECK_INTERRUPT_HANDLER(); - TRACE_0(NativeCallComplete); - - end_new:; + regs.sp -= 3; } -END_CASE(JSOP_NEW) +END_CASE(JSOP_ENUMELEM) BEGIN_CASE(JSOP_EVAL) { - argc = GET_ARGC(regs.pc); - vp = regs.sp - (argc + 2); - - if (!IsBuiltinEvalForScope(®s.fp()->scopeChain(), *vp)) - goto call_using_invoke; - - if (!DirectEval(cx, CallArgsFromVp(argc, vp))) - goto error; - - regs.sp = vp + 1; + CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp); + if (IsBuiltinEvalForScope(®s.fp()->scopeChain(), args.calleev())) { + if (!DirectEval(cx, args)) + goto error; + } else { + if (!InvokeKernel(cx, args)) + goto error; + } + CHECK_INTERRUPT_HANDLER(); + regs.sp = args.spAfterCall(); + TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); } END_CASE(JSOP_EVAL) +BEGIN_CASE(JSOP_NEW) BEGIN_CASE(JSOP_CALL) -BEGIN_CASE(JSOP_FUNAPPLY) BEGIN_CASE(JSOP_FUNCALL) +BEGIN_CASE(JSOP_FUNAPPLY) { - argc = GET_ARGC(regs.pc); - vp = regs.sp - (argc + 2); - - if (IsFunctionObject(*vp, &callee)) { - newfun = callee->getFunctionPrivate(); - - /* Clear frame flags since this is not a constructor call. */ - flags = 0; - if (newfun->isInterpreted()) - inline_call: - { - JSScript *newscript = newfun->script(); - if (JS_UNLIKELY(newscript->isEmpty())) { - vp->setUndefined(); - regs.sp = vp + 1; - goto end_call; - } + CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp); + JS_ASSERT(args.base() >= regs.fp()->base()); - /* Restrict recursion of lightweight functions. */ - if (JS_UNLIKELY(inlineCallCount >= StackSpace::MAX_INLINE_CALLS)) { - js_ReportOverRecursed(cx); - goto error; - } + bool construct = (*regs.pc == JSOP_NEW); + + JSFunction *fun; - /* Get pointer to new frame/slots, prepare arguments. */ - ContextStack &stack = cx->stack; - StackFrame *newfp = stack.getInlineFrame(cx, regs.sp, argc, newfun, - newscript, &flags); - if (JS_UNLIKELY(!newfp)) + /* Don't bother trying to fast-path calls to scripted non-constructors. */ + if (!IsFunctionObject(args.calleev(), &fun) || !fun->isInterpretedConstructor()) { + if (construct) { + if (!InvokeConstructorKernel(cx, args)) goto error; + } else { + if (!InvokeKernel(cx, args)) + goto error; + } + regs.sp = args.spAfterCall(); + TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); + CHECK_INTERRUPT_HANDLER(); + len = JSOP_CALL_LENGTH; + DO_NEXT_OP(len); + } - /* Initialize frame, locals. */ - newfp->initCallFrame(cx, *callee, newfun, argc, flags); - SetValueRangeToUndefined(newfp->slots(), newscript->nfixed); + TypeMonitorCall(cx, args, construct); - /* Officially push the frame. */ - stack.pushInlineFrame(newscript, newfp, regs); + InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - /* Refresh interpreter locals. */ - JS_ASSERT(newfp == regs.fp()); - script = newscript; - argv = regs.fp()->formalArgsEnd() - newfun->nargs; - atoms = script->atomMap.vector; + JSScript *newScript = fun->script(); - /* Now that the new frame is rooted, maybe create a call object. */ - if (newfun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp())) - goto error; + if (newScript->compileAndGo && newScript->hasClearedGlobal()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE); + goto error; + } - RESET_USE_METHODJIT(); - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); + if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, newScript, initial)) + goto error; - TRACE_0(EnterFrame); + RESTORE_INTERP_VARS(); - CHECK_INTERRUPT_HANDLER(); + if (!regs.fp()->functionPrologue(cx)) + goto error; -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL) - ? mjit::CompileRequest_Interpreter - : mjit::CompileRequest_JIT; - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp(), request); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) { - interpReturnOK = mjit::JaegerShot(cx); - CHECK_INTERRUPT_HANDLER(); - goto jit_return; - } -#endif + RESET_USE_METHODJIT(); - if (!ScriptPrologue(cx, regs.fp())) - goto error; + bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc); +#ifdef JS_METHODJIT + if (!newType) { + /* Try to ensure methods are method JIT'd. */ + mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL) + ? mjit::CompileRequest_Interpreter + : mjit::CompileRequest_JIT; + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, script->code, + construct, request); + if (status == mjit::Compile_Error) + goto error; + if (status == mjit::Compile_Okay) { + mjit::JaegerStatus status = mjit::JaegerShot(cx, true); + CHECK_PARTIAL_METHODJIT(status); + interpReturnOK = (status == mjit::Jaeger_Returned); CHECK_INTERRUPT_HANDLER(); - - /* Load first op and dispatch it (safe since JSOP_STOP). */ - op = (JSOp) *regs.pc; - DO_OP(); + goto jit_return; } + } +#endif - Probes::enterJSFun(cx, newfun, script); - JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp); - Probes::exitJSFun(cx, newfun, script); - regs.sp = vp + 1; - if (!ok) + if (!ScriptPrologue(cx, regs.fp(), newType)) + goto error; + + if (cx->compartment->debugMode()) { + switch (ScriptDebugPrologue(cx, regs.fp())) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + interpReturnOK = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + case JSTRAP_ERROR: goto error; - TRACE_0(NativeCallComplete); - goto end_call; + default: + JS_NOT_REACHED("bad ScriptDebugPrologue status"); + } } - call_using_invoke: - bool ok; - ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(argc, vp)); - regs.sp = vp + 1; CHECK_INTERRUPT_HANDLER(); - if (!ok) - goto error; - JS_RUNTIME_METER(rt, nonInlineCalls); - TRACE_0(NativeCallComplete); - end_call:; + /* Load first op and dispatch it (safe since JSOP_STOP). */ + op = (JSOp) *regs.pc; + DO_OP(); } -END_CASE(JSOP_CALL) - -} // end block around calling opcodes BEGIN_CASE(JSOP_SETCALL) { @@ -4693,83 +2776,34 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define PUSH_IMPLICIT_THIS(cx, obj, funval) \ - JS_BEGIN_MACRO \ - Value v; \ - if (!ComputeImplicitThis(cx, obj, funval, &v)) \ - goto error; \ - PUSH_COPY(v); \ - JS_END_MACRO \ +BEGIN_CASE(JSOP_IMPLICITTHIS) +{ + PropertyName *name; + LOAD_NAME(0, name); + + JSObject *obj, *obj2; + JSProperty *prop; + if (!FindPropertyHelper(cx, name, false, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + goto error; + + Value v; + if (!ComputeImplicitThis(cx, obj, &v)) + goto error; + PUSH_COPY(v); +} +END_CASE(JSOP_IMPLICITTHIS) BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) BEGIN_CASE(JSOP_NAME) BEGIN_CASE(JSOP_CALLNAME) { - JSObject *obj = ®s.fp()->scopeChain(); - - const Shape *shape; Value rval; - - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom); - if (!atom) { - ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); - if (entry->vword.isFunObj()) { - PUSH_OBJECT(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uintN slot = entry->vword.toSlot(); - PUSH_COPY(obj2->nativeGetSlot(slot)); - } else { - JS_ASSERT(entry->vword.isShape()); - shape = entry->vword.toShape(); - NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval); - PUSH_COPY(rval); - } - - JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_IMPLICIT_THIS(cx, obj, regs.sp[-1]); - len = JSOP_NAME_LENGTH; - DO_NEXT_OP(len); - } - - jsid id; - id = ATOM_TO_JSID(atom); - JSProperty *prop; - if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) + if (!NameOperation(cx, regs.pc, &rval)) goto error; - if (!prop) { - /* Kludge to allow (typeof foo == "undefined") tests. */ - JSOp op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH); - if (op2 == JSOP_TYPEOF) { - PUSH_UNDEFINED(); - len = JSOP_NAME_LENGTH; - DO_NEXT_OP(len); - } - atomNotDefined = atom; - goto atom_not_defined; - } - - /* Take the slow path if prop was not found in a native object. */ - if (!obj->isNative() || !obj2->isNative()) { - if (!obj->getProperty(cx, id, &rval)) - goto error; - } else { - shape = (Shape *)prop; - JSObject *normalized = obj; - if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter()) - normalized = js_UnwrapWithObject(cx, normalized); - NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval); - } PUSH_COPY(rval); - - /* obj must be on the scope chain, thus not a function. */ - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_IMPLICIT_THIS(cx, obj, rval); + TypeScript::Monitor(cx, script, regs.pc, rval); } END_CASE(JSOP_NAME) @@ -4791,7 +2825,7 @@ END_CASE(JSOP_INT32) BEGIN_CASE(JSOP_INDEXBASE) /* - * Here atoms can exceed script->atomMap.length as we use atoms as a + * Here atoms can exceed script->natoms as we use atoms as a * segment register for object literals as well. */ atoms += GET_INDEXBASE(regs.pc); @@ -4805,12 +2839,11 @@ END_CASE(JSOP_INDEXBASE3) BEGIN_CASE(JSOP_RESETBASE0) BEGIN_CASE(JSOP_RESETBASE) - atoms = script->atomMap.vector; + atoms = script->atoms; END_CASE(JSOP_RESETBASE) BEGIN_CASE(JSOP_DOUBLE) { - JS_ASSERT(!regs.fp()->hasImacropc()); double dbl; LOAD_DOUBLE(0, dbl); PUSH_DOUBLE(dbl); @@ -4827,9 +2860,7 @@ END_CASE(JSOP_STRING) BEGIN_CASE(JSOP_OBJECT) { - JSObject *obj; - LOAD_OBJECT(0, obj); - PUSH_OBJECT(*obj); + PUSH_OBJECT(*script->getObject(GET_UINT32_INDEX(regs.pc))); } END_CASE(JSOP_OBJECT) @@ -4837,18 +2868,13 @@ BEGIN_CASE(JSOP_REGEXP) { /* * Push a regexp object cloned from the regexp literal object mapped by the - * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was - * flouted by many browser-based implementations. - * - * We avoid the GetScopeChain call here and pass fp->scopeChain as - * js_GetClassPrototype uses the latter only to locate the global. + * bytecode at pc. */ - jsatomid index = GET_FULL_INDEX(0); - JSObject *proto; - if (!js_GetClassPrototype(cx, ®s.fp()->scopeChain(), JSProto_RegExp, &proto)) + uint32_t index = GET_UINT32_INDEX(regs.pc); + JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx); + if (!proto) goto error; - JS_ASSERT(proto); - JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto); + JSObject *obj = CloneRegExpObject(cx, script->getRegExp(index), proto); if (!obj) goto error; PUSH_OBJECT(*obj); @@ -4914,60 +2940,16 @@ END_VARLEN_CASE } { -BEGIN_CASE(JSOP_TABLESWITCHX) -{ - jsbytecode *pc2 = regs.pc; - len = GET_JUMPX_OFFSET(pc2); - - /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to the - * default case if the discriminant isn't already an int jsval. (This - * opcode is emitted only for dense jsint-domain switches.) - */ - const Value &rref = *--regs.sp; - int32_t i; - if (rref.isInt32()) { - i = rref.toInt32(); - } else if (rref.isDouble() && rref.toDouble() == 0) { - /* Treat -0 (double) as 0. */ - i = 0; - } else { - DO_NEXT_OP(len); - } - - pc2 += JUMPX_OFFSET_LEN; - jsint low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - jsint high = GET_JUMP_OFFSET(pc2); - - i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { - pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; - jsint off = (jsint) GET_JUMPX_OFFSET(pc2); - if (off) - len = off; - } -} -END_VARLEN_CASE -} - -{ -BEGIN_CASE(JSOP_LOOKUPSWITCHX) +BEGIN_CASE(JSOP_LOOKUPSWITCH) { jsint off; - off = JUMPX_OFFSET_LEN; - goto do_lookup_switch; - -BEGIN_CASE(JSOP_LOOKUPSWITCH) off = JUMP_OFFSET_LEN; - do_lookup_switch: /* - * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom - * index in it would exceed 64K limit. + * JSOP_LOOKUPSWITCH are never used if any atom index in it would exceed + * 64K limit. */ - JS_ASSERT(!regs.fp()->hasImacropc()); - JS_ASSERT(atoms == script->atomMap.vector); + JS_ASSERT(atoms == script->atoms); jsbytecode *pc2 = regs.pc; Value lval = regs.sp[-1]; @@ -5020,153 +3002,57 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH) #undef SEARCH_PAIRS end_lookup_switch: - len = (op == JSOP_LOOKUPSWITCH) - ? GET_JUMP_OFFSET(pc2) - : GET_JUMPX_OFFSET(pc2); + len = GET_JUMP_OFFSET(pc2); } END_VARLEN_CASE } -BEGIN_CASE(JSOP_TRAP) -{ - Value rval; - JSTrapStatus status = JS_HandleTrap(cx, script, regs.pc, Jsvalify(&rval)); - switch (status) { - case JSTRAP_ERROR: - goto error; - case JSTRAP_RETURN: - regs.fp()->setReturnValue(rval); - interpReturnOK = JS_TRUE; - goto forced_return; - case JSTRAP_THROW: - cx->setPendingException(rval); - goto error; - default: - break; - } - JS_ASSERT(status == JSTRAP_CONTINUE); - CHECK_INTERRUPT_HANDLER(); - JS_ASSERT(rval.isInt32()); - op = (JSOp) rval.toInt32(); - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - DO_OP(); -} - BEGIN_CASE(JSOP_ARGUMENTS) { Value rval; - if (!js_GetArgsValue(cx, regs.fp(), &rval)) - goto error; + if (cx->typeInferenceEnabled() && !script->strictModeCode) { + if (!script->ensureRanInference(cx)) + goto error; + if (script->createdArgs) { + if (!js_GetArgsValue(cx, regs.fp(), &rval)) + goto error; + } else { + rval = MagicValue(JS_LAZY_ARGUMENTS); + } + } else { + if (!js_GetArgsValue(cx, regs.fp(), &rval)) + goto error; + } PUSH_COPY(rval); } END_CASE(JSOP_ARGUMENTS) -BEGIN_CASE(JSOP_ARGSUB) -{ - jsid id = INT_TO_JSID(GET_ARGNO(regs.pc)); - Value rval; - if (!js_GetArgsProperty(cx, regs.fp(), id, &rval)) - goto error; - PUSH_COPY(rval); -} -END_CASE(JSOP_ARGSUB) - -BEGIN_CASE(JSOP_ARGCNT) -{ - jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom); - Value rval; - if (!js_GetArgsProperty(cx, regs.fp(), id, &rval)) - goto error; - PUSH_COPY(rval); -} -END_CASE(JSOP_ARGCNT) - BEGIN_CASE(JSOP_GETARG) BEGIN_CASE(JSOP_CALLARG) -{ - uint32 slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numFormalArgs()); - METER_SLOT_OP(op, slot); - PUSH_COPY(argv[slot]); - if (op == JSOP_CALLARG) - PUSH_UNDEFINED(); -} + PUSH_COPY(regs.fp()->formalArg(GET_ARGNO(regs.pc))); END_CASE(JSOP_GETARG) BEGIN_CASE(JSOP_SETARG) -{ - uint32 slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numFormalArgs()); - METER_SLOT_OP(op, slot); - argv[slot] = regs.sp[-1]; -} -END_SET_CASE(JSOP_SETARG) + regs.fp()->formalArg(GET_ARGNO(regs.pc)) = regs.sp[-1]; +END_CASE(JSOP_SETARG) BEGIN_CASE(JSOP_GETLOCAL) -{ - uint32 slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < script->nslots); - PUSH_COPY(regs.fp()->slots()[slot]); -} -END_CASE(JSOP_GETLOCAL) - BEGIN_CASE(JSOP_CALLLOCAL) -{ - uint32 slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < script->nslots); - PUSH_COPY(regs.fp()->slots()[slot]); - PUSH_UNDEFINED(); -} -END_CASE(JSOP_CALLLOCAL) - -BEGIN_CASE(JSOP_SETLOCAL) -{ - uint32 slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < script->nslots); - regs.fp()->slots()[slot] = regs.sp[-1]; -} -END_SET_CASE(JSOP_SETLOCAL) - -BEGIN_CASE(JSOP_GETUPVAR_DBG) -BEGIN_CASE(JSOP_CALLUPVAR_DBG) -{ - JSFunction *fun = regs.fp()->fun(); - JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED); - JS_ASSERT(fun->u.i.wrapper); - - /* Scope for tempPool mark and local names allocation in it. */ - JSObject *obj, *obj2; - JSProperty *prop; - jsid id; - JSAtom *atom; - { - AutoLocalNameArray names(cx, fun); - if (!names) - goto error; - - uintN index = fun->script()->bindings.countArgsAndVars() + GET_UINT16(regs.pc); - atom = JS_LOCAL_NAME_TO_ATOM(names[index]); - id = ATOM_TO_JSID(atom); - - if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) - goto error; - } - - if (!prop) { - atomNotDefined = atom; - goto atom_not_defined; - } + PUSH_COPY_SKIP_CHECK(regs.fp()->localSlot(GET_SLOTNO(regs.pc))); - /* Minimize footprint with generic code instead of NATIVE_GET. */ - Value *vp = regs.sp; - PUSH_NULL(); - if (!obj->getProperty(cx, id, vp)) - goto error; + /* + * Skip the same-compartment assertion if the local will be immediately + * popped. We do not guarantee sync for dead locals when coming in from the + * method JIT, and a GETLOCAL followed by POP is not considered to be + * a use of the variable. + */ + if (regs.pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP) + assertSameCompartment(cx, regs.sp[-1]); +END_CASE(JSOP_GETLOCAL) - if (op == JSOP_CALLUPVAR_DBG) - PUSH_UNDEFINED(); -} -END_CASE(JSOP_GETUPVAR_DBG) +BEGIN_CASE(JSOP_SETLOCAL) + regs.fp()->localSlot(GET_SLOTNO(regs.pc)) = regs.sp[-1]; +END_CASE(JSOP_SETLOCAL) BEGIN_CASE(JSOP_GETFCSLOT) BEGIN_CASE(JSOP_CALLFCSLOT) @@ -5175,70 +3061,28 @@ BEGIN_CASE(JSOP_CALLFCSLOT) uintN index = GET_UINT16(regs.pc); JSObject *obj = &argv[-2].toObject(); - JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars()); - PUSH_COPY(obj->getFlatClosureUpvar(index)); - if (op == JSOP_CALLFCSLOT) - PUSH_UNDEFINED(); + PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index)); + TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); } END_CASE(JSOP_GETFCSLOT) -BEGIN_CASE(JSOP_GETGLOBAL) -BEGIN_CASE(JSOP_CALLGLOBAL) -{ - uint32 slot = GET_SLOTNO(regs.pc); - slot = script->getGlobalSlot(slot); - JSObject *obj = regs.fp()->scopeChain().getGlobal(); - JS_ASSERT(obj->containsSlot(slot)); - PUSH_COPY(obj->getSlot(slot)); - if (op == JSOP_CALLGLOBAL) - PUSH_UNDEFINED(); -} -END_CASE(JSOP_GETGLOBAL) - BEGIN_CASE(JSOP_DEFCONST) BEGIN_CASE(JSOP_DEFVAR) { - uint32 index = GET_INDEX(regs.pc); - JSAtom *atom = atoms[index]; + PropertyName *dn = atoms[GET_INDEX(regs.pc)]->asPropertyName(); - JSObject *obj = &cx->stack.currentVarObj(); - JS_ASSERT(!obj->getOps()->defineProperty); + /* ES5 10.5 step 8 (with subsequent errata). */ uintN attrs = JSPROP_ENUMERATE; if (!regs.fp()->isEvalFrame()) attrs |= JSPROP_PERMANENT; - - /* Lookup id in order to check for redeclaration problems. */ - jsid id = ATOM_TO_JSID(atom); - bool shouldDefine; - if (op == JSOP_DEFVAR) { - /* - * Redundant declaration of a |var|, even one for a non-writable - * property like |undefined| in ES5, does nothing. - */ - JSProperty *prop; - JSObject *obj2; - if (!obj->lookupProperty(cx, id, &obj2, &prop)) - goto error; - shouldDefine = (!prop || obj2 != obj); - } else { - JS_ASSERT(op == JSOP_DEFCONST); + if (op == JSOP_DEFCONST) attrs |= JSPROP_READONLY; - if (!CheckRedeclaration(cx, obj, id, attrs)) - goto error; - /* - * As attrs includes readonly, CheckRedeclaration can succeed only - * if prop does not exist. - */ - shouldDefine = true; - } + /* Step 8b. */ + JSObject &obj = regs.fp()->varObj(); - /* Bind a variable only if it's not yet defined. */ - if (shouldDefine && - !js_DefineNativeProperty(cx, obj, id, UndefinedValue(), - PropertyStub, StrictPropertyStub, attrs, 0, 0, NULL)) { + if (!DefVarOrConstOperation(cx, obj, dn, attrs)) goto error; - } } END_CASE(JSOP_DEFVAR) @@ -5250,12 +3094,11 @@ BEGIN_CASE(JSOP_DEFFUN) * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - JSFunction *fun; - LOAD_FUNCTION(0); - JSObject *obj = FUN_OBJECT(fun); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); + JSObject *obj = fun; JSObject *obj2; - if (FUN_NULL_CLOSURE(fun)) { + if (fun->isNullClosure()) { /* * Even a null closure needs a parent for principals finding. * FIXME: bug 476950, although debugger users may also demand some kind @@ -5265,7 +3108,7 @@ BEGIN_CASE(JSOP_DEFFUN) } else { JS_ASSERT(!fun->isFlatClosure()); - obj2 = GetScopeChainFast(cx, regs.fp(), JSOP_DEFFUN, JSOP_DEFFUN_LENGTH); + obj2 = GetScopeChain(cx, regs.fp()); if (!obj2) goto error; } @@ -5279,10 +3122,11 @@ BEGIN_CASE(JSOP_DEFFUN) * windows, and user-defined JS functions precompiled and then shared among * requests in server-side JS. */ - if (obj->getParent() != obj2) { - obj = CloneFunctionObject(cx, fun, obj2); + if (obj->toFunction()->environment() != obj2) { + obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2); if (!obj) goto error; + JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto()); } /* @@ -5298,13 +3142,13 @@ BEGIN_CASE(JSOP_DEFFUN) * current scope chain even for the case of function expression statements * and functions defined by eval inside let or with blocks. */ - JSObject *parent = &cx->stack.currentVarObj(); + JSObject *parent = ®s.fp()->varObj(); /* ES5 10.5 (NB: with subsequent errata). */ - jsid id = ATOM_TO_JSID(fun->atom); + PropertyName *name = fun->atom->asPropertyName(); JSProperty *prop = NULL; JSObject *pobj; - if (!parent->lookupProperty(cx, id, &pobj, &prop)) + if (!parent->lookupProperty(cx, name, &pobj, &prop)) goto error; Value rval = ObjectValue(*obj); @@ -5312,8 +3156,11 @@ BEGIN_CASE(JSOP_DEFFUN) do { /* Steps 5d, 5f. */ if (!prop || pobj != parent) { - if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) + if (!parent->defineProperty(cx, name, rval, + JS_PropertyStub, JS_StrictPropertyStub, attrs)) + { goto error; + } break; } @@ -5322,16 +3169,19 @@ BEGIN_CASE(JSOP_DEFFUN) Shape *shape = reinterpret_cast(prop); if (parent->isGlobal()) { if (shape->configurable()) { - if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) + if (!parent->defineProperty(cx, name, rval, + JS_PropertyStub, JS_StrictPropertyStub, attrs)) + { goto error; + } break; } if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) { JSAutoByteString bytes; - if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) { + if (js_AtomToPrintableString(cx, name, &bytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_REDEFINE_PROP, name); + JSMSG_CANT_REDEFINE_PROP, bytes.ptr()); } goto error; } @@ -5345,44 +3195,12 @@ BEGIN_CASE(JSOP_DEFFUN) */ /* Step 5f. */ - if (!parent->setProperty(cx, id, &rval, script->strictModeCode)) + if (!parent->setProperty(cx, name, &rval, script->strictModeCode)) goto error; } while (false); } END_CASE(JSOP_DEFFUN) -BEGIN_CASE(JSOP_DEFFUN_FC) -BEGIN_CASE(JSOP_DEFFUN_DBGFC) -{ - JSFunction *fun; - LOAD_FUNCTION(0); - - JSObject *obj = (op == JSOP_DEFFUN_FC) - ? js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH) - : js_NewDebuggableFlatClosure(cx, fun); - if (!obj) - goto error; - - Value rval = ObjectValue(*obj); - - uintN attrs = regs.fp()->isEvalFrame() - ? JSPROP_ENUMERATE - : JSPROP_ENUMERATE | JSPROP_PERMANENT; - - JSObject &parent = cx->stack.currentVarObj(); - - jsid id = ATOM_TO_JSID(fun->atom); - if (!CheckRedeclaration(cx, &parent, id, attrs)) - goto error; - - if ((attrs == JSPROP_ENUMERATE) - ? !parent.setProperty(cx, id, &rval, script->strictModeCode) - : !parent.defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) { - goto error; - } -} -END_CASE(JSOP_DEFFUN_FC) - BEGIN_CASE(JSOP_DEFLOCALFUN) { /* @@ -5392,106 +3210,69 @@ BEGIN_CASE(JSOP_DEFLOCALFUN) * JSOP_DEFFUN that avoids requiring a call object for the outer function's * activation. */ - JSFunction *fun; - LOAD_FUNCTION(SLOTNO_LEN); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN)); JS_ASSERT(fun->isInterpreted()); - JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); - JSObject *obj = FUN_OBJECT(fun); - - if (FUN_NULL_CLOSURE(fun)) { - obj = CloneFunctionObject(cx, fun, ®s.fp()->scopeChain()); - if (!obj) - goto error; - } else { - JSObject *parent = GetScopeChainFast(cx, regs.fp(), JSOP_DEFLOCALFUN, - JSOP_DEFLOCALFUN_LENGTH); - if (!parent) - goto error; + JS_ASSERT(!fun->isFlatClosure()); - if (obj->getParent() != parent) { -#ifdef JS_TRACER - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "DEFLOCALFUN for closure"); -#endif - obj = CloneFunctionObject(cx, fun, parent); - if (!obj) - goto error; - } + JSObject *parent; + if (fun->isNullClosure()) { + parent = ®s.fp()->scopeChain(); + } else { + parent = GetScopeChain(cx, regs.fp()); + if (!parent) + goto error; } + JSObject *obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent); + if (!obj) + goto error; - uint32 slot = GET_SLOTNO(regs.pc); - TRACE_2(DefLocalFunSetSlot, slot, obj); + JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto()); - regs.fp()->slots()[slot].setObject(*obj); + regs.fp()->varSlot(GET_SLOTNO(regs.pc)) = ObjectValue(*obj); } END_CASE(JSOP_DEFLOCALFUN) BEGIN_CASE(JSOP_DEFLOCALFUN_FC) { - JSFunction *fun; - LOAD_FUNCTION(SLOTNO_LEN); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN)); - JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) goto error; - uint32 slot = GET_SLOTNO(regs.pc); - TRACE_2(DefLocalFunSetSlot, slot, obj); - - regs.fp()->slots()[slot].setObject(*obj); + regs.fp()->varSlot(GET_SLOTNO(regs.pc)) = ObjectValue(*obj); } END_CASE(JSOP_DEFLOCALFUN_FC) -BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC) -{ - JSFunction *fun; - LOAD_FUNCTION(SLOTNO_LEN); - - JSObject *obj = js_NewDebuggableFlatClosure(cx, fun); - if (!obj) - goto error; - - uint32 slot = GET_SLOTNO(regs.pc); - regs.fp()->slots()[slot].setObject(*obj); -} -END_CASE(JSOP_DEFLOCALFUN_DBGFC) - BEGIN_CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ - JSFunction *fun; - LOAD_FUNCTION(0); - JSObject *obj = FUN_OBJECT(fun); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); + JSObject *obj = fun; /* do-while(0) so we can break instead of using a goto. */ do { JSObject *parent; - if (FUN_NULL_CLOSURE(fun)) { + if (fun->isNullClosure()) { parent = ®s.fp()->scopeChain(); - if (obj->getParent() == parent) { - jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH); + if (fun->joinable()) { + jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH; JSOp op2 = JSOp(*pc2); /* * Optimize var obj = {method: function () { ... }, ...}, * this.method = function () { ... }; and other significant * single-use-of-null-closure bytecode sequences. - * - * WARNING: code in TraceRecorder::record_JSOP_LAMBDA must - * match the optimization cases in the following code that - * break from the outer do-while(0). */ if (op2 == JSOP_INITMETHOD) { #ifdef DEBUG const Value &lref = regs.sp[-1]; JS_ASSERT(lref.isObject()); JSObject *obj2 = &lref.toObject(); - JS_ASSERT(obj2->getClass() == &js_ObjectClass); + JS_ASSERT(obj2->isObject()); #endif - - fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); - JS_FUNCTION_METER(cx, joinedinitmethod); + JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH))); break; } @@ -5502,105 +3283,74 @@ BEGIN_CASE(JSOP_LAMBDA) #endif const Value &lref = regs.sp[-1]; if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { - fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); - JS_FUNCTION_METER(cx, joinedsetmethod); + JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH))); break; } - } else if (fun->joinable()) { - if (op2 == JSOP_CALL) { - /* - * Array.prototype.sort and String.prototype.replace are - * optimized as if they are special form. We know that they - * won't leak the joined function object in obj, therefore - * we don't need to clone that compiler- created function - * object for identity/mutation reasons. - */ - int iargc = GET_ARGC(pc2); - - /* - * Note that we have not yet pushed obj as the final argument, - * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], - * is the callee for this JSOP_CALL. - */ - const Value &cref = regs.sp[1 - (iargc + 2)]; - JSObject *callee; - - if (IsFunctionObject(cref, &callee)) { - JSFunction *calleeFun = GET_FUNCTION_PRIVATE(cx, callee); - if (Native native = calleeFun->maybeNative()) { - if (iargc == 1 && native == array_sort) { - JS_FUNCTION_METER(cx, joinedsort); - break; - } - if (iargc == 2 && native == str_replace) { - JS_FUNCTION_METER(cx, joinedreplace); - break; - } - } - } - } else if (op2 == JSOP_NULL) { - pc2 += JSOP_NULL_LENGTH; - op2 = JSOp(*pc2); + } else if (op2 == JSOP_CALL) { + /* + * Array.prototype.sort and String.prototype.replace are + * optimized as if they are special form. We know that they + * won't leak the joined function object in obj, therefore + * we don't need to clone that compiler-created function + * object for identity/mutation reasons. + */ + int iargc = GET_ARGC(pc2); - if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) { - JS_FUNCTION_METER(cx, joinedmodulepat); - break; + /* + * Note that we have not yet pushed obj as the final argument, + * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], + * is the callee for this JSOP_CALL. + */ + const Value &cref = regs.sp[1 - (iargc + 2)]; + JSFunction *fun; + + if (IsFunctionObject(cref, &fun)) { + if (Native native = fun->maybeNative()) { + if ((iargc == 1 && native == array_sort) || + (iargc == 2 && native == str_replace)) { + break; + } } } - } - } - -#ifdef DEBUG - if (rt->functionMeterFilename) { - // No locking, this is mainly for js shell testing. - ++rt->functionMeter.unjoined; + } else if (op2 == JSOP_NULL) { + pc2 += JSOP_NULL_LENGTH; + op2 = JSOp(*pc2); - typedef JSRuntime::FunctionCountMap::Ptr Ptr; - if (Ptr p = rt->unjoinedFunctionCountMap.lookupWithDefault(fun, 0)) - ++p->value; + if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) + break; + } } -#endif } else { - parent = GetScopeChainFast(cx, regs.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH); + parent = GetScopeChain(cx, regs.fp()); if (!parent) goto error; } - obj = CloneFunctionObject(cx, fun, parent); + obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent); if (!obj) goto error; } while (0); + JS_ASSERT(obj->getProto()); + JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto()); + PUSH_OBJECT(*obj); } END_CASE(JSOP_LAMBDA) BEGIN_CASE(JSOP_LAMBDA_FC) { - JSFunction *fun; - LOAD_FUNCTION(0); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); - JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) goto error; + JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto()); PUSH_OBJECT(*obj); } END_CASE(JSOP_LAMBDA_FC) -BEGIN_CASE(JSOP_LAMBDA_DBGFC) -{ - JSFunction *fun; - LOAD_FUNCTION(0); - - JSObject *obj = js_NewDebuggableFlatClosure(cx, fun); - if (!obj) - goto error; - - PUSH_OBJECT(*obj); -} -END_CASE(JSOP_LAMBDA_DBGFC) - BEGIN_CASE(JSOP_CALLEE) JS_ASSERT(regs.fp()->isNonEvalFunctionFrame()); PUSH_COPY(argv[-2]); @@ -5610,7 +3360,7 @@ BEGIN_CASE(JSOP_GETTER) BEGIN_CASE(JSOP_SETTER) { do_getter_setter: - JSOp op2 = (JSOp) *++regs.pc; + JSOp op2 = JSOp(*++regs.pc); jsid id; Value rval; jsint i; @@ -5629,9 +3379,9 @@ BEGIN_CASE(JSOP_SETTER) case JSOP_SETNAME: case JSOP_SETPROP: { - JSAtom *atom; - LOAD_ATOM(0, atom); - id = ATOM_TO_JSID(atom); + PropertyName *name; + LOAD_NAME(0, name); + id = ATOM_TO_JSID(name); rval = regs.sp[-1]; i = -1; goto gs_pop_lval; @@ -5649,9 +3399,9 @@ BEGIN_CASE(JSOP_SETTER) JS_ASSERT(regs.sp - regs.fp()->base() >= 2); rval = regs.sp[-1]; i = -1; - JSAtom *atom; - LOAD_ATOM(0, atom); - id = ATOM_TO_JSID(atom); + PropertyName *name; + LOAD_NAME(0, name); + id = ATOM_TO_JSID(name); goto gs_get_lval; } default: @@ -5675,11 +3425,8 @@ BEGIN_CASE(JSOP_SETTER) FETCH_ELEMENT_ID(obj, i, id); if (!js_IsCallable(rval)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - (op == JSOP_GETTER) - ? js_getter_str - : js_setter_str); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, + (op == JSOP_GETTER) ? js_getter_str : js_setter_str); goto error; } @@ -5696,20 +3443,16 @@ BEGIN_CASE(JSOP_SETTER) StrictPropertyOp setter; if (op == JSOP_GETTER) { getter = CastAsPropertyOp(&rval.toObject()); - setter = StrictPropertyStub; + setter = JS_StrictPropertyStub; attrs = JSPROP_GETTER; } else { - getter = PropertyStub; + getter = JS_PropertyStub; setter = CastAsStrictPropertyOp(&rval.toObject()); attrs = JSPROP_SETTER; } attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; - /* Check for a readonly or permanent property of the same name. */ - if (!CheckRedeclaration(cx, obj, id, attrs)) - goto error; - - if (!obj->defineProperty(cx, id, UndefinedValue(), getter, setter, attrs)) + if (!obj->defineGeneric(cx, id, UndefinedValue(), getter, setter, attrs)) goto error; regs.sp += i; @@ -5728,21 +3471,24 @@ END_CASE(JSOP_HOLE) BEGIN_CASE(JSOP_NEWINIT) { - jsint i = regs.pc[1]; - + uint8_t i = GET_UINT8(regs.pc); JS_ASSERT(i == JSProto_Array || i == JSProto_Object); - JSObject *obj; + JSObject *obj; if (i == JSProto_Array) { obj = NewDenseEmptyArray(cx); } else { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(0); + obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); } - if (!obj) goto error; + TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i); + if (!type) + goto error; + obj->setType(type); + PUSH_OBJECT(*obj); CHECK_INTERRUPT_HANDLER(); } @@ -5755,6 +3501,11 @@ BEGIN_CASE(JSOP_NEWARRAY) if (!obj) goto error; + TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Array); + if (!type) + goto error; + obj->setType(type); + PUSH_OBJECT(*obj); CHECK_INTERRUPT_HANDLER(); } @@ -5762,11 +3513,13 @@ END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWOBJECT) { - JSObject *baseobj; - LOAD_OBJECT(0, baseobj); + JSObject *baseobj = script->getObject(GET_UINT32_INDEX(regs.pc)); - JSObject *obj = CopyInitializerObject(cx, baseobj); + TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Object); + if (!type) + goto error; + JSObject *obj = CopyInitializerObject(cx, baseobj, type); if (!obj) goto error; @@ -5794,65 +3547,18 @@ BEGIN_CASE(JSOP_INITMETHOD) JSObject *obj = ®s.sp[-2].toObject(); JS_ASSERT(obj->isObject()); - /* - * Probe the property cache. - * - * On a hit, if the cached shape has a non-default setter, it must be - * __proto__. If shape->previous() != obj->lastProperty(), there must be a - * repeated property name. The fast path does not handle these two cases. - */ - PropertyCacheEntry *entry; - const Shape *shape; - if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) && - shape->hasDefaultSetter() && - shape->previous() == obj->lastProperty()) - { - /* Fast path. Property cache hit. */ - uint32 slot = shape->slot; - - JS_ASSERT(slot == obj->slotSpan()); - JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass())); - if (slot < obj->numSlots()) { - JS_ASSERT(obj->getSlot(slot).isUndefined()); - } else { - if (!obj->allocSlot(cx, &slot)) - goto error; - JS_ASSERT(slot == shape->slot); - } - - /* A new object, or one we just extended in a recent initprop op. */ - JS_ASSERT(!obj->lastProperty() || - obj->shape() == obj->lastProperty()->shape); - obj->extend(cx, shape); + JSAtom *atom; + LOAD_ATOM(0, atom); + jsid id = ATOM_TO_JSID(atom); - /* - * No method change check here because here we are adding a new - * property, not updating an existing slot's value that might - * contain a method of a branded shape. - */ - TRACE_1(AddProperty, obj); - obj->nativeSetSlot(slot, rval); - } else { - PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++); - - /* Get the immediate property name into id. */ - JSAtom *atom; - LOAD_ATOM(0, atom); - jsid id = ATOM_TO_JSID(atom); - - uintN defineHow = (op == JSOP_INITMETHOD) - ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD - : JSDNP_CACHE_RESULT; - if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) - ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode) - : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, - JSPROP_ENUMERATE, 0, 0, NULL, - defineHow))) { - goto error; - } + uintN defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0; + if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) + ? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode) + : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL, + JSPROP_ENUMERATE, 0, 0, defineHow)) { + goto error; } - /* Common tail for property cache hit and miss cases. */ regs.sp--; } END_CASE(JSOP_INITPROP); @@ -5880,119 +3586,24 @@ BEGIN_CASE(JSOP_INITELEM) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX); - if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT && + JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); + if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) { goto error; } } else { - if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) + if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) goto error; } regs.sp -= 2; } END_CASE(JSOP_INITELEM) -#if JS_HAS_SHARP_VARS - -BEGIN_CASE(JSOP_DEFSHARP) -{ - uint32 slot = GET_UINT16(regs.pc); - JS_ASSERT(slot + 1 < regs.fp()->numFixed()); - const Value &lref = regs.fp()->slots()[slot]; - JSObject *obj; - if (lref.isObject()) { - obj = &lref.toObject(); - } else { - JS_ASSERT(lref.isUndefined()); - obj = NewDenseEmptyArray(cx); - if (!obj) - goto error; - regs.fp()->slots()[slot].setObject(*obj); - } - jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN); - jsid id = INT_TO_JSID(i); - const Value &rref = regs.sp[-1]; - if (rref.isPrimitive()) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_DEF, numBuf); - goto error; - } - if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) - goto error; -} -END_CASE(JSOP_DEFSHARP) - -BEGIN_CASE(JSOP_USESHARP) -{ - uint32 slot = GET_UINT16(regs.pc); - JS_ASSERT(slot + 1 < regs.fp()->numFixed()); - const Value &lref = regs.fp()->slots()[slot]; - jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN); - Value rval; - if (lref.isUndefined()) { - rval.setUndefined(); - } else { - JSObject *obj = ®s.fp()->slots()[slot].toObject(); - jsid id = INT_TO_JSID(i); - if (!obj->getProperty(cx, id, &rval)) - goto error; - } - if (!rval.isObjectOrNull()) { - char numBuf[12]; - - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_USE, numBuf); - goto error; - } - PUSH_COPY(rval); -} -END_CASE(JSOP_USESHARP) - -BEGIN_CASE(JSOP_SHARPINIT) -{ - uint32 slot = GET_UINT16(regs.pc); - JS_ASSERT(slot + 1 < regs.fp()->numFixed()); - Value *vp = ®s.fp()->slots()[slot]; - Value rval = vp[1]; - - /* - * We peek ahead safely here because empty initialisers get zero - * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes - * immediately after JSOP_NEWINIT followed by one or more property - * initialisers; and the second comes directly before JSOP_ENDINIT. - */ - if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) { - rval.setInt32(rval.isUndefined() ? 1 : rval.toInt32() + 1); - } else { - JS_ASSERT(rval.isInt32()); - rval.getInt32Ref() -= 1; - if (rval.toInt32() == 0) - vp[0].setUndefined(); - } - vp[1] = rval; -} -END_CASE(JSOP_SHARPINIT) - -#endif /* JS_HAS_SHARP_VARS */ - { BEGIN_CASE(JSOP_GOSUB) PUSH_BOOLEAN(false); - jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH; - PUSH_INT32(i); + jsint i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH; len = GET_JUMP_OFFSET(regs.pc); -END_VARLEN_CASE -} - -{ -BEGIN_CASE(JSOP_GOSUBX) - PUSH_BOOLEAN(false); - jsint i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH; - len = GET_JUMPX_OFFSET(regs.pc); PUSH_INT32(i); END_VARLEN_CASE } @@ -6016,19 +3627,13 @@ BEGIN_CASE(JSOP_RETSUB) } JS_ASSERT(rval.isInt32()); len = rval.toInt32(); - regs.pc = script->main; + regs.pc = script->code; END_VARLEN_CASE } BEGIN_CASE(JSOP_EXCEPTION) PUSH_COPY(cx->getPendingException()); cx->clearPendingException(); -#if defined(JS_TRACER) && defined(JS_METHODJIT) - if (interpMode == JSINTERP_PROFILE) { - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); - } -#endif CHECK_BRANCH(); END_CASE(JSOP_EXCEPTION) @@ -6056,47 +3661,14 @@ BEGIN_CASE(JSOP_THROW) goto error; } BEGIN_CASE(JSOP_SETLOCALPOP) -{ /* * The stack must have a block with at least one local slot below the * exception object. */ JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) >= 2); - uint32 slot = GET_UINT16(regs.pc); - JS_ASSERT(slot + 1 < script->nslots); - POP_COPY_TO(regs.fp()->slots()[slot]); -} + POP_COPY_TO(regs.fp()->localSlot(GET_UINT16(regs.pc))); END_CASE(JSOP_SETLOCALPOP) -BEGIN_CASE(JSOP_IFPRIMTOP) - /* - * If the top of stack is of primitive type, jump to our target. Otherwise - * advance to the next opcode. - */ - JS_ASSERT(regs.sp > regs.fp()->base()); - if (regs.sp[-1].isPrimitive()) { - len = GET_JUMP_OFFSET(regs.pc); - BRANCH(len); - } -END_CASE(JSOP_IFPRIMTOP) - -BEGIN_CASE(JSOP_PRIMTOP) - JS_ASSERT(regs.sp > regs.fp()->base()); - if (regs.sp[-1].isObject()) { - jsint i = GET_INT8(regs.pc); - js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, -2, regs.sp[-2], NULL, - (i == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(i)); - goto error; - } -END_CASE(JSOP_PRIMTOP) - -BEGIN_CASE(JSOP_OBJTOP) - if (regs.sp[-1].isPrimitive()) { - js_ReportValueError(cx, GET_UINT16(regs.pc), -1, regs.sp[-1], NULL); - goto error; - } -END_CASE(JSOP_OBJTOP) - BEGIN_CASE(JSOP_INSTANCEOF) { const Value &rref = regs.sp[-1]; @@ -6116,31 +3688,35 @@ END_CASE(JSOP_INSTANCEOF) BEGIN_CASE(JSOP_DEBUGGER) { - JSDebuggerHandler handler = cx->debugHooks->debuggerHandler; - if (handler) { - Value rval; - switch (handler(cx, script, regs.pc, Jsvalify(&rval), cx->debugHooks->debuggerHandlerData)) { - case JSTRAP_ERROR: - goto error; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - regs.fp()->setReturnValue(rval); - interpReturnOK = JS_TRUE; - goto forced_return; - case JSTRAP_THROW: - cx->setPendingException(rval); - goto error; - default:; - } - CHECK_INTERRUPT_HANDLER(); + JSTrapStatus st = JSTRAP_CONTINUE; + Value rval; + if (JSDebuggerHandler handler = cx->debugHooks->debuggerHandler) + st = handler(cx, script, regs.pc, &rval, cx->debugHooks->debuggerHandlerData); + if (st == JSTRAP_CONTINUE) + st = Debugger::onDebuggerStatement(cx, &rval); + switch (st) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + regs.fp()->setReturnValue(rval); + interpReturnOK = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->setPendingException(rval); + goto error; + default:; } + CHECK_INTERRUPT_HANDLER(); } END_CASE(JSOP_DEBUGGER) #if JS_HAS_XML_SUPPORT BEGIN_CASE(JSOP_DEFXMLNS) { + JS_ASSERT(!script->strictModeCode); + if (!js_SetDefaultXMLNamespace(cx, regs.sp[-1])) goto error; regs.sp--; @@ -6149,6 +3725,8 @@ END_CASE(JSOP_DEFXMLNS) BEGIN_CASE(JSOP_ANYNAME) { + JS_ASSERT(!script->strictModeCode); + jsid id; if (!js_GetAnyName(cx, &id)) goto error; @@ -6158,6 +3736,12 @@ END_CASE(JSOP_ANYNAME) BEGIN_CASE(JSOP_QNAMEPART) { + /* + * We do not JS_ASSERT(!script->strictModeCode) here because JSOP_QNAMEPART + * is used for __proto__ and (in contexts where we favor JSOP_*ELEM instead + * of JSOP_*PROP) obj.prop compiled as obj['prop']. + */ + JSAtom *atom; LOAD_ATOM(0, atom); PUSH_STRING(atom); @@ -6166,6 +3750,8 @@ END_CASE(JSOP_QNAMEPART) BEGIN_CASE(JSOP_QNAMECONST) { + JS_ASSERT(!script->strictModeCode); + JSAtom *atom; LOAD_ATOM(0, atom); Value rval = StringValue(atom); @@ -6179,6 +3765,8 @@ END_CASE(JSOP_QNAMECONST) BEGIN_CASE(JSOP_QNAME) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; Value lval = regs.sp[-2]; JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval); @@ -6191,6 +3779,8 @@ END_CASE(JSOP_QNAME) BEGIN_CASE(JSOP_TOATTRNAME) { + JS_ASSERT(!script->strictModeCode); + Value rval; rval = regs.sp[-1]; if (!js_ToAttributeName(cx, &rval)) @@ -6201,6 +3791,8 @@ END_CASE(JSOP_TOATTRNAME) BEGIN_CASE(JSOP_TOATTRVAL) { + JS_ASSERT(!script->strictModeCode); + Value rval; rval = regs.sp[-1]; JS_ASSERT(rval.isString()); @@ -6214,6 +3806,8 @@ END_CASE(JSOP_TOATTRVAL) BEGIN_CASE(JSOP_ADDATTRNAME) BEGIN_CASE(JSOP_ADDATTRVAL) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; Value lval = regs.sp[-2]; JSString *str = lval.toString(); @@ -6228,6 +3822,8 @@ END_CASE(JSOP_ADDATTRNAME) BEGIN_CASE(JSOP_BINDXMLNAME) { + JS_ASSERT(!script->strictModeCode); + Value lval; lval = regs.sp[-1]; JSObject *obj; @@ -6241,11 +3837,13 @@ END_CASE(JSOP_BINDXMLNAME) BEGIN_CASE(JSOP_SETXMLNAME) { + JS_ASSERT(!script->strictModeCode); + JSObject *obj = ®s.sp[-3].toObject(); Value rval = regs.sp[-1]; jsid id; FETCH_ELEMENT_ID(obj, -2, id); - if (!obj->setProperty(cx, id, &rval, script->strictModeCode)) + if (!obj->setGeneric(cx, id, &rval, script->strictModeCode)) goto error; rval = regs.sp[-1]; regs.sp -= 2; @@ -6256,43 +3854,53 @@ END_CASE(JSOP_SETXMLNAME) BEGIN_CASE(JSOP_CALLXMLNAME) BEGIN_CASE(JSOP_XMLNAME) { + JS_ASSERT(!script->strictModeCode); + Value lval = regs.sp[-1]; JSObject *obj; jsid id; if (!js_FindXMLProperty(cx, lval, &obj, &id)) goto error; Value rval; - if (!obj->getProperty(cx, id, &rval)) + if (!obj->getGeneric(cx, id, &rval)) goto error; regs.sp[-1] = rval; - if (op == JSOP_CALLXMLNAME) - PUSH_IMPLICIT_THIS(cx, obj, rval); + if (op == JSOP_CALLXMLNAME) { + Value v; + if (!ComputeImplicitThis(cx, obj, &v)) + goto error; + PUSH_COPY(v); + } } END_CASE(JSOP_XMLNAME) BEGIN_CASE(JSOP_DESCENDANTS) BEGIN_CASE(JSOP_DELDESC) { + JS_ASSERT(!script->strictModeCode); + JSObject *obj; FETCH_OBJECT(cx, -2, obj); - jsval rval = Jsvalify(regs.sp[-1]); + jsval rval = regs.sp[-1]; if (!js_GetXMLDescendants(cx, obj, rval, &rval)) goto error; if (op == JSOP_DELDESC) { - regs.sp[-1] = Valueify(rval); /* set local root */ + regs.sp[-1] = rval; /* set local root */ if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval))) goto error; rval = JSVAL_TRUE; /* always succeed */ } regs.sp--; - regs.sp[-1] = Valueify(rval); + regs.sp[-1] = rval; } END_CASE(JSOP_DESCENDANTS) -{ BEGIN_CASE(JSOP_FILTER) +{ + JS_ASSERT(!script->strictModeCode); + /* * We push the hole value before jumping to [enditer] so we can detect the * first iteration and direct js_StepXMLListFilter to initialize filter's @@ -6301,15 +3909,17 @@ BEGIN_CASE(JSOP_FILTER) PUSH_HOLE(); len = GET_JUMP_OFFSET(regs.pc); JS_ASSERT(len > 0); -END_VARLEN_CASE } +END_VARLEN_CASE BEGIN_CASE(JSOP_ENDFILTER) { + JS_ASSERT(!script->strictModeCode); + bool cond = !regs.sp[-1].isMagic(); if (cond) { /* Exit the "with" block left from the previous iteration. */ - js_LeaveWith(cx); + LeaveWith(cx); } if (!js_StepXMLListFilter(cx, cond)) goto error; @@ -6319,7 +3929,7 @@ BEGIN_CASE(JSOP_ENDFILTER) * temporaries. */ JS_ASSERT(IsXML(regs.sp[-1])); - if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH)) + if (!EnterWith(cx, -2)) goto error; regs.sp--; len = GET_JUMP_OFFSET(regs.pc); @@ -6332,6 +3942,8 @@ END_CASE(JSOP_ENDFILTER); BEGIN_CASE(JSOP_TOXML) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; JSObject *obj = js_ValueToXMLObject(cx, rval); if (!obj) @@ -6342,6 +3954,8 @@ END_CASE(JSOP_TOXML) BEGIN_CASE(JSOP_TOXMLLIST) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; JSObject *obj = js_ValueToXMLListObject(cx, rval); if (!obj) @@ -6352,8 +3966,10 @@ END_CASE(JSOP_TOXMLLIST) BEGIN_CASE(JSOP_XMLTAGEXPR) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; - JSString *str = js_ValueToString(cx, rval); + JSString *str = ToString(cx, rval); if (!str) goto error; regs.sp[-1].setString(str); @@ -6362,12 +3978,14 @@ END_CASE(JSOP_XMLTAGEXPR) BEGIN_CASE(JSOP_XMLELTEXPR) { + JS_ASSERT(!script->strictModeCode); + Value rval = regs.sp[-1]; JSString *str; if (IsXML(rval)) { str = js_ValueToXMLString(cx, rval); } else { - str = js_ValueToString(cx, rval); + str = ToString(cx, rval); if (str) str = js_EscapeElementValue(cx, str); } @@ -6379,6 +3997,8 @@ END_CASE(JSOP_XMLELTEXPR) BEGIN_CASE(JSOP_XMLCDATA) { + JS_ASSERT(!script->strictModeCode); + JSAtom *atom; LOAD_ATOM(0, atom); JSString *str = atom; @@ -6391,6 +4011,8 @@ END_CASE(JSOP_XMLCDATA) BEGIN_CASE(JSOP_XMLCOMMENT) { + JS_ASSERT(!script->strictModeCode); + JSAtom *atom; LOAD_ATOM(0, atom); JSString *str = atom; @@ -6403,6 +4025,8 @@ END_CASE(JSOP_XMLCOMMENT) BEGIN_CASE(JSOP_XMLPI) { + JS_ASSERT(!script->strictModeCode); + JSAtom *atom; LOAD_ATOM(0, atom); JSString *str = atom; @@ -6417,8 +4041,10 @@ END_CASE(JSOP_XMLPI) BEGIN_CASE(JSOP_GETFUNNS) { + JS_ASSERT(!script->strictModeCode); + Value rval; - if (!cx->fp()->scopeChain().getGlobal()->getFunctionNamespace(cx, &rval)) + if (!cx->fp()->scopeChain().global().getFunctionNamespace(cx, &rval)) goto error; PUSH_COPY(rval); } @@ -6426,18 +4052,30 @@ END_CASE(JSOP_GETFUNNS) #endif /* JS_HAS_XML_SUPPORT */ BEGIN_CASE(JSOP_ENTERBLOCK) -{ - JSObject *obj; - LOAD_OBJECT(0, obj); - JS_ASSERT(obj->isStaticBlock()); - JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); - Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(regs.sp < vp); - JS_ASSERT(vp <= regs.fp()->slots() + script->nslots); - SetValueRangeToUndefined(regs.sp, vp); - regs.sp = vp; +BEGIN_CASE(JSOP_ENTERLET0) +BEGIN_CASE(JSOP_ENTERLET1) +{ + StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(regs.pc))->asStaticBlock(); + JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock()); + + if (op == JSOP_ENTERBLOCK) { + JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp); + Value *vp = regs.sp + blockObj.slotCount(); + JS_ASSERT(regs.sp < vp); + JS_ASSERT(vp <= regs.fp()->slots() + script->nslots); + SetValueRangeToUndefined(regs.sp, vp); + regs.sp = vp; + } else if (op == JSOP_ENTERLET0) { + JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount() + == regs.sp); + } else if (op == JSOP_ENTERLET1) { + JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount() + == regs.sp - 1); + } #ifdef DEBUG + JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock()); + /* * The young end of fp->scopeChain may omit blocks if we haven't closed * over them, but if there are any closure blocks on fp->scopeChain, they'd @@ -6446,53 +4084,54 @@ BEGIN_CASE(JSOP_ENTERBLOCK) * static scope. */ JSObject *obj2 = ®s.fp()->scopeChain(); - Class *clasp; - while ((clasp = obj2->getClass()) == &js_WithClass) - obj2 = obj2->getParent(); - if (clasp == &js_BlockClass && - obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) { - JSObject *youngestProto = obj2->getProto(); - JS_ASSERT(youngestProto->isStaticBlock()); - JSObject *parent = obj; - while ((parent = parent->getParent()) != youngestProto) + while (obj2->isWith()) + obj2 = &obj2->asWith().enclosingScope(); + if (obj2->isBlock() && + obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) + { + StaticBlockObject &youngestProto = obj2->asClonedBlock().staticBlock(); + StaticBlockObject *parent = &blockObj; + while ((parent = parent->enclosingBlock()) != &youngestProto) JS_ASSERT(parent); } #endif + + regs.fp()->setBlockChain(&blockObj); } END_CASE(JSOP_ENTERBLOCK) -BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) BEGIN_CASE(JSOP_LEAVEBLOCK) +BEGIN_CASE(JSOP_LEAVEFORLETIN) +BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) { - JSObject *blockChain; - LOAD_OBJECT(UINT16_LEN, blockChain); -#ifdef DEBUG - JS_ASSERT(blockChain->isStaticBlock()); - uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain); - JS_ASSERT(blockDepth <= StackDepth(script)); -#endif + StaticBlockObject &blockObj = regs.fp()->blockChain(); + JS_ASSERT(blockObj.stackDepth() <= StackDepth(script)); + /* * If we're about to leave the dynamic scope of a block that has been * cloned onto fp->scopeChain, clear its private data, move its locals from * the stack into the clone, and pop it off the chain. */ - JSObject &obj = regs.fp()->scopeChain(); - if (obj.getProto() == blockChain) { - JS_ASSERT(obj.isClonedBlock()); - if (!js_PutBlockObject(cx, JS_TRUE)) - goto error; - } - - /* Move the result of the expression to the new topmost stack slot. */ - Value *vp = NULL; /* silence GCC warnings */ - if (op == JSOP_LEAVEBLOCKEXPR) - vp = ®s.sp[-1]; - regs.sp -= GET_UINT16(regs.pc); - if (op == JSOP_LEAVEBLOCKEXPR) { - JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp - 1); + JSObject &scope = regs.fp()->scopeChain(); + if (scope.getProto() == &blockObj) + scope.asClonedBlock().put(cx); + + regs.fp()->setBlockChain(blockObj.enclosingBlock()); + + if (op == JSOP_LEAVEBLOCK) { + /* Pop the block's slots. */ + regs.sp -= GET_UINT16(regs.pc); + JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp); + } else if (op == JSOP_LEAVEBLOCKEXPR) { + /* Pop the block's slots maintaining the topmost expr. */ + Value *vp = ®s.sp[-1]; + regs.sp -= GET_UINT16(regs.pc); + JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp - 1); regs.sp[-1] = *vp; } else { - JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp); + /* Another op will pop; nothing to do here. */ + len = JSOP_LEAVEFORLETIN_LENGTH; + DO_NEXT_OP(len); } } END_CASE(JSOP_LEAVEBLOCK) @@ -6529,11 +4168,11 @@ BEGIN_CASE(JSOP_YIELD) BEGIN_CASE(JSOP_ARRAYPUSH) { - uint32 slot = GET_UINT16(regs.pc); + uint32_t slot = GET_UINT16(regs.pc); JS_ASSERT(script->nfixed <= slot); JS_ASSERT(slot < script->nslots); JSObject *obj = ®s.fp()->slots()[slot].toObject(); - if (!js_ArrayCompPush(cx, obj, regs.sp[-1])) + if (!js_NewbornArrayPush(cx, obj, regs.sp[-1])) goto error; regs.sp--; } @@ -6550,12 +4189,6 @@ END_CASE(JSOP_ARRAYPUSH) L_JSOP_ARRAYPUSH: # endif -# if !JS_HAS_SHARP_VARS - L_JSOP_DEFSHARP: - L_JSOP_USESHARP: - L_JSOP_SHARPINIT: -# endif - # if !JS_HAS_DESTRUCTURING L_JSOP_ENUMCONSTELEM: # endif @@ -6609,30 +4242,7 @@ END_CASE(JSOP_ARRAYPUSH) error: JS_ASSERT(&cx->regs() == ®s); -#ifdef JS_TRACER - if (regs.fp()->hasImacropc() && cx->isExceptionPending()) { - // Handle exceptions as if they came from the imacro-calling pc. - regs.pc = regs.fp()->imacropc(); - regs.fp()->clearImacropc(); - } -#endif - - JS_ASSERT(size_t((regs.fp()->hasImacropc() ? regs.fp()->imacropc() : regs.pc) - script->code) < - script->length); - -#ifdef JS_TRACER - /* - * This abort could be weakened to permit tracing through exceptions that - * are thrown and caught within a loop, with the co-operation of the tracer. - * For now just bail on any sign of trouble. - */ - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "error or exception while recording"); -# ifdef JS_METHODJIT - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); -# endif -#endif + JS_ASSERT(uint32_t(regs.pc - script->code) < script->length); if (!cx->isExceptionPending()) { /* This is an error, not a catchable exception, quit the frame ASAP. */ @@ -6640,17 +4250,22 @@ END_CASE(JSOP_ARRAYPUSH) } else { JSThrowHook handler; JSTryNote *tn, *tnlimit; - uint32 offset; + uint32_t offset; /* Restore atoms local in case we will resume. */ - atoms = script->atomMap.vector; + atoms = script->atoms; /* Call debugger throw hook if set. */ - handler = cx->debugHooks->throwHook; - if (handler) { + if (cx->debugHooks->throwHook || !cx->compartment->getDebuggees().empty()) { Value rval; - switch (handler(cx, script, regs.pc, Jsvalify(&rval), - cx->debugHooks->throwHookData)) { + JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval); + if (st == JSTRAP_CONTINUE) { + handler = cx->debugHooks->throwHook; + if (handler) + st = handler(cx, script, regs.pc, &rval, cx->debugHooks->throwHookData); + } + + switch (st) { case JSTRAP_ERROR: cx->clearPendingException(); goto error; @@ -6673,7 +4288,7 @@ END_CASE(JSOP_ARRAYPUSH) if (!JSScript::isValidOffset(script->trynotesOffset)) goto no_catch; - offset = (uint32)(regs.pc - script->main); + offset = (uint32_t)(regs.pc - script->main()); tn = script->trynotes()->vector; tnlimit = tn + script->trynotes()->length; do { @@ -6707,20 +4322,15 @@ END_CASE(JSOP_ARRAYPUSH) * to the beginning of catch or finally or to [enditer] closing * the for-in loop. */ - regs.pc = (script)->main + tn->start + tn->length; + regs.pc = (script)->main() + tn->start + tn->length; - JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE); - JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth); - if (!ok) { - /* - * Restart the handler search with updated pc and stack depth - * to properly notify the debugger. - */ - goto error; - } + UnwindScope(cx, tn->stackDepth); + regs.sp = regs.fp()->base() + tn->stackDepth; switch (tn->kind) { case JSTRY_CATCH: + JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK); + #if JS_HAS_GENERATORS /* Catch cannot intercept the closing of a generator. */ if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) @@ -6748,10 +4358,10 @@ END_CASE(JSOP_ARRAYPUSH) case JSTRY_ITER: { /* This is similar to JSOP_ENDITER in the interpreter loop. */ - JS_ASSERT(js_GetOpcode(cx, regs.fp()->script(), regs.pc) == JSOP_ENDITER); + JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER); Value v = cx->getPendingException(); cx->clearPendingException(); - ok = js_CloseIterator(cx, ®s.sp[-1].toObject()); + bool ok = js_CloseIterator(cx, ®s.sp[-1].toObject()); regs.sp -= 1; if (!ok) goto error; @@ -6777,31 +4387,22 @@ END_CASE(JSOP_ARRAYPUSH) } forced_return: - /* - * Unwind the scope making sure that interpReturnOK stays false even when - * js_UnwindScope returns true. - * - * When a trap handler returns JSTRAP_RETURN, we jump here with - * interpReturnOK set to true bypassing any finally blocks. - */ - interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending()); - JS_ASSERT(regs.sp == regs.fp()->base()); - -#ifdef DEBUG - cx->logPrevPc = NULL; -#endif + UnwindScope(cx, 0); + regs.sp = regs.fp()->base(); if (entryFrame != regs.fp()) goto inline_return; exit: + if (cx->compartment->debugMode()) + interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK); interpReturnOK = ScriptEpilogueOrGeneratorYield(cx, regs.fp(), interpReturnOK); regs.fp()->setFinishedInInterpreter(); /* * At this point we are inevitably leaving an interpreted function or a * top-level script, and returning to one of: - * (a) an "out of line" call made through js_Invoke; + * (a) an "out of line" call made through Invoke; * (b) a js_Execute activation; * (c) a generator (SendToGenerator, jsiter.c). * @@ -6810,39 +4411,19 @@ END_CASE(JSOP_ARRAYPUSH) * frame pc. */ JS_ASSERT(entryFrame == regs.fp()); - -#ifdef JS_TRACER - JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx)); - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "recording out of Interpret"); -# ifdef JS_METHODJIT - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); -# endif -#endif - - JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, ®s.fp()->scopeChain(), 0)); - - return interpReturnOK; - - atom_not_defined: - { - JSAutoByteString printable; - if (js_AtomToPrintableString(cx, atomNotDefined, &printable)) - js_ReportIsNotDefined(cx, printable.ptr()); + if (!regs.fp()->isGeneratorFrame()) { + JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); + JS_ASSERT(!regs.fp()->hasBlockChain()); } - goto error; +#ifdef JS_METHODJIT /* * This path is used when it's guaranteed the method can be finished * inside the JIT. */ -#if defined(JS_METHODJIT) leave_on_safe_point: #endif + + gc::VerifyBarriers(cx, true); return interpReturnOK; } - -} /* namespace js */ - -#endif /* !defined jsinvoke_cpp___ */ diff --git a/deps/mozjs/js/src/jsinterp.h b/deps/mozjs/js/src/jsinterp.h index 998f28c12aa..9ac9401c20c 100644 --- a/deps/mozjs/js/src/jsinterp.h +++ b/deps/mozjs/js/src/jsinterp.h @@ -46,22 +46,11 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsopcode.h" -#include "jsscript.h" -#include "jsvalue.h" #include "vm/Stack.h" namespace js { -extern JSObject * -GetBlockChain(JSContext *cx, StackFrame *fp); - -extern JSObject * -GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen); - -extern JSObject * -GetScopeChain(JSContext *cx); - /* * Refresh and return fp->scopeChain. It may be stale if block scopes are * active but not yet reflected by objects in the scope chain. If a block @@ -69,29 +58,12 @@ GetScopeChain(JSContext *cx); * dynamically scoped construct, then compile-time block scope at fp->blocks * must reflect at runtime. */ -extern JSObject * -GetScopeChain(JSContext *cx, StackFrame *fp); extern JSObject * -GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen); - -/* - * Report an error that the this value passed as |this| in the given arguments - * vector is not compatible with the specified class. - */ -void -ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp); +GetScopeChain(JSContext *cx); -/* - * Given a context and a vector of [callee, this, args...] for a function - * whose JSFUN_PRIMITIVE_THIS flag is set, set |*v| to the primitive value - * of |this|. If |this| is an object, insist that it be an instance of the - * appropriate wrapper class for T, and set |*v| to its private slot value. - * If |this| is a primitive, unbox it into |*v| if it's of the required - * type, and throw an error otherwise. - */ -template -bool GetPrimitiveThis(JSContext *cx, Value *vp, T *v); +extern JSObject * +GetScopeChain(JSContext *cx, StackFrame *fp); /* * ScriptPrologue/ScriptEpilogue must be called in pairs. ScriptPrologue @@ -120,7 +92,24 @@ ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok); /* Implemented in jsdbgapi: */ -extern void +/* + * Announce to the debugger that the thread has entered a new JavaScript frame, + * |fp|. Call whatever hooks have been registered to observe new frames, and + * return a JSTrapStatus code indication how execution should proceed: + * + * - JSTRAP_CONTINUE: Continue execution normally. + * + * - JSTRAP_THROW: Throw an exception. ScriptDebugPrologue has set |cx|'s + * pending exception to the value to be thrown. + * + * - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated + * for running too long). ScriptDebugPrologue has cleared |cx|'s pending + * exception. + * + * - JSTRAP_RETURN: Return from the new frame immediately. ScriptDebugPrologue + * has set |cx->fp()|'s return value appropriately. + */ +extern JSTrapStatus ScriptDebugPrologue(JSContext *cx, StackFrame *fp); extern bool @@ -144,136 +133,111 @@ BoxNonStrictThis(JSContext *cx, const CallReceiver &call); inline bool ComputeThis(JSContext *cx, StackFrame *fp); -/* - * Choose enumerator values so that the enum can be passed used directly as the - * stack frame flags. - */ -enum ConstructOption { - INVOKE_NORMAL = 0, - INVOKE_CONSTRUCTOR = StackFrame::CONSTRUCTING +enum MaybeConstruct { + NO_CONSTRUCT = INITIAL_NONE, + CONSTRUCT = INITIAL_CONSTRUCT }; -JS_STATIC_ASSERT(INVOKE_CONSTRUCTOR != INVOKE_NORMAL); - -static inline uintN -ToReportFlags(ConstructOption option) -{ - return (uintN)option; -} - -static inline uint32 -ToFrameFlags(ConstructOption option) -{ - return (uintN)option; -} /* - * The js::InvokeArgumentsGuard passed to js_Invoke must come from an - * immediately-enclosing successful call to js::StackSpace::pushInvokeArgs, - * i.e., there must have been no un-popped pushes to cx->stack. Furthermore, - * |args.getvp()[0]| should be the callee, |args.getvp()[1]| should be |this|, - * and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should - * be initialized actual arguments. + * InvokeKernel assumes that the given args have been pushed on the top of the + * VM stack. Additionally, if 'args' is contained in a CallArgsList, that they + * have already been marked 'active'. */ -extern JS_REQUIRES_STACK bool -Invoke(JSContext *cx, const CallArgs &args, ConstructOption option = INVOKE_NORMAL); +extern bool +InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT); /* - * Natives like sort/forEach/replace call Invoke repeatedly with the same - * callee, this, and number of arguments. To optimize this, such natives can - * start an "invoke session" to factor out much of the dynamic setup logic - * required by a normal Invoke. Usage is: - * - * InvokeSessionGuard session(cx); - * if (!session.start(cx, callee, thisp, argc, &session)) - * ... - * - * while (...) { - * // write actual args (not callee, this) - * session[0] = ... - * ... - * session[argc - 1] = ... - * - * if (!session.invoke(cx, session)) - * ... - * - * ... = session.rval(); - * } - * - * // session ended by ~InvokeSessionGuard + * Invoke assumes that 'args' has been pushed (via ContextStack::pushInvokeArgs) + * and is currently at the top of the VM stack. */ -class InvokeSessionGuard; +inline bool +Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT) +{ + args.setActive(); + bool ok = InvokeKernel(cx, args, construct); + args.setInactive(); + return ok; +} /* - * "External" calls may come from C or C++ code using a JSContext on which no - * JS is running (!cx->fp), so they may need to push a dummy StackFrame. + * This Invoke overload places the least requirements on the caller: it may be + * called at any time and it takes care of copying the given callee, this, and + * arguments onto the stack. */ - extern bool -ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, - uintN argc, Value *argv, Value *rval); +Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, + Value *rval); +/* + * This helper takes care of the infinite-recursion check necessary for + * getter/setter calls. + */ extern bool -ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval, - JSAccessMode mode, uintN argc, Value *argv, Value *rval); +InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, + Value *rval); /* - * These two functions invoke a function called from a constructor context - * (e.g. 'new'). InvokeConstructor handles the general case where a new object - * needs to be created for/by the constructor. ConstructWithGivenThis directly - * calls the constructor with the given 'this', hence the caller must - * understand the semantics of the constructor call. + * InvokeConstructor* implement a function call from a constructor context + * (e.g. 'new') handling the the creation of the new 'this' object. */ +extern bool +InvokeConstructorKernel(JSContext *cx, const CallArgs &args); -extern JS_REQUIRES_STACK bool -InvokeConstructor(JSContext *cx, const CallArgs &args); - -extern JS_REQUIRES_STACK bool -InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval, - uintN argc, Value *argv, Value *rval); +/* See the InvokeArgsGuard overload of Invoke. */ +inline bool +InvokeConstructor(JSContext *cx, InvokeArgsGuard &args) +{ + args.setActive(); + bool ok = InvokeConstructorKernel(cx, ImplicitCast(args)); + args.setInactive(); + return ok; +} +/* See the fval overload of Invoke. */ extern bool -ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, - Value *rval); +InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval); /* - * Executes a script with the given scope chain in the context of the given - * frame. + * Executes a script with the given scopeChain/this. The 'type' indicates + * whether this is eval code or global code. To support debugging, the + * evalFrame parameter can point to an arbitrary frame in the context's call + * stack to simulate executing an eval in that frame. */ -extern JS_FORCES_STACK bool -Execute(JSContext *cx, JSObject &chain, JSScript *script, - StackFrame *prev, uintN flags, Value *result); +extern bool +ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, + ExecuteType type, StackFrame *evalInFrame, Value *result); + +/* Execute a script with the given scopeChain as global code. */ +extern bool +Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, Value *rval); /* Flags to toggle js::Interpret() execution. */ enum InterpMode { JSINTERP_NORMAL = 0, /* interpreter is running normally */ - JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */ - JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */ - JSINTERP_PROFILE = 3 /* interpreter should profile a loop */ + JSINTERP_REJOIN = 1, /* as normal, but the frame has already started */ + JSINTERP_SKIP_TRAP = 2 /* as REJOIN, but skip trap at first opcode */ }; /* * Execute the caller-initialized frame for a user-defined script or function * pointed to by cx->fp until completion or error. */ -extern JS_REQUIRES_STACK JS_NEVER_INLINE bool -Interpret(JSContext *cx, StackFrame *stopFp, uintN inlineCallCount = 0, InterpMode mode = JSINTERP_NORMAL); - -extern JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, StackFrame *fp); +extern JS_NEVER_INLINE bool +Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL); extern bool -CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs); +RunScript(JSContext *cx, JSScript *script, StackFrame *fp); extern bool -StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal); +StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); extern bool -LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal); +LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); /* === except that NaN is the same as NaN and -0 is not the same as +0. */ extern bool -SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same); +SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same); extern JSType TypeOfValue(JSContext *cx, const Value &v); @@ -291,78 +255,103 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp); * closure level. * @return The value of the upvar. */ -extern const js::Value & -GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie); +extern const Value & +GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie); -} /* namespace js */ +/* Search the call stack for the nearest frame with static level targetLevel. */ +extern StackFrame * +FindUpvarFrame(JSContext *cx, uintN targetLevel); /* - * JS_LONE_INTERPRET indicates that the compiler should see just the code for - * the js_Interpret function when compiling jsinterp.cpp. The rest of the code - * from the file should be visible only when compiling jsinvoke.cpp. It allows - * platform builds to optimize selectively js_Interpret when the granularity - * of the optimizations with the given compiler is a compilation unit. + * A linked list of the |FrameRegs regs;| variables belonging to all + * js::Interpret C++ frames on this thread's stack. + * + * Note that this is *not* a list of all JS frames running under the + * interpreter; that would include inlined frames, whose FrameRegs are + * saved in various pieces in various places. Rather, this lists each + * js::Interpret call's live 'regs'; when control returns to that call, it + * will resume execution with this 'regs' instance. + * + * When Debugger puts a script in single-step mode, all js::Interpret + * invocations that might be presently running that script must have + * interrupts enabled. It's not practical to simply check + * script->stepModeEnabled() at each point some callee could have changed + * it, because there are so many places js::Interpret could possibly cause + * JavaScript to run: each place an object might be coerced to a primitive + * or a number, for example. So instead, we simply expose a list of the + * 'regs' those frames are using, and let Debugger tweak the affected + * js::Interpret frames when an onStep handler is established. * - * JS_STATIC_INTERPRET is the modifier for functions defined in jsinterp.cpp - * that only js_Interpret calls. When JS_LONE_INTERPRET is true all such - * functions are declared below. + * Elements of this list are allocated within the js::Interpret stack + * frames themselves; the list is headed by this thread's js::ThreadData. */ -#ifndef JS_LONE_INTERPRET -# ifdef _MSC_VER -# define JS_LONE_INTERPRET 0 -# else -# define JS_LONE_INTERPRET 1 -# endif -#endif - -#if !JS_LONE_INTERPRET -# define JS_STATIC_INTERPRET static -#else -# define JS_STATIC_INTERPRET +class InterpreterFrames { + public: + class InterruptEnablerBase { + public: + virtual void enableInterrupts() const = 0; + }; -extern JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen); + InterpreterFrames(JSContext *cx, FrameRegs *regs, const InterruptEnablerBase &enabler); + ~InterpreterFrames(); -extern JS_REQUIRES_STACK void -js_LeaveWith(JSContext *cx); + /* If this js::Interpret frame is running |script|, enable interrupts. */ + inline void enableInterruptsIfRunning(JSScript *script); -/* - * Find the results of incrementing or decrementing *vp. For pre-increments, - * both *vp and *vp2 will contain the result on return. For post-increments, - * vp will contain the original value converted to a number and vp2 will get - * the result. Both vp and vp2 must be roots. - */ -extern JSBool -js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, js::Value *vp, js::Value *vp2); + InterpreterFrames *older; -/* - * Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the - * previous opcode. - */ -extern JS_REQUIRES_STACK void -js_LogOpcode(JSContext *cx); + private: + JSContext *context; + FrameRegs *regs; + const InterruptEnablerBase &enabler; +}; /* - * JS_OPMETER helper functions. + * Unwind block and scope chains to match the given depth. The function sets + * fp->sp on return to stackDepth. */ extern void -js_MeterOpcodePair(JSOp op1, JSOp op2); +UnwindScope(JSContext *cx, uint32_t stackDepth); -extern void -js_MeterSlotOpcode(JSOp op, uint32 slot); +extern bool +OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp); + +extern bool +IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth); + +/************************************************************************/ -#endif /* JS_LONE_INTERPRET */ /* - * Unwind block and scope chains to match the given depth. The function sets - * fp->sp on return to stackDepth. + * To really poison a set of values, using 'magic' or 'undefined' isn't good + * enough since often these will just be ignored by buggy code (see bug 629974) + * in debug builds and crash in release builds. Instead, we use a safe-for-crash + * pointer. */ -extern JS_REQUIRES_STACK JSBool -js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind); +static JS_ALWAYS_INLINE void +Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end) +{ +#ifdef DEBUG + for (Value *v = beg; v != end; ++v) + v->setObject(*reinterpret_cast(0x42)); +#endif +} -extern JSBool -js_OnUnknownMethod(JSContext *cx, js::Value *vp); +static JS_ALWAYS_INLINE void +Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len) +{ +#ifdef DEBUG + Debug_SetValueRangeToCrashOnTouch(vec, vec + len); +#endif +} + +static JS_ALWAYS_INLINE void +Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len) +{ +#ifdef DEBUG + Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); +#endif +} -extern JS_REQUIRES_STACK js::Class * -js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth); +} /* namespace js */ #endif /* jsinterp_h___ */ diff --git a/deps/mozjs/js/src/jsinterpinlines.h b/deps/mozjs/js/src/jsinterpinlines.h index 9b1ec580afe..cc5812a3d73 100644 --- a/deps/mozjs/js/src/jsinterpinlines.h +++ b/deps/mozjs/js/src/jsinterpinlines.h @@ -44,13 +44,18 @@ #include "jsapi.h" #include "jsbool.h" #include "jscompartment.h" +#include "jsinfer.h" #include "jsinterp.h" +#include "jslibmath.h" #include "jsnum.h" #include "jsprobes.h" #include "jsstr.h" #include "methodjit/MethodJIT.h" #include "jsfuninlines.h" +#include "jsinferinlines.h" +#include "jspropertycacheinlines.h" +#include "jstypedarrayinlines.h" #include "vm/Stack-inl.h" @@ -71,210 +76,41 @@ class AutoPreserveEnumerators { } }; -class InvokeSessionGuard -{ - InvokeArgsGuard args_; - InvokeFrameGuard frame_; - Value savedCallee_, savedThis_; - Value *formals_, *actuals_; - unsigned nformals_; - JSScript *script_; - Value *stackLimit_; - jsbytecode *stop_; - - bool optimized() const { return frame_.pushed(); } - - public: - InvokeSessionGuard() : args_(), frame_() {} - ~InvokeSessionGuard() {} - - bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc); - bool invoke(JSContext *cx) const; - - bool started() const { - return args_.pushed(); - } - - Value &operator[](unsigned i) const { - JS_ASSERT(i < argc()); - Value &arg = i < nformals_ ? formals_[i] : actuals_[i]; - JS_ASSERT_IF(optimized(), &arg == &frame_.fp()->canonicalActualArg(i)); - JS_ASSERT_IF(!optimized(), &arg == &args_[i]); - return arg; - } - - uintN argc() const { - return args_.argc(); - } - - const Value &rval() const { - return optimized() ? frame_.fp()->returnValue() : args_.rval(); - } -}; - -inline bool -InvokeSessionGuard::invoke(JSContext *cx) const -{ - /* N.B. Must be kept in sync with Invoke */ - - /* Refer to canonical (callee, this) for optimized() sessions. */ - formals_[-2] = savedCallee_; - formals_[-1] = savedThis_; - - /* Prevent spurious accessing-callee-after-rval assert. */ - args_.calleeHasBeenReset(); - -#ifdef JS_METHODJIT - void *code; - if (!optimized() || !(code = script_->getJIT(false /* !constructing */)->invokeEntry)) -#else - if (!optimized()) -#endif - return Invoke(cx, args_); - - /* Clear any garbage left from the last Invoke. */ - StackFrame *fp = frame_.fp(); - fp->clearMissingArgs(); - fp->resetInvokeCallFrame(); - SetValueRangeToUndefined(fp->slots(), script_->nfixed); - - JSBool ok; - { - AutoPreserveEnumerators preserve(cx); - Probes::enterJSFun(cx, fp->fun(), script_); -#ifdef JS_METHODJIT - ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_); - cx->regs().pc = stop_; -#else - cx->regs().pc = script_->code; - ok = Interpret(cx, cx->fp()); -#endif - Probes::exitJSFun(cx, fp->fun(), script_); - } - - /* Don't clobber callee with rval; rval gets read from fp->rval. */ - return ok; -} - -namespace detail { - -template class PrimitiveBehavior { }; - -template<> -class PrimitiveBehavior { - public: - static inline bool isType(const Value &v) { return v.isString(); } - static inline JSString *extract(const Value &v) { return v.toString(); } - static inline Class *getClass() { return &js_StringClass; } -}; - -template<> -class PrimitiveBehavior { - public: - static inline bool isType(const Value &v) { return v.isBoolean(); } - static inline bool extract(const Value &v) { return v.toBoolean(); } - static inline Class *getClass() { return &js_BooleanClass; } -}; - -template<> -class PrimitiveBehavior { - public: - static inline bool isType(const Value &v) { return v.isNumber(); } - static inline double extract(const Value &v) { return v.toNumber(); } - static inline Class *getClass() { return &js_NumberClass; } -}; - -} // namespace detail - -template -inline bool -GetPrimitiveThis(JSContext *cx, Value *vp, T *v) -{ - typedef detail::PrimitiveBehavior Behavior; - - const Value &thisv = vp[1]; - if (Behavior::isType(thisv)) { - *v = Behavior::extract(thisv); - return true; - } - - if (thisv.isObject() && thisv.toObject().getClass() == Behavior::getClass()) { - *v = Behavior::extract(thisv.toObject().getPrimitiveThis()); - return true; - } - - ReportIncompatibleMethod(cx, vp, Behavior::getClass()); - return false; -} - /* * Compute the implicit |this| parameter for a call expression where the callee - * is an unqualified name reference. + * funval was resolved from an unqualified name reference to a property on obj + * (an object on the scope chain). * * We can avoid computing |this| eagerly and push the implicit callee-coerced - * |this| value, undefined, according to this decision tree: + * |this| value, undefined, if any of these conditions hold: * - * 1. If the called value, funval, is not an object, bind |this| to undefined. + * 1. The nominal |this|, obj, is a global object. * * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be - * censored. - * - * 3. obj is a global. There are several sub-cases: - * - * a) obj is a proxy: we try unwrapping it (see jswrapper.cpp) in order to find - * a function object inside. If the proxy is not a wrapper, or else it wraps - * a non-function, then bind |this| to undefined per ES5-strict/Harmony. - * - * [Else fall through with callee pointing to an unwrapped function object.] - * - * b) If callee is a function (after unwrapping if necessary), check whether it - * is interpreted and in strict mode. If so, then bind |this| to undefined - * per ES5 strict. + * censored with undefined. * - * c) Now check that callee is scoped by the same global object as the object - * in which its unqualified name was bound as a property. ES1-3 bound |this| - * to the name's "Reference base object", which in the context of multiple - * global objects may not be the callee's global. If globals match, bind - * |this| to undefined. + * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with| + * statements and embedding-specific scope objects fall into this category. * - * This is a backward compatibility measure; see bug 634590. - * - * 4. Finally, obj is neither a declarative scope object to be censored, nor a - * global where the callee requires no backward-compatible special handling - * or future-proofing based on (explicit or imputed by Harmony status in the - * proxy case) strict mode opt-in. Bind |this| to obj->thisObject(). + * If the callee is a strict mode function, then code implementing JSOP_THIS + * in the interpreter and JITs will leave undefined as |this|. If funval is a + * function not in strict mode, JSOP_THIS code replaces undefined with funval's + * global. * * We set *vp to undefined early to reduce code size and bias this code for the * common and future-friendly cases. */ inline bool -ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +ComputeImplicitThis(JSContext *cx, JSObject *obj, Value *vp) { vp->setUndefined(); - if (!funval.isObject()) + if (obj->isGlobal()) return true; - if (!obj->isGlobal()) { - if (IsCacheableNonGlobalScope(obj)) - return true; - } else { - JSObject *callee = &funval.toObject(); - - if (callee->isProxy()) { - callee = callee->unwrap(); - if (!callee->isFunction()) - return true; // treat any non-wrapped-function proxy as strict - } - if (callee->isFunction()) { - JSFunction *fun = callee->getFunctionPrivate(); - if (fun->isInterpreted() && fun->inStrictMode()) - return true; - } - if (callee->getGlobal() == cx->fp()->scopeChain().getGlobal()) - return true;; - } + if (IsCacheableNonGlobalScope(obj)) + return true; obj = obj->thisObject(cx); if (!obj) @@ -317,52 +153,370 @@ ComputeThis(JSContext *cx, StackFrame *fp) * problem to the value at |spindex| on the stack. */ JS_ALWAYS_INLINE JSObject * -ValuePropertyBearer(JSContext *cx, const Value &v, int spindex) +ValuePropertyBearer(JSContext *cx, StackFrame *fp, const Value &v, int spindex) { if (v.isObject()) return &v.toObject(); - JSProtoKey protoKey; - if (v.isString()) { - protoKey = JSProto_String; - } else if (v.isNumber()) { - protoKey = JSProto_Number; - } else if (v.isBoolean()) { - protoKey = JSProto_Boolean; + GlobalObject &global = fp->scopeChain().global(); + + if (v.isString()) + return global.getOrCreateStringPrototype(cx); + if (v.isNumber()) + return global.getOrCreateNumberPrototype(cx); + if (v.isBoolean()) + return global.getOrCreateBooleanPrototype(cx); + + JS_ASSERT(v.isNull() || v.isUndefined()); + js_ReportIsNullOrUndefined(cx, spindex, v, NULL); + return NULL; +} + +inline bool +NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, Value *vp) +{ + if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { + /* Fast path for Object instance properties. */ + JS_ASSERT(shape->hasSlot()); + *vp = pobj->nativeGetSlot(shape->slot()); + } else { + if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) + return false; + } + return true; +} + +#if defined(DEBUG) && !defined(JS_THREADSAFE) +extern void +AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found, + PropertyCacheEntry *entry); +#else +inline void +AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found, + PropertyCacheEntry *entry) +{} +#endif + +inline bool +GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, JSObject *obj, jsid id, Value *vp) +{ + /* + * Various XML properties behave differently when accessed in a + * call vs. normal context, and getGeneric will not work right. + */ +#if JS_HAS_XML_SUPPORT + if (op == JSOP_CALLPROP && obj->isXML()) + return js_GetXMLMethod(cx, obj, id, vp); +#endif + + return obj->getGeneric(cx, id, vp); +} + +inline bool +GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp) +{ + JS_ASSERT(vp != &lval); + + JSOp op = JSOp(*pc); + + if (op == JSOP_LENGTH) { + /* Optimize length accesses on strings, arrays, and arguments. */ + if (lval.isString()) { + *vp = Int32Value(lval.toString()->length()); + return true; + } + if (lval.isMagic(JS_LAZY_ARGUMENTS)) { + *vp = Int32Value(cx->fp()->numActualArgs()); + return true; + } + if (lval.isObject()) { + JSObject *obj = &lval.toObject(); + if (obj->isArray()) { + jsuint length = obj->getArrayLength(); + *vp = NumberValue(length); + return true; + } + + if (obj->isArguments()) { + ArgumentsObject *argsobj = &obj->asArguments(); + if (!argsobj->hasOverriddenLength()) { + uint32_t length = argsobj->initialLength(); + JS_ASSERT(length < INT32_MAX); + *vp = Int32Value(int32_t(length)); + return true; + } + } + + if (js_IsTypedArray(obj)) { + JSObject *tarray = TypedArray::getTypedArray(obj); + *vp = Int32Value(TypedArray::getLength(tarray)); + return true; + } + } + } + + JSObject *obj = ValueToObjectOrPrototype(cx, lval); + if (!obj) + return false; + + uintN flags = (op == JSOP_CALLPROP) + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER; + + PropertyCacheEntry *entry; + JSObject *obj2; + PropertyName *name; + JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name); + if (!name) { + AssertValidPropertyCacheHit(cx, obj, obj2, entry); + if (!NativeGet(cx, obj, obj2, entry->prop, flags, vp)) + return false; + return true; + } + + jsid id = ATOM_TO_JSID(name); + + if (obj->getOps()->getProperty) { + if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp)) + return false; + } else { + if (!GetPropertyHelper(cx, obj, id, flags, vp)) + return false; + } + +#if JS_HAS_NO_SUCH_METHOD + if (op == JSOP_CALLPROP && + JS_UNLIKELY(vp->isPrimitive()) && + lval.isObject()) + { + if (!OnUnknownMethod(cx, obj, IdToValue(id), vp)) + return false; + } +#endif + + return true; +} + +inline bool +SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Value &rval) +{ + JSObject *obj = ValueToObject(cx, lval); + if (!obj) + return false; + + JS_ASSERT_IF(*pc == JSOP_SETMETHOD, IsFunctionObject(rval)); + JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject()); + JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global()); + + PropertyCacheEntry *entry; + JSObject *obj2; + PropertyName *name; + if (JS_PROPERTY_CACHE(cx).testForSet(cx, pc, obj, &entry, &obj2, &name)) { + /* + * Property cache hit, only partially confirmed by testForSet. We + * know that the entry applies to regs.pc and that obj's shape + * matches. + * + * The entry predicts a set either an existing "own" property, or + * on a prototype property that has a setter. + */ + const Shape *shape = entry->prop; + JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable()); + JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit()); + + if (entry->isOwnPropertyHit() || + ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) { +#ifdef DEBUG + if (entry->isOwnPropertyHit()) { + JS_ASSERT(obj->nativeContains(cx, *shape)); + } else { + JS_ASSERT(obj2->nativeContains(cx, *shape)); + JS_ASSERT(entry->isPrototypePropertyHit()); + JS_ASSERT(entry->kshape != entry->pshape); + JS_ASSERT(!shape->hasSlot()); + } +#endif + + if (shape->hasDefaultSetter() && shape->hasSlot() && !shape->isMethod()) { + /* Fast path for, e.g., plain Object instance properties. */ + obj->nativeSetSlotWithType(cx, shape, rval); + } else { + Value rref = rval; + bool strict = cx->stack.currentScript()->strictModeCode; + if (!js_NativeSet(cx, obj, shape, false, strict, &rref)) + return false; + } + return true; + } + + GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name); + } + + bool strict = cx->stack.currentScript()->strictModeCode; + Value rref = rval; + + JSOp op = JSOp(*pc); + + jsid id = ATOM_TO_JSID(name); + if (JS_LIKELY(!obj->getOps()->setProperty)) { + uintN defineHow; + if (op == JSOP_SETMETHOD) + defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD; + else if (op == JSOP_SETNAME) + defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED; + else + defineHow = DNP_CACHE_RESULT; + if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict)) + return false; + } else { + if (!obj->setGeneric(cx, id, &rref, strict)) + return false; + } + + return true; +} + +inline bool +NameOperation(JSContext *cx, jsbytecode *pc, Value *vp) +{ + JSObject *obj = cx->stack.currentScriptedScopeChain(); + + /* + * Skip along the scope chain to the enclosing global object. This is + * used for GNAME opcodes where the bytecode emitter has determined a + * name access must be on the global. It also insulates us from bugs + * in the emitter: type inference will assume that GNAME opcodes are + * accessing the global object, and the inferred behavior should match + * the actual behavior even if the id could be found on the scope chain + * before the global object. + */ + if (js_CodeSpec[*pc].format & JOF_GNAME) + obj = &obj->global(); + + PropertyCacheEntry *entry; + JSObject *obj2; + PropertyName *name; + JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name); + if (!name) { + AssertValidPropertyCacheHit(cx, obj, obj2, entry); + if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp)) + return false; + return true; + } + + jsid id = ATOM_TO_JSID(name); + + JSProperty *prop; + if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop)) + return false; + if (!prop) { + /* Kludge to allow (typeof foo == "undefined") tests. */ + JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]); + if (op2 == JSOP_TYPEOF) { + vp->setUndefined(); + return true; + } + JSAutoByteString printable; + if (js_AtomToPrintableString(cx, name, &printable)) + js_ReportIsNotDefined(cx, printable.ptr()); + return false; + } + + /* Take the slow path if prop was not found in a native object. */ + if (!obj->isNative() || !obj2->isNative()) { + if (!obj->getGeneric(cx, id, vp)) + return false; } else { - JS_ASSERT(v.isNull() || v.isUndefined()); - js_ReportIsNullOrUndefined(cx, spindex, v, NULL); - return NULL; + Shape *shape = (Shape *)prop; + JSObject *normalized = obj; + if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter()) + normalized = &normalized->asWith().object(); + if (!NativeGet(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, vp)) + return false; } - JSObject *pobj; - if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) - return NULL; - return pobj; + return true; } inline bool -ScriptPrologue(JSContext *cx, StackFrame *fp) +DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, uintN attrs) +{ + JS_ASSERT(varobj.isVarObj()); + JS_ASSERT(!varobj.getOps()->defineProperty); + + JSProperty *prop; + JSObject *obj2; + if (!varobj.lookupProperty(cx, dn, &obj2, &prop)) + return false; + + /* Steps 8c, 8d. */ + if (!prop || (obj2 != &varobj && varobj.isGlobal())) { + if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(), + JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0)) + { + return false; + } + } else { + /* + * Extension: ordinarily we'd be done here -- but for |const|. If we + * see a redeclaration that's |const|, we consider it a conflict. + */ + uintN oldAttrs; + if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs)) + return false; + if (attrs & JSPROP_READONLY) { + JSAutoByteString bytes; + if (js_AtomToPrintableString(cx, dn, &bytes)) { + JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, + NULL, JSMSG_REDECLARED_VAR, + (oldAttrs & JSPROP_READONLY) + ? "const" + : "var", + bytes.ptr())); + } + return false; + } + } + + return true; +} + +inline bool +FunctionNeedsPrologue(JSContext *cx, JSFunction *fun) +{ + /* Heavyweight functions need call objects created. */ + if (fun->isHeavyweight()) + return true; + + /* Outer and inner functions need to preserve nesting invariants. */ + if (cx->typeInferenceEnabled() && fun->script()->nesting()) + return true; + + return false; +} + +inline bool +ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType) { JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj()); if (fp->isConstructing()) { - JSObject *obj = js_CreateThisForFunction(cx, &fp->callee()); + JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType); if (!obj) return false; fp->functionThis().setObject(*obj); } - if (cx->compartment->debugMode) - ScriptDebugPrologue(cx, fp); + Probes::enterJSFun(cx, fp->maybeFun(), fp->script()); + return true; } inline bool ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok) { - if (cx->compartment->debugMode) - ok = ScriptDebugEpilogue(cx, fp, ok); + Probes::exitJSFun(cx, fp->maybeFun(), fp->script()); /* * If inline-constructing, replace primitive rval with the new object @@ -371,19 +525,16 @@ ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok) if (fp->isConstructing() && ok) { if (fp->returnValue().isPrimitive()) fp->setReturnValue(ObjectValue(fp->constructorThis())); - JS_RUNTIME_METER(cx->runtime, constructs); } return ok; } inline bool -ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp) +ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp, bool newType) { if (!fp->isGeneratorFrame()) - return ScriptPrologue(cx, fp); - if (cx->compartment->debugMode) - ScriptDebugPrologue(cx, fp); + return ScriptPrologue(cx, fp, newType); return true; } @@ -392,11 +543,345 @@ ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok) { if (!fp->isYielding()) return ScriptEpilogue(cx, fp, ok); - if (cx->compartment->debugMode) - return ScriptDebugEpilogue(cx, fp, ok); return ok; } +inline void +InterpreterFrames::enableInterruptsIfRunning(JSScript *script) +{ + if (script == regs->fp()->script()) + enabler.enableInterrupts(); +} + +static JS_ALWAYS_INLINE bool +AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) +{ + Value lval = lhs; + Value rval = rhs; + + if (lval.isInt32() && rval.isInt32()) { + int32_t l = lval.toInt32(), r = rval.toInt32(); + int32_t sum = l + r; + if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) { + res->setDouble(double(l) + double(r)); + types::TypeScript::MonitorOverflow(cx); + } else { + res->setInt32(sum); + } + } else +#if JS_HAS_XML_SUPPORT + if (IsXML(lval) && IsXML(rval)) { + if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res)) + return false; + types::TypeScript::MonitorUnknown(cx); + } else +#endif + { + /* + * If either operand is an object, any non-integer result must be + * reported to inference. + */ + bool lIsObject = lval.isObject(), rIsObject = rval.isObject(); + + if (!ToPrimitive(cx, &lval)) + return false; + if (!ToPrimitive(cx, &rval)) + return false; + bool lIsString, rIsString; + if ((lIsString = lval.isString()) | (rIsString = rval.isString())) { + js::AutoStringRooter lstr(cx), rstr(cx); + if (lIsString) { + lstr.setString(lval.toString()); + } else { + lstr.setString(ToString(cx, lval)); + if (!lstr.string()) + return false; + } + if (rIsString) { + rstr.setString(rval.toString()); + } else { + rstr.setString(ToString(cx, rval)); + if (!rstr.string()) + return false; + } + JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string()); + if (!str) + return false; + if (lIsObject || rIsObject) + types::TypeScript::MonitorString(cx); + res->setString(str); + } else { + double l, r; + if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) + return false; + l += r; + if (!res->setNumber(l) && + (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) { + types::TypeScript::MonitorOverflow(cx); + } + } + } + return true; +} + +static JS_ALWAYS_INLINE bool +SubOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + double d = d1 - d2; + if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) + types::TypeScript::MonitorOverflow(cx); + return true; +} + +static JS_ALWAYS_INLINE bool +MulOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + double d = d1 * d2; + if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble())) + types::TypeScript::MonitorOverflow(cx); + return true; +} + +static JS_ALWAYS_INLINE bool +DivOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) +{ + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + res->setNumber(NumberDiv(d1, d2)); + + if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble()))) + types::TypeScript::MonitorOverflow(cx); + return true; +} + +static JS_ALWAYS_INLINE bool +ModOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) +{ + int32_t l, r; + if (lhs.isInt32() && rhs.isInt32() && + (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) { + int32_t mod = l % r; + res->setInt32(mod); + return true; + } + + double d1, d2; + if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) + return false; + + if (d2 == 0) + res->setDouble(js_NaN); + else + res->setDouble(js_fmod(d1, d2)); + types::TypeScript::MonitorOverflow(cx); + return true; +} + +static inline bool +FetchElementId(JSContext *cx, JSObject *obj, const Value &idval, jsid &id, Value *vp) +{ + int32_t i_; + if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) { + id = INT_TO_JSID(i_); + return true; + } + return !!js_InternNonIntElementId(cx, obj, idval, &id, vp); +} + +static JS_ALWAYS_INLINE bool +ToIdOperation(JSContext *cx, const Value &objval, const Value &idval, Value *res) +{ + if (idval.isInt32()) { + *res = idval; + return true; + } + + JSObject *obj = ValueToObject(cx, objval); + if (!obj) + return false; + + jsid dummy; + if (!js_InternNonIntElementId(cx, obj, idval, &dummy, res)) + return false; + + if (!res->isInt32()) + types::TypeScript::MonitorUnknown(cx); + return true; +} + +static JS_ALWAYS_INLINE bool +GetObjectElementOperation(JSContext *cx, JSObject *obj, const Value &rref, Value *res) +{ + JSScript *script; + jsbytecode *pc; + types::TypeScript::GetPcScript(cx, &script, &pc); + + uint32_t index; + if (IsDefinitelyIndex(rref, &index)) { + do { + if (obj->isDenseArray()) { + if (index < obj->getDenseArrayInitializedLength()) { + *res = obj->getDenseArrayElement(index); + if (!res->isMagic()) + break; + } + } else if (obj->isArguments()) { + if (obj->asArguments().getElement(index, res)) + break; + } + if (!obj->getElement(cx, index, res)) + return false; + } while(0); + } else { + if (script->hasAnalysis()) + script->analysis()->getCode(pc).getStringElement = true; + + SpecialId special; + *res = rref; + if (ValueIsSpecial(obj, res, &special, cx)) { + if (!obj->getSpecial(cx, obj, special, res)) + return false; + } else { + JSAtom *name; + if (!js_ValueToAtom(cx, *res, &name)) + return false; + + if (name->isIndex(&index)) { + if (!obj->getElement(cx, index, res)) + return false; + } else { + if (!obj->getProperty(cx, name->asPropertyName(), res)) + return false; + } + } + } + + assertSameCompartment(cx, *res); + types::TypeScript::Monitor(cx, script, pc, *res); + return true; +} + +static JS_ALWAYS_INLINE bool +GetElementOperation(JSContext *cx, const Value &lref, const Value &rref, Value *res) +{ + if (lref.isString() && rref.isInt32()) { + JSString *str = lref.toString(); + int32_t i = rref.toInt32(); + if (size_t(i) < str->length()) { + str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i)); + if (!str) + return false; + res->setString(str); + types::TypeScript::Monitor(cx, *res); + return true; + } + } + + if (lref.isMagic(JS_LAZY_ARGUMENTS)) { + if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) { + *res = cx->regs().fp()->canonicalActualArg(rref.toInt32()); + types::TypeScript::Monitor(cx, *res); + return true; + } + types::MarkArgumentsCreated(cx, cx->fp()->script()); + JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS)); + } + + JSObject *obj = ValueToObject(cx, lref); + if (!obj) + return false; + return GetObjectElementOperation(cx, obj, rref, res); +} + +static JS_ALWAYS_INLINE bool +SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value) +{ + JSScript *script; + jsbytecode *pc; + types::TypeScript::GetPcScript(cx, &script, &pc); + types::TypeScript::MonitorAssign(cx, script, pc, obj, id, value); + + do { + if (obj->isDenseArray() && JSID_IS_INT(id)) { + jsuint length = obj->getDenseArrayInitializedLength(); + jsint i = JSID_TO_INT(id); + if ((jsuint)i < length) { + if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { + if (js_PrototypeHasIndexedProperties(cx, obj)) + break; + if ((jsuint)i >= obj->getArrayLength()) + obj->setArrayLength(cx, i + 1); + } + obj->setDenseArrayElementWithType(cx, i, value); + return true; + } else { + if (script->hasAnalysis()) + script->analysis()->getCode(pc).arrayWriteHole = true; + } + } + } while (0); + + Value tmp = value; + return obj->setGeneric(cx, id, &tmp, script->strictModeCode); +} + +#define RELATIONAL_OP(OP) \ + JS_BEGIN_MACRO \ + Value lval = lhs; \ + Value rval = rhs; \ + /* Optimize for two int-tagged operands (typical loop control). */ \ + if (lval.isInt32() && rval.isInt32()) { \ + *res = lval.toInt32() OP rval.toInt32(); \ + } else { \ + if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \ + return false; \ + if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \ + return false; \ + if (lval.isString() && rval.isString()) { \ + JSString *l = lval.toString(), *r = rval.toString(); \ + int32_t result; \ + if (!CompareStrings(cx, l, r, &result)) \ + return false; \ + *res = result OP 0; \ + } else { \ + double l, r; \ + if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) \ + return false;; \ + *res = (l OP r); \ + } \ + } \ + return true; \ + JS_END_MACRO + +static JS_ALWAYS_INLINE bool +LessThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) { + RELATIONAL_OP(<); +} + +static JS_ALWAYS_INLINE bool +LessThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) { + RELATIONAL_OP(<=); +} + +static JS_ALWAYS_INLINE bool +GreaterThanOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) { + RELATIONAL_OP(>); +} + +static JS_ALWAYS_INLINE bool +GreaterThanOrEqualOperation(JSContext *cx, const Value &lhs, const Value &rhs, bool *res) { + RELATIONAL_OP(>=); +} + +#undef RELATIONAL_OP + } /* namespace js */ #endif /* jsinterpinlines_h__ */ diff --git a/deps/mozjs/js/src/jsinttypes.h b/deps/mozjs/js/src/jsinttypes.h deleted file mode 100644 index 17ad539ac9f..00000000000 --- a/deps/mozjs/js/src/jsinttypes.h +++ /dev/null @@ -1,138 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * May 28, 2008. - * - * The Initial Developer of the Original Code is - * Jim Blandy - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsinttypes_h___ -#define jsinttypes_h___ - -#include "js-config.h" - -/* - * Types: - * JSInt, JSUint (for = 8, 16, 32, and 64) - * JSIntPtr, JSUIntPtr - * - * JSInt and JSUint are signed and unsigned types known to be - * bits long. Note that neither JSInt8 nor JSUInt8 is necessarily - * equivalent to a plain "char". - * - * JSIntPtr and JSUintPtr are signed and unsigned types capable of - * holding an object pointer. - * - * Use these types in public SpiderMonkey header files, not the - * corresponding types from the C standard header. Windows - * doesn't support , and Microsoft says it has no plans to - * do so in the future; this means that projects that embed - * SpiderMonkey often take matters into their own hands and define the - * standard types themselves. If we define them in our public - * headers, our definitions may conflict with embedders' (see bug - * 479258). The JS* types are in our namespace, and can be used - * without troubling anyone. - * - * Internal SpiderMonkey code wishing to use the type names ISO C - * defines in the standard header can #include - * "jsstdint.h", which provides those types regardless of whether - * itself is available. - */ - -#if defined(JS_HAVE_STDINT_H) || \ - defined(JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES) - -#if defined(JS_HAVE_STDINT_H) -#include -#else -#include -#endif - -typedef int8_t JSInt8; -typedef int16_t JSInt16; -typedef int32_t JSInt32; -typedef int64_t JSInt64; -typedef intptr_t JSIntPtr; - -typedef uint8_t JSUint8; -typedef uint16_t JSUint16; -typedef uint32_t JSUint32; -typedef uint64_t JSUint64; -typedef uintptr_t JSUintPtr; - -#else - -#if defined(JS_HAVE___INTN) - -typedef __int8 JSInt8; -typedef __int16 JSInt16; -typedef __int32 JSInt32; -typedef __int64 JSInt64; - -typedef unsigned __int8 JSUint8; -typedef unsigned __int16 JSUint16; -typedef unsigned __int32 JSUint32; -typedef unsigned __int64 JSUint64; - -#elif defined(JS_INT8_TYPE) - -typedef signed JS_INT8_TYPE JSInt8; -typedef signed JS_INT16_TYPE JSInt16; -typedef signed JS_INT32_TYPE JSInt32; -typedef signed JS_INT64_TYPE JSInt64; - -typedef unsigned JS_INT8_TYPE JSUint8; -typedef unsigned JS_INT16_TYPE JSUint16; -typedef unsigned JS_INT32_TYPE JSUint32; -typedef unsigned JS_INT64_TYPE JSUint64; - -#else -#error "couldn't find exact-width integer types" -#endif - -/* Microsoft Visual C/C++ defines intptr_t and uintptr_t in . */ -#if defined(JS_STDDEF_H_HAS_INTPTR_T) -#include -typedef intptr_t JSIntPtr; -typedef uintptr_t JSUintPtr; - -/* Failing that, the configure script will have found something. */ -#elif defined(JS_INTPTR_TYPE) -typedef signed JS_INTPTR_TYPE JSIntPtr; -typedef unsigned JS_INTPTR_TYPE JSUintPtr; - -#else -#error "couldn't find pointer-sized integer types" -#endif - -#endif /* JS_HAVE_STDINT_H */ - -#endif /* jsinttypes_h___ */ diff --git a/deps/mozjs/js/src/jsinvoke.cpp b/deps/mozjs/js/src/jsinvoke.cpp deleted file mode 100644 index 5857e41d75c..00000000000 --- a/deps/mozjs/js/src/jsinvoke.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.8 code, released - * March 4, 2008. - * - * The Initial Developer of the Original Code is - * Igor Bukanov - * - * Contributor(s): - * Brendan Eich /* for memcpy */ +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" -#include "jsarena.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" -#include "jshashtable.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" @@ -65,21 +62,24 @@ #include "jsobj.h" #include "jsopcode.h" #include "jsproxy.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" -#include "jsvector.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif +#include "ds/Sort.h" +#include "frontend/TokenStream.h" +#include "vm/GlobalObject.h" + +#include "jsinferinlines.h" #include "jsobjinlines.h" -#include "jsstrinlines.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" +using namespace mozilla; using namespace js; using namespace js::gc; @@ -87,52 +87,81 @@ static void iterator_finalize(JSContext *cx, JSObject *obj); static void iterator_trace(JSTracer *trc, JSObject *obj); static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly); -Class js_IteratorClass = { +Class js::IteratorClass = { "Iterator", JSCLASS_HAS_PRIVATE | - JSCLASS_CONCURRENT_FINALIZER | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, iterator_finalize, - NULL, /* reserved */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ + NULL, /* reserved */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ iterator_trace, { - NULL, /* equality */ - NULL, /* outerObject */ - NULL, /* innerObject */ + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ iterator_iterator, - NULL /* unused */ + NULL /* unused */ } }; +Class js::ElementIteratorClass = { + "ElementIterator", + JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* reserved */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* trace */ + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + iterator_iterator, + NULL /* unused */ + } +}; + +static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2; + void NativeIterator::mark(JSTracer *trc) { - MarkIdRange(trc, begin(), end(), "props"); + for (HeapPtr *str = begin(); str < end(); str++) + MarkString(trc, *str, "prop"); if (obj) - MarkObject(trc, *obj, "obj"); + MarkObject(trc, obj, "obj"); } static void iterator_finalize(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->getClass() == &js_IteratorClass); + JS_ASSERT(obj->isIterator()); NativeIterator *ni = obj->getNativeIterator(); if (ni) { + obj->setPrivate(NULL); cx->free_(ni); - obj->setNativeIterator(NULL); } } @@ -155,13 +184,13 @@ struct IdHashPolicy { } }; -typedef HashSet IdSet; +typedef HashSet IdSet; static inline bool NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval) { Value vec[2] = { IdToValue(id), val }; - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec); + AutoArrayRooter tvr(cx, ArrayLength(vec), vec); JSObject *aobj = NewDenseCopiedArray(cx, 2, vec); if (!aobj) @@ -172,37 +201,34 @@ NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval) static inline bool Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id, - bool enumerable, bool sharedPermanent, uintN flags, IdSet& ht, - AutoIdVector *props) + bool enumerable, uintN flags, IdSet& ht, AutoIdVector *props) { - IdSet::AddPtr p = ht.lookupForAdd(id); - JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p); - - /* If we've already seen this, we definitely won't add it. */ - if (JS_UNLIKELY(!!p)) - return true; + JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj); /* - * It's not necessary to add properties to the hash table at the end of the - * prototype chain -- but a proxy might return duplicated properties, so - * always add for them. + * We implement __proto__ using a property on |Object.prototype|, but + * because __proto__ is highly deserving of removal, we don't want it to + * show up in property enumeration, even if only for |Object.prototype| + * (think introspection by Prototype-like frameworks that add methods to + * the built-in prototypes). So exclude __proto__ if the object where the + * property was found has no [[Prototype]] and might be |Object.prototype|. */ - if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id)) - return false; + if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom))) + return true; + + if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) { + /* If we've already seen this, we definitely won't add it. */ + IdSet::AddPtr p = ht.lookupForAdd(id); + if (JS_UNLIKELY(!!p)) + return true; - if (JS_UNLIKELY(flags & JSITER_OWNONLY)) { /* - * Shared-permanent hack: If this property is shared permanent - * and pobj and obj have the same class, then treat it as an own - * property of obj, even if pobj != obj. (But see bug 575997.) - * - * Omit the magic __proto__ property so that JS code can use - * Object.getOwnPropertyNames without worrying about it. + * It's not necessary to add properties to the hash table at the end of + * the prototype chain, but custom enumeration behaviors might return + * duplicated properties, so always add in such cases. */ - if (!pobj->getProto() && id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) - return true; - if (pobj != obj && !(sharedPermanent && pobj->getClass() == obj->getClass())) - return true; + if ((pobj->getProto() || pobj->isProxy() || pobj->getOps()->enumerate) && !ht.add(p, id)) + return false; } if (enumerable || (flags & JSITER_HIDDEN)) @@ -215,16 +241,19 @@ static bool EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht, AutoIdVector *props) { + RootObject objRoot(cx, &obj); + RootObject pobjRoot(cx, &pobj); + size_t initialLength = props->length(); /* Collect all unique properties from this object's scope. */ - for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) { + Shape::Range r = pobj->lastProperty()->all(); + Shape::Range::Root root(cx, &r); + for (; !r.empty(); r.popFront()) { const Shape &shape = r.front(); - if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.id) && - !shape.isAlias() && - !Enumerate(cx, obj, pobj, shape.id, shape.enumerable(), - shape.isSharedPermanent(), flags, ht, props)) + if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) && + !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props)) { return false; } @@ -238,18 +267,18 @@ static bool EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht, AutoIdVector *props) { - if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true, + if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, flags, ht, props)) { return false; } if (pobj->getArrayLength() > 0) { - size_t capacity = pobj->getDenseArrayCapacity(); - Value *vp = pobj->getDenseArrayElements(); - for (size_t i = 0; i < capacity; ++i, ++vp) { + size_t initlen = pobj->getDenseArrayInitializedLength(); + const Value *vp = pobj->getDenseArrayElements(); + for (size_t i = 0; i < initlen; ++i, ++vp) { if (!vp->isMagic(JS_ARRAY_HOLE)) { /* Dense arrays never get so large that i would not fit into an integer id. */ - if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props)) + if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, flags, ht, props)) return false; } } @@ -258,19 +287,47 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint return true; } +#ifdef JS_MORE_DETERMINISTIC + +struct SortComparatorIds +{ + JSContext *const cx; + + SortComparatorIds(JSContext *cx) + : cx(cx) {} + + bool operator()(jsid a, jsid b, bool *lessOrEqualp) + { + /* Pick an arbitrary total order on jsids that is stable across executions. */ + JSString *astr = IdToString(cx, a); + if (!astr) + return false; + JSString *bstr = IdToString(cx, b); + if (!bstr) + return false; + + int32_t result; + if (!CompareStrings(cx, astr, bstr, &result)) + return false; + + *lessOrEqualp = (result <= 0); + return true; + } +}; + +#endif /* JS_MORE_DETERMINISTIC */ + static bool Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) { - /* - * FIXME: Bug 575997 - We won't need to initialize this hash table if - * (flags & JSITER_OWNONLY) when we eliminate inheritance of - * shared-permanent properties as own properties. - */ IdSet ht(cx); if (!ht.init(32)) return NULL; - JSObject *pobj = obj; + RootObject objRoot(cx, &obj); + RootedVarObject pobj(cx); + pobj = obj; + do { Class *clasp = pobj->getClass(); if (pobj->isNative() && @@ -288,18 +345,18 @@ Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) AutoIdVector proxyProps(cx); if (flags & JSITER_OWNONLY) { if (flags & JSITER_HIDDEN) { - if (!JSProxy::getOwnPropertyNames(cx, pobj, proxyProps)) + if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps)) return false; } else { - if (!JSProxy::keys(cx, pobj, proxyProps)) + if (!Proxy::keys(cx, pobj, proxyProps)) return false; } } else { - if (!JSProxy::enumerate(cx, pobj, proxyProps)) + if (!Proxy::enumerate(cx, pobj, proxyProps)) return false; } for (size_t n = 0, len = proxyProps.length(); n < len; n++) { - if (!Enumerate(cx, obj, pobj, proxyProps[n], true, false, flags, ht, props)) + if (!Enumerate(cx, obj, pobj, proxyProps[n], true, flags, ht, props)) return false; } /* Proxy objects enumerate the prototype on their own, so we are done here. */ @@ -319,23 +376,50 @@ Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) return false; if (state.isNull()) break; - if (!Enumerate(cx, obj, pobj, id, true, false, flags, ht, props)) + if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props)) return false; } } } - if (JS_UNLIKELY(pobj->isXML())) + if ((flags & JSITER_OWNONLY) || pobj->isXML()) break; } while ((pobj = pobj->getProto()) != NULL); +#ifdef JS_MORE_DETERMINISTIC + + /* + * In some cases the enumeration order for an object depends on the + * execution mode (interpreter vs. JIT), especially for native objects + * with a class enumerate hook (where resolving a property changes the + * resulting enumeration order). These aren't really bugs, but the + * differences can change the generated output and confuse correctness + * fuzzers, so we sort the ids if such a fuzzer is running. + * + * We don't do this in the general case because (a) doing so is slow, + * and (b) it also breaks the web, which expects enumeration order to + * follow the order in which properties are added, in certain cases. + * Since ECMA does not specify an enumeration order for objects, both + * behaviors are technically correct to do. + */ + + jsid *ids = props->begin(); + size_t n = props->length(); + + Vector tmp(cx); + if (!tmp.resizeUninitialized(n)) + return false; + + if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) + return false; + +#endif /* JS_MORE_DETERMINISTIC */ + return true; } -namespace js { - bool -VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) +js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) { JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid)); size_t len = props.length(); @@ -346,22 +430,36 @@ VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) return false; ida->length = static_cast(len); - memcpy(ida->vector, props.begin(), idsz); + jsid *v = props.begin(); + for (jsint i = 0; i < ida->length; i++) + ida->vector[i].init(v[i]); *idap = ida; return true; } JS_FRIEND_API(bool) -GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) +js::GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) { return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props); } -} +size_t sCustomIteratorCount = 0; static inline bool GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) { + JS_CHECK_RECURSION(cx, return false); + + /* + * for-of iteration does not fall back on __iterator__ or property + * enumeration. This is more conservative than the current proposed spec. + */ + if (flags == JSITER_FOR_OF) { + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE, + JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL); + return false; + } + /* Check whether we have a valid __iterator__ method. */ JSAtom *atom = cx->runtime->atomState.iteratorAtom; if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp)) @@ -373,10 +471,12 @@ GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) return true; } + if (!cx->runningWithTrustedPrincipals()) + ++sCustomIteratorCount; + /* Otherwise call it and return that object. */ - LeaveTrace(cx); Value arg = BooleanValue((flags & JSITER_FOREACH) == 0); - if (!ExternalInvoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp)) + if (!Invoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp)) return false; if (vp->isPrimitive()) { /* @@ -416,51 +516,59 @@ static inline JSObject * NewIteratorObject(JSContext *cx, uintN flags) { if (flags & JSITER_ENUMERATE) { - /* - * Non-escaping native enumerator objects do not need map, proto, or - * parent. However, code in jstracer.cpp and elsewhere may find such a - * native enumerator object via the stack and (as for all objects that - * are not stillborn, with the exception of "NoSuchMethod" internal - * helper objects) expect it to have a non-null map pointer, so we - * share an empty Enumerator scope in the runtime. - */ - JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0); - if (!obj) + RootedVarTypeObject type(cx); + type = cx->compartment->getEmptyType(cx); + if (!type) return NULL; - EmptyShape *emptyEnumeratorShape = EmptyShape::getEmptyEnumeratorShape(cx); + RootedVarShape emptyEnumeratorShape(cx); + emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL, + ITERATOR_FINALIZE_KIND); if (!emptyEnumeratorShape) return NULL; - obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false); - obj->setMap(emptyEnumeratorShape); + JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND, + emptyEnumeratorShape, type, NULL); + if (!obj) + return NULL; + + JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS); return obj; } - return NewBuiltinClassInstance(cx, &js_IteratorClass); + return NewBuiltinClassInstance(cx, &IteratorClass); } NativeIterator * -NativeIterator::allocateIterator(JSContext *cx, uint32 slength, const AutoIdVector &props) +NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props) { size_t plength = props.length(); NativeIterator *ni = (NativeIterator *) - cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32)); + cx->malloc_(sizeof(NativeIterator) + + plength * sizeof(JSString *) + + slength * sizeof(Shape *)); if (!ni) return NULL; - ni->props_array = ni->props_cursor = (jsid *) (ni + 1); - ni->props_end = (jsid *)ni->props_array + plength; - if (plength) - memcpy(ni->props_array, props.begin(), plength * sizeof(jsid)); + AutoValueVector strings(cx); + ni->props_array = ni->props_cursor = (HeapPtr *) (ni + 1); + ni->props_end = ni->props_array + plength; + if (plength) { + for (size_t i = 0; i < plength; i++) { + JSFlatString *str = IdToString(cx, props[i]); + if (!str || !strings.append(StringValue(str))) + return NULL; + ni->props_array[i].init(str); + } + } return ni; } inline void -NativeIterator::init(JSObject *obj, uintN flags, uint32 slength, uint32 key) +NativeIterator::init(JSObject *obj, uintN flags, uint32_t slength, uint32_t key) { - this->obj = obj; + this->obj.init(obj); this->flags = flags; - this->shapes_array = (uint32 *) this->props_end; + this->shapes_array = (const Shape **) this->props_end; this->shapes_length = slength; this->shapes_key = key; } @@ -480,10 +588,16 @@ RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni) static inline bool VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys, - uint32 slength, uint32 key, Value *vp) + uint32_t slength, uint32_t key, Value *vp) { JS_ASSERT(!(flags & JSITER_FOREACH)); + if (obj) { + if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) + return false; + types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + } + JSObject *iterobj = NewIteratorObject(cx, flags); if (!iterobj) return false; @@ -504,7 +618,7 @@ VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &key JSObject *pobj = obj; size_t ind = 0; do { - ni->shapes_array[ind++] = pobj->shape(); + ni->shapes_array[ind++] = pobj->lastProperty(); pobj = pobj->getProto(); } while (pobj); JS_ASSERT(ind == slength); @@ -531,6 +645,12 @@ VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &k { JS_ASSERT(flags & JSITER_FOREACH); + if (obj) { + if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) + return false; + types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + } + JSObject *iterobj = NewIteratorObject(cx, flags); if (!iterobj) return false; @@ -567,20 +687,30 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj) bool GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) { - Vector shapes(cx); - uint32 key = 0; + Vector shapes(cx); + uint32_t key = 0; bool keysOnly = (flags == JSITER_ENUMERATE); if (obj) { /* Enumerate Iterator.prototype directly. */ - JSIteratorOp op = obj->getClass()->ext.iteratorObject; - if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) { - JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); - if (!iterobj) - return false; - vp->setObject(*iterobj); - return true; + if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) { + /* + * Arrays and other classes representing iterable collections have + * the JSCLASS_FOR_OF_ITERATION flag. This flag means that the + * object responds to all other kinds of enumeration (for-in, + * for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the + * default way, ignoring the hook. The hook is used only when + * iterating in the style of a for-of loop. + */ + if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) { + JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF))); + if (!iterobj) + return false; + vp->setObject(*iterobj); + types::MarkIteratorUnknown(cx); + return true; + } } if (keysOnly) { @@ -596,9 +726,9 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) NativeIterator *lastni = last->getNativeIterator(); if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && obj->isNative() && - obj->shape() == lastni->shapes_array[0] && + obj->lastProperty() == lastni->shapes_array[0] && proto && proto->isNative() && - proto->shape() == lastni->shapes_array[1] && + proto->lastProperty() == lastni->shapes_array[1] && !proto->getProto()) { vp->setObject(*last); UpdateNativeIterator(lastni, obj); @@ -616,14 +746,15 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) JSObject *pobj = obj; do { if (!pobj->isNative() || + pobj->hasUncacheableProto() || obj->getOps()->enumerate || pobj->getClass()->enumerate != JS_EnumerateStub) { shapes.clear(); goto miss; } - uint32 shape = pobj->shape(); - key = (key + (key << 16)) ^ shape; - if (!shapes.append(shape)) + const Shape *shape = pobj->lastProperty(); + key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3); + if (!shapes.append((Shape *) shape)) return false; pobj = pobj->getProto(); } while (pobj); @@ -647,12 +778,16 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) } miss: - if (obj->isProxy()) - return JSProxy::iterate(cx, obj, flags, vp); + if (obj->isProxy()) { + types::MarkIteratorUnknown(cx); + return Proxy::iterate(cx, obj, flags, vp); + } if (!GetCustomIterator(cx, obj, flags, vp)) return false; - if (!vp->isUndefined()) + if (!vp->isUndefined()) { + types::MarkIteratorUnknown(cx); return true; + } } /* NB: for (var p in null) succeeds by iterating over no properties. */ @@ -714,21 +849,22 @@ js_ThrowStopIteration(JSContext *cx) static JSBool iterator_next(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, &ok); if (!obj) - return false; - if (obj->getClass() != &js_IteratorClass) { - ReportIncompatibleMethod(cx, vp, &js_IteratorClass); - return false; - } + return ok; - if (!js_IteratorMore(cx, obj, vp)) + if (!js_IteratorMore(cx, obj, &args.rval())) return false; - if (!vp->toBoolean()) { + + if (!args.rval().toBoolean()) { js_ThrowStopIteration(cx); return false; } - return js_IteratorNext(cx, obj, vp); + + return js_IteratorNext(cx, obj, &args.rval()); } #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) @@ -782,7 +918,7 @@ js_ValueToIterator(JSContext *cx, uintN flags, Value *vp) } #if JS_HAS_GENERATORS -static JS_REQUIRES_STACK JSBool +static JSBool CloseGenerator(JSContext *cx, JSObject *genobj); #endif @@ -791,8 +927,7 @@ js_CloseIterator(JSContext *cx, JSObject *obj) { cx->iterValue.setMagic(JS_NO_ITER_VALUE); - Class *clasp = obj->getClass(); - if (clasp == &js_IteratorClass) { + if (obj->isIterator()) { /* Remove enumerators from the active list, which is a stack. */ NativeIterator *ni = obj->getNativeIterator(); @@ -811,7 +946,7 @@ js_CloseIterator(JSContext *cx, JSObject *obj) } } #if JS_HAS_GENERATORS - else if (clasp == &js_GeneratorClass) { + else if (obj->isGenerator()) { return CloseGenerator(cx, obj); } #endif @@ -835,9 +970,9 @@ js_CloseIterator(JSContext *cx, JSObject *obj) * false. It also must have a method |matchesAtMostOne| which allows us to * stop searching after the first deletion if true. */ -template +template static bool -SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate) +SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate) { JSObject *iterobj = cx->enumerators; while (iterobj) { @@ -846,25 +981,29 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat /* This only works for identified surpressed keys, not values. */ if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) { /* Check whether id is still to come. */ - jsid *props_cursor = ni->current(); - jsid *props_end = ni->end(); - for (jsid *idp = props_cursor; idp < props_end; ++idp) { + HeapPtr *props_cursor = ni->current(); + HeapPtr *props_end = ni->end(); + for (HeapPtr *idp = props_cursor; idp < props_end; ++idp) { if (predicate(*idp)) { /* * Check whether another property along the prototype chain * became visible as a result of this deletion. */ if (obj->getProto()) { - AutoObjectRooter proto(cx, obj->getProto()); - AutoObjectRooter obj2(cx); + JSObject *proto = obj->getProto(); + JSObject *obj2; JSProperty *prop; - if (!proto.object()->lookupProperty(cx, *idp, obj2.addr(), &prop)) + jsid id; + if (!ValueToId(cx, StringValue(*idp), &id)) + return false; + id = js_CheckForStringIndex(id); + if (!proto->lookupGeneric(cx, id, &obj2, &prop)) return false; if (prop) { uintN attrs; - if (obj2.object()->isNative()) + if (obj2->isNative()) attrs = ((Shape *) prop)->attributes(); - else if (!obj2.object()->getAttributes(cx, *idp, &attrs)) + else if (!obj2->getGenericAttributes(cx, id, &attrs)) return false; if (attrs & JSPROP_ENUMERATE) @@ -887,8 +1026,15 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat if (idp == props_cursor) { ni->incCursor(); } else { - memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid)); + for (HeapPtr *p = idp; p + 1 != props_end; p++) + *p = *(p + 1); ni->props_end = ni->end() - 1; + + /* + * Invoke the write barrier on this element, since it's + * no longer going to be marked. + */ + ni->props_end->HeapPtr::~HeapPtr(); } /* Don't reuse modified native iterators. */ @@ -904,53 +1050,169 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat return true; } -class SingleIdPredicate { - jsid id; +class SingleStringPredicate { + JSFlatString *str; public: - SingleIdPredicate(jsid id) : id(id) {} + SingleStringPredicate(JSFlatString *str) : str(str) {} - bool operator()(jsid id) { return id == this->id; } + bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); } bool matchesAtMostOne() { return true; } }; bool js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) { - id = js_CheckForStringIndex(id); - return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id)); + JSFlatString *str = IdToString(cx, id); + if (!str) + return false; + return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str)); +} + +bool +js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return js_SuppressDeletedProperty(cx, obj, id); } class IndexRangePredicate { - jsint begin, end; -public: - IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {} + uint32_t begin, end; - bool operator()(jsid id) { - return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end; + public: + IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {} + + bool operator()(JSFlatString *str) { + uint32_t index; + return str->isIndex(&index) && begin <= index && index < end; } + bool matchesAtMostOne() { return false; } }; bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end) +js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end) { return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); } +const uint32_t CLOSED_INDEX = UINT32_MAX; + +JSObject * +ElementIteratorObject::create(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj); + JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj); + if (iterobj) { + iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj)); + iterobj->setReservedSlot(IndexSlot, Int32Value(0)); + } + return iterobj; +} + +inline uint32_t +ElementIteratorObject::getIndex() const +{ + return uint32_t(getReservedSlot(IndexSlot).toInt32()); +} + +inline JSObject * +ElementIteratorObject::getTargetObject() const +{ + return &getReservedSlot(TargetSlot).toObject(); +} + +inline void +ElementIteratorObject::setIndex(uint32_t index) +{ + setReservedSlot(IndexSlot, Int32Value(int32_t(index))); +} + +bool +ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp) +{ + uint32_t i, length; + JSObject *obj = getTargetObject(); + if (!js_GetLengthProperty(cx, obj, &length)) + goto error; + + i = getIndex(); + if (i >= length) { + setIndex(CLOSED_INDEX); + vp->setMagic(JS_NO_ITER_VALUE); + return true; + } + + JS_ASSERT(i + 1 > i); + + /* Simple fast path for dense arrays. */ + if (obj->isDenseArray()) { + *vp = obj->getDenseArrayElement(i); + if (vp->isMagic(JS_ARRAY_HOLE)) + vp->setUndefined(); + } else { + /* Make a jsid for this index. */ + jsid id; + if (i < uint32_t(INT32_MAX) && INT_FITS_IN_JSID(i)) { + id = INT_TO_JSID(i); + } else { + Value v = DoubleValue(i); + if (!js_ValueToStringId(cx, v, &id)) + goto error; + } + + /* Find out if this object has an element i. */ + bool has; + if (obj->isProxy()) { + /* js_HasOwnProperty does not work on proxies. */ + if (!Proxy::hasOwn(cx, obj, id, &has)) + goto error; + } else { + JSObject *obj2; + JSProperty *prop; + if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop)) + goto error; + has = !!prop; + } + + /* Populate *vp. */ + if (has) { + if (!obj->getElement(cx, obj, i, vp)) + goto error; + } else { + vp->setUndefined(); + } + } + + /* On success, bump the index. */ + setIndex(i + 1); + return true; + + error: + setIndex(CLOSED_INDEX); + return false; +} + +inline js::ElementIteratorObject * +JSObject::asElementIterator() +{ + JS_ASSERT(isElementIterator()); + return static_cast(this); +} + JSBool js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { /* Fast path for native iterators */ NativeIterator *ni = NULL; - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { /* Key iterators are handled by fast-paths. */ ni = iterobj->getNativeIterator(); - if (ni) { - bool more = ni->props_cursor < ni->props_end; - if (ni->isKeyIter() || !more) { - rval->setBoolean(more); - return true; - } + bool more = ni->props_cursor < ni->props_end; + if (ni->isKeyIter() || !more) { + rval->setBoolean(more); + return true; } } @@ -960,14 +1222,48 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) return true; } + /* We're reentering below and can call anything. */ + JS_CHECK_RECURSION(cx, return false); + /* Fetch and cache the next value from the iterator. */ - if (!ni) { + if (ni) { + JS_ASSERT(!ni->isKeyIter()); + jsid id; + if (!ValueToId(cx, StringValue(*ni->current()), &id)) + return false; + id = js_CheckForStringIndex(id); + ni->incCursor(); + if (!ni->obj->getGeneric(cx, id, rval)) + return false; + if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval)) + return false; + } else if (iterobj->isElementIterator()) { + /* + * Like native iterators, element iterators do not have a .next + * method, so this fast path is necessary for correctness. + */ + if (!iterobj->asElementIterator()->iteratorNext(cx, rval)) + return false; + if (rval->isMagic(JS_NO_ITER_VALUE)) { + cx->iterValue.setMagic(JS_NO_ITER_VALUE); + rval->setBoolean(false); + return true; + } + } else if (iterobj->isProxy()) { + if (!Proxy::iteratorNext(cx, iterobj, rval)) + return false; + if (rval->isMagic(JS_NO_ITER_VALUE)) { + rval->setBoolean(false); + return true; + } + } else { + /* Call the iterator object's .next method. */ jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval)) return false; - if (!ExternalInvoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) { + if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) { /* Check for StopIteration. */ - if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException())) + if (!cx->isExceptionPending() || !IsStopIteration(cx->getPendingException())) return false; cx->clearPendingException(); @@ -975,14 +1271,6 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) rval->setBoolean(false); return true; } - } else { - JS_ASSERT(!ni->isKeyIter()); - jsid id = *ni->current(); - ni->incCursor(); - if (!ni->obj->getProperty(cx, id, rval)) - return false; - if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval)) - return false; } /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */ @@ -996,15 +1284,15 @@ JSBool js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) { /* Fast path for native iterators */ - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { /* * Implement next directly as all the methods of the native iterator are * read-only and permanent. */ NativeIterator *ni = iterobj->getNativeIterator(); - if (ni && ni->isKeyIter()) { + if (ni->isKeyIter()) { JS_ASSERT(ni->props_cursor < ni->props_end); - *rval = IdToValue(*ni->current()); + *rval = StringValue(*ni->current()); ni->incCursor(); if (rval->isString()) @@ -1012,10 +1300,10 @@ js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) JSString *str; jsint i; - if (rval->isInt32() && JSAtom::hasIntStatic(i = rval->toInt32())) { - str = &JSAtom::intStatic(i); + if (rval->isInt32() && StaticStrings::hasInt(i = rval->toInt32())) { + str = cx->runtime->staticStrings.getInt(i); } else { - str = js_ValueToString(cx, *rval); + str = ToString(cx, *rval); if (!str) return false; } @@ -1035,27 +1323,27 @@ js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) static JSBool stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) { - *bp = js_ValueIsStopIteration(*v); + *bp = IsStopIteration(*v); return JS_TRUE; } -Class js_StopIterationClass = { +Class js::StopIterationClass = { js_StopIteration_str, JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) | JSCLASS_FREEZE_PROTO, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - NULL, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ stopiter_hasInstance }; @@ -1078,6 +1366,39 @@ generator_finalize(JSContext *cx, JSObject *obj) cx->free_(gen); } +static void +MarkGenerator(JSTracer *trc, JSGenerator *gen) +{ + StackFrame *fp = gen->floatingFrame(); + + /* + * MarkGenerator should only be called when regs is based on the floating frame. + * See calls to RebaseRegsFromTo. + */ + JS_ASSERT(size_t(gen->regs.sp - fp->slots()) <= fp->numSlots()); + + /* + * Currently, generators are not mjitted. Still, (overflow) args can be + * pushed by the mjit and need to be conservatively marked. Technically, the + * formal args and generator slots are safe for exact marking, but since the + * plan is to eventually mjit generators, it makes sense to future-proof + * this code and save someone an hour later. + */ + MarkValueRange(trc, (HeapValue *)fp->formalArgsEnd() - gen->floatingStack, + gen->floatingStack, "Generator Floating Args"); + fp->mark(trc); + MarkValueRange(trc, gen->regs.sp - fp->slots(), + (HeapValue *)fp->slots(), "Generator Floating Stack"); +} + +static void +GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen) +{ + JSCompartment *comp = cx->compartment; + if (comp->needsBarrier()) + MarkGenerator(comp->barrierTracer(), gen); +} + static void generator_trace(JSTracer *trc, JSObject *obj) { @@ -1092,46 +1413,34 @@ generator_trace(JSTracer *trc, JSObject *obj) if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) return; - StackFrame *fp = gen->floatingFrame(); - JS_ASSERT(gen->liveFrame() == fp); - - /* - * Currently, generators are not mjitted. Still, (overflow) args can be - * pushed by the mjit and need to be conservatively marked. Technically, the - * formal args and generator slots are safe for exact marking, but since the - * plan is to eventually mjit generators, it makes sense to future-proof - * this code and save someone an hour later. - */ - MarkStackRangeConservatively(trc, gen->floatingStack, fp->formalArgsEnd()); - js_TraceStackFrame(trc, fp); - MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp); + JS_ASSERT(gen->liveFrame() == gen->floatingFrame()); + MarkGenerator(trc, gen); } -Class js_GeneratorClass = { - js_Generator_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) | - JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, +Class js::GeneratorClass = { + "Generator", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, generator_finalize, - NULL, /* reserved */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ + NULL, /* reserved */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ generator_trace, { - NULL, /* equality */ - NULL, /* outerObject */ - NULL, /* innerObject */ + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ iterator_iterator, - NULL /* unused */ + NULL /* unused */ } }; @@ -1143,17 +1452,22 @@ Class js_GeneratorClass = { * from the activation in fp, so we can steal away fp->callobj and fp->argsobj * if they are non-null. */ -JS_REQUIRES_STACK JSObject * +JSObject * js_NewGenerator(JSContext *cx) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass); - if (!obj) - return NULL; - - StackFrame *stackfp = cx->fp(); + FrameRegs &stackRegs = cx->regs(); + StackFrame *stackfp = stackRegs.fp(); JS_ASSERT(stackfp->base() == cx->regs().sp); JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs()); + GlobalObject *global = &stackfp->scopeChain().global(); + JSObject *proto = global->getOrCreateGeneratorPrototype(cx); + if (!proto) + return NULL; + JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global); + if (!obj) + return NULL; + /* Load and compute stack slot counts. */ Value *stackvp = stackfp->actualArgs() - 2; uintN vplen = stackfp->formalArgsEnd() - stackvp; @@ -1163,28 +1477,30 @@ js_NewGenerator(JSContext *cx) (-1 + /* one Value included in JSGenerator */ vplen + VALUES_PER_STACK_FRAME + - stackfp->numSlots()) * sizeof(Value); + stackfp->numSlots()) * sizeof(HeapValue); + + JS_ASSERT(nbytes % sizeof(Value) == 0); + JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0); JSGenerator *gen = (JSGenerator *) cx->malloc_(nbytes); if (!gen) return NULL; + SetValueRangeToUndefined((Value *)gen, nbytes / sizeof(Value)); /* Cut up floatingStack space. */ - Value *genvp = gen->floatingStack; + HeapValue *genvp = gen->floatingStack; StackFrame *genfp = reinterpret_cast(genvp + vplen); /* Initialize JSGenerator. */ - gen->obj = obj; + gen->obj.init(obj); gen->state = JSGEN_NEWBORN; gen->enumerators = NULL; gen->floating = genfp; - /* Initialize regs stored in generator. */ - gen->regs = cx->regs(); - gen->regs.rebaseFromTo(stackfp, genfp); - - /* Copy frame off the stack. */ - genfp->stealFrameAndSlots(genvp, stackfp, stackvp, cx->regs().sp); + /* Copy from the stack to the generator's floating frame. */ + gen->regs.rebaseFromTo(stackRegs, *genfp); + genfp->stealFrameAndSlots( + genfp, genvp, stackfp, stackvp, stackRegs.sp); genfp->initFloatingGenerator(); obj->setPrivate(gen); @@ -1211,7 +1527,7 @@ typedef enum JSGeneratorOp { * Start newborn or restart yielding generator and perform the requested * operation inside its frame. */ -static JS_REQUIRES_STACK JSBool +static JSBool SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSGenerator *gen, const Value &arg) { @@ -1226,6 +1542,19 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, if (!cx->ensureGeneratorStackSpace()) return JS_FALSE; + /* + * Write barrier is needed since the generator stack can be updated, + * and it's not barriered in any other way. We need to do it before + * gen->state changes, which can cause us to trace the generator + * differently. + * + * We could optimize this by setting a bit on the generator to signify + * that it has been marked. If this bit has already been set, there is no + * need to mark again. The bit would have to be reset before the next GC, + * or else some kind of epoch scheme would have to be used. + */ + GeneratorWriteBarrierPre(cx, gen); + JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); switch (op) { case JSGENOP_NEXT: @@ -1253,54 +1582,29 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, } StackFrame *genfp = gen->floatingFrame(); - Value *genvp = gen->floatingStack; - uintN vplen = genfp->formalArgsEnd() - genvp; - StackFrame *stackfp; - Value *stackvp; JSBool ok; { - /* - * Get a pointer to new frame/slots. This memory is not "claimed", so - * the code before pushExecuteFrame must not reenter the interpreter. - */ - GeneratorFrameGuard frame; - if (!cx->stack.getGeneratorFrame(cx, vplen, genfp->numSlots(), &frame)) { + GeneratorFrameGuard gfg; + if (!cx->stack.pushGeneratorFrame(cx, gen, &gfg)) { gen->state = JSGEN_CLOSED; return JS_FALSE; } - stackfp = frame.fp(); - stackvp = frame.vp(); - - /* Copy frame onto the stack. */ - stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp); - stackfp->resetGeneratorPrev(cx); - stackfp->unsetFloatingGenerator(); - gen->regs.rebaseFromTo(genfp, stackfp); - MUST_FLOW_THROUGH("restore"); - /* Officially push frame. frame's destructor pops. */ - cx->stack.pushGeneratorFrame(gen->regs, &frame); + StackFrame *fp = gfg.fp(); + gen->regs = cx->regs(); + JS_ASSERT(gen->liveFrame() == fp); cx->enterGenerator(gen); /* OOM check above. */ JSObject *enumerators = cx->enumerators; cx->enumerators = gen->enumerators; - ok = RunScript(cx, stackfp->script(), stackfp); + ok = RunScript(cx, fp->script(), fp); gen->enumerators = cx->enumerators; cx->enumerators = enumerators; cx->leaveGenerator(gen); - - /* - * Copy the stack frame and rebase the regs, but not before popping - * the stack, since cx->regs == &gen->regs. - */ - genfp->stealFrameAndSlots(genvp, stackfp, stackvp, gen->regs.sp); - genfp->setFloatingGenerator(); } - MUST_FLOW_LABEL(restore) - gen->regs.rebaseFromTo(stackfp, genfp); if (gen->floatingFrame()->isYielding()) { /* Yield cannot fail, throw or be called on closing. */ @@ -1329,10 +1633,10 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, return JS_FALSE; } -static JS_REQUIRES_STACK JSBool +static JSBool CloseGenerator(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->getClass() == &js_GeneratorClass); + JS_ASSERT(obj->isGenerator()); JSGenerator *gen = (JSGenerator *) obj->getPrivate(); if (!gen) { @@ -1350,17 +1654,14 @@ CloseGenerator(JSContext *cx, JSObject *obj) * Common subroutine of generator_(next|send|throw|close) methods. */ static JSBool -generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc) +generator_op(JSContext *cx, Native native, JSGeneratorOp op, Value *vp, uintN argc) { - LeaveTrace(cx); + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, native, &GeneratorClass, &ok); if (!obj) - return JS_FALSE; - if (obj->getClass() != &js_GeneratorClass) { - ReportIncompatibleMethod(cx, vp, &js_GeneratorClass); - return JS_FALSE; - } + return ok; JSGenerator *gen = (JSGenerator *) obj->getPrivate(); if (!gen) { @@ -1375,18 +1676,18 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc) break; case JSGENOP_SEND: - if (argc >= 1 && !vp[2].isUndefined()) { + if (args.length() >= 1 && !args[0].isUndefined()) { js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, - JSDVG_SEARCH_STACK, vp[2], NULL); - return JS_FALSE; + JSDVG_SEARCH_STACK, args[0], NULL); + return false; } break; default: JS_ASSERT(op == JSGENOP_CLOSE); gen->state = JSGEN_CLOSED; - JS_SET_RVAL(cx, vp, UndefinedValue()); - return JS_TRUE; + args.rval().setUndefined(); + return true; } } else if (gen->state == JSGEN_CLOSED) { closed_generator: @@ -1395,45 +1696,45 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc) case JSGENOP_SEND: return js_ThrowStopIteration(cx); case JSGENOP_THROW: - cx->setPendingException(argc >= 1 ? vp[2] : UndefinedValue()); - return JS_FALSE; + cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue()); + return false; default: JS_ASSERT(op == JSGENOP_CLOSE); - JS_SET_RVAL(cx, vp, UndefinedValue()); - return JS_TRUE; + args.rval().setUndefined(); + return true; } } - bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0); - if (!SendToGenerator(cx, op, obj, gen, undef ? vp[2] : UndefinedValue())) - return JS_FALSE; + bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && args.length() != 0); + if (!SendToGenerator(cx, op, obj, gen, undef ? args[0] : UndefinedValue())) + return false; - JS_SET_RVAL(cx, vp, gen->floatingFrame()->returnValue()); - return JS_TRUE; + args.rval() = gen->floatingFrame()->returnValue(); + return true; } static JSBool generator_send(JSContext *cx, uintN argc, Value *vp) { - return generator_op(cx, JSGENOP_SEND, vp, argc); + return generator_op(cx, generator_send, JSGENOP_SEND, vp, argc); } static JSBool generator_next(JSContext *cx, uintN argc, Value *vp) { - return generator_op(cx, JSGENOP_NEXT, vp, argc); + return generator_op(cx, generator_next, JSGENOP_NEXT, vp, argc); } static JSBool generator_throw(JSContext *cx, uintN argc, Value *vp) { - return generator_op(cx, JSGENOP_THROW, vp, argc); + return generator_op(cx, generator_throw, JSGENOP_THROW, vp, argc); } static JSBool generator_close(JSContext *cx, uintN argc, Value *vp) { - return generator_op(cx, JSGENOP_CLOSE, vp, argc); + return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc); } static JSFunctionSpec generator_methods[] = { @@ -1446,30 +1747,83 @@ static JSFunctionSpec generator_methods[] = { #endif /* JS_HAS_GENERATORS */ -JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj) +static bool +InitIteratorClass(JSContext *cx, GlobalObject *global) +{ + JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass); + if (!iteratorProto) + return false; + + AutoIdVector blank(cx); + NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank); + if (!ni) + return false; + ni->init(NULL, 0 /* flags */, 0, 0); + + iteratorProto->setNativeIterator(ni); + + JSFunction *ctor = global->createConstructor(cx, Iterator, &IteratorClass, + CLASS_ATOM(cx, Iterator), 2); + if (!ctor) + return false; + + if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto)) + return false; + + if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods)) + return false; + + return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto); +} + +bool +GlobalObject::initGeneratorClass(JSContext *cx) { - JSObject *proto, *stop; +#if JS_HAS_GENERATORS + JSObject *proto = createBlankPrototype(cx, &GeneratorClass); + if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods)) + return false; + setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto)); +#endif + return true; +} - /* Idempotency required: we initialize several things, possibly lazily. */ - if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) +static JSObject * +InitStopIterationClass(JSContext *cx, GlobalObject *global) +{ + JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass); + if (!proto || !proto->freeze(cx)) return NULL; - if (stop) - return stop; - proto = js_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, - NULL, iterator_methods, NULL, NULL); - if (!proto) + /* This should use a non-JSProtoKey'd slot, but this is easier for now. */ + if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto)) return NULL; -#if JS_HAS_GENERATORS - /* Initialize the generator internals if configured. */ - if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, - NULL, generator_methods, NULL, NULL)) { + MarkStandardClassInitializedNoProto(global, &StopIterationClass); + + return proto; +} + +JSObject * +js_InitIteratorClasses(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + /* + * Bail if Iterator has already been initialized. We test for Iterator + * rather than for StopIteration because if js_InitIteratorClasses recurs, + * as happens when the StopIteration object is frozen, initializing the + * Iterator class a second time will assert. + */ + JSObject *iter; + if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter)) return NULL; - } -#endif + if (iter) + return iter; - return js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, - NULL, NULL, NULL, NULL); + if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx)) + return NULL; + return InitStopIterationClass(cx, global); } diff --git a/deps/mozjs/js/src/jsiter.h b/deps/mozjs/js/src/jsiter.h index 41086e0dbe5..85e5c0518c2 100644 --- a/deps/mozjs/js/src/jsiter.h +++ b/deps/mozjs/js/src/jsiter.h @@ -48,16 +48,8 @@ #include "jspubtd.h" #include "jsversion.h" -/* - * NB: these flag bits are encoded into the bytecode stream in the immediate - * operand of JSOP_ITER, so don't change them without advancing jsxdrapi.h's - * JSXDR_BYTECODE_VERSION. - */ -#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ -#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ -#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ -#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */ -#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ +#include "gc/Barrier.h" +#include "vm/Stack.h" /* * For cacheable native iterators, whether the iterator is currently active. @@ -69,23 +61,23 @@ namespace js { struct NativeIterator { - JSObject *obj; - jsid *props_array; - jsid *props_cursor; - jsid *props_end; - uint32 *shapes_array; - uint32 shapes_length; - uint32 shapes_key; - uint32 flags; + HeapPtrObject obj; + HeapPtr *props_array; + HeapPtr *props_cursor; + HeapPtr *props_end; + const Shape **shapes_array; + uint32_t shapes_length; + uint32_t shapes_key; + uint32_t flags; JSObject *next; /* Forms cx->enumerators list, garbage otherwise. */ bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; } - inline jsid *begin() const { + inline HeapPtr *begin() const { return props_array; } - inline jsid *end() const { + inline HeapPtr *end() const { return props_end; } @@ -93,7 +85,7 @@ struct NativeIterator { return end() - begin(); } - jsid *current() const { + HeapPtr *current() const { JS_ASSERT(props_cursor < props_end); return props_cursor; } @@ -102,19 +94,77 @@ struct NativeIterator { props_cursor = props_cursor + 1; } - static NativeIterator *allocateIterator(JSContext *cx, uint32 slength, + static NativeIterator *allocateIterator(JSContext *cx, uint32_t slength, const js::AutoIdVector &props); - void init(JSObject *obj, uintN flags, uint32 slength, uint32 key); + void init(JSObject *obj, uintN flags, uint32_t slength, uint32_t key); void mark(JSTracer *trc); }; +class ElementIteratorObject : public JSObject { + public: + enum { + TargetSlot, + IndexSlot, + NumSlots + }; + + static JSObject *create(JSContext *cx, JSObject *target); + + inline uint32_t getIndex() const; + inline void setIndex(uint32_t index); + inline JSObject *getTargetObject() const; + + /* + Array iterators are like this: + + Array.prototype[iterate] = function () { + for (var i = 0; i < (this.length >>> 0); i++) { + var desc = Object.getOwnPropertyDescriptor(this, i); + yield desc === undefined ? undefined : this[i]; + } + } + + This has the following implications: + + - Array iterators are generic; Array.prototype[iterate] can be transferred to + any other object to create iterators over it. + + - The next() method of an Array iterator is non-reentrant. Trying to reenter, + e.g. by using it on an object with a length getter that calls it.next() on + the same iterator, causes a TypeError. + + - The iterator fetches obj.length every time its next() method is called. + + - The iterator converts obj.length to a whole number using ToUint32. As a + consequence the iterator can't go on forever; it can yield at most 2^32-1 + values. Then i will be 0xffffffff, and no possible length value will be + greater than that. + + - The iterator does not skip "array holes". When it encounters a hole, it + yields undefined. + + - The iterator never consults the prototype chain. + + - If an element has a getter which throws, the exception is propagated, and + the iterator is closed (that is, all future calls to next() will simply + throw StopIteration). + + Note that if next() were reentrant, even more details of its inner + workings would be observable. + */ + + /* + * If there are any more elements to visit, store the value of the next + * element in *vp, increment the index, and return true. If not, call + * vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error. + */ + bool iteratorNext(JSContext *cx, Value *vp); +}; + bool VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap); -JS_FRIEND_API(bool) -GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector *props); - bool GetIterator(JSContext *cx, JSObject *obj, uintN flags, js::Value *vp); @@ -145,11 +195,14 @@ js_ValueToIterator(JSContext *cx, uintN flags, js::Value *vp); extern JS_FRIEND_API(JSBool) js_CloseIterator(JSContext *cx, JSObject *iterObj); -bool +extern bool js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id); -bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end); +extern bool +js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index); + +extern bool +js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end); /* * IteratorMore() indicates whether another value is available. It might @@ -179,12 +232,12 @@ typedef enum JSGeneratorState { } JSGeneratorState; struct JSGenerator { - JSObject *obj; + js::HeapPtrObject obj; JSGeneratorState state; js::FrameRegs regs; JSObject *enumerators; js::StackFrame *floating; - js::Value floatingStack[1]; + js::HeapValue floatingStack[1]; js::StackFrame *floatingFrame() { return floating; @@ -214,7 +267,6 @@ js_NewGenerator(JSContext *cx); inline js::StackFrame * js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp) { - JS_ASSERT(cx->stack.contains(fp)); if (JS_UNLIKELY(fp->isGeneratorFrame())) return cx->generatorFor(fp)->floatingFrame(); return fp; @@ -232,16 +284,6 @@ js_LiveFrameIfGenerator(js::StackFrame *fp) #endif -extern js::Class js_GeneratorClass; -extern js::Class js_IteratorClass; -extern js::Class js_StopIterationClass; - -static inline bool -js_ValueIsStopIteration(const js::Value &v) -{ - return v.isObject() && v.toObject().getClass() == &js_StopIterationClass; -} - extern JSObject * js_InitIteratorClasses(JSContext *cx, JSObject *obj); diff --git a/deps/mozjs/js/src/jskeyword.tbl b/deps/mozjs/js/src/jskeyword.tbl index 01933d2e3b5..1277aee9463 100644 --- a/deps/mozjs/js/src/jskeyword.tbl +++ b/deps/mozjs/js/src/jskeyword.tbl @@ -38,13 +38,9 @@ * * ***** END LICENSE BLOCK ***** */ -/* - * Keywords used as primary expressions, sharing the TOK_PRIMARY token kind, - * distinguished by opcode. - */ -JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) -JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) -JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) +JS_KEYWORD(false, TOK_FALSE, JSOP_FALSE, JSVERSION_DEFAULT) +JS_KEYWORD(true, TOK_TRUE, JSOP_TRUE, JSVERSION_DEFAULT) +JS_KEYWORD(null, TOK_NULL, JSOP_NULL, JSVERSION_DEFAULT) /* ES5 Keywords. */ JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) @@ -65,12 +61,12 @@ JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) +JS_KEYWORD(this, TOK_THIS, JSOP_THIS, JSVERSION_DEFAULT) JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) +JS_KEYWORD(typeof, TOK_TYPEOF, JSOP_TYPEOF, JSVERSION_DEFAULT) JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) -JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) +JS_KEYWORD(void, TOK_VOID, JSOP_VOID, JSVERSION_DEFAULT) JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) @@ -87,7 +83,7 @@ JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) * implementation to ease code migration. */ #if JS_HAS_CONST -JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) +JS_KEYWORD(const, TOK_CONST, JSOP_DEFCONST, JSVERSION_DEFAULT) #else JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) #endif diff --git a/deps/mozjs/js/src/jslibmath.h b/deps/mozjs/js/src/jslibmath.h index 8816b0524e8..53869116958 100644 --- a/deps/mozjs/js/src/jslibmath.h +++ b/deps/mozjs/js/src/jslibmath.h @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=79: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -42,9 +43,7 @@ #define _LIBMATH_H #include -#ifdef XP_WIN -# include "jsnum.h" -#endif +#include "jsnum.h" /* * Use system provided math routines. @@ -65,6 +64,12 @@ extern double js_copysign(double, double); #define js_copysign copysign #endif +#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER <= 1500 +// This is a workaround for fmod bug (http://support.microsoft.com/kb/982107) +extern "C" double js_myfmod(double x, double y); +#define fmod js_myfmod +#endif + /* Consistency wrapper for platform deviations in fmod() */ static inline double js_fmod(double d, double d2) @@ -82,5 +87,27 @@ js_fmod(double d, double d2) return fmod(d, d2); } +namespace js { + +inline double +NumberDiv(double a, double b) { + if (b == 0) { + if (a == 0 || JSDOUBLE_IS_NaN(a) +#ifdef XP_WIN + || JSDOUBLE_IS_NaN(b) /* XXX MSVC miscompiles such that (NaN == 0) */ +#endif + ) + return js_NaN; + + if (JSDOUBLE_IS_NEG(a) != JSDOUBLE_IS_NEG(b)) + return js_NegativeInfinity; + return js_PositiveInfinity; + } + + return a / b; +} + +} + #endif /* _LIBMATH_H */ diff --git a/deps/mozjs/js/src/jslock.cpp b/deps/mozjs/js/src/jslock.cpp deleted file mode 100644 index d7d68373547..00000000000 --- a/deps/mozjs/js/src/jslock.cpp +++ /dev/null @@ -1,736 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifdef JS_THREADSAFE - -/* - * JS locking stubs. - */ -#include -#include -#include "jspubtd.h" -#include "jsutil.h" -#include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" -#include "jscntxt.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsscope.h" -#include "jsstr.h" - -using namespace js; - -#define ReadWord(W) (W) - -#if !defined(__GNUC__) -# define __asm__ asm -# define __volatile__ volatile -#endif - -/* Implement NativeCompareAndSwap. */ - -#if defined(_MSC_VER) && defined(_M_IX86) -#pragma warning( disable : 4035 ) -JS_BEGIN_EXTERN_C -extern long __cdecl -_InterlockedCompareExchange(long *volatile dest, long exchange, long comp); -JS_END_EXTERN_C -#pragma intrinsic(_InterlockedCompareExchange) - -JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwapHelper(volatile jsword *w, jsword ov, jsword nv) -{ - _InterlockedCompareExchange((long*) w, nv, ov); - __asm { - sete al - } -} - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - return (NativeCompareAndSwapHelper(w, ov, nv) & 1); -} - -#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64)) -JS_BEGIN_EXTERN_C -extern long long __cdecl -_InterlockedCompareExchange64(long long *volatile dest, long long exchange, long long comp); -JS_END_EXTERN_C -#pragma intrinsic(_InterlockedCompareExchange64) - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - return _InterlockedCompareExchange64((long long *volatile)w, nv, ov) == ov; -} - -#elif defined(XP_MACOSX) || defined(DARWIN) - -#include - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - /* Details on these functions available in the manpage for atomic */ - return OSAtomicCompareAndSwapPtrBarrier(reinterpret_cast(ov), - reinterpret_cast(nv), - reinterpret_cast(w)); -} - -#elif defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC)) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgl %2, (%1)\n" - "sete %%al\n" - "andl $1, %%eax\n" - : "=a" (res) -#ifdef __SUNPRO_CC -/* Different code for Sun Studio because of a bug of SS12U1 */ - : "c" (w), "d" (nv), "a" (ov) -#else - : "r" (w), "r" (nv), "a" (ov) -#endif - : "cc", "memory"); - return (int)res; -} -#elif defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC)) - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgq %2, (%1)\n" - "sete %%al\n" - "movzbl %%al, %%eax\n" - : "=a" (res) - : "r" (w), "r" (nv), "a" (ov) - : "cc", "memory"); - return (int)res; -} - -#elif defined(__sparc) -#if defined(__GNUC__) - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "membar #StoreLoad | #LoadLoad\n" -#if JS_BITS_PER_WORD == 32 - "cas [%1],%2,%3\n" -#else - "casx [%1],%2,%3\n" -#endif - "membar #StoreLoad | #LoadLoad\n" - "cmp %2,%3\n" - "be,a 1f\n" - "mov 1,%0\n" - "mov 0,%0\n" - "1:" - : "=r" (res) - : "r" (w), "r" (ov), "r" (nv)); - return (int)res; -} - -#elif defined(__SUNPRO_CC) - -/* Implementation in lock_sparc*.il */ -extern "C" int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv); - -#endif - -#elif defined(AIX) - -#include - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - int res; - JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); - - res = compare_and_swaplp((atomic_l)w, &ov, nv); - if (res) - __asm__("isync"); - return res; -} - -#elif defined(USE_ARM_KUSER) - -/* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a - * description of this ABI; this is a function provided at a fixed - * location by the kernel in the memory space of each process. - */ -typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); -#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) - -JS_STATIC_ASSERT(sizeof(jsword) == sizeof(int)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - volatile int *vp = (volatile int *) w; - PRInt32 failed = 1; - - /* Loop until a __kernel_cmpxchg succeeds. See bug 446169 */ - do { - failed = __kernel_cmpxchg(ov, nv, vp); - } while (failed && *vp == ov); - return !failed; -} - -#elif JS_HAS_NATIVE_COMPARE_AND_SWAP - -#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction." - -#endif /* arch-tests */ - -#if JS_HAS_NATIVE_COMPARE_AND_SWAP - -JSBool -js_CompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - return !!NativeCompareAndSwap(w, ov, nv); -} - -#elif defined(NSPR_LOCK) - -# ifdef __GNUC__ -# warning "js_CompareAndSwap is implemented using NSPR lock" -# endif - -JSBool -js_CompareAndSwap(volatile jsword *w, jsword ov, jsword nv) -{ - int result; - static PRLock *CompareAndSwapLock = JS_NEW_LOCK(); - - JS_ACQUIRE_LOCK(CompareAndSwapLock); - result = (*w == ov); - if (result) - *w = nv; - JS_RELEASE_LOCK(CompareAndSwapLock); - return result; -} - -#else /* !defined(NSPR_LOCK) */ - -#error "NSPR_LOCK should be on when the platform lacks native compare-and-swap." - -#endif - -void -js_AtomicSetMask(volatile jsword *w, jsword mask) -{ - jsword ov, nv; - - do { - ov = *w; - nv = ov | mask; - } while (!js_CompareAndSwap(w, ov, nv)); -} - -void -js_AtomicClearMask(volatile jsword *w, jsword mask) -{ - jsword ov, nv; - - do { - ov = *w; - nv = ov & ~mask; - } while (!js_CompareAndSwap(w, ov, nv)); -} - -#ifndef NSPR_LOCK - -struct JSFatLock { - int susp; - PRLock *slock; - PRCondVar *svar; - JSFatLock *next; - JSFatLock **prevp; -}; - -typedef struct JSFatLockTable { - JSFatLock *free_; - JSFatLock *taken; -} JSFatLockTable; - -#define GLOBAL_LOCK_INDEX(id) (((uint32)(jsuword)(id)>>2) & global_locks_mask) - -static void -js_Dequeue(JSThinLock *); - -static PRLock **global_locks; -static uint32 global_lock_count = 1; -static uint32 global_locks_log2 = 0; -static uint32 global_locks_mask = 0; - -static void -js_LockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Lock(global_locks[i]); -} - -static void -js_UnlockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Unlock(global_locks[i]); -} - -#endif /* !NSPR_LOCK */ - -void -js_InitLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0; - tl->fat = (JSFatLock*)JS_NEW_LOCK(); -#else - PodZero(tl); -#endif -} - -void -js_FinishLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0xdeadbeef; - if (tl->fat) - JS_DESTROY_LOCK(((JSLock*)tl->fat)); -#else - JS_ASSERT(tl->owner == 0); - JS_ASSERT(tl->fat == NULL); -#endif -} - -#ifndef NSPR_LOCK - -static JSFatLock * -NewFatlock() -{ - JSFatLock *fl = (JSFatLock *) OffTheBooks::malloc_(sizeof(JSFatLock)); /* for now */ - if (!fl) return NULL; - fl->susp = 0; - fl->next = NULL; - fl->prevp = NULL; - fl->slock = PR_NewLock(); - fl->svar = PR_NewCondVar(fl->slock); - return fl; -} - -static void -DestroyFatlock(JSFatLock *fl) -{ - PR_DestroyLock(fl->slock); - PR_DestroyCondVar(fl->svar); - UnwantedForeground::free_(fl); -} - -static JSFatLock * -ListOfFatlocks(int listc) -{ - JSFatLock *m; - JSFatLock *m0; - int i; - - JS_ASSERT(listc>0); - m0 = m = NewFatlock(); - for (i=1; inext = NewFatlock(); - m = m->next; - } - return m0; -} - -static void -DeleteListOfFatlocks(JSFatLock *m) -{ - JSFatLock *m0; - for (; m; m=m0) { - m0 = m->next; - DestroyFatlock(m); - } -} - -static JSFatLockTable *fl_list_table = NULL; -static uint32 fl_list_table_len = 0; -static uint32 fl_list_chunk_len = 0; - -static JSFatLock * -GetFatlock(void *id) -{ - JSFatLock *m; - - uint32 i = GLOBAL_LOCK_INDEX(id); - if (fl_list_table[i].free_ == NULL) { -#ifdef DEBUG - if (fl_list_table[i].taken) - printf("Ran out of fat locks!\n"); -#endif - fl_list_table[i].free_ = ListOfFatlocks(fl_list_chunk_len); - } - m = fl_list_table[i].free_; - fl_list_table[i].free_ = m->next; - m->susp = 0; - m->next = fl_list_table[i].taken; - m->prevp = &fl_list_table[i].taken; - if (fl_list_table[i].taken) - fl_list_table[i].taken->prevp = &m->next; - fl_list_table[i].taken = m; - return m; -} - -static void -PutFatlock(JSFatLock *m, void *id) -{ - uint32 i; - if (m == NULL) - return; - - /* Unlink m from fl_list_table[i].taken. */ - *m->prevp = m->next; - if (m->next) - m->next->prevp = m->prevp; - - /* Insert m in fl_list_table[i].free. */ - i = GLOBAL_LOCK_INDEX(id); - m->next = fl_list_table[i].free_; - fl_list_table[i].free_ = m; -} - -#endif /* !NSPR_LOCK */ - -JSBool -js_SetupLocks(int listc, int globc) -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) - return JS_TRUE; -#ifdef DEBUG - if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ - printf("Bad number %d in js_SetupLocks()!\n", listc); - if (globc > 100 || globc < 0) /* globc == number of global locks */ - printf("Bad number %d in js_SetupLocks()!\n", listc); -#endif - global_locks_log2 = JS_CeilingLog2(globc); - global_locks_mask = JS_BITMASK(global_locks_log2); - global_lock_count = JS_BIT(global_locks_log2); - global_locks = (PRLock **) OffTheBooks::malloc_(global_lock_count * sizeof(PRLock*)); - if (!global_locks) - return JS_FALSE; - for (i = 0; i < global_lock_count; i++) { - global_locks[i] = PR_NewLock(); - if (!global_locks[i]) { - global_lock_count = i; - js_CleanupLocks(); - return JS_FALSE; - } - } - fl_list_table = (JSFatLockTable *) OffTheBooks::malloc_(i * sizeof(JSFatLockTable)); - if (!fl_list_table) { - js_CleanupLocks(); - return JS_FALSE; - } - fl_list_table_len = global_lock_count; - for (i = 0; i < global_lock_count; i++) - fl_list_table[i].free_ = fl_list_table[i].taken = NULL; - fl_list_chunk_len = listc; -#endif /* !NSPR_LOCK */ - return JS_TRUE; -} - -void -js_CleanupLocks() -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) { - for (i = 0; i < global_lock_count; i++) - PR_DestroyLock(global_locks[i]); - UnwantedForeground::free_(global_locks); - global_locks = NULL; - global_lock_count = 1; - global_locks_log2 = 0; - global_locks_mask = 0; - } - if (fl_list_table) { - for (i = 0; i < fl_list_table_len; i++) { - DeleteListOfFatlocks(fl_list_table[i].free_); - fl_list_table[i].free_ = NULL; - DeleteListOfFatlocks(fl_list_table[i].taken); - fl_list_table[i].taken = NULL; - } - UnwantedForeground::free_(fl_list_table); - fl_list_table = NULL; - fl_list_table_len = 0; - } -#endif /* !NSPR_LOCK */ -} - -#ifdef NSPR_LOCK - -static JS_ALWAYS_INLINE void -ThinLock(JSThinLock *tl, jsword me) -{ - JS_ACQUIRE_LOCK((JSLock *) tl->fat); - tl->owner = me; -} - -static JS_ALWAYS_INLINE void -ThinUnlock(JSThinLock *tl, jsword /*me*/) -{ - tl->owner = 0; - JS_RELEASE_LOCK((JSLock *) tl->fat); -} - -#else - -/* - * Fast locking and unlocking is implemented by delaying the allocation of a - * system lock (fat lock) until contention. As long as a locking thread A - * runs uncontended, the lock is represented solely by storing A's identity in - * the object being locked. - * - * If another thread B tries to lock the object currently locked by A, B is - * enqueued into a fat lock structure (which might have to be allocated and - * pointed to by the object), and suspended using NSPR conditional variables - * (wait). A wait bit (Bacon bit) is set in the lock word of the object, - * signalling to A that when releasing the lock, B must be dequeued and - * notified. - * - * The basic operation of the locking primitives (js_Lock, js_Unlock, - * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into - * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p - * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) - * succeeds this implies that p is uncontended (no one is waiting because the - * wait bit is not set). - * - * When dequeueing, the lock is released, and one of the threads suspended on - * the lock is notified. If other threads still are waiting, the wait bit is - * kept (in js_Enqueue), and if not, the fat lock is deallocated. - * - * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread - * are serialized using a global lock. For scalability, a hashtable of global - * locks is used, which is indexed modulo the thin lock pointer. - */ - -/* - * Invariants: - * (i) global lock is held - * (ii) fl->susp >= 0 - */ -static int -js_SuspendThread(JSThinLock *tl) -{ - JSFatLock *fl; - PRStatus stat; - - if (tl->fat == NULL) - fl = tl->fat = GetFatlock(tl); - else - fl = tl->fat; - JS_ASSERT(fl->susp >= 0); - fl->susp++; - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); - js_LockGlobal(tl); - fl->susp--; - if (fl->susp == 0) { - PutFatlock(fl, tl); - tl->fat = NULL; - } - return tl->fat == NULL; -} - -/* - * (i) global lock is held - * (ii) fl->susp > 0 - */ -static void -js_ResumeThread(JSThinLock *tl) -{ - JSFatLock *fl = tl->fat; - PRStatus stat; - - JS_ASSERT(fl != NULL); - JS_ASSERT(fl->susp > 0); - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_NotifyCondVar(fl->svar); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); -} - -static void -js_Enqueue(JSThinLock *tl, jsword me) -{ - jsword o, n; - - js_LockGlobal(tl); - for (;;) { - o = ReadWord(tl->owner); - n = Thin_SetWait(o); - if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) { - if (js_SuspendThread(tl)) - me = Thin_RemoveWait(me); - else - me = Thin_SetWait(me); - } - else if (NativeCompareAndSwap(&tl->owner, 0, me)) { - js_UnlockGlobal(tl); - return; - } - } -} - -static void -js_Dequeue(JSThinLock *tl) -{ - jsword o; - - js_LockGlobal(tl); - o = ReadWord(tl->owner); - JS_ASSERT(Thin_GetWait(o) != 0); - JS_ASSERT(tl->fat != NULL); - if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */ - JS_ASSERT(0); - js_ResumeThread(tl); -} - -static JS_ALWAYS_INLINE void -ThinLock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (NativeCompareAndSwap(&tl->owner, 0, me)) - return; - if (Thin_RemoveWait(ReadWord(tl->owner)) != me) - js_Enqueue(tl, me); -#ifdef DEBUG - else - JS_ASSERT(0); -#endif -} - -static JS_ALWAYS_INLINE void -ThinUnlock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - - /* - * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need - * to use a C_A_S here as well -- Arjan van de Ven 30/1/08 - */ - if (NativeCompareAndSwap(&tl->owner, me, 0)) - return; - - JS_ASSERT(Thin_GetWait(tl->owner)); - if (Thin_RemoveWait(ReadWord(tl->owner)) == me) - js_Dequeue(tl); -#ifdef DEBUG - else - JS_ASSERT(0); /* unbalanced unlock */ -#endif -} - -#endif /* !NSPR_LOCK */ - -void -js_Lock(JSContext *cx, JSThinLock *tl) -{ - ThinLock(tl, CX_THINLOCK_ID(cx)); -} - -void -js_Unlock(JSContext *cx, JSThinLock *tl) -{ - ThinUnlock(tl, CX_THINLOCK_ID(cx)); -} - -void -js_LockRuntime(JSRuntime *rt) -{ - PR_Lock(rt->rtLock); -#ifdef DEBUG - rt->rtLockOwner = js_CurrentThreadId(); -#endif -} - -void -js_UnlockRuntime(JSRuntime *rt) -{ -#ifdef DEBUG - rt->rtLockOwner = NULL; -#endif - PR_Unlock(rt->rtLock); -} - -#ifdef DEBUG -JSBool -js_IsRuntimeLocked(JSRuntime *rt) -{ - return js_CurrentThreadId() == rt->rtLockOwner; -} -#endif /* DEBUG */ -#endif /* JS_THREADSAFE */ diff --git a/deps/mozjs/js/src/jslock.h b/deps/mozjs/js/src/jslock.h index 909871c81e1..adbddd9d2ba 100644 --- a/deps/mozjs/js/src/jslock.h +++ b/deps/mozjs/js/src/jslock.h @@ -39,198 +39,49 @@ #ifndef jslock_h__ #define jslock_h__ -#include "jstypes.h" #include "jsapi.h" -#include "jsprvtd.h" #ifdef JS_THREADSAFE + # include "pratom.h" # include "prlock.h" # include "prcvar.h" # include "prthread.h" -#endif - -#ifdef JS_THREADSAFE - -#if (defined(_WIN32) && defined(_M_IX86)) || \ - (defined(_WIN64) && (defined(_M_AMD64) || defined(_M_X64))) || \ - (defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - (defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - (defined(__sparc) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - defined(AIX) || \ - defined(USE_ARM_KUSER) -# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1 -#else -# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0 -#endif - -#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP -# define NSPR_LOCK 1 -#else -# undef NSPR_LOCK -#endif - -#define Thin_GetWait(W) ((jsword)(W) & 0x1) -#define Thin_SetWait(W) ((jsword)(W) | 0x1) -#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1) - -typedef struct JSFatLock JSFatLock; - -typedef struct JSThinLock { - jsword owner; - JSFatLock *fat; -} JSThinLock; - -#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread()) -#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) - -typedef PRLock JSLock; - -/* - * Atomic increment and decrement for a reference counter, given jsrefcount *p. - * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. - */ -#define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p)) -#define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p)) -#define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v)) -#define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v)) - -#define js_CurrentThreadId() PR_GetCurrentThread() -#define JS_NEW_LOCK() PR_NewLock() -#define JS_DESTROY_LOCK(l) PR_DestroyLock(l) -#define JS_ACQUIRE_LOCK(l) PR_Lock(l) -#define JS_RELEASE_LOCK(l) PR_Unlock(l) - -#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) -#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) -#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) -#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT -#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) -#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) - -#define JS_LOCK(cx, tl) js_Lock(cx, tl) -#define JS_UNLOCK(cx, tl) js_Unlock(cx, tl) - -#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) -#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) - -extern void js_Lock(JSContext *cx, JSThinLock *tl); -extern void js_Unlock(JSContext *cx, JSThinLock *tl); -extern void js_LockRuntime(JSRuntime *rt); -extern void js_UnlockRuntime(JSRuntime *rt); -extern int js_SetupLocks(int,int); -extern void js_CleanupLocks(); -extern void js_InitLock(JSThinLock *); -extern void js_FinishLock(JSThinLock *); - -#ifdef DEBUG - -#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) - -extern JSBool js_IsRuntimeLocked(JSRuntime *rt); - -#else - -#define JS_IS_RUNTIME_LOCKED(rt) 0 +# include "prinit.h" -#endif /* DEBUG */ +# define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p)) +# define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p)) +# define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v)) +# define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v)) -#else /* !JS_THREADSAFE */ +#else /* JS_THREADSAFE */ -#define JS_ATOMIC_INCREMENT(p) (++*(p)) -#define JS_ATOMIC_DECREMENT(p) (--*(p)) -#define JS_ATOMIC_ADD(p,v) (*(p) += (v)) -#define JS_ATOMIC_SET(p,v) (*(p) = (v)) +# define JS_ATOMIC_INCREMENT(p) (++*(p)) +# define JS_ATOMIC_DECREMENT(p) (--*(p)) +# define JS_ATOMIC_ADD(p,v) (*(p) += (v)) +# define JS_ATOMIC_SET(p,v) (*(p) = (v)) -#define js_CurrentThreadId() 0 -#define JS_NEW_LOCK() NULL -#define JS_DESTROY_LOCK(l) ((void)0) -#define JS_ACQUIRE_LOCK(l) ((void)0) -#define JS_RELEASE_LOCK(l) ((void)0) -#define JS_LOCK(cx, tl) ((void)0) -#define JS_UNLOCK(cx, tl) ((void)0) +#endif /* JS_THREADSAFE */ -#define JS_NEW_CONDVAR(l) NULL -#define JS_DESTROY_CONDVAR(cv) ((void)0) -#define JS_WAIT_CONDVAR(cv,to) ((void)0) -#define JS_NOTIFY_CONDVAR(cv) ((void)0) -#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) - -#define JS_LOCK_RUNTIME(rt) ((void)0) -#define JS_UNLOCK_RUNTIME(rt) ((void)0) - -#define JS_IS_RUNTIME_LOCKED(rt) 1 - -#endif /* !JS_THREADSAFE */ - -#define JS_LOCK_RUNTIME_VOID(rt,e) \ - JS_BEGIN_MACRO \ - JS_LOCK_RUNTIME(rt); \ - e; \ - JS_UNLOCK_RUNTIME(rt); \ - JS_END_MACRO - -#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) -#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) -#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) -#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) -#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ - JS_NO_TIMEOUT) -#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) - -#ifndef JS_SET_OBJ_INFO -#define JS_SET_OBJ_INFO(obj,f,l) ((void)0) -#endif -#ifndef JS_SET_TITLE_INFO -#define JS_SET_TITLE_INFO(title,f,l) ((void)0) -#endif - -#ifdef JS_THREADSAFE - -extern JSBool -js_CompareAndSwap(volatile jsword *w, jsword ov, jsword nv); - -/* Atomically bitwise-or the mask into the word *w using compare and swap. */ -extern void -js_AtomicSetMask(volatile jsword *w, jsword mask); - -/* - * Atomically bitwise-and the complement of the mask into the word *w using - * compare and swap. - */ -extern void -js_AtomicClearMask(volatile jsword *w, jsword mask); - -#define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask) -#define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask) - -#else +namespace js { -static inline JSBool -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +class AutoAtomicIncrement { - return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE; -} - -#define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask)) -#define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask)) - -#endif - -#ifdef JS_THREADSAFE -namespace js { -class AutoLock { - private: - JSLock *lock; + int32_t *p; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: - AutoLock(JSLock *lock) : lock(lock) { JS_ACQUIRE_LOCK(lock); } - ~AutoLock() { JS_RELEASE_LOCK(lock); } + AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM) + : p(p) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_ATOMIC_INCREMENT(p); + } + + ~AutoAtomicIncrement() { + JS_ATOMIC_DECREMENT(p); + } }; + } /* namespace js */ -# define JS_AUTO_LOCK_GUARD(name, l) AutoLock name((l)); -#else -# define JS_AUTO_LOCK_GUARD(name, l) -#endif #endif /* jslock_h___ */ diff --git a/deps/mozjs/js/src/jslocko.asm b/deps/mozjs/js/src/jslocko.asm deleted file mode 100644 index 95353ba1a09..00000000000 --- a/deps/mozjs/js/src/jslocko.asm +++ /dev/null @@ -1,60 +0,0 @@ -; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*- - -; ***** BEGIN LICENSE BLOCK ***** -; Version: MPL 1.1/GPL 2.0/LGPL 2.1 -; -; The contents of this file are subject to the Mozilla Public License Version -; 1.1 (the "License"); you may not use this file except in compliance with -; the License. You may obtain a copy of the License at -; http://www.mozilla.org/MPL/ -; -; Software distributed under the License is distributed on an "AS IS" basis, -; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -; for the specific language governing rights and limitations under the -; License. -; -; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly. -; -; The Initial Developer of the Original Code is -; IBM Corporation. -; Portions created by the Initial Developer are Copyright (C) 2001 -; the Initial Developer. All Rights Reserved. -; -; Contributor(s): -; -; Alternatively, the contents of this file may be used under the terms of -; either the GNU General Public License Version 2 or later (the "GPL"), or -; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -; in which case the provisions of the GPL or the LGPL are applicable instead -; of those above. If you wish to allow use of your version of this file only -; under the terms of either the GPL or the LGPL, and not to allow others to -; use your version of this file under the terms of the MPL, indicate your -; decision by deleting the provisions above and replace them with the notice -; and other provisions required by the GPL or the LGPL. If you do not delete -; the provisions above, a recipient may use your version of this file under -; the terms of any one of the MPL, the GPL or the LGPL. -; -; ***** END LICENSE BLOCK ***** - - .486P - .MODEL FLAT, OPTLINK - .STACK - - .CODE - -;;;--------------------------------------------------------------------- -;;; int _Optlink js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -;;;--------------------------------------------------------------------- -js_CompareAndSwap PROC OPTLINK EXPORT - push ebx - mov ebx, eax - mov eax, edx - mov edx, ebx - lock cmpxchg [ebx], ecx - sete al - and eax, 1h - pop ebx - ret -js_CompareAndSwap endp - - END diff --git a/deps/mozjs/js/src/jslog2.cpp b/deps/mozjs/js/src/jslog2.cpp index 5241a97a57b..6efd8303b26 100644 --- a/deps/mozjs/js/src/jslog2.cpp +++ b/deps/mozjs/js/src/jslog2.cpp @@ -36,8 +36,6 @@ * * ***** END LICENSE BLOCK ***** */ -#include "jsstdint.h" -#include "jsbit.h" #include "jsutil.h" /* @@ -46,40 +44,26 @@ * systems. */ #ifdef JS_HAS_BUILTIN_BITSCAN32 -JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); +JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(uint32_t)); JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 4, - sizeof(unsigned int) == sizeof(JSUword)); + sizeof(unsigned int) == sizeof(uintptr_t)); #endif #ifdef JS_HAS_BUILTIN_BITSCAN64 JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 8, - sizeof(unsigned long long) == sizeof(JSUword)); + sizeof(unsigned long long) == sizeof(uintptr_t)); #endif -/* - * Compute the log of the least power of 2 greater than or equal to n - */ -JS_PUBLIC_API(JSIntn) -JS_CeilingLog2(JSUint32 n) -{ - JSIntn log2; - - JS_CEILING_LOG2(log2, n); - return log2; -} +#if !defined(JS_HAS_BUILTIN_BITSCAN32) && JS_BYTES_PER_WORD == 4 -/* - * Compute the log of the greatest power of 2 less than or equal to n. - * This really just finds the highest set bit in the word. - */ -JS_PUBLIC_API(JSIntn) -JS_FloorLog2(JSUint32 n) +size_t +js_FloorLog2wImpl(size_t n) { - JSIntn log2; + size_t log2; JS_FLOOR_LOG2(log2, n); return log2; } - +#endif /* * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. */ diff --git a/deps/mozjs/js/src/jslong.h b/deps/mozjs/js/src/jslong.h deleted file mode 100644 index 4a1fccc9ea3..00000000000 --- a/deps/mozjs/js/src/jslong.h +++ /dev/null @@ -1,167 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -** File: jslong.h -** Description: Portable access to 64 bit numerics -** -** Long-long (64-bit signed integer type) support. Some C compilers -** don't support 64 bit integers yet, so we use these macros to -** support both machines that do and don't. -**/ -#ifndef jslong_h___ -#define jslong_h___ - -#include "jstypes.h" - -JS_BEGIN_EXTERN_C - -#define JSLL_INIT(hi, lo) ((((JSInt64)(hi)) << 32) + (JSInt64)(lo)) - -/*********************************************************************** -** MACROS: JSLL_* -** DESCRIPTION: -** The following macros define portable access to the 64 bit -** math facilities. -** -***********************************************************************/ - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_IS_ZERO Test for zero -** JSLL_EQ Test for equality -** JSLL_NE Test for inequality -** JSLL_GE_ZERO Test for zero or positive -** JSLL_CMP Compare two values -***********************************************************************/ -#define JSLL_IS_ZERO(a) ((a) == 0) -#define JSLL_EQ(a, b) ((a) == (b)) -#define JSLL_NE(a, b) ((a) != (b)) -#define JSLL_GE_ZERO(a) ((a) >= 0) -#define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) -#define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_AND Logical and -** JSLL_OR Logical or -** JSLL_XOR Logical exclusion -** JSLL_OR2 A disgusting deviation -** JSLL_NOT Negation (one's compliment) -***********************************************************************/ -#define JSLL_AND(r, a, b) ((r) = (a) & (b)) -#define JSLL_OR(r, a, b) ((r) = (a) | (b)) -#define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) -#define JSLL_OR2(r, a) ((r) = (r) | (a)) -#define JSLL_NOT(r, a) ((r) = ~(a)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_NEG Negation (two's compliment) -** JSLL_ADD Summation (two's compliment) -** JSLL_SUB Difference (two's compliment) -***********************************************************************/ -#define JSLL_NEG(r, a) ((r) = -(a)) -#define JSLL_ADD(r, a, b) ((r) = (a) + (b)) -#define JSLL_SUB(r, a, b) ((r) = (a) - (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_MUL Product (two's compliment) -** JSLL_DIV Quotient (two's compliment) -** JSLL_MOD Modulus (two's compliment) -***********************************************************************/ -#define JSLL_MUL(r, a, b) ((r) = (a) * (b)) -#define JSLL_DIV(r, a, b) ((r) = (a) / (b)) -#define JSLL_MOD(r, a, b) ((r) = (a) % (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_SHL Shift left [0..64] bits -** JSLL_SHR Shift right [0..64] bits with sign extension -** JSLL_USHR Unsigned shift right [0..64] bits -** JSLL_ISHL Signed shift left [0..64] bits -***********************************************************************/ -#define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) -#define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) -#define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) -#define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_L2I Convert to signed 32 bit -** JSLL_L2UI Convert to unsigned 32 bit -** JSLL_L2F Convert to floating point -** JSLL_L2D Convert to floating point -** JSLL_I2L Convert signed to 64 bit -** JSLL_UI2L Convert unsigned to 64 bit -** JSLL_F2L Convert float to 64 bit -** JSLL_D2L Convert float to 64 bit -***********************************************************************/ -#define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) -#define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) -#define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) -#define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) - -#define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) -#define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) -#define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) -#define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) - -/*********************************************************************** -** MACROS: JSLL_UDIVMOD -** DESCRIPTION: -** Produce both a quotient and a remainder given an unsigned -** INPUTS: JSUint64 a: The dividend of the operation -** JSUint64 b: The quotient of the operation -** OUTPUTS: JSUint64 *qp: pointer to quotient -** JSUint64 *rp: pointer to remainder -***********************************************************************/ -#define JSLL_UDIVMOD(qp, rp, a, b) \ - (*(qp) = ((JSUint64)(a) / (b)), \ - *(rp) = ((JSUint64)(a) % (b))) - -JS_END_EXTERN_C - -#endif /* jslong_h___ */ diff --git a/deps/mozjs/js/src/jsmath.cpp b/deps/mozjs/js/src/jsmath.cpp index 8901c7e3a96..bf6087cd0fd 100644 --- a/deps/mozjs/js/src/jsmath.cpp +++ b/deps/mozjs/js/src/jsmath.cpp @@ -42,12 +42,9 @@ */ #include #include "jstypes.h" -#include "jsstdint.h" -#include "jslong.h" #include "prmjtime.h" #include "jsapi.h" #include "jsatom.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" #include "jslock.h" @@ -56,6 +53,9 @@ #include "jslibmath.h" #include "jscompartment.h" +#include "jsinferinlines.h" +#include "jsobjinlines.h" + using namespace js; #ifndef M_E @@ -104,16 +104,16 @@ MathCache::MathCache() { JS_ASSERT(hash(-0.0) != hash(+0.0)); } -Class js_MathClass = { +Class js::MathClass = { js_Math_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Math), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub }; JSBool @@ -125,7 +125,7 @@ js_math_abs(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; z = fabs(x); vp->setNumber(z); @@ -141,7 +141,7 @@ math_acos(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; #if defined(SOLARIS) && defined(__GNUC__) if (x < -1 || 1 < x) { @@ -166,7 +166,7 @@ math_asin(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; #if defined(SOLARIS) && defined(__GNUC__) if (x < -1 || 1 < x) { @@ -191,7 +191,7 @@ math_atan(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -240,9 +240,7 @@ math_atan2(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) - return JS_FALSE; - if (!ValueToNumber(cx, vp[3], &y)) + if (!ToNumber(cx, vp[2], &x) || !ToNumber(cx, vp[3], &y)) return JS_FALSE; z = math_atan2_kernel(x, y); vp->setDouble(z); @@ -268,7 +266,7 @@ js_math_ceil(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; z = js_math_ceil_impl(x); vp->setNumber(z); @@ -284,7 +282,7 @@ math_cos(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -317,7 +315,7 @@ math_exp(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -342,7 +340,7 @@ js_math_floor(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; z = js_math_floor_impl(x); vp->setNumber(z); @@ -358,7 +356,7 @@ math_log(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; #if defined(SOLARIS) && defined(__GNUC__) if (x < 0) { @@ -387,7 +385,7 @@ js_math_max(JSContext *cx, uintN argc, Value *vp) } argv = vp + 2; for (i = 0; i < argc; i++) { - if (!ValueToNumber(cx, argv[i], &x)) + if (!ToNumber(cx, argv[i], &x)) return JS_FALSE; if (JSDOUBLE_IS_NaN(x)) { vp->setDouble(js_NaN); @@ -417,7 +415,7 @@ js_math_min(JSContext *cx, uintN argc, Value *vp) } argv = vp + 2; for (i = 0; i < argc; i++) { - if (!ValueToNumber(cx, argv[i], &x)) + if (!ToNumber(cx, argv[i], &x)) return JS_FALSE; if (JSDOUBLE_IS_NaN(x)) { vp->setDouble(js_NaN); @@ -462,8 +460,8 @@ powi(jsdouble x, jsint y) } } -static JSBool -math_pow(JSContext *cx, uintN argc, Value *vp) +JSBool +js_math_pow(JSContext *cx, uintN argc, Value *vp) { jsdouble x, y, z; @@ -471,9 +469,7 @@ math_pow(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) - return JS_FALSE; - if (!ValueToNumber(cx, vp[3], &y)) + if (!ToNumber(cx, vp[2], &x) || !ToNumber(cx, vp[3], &y)) return JS_FALSE; /* * Special case for square roots. Note that pow(x, 0.5) != sqrt(x) @@ -512,18 +508,18 @@ math_pow(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } -static const int64 RNG_MULTIPLIER = 0x5DEECE66DLL; -static const int64 RNG_ADDEND = 0xBLL; -static const int64 RNG_MASK = (1LL << 48) - 1; +static const int64_t RNG_MULTIPLIER = 0x5DEECE66DLL; +static const int64_t RNG_ADDEND = 0xBLL; +static const int64_t RNG_MASK = (1LL << 48) - 1; static const jsdouble RNG_DSCALE = jsdouble(1LL << 53); /* * Math.random() support, lifted from java.util.Random.java. */ -static inline void -random_setSeed(JSContext *cx, int64 seed) +extern void +random_setSeed(int64_t *rngSeed, int64_t seed) { - cx->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK; + *rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK; } void @@ -535,26 +531,24 @@ js_InitRandom(JSContext *cx) * the context and its successor. We don't just use the context because it might be * possible to reverse engineer the context pointer if one guesses the time right. */ - random_setSeed(cx, - (PRMJ_Now() / 1000) ^ - int64(cx) ^ - int64(cx->link.next)); + random_setSeed(&cx->rngSeed, (PRMJ_Now() / 1000) ^ int64_t(cx) ^ int64_t(cx->link.next)); } -static inline uint64 -random_next(JSContext *cx, int bits) +extern uint64_t +random_next(int64_t *rngSeed, int bits) { - uint64 nextseed = cx->rngSeed * RNG_MULTIPLIER; + uint64_t nextseed = *rngSeed * RNG_MULTIPLIER; nextseed += RNG_ADDEND; nextseed &= RNG_MASK; - cx->rngSeed = nextseed; + *rngSeed = nextseed; return nextseed >> (48 - bits); } static inline jsdouble random_nextDouble(JSContext *cx) { - return jsdouble((random_next(cx, 26) << 27) + random_next(cx, 27)) / RNG_DSCALE; + return jsdouble((random_next(&cx->rngSeed, 26) << 27) + random_next(&cx->rngSeed, 27)) / + RNG_DSCALE; } static JSBool @@ -580,26 +574,39 @@ js_copysign(double x, double y) } #endif -jsdouble -js_math_round_impl(jsdouble x) -{ - return js_copysign(floor(x + 0.5), x); -} -JSBool +JSBool /* ES5 15.8.2.15. */ js_math_round(JSContext *cx, uintN argc, Value *vp) { - jsdouble x, z; + CallArgs args = CallArgsFromVp(argc, vp); - if (argc == 0) { - vp->setDouble(js_NaN); - return JS_TRUE; + if (args.length() == 0) { + args.rval().setDouble(js_NaN); + return true; } - if (!ValueToNumber(cx, vp[2], &x)) - return JS_FALSE; - z = js_copysign(floor(x + 0.5), x); - vp->setNumber(z); - return JS_TRUE; + + double x; + if (!ToNumber(cx, args[0], &x)) + return false; + + int32_t i; + if (JSDOUBLE_IS_INT32(x, &i)) { + args.rval().setInt32(i); + return true; + } + + jsdpun u; + u.d = x; + + /* Some numbers are so big that adding 0.5 would give the wrong number */ + int exponent = ((u.s.hi & JSDOUBLE_HI32_EXPMASK) >> JSDOUBLE_HI32_EXPSHIFT) - JSDOUBLE_EXPBIAS; + if (exponent >= 52) { + args.rval().setNumber(x); + return true; + } + + args.rval().setNumber(js_copysign(floor(x + 0.5), x)); + return true; } static JSBool @@ -611,7 +618,7 @@ math_sin(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -621,8 +628,8 @@ math_sin(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } -static JSBool -math_sqrt(JSContext *cx, uintN argc, Value *vp) +JSBool +js_math_sqrt(JSContext *cx, uintN argc, Value *vp) { jsdouble x, z; @@ -630,7 +637,7 @@ math_sqrt(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -649,7 +656,7 @@ math_tan(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; MathCache *mathCache = GetMathCache(cx); if (!mathCache) @@ -668,194 +675,33 @@ math_toSource(JSContext *cx, uintN argc, Value *vp) } #endif -#ifdef JS_TRACER - -#define MATH_BUILTIN_1(name, cfun) \ - static jsdouble FASTCALL name##_tn(MathCache *cache, jsdouble d) { \ - return cache->lookup(cfun, d); \ - } \ - JS_DEFINE_TRCINFO_1(name, \ - (2, (static, DOUBLE, name##_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) - -MATH_BUILTIN_1(js_math_abs, fabs) -MATH_BUILTIN_1(math_atan, atan) -MATH_BUILTIN_1(math_sin, sin) -MATH_BUILTIN_1(math_cos, cos) -MATH_BUILTIN_1(math_sqrt, sqrt) -MATH_BUILTIN_1(math_tan, tan) - -static jsdouble FASTCALL -math_acos_tn(MathCache *cache, jsdouble d) -{ -#if defined(SOLARIS) && defined(__GNUC__) - if (d < -1 || 1 < d) { - return js_NaN; - } -#endif - return cache->lookup(acos, d); -} - -static jsdouble FASTCALL -math_asin_tn(MathCache *cache, jsdouble d) -{ -#if defined(SOLARIS) && defined(__GNUC__) - if (d < -1 || 1 < d) { - return js_NaN; - } -#endif - return cache->lookup(asin, d); -} - -static jsdouble FASTCALL -math_exp_tn(MathCache *cache, jsdouble d) -{ - return cache->lookup(math_exp_body, d); -} - -JS_DEFINE_TRCINFO_1(math_exp, - (2, (static, DOUBLE, math_exp_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) - -static jsdouble FASTCALL -math_log_tn(MathCache *cache, jsdouble d) -{ -#if defined(SOLARIS) && defined(__GNUC__) - if (d < 0) - return js_NaN; -#endif - return cache->lookup(log, d); -} - -static jsdouble FASTCALL -math_max_tn(jsdouble d, jsdouble p) -{ - if (JSDOUBLE_IS_NaN(d) || JSDOUBLE_IS_NaN(p)) - return js_NaN; - - if (p == 0 && p == d) { - // Max prefers 0.0 to -0.0. - if (js_copysign(1.0, d) == -1) - return p; - return d; - } - return (p > d) ? p : d; -} - -static jsdouble FASTCALL -math_min_tn(jsdouble d, jsdouble p) -{ - if (JSDOUBLE_IS_NaN(d) || JSDOUBLE_IS_NaN(p)) - return js_NaN; - - if (p == 0 && p == d) { - // Min prefers -0.0 to 0.0. - if (js_copysign (1.0, p) == -1) - return p; - return d; - } - return (p < d) ? p : d; -} - -static jsdouble FASTCALL -math_pow_tn(jsdouble d, jsdouble p) -{ - /* - * Special case for square roots. Note that pow(x, 0.5) != sqrt(x) - * when x = -0.0, so we have to guard for this. - */ - if (JSDOUBLE_IS_FINITE(d) && d != 0.0) { - if (p == 0.5) - return sqrt(d); - - if (p == -0.5) - return 1.0/sqrt(d); - } - if (!JSDOUBLE_IS_FINITE(p) && (d == 1.0 || d == -1.0)) - return js_NaN; - if (p == 0) - return 1.0; - int32_t i; - if (JSDOUBLE_IS_INT32(p, &i)) - return powi(d, i); - - return pow(d, p); -} - -static jsdouble FASTCALL -math_random_tn(JSContext *cx) -{ - return random_nextDouble(cx); -} - -static jsdouble FASTCALL -math_round_tn(jsdouble x) -{ - return js_math_round_impl(x); -} - -static jsdouble FASTCALL -math_ceil_tn(jsdouble x) -{ - return js_math_ceil_impl(x); -} - -static jsdouble FASTCALL -math_floor_tn(jsdouble x) -{ - return js_math_floor_impl(x); -} - -JS_DEFINE_TRCINFO_1(math_acos, - (2, (static, DOUBLE, math_acos_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(math_asin, - (2, (static, DOUBLE, math_asin_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(math_atan2, - (2, (static, DOUBLE, math_atan2_kernel, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(js_math_floor, - (1, (static, DOUBLE, math_floor_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(math_log, - (2, (static, DOUBLE, math_log_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(js_math_max, - (2, (static, DOUBLE, math_max_tn, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(js_math_min, - (2, (static, DOUBLE, math_min_tn, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(math_pow, - (2, (static, DOUBLE, math_pow_tn, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(math_random, - (1, (static, DOUBLE, math_random_tn, CONTEXT, 0, nanojit::ACCSET_STORE_ANY))) -JS_DEFINE_TRCINFO_1(js_math_round, - (1, (static, DOUBLE, math_round_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) -JS_DEFINE_TRCINFO_1(js_math_ceil, - (1, (static, DOUBLE, math_ceil_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) - -#endif /* JS_TRACER */ - static JSFunctionSpec math_static_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, math_toSource, 0, 0), #endif - JS_TN("abs", js_math_abs, 1, 0, &js_math_abs_trcinfo), - JS_TN("acos", math_acos, 1, 0, &math_acos_trcinfo), - JS_TN("asin", math_asin, 1, 0, &math_asin_trcinfo), - JS_TN("atan", math_atan, 1, 0, &math_atan_trcinfo), - JS_TN("atan2", math_atan2, 2, 0, &math_atan2_trcinfo), - JS_TN("ceil", js_math_ceil, 1, 0, &js_math_ceil_trcinfo), - JS_TN("cos", math_cos, 1, 0, &math_cos_trcinfo), - JS_TN("exp", math_exp, 1, 0, &math_exp_trcinfo), - JS_TN("floor", js_math_floor, 1, 0, &js_math_floor_trcinfo), - JS_TN("log", math_log, 1, 0, &math_log_trcinfo), - JS_TN("max", js_math_max, 2, 0, &js_math_max_trcinfo), - JS_TN("min", js_math_min, 2, 0, &js_math_min_trcinfo), - JS_TN("pow", math_pow, 2, 0, &math_pow_trcinfo), - JS_TN("random", math_random, 0, 0, &math_random_trcinfo), - JS_TN("round", js_math_round, 1, 0, &js_math_round_trcinfo), - JS_TN("sin", math_sin, 1, 0, &math_sin_trcinfo), - JS_TN("sqrt", math_sqrt, 1, 0, &math_sqrt_trcinfo), - JS_TN("tan", math_tan, 1, 0, &math_tan_trcinfo), + JS_FN("abs", js_math_abs, 1, 0), + JS_FN("acos", math_acos, 1, 0), + JS_FN("asin", math_asin, 1, 0), + JS_FN("atan", math_atan, 1, 0), + JS_FN("atan2", math_atan2, 2, 0), + JS_FN("ceil", js_math_ceil, 1, 0), + JS_FN("cos", math_cos, 1, 0), + JS_FN("exp", math_exp, 1, 0), + JS_FN("floor", js_math_floor, 1, 0), + JS_FN("log", math_log, 1, 0), + JS_FN("max", js_math_max, 2, 0), + JS_FN("min", js_math_min, 2, 0), + JS_FN("pow", js_math_pow, 2, 0), + JS_FN("random", math_random, 0, 0), + JS_FN("round", js_math_round, 1, 0), + JS_FN("sin", math_sin, 1, 0), + JS_FN("sqrt", js_math_sqrt, 1, 0), + JS_FN("tan", math_tan, 1, 0), JS_FS_END }; bool -js_IsMathFunction(JSNative native) +js_IsMathFunction(Native native) { for (size_t i=0; math_static_methods[i].name != NULL; i++) { if (native == math_static_methods[i].call) @@ -867,11 +713,10 @@ js_IsMathFunction(JSNative native) JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { - JSObject *Math; - - Math = JS_NewObject(cx, Jsvalify(&js_MathClass), NULL, obj); - if (!Math) + JSObject *Math = NewObjectWithClassProto(cx, &MathClass, NULL, obj); + if (!Math || !Math->setSingletonType(cx)) return NULL; + if (!JS_DefineProperty(cx, obj, js_Math_str, OBJECT_TO_JSVAL(Math), JS_PropertyStub, JS_StrictPropertyStub, 0)) { return NULL; @@ -882,7 +727,7 @@ js_InitMathClass(JSContext *cx, JSObject *obj) if (!JS_DefineConstDoubles(cx, Math, math_constants)) return NULL; - MarkStandardClassInitializedNoProto(obj, &js_MathClass); + MarkStandardClassInitializedNoProto(obj, &MathClass); return Math; } diff --git a/deps/mozjs/js/src/jsmath.h b/deps/mozjs/js/src/jsmath.h index b6e059d3b1a..9e2efa69fcf 100644 --- a/deps/mozjs/js/src/jsmath.h +++ b/deps/mozjs/js/src/jsmath.h @@ -55,9 +55,9 @@ class MathCache MathCache(); uintN hash(double x) { - union { double d; struct { uint32 one, two; } s; } u = { x }; - uint32 hash32 = u.s.one ^ u.s.two; - uint16 hash16 = (uint16)(hash32 ^ (hash32 >> 16)); + union { double d; struct { uint32_t one, two; } s; } u = { x }; + uint32_t hash32 = u.s.one ^ u.s.two; + uint16_t hash16 = uint16_t(hash32 ^ (hash32 >> 16)); return (hash16 & (Size - 1)) ^ (hash16 >> (16 - SizeLog2)); } @@ -82,8 +82,6 @@ class MathCache * JS math functions. */ -extern js::Class js_MathClass; - extern JSObject * js_InitMathClass(JSContext *cx, JSObject *obj); @@ -111,13 +109,16 @@ js_math_min(JSContext *cx, uintN argc, js::Value *vp); extern JSBool js_math_round(JSContext *cx, uintN argc, js::Value *vp); +extern JSBool +js_math_sqrt(JSContext *cx, uintN argc, js::Value *vp); + +extern JSBool +js_math_pow(JSContext *cx, uintN argc, js::Value *vp); + extern jsdouble js_math_ceil_impl(jsdouble x); extern jsdouble js_math_floor_impl(jsdouble x); -extern jsdouble -js_math_round_impl(jsdouble x); - #endif /* jsmath_h___ */ diff --git a/deps/mozjs/js/src/jsnativestack.h b/deps/mozjs/js/src/jsnativestack.h index f8c4991dd7e..2f41b643430 100644 --- a/deps/mozjs/js/src/jsnativestack.h +++ b/deps/mozjs/js/src/jsnativestack.h @@ -47,12 +47,12 @@ namespace js { extern void * GetNativeStackBaseImpl(); -inline jsuword * +inline uintptr_t GetNativeStackBase() { - void *stackBase = GetNativeStackBaseImpl(); - JS_ASSERT(reinterpret_cast(stackBase) % sizeof(void *) == 0); - return static_cast(stackBase); + uintptr_t stackBase = reinterpret_cast(GetNativeStackBaseImpl()); + JS_ASSERT(stackBase % sizeof(void *) == 0); + return stackBase; } } /* namespace js */ diff --git a/deps/mozjs/js/src/jsnum.cpp b/deps/mozjs/js/src/jsnum.cpp index 73fb0c0942b..2dd2471e9ce 100644 --- a/deps/mozjs/js/src/jsnum.cpp +++ b/deps/mozjs/js/src/jsnum.cpp @@ -51,12 +51,13 @@ #include #include #include + +#include "mozilla/RangedPtr.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jsapi.h" #include "jsatom.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdtoa.h" @@ -68,41 +69,21 @@ #include "jsprf.h" #include "jsscope.h" #include "jsstr.h" -#include "jstracer.h" -#include "jsvector.h" +#include "jslibmath.h" + +#include "vm/GlobalObject.h" -#include "jsinterpinlines.h" +#include "jsatominlines.h" +#include "jsinferinlines.h" +#include "jsnuminlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" -using namespace js; +#include "vm/NumberObject-inl.h" +#include "vm/String-inl.h" -#ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */ - -JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX); -JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX); -JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX); -JS_STATIC_ASSERT(uint64_t(-1) == UINT64_MAX); - -JS_STATIC_ASSERT(INT8_MAX > INT8_MIN); -JS_STATIC_ASSERT(uint8_t(INT8_MAX) + uint8_t(1) == uint8_t(INT8_MIN)); -JS_STATIC_ASSERT(INT16_MAX > INT16_MIN); -JS_STATIC_ASSERT(uint16_t(INT16_MAX) + uint16_t(1) == uint16_t(INT16_MIN)); -JS_STATIC_ASSERT(INT32_MAX > INT32_MIN); -JS_STATIC_ASSERT(uint32_t(INT32_MAX) + uint32_t(1) == uint32_t(INT32_MIN)); -JS_STATIC_ASSERT(INT64_MAX > INT64_MIN); -JS_STATIC_ASSERT(uint64_t(INT64_MAX) + uint64_t(1) == uint64_t(INT64_MIN)); - -JS_STATIC_ASSERT(INTPTR_MAX > INTPTR_MIN); -JS_STATIC_ASSERT(uintptr_t(INTPTR_MAX) + uintptr_t(1) == uintptr_t(INTPTR_MIN)); -JS_STATIC_ASSERT(uintptr_t(-1) == UINTPTR_MAX); -JS_STATIC_ASSERT(size_t(-1) == SIZE_MAX); -JS_STATIC_ASSERT(PTRDIFF_MAX > PTRDIFF_MIN); -JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MAX) == PTRDIFF_MAX); -JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MIN) == PTRDIFF_MIN); -JS_STATIC_ASSERT(uintptr_t(PTRDIFF_MAX) + uintptr_t(1) == uintptr_t(PTRDIFF_MIN)); - -#endif /* JS_HAVE_STDINT_H */ +using namespace js; +using namespace js::types; /* * If we're accumulating a decimal number and the number is >= 2^53, then the @@ -126,7 +107,7 @@ ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar * char *estr; int err = 0; - *dp = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err); + *dp = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err); if (err == JS_DTOA_ENOMEM) { JS_ReportOutOfMemory(cx); cx->free_(cstr); @@ -281,7 +262,7 @@ num_isNaN(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } jsdouble x; - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return false; vp->setBoolean(JSDOUBLE_IS_NaN(x)); return JS_TRUE; @@ -295,7 +276,7 @@ num_isFinite(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } jsdouble x; - if (!ValueToNumber(cx, vp[2], &x)) + if (!ToNumber(cx, vp[2], &x)) return JS_FALSE; vp->setBoolean(JSDOUBLE_IS_FINITE(x)); return JS_TRUE; @@ -312,7 +293,7 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) vp->setDouble(js_NaN); return JS_TRUE; } - str = js_ValueToString(cx, vp[2]); + str = ToString(cx, vp[2]); if (!str) return JS_FALSE; bp = str->getChars(cx); @@ -329,27 +310,6 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } -#ifdef JS_TRACER -static jsdouble FASTCALL -ParseFloat(JSContext* cx, JSString* str) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - const jschar *bp = str->getChars(cx); - if (!bp) { - SetBuiltinError(tm); - return js_NaN; - } - const jschar *end = bp + str->length(); - - const jschar *ep; - double d; - if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) - return js_NaN; - return d; -} -#endif - static bool ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int maybeRadix, bool stripPrefix, jsdouble *dp) @@ -357,7 +317,7 @@ ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int may JS_ASSERT(maybeRadix == 0 || (2 <= maybeRadix && maybeRadix <= 36)); JS_ASSERT(ws <= end); - const jschar *s = js_SkipWhiteSpace(ws, end); + const jschar *s = SkipSpace(ws, end); JS_ASSERT(ws <= s); JS_ASSERT(s <= end); @@ -403,54 +363,67 @@ ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int may return true; } -static jsdouble -ParseIntDoubleHelper(jsdouble d) -{ - if (!JSDOUBLE_IS_FINITE(d)) - return js_NaN; - if (d > 0) - return floor(d); - if (d < 0) - return -floor(-d); - return 0; -} - /* See ECMA 15.1.2.2. */ -static JSBool -num_parseInt(JSContext *cx, uintN argc, Value *vp) +JSBool +js::num_parseInt(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* Fast paths and exceptional cases. */ - if (argc == 0) { - vp->setDouble(js_NaN); + if (args.length() == 0) { + args.rval().setDouble(js_NaN); return true; } - if (argc == 1 || (vp[3].isInt32() && (vp[3].toInt32() == 0 || vp[3].toInt32() == 10))) { - if (vp[2].isInt32()) { - *vp = vp[2]; + if (args.length() == 1 || + (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) { + if (args[0].isInt32()) { + args.rval() = args[0]; return true; } - if (vp[2].isDouble()) { - vp->setDouble(ParseIntDoubleHelper(vp[2].toDouble())); - return true; + /* + * Step 1 is |inputString = ToString(string)|. When string >= + * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of + * the word, which would mean the result of parseInt(string) should be |N|. + * + * To preserve this behaviour, we can't use the fast-path when string > + * 1e21, or else the result would be |NeM|. + * + * The same goes for values smaller than 1.0e-6, because the string would be in + * the form of "Ne-M". + */ + if (args[0].isDouble()) { + double d = args[0].toDouble(); + if (1.0e-6 < d && d < 1.0e21) { + args.rval().setNumber(floor(d)); + return true; + } + if (-1.0e21 < d && d < -1.0e-6) { + args.rval().setNumber(-floor(-d)); + return true; + } + if (d == 0.0) { + args.rval().setInt32(0); + return true; + } } } /* Step 1. */ - JSString *inputString = js_ValueToString(cx, vp[2]); + JSString *inputString = ToString(cx, args[0]); if (!inputString) return false; - vp[2].setString(inputString); + args[0].setString(inputString); /* 15.1.2.2 steps 6-8. */ bool stripPrefix = true; int32_t radix = 0; - if (argc > 1) { - if (!ValueToECMAInt32(cx, vp[3], &radix)) + if (args.length() > 1) { + if (!ToInt32(cx, args[1], &radix)) return false; if (radix != 0) { if (radix < 2 || radix > 36) { - vp->setDouble(js_NaN); + args.rval().setDouble(js_NaN); return true; } if (radix != 16) @@ -469,38 +442,10 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp) return false; /* Step 15. */ - vp->setNumber(number); + args.rval().setNumber(number); return true; } -#ifdef JS_TRACER -static jsdouble FASTCALL -ParseInt(JSContext* cx, JSString* str) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - const jschar *start = str->getChars(cx); - if (!start) { - SetBuiltinError(tm); - return js_NaN; - } - const jschar *end = start + str->length(); - - jsdouble d; - if (!ParseIntStringHelper(cx, start, end, 0, true, &d)) { - SetBuiltinError(tm); - return js_NaN; - } - return d; -} - -static jsdouble FASTCALL -ParseIntDouble(jsdouble d) -{ - return ParseIntDoubleHelper(d); -} -#endif - const char js_Infinity_str[] = "Infinity"; const char js_NaN_str[] = "NaN"; const char js_isNaN_str[] = "isNaN"; @@ -508,35 +453,24 @@ const char js_isFinite_str[] = "isFinite"; const char js_parseFloat_str[] = "parseFloat"; const char js_parseInt_str[] = "parseInt"; -#ifdef JS_TRACER - -JS_DEFINE_TRCINFO_2(num_parseInt, - (2, (static, DOUBLE_FAIL, ParseInt, CONTEXT, STRING,1, nanojit::ACCSET_NONE)), - (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, nanojit::ACCSET_NONE))) - -JS_DEFINE_TRCINFO_1(num_parseFloat, - (2, (static, DOUBLE_FAIL, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE))) - -#endif /* JS_TRACER */ - static JSFunctionSpec number_functions[] = { JS_FN(js_isNaN_str, num_isNaN, 1,0), JS_FN(js_isFinite_str, num_isFinite, 1,0), - JS_TN(js_parseFloat_str, num_parseFloat, 1,0, &num_parseFloat_trcinfo), - JS_TN(js_parseInt_str, num_parseInt, 2,0, &num_parseInt_trcinfo), + JS_FN(js_parseFloat_str, num_parseFloat, 1,0), + JS_FN(js_parseInt_str, num_parseInt, 2,0), JS_FS_END }; -Class js_NumberClass = { +Class js::NumberClass = { js_Number_str, JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub }; static JSBool @@ -546,7 +480,7 @@ Number(JSContext *cx, uintN argc, Value *vp) bool isConstructing = IsConstructing(vp); if (argc > 0) { - if (!ValueToNumber(cx, &vp[2])) + if (!ToNumber(cx, &vp[2])) return false; vp[0] = vp[2]; } else { @@ -555,8 +489,8 @@ Number(JSContext *cx, uintN argc, Value *vp) if (!isConstructing) return true; - - JSObject *obj = NewBuiltinClassInstance(cx, &js_NumberClass); + + JSObject *obj = NewBuiltinClassInstance(cx, &NumberClass); if (!obj) return false; obj->setPrimitiveThis(vp[0]); @@ -568,23 +502,24 @@ Number(JSContext *cx, uintN argc, Value *vp) static JSBool num_toSource(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + double d; - if (!GetPrimitiveThis(cx, vp, &d)) - return false; + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, num_toSource, &d, &ok)) + return ok; - ToCStringBuf cbuf; - char *numStr = NumberToCString(cx, &cbuf, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); + StringBuffer sb(cx); + if (!sb.append("(new Number(") || !NumberValueToStringBuffer(cx, NumberValue(d), sb) || + !sb.append("))")) + { return false; } - char buf[64]; - JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); - JSString *str = js_NewStringCopyZ(cx, buf); + JSString *str = sb.finishString(); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } #endif @@ -601,16 +536,16 @@ ToCStringBuf::~ToCStringBuf() } JSString * JS_FASTCALL -js_IntToString(JSContext *cx, int32 si) +js_IntToString(JSContext *cx, int32_t si) { - uint32 ui; + uint32_t ui; if (si >= 0) { - if (JSAtom::hasIntStatic(si)) - return &JSAtom::intStatic(si); + if (StaticStrings::hasInt(si)) + return cx->runtime->staticStrings.getInt(si); ui = si; } else { - ui = uint32(-si); - JS_ASSERT_IF(si == INT32_MIN, ui == uint32(INT32_MAX) + 1); + ui = uint32_t(-si); + JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1); } JSCompartment *c = cx->compartment; @@ -621,23 +556,17 @@ js_IntToString(JSContext *cx, int32 si) if (!str) return NULL; - /* +1, since MAX_LENGTH does not count the null char. */ - JS_STATIC_ASSERT(JSShortString::MAX_LENGTH + 1 >= sizeof("-2147483648")); - - jschar *end = str->inlineStorageBeforeInit() + JSShortString::MAX_SHORT_LENGTH; - jschar *cp = end; - *cp = 0; + jschar *storage = str->inlineStorageBeforeInit(); + RangedPtr end(storage + JSShortString::MAX_SHORT_LENGTH, + storage, JSShortString::MAX_SHORT_LENGTH + 1); + *end = '\0'; - do { - jsuint newui = ui / 10, digit = ui % 10; /* optimizers are our friends */ - *--cp = '0' + digit; - ui = newui; - } while (ui != 0); + RangedPtr start = BackfillIndexInCharBuffer(ui, end); if (si < 0) - *--cp = '-'; + *--start = '-'; - str->initAtOffsetInBuffer(cp, end - cp); + str->initAtOffsetInBuffer(start.get(), end - start); c->dtoaCache.cache(10, si, str); return str; @@ -647,25 +576,15 @@ js_IntToString(JSContext *cx, int32 si) static char * IntToCString(ToCStringBuf *cbuf, jsint i, jsint base = 10) { - char *cp; - jsuint u; - - u = (i < 0) ? -i : i; + jsuint u = (i < 0) ? -i : i; - cp = cbuf->sbuf + cbuf->sbufSize; /* one past last buffer cell */ - *--cp = '\0'; /* null terminate the string to be */ + RangedPtr cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize); + *cp = '\0'; - /* - * Build the string from behind. We use multiply and subtraction - * instead of modulus because that's much faster. - */ + /* Build the string from behind. */ switch (base) { case 10: - do { - jsuint newu = u / 10; - *--cp = (char)(u - newu * 10) + '0'; - u = newu; - } while (u != 0); + cp = BackfillIndexInCharBuffer(u, cp); break; case 16: do { @@ -686,24 +605,26 @@ IntToCString(ToCStringBuf *cbuf, jsint i, jsint base = 10) if (i < 0) *--cp = '-'; - JS_ASSERT(cp >= cbuf->sbuf); - return cp; + return cp.get(); } static JSString * JS_FASTCALL js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base); -static JSBool -num_toString(JSContext *cx, uintN argc, Value *vp) +static JS_ALWAYS_INLINE bool +num_toStringHelper(JSContext *cx, Native native, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + double d; - if (!GetPrimitiveThis(cx, vp, &d)) - return false; + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok)) + return ok; - int32 base = 10; - if (argc != 0 && !vp[2].isUndefined()) { + int32_t base = 10; + if (args.length() != 0 && !args[0].isUndefined()) { jsdouble d2; - if (!ToInteger(cx, vp[2], &d2)) + if (!ToInteger(cx, args[0], &d2)) return false; if (d2 < 2 || d2 > 36) { @@ -711,15 +632,21 @@ num_toString(JSContext *cx, uintN argc, Value *vp) return false; } - base = int32(d2); + base = int32_t(d2); } JSString *str = js_NumberToStringWithBase(cx, d, base); if (!str) { JS_ReportOutOfMemory(cx); - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + args.rval().setString(str); + return true; +} + +static JSBool +num_toString(JSContext *cx, uintN argc, Value *vp) +{ + return num_toStringHelper(cx, num_toString, argc, vp); } static JSBool @@ -738,7 +665,7 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) * Create the string, move back to bytes to make string twiddling * a bit easier and so we can insert platform charset seperators. */ - if (!num_toString(cx, 0, vp)) + if (!num_toStringHelper(cx, num_toLocaleString, 0, vp)) return JS_FALSE; JS_ASSERT(vp->isString()); JSAutoByteString numBytes(cx, vp->toString()); @@ -808,7 +735,7 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) strcpy(tmpDest, rt->thousandsSeparator); tmpDest += thousandsLength; JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen); - memcpy(tmpDest, tmpSrc, *tmpGroup); + js_memcpy(tmpDest, tmpSrc, *tmpGroup); tmpDest += *tmpGroup; tmpSrc += *tmpGroup; if (--nrepeat < 0) @@ -827,7 +754,7 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) } if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) { - JSBool ok = cx->localeCallbacks->localeToUnicode(cx, buf, Jsvalify(vp)); + JSBool ok = cx->localeCallbacks->localeToUnicode(cx, buf, vp); cx->free_(buf); return ok; } @@ -844,11 +771,14 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) JSBool js_num_valueOf(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + double d; - if (!GetPrimitiveThis(cx, vp, &d)) - return false; + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, js_num_valueOf, &d, &ok)) + return ok; - vp->setNumber(d); + args.rval().setNumber(d); return true; } @@ -856,24 +786,25 @@ js_num_valueOf(JSContext *cx, uintN argc, Value *vp) #define MAX_PRECISION 100 static JSBool -num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, +num_to(JSContext *cx, Native native, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset, - uintN argc, Value *vp) + CallArgs args) { /* Use MAX_PRECISION+1 because precisionOffset can be 1. */ char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)]; char *numStr; double d; - if (!GetPrimitiveThis(cx, vp, &d)) - return false; + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok)) + return ok; double precision; - if (argc == 0) { + if (args.length() == 0) { precision = 0.0; oneArgMode = zeroArgMode; } else { - if (!ToInteger(cx, vp[2], &precision)) + if (!ToInteger(cx, args[0], &precision)) return false; if (precision < precisionMin || precision > precisionMax) { ToCStringBuf cbuf; @@ -884,7 +815,7 @@ num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, } } - numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, + numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); if (!numStr) { JS_ReportOutOfMemory(cx); @@ -893,7 +824,7 @@ num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, JSString *str = js_NewStringCopyZ(cx, numStr); if (!str) return JS_FALSE; - vp->setString(str); + args.rval().setString(str); return JS_TRUE; } @@ -904,41 +835,31 @@ num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, static JSBool num_toFixed(JSContext *cx, uintN argc, Value *vp) { - return num_to(cx, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0, - argc, vp); + return num_to(cx, num_toFixed, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0, + CallArgsFromVp(argc, vp)); } static JSBool num_toExponential(JSContext *cx, uintN argc, Value *vp) { - return num_to(cx, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1, - argc, vp); + return num_to(cx, num_toExponential, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, + MAX_PRECISION, 1, CallArgsFromVp(argc, vp)); } static JSBool num_toPrecision(JSContext *cx, uintN argc, Value *vp) { if (argc == 0 || vp[2].isUndefined()) - return num_toString(cx, 0, vp); - return num_to(cx, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0, - argc, vp); + return num_toStringHelper(cx, num_toPrecision, 0, vp); + return num_to(cx, num_toPrecision, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0, + CallArgsFromVp(argc, vp)); } -#ifdef JS_TRACER - -JS_DEFINE_TRCINFO_2(num_toString, - (2, (extern, STRING_RETRY, js_NumberToString, CONTEXT, THIS_DOUBLE, - 1, nanojit::ACCSET_NONE)), - (3, (static, STRING_RETRY, js_NumberToStringWithBase, CONTEXT, THIS_DOUBLE, INT32, - 1, nanojit::ACCSET_NONE))) - -#endif /* JS_TRACER */ - static JSFunctionSpec number_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, num_toSource, 0, 0), #endif - JS_TN(js_toString_str, num_toString, 1, 0, &num_toString_trcinfo), + JS_FN(js_toString_str, num_toString, 1, 0), JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0), JS_FN(js_valueOf_str, js_num_valueOf, 0, 0), JS_FN("toFixed", num_toFixed, 1, 0), @@ -959,7 +880,7 @@ enum nc_slot { /* * Some to most C compilers forbid spelling these at compile time, or barf - * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState + * if you try, so all but MAX_VALUE are set up by InitRuntimeNumberState * using union jsdpun. */ static JSConstDoubleSpec number_constants[] = { @@ -996,11 +917,11 @@ inline void FIX_FPU() { #endif -JSBool -js_InitRuntimeNumberState(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; +namespace js { +bool +InitRuntimeNumberState(JSRuntime *rt) +{ FIX_FPU(); jsdpun u; @@ -1023,76 +944,114 @@ js_InitRuntimeNumberState(JSContext *cx) u.s.lo = 1; number_constants[NC_MIN_VALUE].dval = u.d; -#ifndef HAVE_LOCALECONV - const char* thousands_sep = getenv("LOCALE_THOUSANDS_SEP"); - const char* decimal_point = getenv("LOCALE_DECIMAL_POINT"); - const char* grouping = getenv("LOCALE_GROUPING"); - - rt->thousandsSeparator = - JS_strdup(cx, thousands_sep ? thousands_sep : "'"); - rt->decimalSeparator = - JS_strdup(cx, decimal_point ? decimal_point : "."); - rt->numGrouping = - JS_strdup(cx, grouping ? grouping : "\3\0"); -#else + /* Copy locale-specific separators into the runtime strings. */ + const char *thousandsSeparator, *decimalPoint, *grouping; +#ifdef HAVE_LOCALECONV struct lconv *locale = localeconv(); - rt->thousandsSeparator = - JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); - rt->decimalSeparator = - JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); - rt->numGrouping = - JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); + thousandsSeparator = locale->thousands_sep; + decimalPoint = locale->decimal_point; + grouping = locale->grouping; +#else + thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP"); + decimalPoint = getenv("LOCALE_DECIMAL_POINT"); + grouping = getenv("LOCALE_GROUPING"); #endif + if (!thousandsSeparator) + thousandsSeparator = "'"; + if (!decimalPoint) + decimalPoint = "."; + if (!grouping) + grouping = "\3\0"; + + /* + * We use single malloc to get the memory for all separator and grouping + * strings. + */ + size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1; + size_t decimalPointSize = strlen(decimalPoint) + 1; + size_t groupingSize = strlen(grouping) + 1; + + char *storage = static_cast(OffTheBooks::malloc_(thousandsSeparatorSize + + decimalPointSize + + groupingSize)); + if (!storage) + return false; + + js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize); + rt->thousandsSeparator = storage; + storage += thousandsSeparatorSize; - return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; + js_memcpy(storage, decimalPoint, decimalPointSize); + rt->decimalSeparator = storage; + storage += decimalPointSize; + + js_memcpy(storage, grouping, groupingSize); + rt->numGrouping = grouping; + return true; } void -js_FinishRuntimeNumberState(JSContext *cx) +FinishRuntimeNumberState(JSRuntime *rt) { - JSRuntime *rt = cx->runtime; - - cx->free_((void *) rt->thousandsSeparator); - cx->free_((void *) rt->decimalSeparator); - cx->free_((void *) rt->numGrouping); - rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; + /* + * The free also releases the memory for decimalSeparator and numGrouping + * strings. + */ + char *storage = const_cast(rt->thousandsSeparator); + Foreground::free_(storage); } +} /* namespace js */ + JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj) { - JSObject *proto, *ctor; - JSRuntime *rt; + JS_ASSERT(obj->isNative()); /* XXX must do at least once per new thread, so do it per JSContext... */ FIX_FPU(); - if (!JS_DefineFunctions(cx, obj, number_functions)) + GlobalObject *global = &obj->asGlobal(); + + JSObject *numberProto = global->createBlankPrototype(cx, &NumberClass); + if (!numberProto) + return NULL; + numberProto->asNumber().setPrimitiveValue(0); + + JSFunction *ctor = global->createConstructor(cx, Number, &NumberClass, + CLASS_ATOM(cx, Number), 1); + if (!ctor) return NULL; - proto = js_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, - NULL, number_methods, NULL, NULL); - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) + if (!LinkConstructorAndPrototype(cx, ctor, numberProto)) return NULL; - proto->setPrimitiveThis(Int32Value(0)); + + /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */ if (!JS_DefineConstDoubles(cx, ctor, number_constants)) return NULL; - /* ECMA 15.1.1.1 */ - rt = cx->runtime; - if (!JS_DefineProperty(cx, obj, js_NaN_str, Jsvalify(rt->NaNValue), - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_PERMANENT | JSPROP_READONLY)) { + if (!DefinePropertiesAndBrand(cx, numberProto, NULL, number_methods)) + return NULL; + + if (!JS_DefineFunctions(cx, global, number_functions)) return NULL; - } - /* ECMA 15.1.1.2 */ - if (!JS_DefineProperty(cx, obj, js_Infinity_str, Jsvalify(rt->positiveInfinityValue), - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_PERMANENT | JSPROP_READONLY)) { + /* ES5 15.1.1.1, 15.1.1.2 */ + if (!DefineNativeProperty(cx, global, ATOM_TO_JSID(cx->runtime->atomState.NaNAtom), + cx->runtime->NaNValue, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY, 0, 0) || + !DefineNativeProperty(cx, global, ATOM_TO_JSID(cx->runtime->atomState.InfinityAtom), + cx->runtime->positiveInfinityValue, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) + { return NULL; } - return proto; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_Number, ctor, numberProto)) + return NULL; + + return numberProto; } namespace v8 { @@ -1119,7 +1078,7 @@ FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = * This is V8's implementation of the algorithm described in the * following paper: * - * Printing floating-point numbers quickly and accurately with integers. + * Printing floating-point numbers quickly and accurately with integers. * Florian Loitsch, PLDI 2010. * * It fails on a small number of cases, whereupon we fall back to @@ -1127,10 +1086,10 @@ FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = */ numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize); if (!numStr) - numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, cbuf->sbuf, cbuf->sbufSize, + numStr = js_dtostr(cx->runtime->dtoaState, cbuf->sbuf, cbuf->sbufSize, DTOSTR_STANDARD, 0, d); } else { - numStr = cbuf->dbuf = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d); + numStr = cbuf->dbuf = js_dtobasestr(cx->runtime->dtoaState, base, d); } return numStr; } @@ -1164,14 +1123,14 @@ js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) int32_t i; if (JSDOUBLE_IS_INT32(d, &i)) { - if (base == 10 && JSAtom::hasIntStatic(i)) - return &JSAtom::intStatic(i); + if (base == 10 && StaticStrings::hasInt(i)) + return cx->runtime->staticStrings.getInt(i); if (jsuint(i) < jsuint(base)) { if (i < 10) - return &JSAtom::intStatic(i); + return cx->runtime->staticStrings.getInt(i); jschar c = 'a' + i - 10; - JS_ASSERT(JSAtom::hasUnitStatic(c)); - return &JSAtom::unitStatic(c); + JS_ASSERT(StaticStrings::hasUnit(c)); + return cx->runtime->staticStrings.getUnit(c); } if (JSFlatString *str = c->dtoaCache.lookup(base, d)) @@ -1215,6 +1174,33 @@ NumberToString(JSContext *cx, jsdouble d) return NULL; } +JSFixedString * +IndexToString(JSContext *cx, uint32_t index) +{ + if (StaticStrings::hasUint(index)) + return cx->runtime->staticStrings.getUint(index); + + JSCompartment *c = cx->compartment; + if (JSFixedString *str = c->dtoaCache.lookup(10, index)) + return str; + + JSShortString *str = js_NewGCShortString(cx); + if (!str) + return NULL; + + jschar *storage = str->inlineStorageBeforeInit(); + size_t length = JSShortString::MAX_SHORT_LENGTH; + const RangedPtr end(storage + length, storage, length + 1); + *end = '\0'; + + RangedPtr start = BackfillIndexInCharBuffer(index, end); + + str->initAtOffsetInBuffer(start.get(), end - start); + + c->dtoaCache.cache(10, index, str); + return str; +} + bool JS_FASTCALL NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) { @@ -1241,7 +1227,7 @@ NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) } bool -ValueToNumberSlow(JSContext *cx, Value v, double *out) +ToNumberSlow(JSContext *cx, Value v, double *out) { JS_ASSERT(!v.isNumber()); goto skip_int_double; @@ -1269,7 +1255,7 @@ ValueToNumberSlow(JSContext *cx, Value v, double *out) break; JS_ASSERT(v.isObject()); - if (!DefaultValue(cx, &v.toObject(), JSTYPE_NUMBER, &v)) + if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) return false; if (v.isObject()) break; @@ -1280,14 +1266,14 @@ ValueToNumberSlow(JSContext *cx, Value v, double *out) } bool -ValueToECMAInt32Slow(JSContext *cx, const Value &v, int32_t *out) +ToInt32Slow(JSContext *cx, const Value &v, int32_t *out) { JS_ASSERT(!v.isInt32()); jsdouble d; if (v.isDouble()) { d = v.toDouble(); } else { - if (!ValueToNumberSlow(cx, v, &d)) + if (!ToNumberSlow(cx, v, &d)) return false; } *out = js_DoubleToECMAInt32(d); @@ -1295,14 +1281,14 @@ ValueToECMAInt32Slow(JSContext *cx, const Value &v, int32_t *out) } bool -ValueToECMAUint32Slow(JSContext *cx, const Value &v, uint32_t *out) +ToUint32Slow(JSContext *cx, const Value &v, uint32_t *out) { JS_ASSERT(!v.isInt32()); jsdouble d; if (v.isDouble()) { d = v.toDouble(); } else { - if (!ValueToNumberSlow(cx, v, &d)) + if (!ToNumberSlow(cx, v, &d)) return false; } *out = js_DoubleToECMAUint32(d); @@ -1311,45 +1297,16 @@ ValueToECMAUint32Slow(JSContext *cx, const Value &v, uint32_t *out) } /* namespace js */ -uint32 -js_DoubleToECMAUint32(jsdouble d) -{ - int32 i; - JSBool neg; - jsdouble two32; - - if (!JSDOUBLE_IS_FINITE(d)) - return 0; - - /* - * We check whether d fits int32, not uint32, as all but the ">>>" bit - * manipulation bytecode stores the result as int, not uint. When the - * result does not fit int Value, it will be stored as a negative double. - */ - i = (int32) d; - if ((jsdouble) i == d) - return (int32)i; - - neg = (d < 0); - d = floor(neg ? -d : d); - d = neg ? -d : d; - - two32 = 4294967296.0; - d = fmod(d, two32); - - return (uint32) (d >= 0 ? d : d + two32); -} - namespace js { bool -ValueToInt32Slow(JSContext *cx, const Value &v, int32_t *out) +NonstandardToInt32Slow(JSContext *cx, const Value &v, int32_t *out) { JS_ASSERT(!v.isInt32()); jsdouble d; if (v.isDouble()) { d = v.toDouble(); - } else if (!ValueToNumberSlow(cx, v, &d)) { + } else if (!ToNumberSlow(cx, v, &d)) { return false; } @@ -1358,7 +1315,7 @@ ValueToInt32Slow(JSContext *cx, const Value &v, int32_t *out) JSDVG_SEARCH_STACK, v, NULL); return false; } - *out = (int32) floor(d + 0.5); /* Round to nearest */ + *out = (int32_t) floor(d + 0.5); /* Round to nearest */ return true; } @@ -1369,7 +1326,7 @@ ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out) jsdouble d; if (v.isDouble()) { d = v.toDouble(); - } else if (!ValueToNumberSlow(cx, v, &d)) { + } else if (!ToNumberSlow(cx, v, &d)) { return false; } @@ -1378,7 +1335,7 @@ ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out) return true; } - uint16 u = (uint16) d; + uint16_t u = (uint16_t) d; if ((jsdouble)u == d) { *out = u; return true; @@ -1401,15 +1358,14 @@ JSBool js_strtod(JSContext *cx, const jschar *s, const jschar *send, const jschar **ep, jsdouble *dp) { - const jschar *s1; - size_t length, i; + size_t i; char cbuf[32]; char *cstr, *istr, *estr; JSBool negative; jsdouble d; - s1 = js_SkipWhiteSpace(s, send); - length = send - s1; + const jschar *s1 = SkipSpace(s, send); + size_t length = send - s1; /* Use cbuf to avoid malloc */ if (length >= sizeof cbuf) { @@ -1435,7 +1391,7 @@ js_strtod(JSContext *cx, const jschar *s, const jschar *send, estr = istr + 8; } else { int err; - d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err); + d = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err); if (d == HUGE_VAL) d = js_PositiveInfinity; else if (d == -HUGE_VAL) diff --git a/deps/mozjs/js/src/jsnum.h b/deps/mozjs/js/src/jsnum.h index aeb7362c4d8..76b9db03618 100644 --- a/deps/mozjs/js/src/jsnum.h +++ b/deps/mozjs/js/src/jsnum.h @@ -41,13 +41,7 @@ #define jsnum_h___ #include -#ifdef WIN32 -#include -#endif -#include "jsvalue.h" -#include "jsstdint.h" -#include "jsstr.h" #include "jsobj.h" /* @@ -70,32 +64,35 @@ #endif #endif +/* Low-level floating-point predicates. See bug 640494. */ +#define JSDOUBLE_HI32_SIGNBIT 0x80000000 +#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 +#define JSDOUBLE_HI32_MANTMASK 0x000fffff +#define JSDOUBLE_HI32_NAN 0x7ff80000 +#define JSDOUBLE_LO32_NAN 0x00000000 + +#define JSDOUBLE_HI32_EXPSHIFT 20 +#define JSDOUBLE_EXPBIAS 1023 + typedef union jsdpun { struct { #if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA) - uint32 lo, hi; + uint32_t lo, hi; #else - uint32 hi, lo; + uint32_t hi, lo; #endif } s; - uint64 u64; + uint64_t u64; jsdouble d; } jsdpun; -/* Low-level floating-point predicates. See bug 640494. */ - static inline int JSDOUBLE_IS_NaN(jsdouble d) { -/* Visual Studio PGO miscompiles the bitwise version, so keep using _isnan - * from float.h until we figure out what's going on. */ -#ifdef WIN32 - return _isnan(d); -#else jsdpun u; u.d = d; - return (u.u64 & ~JSDOUBLE_SIGNBIT) > JSDOUBLE_EXPMASK; -#endif + return (u.u64 & JSDOUBLE_EXPMASK) == JSDOUBLE_EXPMASK && + (u.u64 & JSDOUBLE_MANTMASK) != 0; } static inline int @@ -115,12 +112,6 @@ JSDOUBLE_IS_INFINITE(jsdouble d) return (u.u64 & ~JSDOUBLE_SIGNBIT) == JSDOUBLE_EXPMASK; } -#define JSDOUBLE_HI32_SIGNBIT 0x80000000 -#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 -#define JSDOUBLE_HI32_MANTMASK 0x000fffff -#define JSDOUBLE_HI32_NAN 0x7ff80000 -#define JSDOUBLE_LO32_NAN 0x00000000 - static inline bool JSDOUBLE_IS_NEG(jsdouble d) { @@ -129,7 +120,7 @@ JSDOUBLE_IS_NEG(jsdouble d) return (u.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; } -static inline uint32 +static inline uint32_t JS_HASH_DOUBLE(jsdouble d) { jsdpun u; @@ -137,35 +128,21 @@ JS_HASH_DOUBLE(jsdouble d) return u.s.lo ^ u.s.hi; } -#if defined(XP_WIN) -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ - ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ - ? (IFNAN) \ - : (LVAL) OP (RVAL)) -#else -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) -#endif - extern jsdouble js_NaN; extern jsdouble js_PositiveInfinity; extern jsdouble js_NegativeInfinity; -/* Initialize number constants and runtime state for the first context. */ -extern JSBool -js_InitRuntimeNumberState(JSContext *cx); +namespace js { -extern void -js_FinishRuntimeNumberState(JSContext *cx); +extern bool +InitRuntimeNumberState(JSRuntime *rt); -/* Initialize the Number class, returning its prototype object. */ -extern js::Class js_NumberClass; +extern void +FinishRuntimeNumberState(JSRuntime *rt); -inline bool -JSObject::isNumber() const -{ - return getClass() == &js_NumberClass; -} +} /* namespace js */ +/* Initialize the Number class, returning its prototype object. */ extern JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj); @@ -179,6 +156,9 @@ extern const char js_isFinite_str[]; extern const char js_parseFloat_str[]; extern const char js_parseInt_str[]; +class JSString; +class JSFixedString; + extern JSString * JS_FASTCALL js_IntToString(JSContext *cx, jsint i); @@ -203,6 +183,9 @@ NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb); extern JSFixedString * NumberToString(JSContext *cx, jsdouble d); +extern JSFixedString * +IndexToString(JSContext *cx, uint32_t index); + /* * Usually a small amount of static storage is enough, but sometimes we need * to dynamically allocate much more. This struct encapsulates that. @@ -236,7 +219,7 @@ NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = 10); * The largest positive integer such that all positive integers less than it * may be precisely represented using the IEEE-754 double-precision format. */ -const double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64(1) << 53; +const double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64_t(1) << 53; /* * Compute the positive integer of the given base described immediately at the @@ -254,80 +237,77 @@ extern bool GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base, const jschar **endp, jsdouble *dp); -/* - * Convert a value to a number, returning the converted value in 'out' if the - * conversion succeeds. - */ +/* ES5 9.3 ToNumber. */ JS_ALWAYS_INLINE bool -ValueToNumber(JSContext *cx, const js::Value &v, double *out) +ToNumber(JSContext *cx, const Value &v, double *out) { if (v.isNumber()) { *out = v.toNumber(); return true; } - extern bool ValueToNumberSlow(JSContext *, js::Value, double *); - return ValueToNumberSlow(cx, v, out); + extern bool ToNumberSlow(JSContext *cx, js::Value v, double *dp); + return ToNumberSlow(cx, v, out); } -/* Convert a value to a number, replacing 'vp' with the converted value. */ +/* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */ JS_ALWAYS_INLINE bool -ValueToNumber(JSContext *cx, js::Value *vp) +ToNumber(JSContext *cx, Value *vp) { if (vp->isNumber()) return true; double d; - extern bool ValueToNumberSlow(JSContext *, js::Value, double *); - if (!ValueToNumberSlow(cx, *vp, &d)) + extern bool ToNumberSlow(JSContext *cx, js::Value v, double *dp); + if (!ToNumberSlow(cx, *vp, &d)) return false; vp->setNumber(d); return true; } /* - * Convert a value to an int32 or uint32, according to the ECMA rules for + * Convert a value to an int32_t or uint32_t, according to the ECMA rules for * ToInt32 and ToUint32. Return converted value in *out on success, !ok on * failure. */ JS_ALWAYS_INLINE bool -ValueToECMAInt32(JSContext *cx, const js::Value &v, int32_t *out) +ToInt32(JSContext *cx, const js::Value &v, int32_t *out) { if (v.isInt32()) { *out = v.toInt32(); return true; } - extern bool ValueToECMAInt32Slow(JSContext *, const js::Value &, int32_t *); - return ValueToECMAInt32Slow(cx, v, out); + extern bool ToInt32Slow(JSContext *cx, const js::Value &v, int32_t *ip); + return ToInt32Slow(cx, v, out); } JS_ALWAYS_INLINE bool -ValueToECMAUint32(JSContext *cx, const js::Value &v, uint32_t *out) +ToUint32(JSContext *cx, const js::Value &v, uint32_t *out) { if (v.isInt32()) { *out = (uint32_t)v.toInt32(); return true; } - extern bool ValueToECMAUint32Slow(JSContext *, const js::Value &, uint32_t *); - return ValueToECMAUint32Slow(cx, v, out); + extern bool ToUint32Slow(JSContext *cx, const js::Value &v, uint32_t *ip); + return ToUint32Slow(cx, v, out); } /* - * Convert a value to a number, then to an int32 if it fits by rounding to + * Convert a value to a number, then to an int32_t if it fits by rounding to * nearest. Return converted value in *out on success, !ok on failure. As a * side effect, *vp will be mutated to match *out. */ JS_ALWAYS_INLINE bool -ValueToInt32(JSContext *cx, const js::Value &v, int32_t *out) +NonstandardToInt32(JSContext *cx, const js::Value &v, int32_t *out) { if (v.isInt32()) { *out = v.toInt32(); return true; } - extern bool ValueToInt32Slow(JSContext *, const js::Value &, int32_t *); - return ValueToInt32Slow(cx, v, out); + extern bool NonstandardToInt32Slow(JSContext *cx, const js::Value &v, int32_t *ip); + return NonstandardToInt32Slow(cx, v, out); } /* - * Convert a value to a number, then to a uint16 according to the ECMA rules + * Convert a value to a number, then to a uint16_t according to the ECMA rules * for ToUint16. Return converted value on success, !ok on failure. v must be a * copy of a rooted value. */ @@ -335,13 +315,16 @@ JS_ALWAYS_INLINE bool ValueToUint16(JSContext *cx, const js::Value &v, uint16_t *out) { if (v.isInt32()) { - *out = (uint16_t)v.toInt32(); + *out = uint16_t(v.toInt32()); return true; } - extern bool ValueToUint16Slow(JSContext *, const js::Value &, uint16_t *); + extern bool ValueToUint16Slow(JSContext *cx, const js::Value &v, uint16_t *out); return ValueToUint16Slow(cx, v, out); } +JSBool +num_parseInt(JSContext *cx, uintN argc, Value *vp); + } /* namespace js */ /* @@ -357,14 +340,14 @@ ValueToUint16(JSContext *cx, const js::Value &v, uint16_t *out) * 5. If Result(4) is greater than or equal to 2^31, return Result(4)- 2^32, * otherwise return Result(4). */ -static inline int32 +static inline int32_t js_DoubleToECMAInt32(jsdouble d) { #if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64) jsdpun du, duh, two32; - uint32 di_h, u_tmp, expon, shift_amount; - int32 mask32; + uint32_t di_h, u_tmp, expon, shift_amount; + int32_t mask32; /* * Algorithm Outline @@ -432,7 +415,7 @@ js_DoubleToECMAInt32(jsdouble d) du.d -= two32.d; } - return int32(du.d); + return int32_t(du.d); #elif defined (__arm__) && defined (__GNUC__) int32_t i; uint32_t tmp0; @@ -469,7 +452,7 @@ js_DoubleToECMAInt32(jsdouble d) // bit-shifted left by the (decoded) exponent. Note that because the r1[20] // is the bit with value '1', r1 is effectively already shifted (left) by // 20 bits, and r0 is already shifted by 52 bits. - + // Adjust the exponent to remove the encoding offset. If the decoded // exponent is negative, quickly bail out with '0' as such values round to // zero anyway. This also catches +/-0 and subnormals. @@ -555,13 +538,13 @@ js_DoubleToECMAInt32(jsdouble d) ); return i; #else - int32 i; + int32_t i; jsdouble two32, two31; if (!JSDOUBLE_IS_FINITE(d)) return 0; - i = (int32) d; + i = (int32_t) d; if ((jsdouble) i == d) return i; @@ -569,12 +552,15 @@ js_DoubleToECMAInt32(jsdouble d) two31 = 2147483648.0; d = fmod(d, two32); d = (d >= 0) ? floor(d) : ceil(d) + two32; - return (int32) (d >= two31 ? d - two32 : d); + return (int32_t) (d >= two31 ? d - two32 : d); #endif } -uint32 -js_DoubleToECMAUint32(jsdouble d); +inline uint32_t +js_DoubleToECMAUint32(jsdouble d) +{ + return uint32_t(js_DoubleToECMAInt32(d)); +} /* * Convert a jsdouble to an integral number, stored in a jsdouble. @@ -627,74 +613,30 @@ ValueFitsInInt32(const Value &v, int32_t *pi) return v.isDouble() && JSDOUBLE_IS_INT32(v.toDouble(), pi); } -template struct NumberTraits { }; -template<> struct NumberTraits { - static JS_ALWAYS_INLINE int32 NaN() { return 0; } - static JS_ALWAYS_INLINE int32 toSelfType(int32 i) { return i; } - static JS_ALWAYS_INLINE int32 toSelfType(jsdouble d) { return js_DoubleToECMAUint32(d); } -}; -template<> struct NumberTraits { - static JS_ALWAYS_INLINE jsdouble NaN() { return js_NaN; } - static JS_ALWAYS_INLINE jsdouble toSelfType(int32 i) { return i; } - static JS_ALWAYS_INLINE jsdouble toSelfType(jsdouble d) { return d; } -}; - -template +/* + * Returns true if the given value is definitely an index: that is, the value + * is a number that's an unsigned 32-bit integer. + * + * This method prioritizes common-case speed over accuracy in every case. It + * can produce false negatives (but not false positives): some values which are + * indexes will be reported not to be indexes by this method. Users must + * consider this possibility when using this method. + */ static JS_ALWAYS_INLINE bool -StringToNumberType(JSContext *cx, JSString *str, T *result) +IsDefinitelyIndex(const Value &v, uint32_t *indexp) { - size_t length = str->length(); - const jschar *chars = str->getChars(NULL); - if (!chars) - return false; - - if (length == 1) { - jschar c = chars[0]; - if ('0' <= c && c <= '9') { - *result = NumberTraits::toSelfType(T(c - '0')); - return true; - } - if (JS_ISSPACE(c)) { - *result = NumberTraits::toSelfType(T(0)); - return true; - } - *result = NumberTraits::NaN(); + if (v.isInt32() && v.toInt32() >= 0) { + *indexp = v.toInt32(); return true; } - const jschar *bp = chars; - const jschar *end = chars + length; - bp = js_SkipWhiteSpace(bp, end); - - /* ECMA doesn't allow signed hex numbers (bug 273467). */ - if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { - /* Looks like a hex number. */ - const jschar *endptr; - double d; - if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || - js_SkipWhiteSpace(endptr, end) != end) { - *result = NumberTraits::NaN(); - return true; - } - *result = NumberTraits::toSelfType(d); + int32_t i; + if (v.isDouble() && JSDOUBLE_IS_INT32(v.toDouble(), &i) && i >= 0) { + *indexp = uint32_t(i); return true; } - /* - * Note that ECMA doesn't treat a string beginning with a '0' as - * an octal number here. This works because all such numbers will - * be interpreted as decimal by js_strtod. Also, any hex numbers - * that have made it here (which can only be negative ones) will - * be treated as 0 without consuming the 'x' by js_strtod. - */ - const jschar *ep; - double d; - if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) { - *result = NumberTraits::NaN(); - return true; - } - *result = NumberTraits::toSelfType(d); - return true; + return false; } /* ES5 9.4 ToInteger. */ @@ -708,8 +650,8 @@ ToInteger(JSContext *cx, const js::Value &v, jsdouble *dp) if (v.isDouble()) { *dp = v.toDouble(); } else { - extern bool ValueToNumberSlow(JSContext *cx, js::Value v, double *dp); - if (!ValueToNumberSlow(cx, v, dp)) + extern bool ToNumberSlow(JSContext *cx, Value v, double *dp); + if (!ToNumberSlow(cx, v, dp)) return false; } *dp = js_DoubleToInteger(*dp); diff --git a/deps/mozjs/js/src/jsnuminlines.h b/deps/mozjs/js/src/jsnuminlines.h new file mode 100644 index 00000000000..771eba5ccd9 --- /dev/null +++ b/deps/mozjs/js/src/jsnuminlines.h @@ -0,0 +1,120 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsnuminlines_h___ +#define jsnuminlines_h___ + +#include "vm/Unicode.h" + +#include "jsstrinlines.h" + + +namespace js { + +template struct NumberTraits { }; +template<> struct NumberTraits { + static JS_ALWAYS_INLINE int32_t NaN() { return 0; } + static JS_ALWAYS_INLINE int32_t toSelfType(int32_t i) { return i; } + static JS_ALWAYS_INLINE int32_t toSelfType(jsdouble d) { return js_DoubleToECMAUint32(d); } +}; +template<> struct NumberTraits { + static JS_ALWAYS_INLINE jsdouble NaN() { return js_NaN; } + static JS_ALWAYS_INLINE jsdouble toSelfType(int32_t i) { return i; } + static JS_ALWAYS_INLINE jsdouble toSelfType(jsdouble d) { return d; } +}; + +template +static JS_ALWAYS_INLINE bool +StringToNumberType(JSContext *cx, JSString *str, T *result) +{ + size_t length = str->length(); + const jschar *chars = str->getChars(NULL); + if (!chars) + return false; + + if (length == 1) { + jschar c = chars[0]; + if ('0' <= c && c <= '9') { + *result = NumberTraits::toSelfType(T(c - '0')); + return true; + } + if (unicode::IsSpace(c)) { + *result = NumberTraits::toSelfType(T(0)); + return true; + } + *result = NumberTraits::NaN(); + return true; + } + + const jschar *end = chars + length; + const jschar *bp = SkipSpace(chars, end); + + /* ECMA doesn't allow signed hex numbers (bug 273467). */ + if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { + /* Looks like a hex number. */ + const jschar *endptr; + double d; + if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || SkipSpace(endptr, end) != end) { + *result = NumberTraits::NaN(); + return true; + } + *result = NumberTraits::toSelfType(d); + return true; + } + + /* + * Note that ECMA doesn't treat a string beginning with a '0' as + * an octal number here. This works because all such numbers will + * be interpreted as decimal by js_strtod. Also, any hex numbers + * that have made it here (which can only be negative ones) will + * be treated as 0 without consuming the 'x' by js_strtod. + */ + const jschar *ep; + double d; + if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end) { + *result = NumberTraits::NaN(); + return true; + } + *result = NumberTraits::toSelfType(d); + return true; +} + +} // namespace js + +#endif /* jsnuminlines_h___ */ diff --git a/deps/mozjs/js/src/jsobj.cpp b/deps/mozjs/js/src/jsobj.cpp index 99bdd2b2744..5fd9286f900 100644 --- a/deps/mozjs/js/src/jsobj.cpp +++ b/deps/mozjs/js/src/jsobj.cpp @@ -43,10 +43,10 @@ */ #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" -#include "jsbit.h" #include "jsutil.h" #include "jshash.h" #include "jsdhash.h" @@ -55,10 +55,8 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" -#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" @@ -69,23 +67,31 @@ #include "jsobj.h" #include "jsonparser.h" #include "jsopcode.h" -#include "jsparse.h" +#include "jsprobes.h" #include "jsproxy.h" #include "jsscope.h" #include "jsscript.h" -#include "jsstaticcheck.h" -#include "jsstdint.h" #include "jsstr.h" -#include "jstracer.h" #include "jsdbgapi.h" #include "json.h" +#include "jswatchpoint.h" #include "jswrapper.h" +#include "builtin/MapObject.h" +#include "frontend/BytecodeCompiler.h" +#include "frontend/BytecodeEmitter.h" +#include "frontend/Parser.h" +#include "js/MemoryMetrics.h" + +#include "jsarrayinlines.h" #include "jsinterpinlines.h" +#include "jsobjinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" -#include "jsobjinlines.h" +#include "jsstrinlines.h" +#include "vm/BooleanObject-inl.h" +#include "vm/NumberObject-inl.h" #include "vm/StringObject-inl.h" #if JS_HAS_GENERATORS @@ -100,32 +106,44 @@ #include "jsxdrapi.h" #endif -#include "jsprobes.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "jsautooplen.h" +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; -JS_FRIEND_DATA(js::Shape) Shape::sharedNonNative(SHAPELESS); +JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value))); -Class js_ObjectClass = { +Class js::ObjectClass = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub }; JS_FRIEND_API(JSObject *) -js_ObjectToOuterObject(JSContext *cx, JSObject *obj) +JS_ObjectToInnerObject(JSContext *cx, JSObject *obj) +{ + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; +} + +JS_FRIEND_API(JSObject *) +JS_ObjectToOuterObject(JSContext *cx, JSObject *obj) { OBJ_TO_OUTER_OBJECT(cx, obj); return obj; @@ -139,8 +157,8 @@ obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp); -static JSPropertySpec object_props[] = { - {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)}, +JSPropertySpec object_props[] = { + {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, obj_getProto, obj_setProto}, {0,0,0,0,0} }; @@ -153,9 +171,14 @@ obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp) return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs); } +size_t sSetProtoCalled = 0; + static JSBool obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { + if (!cx->runningWithTrustedPrincipals()) + ++sSetProtoCalled; + /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */ if (!obj->isExtensible()) { obj->reportNotExtensible(cx); @@ -163,26 +186,15 @@ obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) } if (!vp->isObjectOrNull()) - return JS_TRUE; + return true; JSObject *pobj = vp->toObjectOrNull(); - if (pobj) { - /* - * Innerize pobj here to avoid sticking unwanted properties on the - * outer object. This ensures that any with statements only grant - * access to the inner object. - */ - OBJ_TO_INNER_OBJECT(cx, pobj); - if (!pobj) - return JS_FALSE; - } - uintN attrs; id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs)) - return JS_FALSE; + return false; - return SetProto(cx, obj, pobj, JS_TRUE); + return SetProto(cx, obj, pobj, true); } #else /* !JS_HAS_OBJ_PROTO_PROP */ @@ -200,28 +212,18 @@ js_hash_object(const void *key) static JSHashEntry * MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) { - JSSharpObjectMap *map; - JSHashTable *table; - JSHashNumber hash; - JSHashEntry **hep, *he; - jsatomid sharpid; - JSIdArray *ida; - JSBool ok; - jsint i, length; - jsid id; - JSObject *obj2; - JSProperty *prop; - JS_CHECK_RECURSION(cx, return NULL); - map = &cx->sharpObjectMap; + JSIdArray *ida; + + JSSharpObjectMap *map = &cx->sharpObjectMap; JS_ASSERT(map->depth >= 1); - table = map->table; - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; + JSHashTable *table = map->table; + JSHashNumber hash = js_hash_object(obj); + JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); + JSHashEntry *he = *hep; if (!he) { - sharpid = 0; + jsatomid sharpid = 0; he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid); if (!he) { JS_ReportOutOfMemory(cx); @@ -232,10 +234,12 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (!ida) return NULL; - ok = JS_TRUE; - for (i = 0, length = ida->length; i < length; i++) { - id = ida->vector[i]; - ok = obj->lookupProperty(cx, id, &obj2, &prop); + bool ok = true; + for (jsint i = 0, length = ida->length; i < length; i++) { + jsid id = ida->vector[i]; + JSObject *obj2; + JSProperty *prop; + ok = obj->lookupGeneric(cx, id, &obj2, &prop); if (!ok) break; if (!prop) @@ -263,13 +267,12 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) } v.set(setter.value()); } else if (!hasGetter) { - ok = obj->getProperty(cx, id, v.addr()); + ok = obj->getGeneric(cx, id, v.addr()); if (!ok) break; } - if (v.value().isObject() && - !MarkSharpObjects(cx, &v.value().toObject(), NULL)) { - ok = JS_FALSE; + if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL)) { + ok = false; break; } } @@ -278,7 +281,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (!ok) return NULL; } else { - sharpid = uintptr_t(he->value); + jsatomid sharpid = uintptr_t(he->value); if (sharpid == 0) { sharpid = ++map->sharpgen << SHARP_ID_SHIFT; he->value = (void *) sharpid; @@ -291,25 +294,15 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) } JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp) +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen) { - JSSharpObjectMap *map; - JSHashTable *table; - JSIdArray *ida; - JSHashNumber hash; - JSHashEntry *he, **hep; - jsatomid sharpid; - char buf[20]; - size_t len; - if (!JS_CHECK_OPERATION_LIMIT(cx)) return NULL; - /* Set to null in case we return an early error. */ - *sp = NULL; - map = &cx->sharpObjectMap; - table = map->table; + *alreadySeen = false; + + JSSharpObjectMap *map = &cx->sharpObjectMap; + JSHashTable *table = map->table; if (!table) { table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, JS_CompareValues, NULL, NULL); @@ -321,8 +314,11 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, JS_KEEP_ATOMS(cx->runtime); } + JSHashEntry *he; + jsatomid sharpid; + JSIdArray *ida = NULL; + /* From this point the control must flow either through out: or bad:. */ - ida = NULL; if (map->depth == 0) { /* * Although MarkSharpObjects tries to avoid invoking getters, @@ -347,8 +343,8 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, ida = NULL; } } else { - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); + JSHashNumber hash = js_hash_object(obj); + JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); he = *hep; /* @@ -370,30 +366,16 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, } sharpid = uintptr_t(he->value); - if (sharpid != 0) { - len = JS_snprintf(buf, sizeof buf, "#%u%c", - sharpid >> SHARP_ID_SHIFT, - (sharpid & SHARP_BIT) ? '#' : '='); - *sp = js_InflateString(cx, buf, &len); - if (!*sp) { - if (ida) - JS_DestroyIdArray(cx, ida); - goto bad; - } - } + if (sharpid != 0) + *alreadySeen = true; out: JS_ASSERT(he); if ((sharpid & SHARP_BIT) == 0) { if (idap && !ida) { ida = JS_Enumerate(cx, obj); - if (!ida) { - if (*sp) { - cx->free_(*sp); - *sp = NULL; - } + if (!ida) goto bad; - } } map->depth++; } @@ -416,10 +398,7 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) { - JSSharpObjectMap *map; - JSIdArray *ida; - - map = &cx->sharpObjectMap; + JSSharpObjectMap *map = &cx->sharpObjectMap; JS_ASSERT(map->depth > 0); if (--map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); @@ -428,8 +407,7 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) map->table = NULL; } if (idap) { - ida = *idap; - if (ida) { + if (JSIdArray *ida = *idap) { JS_DestroyIdArray(cx, ida); *idap = NULL; } @@ -439,7 +417,7 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) static intN gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) { - MarkObject((JSTracer *)arg, *(JSObject *)he->key, "sharp table entry"); + MarkObjectRoot((JSTracer *)arg, (JSObject *)he->key, "sharp table entry"); return JS_DHASH_NEXT; } @@ -476,88 +454,64 @@ js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) static JSBool obj_toSource(JSContext *cx, uintN argc, Value *vp) { - JSBool ok; - JSHashEntry *he; - JSIdArray *ida; - jschar *chars, *ochars, *vsharp; - const jschar *idstrchars, *vchars; - size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; - const char *comma; - JSObject *obj2; - JSProperty *prop; + bool comma = false; + const jschar *vchars; + size_t vlength; Value *val; JSString *gsop[2]; - JSString *valstr, *str; - JSLinearString *idstr; JS_CHECK_RECURSION(cx, return JS_FALSE); Value localroot[4]; PodArrayZero(localroot); - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot); + AutoArrayRooter tvr(cx, ArrayLength(localroot), localroot); /* If outermost, we need parentheses to be an expression, not a block. */ - JSBool outermost = (cx->sharpObjectMap.depth == 0); + bool outermost = (cx->sharpObjectMap.depth == 0); JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; - if (!(he = js_EnterSharpObject(cx, obj, &ida, &chars))) { - ok = JS_FALSE; - goto out; - } + JSIdArray *ida; + bool alreadySeen = false; + JSHashEntry *he = js_EnterSharpObject(cx, obj, &ida, &alreadySeen); + if (!he) + return false; + if (!ida) { /* - * We didn't enter -- obj is already "sharp", meaning we've visited it - * already in our depth first search, and therefore chars contains a - * string of the form "#n#". + * We've already seen obj, so don't serialize it again (particularly as + * we might recur in the process): just serialize an empty object. */ - JS_ASSERT(IS_SHARP(he)); -#if JS_HAS_SHARP_VARS - nchars = js_strlen(chars); -#else - chars[0] = '{'; - chars[1] = '}'; - chars[2] = 0; - nchars = 2; -#endif - goto make_string; + JS_ASSERT(alreadySeen); + JSString *str = js_NewStringCopyZ(cx, "{}"); + if (!str) + return false; + vp->setString(str); + return true; } JS_ASSERT(!IS_SHARP(he)); - ok = JS_TRUE; - - if (!chars) { - /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ - chars = (jschar *) cx->malloc_(((outermost ? 4 : 2) + 1) * sizeof(jschar)); - nchars = 0; - if (!chars) - goto error; - if (outermost) - chars[nchars++] = '('; - } else { - /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ + + if (alreadySeen) MAKE_SHARP(he); - nchars = js_strlen(chars); - chars = (jschar *) - cx->realloc_((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); - if (!chars) { - Foreground::free_(ochars); - goto error; - } - if (outermost) { - /* - * No need for parentheses around the whole shebang, because #n= - * unambiguously begins an object initializer, and never a block - * statement. - */ - outermost = JS_FALSE; - } - } - chars[nchars++] = '{'; + /* Automatically call js_LeaveSharpObject when we leave this frame. */ + class AutoLeaveSharpObject { + JSContext *cx; + JSIdArray *ida; + public: + AutoLeaveSharpObject(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) {} + ~AutoLeaveSharpObject() { + js_LeaveSharpObject(cx, &ida); + } + } autoLeaveSharpObject(cx, ida); - comma = NULL; + StringBuffer buf(cx); + if (outermost && !buf.append('(')) + return false; + if (!buf.append('{')) + return false; /* * We have four local roots for cooked and raw value GC safety. Hoist the @@ -566,26 +520,26 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) */ val = localroot + 2; - for (jsint i = 0, length = ida->length; i < length; i++) { + for (jsint i = 0; i < ida->length; i++) { /* Get strings for id and value and GC-root them via vp. */ jsid id = ida->vector[i]; + JSLinearString *idstr; - ok = obj->lookupProperty(cx, id, &obj2, &prop); - if (!ok) - goto error; + JSObject *obj2; + JSProperty *prop; + if (!obj->lookupGeneric(cx, id, &obj2, &prop)) + return false; /* * Convert id to a value and then to a string. Decide early whether we * prefer get/set or old getter/setter syntax. */ - JSString *s = js_ValueToString(cx, IdToValue(id)); - if (!s || !(idstr = s->ensureLinear(cx))) { - ok = JS_FALSE; - goto error; - } + JSString *s = ToString(cx, IdToValue(id)); + if (!s || !(idstr = s->ensureLinear(cx))) + return false; vp->setString(idstr); /* local root */ - jsint valcnt = 0; + int valcnt = 0; if (prop) { bool doGet = true; if (obj2->isNative()) { @@ -607,9 +561,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) if (doGet) { valcnt = 1; gsop[0] = NULL; - ok = obj->getProperty(cx, id, &val[0]); - if (!ok) - goto error; + if (!obj->getGeneric(cx, id, &val[0])) + return false; } } @@ -617,25 +570,16 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ - bool idIsLexicalIdentifier = js_IsIdentifier(idstr); if (JSID_IS_ATOM(id) - ? !idIsLexicalIdentifier + ? !IsIdentifier(idstr) : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { s = js_QuoteString(cx, idstr, jschar('\'')); - if (!s || !(idstr = s->ensureLinear(cx))) { - ok = JS_FALSE; - goto error; - } + if (!s || !(idstr = s->ensureLinear(cx))) + return false; vp->setString(idstr); /* local root */ } - idstrlength = idstr->length(); - idstrchars = idstr->getChars(cx); - if (!idstrchars) { - ok = JS_FALSE; - goto error; - } - for (jsint j = 0; j < valcnt; j++) { + for (int j = 0; j < valcnt; j++) { /* * Censor an accessor descriptor getter or setter part if it's * undefined. @@ -644,46 +588,15 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) continue; /* Convert val[j] to its canonical source form. */ - valstr = js_ValueToSource(cx, val[j]); - if (!valstr) { - ok = JS_FALSE; - goto error; - } + JSString *valstr = js_ValueToSource(cx, val[j]); + if (!valstr) + return false; localroot[j].setString(valstr); /* local root */ vchars = valstr->getChars(cx); - if (!vchars) { - ok = JS_FALSE; - goto error; - } + if (!vchars) + return false; vlength = valstr->length(); - /* - * If val[j] is a non-sharp object, and we're not serializing an - * accessor (ECMA syntax can't accommodate sharpened accessors), - * consider sharpening it. - */ - vsharp = NULL; - vsharplength = 0; -#if JS_HAS_SHARP_VARS - if (!gsop[j] && val[j].isObject() && vchars[0] != '#') { - he = js_EnterSharpObject(cx, &val[j].toObject(), NULL, &vsharp); - if (!he) { - ok = JS_FALSE; - goto error; - } - if (IS_SHARP(he)) { - vchars = vsharp; - vlength = js_strlen(vchars); - } else { - if (vsharp) { - vsharplength = js_strlen(vsharp); - MAKE_SHARP(he); - } - js_LeaveSharpObject(cx, NULL); - } - } -#endif - /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. @@ -692,7 +605,7 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) const jschar *start = vchars; const jschar *end = vchars + vlength; - uint8 parenChomp = 0; + uint8_t parenChomp = 0; if (vchars[0] == '(') { vchars++; parenChomp = 1; @@ -719,104 +632,34 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) } } -#define SAFE_ADD(n) \ - JS_BEGIN_MACRO \ - size_t n_ = (n); \ - curlen += n_; \ - if (curlen < n_) \ - goto overflow; \ - JS_END_MACRO - - curlen = nchars; - if (comma) - SAFE_ADD(2); - SAFE_ADD(idstrlength + 1); + if (comma && !buf.append(", ")) + return false; + comma = true; + if (gsop[j]) - SAFE_ADD(gsop[j]->length() + 1); - SAFE_ADD(vsharplength); - SAFE_ADD(vlength); - /* Account for the trailing null. */ - SAFE_ADD((outermost ? 2 : 1) + 1); -#undef SAFE_ADD - - if (curlen > size_t(-1) / sizeof(jschar)) - goto overflow; - - /* Allocate 1 + 1 at end for closing brace and terminating 0. */ - chars = (jschar *) cx->realloc_((ochars = chars), curlen * sizeof(jschar)); - if (!chars) { - chars = ochars; - goto overflow; - } + if (!buf.append(gsop[j]) || !buf.append(' ')) + return false; - if (comma) { - chars[nchars++] = comma[0]; - chars[nchars++] = comma[1]; - } - comma = ", "; - - if (gsop[j]) { - gsoplength = gsop[j]->length(); - const jschar *gsopchars = gsop[j]->getChars(cx); - if (!gsopchars) - goto overflow; - js_strncpy(&chars[nchars], gsopchars, gsoplength); - nchars += gsoplength; - chars[nchars++] = ' '; - } - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - /* Extraneous space after id here will be extracted later */ - chars[nchars++] = gsop[j] ? ' ' : ':'; - - if (vsharplength) { - js_strncpy(&chars[nchars], vsharp, vsharplength); - nchars += vsharplength; - } - js_strncpy(&chars[nchars], vchars, vlength); - nchars += vlength; + if (!buf.append(idstr)) + return false; + if (!buf.append(gsop[j] ? ' ' : ':')) + return false; - if (vsharp) - cx->free_(vsharp); + if (!buf.append(vchars, vlength)) + return false; } } - chars[nchars++] = '}'; - if (outermost) - chars[nchars++] = ')'; - chars[nchars] = 0; - - error: - js_LeaveSharpObject(cx, &ida); - - if (!ok) { - if (chars) - Foreground::free_(chars); - goto out; - } + if (!buf.append('}')) + return false; + if (outermost && !buf.append(')')) + return false; - if (!chars) { - JS_ReportOutOfMemory(cx); - ok = JS_FALSE; - goto out; - } - make_string: - str = js_NewString(cx, chars, nchars); - if (!str) { - cx->free_(chars); - ok = JS_FALSE; - goto out; - } + JSString *str = buf.finishString(); + if (!str) + return false; vp->setString(str); - ok = JS_TRUE; - out: - return ok; - - overflow: - cx->free_(vsharp); - cx->free_(chars); - chars = NULL; - goto error; + return true; } #endif /* JS_HAS_TOSOURCE */ @@ -826,27 +669,16 @@ JSString * obj_toStringHelper(JSContext *cx, JSObject *obj) { if (obj->isProxy()) - return JSProxy::obj_toString(cx, obj); + return Proxy::obj_toString(cx, obj); - const char *clazz = obj->getClass()->name; - size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ - jschar *chars = (jschar *) cx->malloc_((nchars + 1) * sizeof(jschar)); - if (!chars) + StringBuffer sb(cx); + const char *className = obj->getClass()->name; + if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) || + !sb.append("]")) + { return NULL; - - const char *prefix = "[object "; - nchars = 0; - while ((chars[nchars] = (jschar)*prefix) != 0) - nchars++, prefix++; - while ((chars[nchars] = (jschar)*clazz) != 0) - nchars++, clazz++; - chars[nchars++] = ']'; - chars[nchars] = 0; - - JSString *str = js_NewString(cx, chars, nchars); - if (!str) - cx->free_(chars); - return str; + } + return sb.finishString(); } JSObject * @@ -859,8 +691,26 @@ NonNullObject(JSContext *cx, const Value &v) return &v.toObject(); } +const char * +InformalValueTypeName(const Value &v) +{ + if (v.isObject()) + return v.toObject().getClass()->name; + if (v.isString()) + return "string"; + if (v.isNumber()) + return "number"; + if (v.isBoolean()) + return "boolean"; + if (v.isNull()) + return "null"; + if (v.isUndefined()) + return "undefined"; + return "value"; } +} /* namespace js */ + /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */ static JSBool obj_toString(JSContext *cx, uintN argc, Value *vp) @@ -892,19 +742,19 @@ obj_toString(JSContext *cx, uintN argc, Value *vp) return true; } +/* ES5 15.2.4.3. */ static JSBool obj_toLocaleString(JSContext *cx, uintN argc, Value *vp) { + JS_CHECK_RECURSION(cx, return false); + + /* Step 1. */ JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; - JSString *str = js_ValueToString(cx, ObjectValue(*obj)); - if (!str) - return JS_FALSE; - - vp->setString(str); - return JS_TRUE; + /* Steps 2-4. */ + return obj->callMethod(cx, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), 0, NULL, vp); } static JSBool @@ -917,22 +767,12 @@ obj_valueOf(JSContext *cx, uintN argc, Value *vp) return true; } -/* - * Check if CSP allows new Function() or eval() to run in the current - * principals. - */ -JSBool -js_CheckContentSecurityPolicy(JSContext *cx, JSObject *scopeobj) -{ - return scopeobj->getGlobal()->isEvalAllowed(cx); -} - /* We should be able to assert this for *any* fp->scopeChain(). */ static void AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj) { #ifdef DEBUG - for (JSObject *o = &scopeobj; o; o = o->getParent()) { + for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) { if (JSObjectOp op = o->getClass()->ext.innerObject) JS_ASSERT(op(cx, o) == o); } @@ -951,13 +791,13 @@ EvalCacheHash(JSContext *cx, JSLinearString *str) if (n > 100) n = 100; - uint32 h; + uint32_t h; for (h = 0; n; s++, n--) h = JS_ROTATE_LEFT32(h, 4) ^ *s; h *= JS_GOLDEN_RATIO; h >>= 32 - JS_EVAL_CACHE_SHIFT; - return &JS_SCRIPTS_TO_GC(cx)[h]; + return &cx->compartment->evalCache[h]; } static JS_ALWAYS_INLINE JSScript * @@ -983,7 +823,6 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st uintN count = 0; JSScript **scriptp = bucket; - EVAL_CACHE_METER(probe); JSVersion version = cx->findVersion(); JSScript *script; while ((script = *scriptp) != NULL) { @@ -992,47 +831,35 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st script->getVersion() == version && !script->hasSingletons && (script->principals == principals || - (principals->subsume(principals, script->principals) && + (principals && script->principals && + principals->subsume(principals, script->principals) && script->principals->subsume(script->principals, principals)))) { /* * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. + * See frontend::CompileScript. */ - JSFunction *fun = script->getFunction(0); + JSFunction *fun = script->getCallerFunction(); if (fun == caller->fun()) { /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. + * Get the source string passed for safekeeping in the atom map + * by the prior eval to frontend::CompileScript. */ - JSAtom *src = script->atomMap.vector[0]; + JSAtom *src = script->atoms[0]; if (src == str || EqualStrings(src, str)) { /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. + * Source matches. Make sure there are no inner objects + * which might use the wrong parent and/or call scope by + * reusing the previous eval's script. Skip the script's + * first object, which entrains the eval's scope. */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (JSScript::isValidOffset(script->regexpsOffset)) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == &scopeobj) { + JS_ASSERT(script->objects()->length >= 1); + if (script->objects()->length == 1 && + !JSScript::isValidOffset(script->regexpsOffset)) { JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; + *scriptp = script->evalHashLink(); + script->evalHashLink() = NULL; return script; } } @@ -1041,8 +868,7 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st if (++count == EVAL_CACHE_CHAIN_LIMIT) return NULL; - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; + scriptp = &script->evalHashLink(); } return NULL; } @@ -1056,7 +882,7 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st * a jsdbgapi user's perspective, we want each eval() to create and destroy a * script. This hides implementation details and means we don't have to deal * with calls to JS_GetScriptObject for scripts in the eval cache (currently, - * script->u.object aliases script->u.nextToGC). + * script->object aliases script->evalHashLink()). */ class EvalScriptGuard { @@ -1076,11 +902,10 @@ class EvalScriptGuard ~EvalScriptGuard() { if (script_) { js_CallDestroyScriptHook(cx_, script_); - script_->u.nextToGC = *bucket_; + script_->isActiveEval = false; + script_->isCachedEval = true; + script_->evalHashLink() = *bucket_; *bucket_ = script_; -#ifdef CHECK_SCRIPT_OWNER - script_->owner = NULL; -#endif } } @@ -1090,13 +915,16 @@ class EvalScriptGuard principals, scopeobj, bucket_)) { js_CallNewScriptHook(cx_, found, NULL); script_ = found; + script_->isCachedEval = false; + script_->isActiveEval = true; } } void setNewScript(JSScript *script) { - /* NewScriptFromCG has already called js_CallNewScriptHook. */ + /* NewScriptFromEmitter has already called js_CallNewScriptHook. */ JS_ASSERT(!script_ && script); script_ = script; + script_->isActiveEval = true; } bool foundScript() { @@ -1109,6 +937,9 @@ class EvalScriptGuard } }; +/* Define subset of ExecuteType so that casting performs the injection. */ +enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL }; + /* * Common code implementing direct and indirect eval. * @@ -1119,34 +950,28 @@ class EvalScriptGuard * * On success, store the completion value in call.rval and return true. */ -enum EvalType { DIRECT_EVAL, INDIRECT_EVAL }; - static bool -EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *caller, +EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller, JSObject &scopeobj) { JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL)); AssertInnerizedScopeChain(cx, scopeobj); - /* - * CSP check: Is eval() allowed at all? - * Report errors via CSP is done in the script security mgr. - */ - if (!js_CheckContentSecurityPolicy(cx, &scopeobj)) { - JS_ReportError(cx, "call to eval() blocked by CSP"); + if (!scopeobj.global().isRuntimeCodeGenEnabled(cx)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL); return false; } /* ES5 15.1.2.1 step 1. */ - if (call.argc() < 1) { - call.rval().setUndefined(); + if (args.length() < 1) { + args.rval().setUndefined(); return true; } - if (!call[0].isString()) { - call.rval() = call[0]; + if (!args[0].isString()) { + args.rval() = args[0]; return true; } - JSString *str = call[0].toString(); + JSString *str = args[0].toString(); /* ES5 15.1.2.1 steps 2-8. */ @@ -1156,17 +981,32 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c * may not exist in the current frame if it doesn't see 'eval'.) */ uintN staticLevel; + Value thisv; if (evalType == DIRECT_EVAL) { staticLevel = caller->script()->staticLevel + 1; + /* + * Direct calls to eval are supposed to see the caller's |this|. If we + * haven't wrapped that yet, do so now, before we make a copy of it for + * the eval code to use. + */ + if (!ComputeThis(cx, caller)) + return false; + thisv = caller->thisValue(); + #ifdef DEBUG - jsbytecode *callerPC = caller->pc(cx); - JS_ASSERT_IF(caller->isFunctionFrame(), caller->fun()->isHeavyweight()); - JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL); + jsbytecode *callerPC = caller->pcQuadratic(cx); + JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL); #endif } else { - JS_ASSERT(call.callee().getGlobal() == &scopeobj); + JS_ASSERT(args.callee().global() == scopeobj); staticLevel = 0; + + /* Use the global as 'this', modulo outerization. */ + JSObject *thisobj = scopeobj.thisObject(cx); + if (!thisobj) + return false; + thisv = ObjectValue(*thisobj); } JSLinearString *linearStr = str->ensureLinear(cx); @@ -1176,68 +1016,76 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c size_t length = linearStr->length(); /* - * If the eval string starts with '(' and ends with ')', it may be JSON. + * If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON. * Try the JSON parser first because it's much faster. If the eval string * isn't JSON, JSON parsing will probably fail quickly, so little time * will be lost. + * + * Don't use the JSON parser if the caller is strict mode code, because in + * strict mode object literals must not have repeated properties, and the + * JSON parser cheerfully (and correctly) accepts them. If you're parsing + * JSON with eval and using strict mode, you deserve to be slow. */ - if (length > 2 && chars[0] == '(' && chars[length - 1] == ')') { -#if USE_OLD_AND_BUSTED_JSON_PARSER - Value tmp; - JSONParser *jp = js_BeginJSONParse(cx, &tmp, /* suppressErrors = */true); - if (jp != NULL) { - /* Run JSON-parser on string inside ( and ). */ - bool ok = js_ConsumeJSONText(cx, jp, chars + 1, length - 2); - ok &= js_FinishJSONParse(cx, jp, NullValue()); - if (ok) { - call.rval() = tmp; + if (length > 2 && + ((chars[0] == '[' && chars[length - 1] == ']') || + (chars[0] == '(' && chars[length - 1] == ')')) && + (!caller || !caller->script()->strictModeCode)) + { + /* + * Remarkably, JavaScript syntax is not a superset of JSON syntax: + * strings in JavaScript cannot contain the Unicode line and paragraph + * terminator characters U+2028 and U+2029, but strings in JSON can. + * Rather than force the JSON parser to handle this quirk when used by + * eval, we simply don't use the JSON parser when either character + * appears in the provided string. See bug 657367. + */ + for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) { + if (*cp == 0x2028 || *cp == 0x2029) + break; + + if (cp == end) { + bool isArray = (chars[0] == '['); + JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2, + JSONParser::StrictJSON, JSONParser::NoError); + Value tmp; + if (!parser.parse(&tmp)) + return false; + if (tmp.isUndefined()) + break; + args.rval() = tmp; return true; } -#else - JSONSourceParser parser(cx, chars + 1, length - 2, JSONSourceParser::StrictJSON, - JSONSourceParser::NoError); - Value tmp; - if (!parser.parse(&tmp)) - return false; - if (!tmp.isUndefined()) { - call.rval() = tmp; - return true; } -#endif } - /* - * Direct calls to eval are supposed to see the caller's |this|. If we - * haven't wrapped that yet, do so now, before we make a copy of it for - * the eval code to use. - */ - if (evalType == DIRECT_EVAL && !ComputeThis(cx, caller)) - return false; - EvalScriptGuard esg(cx, linearStr); - JSPrincipals *principals = PrincipalsForCompiledCode(call, cx); + JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame()) esg.lookupInEvalCache(caller, staticLevel, principals, scopeobj); if (!esg.foundScript()) { uintN lineno; - const char *filename = CurrentScriptFileAndLine(cx, &lineno, - evalType == DIRECT_EVAL - ? CALLED_FROM_JSOP_EVAL - : NOT_CALLED_FROM_JSOP_EVAL); - uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL; - JSScript *compiled = Compiler::compileScript(cx, &scopeobj, caller, principals, tcflags, - chars, length, filename, lineno, - cx->findVersion(), linearStr, staticLevel); + const char *filename; + JSPrincipals *originPrincipals; + CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, + evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL + : NOT_CALLED_FROM_JSOP_EVAL); + uint32_t tcflags = TCF_COMPILE_N_GO | TCF_COMPILE_FOR_EVAL; + JSScript *compiled = frontend::CompileScript(cx, &scopeobj, caller, + principals, originPrincipals, + tcflags, chars, length, filename, + lineno, cx->findVersion(), linearStr, + staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } - return Execute(cx, scopeobj, esg.script(), caller, StackFrame::EVAL, &call.rval()); + return ExecuteKernel(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType), + NULL /* evalInFrame */, &args.rval()); } /* @@ -1246,17 +1094,17 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c * authors will know that support for eval(s, o) has been removed. */ static inline bool -WarnOnTooManyArgs(JSContext *cx, const CallArgs &call) +WarnOnTooManyArgs(JSContext *cx, const CallArgs &args) { - if (call.argc() > 1) { - if (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) { - if (!caller->script()->warnedAboutTwoArgumentEval) { + if (args.length() > 1) { + if (JSScript *script = cx->stack.currentScript()) { + if (!script->warnedAboutTwoArgumentEval) { static const char TWO_ARGUMENT_WARNING[] = "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) return false; - caller->script()->warnedAboutTwoArgumentEval = true; + script->warnedAboutTwoArgumentEval = true; } } else { /* @@ -1269,44 +1117,46 @@ WarnOnTooManyArgs(JSContext *cx, const CallArgs &call) return true; } +namespace js { + /* * ES5 15.1.2.1. * * NB: This method handles only indirect eval. */ -static JSBool +JSBool eval(JSContext *cx, uintN argc, Value *vp) { - CallArgs call = CallArgsFromVp(argc, vp); - return WarnOnTooManyArgs(cx, call) && - EvalKernel(cx, call, INDIRECT_EVAL, NULL, *call.callee().getGlobal()); + CallArgs args = CallArgsFromVp(argc, vp); + return WarnOnTooManyArgs(cx, args) && + EvalKernel(cx, args, INDIRECT_EVAL, NULL, args.callee().global()); } -namespace js { - bool -DirectEval(JSContext *cx, const CallArgs &call) +DirectEval(JSContext *cx, const CallArgs &args) { /* Direct eval can assume it was called from an interpreted frame. */ StackFrame *caller = cx->fp(); JS_ASSERT(caller->isScriptFrame()); - JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), call.calleev())); - JS_ASSERT(*cx->regs().pc == JSOP_EVAL); + JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), args.calleev())); + JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL); + + AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script()); - AutoFunctionCallProbe callProbe(cx, call.callee().getFunctionPrivate(), caller->script()); + JSObject *scopeChain = GetScopeChain(cx, caller); + if (!scopeChain) + return false; - JSObject *scopeChain = - GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); + if (!WarnOnTooManyArgs(cx, args)) + return false; - return scopeChain && - WarnOnTooManyArgs(cx, call) && - EvalKernel(cx, call, DIRECT_EVAL, caller, *scopeChain); + return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain); } bool IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v) { - return scopeChain->getGlobal()->getOriginalEval() == v; + return scopeChain->global().getOriginalEval() == v; } bool @@ -1316,10 +1166,10 @@ IsAnyBuiltinEval(JSFunction *fun) } JSPrincipals * -PrincipalsForCompiledCode(const CallArgs &call, JSContext *cx) +PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx) { - JS_ASSERT(IsAnyBuiltinEval(call.callee().getFunctionPrivate()) || - IsBuiltinFunctionConstructor(call.callee().getFunctionPrivate())); + JS_ASSERT(IsAnyBuiltinEval(call.callee().toFunction()) || + IsBuiltinFunctionConstructor(call.callee().toFunction())); /* * To compute the principals of the compiled eval/Function code, we simply @@ -1337,19 +1187,7 @@ PrincipalsForCompiledCode(const CallArgs &call, JSContext *cx) * fp->script()->compartment() != fp->compartment(). */ - JSPrincipals *calleePrincipals = call.callee().compartment()->principals; - -#ifdef DEBUG - if (calleePrincipals) { - if (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) { - if (JSPrincipals *callerPrincipals = caller->scopeChain().principals(cx)) { - JS_ASSERT(callerPrincipals->subsume(callerPrincipals, calleePrincipals)); - } - } - } -#endif - - return calleePrincipals; + return call.callee().principals(cx); } } /* namespace js */ @@ -1362,8 +1200,8 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old, { JSObject *callable = (JSObject *) closure; if (JSPrincipals *watcher = callable->principals(cx)) { - if (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) { - if (JSPrincipals *subject = caller->scopeChain().principals(cx)) { + if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) { + if (JSPrincipals *subject = scopeChain->principals(cx)) { if (!watcher->subsume(watcher, subject)) { /* Silently don't call the watch handler. */ return JS_TRUE; @@ -1377,9 +1215,8 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old, if (resolving.alreadyStarted()) return true; - Value argv[] = { IdToValue(id), Valueify(old), Valueify(*nvp) }; - return ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), - JS_ARRAY_LENGTH(argv), argv, Valueify(nvp)); + Value argv[] = { IdToValue(id), old, *nvp }; + return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp); } static JSBool @@ -1394,7 +1231,6 @@ obj_watch(JSContext *cx, uintN argc, Value *vp) if (!callable) return JS_FALSE; - /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ jsid propid; if (!ValueToId(cx, vp[2], &propid)) return JS_FALSE; @@ -1448,11 +1284,11 @@ obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp) JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; - return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp); + return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, argc, vp); } JSBool -js_HasOwnPropertyHelper(JSContext *cx, LookupPropOp lookup, uintN argc, +js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, uintN argc, Value *vp) { jsid id; @@ -1466,7 +1302,7 @@ js_HasOwnPropertyHelper(JSContext *cx, LookupPropOp lookup, uintN argc, JSProperty *prop; if (obj->isProxy()) { bool has; - if (!JSProxy::hasOwn(cx, obj, id, &has)) + if (!Proxy::hasOwn(cx, obj, id, &has)) return false; vp->setBoolean(has); return true; @@ -1478,7 +1314,7 @@ js_HasOwnPropertyHelper(JSContext *cx, LookupPropOp lookup, uintN argc, } JSBool -js_HasOwnProperty(JSContext *cx, LookupPropOp lookup, JSObject *obj, jsid id, +js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING); @@ -1490,7 +1326,6 @@ js_HasOwnProperty(JSContext *cx, LookupPropOp lookup, JSObject *obj, jsid id, if (*objp == obj) return true; - Class *clasp = (*objp)->getClass(); JSObject *outer = NULL; if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) { outer = op(cx, *objp); @@ -1498,30 +1333,8 @@ js_HasOwnProperty(JSContext *cx, LookupPropOp lookup, JSObject *obj, jsid id, return false; } - if (outer != *objp) { - if ((*objp)->isNative() && obj->getClass() == clasp) { - /* - * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a - * delegated property makes that property appear to be direct in - * all delegating instances of the same native class. This hack - * avoids bloating every function instance with its own 'length' - * (AKA 'arity') property. But it must not extend across class - * boundaries, to avoid making hasOwnProperty lie (bug 320854). - * - * It's not really a hack, of course: a permanent property can't - * be deleted, and JSPROP_SHARED means "don't allocate a slot in - * any instance, prototype or delegating". Without a slot, and - * without the ability to remove and recreate (with differences) - * the property, there is no way to tell whether it is directly - * owned, or indirectly delegated. - */ - Shape *shape = reinterpret_cast(*propp); - if (shape->isSharedPermanent()) - return true; - } - + if (outer != *objp) *propp = NULL; - } return true; } @@ -1568,40 +1381,27 @@ js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp) { JSObject *pobj; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &pobj, &prop)) - return JS_FALSE; + if (!obj->lookupGeneric(cx, id, &pobj, &prop)) + return false; if (!prop) { vp->setBoolean(false); - return JS_TRUE; + return true; } /* - * XXX ECMA spec error compatible: return false unless hasOwnProperty. - * The ECMA spec really should be fixed so propertyIsEnumerable and the - * for..in loop agree on whether prototype properties are enumerable, - * obviously by fixing this method (not by breaking the for..in loop!). - * - * We check here for shared permanent prototype properties, which should - * be treated as if they are local to obj. They are an implementation - * technique used to satisfy ECMA requirements; users should not be able - * to distinguish a shared permanent proto-property from a local one. + * ECMA spec botch: return false unless hasOwnProperty. Leaving "own" out + * of propertyIsEnumerable's name was a mistake. */ - bool shared; - uintN attrs; - if (pobj->isNative()) { - Shape *shape = (Shape *) prop; - shared = shape->isSharedPermanent(); - attrs = shape->attributes(); - } else { - shared = false; - if (!pobj->getAttributes(cx, id, &attrs)) - return false; - } - if (pobj != obj && !shared) { + if (pobj != obj) { vp->setBoolean(false); return true; } + + uintN attrs; + if (!pobj->getGenericAttributes(cx, id, &attrs)) + return false; + vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0); return true; } @@ -1613,72 +1413,62 @@ const char js_defineSetter_str[] = "__defineSetter__"; const char js_lookupGetter_str[] = "__lookupGetter__"; const char js_lookupSetter_str[] = "__lookupSetter__"; -JS_FRIEND_API(JSBool) -js_obj_defineGetter(JSContext *cx, uintN argc, Value *vp) +enum DefineType { Getter, Setter }; + +template +static bool +DefineAccessor(JSContext *cx, uintN argc, Value *vp) { - CallArgs call = CallArgsFromVp(argc, vp); - if (!BoxNonStrictThis(cx, call)) + CallArgs args = CallArgsFromVp(argc, vp); + if (!BoxNonStrictThis(cx, args)) return false; - JSObject *obj = &call.thisv().toObject(); - if (argc <= 1 || !js_IsCallable(call[1])) { + if (args.length() < 2 || !js_IsCallable(args[1])) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, - js_getter_str); - return JS_FALSE; + Type == Getter ? js_getter_str : js_setter_str); + return false; } - PropertyOp getter = CastAsPropertyOp(&call[1].toObject()); jsid id; - if (!ValueToId(cx, call[0], &id)) - return JS_FALSE; - if (!CheckRedeclaration(cx, obj, id, JSPROP_GETTER)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - Value junk; - uintN attrs; - if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - call.rval().setUndefined(); - return obj->defineProperty(cx, id, UndefinedValue(), getter, StrictPropertyStub, - JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED); + if (!ValueToId(cx, args[0], &id)) + return false; + + JSObject *descObj = NewBuiltinClassInstance(cx, &ObjectClass); + if (!descObj) + return false; + + JSAtomState &state = cx->runtime->atomState; + /* enumerable: true */ + if (!descObj->defineProperty(cx, state.enumerableAtom, BooleanValue(true))) + return false; + + /* configurable: true */ + if (!descObj->defineProperty(cx, state.configurableAtom, BooleanValue(true))) + return false; + + /* enumerable: true */ + PropertyName *acc = (Type == Getter) ? state.getAtom : state.setAtom; + if (!descObj->defineProperty(cx, acc, args[1])) + return false; + + JSBool dummy; + if (!js_DefineOwnProperty(cx, &args.thisv().toObject(), id, ObjectValue(*descObj), &dummy)) + return false; + args.rval().setUndefined(); + return true; } JS_FRIEND_API(JSBool) -js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp) +js::obj_defineGetter(JSContext *cx, uintN argc, Value *vp) { - CallArgs call = CallArgsFromVp(argc, vp); - if (!BoxNonStrictThis(cx, call)) - return false; - JSObject *obj = &call.thisv().toObject(); - - if (argc <= 1 || !js_IsCallable(call[1])) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - js_setter_str); - return JS_FALSE; - } - StrictPropertyOp setter = CastAsStrictPropertyOp(&call[1].toObject()); + return DefineAccessor(cx, argc, vp); +} - jsid id; - if (!ValueToId(cx, call[0], &id)) - return JS_FALSE; - if (!CheckRedeclaration(cx, obj, id, JSPROP_SETTER)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - Value junk; - uintN attrs; - if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - call.rval().setUndefined(); - return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter, - JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED); +JS_FRIEND_API(JSBool) +js::obj_defineSetter(JSContext *cx, uintN argc, Value *vp) +{ + return DefineAccessor(cx, argc, vp); } static JSBool @@ -1692,7 +1482,7 @@ obj_lookupGetter(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; JSObject *pobj; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &pobj, &prop)) + if (!obj->lookupGeneric(cx, id, &pobj, &prop)) return JS_FALSE; vp->setUndefined(); if (prop) { @@ -1716,7 +1506,7 @@ obj_lookupSetter(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; JSObject *pobj; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &pobj, &prop)) + if (!obj->lookupGeneric(cx, id, &pobj, &prop)) return JS_FALSE; vp->setUndefined(); if (prop) { @@ -1754,86 +1544,133 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp) JSACC_PROTO, vp, &attrs); } -extern JSBool -js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, - const Value &getter, const Value &setter, - const Value &value, Value *vp) +namespace js { + +bool +NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp) { + if (!desc->obj) { + vp->setUndefined(); + return true; + } + /* We have our own property, so start creating the descriptor. */ - JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass); - if (!desc) + PropDesc d; + d.initFromPropertyDescriptor(*desc); + if (!d.makeObject(cx)) return false; - vp->setObject(*desc); /* Root and return. */ + *vp = d.pd; + return true; +} - const JSAtomState &atomState = cx->runtime->atomState; - if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { - if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter, - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE) || - !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter, - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE)) { - return false; - } +void +PropDesc::initFromPropertyDescriptor(const PropertyDescriptor &desc) +{ + pd.setUndefined(); + attrs = uint8_t(desc.attrs); + JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + hasGet = true; + get = ((desc.attrs & JSPROP_GETTER) && desc.getter) + ? CastAsObjectJsval(desc.getter) + : UndefinedValue(); + hasSet = true; + set = ((desc.attrs & JSPROP_SETTER) && desc.setter) + ? CastAsObjectJsval(desc.setter) + : UndefinedValue(); + hasValue = false; + value.setUndefined(); + hasWritable = false; } else { - if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value, - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE) || - !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom), - BooleanValue((attrs & JSPROP_READONLY) == 0), - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE)) { - return false; - } + hasGet = false; + get.setUndefined(); + hasSet = false; + set.setUndefined(); + hasValue = true; + value = desc.value; + hasWritable = true; + } + hasEnumerable = true; + hasConfigurable = true; +} + +bool +PropDesc::makeObject(JSContext *cx) +{ + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); + if (!obj) + return false; + + const JSAtomState &atomState = cx->runtime->atomState; + if ((hasConfigurable && + !obj->defineProperty(cx, atomState.configurableAtom, + BooleanValue((attrs & JSPROP_PERMANENT) == 0))) || + (hasEnumerable && + !obj->defineProperty(cx, atomState.enumerableAtom, + BooleanValue((attrs & JSPROP_ENUMERATE) != 0))) || + (hasGet && + !obj->defineProperty(cx, atomState.getAtom, get)) || + (hasSet && + !obj->defineProperty(cx, atomState.setAtom, set)) || + (hasValue && + !obj->defineProperty(cx, atomState.valueAtom, value)) || + (hasWritable && + !obj->defineProperty(cx, atomState.writableAtom, + BooleanValue((attrs & JSPROP_READONLY) == 0)))) + { + return false; } - return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom), - BooleanValue((attrs & JSPROP_ENUMERATE) != 0), - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE) && - desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom), - BooleanValue((attrs & JSPROP_PERMANENT) == 0), - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE); + pd.setObject(*obj); + return true; } -JSBool -js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp) +bool +GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc) { if (obj->isProxy()) - return JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, vp); + return Proxy::getOwnPropertyDescriptor(cx, obj, id, false, desc); JSObject *pobj; JSProperty *prop; - if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop)) + if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &prop)) return false; if (!prop) { - vp->setUndefined(); + desc->obj = NULL; return true; } - Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() }; - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots); - unsigned attrs; bool doGet = true; if (pobj->isNative()) { Shape *shape = (Shape *) prop; - attrs = shape->attributes(); - if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + desc->attrs = shape->attributes(); + if (desc->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { doGet = false; - if (attrs & JSPROP_GETTER) - roots[0] = shape->getterValue(); - if (attrs & JSPROP_SETTER) - roots[1] = shape->setterValue(); + if (desc->attrs & JSPROP_GETTER) + desc->getter = CastAsPropertyOp(shape->getterObject()); + if (desc->attrs & JSPROP_SETTER) + desc->setter = CastAsStrictPropertyOp(shape->setterObject()); } } else { - if (!pobj->getAttributes(cx, id, &attrs)) + if (!pobj->getGenericAttributes(cx, id, &desc->attrs)) return false; } - if (doGet && !obj->getProperty(cx, id, &roots[2])) + if (doGet && !obj->getGeneric(cx, id, &desc->value)) return false; - return js_NewPropertyDescriptorObject(cx, id, - attrs, - roots[0], /* getter */ - roots[1], /* setter */ - roots[2], /* value */ - vp); + desc->obj = obj; + return true; +} + +bool +GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp) +{ + AutoPropertyDescriptorRooter desc(cx); + return GetOwnPropertyDescriptor(cx, obj, id, &desc) && + NewPropertyDescriptorObject(cx, &desc, vp); +} + } static bool @@ -1869,7 +1706,7 @@ obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, Value *vp) AutoIdRooter nameidr(cx); if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr())) return JS_FALSE; - return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp); + return GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp); } static JSBool @@ -1889,7 +1726,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp) for (size_t i = 0, len = props.length(); i < len; i++) { jsid id = props[i]; if (JSID_IS_STRING(id)) { - JS_ALWAYS_TRUE(vals.append(StringValue(JSID_TO_STRING(id)))); + vals.infallibleAppend(StringValue(JSID_TO_STRING(id))); } else if (JSID_IS_INT(id)) { JSString *str = js_IntToString(cx, JSID_TO_INT(id)); if (!str) @@ -1901,7 +1738,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp) } JS_ASSERT(props.length() <= UINT32_MAX); - JSObject *aobj = NewDenseCopiedArray(cx, jsuint(vals.length()), vals.begin()); + JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin()); if (!aobj) return false; vp->setObject(*aobj); @@ -1910,7 +1747,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp) } static bool -HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp) +HasProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, bool *foundp) { if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING)) return false; @@ -1923,9 +1760,9 @@ HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp) * We must go through the method read barrier in case id is 'get' or 'set'. * There is no obvious way to defer cloning a joined function object whose * identity will be used by DefinePropertyOnObject, e.g., or reflected via - * js_GetOwnPropertyDescriptor, as the getter or setter callable object. + * js::GetOwnPropertyDescriptor, as the getter or setter callable object. */ - return !!obj->getProperty(cx, id, vp); + return !!obj->getGeneric(cx, id, vp); } PropDesc::PropDesc() @@ -1944,7 +1781,7 @@ PropDesc::PropDesc() } bool -PropDesc::initialize(JSContext* cx, const Value &origval) +PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors) { Value v = origval; @@ -1953,7 +1790,7 @@ PropDesc::initialize(JSContext* cx, const Value &origval) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT); return false; } - JSObject* desc = &v.toObject(); + JSObject *desc = &v.toObject(); /* Make a copy of the descriptor. We might need it later. */ pd = v; @@ -2005,28 +1842,24 @@ PropDesc::initialize(JSContext* cx, const Value &origval) if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &found)) return false; if (found) { - if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD, - js_getter_str); - return false; - } hasGet = true; get = v; attrs |= JSPROP_GETTER | JSPROP_SHARED; + attrs &= ~JSPROP_READONLY; + if (checkAccessors && !checkGetter(cx)) + return false; } /* 8.10.7 step 8 */ if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &found)) return false; if (found) { - if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD, - js_setter_str); - return false; - } hasSet = true; set = v; attrs |= JSPROP_SETTER | JSPROP_SHARED; + attrs &= ~JSPROP_READONLY; + if (checkAccessors && !checkSetter(cx)) + return false; } /* 8.10.7 step 9 */ @@ -2035,6 +1868,8 @@ PropDesc::initialize(JSContext* cx, const Value &origval) return false; } + JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + return true; } @@ -2082,29 +1917,12 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD /* 8.12.9 step 1. */ JSProperty *current; JSObject *obj2; - JS_ASSERT(!obj->getOps()->lookupProperty); + JS_ASSERT(!obj->getOps()->lookupGeneric); if (!js_HasOwnProperty(cx, NULL, obj, id, &obj2, ¤t)) return JS_FALSE; JS_ASSERT(!obj->getOps()->defineProperty); - /* - * If we find a shared permanent property in a different object obj2 from - * obj, then if the property is shared permanent (an old hack to optimize - * per-object properties into one prototype property), ignore that lookup - * result (null current). - * - * FIXME: bug 575997 (see also bug 607863). - */ - if (current && obj2 != obj && obj2->isNative()) { - /* See same assertion with comment further below. */ - JS_ASSERT(obj2->getClass() == obj->getClass()); - - Shape *shape = (Shape *) current; - if (shape->isSharedPermanent()) - current = NULL; - } - /* 8.12.9 steps 2-4. */ if (!current) { if (!obj->isExtensible()) @@ -2115,7 +1933,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { JS_ASSERT(!obj->getOps()->defineProperty); return js_DefineProperty(cx, obj, id, &desc.value, - PropertyStub, StrictPropertyStub, desc.attrs); + JS_PropertyStub, JS_StrictPropertyStub, desc.attrs); } JS_ASSERT(desc.isAccessorDescriptor()); @@ -2137,15 +1955,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */ Value v = UndefinedValue(); - /* - * In the special case of shared permanent properties, the "own" property - * can be found on a different object. In that case the returned property - * might not be native, except: the shared permanent property optimization - * is not applied if the objects have different classes (bug 320854), as - * must be enforced by js_HasOwnProperty for the Shape cast below to be - * safe. - */ - JS_ASSERT(obj->getClass() == obj2->getClass()); + JS_ASSERT(obj == obj2); const Shape *shape = reinterpret_cast(current); do { @@ -2154,17 +1964,17 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD break; if (desc.hasGet) { - JSBool same; + bool same; if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) - return JS_FALSE; + return false; if (!same) break; } if (desc.hasSet) { - JSBool same; + bool same; if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) - return JS_FALSE; + return false; if (!same) break; } @@ -2203,10 +2013,10 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD if (!shape->isDataDescriptor()) break; - JSBool same; + bool same; if (desc.hasValue) { if (!SameValue(cx, desc.value, v, &same)) - return JS_FALSE; + return false; if (!same) { /* * Insist that a non-configurable js::PropertyOp data @@ -2281,9 +2091,9 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD if (desc.hasWritable && desc.writable()) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); if (desc.hasValue) { - JSBool same; + bool same; if (!SameValue(cx, desc.value, v, &same)) - return JS_FALSE; + return false; if (!same) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); } @@ -2295,17 +2105,17 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); if (!shape->configurable()) { if (desc.hasSet) { - JSBool same; + bool same; if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) - return JS_FALSE; + return false; if (!same) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); } if (desc.hasGet) { - JSBool same; + bool same; if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) - return JS_FALSE; + return false; if (!same) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); } @@ -2326,8 +2136,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD attrs = (shape->attributes() & ~changed) | (desc.attrs & changed); if (shape->isMethod()) { JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); - getter = PropertyStub; - setter = StrictPropertyStub; + getter = JS_PropertyStub; + setter = JS_StrictPropertyStub; } else { getter = shape->getter(); setter = shape->setter(); @@ -2338,14 +2148,15 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD unchanged |= JSPROP_PERMANENT; if (!desc.hasEnumerable) unchanged |= JSPROP_ENUMERATE; - if (!desc.hasWritable) + /* Watch out for accessor -> data transformations here. */ + if (!desc.hasWritable && shape->isDataDescriptor()) unchanged |= JSPROP_READONLY; if (desc.hasValue) v = desc.value; attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged); - getter = PropertyStub; - setter = StrictPropertyStub; + getter = JS_PropertyStub; + setter = JS_StrictPropertyStub; } else { JS_ASSERT(desc.isAccessorDescriptor()); @@ -2366,23 +2177,23 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD if (desc.hasEnumerable) changed |= JSPROP_ENUMERATE; if (desc.hasGet) - changed |= JSPROP_GETTER | JSPROP_SHARED; + changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY; if (desc.hasSet) - changed |= JSPROP_SETTER | JSPROP_SHARED; + changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY; attrs = (desc.attrs & changed) | (shape->attributes() & ~changed); if (desc.hasGet) { getter = desc.getter(); } else { getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue())) - ? PropertyStub + ? JS_PropertyStub : shape->getter(); } if (desc.hasSet) { setter = desc.setter(); } else { setter = (shape->hasDefaultSetter() && !shape->hasSetterValue()) - ? StrictPropertyStub + ? JS_StrictPropertyStub : shape->setter(); } } @@ -2435,7 +2246,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDe return JS_FALSE; } - uint32 index; + uint32_t index; if (js_IdIsIndex(id, &index)) { /* // Disabled until we support defining "length": @@ -2449,7 +2260,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDe if (index >= oldLen) { JS_ASSERT(index != UINT32_MAX); - obj->setArrayLength(index + 1); + obj->setArrayLength(cx, index + 1); } *rval = true; @@ -2459,22 +2270,26 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDe return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval); } -static JSBool +namespace js { + +bool DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError, bool *rval) { if (obj->isArray()) return DefinePropertyOnArray(cx, obj, id, desc, throwError, rval); - if (obj->getOps()->lookupProperty) { + if (obj->getOps()->lookupGeneric) { if (obj->isProxy()) - return JSProxy::defineProperty(cx, obj, id, desc.pd); + return Proxy::defineProperty(cx, obj, id, desc.pd); return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); } return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval); } +} /* namespace js */ + JSBool js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descriptor, JSBool *bp) { @@ -2492,51 +2307,62 @@ js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descrip /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */ static JSBool -obj_defineProperty(JSContext* cx, uintN argc, Value* vp) +obj_defineProperty(JSContext *cx, uintN argc, Value *vp) { - /* 15.2.3.6 steps 1 and 5. */ JSObject *obj; if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj)) - return JS_FALSE; - vp->setObject(*obj); + return false; - /* 15.2.3.6 step 2. */ - AutoIdRooter nameidr(cx); - if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr())) + jsid id; + if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), &id)) return JS_FALSE; - /* 15.2.3.6 step 3. */ - const Value &descval = argc >= 3 ? vp[4] : UndefinedValue(); + const Value descval = argc >= 3 ? vp[4] : UndefinedValue(); - /* 15.2.3.6 step 4 */ JSBool junk; - return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk); + if (!js_DefineOwnProperty(cx, obj, id, descval, &junk)) + return false; + + vp->setObject(*obj); + return true; +} + +namespace js { + +bool +ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors, + AutoIdVector *ids, AutoPropDescArrayRooter *descs) +{ + if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids)) + return false; + + for (size_t i = 0, len = ids->length(); i < len; i++) { + jsid id = (*ids)[i]; + PropDesc* desc = descs->append(); + Value v; + if (!desc || !props->getGeneric(cx, id, &v) || !desc->initialize(cx, v, checkAccessors)) + return false; + } + return true; } +} /* namespace js */ + static bool DefineProperties(JSContext *cx, JSObject *obj, JSObject *props) { AutoIdVector ids(cx); - if (!GetPropertyNames(cx, props, JSITER_OWNONLY, &ids)) + AutoPropDescArrayRooter descs(cx); + if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs)) return false; - AutoPropDescArrayRooter descs(cx); - size_t len = ids.length(); - for (size_t i = 0; i < len; i++) { - jsid id = ids[i]; - PropDesc* desc = descs.append(); - 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, ids[i], descs[i], true, &dummy)) - return false; - } + bool dummy; + for (size_t i = 0, len = ids.length(); i < len; i++) { + if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) + return false; + } - return true; + return true; } extern JSBool @@ -2547,7 +2373,7 @@ js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props) /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */ static JSBool -obj_defineProperties(JSContext* cx, uintN argc, Value* vp) +obj_defineProperties(JSContext *cx, uintN argc, Value *vp) { /* Steps 1 and 7. */ JSObject *obj; @@ -2561,7 +2387,7 @@ obj_defineProperties(JSContext* cx, uintN argc, Value* vp) "Object.defineProperties", "0", "s"); return false; } - JSObject* props = ToObject(cx, &vp[3]); + JSObject *props = ToObject(cx, &vp[3]); if (!props) return false; @@ -2590,16 +2416,24 @@ obj_create(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } + JSObject *proto = v.toObjectOrNull(); + if (proto && proto->isXML()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN); + return false; + } + /* * Use the callee's global as the parent of the new object to avoid dynamic * scoping (i.e., using the caller's global). */ - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, v.toObjectOrNull(), - vp->toObject().getGlobal()); + JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &vp->toObject().global()); if (!obj) return JS_FALSE; vp->setObject(*obj); /* Root and prepare for eventual return. */ + /* Don't track types or array-ness for objects created here. */ + MarkTypeObjectUnknownProperties(cx, obj->type()); + /* 15.2.3.5 step 4. */ if (argc > 1 && !vp[3].isUndefined()) { if (vp[3].isPrimitive()) { @@ -2633,7 +2467,7 @@ obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp) for (size_t i = 0, len = keys.length(); i < len; i++) { jsid id = keys[i]; if (JSID_IS_INT(id)) { - JSString *str = js_ValueToString(cx, Int32Value(JSID_TO_INT(id))); + JSString *str = js_IntToString(cx, JSID_TO_INT(id)); if (!str) return false; vals[i].setString(str); @@ -2678,12 +2512,23 @@ obj_preventExtensions(JSContext *cx, uintN argc, Value *vp) return obj->preventExtensions(cx, &props); } +/* static */ inline uintN +JSObject::getSealedOrFrozenAttributes(uintN attrs, ImmutabilityType it) +{ + /* Make all attributes permanent; if freezing, make data attributes read-only. */ + if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) + return JSPROP_PERMANENT | JSPROP_READONLY; + return JSPROP_PERMANENT; +} + bool JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it) { assertSameCompartment(cx, this); JS_ASSERT(it == SEAL || it == FREEZE); + RootedVarObject self(cx, this); + AutoIdVector props(cx); if (isExtensible()) { if (!preventExtensions(cx, &props)) @@ -2694,31 +2539,103 @@ JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it) } /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */ - JS_ASSERT(!isDenseArray()); + JS_ASSERT(!self->isDenseArray()); + + if (isNative() && !inDictionaryMode()) { + /* + * Seal/freeze non-dictionary objects by constructing a new shape + * hierarchy mirroring the original one, which can be shared if many + * objects with the same structure are sealed/frozen. If we use the + * generic path below then any non-empty object will be converted to + * dictionary mode. + */ + Shape *last = EmptyShape::getInitialShape(cx, self->getClass(), + self->getProto(), + self->getParent(), + self->getAllocKind(), + self->lastProperty()->getObjectFlags()); + if (!last) + return false; + + /* Get an in order list of the shapes in this object. */ + AutoShapeVector shapes(cx); + for (Shape::Range r = self->lastProperty()->all(); !r.empty(); r.popFront()) { + if (!shapes.append(&r.front())) + return false; + } + Reverse(shapes.begin(), shapes.end()); + + for (size_t i = 0; i < shapes.length(); i++) { + StackShape child(shapes[i]); + child.attrs |= getSealedOrFrozenAttributes(child.attrs, it); + + if (!JSID_IS_EMPTY(child.propid)) + MarkTypePropertyConfigured(cx, self, child.propid); + + last = JS_PROPERTY_TREE(cx).getChild(cx, last, self->numFixedSlots(), child); + if (!last) + return NULL; + } + + JS_ASSERT(self->lastProperty()->slotSpan() == last->slotSpan()); + JS_ALWAYS_TRUE(setLastProperty(cx, last)); + } else { + for (size_t i = 0; i < props.length(); i++) { + jsid id = props[i]; + + uintN attrs; + if (!self->getGenericAttributes(cx, id, &attrs)) + return false; + + uintN new_attrs = getSealedOrFrozenAttributes(attrs, it); + + /* If we already have the attributes we need, skip the setAttributes call. */ + if ((attrs | new_attrs) == attrs) + continue; + + attrs |= new_attrs; + if (!self->setGenericAttributes(cx, id, &attrs)) + return false; + } + } + + return true; +} + +bool +JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp) +{ + if (isExtensible()) { + *resultp = false; + return true; + } + + AutoIdVector props(cx); + if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props)) + return false; for (size_t i = 0, len = props.length(); i < len; i++) { jsid id = props[i]; uintN attrs; - if (!getAttributes(cx, id, &attrs)) + if (!getGenericAttributes(cx, id, &attrs)) return false; - /* Make all attributes permanent; if freezing, make data attributes read-only. */ - uintN new_attrs; - if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) - new_attrs = JSPROP_PERMANENT | JSPROP_READONLY; - else - new_attrs = JSPROP_PERMANENT; - - /* If we already have the attributes we need, skip the setAttributes call. */ - if ((attrs | new_attrs) == attrs) - continue; - - attrs |= new_attrs; - if (!setAttributes(cx, id, &attrs)) - return false; + /* + * If the property is configurable, this object is neither sealed nor + * frozen. If the property is a writable data property, this object is + * not frozen. + */ + if (!(attrs & JSPROP_PERMANENT) || + (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)))) + { + *resultp = false; + return true; + } } + /* All properties checked out. This object is sealed/frozen. */ + *resultp = true; return true; } @@ -2741,30 +2658,10 @@ obj_isFrozen(JSContext *cx, uintN argc, Value *vp) if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj)) return false; - vp->setBoolean(false); - - if (obj->isExtensible()) - return true; /* The JavaScript value returned is false. */ - - AutoIdVector props(cx); - if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) + bool frozen; + if (!obj->isFrozen(cx, &frozen)) return false; - - for (size_t i = 0, len = props.length(); i < len; i++) { - jsid id = props[i]; - - uintN attrs = 0; - if (!obj->getAttributes(cx, id, &attrs)) - return false; - - /* The property must be non-configurable and either read-only or an accessor. */ - if (!(attrs & JSPROP_PERMANENT) || - !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))) - return true; /* The JavaScript value returned is false. */ - } - - /* It really was sealed, so return true. */ - vp->setBoolean(true); + vp->setBoolean(frozen); return true; } @@ -2787,29 +2684,10 @@ obj_isSealed(JSContext *cx, uintN argc, Value *vp) if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj)) return false; - /* Assume not sealed until proven otherwise. */ - vp->setBoolean(false); - - if (obj->isExtensible()) - return true; /* The JavaScript value returned is false. */ - - AutoIdVector props(cx); - if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) + bool sealed; + if (!obj->isSealed(cx, &sealed)) return false; - - for (size_t i = 0, len = props.length(); i < len; i++) { - jsid id = props[i]; - - uintN attrs; - if (!obj->getAttributes(cx, id, &attrs)) - return false; - - if (!(attrs & JSPROP_PERMANENT)) - return true; /* The JavaScript value returned is false. */ - } - - /* It really was sealed, so return true. */ - vp->setBoolean(true); + vp->setBoolean(sealed); return true; } @@ -2821,7 +2699,7 @@ const char js_hasOwnProperty_str[] = "hasOwnProperty"; const char js_isPrototypeOf_str[] = "isPrototypeOf"; const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; -static JSFunctionSpec object_methods[] = { +JSFunctionSpec object_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, obj_toSource, 0,0), #endif @@ -2836,15 +2714,15 @@ static JSFunctionSpec object_methods[] = { JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0), #if OLD_GETTER_SETTER_METHODS - JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), - JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), + JS_FN(js_defineGetter_str, js::obj_defineGetter, 2,0), + JS_FN(js_defineSetter_str, js::obj_defineSetter, 2,0), JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), #endif JS_FS_END }; -static JSFunctionSpec object_static_methods[] = { +JSFunctionSpec object_static_methods[] = { JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0), JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0), JS_FN("keys", obj_keys, 1,0), @@ -2876,156 +2754,309 @@ js_Object(JSContext *cx, uintN argc, Value *vp) if (!obj) { /* Make an object whether this was called with 'new' or not. */ JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined()); - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) return JS_FALSE; + TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object); + if (!type) + return JS_FALSE; + obj->setType(type); } vp->setObject(*obj); return JS_TRUE; } -JSObject* -js_CreateThis(JSContext *cx, JSObject *callee) +static inline JSObject * +NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent, + gc::AllocKind kind) { - Class *clasp = callee->getClass(); + JS_ASSERT(clasp != &ArrayClass); + JS_ASSERT_IF(clasp == &FunctionClass, + kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); - Class *newclasp = &js_ObjectClass; - if (clasp == &js_FunctionClass) { - JSFunction *fun = callee->getFunctionPrivate(); - if (fun->isNative() && fun->u.n.clasp) - newclasp = fun->u.n.clasp; - } + RootTypeObject typeRoot(cx, &type); - Value protov; - if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) + RootedVarShape shape(cx); + shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind); + if (!shape) return NULL; - JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; - JSObject *parent = callee->getParent(); - gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp); - JSObject *obj = NewObject(cx, newclasp, proto, parent, kind); - if (obj) - obj->syncSpecialEquality(); + HeapValue *slots; + if (!PreallocateObjectDynamicSlots(cx, shape, &slots)) + return NULL; + + JSObject *obj = JSObject::create(cx, kind, shape, typeRoot, slots); + if (!obj) + return NULL; + + Probes::createObject(cx, obj); return obj; } JSObject * -js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::AllocKind kind) { - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); - return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent(), kind); + if (CanBeFinalizedInBackground(kind, clasp)) + kind = GetBackgroundAllocKind(kind); + + NewObjectCache &cache = cx->compartment->newObjectCache; + + NewObjectCache::EntryIndex entry = -1; + if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) { + if (cache.lookupProto(clasp, proto, kind, &entry)) + return cache.newObjectFromHit(cx, entry); + } + + RootObject protoRoot(cx, &proto); + RootObject parentRoot(cx, &parent); + + types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx); + if (!type) + return NULL; + + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + if (!parent && proto) + parent = proto->getParent(); + + JSObject *obj = NewObject(cx, clasp, type, parent, kind); + if (!obj) + return NULL; + + if (entry != -1 && !obj->hasDynamicSlots()) + cache.fillProto(entry, clasp, proto, kind, obj); + + return obj; } JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee) +js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::AllocKind kind) { - Value protov; - if (!callee->getProperty(cx, - ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &protov)) { - return NULL; - } - JSObject *proto = protov.isObject() ? &protov.toObject() : NULL; - return js_CreateThisForFunctionWithProto(cx, callee, proto); -} + if (proto) + return NewObjectWithGivenProto(cx, clasp, proto, parent, kind); -#ifdef JS_TRACER + if (CanBeFinalizedInBackground(kind, clasp)) + kind = GetBackgroundAllocKind(kind); -JSObject* FASTCALL -js_Object_tn(JSContext* cx, JSObject* proto) -{ - JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8); -} + if (!parent) + parent = GetCurrentGlobal(cx); + + /* + * Use the object cache, except for classes without a cached proto key. + * On these objects, FindProto will do a dynamic property lookup to get + * global[className].prototype, where changes to either the className or + * prototype property would render the cached lookup incorrect. For classes + * with a proto key, the prototype created during class initialization is + * stored in an immutable slot on the global (except for ClearScope, which + * will flush the new object cache). + */ + JSProtoKey protoKey = GetClassProtoKey(clasp); -JS_DEFINE_TRCINFO_1(js_Object, - (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, - nanojit::ACCSET_STORE_ANY))) + NewObjectCache &cache = cx->compartment->newObjectCache; -JSObject* FASTCALL -js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj) -{ - if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind); + NewObjectCache::EntryIndex entry = -1; + if (parent->isGlobal() && protoKey != JSProto_Null) { + if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry)) + return cache.newObjectFromHit(cx, entry); } - return CopyInitializerObject(cx, baseobj); -} + RootObject parentRoot(cx, &parent); + + if (!FindProto(cx, clasp, parentRoot, &proto)) + return NULL; -JS_DEFINE_CALLINFO_3(extern, OBJECT, js_InitializerObject, CONTEXT, OBJECT, OBJECT, - 0, nanojit::ACCSET_STORE_ANY) + types::TypeObject *type = proto->getNewType(cx); + if (!type) + return NULL; -JSObject* FASTCALL -js_String_tn(JSContext* cx, JSObject* proto, JSString* str) -{ - JS_ASSERT(JS_ON_TRACE(cx)); - JS_ASSERT(proto); - return StringObject::createWithProto(cx, str, *proto); + JSObject *obj = NewObject(cx, clasp, type, parent, kind); + if (!obj) + return NULL; + + if (entry != -1 && !obj->hasDynamicSlots()) + cache.fillGlobal(entry, clasp, &parent->asGlobal(), kind, obj); + + return obj; } -JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, - nanojit::ACCSET_STORE_ANY) -JSObject * FASTCALL -js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot) +JSObject * +js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind) { -#ifdef DEBUG - JS_ASSERT(ctor->isFunction()); - JS_ASSERT(ctor->getFunctionPrivate()->isInterpreted()); - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - const Shape *shape = ctor->nativeLookup(id); - JS_ASSERT(shape->slot == protoSlot); - JS_ASSERT(!shape->configurable()); - JS_ASSERT(!shape->isMethod()); -#endif + JS_ASSERT(type->proto->hasNewType(type)); + JS_ASSERT(parent); - JSObject *parent = ctor->getParent(); - JSObject *proto; - const Value &protov = ctor->getSlotRef(protoSlot); - if (protov.isObject()) { - proto = &protov.toObject(); - } else { - /* - * GetInterpretedFunctionPrototype found that ctor.prototype is - * primitive. Use Object.prototype for proto, per ES5 13.2.2 step 7. - */ - if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) - return NULL; + if (CanBeFinalizedInBackground(kind, &ObjectClass)) + kind = GetBackgroundAllocKind(kind); + + NewObjectCache &cache = cx->compartment->newObjectCache; + + NewObjectCache::EntryIndex entry = -1; + if (parent == type->proto->getParent()) { + if (cache.lookupType(&ObjectClass, type, kind, &entry)) + return cache.newObjectFromHit(cx, entry); } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); - return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind); + JSObject *obj = NewObject(cx, &ObjectClass, type, parent, kind); + if (!obj) + return NULL; + + if (entry != -1 && !obj->hasDynamicSlots()) + cache.fillType(entry, &ObjectClass, type, kind, obj); + + return obj; } -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0, - nanojit::ACCSET_STORE_ANY) -#else /* !JS_TRACER */ +JSObject * +js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent, + gc::AllocKind kind, const Shape *shape) +{ + JSObject *res = NewObjectWithType(cx, type, parent, kind); + if (!res) + return NULL; -# define js_Object_trcinfo NULL + if (shape->isEmptyShape()) + return res; -#endif /* !JS_TRACER */ + /* Get all the ids in the object, in order. */ + js::AutoIdVector ids(cx); + for (unsigned i = 0; i <= shape->slot(); i++) { + if (!ids.append(JSID_VOID)) + return NULL; + } + const js::Shape *nshape = shape; + while (!nshape->isEmptyShape()) { + ids[nshape->slot()] = nshape->propid(); + nshape = nshape->previous(); + } -/* - * Given pc pointing after a property accessing bytecode, return true if the - * access is "object-detecting" in the sense used by web scripts, e.g., when - * checking whether document.all is defined. - */ -JS_REQUIRES_STACK JSBool -Detecting(JSContext *cx, jsbytecode *pc) + /* Construct the new shape. */ + for (unsigned i = 0; i < ids.length(); i++) { + if (!DefineNativeProperty(cx, res, ids[i], js::UndefinedValue(), NULL, NULL, + JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) { + return NULL; + } + } + JS_ASSERT(!res->inDictionaryMode()); + + return res; +} + +JSObject* +js_CreateThis(JSContext *cx, JSObject *callee) +{ + Class *clasp = callee->getClass(); + + Class *newclasp = &ObjectClass; + if (clasp == &FunctionClass) { + JSFunction *fun = callee->toFunction(); + if (fun->isNative() && fun->u.n.clasp) + newclasp = fun->u.n.clasp; + } + + Value protov; + if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) + return NULL; + + JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; + JSObject *parent = callee->getParent(); + gc::AllocKind kind = NewObjectGCKind(cx, newclasp); + return NewObjectWithClassProto(cx, newclasp, proto, parent, kind); +} + +static inline JSObject * +CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject *parent) +{ + if (type->newScript) { + /* + * Make an object with the type's associated finalize kind and shape, + * which reflects any properties that will definitely be added to the + * object before it is read from. + */ + gc::AllocKind kind = type->newScript->allocKind; + JSObject *res = NewObjectWithType(cx, type, parent, kind); + if (res) + JS_ALWAYS_TRUE(res->setLastProperty(cx, (Shape *) type->newScript->shape.get())); + return res; + } + + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + return NewObjectWithType(cx, type, parent, kind); +} + +JSObject * +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +{ + JSObject *res; + + if (proto) { + types::TypeObject *type = proto->getNewType(cx, callee->toFunction()); + if (!type) + return NULL; + res = CreateThisForFunctionWithType(cx, type, callee->getParent()); + } else { + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), kind); + } + + if (res && cx->typeInferenceEnabled()) + TypeScript::SetThis(cx, callee->toFunction()->script(), types::Type::ObjectType(res)); + + return res; +} + +JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType) +{ + Value protov; + if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) + return NULL; + JSObject *proto; + if (protov.isObject()) + proto = &protov.toObject(); + else + proto = NULL; + JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); + + if (obj && newType) { + /* + * Reshape the object and give it a (lazily instantiated) singleton + * type before passing it as the 'this' value for the call. + */ + obj->clear(cx); + if (!obj->setSingletonType(cx)) + return NULL; + + JSScript *calleeScript = callee->toFunction()->script(); + TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(obj)); + } + + return obj; +} + +/* + * Given pc pointing after a property accessing bytecode, return true if the + * access is "object-detecting" in the sense used by web scripts, e.g., when + * checking whether document.all is defined. + */ +JSBool +Detecting(JSContext *cx, jsbytecode *pc) { - JSScript *script; jsbytecode *endpc; JSOp op; JSAtom *atom; - script = cx->fp()->script(); + JSScript *script = cx->stack.currentScript(); endpc = script->code + script->length; for (;; pc += js_CodeSpec[op].length) { - JS_ASSERT_IF(!cx->fp()->hasImacropc(), script->code <= pc && pc < endpc); + JS_ASSERT(script->code <= pc && pc < endpc); /* General case: a branch or equality op follows the access. */ - op = js_GetOpcode(cx, script, pc); + op = JSOp(*pc); if (js_CodeSpec[op].format & JOF_DETECTING) return JS_TRUE; @@ -3036,8 +3067,8 @@ Detecting(JSContext *cx, jsbytecode *pc) * about JS1.2's revision of the equality operators here. */ if (++pc < endpc) { - op = js_GetOpcode(cx, script, pc); - return *pc == JSOP_EQ || *pc == JSOP_NE; + op = JSOp(*pc); + return op == JSOP_EQ || op == JSOP_NE; } return JS_FALSE; @@ -3051,7 +3082,7 @@ Detecting(JSContext *cx, jsbytecode *pc) GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && (pc += js_CodeSpec[op].length) < endpc) { - op = js_GetOpcode(cx, script, pc); + op = JSOp(*pc); return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE; } @@ -3078,30 +3109,23 @@ Detecting(JSContext *cx, jsbytecode *pc) uintN js_InferFlags(JSContext *cx, uintN defaultFlags) { -#ifdef JS_TRACER - if (JS_ON_TRACE(cx)) - return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->lookupFlags; -#endif - - JS_ASSERT_NOT_ON_TRACE(cx); - - jsbytecode *pc; const JSCodeSpec *cs; - uint32 format; + uint32_t format; uintN flags = 0; - StackFrame *const fp = js_GetTopStackFrame(cx); - if (!fp || !(pc = cx->regs().pc)) + jsbytecode *pc; + JSScript *script = cx->stack.currentScript(&pc); + if (!script || !pc) return defaultFlags; - cs = &js_CodeSpec[js_GetOpcode(cx, fp->script(), pc)]; + + cs = &js_CodeSpec[*pc]; format = cs->format; if (JOF_MODE(format) != JOF_NAME) flags |= JSRESOLVE_QUALIFIED; - if (format & (JOF_SET | JOF_FOR)) { + if (format & JOF_SET) { flags |= JSRESOLVE_ASSIGNING; } else if (cs->length >= 0) { pc += cs->length; - JSScript *script = cx->fp()->script(); if (pc < script->code + script->length && Detecting(cx, pc)) flags |= JSRESOLVE_DETECTING; } @@ -3110,295 +3134,61 @@ js_InferFlags(JSContext *cx, uintN defaultFlags) return flags; } -/* - * ObjectOps and Class for with-statement stack objects. - */ -static JSBool -with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - /* Fixes bug 463997 */ - uintN flags = cx->resolveFlags; - if (flags == JSRESOLVE_INFER) - flags = js_InferFlags(cx, flags); - flags |= JSRESOLVE_WITH; - JSAutoResolveFlags rf(cx, flags); - return obj->getProto()->lookupProperty(cx, id, objp, propp); -} - -static JSBool -with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) -{ - return obj->getProto()->getProperty(cx, id, vp); -} - -static JSBool -with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) -{ - return obj->getProto()->setProperty(cx, id, vp, strict); -} - -static JSBool -with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) -{ - return obj->getProto()->getAttributes(cx, id, attrsp); -} - -static JSBool -with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) -{ - return obj->getProto()->setAttributes(cx, id, attrsp); -} - -static JSBool -with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) -{ - return obj->getProto()->deleteProperty(cx, id, rval, strict); -} - -static JSBool -with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - Value *statep, jsid *idp) -{ - return obj->getProto()->enumerate(cx, enum_op, statep, idp); -} - -static JSType -with_TypeOf(JSContext *cx, JSObject *obj) +JSBool +JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict) { - return JSTYPE_OBJECT; -} - -static JSObject * -with_ThisObject(JSContext *cx, JSObject *obj) -{ - return obj->getWithThis(); -} - -Class js_WithClass = { - "With", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - NULL, /* finalize */ - NULL, /* reserved */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - NULL, /* trace */ - JS_NULL_CLASS_EXT, - { - with_LookupProperty, - NULL, /* defineProperty */ - with_GetProperty, - with_SetProperty, - with_GetAttributes, - with_SetAttributes, - with_DeleteProperty, - with_Enumerate, - with_TypeOf, - NULL, /* fix */ - with_ThisObject, - NULL, /* clear */ + if (JS_UNLIKELY(watched())) { + id = js_CheckForStringIndex(id); + WatchpointMap *wpmap = cx->compartment->watchpointMap; + if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp)) + return false; } -}; - -JS_REQUIRES_STACK JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) -{ - JSObject *obj; - - obj = js_NewGCObject(cx, FINALIZE_OBJECT2); - if (!obj) - return NULL; - - StackFrame *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(emptyWithShape); - OBJ_SET_BLOCK_DEPTH(cx, obj, depth); - - AutoObjectRooter tvr(cx, obj); - JSObject *thisp = proto->thisObject(cx); - if (!thisp) - return NULL; - - assertSameCompartment(cx, obj, thisp); - - obj->setWithThis(thisp); - return obj; -} - -JSObject * -js_NewBlockObject(JSContext *cx) -{ - /* - * Null obj's proto slot so that Object.prototype.* does not pollute block - * scopes and to give the block object its own scope. - */ - JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2); - if (!blockObj) - return NULL; - - EmptyShape *emptyBlockShape = EmptyShape::getEmptyBlockShape(cx); - if (!emptyBlockShape) - return NULL; - - blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false); - blockObj->setMap(emptyBlockShape); - return blockObj; -} - -JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp) -{ - JS_ASSERT(proto->isStaticBlock()); - - size_t count = OBJ_BLOCK_COUNT(cx, proto); - gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1); - - JSObject *clone = js_NewGCObject(cx, kind); - if (!clone) - return NULL; - - StackFrame *priv = js_FloatingFrameIfGenerator(cx, fp); - - /* The caller sets parent on its own. */ - clone->initClonedBlock(cx, proto, priv); - - if (!clone->ensureInstanceReservedSlots(cx, count + 1)) - return NULL; - - clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH)); - - JS_ASSERT(clone->isClonedBlock()); - return clone; + return getOps()->setGeneric(cx, this, id, vp, strict); } -JS_REQUIRES_STACK JSBool -js_PutBlockObject(JSContext *cx, JSBool normalUnwind) +JSBool +JSObject::nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict) { - StackFrame *const fp = cx->fp(); - JSObject *obj = &fp->scopeChain(); - JS_ASSERT(obj->isClonedBlock()); - JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp())); - - /* Block objects should have all reserved slots allocated early. */ - uintN count = OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count); - - /* The block and its locals must be on the current stack for GC safety. */ - uintN depth = OBJ_BLOCK_DEPTH(cx, obj); - JS_ASSERT(depth <= size_t(cx->regs().sp - fp->base())); - JS_ASSERT(count <= size_t(cx->regs().sp - fp->base() - depth)); - - /* See comments in CheckDestructuring from jsparse.cpp. */ - JS_ASSERT(count >= 1); - - if (normalUnwind) { - uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT; - depth += fp->numFixed(); - memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value)); + if (JS_UNLIKELY(watched())) { + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + JS_ASSERT(id == js_CheckForStringIndex(id)); + WatchpointMap *wpmap = cx->compartment->watchpointMap; + if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp)) + return false; } - - /* We must clear the private slot even with errors. */ - obj->setPrivate(NULL); - fp->setScopeChainNoCallObj(*obj->getParent()); - return normalUnwind; + return getOps()->setElement(cx, this, index, vp, strict); } -static JSBool -block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) +bool +JSObject::deleteByValue(JSContext *cx, const Value &property, Value *rval, bool strict) { - /* - * Block objects are never exposed to script, and the engine handles them - * with care. So unlike other getters, this one can assert (rather than - * check) certain invariants about obj. - */ - JS_ASSERT(obj->isClonedBlock()); - uintN index = (uintN) JSID_TO_INT(id); - JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); - - StackFrame *fp = (StackFrame *) obj->getPrivate(); - if (fp) { - fp = js_LiveFrameIfGenerator(fp); - index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj); - JS_ASSERT(index < fp->numSlots()); - *vp = fp->slots()[index]; - return true; - } + uint32_t index; + if (IsDefinitelyIndex(property, &index)) + return deleteElement(cx, index, rval, strict); - /* Values are in slots immediately following the class-reserved ones. */ - JS_ASSERT(obj->getSlot(JSSLOT_FREE(&js_BlockClass) + index) == *vp); - return true; -} + Value propval = property; + SpecialId sid; + if (ValueIsSpecial(this, &propval, &sid, cx)) + return deleteSpecial(cx, sid, rval, strict); -static JSBool -block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) -{ - JS_ASSERT(obj->isClonedBlock()); - uintN index = (uintN) JSID_TO_INT(id); - JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); - - StackFrame *fp = (StackFrame *) obj->getPrivate(); - if (fp) { - fp = js_LiveFrameIfGenerator(fp); - index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj); - JS_ASSERT(index < fp->numSlots()); - fp->slots()[index] = *vp; - return true; - } - - /* - * The value in *vp will be written back to the slot in obj that was - * allocated when this let binding was defined. - */ - return true; -} + JSAtom *name; + if (!js_ValueToAtom(cx, propval, &name)) + return false; -const Shape * -JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index) -{ - JS_ASSERT(isStaticBlock()); + if (name->isIndex(&index)) + return deleteElement(cx, index, rval, false); - /* Use JSPROP_ENUMERATE to aid the disassembler. */ - uint32 slot = JSSLOT_FREE(&js_BlockClass) + index; - const Shape *shape = addProperty(cx, id, - block_getProperty, block_setProperty, - slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, - Shape::HAS_SHORTID, index); - if (!shape) - return NULL; - if (slot >= numSlots() && !growSlots(cx, slot + 1)) - return NULL; - return shape; + return deleteProperty(cx, name->asPropertyName(), rval, false); } -static size_t -GetObjectSize(JSObject *obj) -{ - return (obj->isFunction() && !obj->getPrivate()) - ? sizeof(JSFunction) - : sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots(); -} - -bool -JSObject::copyPropertiesFrom(JSContext *cx, JSObject *obj) +JS_FRIEND_API(bool) +JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj) { // If we're not native, then we cannot copy properties. - JS_ASSERT(isNative() == obj->isNative()); - if (!isNative()) + JS_ASSERT(target->isNative() == obj->isNative()); + if (!target->isNative()) return true; AutoShapeVector shapes(cx); @@ -3417,10 +3207,10 @@ JSObject::copyPropertiesFrom(JSContext *cx, JSObject *obj) StrictPropertyOp setter = shape->setter(); if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter)) return false; - Value v = shape->hasSlot() ? obj->getSlot(shape->slot) : UndefinedValue(); + Value v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue(); if (!cx->compartment->wrap(cx, &v)) return false; - if (!defineProperty(cx, shape->id, v, getter, setter, attrs)) + if (!target->defineGeneric(cx, shape->propid(), v, getter, setter, attrs)) return false; } return true; @@ -3430,72 +3220,196 @@ static bool CopySlots(JSContext *cx, JSObject *from, JSObject *to) { JS_ASSERT(!from->isNative() && !to->isNative()); - size_t nslots = from->numSlots(); - if (to->ensureSlots(cx, nslots)) - return false; + JS_ASSERT(from->getClass() == to->getClass()); size_t n = 0; - if (to->isWrapper() && - (JSWrapper::wrapperHandler(to)->flags() & JSWrapper::CROSS_COMPARTMENT)) { - to->slots[0] = from->slots[0]; - to->slots[1] = from->slots[1]; + if (from->isWrapper() && + (Wrapper::wrapperHandler(from)->flags() & Wrapper::CROSS_COMPARTMENT)) { + to->setSlot(0, from->getSlot(0)); + to->setSlot(1, from->getSlot(1)); n = 2; } - for (; n < nslots; ++n) { - Value v = from->slots[n]; + size_t span = JSCLASS_RESERVED_SLOTS(from->getClass()); + for (; n < span; ++n) { + Value v = from->getSlot(n); if (!cx->compartment->wrap(cx, &v)) return false; - to->slots[n] = v; + to->setSlot(n, v); } return true; } -JSObject * -JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) +JS_FRIEND_API(JSObject *) +JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent) { /* * We can only clone native objects and proxies. Dense arrays are slowified if * we try to clone them. */ - if (!isNative()) { - if (isDenseArray()) { - if (!makeDenseArraySlow(cx)) + if (!obj->isNative()) { + if (obj->isDenseArray()) { + if (!obj->makeDenseArraySlow(cx)) return NULL; - } else if (!isProxy()) { + } else if (!obj->isProxy()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CLONE_OBJECT); return NULL; } } - JSObject *clone = NewObject(cx, getClass(), - proto, parent, - gc::FinalizeKind(finalizeKind())); + JSObject *clone = NewObjectWithGivenProto(cx, obj->getClass(), proto, parent, obj->getAllocKind()); if (!clone) return NULL; - if (isNative()) { - if (clone->isFunction() && (compartment() != clone->compartment())) { + if (obj->isNative()) { + if (clone->isFunction() && (obj->compartment() != clone->compartment())) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CLONE_OBJECT); return NULL; } - if (getClass()->flags & JSCLASS_HAS_PRIVATE) - clone->setPrivate(getPrivate()); + if (obj->hasPrivate()) + clone->setPrivate(obj->getPrivate()); } else { - JS_ASSERT(isProxy()); - if (!CopySlots(cx, this, clone)) + JS_ASSERT(obj->isProxy()); + if (!CopySlots(cx, obj, clone)) return NULL; } + return clone; } -static void -TradeGuts(JSObject *a, JSObject *b) +struct JSObject::TradeGutsReserved { + JSContext *cx; + Vector avals; + Vector bvals; + int newafixed; + int newbfixed; + Shape *newashape; + Shape *newbshape; + HeapValue *newaslots; + HeapValue *newbslots; + + TradeGutsReserved(JSContext *cx) + : cx(cx), avals(cx), bvals(cx), + newafixed(0), newbfixed(0), + newashape(NULL), newbshape(NULL), + newaslots(NULL), newbslots(NULL) + {} + + ~TradeGutsReserved() + { + if (newaslots) + cx->free_(newaslots); + if (newbslots) + cx->free_(newbslots); + } +}; + +bool +JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, + TradeGutsReserved &reserved) +{ + /* + * When performing multiple swaps between objects which may have different + * numbers of fixed slots, we reserve all space ahead of time so that the + * swaps can be performed infallibly. + */ + + if (a->sizeOfThis() == b->sizeOfThis()) + return true; + + /* + * If either object is native, it needs a new shape to preserve the + * invariant that objects with the same shape have the same number of + * inline slots. The fixed slots will be updated in place during TradeGuts. + * Non-native objects need to be reshaped according to the new count. + */ + if (a->isNative()) { + if (!a->generateOwnShape(cx)) + return false; + } else { + reserved.newbshape = EmptyShape::getInitialShape(cx, a->getClass(), + a->getProto(), a->getParent(), + b->getAllocKind()); + if (!reserved.newbshape) + return false; + } + if (b->isNative()) { + if (!b->generateOwnShape(cx)) + return false; + } else { + reserved.newashape = EmptyShape::getInitialShape(cx, b->getClass(), + b->getProto(), b->getParent(), + a->getAllocKind()); + if (!reserved.newashape) + return false; + } + + /* The avals/bvals vectors hold all original values from the objects. */ + + if (!reserved.avals.reserve(a->slotSpan())) + return false; + if (!reserved.bvals.reserve(b->slotSpan())) + return false; + + JS_ASSERT(a->elements == emptyObjectElements); + JS_ASSERT(b->elements == emptyObjectElements); + + /* + * The newafixed/newbfixed hold the number of fixed slots in the objects + * after the swap. Adjust these counts according to whether the objects + * use their last fixed slot for storing private data. + */ + + reserved.newafixed = a->numFixedSlots(); + reserved.newbfixed = b->numFixedSlots(); + + if (a->hasPrivate()) { + reserved.newafixed++; + reserved.newbfixed--; + } + if (b->hasPrivate()) { + reserved.newbfixed++; + reserved.newafixed--; + } + + JS_ASSERT(reserved.newafixed >= 0); + JS_ASSERT(reserved.newbfixed >= 0); + + /* + * The newaslots/newbslots arrays hold any dynamic slots for the objects + * if they do not have enough fixed slots to accomodate the slots in the + * other object. + */ + + unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan()); + unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan()); + + if (adynamic) { + reserved.newaslots = (HeapValue *) cx->malloc_(sizeof(HeapValue) * adynamic); + if (!reserved.newaslots) + return false; + Debug_SetValueRangeToCrashOnTouch(reserved.newaslots, adynamic); + } + if (bdynamic) { + reserved.newbslots = (HeapValue *) cx->malloc_(sizeof(HeapValue) * bdynamic); + if (!reserved.newbslots) + return false; + Debug_SetValueRangeToCrashOnTouch(reserved.newbslots, bdynamic); + } + + return true; +} + +void +JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved) { JS_ASSERT(a->compartment() == b->compartment()); JS_ASSERT(a->isFunction() == b->isFunction()); + /* Don't try to swap a JSFunction for a plain function JSObject. */ + JS_ASSERT_IF(a->isFunction(), a->sizeOfThis() == b->sizeOfThis()); + /* * Regexp guts are more complicated -- we would need to migrate the * refcounted JIT code blob for them across compartments instead of just @@ -3503,12 +3417,29 @@ TradeGuts(JSObject *a, JSObject *b) */ JS_ASSERT(!a->isRegExp() && !b->isRegExp()); - bool aInline = !a->hasSlotsArray(); - bool bInline = !b->hasSlotsArray(); + /* + * Callers should not try to swap dense arrays or ArrayBuffer objects, + * these use a different slot representation from other objects. + */ + JS_ASSERT(!a->isDenseArray() && !b->isDenseArray()); + JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer()); + +#ifdef JSGC_INCREMENTAL + /* + * We need a write barrier here. If |a| was marked and |b| was not, then + * after the swap, |b|'s guts would never be marked. The write barrier + * solves this. + */ + JSCompartment *comp = a->compartment(); + if (comp->needsBarrier()) { + MarkChildren(comp->barrierTracer(), a); + MarkChildren(comp->barrierTracer(), b); + } +#endif /* Trade the guts of the objects. */ - const size_t size = GetObjectSize(a); - if (size == GetObjectSize(b)) { + const size_t size = a->sizeOfThis(); + if (size == b->sizeOfThis()) { /* * If the objects are the same size, then we make no assumptions about * whether they have dynamically allocated slots and instead just copy @@ -3517,36 +3448,83 @@ TradeGuts(JSObject *a, JSObject *b) char tmp[tl::Max::result]; JS_ASSERT(size <= sizeof(tmp)); - memcpy(tmp, a, size); - memcpy(a, b, size); - memcpy(b, tmp, size); + js_memcpy(tmp, a, size); + js_memcpy(a, b, size); + js_memcpy(b, tmp, size); - /* Fixup pointers for inline slots on the objects. */ - if (aInline) - b->slots = b->fixedSlots(); - if (bInline) - a->slots = a->fixedSlots(); - } else { +#ifdef JSGC_GENERATIONAL /* - * If the objects are of differing sizes, then we only copy over the - * JSObject portion (things like class, etc.) and leave it to - * JSObject::clone to copy over the dynamic slots for us. + * Trigger post barriers for fixed slots. JSObject bits are barriered + * below, in common with the other case. */ - if (a->isFunction()) { - JSFunction tmp; - memcpy(&tmp, a, sizeof tmp); - memcpy(a, b, sizeof tmp); - memcpy(b, &tmp, sizeof tmp); - } else { - JSObject tmp; - memcpy(&tmp, a, sizeof tmp); - memcpy(a, b, sizeof tmp); - memcpy(b, &tmp, sizeof tmp); + for (size_t i = 0; i < a->numFixedSlots(); ++i) { + HeapValue *slotA = &a->getFixedSlotRef(i); + HeapValue *slotB = &b->getFixedSlotRef(i); + HeapValue::writeBarrierPost(*slotA, slotA); + HeapValue::writeBarrierPost(*slotB, slotB); } +#endif + } else { + /* + * If the objects are of differing sizes, use the space we reserved + * earlier to save the slots from each object and then copy them into + * the new layout for the other object. + */ + + unsigned acap = a->slotSpan(); + unsigned bcap = b->slotSpan(); + + for (size_t i = 0; i < acap; i++) + reserved.avals.infallibleAppend(a->getSlot(i)); + + for (size_t i = 0; i < bcap; i++) + reserved.bvals.infallibleAppend(b->getSlot(i)); + + /* Done with the dynamic slots. */ + if (a->hasDynamicSlots()) + cx->free_(a->slots); + if (b->hasDynamicSlots()) + cx->free_(b->slots); + + void *apriv = a->hasPrivate() ? a->getPrivate() : NULL; + void *bpriv = b->hasPrivate() ? b->getPrivate() : NULL; - JS_ASSERT(!aInline); - JS_ASSERT(!bInline); + char tmp[sizeof(JSObject)]; + js_memcpy(&tmp, a, sizeof tmp); + js_memcpy(a, b, sizeof tmp); + js_memcpy(b, &tmp, sizeof tmp); + + if (a->isNative()) + a->shape_->setNumFixedSlots(reserved.newafixed); + else + a->shape_ = reserved.newashape; + + a->slots = reserved.newaslots; + a->initSlotRange(0, reserved.bvals.begin(), bcap); + if (a->hasPrivate()) + a->setPrivate(bpriv); + + if (b->isNative()) + b->shape_->setNumFixedSlots(reserved.newbfixed); + else + b->shape_ = reserved.newbshape; + + b->slots = reserved.newbslots; + b->initSlotRange(0, reserved.avals.begin(), acap); + if (b->hasPrivate()) + b->setPrivate(apriv); + + /* Make sure the destructor for reserved doesn't free the slots. */ + reserved.newaslots = NULL; + reserved.newbslots = NULL; } + +#ifdef JSGC_GENERATIONAL + Shape::writeBarrierPost(a->shape_, &a->shape_); + Shape::writeBarrierPost(b->shape_, &b->shape_); + types::TypeObject::writeBarrierPost(a->type_, &a->type_); + types::TypeObject::writeBarrierPost(b->type_, &b->type_); +#endif } /* @@ -3558,23 +3536,11 @@ TradeGuts(JSObject *a, JSObject *b) bool JSObject::swap(JSContext *cx, JSObject *other) { - /* - * If we are swapping objects with a different number of builtin slots, force - * both to not use their inline slots. - */ - if (GetObjectSize(this) != GetObjectSize(other)) { - if (!hasSlotsArray()) { - if (!allocSlots(cx, numSlots())) - return false; - } - if (!other->hasSlotsArray()) { - if (!other->allocSlots(cx, other->numSlots())) - return false; - } - } - if (this->compartment() == other->compartment()) { - TradeGuts(this, other); + TradeGutsReserved reserved(cx); + if (!ReserveForTradeGuts(cx, this, other, reserved)) + return false; + TradeGuts(cx, this, other, reserved); return true; } @@ -3584,190 +3550,36 @@ JSObject::swap(JSContext *cx, JSObject *other) AutoCompartment ac(cx, other); if (!ac.enter()) return false; - thisClone = this->clone(cx, other->getProto(), other->getParent()); - if (!thisClone || !thisClone->copyPropertiesFrom(cx, this)) + thisClone = JS_CloneObject(cx, this, other->getProto(), other->getParent()); + if (!thisClone || !JS_CopyPropertiesFrom(cx, thisClone, this)) return false; } { AutoCompartment ac(cx, this); if (!ac.enter()) return false; - otherClone = other->clone(cx, other->getProto(), other->getParent()); - if (!otherClone || !otherClone->copyPropertiesFrom(cx, other)) + otherClone = JS_CloneObject(cx, other, other->getProto(), other->getParent()); + if (!otherClone || !JS_CopyPropertiesFrom(cx, otherClone, other)) return false; } - TradeGuts(this, otherClone); - TradeGuts(other, thisClone); - - return true; -} - -#if JS_HAS_XDR -#define NO_PARENT_INDEX ((uint32)-1) + TradeGutsReserved reservedThis(cx); + TradeGutsReserved reservedOther(cx); -uint32 -FindObjectIndex(JSObjectArray *array, JSObject *obj) -{ - size_t i; - - if (array) { - i = array->length; - do { - - if (array->vector[--i] == obj) - return i; - } while (i != 0); - } - - return NO_PARENT_INDEX; -} - -JSBool -js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - uint32 parentId; - JSObject *obj, *parent; - uintN depth, count; - uint32 depthAndCount; - const Shape *shape; - - cx = xdr->cx; -#ifdef __GNUC__ - obj = NULL; /* quell GCC overwarning */ -#endif - - if (xdr->mode == JSXDR_ENCODE) { - obj = *objp; - parent = obj->getParent(); - parentId = JSScript::isValidOffset(xdr->script->objectsOffset) - ? FindObjectIndex(xdr->script->objects(), parent) - : NO_PARENT_INDEX; - depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); - count = (uint16)OBJ_BLOCK_COUNT(cx, obj); - depthAndCount = (uint32)(depth << 16) | count; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else count = 0; -#endif - - /* First, XDR the parent atomid. */ - if (!JS_XDRUint32(xdr, &parentId)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewBlockObject(cx); - if (!obj) - return JS_FALSE; - *objp = obj; - - /* - * If there's a parent id, then get the parent out of our script's - * object array. We know that we XDR block object in outer-to-inner - * order, which means that getting the parent now will work. - */ - if (parentId == NO_PARENT_INDEX) - parent = NULL; - else - parent = xdr->script->getObject(parentId); - obj->setParent(parent); - } - - AutoObjectRooter tvr(cx, obj); - - if (!JS_XDRUint32(xdr, &depthAndCount)) + if (!ReserveForTradeGuts(cx, this, otherClone, reservedThis) || + !ReserveForTradeGuts(cx, other, thisClone, reservedOther)) { return false; - - if (xdr->mode == JSXDR_DECODE) { - depth = (uint16)(depthAndCount >> 16); - count = (uint16)depthAndCount; - obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth))); - - /* - * XDR the block object's properties. We know that there are 'count' - * properties to XDR, stored as id/shortid pairs. - */ - for (uintN i = 0; i < count; i++) { - JSAtom *atom; - - /* XDR the real id. */ - if (!js_XDRAtom(xdr, &atom)) - return false; - - if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i)) - return false; - } - } else { - AutoShapeVector shapes(cx); - shapes.growBy(count); - - for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { - shape = &r.front(); - shapes[shape->shortid] = shape; - } - - /* - * XDR the block object's properties. We know that there are 'count' - * properties to XDR, stored as id/shortid pairs. - */ - for (uintN i = 0; i < count; i++) { - shape = shapes[i]; - JS_ASSERT(shape->getter() == block_getProperty); - - jsid propid = shape->id; - 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. */ - if (!js_XDRAtom(xdr, &atom)) - return false; - } } - return true; -} - -#endif -Class js_BlockClass = { - "Block", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub -}; - -JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, - object_props, object_methods, NULL, object_static_methods); - if (!proto) - return NULL; - - /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */ - jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom); - JSObject *evalobj = js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS); - if (!evalobj) - return NULL; - if (obj->isGlobal()) - obj->asGlobal()->setOriginalEval(evalobj); + TradeGuts(cx, this, otherClone, reservedThis); + TradeGuts(cx, other, thisClone, reservedOther); - return proto; + return true; } static bool DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, - const Value &v, uint32 attrs, bool &named) + const Value &v, uint32_t attrs, bool &named) { jsid id = ATOM_TO_JSID(atom); @@ -3780,34 +3592,54 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, JS_ASSERT(obj->isGlobal()); JS_ASSERT(obj->isNative()); - if (!obj->ensureClassReservedSlots(cx)) - return false; - - const Shape *shape = obj->nativeLookup(id); + const Shape *shape = obj->nativeLookup(cx, id); if (!shape) { - uint32 slot = 2 * JSProto_LIMIT + key; - if (!js_SetReservedSlot(cx, obj, slot, v)) - return false; - if (!obj->addProperty(cx, id, PropertyStub, StrictPropertyStub, slot, attrs, 0, 0)) + uint32_t slot = 2 * JSProto_LIMIT + key; + SetReservedSlot(obj, slot, v); + if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0)) return false; + AddTypePropertyId(cx, obj, id, v); named = true; return true; } } - named = obj->defineProperty(cx, id, v, PropertyStub, StrictPropertyStub, attrs); - return named; + named = obj->defineGeneric(cx, id, v, JS_PropertyStub, JS_StrictPropertyStub, attrs); + return named; +} + +namespace js { + +static void +SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) +{ + JS_ASSERT(!obj->getParent()); + if (!obj->isGlobal()) + return; + + SetReservedSlot(obj, key, ObjectOrNullValue(cobj)); + SetReservedSlot(obj, JSProto_LIMIT + key, ObjectOrNullValue(proto)); } -namespace js { +static void +ClearClassObject(JSContext *cx, JSObject *obj, JSProtoKey key) +{ + JS_ASSERT(!obj->getParent()); + if (!obj->isGlobal()) + return; + + obj->setSlot(key, UndefinedValue()); + obj->setSlot(JSProto_LIMIT + key, UndefinedValue()); +} JSObject * -DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, +DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom, JSObject *protoProto, Class *clasp, Native constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs) + JSPropertySpec *static_ps, JSFunctionSpec *static_fs, + JSObject **ctorp, AllocKind ctorKind) { /* * Create a prototype object for this class. @@ -3823,24 +3655,36 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt * otherwise-uninitialized global. * * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is - * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing. + * &FunctionClass, not a JSObject-sized (smaller) GC-thing. * * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to - * be &js_FunctionClass (we could break compatibility easily). But fixing + * be &FunctionClass (we could break compatibility easily). But fixing * (3) is not enough without addressing the bootstrapping dependency on (1) * and (2). */ - JSObject *proto = NewObject(cx, clasp, protoProto, obj); + + /* + * Create the prototype object. (GlobalObject::createBlankPrototype isn't + * used because it parents the prototype object to the global and because + * it uses WithProto::Given. FIXME: Undo dependencies on this parentage + * [which already needs to happen for bug 638316], figure out nicer + * semantics for null-protoProto, and use createBlankPrototype.) + */ + RootedVarObject proto(cx); + proto = NewObjectWithClassProto(cx, clasp, protoProto, obj); if (!proto) return NULL; - proto->syncSpecialEquality(); + if (!proto->setSingletonType(cx)) + return NULL; - /* After this point, control must exit via label bad or out. */ - AutoObjectRooter tvr(cx, proto); + if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx)) + return NULL; - JSObject *ctor; + /* After this point, control must exit via label bad or out. */ + RootedVarObject ctor(cx); bool named = false; + bool cached = false; if (!constructor) { /* * Lacking a constructor, name the prototype (e.g., Math) unless this @@ -3849,7 +3693,7 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt * and (c) key is not the null key. */ if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->isGlobal() || key == JSProto_Null) { - uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS) + uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS) ? JSPROP_READONLY | JSPROP_PERMANENT : 0; if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named)) @@ -3858,82 +3702,54 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt ctor = proto; } else { - JSFunction *fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom); + /* + * Create the constructor, not using GlobalObject::createConstructor + * because the constructor currently must have |obj| as its parent. + * (FIXME: remove this dependency on the exact identity of the parent, + * perhaps as part of bug 638316.) + */ + RootedVarFunction fun(cx); + fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom, + ctorKind); if (!fun) goto bad; + fun->setConstructorClass(clasp); + + /* + * Set the class object early for standard class constructors. Type + * inference may need to access these, and js_GetClassPrototype will + * fail if it tries to do a reentrant reconstruction of the class. + */ + if (key != JSProto_Null) { + SetClassObject(obj, key, fun, proto); + cached = true; + } AutoValueRooter tvr2(cx, ObjectValue(*fun)); if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named)) goto bad; - /* - * Remember the class this function is a constructor for so that - * we know to create an object of this class when we call the - * constructor. - */ - FUN_CLASP(fun) = clasp; - /* * Optionally construct the prototype object, before the class has * been fully initialized. Allow the ctor to replace proto with a * different object, as is done for operator new -- and as at least * XML support requires. */ - ctor = FUN_OBJECT(fun); - if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { - Value rval; - if (!InvokeConstructorWithGivenThis(cx, proto, ObjectOrNullValue(ctor), - 0, NULL, &rval)) { - goto bad; - } - if (rval.isObject() && &rval.toObject() != proto) - proto = &rval.toObject(); - } - - /* Connect constructor and prototype by named properties. */ - if (!js_SetClassPrototype(cx, ctor, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { + ctor = fun; + if (!LinkConstructorAndPrototype(cx, ctor, proto)) goto bad; - } /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ - if (ctor->getClass() == clasp) - ctor->setProto(proto); + if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, proto)) + goto bad; } - /* Add properties and methods to the prototype and the constructor. */ - if ((ps && !JS_DefineProperties(cx, proto, ps)) || - (fs && !JS_DefineFunctions(cx, proto, fs)) || - (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || - (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { + if (!DefinePropertiesAndBrand(cx, proto, ps, fs) || + (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs))) + { goto bad; } - /* - * Pre-brand the prototype and constructor if they have built-in methods. - * This avoids extra shape guard branch exits in the tracejitted code. - */ - if (fs) - proto->brand(cx); - if (ctor != proto && static_fs) - ctor->brand(cx); - - /* - * Make sure proto's emptyShape is available to be shared by objects of - * this class. JSObject::emptyShape is a one-slot cache. If we omit this, - * some other class could snap it up. (The risk is particularly great for - * Object.prototype.) - * - * All callers of JSObject::initSharingEmptyShape depend on this. - * - * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass - * and make the Array.prototype slow from the start. - */ - JS_ASSERT_IF(proto->clasp != clasp, - clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass); - if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0)) - goto bad; - if (clasp->flags & (JSCLASS_FREEZE_PROTO|JSCLASS_FREEZE_CTOR)) { JS_ASSERT_IF(ctor == proto, !(clasp->flags & JSCLASS_FREEZE_CTOR)); if (proto && (clasp->flags & JSCLASS_FREEZE_PROTO) && !proto->freeze(cx)) @@ -3943,16 +3759,20 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt } /* If this is a standard class, cache its prototype. */ - if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto)) - goto bad; + if (!cached && key != JSProto_Null) + SetClassObject(obj, key, ctor, proto); + if (ctorp) + *ctorp = ctor; return proto; bad: if (named) { Value rval; - obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false); + obj->deleteByValue(cx, StringValue(atom), &rval, false); } + if (cached) + ClearClassObject(cx, obj, key); return NULL; } @@ -3975,7 +3795,7 @@ IsStandardClassResolved(JSObject *obj, js::Class *clasp) } void -MarkStandardClassInitializedNoProto(JSObject* obj, js::Class *clasp) +MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp) { JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); @@ -3990,12 +3810,16 @@ MarkStandardClassInitializedNoProto(JSObject* obj, js::Class *clasp) } JSObject * -js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto, +js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto, Class *clasp, Native constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs) + JSPropertySpec *static_ps, JSFunctionSpec *static_fs, + JSObject **ctorp, AllocKind ctorKind) { - JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + RootObject rootProto(cx, &protoProto); + + RootedVarAtom atom(cx); + atom = js_Atomize(cx, clasp->name, strlen(clasp->name)); if (!atom) return NULL; @@ -4019,39 +3843,293 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto, } return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs, - ps, fs, static_ps, static_fs); + ps, fs, static_ps, static_fs, ctorp, ctorKind); +} + +void +JSObject::getSlotRange(size_t start, size_t length, + HeapValue **fixedStart, HeapValue **fixedEnd, + HeapValue **slotsStart, HeapValue **slotsEnd) +{ + JS_ASSERT(!isDenseArray()); + JS_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED)); + + size_t fixed = numFixedSlots(); + if (start < fixed) { + if (start + length < fixed) { + *fixedStart = &fixedSlots()[start]; + *fixedEnd = &fixedSlots()[start + length]; + *slotsStart = *slotsEnd = NULL; + } else { + size_t localCopy = fixed - start; + *fixedStart = &fixedSlots()[start]; + *fixedEnd = &fixedSlots()[start + localCopy]; + *slotsStart = &slots[0]; + *slotsEnd = &slots[length - localCopy]; + } + } else { + *fixedStart = *fixedEnd = NULL; + *slotsStart = &slots[start - fixed]; + *slotsEnd = &slots[start - fixed + length]; + } +} + +void +JSObject::initSlotRange(size_t start, const Value *vector, size_t length) +{ + JSCompartment *comp = compartment(); + HeapValue *fixedStart, *fixedEnd, *slotsStart, *slotsEnd; + getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd); + for (HeapValue *vp = fixedStart; vp != fixedEnd; vp++) + vp->init(comp, *vector++); + for (HeapValue *vp = slotsStart; vp != slotsEnd; vp++) + vp->init(comp, *vector++); +} + +void +JSObject::copySlotRange(size_t start, const Value *vector, size_t length) +{ + JSCompartment *comp = compartment(); + HeapValue *fixedStart, *fixedEnd, *slotsStart, *slotsEnd; + getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd); + for (HeapValue *vp = fixedStart; vp != fixedEnd; vp++) + vp->set(comp, *vector++); + for (HeapValue *vp = slotsStart; vp != slotsEnd; vp++) + vp->set(comp, *vector++); +} + +inline void +JSObject::invalidateSlotRange(size_t start, size_t length) +{ +#ifdef DEBUG + JS_ASSERT(!isDenseArray()); + + size_t fixed = numFixedSlots(); + + /* No bounds checks, allocated space has been updated but not the shape. */ + if (start < fixed) { + if (start + length < fixed) { + Debug_SetValueRangeToCrashOnTouch(fixedSlots() + start, length); + } else { + size_t localClear = fixed - start; + Debug_SetValueRangeToCrashOnTouch(fixedSlots() + start, localClear); + Debug_SetValueRangeToCrashOnTouch(slots, length - localClear); + } + } else { + Debug_SetValueRangeToCrashOnTouch(slots + start - fixed, length); + } +#endif /* DEBUG */ +} + +inline bool +JSObject::updateSlotsForSpan(JSContext *cx, size_t oldSpan, size_t newSpan) +{ + JS_ASSERT(oldSpan != newSpan); + + size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan); + size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan); + + if (oldSpan < newSpan) { + if (oldCount < newCount && !growSlots(cx, oldCount, newCount)) + return false; + + if (newSpan == oldSpan + 1) + initSlotUnchecked(oldSpan, UndefinedValue()); + else + initializeSlotRange(oldSpan, newSpan - oldSpan); + } else { + /* Trigger write barriers on the old slots before reallocating. */ + prepareSlotRangeForOverwrite(newSpan, oldSpan); + invalidateSlotRange(newSpan, oldSpan - newSpan); + + if (oldCount > newCount) + shrinkSlots(cx, oldCount, newCount); + } + + return true; +} + +bool +JSObject::setLastProperty(JSContext *cx, const js::Shape *shape) +{ + JS_ASSERT(!inDictionaryMode()); + JS_ASSERT(!shape->inDictionary()); + JS_ASSERT(shape->compartment() == compartment()); + JS_ASSERT(shape->numFixedSlots() == numFixedSlots()); + + size_t oldSpan = lastProperty()->slotSpan(); + size_t newSpan = shape->slotSpan(); + + if (oldSpan == newSpan) { + shape_ = const_cast(shape); + return true; + } + + if (!updateSlotsForSpan(cx, oldSpan, newSpan)) + return false; + + shape_ = const_cast(shape); + return true; } bool -JSObject::allocSlots(JSContext *cx, size_t newcap) +JSObject::setSlotSpan(JSContext *cx, uint32_t span) { - uint32 oldcap = numSlots(); + JS_ASSERT(inDictionaryMode()); + js::BaseShape *base = lastProperty()->base(); - JS_ASSERT(newcap >= oldcap && !hasSlotsArray()); + size_t oldSpan = base->slotSpan(); + + if (oldSpan == span) + return true; - if (newcap > NSLOTS_LIMIT) { - if (!JS_ON_TRACE(cx)) - js_ReportAllocationOverflow(cx); + if (!updateSlotsForSpan(cx, oldSpan, span)) return false; + + base->setSlotSpan(span); + return true; +} + +#if defined(_MSC_VER) && _MSC_VER >= 1500 +/* Work around a compiler bug in MSVC9 and above, where inlining this function + causes stack pointer offsets to go awry and spp to refer to something higher + up the stack. */ +MOZ_NEVER_INLINE +#endif +const js::Shape * +JSObject::nativeLookup(JSContext *cx, jsid id) +{ + JS_ASSERT(isNative()); + js::Shape **spp; + return js::Shape::search(cx, lastProperty(), id, &spp); +} + +bool +JSObject::growSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount) +{ + JS_ASSERT(newCount > oldCount); + JS_ASSERT(newCount >= SLOT_CAPACITY_MIN); + JS_ASSERT(!isDenseArray()); + + /* + * Slots are only allocated for call objects when new properties are + * added to them, which can only happen while the call is still on the + * stack (and an eval, DEFFUN, etc. happens). We thus do not need to + * worry about updating any active outer function args/vars. + */ + JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL); + + /* + * Slot capacities are determined by the span of allocated objects. Due to + * the limited number of bits to store shape slots, object growth is + * throttled well before the slot capacity can overflow. + */ + JS_ASSERT(newCount < NELEMENTS_LIMIT); + + size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0; + size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value); + + /* + * If we are allocating slots for an object whose type is always created + * by calling 'new' on a particular script, bump the GC kind for that + * type to give these objects a larger number of fixed slots when future + * objects are constructed. + */ + if (!hasLazyType() && !oldCount && type()->newScript) { + gc::AllocKind kind = type()->newScript->allocKind; + unsigned newScriptSlots = gc::GetGCKindSlots(kind); + if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) { + JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, + type()->newScript->shape); + if (!obj) + return false; + + type()->newScript->allocKind = kind; + type()->newScript->shape = obj->lastProperty(); + type()->markStateChange(cx); + } } - Value *tmpslots = (Value*) cx->malloc_(newcap * sizeof(Value)); - if (!tmpslots) - return false; /* Leave slots at inline buffer. */ - slots = tmpslots; - capacity = newcap; + if (!oldCount) { + slots = (HeapValue *) cx->malloc_(newCount * sizeof(HeapValue)); + if (!slots) + return false; + Debug_SetValueRangeToCrashOnTouch(slots, newCount); + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, newSize); + return true; + } + + HeapValue *newslots = (HeapValue*) cx->realloc_(slots, oldCount * sizeof(HeapValue), + newCount * sizeof(HeapValue)); + if (!newslots) + return false; /* Leave slots at its old size. */ + + bool changed = slots != newslots; + slots = newslots; + + Debug_SetValueRangeToCrashOnTouch(slots + oldCount, newCount - oldCount); + + /* Changes in the slots of global objects can trigger recompilation. */ + if (changed && isGlobal()) + types::MarkObjectStateChange(cx, this); + + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, newSize); - /* Copy over anything from the inline buffer. */ - memcpy(slots, fixedSlots(), oldcap * sizeof(Value)); - ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray()); return true; } +void +JSObject::shrinkSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount) +{ + JS_ASSERT(newCount < oldCount); + JS_ASSERT(!isDenseArray()); + + /* + * Refuse to shrink slots for call objects. This only happens in a very + * obscure situation (deleting names introduced by a direct 'eval') and + * allowing the slots pointer to change may require updating pointers in + * the function's active args/vars information. + */ + if (isCall()) + return; + + size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0; + size_t newSize = oldSize - (oldCount - newCount) * sizeof(Value); + + if (newCount == 0) { + cx->free_(slots); + slots = NULL; + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, newSize); + return; + } + + JS_ASSERT(newCount >= SLOT_CAPACITY_MIN); + + HeapValue *newslots = (HeapValue*) cx->realloc_(slots, newCount * sizeof(HeapValue)); + if (!newslots) + return; /* Leave slots at its old size. */ + + bool changed = slots != newslots; + slots = newslots; + + /* Watch for changes in global object slots, as for growSlots. */ + if (changed && isGlobal()) + types::MarkObjectStateChange(cx, this); + + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, newSize); +} + bool -JSObject::growSlots(JSContext *cx, size_t newcap) +JSObject::growElements(JSContext *cx, uintN newcap) { + JS_ASSERT(isDenseArray()); + /* - * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to + * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to * grow, double its capacity, to add N elements in amortized O(N) time. * * Above this limit, grow by 12.5% each time. Speed is still amortized @@ -4060,80 +4138,97 @@ JSObject::growSlots(JSContext *cx, size_t newcap) static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value); - uint32 oldcap = numSlots(); - JS_ASSERT(oldcap < newcap); + uint32_t oldcap = getDenseArrayCapacity(); + JS_ASSERT(oldcap <= newcap); - uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) - ? oldcap * 2 - : oldcap + (oldcap >> 3); + size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0; - uint32 actualCapacity = JS_MAX(newcap, nextsize); + uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) + ? oldcap * 2 + : oldcap + (oldcap >> 3); + + uint32_t actualCapacity = JS_MAX(newcap, nextsize); if (actualCapacity >= CAPACITY_CHUNK) actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK); else if (actualCapacity < SLOT_CAPACITY_MIN) actualCapacity = SLOT_CAPACITY_MIN; - /* Don't let nslots get close to wrapping around uint32. */ - if (actualCapacity >= NSLOTS_LIMIT) { + /* Don't let nelements get close to wrapping around uint32_t. */ + if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) { JS_ReportOutOfMemory(cx); return false; } - /* If nothing was allocated yet, treat it as initial allocation. */ - if (!hasSlotsArray()) - return allocSlots(cx, actualCapacity); + uint32_t initlen = getDenseArrayInitializedLength(); + uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER; + + ObjectElements *newheader; + if (hasDynamicElements()) { + uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; + newheader = (ObjectElements *) + cx->realloc_(getElementsHeader(), oldAllocated * sizeof(Value), + newAllocated * sizeof(Value)); + if (!newheader) + return false; /* Leave elements as its old size. */ + } else { + newheader = (ObjectElements *) cx->malloc_(newAllocated * sizeof(Value)); + if (!newheader) + return false; /* Ditto. */ + js_memcpy(newheader, getElementsHeader(), + (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value)); + } + + newheader->capacity = actualCapacity; + elements = newheader->elements(); + + Debug_SetValueRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen); - Value *tmpslots = (Value*) cx->realloc_(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value)); - if (!tmpslots) - return false; /* Leave dslots as its old size. */ - slots = tmpslots; - capacity = actualCapacity; + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements()); - /* Initialize the additional slots we added. */ - ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray()); return true; } void -JSObject::shrinkSlots(JSContext *cx, size_t newcap) +JSObject::shrinkElements(JSContext *cx, uintN newcap) { - uint32 oldcap = numSlots(); + JS_ASSERT(isDenseArray()); + + uint32_t oldcap = getDenseArrayCapacity(); JS_ASSERT(newcap <= oldcap); - JS_ASSERT(newcap >= slotSpan()); - if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) { - /* We won't shrink the slots any more. Clear excess holes. */ - ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray()); + size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0; + + /* Don't shrink elements below the minimum capacity. */ + if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements()) return; - } - uint32 fill = newcap; - if (newcap < SLOT_CAPACITY_MIN) - newcap = SLOT_CAPACITY_MIN; - if (newcap < numFixedSlots()) - newcap = numFixedSlots(); + newcap = Max(newcap, SLOT_CAPACITY_MIN); - Value *tmpslots = (Value*) cx->realloc_(slots, newcap * sizeof(Value)); - if (!tmpslots) - return; /* Leave slots at its old size. */ - slots = tmpslots; - capacity = newcap; + uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER; - if (fill < newcap) { - /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */ - ClearValueRange(slots + fill, newcap - fill, isDenseArray()); - } + ObjectElements *newheader = (ObjectElements *) + cx->realloc_(getElementsHeader(), newAllocated * sizeof(Value)); + if (!newheader) + return; /* Leave elements at its old size. */ + + newheader->capacity = newcap; + elements = newheader->elements(); + + if (Probes::objectResizeActive()) + Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements()); } +#ifdef DEBUG bool -JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved) +JSObject::slotInRange(uintN slot, SentinelAllowed sentinel) const { - JS_ASSERT_IF(isNative(), - isBlock() || isCall() || (isFunction() && isBoundFunction())); - - uintN nslots = JSSLOT_FREE(clasp) + nreserved; - return nslots <= numSlots() || allocSlots(cx, nslots); + size_t capacity = numFixedSlots() + numDynamicSlots(); + if (sentinel == SENTINEL_ALLOWED) + return slot <= capacity; + return slot < capacity; } +#endif /* DEBUG */ static JSObject * js_InitNullClass(JSContext *cx, JSObject *obj) @@ -4160,28 +4255,84 @@ SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles) JS_ASSERT_IF(!checkForCycles, obj != proto); JS_ASSERT(obj->isExtensible()); - if (obj->isNative()) { - if (!obj->ensureClassReservedSlots(cx)) - return false; + if (proto && proto->isXML()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN); + return false; } /* - * Regenerate property cache shape ids for all of the scopes along the - * old prototype chain to invalidate their property cache entries, in - * case any entries were filled by looking up through obj. + * Regenerate shapes for all of the scopes along the old prototype chain, + * in case any entries were filled by looking up through obj. Stop when a + * non-native object is found, prototype lookups will not be cached across + * these. + * + * How this shape change is done is very delicate; the change can be made + * either by marking the object's prototype as uncacheable (such that the + * property cache and JIT'ed ICs cannot assume the shape determines the + * prototype) or by just generating a new shape for the object. Choosing + * the former is bad if the object is on the prototype chain of other + * objects, as the uncacheable prototype can inhibit iterator caches on + * those objects and slow down prototype accesses. Choosing the latter is + * bad if there are many similar objects to this one which will have their + * prototype mutated, as the generateOwnShape forces the object into + * dictionary mode and similar property lineages will be repeatedly cloned. + * + * :XXX: bug 707717 make this code less brittle. */ JSObject *oldproto = obj; while (oldproto && oldproto->isNative()) { - oldproto->protoShapeChange(cx); + if (oldproto->hasSingletonType()) { + if (!oldproto->generateOwnShape(cx)) + return false; + } else { + if (!oldproto->setUncacheableProto(cx)) + return false; + } oldproto = oldproto->getProto(); } - if (!proto || !checkForCycles) { - obj->setProto(proto); - } else if (!SetProtoCheckingForCycles(cx, obj, proto)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_proto_str); - return false; + if (checkForCycles) { + for (JSObject *obj2 = proto; obj2; obj2 = obj2->getProto()) { + if (obj2 == obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, + js_proto_str); + return false; + } + } + } + + if (obj->hasSingletonType()) { + /* + * Just splice the prototype, but mark the properties as unknown for + * consistent behavior. + */ + if (!obj->splicePrototype(cx, proto)) + return false; + MarkTypeObjectUnknownProperties(cx, obj->type()); + return true; } + + if (proto && !proto->setNewTypeUnknown(cx)) + return false; + + TypeObject *type = proto + ? proto->getNewType(cx, NULL) + : cx->compartment->getEmptyType(cx); + if (!type) + return false; + + /* + * Setting __proto__ on an object that has escaped and may be referenced by + * other heap objects can only be done if the properties of both objects + * are unknown. Type sets containing this object will contain the original + * type but not the new type of the object, so we need to go and scan the + * entire compartment for type sets which have these objects and mark them + * as containing generic objects. + */ + MarkTypeObjectUnknownProperties(cx, obj->type(), true); + MarkTypeObjectUnknownProperties(cx, type, true); + + obj->setType(type); return true; } @@ -4191,7 +4342,9 @@ JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) { - obj = obj->getGlobal(); + RootObject objRoot(cx, &obj); + + obj = &obj->global(); if (!obj->isGlobal()) { *objp = NULL; return true; @@ -4223,327 +4376,159 @@ js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, return true; } -JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) -{ - JS_ASSERT(!obj->getParent()); - if (!obj->isGlobal()) - return JS_TRUE; - - return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) && - js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto)); -} - JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey, Value *vp, Class *clasp) { - StackFrame *fp; - JSObject *obj, *cobj, *pobj; + JSObject *cobj, *pobj; jsid id; JSProperty *prop; const Shape *shape; - /* - * Find the global object. Use cx->fp() directly to avoid falling off - * trace; all JIT-elided stack frames have the same global object as - * cx->fp(). - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - if (!start && (fp = cx->maybefp()) != NULL) - start = &fp->scopeChain(); + RootedVarObject obj(cx); if (start) { - /* Find the topmost object in the scope chain. */ - do { - obj = start; - start = obj->getParent(); - } while (start); + obj = &start->global(); + OBJ_TO_INNER_OBJECT(cx, *obj.address()); } else { - obj = cx->globalObject; - if (!obj) { - vp->setUndefined(); - return JS_TRUE; - } + obj = GetGlobalForScopeChain(cx); } - - OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) - return JS_FALSE; + return false; if (protoKey != JSProto_Null) { JS_ASSERT(JSProto_Null < protoKey); JS_ASSERT(protoKey < JSProto_LIMIT); if (!js_GetClassObject(cx, obj, protoKey, &cobj)) - return JS_FALSE; + return false; if (cobj) { vp->setObject(*cobj); return JS_TRUE; } id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]); } else { - JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name)); if (!atom) return false; id = ATOM_TO_JSID(atom); } JS_ASSERT(obj->isNative()); - if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, - &pobj, &prop) < 0) { - return JS_FALSE; - } + if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, &pobj, &prop)) + return false; Value v = UndefinedValue(); if (prop && pobj->isNative()) { shape = (Shape *) prop; - if (pobj->containsSlot(shape->slot)) { - v = pobj->nativeGetSlot(shape->slot); + if (shape->hasSlot()) { + v = pobj->nativeGetSlot(shape->slot()); if (v.isPrimitive()) v.setUndefined(); - } - } - *vp = v; - return JS_TRUE; -} - -JSObject * -js_ConstructObject(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent, - uintN argc, Value *argv) -{ - AutoArrayRooter argtvr(cx, argc, argv); - - JSProtoKey protoKey = GetClassProtoKey(clasp); - - /* Protect constructor in case a crazy getter for .prototype uproots it. */ - AutoValueRooter tvr(cx); - if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp)) - return NULL; - - const Value &cval = tvr.value(); - if (tvr.value().isPrimitive()) { - js_ReportIsNotFunction(cx, tvr.addr(), JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); - return NULL; - } - - /* - * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW - * does, likewise for the new object's parent. - */ - JSObject *ctor = &cval.toObject(); - if (!parent) - parent = ctor->getParent(); - if (!proto) { - Value rval; - if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &rval)) { - return NULL; - } - if (rval.isObjectOrNull()) - proto = rval.toObjectOrNull(); - } - - JSObject *obj = NewObject(cx, clasp, proto, parent); - if (!obj) - return NULL; - - obj->syncSpecialEquality(); - - Value rval; - if (!InvokeConstructorWithGivenThis(cx, obj, cval, argc, argv, &rval)) - return NULL; - - if (rval.isPrimitive()) - return obj; - - /* - * If the instance's class differs from what was requested, throw a type - * error. If the given class has both the JSCLASS_HAS_PRIVATE and the - * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its - * private data set at this point, then the constructor was replaced and - * we should throw a type error. - */ - obj = &rval.toObject(); - if (obj->getClass() != clasp || - (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_CONSTRUCT_PROTOTYPE)) && - !obj->getPrivate())) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_WRONG_CONSTRUCTOR, clasp->name); - return NULL; + } } - return obj; + *vp = v; + return true; } bool -JSObject::allocSlot(JSContext *cx, uint32 *slotp) +JSObject::allocSlot(JSContext *cx, uint32_t *slotp) { - uint32 slot = slotSpan(); - JS_ASSERT(slot >= JSSLOT_FREE(clasp)); + uint32_t slot = slotSpan(); + JS_ASSERT(slot >= JSSLOT_FREE(getClass())); /* - * If this object is in dictionary mode and it has a property table, try to - * pull a free slot from the property table's slot-number freelist. + * If this object is in dictionary mode, try to pull a free slot from the + * property table's slot-number freelist. */ - if (inDictionaryMode() && lastProp->hasTable()) { - uint32 &last = lastProp->getTable()->freelist; + if (inDictionaryMode()) { + PropertyTable &table = lastProperty()->table(); + uint32_t last = table.freelist; if (last != SHAPE_INVALID_SLOT) { #ifdef DEBUG JS_ASSERT(last < slot); - uint32 next = getSlot(last).toPrivateUint32(); + uint32_t next = getSlot(last).toPrivateUint32(); JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot); #endif *slotp = last; - Value &vref = getSlotRef(last); - last = vref.toPrivateUint32(); - vref.setUndefined(); + const Value &vref = getSlot(last); + table.freelist = vref.toPrivateUint32(); + setSlot(last, UndefinedValue()); return true; } } - if (slot >= numSlots() && !growSlots(cx, slot + 1)) + if (slot >= SHAPE_MAXIMUM_SLOT) { + js_ReportOutOfMemory(cx); return false; + } - /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */ - JS_ASSERT(getSlot(slot).isUndefined()); *slotp = slot; + + if (inDictionaryMode() && !setSlotSpan(cx, slot + 1)) + return false; + return true; } -bool -JSObject::freeSlot(JSContext *cx, uint32 slot) +void +JSObject::freeSlot(JSContext *cx, uint32_t slot) { - uint32 limit = slotSpan(); - JS_ASSERT(slot < limit); + JS_ASSERT(slot < slotSpan()); - Value &vref = getSlotRef(slot); - if (inDictionaryMode() && lastProp->hasTable()) { - uint32 &last = lastProp->getTable()->freelist; + if (inDictionaryMode()) { + uint32_t &last = lastProperty()->table().freelist; /* Can't afford to check the whole freelist, but let's check the head. */ - JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < limit && last != slot); + JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot); /* - * Freeing a slot other than the last one mapped by this object's - * shape (and not a reserved slot; see bug 595230): push the slot onto - * the dictionary property table's freelist. We want to let the last - * slot be freed by shrinking the dslots vector; see js_TraceObject. + * Place all freed slots other than reserved slots (bug 595230) on the + * dictionary's free list. */ - if (JSSLOT_FREE(clasp) <= slot && slot + 1 < limit) { + if (JSSLOT_FREE(getClass()) <= slot) { JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan()); - vref.setPrivateUint32(last); + setSlot(slot, PrivateUint32Value(last)); last = slot; - return true; - } - } - vref.setUndefined(); - return false; -} - -/* JSBOXEDWORD_INT_MAX as a string */ -#define JSBOXEDWORD_INT_MAX_STRING "1073741823" - -/* - * Convert string indexes that convert to int jsvals as ints to save memory. - * Care must be taken to use this macro every time a property name is used, or - * else double-sets, incorrect property cache misses, or other mistakes could - * occur. - */ -jsid -js_CheckForStringIndex(jsid id) -{ - if (!JSID_IS_ATOM(id)) - return id; - - JSAtom *atom = JSID_TO_ATOM(id); - const jschar *s = atom->chars(); - jschar ch = *s; - - JSBool negative = (ch == '-'); - if (negative) - ch = *++s; - - if (!JS7_ISDEC(ch)) - return id; - - size_t n = atom->length() - negative; - if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1) - return id; - - const jschar *cp = s; - const jschar *end = s + n; - - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10 * index + c; - cp++; - } - } - - /* - * Non-integer indexes can't be represented as integers. Also, distinguish - * index "-0" from "0", because JSBOXEDWORD_INT cannot. - */ - if (cp != end || (negative && index == 0)) - return id; - - if (negative) { - if (oldIndex < -(JSID_INT_MIN / 10) || - (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10))) - { - id = INT_TO_JSID(-jsint(index)); - } - } else { - if (oldIndex < JSID_INT_MAX / 10 || - (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) - { - id = INT_TO_JSID(jsint(index)); + return; } } - - return id; + setSlot(slot, UndefinedValue()); } -static JSBool +static bool PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id) { const Shape *shape; + RootObject objRoot(cx, &obj); + RootId idRoot(cx, &id); + while (obj) { if (!obj->isNative()) { obj = obj->getProto(); continue; } - shape = obj->nativeLookup(id); + shape = obj->nativeLookup(cx, id); if (shape) { - PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); - obj->shadowingShapeChange(cx, *shape); + if (!obj->shadowingShapeChange(cx, *shape)) + return false; - if (!obj->getParent()) { - /* - * All scope chains end in a global object, so this will change - * the global shape. jstracer.cpp assumes that the global shape - * never changes on trace, so we must deep-bail here. - */ - LeaveTrace(cx); - } - return JS_TRUE; + obj->shadowingShapeChange(cx, *shape); + return true; } obj = obj->getProto(); } - return JS_FALSE; + + return true; } -void +bool js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) { + RootObject objRoot(cx, &obj); + RootId idRoot(cx, &id); + JS_ASSERT(obj->isDelegate()); PurgeProtoChain(cx, obj->getProto(), id); @@ -4554,61 +4539,51 @@ js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) * may gain such properties via eval introducing new vars; see bug 490364. */ if (obj->isCall()) { - while ((obj = obj->getParent()) != NULL) { - if (PurgeProtoChain(cx, obj, id)) - break; + while ((obj = obj->enclosingScope()) != NULL) { + if (!PurgeProtoChain(cx, obj, id)) + return false; } } + + return true; } -const Shape * +Shape * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - PropertyOp getter, StrictPropertyOp setter, uint32 slot, + PropertyOp getter, StrictPropertyOp setter, uint32_t slot, uintN attrs, uintN flags, intN shortid) { JS_ASSERT(!(flags & Shape::METHOD)); + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + /* * Purge the property cache of now-shadowed id in obj's scope chain. Do * this optimistically (assuming no failure below) before locking obj, so * we can lock the shadowed scope. */ - js_PurgeScopeChain(cx, obj, id); - - if (!obj->ensureClassReservedSlots(cx)) + if (!js_PurgeScopeChain(cx, obj, id)) return NULL; - /* Convert string indices to integers if appropriate. */ - id = js_CheckForStringIndex(id); return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid); } -const Shape * +Shape * js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - const Shape *shape, uintN attrs, uintN mask, + Shape *shape, uintN attrs, uintN mask, PropertyOp getter, StrictPropertyOp setter) { - if (!obj->ensureClassReservedSlots(cx)) - return NULL; - /* * Check for freezing an object with shape-memoized methods here, on a - * shape-by-shape basis. Note that getter may be a pun of the method's - * joined function object value, to indicate "no getter change". In this - * case we must null getter to get the desired PropertyStub behavior. + * shape-by-shape basis. */ if ((attrs & JSPROP_READONLY) && shape->isMethod()) { - JSObject *funobj = &shape->methodObject(); - Value v = ObjectValue(*funobj); + Value v = ObjectValue(*obj->nativeGetMethod(shape)); shape = obj->methodReadBarrier(cx, *shape, &v); if (!shape) return NULL; - - if (CastAsObject(getter) == funobj) { - JS_ASSERT(!(attrs & JSPROP_GETTER)); - getter = NULL; - } } return obj->changeProperty(cx, shape, attrs, mask, getter, setter); @@ -4618,8 +4593,17 @@ JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, PropertyOp getter, StrictPropertyOp setter, uintN attrs) { - return js_DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, - 0, 0, NULL); + return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0); +} + +JSBool +js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0); } /* @@ -4631,27 +4615,38 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, static inline bool CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp) { - if (clasp->addProperty != PropertyStub) { + if (clasp->addProperty != JS_PropertyStub) { Value nominal = *vp; - if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->id, vp)) + if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->propid(), vp)) return false; if (*vp != nominal) { - if (obj->containsSlot(shape->slot)) - obj->nativeSetSlot(shape->slot, *vp); + if (shape->hasSlot()) + obj->nativeSetSlotWithType(cx, shape, *vp); } } return true; } -JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value, - PropertyOp getter, StrictPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp, - uintN defineHow /* = 0 */) +namespace js { + +const Shape * +DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_, + PropertyOp getter, StrictPropertyOp setter, uintN attrs, + uintN flags, intN shortid, uintN defineHow /* = 0 */) { - JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0); - LeaveTraceIfGlobalObject(cx, obj); + JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE | + DNP_SET_METHOD | DNP_SKIP_TYPE)) == 0); + + RootObject objRoot(cx, &obj); + RootId idRoot(cx, &id); + + /* + * Make a local copy of value, in case a method barrier needs to update the + * value to define, and just so addProperty can mutate its inout parameter. + */ + RootedVarValue value(cx); + value = value_; /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); @@ -4661,37 +4656,38 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu * update the attributes and property ops. A getter or setter is really * only half of a property. */ - const Shape *shape = NULL; + Shape *shape = NULL; if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { JSObject *pobj; JSProperty *prop; + /* Type information for getter/setter properties is unknown. */ + AddTypePropertyId(cx, obj, id, types::Type::UnknownType()); + MarkTypePropertyConfigured(cx, obj, id); + /* - * If JS_THREADSAFE and id is found, js_LookupProperty returns with - * shape non-null and pobj locked. If pobj == obj, the property is - * already in obj and obj has its own (mutable) scope. So if we are - * defining a getter whose setter was already defined, or vice versa, - * finish the job via obj->changeProperty, and refresh the property - * cache line for (obj, id) to map shape. + * If we are defining a getter whose setter was already defined, or + * vice versa, finish the job via obj->changeProperty, and refresh the + * property cache line for (obj, id) to map shape. */ if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - shape = (Shape *) prop; - if (shape && pobj == obj && shape->isAccessorDescriptor()) { - shape = obj->changeProperty(cx, shape, attrs, - JSPROP_GETTER | JSPROP_SETTER, - (attrs & JSPROP_GETTER) - ? getter - : shape->getter(), - (attrs & JSPROP_SETTER) - ? setter - : shape->setter()); - - if (!shape) - return false; - } else if (prop) { - prop = NULL; - shape = NULL; + return NULL; + if (prop && pobj == obj) { + shape = (Shape *) prop; + if (shape->isAccessorDescriptor()) { + shape = obj->changeProperty(cx, shape, attrs, + JSPROP_GETTER | JSPROP_SETTER, + (attrs & JSPROP_GETTER) + ? getter + : shape->getter(), + (attrs & JSPROP_SETTER) + ? setter + : shape->setter()); + if (!shape) + return NULL; + } else { + shape = NULL; + } } } @@ -4700,131 +4696,77 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu * to be shadowed in obj's scope chain unless it is known a priori that it * is not possible. We do this before locking obj to avoid nesting locks. */ - if (!(defineHow & JSDNP_DONT_PURGE)) - js_PurgeScopeChain(cx, obj, id); - - /* - * Check whether a readonly property or setter is being defined on a known - * prototype object. See the comment in jscntxt.h before protoHazardShape's - * member declaration. - */ - if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) - cx->runtime->protoHazardShape = js_GenerateShape(cx); + if (!(defineHow & DNP_DONT_PURGE)) { + if (!js_PurgeScopeChain(cx, obj, id)) + return NULL; + } /* Use the object's class getter and setter by default. */ Class *clasp = obj->getClass(); - if (!(defineHow & JSDNP_SET_METHOD)) { + if (!(defineHow & DNP_SET_METHOD)) { if (!getter && !(attrs & JSPROP_GETTER)) getter = clasp->getProperty; if (!setter && !(attrs & JSPROP_SETTER)) setter = clasp->setProperty; } - /* Get obj's own scope if it has one, or create a new one for obj. */ - if (!obj->ensureClassReservedSlots(cx)) - return false; - - /* - * Make a local copy of value, in case a method barrier needs to update the - * value to define, and just so addProperty can mutate its inout parameter. - */ - Value valueCopy = value; - bool adding = false; + if (((defineHow & DNP_SET_METHOD) || getter == JS_PropertyStub) && + !(defineHow & DNP_SKIP_TYPE)) { + /* + * Type information for normal native properties should reflect the + * initial value of the property. + */ + AddTypePropertyId(cx, obj, id, value); + if (attrs & JSPROP_READONLY) + MarkTypePropertyConfigured(cx, obj, id); + } if (!shape) { /* Add a new property, or replace an existing one of the same id. */ - if (defineHow & JSDNP_SET_METHOD) { - JS_ASSERT(clasp == &js_ObjectClass); + if (defineHow & DNP_SET_METHOD) { + JS_ASSERT(clasp == &ObjectClass); JS_ASSERT(IsFunctionObject(value)); JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); JS_ASSERT(!getter && !setter); - JSObject *funobj = &value.toObject(); - if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) { + JSObject *funobj = &value.raw().toObject(); + if (!funobj->toFunction()->isClonedMethod()) flags |= Shape::METHOD; - getter = CastAsPropertyOp(funobj); - } } - if (const Shape *existingShape = obj->nativeLookup(id)) { - if (existingShape->hasSlot()) - AbortRecordingIfUnexpectedGlobalWrite(cx, obj, existingShape->slot); - + if (const Shape *existingShape = obj->nativeLookup(cx, id)) { if (existingShape->isMethod() && - ObjectValue(existingShape->methodObject()) == valueCopy) + ObjectValue(*obj->nativeGetMethod(existingShape)) == value) { /* * Redefining an existing shape-memoized method object without * changing the property's value, perhaps to change attributes. * Clone now via the method read barrier. - * - * But first, assert that our caller is not trying to preserve - * the joined function object value as the getter object for - * the redefined property. The joined function object cannot - * yet have leaked, so only an internal code path could attempt - * such a thing. Any such path would be a bug to fix. */ - JS_ASSERT(existingShape->getter() != getter); - - if (!obj->methodReadBarrier(cx, *existingShape, &valueCopy)) - return false; + if (!obj->methodReadBarrier(cx, *existingShape, value.address())) + return NULL; } - } else { - adding = true; } - uint32 oldShape = obj->shape(); shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT, attrs, flags, shortid); if (!shape) - return false; - - /* - * If shape is a joined method, the above call to putProperty suffices - * to update the object's shape id if need be (because the shape's hash - * identity includes the method value). - * - * But if scope->branded(), the object's shape id may not have changed - * and we may be overwriting a cached function-valued property (note - * how methodWriteBarrier checks previous vs. would-be current value). - * See bug 560998. - */ - if (obj->shape() == oldShape && obj->branded() && shape->slot != SHAPE_INVALID_SLOT) { - DebugOnly newshape = - obj->methodWriteBarrier(cx, *shape, valueCopy); - JS_ASSERT(newshape == shape); - } + return NULL; } /* Store valueCopy before calling addProperty, in case the latter GC's. */ - if (obj->containsSlot(shape->slot)) - obj->nativeSetSlot(shape->slot, valueCopy); + if (shape->hasSlot()) + obj->nativeSetSlot(shape->slot(), value); - /* XXXbe called with lock held */ - if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) { + if (!CallAddPropertyHook(cx, clasp, obj, shape, value.address())) { obj->removeProperty(cx, id); - return false; - } - - if (defineHow & JSDNP_CACHE_RESULT) { - JS_ASSERT_NOT_ON_TRACE(cx); - if (adding) { - JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, true); - TRACE_1(AddProperty, obj); - } + return NULL; } - if (propp) - *propp = (JSProperty *) shape; - return true; -#ifdef JS_TRACER - error: // TRACE_1 jumps here on error. -#endif - return false; + return shape; } -#define SCOPE_DEPTH_ACCUM(bs,val) \ - JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val)) +} /* namespace js */ /* * Call obj's resolve hook. @@ -4847,7 +4789,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu * and return true. */ static JSBool -CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags, +CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, uintN flags, JSObject **objp, JSProperty **propp, bool *recursedp) { Class *clasp = obj->getClass(); @@ -4873,10 +4815,12 @@ CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flag if (clasp->flags & JSCLASS_NEW_RESOLVE) { JSNewResolveOp newresolve = reinterpret_cast(resolve); - if (flags == JSRESOLVE_INFER) + if (flags == RESOLVE_INFER) flags = js_InferFlags(cx, 0); - JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL; - if (!newresolve(cx, obj, id, flags, &obj2)) + + RootedVarObject obj2(cx); + obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL; + if (!newresolve(cx, obj, id, flags, obj2.address())) return false; /* @@ -4891,7 +4835,7 @@ CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flag if (!obj2->isNative()) { /* Whoops, newresolve handed back a foreign obj2. */ JS_ASSERT(obj2 != obj); - return obj2->lookupProperty(cx, id, objp, propp); + return obj2->lookupGeneric(cx, id, objp, propp); } obj = obj2; } else { @@ -4900,7 +4844,7 @@ CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flag } if (!obj->nativeEmpty()) { - if (const Shape *shape = obj->nativeLookup(id)) { + if (const Shape *shape = obj->nativeLookup(cx, id)) { *objp = obj; *propp = (JSProperty *) shape; } @@ -4909,39 +4853,39 @@ CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flag return true; } -static JS_ALWAYS_INLINE int -js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp) +static JS_ALWAYS_INLINE bool +LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) { /* We should not get string indices which aren't already integers here. */ JS_ASSERT(id == js_CheckForStringIndex(id)); + RootObject objRoot(cx, &obj); + RootId idRoot(cx, &id); + /* Search scopes starting with obj and following the prototype link. */ JSObject *start = obj; - int protoIndex; - for (protoIndex = 0; ; protoIndex++) { - const Shape *shape = obj->nativeLookup(id); + while (true) { + const Shape *shape = obj->nativeLookup(cx, id); if (shape) { - SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex); *objp = obj; *propp = (JSProperty *) shape; - return protoIndex; + return true; } /* Try obj's class resolve hook if id was not found in obj's scope. */ - if (!shape && obj->getClass()->resolve != JS_ResolveStub) { + if (obj->getClass()->resolve != JS_ResolveStub) { bool recursed; - if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed)) - return -1; + if (!CallResolveOp(cx, start, objRoot, idRoot, flags, objp, propp, &recursed)) + return false; if (recursed) break; if (*propp) { - /* Recalculate protoIndex in case it was resolved on some other object. */ - protoIndex = 0; - for (JSObject *proto = start; proto && proto != *objp; proto = proto->getProto()) - protoIndex++; - SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex); - return protoIndex; + /* + * For stats we do not recalculate protoIndex even if it was + * resolved on some other object. + */ + return true; } } @@ -4949,8 +4893,8 @@ js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN fl if (!proto) break; if (!proto->isNative()) { - if (!proto->lookupProperty(cx, id, objp, propp)) - return -1; + if (!proto->lookupGeneric(cx, id, objp, propp)) + return false; #ifdef DEBUG /* * Non-native objects must have either non-native lookup results, @@ -4966,7 +4910,7 @@ js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN fl JS_ASSERT(proto); } #endif - return protoIndex + 1; + return true; } obj = proto; @@ -4974,7 +4918,7 @@ js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN fl *objp = NULL; *propp = NULL; - return protoIndex; + return true; } JS_FRIEND_API(JSBool) @@ -4984,79 +4928,80 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); - return js_LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp) >= 0; + return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp); +} + +JS_FRIEND_API(JSBool) +js_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, JSProperty **propp) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + + return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp); } -int -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp) +bool +js::LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) { /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); - return js_LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp); + return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp); } -PropertyCacheEntry * -js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, - JSObject **objp, JSObject **pobjp, JSProperty **propp) +bool +js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain, + JSObject **objp, JSObject **pobjp, JSProperty **propp) { - JSObject *scopeChain, *obj, *parent, *pobj; - PropertyCacheEntry *entry; - int scopeIndex, protoIndex; + jsid id = ATOM_TO_JSID(name); + JSObject *obj, *parent, *pobj; + int scopeIndex; JSProperty *prop; - JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); - scopeChain = &js_GetTopStackFrame(cx)->scopeChain(); - /* Scan entries on the scope chain that we can cache across. */ - entry = JS_NO_PROP_CACHE_FILL; obj = scopeChain; - parent = obj->getParent(); + parent = obj->enclosingScope(); for (scopeIndex = 0; parent ? IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { - protoIndex = - js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, - &pobj, &prop); - if (protoIndex < 0) - return NULL; + if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop)) + return false; if (prop) { #ifdef DEBUG if (parent) { - Class *clasp = obj->getClass(); JS_ASSERT(pobj->isNative()); - JS_ASSERT(pobj->getClass() == clasp); - if (clasp == &js_BlockClass) { + JS_ASSERT(pobj->getClass() == obj->getClass()); + if (obj->isBlock()) { /* - * A block instance on the scope chain is immutable and it - * shares its shapes with its compile-time prototype. + * A block instance on the scope chain is immutable and + * shares its shape with the compile-time prototype. Thus + * we cannot find any property on the prototype. */ - JS_ASSERT(pobj == obj); JS_ASSERT(pobj->isClonedBlock()); - JS_ASSERT(protoIndex == 0); } else { /* Call and DeclEnvClass objects have no prototypes. */ JS_ASSERT(!obj->getProto()); - JS_ASSERT(protoIndex == 0); } + JS_ASSERT(pobj == obj); } else { JS_ASSERT(obj->isNative()); } #endif + /* * We must check if pobj is native as a global object can have * non-native prototype. */ if (cacheResult && pobj->isNative()) { - entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, - protoIndex, pobj, - (Shape *) prop); + JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, + (Shape *) prop); } - SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex); + goto out; } @@ -5065,22 +5010,20 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, goto out; } obj = parent; - parent = obj->getParent(); + parent = obj->enclosingScope(); } for (;;) { - if (!obj->lookupProperty(cx, id, &pobj, &prop)) - return NULL; - if (prop) { - PCMETER(JS_PROPERTY_CACHE(cx).nofills++); + if (!obj->lookupGeneric(cx, id, &pobj, &prop)) + return false; + if (prop) goto out; - } /* * We conservatively assume that a resolve hook could mutate the scope - * chain during JSObject::lookupProperty. So we read parent here again. + * chain during JSObject::lookupGeneric. So we read parent here again. */ - parent = obj->getParent(); + parent = obj->enclosingScope(); if (!parent) { pobj = NULL; break; @@ -5093,29 +5036,28 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, *objp = obj; *pobjp = pobj; *propp = prop; - return entry; + return true; } /* * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|. * Otherwise, its type and meaning depends on the host object's implementation. */ -JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp) +bool +js::FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain, + JSObject **objp, JSObject **pobjp, JSProperty **propp) { - return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp); + return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp); } JSObject * -js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) +js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name) { /* * This function should not be called for a global object or from the * trace and should have a valid cache entry for native scopeChain. */ - JS_ASSERT(scopeChain->getParent()); - JS_ASSERT(!JS_ON_TRACE(cx)); + JS_ASSERT(scopeChain->enclosingScope() != NULL); JSObject *obj = scopeChain; @@ -5129,29 +5071,23 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || IsCacheableNonGlobalScope(obj); + obj->isGlobal() || IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; - int protoIndex = js_LookupPropertyWithFlags(cx, obj, id, - cx->resolveFlags, - &pobj, &prop); - if (protoIndex < 0) + if (!LookupPropertyWithFlags(cx, obj, name, cx->resolveFlags, &pobj, &prop)) return NULL; if (prop) { if (!pobj->isNative()) { - JS_ASSERT(!obj->getParent()); + JS_ASSERT(obj->isGlobal()); return obj; } - JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass()); - DebugOnly entry = - JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj, - (Shape *) prop); - JS_ASSERT(entry); + JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass()); + JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop); return obj; } - JSObject *parent = obj->getParent(); + JSObject *parent = obj->enclosingScope(); if (!parent) return obj; obj = parent; @@ -5161,21 +5097,21 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) do { JSObject *pobj; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &pobj, &prop)) + if (!obj->lookupProperty(cx, name, &pobj, &prop)) return NULL; if (prop) break; /* * We conservatively assume that a resolve hook could mutate the scope - * chain during JSObject::lookupProperty. So we must check if parent is + * chain during JSObject::lookupGeneric. So we must check if parent is * not null here even if it wasn't before the lookup. */ - JSObject *parent = obj->getParent(); + JSObject *parent = obj->enclosingScope(); if (!parent) break; obj = parent; - } while (obj->getParent()); + } while (!obj->isGlobal()); return obj; } @@ -5183,42 +5119,38 @@ static JS_ALWAYS_INLINE JSBool js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, Value *vp) { - LeaveTraceIfGlobalObject(cx, pobj); - - uint32 slot; - int32 sample; - JS_ASSERT(pobj->isNative()); - slot = shape->slot; - if (slot != SHAPE_INVALID_SLOT) { - *vp = pobj->nativeGetSlot(slot); + if (shape->hasSlot()) { + *vp = pobj->nativeGetSlot(shape->slot()); JS_ASSERT(!vp->isMagic()); + JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(), + js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp)); } else { vp->setUndefined(); } if (shape->hasDefaultGetter()) return true; - if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) { - JS_ASSERT(shape->methodObject() == vp->toObject()); + if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) return true; - } - sample = cx->runtime->propertyRemovals; - { - AutoShapeRooter tvr(cx, shape); - AutoObjectRooter tvr2(cx, pobj); - if (!shape->get(cx, receiver, obj, pobj, vp)) - return false; + jsbytecode *pc; + JSScript *script = cx->stack.currentScript(&pc); + if (script && script->hasAnalysis()) { + analyze::Bytecode *code = script->analysis()->maybeCode(pc); + if (code) + code->accessGetter = true; } - if (pobj->containsSlot(slot) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - pobj->nativeContains(*shape))) { - if (!pobj->methodWriteBarrier(cx, *shape, *vp)) - return false; - pobj->nativeSetSlot(slot, *vp); + if (!shape->get(cx, receiver, obj, pobj, vp)) + return false; + + /* Update slotful shapes according to the value produced by the getter. */ + if (shape->hasSlot() && pobj->nativeContains(cx, *shape)) { + /* Method shapes were removed by methodReadBarrier under shape->get(). */ + JS_ASSERT(!shape->isMethod()); + pobj->nativeSetSlot(shape->slot(), *vp); } return true; @@ -5234,24 +5166,17 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, u JSBool js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool strict, Value *vp) { - LeaveTraceIfGlobalObject(cx, obj); - - uint32 slot; - int32 sample; + AddTypePropertyId(cx, obj, shape->propid(), *vp); JS_ASSERT(obj->isNative()); - slot = shape->slot; - if (slot != SHAPE_INVALID_SLOT) { - JS_ASSERT(obj->containsSlot(slot)); + if (shape->hasSlot()) { + uint32_t slot = shape->slot(); - /* If shape has a stub setter, keep obj locked and just store *vp. */ + /* If shape has a stub setter, just store *vp. */ if (shape->hasDefaultSetter()) { if (!added) { - AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot); - - /* FIXME: This should pass *shape, not slot, but see bug 630354. */ - if (!obj->methodWriteBarrier(cx, slot, *vp)) + if (shape->isMethod() && !obj->methodShapeChange(cx, *shape)) return false; } obj->nativeSetSlot(slot, *vp); @@ -5268,55 +5193,38 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool return js_ReportGetterOnlyAssignment(cx); } - sample = cx->runtime->propertyRemovals; - { - AutoShapeRooter tvr(cx, shape); - if (!shape->set(cx, obj, strict, vp)) - return false; - - JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot); - slot = shape->slot; - } + int32_t sample = cx->runtime->propertyRemovals; + if (!shape->set(cx, obj, strict, vp)) + return false; - if (obj->containsSlot(slot) && + /* + * Update any slot for the shape with the value produced by the setter, + * unless the setter deleted the shape. + */ + if (shape->hasSlot() && (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - obj->nativeContains(*shape))) { - if (!added) { - AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot); - if (!obj->methodWriteBarrier(cx, *shape, *vp)) - return false; - } - obj->setSlot(slot, *vp); + obj->nativeContains(cx, *shape))) { + obj->setSlot(shape->slot(), *vp); } return true; } -static JS_ALWAYS_INLINE bool -js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, - uintN getHow, Value *vp, - const Shape **shapeOut, JSObject **holderOut) +static JS_ALWAYS_INLINE JSBool +js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, + uint32_t getHow, Value *vp) { JSObject *aobj, *obj2; - int protoIndex; JSProperty *prop; const Shape *shape; - JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx)); - - *shapeOut = NULL; - /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); aobj = js_GetProtoIfDenseArray(obj); - /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */ - protoIndex = js_LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, - &obj2, &prop); - if (protoIndex < 0) - return JS_FALSE; - - *holderOut = obj2; + /* This call site is hot -- use the always-inlined variant of LookupPropertyWithFlags(). */ + if (!LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, &obj2, &prop)) + return false; if (!prop) { vp->setUndefined(); @@ -5324,7 +5232,9 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *rece if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp)) return JS_FALSE; - PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++); + /* Record non-undefined values produced by the class getter hook. */ + if (!vp->isUndefined()) + AddTypePropertyId(cx, obj, id, *vp); /* * Give a strict warning if foo.bar is evaluated by a script for an @@ -5332,48 +5242,47 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *rece */ jsbytecode *pc; if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) { - JSOp op; - uintN flags; + JSOp op = (JSOp) *pc; - op = (JSOp) *pc; - if (op == JSOP_TRAP) { - JS_ASSERT_NOT_ON_TRACE(cx); - op = JS_GetTrapOpcode(cx, cx->fp()->script(), pc); - } if (op == JSOP_GETXPROP) { - flags = JSREPORT_ERROR; - } else { - if (!cx->hasStrictOption() || - (op != JSOP_GETPROP && op != JSOP_GETELEM) || - js_CurrentPCIsInImacro(cx)) { - return JS_TRUE; - } + /* Undefined property during a name lookup, report an error. */ + JSAutoByteString printable; + if (js_ValueToPrintable(cx, IdToValue(id), &printable)) + js_ReportIsNotDefined(cx, printable.ptr()); + return false; + } - /* - * XXX do not warn about missing __iterator__ as the function - * may be called from JS_GetMethodById. See bug 355145. - */ - if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom)) - return JS_TRUE; + if (!cx->hasStrictOption() || + cx->stack.currentScript()->warnedAboutUndefinedProp || + (op != JSOP_GETPROP && op != JSOP_GETELEM)) { + return JS_TRUE; + } - /* Do not warn about tests like (obj[prop] == undefined). */ - if (cx->resolveFlags == JSRESOLVE_INFER) { - LeaveTrace(cx); - pc += js_CodeSpec[op].length; - if (Detecting(cx, pc)) - return JS_TRUE; - } else if (cx->resolveFlags & JSRESOLVE_DETECTING) { - return JS_TRUE; - } + /* + * XXX do not warn about missing __iterator__ as the function + * may be called from JS_GetMethodById. See bug 355145. + */ + if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom)) + return JS_TRUE; - flags = JSREPORT_WARNING | JSREPORT_STRICT; + /* Do not warn about tests like (obj[prop] == undefined). */ + if (cx->resolveFlags == RESOLVE_INFER) { + pc += js_CodeSpec[op].length; + if (Detecting(cx, pc)) + return JS_TRUE; + } else if (cx->resolveFlags & JSRESOLVE_DETECTING) { + return JS_TRUE; } + uintN flags = JSREPORT_WARNING | JSREPORT_STRICT; + cx->stack.currentScript()->warnedAboutUndefinedProp = true; + /* Ok, bad undefined property reference: whine about it. */ if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, IdToValue(id), - NULL, NULL, NULL)) { - return JS_FALSE; + NULL, NULL, NULL)) + { + return false; } } return JS_TRUE; @@ -5381,17 +5290,14 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *rece if (!obj2->isNative()) { return obj2->isProxy() - ? JSProxy::get(cx, obj2, receiver, id, vp) - : obj2->getProperty(cx, id, vp); + ? Proxy::get(cx, obj2, receiver, id, vp) + : obj2->getGeneric(cx, id, vp); } shape = (Shape *) prop; - *shapeOut = shape; - if (getHow & JSGET_CACHE_RESULT) { - JS_ASSERT_NOT_ON_TRACE(cx); - JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, shape); - } + if (getHow & JSGET_CACHE_RESULT) + JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, obj2, shape); /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */ if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp)) @@ -5401,32 +5307,25 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *rece } bool -js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, - uint32 getHow, Value *vp, - const Shape **shapeOut, JSObject **holderOut) -{ - return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp, - shapeOut, holderOut); -} - -static JS_ALWAYS_INLINE JSBool -js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, - uint32 getHow, Value *vp) +js::GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp) { - const Shape *shape; - JSObject *holder; - return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp, &shape, &holder); + return !!js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp); } JSBool -js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp) +js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { - return js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp); + /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ + return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp); } JSBool -js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) +js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) { + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp); } @@ -5436,7 +5335,7 @@ js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, { JSProperty *prop; JSObject *obj2; - if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0) + if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop)) return false; if (!prop) { @@ -5452,14 +5351,13 @@ js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp) { JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); - PropertyIdOp op = obj->getOps()->getProperty; + GenericIdOp op = obj->getOps()->getGeneric; if (!op) { #if JS_HAS_XML_SUPPORT JS_ASSERT(!obj->isXML()); #endif - return js_GetPropertyHelper(cx, obj, id, getHow, vp); + return GetPropertyHelper(cx, obj, id, getHow, vp); } - JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray()); #if JS_HAS_XML_SUPPORT if (obj->isXML()) return js_GetXMLMethod(cx, obj, id, vp); @@ -5468,9 +5366,9 @@ js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp) } JS_FRIEND_API(bool) -js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname) +js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname) { - StackFrame *const fp = js_GetTopStackFrame(cx); + StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); if (!fp) return true; @@ -5490,7 +5388,7 @@ js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname) } bool -JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report) +JSObject::reportReadOnly(JSContext *cx, jsid id, uintN report) { return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY, JSDVG_IGNORE_STACK, IdToValue(id), NULL, @@ -5498,7 +5396,7 @@ JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report) } bool -JSObject::reportNotConfigurable(JSContext* cx, jsid id, uintN report) +JSObject::reportNotConfigurable(JSContext *cx, jsid id, uintN report) { return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, IdToValue(id), NULL, @@ -5518,14 +5416,28 @@ JSObject::callMethod(JSContext *cx, jsid id, uintN argc, Value *argv, Value *vp) { Value fval; return js_GetMethod(cx, this, id, JSGET_NO_METHOD_BARRIER, &fval) && - ExternalInvoke(cx, ObjectValue(*this), fval, argc, argv, vp); + Invoke(cx, ObjectValue(*this), fval, argc, argv, vp); +} + +static bool +CloneFunctionForSetMethod(JSContext *cx, Value *vp) +{ + JSFunction *fun = vp->toObject().toFunction(); + + /* Clone the fun unless it already has been. */ + if (!fun->isClonedMethod()) { + fun = CloneFunctionObject(cx, fun); + if (!fun) + return false; + vp->setObject(*fun); + } + return true; } JSBool js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, Value *vp, JSBool strict) { - int protoIndex; JSObject *pobj; JSProperty *prop; const Shape *shape; @@ -5536,23 +5448,28 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, StrictPropertyOp setter; bool added; - JS_ASSERT((defineHow & - ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0); - if (defineHow & JSDNP_CACHE_RESULT) - JS_ASSERT_NOT_ON_TRACE(cx); + JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_SET_METHOD | DNP_UNQUALIFIED)) == 0); /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); - protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, - &pobj, &prop); - if (protoIndex < 0) - return JS_FALSE; + if (JS_UNLIKELY(obj->watched())) { + /* Fire watchpoints, if any. */ + WatchpointMap *wpmap = cx->compartment->watchpointMap; + if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) + return false; + + /* A watchpoint handler may set *vp to a non-function value. */ + defineHow &= ~DNP_SET_METHOD; + } + + if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop)) + return false; if (prop) { if (!pobj->isNative()) { if (pobj->isProxy()) { AutoPropertyDescriptorRooter pd(cx); - if (!JSProxy::getPropertyDescriptor(cx, pobj, id, true, &pd)) + if (!Proxy::getPropertyDescriptor(cx, pobj, id, true, &pd)) return false; if ((pd.attrs & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) { @@ -5575,9 +5492,9 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, /* We should never add properties to lexical blocks. */ JS_ASSERT(!obj->isBlock()); - if (!obj->getParent() && - (defineHow & JSDNP_UNQUALIFIED) && - !js_CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) { + if (obj->isGlobal() && + (defineHow & DNP_UNQUALIFIED) && + !js::CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) { return JS_FALSE; } } @@ -5603,8 +5520,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, JS_ASSERT(shape->isDataDescriptor()); if (!shape->writable()) { - PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++); - /* Error in strict mode code, warn with strict option, otherwise do nothing. */ if (strict) return obj->reportReadOnly(cx, id); @@ -5620,8 +5535,14 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, * We found id in a prototype object: prepare to share or shadow. */ if (!shape->shadowable()) { - if (defineHow & JSDNP_CACHE_RESULT) - JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, shape); + if (defineHow & DNP_SET_METHOD) { + JS_ASSERT(!shape->isMethod()); + if (!CloneFunctionForSetMethod(cx, vp)) + return false; + } + + if (defineHow & DNP_CACHE_RESULT) + JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, pobj, shape); if (shape->hasDefaultSetter() && !shape->hasGetterValue()) return JS_TRUE; @@ -5645,10 +5566,10 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, * about to create in obj. */ if (!shape->hasSlot()) { - defineHow &= ~JSDNP_SET_METHOD; + defineHow &= ~DNP_SET_METHOD; if (shape->hasShortID()) { flags = Shape::HAS_SHORTID; - shortid = shape->shortid; + shortid = shape->shortid(); } attrs &= ~JSPROP_SHARED; getter = shape->getter(); @@ -5665,10 +5586,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, shape = NULL; } - JS_ASSERT_IF(shape && shape->isMethod(), pobj->hasMethodBarrier()); - JS_ASSERT_IF(shape && shape->isMethod(), - pobj->getSlot(shape->slot).toObject() == shape->methodObject()); - if (shape && (defineHow & JSDNP_SET_METHOD)) { + if (shape && (defineHow & DNP_SET_METHOD)) { /* * JSOP_SETMETHOD is assigning to an existing own property. If it * is an identical method property, do nothing. Otherwise downgrade @@ -5676,22 +5594,16 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, * cache, as the interpreter has no fast path for these unusual * cases. */ - bool identical = shape->isMethod() && shape->methodObject() == vp->toObject(); - if (!identical) { + if (shape->isMethod()) { + if (obj->nativeGetMethod(shape) == &vp->toObject()) + return true; shape = obj->methodShapeChange(cx, *shape); if (!shape) - return false; - - JSObject *funobj = &vp->toObject(); - JSFunction *fun = funobj->getFunctionPrivate(); - if (fun == funobj) { - funobj = CloneFunctionObject(cx, fun, fun->parent); - if (!funobj) - return JS_FALSE; - vp->setObject(*funobj); - } + return false; } - return identical || js_NativeSet(cx, obj, shape, false, strict, vp); + if (!CloneFunctionForSetMethod(cx, vp)) + return false; + return js_NativeSet(cx, obj, shape, false, strict, vp); } } @@ -5710,26 +5622,20 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, * Purge the property cache of now-shadowed id in obj's scope chain. * Do this early, before locking obj to avoid nesting locks. */ - js_PurgeScopeChain(cx, obj, id); - - /* Find or make a property descriptor with the right heritage. */ - if (!obj->ensureClassReservedSlots(cx)) + if (!js_PurgeScopeChain(cx, obj, id)) return JS_FALSE; /* * Check for Object class here to avoid defining a method on a class * with magic resolve, addProperty, getProperty, etc. hooks. */ - if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) { + if ((defineHow & DNP_SET_METHOD) && obj->canHaveMethodBarrier()) { JS_ASSERT(IsFunctionObject(*vp)); JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); JSObject *funobj = &vp->toObject(); - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); - if (fun == funobj) { + if (!funobj->toFunction()->isClonedMethod()) flags |= Shape::METHOD; - getter = CastAsPropertyOp(funobj); - } } shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT, @@ -5737,16 +5643,13 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, if (!shape) return JS_FALSE; - if (defineHow & JSDNP_CACHE_RESULT) - TRACE_1(AddProperty, obj); - /* * Initialize the new property value (passed to setter) to undefined. * Note that we store before calling addProperty, to match the order - * in js_DefineNativeProperty. + * in DefineNativeProperty. */ - if (obj->containsSlot(shape->slot)) - obj->nativeSetSlot(shape->slot, UndefinedValue()); + if (shape->hasSlot()) + obj->nativeSetSlot(shape->slot(), UndefinedValue()); /* XXXbe called with obj locked */ if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) { @@ -5756,21 +5659,20 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, added = true; } - if (defineHow & JSDNP_CACHE_RESULT) - JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added); + if ((defineHow & DNP_CACHE_RESULT) && !added) + JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape); return js_NativeSet(cx, obj, shape, added, strict, vp); - -#ifdef JS_TRACER - error: // TRACE_1 jumps here in case of error. - return JS_FALSE; -#endif } JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, uintN defineHow, + Value *vp, JSBool strict) { - return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return js_SetPropertyHelper(cx, obj, id, defineHow, vp, strict); } JSBool @@ -5784,7 +5686,25 @@ js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) return true; } if (!obj->isNative()) - return obj->getAttributes(cx, id, attrsp); + return obj->getGenericAttributes(cx, id, attrsp); + + const Shape *shape = (Shape *)prop; + *attrsp = shape->attributes(); + return true; +} + +JSBool +js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JSProperty *prop; + if (!js_LookupElement(cx, obj, index, &obj, &prop)) + return false; + if (!prop) { + *attrsp = 0; + return true; + } + if (!obj->isNative()) + return obj->getElementAttributes(cx, index, attrsp); const Shape *shape = (Shape *)prop; *attrsp = shape->attributes(); @@ -5809,11 +5729,24 @@ js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) return true; return obj->isNative() ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp) - : obj->setAttributes(cx, id, attrsp); + : obj->setGenericAttributes(cx, id, attrsp); +} + +JSBool +js_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JSProperty *prop; + if (!js_LookupElement(cx, obj, index, &obj, &prop)) + return false; + if (!prop) + return true; + return obj->isNative() + ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp) + : obj->setElementAttributes(cx, index, attrsp); } JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) +js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) { JSObject *proto; JSProperty *prop; @@ -5828,25 +5761,8 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str return false; if (!prop || proto != obj) { /* - * If the property was found in a native prototype, check whether it's - * shared and permanent. Such a property stands for direct properties - * in all delegating objects, matching ECMA semantics without bloating - * each delegating object. - */ - if (prop && proto->isNative()) { - shape = (Shape *)prop; - if (shape->isSharedPermanent()) { - if (strict) - return obj->reportNotConfigurable(cx, id); - rval->setBoolean(false); - return true; - } - } - - /* - * If no property, or the property comes unshared or impermanent from - * a prototype, call the class's delProperty hook, passing rval as the - * result parameter. + * If no property, or the property comes from a prototype, call the + * class's delProperty hook, passing rval as the result parameter. */ return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval); } @@ -5859,12 +5775,9 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str return true; } - if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, SHAPE_USERID(shape), rval)) - return false; - - if (obj->containsSlot(shape->slot)) { - const Value &v = obj->nativeGetSlot(shape->slot); - GC_POKE(cx, v); + if (shape->hasSlot()) { + const Value &v = obj->nativeGetSlot(shape->slot()); + GCPoke(cx->runtime, v); /* * Delete is rare enough that we can take the hit of checking for an @@ -5877,150 +5790,167 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str * so the only way they could have the method's joined function object * as callee is through an API abusage. We break any such edge case. */ - if (obj->hasMethodBarrier()) { - JSObject *funobj; - - if (IsFunctionObject(v, &funobj)) { - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); - - if (fun != funobj) { - for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) { - if (fp->isFunctionFrame() && - fp->callee() == fun->compiledFunObj() && - fp->thisValue().isObject()) - { - JSObject *tmp = &fp->thisValue().toObject(); - do { - if (tmp == obj) { - fp->calleev().setObject(*funobj); - break; - } - } while ((tmp = tmp->getProto()) != NULL); + JSFunction *fun; + if (IsFunctionObject(v, &fun) && fun->isClonedMethod()) { + for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) { + if (fp->isFunctionFrame() && + fp->fun()->script() == fun->script() && + fp->thisValue().isObject()) + { + JSObject *tmp = &fp->thisValue().toObject(); + do { + if (tmp == obj) { + fp->overwriteCallee(*fun); + break; } - } + } while ((tmp = tmp->getProto()) != NULL); } } } } + if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, shape->getUserId(), rval)) + return false; + if (rval->isFalse()) + return true; + return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id); } +JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict) +{ + return js_DeleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict); +} + +JSBool +js_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return js_DeleteGeneric(cx, obj, id, rval, strict); +} + +JSBool +js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) +{ + return js_DeleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict); +} + namespace js { -JSObject * -HasNativeMethod(JSObject *obj, jsid methodid, Native native) +bool +HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, Value *vp) { - const Shape *shape = obj->nativeLookup(methodid); - if (!shape || !shape->hasDefaultGetter() || !obj->containsSlot(shape->slot)) - return NULL; + if (const Shape *shape = obj->nativeLookup(cx, methodid)) { + if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) { + *vp = obj->nativeGetSlot(shape->slot()); + return true; + } + } - const Value &fval = obj->nativeGetSlot(shape->slot); - JSObject *funobj; - if (!IsFunctionObject(fval, &funobj) || funobj->getFunctionPrivate()->maybeNative() != native) - return NULL; + return false; +} - return funobj; +/* + * Gets |obj[id]|. If that value's not callable, returns true and stores a + * non-primitive value in *vp. If it's callable, calls it with no arguments + * and |obj| as |this|, returning the result in *vp. + * + * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2 + * or steps 3-4. + */ +static bool +MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp) +{ + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, vp)) + return false; + if (!js_IsCallable(*vp)) { + *vp = ObjectValue(*obj); + return true; + } + return Invoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp); } -bool +JSBool DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) { - JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION); + JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); + JS_ASSERT(!obj->isXML()); - Value v = ObjectValue(*obj); + Class *clasp = obj->getClass(); if (hint == JSTYPE_STRING) { /* Optimize (new String(...)).toString(). */ - if (obj->getClass() == &js_StringClass && + if (clasp == &StringClass && ClassMethodIsNative(cx, obj, - &js_StringClass, + &StringClass, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { *vp = obj->getPrimitiveThis(); return true; } - Value fval; - jsid id = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) + if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp)) return false; - if (js_IsCallable(fval)) { - if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, &v)) - return false; - if (v.isPrimitive()) { - *vp = v; - return true; - } - } + if (vp->isPrimitive()) + return true; - if (!obj->getClass()->convert(cx, obj, hint, &v)) + if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp)) return false; + if (vp->isPrimitive()) + return true; } else { /* Optimize (new String(...)).valueOf(). */ - Class *clasp = obj->getClass(); - if ((clasp == &js_StringClass && - ClassMethodIsNative(cx, obj, &js_StringClass, + if ((clasp == &StringClass && + ClassMethodIsNative(cx, obj, &StringClass, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), js_str_toString)) || - (clasp == &js_NumberClass && - ClassMethodIsNative(cx, obj, &js_NumberClass, + (clasp == &NumberClass && + ClassMethodIsNative(cx, obj, &NumberClass, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), js_num_valueOf))) { *vp = obj->getPrimitiveThis(); return true; } - if (!obj->getClass()->convert(cx, obj, hint, &v)) + if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp)) return false; - if (v.isObject()) { - JS_ASSERT(hint != TypeOfValue(cx, v)); - Value fval; - jsid id = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) - return false; - if (js_IsCallable(fval)) { - if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, &v)) - return false; - if (v.isPrimitive()) { - *vp = v; - return true; - } - } - } + if (vp->isPrimitive()) + return true; + + if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp)) + return false; + if (vp->isPrimitive()) + return true; } - if (v.isObject()) { - /* Avoid recursive death when decompiling in js_ReportValueError. */ - JSString *str; - if (hint == JSTYPE_STRING) { - str = JS_InternString(cx, obj->getClass()->name); - if (!str) - return false; - } else { - str = NULL; - } - vp->setObject(*obj); - js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, - JSDVG_SEARCH_STACK, *vp, str, - (hint == JSTYPE_VOID) - ? "primitive type" - : JS_TYPE_STR(hint)); - return false; + + /* Avoid recursive death when decompiling in js_ReportValueError. */ + JSString *str; + if (hint == JSTYPE_STRING) { + str = JS_InternString(cx, clasp->name); + if (!str) + return false; + } else { + str = NULL; } - *vp = v; - return true; + + js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, ObjectValue(*obj), str, + (hint == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(hint)); + return false; } } /* namespace js */ JS_FRIEND_API(JSBool) -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp) +JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp) { /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */ Class *clasp = obj->getClass(); JSEnumerateOp enumerate = clasp->enumerate; if (clasp->flags & JSCLASS_NEW_ENUMERATE) { JS_ASSERT(enumerate != JS_EnumerateStub); - return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); + return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); } if (!enumerate(cx, obj)) @@ -6044,9 +5974,9 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, Class *clasp; const Shape *shape; JSSecurityCallbacks *callbacks; - CheckAccessOp check; + JSCheckAccessOp check; - while (JS_UNLIKELY(obj->getClass() == &js_WithClass)) + while (JS_UNLIKELY(obj->isWith())) obj = obj->getProto(); writing = (mode & JSACC_WRITE) != 0; @@ -6066,7 +5996,7 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, break; default: - if (!obj->lookupProperty(cx, id, &pobj, &prop)) + if (!obj->lookupGeneric(cx, id, &pobj, &prop)) return JS_FALSE; if (!prop) { if (!writing) @@ -6087,13 +6017,15 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, shape = (Shape *)prop; *attrsp = shape->attributes(); if (!writing) { - if (pobj->containsSlot(shape->slot)) - *vp = pobj->nativeGetSlot(shape->slot); + if (shape->hasSlot()) + *vp = pobj->nativeGetSlot(shape->slot()); else vp->setUndefined(); } } + JS_ASSERT_IF(*attrsp & JSPROP_READONLY, !(*attrsp & (JSPROP_GETTER | JSPROP_SETTER))); + /* * If obj's class has a stub (null) checkAccess hook, use the per-runtime * checkObjectAccess callback, if configured. @@ -6110,7 +6042,7 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, check = clasp->checkAccess; if (!check) { callbacks = JS_GetSecurityCallbacks(cx); - check = callbacks ? Valueify(callbacks->checkObjectAccess) : NULL; + check = callbacks ? callbacks->checkObjectAccess : NULL; } return !check || check(cx, pobj, id, mode, vp); } @@ -6146,7 +6078,7 @@ js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey, if (IsFunctionObject(v)) { JSObject *ctor = &v.toObject(); - if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v)) + if (!ctor->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &v)) return false; } @@ -6162,71 +6094,40 @@ JSBool js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey, JSObject **protop, Class *clasp) { - VOUCH_DOES_NOT_REQUIRE_STACK(); JS_ASSERT(JSProto_Null <= protoKey); JS_ASSERT(protoKey < JSProto_LIMIT); if (protoKey != JSProto_Null) { - if (!scopeobj) { - if (cx->running()) - scopeobj = &cx->fp()->scopeChain(); - if (!scopeobj) { - scopeobj = cx->globalObject; - if (!scopeobj) { - *protop = NULL; - return true; - } - } - } - scopeobj = scopeobj->getGlobal(); - if (scopeobj->isGlobal()) { - const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey); - if (v.isObject()) { - *protop = &v.toObject(); + GlobalObject *global; + if (scopeobj) { + global = &scopeobj->global(); + } else { + global = GetCurrentGlobal(cx); + if (!global) { + *protop = NULL; return true; } } + const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey); + if (v.isObject()) { + *protop = &v.toObject(); + return true; + } } return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp); } -JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs) -{ - /* - * Use the given attributes for the prototype property of the constructor, - * as user-defined constructors have a DontDelete prototype (which may be - * reset), while native or "system" constructors have DontEnum | ReadOnly | - * DontDelete. - */ - if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - ObjectOrNullValue(proto), PropertyStub, StrictPropertyStub, attrs)) { - return JS_FALSE; - } - - /* - * ECMA says that Object.prototype.constructor, or f.prototype.constructor - * for a user-defined function f, is DontEnum. - */ - return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - ObjectOrNullValue(ctor), PropertyStub, StrictPropertyStub, 0); -} - JSObject * PrimitiveToObject(JSContext *cx, const Value &v) { if (v.isString()) return StringObject::create(cx, v.toString()); + if (v.isNumber()) + return NumberObject::create(cx, v.toNumber()); - JS_ASSERT(v.isNumber() || v.isBoolean()); - Class *clasp = v.isNumber() ? &js_NumberClass : &js_BooleanClass; - JSObject *obj = NewBuiltinClassInstance(cx, clasp); - if (!obj) - return NULL; - - obj->setPrimitiveThis(v); - return obj; + JS_ASSERT(v.isBoolean()); + return BooleanObject::create(cx, v.toBoolean()); } JSBool @@ -6293,26 +6194,6 @@ js_ValueToNonNullObject(JSContext *cx, const Value &v) return obj; } -JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval) -{ - Value fval; - jsid id = ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom); - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) - return false; - if (js_IsCallable(fval)) { - Value v; - Value argv[] = { StringValue(cx->runtime->atomState.typeAtoms[type]) }; - if (!ExternalInvoke(cx, ObjectValue(*obj), fval, JS_ARRAY_LENGTH(argv), argv, &v)) - return false; - if (v.isPrimitive()) { - *rval = v; - return true; - } - } - return true; -} - #if JS_HAS_XDR JSBool @@ -6321,7 +6202,7 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) JSContext *cx; JSAtom *atom; Class *clasp; - uint32 classId, classDef; + uint32_t classId, classDef; JSProtoKey protoKey; JSObject *proto; @@ -6338,7 +6219,7 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) if (protoKey != JSProto_Null) { classDef |= (protoKey << 1); } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + atom = js_Atomize(cx, clasp->name, strlen(clasp->name)); if (!atom) return JS_FALSE; } @@ -6395,45 +6276,6 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) #endif /* JS_HAS_XDR */ -#ifdef JS_DUMP_SCOPE_METERS - -#include - -JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS; - -namespace js { - -void -MeterEntryCount(uintN count) -{ - JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count); -} - -} - -void -js_DumpScopeMeters(JSRuntime *rt) -{ - static FILE *logfp; - if (!logfp) - logfp = fopen("/tmp/scope.stats", "a"); - - { - double mean, sigma; - - mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma); - - fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u", - js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma, - js_entry_count_bs.max); - } - - JS_DumpHistogram(&js_entry_count_bs, logfp); - JS_BASIC_STATS_INIT(&js_entry_count_bs); - fflush(logfp); -} -#endif - #ifdef DEBUG void js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) @@ -6441,15 +6283,13 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName); JSObject *obj = (JSObject *)trc->debugPrintArg; - uint32 slot = (uint32)trc->debugPrintIndex; + uint32_t slot = uint32_t(trc->debugPrintIndex); const Shape *shape; if (obj->isNative()) { shape = obj->lastProperty(); - while (shape->previous() && shape->slot != slot) + while (shape && (!shape->hasSlot() || shape->slot() != slot)) shape = shape->previous(); - if (shape->slot != slot) - shape = NULL; } else { shape = NULL; } @@ -6468,11 +6308,11 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) else JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); } else { - jsid id = shape->id; - if (JSID_IS_INT(id)) { - JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id)); - } else if (JSID_IS_ATOM(id)) { - PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); + jsid propid = shape->propid(); + if (JSID_IS_INT(propid)) { + JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid)); + } else if (JSID_IS_ATOM(propid)) { + PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0); } else { JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); } @@ -6496,7 +6336,7 @@ js_ClearNative(JSContext *cx, JSObject *obj) { /* Remove all configurable properties from obj. */ while (const Shape *shape = LastConfigurableShape(obj)) { - if (!obj->removeProperty(cx, shape->id)) + if (!obj->removeProperty(cx, shape->propid())) return false; } @@ -6506,56 +6346,16 @@ js_ClearNative(JSContext *cx, JSObject *obj) if (shape->isDataDescriptor() && shape->writable() && shape->hasDefaultSetter() && - obj->containsSlot(shape->slot)) { - obj->setSlot(shape->slot, UndefinedValue()); + shape->hasSlot()) { + obj->nativeSetSlot(shape->slot(), UndefinedValue()); } } return true; } -bool -js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp) -{ - if (!obj->isNative()) { - vp->setUndefined(); - return true; - } - - if (slot < obj->numSlots()) - *vp = obj->getSlot(slot); - else - vp->setUndefined(); - return true; -} - -bool -js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v) -{ - if (!obj->isNative()) - return true; - - Class *clasp = obj->getClass(); - - if (slot >= obj->numSlots()) { - uint32 nslots = JSSLOT_FREE(clasp); - JS_ASSERT(slot < nslots); - if (!obj->allocSlots(cx, nslots)) - return false; - } - - obj->setSlot(slot, v); - GC_POKE(cx, JS_NULL); - return true; -} - -GlobalObject * -JSObject::getGlobal() const -{ - JSObject *obj = const_cast(this); - while (JSObject *parent = obj->getParent()) - obj = parent; - return obj->asGlobal(); -} +static ObjectElements emptyObjectHeader(0, 0); +HeapValue *js::emptyObjectElements = + (HeapValue *) (uintptr_t(&emptyObjectHeader) + sizeof(ObjectElements)); JSBool js_ReportGetterOnlyAssignment(JSContext *cx) @@ -6574,75 +6374,56 @@ js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, return JS_FALSE; } -#ifdef DEBUG - -/* - * Routines to print out values during debugging. These are FRIEND_API to help - * the debugger find them and to support temporarily hacking js_Dump* calls - * into other code. - */ - void -dumpChars(const jschar *s, size_t n) +js::ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp) { - size_t i; + Value &thisv = call.thisv(); - if (n == (size_t) -1) { - while (s[++n]) ; +#ifdef DEBUG + if (thisv.isObject()) { + JS_ASSERT(thisv.toObject().getClass() != clasp || + !thisv.toObject().getProto() || + thisv.toObject().getProto()->getClass() != clasp); + } else if (thisv.isString()) { + JS_ASSERT(clasp != &StringClass); + } else if (thisv.isNumber()) { + JS_ASSERT(clasp != &NumberClass); + } else if (thisv.isBoolean()) { + JS_ASSERT(clasp != &BooleanClass); + } else { + JS_ASSERT(thisv.isUndefined() || thisv.isNull()); } +#endif - fputc('"', stderr); - for (i = 0; i < n; i++) { - if (s[i] == '\n') - fprintf(stderr, "\\n"); - else if (s[i] == '\t') - fprintf(stderr, "\\t"); - else if (s[i] >= 32 && s[i] < 127) - fputc(s[i], stderr); - else if (s[i] <= 255) - fprintf(stderr, "\\x%02x", (unsigned int) s[i]); - else - fprintf(stderr, "\\u%04x", (unsigned int) s[i]); + if (JSFunction *fun = js_ValueToFunction(cx, &call.calleev(), 0)) { + JSAutoByteString funNameBytes; + if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, + clasp->name, funName, InformalValueTypeName(thisv)); + } } - fputc('"', stderr); } -JS_FRIEND_API(void) -js_DumpChars(const jschar *s, size_t n) +bool +js::HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native native, Class *clasp) { - fprintf(stderr, "jschar * (%p) = ", (void *) s); - dumpChars(s, n); - fputc('\n', stderr); -} + if (args.thisv().isObject()) { + JSObject &thisObj = args.thisv().toObject(); + if (thisObj.isProxy()) + return Proxy::nativeCall(cx, &thisObj, clasp, native, args); + } -void -dumpString(JSString *str) -{ - if (const jschar *chars = str->getChars(NULL)) - dumpChars(chars, str->length()); - else - fprintf(stderr, "(oom in dumpString)"); + ReportIncompatibleMethod(cx, args, clasp); + return false; } -JS_FRIEND_API(void) -js_DumpString(JSString *str) -{ - if (const jschar *chars = str->getChars(NULL)) { - fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", - (void *) str, (void *) chars); - dumpString(str); - } else { - fprintf(stderr, "(oom in JS_DumpString)"); - } - fputc('\n', stderr); -} +#ifdef DEBUG -JS_FRIEND_API(void) -js_DumpAtom(JSAtom *atom) -{ - fprintf(stderr, "JSAtom* (%p) = ", (void *) atom); - js_DumpString(atom); -} +/* + * Routines to print out values during debugging. These are FRIEND_API to help + * the debugger find them and to support temporarily hacking js_Dump* calls + * into other code. + */ void dumpValue(const Value &v) @@ -6656,10 +6437,9 @@ dumpValue(const Value &v) else if (v.isDouble()) fprintf(stderr, "%g", v.toDouble()); else if (v.isString()) - dumpString(v.toString()); + v.toString()->dump(); else if (v.isObject() && v.toObject().isFunction()) { - JSObject *funobj = &v.toObject(); - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + JSFunction *fun = v.toObject().toFunction(); if (fun->atom) { fputs("atom, 0); @@ -6671,13 +6451,13 @@ dumpValue(const Value &v) fprintf(stderr, " (%s:%u)", script->filename ? script->filename : "", script->lineno); } - fprintf(stderr, " at %p (JSFunction at %p)>", (void *) funobj, (void *) fun); + fprintf(stderr, " at %p>", (void *) fun); } else if (v.isObject()) { JSObject *obj = &v.toObject(); Class *clasp = obj->getClass(); fprintf(stderr, "<%s%s at %p>", clasp->name, - (clasp == &js_ObjectClass) ? "" : " object", + (clasp == &ObjectClass) ? "" : " object", (void *) obj); } else if (v.isBoolean()) { if (v.toBoolean()) @@ -6720,16 +6500,15 @@ js_DumpId(jsid id) static void DumpProperty(JSObject *obj, const Shape &shape) { - jsid id = shape.id; - uint8 attrs = shape.attributes(); + jsid id = shape.propid(); + uint8_t attrs = shape.attributes(); fprintf(stderr, " ((Shape *) %p) ", (void *) &shape); if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate "); if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly "); if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent "); if (attrs & JSPROP_SHARED) fprintf(stderr, "shared "); - if (shape.isAlias()) fprintf(stderr, "alias "); - if (shape.isMethod()) fprintf(stderr, "method=%p ", (void *) &shape.methodObject()); + if (shape.isMethod()) fprintf(stderr, "method "); if (shape.hasGetterValue()) fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject()); @@ -6738,63 +6517,51 @@ DumpProperty(JSObject *obj, const Shape &shape) if (shape.hasSetterValue()) fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject()); - else if (shape.setterOp() == js_watch_set) - fprintf(stderr, "setterOp=js_watch_set "); else if (!shape.hasDefaultSetter()) fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp())); if (JSID_IS_ATOM(id)) - dumpString(JSID_TO_STRING(id)); + JSID_TO_STRING(id)->dump(); else if (JSID_IS_INT(id)) fprintf(stderr, "%d", (int) JSID_TO_INT(id)); else fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id)); - fprintf(stderr, ": slot %d", shape.slot); - if (obj->containsSlot(shape.slot)) { + + uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT; + fprintf(stderr, ": slot %d", slot); + if (shape.hasSlot()) { fprintf(stderr, " = "); - dumpValue(obj->getSlot(shape.slot)); - } else if (shape.slot != SHAPE_INVALID_SLOT) { + dumpValue(obj->getSlot(slot)); + } else if (slot != SHAPE_INVALID_SLOT) { fprintf(stderr, " (INVALID!)"); } fprintf(stderr, "\n"); } -JS_FRIEND_API(void) -js_DumpObject(JSObject *obj) +void +JSObject::dump() { + JSObject *obj = this; fprintf(stderr, "object %p\n", (void *) obj); Class *clasp = obj->getClass(); fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name); fprintf(stderr, "flags:"); - uint32 flags = obj->flags; - if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate"); - if (flags & JSObject::SYSTEM) fprintf(stderr, " system"); - if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not_extensible"); - if (flags & JSObject::BRANDED) fprintf(stderr, " branded"); - if (flags & JSObject::GENERIC) fprintf(stderr, " generic"); - if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier"); - if (flags & JSObject::INDEXED) fprintf(stderr, " indexed"); - if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape"); - if (flags & JSObject::HAS_EQUALITY) fprintf(stderr, " has_equality"); - - bool anyFlags = flags != 0; + if (obj->isDelegate()) fprintf(stderr, " delegate"); + if (obj->isSystem()) fprintf(stderr, " system"); + if (!obj->isExtensible()) fprintf(stderr, " not_extensible"); + if (obj->isIndexed()) fprintf(stderr, " indexed"); + if (obj->isNative()) { - if (obj->inDictionaryMode()) { + if (obj->inDictionaryMode()) fprintf(stderr, " inDictionaryMode"); - anyFlags = true; - } - if (obj->hasPropertyTable()) { + if (obj->hasPropertyTable()) fprintf(stderr, " hasPropertyTable"); - anyFlags = true; - } } - if (!anyFlags) - fprintf(stderr, " none"); fprintf(stderr, "\n"); if (obj->isDenseArray()) { - unsigned slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity()); + unsigned slots = obj->getDenseArrayInitializedLength(); fprintf(stderr, "elements\n"); for (unsigned i = 0; i < slots; i++) { fprintf(stderr, " %3d: ", i); @@ -6868,17 +6635,21 @@ JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, StackFrame *start) { /* This should only called during live debugging. */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - if (!start) - start = cx->maybefp(); - FrameRegsIter i(cx); - while (!i.done() && i.fp() != start) - ++i; + FrameRegsIter i(cx, StackIter::GO_THROUGH_SAVED); + if (!start) { + if (i.done()) { + fprintf(stderr, "no stack for cx = %p\n", (void*) cx); + return; + } + } else { + while (!i.done() && i.fp() != start) + ++i; - if (i.done()) { - fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx); - return; + if (i.done()) { + fprintf(stderr, "fp = %p not found in cx = %p\n", + (void *)start, (void *)cx); + return; + } } for (; !i.done(); ++i) { @@ -6903,13 +6674,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start) fprintf(stderr, "*** pc && !script, skipping frame\n\n"); continue; } - if (fp->hasImacropc()) { - fprintf(stderr, " pc in imacro at %p\n called from ", pc); - pc = fp->imacropc(); - } else { - fprintf(stderr, " "); - } - fprintf(stderr, "pc = %p\n", pc); + fprintf(stderr, " pc = %p\n", pc); fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); } Value *sp = i.sp(); @@ -6932,6 +6697,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start) fprintf(stderr, "\n"); } MaybeDumpObject("argsobj", fp->maybeArgsObj()); + MaybeDumpObject("blockChain", fp->maybeBlockChain()); if (!fp->isDummyFrame()) { MaybeDumpValue("this", fp->thisValue()); fprintf(stderr, " rval: "); diff --git a/deps/mozjs/js/src/jsobj.h b/deps/mozjs/js/src/jsobj.h index 0c56922005b..3f9a322e581 100644 --- a/deps/mozjs/js/src/jsobj.h +++ b/deps/mozjs/js/src/jsobj.h @@ -50,22 +50,28 @@ * is reference counted and the slot vector is malloc'ed. */ #include "jsapi.h" +#include "jsatom.h" +#include "jsclass.h" +#include "jsfriendapi.h" +#include "jsinfer.h" #include "jshash.h" #include "jspubtd.h" #include "jsprvtd.h" #include "jslock.h" -#include "jsvalue.h" -#include "jsvector.h" #include "jscell.h" +#include "gc/Barrier.h" +#include "vm/String.h" + namespace js { -class JSProxyHandler; class AutoPropDescArrayRooter; +class ProxyHandler; +class CallObject; +struct GCMarker; +struct NativeIterator; -namespace mjit { -class Compiler; -} +namespace mjit { class Compiler; } static inline PropertyOp CastAsPropertyOp(JSObject *object) @@ -79,18 +85,6 @@ CastAsStrictPropertyOp(JSObject *object) return JS_DATA_TO_FUNC_PTR(StrictPropertyOp, object); } -static inline JSPropertyOp -CastAsJSPropertyOp(JSObject *object) -{ - return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object); -} - -static inline JSStrictPropertyOp -CastAsJSStrictPropertyOp(JSObject *object) -{ - return JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, object); -} - inline JSObject * CastAsObject(PropertyOp op) { @@ -115,6 +109,22 @@ CastAsObjectJsval(StrictPropertyOp op) return ObjectOrNullValue(CastAsObject(op)); } +/* + * JSPropertySpec uses JSAPI JSPropertyOp and JSStrictPropertyOp in function + * signatures, but with JSPROP_NATIVE_ACCESSORS the actual values must be + * JSNatives. To avoid widespread casting, have JS_PSG and JS_PSGS perform + * type-safe casts. + */ +#define JS_PSG(name,getter,flags) \ + {name, 0, (flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS, \ + (JSPropertyOp)getter, NULL} +#define JS_PSGS(name,getter,setter,flags) \ + {name, 0, (flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS, \ + (JSPropertyOp)getter, (JSStrictPropertyOp)setter} +#define JS_PS_END {0, 0, 0, 0, 0} + +/******************************************************************************/ + /* * A representation of ECMA-262 ed. 5's internal Property Descriptor data * structure. @@ -129,7 +139,7 @@ struct PropDesc { js::Value value, get, set; /* Property descriptor boolean fields. */ - uint8 attrs; + uint8_t attrs; /* Bits indicating which values are set. */ bool hasGet : 1; @@ -143,9 +153,29 @@ struct PropDesc { PropDesc(); - public: - /* 8.10.5 ToPropertyDescriptor(Obj) */ - bool initialize(JSContext* cx, const js::Value &v); + /* + * 8.10.5 ToPropertyDescriptor(Obj) + * + * If checkAccessors is false, skip steps 7.b and 8.b, which throw a + * TypeError if .get or .set is neither a callable object nor undefined. + * + * (DebuggerObject_defineProperty uses this: the .get and .set properties + * are expected to be Debugger.Object wrappers of functions, which are not + * themselves callable.) + */ + bool initialize(JSContext* cx, const js::Value &v, bool checkAccessors=true); + + /* + * 8.10.4 FromPropertyDescriptor(Desc) + * + * initFromPropertyDescriptor sets pd to undefined and populates all the + * other fields of this PropDesc from desc. + * + * makeObject populates pd based on the other fields of *this, creating a + * new property descriptor JSObject and defining properties on it. + */ + void initFromPropertyDescriptor(const PropertyDescriptor &desc); + bool makeObject(JSContext *cx); /* 8.10.1 IsAccessorDescriptor(desc) */ bool isAccessorDescriptor() const { @@ -188,49 +218,76 @@ struct PropDesc { return set; } - js::PropertyOp getter() const { + PropertyOp getter() const { return js::CastAsPropertyOp(getterObject()); } - js::StrictPropertyOp setter() const { + StrictPropertyOp setter() const { return js::CastAsStrictPropertyOp(setterObject()); } + + /* + * Throw a TypeError if a getter/setter is present and is neither callable + * nor undefined. These methods do exactly the type checks that are skipped + * by passing false as the checkAccessors parameter of initialize. + */ + inline bool checkGetter(JSContext *cx); + inline bool checkSetter(JSContext *cx); }; typedef Vector PropDescArray; -void -MeterEntryCount(uintN count); - } /* namespace js */ -enum { - INVALID_SHAPE = 0x8fffffff, - SHAPELESS = 0xffffffff -}; - /* - * Unlike js_DefineNativeProperty, propp must be non-null. On success, and if - * id was found, return true with *objp non-null and with a property of *objp - * stored in *propp. If successful but id was not found, return true with both - * *objp and *propp null. + * On success, and if id was found, return true with *objp non-null and with a + * property of *objp stored in *propp. If successful but id was not found, + * return true with both *objp and *propp null. */ extern JS_FRIEND_API(JSBool) js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); +namespace js { + +inline bool +LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, + JSObject **objp, JSProperty **propp) +{ + return js_LookupProperty(cx, obj, ATOM_TO_JSID(name), objp, propp); +} + +} + +extern JS_FRIEND_API(JSBool) +js_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, + JSObject **objp, JSProperty **propp); + extern JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value *value, - js::PropertyOp getter, js::StrictPropertyOp setter, uintN attrs); + JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); + +extern JSBool +js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const js::Value *value, + JSPropertyOp getter, JSStrictPropertyOp setter, uintN attrs); extern JSBool js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, js::Value *vp); +extern JSBool +js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t, js::Value *vp); + inline JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp) { return js_GetProperty(cx, obj, obj, id, vp); } +inline JSBool +js_GetElement(JSContext *cx, JSObject *obj, uint32_t index, js::Value *vp) +{ + return js_GetElement(cx, obj, obj, index, vp); +} + namespace js { extern JSBool @@ -239,334 +296,372 @@ GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Valu } /* namespace js */ extern JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp, JSBool strict); +js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, + js::Value *vp, JSBool strict); + +namespace js { + +inline bool +SetPropertyHelper(JSContext *cx, JSObject *obj, PropertyName *name, uintN defineHow, + Value *vp, JSBool strict) +{ + return !!js_SetPropertyHelper(cx, obj, ATOM_TO_JSID(name), defineHow, vp, strict); +} + +} /* namespace js */ + +extern JSBool +js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, uintN defineHow, + js::Value *vp, JSBool strict); extern JSBool js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); +extern JSBool +js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); + extern JSBool js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); extern JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *rval, JSBool strict); +js_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); -extern JS_FRIEND_API(JSBool) -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - js::Value *statep, jsid *idp); +extern JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, js::PropertyName *name, js::Value *rval, JSBool strict); + +extern JSBool +js_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, js::Value *rval, JSBool strict); + +extern JSBool +js_DeleteSpecial(JSContext *cx, JSObject *obj, js::SpecialId sid, js::Value *rval, JSBool strict); + +extern JSBool +js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, js::Value *rval, JSBool strict); extern JSType js_TypeOf(JSContext *cx, JSObject *obj); namespace js { -struct NativeIterator; -class RegExp; +/* ES5 8.12.8. */ +extern JSBool +DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); + +extern Class ArrayClass; +extern Class ArrayBufferClass; +extern Class BlockClass; +extern Class BooleanClass; +extern Class CallableObjectClass; +extern Class DateClass; +extern Class ErrorClass; +extern Class ElementIteratorClass; +extern Class GeneratorClass; +extern Class IteratorClass; +extern Class JSONClass; +extern Class MathClass; +extern Class NumberClass; +extern Class NormalArgumentsObjectClass; +extern Class ObjectClass; +extern Class ProxyClass; +extern Class RegExpClass; +extern Class RegExpStaticsClass; +extern Class SlowArrayClass; +extern Class StopIterationClass; +extern Class StringClass; +extern Class StrictArgumentsObjectClass; +extern Class WeakMapClass; +extern Class WithClass; +extern Class XMLFilterClass; + +class ArgumentsObject; +class BlockObject; +class BooleanObject; +class ClonedBlockObject; +class DeclEnvObject; +class ElementIteratorObject; class GlobalObject; +class NestedScopeObject; +class NewObjectCache; +class NormalArgumentsObject; +class NumberObject; +class ScopeObject; +class StaticBlockObject; +class StrictArgumentsObject; class StringObject; +class RegExpObject; +class WithObject; -} +/* + * Header structure for object element arrays. This structure is immediately + * followed by an array of elements, with the elements member in an object + * pointing to the beginning of that array (the end of this structure). + * See below for usage of this structure. + */ +class ObjectElements +{ + friend struct ::JSObject; -struct JSFunction; + /* Number of allocated slots. */ + uint32_t capacity; -namespace nanojit { -class ValidateWriter; -} + /* + * Number of initialized elements. This is <= the capacity, and for arrays + * is <= the length. Memory for elements above the initialized length is + * uninitialized, but values between the initialized length and the proper + * length are conceptually holes. + */ + uint32_t initializedLength; + + /* 'length' property of array objects, unused for other objects. */ + uint32_t length; + + /* :XXX: bug 586842 store state about sparse slots. */ + uint32_t unused; + + void staticAsserts() { + JS_STATIC_ASSERT(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value)); + } + + public: + + ObjectElements(uint32_t capacity, uint32_t length) + : capacity(capacity), initializedLength(0), length(length) + {} + + HeapValue * elements() { return (HeapValue *)(uintptr_t(this) + sizeof(ObjectElements)); } + static ObjectElements * fromElements(HeapValue *elems) { + return (ObjectElements *)(uintptr_t(elems) - sizeof(ObjectElements)); + } + + static int offsetOfCapacity() { + return (int)offsetof(ObjectElements, capacity) - (int)sizeof(ObjectElements); + } + static int offsetOfInitializedLength() { + return (int)offsetof(ObjectElements, initializedLength) - (int)sizeof(ObjectElements); + } + static int offsetOfLength() { + return (int)offsetof(ObjectElements, length) - (int)sizeof(ObjectElements); + } + + static const size_t VALUES_PER_HEADER = 2; +}; + +/* Shared singleton for objects with no elements. */ +extern HeapValue *emptyObjectElements; + +} /* namespace js */ /* - * JSObject struct, with members sized to fit in 32 bytes on 32-bit targets, - * 64 bytes on 64-bit systems. The JSFunction struct is an extension of this - * struct allocated from a larger GC size-class. + * JSObject struct. The JSFunction struct is an extension of this struct + * allocated from a larger GC size-class. + * + * The |shape_| member stores the shape of the object, which includes the + * object's class and the layout of all its properties. + * + * The type member stores the type of the object, which contains its prototype + * object and the possible types of its properties. * - * The clasp member stores the js::Class pointer for this object. We do *not* - * synchronize updates of clasp or flags -- API clients must take care. + * The rest of the object stores its named properties and indexed elements. + * These are stored separately from one another. Objects are followed by an + * variable-sized array of values for inline storage, which may be used by + * either properties of native objects (fixed slots) or by elements. * - * An object is a delegate if it is on another object's prototype (the proto - * field) or scope chain (the parent field), and therefore the delegate might - * be asked implicitly to get or set a property on behalf of another object. - * Delegates may be accessed directly too, as may any object, but only those - * objects linked after the head of any prototype or scope chain are flagged - * as delegates. This definition helps to optimize shape-based property cache - * invalidation (see Purge{Scope,Proto}Chain in jsobj.cpp). + * Two native objects with the same shape are guaranteed to have the same + * number of fixed slots. * - * The meaning of the system object bit is defined by the API client. It is - * set in JS_NewSystemObject and is queried by JS_IsSystemObject (jsdbgapi.h), - * but it has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM - * and JS_FlagScriptFilenamePrefix (also exported via jsdbgapi.h) are intended - * to be complementary to this bit, but it is up to the API client to implement - * any such association. + * Named property storage can be split between fixed slots and a dynamically + * allocated array (the slots member). For an object with N fixed slots, shapes + * with slots [0..N-1] are stored in the fixed slots, and the remainder are + * stored in the dynamic array. If all properties fit in the fixed slots, the + * 'slots' member is NULL. * - * Both these flag bits are initially zero; they may be set or queried using - * the (is|set)(Delegate|System) inline methods. + * Elements are indexed via the 'elements' member. This member can point to + * either the shared emptyObjectElements singleton, into the inline value array + * (the address of the third value, to leave room for a ObjectElements header; + * in this case numFixedSlots() is zero) or to a dynamically allocated array. * - * The slots member is a pointer to the slot vector for the object. - * This can be either a fixed array allocated immediately after the object, - * or a dynamically allocated array. A dynamic array can be tested for with - * hasSlotsArray(). In all cases, capacity gives the number of usable slots. - * Two objects with the same shape have the same number of fixed slots, - * and either both have or neither have dynamically allocated slot arrays. + * Only certain combinations of properties and elements storage are currently + * possible. This will be changing soon :XXX: bug 586842. * - * If you change this struct, you'll probably need to change the AccSet values - * in jsbuiltins.h. + * - For objects other than arrays and typed arrays, the elements are empty. + * + * - For 'slow' arrays, both elements and properties are used, but the + * elements have zero capacity --- only the length member is used. + * + * - For dense arrays, elements are used and properties are not used. + * + * - For typed array buffers, elements are used and properties are not used. + * The data indexed by the elements do not represent Values, but primitive + * unboxed integers or floating point values. */ -struct JSObject : js::gc::Cell { - /* - * TraceRecorder must be a friend because it generates code that - * manipulates JSObjects, which requires peeking under any encapsulation. - * ValidateWriter must be a friend because it works in tandem with - * TraceRecorder. - */ - friend class js::TraceRecorder; - friend class nanojit::ValidateWriter; - friend class GetPropCompiler; +struct JSObject : js::gc::Cell +{ + private: + friend struct js::Shape; + friend struct js::GCMarker; + friend class js::NewObjectCache; /* - * Private pointer to the last added property and methods to manipulate the - * list it links among properties in this scope. + * Shape of the object, encodes the layout of the object's properties and + * all other information about its structure. See jsscope.h. */ - js::Shape *lastProp; - - js::Class *clasp; - - private: - inline void setLastProperty(const js::Shape *shape); - inline void removeLastProperty(); - - /* For setLastProperty() only. */ - friend class js::StringObject; + js::HeapPtrShape shape_; #ifdef DEBUG void checkShapeConsistency(); #endif + /* + * The object's type and prototype. For objects with the LAZY_TYPE flag + * set, this is the prototype's default 'new' type and can only be used + * to get that prototype. + */ + js::HeapPtrTypeObject type_; + + /* Make the type object to use for LAZY_TYPE objects. */ + void makeLazyType(JSContext *cx); + public: - inline const js::Shape *lastProperty() const; - - inline js::Shape **nativeSearch(jsid id, bool adding = false); - inline const js::Shape *nativeLookup(jsid id); - - inline bool nativeContains(jsid id); - inline bool nativeContains(const js::Shape &shape); - - enum { - DELEGATE = 0x01, - SYSTEM = 0x02, - NOT_EXTENSIBLE = 0x04, - BRANDED = 0x08, - GENERIC = 0x10, - METHOD_BARRIER = 0x20, - INDEXED = 0x40, - OWN_SHAPE = 0x80, - BOUND_FUNCTION = 0x100, - HAS_EQUALITY = 0x200, - METHOD_THRASH_COUNT_MASK = 0xc00, - METHOD_THRASH_COUNT_SHIFT = 10, - METHOD_THRASH_COUNT_MAX = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT - }; + inline js::Shape *lastProperty() const { + JS_ASSERT(shape_); + return shape_; + } /* - * Impose a sane upper bound, originally checked only for dense arrays, on - * number of slots in an object. + * Update the last property, keeping the number of allocated slots in sync + * with the object's new slot span. */ - enum { - NSLOTS_BITS = 29, - NSLOTS_LIMIT = JS_BIT(NSLOTS_BITS) - }; + bool setLastProperty(JSContext *cx, const js::Shape *shape); - uint32 flags; /* flags */ - uint32 objShape; /* copy of lastProp->shape, or override if different */ + /* As above, but does not change the slot span. */ + inline void setLastPropertyInfallible(const js::Shape *shape); - /* If prototype, lazily filled array of empty shapes for each object size. */ - js::EmptyShape **emptyShapes; + /* Make a non-array object with the specified initial state. */ + static inline JSObject *create(JSContext *cx, + js::gc::AllocKind kind, + js::HandleShape shape, + js::HandleTypeObject type, + js::HeapValue *slots); - JSObject *proto; /* object's prototype */ - JSObject *parent; /* object's parent */ - void *privateData; /* private data */ - jsuword capacity; /* capacity of slots */ - js::Value *slots; /* dynamically allocated slots, - or pointer to fixedSlots() */ + /* Make a dense array object with the specified initial state. */ + static inline JSObject *createDenseArray(JSContext *cx, + js::gc::AllocKind kind, + js::HandleShape shape, + js::HandleTypeObject type, + uint32_t length); /* - * Return an immutable, shareable, empty shape with the same clasp as this - * and the same slotSpan as this had when empty. - * - * If |this| is the scope of an object |proto|, the resulting scope can be - * used as the scope of a new object whose prototype is |proto|. + * Remove the last property of an object, provided that it is safe to do so + * (the shape and previous shape do not carry conflicting information about + * the object itself). */ - inline bool canProvideEmptyShape(js::Class *clasp); - inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind); + inline void removeLastProperty(JSContext *cx); + inline bool canRemoveLastProperty(); - inline bool isNative() const; - inline bool isNewborn() const; + /* + * Update the slot span directly for a dictionary object, and allocate + * slots to cover the new span if necessary. + */ + bool setSlotSpan(JSContext *cx, uint32_t span); - js::Class *getClass() const { return clasp; } - JSClass *getJSClass() const { return Jsvalify(clasp); } + static inline size_t offsetOfShape() { return offsetof(JSObject, shape_); } + inline js::HeapPtrShape *addressOfShape() { return &shape_; } - bool hasClass(const js::Class *c) const { - return c == clasp; - } + const js::Shape *nativeLookup(JSContext *cx, jsid id); - const js::ObjectOps *getOps() const { - return &getClass()->ops; - } + inline bool nativeContains(JSContext *cx, jsid id); + inline bool nativeContains(JSContext *cx, const js::Shape &shape); - uint32 shape() const { - JS_ASSERT(objShape != INVALID_SHAPE); - return objShape; - } + /* Upper bound on the number of elements in an object. */ + static const uint32_t NELEMENTS_LIMIT = JS_BIT(28); - bool isDelegate() const { return !!(flags & DELEGATE); } - void setDelegate() { flags |= DELEGATE; } - void clearDelegate() { flags &= ~DELEGATE; } + private: + js::HeapValue *slots; /* Slots for object properties. */ + js::HeapValue *elements; /* Slots for object elements. */ - bool isBoundFunction() const { return !!(flags & BOUND_FUNCTION); } + public: - static void setDelegateNullSafe(JSObject *obj) { - if (obj) - obj->setDelegate(); - } + inline bool isNative() const; - bool isSystem() const { return !!(flags & SYSTEM); } - void setSystem() { flags |= SYSTEM; } + inline js::Class *getClass() const; + inline JSClass *getJSClass() const; + inline bool hasClass(const js::Class *c) const; + inline const js::ObjectOps *getOps() const; /* - * A branded object contains plain old methods (function-valued properties - * without magic getters and setters), and its shape evolves whenever a - * function value changes. + * An object is a delegate if it is on another object's prototype or scope + * chain, and therefore the delegate might be asked implicitly to get or + * set a property on behalf of another object. Delegates may be accessed + * directly too, as may any object, but only those objects linked after the + * head of any prototype or scope chain are flagged as delegates. This + * definition helps to optimize shape-based property cache invalidation + * (see Purge{Scope,Proto}Chain in jsobj.cpp). */ - bool branded() { return !!(flags & BRANDED); } + inline bool isDelegate() const; + inline bool setDelegate(JSContext *cx); + + inline bool isBoundFunction() const; /* - * NB: these return false on shape overflow but do not report any error. - * Callers who depend on shape guarantees should therefore bail off trace, - * e.g., on false returns. + * The meaning of the system object bit is defined by the API client. It is + * set in JS_NewSystemObject and is queried by JS_IsSystemObject, but it + * has no intrinsic meaning to SpiderMonkey. */ - bool brand(JSContext *cx); - bool unbrand(JSContext *cx); + inline bool isSystem() const; + inline bool setSystem(JSContext *cx); - bool generic() { return !!(flags & GENERIC); } - void setGeneric() { flags |= GENERIC; } + inline bool hasSpecialEquality() const; - uintN getMethodThrashCount() const { - return (flags & METHOD_THRASH_COUNT_MASK) >> METHOD_THRASH_COUNT_SHIFT; - } + inline bool watched() const; + inline bool setWatched(JSContext *cx); - void setMethodThrashCount(uintN count) { - JS_ASSERT(count <= METHOD_THRASH_COUNT_MAX); - flags = (flags & ~METHOD_THRASH_COUNT_MASK) | (count << METHOD_THRASH_COUNT_SHIFT); - } + /* See StackFrame::varObj. */ + inline bool isVarObj() const; + inline bool setVarObj(JSContext *cx); - bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); } - void assertSpecialEqualitySynced() const { - JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality()); - } + /* + * Objects with an uncacheable proto can have their prototype mutated + * without inducing a shape change on the object. Property cache entries + * and JIT inline caches should not be filled for lookups across prototype + * lookups on the object. + */ + inline bool hasUncacheableProto() const; + inline bool setUncacheableProto(JSContext *cx); - /* Sets an object's HAS_EQUALITY flag based on its clasp. */ - inline void syncSpecialEquality(); + bool generateOwnShape(JSContext *cx, js::Shape *newShape = NULL) { + return replaceWithNewEquivalentShape(cx, lastProperty(), newShape); + } private: - void generateOwnShape(JSContext *cx); + js::Shape *replaceWithNewEquivalentShape(JSContext *cx, js::Shape *existingShape, + js::Shape *newShape = NULL); - inline void setOwnShape(uint32 s); - inline void clearOwnShape(); + enum GenerateShape { + GENERATE_NONE, + GENERATE_SHAPE + }; + + bool setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag, + GenerateShape generateShape = GENERATE_NONE); public: inline bool nativeEmpty() const; - bool hasOwnShape() const { return !!(flags & OWN_SHAPE); } - - inline void setMap(js::Shape *amap); - - inline void setSharedNonNativeMap(); - - /* 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, js::StackFrame *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); - void protoShapeChange(JSContext *cx); - void shadowingShapeChange(JSContext *cx, const js::Shape &shape); - bool globalObjectOwnShapeChange(JSContext *cx); - void watchpointOwnShapeChange(JSContext *cx) { generateOwnShape(cx); } - - void extensibleShapeChange(JSContext *cx) { - /* This will do for now. */ - generateOwnShape(cx); - } - - /* - * A scope has a method barrier when some compiler-created "null closure" - * function objects (functions that do not use lexical bindings above their - * scope, only free variable names) that have a correct JSSLOT_PARENT value - * thanks to the COMPILE_N_GO optimization are stored as newly added direct - * property values of the scope's object. - * - * The de-facto standard JS language requires each evaluation of such a - * closure to result in a unique (according to === and observable effects) - * function object. ES3 tried to allow implementations to "join" such - * objects to a single compiler-created object, but this makes an overt - * mutation hazard, also an "identity hazard" against interoperation among - * implementations that join and do not join. - * - * To stay compatible with the de-facto standard, we store the compiler- - * created function object as the method value and set the METHOD_BARRIER - * flag. - * - * The method value is part of the method property tree node's identity, so - * it effectively brands the scope with a predictable shape corresponding - * to the method value, but without the overhead of setting the BRANDED - * flag, which requires assigning a new shape peculiar to each branded - * scope. Instead the shape is shared via the property tree among all the - * scopes referencing the method property tree node. - * - * Then when reading from a scope for which scope->hasMethodBarrier() is - * true, we count on the scope's qualified/guarded shape being unique and - * add a read barrier that clones the compiler-created function object on - * demand, reshaping the scope. - * - * This read barrier is bypassed when evaluating the callee sub-expression - * of a call expression (see the JOF_CALLOP opcodes in jsopcode.tbl), since - * such ops do not present an identity or mutation hazard. The compiler - * performs this optimization only for null closures that do not use their - * own name or equivalent built-in references (arguments.callee). - * - * The BRANDED write barrier, JSObject::methodWriteBarrer, must check for - * METHOD_BARRIER too, and regenerate this scope's shape if the method's - * value is in fact changing. - */ - bool hasMethodBarrier() { return !!(flags & METHOD_BARRIER); } - void setMethodBarrier() { flags |= METHOD_BARRIER; } - - /* - * Test whether this object may be branded due to method calls, which means - * any assignment to a function-valued property must regenerate shape; else - * test whether this object has method properties, which require a method - * write barrier. - */ - bool brandedOrHasMethodBarrier() { return !!(flags & (BRANDED | METHOD_BARRIER)); } + js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape); + bool shadowingShapeChange(JSContext *cx, const js::Shape &shape); /* * Read barrier to clone a joined function object stored as a method. * Defined in jsobjinlines.h, but not declared inline per standard style in * order to avoid gcc warnings. */ - const js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp); + js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp); - /* - * Write barrier to check for a change of method value. Defined inline in - * jsobjinlines.h after methodReadBarrier. The slot flavor is required by - * JSOP_*GVAR, which deals in slots not shapes, while not deoptimizing to - * map slot to shape unless JSObject::flags show that this is necessary. - * The methodShapeChange overload (above) parallels this. - */ - const js::Shape *methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v); - bool methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v); + /* Whether method shapes can be added to this object. */ + inline bool canHaveMethodBarrier() const; - bool isIndexed() const { return !!(flags & INDEXED); } - void setIndexed() { flags |= INDEXED; } + /* Whether there may be indexed properties on this object. */ + inline bool isIndexed() const; /* * Return true if this object is a native one that has been converted from @@ -575,155 +670,301 @@ struct JSObject : js::gc::Cell { */ inline bool inDictionaryMode() const; - inline uint32 propertyCount() const; + inline uint32_t propertyCount() const; inline bool hasPropertyTable() const; - /* gc::FinalizeKind */ unsigned finalizeKind() const; - - uint32 numSlots() const { return capacity; } + inline size_t sizeOfThis() const; + inline size_t computedSizeOfThisSlotsElements() const; - size_t slotsAndStructSize(uint32 nslots) const; - size_t slotsAndStructSize() const { return slotsAndStructSize(numSlots()); } + inline void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, + size_t *slotsSize, size_t *elementsSize, + size_t *miscSize) const; - inline js::Value* fixedSlots() const; inline size_t numFixedSlots() const; - static inline size_t getFixedSlotOffset(size_t slot); + static const uint32_t MAX_FIXED_SLOTS = 16; + + private: + inline js::HeapValue* fixedSlots() const; + /* + * Get internal pointers to the range of values starting at start and + * running for length. + */ + void getSlotRange(size_t start, size_t length, + js::HeapValue **fixedStart, js::HeapValue **fixedEnd, + js::HeapValue **slotsStart, js::HeapValue **slotsEnd); public: + + /* Accessors for properties. */ + + /* Whether a slot is at a fixed offset from this object. */ + inline bool isFixedSlot(size_t slot); + + /* Index into the dynamic slots array to use for a dynamic slot. */ + inline size_t dynamicSlotIndex(size_t slot); + + /* Get a raw pointer to the object's properties. */ + inline const js::HeapValue *getRawSlots(); + + /* JIT Accessors */ + static inline size_t getFixedSlotOffset(size_t slot); + static inline size_t getPrivateDataOffset(size_t nfixed); + static inline size_t offsetOfSlots() { return offsetof(JSObject, slots); } + /* Minimum size for dynamically allocated slots. */ - static const uint32 SLOT_CAPACITY_MIN = 8; + static const uint32_t SLOT_CAPACITY_MIN = 8; - bool allocSlots(JSContext *cx, size_t nslots); - bool growSlots(JSContext *cx, size_t nslots); - void shrinkSlots(JSContext *cx, size_t nslots); + /* + * Grow or shrink slots immediately before changing the slot span. + * The number of allocated slots is not stored explicitly, and changes to + * the slots must track changes in the slot span. + */ + bool growSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount); + void shrinkSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount); - bool ensureSlots(JSContext *cx, size_t nslots) { - if (numSlots() < nslots) - return growSlots(cx, nslots); - return true; - } + bool hasDynamicSlots() const { return slots != NULL; } /* - * Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp) + - * nreserved slots. - * - * This method may be called only for native objects freshly created using - * NewObject or one of its variant where the new object will both (a) never - * escape to script and (b) never be extended with ad-hoc properties that - * would try to allocate higher slots without the fresh object first having - * its map set to a shape path that maps those slots. - * - * Block objects satisfy (a) and (b), as there is no evil eval-based way to - * add ad-hoc properties to a Block instance. Call objects satisfy (a) and - * (b) as well, because the compiler-created Shape path that covers args, - * vars, and upvars, stored in their callee function in u.i.names, becomes - * their initial map. + * Get the number of dynamic slots to allocate to cover the properties in + * an object with the given number of fixed slots and slot span. The slot + * capacity is not stored explicitly, and the allocated size of the slot + * array is kept in sync with this count. */ - bool ensureInstanceReservedSlots(JSContext *cx, size_t nreserved); + static inline size_t dynamicSlotsCount(size_t nfixed, size_t span); + + /* Compute dynamicSlotsCount() for this object. */ + inline size_t numDynamicSlots() const; + + protected: + inline bool hasContiguousSlots(size_t start, size_t count) const; + + inline void initializeSlotRange(size_t start, size_t count); + inline void invalidateSlotRange(size_t start, size_t count); + inline bool updateSlotsForSpan(JSContext *cx, size_t oldSpan, size_t newSpan); + + public: /* - * Get a direct pointer to the object's slots. - * This can be reallocated if the object is modified, watch out! + * Trigger the write barrier on a range of slots that will no longer be + * reachable. */ - js::Value *getSlots() const { - return slots; - } + inline void prepareSlotRangeForOverwrite(size_t start, size_t end); + inline void prepareElementRangeForOverwrite(size_t start, size_t end); + + /* + * Initialize a flat array of slots to this object at a start slot. The + * caller must ensure that are enough slots. + */ + void initSlotRange(size_t start, const js::Value *vector, size_t length); /* - * NB: ensureClassReservedSlotsForEmptyObject asserts that nativeEmpty() - * Use ensureClassReservedSlots for any object, either empty or already - * extended with properties. + * Copy a flat array of slots to this object at a start slot. Caller must + * ensure there are enough slots in this object. */ - bool ensureClassReservedSlotsForEmptyObject(JSContext *cx); + void copySlotRange(size_t start, const js::Value *vector, size_t length); + + inline uint32_t slotSpan() const; + + void rollbackProperties(JSContext *cx, uint32_t slotSpan); - inline bool ensureClassReservedSlots(JSContext *cx); +#ifdef DEBUG + enum SentinelAllowed { + SENTINEL_NOT_ALLOWED, + SENTINEL_ALLOWED + }; + + /* + * Check that slot is in range for the object's allocated slots. + * If sentinelAllowed then slot may equal the slot capacity. + */ + bool slotInRange(uintN slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const; +#endif - inline uint32 slotSpan() const; + js::HeapValue *getSlotAddressUnchecked(uintN slot) { + size_t fixed = numFixedSlots(); + if (slot < fixed) + return fixedSlots() + slot; + return slots + (slot - fixed); + } - inline bool containsSlot(uint32 slot) const; + js::HeapValue *getSlotAddress(uintN slot) { + /* + * This can be used to get the address of the end of the slots for the + * object, which may be necessary when fetching zero-length arrays of + * slots (e.g. for callObjVarArray). + */ + JS_ASSERT(slotInRange(slot, SENTINEL_ALLOWED)); + return getSlotAddressUnchecked(slot); + } - js::Value& getSlotRef(uintN slot) { - JS_ASSERT(slot < capacity); - return slots[slot]; + js::HeapValue &getSlotRef(uintN slot) { + JS_ASSERT(slotInRange(slot)); + return *getSlotAddress(slot); } - inline js::Value &nativeGetSlotRef(uintN slot); + inline js::HeapValue &nativeGetSlotRef(uintN slot); const js::Value &getSlot(uintN slot) const { - JS_ASSERT(slot < capacity); - return slots[slot]; + JS_ASSERT(slotInRange(slot)); + size_t fixed = numFixedSlots(); + if (slot < fixed) + return fixedSlots()[slot]; + return slots[slot - fixed]; } inline const js::Value &nativeGetSlot(uintN slot) const; + inline JSFunction *nativeGetMethod(const js::Shape *shape) const; - void setSlot(uintN slot, const js::Value &value) { - JS_ASSERT(slot < capacity); - slots[slot] = value; - } + inline void setSlot(uintN slot, const js::Value &value); + inline void initSlot(uintN slot, const js::Value &value); + inline void initSlotUnchecked(uintN slot, const js::Value &value); inline void nativeSetSlot(uintN slot, const js::Value &value); + inline void nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value); - inline js::Value getReservedSlot(uintN index) const; - - /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */ - inline void updateShape(JSContext *cx); - inline void updateFlags(const js::Shape *shape, bool isDefinitelyAtom = false); + inline const js::Value &getReservedSlot(uintN index) const; + inline js::HeapValue &getReservedSlotRef(uintN index); + inline void setReservedSlot(uintN index, const js::Value &v); - /* Extend this object to have shape as its last-added property. */ - inline void extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom = false); + /* For slots which are known to always be fixed, due to the way they are allocated. */ - JSObject *getProto() const { return proto; } - void clearProto() { proto = NULL; } + js::HeapValue &getFixedSlotRef(uintN slot) { + JS_ASSERT(slot < numFixedSlots()); + return fixedSlots()[slot]; + } - void setProto(JSObject *newProto) { -#ifdef DEBUG - for (JSObject *obj = newProto; obj; obj = obj->getProto()) - JS_ASSERT(obj != this); -#endif - setDelegateNullSafe(newProto); - proto = newProto; + const js::Value &getFixedSlot(uintN slot) const { + JS_ASSERT(slot < numFixedSlots()); + return fixedSlots()[slot]; } - JSObject *getParent() const { - return parent; + inline void setFixedSlot(uintN slot, const js::Value &value); + inline void initFixedSlot(uintN slot, const js::Value &value); + + /* + * Whether this is the only object which has its specified type. This + * object will have its type constructed lazily as needed by analysis. + */ + bool hasSingletonType() const { return !!type_->singleton; } + + /* + * Whether the object's type has not been constructed yet. If an object + * might have a lazy type, use getType() below, otherwise type(). + */ + bool hasLazyType() const { return type_->lazy(); } + + /* + * Marks this object as having a singleton type, and leave the type lazy. + * Constructs a new, unique shape for the object. + */ + inline bool setSingletonType(JSContext *cx); + + inline js::types::TypeObject *getType(JSContext *cx); + + js::types::TypeObject *type() const { + JS_ASSERT(!hasLazyType()); + return type_; } - void clearParent() { - parent = NULL; + const js::HeapPtr &typeFromGC() const { + /* Direct field access for use by GC. */ + return type_; } - void setParent(JSObject *newParent) { + static inline size_t offsetOfType() { return offsetof(JSObject, type_); } + inline js::HeapPtrTypeObject *addressOfType() { return &type_; } + + inline void setType(js::types::TypeObject *newType); + + js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL); + #ifdef DEBUG - for (JSObject *obj = newParent; obj; obj = obj->getParent()) - JS_ASSERT(obj != this); + bool hasNewType(js::types::TypeObject *newType); #endif - setDelegateNullSafe(newParent); - parent = newParent; - } - JS_FRIEND_API(js::GlobalObject *) getGlobal() const; + /* + * Mark an object that has been iterated over and is a singleton. We need + * to recover this information in the object's type information after it + * is purged on GC. + */ + inline bool setIteratedSingleton(JSContext *cx); - bool isGlobal() const { - return !!(getClass()->flags & JSCLASS_IS_GLOBAL); - } + /* + * Mark an object as requiring its default 'new' type to have unknown + * properties. + */ + bool setNewTypeUnknown(JSContext *cx); - inline js::GlobalObject *asGlobal(); + /* Set a new prototype for an object with a singleton type. */ + bool splicePrototype(JSContext *cx, JSObject *proto); - void *getPrivate() const { - JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - return privateData; - } + /* + * For bootstrapping, whether to splice a prototype for Function.prototype + * or the global object. + */ + bool shouldSplicePrototype(JSContext *cx); - void setPrivate(void *data) { - JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - privateData = data; + JSObject * getProto() const { + return type_->proto; } + /* + * Parents and scope chains. + * + * All script-accessible objects with a NULL parent are global objects, + * and all global objects have a NULL parent. Some builtin objects which + * are not script-accessible also have a NULL parent, such as parser + * created functions for non-compileAndGo scripts. + * + * Except for the non-script-accessible builtins, the global with which an + * object is associated can be reached by following parent links to that + * global (see global()). + * + * The scope chain of an object is the link in the search path when a + * script does a name lookup on a scope object. For JS internal scope + * objects --- Call, DeclEnv and block --- the chain is stored in + * the first fixed slot of the object, and the object's parent is the + * associated global. For other scope objects, the chain is stored in the + * object's parent. + * + * In compileAndGo code, scope chains can contain only internal scope + * objects with a global object at the root as the scope of the outermost + * non-function script. In non-compileAndGo code, the scope of the + * outermost non-function script might not be a global object, and can have + * a mix of other objects above it before the global object is reached. + */ + + /* Access the parent link of an object. */ + inline JSObject *getParent() const; + bool setParent(JSContext *cx, JSObject *newParent); + + /* + * Get the enclosing scope of an object. When called on non-scope object, + * this will just be the global (the name "enclosing scope" still applies + * in this situation because non-scope objects can be on the scope chain). + */ + inline JSObject *enclosingScope(); + + inline js::GlobalObject &global() const; + + /* Private data accessors. */ + + inline bool hasPrivate() const; + inline void *getPrivate() const; + inline void setPrivate(void *data); + + /* Access private data for an object with a known number of fixed slots. */ + inline void *getPrivate(size_t nfixed) const; + /* N.B. Infallible: NULL means 'no principal', not an error. */ inline JSPrincipals *principals(JSContext *cx); + /* Remove the type (and prototype) or parent from a new object. */ + inline bool clearType(JSContext *cx); + bool clearParent(JSContext *cx); + /* * ES5 meta-object properties and operations. */ @@ -739,216 +980,159 @@ struct JSObject : js::gc::Cell { */ bool sealOrFreeze(JSContext *cx, ImmutabilityType it); + bool isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp); + + static inline uintN getSealedOrFrozenAttributes(uintN attrs, ImmutabilityType it); + + inline void *&privateRef(uint32_t nfixed) const; + public: - bool isExtensible() const { return !(flags & NOT_EXTENSIBLE); } + inline bool isExtensible() const; bool preventExtensions(JSContext *cx, js::AutoIdVector *props); /* ES5 15.2.3.8: non-extensible, all props non-configurable */ inline bool seal(JSContext *cx) { return sealOrFreeze(cx, SEAL); } /* ES5 15.2.3.9: non-extensible, all properties non-configurable, all data props read-only */ bool freeze(JSContext *cx) { return sealOrFreeze(cx, FREEZE); } - + + bool isSealed(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, SEAL, resultp); } + bool isFrozen(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, FREEZE, resultp); } + /* * Primitive-specific getters and setters. */ private: - static const uint32 JSSLOT_PRIMITIVE_THIS = 0; + static const uint32_t JSSLOT_PRIMITIVE_THIS = 0; public: inline const js::Value &getPrimitiveThis() const; inline void setPrimitiveThis(const js::Value &pthis); - public: - inline js::StringObject *asString(); + static size_t getPrimitiveThisOffset() { + /* All primitive objects have their value in a fixed slot. */ + return getFixedSlotOffset(JSSLOT_PRIMITIVE_THIS); + } - /* - * Array-specific getters and setters (for both dense and slow arrays). - */ + /* Accessors for elements. */ - inline uint32 getArrayLength() const; - inline void setArrayLength(uint32 length); + js::ObjectElements *getElementsHeader() const { + return js::ObjectElements::fromElements(elements); + } - inline uint32 getDenseArrayCapacity(); - inline js::Value* getDenseArrayElements(); - inline const js::Value &getDenseArrayElement(uintN idx); - inline js::Value* addressOfDenseArrayElement(uintN idx); - inline void setDenseArrayElement(uintN idx, const js::Value &val); - inline void shrinkDenseArrayElements(JSContext *cx, uintN cap); + inline bool ensureElements(JSContext *cx, uintN cap); + bool growElements(JSContext *cx, uintN cap); + void shrinkElements(JSContext *cx, uintN cap); - /* - * ensureDenseArrayElements ensures that the dense array can hold at least - * index + extra elements. It returns ED_OK on success, ED_FAILED on - * failure to grow the array, ED_SPARSE when the array is too sparse to - * grow (this includes the case of index + extra overflow). In the last - * two cases the array is kept intact. - */ - enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE }; - inline EnsureDenseResult ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra); + inline js::HeapValue* fixedElements() const { + JS_STATIC_ASSERT(2 * sizeof(js::Value) == sizeof(js::ObjectElements)); + return &fixedSlots()[2]; + } - /* - * Check if after growing the dense array will be too sparse. - * newElementsHint is an estimated number of elements to be added. - */ - bool willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint); + void setFixedElements() { this->elements = fixedElements(); } + + inline bool hasDynamicElements() const { + /* + * Note: for objects with zero fixed slots this could potentially give + * a spurious 'true' result, if the end of this object is exactly + * aligned with the end of its arena and dynamic slots are allocated + * immediately afterwards. Such cases cannot occur for dense arrays + * (which have at least two fixed slots) and can only result in a leak. + */ + return elements != js::emptyObjectElements && elements != fixedElements(); + } - JSBool makeDenseArraySlow(JSContext *cx); + /* JIT Accessors */ + static inline size_t offsetOfElements() { return offsetof(JSObject, elements); } + static inline size_t offsetOfFixedElements() { + return sizeof(JSObject) + sizeof(js::ObjectElements); + } - /* - * Arguments-specific getters and setters. - */ + inline js::ElementIteratorObject *asElementIterator(); - private: /* - * We represent arguments objects using js_ArgumentsClass and - * js::StrictArgumentsClass. The two are structured similarly, and methods - * valid on arguments objects of one class are also generally valid on - * arguments objects of the other. - * - * Arguments objects of either class store arguments length in a slot: - * - * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag - * indicating whether arguments.length was - * overwritten. This slot is not used to represent - * arguments.length after that property has been - * assigned, even if the new value is integral: it's - * always the original length. - * - * Both arguments classes use a slot for storing arguments data: - * - * JSSLOT_ARGS_DATA - pointer to an ArgumentsData structure - * - * ArgumentsData for normal arguments stores the value of arguments.callee, - * as long as that property has not been overwritten. If arguments.callee - * is overwritten, the corresponding value in ArgumentsData is set to - * MagicValue(JS_ARGS_HOLE). Strict arguments do not store this value - * because arguments.callee is a poison pill for strict mode arguments. - * - * The ArgumentsData structure also stores argument values. For normal - * arguments this occurs after the corresponding function has returned, and - * for strict arguments this occurs when the arguments object is created, - * or sometimes shortly after (but not observably so). arguments[i] is - * stored in ArgumentsData.slots[i], accessible via getArgsElement() and - * setArgsElement(). Deletion of arguments[i] overwrites that slot with - * MagicValue(JS_ARGS_HOLE); subsequent redefinition of arguments[i] will - * use a normal property to store the value, ignoring the slot. - * - * Non-strict arguments have a private: - * - * private - the function's stack frame until the function - * returns, when it is replaced with null; also, - * JS_ARGUMENTS_OBJECT_ON_TRACE while on trace, if - * arguments was created on trace - * - * Technically strict arguments have a private, but it's always null. - * Conceptually it would be better to remove this oddity, but preserving it - * allows us to work with arguments objects of either kind more abstractly, - * so we keep it for now. + * Array-specific getters and setters (for both dense and slow arrays). */ - static const uint32 JSSLOT_ARGS_DATA = 1; - public: - /* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */ - static const uint32 JSSLOT_ARGS_LENGTH = 0; - static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2; - static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1; + bool allocateSlowArrayElements(JSContext *cx); - /* Lower-order bit stolen from the length slot. */ - static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1; - static const uint32 ARGS_PACKED_BITS_COUNT = 1; + inline uint32_t getArrayLength() const; + inline void setArrayLength(JSContext *cx, uint32_t length); - /* - * Set the initial length of the arguments, and mark it as not overridden. - */ - inline void setArgsLength(uint32 argc); + inline uint32_t getDenseArrayCapacity(); + inline uint32_t getDenseArrayInitializedLength(); + inline void setDenseArrayLength(uint32_t length); + inline void setDenseArrayInitializedLength(uint32_t length); + inline void ensureDenseArrayInitializedLength(JSContext *cx, uintN index, uintN extra); + inline js::HeapValueArray getDenseArrayElements(); + inline const js::Value &getDenseArrayElement(uintN idx); + inline void setDenseArrayElement(uintN idx, const js::Value &val); + inline void initDenseArrayElement(uintN idx, const js::Value &val); + inline void setDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val); + inline void initDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val); + inline void copyDenseArrayElements(uintN dstStart, const js::Value *src, uintN count); + inline void initDenseArrayElements(uintN dstStart, const js::Value *src, uintN count); + inline void moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count); + inline void moveDenseArrayElementsUnbarriered(uintN dstStart, uintN srcStart, uintN count); + inline bool denseArrayHasInlineSlots() const; + + /* Packed information for this array. */ + inline void markDenseArrayNotPacked(JSContext *cx); /* - * Return the initial length of the arguments. This may differ from the - * current value of arguments.length! + * ensureDenseArrayElements ensures that the dense array can hold at least + * index + extra elements. It returns ED_OK on success, ED_FAILED on + * failure to grow the array, ED_SPARSE when the array is too sparse to + * grow (this includes the case of index + extra overflow). In the last + * two cases the array is kept intact. */ - inline uint32 getArgsInitialLength() const; - - inline void setArgsLengthOverridden(); - inline bool isArgsLengthOverridden() const; - - inline js::ArgumentsData *getArgsData() const; - inline void setArgsData(js::ArgumentsData *data); - - inline const js::Value &getArgsCallee() const; - inline void setArgsCallee(const js::Value &callee); - - inline const js::Value &getArgsElement(uint32 i) const; - inline js::Value *getArgsElements() const; - inline js::Value *addressOfArgsElement(uint32 i); - inline void setArgsElement(uint32 i, const js::Value &v); + enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE }; + inline EnsureDenseResult ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra); - private: /* - * Reserved slot structure for Call objects: - * - * private - the stack frame corresponding to the Call object - * until js_PutCallObject or its on-trace analog - * is called, null thereafter - * JSSLOT_CALL_CALLEE - callee function for the stack frame, or null if - * the stack frame is for strict mode eval code - * JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack - * frames (not valid for strict mode eval frames) + * Check if after growing the dense array will be too sparse. + * newElementsHint is an estimated number of elements to be added. */ - static const uint32 JSSLOT_CALL_CALLEE = 0; - static const uint32 JSSLOT_CALL_ARGUMENTS = 1; - - public: - /* Number of reserved slots. */ - static const uint32 CALL_RESERVED_SLOTS = 2; - - /* True if this is for a strict mode eval frame or for a function call. */ - inline bool callIsForEval() const; + bool willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint); - /* The stack frame for this Call object, if the frame is still active. */ - inline js::StackFrame *maybeCallObjStackFrame() const; + JSBool makeDenseArraySlow(JSContext *cx); /* - * The callee function if this Call object was created for a function - * invocation, or null if it was created for a strict mode eval frame. + * If this array object has a data property with index i, set *vp to its + * value and return true. If not, do vp->setMagic(JS_ARRAY_HOLE) and return + * true. On OOM, report it and return false. */ - inline JSObject *getCallObjCallee() const; - inline JSFunction *getCallObjCalleeFunction() const; - inline void setCallObjCallee(JSObject *callee); - - inline const js::Value &getCallObjArguments() const; - inline void setCallObjArguments(const js::Value &v); + bool arrayGetOwnDataElement(JSContext *cx, size_t i, js::Value *vp); - /* Returns the formal argument at the given index. */ - inline const js::Value &callObjArg(uintN i) const; - inline js::Value &callObjArg(uintN i); - - /* Returns the variable at the given index. */ - inline const js::Value &callObjVar(uintN i) const; - inline js::Value &callObjVar(uintN i); + public: + bool allocateArrayBufferSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL); + inline uint32_t arrayBufferByteLength(); + inline uint8_t * arrayBufferDataOffset(); + public: /* * Date-specific getters and setters. */ - static const uint32 JSSLOT_DATE_UTC_TIME = 0; + static const uint32_t JSSLOT_DATE_UTC_TIME = 0; /* * Cached slots holding local properties of the date. * These are undefined until the first actual lookup occurs * and are reset to undefined whenever the date's time is modified. */ - static const uint32 JSSLOT_DATE_COMPONENTS_START = 1; + static const uint32_t JSSLOT_DATE_COMPONENTS_START = 1; - static const uint32 JSSLOT_DATE_LOCAL_TIME = 1; - static const uint32 JSSLOT_DATE_LOCAL_YEAR = 2; - static const uint32 JSSLOT_DATE_LOCAL_MONTH = 3; - static const uint32 JSSLOT_DATE_LOCAL_DATE = 4; - static const uint32 JSSLOT_DATE_LOCAL_DAY = 5; - static const uint32 JSSLOT_DATE_LOCAL_HOURS = 6; - static const uint32 JSSLOT_DATE_LOCAL_MINUTES = 7; - static const uint32 JSSLOT_DATE_LOCAL_SECONDS = 8; + static const uint32_t JSSLOT_DATE_LOCAL_TIME = 1; + static const uint32_t JSSLOT_DATE_LOCAL_YEAR = 2; + static const uint32_t JSSLOT_DATE_LOCAL_MONTH = 3; + static const uint32_t JSSLOT_DATE_LOCAL_DATE = 4; + static const uint32_t JSSLOT_DATE_LOCAL_DAY = 5; + static const uint32_t JSSLOT_DATE_LOCAL_HOURS = 6; + static const uint32_t JSSLOT_DATE_LOCAL_MINUTES = 7; + static const uint32_t JSSLOT_DATE_LOCAL_SECONDS = 8; - static const uint32 DATE_CLASS_RESERVED_SLOTS = 9; + static const uint32_t DATE_CLASS_RESERVED_SLOTS = 9; inline const js::Value &getDateUTCTime() const; inline void setDateUTCTime(const js::Value &pthis); @@ -957,117 +1141,43 @@ struct JSObject : js::gc::Cell { * Function-specific getters and setters. */ - private: friend struct JSFunction; - friend class js::mjit::Compiler; - - /* - * Flat closures with one or more upvars snapshot the upvars' values into a - * vector of js::Values referenced from this slot. - */ - static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = 0; - - /* - * Null closures set or initialized as methods have these slots. See the - * "method barrier" comments and methods. - */ - - static const uint32 JSSLOT_FUN_METHOD_ATOM = 0; - static const uint32 JSSLOT_FUN_METHOD_OBJ = 1; - - static const uint32 JSSLOT_BOUND_FUNCTION_THIS = 0; - static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1; - - public: - static const uint32 FUN_CLASS_RESERVED_SLOTS = 2; - - inline JSFunction *getFunctionPrivate() const; - - inline js::Value *getFlatClosureUpvars() const; - inline js::Value getFlatClosureUpvar(uint32 i) const; - inline js::Value &getFlatClosureUpvar(uint32 i); - inline void setFlatClosureUpvars(js::Value *upvars); - - inline bool hasMethodObj(const JSObject& obj) const; - inline void setMethodObj(JSObject& obj); - - inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg, - const js::Value *args, uintN argslen); - - inline JSObject *getBoundFunctionTarget() const; - inline const js::Value &getBoundFunctionThis() const; - inline const js::Value *getBoundFunctionArguments(uintN &argslen) const; - - /* - * RegExp-specific getters and setters. - */ - - private: - static const uint32 JSSLOT_REGEXP_LAST_INDEX = 0; - static const uint32 JSSLOT_REGEXP_SOURCE = 1; - static const uint32 JSSLOT_REGEXP_GLOBAL = 2; - static const uint32 JSSLOT_REGEXP_IGNORE_CASE = 3; - static const uint32 JSSLOT_REGEXP_MULTILINE = 4; - static const uint32 JSSLOT_REGEXP_STICKY = 5; - /* - * Compute the initial shape to associate with fresh regular expression - * objects, encoding their initial properties. Return the shape after - * changing this regular expression object's last property to it. - */ - const js::Shape *assignInitialRegExpShape(JSContext *cx); + inline JSFunction *toFunction(); + inline const JSFunction *toFunction() const; public: - static const uint32 REGEXP_CLASS_RESERVED_SLOTS = 6; - - inline const js::Value &getRegExpLastIndex() const; - inline void setRegExpLastIndex(const js::Value &v); - inline void setRegExpLastIndex(jsdouble d); - inline void zeroRegExpLastIndex(); - - inline void setRegExpSource(JSString *source); - inline void setRegExpGlobal(bool global); - inline void setRegExpIgnoreCase(bool ignoreCase); - inline void setRegExpMultiline(bool multiline); - inline void setRegExpSticky(bool sticky); - - inline bool initRegExp(JSContext *cx, js::RegExp *re); - /* * Iterator-specific getters and setters. */ + static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1; + inline js::NativeIterator *getNativeIterator() const; inline void setNativeIterator(js::NativeIterator *); - /* - * Script-related getters. - */ - - inline JSScript *getScript() const; - /* * XML-related getters and setters. */ /* * Slots for XML-related classes are as follows: - * - js_NamespaceClass.base reserves the *_NAME_* and *_NAMESPACE_* slots. - * - js_QNameClass.base, js_AttributeNameClass, js_AnyNameClass reserve + * - NamespaceClass.base reserves the *_NAME_* and *_NAMESPACE_* slots. + * - QNameClass.base, AttributeNameClass, AnyNameClass reserve * the *_NAME_* and *_QNAME_* slots. - * - Others (js_XMLClass, js_XMLFilterClass) don't reserve any slots. + * - Others (XMLClass, js_XMLFilterClass) don't reserve any slots. */ private: - static const uint32 JSSLOT_NAME_PREFIX = 0; // shared - static const uint32 JSSLOT_NAME_URI = 1; // shared + static const uint32_t JSSLOT_NAME_PREFIX = 0; // shared + static const uint32_t JSSLOT_NAME_URI = 1; // shared - static const uint32 JSSLOT_NAMESPACE_DECLARED = 2; + static const uint32_t JSSLOT_NAMESPACE_DECLARED = 2; - static const uint32 JSSLOT_QNAME_LOCAL_NAME = 2; + static const uint32_t JSSLOT_QNAME_LOCAL_NAME = 2; public: - static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; - static const uint32 QNAME_CLASS_RESERVED_SLOTS = 3; + static const uint32_t NAMESPACE_CLASS_RESERVED_SLOTS = 3; + static const uint32_t QNAME_CLASS_RESERVED_SLOTS = 3; inline JSLinearString *getNamePrefix() const; inline jsval getNamePrefixVal() const; @@ -1081,70 +1191,34 @@ struct JSObject : js::gc::Cell { inline jsval getNamespaceDeclared() const; inline void setNamespaceDeclared(jsval decl); - inline JSLinearString *getQNameLocalName() const; + inline JSAtom *getQNameLocalName() const; inline jsval getQNameLocalNameVal() const; - inline void setQNameLocalName(JSLinearString *name); + inline void setQNameLocalName(JSAtom *name); /* * Proxy-specific getters and setters. */ - - inline js::JSProxyHandler *getProxyHandler() const; - inline const js::Value &getProxyPrivate() const; - inline void setProxyPrivate(const js::Value &priv); - inline const js::Value &getProxyExtra() const; - inline void setProxyExtra(const js::Value &extra); - - /* - * With object-specific getters and setters. - */ - inline JSObject *getWithThis() const; - inline void setWithThis(JSObject *thisp); + inline js::Wrapper *getWrapperHandler() const; /* * Back to generic stuff. */ inline bool isCallable(); - /* The map field is not initialized here and should be set separately. */ - void init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, bool useHoles); - inline void finish(JSContext *cx); - JS_ALWAYS_INLINE void finalize(JSContext *cx); - - /* - * Like init, but also initializes map. The catch: proto must be the result - * of a call to js_InitClass(...clasp, ...). - */ - inline bool initSharingEmptyShape(JSContext *cx, - js::Class *clasp, - JSObject *proto, - JSObject *parent, - void *priv, - /* gc::FinalizeKind */ unsigned kind); - - inline bool hasSlotsArray() const; - - /* This method can only be called when hasSlotsArray() returns true. */ - inline void freeSlotsArray(JSContext *cx); - - /* Free the slots array and copy slots that fit into the fixed array. */ - inline void revertToFixedSlots(JSContext *cx); + JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background); inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0); /* - * Allocate and free an object slot. Note that freeSlot is infallible: it - * returns true iff this is a dictionary-mode object and the freed slot was - * added to the freelist. + * Allocate and free an object slot. * * FIXME: bug 593129 -- slot allocation should be done by object methods * after calling object-parameter-free shape methods, avoiding coupling * logic across the object vs. shape module wall. */ - bool allocSlot(JSContext *cx, uint32 *slotp); - bool freeSlot(JSContext *cx, uint32 slot); + bool allocSlot(JSContext *cx, uint32_t *slotp); + void freeSlot(JSContext *cx, uint32_t slot); public: bool reportReadOnly(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR); @@ -1160,8 +1234,9 @@ struct JSObject : js::gc::Cell { bool callMethod(JSContext *cx, jsid id, uintN argc, js::Value *argv, js::Value *vp); private: - js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::Shape &child); + js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::StackShape &child); + protected: /* * Internal helper that adds a shape not yet mapped by this object. * @@ -1169,36 +1244,50 @@ struct JSObject : js::gc::Cell { * 1. getter and setter must be normalized based on flags (see jsscope.cpp). * 2. !isExtensible() checking must be done by callers. */ - const js::Shape *addPropertyInternal(JSContext *cx, jsid id, - js::PropertyOp getter, js::StrictPropertyOp setter, - uint32 slot, uintN attrs, - uintN flags, intN shortid, - js::Shape **spp); + js::Shape *addPropertyInternal(JSContext *cx, jsid id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, uintN attrs, + uintN flags, intN shortid, js::Shape **spp, + bool allowDictionary); + private: bool toDictionaryMode(JSContext *cx); + struct TradeGutsReserved; + static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, + TradeGutsReserved &reserved); + + static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b, + TradeGutsReserved &reserved); + public: /* Add a property whose id is not yet in this scope. */ - const js::Shape *addProperty(JSContext *cx, jsid id, - js::PropertyOp getter, js::StrictPropertyOp setter, - uint32 slot, uintN attrs, - uintN flags, intN shortid); + js::Shape *addProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, uintN attrs, + uintN flags, intN shortid, bool allowDictionary = true); /* Add a data property whose id is not yet in this scope. */ - const js::Shape *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) { + js::Shape *addDataProperty(JSContext *cx, jsid id, uint32_t slot, uintN attrs) { JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0); } /* Add or overwrite a property for id in this scope. */ - const js::Shape *putProperty(JSContext *cx, jsid id, - js::PropertyOp getter, js::StrictPropertyOp setter, - uint32 slot, uintN attrs, - uintN flags, intN shortid); + js::Shape *putProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, uintN attrs, + uintN flags, intN shortid); + inline js::Shape * + putProperty(JSContext *cx, js::PropertyName *name, + JSPropertyOp getter, JSStrictPropertyOp setter, + uint32_t slot, uintN attrs, uintN flags, intN shortid) { + return putProperty(cx, js_CheckForStringIndex(ATOM_TO_JSID(name)), getter, setter, slot, attrs, flags, shortid); + } /* Change the given property into a sibling with the same id in this scope. */ - const js::Shape *changeProperty(JSContext *cx, const js::Shape *shape, uintN attrs, uintN mask, - js::PropertyOp getter, js::StrictPropertyOp setter); + js::Shape *changeProperty(JSContext *cx, js::Shape *shape, uintN attrs, uintN mask, + JSPropertyOp getter, JSStrictPropertyOp setter); /* Remove the property named by id from this object. */ bool removeProperty(JSContext *cx, jsid id); @@ -1206,117 +1295,201 @@ struct JSObject : js::gc::Cell { /* Clear the scope, making it empty. */ void clear(JSContext *cx); - JSBool lookupProperty(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp) { - js::LookupPropOp op = getOps()->lookupProperty; - return (op ? op : js_LookupProperty)(cx, this, id, objp, propp); - } - - JSBool defineProperty(JSContext *cx, jsid id, const js::Value &value, - js::PropertyOp getter = js::PropertyStub, - js::StrictPropertyOp setter = js::StrictPropertyStub, - uintN attrs = JSPROP_ENUMERATE) { - js::DefinePropOp op = getOps()->defineProperty; - return (op ? op : js_DefineProperty)(cx, this, id, &value, getter, setter, attrs); - } - - JSBool getProperty(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp) { - js::PropertyIdOp op = getOps()->getProperty; - return (op ? op : (js::PropertyIdOp)js_GetProperty)(cx, this, receiver, id, vp); - } - - JSBool getProperty(JSContext *cx, jsid id, js::Value *vp) { - return getProperty(cx, this, id, vp); - } - - JSBool setProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict) { - js::StrictPropertyIdOp op = getOps()->setProperty; - return (op ? op : js_SetProperty)(cx, this, id, vp, strict); - } - - JSBool getAttributes(JSContext *cx, jsid id, uintN *attrsp) { - js::AttributesOp op = getOps()->getAttributes; - return (op ? op : js_GetAttributes)(cx, this, id, attrsp); - } - - JSBool setAttributes(JSContext *cx, jsid id, uintN *attrsp) { - js::AttributesOp op = getOps()->setAttributes; - return (op ? op : js_SetAttributes)(cx, this, id, attrsp); - } - - JSBool deleteProperty(JSContext *cx, jsid id, js::Value *rval, JSBool strict) { - js::DeleteIdOp op = getOps()->deleteProperty; - return (op ? op : js_DeleteProperty)(cx, this, id, rval, strict); - } - - JSBool enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) { - js::NewEnumerateOp op = getOps()->enumerate; - return (op ? op : js_Enumerate)(cx, this, iterop, statep, idp); - } - - JSType typeOf(JSContext *cx) { - js::TypeOfOp op = getOps()->typeOf; - return (op ? op : js_TypeOf)(cx, this); - } - - /* These four are time-optimized to avoid stub calls. */ - JSObject *thisObject(JSContext *cx) { - JSObjectOp op = getOps()->thisObject; - return op ? op(cx, this) : this; - } + inline JSBool lookupGeneric(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp); + inline JSBool lookupProperty(JSContext *cx, js::PropertyName *name, JSObject **objp, JSProperty **propp); + inline JSBool lookupElement(JSContext *cx, uint32_t index, + JSObject **objp, JSProperty **propp); + inline JSBool lookupSpecial(JSContext *cx, js::SpecialId sid, + JSObject **objp, JSProperty **propp); + + inline JSBool defineGeneric(JSContext *cx, jsid id, const js::Value &value, + JSPropertyOp getter = JS_PropertyStub, + JSStrictPropertyOp setter = JS_StrictPropertyStub, + uintN attrs = JSPROP_ENUMERATE); + inline JSBool defineProperty(JSContext *cx, js::PropertyName *name, const js::Value &value, + JSPropertyOp getter = JS_PropertyStub, + JSStrictPropertyOp setter = JS_StrictPropertyStub, + uintN attrs = JSPROP_ENUMERATE); + + inline JSBool defineElement(JSContext *cx, uint32_t index, const js::Value &value, + JSPropertyOp getter = JS_PropertyStub, + JSStrictPropertyOp setter = JS_StrictPropertyStub, + uintN attrs = JSPROP_ENUMERATE); + inline JSBool defineSpecial(JSContext *cx, js::SpecialId sid, const js::Value &value, + JSPropertyOp getter = JS_PropertyStub, + JSStrictPropertyOp setter = JS_StrictPropertyStub, + uintN attrs = JSPROP_ENUMERATE); + + inline JSBool getGeneric(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp); + inline JSBool getProperty(JSContext *cx, JSObject *receiver, js::PropertyName *name, + js::Value *vp); + inline JSBool getElement(JSContext *cx, JSObject *receiver, uint32_t index, js::Value *vp); + /* If element is not present (e.g. array hole) *present is set to + false and the contents of *vp are unusable garbage. */ + inline JSBool getElementIfPresent(JSContext *cx, JSObject *receiver, uint32_t index, + js::Value *vp, bool *present); + inline JSBool getSpecial(JSContext *cx, JSObject *receiver, js::SpecialId sid, js::Value *vp); + + inline JSBool getGeneric(JSContext *cx, jsid id, js::Value *vp); + inline JSBool getProperty(JSContext *cx, js::PropertyName *name, js::Value *vp); + inline JSBool getElement(JSContext *cx, uint32_t index, js::Value *vp); + inline JSBool getSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp); + + inline JSBool setGeneric(JSContext *cx, jsid id, js::Value *vp, JSBool strict); + inline JSBool setProperty(JSContext *cx, js::PropertyName *name, js::Value *vp, JSBool strict); + inline JSBool setElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict); + inline JSBool setSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp, JSBool strict); + + JSBool nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict); + JSBool nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict); + + inline JSBool getGenericAttributes(JSContext *cx, jsid id, uintN *attrsp); + inline JSBool getPropertyAttributes(JSContext *cx, js::PropertyName *name, uintN *attrsp); + inline JSBool getElementAttributes(JSContext *cx, uint32_t index, uintN *attrsp); + inline JSBool getSpecialAttributes(JSContext *cx, js::SpecialId sid, uintN *attrsp); + + inline JSBool setGenericAttributes(JSContext *cx, jsid id, uintN *attrsp); + inline JSBool setPropertyAttributes(JSContext *cx, js::PropertyName *name, uintN *attrsp); + inline JSBool setElementAttributes(JSContext *cx, uint32_t index, uintN *attrsp); + inline JSBool setSpecialAttributes(JSContext *cx, js::SpecialId sid, uintN *attrsp); + + inline bool deleteProperty(JSContext *cx, js::PropertyName *name, js::Value *rval, bool strict); + inline bool deleteElement(JSContext *cx, uint32_t index, js::Value *rval, bool strict); + inline bool deleteSpecial(JSContext *cx, js::SpecialId sid, js::Value *rval, bool strict); + bool deleteByValue(JSContext *cx, const js::Value &property, js::Value *rval, bool strict); + + inline bool enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp); + inline bool defaultValue(JSContext *cx, JSType hint, js::Value *vp); + inline JSType typeOf(JSContext *cx); + inline JSObject *thisObject(JSContext *cx); static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp); - inline JSCompartment *getCompartment() const; - inline JSObject *getThrowTypeError() const; - JS_FRIEND_API(JSObject *) clone(JSContext *cx, JSObject *proto, JSObject *parent); - JS_FRIEND_API(bool) copyPropertiesFrom(JSContext *cx, JSObject *obj); bool swap(JSContext *cx, JSObject *other); - const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index); + inline void initArrayClass(); + + static inline void writeBarrierPre(JSObject *obj); + static inline void writeBarrierPost(JSObject *obj, void *addr); + inline void privateWriteBarrierPre(void **oldval); + inline void privateWriteBarrierPost(void **oldval); - inline bool canHaveMethodBarrier() const; + /* + * In addition to the generic object interface provided by JSObject, + * specific types of objects may provide additional operations. To access, + * these addition operations, callers should use the pattern: + * + * if (obj.isX()) { + * XObject &x = obj.asX(); + * x.foo(); + * } + * + * These XObject classes form a hierarchy. For example, for a cloned block + * object, the following predicates are true: isClonedBlock, isBlock, + * isNestedScope and isScope. Each of these has a respective class that + * derives and adds operations. + * + * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file + * triplet (along with any class YObject that derives XObject). + * + * Note that X represents a low-level representation and does not query the + * [[Class]] property of object defined by the spec (for this, see + * js::ObjectClassIs). + * + * SpiderMonkey has not been completely switched to the isX/asX/XObject + * pattern so in some cases there is no XObject class and the engine + * instead pokes directly at reserved slots and getPrivate. In such cases, + * consider adding the missing XObject class. + */ + /* Direct subtypes of JSObject: */ inline bool isArguments() const; - inline bool isNormalArguments() const; - inline bool isStrictArguments() const; + inline bool isArrayBuffer() const; inline bool isArray() const; - inline bool isDenseArray() const; - inline bool isSlowArray() const; - inline bool isNumber() const; - inline bool isBoolean() const; - inline bool isString() const; - inline bool isPrimitive() const; inline bool isDate() const; + inline bool isDenseArray() const; + inline bool isElementIterator() const; + inline bool isError() const; inline bool isFunction() const; + inline bool isGenerator() const; + inline bool isGlobal() const; + inline bool isIterator() const; + inline bool isNamespace() const; inline bool isObject() const; - inline bool isWith() const; - inline bool isBlock() const; - inline bool isStaticBlock() const; - inline bool isClonedBlock() const; - inline bool isCall() const; + inline bool isQName() const; + inline bool isPrimitive() const; + inline bool isProxy() const; inline bool isRegExp() const; + inline bool isRegExpStatics() const; + inline bool isScope() const; inline bool isScript() const; + inline bool isSlowArray() const; + inline bool isStopIteration() const; + inline bool isWeakMap() const; inline bool isXML() const; inline bool isXMLId() const; - inline bool isNamespace() const; - inline bool isQName() const; - inline bool isWeakMap() const; - inline bool isProxy() const; - inline bool isObjectProxy() const; + /* Subtypes of ScopeObject. */ + inline bool isBlock() const; + inline bool isCall() const; + inline bool isDeclEnv() const; + inline bool isNestedScope() const; + inline bool isWith() const; + inline bool isClonedBlock() const; + inline bool isStaticBlock() const; + + /* Subtypes of PrimitiveObject. */ + inline bool isBoolean() const; + inline bool isNumber() const; + inline bool isString() const; + + /* Subtypes of ArgumentsObject. */ + inline bool isNormalArguments() const; + inline bool isStrictArguments() const; + + /* Subtypes of Proxy. */ + inline bool isWrapper() const; inline bool isFunctionProxy() const; + inline bool isCrossCompartmentWrapper() const; + + inline js::ArgumentsObject &asArguments(); + inline const js::ArgumentsObject &asArguments() const; + inline js::BlockObject &asBlock(); + inline js::BooleanObject &asBoolean(); + inline js::CallObject &asCall(); + inline js::ClonedBlockObject &asClonedBlock(); + inline js::DeclEnvObject &asDeclEnv(); + inline js::GlobalObject &asGlobal(); + inline js::NestedScopeObject &asNestedScope(); + inline js::NormalArgumentsObject &asNormalArguments(); + inline js::NumberObject &asNumber(); + inline js::RegExpObject &asRegExp(); + inline js::ScopeObject &asScope(); + inline js::StrictArgumentsObject &asStrictArguments(); + inline js::StaticBlockObject &asStaticBlock(); + inline js::StringObject &asString(); + inline js::WithObject &asWith(); + + static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; } - JS_FRIEND_API(bool) isWrapper() const; - JS_FRIEND_API(JSObject *) unwrap(uintN *flagsp = NULL); +#ifdef DEBUG + void dump(); +#endif - inline void initArrayClass(); + private: + static void staticAsserts() { + /* Check alignment for any fixed slots allocated after the object. */ + JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0); + + JS_STATIC_ASSERT(offsetof(JSObject, shape_) == offsetof(js::shadow::Object, shape)); + JS_STATIC_ASSERT(offsetof(JSObject, slots) == offsetof(js::shadow::Object, slots)); + JS_STATIC_ASSERT(offsetof(JSObject, type_) == offsetof(js::shadow::Object, type)); + JS_STATIC_ASSERT(sizeof(JSObject) == sizeof(js::shadow::Object)); + } }; -/* Check alignment for any fixed slots allocated after the object. */ -JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0); - /* * The only sensible way to compare JSObject with == is by identity. We use * const& instead of * as a syntactic way to assert non-null. This leads to an @@ -1328,19 +1501,34 @@ operator==(const JSObject &lhs, const JSObject &rhs) return &lhs == &rhs; } -inline js::Value* -JSObject::fixedSlots() const { - return (js::Value*) (jsuword(this) + sizeof(JSObject)); +static JS_ALWAYS_INLINE bool +operator!=(const JSObject &lhs, const JSObject &rhs) +{ + return &lhs != &rhs; } -inline bool -JSObject::hasSlotsArray() const { return this->slots != fixedSlots(); } +inline js::HeapValue* +JSObject::fixedSlots() const +{ + return (js::HeapValue *) (uintptr_t(this) + sizeof(JSObject)); +} + +inline size_t +JSObject::numFixedSlots() const +{ + return reinterpret_cast(this)->numFixedSlots(); +} /* static */ inline size_t JSObject::getFixedSlotOffset(size_t slot) { return sizeof(JSObject) + (slot * sizeof(js::Value)); } +/* static */ inline size_t +JSObject::getPrivateDataOffset(size_t nfixed) { + return getFixedSlotOffset(nfixed); +} + struct JSObject_Slots2 : JSObject { js::Value fslots[2]; }; struct JSObject_Slots4 : JSObject { js::Value fslots[4]; }; struct JSObject_Slots8 : JSObject { js::Value fslots[8]; }; @@ -1349,35 +1537,6 @@ struct JSObject_Slots16 : JSObject { js::Value fslots[16]; }; #define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp) -#ifdef JS_THREADSAFE - -/* - * The GC runs only when all threads except the one on which the GC is active - * are suspended at GC-safe points, so calling obj->getSlot() from the GC's - * thread is safe when rt->gcRunning is set. See jsgc.cpp for details. - */ -#define THREAD_IS_RUNNING_GC(rt, thread) \ - ((rt)->gcRunning && (rt)->gcThread == (thread)) - -#define CX_THREAD_IS_RUNNING_GC(cx) \ - THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) - -#endif /* JS_THREADSAFE */ - -inline void -OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj) -{ - if (JSObjectOp op = obj->getClass()->ext.innerObject) - obj = op(cx, obj); -} - -inline void -OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj) -{ - if (JSObjectOp op = obj->getClass()->ext.outerObject) - obj = op(cx, obj); -} - class JSValueArray { public: jsval *array; @@ -1394,107 +1553,14 @@ class ValueArray { ValueArray(js::Value *v, size_t c) : array(v), length(c) {} }; -extern js::Class js_ObjectClass; -extern js::Class js_WithClass; -extern js::Class js_BlockClass; - -inline bool JSObject::isObject() const { return getClass() == &js_ObjectClass; } -inline bool JSObject::isWith() const { return getClass() == &js_WithClass; } -inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } - -/* - * Block scope object macros. The slots reserved by js_BlockClass are: - * - * private StackFrame * active frame pointer or null - * JSSLOT_BLOCK_DEPTH int depth of block slots in frame - * - * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. - * - * A With object is like a Block object, in that both have one reserved slot - * telling the stack depth of the relevant slots (the slot whose value is the - * object named in the with statement, the slots containing the block's local - * variables); and both have a private slot referring to the StackFrame in - * whose activation they were created (or null if the with or block object - * outlives the frame). - */ -static const uint32 JSSLOT_BLOCK_DEPTH = 0; -static const uint32 JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1; - -inline bool -JSObject::isStaticBlock() const -{ - return isBlock() && !getProto(); -} - -inline bool -JSObject::isClonedBlock() const -{ - return isBlock() && !!getProto(); -} - -static const uint32 JSSLOT_WITH_THIS = 1; - -#define OBJ_BLOCK_COUNT(cx,obj) \ - (obj)->propertyCount() -#define OBJ_BLOCK_DEPTH(cx,obj) \ - (obj)->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() -#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ - (obj)->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth))) - -/* - * To make sure this slot is well-defined, always call js_NewWithObject to - * create a With object, don't call js_NewObject directly. When creating a - * With object that does not correspond to a stack slot, pass -1 for depth. - * - * When popping the stack across this object's "with" statement, client code - * must call withobj->setPrivate(NULL). - */ -extern JS_REQUIRES_STACK JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); - -inline JSObject * -js_UnwrapWithObject(JSContext *cx, JSObject *withobj) -{ - JS_ASSERT(withobj->getClass() == &js_WithClass); - return withobj->getProto(); -} - -/* - * Create a new block scope object not linked to any proto or parent object. - * Blocks are created by the compiler to reify let blocks and comprehensions. - * Only when dynamic scope is captured do they need to be cloned and spliced - * into an active scope chain. - */ -extern JSObject * -js_NewBlockObject(JSContext *cx); - -extern JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, js::StackFrame *fp); - -extern JS_REQUIRES_STACK JSBool -js_PutBlockObject(JSContext *cx, JSBool normalUnwind); - -JSBool -js_XDRBlockObject(JSXDRState *xdr, JSObject **objp); - -struct JSSharpObjectMap { - jsrefcount depth; - jsatomid sharpgen; - JSHashTable *table; -}; - +/* For manipulating JSContext::sharpObjectMap. */ #define SHARP_BIT ((jsatomid) 1) -#define BUSY_BIT ((jsatomid) 2) #define SHARP_ID_SHIFT 2 #define IS_SHARP(he) (uintptr_t((he)->value) & SHARP_BIT) #define MAKE_SHARP(he) ((he)->value = (void *) (uintptr_t((he)->value)|SHARP_BIT)) -#define IS_BUSY(he) (uintptr_t((he)->value) & BUSY_BIT) -#define MAKE_BUSY(he) ((he)->value = (void *) (uintptr_t((he)->value)|BUSY_BIT)) -#define CLEAR_BUSY(he) ((he)->value = (void *) (uintptr_t((he)->value)&~BUSY_BIT)) extern JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp); +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen); extern void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); @@ -1507,36 +1573,26 @@ extern void js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map); extern JSBool -js_HasOwnPropertyHelper(JSContext *cx, js::LookupPropOp lookup, uintN argc, +js_HasOwnPropertyHelper(JSContext *cx, js::LookupGenericOp lookup, uintN argc, js::Value *vp); extern JSBool -js_HasOwnProperty(JSContext *cx, js::LookupPropOp lookup, JSObject *obj, jsid id, +js_HasOwnProperty(JSContext *cx, js::LookupGenericOp lookup, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); -extern JSBool -js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, - const js::Value &getter, const js::Value &setter, - const js::Value &value, js::Value *vp); - extern JSBool js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); -#ifdef OLD_GETTER_SETTER_METHODS -JS_FRIEND_API(JSBool) js_obj_defineGetter(JSContext *cx, uintN argc, js::Value *vp); -JS_FRIEND_API(JSBool) js_obj_defineSetter(JSContext *cx, uintN argc, js::Value *vp); +#if JS_HAS_OBJ_PROTO_PROP +extern JSPropertySpec object_props[]; +#else +#define object_props NULL #endif -extern JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj); +extern JSFunctionSpec object_methods[]; +extern JSFunctionSpec object_static_methods[]; namespace js { -JSObject * -DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, - JSObject *protoProto, Class *clasp, - Native constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs); bool IsStandardClassResolved(JSObject *obj, js::Class *clasp); @@ -1544,13 +1600,84 @@ IsStandardClassResolved(JSObject *obj, js::Class *clasp); void MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp); -} +/* + * Cache for speeding up repetitive creation of objects in the VM. + * When an object is created which matches the criteria in the 'key' section + * below, an entry is filled with the resulting object. + */ +class NewObjectCache +{ + struct Entry + { + /* Class of the constructed object. */ + Class *clasp; + + /* + * Key with one of three possible values: + * + * - Global for the object. The object must have a standard class for + * which the global's prototype can be determined, and the object's + * parent will be the global. + * + * - Prototype for the object (cannot be global). The object's parent + * will be the prototype's parent. + * + * - Type for the object. The object's parent will be the type's + * prototype's parent. + */ + gc::Cell *key; + + /* Allocation kind for the constructed object. */ + gc::AllocKind kind; + + /* Number of bytes to copy from the template object. */ + uint32_t nbytes; + + /* + * Template object to copy from, with the initial values of fields, + * fixed slots (undefined) and private data (NULL). + */ + JSObject_Slots16 templateObject; + }; -extern JSObject * -js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, - js::Class *clasp, js::Native constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs); + Entry entries[41]; + + void staticAsserts() { + JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND); + } + + public: + + typedef int EntryIndex; + + void reset() { PodZero(this); } + + /* + * Get the entry index for the given lookup, return whether there was a hit + * on an existing entry. + */ + inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry); + inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry); + inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry); + + /* Return a new object from a cache hit produced by a lookup method. */ + inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry); + + /* Fill an entry after a cache miss. */ + inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj); + inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj); + inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj); + + /* Invalidate any entries which might produce an object with shape/proto. */ + void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto); + + private: + inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry); + inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj); + static inline void copyCachedToObject(JSObject *dst, JSObject *src); +}; + +} /* namespace js */ /* * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp. @@ -1578,10 +1705,6 @@ extern JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); -extern JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject *cobj, JSObject *prototype); - /* * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is * JSProto_Null, clasp must non-null. @@ -1590,10 +1713,6 @@ extern JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey key, js::Value *vp, js::Class *clasp = NULL); -extern JSObject * -js_ConstructObject(JSContext *cx, js::Class *clasp, JSObject *proto, - JSObject *parent, uintN argc, js::Value *argv); - // Specialized call for constructing |this| with a known function callee, // and a known prototype. extern JSObject * @@ -1601,7 +1720,7 @@ js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *pro // Specialized call for constructing |this| with a known function callee. extern JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee); +js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType); // Generic call for constructing |this|. extern JSObject * @@ -1610,29 +1729,13 @@ js_CreateThis(JSContext *cx, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); -/* - * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent - * scope, else it reshapes the scope and prototype chains it links. It calls - * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate - * (i.e., obj has ever been on a prototype or parent chain). - */ -extern void -js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id); - -inline void -js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) -{ - if (obj->isDelegate()) - js_PurgeScopeChainHelper(cx, obj, id); -} - /* * Find or create a property named by id in obj's scope, with the given getter * and setter, slot, attributes, and other members. */ -extern const js::Shape * +extern js::Shape * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - js::PropertyOp getter, js::StrictPropertyOp setter, uint32 slot, + JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot, uintN attrs, uintN flags, intN shortid); /* @@ -1640,96 +1743,106 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, * it into a potentially new js::Shape. Return a pointer to the changed * or identical property. */ -extern const js::Shape * +extern js::Shape * js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - const js::Shape *shape, uintN attrs, uintN mask, - js::PropertyOp getter, js::StrictPropertyOp setter); + js::Shape *shape, uintN attrs, uintN mask, + JSPropertyOp getter, JSStrictPropertyOp setter); extern JSBool js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value &descriptor, JSBool *bp); +namespace js { + /* * Flags for the defineHow parameter of js_DefineNativeProperty. */ -const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ -const uintN JSDNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */ -const uintN JSDNP_SET_METHOD = 4; /* js_{DefineNativeProperty,SetPropertyHelper} +const uintN DNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ +const uintN DNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */ +const uintN DNP_SET_METHOD = 4; /* DefineNativeProperty,js_SetPropertyHelper must pass the js::Shape::METHOD flag on to JSObject::{add,put}Property */ -const uintN JSDNP_UNQUALIFIED = 8; /* Unqualified property set. Only used in +const uintN DNP_UNQUALIFIED = 8; /* Unqualified property set. Only used in the defineHow argument of js_SetPropertyHelper. */ +const uintN DNP_SKIP_TYPE = 0x10; /* Don't update type information */ /* - * On error, return false. On success, if propp is non-null, return true with - * obj locked and with a held property in *propp; if propp is null, return true - * but release obj's lock first. + * Return successfully added or changed shape or NULL on error. */ -extern JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value &value, - js::PropertyOp getter, js::StrictPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp, - uintN defineHow = 0); +extern const Shape * +DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs, + uintN flags, intN shortid, uintN defineHow = 0); + +inline const Shape * +DefineNativeProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value &value, + PropertyOp getter, StrictPropertyOp setter, uintN attrs, + uintN flags, intN shortid, uintN defineHow = 0) +{ + return DefineNativeProperty(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs, flags, + shortid, defineHow); +} /* - * Specialized subroutine that allows caller to preset JSRESOLVE_* flags and - * returns the index along the prototype chain in which *propp was found, or - * the last index if not found, or -1 on error. + * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. */ -extern int -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp); +extern bool +LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp); + +inline bool +LookupPropertyWithFlags(JSContext *cx, JSObject *obj, PropertyName *name, uintN flags, + JSObject **objp, JSProperty **propp) +{ + return LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(name), flags, objp, propp); +} /* - * Constant to pass to js_LookupPropertyWithFlags to infer bits from current - * bytecode. + * Call the [[DefineOwnProperty]] internal method of obj. + * + * If obj is an array, this follows ES5 15.4.5.1. + * If obj is any other native object, this follows ES5 8.12.9. + * If obj is a proxy, this calls the proxy handler's defineProperty method. + * Otherwise, this reports an error and returns false. */ -static const uintN JSRESOLVE_INFER = 0xffff; - -extern JS_FRIEND_DATA(js::Class) js_CallClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - -namespace js { +extern bool +DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError, + bool *rval); /* - * We cache name lookup results only for the global object or for native - * non-global objects without prototype or with prototype that never mutates, - * see bug 462734 and bug 487039. + * Read property descriptors from props, as for Object.defineProperties. See + * ES5 15.2.3.7 steps 3-5. */ -static inline bool -IsCacheableNonGlobalScope(JSObject *obj) -{ - JS_ASSERT(obj->getParent()); - - js::Class *clasp = obj->getClass(); - bool cacheable = (clasp == &js_CallClass || - clasp == &js_BlockClass || - clasp == &js_DeclEnvClass); - - JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty); - return cacheable; -} +extern bool +ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors, + AutoIdVector *ids, AutoPropDescArrayRooter *descs); -} +/* + * Constant to pass to js_LookupPropertyWithFlags to infer bits from current + * bytecode. + */ +static const uintN RESOLVE_INFER = 0xffff; /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ -extern js::PropertyCacheEntry * -js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, - JSObject **objp, JSObject **pobjp, JSProperty **propp); +extern bool +FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain, + JSObject **objp, JSObject **pobjp, JSProperty **propp); /* - * Return the index along the scope chain in which id was found, or the last - * index if not found, or -1 on error. + * Search for name either on the current scope chain or on the scope chain's + * global object, per the global parameter. */ -extern JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp); +extern bool +FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain, + JSObject **objp, JSObject **pobjp, JSProperty **propp); -extern JS_REQUIRES_STACK JSObject * -js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id); +extern JSObject * +FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name); + +} extern JSObject * js_FindVariableScope(JSContext *cx, JSFunction **funp); @@ -1747,9 +1860,9 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp); * barrier, which is not needed when invoking a lambda that otherwise does not * leak its callee reference (via arguments.callee or its name). */ -const uintN JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode const uintN JSGET_METHOD_BARRIER = 0; // get can leak joined function object -const uintN JSGET_NO_METHOD_BARRIER = 2; // call to joined function can't leak +const uintN JSGET_NO_METHOD_BARRIER = 1; // call to joined function can't leak +const uintN JSGET_CACHE_RESULT = 2; // from a caching interpreter opcode /* * NB: js_NativeGet and js_NativeSet are called with the scope containing shape @@ -1765,32 +1878,40 @@ extern JSBool js_NativeSet(JSContext *cx, JSObject *obj, const js::Shape *shape, bool added, bool strict, js::Value *vp); -extern JSBool -js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, js::Value *vp); +namespace js { -extern bool -js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, - uint32 getHow, js::Value *vp, - const js::Shape **shapeOut, JSObject **holderOut); +bool +GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp); -extern JSBool -js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); +inline bool +GetPropertyHelper(JSContext *cx, JSObject *obj, PropertyName *name, uint32_t getHow, Value *vp) +{ + return GetPropertyHelper(cx, obj, ATOM_TO_JSID(name), getHow, vp); +} + +bool +GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc); + +bool +GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp); + +bool +NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp); + +} /* namespace js */ extern JSBool js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, js::Value *vp); -/* - * Check whether it is OK to assign an undeclared property with name - * propname of the global object in the current script on cx. Reports - * an error if one needs to be reported (in particular in all cases - * when it returns false). - */ -extern JS_FRIEND_API(bool) -js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname); +namespace js { -extern JSBool -js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, - js::Value *vp, JSBool strict); +inline bool +GetMethod(JSContext *cx, JSObject *obj, PropertyName *name, uintN getHow, Value *vp) +{ + return js_GetMethod(cx, obj, ATOM_TO_JSID(name), getHow, vp); +} + +} /* namespace js */ /* * Change attributes for the given native property. The caller must ensure @@ -1803,14 +1924,11 @@ js_SetNativeAttributes(JSContext *cx, JSObject *obj, js::Shape *shape, namespace js { /* - * If obj has a data property methodid which is a function object for the given - * native, return that function object. Otherwise, return NULL. + * If obj has an already-resolved data property for methodid, return true and + * store the property value in *vp. */ -extern JSObject * -HasNativeMethod(JSObject *obj, jsid methodid, Native native); - extern bool -DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); +HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, js::Value *vp); extern JSBool CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, @@ -1821,18 +1939,6 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, extern bool js_IsDelegate(JSContext *cx, JSObject *obj, const js::Value &v); -/* - * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is - * JSProto_Null, clasp must non-null. - */ -extern JS_FRIEND_API(JSBool) -js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, - JSObject **protop, js::Class *clasp = NULL); - -extern JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs); - /* * Wrap boolean, number or string as Boolean, Number or String object. * *vp must not be an object, null or undefined. @@ -1840,13 +1946,13 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, extern JSBool js_PrimitiveToObject(JSContext *cx, js::Value *vp); -/* - * v and vp may alias. On successful return, vp->isObjectOrNull(). If vp is not - * rooted, the caller must root vp before the next possible GC. - */ extern JSBool js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, JSObject **objp); +/* Throws if v could not be converted to an object. */ +extern JSObject * +js_ValueToNonNullObject(JSContext *cx, const js::Value &v); + namespace js { /* @@ -1854,27 +1960,26 @@ namespace js { * If *vp might already be an object, use ToObject. */ extern JSObject * -ToObjectSlow(JSContext *cx, js::Value *vp); +ToObjectSlow(JSContext *cx, Value *vp); JS_ALWAYS_INLINE JSObject * -ToObject(JSContext *cx, js::Value *vp) +ToObject(JSContext *cx, Value *vp) { if (vp->isObject()) return &vp->toObject(); return ToObjectSlow(cx, vp); } +/* As for ToObject, but preserves the original value. */ +inline JSObject * +ValueToObject(JSContext *cx, const Value &v) +{ + if (v.isObject()) + return &v.toObject(); + return js_ValueToNonNullObject(cx, v); } -/* - * v and vp may alias. On successful return, vp->isObject(). If vp is not - * rooted, the caller must root vp before the next possible GC. - */ -extern JSObject * -js_ValueToNonNullObject(JSContext *cx, const js::Value &v); - -extern JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, js::Value *rval); +} /* namespace js */ extern JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp); @@ -1885,32 +1990,9 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize); extern bool js_ClearNative(JSContext *cx, JSObject *obj); -extern bool -js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, js::Value *vp); - -extern bool -js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const js::Value &v); - -/* For CSP -- checks if eval() and friends are allowed to run. */ -extern JSBool -js_CheckContentSecurityPolicy(JSContext *cx, JSObject *scopeObj); - extern JSBool js_ReportGetterOnlyAssignment(JSContext *cx); -extern JS_FRIEND_API(JSBool) -js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp); - -#ifdef DEBUG -JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n); -JS_FRIEND_API(void) js_DumpString(JSString *str); -JS_FRIEND_API(void) js_DumpAtom(JSAtom *atom); -JS_FRIEND_API(void) js_DumpObject(JSObject *obj); -JS_FRIEND_API(void) js_DumpValue(const js::Value &val); -JS_FRIEND_API(void) js_DumpId(jsid id); -JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = NULL); -#endif - extern uintN js_InferFlags(JSContext *cx, uintN defaultFlags); @@ -1918,6 +2000,17 @@ js_InferFlags(JSContext *cx, uintN defaultFlags); JSBool js_Object(JSContext *cx, uintN argc, js::Value *vp); +/* + * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is + * JSProto_Null, clasp must non-null. + * + * If protoKey is constant and scope is non-null, use GlobalObject's prototype + * methods instead. + */ +extern JS_FRIEND_API(JSBool) +js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, + JSObject **protop, js::Class *clasp = NULL); + namespace js { extern bool @@ -1926,13 +2019,16 @@ SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles); extern JSString * obj_toStringHelper(JSContext *cx, JSObject *obj); +extern JSBool +eval(JSContext *cx, uintN argc, Value *vp); + /* * Performs a direct eval for the given arguments, which must correspond to the * currently-executing stack frame, which must be a script frame. On completion - * the result is returned in call.rval. + * the result is returned in args.rval. */ -extern JS_REQUIRES_STACK bool -DirectEval(JSContext *cx, const CallArgs &call); +extern bool +DirectEval(JSContext *cx, const CallArgs &args); /* * True iff |v| is the built-in eval function for the global object that @@ -1947,11 +2043,79 @@ IsAnyBuiltinEval(JSFunction *fun); /* 'call' should be for the eval/Function native invocation. */ extern JSPrincipals * -PrincipalsForCompiledCode(const CallArgs &call, JSContext *cx); +PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx); extern JSObject * NonNullObject(JSContext *cx, const Value &v); -} +extern const char * +InformalValueTypeName(const Value &v); + +/* + * Report an error if call.thisv is not compatible with the specified class. + * + * NB: most callers should be calling or NonGenericMethodGuard, + * HandleNonGenericMethodClassMismatch, or BoxedPrimitiveMethodGuard (so that + * transparent proxies are handled correctly). Thus, any caller of this + * function better have a good explanation for why proxies are being handled + * correctly (e.g., by IsCallable) or are not an issue (E4X). + */ +extern void +ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp); + +/* + * A non-generic method is specified to report an error if args.thisv is not an + * object with a specific [[Class]] internal property (ES5 8.6.2). + * NonGenericMethodGuard performs this checking. Canonical usage is: + * + * CallArgs args = ... + * bool ok; + * JSObject *thisObj = NonGenericMethodGuard(cx, args, clasp, &ok); + * if (!thisObj) + * return ok; + * + * Specifically: if obj is a proxy, NonGenericMethodGuard will call the + * object's ProxyHandler's nativeCall hook (which may recursively call + * args.callee in args.thisv's compartment). Thus, there are three possible + * post-conditions: + * + * 1. thisv is an object of the given clasp: the caller may proceed; + * + * 2. there was an error: the caller must return 'false'; + * + * 3. thisv wrapped an object of the given clasp and the native was reentered + * and completed succesfully: the caller must return 'true'. + * + * Case 1 is indicated by a non-NULL return value; case 2 by a NULL return + * value with *ok == false; and case 3 by a NULL return value with *ok == true. + * + * NB: since this guard may reenter the native, the guard must be placed before + * any effectful operations are performed. + */ +inline JSObject * +NonGenericMethodGuard(JSContext *cx, CallArgs args, Native native, Class *clasp, bool *ok); + +/* + * NonGenericMethodGuard tests args.thisv's class using 'clasp'. If more than + * one class is acceptable (viz., isDenseArray() || isSlowArray()), the caller + * may test the class and delegate to HandleNonGenericMethodClassMismatch to + * handle the proxy case and error reporting. The 'clasp' argument is only used + * for error reporting (clasp->name). + */ +extern bool +HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native native, Class *clasp); + +/* + * Implement the extraction of a primitive from a value as needed for the + * toString, valueOf, and a few other methods of the boxed primitives classes + * Boolean, Number, and String (e.g., ES5 15.6.4.2). If 'true' is returned, the + * extracted primitive is stored in |*v|. If 'false' is returned, the caller + * must immediately 'return *ok'. For details, see NonGenericMethodGuard. + */ +template +inline bool +BoxedPrimitiveMethodGuard(JSContext *cx, CallArgs args, Native native, T *v, bool *ok); + +} /* namespace js */ #endif /* jsobj_h___ */ diff --git a/deps/mozjs/js/src/jsobjinlines.h b/deps/mozjs/js/src/jsobjinlines.h index 9413da1b1fd..395d06a5e3d 100644 --- a/deps/mozjs/js/src/jsobjinlines.h +++ b/deps/mozjs/js/src/jsobjinlines.h @@ -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 ***** @@ -42,645 +42,687 @@ #define jsobjinlines_h___ #include + +#include "jsapi.h" +#include "jsarray.h" +#include "jsbool.h" +#include "jscntxt.h" #include "jsdate.h" #include "jsfun.h" +#include "jsgcmark.h" #include "jsiter.h" #include "jslock.h" +#include "jsnum.h" #include "jsobj.h" #include "jsprobes.h" #include "jspropertytree.h" #include "jsproxy.h" #include "jsscope.h" -#include "jsstaticcheck.h" +#include "jsstr.h" #include "jstypedarray.h" #include "jsxml.h" +#include "jswrapper.h" -/* Headers included for inline implementations used by this header. */ -#include "jsbool.h" -#include "jscntxt.h" -#include "GlobalObject.h" -#include "jsnum.h" -#include "jsscriptinlines.h" -#include "jsstr.h" +#include "gc/Barrier.h" +#include "js/TemplateLib.h" +#include "vm/GlobalObject.h" +#include "vm/RegExpStatics.h" +#include "jsatominlines.h" #include "jsfuninlines.h" #include "jsgcinlines.h" -#include "jsprobes.h" +#include "jsinferinlines.h" #include "jsscopeinlines.h" +#include "jsscriptinlines.h" + +#include "gc/Barrier-inl.h" +#include "vm/String-inl.h" +#include "vm/RegExpStatics-inl.h" inline bool -JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props) +JSObject::hasPrivate() const { - JS_ASSERT(isExtensible()); + return getClass()->hasPrivate(); +} - if (js::FixOp fix = getOps()->fix) { - bool success; - if (!fix(cx, this, &success, props)) - return false; - if (!success) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY); - return false; - } - } else { - if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props)) - return false; - } +inline void *& +JSObject::privateRef(uint32_t nfixed) const +{ + /* + * The private pointer of an object can hold any word sized value. + * Private pointers are stored immediately after the last fixed slot of + * the object. + */ + JS_ASSERT(nfixed == numFixedSlots()); + JS_ASSERT(hasPrivate()); + js::HeapValue *end = &fixedSlots()[nfixed]; + return *reinterpret_cast(end); +} - if (isNative()) - extensibleShapeChange(cx); +inline void * +JSObject::getPrivate() const { return privateRef(numFixedSlots()); } - flags |= NOT_EXTENSIBLE; - return true; +inline void * +JSObject::getPrivate(size_t nfixed) const { return privateRef(nfixed); } + +inline void +JSObject::setPrivate(void *data) +{ + void **pprivate = &privateRef(numFixedSlots()); + + privateWriteBarrierPre(pprivate); + *pprivate = data; + privateWriteBarrierPost(pprivate); } inline bool -JSObject::brand(JSContext *cx) +JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) { - JS_ASSERT(!generic()); - JS_ASSERT(!branded()); - JS_ASSERT(isNative()); - generateOwnShape(cx); - if (js_IsPropertyCacheDisabled(cx)) // check for rt->shapeGen overflow - return false; - flags |= BRANDED; - return true; + JSNewEnumerateOp op = getOps()->enumerate; + return (op ? op : JS_EnumerateState)(cx, this, iterop, statep, idp); } inline bool -JSObject::unbrand(JSContext *cx) +JSObject::defaultValue(JSContext *cx, JSType hint, js::Value *vp) { - JS_ASSERT(isNative()); - if (branded()) { - generateOwnShape(cx); - if (js_IsPropertyCacheDisabled(cx)) // check for rt->shapeGen overflow + JSConvertOp op = getClass()->convert; + bool ok = (op == JS_ConvertStub ? js::DefaultValue : op)(cx, this, hint, vp); + JS_ASSERT_IF(ok, vp->isPrimitive()); + return ok; +} + +inline JSType +JSObject::typeOf(JSContext *cx) +{ + js::TypeOfOp op = getOps()->typeOf; + return (op ? op : js_TypeOf)(cx, this); +} + +inline JSObject * +JSObject::thisObject(JSContext *cx) +{ + JSObjectOp op = getOps()->thisObject; + return op ? op(cx, this) : this; +} + +inline JSBool +JSObject::setGeneric(JSContext *cx, jsid id, js::Value *vp, JSBool strict) +{ + if (getOps()->setGeneric) + return nonNativeSetProperty(cx, id, vp, strict); + return js_SetPropertyHelper(cx, this, id, 0, vp, strict); +} + +inline JSBool +JSObject::setProperty(JSContext *cx, js::PropertyName *name, js::Value *vp, JSBool strict) +{ + return setGeneric(cx, ATOM_TO_JSID(name), vp, strict); +} + +inline JSBool +JSObject::setElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict) +{ + if (getOps()->setElement) + return nonNativeSetElement(cx, index, vp, strict); + return js_SetElementHelper(cx, this, index, 0, vp, strict); +} + +inline JSBool +JSObject::setSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp, JSBool strict) +{ + return setGeneric(cx, SPECIALID_TO_JSID(sid), vp, strict); +} + +inline JSBool +JSObject::setGenericAttributes(JSContext *cx, jsid id, uintN *attrsp) +{ + js::types::MarkTypePropertyConfigured(cx, this, id); + js::GenericAttributesOp op = getOps()->setGenericAttributes; + return (op ? op : js_SetAttributes)(cx, this, id, attrsp); +} + +inline JSBool +JSObject::setPropertyAttributes(JSContext *cx, js::PropertyName *name, uintN *attrsp) +{ + return setGenericAttributes(cx, ATOM_TO_JSID(name), attrsp); +} + +inline JSBool +JSObject::setElementAttributes(JSContext *cx, uint32_t index, uintN *attrsp) +{ + js::ElementAttributesOp op = getOps()->setElementAttributes; + return (op ? op : js_SetElementAttributes)(cx, this, index, attrsp); +} + +inline JSBool +JSObject::setSpecialAttributes(JSContext *cx, js::SpecialId sid, uintN *attrsp) +{ + return setGenericAttributes(cx, SPECIALID_TO_JSID(sid), attrsp); +} + +inline JSBool +JSObject::getGeneric(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp) +{ + js::GenericIdOp op = getOps()->getGeneric; + if (op) { + if (!op(cx, this, receiver, id, vp)) + return false; + } else { + if (!js_GetProperty(cx, this, receiver, id, vp)) return false; - flags &= ~BRANDED; } - setGeneric(); return true; } -inline void -JSObject::syncSpecialEquality() +inline JSBool +JSObject::getProperty(JSContext *cx, JSObject *receiver, js::PropertyName *name, js::Value *vp) { - if (clasp->ext.equality) - flags |= JSObject::HAS_EQUALITY; + return getGeneric(cx, receiver, ATOM_TO_JSID(name), vp); } -inline void -JSObject::finalize(JSContext *cx) +inline JSBool +JSObject::getGeneric(JSContext *cx, jsid id, js::Value *vp) { - /* Cope with stillborn objects that have no map. */ - if (isNewborn()) - return; - - /* Finalize obj first, in case it needs map and slots. */ - js::Class *clasp = getClass(); - if (clasp->finalize) - clasp->finalize(cx, this); + return getGeneric(cx, this, id, vp); +} - js::Probes::finalizeObject(this); +inline JSBool +JSObject::getProperty(JSContext *cx, js::PropertyName *name, js::Value *vp) +{ + return getGeneric(cx, ATOM_TO_JSID(name), vp); +} - finish(cx); +inline bool +JSObject::deleteProperty(JSContext *cx, js::PropertyName *name, js::Value *rval, bool strict) +{ + jsid id = js_CheckForStringIndex(ATOM_TO_JSID(name)); + js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType()); + js::types::MarkTypePropertyConfigured(cx, this, id); + js::DeletePropertyOp op = getOps()->deleteProperty; + return (op ? op : js_DeleteProperty)(cx, this, name, rval, strict); } -/* - * 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) +inline bool +JSObject::deleteElement(JSContext *cx, uint32_t index, js::Value *rval, bool strict) { - init(cx, &js_CallClass, NULL, parent, NULL, false); - lastProp = bindings.lastShape(); + jsid id; + if (!js::IndexToId(cx, index, &id)) + return false; + js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType()); + js::types::MarkTypePropertyConfigured(cx, this, id); + js::DeleteElementOp op = getOps()->deleteElement; + return (op ? op : js_DeleteElement)(cx, this, index, rval, strict); +} - /* - * 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 = lastProp->shape; +inline bool +JSObject::deleteSpecial(JSContext *cx, js::SpecialId sid, js::Value *rval, bool strict) +{ + jsid id = SPECIALID_TO_JSID(sid); + js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType()); + js::types::MarkTypePropertyConfigured(cx, this, id); + js::DeleteSpecialOp op = getOps()->deleteSpecial; + return (op ? op : js_DeleteSpecial)(cx, this, sid, rval, strict); } -/* - * Initializer for cloned block objects. Set class, prototype, frame, map, and - * shape. - */ inline void -JSObject::initClonedBlock(JSContext *cx, JSObject *proto, js::StackFrame *frame) +JSObject::finalize(JSContext *cx, bool background) { - init(cx, &js_BlockClass, proto, NULL, frame, false); + js::Probes::finalizeObject(this); - /* Cloned blocks copy their prototype's map; it had better be shareable. */ - JS_ASSERT(!proto->inDictionaryMode() || proto->lastProp->frozen()); - lastProp = proto->lastProp; + if (!background) { + /* + * Finalize obj first, in case it needs map and slots. Objects with + * finalize hooks are not finalized in the background, as the class is + * stored in the object's shape, which may have already been destroyed. + */ + js::Class *clasp = getClass(); + if (clasp->finalize) + clasp->finalize(cx, this); + } - /* - * 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 = lastProp->shape; + finish(cx); } -/* - * 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)); +inline JSObject * +JSObject::getParent() const +{ + return lastProperty()->getObjectParent(); +} + +inline JSObject * +JSObject::enclosingScope() +{ + return isScope() ? &asScope().enclosingScope() : getParent(); } /* * Property read barrier for deferred cloning of compiler-created function * objects optimized as typically non-escaping, ad-hoc methods in obj. */ -inline const js::Shape * +inline js::Shape * JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp) { - JS_ASSERT(canHaveMethodBarrier()); - JS_ASSERT(hasMethodBarrier()); - JS_ASSERT(nativeContains(shape)); + JS_ASSERT(nativeContains(cx, shape)); JS_ASSERT(shape.isMethod()); - JS_ASSERT(shape.methodObject() == vp->toObject()); - JS_ASSERT(shape.writable()); - JS_ASSERT(shape.slot != SHAPE_INVALID_SLOT); - JS_ASSERT(shape.hasDefaultSetter() || shape.setterOp() == js_watch_set); + JS_ASSERT(shape.hasSlot()); + JS_ASSERT(shape.hasDefaultSetter()); JS_ASSERT(!isGlobal()); /* i.e. we are not changing the global shape */ - JSObject *funobj = &vp->toObject(); - JSFunction *fun = funobj->getFunctionPrivate(); - JS_ASSERT(fun == funobj); - JS_ASSERT(FUN_NULL_CLOSURE(fun)); + JSFunction *fun = vp->toObject().toFunction(); + JS_ASSERT(!fun->isClonedMethod()); + JS_ASSERT(fun->isNullClosure()); - funobj = CloneFunctionObject(cx, fun, funobj->getParent()); - if (!funobj) + fun = js::CloneFunctionObject(cx, fun); + if (!fun) return NULL; - funobj->setMethodObj(*this); + fun->setMethodObj(*this); /* * Replace the method property with an ordinary data property. This is * equivalent to this->setProperty(cx, shape.id, vp) except that any * watchpoint on the property is not triggered. */ - uint32 slot = shape.slot; - const js::Shape *newshape = methodShapeChange(cx, shape); + uint32_t slot = shape.slot(); + js::Shape *newshape = methodShapeChange(cx, shape); if (!newshape) return NULL; JS_ASSERT(!newshape->isMethod()); - JS_ASSERT(newshape->slot == slot); - vp->setObject(*funobj); + JS_ASSERT(newshape->slot() == slot); + vp->setObject(*fun); nativeSetSlot(slot, *vp); + return newshape; +} -#ifdef DEBUG - if (cx->runtime->functionMeterFilename) { - JS_FUNCTION_METER(cx, mreadbarrier); +inline bool +JSObject::canHaveMethodBarrier() const +{ + return isObject() || isFunction() || isPrimitive() || isDate(); +} - typedef JSRuntime::FunctionCountMap::Ptr Ptr; - if (Ptr p = cx->runtime->methodReadBarrierCountMap.lookupWithDefault(fun, 0)) - ++p->value; - } -#endif - return newshape; +inline bool +JSObject::isFixedSlot(size_t slot) +{ + JS_ASSERT(!isDenseArray()); + return slot < numFixedSlots(); } -static JS_ALWAYS_INLINE bool -ChangesMethodValue(const js::Value &prev, const js::Value &v) +inline size_t +JSObject::dynamicSlotIndex(size_t slot) { - JSObject *prevObj; - return prev.isObject() && (prevObj = &prev.toObject())->isFunction() && - (!v.isObject() || &v.toObject() != prevObj); + JS_ASSERT(!isDenseArray() && slot >= numFixedSlots()); + return slot - numFixedSlots(); } -inline const js::Shape * -JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v) +/*static*/ inline size_t +JSObject::dynamicSlotsCount(size_t nfixed, size_t span) { - if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) { - const js::Value &prev = nativeGetSlot(shape.slot); + if (span <= nfixed) + return 0; + span -= nfixed; + if (span <= SLOT_CAPACITY_MIN) + return SLOT_CAPACITY_MIN; - if (ChangesMethodValue(prev, v)) { - JS_FUNCTION_METER(cx, mwritebarrier); - return methodShapeChange(cx, shape); - } - } - return &shape; + size_t slots = js::RoundUpPow2(span); + JS_ASSERT(slots >= span); + return slots; } -inline bool -JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v) +inline size_t +JSObject::numDynamicSlots() const { - if (brandedOrHasMethodBarrier()) { - const js::Value &prev = nativeGetSlot(slot); + return dynamicSlotsCount(numFixedSlots(), slotSpan()); +} - if (ChangesMethodValue(prev, v)) { - JS_FUNCTION_METER(cx, mwslotbarrier); - return methodShapeChange(cx, slot); - } - } - return true; +inline void +JSObject::setLastPropertyInfallible(const js::Shape *shape) +{ + JS_ASSERT(!shape->inDictionary()); + JS_ASSERT(shape->compartment() == compartment()); + JS_ASSERT(!inDictionaryMode()); + JS_ASSERT(slotSpan() == shape->slotSpan()); + JS_ASSERT(numFixedSlots() == shape->numFixedSlots()); + + shape_ = const_cast(shape); +} + +inline void +JSObject::removeLastProperty(JSContext *cx) +{ + JS_ASSERT(canRemoveLastProperty()); + JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous())); } inline bool -JSObject::ensureClassReservedSlots(JSContext *cx) +JSObject::canRemoveLastProperty() +{ + /* + * Check that the information about the object stored in the last + * property's base shape is consistent with that stored in the previous + * shape. If not consistent, then the last property cannot be removed as it + * will induce a change in the object itself, and the object must be + * converted to dictionary mode instead. See BaseShape comment in jsscope.h + */ + JS_ASSERT(!inDictionaryMode()); + const js::Shape *previous = lastProperty()->previous(); + return previous->getObjectParent() == lastProperty()->getObjectParent() + && previous->getObjectFlags() == lastProperty()->getObjectFlags(); +} + +inline const js::HeapValue * +JSObject::getRawSlots() { - return !nativeEmpty() || ensureClassReservedSlotsForEmptyObject(cx); + JS_ASSERT(isGlobal()); + return slots; } -inline js::Value +inline const js::Value & JSObject::getReservedSlot(uintN index) const { - return (index < numSlots()) ? getSlot(index) : js::UndefinedValue(); + JS_ASSERT(index < JSSLOT_FREE(getClass())); + return getSlot(index); } -inline bool -JSObject::canHaveMethodBarrier() const +inline js::HeapValue & +JSObject::getReservedSlotRef(uintN index) { - return isObject() || isFunction() || isPrimitive() || isDate(); + JS_ASSERT(index < JSSLOT_FREE(getClass())); + return getSlotRef(index); } -inline bool -JSObject::isPrimitive() const +inline void +JSObject::setReservedSlot(uintN index, const js::Value &v) { - return isNumber() || isString() || isBoolean(); + JS_ASSERT(index < JSSLOT_FREE(getClass())); + setSlot(index, v); } inline const js::Value & JSObject::getPrimitiveThis() const { JS_ASSERT(isPrimitive()); - return getSlot(JSSLOT_PRIMITIVE_THIS); + return getFixedSlot(JSSLOT_PRIMITIVE_THIS); } inline void JSObject::setPrimitiveThis(const js::Value &pthis) { JS_ASSERT(isPrimitive()); - setSlot(JSSLOT_PRIMITIVE_THIS, pthis); + setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis); } -inline /* gc::FinalizeKind */ unsigned -JSObject::finalizeKind() const +inline bool +JSObject::hasContiguousSlots(size_t start, size_t count) const { - return js::gc::FinalizeKind(arenaHeader()->getThingKind()); + /* + * Check that the range [start, start+count) is either all inline or all + * out of line. + */ + JS_ASSERT(slotInRange(start + count, SENTINEL_ALLOWED)); + return (start + count <= numFixedSlots()) || (start >= numFixedSlots()); } -inline size_t -JSObject::numFixedSlots() const +inline void +JSObject::prepareSlotRangeForOverwrite(size_t start, size_t end) { - if (isFunction()) - return JSObject::FUN_CLASS_RESERVED_SLOTS; - if (!hasSlotsArray()) - return capacity; - return js::gc::GetGCKindSlots(js::gc::FinalizeKind(finalizeKind())); + for (size_t i = start; i < end; i++) + getSlotAddressUnchecked(i)->js::HeapValue::~HeapValue(); } -inline size_t -JSObject::slotsAndStructSize(uint32 nslots) const +inline void +JSObject::prepareElementRangeForOverwrite(size_t start, size_t end) { - bool isFun = isFunction() && this == (JSObject*) getPrivate(); - - int ndslots = hasSlotsArray() ? nslots : 0; - int nfslots = isFun ? 0 : numFixedSlots(); - - return sizeof(js::Value) * (ndslots + nfslots) - + isFun ? sizeof(JSFunction) : sizeof(JSObject); + JS_ASSERT(isDenseArray()); + JS_ASSERT(end <= getDenseArrayInitializedLength()); + for (size_t i = start; i < end; i++) + elements[i].js::HeapValue::~HeapValue(); } -inline uint32 +inline uint32_t JSObject::getArrayLength() const { JS_ASSERT(isArray()); - return (uint32)(size_t) getPrivate(); + return getElementsHeader()->length; } inline void -JSObject::setArrayLength(uint32 length) +JSObject::setArrayLength(JSContext *cx, uint32_t length) { JS_ASSERT(isArray()); - setPrivate((void*) length); -} -inline uint32 -JSObject::getDenseArrayCapacity() -{ - JS_ASSERT(isDenseArray()); - return numSlots(); -} + if (length > INT32_MAX) { + /* + * Mark the type of this object as possibly not a dense array, per the + * requirements of OBJECT_FLAG_NON_DENSE_ARRAY. + */ + js::types::MarkTypeObjectFlags(cx, this, + js::types::OBJECT_FLAG_NON_PACKED_ARRAY | + js::types::OBJECT_FLAG_NON_DENSE_ARRAY); + jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + js::types::AddTypePropertyId(cx, this, lengthId, + js::types::Type::DoubleType()); + } -inline js::Value* -JSObject::getDenseArrayElements() -{ - JS_ASSERT(isDenseArray()); - return getSlots(); + getElementsHeader()->length = length; } -inline const js::Value & -JSObject::getDenseArrayElement(uintN idx) +inline void +JSObject::setDenseArrayLength(uint32_t length) { + /* Variant of setArrayLength for use on dense arrays where the length cannot overflow int32. */ JS_ASSERT(isDenseArray()); - return getSlot(idx); + JS_ASSERT(length <= INT32_MAX); + getElementsHeader()->length = length; } -inline js::Value * -JSObject::addressOfDenseArrayElement(uintN idx) +inline uint32_t +JSObject::getDenseArrayInitializedLength() { JS_ASSERT(isDenseArray()); - return &getSlotRef(idx); + return getElementsHeader()->initializedLength; } inline void -JSObject::setDenseArrayElement(uintN idx, const js::Value &val) +JSObject::setDenseArrayInitializedLength(uint32_t length) { JS_ASSERT(isDenseArray()); - setSlot(idx, val); + JS_ASSERT(length <= getDenseArrayCapacity()); + prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength); + getElementsHeader()->initializedLength = length; } -inline void -JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap) +inline uint32_t +JSObject::getDenseArrayCapacity() { JS_ASSERT(isDenseArray()); - shrinkSlots(cx, cap); -} - -inline void -JSObject::setArgsLength(uint32 argc) -{ - JS_ASSERT(isArguments()); - JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); - JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT)); - getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT); - JS_ASSERT(!isArgsLengthOverridden()); -} - -inline uint32 -JSObject::getArgsInitialLength() const -{ - JS_ASSERT(isArguments()); - uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT; - JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); - return argc; + return getElementsHeader()->capacity; } -inline void -JSObject::setArgsLengthOverridden() +inline bool +JSObject::ensureElements(JSContext *cx, uint32_t capacity) { - JS_ASSERT(isArguments()); - getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT; + if (capacity > getDenseArrayCapacity()) + return growElements(cx, capacity); + return true; } -inline bool -JSObject::isArgsLengthOverridden() const +inline js::HeapValueArray +JSObject::getDenseArrayElements() { - JS_ASSERT(isArguments()); - const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH); - return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT; + JS_ASSERT(isDenseArray()); + return js::HeapValueArray(elements); } -inline js::ArgumentsData * -JSObject::getArgsData() const +inline const js::Value & +JSObject::getDenseArrayElement(uintN idx) { - JS_ASSERT(isArguments()); - return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate(); + JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength()); + return elements[idx]; } inline void -JSObject::setArgsData(js::ArgumentsData *data) +JSObject::setDenseArrayElement(uintN idx, const js::Value &val) { - JS_ASSERT(isArguments()); - getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data); + JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength()); + elements[idx] = val; } -inline const js::Value & -JSObject::getArgsCallee() const +inline void +JSObject::initDenseArrayElement(uintN idx, const js::Value &val) { - return getArgsData()->callee; + JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength()); + elements[idx].init(val); } inline void -JSObject::setArgsCallee(const js::Value &callee) +JSObject::setDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val) { - getArgsData()->callee = callee; + js::types::AddTypePropertyId(cx, this, JSID_VOID, val); + setDenseArrayElement(idx, val); } -inline const js::Value & -JSObject::getArgsElement(uint32 i) const +inline void +JSObject::initDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val) { - JS_ASSERT(isArguments()); - JS_ASSERT(i < getArgsInitialLength()); - return getArgsData()->slots[i]; + js::types::AddTypePropertyId(cx, this, JSID_VOID, val); + initDenseArrayElement(idx, val); } -inline js::Value * -JSObject::getArgsElements() const +inline void +JSObject::copyDenseArrayElements(uintN dstStart, const js::Value *src, uintN count) { - JS_ASSERT(isArguments()); - return getArgsData()->slots; + JS_ASSERT(dstStart + count <= getDenseArrayCapacity()); + JSCompartment *comp = compartment(); + for (unsigned i = 0; i < count; ++i) + elements[dstStart + i].set(comp, src[i]); } -inline js::Value * -JSObject::addressOfArgsElement(uint32 i) +inline void +JSObject::initDenseArrayElements(uintN dstStart, const js::Value *src, uintN count) { - JS_ASSERT(isArguments()); - JS_ASSERT(i < getArgsInitialLength()); - return &getArgsData()->slots[i]; + JS_ASSERT(dstStart + count <= getDenseArrayCapacity()); + JSCompartment *comp = compartment(); + for (unsigned i = 0; i < count; ++i) + elements[dstStart + i].init(comp, src[i]); } inline void -JSObject::setArgsElement(uint32 i, const js::Value &v) +JSObject::moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count) { - JS_ASSERT(isArguments()); - JS_ASSERT(i < getArgsInitialLength()); - getArgsData()->slots[i] = v; -} + JS_ASSERT(dstStart + count <= getDenseArrayCapacity()); + JS_ASSERT(srcStart + count <= getDenseArrayInitializedLength()); -inline bool -JSObject::callIsForEval() const -{ - JS_ASSERT(isCall()); - JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull()); - JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(), - getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction()); - return getSlot(JSSLOT_CALL_CALLEE).isNull(); -} + /* + * Use a custom write barrier here since it's performance sensitive. We + * only want to barrier the elements that are being overwritten. + */ + uintN markStart, markEnd; + if (dstStart > srcStart) { + markStart = js::Max(srcStart + count, dstStart); + markEnd = dstStart + count; + } else { + markStart = dstStart; + markEnd = js::Min(dstStart + count, srcStart); + } + prepareElementRangeForOverwrite(markStart, markEnd); -inline js::StackFrame * -JSObject::maybeCallObjStackFrame() const -{ - JS_ASSERT(isCall()); - return reinterpret_cast(getPrivate()); + memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); } inline void -JSObject::setCallObjCallee(JSObject *callee) -{ - JS_ASSERT(isCall()); - JS_ASSERT_IF(callee, callee->isFunction()); - return getSlotRef(JSSLOT_CALL_CALLEE).setObjectOrNull(callee); -} - -inline JSObject * -JSObject::getCallObjCallee() const +JSObject::moveDenseArrayElementsUnbarriered(uintN dstStart, uintN srcStart, uintN count) { - JS_ASSERT(isCall()); - return getSlot(JSSLOT_CALL_CALLEE).toObjectOrNull(); -} + JS_ASSERT(!compartment()->needsBarrier()); -inline JSFunction * -JSObject::getCallObjCalleeFunction() const -{ - JS_ASSERT(isCall()); - return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate(); -} + JS_ASSERT(dstStart + count <= getDenseArrayCapacity()); + JS_ASSERT(srcStart + count <= getDenseArrayCapacity()); -inline const js::Value & -JSObject::getCallObjArguments() const -{ - JS_ASSERT(isCall()); - JS_ASSERT(!callIsForEval()); - return getSlot(JSSLOT_CALL_ARGUMENTS); + memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); } -inline void -JSObject::setCallObjArguments(const js::Value &v) +inline bool +JSObject::denseArrayHasInlineSlots() const { - JS_ASSERT(isCall()); - JS_ASSERT(!callIsForEval()); - setSlot(JSSLOT_CALL_ARGUMENTS, v); + JS_ASSERT(isDenseArray()); + return elements == fixedElements(); } -inline const js::Value & -JSObject::callObjArg(uintN i) const -{ - JS_ASSERT(isCall()); - JS_ASSERT(i < getCallObjCalleeFunction()->nargs); - return getSlot(JSObject::CALL_RESERVED_SLOTS + i); -} +namespace js { -inline js::Value & -JSObject::callObjArg(uintN i) -{ - JS_ASSERT(isCall()); - JS_ASSERT(i < getCallObjCalleeFunction()->nargs); - return getSlotRef(JSObject::CALL_RESERVED_SLOTS + i); +inline JSObject * +ValueToObjectOrPrototype(JSContext *cx, const Value &v) +{ + if (v.isObject()) + return &v.toObject(); + GlobalObject *global = &cx->fp()->scopeChain().global(); + if (v.isString()) + return global->getOrCreateStringPrototype(cx); + if (v.isNumber()) + return global->getOrCreateNumberPrototype(cx); + if (v.isBoolean()) + return global->getOrCreateBooleanPrototype(cx); + JS_ASSERT(v.isNull() || v.isUndefined()); + js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL); + return NULL; } -inline const js::Value & -JSObject::callObjVar(uintN i) const +/* + * Any name atom for a function which will be added as a DeclEnv object to the + * scope chain above call objects for fun. + */ +static inline JSAtom * +CallObjectLambdaName(JSFunction *fun) { - JSFunction *fun = getCallObjCalleeFunction(); - JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs()); - JS_ASSERT(i < fun->script()->bindings.countVars()); - return getSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i); + return (fun->flags & JSFUN_LAMBDA) ? fun->atom : NULL; } -inline js::Value & -JSObject::callObjVar(uintN i) -{ - JSFunction *fun = getCallObjCalleeFunction(); - JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs()); - JS_ASSERT(i < fun->script()->bindings.countVars()); - return getSlotRef(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i); -} +} /* namespace js */ inline const js::Value & JSObject::getDateUTCTime() const { JS_ASSERT(isDate()); - return getSlot(JSSLOT_DATE_UTC_TIME); + return getFixedSlot(JSSLOT_DATE_UTC_TIME); } inline void JSObject::setDateUTCTime(const js::Value &time) { JS_ASSERT(isDate()); - setSlot(JSSLOT_DATE_UTC_TIME, time); + setFixedSlot(JSSLOT_DATE_UTC_TIME, time); } -inline js::Value * -JSObject::getFlatClosureUpvars() const +inline js::NativeIterator * +JSObject::getNativeIterator() const { -#ifdef DEBUG - JSFunction *fun = getFunctionPrivate(); - JS_ASSERT(fun->isFlatClosure()); - JS_ASSERT(fun->script()->bindings.countUpvars() == fun->script()->upvars()->length); -#endif - return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate(); + return (js::NativeIterator *) getPrivate(); } -inline js::Value -JSObject::getFlatClosureUpvar(uint32 i) const +inline void +JSObject::setNativeIterator(js::NativeIterator *ni) { - JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars()); - return getFlatClosureUpvars()[i]; + setPrivate(ni); } -inline js::Value & -JSObject::getFlatClosureUpvar(uint32 i) +inline JSLinearString * +JSObject::getNamePrefix() const { - JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars()); - return getFlatClosureUpvars()[i]; + JS_ASSERT(isNamespace() || isQName()); + const js::Value &v = getSlot(JSSLOT_NAME_PREFIX); + return !v.isUndefined() ? &v.toString()->asLinear() : NULL; } -inline void -JSObject::setFlatClosureUpvars(js::Value *upvars) +inline jsval +JSObject::getNamePrefixVal() const { - JS_ASSERT(isFunction()); - JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate())); - getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars); + JS_ASSERT(isNamespace() || isQName()); + return getSlot(JSSLOT_NAME_PREFIX); } -inline bool -JSObject::hasMethodObj(const JSObject& obj) const +inline void +JSObject::setNamePrefix(JSLinearString *prefix) { - return JSSLOT_FUN_METHOD_OBJ < numSlots() && - getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() && - getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == obj; -} - -inline void -JSObject::setMethodObj(JSObject& obj) -{ - getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj); -} - -inline js::NativeIterator * -JSObject::getNativeIterator() const -{ - return (js::NativeIterator *) getPrivate(); -} - -inline void -JSObject::setNativeIterator(js::NativeIterator *ni) -{ - setPrivate(ni); -} - -inline JSLinearString * -JSObject::getNamePrefix() const -{ - JS_ASSERT(isNamespace() || isQName()); - const js::Value &v = getSlot(JSSLOT_NAME_PREFIX); - return !v.isUndefined() ? &v.toString()->asLinear() : NULL; -} - -inline jsval -JSObject::getNamePrefixVal() const -{ - JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); -} - -inline void -JSObject::setNamePrefix(JSLinearString *prefix) -{ - JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_PREFIX, prefix ? js::StringValue(prefix) : js::UndefinedValue()); + JS_ASSERT(isNamespace() || isQName()); + setSlot(JSSLOT_NAME_PREFIX, prefix ? js::StringValue(prefix) : js::UndefinedValue()); } inline void @@ -702,7 +744,7 @@ inline jsval JSObject::getNameURIVal() const { JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); + return getSlot(JSSLOT_NAME_URI); } inline void @@ -716,128 +758,327 @@ inline jsval JSObject::getNamespaceDeclared() const { JS_ASSERT(isNamespace()); - return js::Jsvalify(getSlot(JSSLOT_NAMESPACE_DECLARED)); + return getSlot(JSSLOT_NAMESPACE_DECLARED); } inline void JSObject::setNamespaceDeclared(jsval decl) { JS_ASSERT(isNamespace()); - setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); + setSlot(JSSLOT_NAMESPACE_DECLARED, decl); } -inline JSLinearString * +inline JSAtom * JSObject::getQNameLocalName() const { JS_ASSERT(isQName()); const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME); - return !v.isUndefined() ? &v.toString()->asLinear() : NULL; + return !v.isUndefined() ? &v.toString()->asAtom() : NULL; } inline jsval JSObject::getQNameLocalNameVal() const { JS_ASSERT(isQName()); - return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); + return getSlot(JSSLOT_QNAME_LOCAL_NAME); } inline void -JSObject::setQNameLocalName(JSLinearString *name) +JSObject::setQNameLocalName(JSAtom *name) { JS_ASSERT(isQName()); setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue()); } -inline JSObject * -JSObject::getWithThis() const +inline bool +JSObject::setSingletonType(JSContext *cx) { - return &getSlot(JSSLOT_WITH_THIS).toObject(); + if (!cx->typeInferenceEnabled()) + return true; + + JS_ASSERT(!lastProperty()->previous()); + JS_ASSERT(!hasLazyType()); + JS_ASSERT_IF(getProto(), type() == getProto()->getNewType(cx, NULL)); + + js::types::TypeObject *type = cx->compartment->getLazyType(cx, getProto()); + if (!type) + return false; + + type_ = type; + return true; } -inline void -JSObject::setWithThis(JSObject *thisp) +inline js::types::TypeObject * +JSObject::getType(JSContext *cx) { - getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp); + if (hasLazyType()) + makeLazyType(cx); + return type_; } -inline void -JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, bool useHoles) +inline bool +JSObject::clearType(JSContext *cx) { - clasp = aclasp; - flags = 0; + JS_ASSERT(!hasSingletonType()); + + js::types::TypeObject *type = cx->compartment->getEmptyType(cx); + if (!type) + return false; + type_ = type; + return true; +} + +inline void +JSObject::setType(js::types::TypeObject *newType) +{ #ifdef DEBUG - /* - * NB: objShape must not be set here; rather, the caller must call setMap - * or setSharedNonNativeMap after calling init. To defend this requirement - * we set objShape to a value that obj->shape() is asserted never to return. - */ - objShape = INVALID_SHAPE; + JS_ASSERT(newType); + for (JSObject *obj = newType->proto; obj; obj = obj->getProto()) + JS_ASSERT(obj != this); #endif + JS_ASSERT_IF(hasSpecialEquality(), + newType->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY)); + JS_ASSERT(!hasSingletonType()); + type_ = newType; +} + +inline bool JSObject::setIteratedSingleton(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::ITERATED_SINGLETON); +} + +inline bool JSObject::isSystem() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::SYSTEM); +} - setProto(proto); - setParent(parent); +inline bool JSObject::setSystem(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::SYSTEM); +} - privateData = priv; - slots = fixedSlots(); +inline bool JSObject::isDelegate() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::DELEGATE); +} - /* - * Fill the fixed slots with undefined or array holes. This object must - * already have its capacity filled in, as by js_NewGCObject. - */ - JS_ASSERT(capacity == numFixedSlots()); - ClearValueRange(slots, capacity, useHoles); +inline bool JSObject::setDelegate(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE); +} - emptyShapes = NULL; +inline bool JSObject::isVarObj() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ); } -inline void -JSObject::finish(JSContext *cx) +inline bool JSObject::setVarObj(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::VAROBJ); +} + +inline bool JSObject::setWatched(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::WATCHED, GENERATE_SHAPE); +} + +inline bool JSObject::hasUncacheableProto() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO); +} + +inline bool JSObject::setUncacheableProto(JSContext *cx) +{ + return setFlag(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE); +} + +inline bool JSObject::isExtensible() const +{ + return !lastProperty()->hasObjectFlag(js::BaseShape::NOT_EXTENSIBLE); +} + +inline bool JSObject::isBoundFunction() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION); +} + +inline bool JSObject::isIndexed() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED); +} + +inline bool JSObject::watched() const +{ + return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED); +} + +inline bool JSObject::hasSpecialEquality() const +{ + return !!getClass()->ext.equality; +} + +inline bool JSObject::isArguments() const { return isNormalArguments() || isStrictArguments(); } +inline bool JSObject::isArrayBuffer() const { return hasClass(&js::ArrayBufferClass); } +inline bool JSObject::isBlock() const { return hasClass(&js::BlockClass); } +inline bool JSObject::isBoolean() const { return hasClass(&js::BooleanClass); } +inline bool JSObject::isCall() const { return hasClass(&js::CallClass); } +inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); } +inline bool JSObject::isDate() const { return hasClass(&js::DateClass); } +inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); } +inline bool JSObject::isElementIterator() const { return hasClass(&js::ElementIteratorClass); } +inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); } +inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); } +inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); } +inline bool JSObject::isGenerator() const { return hasClass(&js::GeneratorClass); } +inline bool JSObject::isIterator() const { return hasClass(&js::IteratorClass); } +inline bool JSObject::isNamespace() const { return hasClass(&js::NamespaceClass); } +inline bool JSObject::isNestedScope() const { return isBlock() || isWith(); } +inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); } +inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); } +inline bool JSObject::isObject() const { return hasClass(&js::ObjectClass); } +inline bool JSObject::isPrimitive() const { return isNumber() || isString() || isBoolean(); } +inline bool JSObject::isRegExp() const { return hasClass(&js::RegExpClass); } +inline bool JSObject::isRegExpStatics() const { return hasClass(&js::RegExpStaticsClass); } +inline bool JSObject::isScope() const { return isCall() || isDeclEnv() || isNestedScope(); } +inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); } +inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); } +inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); } +inline bool JSObject::isString() const { return hasClass(&js::StringClass); } +inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); } +inline bool JSObject::isWith() const { return hasClass(&js::WithClass); } +inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); } + +inline bool JSObject::isArray() const +{ + return isSlowArray() || isDenseArray(); +} + +inline bool JSObject::isDenseArray() const +{ + bool result = hasClass(&js::ArrayClass); + JS_ASSERT_IF(result, elements != js::emptyObjectElements); + return result; +} + +inline bool JSObject::isSlowArray() const +{ + bool result = hasClass(&js::SlowArrayClass); + JS_ASSERT_IF(result, elements != js::emptyObjectElements); + return result; +} + +inline bool +JSObject::isXMLId() const { - if (hasSlotsArray()) - freeSlotsArray(cx); - if (emptyShapes) - cx->free_(emptyShapes); + return hasClass(&js::QNameClass) + || hasClass(&js::AttributeNameClass) + || hasClass(&js::AnyNameClass); } inline bool -JSObject::initSharingEmptyShape(JSContext *cx, - js::Class *aclasp, - JSObject *proto, - JSObject *parent, - void *privateValue, - /* js::gc::FinalizeKind */ unsigned kind) +JSObject::isQName() const { - init(cx, aclasp, proto, parent, privateValue, false); + return hasClass(&js::QNameClass) + || hasClass(&js::AttributeNameClass) + || hasClass(&js::AnyNameClass); +} +inline void +JSObject::initializeSlotRange(size_t start, size_t length) +{ + /* + * No bounds check, as this is used when the object's shape does not + * reflect its allocated slots (updateSlotsForSpan). + */ JS_ASSERT(!isDenseArray()); + size_t fixed = numFixedSlots(); + if (start < fixed) { + if (start + length < fixed) { + js::InitValueRange(fixedSlots() + start, length, false); + } else { + size_t localClear = fixed - start; + js::InitValueRange(fixedSlots() + start, localClear, false); + js::InitValueRange(slots, length - localClear, false); + } + } else { + js::InitValueRange(slots + start - fixed, length, false); + } +} - js::EmptyShape *empty = proto->getEmptyShape(cx, aclasp, kind); - if (!empty) - return false; +/* static */ inline JSObject * +JSObject::create(JSContext *cx, js::gc::AllocKind kind, + js::HandleShape shape, js::HandleTypeObject type, js::HeapValue *slots) +{ + /* + * Callers must use dynamicSlotsCount to size the initial slot array of the + * object. We can't check the allocated capacity of the dynamic slots, but + * make sure their presence is consistent with the shape. + */ + JS_ASSERT(shape && type); + JS_ASSERT(!!dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()) == !!slots); + JS_ASSERT(js::gc::GetGCKindSlots(kind, shape->getObjectClass()) == shape->numFixedSlots()); - setMap(empty); - return true; + JSObject *obj = js_NewGCObject(cx, kind); + if (!obj) + return NULL; + + obj->shape_.init(shape); + obj->type_.init(type); + obj->slots = slots; + obj->elements = js::emptyObjectElements; + + if (shape->getObjectClass()->hasPrivate()) + obj->privateRef(shape->numFixedSlots()) = NULL; + + if (size_t span = shape->slotSpan()) + obj->initializeSlotRange(0, span); + + return obj; } -inline void -JSObject::freeSlotsArray(JSContext *cx) +/* static */ inline JSObject * +JSObject::createDenseArray(JSContext *cx, js::gc::AllocKind kind, + js::HandleShape shape, js::HandleTypeObject type, + uint32_t length) { - JS_ASSERT(hasSlotsArray()); - cx->free_(slots); + JS_ASSERT(shape && type); + JS_ASSERT(shape->getObjectClass() == &js::ArrayClass); + + /* + * Dense arrays are non-native, and never have properties to store. + * The number of fixed slots in the shape of such objects is zero. + */ + JS_ASSERT(shape->numFixedSlots() == 0); + + /* + * The array initially stores its elements inline, there must be enough + * space for an elements header. + */ + JS_ASSERT(js::gc::GetGCKindSlots(kind) >= js::ObjectElements::VALUES_PER_HEADER); + + uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER; + + JSObject *obj = js_NewGCObject(cx, kind); + if (!obj) + return NULL; + + obj->shape_.init(shape); + obj->type_.init(type); + obj->slots = NULL; + obj->setFixedElements(); + new (obj->getElementsHeader()) js::ObjectElements(capacity, length); + + return obj; } inline void -JSObject::revertToFixedSlots(JSContext *cx) +JSObject::finish(JSContext *cx) { - JS_ASSERT(hasSlotsArray()); - size_t fixed = numFixedSlots(); - JS_ASSERT(capacity >= fixed); - memcpy(fixedSlots(), slots, fixed * sizeof(js::Value)); - freeSlotsArray(cx); - slots = fixedSlots(); - capacity = fixed; + if (hasDynamicSlots()) + cx->free_(slots); + if (hasDynamicElements()) + cx->free_(getElementsHeader()); } inline bool @@ -846,7 +1087,7 @@ JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags) JSObject *pobj; JSProperty *prop; JSAutoResolveFlags rf(cx, flags); - if (!lookupProperty(cx, id, &pobj, &prop)) + if (!lookupGeneric(cx, id, &pobj, &prop)) return false; *foundp = !!prop; return true; @@ -861,51 +1102,25 @@ JSObject::isCallable() inline JSPrincipals * JSObject::principals(JSContext *cx) { - JSPrincipals *compPrincipals = compartment()->principals; -#ifdef DEBUG - if (!compPrincipals) - return NULL; - - /* - * Assert that the compartment's principals are either the same or - * equivalent to those we would find through security hooks. - */ JSSecurityCallbacks *cb = JS_GetSecurityCallbacks(cx); - if (JSObjectPrincipalsFinder finder = cb ? cb->findObjectPrincipals : NULL) { - JSPrincipals *hookPrincipals = finder(cx, this); - JS_ASSERT(hookPrincipals == compPrincipals || - (hookPrincipals->subsume(hookPrincipals, compPrincipals) && - compPrincipals->subsume(compPrincipals, hookPrincipals))); - } -#endif - return compPrincipals; + if (JSObjectPrincipalsFinder finder = cb ? cb->findObjectPrincipals : NULL) + return finder(cx, this); + return cx->compartment ? cx->compartment->principals : NULL; } -inline uint32 +inline uint32_t JSObject::slotSpan() const { - return lastProp->slotSpan; + if (inDictionaryMode()) + return lastProperty()->base()->slotSpan(); + return lastProperty()->slotSpan(); } -inline bool -JSObject::containsSlot(uint32 slot) const -{ - return slot < slotSpan(); -} - -inline void -JSObject::setMap(js::Shape *amap) -{ - JS_ASSERT(!hasOwnShape()); - lastProp = amap; - objShape = lastProp->shape; -} - -inline js::Value & +inline js::HeapValue & JSObject::nativeGetSlotRef(uintN slot) { JS_ASSERT(isNative()); - JS_ASSERT(containsSlot(slot)); + JS_ASSERT(slot < slotSpan()); return getSlotRef(slot); } @@ -913,75 +1128,57 @@ inline const js::Value & JSObject::nativeGetSlot(uintN slot) const { JS_ASSERT(isNative()); - JS_ASSERT(containsSlot(slot)); + JS_ASSERT(slot < slotSpan()); return getSlot(slot); } -inline void -JSObject::nativeSetSlot(uintN slot, const js::Value &value) -{ - JS_ASSERT(isNative()); - JS_ASSERT(containsSlot(slot)); - return setSlot(slot, value); -} - -inline bool -JSObject::isNative() const +inline JSFunction * +JSObject::nativeGetMethod(const js::Shape *shape) const { - return lastProp->isNative(); -} + /* + * For method shapes, this object must have an uncloned function object in + * the shape's slot. + */ + JS_ASSERT(shape->isMethod()); +#ifdef DEBUG + JSObject *obj = &nativeGetSlot(shape->slot()).toObject(); + JS_ASSERT(obj->isFunction() && !obj->toFunction()->isClonedMethod()); +#endif -inline bool -JSObject::isNewborn() const -{ - return !lastProp; + return static_cast(&nativeGetSlot(shape->slot()).toObject()); } inline void -JSObject::clearOwnShape() +JSObject::nativeSetSlot(uintN slot, const js::Value &value) { - flags &= ~OWN_SHAPE; - objShape = lastProp->shape; + JS_ASSERT(isNative()); + JS_ASSERT(slot < slotSpan()); + return setSlot(slot, value); } inline void -JSObject::setOwnShape(uint32 s) -{ - flags |= OWN_SHAPE; - objShape = s; -} - -inline js::Shape ** -JSObject::nativeSearch(jsid id, bool adding) -{ - return js::Shape::search(compartment()->rt, &lastProp, id, adding); -} - -inline const js::Shape * -JSObject::nativeLookup(jsid id) +JSObject::nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value) { - JS_ASSERT(isNative()); - return SHAPE_FETCH(nativeSearch(id)); + nativeSetSlot(shape->slot(), value); + js::types::AddTypePropertyId(cx, this, shape->propid(), value); } inline bool -JSObject::nativeContains(jsid id) +JSObject::isNative() const { - return nativeLookup(id) != NULL; + return lastProperty()->isNative(); } inline bool -JSObject::nativeContains(const js::Shape &shape) +JSObject::nativeContains(JSContext *cx, jsid id) { - return nativeLookup(shape.id) == &shape; + return nativeLookup(cx, id) != NULL; } -inline const js::Shape * -JSObject::lastProperty() const +inline bool +JSObject::nativeContains(JSContext *cx, const js::Shape &shape) { - JS_ASSERT(isNative()); - JS_ASSERT(!JSID_IS_VOID(lastProp->id)); - return lastProp; + return nativeLookup(cx, shape.propid()) == &shape; } inline bool @@ -996,7 +1193,7 @@ JSObject::inDictionaryMode() const return lastProperty()->inDictionary(); } -inline uint32 +inline uint32_t JSObject::propertyCount() const { return lastProperty()->entryCount(); @@ -1008,75 +1205,331 @@ JSObject::hasPropertyTable() const return lastProperty()->hasTable(); } -/* - * FIXME: shape must not be null, should use a reference here and other places. - */ -inline void -JSObject::setLastProperty(const js::Shape *shape) +inline size_t +JSObject::sizeOfThis() const { - JS_ASSERT(!inDictionaryMode()); - JS_ASSERT(!JSID_IS_VOID(shape->id)); - JS_ASSERT_IF(lastProp, !JSID_IS_VOID(lastProp->id)); - JS_ASSERT(shape->compartment() == compartment()); + return arenaHeader()->getThingSize(); +} + +inline size_t +JSObject::computedSizeOfThisSlotsElements() const +{ + size_t n = sizeOfThis(); - lastProp = const_cast(shape); + if (hasDynamicSlots()) + n += numDynamicSlots() * sizeof(js::Value); + + if (hasDynamicElements()) + n += (js::ObjectElements::VALUES_PER_HEADER + getElementsHeader()->capacity) * + sizeof(js::Value); + + return n; } inline void -JSObject::removeLastProperty() +JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, + size_t *slotsSize, size_t *elementsSize, + size_t *miscSize) const { - JS_ASSERT(!inDictionaryMode()); - JS_ASSERT(!JSID_IS_VOID(lastProp->parent->id)); + *slotsSize = 0; + if (hasDynamicSlots()) { + *slotsSize += mallocSizeOf(slots); + } - lastProp = lastProp->parent; + *elementsSize = 0; + if (hasDynamicElements()) { + *elementsSize += mallocSizeOf(getElementsHeader()); + } + + /* Other things may be measured in the future if DMD indicates it is worthwhile. */ + *miscSize = 0; + if (isFunction()) { + *miscSize += toFunction()->sizeOfMisc(mallocSizeOf); + } else if (isArguments()) { + *miscSize += asArguments().sizeOfMisc(mallocSizeOf); + } else if (isRegExpStatics()) { + *miscSize += js::SizeOfRegExpStaticsData(this, mallocSizeOf); + } } -inline void -JSObject::setSharedNonNativeMap() +inline JSBool +JSObject::lookupGeneric(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp) { - setMap(&js::Shape::sharedNonNative); + js::LookupGenericOp op = getOps()->lookupGeneric; + return (op ? op : js_LookupProperty)(cx, this, id, objp, propp); } -static inline bool -js_IsCallable(const js::Value &v) +inline JSBool +JSObject::lookupProperty(JSContext *cx, js::PropertyName *name, JSObject **objp, JSProperty **propp) { - return v.isObject() && v.toObject().isCallable(); + return lookupGeneric(cx, ATOM_TO_JSID(name), objp, propp); } -namespace js { +inline JSBool +JSObject::defineGeneric(JSContext *cx, jsid id, const js::Value &value, + JSPropertyOp getter /* = JS_PropertyStub */, + JSStrictPropertyOp setter /* = JS_StrictPropertyStub */, + uintN attrs /* = JSPROP_ENUMERATE */) +{ + js::DefineGenericOp op = getOps()->defineGeneric; + return (op ? op : js_DefineProperty)(cx, this, id, &value, getter, setter, attrs); +} -class AutoPropDescArrayRooter : private AutoGCRooter +inline JSBool +JSObject::defineProperty(JSContext *cx, js::PropertyName *name, const js::Value &value, + JSPropertyOp getter /* = JS_PropertyStub */, + JSStrictPropertyOp setter /* = JS_StrictPropertyStub */, + uintN attrs /* = JSPROP_ENUMERATE */) { - public: - AutoPropDescArrayRooter(JSContext *cx) - : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx) - { } + return defineGeneric(cx, ATOM_TO_JSID(name), value, getter, setter, attrs); +} - PropDesc *append() { - if (!descriptors.append(PropDesc())) - return NULL; - return &descriptors.back(); - } +inline JSBool +JSObject::defineElement(JSContext *cx, uint32_t index, const js::Value &value, + JSPropertyOp getter /* = JS_PropertyStub */, + JSStrictPropertyOp setter /* = JS_StrictPropertyStub */, + uintN attrs /* = JSPROP_ENUMERATE */) +{ + js::DefineElementOp op = getOps()->defineElement; + return (op ? op : js_DefineElement)(cx, this, index, &value, getter, setter, attrs); +} - PropDesc& operator[](size_t i) { - JS_ASSERT(i < descriptors.length()); - return descriptors[i]; - } +inline JSBool +JSObject::defineSpecial(JSContext *cx, js::SpecialId sid, const js::Value &value, + JSPropertyOp getter /* = JS_PropertyStub */, + JSStrictPropertyOp setter /* = JS_StrictPropertyStub */, + uintN attrs /* = JSPROP_ENUMERATE */) +{ + return defineGeneric(cx, SPECIALID_TO_JSID(sid), value, getter, setter, attrs); +} - friend void AutoGCRooter::trace(JSTracer *trc); +inline JSBool +JSObject::lookupElement(JSContext *cx, uint32_t index, JSObject **objp, JSProperty **propp) +{ + js::LookupElementOp op = getOps()->lookupElement; + return (op ? op : js_LookupElement)(cx, this, index, objp, propp); +} - private: - PropDescArray descriptors; -}; +inline JSBool +JSObject::lookupSpecial(JSContext *cx, js::SpecialId sid, JSObject **objp, JSProperty **propp) +{ + return lookupGeneric(cx, SPECIALID_TO_JSID(sid), objp, propp); +} -class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescriptor +inline JSBool +JSObject::getElement(JSContext *cx, JSObject *receiver, uint32_t index, js::Value *vp) { - public: - AutoPropertyDescriptorRooter(JSContext *cx) : AutoGCRooter(cx, DESCRIPTOR) { - obj = NULL; - attrs = 0; - getter = (PropertyOp) NULL; - setter = (StrictPropertyOp) NULL; + js::ElementIdOp op = getOps()->getElement; + if (op) + return op(cx, this, receiver, index, vp); + + jsid id; + if (!js::IndexToId(cx, index, &id)) + return false; + return getGeneric(cx, receiver, id, vp); +} + +inline JSBool +JSObject::getElement(JSContext *cx, uint32_t index, js::Value *vp) +{ + return getElement(cx, this, index, vp); +} + +inline JSBool +JSObject::getElementIfPresent(JSContext *cx, JSObject *receiver, uint32_t index, js::Value *vp, + bool *present) +{ + js::ElementIfPresentOp op = getOps()->getElementIfPresent; + if (op) + return op(cx, this, receiver, index, vp, present); + + /* For now, do the index-to-id conversion just once, then use + * lookupGeneric/getGeneric. Once lookupElement and getElement stop both + * doing index-to-id conversions, we can use those here. + */ + jsid id; + if (!js::IndexToId(cx, index, &id)) + return false; + + JSObject *obj2; + JSProperty *prop; + if (!lookupGeneric(cx, id, &obj2, &prop)) + return false; + + if (!prop) { + *present = false; + js::Debug_SetValueRangeToCrashOnTouch(vp, 1); + return true; + } + + *present = true; + return getGeneric(cx, receiver, id, vp); +} + +inline JSBool +JSObject::getSpecial(JSContext *cx, JSObject *receiver, js::SpecialId sid, js::Value *vp) +{ + return getGeneric(cx, receiver, SPECIALID_TO_JSID(sid), vp); +} + +inline JSBool +JSObject::getGenericAttributes(JSContext *cx, jsid id, uintN *attrsp) +{ + js::GenericAttributesOp op = getOps()->getGenericAttributes; + return (op ? op : js_GetAttributes)(cx, this, id, attrsp); +} + +inline JSBool +JSObject::getPropertyAttributes(JSContext *cx, js::PropertyName *name, uintN *attrsp) +{ + return getGenericAttributes(cx, ATOM_TO_JSID(name), attrsp); +} + +inline JSBool +JSObject::getElementAttributes(JSContext *cx, uint32_t index, uintN *attrsp) +{ + jsid id; + if (!js::IndexToId(cx, index, &id)) + return false; + return getGenericAttributes(cx, id, attrsp); +} + +inline JSBool +JSObject::getSpecialAttributes(JSContext *cx, js::SpecialId sid, uintN *attrsp) +{ + return getGenericAttributes(cx, SPECIALID_TO_JSID(sid), attrsp); +} + +inline bool +JSObject::isProxy() const +{ + return js::IsProxy(this); +} + +inline bool +JSObject::isCrossCompartmentWrapper() const +{ + return js::IsCrossCompartmentWrapper(this); +} + +inline bool +JSObject::isWrapper() const +{ + return js::IsWrapper(this); +} + +inline js::GlobalObject & +JSObject::global() const +{ + JSObject *obj = const_cast(this); + while (JSObject *parent = obj->getParent()) + obj = parent; + return obj->asGlobal(); +} + +static inline bool +js_IsCallable(const js::Value &v) +{ + return v.isObject() && v.toObject().isCallable(); +} + +namespace js { + +inline void +OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj) +{ + if (JSObjectOp op = obj->getClass()->ext.innerObject) + obj = op(cx, obj); +} + +inline void +OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj) +{ + if (JSObjectOp op = obj->getClass()->ext.outerObject) + obj = op(cx, obj); +} + +/* + * Methods to test whether an object or a value is of type "xml" (per typeof). + */ + +#define VALUE_IS_XML(v) (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isXML()) + +static inline bool +IsXML(const js::Value &v) +{ + return v.isObject() && v.toObject().isXML(); +} + +static inline bool +IsStopIteration(const js::Value &v) +{ + return v.isObject() && v.toObject().isStopIteration(); +} + +/* ES5 9.1 ToPrimitive(input). */ +static JS_ALWAYS_INLINE bool +ToPrimitive(JSContext *cx, Value *vp) +{ + if (vp->isPrimitive()) + return true; + return vp->toObject().defaultValue(cx, JSTYPE_VOID, vp); +} + +/* ES5 9.1 ToPrimitive(input, PreferredType). */ +static JS_ALWAYS_INLINE bool +ToPrimitive(JSContext *cx, JSType preferredType, Value *vp) +{ + JS_ASSERT(preferredType != JSTYPE_VOID); /* Use the other ToPrimitive! */ + if (vp->isPrimitive()) + return true; + return vp->toObject().defaultValue(cx, preferredType, vp); +} + +/* + * Return true if this is a compiler-created internal function accessed by + * its own object. Such a function object must not be accessible to script + * or embedding code. + */ +inline bool +IsInternalFunctionObject(JSObject *funobj) +{ + JSFunction *fun = funobj->toFunction(); + return (fun->flags & JSFUN_LAMBDA) && !funobj->getParent(); +} + +class AutoPropDescArrayRooter : private AutoGCRooter +{ + public: + AutoPropDescArrayRooter(JSContext *cx) + : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx) + { } + + PropDesc *append() { + if (!descriptors.append(PropDesc())) + return NULL; + return &descriptors.back(); + } + + PropDesc& operator[](size_t i) { + JS_ASSERT(i < descriptors.length()); + return descriptors[i]; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + private: + PropDescArray descriptors; +}; + +class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescriptor +{ + public: + AutoPropertyDescriptorRooter(JSContext *cx) : AutoGCRooter(cx, DESCRIPTOR) { + obj = NULL; + attrs = 0; + getter = (PropertyOp) NULL; + setter = (StrictPropertyOp) NULL; value.setUndefined(); } @@ -1093,107 +1546,223 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri friend void AutoGCRooter::trace(JSTracer *trc); }; -static inline bool -InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto, - gc::FinalizeKind kind) +inline bool +NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry) { - JS_ASSERT(clasp->isNative()); - JS_ASSERT(proto == obj->getProto()); + uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind; + *pentry = hash % js::ArrayLength(entries); - /* Share proto's emptyShape only if obj is similar to proto. */ - js::EmptyShape *empty = NULL; + Entry *entry = &entries[*pentry]; - if (proto) { - if (proto->canProvideEmptyShape(clasp)) { - empty = proto->getEmptyShape(cx, clasp, kind); - if (!empty) - goto bad; - } - } + /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */ + return (entry->clasp == clasp && entry->key == key); +} + +inline bool +NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry) +{ + JS_ASSERT(!proto->isGlobal()); + return lookup(clasp, proto, kind, pentry); +} + +inline bool +NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry) +{ + return lookup(clasp, global, kind, pentry); +} + +inline bool +NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry) +{ + return lookup(clasp, type, kind, pentry); +} + +inline void +NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj) +{ + JS_ASSERT(unsigned(entry_) < ArrayLength(entries)); + Entry *entry = &entries[entry_]; + + JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements()); + + entry->clasp = clasp; + entry->key = key; + entry->kind = kind; - if (!empty) { - empty = js::EmptyShape::create(cx, clasp); - if (!empty) - goto bad; - uint32 freeslot = JSSLOT_FREE(clasp); - if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot)) - goto bad; + entry->nbytes = obj->sizeOfThis(); + js_memcpy(&entry->templateObject, obj, entry->nbytes); +} + +inline void +NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj) +{ + JS_ASSERT(!proto->isGlobal()); + JS_ASSERT(obj->getProto() == proto); + return fill(entry, clasp, proto, kind, obj); +} + +inline void +NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj) +{ + //JS_ASSERT(global == obj->getGlobal()); + return fill(entry, clasp, global, kind, obj); +} + +inline void +NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj) +{ + JS_ASSERT(obj->type() == type); + return fill(entry, clasp, type, kind, obj); +} + +inline void +NewObjectCache::copyCachedToObject(JSObject *dst, JSObject *src) +{ + js_memcpy(dst, src, dst->sizeOfThis()); +#ifdef JSGC_GENERATIONAL + Shape::writeBarrierPost(dst->shape_, &dst->shape_); + types::TypeObject::writeBarrierPost(dst->type_, &dst->type_); +#endif +} + +inline JSObject * +NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_) +{ + JS_ASSERT(unsigned(entry_) < ArrayLength(entries)); + Entry *entry = &entries[entry_]; + + JSObject *obj = js_TryNewGCObject(cx, entry->kind); + if (obj) { + copyCachedToObject(obj, &entry->templateObject); + Probes::createObject(cx, obj); + return obj; } - obj->setMap(empty); - return true; + /* Copy the entry to the stack first in case it is purged by a GC. */ + size_t nbytes = entry->nbytes; + char stackObject[sizeof(JSObject_Slots16)]; + JS_ASSERT(nbytes <= sizeof(stackObject)); + js_memcpy(&stackObject, &entry->templateObject, nbytes); - bad: - /* The GC nulls map initially. It should still be null on error. */ - JS_ASSERT(obj->isNewborn()); - return false; + JSObject *baseobj = (JSObject *) stackObject; + RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape()); + RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType()); + + obj = js_NewGCObject(cx, entry->kind); + if (obj) { + copyCachedToObject(obj, baseobj); + Probes::createObject(cx, obj); + return obj; + } + + return NULL; } static inline bool -CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) +CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp) { +#ifdef JS_THREADSAFE JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST); /* If the class has no finalizer or a finalizer that is safe to call on * a different thread, we change the finalize kind. For example, * FINALIZE_OBJECT0 calls the finalizer on the main thread, * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread. - * kind % 2 prevents from recursivly incrementing the finalize kind because - * we can call NewObject with a background finalize kind. + * IsBackgroundAllocKind is called to prevent recursively incrementing + * the finalize kind; kind may already be a background finalize kind. */ - if (kind % 2 == 0 && (!clasp->finalize || clasp->flags & JSCLASS_CONCURRENT_FINALIZER)) + if (!gc::IsBackgroundAllocKind(kind) && !clasp->finalize) return true; +#endif return false; } /* - * Helper optimized for creating a native instance of the given class (not the - * class's prototype object). Use this in preference to NewObject, but use - * NewBuiltinClassInstance if you need the default class prototype as proto, - * and its parent global as parent. + * Make an object with the specified prototype. If parent is null, it will + * default to the prototype's global if the prototype is non-null. */ -static inline JSObject * -NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, - JSObject *parent, gc::FinalizeKind kind) -{ - JS_ASSERT(proto); - JS_ASSERT(parent); - JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST); +JSObject * +NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::AllocKind kind); - /* - * Allocate an object from the GC heap and initialize all its fields before - * doing any operation that can potentially trigger GC. - */ +inline JSObject * +NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) +{ + gc::AllocKind kind = gc::GetGCObjectKind(clasp); + return NewObjectWithGivenProto(cx, clasp, proto, parent, kind); +} - if (CanBeFinalizedInBackground(kind, clasp)) - kind = (gc::FinalizeKind)(kind + 1); +inline JSProtoKey +GetClassProtoKey(js::Class *clasp) +{ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); + if (key != JSProto_Null) + return key; + if (clasp->flags & JSCLASS_IS_ANONYMOUS) + return JSProto_Object; + return JSProto_Null; +} - JSObject* obj = js_NewGCObject(cx, kind); +inline bool +FindProto(JSContext *cx, js::Class *clasp, HandleObject parent, JSObject **proto) +{ + JSProtoKey protoKey = GetClassProtoKey(clasp); + if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp)) + return false; + if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto)) + return false; + return true; +} - if (obj) { - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - bool useHoles = (clasp == &js_ArrayClass); - obj->init(cx, clasp, proto, parent, NULL, useHoles); +/* + * Make an object with the prototype set according to the specified prototype or class: + * + * if proto is non-null: + * use the specified proto + * for a built-in class: + * use the memoized original value of the class constructor .prototype + * property object + * else if available + * the current value of .prototype + * else + * Object.prototype. + * + * The class prototype will be fetched from the parent's global. If global is + * null, the context's active global will be used, and the resulting object's + * parent will be that global. + */ +JSObject * +NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::AllocKind kind); - JS_ASSERT(proto->canProvideEmptyShape(clasp)); - js::EmptyShape *empty = proto->getEmptyShape(cx, clasp, kind); +inline JSObject * +NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) +{ + gc::AllocKind kind = gc::GetGCObjectKind(clasp); + return NewObjectWithClassProto(cx, clasp, proto, parent, kind); +} - if (empty) - obj->setMap(empty); - else - obj = NULL; - } +/* + * Create a native instance of the given class with parent and proto set + * according to the context's active global. + */ +inline JSObject * +NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::AllocKind kind) +{ + return NewObjectWithClassProto(cx, clasp, NULL, NULL, kind); +} - return obj; +inline JSObject * +NewBuiltinClassInstance(JSContext *cx, Class *clasp) +{ + gc::AllocKind kind = gc::GetGCObjectKind(clasp); + return NewBuiltinClassInstance(cx, clasp, kind); } -static inline JSObject * -NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) +inline GlobalObject * +GetCurrentGlobal(JSContext *cx) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return NewNativeClassInstance(cx, clasp, proto, parent, kind); + JSObject *scopeChain = (cx->hasfp()) ? &cx->fp()->scopeChain() : cx->globalObject; + return scopeChain ? &scopeChain->global() : NULL; } bool @@ -1201,310 +1770,372 @@ FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject Class *clasp); /* - * Helper used to create Boolean, Date, RegExp, etc. instances of built-in - * classes with class prototypes of the same Class. See, e.g., jsdate.cpp, - * jsregexp.cpp, and js_PrimitiveToObject in jsobj.cpp. Use this to get the - * right default proto and parent for clasp in cx. + * Create a plain object with the specified type. This bypasses getNewType to + * avoid losing creation site information for objects made by scripted 'new'. */ +JSObject * +NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind); + +/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */ static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) +CopyInitializerObject(JSContext *cx, JSObject *baseobj, types::TypeObject *type) { - VOUCH_DOES_NOT_REQUIRE_STACK(); + JS_ASSERT(baseobj->getClass() == &ObjectClass); + JS_ASSERT(!baseobj->inDictionaryMode()); - JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); - JS_ASSERT(protoKey != JSProto_Null); + gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots()); +#ifdef JS_THREADSAFE + kind = gc::GetBackgroundAllocKind(kind); +#endif + JS_ASSERT(kind == baseobj->getAllocKind()); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); - /* NB: inline-expanded and specialized version of js_GetClassPrototype. */ - JSObject *global; - if (!cx->running()) { - global = cx->globalObject; - OBJ_TO_INNER_OBJECT(cx, global); - if (!global) - return NULL; - } else { - global = cx->fp()->scopeChain().getGlobal(); - } - JS_ASSERT(global->isGlobal()); + if (!obj) + return NULL; - const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey); - JSObject *proto; - if (v.isObject()) { - proto = &v.toObject(); - JS_ASSERT(proto->getParent() == global); - } else { - if (!FindClassPrototype(cx, global, protoKey, &proto, clasp)) - return NULL; - } + obj->setType(type); + + if (!obj->setLastProperty(cx, baseobj->lastProperty())) + return NULL; - return NewNativeClassInstance(cx, clasp, proto, global, kind); + return obj; } -static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp) +JSObject * +NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent, + gc::AllocKind kind, const Shape *shape); + +/* + * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of + * the object, zero if the final size is unknown. This should only be used for + * objects that do not require any fixed slots. + */ +static inline gc::AllocKind +GuessObjectGCKind(size_t numSlots) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return NewBuiltinClassInstance(cx, clasp, kind); + if (numSlots) + return gc::GetGCObjectKind(numSlots); + return gc::FINALIZE_OBJECT4; } -static inline JSProtoKey -GetClassProtoKey(js::Class *clasp) +static inline gc::AllocKind +GuessArrayGCKind(size_t numSlots) { - JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null) - return key; - if (clasp->flags & JSCLASS_IS_ANONYMOUS) - return JSProto_Object; - return JSProto_Null; + if (numSlots) + return gc::GetGCArrayKind(numSlots); + return gc::FINALIZE_OBJECT8; } -namespace WithProto { - enum e { - Class = 0, - Given = 1 - }; +/* + * Get the GC kind to use for scripted 'new' on the given class. + * FIXME bug 547327: estimate the size from the allocation site. + */ +static inline gc::AllocKind +NewObjectGCKind(JSContext *cx, js::Class *clasp) +{ + if (clasp == &ArrayClass || clasp == &SlowArrayClass) + return gc::FINALIZE_OBJECT8; + if (clasp == &FunctionClass) + return gc::FINALIZE_OBJECT2; + return gc::FINALIZE_OBJECT4; } /* - * Create an instance of any class, native or not, JSFunction-sized or not. - * - * If withProto is 'Class': - * If proto is null: - * for a built-in class: - * use the memoized original value of the class constructor .prototype - * property object - * else if available - * the current value of .prototype - * else - * Object.prototype. - * - * If parent is null, default it to proto->getParent() if proto is non - * null, else to null. - * - * If withProto is 'Given': - * We allocate an object with exactly the given proto. A null parent - * defaults to proto->getParent() if proto is non-null (else to null). - * - * If isFunction is true, return a JSFunction-sized object. If isFunction is - * false, return a normal object. - * - * Note that as a template, there will be lots of instantiations, which means - * the internals will be specialized based on the template parameters. + * Fill slots with the initial slot array to use for a newborn object which + * may or may not need dynamic slots. */ -static JS_ALWAYS_INLINE bool -FindProto(JSContext *cx, js::Class *clasp, JSObject *parent, JSObject ** proto) +inline bool +PreallocateObjectDynamicSlots(JSContext *cx, Shape *shape, HeapValue **slots) { - JSProtoKey protoKey = GetClassProtoKey(clasp); - if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp)) - return false; - if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto)) + if (size_t count = JSObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) { + *slots = (HeapValue *) cx->malloc_(count * sizeof(HeapValue)); + if (!*slots) + return false; + Debug_SetValueRangeToCrashOnTouch(*slots, count); + return true; + } + *slots = NULL; + return true; +} + +inline bool +DefineConstructorAndPrototype(JSContext *cx, GlobalObject *global, + JSProtoKey key, JSObject *ctor, JSObject *proto) +{ + JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */ + JS_ASSERT(ctor); + JS_ASSERT(proto); + + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); + JS_ASSERT(!global->nativeLookup(cx, id)); + + /* Set these first in case AddTypePropertyId looks for this class. */ + global->setSlot(key, ObjectValue(*ctor)); + global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto)); + global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor)); + + types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor)); + if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) { + global->setSlot(key, UndefinedValue()); + global->setSlot(key + JSProto_LIMIT, UndefinedValue()); + global->setSlot(key + JSProto_LIMIT * 2, UndefinedValue()); return false; + } return true; } -namespace detail +bool +PropDesc::checkGetter(JSContext *cx) { -template -static JS_ALWAYS_INLINE JSObject * -NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + if (hasGet && !js_IsCallable(get) && !get.isUndefined()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD, + js_getter_str); + return false; + } + return true; +} + +bool +PropDesc::checkSetter(JSContext *cx) { - /* Bootstrap the ur-object, and make it the default prototype object. */ - if (withProto == WithProto::Class && !proto) { - if (!FindProto(cx, clasp, parent, &proto)) - return NULL; + if (hasSet && !js_IsCallable(set) && !set.isUndefined()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD, + js_setter_str); + return false; } + return true; +} - /* - * Allocate an object from the GC heap and initialize all its fields before - * doing any operation that can potentially trigger GC. Functions have a - * larger non-standard allocation size. - * - * The should be specialized by the template. - */ +namespace detail { - if (!isFunction && CanBeFinalizedInBackground(kind, clasp)) - kind = (gc::FinalizeKind)(kind + 1); +template class PrimitiveBehavior { }; - JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind); - if (!obj) - goto out; +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isString(); } + static inline JSString *extract(const Value &v) { return v.toString(); } + static inline Class *getClass() { return &StringClass; } +}; - /* This needs to match up with the size of JSFunction::data_padding. */ - JS_ASSERT_IF(isFunction, kind == gc::FINALIZE_OBJECT2); +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isBoolean(); } + static inline bool extract(const Value &v) { return v.toBoolean(); } + static inline Class *getClass() { return &BooleanClass; } +}; - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - obj->init(cx, clasp, proto, - (!parent && proto) ? proto->getParent() : parent, - NULL, clasp == &js_ArrayClass); - - if (clasp->isNative()) { - if (!InitScopeForObject(cx, obj, clasp, proto, kind)) { - obj = NULL; - goto out; +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isNumber(); } + static inline double extract(const Value &v) { return v.toNumber(); } + static inline Class *getClass() { return &NumberClass; } +}; + +} /* namespace detail */ + +inline JSObject * +NonGenericMethodGuard(JSContext *cx, CallArgs args, Native native, Class *clasp, bool *ok) +{ + const Value &thisv = args.thisv(); + if (thisv.isObject()) { + JSObject &obj = thisv.toObject(); + if (obj.getClass() == clasp) { + *ok = true; /* quell gcc overwarning */ + return &obj; } - } else { - obj->setSharedNonNativeMap(); } -out: - Probes::createObject(cx, obj); - return obj; + *ok = HandleNonGenericMethodClassMismatch(cx, args, native, clasp); + return NULL; } -} /* namespace detail */ -static JS_ALWAYS_INLINE JSObject * -NewFunction(JSContext *cx, JSObject *parent) +template +inline bool +BoxedPrimitiveMethodGuard(JSContext *cx, CallArgs args, Native native, T *v, bool *ok) { - return detail::NewObject(cx, &js_FunctionClass, NULL, parent, - gc::FINALIZE_OBJECT2); + typedef detail::PrimitiveBehavior Behavior; + + const Value &thisv = args.thisv(); + if (Behavior::isType(thisv)) { + *v = Behavior::extract(thisv); + return true; + } + + if (!NonGenericMethodGuard(cx, args, native, Behavior::getClass(), ok)) + return false; + + *v = Behavior::extract(thisv.toObject().getPrimitiveThis()); + return true; } -template -static JS_ALWAYS_INLINE JSObject * -NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) +inline bool +ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx) { - return detail::NewObject(cx, clasp, proto, parent, kind); + if (JS_UNLIKELY(obj.isProxy())) + return Proxy::objectClassIs(&obj, classValue, cx); + + switch (classValue) { + case ESClass_Array: return obj.isArray(); + case ESClass_Number: return obj.isNumber(); + case ESClass_String: return obj.isString(); + case ESClass_Boolean: return obj.isBoolean(); + case ESClass_RegExp: return obj.isRegExp(); + } + JS_NOT_REACHED("bad classValue"); + return false; } -template -static JS_ALWAYS_INLINE JSObject * -NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) +inline bool +IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return detail::NewObject(cx, clasp, proto, parent, kind); + if (!v.isObject()) + return false; + return ObjectClassIs(v.toObject(), classValue, cx); } -template -static JS_ALWAYS_INLINE JSObject * -NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) +static JS_ALWAYS_INLINE bool +ValueIsSpecial(JSObject *obj, Value *propval, SpecialId *sidp, JSContext *cx) { - if (clasp == &js_FunctionClass) - return detail::NewObject(cx, clasp, proto, parent, kind); - return detail::NewObject(cx, clasp, proto, parent, kind); + if (!propval->isObject()) + return false; + +#if JS_HAS_XML_SUPPORT + if (obj->isXML()) { + *sidp = SpecialId(propval->toObject()); + return true; + } + + JSObject &propobj = propval->toObject(); + JSAtom *name; + if (propobj.isQName() && GetLocalNameFromFunctionQName(&propobj, &name, cx)) { + propval->setString(name); + return false; + } +#endif + + return false; } -template -static JS_ALWAYS_INLINE JSObject * -NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) +JSObject * +DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom, + JSObject *protoProto, Class *clasp, + Native constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs, + JSObject **ctorp = NULL, + gc::AllocKind ctorKind = JSFunction::FinalizeKind); + +} /* namespace js */ + +extern JSObject * +js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto, + js::Class *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs, + JSObject **ctorp = NULL, + js::gc::AllocKind ctorKind = JSFunction::FinalizeKind); + +inline JSObject * +js_GetProtoIfDenseArray(JSObject *obj) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return NewObject(cx, clasp, proto, parent, kind); + return obj->isDenseArray() ? obj->getProto() : obj; } /* - * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of - * the object, zero if the final size is unknown. + * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent + * scope, else it reshapes the scope and prototype chains it links. It calls + * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate + * (i.e., obj has ever been on a prototype or parent chain). */ -static inline gc::FinalizeKind -GuessObjectGCKind(size_t numSlots, bool isArray) +extern bool +js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id); + +inline bool +js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) { - if (numSlots) - return gc::GetGCObjectKind(numSlots); - return isArray ? gc::FINALIZE_OBJECT8 : gc::FINALIZE_OBJECT4; + if (obj->isDelegate()) + return js_PurgeScopeChainHelper(cx, obj, id); + return true; } -/* - * Get the GC kind to use for scripted 'new' on the given class. - * FIXME bug 547327: estimate the size from the allocation site. - */ -static inline gc::FinalizeKind -NewObjectGCKind(JSContext *cx, js::Class *clasp) +inline void +JSObject::setSlot(uintN slot, const js::Value &value) { - if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass) - return gc::FINALIZE_OBJECT8; - if (clasp == &js_FunctionClass) - return gc::FINALIZE_OBJECT2; - return gc::FINALIZE_OBJECT4; + JS_ASSERT(slotInRange(slot)); + getSlotRef(slot).set(compartment(), value); } -static JS_ALWAYS_INLINE JSObject* -NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, - /*gc::FinalizeKind*/ unsigned _kind) +inline void +JSObject::initSlot(uintN slot, const js::Value &value) { - JS_ASSERT(clasp->isNative()); - gc::FinalizeKind kind = gc::FinalizeKind(_kind); - - if (CanBeFinalizedInBackground(kind, clasp)) - kind = (gc::FinalizeKind)(kind + 1); - - JSObject* obj = js_NewGCObject(cx, kind); - if (!obj) - return NULL; - - if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind)) - return NULL; - return obj; + JS_ASSERT(getSlot(slot).isUndefined() || getSlot(slot).isMagic(JS_ARRAY_HOLE)); + JS_ASSERT(slotInRange(slot)); + initSlotUnchecked(slot, value); } -/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */ -static inline JSObject * -CopyInitializerObject(JSContext *cx, JSObject *baseobj) +inline void +JSObject::initSlotUnchecked(uintN slot, const js::Value &value) { - JS_ASSERT(baseobj->getClass() == &js_ObjectClass); - JS_ASSERT(!baseobj->inDictionaryMode()); - - gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind()); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); - - if (!obj || !obj->ensureSlots(cx, baseobj->numSlots())) - return NULL; - - obj->flags = baseobj->flags; - obj->lastProp = baseobj->lastProp; - obj->objShape = baseobj->objShape; - - return obj; + getSlotAddressUnchecked(slot)->init(value); } -/* - * When we have an object of a builtin class, we don't quite know what its - * valueOf/toString methods are, since these methods may have been overwritten - * or shadowed. However, we can still do better than js_TryMethod by - * hard-coding the necessary properties for us to find the native we expect. - * - * TODO: a per-thread shape-based cache would be faster and simpler. - */ -static JS_ALWAYS_INLINE bool -ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid, - Native native) +inline void +JSObject::setFixedSlot(uintN slot, const js::Value &value) { - JS_ASSERT(obj->getClass() == clasp); - - if (HasNativeMethod(obj, methodid, native)) - return true; + JS_ASSERT(slot < numFixedSlots()); + fixedSlots()[slot] = value; +} - JSObject *pobj = obj->getProto(); - return pobj && pobj->getClass() == clasp && - HasNativeMethod(pobj, methodid, native); +inline void +JSObject::initFixedSlot(uintN slot, const js::Value &value) +{ + JS_ASSERT(slot < numFixedSlots()); + fixedSlots()[slot].init(value); } -inline bool -DefineConstructorAndPrototype(JSContext *cx, JSObject *global, - JSProtoKey key, JSFunction *ctor, JSObject *proto) +inline void +JSObject::privateWriteBarrierPre(void **old) { - JS_ASSERT(global->isGlobal()); - JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */ - JS_ASSERT(ctor); - JS_ASSERT(proto); +#ifdef JSGC_INCREMENTAL + JSCompartment *comp = compartment(); + if (comp->needsBarrier()) { + if (*old && getClass()->trace) + getClass()->trace(comp->barrierTracer(), this); + } +#endif +} - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - JS_ASSERT(!global->nativeLookup(id)); +inline void +JSObject::privateWriteBarrierPost(void **old) +{ +} - if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) - return false; +inline void +JSObject::writeBarrierPre(JSObject *obj) +{ +#ifdef JSGC_INCREMENTAL + /* + * This would normally be a null test, but TypeScript::global uses 0x1 as a + * special value. + */ + if (uintptr_t(obj) < 32) + return; - global->setSlot(key, ObjectValue(*ctor)); - global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto)); - global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor)); - return true; + JSCompartment *comp = obj->compartment(); + if (comp->needsBarrier()) { + JS_ASSERT(!comp->rt->gcRunning); + MarkObjectUnbarriered(comp->barrierTracer(), obj, "write barrier"); + } +#endif } -} /* namespace js */ +inline void +JSObject::writeBarrierPost(JSObject *obj, void *addr) +{ +} #endif /* jsobjinlines_h___ */ diff --git a/deps/mozjs/js/src/json.cpp b/deps/mozjs/js/src/json.cpp index 65e1c4d9675..4f930addd59 100644 --- a/deps/mozjs/js/src/json.cpp +++ b/deps/mozjs/js/src/json.cpp @@ -41,7 +41,6 @@ #include #include "jsapi.h" -#include "jsarena.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" @@ -51,19 +50,19 @@ #include "jsiter.h" #include "jsnum.h" #include "jsobj.h" +#include "json.h" #include "jsonparser.h" #include "jsprf.h" -#include "jsscan.h" #include "jsstr.h" #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jsxml.h" -#include "jsvector.h" -#include "json.h" +#include "frontend/TokenStream.h" #include "jsatominlines.h" +#include "jsboolinlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" @@ -71,64 +70,42 @@ using namespace js; using namespace js::gc; +using namespace js::types; -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4351) -#endif - -struct JSONParser -{ - JSONParser(JSContext *cx) - : hexChar(), numHex(), statep(), stateStack(), rootVal(), objectStack(), - objectKey(cx), buffer(cx), suppressErrors(false) - {} - - /* Used while handling \uNNNN in strings */ - jschar hexChar; - uint8 numHex; - - JSONParserState *statep; - JSONParserState stateStack[JSON_MAX_DEPTH]; - Value *rootVal; - JSObject *objectStack; - js::Vector objectKey; - js::Vector buffer; - bool suppressErrors; -}; - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -Class js_JSONClass = { +Class js::JSONClass = { js_JSON_str, JSCLASS_HAS_CACHED_PROTO(JSProto_JSON), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub }; +/* ES5 15.12.2. */ JSBool js_json_parse(JSContext *cx, uintN argc, Value *vp) { - JSString *s = NULL; - Value *argv = vp + 2; - Value reviver = UndefinedValue(); - - if (!JS_ConvertArguments(cx, argc, Jsvalify(argv), "S / v", &s, &reviver)) - return JS_FALSE; + /* Step 1. */ + JSLinearString *linear; + if (argc >= 1) { + JSString *str = ToString(cx, vp[2]); + if (!str) + return false; + linear = str->ensureLinear(cx); + if (!linear) + return false; + } else { + linear = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; + } + JS::Anchor anchor(linear); - JSLinearString *linearStr = s->ensureLinear(cx); - if (!linearStr) - return JS_FALSE; - JS::Anchor anchor(linearStr); + Value reviver = (argc >= 2) ? vp[3] : UndefinedValue(); - return ParseJSONWithReviver(cx, linearStr->chars(), linearStr->length(), reviver, vp); + /* Steps 2-5. */ + return ParseJSONWithReviver(cx, linear->chars(), linear->length(), reviver, vp); } /* ES5 15.12.3. */ @@ -160,25 +137,6 @@ js_json_stringify(JSContext *cx, uintN argc, Value *vp) return true; } -JSBool -js_TryJSON(JSContext *cx, Value *vp) -{ - if (!vp->isObject()) - return true; - - JSObject *obj = &vp->toObject(); - Value fval; - jsid id = ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom); - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) - return false; - if (js_IsCallable(fval)) { - if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, vp)) - return false; - } - return true; -} - - static inline bool IsQuoteSpecialCharacter(jschar c) { JS_STATIC_ASSERT('\b' < ' '); @@ -234,13 +192,12 @@ Quote(JSContext *cx, StringBuffer &sb, JSString *str) : 't'; if (!sb.append('\\') || !sb.append(abbrev)) return false; - mark = i + 1; } else { JS_ASSERT(c < ' '); if (!sb.append("\\u00")) return false; JS_ASSERT((c >> 4) < 10); - uint8 x = c >> 4, y = c % 16; + uint8_t x = c >> 4, y = c % 16; if (!sb.append('0' + x) || !sb.append(y < 10 ? '0' + y : 'a' + (y - 10))) return false; } @@ -275,19 +232,19 @@ class StringifyContext const StringBuffer ⪆ JSObject * const replacer; const AutoIdVector &propertyList; - uint32 depth; + uint32_t depth; HashSet objectStack; }; static JSBool Str(JSContext *cx, const Value &v, StringifyContext *scx); static JSBool -WriteIndent(JSContext *cx, StringifyContext *scx, uint32 limit) +WriteIndent(JSContext *cx, StringifyContext *scx, uint32_t limit) { if (!scx->gap.empty()) { if (!scx->sb.append('\n')) return JS_FALSE; - for (uint32 i = 0; i < limit; i++) { + for (uint32_t i = 0; i < limit; i++) { if (!scx->sb.append(scx->gap.begin(), scx->gap.end())) return JS_FALSE; } @@ -321,12 +278,33 @@ class CycleDetector JSObject *const obj; }; +template +class KeyStringifier { +}; + +template<> +class KeyStringifier { + public: + static JSString *toString(JSContext *cx, uint32_t index) { + return IndexToString(cx, index); + } +}; + +template<> +class KeyStringifier { + public: + static JSString *toString(JSContext *cx, jsid id) { + return IdToString(cx, id); + } +}; + /* * ES5 15.12.3 Str, steps 2-4, extracted to enable preprocessing of property * values when stringifying objects in JO. */ +template static bool -PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyContext *scx) +PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, Value *vp, StringifyContext *scx) { JSString *keyStr = NULL; @@ -338,11 +316,10 @@ PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyC return false; if (js_IsCallable(toJSON)) { - keyStr = IdToString(cx, key); + keyStr = KeyStringifier::toString(cx, key); if (!keyStr) return false; - LeaveTrace(cx); InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 1, &args)) return false; @@ -360,12 +337,11 @@ PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyC /* Step 3. */ if (scx->replacer && scx->replacer->isCallable()) { if (!keyStr) { - keyStr = IdToString(cx, key); + keyStr = KeyStringifier::toString(cx, key); if (!keyStr) return false; } - LeaveTrace(cx); InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 2, &args)) return false; @@ -382,20 +358,20 @@ PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyC /* Step 4. */ if (vp->isObject()) { - JSObject *obj = &vp->toObject(); - Class *clasp = obj->getClass(); - if (clasp == &js_NumberClass) { + JSObject &obj = vp->toObject(); + if (ObjectClassIs(obj, ESClass_Number, cx)) { double d; - if (!ValueToNumber(cx, *vp, &d)) + if (!ToNumber(cx, *vp, &d)) return false; vp->setNumber(d); - } else if (clasp == &js_StringClass) { - JSString *str = js_ValueToString(cx, *vp); + } else if (ObjectClassIs(obj, ESClass_String, cx)) { + JSString *str = ToStringSlow(cx, *vp); if (!str) return false; vp->setString(str); - } else if (clasp == &js_BooleanClass) { - *vp = obj->getPrimitiveThis(); + } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) { + if (!BooleanGetPrimitiveValue(cx, obj, vp)) + return false; JS_ASSERT(vp->isBoolean()); } } @@ -439,16 +415,17 @@ JO(JSContext *cx, JSObject *obj, StringifyContext *scx) return JS_FALSE; /* Steps 5-7. */ - AutoIdVector ids(cx); + Maybe ids; const AutoIdVector *props; if (scx->replacer && !scx->replacer->isCallable()) { JS_ASSERT(JS_IsArrayObject(cx, scx->replacer)); props = &scx->propertyList; } else { JS_ASSERT_IF(scx->replacer, scx->propertyList.length() == 0); - if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &ids)) + ids.construct(cx); + if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, ids.addr())) return false; - props = &ids; + props = ids.addr(); } /* My kingdom for not-quite-initialized-from-the-start references. */ @@ -466,7 +443,7 @@ JO(JSContext *cx, JSObject *obj, StringifyContext *scx) */ const jsid &id = propertyList[i]; Value outputValue; - if (!obj->getProperty(cx, id, &outputValue)) + if (!obj->getGeneric(cx, id, &outputValue)) return false; if (!PreprocessValue(cx, obj, id, &outputValue, scx)) return false; @@ -535,18 +512,16 @@ JA(JSContext *cx, JSObject *obj, StringifyContext *scx) /* Steps 7-10. */ Value outputValue; - for (jsuint i = 0; i < length; i++) { - jsid id = INT_TO_JSID(i); - + for (uint32_t i = 0; i < length; i++) { /* * Steps 8a-8c. Again note how the call to the spec's Str method * is broken up into getting the property, running it past toJSON * and the replacer and maybe unboxing, and interpreting some * values as |null| in separate steps. */ - if (!obj->getProperty(cx, id, &outputValue)) + if (!obj->getElement(cx, i, &outputValue)) return JS_FALSE; - if (!PreprocessValue(cx, obj, id, &outputValue, scx)) + if (!PreprocessValue(cx, obj, i, &outputValue, scx)) return JS_FALSE; if (IsFilteredValue(outputValue)) { if (!scx->sb.append("null")) @@ -622,10 +597,14 @@ Str(JSContext *cx, const Value &v, StringifyContext *scx) /* Step 10. */ JS_ASSERT(v.isObject()); - JSBool ok; + JSObject *obj = &v.toObject(); scx->depth++; - ok = (JS_IsArrayObject(cx, &v.toObject()) ? JA : JO)(cx, &v.toObject(), scx); + JSBool ok; + if (ObjectClassIs(v.toObject(), ESClass_Array, cx)) + ok = JA(cx, obj, scx); + else + ok = JO(cx, obj, scx); scx->depth--; return ok; @@ -635,99 +614,107 @@ Str(JSContext *cx, const Value &v, StringifyContext *scx) JSBool js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBuffer &sb) { - /* - * Step 4. - * - * The spec algorithm is unhelpfully vague in 15.12.3 step 4b about the - * exact steps taken when the replacer is an array, regarding the exact - * sequence of [[Get]] calls for the array's elements, when its overall - * length is calculated, whether own or own plus inherited properties are - * considered, and so on. A rewrite of the step was proposed in - * , - * whose steps are copied below, and which are implemented here. - * - * i. Let PropertyList be an empty internal List. - * ii. Let len be the result of calling the [[Get]] internal method of - * replacer with the argument "length". - * iii. Let i be 0. - * iv. While i < len: - * 1. Let item be undefined. - * 2. Let v be the result of calling the [[Get]] internal method of - * replacer with the argument ToString(i). - * 3. If Type(v) is String then let item be v. - * 4. Else if Type(v) is Number then let item be ToString(v). - * 5. Else if Type(v) is Object then - * a. If the [[Class]] internal property of v is "String" or - * "Number" then let item be ToString(v). - * 6. If item is not undefined and item is not currently an element of - * PropertyList then, - * a. Append item to the end of PropertyList. - * 7. Let i be i + 1. - */ + /* Step 4. */ AutoIdVector propertyList(cx); - if (replacer && JS_IsArrayObject(cx, replacer)) { - /* Step 4b(ii). */ - jsuint len; - JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len)); - if (replacer->isDenseArray()) - len = JS_MIN(len, replacer->getDenseArrayCapacity()); - - HashSet idSet(cx); - if (!idSet.init(len)) - return false; + if (replacer) { + if (replacer->isCallable()) { + /* Step 4a(i): use replacer to transform values. */ + } else if (ObjectClassIs(*replacer, ESClass_Array, cx)) { + /* + * Step 4b: The spec algorithm is unhelpfully vague about the exact + * steps taken when the replacer is an array, regarding the exact + * sequence of [[Get]] calls for the array's elements, when its + * overall length is calculated, whether own or own plus inherited + * properties are considered, and so on. A rewrite was proposed in + * , + * whose steps are copied below, and which are implemented here. + * + * i. Let PropertyList be an empty internal List. + * ii. Let len be the result of calling the [[Get]] internal + * method of replacer with the argument "length". + * iii. Let i be 0. + * iv. While i < len: + * 1. Let item be undefined. + * 2. Let v be the result of calling the [[Get]] internal + * method of replacer with the argument ToString(i). + * 3. If Type(v) is String then let item be v. + * 4. Else if Type(v) is Number then let item be ToString(v). + * 5. Else if Type(v) is Object then + * a. If the [[Class]] internal property of v is "String" + * or "Number" then let item be ToString(v). + * 6. If item is not undefined and item is not currently an + * element of PropertyList then, + * a. Append item to the end of PropertyList. + * 7. Let i be i + 1. + */ - /* Step 4b(iii). */ - jsuint i = 0; + /* Step 4b(ii). */ + jsuint len; + JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len)); + if (replacer->isDenseArray()) + len = JS_MIN(len, replacer->getDenseArrayCapacity()); - /* Step 4b(iv). */ - for (; i < len; i++) { - /* Step 4b(iv)(2). */ - Value v; - if (!replacer->getProperty(cx, INT_TO_JSID(i), &v)) + HashSet idSet(cx); + if (!idSet.init(len)) return false; - jsid id; - if (v.isNumber()) { - /* Step 4b(iv)(4). */ - int32_t n; - if (v.isNumber() && ValueFitsInInt32(v, &n) && INT_FITS_IN_JSID(n)) { - id = INT_TO_JSID(n); - } else { + /* Step 4b(iii). */ + jsuint i = 0; + + /* Step 4b(iv). */ + for (; i < len; i++) { + /* Step 4b(iv)(2). */ + Value v; + if (!replacer->getElement(cx, i, &v)) + return false; + + jsid id; + if (v.isNumber()) { + /* Step 4b(iv)(4). */ + int32_t n; + if (v.isNumber() && ValueFitsInInt32(v, &n) && INT_FITS_IN_JSID(n)) { + id = INT_TO_JSID(n); + } else { + if (!js_ValueToStringId(cx, v, &id)) + return false; + id = js_CheckForStringIndex(id); + } + } else if (v.isString() || + (v.isObject() && + (ObjectClassIs(v.toObject(), ESClass_String, cx) || + ObjectClassIs(v.toObject(), ESClass_Number, cx)))) + { + /* Step 4b(iv)(3), 4b(iv)(5). */ if (!js_ValueToStringId(cx, v, &id)) return false; id = js_CheckForStringIndex(id); + } else { + continue; } - } else if (v.isString() || - (v.isObject() && (v.toObject().isString() || v.toObject().isNumber()))) - { - /* Step 4b(iv)(3), 4b(iv)(5). */ - if (!js_ValueToStringId(cx, v, &id)) - return false; - id = js_CheckForStringIndex(id); - } else { - continue; - } - /* Step 4b(iv)(6). */ - HashSet::AddPtr p = idSet.lookupForAdd(id); - if (!p) { - /* Step 4b(iv)(6)(a). */ - if (!idSet.add(p, id) || !propertyList.append(id)) - return false; + /* Step 4b(iv)(6). */ + HashSet::AddPtr p = idSet.lookupForAdd(id); + if (!p) { + /* Step 4b(iv)(6)(a). */ + if (!idSet.add(p, id) || !propertyList.append(id)) + return false; + } } + } else { + replacer = NULL; } } /* Step 5. */ if (space.isObject()) { JSObject &spaceObj = space.toObject(); - if (spaceObj.isNumber()) { + if (ObjectClassIs(spaceObj, ESClass_Number, cx)) { jsdouble d; - if (!ValueToNumber(cx, space, &d)) + if (!ToNumber(cx, space, &d)) return false; space = NumberValue(d); - } else if (spaceObj.isString()) { - JSString *str = js_ValueToString(cx, space); + } else if (ObjectClassIs(spaceObj, ESClass_String, cx)) { + JSString *str = ToStringSlow(cx, space); if (!str) return false; space = StringValue(str); @@ -741,7 +728,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu jsdouble d; JS_ALWAYS_TRUE(ToInteger(cx, space, &d)); d = JS_MIN(10, d); - if (d >= 1 && !gap.appendN(' ', uint32(d))) + if (d >= 1 && !gap.appendN(' ', uint32_t(d))) return false; } else if (space.isString()) { /* Step 7. */ @@ -758,14 +745,14 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu } /* Step 9. */ - JSObject *wrapper = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *wrapper = NewBuiltinClassInstance(cx, &ObjectClass); if (!wrapper) return false; /* Step 10. */ jsid emptyId = ATOM_TO_JSID(cx->runtime->atomState.emptyAtom); - if (!js_DefineNativeProperty(cx, wrapper, emptyId, *vp, PropertyStub, StrictPropertyStub, - JSPROP_ENUMERATE, 0, 0, NULL)) + if (!DefineNativeProperty(cx, wrapper, emptyId, *vp, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE, 0, 0)) { return false; } @@ -783,62 +770,82 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu return Str(cx, *vp, &scx); } -// helper to determine whether a character could be part of a number -static JSBool IsNumChar(jschar c) -{ - return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E'); -} - -static JSBool HandleDataString(JSContext *cx, JSONParser *jp); -static JSBool HandleDataKeyString(JSContext *cx, JSONParser *jp); -static JSBool HandleDataNumber(JSContext *cx, JSONParser *jp); -static JSBool HandleDataKeyword(JSContext *cx, JSONParser *jp); -static JSBool PopState(JSContext *cx, JSONParser *jp); - +/* ES5 15.12.2 Walk. */ static bool -Walk(JSContext *cx, jsid id, JSObject *holder, const Value &reviver, Value *vp) +Walk(JSContext *cx, JSObject *holder, jsid name, const Value &reviver, Value *vp) { JS_CHECK_RECURSION(cx, return false); - if (!holder->getProperty(cx, id, vp)) + /* Step 1. */ + Value val; + if (!holder->getGeneric(cx, name, &val)) return false; - JSObject *obj; - - if (vp->isObject() && !(obj = &vp->toObject())->isCallable()) { - AutoValueRooter propValue(cx); - - if(obj->isArray()) { - jsuint length = 0; - if (!js_GetLengthProperty(cx, obj, &length)) - return false; - - for (jsuint i = 0; i < length; i++) { - jsid index; - if (!js_IndexToId(cx, i, &index)) + /* Step 2. */ + if (val.isObject()) { + JSObject *obj = &val.toObject(); + + /* 'val' must have been produced by the JSON parser, so not a proxy. */ + JS_ASSERT(!obj->isProxy()); + if (obj->isArray()) { + /* Step 2a(ii). */ + uint32_t length = obj->getArrayLength(); + + /* Step 2a(i), 2a(iii-iv). */ + for (uint32_t i = 0; i < length; i++) { + jsid id; + if (!IndexToId(cx, i, &id)) return false; - if (!Walk(cx, index, obj, reviver, propValue.addr())) + /* Step 2a(iii)(1). */ + Value newElement; + if (!Walk(cx, obj, id, reviver, &newElement)) return false; - if (!obj->defineProperty(cx, index, propValue.value(), NULL, NULL, JSPROP_ENUMERATE)) - return false; + /* + * Arrays which begin empty and whose properties are always + * incrementally appended are always dense, no matter their + * length, under current dense/slow array heuristics. + * Also, deleting a property from a dense array which is not + * currently being enumerated never makes it slow. This array + * is never exposed until the reviver sees it below, so it must + * be dense and isn't currently being enumerated. Therefore + * property definition and deletion will always succeed, + * and we need not check for failure. + */ + if (newElement.isUndefined()) { + /* Step 2a(iii)(2). */ + JS_ALWAYS_TRUE(array_deleteElement(cx, obj, i, &newElement, false)); + } else { + /* Step 2a(iii)(3). */ + JS_ALWAYS_TRUE(array_defineElement(cx, obj, i, &newElement, JS_PropertyStub, + JS_StrictPropertyStub, JSPROP_ENUMERATE)); + } } } else { - AutoIdVector props(cx); - if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props)) + /* Step 2b(i). */ + AutoIdVector keys(cx); + if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &keys)) return false; - for (size_t i = 0, len = props.length(); i < len; i++) { - jsid idName = props[i]; - if (!Walk(cx, idName, obj, reviver, propValue.addr())) + /* Step 2b(ii). */ + for (size_t i = 0, len = keys.length(); i < len; i++) { + /* Step 2b(ii)(1). */ + Value newElement; + jsid id = keys[i]; + if (!Walk(cx, obj, id, reviver, &newElement)) return false; - if (propValue.value().isUndefined()) { - if (!js_DeleteProperty(cx, obj, idName, propValue.addr(), false)) + + if (newElement.isUndefined()) { + /* Step 2b(ii)(2). */ + if (!obj->deleteByValue(cx, IdToValue(id), &newElement, false)) return false; } else { - if (!obj->defineProperty(cx, idName, propValue.value(), NULL, NULL, - JSPROP_ENUMERATE)) { + /* Step 2b(ii)(3). */ + JS_ASSERT(obj->isNative()); + if (!DefineNativeProperty(cx, obj, id, newElement, JS_PropertyStub, + JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) + { return false; } } @@ -846,117 +853,38 @@ Walk(JSContext *cx, jsid id, JSObject *holder, const Value &reviver, Value *vp) } } - // return reviver.call(holder, key, value); - const Value &value = *vp; - JSString *key = js_ValueToString(cx, IdToValue(id)); + /* Step 3. */ + JSString *key = IdToString(cx, name); if (!key) return false; - Value vec[2] = { StringValue(key), value }; - Value reviverResult; - if (!JS_CallFunctionValue(cx, holder, Jsvalify(reviver), - 2, Jsvalify(vec), Jsvalify(&reviverResult))) { + InvokeArgsGuard args; + if (!cx->stack.pushInvokeArgs(cx, 2, &args)) return false; - } - *vp = reviverResult; - return true; -} + args.calleev() = reviver; + args.thisv() = ObjectValue(*holder); + args[0] = StringValue(key); + args[1] = val; -static JSBool -JSONParseError(JSONParser *jp, JSContext *cx) -{ - if (!jp->suppressErrors) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, "syntax error"); - return JS_FALSE; + if (!Invoke(cx, args)) + return false; + *vp = args.rval(); + return true; } static bool Revive(JSContext *cx, const Value &reviver, Value *vp) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj) return false; - AutoObjectRooter tvr(cx, obj); - if (!obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), - *vp, NULL, NULL, JSPROP_ENUMERATE)) { + if (!obj->defineProperty(cx, cx->runtime->atomState.emptyAtom, *vp)) return false; - } - - return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp); -} - -JSONParser * -js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/) -{ - if (!cx) - return NULL; - - JSObject *arr = NewDenseEmptyArray(cx); - if (!arr) - return NULL; - - JSONParser *jp = cx->new_(cx); - if (!jp) - return NULL; - - jp->objectStack = arr; - if (!JS_AddNamedObjectRoot(cx, &jp->objectStack, "JSON parse stack")) - goto bad; - - jp->statep = jp->stateStack; - *jp->statep = JSON_PARSE_STATE_INIT; - jp->rootVal = rootVal; - jp->suppressErrors = suppressErrors; - - return jp; - -bad: - js_FinishJSONParse(cx, jp, NullValue()); - return NULL; -} - -bool -js_FinishJSONParse(JSContext *cx, JSONParser *jp, const Value &reviver) -{ - if (!jp) - return true; - - JSBool early_ok = JS_TRUE; - - // Check for unprocessed primitives at the root. This doesn't happen for - // strings because a closing quote triggers value processing. - if ((jp->statep - jp->stateStack) == 1) { - if (*jp->statep == JSON_PARSE_STATE_KEYWORD) { - early_ok = HandleDataKeyword(cx, jp); - if (early_ok) - PopState(cx, jp); - } else if (*jp->statep == JSON_PARSE_STATE_NUMBER) { - early_ok = HandleDataNumber(cx, jp); - if (early_ok) - PopState(cx, jp); - } - } - - // This internal API is infallible, in spite of its JSBool return type. - js_RemoveRoot(cx->runtime, &jp->objectStack); - - bool ok = *jp->statep == JSON_PARSE_STATE_FINISHED; - Value *vp = jp->rootVal; - if (!early_ok) { - ok = false; - } else if (!ok) { - JSONParseError(jp, cx); - } else if (js_IsCallable(reviver)) { - ok = Revive(cx, reviver, vp); - } - - cx->delete_(jp); - - return ok; + return Walk(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), reviver, vp); } namespace js { @@ -965,511 +893,20 @@ JSBool ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, const Value &reviver, Value *vp, DecodingMode decodingMode /* = STRICT */) { -#if USE_OLD_AND_BUSTED_JSON_PARSER - JSONParser *jp = js_BeginJSONParse(cx, vp); - if (!jp) - return false; - JSBool ok = js_ConsumeJSONText(cx, jp, chars, length, decodingMode); - ok &= !!js_FinishJSONParse(cx, jp, reviver); - return ok; -#else - JSONSourceParser parser(cx, chars, length, - decodingMode == STRICT - ? JSONSourceParser::StrictJSON - : JSONSourceParser::LegacyJSON); + /* 15.12.2 steps 2-3. */ + JSONParser parser(cx, chars, length, + decodingMode == STRICT ? JSONParser::StrictJSON : JSONParser::LegacyJSON); if (!parser.parse(vp)) return false; + + /* 15.12.2 steps 4-5. */ if (js_IsCallable(reviver)) return Revive(cx, reviver, vp); return true; -#endif } } /* namespace js */ -static JSBool -PushState(JSContext *cx, JSONParser *jp, JSONParserState state) -{ - if (*jp->statep == JSON_PARSE_STATE_FINISHED) { - // extra input - return JSONParseError(jp, cx); - } - - jp->statep++; - if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) { - // too deep - return JSONParseError(jp, cx); - } - - *jp->statep = state; - - return JS_TRUE; -} - -static JSBool -PopState(JSContext *cx, JSONParser *jp) -{ - jp->statep--; - if (jp->statep < jp->stateStack) { - jp->statep = jp->stateStack; - return JSONParseError(jp, cx); - } - - if (*jp->statep == JSON_PARSE_STATE_INIT) - *jp->statep = JSON_PARSE_STATE_FINISHED; - - return JS_TRUE; -} - -static JSBool -PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, const Value &value) -{ - JSBool ok; - if (parent->isArray()) { - jsuint len; - ok = js_GetLengthProperty(cx, parent, &len); - if (ok) { - jsid index; - if (!js_IndexToId(cx, len, &index)) - return JS_FALSE; - ok = parent->defineProperty(cx, index, value, NULL, NULL, JSPROP_ENUMERATE); - } - } else { - ok = JS_DefineUCProperty(cx, parent, jp->objectKey.begin(), - jp->objectKey.length(), Jsvalify(value), - NULL, NULL, JSPROP_ENUMERATE); - jp->objectKey.clear(); - } - - return ok; -} - -static JSBool -PushObject(JSContext *cx, JSONParser *jp, JSObject *obj) -{ - jsuint len; - if (!js_GetLengthProperty(cx, jp->objectStack, &len)) - return JS_FALSE; - if (len >= JSON_MAX_DEPTH) - return JSONParseError(jp, cx); - - AutoObjectRooter tvr(cx, obj); - Value v = ObjectOrNullValue(obj); - - // Check if this is the root object - if (len == 0) { - *jp->rootVal = v; - // This property must be enumerable to keep the array dense - if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(0), *jp->rootVal, - NULL, NULL, JSPROP_ENUMERATE)) { - return JS_FALSE; - } - return JS_TRUE; - } - - Value p; - if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &p)) - return JS_FALSE; - - JSObject *parent = &p.toObject(); - if (!PushValue(cx, jp, parent, v)) - return JS_FALSE; - - // This property must be enumerable to keep the array dense - if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(len), v, - NULL, NULL, JSPROP_ENUMERATE)) { - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -OpenObject(JSContext *cx, JSONParser *jp) -{ - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); - if (!obj) - return JS_FALSE; - - return PushObject(cx, jp, obj); -} - -static JSBool -OpenArray(JSContext *cx, JSONParser *jp) -{ - // Add an array to an existing array or object - JSObject *arr = NewDenseEmptyArray(cx); - if (!arr) - return JS_FALSE; - - return PushObject(cx, jp, arr); -} - -static JSBool -CloseObject(JSContext *cx, JSONParser *jp) -{ - jsuint len; - if (!js_GetLengthProperty(cx, jp->objectStack, &len)) - return JS_FALSE; - if (!js_SetLengthProperty(cx, jp->objectStack, len - 1)) - return JS_FALSE; - - return JS_TRUE; -} - -static JSBool -CloseArray(JSContext *cx, JSONParser *jp) -{ - return CloseObject(cx, jp); -} - -static JSBool -PushPrimitive(JSContext *cx, JSONParser *jp, const Value &value) -{ - AutoValueRooter tvr(cx, value); - - jsuint len; - if (!js_GetLengthProperty(cx, jp->objectStack, &len)) - return JS_FALSE; - - if (len > 0) { - Value o; - if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &o)) - return JS_FALSE; - - return PushValue(cx, jp, &o.toObject(), value); - } - - // root value must be primitive - *jp->rootVal = value; - return JS_TRUE; -} - -static JSBool -HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) -{ - const jschar *ep; - double val; - if (!js_strtod(cx, buf, buf + len, &ep, &val)) - return JS_FALSE; - if (ep != buf + len) { - // bad number input - return JSONParseError(jp, cx); - } - - return PushPrimitive(cx, jp, NumberValue(val)); -} - -static JSBool -HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) -{ - JSString *str = js_NewStringCopyN(cx, buf, len); - if (!str) - return JS_FALSE; - - return PushPrimitive(cx, jp, StringValue(str)); -} - -static JSBool -HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) -{ - const KeywordInfo *ki = FindKeyword(buf, len); - if (!ki || ki->tokentype != TOK_PRIMARY) { - // bad keyword - return JSONParseError(jp, cx); - } - - Value keyword; - if (buf[0] == 'n') { - keyword.setNull(); - } else if (buf[0] == 't') { - keyword.setBoolean(true); - } else if (buf[0] == 'f') { - keyword.setBoolean(false); - } else { - return JSONParseError(jp, cx); - } - - return PushPrimitive(cx, jp, keyword); -} - -static JSBool -HandleDataString(JSContext *cx, JSONParser *jp) -{ - JSBool ok = HandleString(cx, jp, jp->buffer.begin(), jp->buffer.length()); - if (ok) - jp->buffer.clear(); - return ok; -} - -static JSBool -HandleDataKeyString(JSContext *cx, JSONParser *jp) -{ - JSBool ok = jp->objectKey.append(jp->buffer.begin(), jp->buffer.end()); - if (ok) - jp->buffer.clear(); - return ok; -} - -static JSBool -HandleDataNumber(JSContext *cx, JSONParser *jp) -{ - JSBool ok = HandleNumber(cx, jp, jp->buffer.begin(), jp->buffer.length()); - if (ok) - jp->buffer.clear(); - return ok; -} - -static JSBool -HandleDataKeyword(JSContext *cx, JSONParser *jp) -{ - JSBool ok = HandleKeyword(cx, jp, jp->buffer.begin(), jp->buffer.length()); - if (ok) - jp->buffer.clear(); - return ok; -} - -JSBool -js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len, - DecodingMode decodingMode) -{ - CHECK_REQUEST(cx); - - if (*jp->statep == JSON_PARSE_STATE_INIT) { - PushState(cx, jp, JSON_PARSE_STATE_VALUE); - } - - for (uint32 i = 0; i < len; i++) { - jschar c = data[i]; - switch (*jp->statep) { - case JSON_PARSE_STATE_ARRAY_INITIAL_VALUE: - if (c == ']') { - if (!PopState(cx, jp)) - return JS_FALSE; - JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT); - if (!CloseArray(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - break; - } - // fall through if non-empty array or whitespace - - case JSON_PARSE_STATE_VALUE: - if (c == '"') { - *jp->statep = JSON_PARSE_STATE_STRING; - break; - } - - if (IsNumChar(c)) { - *jp->statep = JSON_PARSE_STATE_NUMBER; - if (!jp->buffer.append(c)) - return JS_FALSE; - break; - } - - if (JS7_ISLET(c)) { - *jp->statep = JSON_PARSE_STATE_KEYWORD; - if (!jp->buffer.append(c)) - return JS_FALSE; - break; - } - - if (c == '{') { - *jp->statep = JSON_PARSE_STATE_OBJECT_AFTER_PAIR; - if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_INITIAL_PAIR)) - return JS_FALSE; - } else if (c == '[') { - *jp->statep = JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT; - if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_ARRAY_INITIAL_VALUE)) - return JS_FALSE; - } else if (JS_ISXMLSPACE(c)) { - // nothing to do - } else if (decodingMode == LEGACY && c == ']') { - if (!PopState(cx, jp)) - return JS_FALSE; - JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT); - if (!CloseArray(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - } else { - return JSONParseError(jp, cx); - } - break; - - case JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT: - if (c == ',') { - if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE)) - return JS_FALSE; - } else if (c == ']') { - if (!CloseArray(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - } else if (!JS_ISXMLSPACE(c)) { - return JSONParseError(jp, cx); - } - break; - - case JSON_PARSE_STATE_OBJECT_AFTER_PAIR: - if (c == ',') { - if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR)) - return JS_FALSE; - } else if (c == '}') { - if (!CloseObject(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - } else if (!JS_ISXMLSPACE(c)) { - return JSONParseError(jp, cx); - } - break; - - case JSON_PARSE_STATE_OBJECT_INITIAL_PAIR: - if (c == '}') { - if (!PopState(cx, jp)) - return JS_FALSE; - JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR); - if (!CloseObject(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - break; - } - // fall through if non-empty object or whitespace - - case JSON_PARSE_STATE_OBJECT_PAIR: - if (c == '"') { - // we want to be waiting for a : when the string has been read - *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR; - if (!PushState(cx, jp, JSON_PARSE_STATE_STRING)) - return JS_FALSE; - } else if (JS_ISXMLSPACE(c)) { - // nothing to do - } else if (decodingMode == LEGACY && c == '}') { - if (!PopState(cx, jp)) - return JS_FALSE; - JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR); - if (!CloseObject(cx, jp) || !PopState(cx, jp)) - return JS_FALSE; - } else { - return JSONParseError(jp, cx); - } - break; - - case JSON_PARSE_STATE_OBJECT_IN_PAIR: - if (c == ':') { - *jp->statep = JSON_PARSE_STATE_VALUE; - } else if (!JS_ISXMLSPACE(c)) { - return JSONParseError(jp, cx); - } - break; - - case JSON_PARSE_STATE_STRING: - if (c == '"') { - if (!PopState(cx, jp)) - return JS_FALSE; - if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) { - if (!HandleDataKeyString(cx, jp)) - return JS_FALSE; - } else { - if (!HandleDataString(cx, jp)) - return JS_FALSE; - } - } else if (c == '\\') { - *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE; - } else if (c <= 0x1F) { - // The JSON lexical grammer does not allow a JSONStringCharacter to be - // any of the Unicode characters U+0000 thru U+001F (control characters). - return JSONParseError(jp, cx); - } else { - if (!jp->buffer.append(c)) - return JS_FALSE; - } - break; - - case JSON_PARSE_STATE_STRING_ESCAPE: - switch (c) { - case '"': - case '\\': - case '/': - break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - default : - if (c == 'u') { - jp->numHex = 0; - jp->hexChar = 0; - *jp->statep = JSON_PARSE_STATE_STRING_HEX; - continue; - } else { - return JSONParseError(jp, cx); - } - } - - if (!jp->buffer.append(c)) - return JS_FALSE; - *jp->statep = JSON_PARSE_STATE_STRING; - break; - - case JSON_PARSE_STATE_STRING_HEX: - if (('0' <= c) && (c <= '9')) { - jp->hexChar = (jp->hexChar << 4) | (c - '0'); - } else if (('a' <= c) && (c <= 'f')) { - jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a); - } else if (('A' <= c) && (c <= 'F')) { - jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a); - } else { - return JSONParseError(jp, cx); - } - - if (++(jp->numHex) == 4) { - if (!jp->buffer.append(jp->hexChar)) - return JS_FALSE; - jp->hexChar = 0; - jp->numHex = 0; - *jp->statep = JSON_PARSE_STATE_STRING; - } - break; - - case JSON_PARSE_STATE_KEYWORD: - if (JS7_ISLET(c)) { - if (!jp->buffer.append(c)) - return JS_FALSE; - } else { - // this character isn't part of the keyword, process it again - i--; - if (!PopState(cx, jp)) - return JS_FALSE; - - if (!HandleDataKeyword(cx, jp)) - return JS_FALSE; - } - break; - - case JSON_PARSE_STATE_NUMBER: - if (IsNumChar(c)) { - if (!jp->buffer.append(c)) - return JS_FALSE; - } else { - // this character isn't part of the number, process it again - i--; - if (!PopState(cx, jp)) - return JS_FALSE; - if (!HandleDataNumber(cx, jp)) - return JS_FALSE; - } - break; - - case JSON_PARSE_STATE_FINISHED: - if (!JS_ISXMLSPACE(c)) { - // extra input - return JSONParseError(jp, cx); - } - break; - - default: - JS_NOT_REACHED("Invalid JSON parser state"); - } - } - - return JS_TRUE; -} - #if JS_HAS_TOSOURCE static JSBool json_toSource(JSContext *cx, uintN argc, Value *vp) @@ -1491,11 +928,10 @@ static JSFunctionSpec json_static_methods[] = { JSObject * js_InitJSONClass(JSContext *cx, JSObject *obj) { - JSObject *JSON; - - JSON = NewNonFunction(cx, &js_JSONClass, NULL, obj); - if (!JSON) + JSObject *JSON = NewObjectWithClassProto(cx, &JSONClass, NULL, obj); + if (!JSON || !JSON->setSingletonType(cx)) return NULL; + if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON), JS_PropertyStub, JS_StrictPropertyStub, 0)) return NULL; @@ -1503,7 +939,7 @@ js_InitJSONClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, JSON, json_static_methods)) return NULL; - MarkStandardClassInitializedNoProto(obj, &js_JSONClass); + MarkStandardClassInitializedNoProto(obj, &JSONClass); return JSON; } diff --git a/deps/mozjs/js/src/json.h b/deps/mozjs/js/src/json.h index 005b8005aa7..64c36dbca0a 100644 --- a/deps/mozjs/js/src/json.h +++ b/deps/mozjs/js/src/json.h @@ -40,14 +40,12 @@ #include "jsprvtd.h" #include "jspubtd.h" -#include "jsvalue.h" -#include "jsvector.h" + +#include "js/Vector.h" #define JSON_MAX_DEPTH 2048 #define JSON_PARSER_BUFSIZE 1024 -extern js::Class js_JSONClass; - extern JSObject * js_InitJSONClass(JSContext *cx, JSObject *obj); @@ -55,69 +53,6 @@ extern JSBool js_Stringify(JSContext *cx, js::Value *vp, JSObject *replacer, js::Value space, js::StringBuffer &sb); -extern JSBool js_TryJSON(JSContext *cx, js::Value *vp); - -/* JSON parsing states; most permit leading whitespace. */ -enum JSONParserState { - /* Start of string. */ - JSON_PARSE_STATE_INIT, - - /* JSON fully processed, expecting only trailing whitespace. */ - JSON_PARSE_STATE_FINISHED, - - /* Start of JSON value. */ - JSON_PARSE_STATE_VALUE, - - /* Start of first key/value pair in object, or at }. */ - JSON_PARSE_STATE_OBJECT_INITIAL_PAIR, - - /* Start of subsequent key/value pair in object, after delimiting comma. */ - JSON_PARSE_STATE_OBJECT_PAIR, - - /* At : in key/value pair in object. */ - JSON_PARSE_STATE_OBJECT_IN_PAIR, - - /* Immediately after key/value pair in object: at , or }. */ - JSON_PARSE_STATE_OBJECT_AFTER_PAIR, - - /* Start of first element of array or at ]. */ - JSON_PARSE_STATE_ARRAY_INITIAL_VALUE, - - /* Immediately after element in array: at , or ]. */ - JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT, - - - /* The following states allow no leading whitespace. */ - - /* Within string literal. */ - JSON_PARSE_STATE_STRING, - - /* At first character after \ in string literal. */ - JSON_PARSE_STATE_STRING_ESCAPE, - - /* Within numbers in \uXXXX in string literal. */ - JSON_PARSE_STATE_STRING_HEX, - - /* Within numeric literal. */ - JSON_PARSE_STATE_NUMBER, - - /* Handling keywords (only null/true/false pass validity post-check). */ - JSON_PARSE_STATE_KEYWORD -}; - -struct JSONParser; - -extern JSONParser * -js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false); - -/* Aargh, Windows. */ -#ifdef STRICT -#undef STRICT -#endif -#ifdef LEGACY -#undef LEGACY -#endif - /* * The type of JSON decoding to perform. Strict decoding is to-the-spec; * legacy decoding accepts a few non-JSON syntaxes historically accepted by the @@ -127,13 +62,6 @@ js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false */ enum DecodingMode { STRICT, LEGACY }; -extern JS_FRIEND_API(JSBool) -js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len, - DecodingMode decodingMode = STRICT); - -extern bool -js_FinishJSONParse(JSContext *cx, JSONParser *jp, const js::Value &reviver); - namespace js { extern JS_FRIEND_API(JSBool) diff --git a/deps/mozjs/js/src/jsonparser.cpp b/deps/mozjs/js/src/jsonparser.cpp index 286e68d9a50..7b14205e97a 100644 --- a/deps/mozjs/js/src/jsonparser.cpp +++ b/deps/mozjs/js/src/jsonparser.cpp @@ -48,21 +48,21 @@ using namespace js; void -JSONSourceParser::error(const char *msg) +JSONParser::error(const char *msg) { if (errorHandling == RaiseError) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, msg); } bool -JSONSourceParser::errorReturn() +JSONParser::errorReturn() { return errorHandling == NoError; } -template -JSONSourceParser::Token -JSONSourceParser::readString() +template +JSONParser::Token +JSONParser::readString() { JS_ASSERT(current < end); JS_ASSERT(*current == '"'); @@ -81,14 +81,14 @@ JSONSourceParser::readString() * Optimization: if the source contains no escaped characters, create the * string directly from the source text. */ - RangeCheckedPointer start = current; + RangedPtr start = current; for (; current < end; current++) { if (*current == '"') { size_t length = current - start; current++; - JSFlatString *str = (ST == JSONSourceParser::PropertyName) - ? js_AtomizeChars(cx, start, length, 0) - : js_NewStringCopyN(cx, start, length); + JSFlatString *str = (ST == JSONParser::PropertyName) + ? js_AtomizeChars(cx, start.get(), length) + : js_NewStringCopyN(cx, start.get(), length); if (!str) return token(OOM); return stringToken(str); @@ -110,7 +110,7 @@ JSONSourceParser::readString() */ StringBuffer buffer(cx); do { - if (start < current && !buffer.append(start, current)) + if (start < current && !buffer.append(start.get(), current.get())) return token(OOM); if (current >= end) @@ -118,7 +118,7 @@ JSONSourceParser::readString() jschar c = *current++; if (c == '"') { - JSFlatString *str = (ST == JSONSourceParser::PropertyName) + JSFlatString *str = (ST == JSONParser::PropertyName) ? buffer.finishAtom() : buffer.finishString(); if (!str) @@ -181,8 +181,8 @@ JSONSourceParser::readString() return token(Error); } -JSONSourceParser::Token -JSONSourceParser::readNumber() +JSONParser::Token +JSONParser::readNumber() { JS_ASSERT(current < end); JS_ASSERT(JS7_ISDEC(*current) || *current == '-'); @@ -200,7 +200,7 @@ JSONSourceParser::readNumber() return token(Error); } - const RangeCheckedPointer digitStart = current; + const RangedPtr digitStart = current; /* 0|[1-9][0-9]+ */ if (!JS7_ISDEC(*current)) { @@ -218,7 +218,7 @@ JSONSourceParser::readNumber() if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) { const jschar *dummy; jsdouble d; - if (!GetPrefixInteger(cx, digitStart, current, 10, &dummy, &d)) + if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d)) return token(OOM); JS_ASSERT(current == dummy); return numberToken(negative ? -d : d); @@ -264,7 +264,7 @@ JSONSourceParser::readNumber() jsdouble d; const jschar *finish; - if (!js_strtod(cx, digitStart, current, &finish, &d)) + if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d)) return token(OOM); JS_ASSERT(current == finish); return numberToken(negative ? -d : d); @@ -276,8 +276,8 @@ IsJSONWhitespace(jschar c) return c == '\t' || c == '\r' || c == '\n' || c == ' '; } -JSONSourceParser::Token -JSONSourceParser::advance() +JSONParser::Token +JSONParser::advance() { while (current < end && IsJSONWhitespace(*current)) current++; @@ -357,8 +357,8 @@ JSONSourceParser::advance() } } -JSONSourceParser::Token -JSONSourceParser::advanceAfterObjectOpen() +JSONParser::Token +JSONParser::advanceAfterObjectOpen() { JS_ASSERT(current[-1] == '{'); @@ -382,7 +382,7 @@ JSONSourceParser::advanceAfterObjectOpen() } static inline void -AssertPastValue(const jschar *current) +AssertPastValue(const RangedPtr current) { /* * We're past an arbitrary JSON value, so the previous character is @@ -408,8 +408,8 @@ AssertPastValue(const jschar *current) JS7_ISDEC(current[-1])); } -JSONSourceParser::Token -JSONSourceParser::advanceAfterArrayElement() +JSONParser::Token +JSONParser::advanceAfterArrayElement() { AssertPastValue(current); @@ -434,8 +434,8 @@ JSONSourceParser::advanceAfterArrayElement() return token(Error); } -JSONSourceParser::Token -JSONSourceParser::advancePropertyName() +JSONParser::Token +JSONParser::advancePropertyName() { JS_ASSERT(current[-1] == ','); @@ -466,8 +466,8 @@ JSONSourceParser::advancePropertyName() return token(Error); } -JSONSourceParser::Token -JSONSourceParser::advancePropertyColon() +JSONParser::Token +JSONParser::advancePropertyColon() { JS_ASSERT(current[-1] == '"'); @@ -487,8 +487,8 @@ JSONSourceParser::advancePropertyColon() return token(Error); } -JSONSourceParser::Token -JSONSourceParser::advanceAfterProperty() +JSONParser::Token +JSONParser::advanceAfterProperty() { AssertPastValue(current); @@ -514,13 +514,13 @@ JSONSourceParser::advanceAfterProperty() } /* - * This enum is local to JSONSourceParser::parse, below, but ISO C++98 doesn't - * allow templates to depend on local types. Boo-urns! + * This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow + * templates to depend on local types. Boo-urns! */ enum ParserState { FinishArrayElement, FinishObjectMember, JSONValue }; bool -JSONSourceParser::parse(Value *vp) +JSONParser::parse(Value *vp) { Vector stateStack(cx); AutoValueVector valueStack(cx); @@ -538,9 +538,9 @@ JSONSourceParser::parse(Value *vp) * js_CheckForStringIndex. */ jsid propid = ATOM_TO_JSID(&valueStack.popCopy().toString()->asAtom()); - if (!js_DefineNativeProperty(cx, &valueStack.back().toObject(), propid, v, - PropertyStub, StrictPropertyStub, JSPROP_ENUMERATE, - 0, 0, NULL)) + if (!DefineNativeProperty(cx, &valueStack.back().toObject(), propid, v, + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, + 0, 0)) { return false; } @@ -584,7 +584,7 @@ JSONSourceParser::parse(Value *vp) case FinishArrayElement: { Value v = valueStack.popCopy(); - if (!js_ArrayCompPush(cx, &valueStack.back().toObject(), v)) + if (!js_NewbornArrayPush(cx, &valueStack.back().toObject(), v)) return false; token = advanceAfterArrayElement(); if (token == Comma) { @@ -634,7 +634,7 @@ JSONSourceParser::parse(Value *vp) } case ObjectOpen: { - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj || !valueStack.append(ObjectValue(*obj))) return false; token = advanceAfterObjectOpen(); diff --git a/deps/mozjs/js/src/jsonparser.h b/deps/mozjs/js/src/jsonparser.h index 292323d0730..cfc658a3a24 100644 --- a/deps/mozjs/js/src/jsonparser.h +++ b/deps/mozjs/js/src/jsonparser.h @@ -41,19 +41,20 @@ #ifndef jsonparser_h___ #define jsonparser_h___ +#include "mozilla/Attributes.h" +#include "mozilla/RangedPtr.h" + #include "jscntxt.h" #include "jsstr.h" -#include "jstl.h" -#include "jsvalue.h" /* - * This class should be JSONParser, but the old JSON parser uses that name, so - * until we remove the old parser the class name will be overlong. - * * NB: This class must only be used on the stack as it contains a js::Value. */ -class JSONSourceParser +class JSONParser { + JSONParser(const JSONParser &other) MOZ_DELETE; + void operator=(const JSONParser &other) MOZ_DELETE; + public: enum ErrorHandling { RaiseError, NoError }; enum ParsingMode { StrictJSON, LegacyJSON }; @@ -62,8 +63,8 @@ class JSONSourceParser /* Data members */ JSContext * const cx; - js::RangeCheckedPointer current; - const js::RangeCheckedPointer end; + mozilla::RangedPtr current; + const mozilla::RangedPtr end; js::Value v; @@ -88,11 +89,11 @@ class JSONSourceParser * Description of this syntax is deliberately omitted: new code should only * use strict JSON parsing. */ - JSONSourceParser(JSContext *cx, const jschar *data, size_t length, - ParsingMode parsingMode = StrictJSON, - ErrorHandling errorHandling = RaiseError) + JSONParser(JSContext *cx, const jschar *data, size_t length, + ParsingMode parsingMode = StrictJSON, + ErrorHandling errorHandling = RaiseError) : cx(cx), - current(data, data, length), + current(data, length), end(data + length, data, length), parsingMode(parsingMode), errorHandling(errorHandling) diff --git a/deps/mozjs/js/src/jsopcode.cpp b/deps/mozjs/js/src/jsopcode.cpp index 74fa354cdc3..97eb9b25075 100644 --- a/deps/mozjs/js/src/jsopcode.cpp +++ b/deps/mozjs/js/src/jsopcode.cpp @@ -48,9 +48,10 @@ #include #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" @@ -58,33 +59,38 @@ #include "jsatom.h" #include "jscntxt.h" #include "jsversion.h" -#include "jsemit.h" #include "jsfun.h" #include "jsiter.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#include "jsstaticcheck.h" -#include "jsvector.h" +#include "ds/Sort.h" + +#include "frontend/BytecodeEmitter.h" +#include "frontend/TokenStream.h" +#include "vm/Debugger.h" + +#include "jscntxtinlines.h" #include "jsobjinlines.h" +#include "jsopcodeinlines.h" #include "jsscriptinlines.h" -#include "jscntxtinlines.h" #include "jsautooplen.h" +#include "vm/RegExpObject-inl.h" + +using namespace mozilla; using namespace js; using namespace js::gc; /* * Index limit must stay within 32 bits. */ -JS_STATIC_ASSERT(sizeof(uint32) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1); +JS_STATIC_ASSERT(sizeof(uint32_t) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1); /* Verify JSOP_XXX_LENGTH constant definitions. */ #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ @@ -115,10 +121,9 @@ static const char *CodeToken[] = { #undef OPDEF }; -#if defined(DEBUG) || defined(JS_JIT_SPEW) || defined(JS_METHODJIT_SPEW) /* - * Array of JS bytecode names used by DEBUG-only js_Disassemble and by - * JIT debug spew. + * Array of JS bytecode names used by PC count JSON, DEBUG-only js_Disassemble + * and JIT debug spew. */ const char *js_CodeName[] = { #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ @@ -126,90 +131,91 @@ const char *js_CodeName[] = { #include "jsopcode.tbl" #undef OPDEF }; -#endif /************************************************************************/ -static ptrdiff_t -GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) -{ - uint32 type; +#define COUNTS_LEN 16 - type = JOF_OPTYPE(*pc); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) - return GET_JUMPX_OFFSET(pc2); - return GET_JUMP_OFFSET(pc2); +typedef Vector DupBuffer; + +static bool +Dup(const char *chars, DupBuffer *cb) +{ + return cb->append(chars, strlen(chars) + 1); } uintN -js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, - ptrdiff_t pcoff) +js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff) { - JSOp op; - uintN span, base; - - op = js_GetOpcode(cx, script, pc); + JSOp op = JSOp(*pc); JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN); + JS_ASSERT(js_CodeSpec[op].format & JOF_ATOM); /* * We need to detect index base prefix. It presents when resetbase * follows the bytecode. */ - span = js_CodeSpec[op].length; - base = 0; + uintN span = js_CodeSpec[op].length; + uintN base = 0; if (pc - script->code + span < script->length) { - if (pc[span] == JSOP_RESETBASE) { + JSOp next = JSOp(pc[span]); + if (next == JSOP_RESETBASE) { + JS_ASSERT(JSOp(pc[-JSOP_INDEXBASE_LENGTH]) == JSOP_INDEXBASE); base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH); - } else if (pc[span] == JSOP_RESETBASE0) { - JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3); - base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16; + } else if (next == JSOP_RESETBASE0) { + JSOp prev = JSOp(pc[-1]); + JS_ASSERT(JSOP_INDEXBASE1 <= prev && prev <= JSOP_INDEXBASE3); + base = (prev - JSOP_INDEXBASE1 + 1) << 16; } } return base + GET_UINT16(pc + pcoff); } -uintN +size_t js_GetVariableBytecodeLength(jsbytecode *pc) { - JSOp op; - uintN jmplen, ncases; + uintN ncases; jsint low, high; - op = (JSOp) *pc; + JSOp op = JSOp(*pc); JS_ASSERT(js_CodeSpec[op].length == -1); switch (op) { - case JSOP_TABLESWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto do_table; case JSOP_TABLESWITCH: - jmplen = JUMP_OFFSET_LEN; - do_table: /* Structure: default-jump case-low case-high case1-jump ... */ - pc += jmplen; + pc += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc); ncases = (uintN)(high - low + 1); - return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen; + return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN; - case JSOP_LOOKUPSWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto do_lookup; default: - JS_ASSERT(op == JSOP_LOOKUPSWITCH); - jmplen = JUMP_OFFSET_LEN; - do_lookup: /* Structure: default-jump case-count (case1-value case1-jump) ... */ - pc += jmplen; + JS_ASSERT(op == JSOP_LOOKUPSWITCH); + pc += JUMP_OFFSET_LEN; ncases = GET_UINT16(pc); - return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen); + return 1 + JUMP_OFFSET_LEN + INDEX_LEN + ncases * (INDEX_LEN + JUMP_OFFSET_LEN); } } +static uint32_t +NumBlockSlots(JSScript *script, jsbytecode *pc) +{ + JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1); + JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH); + JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH); + + return script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock().slotCount(); +} + uintN -js_GetVariableStackUses(JSOp op, jsbytecode *pc) +js::StackUses(JSScript *script, jsbytecode *pc) { - JS_ASSERT(*pc == op || *pc == JSOP_TRAP); + JSOp op = (JSOp) *pc; + const JSCodeSpec &cs = js_CodeSpec[op]; + if (cs.nuses >= 0) + return cs.nuses; + JS_ASSERT(js_CodeSpec[op].nuses == -1); switch (op) { case JSOP_POPN: @@ -218,6 +224,10 @@ js_GetVariableStackUses(JSOp op, jsbytecode *pc) return GET_UINT16(pc); case JSOP_LEAVEBLOCKEXPR: return GET_UINT16(pc) + 1; + case JSOP_ENTERLET0: + return NumBlockSlots(script, pc); + case JSOP_ENTERLET1: + return NumBlockSlots(script, pc) + 1; default: /* stack: fun, this, [argc arguments] */ JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL || @@ -227,70 +237,174 @@ js_GetVariableStackUses(JSOp op, jsbytecode *pc) } uintN -js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc) +js::StackDefs(JSScript *script, jsbytecode *pc) { - JSObject *obj; + JSOp op = (JSOp) *pc; + const JSCodeSpec &cs = js_CodeSpec[op]; + if (cs.ndefs >= 0) + return cs.ndefs; - JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_TRAP); - GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj); - return OBJ_BLOCK_COUNT(cx, obj); + uint32_t n = NumBlockSlots(script, pc); + return op == JSOP_ENTERLET1 ? n + 1 : n; } -class AutoScriptUntrapper { - JSContext *cx; - JSScript *script; - jsbytecode *origPC; - jsbytecode *newPC; +static const char * countBaseNames[] = { + "interp", + "mjit", + "mjit_calls", + "mjit_code", + "mjit_pics" +}; -public: - AutoScriptUntrapper(JSContext *cx, JSScript *script, jsbytecode **pc) - : cx(cx), script(script), origPC(*pc) - { - jsbytecode *newCode = js_UntrapScriptCode(cx, script); - if (newCode == script->code) { - // No change needed - newPC = origPC; - } else { - script->main += newCode - script->code; - *pc = newPC = origPC + (newCode - script->code); - script->code = newCode; - } - } - ~AutoScriptUntrapper() - { - ptrdiff_t delta = newPC - origPC; - if (delta) { - jsbytecode *oldCode = script->code - delta; - cx->free_(script->code); - script->code = oldCode; - script->main -= delta; - } - } +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) == OpcodeCounts::BASE_COUNT); + +static const char * countAccessNames[] = { + "infer_mono", + "infer_di", + "infer_poly", + "infer_barrier", + "infer_nobarrier", + "observe_undefined", + "observe_null", + "observe_boolean", + "observe_int32", + "observe_double", + "observe_string", + "observe_object" }; +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) + + JS_ARRAY_LENGTH(countAccessNames) == OpcodeCounts::ACCESS_COUNT); + +static const char * countElementNames[] = { + "id_int", + "id_double", + "id_other", + "id_unknown", + "elem_typed", + "elem_packed", + "elem_dense", + "elem_other" +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) + + JS_ARRAY_LENGTH(countAccessNames) + + JS_ARRAY_LENGTH(countElementNames) == OpcodeCounts::ELEM_COUNT); + +static const char * countPropertyNames[] = { + "prop_static", + "prop_definite", + "prop_other" +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) + + JS_ARRAY_LENGTH(countAccessNames) + + JS_ARRAY_LENGTH(countPropertyNames) == OpcodeCounts::PROP_COUNT); + +static const char * countArithNames[] = { + "arith_int", + "arith_double", + "arith_other", + "arith_unknown", +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) + + JS_ARRAY_LENGTH(countArithNames) == OpcodeCounts::ARITH_COUNT); + +/* static */ const char * +OpcodeCounts::countName(JSOp op, size_t which) +{ + JS_ASSERT(which < numCounts(op)); + + if (which < BASE_COUNT) + return countBaseNames[which]; + + if (accessOp(op)) { + if (which < ACCESS_COUNT) + return countAccessNames[which - BASE_COUNT]; + if (elementOp(op)) + return countElementNames[which - ACCESS_COUNT]; + if (propertyOp(op)) + return countPropertyNames[which - ACCESS_COUNT]; + JS_NOT_REACHED("bad op"); + return NULL; + } + + if (arithOp(op)) + return countArithNames[which - BASE_COUNT]; + + JS_NOT_REACHED("bad op"); + return NULL; +} + #ifdef DEBUG -/* If pc != NULL, includes a prefix indicating whether the PC is at the current line. */ +JS_FRIEND_API(void) +js_DumpPCCounts(JSContext *cx, JSScript *script, js::Sprinter *sp) +{ + JS_ASSERT(script->pcCounters); + + jsbytecode *pc = script->code; + while (pc < script->code + script->length) { + JSOp op = JSOp(*pc); + + int len = js_CodeSpec[op].length; + jsbytecode *next = (len != -1) ? pc + len : pc + js_GetVariableBytecodeLength(pc); + + if (!js_Disassemble1(cx, script, pc, pc - script->code, true, sp)) + return; + + size_t total = OpcodeCounts::numCounts(op); + double *raw = script->getCounts(pc).rawCounts(); + + Sprint(sp, " {"); + bool printed = false; + for (size_t i = 0; i < total; i++) { + double val = raw[i]; + if (val) { + if (printed) + Sprint(sp, ", "); + Sprint(sp, "\"%s\": %.0f", OpcodeCounts::countName(op, i), val); + printed = true; + } + } + Sprint(sp, "}\n"); + + pc = next; + } +} + +/* + * If pc != NULL, include a prefix indicating whether the PC is at the current line. + * If counts != NULL, include a counter of the number of times each op was executed. + */ JS_FRIEND_API(JSBool) js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, Sprinter *sp) { jsbytecode *next, *end; uintN len; + sp->put("loc "); + if (lines) + sp->put("line"); + sp->put(" op\n"); + sp->put("----- "); + if (lines) + sp->put("----"); + sp->put(" --\n"); + next = script->code; end = next + script->length; while (next < end) { - if (next == script->main) - SprintCString(sp, "main:\n"); + if (next == script->main()) + sp->put("main:\n"); if (pc != NULL) { if (pc == next) - SprintCString(sp, "--> "); + sp->put("--> "); else - SprintCString(sp, " "); + sp->put(" "); } - len = js_Disassemble1(cx, script, next, - next - script->code, - lines, sp); + len = js_Disassemble1(cx, script, next, next - script->code, lines, sp); if (!len) return JS_FALSE; next += len; @@ -307,49 +421,74 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp) JS_FRIEND_API(JSBool) js_DumpPC(JSContext *cx) { - void *mark = JS_ARENA_MARK(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return JS_FALSE; JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter); - fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); + fprintf(stdout, "%s", sprinter.string()); return ok; } JSBool js_DumpScript(JSContext *cx, JSScript *script) { - void *mark = JS_ARENA_MARK(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return JS_FALSE; JSBool ok = js_Disassemble(cx, script, true, &sprinter); - fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); + fprintf(stdout, "%s", sprinter.string()); return ok; } +static char * +QuoteString(Sprinter *sp, JSString *str, uint32_t quote); + static bool ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) { + if (JSVAL_IS_STRING(v)) { + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; + char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"'); + if (!nbytes) + return false; + nbytes = JS_sprintf_append(NULL, "%s", nbytes); + if (!nbytes) + return false; + bytes->initBytes(nbytes); + return true; + } + + if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) { + char *source = JS_sprintf_append(NULL, ""); + if (!source) + return false; + bytes->initBytes(source); + return true; + } + if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *obj = JSVAL_TO_OBJECT(v); - Class *clasp = obj->getClass(); - - if (clasp == &js_BlockClass) { - char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj)); + if (obj->isBlock()) { + char *source = JS_sprintf_append(NULL, "depth %d {", obj->asBlock().stackDepth()); if (!source) return false; Shape::Range r = obj->lastProperty()->all(); while (!r.empty()) { const Shape &shape = r.front(); + JSAtom *atom = JSID_IS_INT(shape.propid()) + ? cx->runtime->atomState.emptyAtom + : JSID_TO_ATOM(shape.propid()); + JSAutoByteString bytes; - if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.id), &bytes)) + if (!js_AtomToPrintableString(cx, atom, &bytes)) return false; r.popFront(); source = JS_sprintf_append(source, "%s: %d%s", - bytes.ptr(), shape.shortid, + bytes.ptr(), shape.shortid(), !r.empty() ? ", " : ""); if (!source) return false; @@ -362,42 +501,30 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) return true; } - if (clasp == &js_FunctionClass) { - JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj); - JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); + if (obj->isFunction()) { + JSString *str = JS_DecompileFunction(cx, obj->toFunction(), JS_DONT_PRETTY_PRINT); if (!str) return false; return bytes->encode(cx, str); } - if (clasp == &js_RegExpClass) { - AutoValueRooter tvr(cx); - if (!js_regexp_toString(cx, obj, tvr.addr())) + if (obj->isRegExp()) { + JSString *source = obj->asRegExp().toString(cx); + if (!source) return false; - return bytes->encode(cx, JSVAL_TO_STRING(Jsvalify(tvr.value()))); + JS::Anchor anchor(source); + return bytes->encode(cx, source); } } - return !!js_ValueToPrintable(cx, Valueify(v), bytes, true); + return !!js_ValueToPrintable(cx, v, bytes, true); } JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, Sprinter *sp) { - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t len, off, jmplen; - uint32 type; - JSAtom *atom; - uintN index; - JSObject *obj; - jsval v; - jsint i; - - AutoScriptUntrapper untrapper(cx, script, &pc); - - op = (JSOp)*pc; + JSOp op = (JSOp)*pc; if (op >= JSOP_LIMIT) { char numBuf1[12], numBuf2[12]; JS_snprintf(numBuf1, sizeof numBuf1, "%d", op); @@ -406,40 +533,48 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2); return 0; } - cs = &js_CodeSpec[op]; - len = (ptrdiff_t) cs->length; + const JSCodeSpec *cs = &js_CodeSpec[op]; + ptrdiff_t len = (ptrdiff_t) cs->length; Sprint(sp, "%05u:", loc); if (lines) Sprint(sp, "%4u", JS_PCToLineNumber(cx, script, pc)); Sprint(sp, " %s", js_CodeName[op]); - type = JOF_TYPE(cs->format); - switch (type) { + + switch (JOF_TYPE(cs->format)) { case JOF_BYTE: + // Scan the trynotes to find the associated catch block + // and make the try opcode look like a jump instruction + // with an offset. This simplifies code coverage analysis + // based on this disassembled output. + if (op == JSOP_TRY) { + JSTryNoteArray *trynotes = script->trynotes(); + uint32_t i; + for(i = 0; i < trynotes->length; i++) { + JSTryNote note = trynotes->vector[i]; + if (note.kind == JSTRY_CATCH && note.start == loc + 1) { + Sprint(sp, " %u (%+d)", + (unsigned int) (loc+note.length+1), + (int) (note.length+1)); + break; + } + } + } break; - case JOF_JUMP: - case JOF_JUMPX: - off = GetJumpOffset(pc, pc); - Sprint(sp, " %u (%d)", loc + intN(off), intN(off)); + case JOF_JUMP: { + ptrdiff_t off = GET_JUMP_OFFSET(pc); + Sprint(sp, " %u (%+d)", loc + (intN) off, (intN) off); break; + } - case JOF_ATOM: - case JOF_OBJECT: - case JOF_REGEXP: - index = js_GetIndexFromBytecode(cx, script, pc, 0); - if (type == JOF_ATOM) { - if (op == JSOP_DOUBLE) { - v = Jsvalify(script->getConst(index)); - } else { - JS_GET_SCRIPT_ATOM(script, pc, index, atom); - v = STRING_TO_JSVAL(atom); - } + case JOF_ATOM: { + uintN index = js_GetIndexFromBytecode(script, pc, 0); + jsval v; + if (op == JSOP_DOUBLE) { + v = script->getConst(index); } else { - if (type == JOF_OBJECT) - obj = script->getObject(index); - else - obj = script->getRegExp(index); - v = OBJECT_TO_JSVAL(obj); + JSAtom *atom = script->getAtom(index); + v = STRING_TO_JSVAL(atom); } { JSAutoByteString bytes; @@ -448,75 +583,71 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, Sprint(sp, " %s", bytes.ptr()); } break; + } + + case JOF_OBJECT: { + /* Don't call obj.toSource if analysis/inference is active. */ + if (cx->compartment->activeAnalysis) { + Sprint(sp, " object"); + break; + } - case JOF_GLOBAL: - atom = script->getGlobalAtom(GET_SLOTNO(pc)); - v = STRING_TO_JSVAL(atom); + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); { JSAutoByteString bytes; - if (!ToDisassemblySource(cx, v, &bytes)) + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) return 0; Sprint(sp, " %s", bytes.ptr()); } break; + } - case JOF_UINT16PAIR: - i = (jsint)GET_UINT16(pc); - Sprint(sp, " %d", i); - pc += UINT16_LEN; - /* FALL THROUGH */ - - case JOF_UINT16: - i = (jsint)GET_UINT16(pc); - goto print_int; + case JOF_REGEXP: { + JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc)); + JSAutoByteString bytes; + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) + return 0; + Sprint(sp, " %s", bytes.ptr()); + break; + } case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: { - jsbytecode *pc2; jsint i, low, high; - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; + ptrdiff_t off = GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; Sprint(sp, " defaultOffset %d low %d high %d", intN(off), low, high); for (i = low; i <= high; i++) { - off = GetJumpOffset(pc, pc2); + off = GET_JUMP_OFFSET(pc2); Sprint(sp, "\n\t%d: %d", i, intN(off)); - pc2 += jmplen; + pc2 += JUMP_OFFSET_LEN; } len = 1 + pc2 - pc; break; } case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: { - jsbytecode *pc2; jsatomid npairs; - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; + ptrdiff_t off = GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; npairs = GET_UINT16(pc2); pc2 += UINT16_LEN; Sprint(sp, " offset %d npairs %u", intN(off), uintN(npairs)); while (npairs) { - uint16 constIndex = GET_INDEX(pc2); + uint16_t constIndex = GET_INDEX(pc2); pc2 += INDEX_LEN; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; + off = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; JSAutoByteString bytes; - if (!ToDisassemblySource(cx, Jsvalify(script->getConst(constIndex)), &bytes)) + if (!ToDisassemblySource(cx, script->getConst(constIndex), &bytes)) return 0; Sprint(sp, "\n\t%s: %d", bytes.ptr(), intN(off)); npairs--; @@ -533,32 +664,36 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, Sprint(sp, " %u", GET_SLOTNO(pc)); break; - case JOF_SLOTATOM: case JOF_SLOTOBJECT: { Sprint(sp, " %u", GET_SLOTNO(pc)); - index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN); - if (type == JOF_SLOTATOM) { - JS_GET_SCRIPT_ATOM(script, pc, index, atom); - v = STRING_TO_JSVAL(atom); - } else { - obj = script->getObject(index); - v = OBJECT_TO_JSVAL(obj); - } - + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + SLOTNO_LEN)); JSAutoByteString bytes; - if (!ToDisassemblySource(cx, v, &bytes)) + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) return 0; Sprint(sp, " %s", bytes.ptr()); break; } + { + int i; + + case JOF_UINT16PAIR: + i = (jsint)GET_UINT16(pc); + Sprint(sp, " %d", i); + pc += UINT16_LEN; + /* FALL THROUGH */ + + case JOF_UINT16: + i = (jsint)GET_UINT16(pc); + goto print_int; + case JOF_UINT24: JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY); i = (jsint)GET_UINT24(pc); goto print_int; case JOF_UINT8: - i = pc[1]; + i = GET_UINT8(pc); goto print_int; case JOF_INT8: @@ -571,6 +706,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, print_int: Sprint(sp, " %d", i); break; + } default: { char numBuf[12]; @@ -580,7 +716,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, return 0; } } - SprintCString(sp, "\n"); + sp->put("\n"); return len; } @@ -588,101 +724,224 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, /************************************************************************/ -#define OFF2STR(sp,off) ((sp)->base + (off)) -#define STR2OFF(sp,str) ((str) - (sp)->base) -#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) +const size_t Sprinter::DefaultSize = 64; -static JSBool -SprintEnsureBuffer(Sprinter *sp, size_t len) +bool +Sprinter::realloc_(size_t newSize) { - ptrdiff_t nb; - char *base; + JS_ASSERT(newSize > (size_t) offset); + char *newBuf = (char *) context->realloc_(base, newSize); + if (!newBuf) + return false; + base = newBuf; + size = newSize; + base[size - 1] = 0; + return true; +} - nb = (sp->offset + len + 1) - sp->size; - if (nb < 0) - return JS_TRUE; - base = sp->base; - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); - } else { - JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); - } - if (!base) { - js_ReportOutOfScriptQuota(sp->context); - return JS_FALSE; - } - sp->base = base; - sp->size += nb; - return JS_TRUE; +Sprinter::Sprinter(JSContext *cx) + : context(cx), +#ifdef DEBUG + initialized(false), +#endif + base(NULL), size(0), offset(0) +{ } + +Sprinter::~Sprinter() +{ +#ifdef DEBUG + if (initialized) + checkInvariants(); +#endif + context->free_(base); } -namespace js { +bool +Sprinter::init() +{ + JS_ASSERT(!initialized); + base = (char *) context->malloc_(DefaultSize); + if (!base) + return false; +#ifdef DEBUG + initialized = true; +#endif + *base = 0; + size = DefaultSize; + base[size - 1] = 0; + return true; +} + +void +Sprinter::checkInvariants() const +{ + JS_ASSERT(initialized); + JS_ASSERT((size_t) offset < size); + JS_ASSERT(base[size - 1] == 0); +} + +const char * +Sprinter::string() const +{ + return base; +} + +const char * +Sprinter::stringEnd() const +{ + return base + offset; +} char * -SprintReserveAmount(Sprinter *sp, size_t len) +Sprinter::stringAt(ptrdiff_t off) const { - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) - return NULL; + JS_ASSERT(off >= 0 && (size_t) off < size); + return base + off; +} - /* Advance offset and return the previous offset for caller to fill. */ - ptrdiff_t offset = sp->offset; - sp->offset += len; - return sp->base + offset; +char & +Sprinter::operator[](size_t off) +{ + JS_ASSERT(off >= 0 && (size_t) off < size); + return *(base + off); +} + +bool +Sprinter::empty() const +{ + return *base == 0; +} + +char * +Sprinter::reserve(size_t len) +{ + InvariantChecker ic(this); + + while (len + 1 > size - offset) { /* Include trailing \0 */ + if (!realloc_(size * 2)) + return NULL; + } + + char *sb = base + offset; + offset += len; + return sb; +} + +char * +Sprinter::reserveAndClear(size_t len) +{ + char *sb = reserve(len); + if (sb) + memset(sb, 0, len); + return sb; } ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len) +Sprinter::put(const char *s, size_t len) { - ptrdiff_t offset = sp->size; /* save old size */ - char *bp = sp->base; /* save old base */ + InvariantChecker ic(this); + + const char *oldBase = base; + const char *oldEnd = base + size; - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) + ptrdiff_t oldOffset = offset; + char *bp = reserve(len); + if (!bp) return -1; - if (sp->base != bp && /* buffer was realloc'ed */ - s >= bp && s < bp + offset) { /* s was within the buffer */ - s = sp->base + (s - bp); /* this is where it lives now */ + /* s is within the buffer already */ + if (s >= oldBase && s < oldEnd) { + /* buffer was realloc'ed */ + if (base != oldBase) + s = stringAt(s - oldBase); /* this is where it lives now */ + memmove(bp, s, len); + } else { + js_memcpy(bp, s, len); } - /* Advance offset and copy s into sp's buffer. */ - offset = sp->offset; - sp->offset += len; - bp = sp->base + offset; - memmove(bp, s, len); bp[len] = 0; - return offset; + return oldOffset; } ptrdiff_t -SprintCString(Sprinter *sp, const char *s) +Sprinter::put(const char *s) { - return SprintPut(sp, s, strlen(s)); + return put(s, strlen(s)); } ptrdiff_t -SprintString(Sprinter *sp, JSString *str) +Sprinter::putString(JSString *s) { - size_t length = str->length(); - const jschar *chars = str->getChars(sp->context); + InvariantChecker ic(this); + + size_t length = s->length(); + const jschar *chars = s->getChars(context); if (!chars) return -1; - size_t size = js_GetDeflatedStringLength(sp->context, chars, length); - if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) + size_t size = GetDeflatedStringLength(context, chars, length); + if (size == (size_t) -1) return -1; - ptrdiff_t offset = sp->offset; - sp->offset += size; - js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, - &size); - sp->base[sp->offset] = 0; + ptrdiff_t oldOffset = offset; + char *buffer = reserve(size); + if (!buffer) + return -1; + DeflateStringToBuffer(context, chars, length, buffer, &size); + buffer[size] = 0; + + return oldOffset; +} + +int +Sprinter::printf(const char *fmt, ...) +{ + InvariantChecker ic(this); + + do { + va_list va; + va_start(va, fmt); + int i = vsnprintf(base + offset, size - offset, fmt, va); + va_end(va); + + if (i > -1 && (size_t) i < size - offset) { + offset += i; + return i; + } + } while (realloc_(size * 2)); + + return -1; +} + +void +Sprinter::setOffset(const char *end) +{ + JS_ASSERT(end >= base && end < base + size); + offset = end - base; +} + +void +Sprinter::setOffset(ptrdiff_t off) +{ + JS_ASSERT(off >= 0 && (size_t) off < size); + offset = off; +} + +ptrdiff_t +Sprinter::getOffset() const +{ return offset; } ptrdiff_t -Sprint(Sprinter *sp, const char *format, ...) +Sprinter::getOffsetOf(const char *string) const +{ + JS_ASSERT(string >= base && string < base + size); + return string - base; +} + +ptrdiff_t +js::Sprint(Sprinter *sp, const char *format, ...) { va_list ap; char *bp; @@ -695,13 +954,11 @@ Sprint(Sprinter *sp, const char *format, ...) JS_ReportOutOfMemory(sp->context); return -1; } - offset = SprintCString(sp, bp); + offset = sp->put(bp); sp->context->free_(bp); return offset; } -} // namespace js - const char js_EscapeMap[] = { '\b', 'b', '\f', 'f', @@ -712,18 +969,18 @@ const char js_EscapeMap[] = { '"', '"', '\'', '\'', '\\', '\\', - '\0', '0' + '\0' }; #define DONT_ESCAPE 0x10000 static char * -QuoteString(Sprinter *sp, JSString *str, uint32 quote) +QuoteString(Sprinter *sp, JSString *str, uint32_t quote) { /* Sample off first for later return value pointer computation. */ JSBool dontEscape = (quote & DONT_ESCAPE) != 0; jschar qc = (jschar) quote; - ptrdiff_t off = sp->offset; + ptrdiff_t offset = sp->getOffset(); if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; @@ -736,24 +993,23 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) for (const jschar *t = s; t < z; s = ++t) { /* Move t forward from s past un-quote-worthy characters. */ jschar c = *t; - while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' && - !(c >> 8)) { + while (c < 127 && isprint(c) && c != qc && c != '\\' && c != '\t') { c = *++t; if (t == z) break; } - ptrdiff_t len = t - s; - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) - return NULL; + { + ptrdiff_t len = t - s; + ptrdiff_t base = sp->getOffset(); + char *bp = sp->reserve(len); + if (!bp) + return NULL; - /* Advance sp->offset and copy s into sp's buffer. */ - char *bp = sp->base + sp->offset; - sp->offset += len; - while (--len >= 0) - *bp++ = (char) *s++; - *bp = '\0'; + for (ptrdiff_t i = 0; i < len; ++i) + (*sp)[base + i] = (char) *s++; + (*sp)[base + len] = 0; + } if (t == z) break; @@ -761,7 +1017,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) /* Use js_EscapeMap, \u, or \x only if necessary. */ bool ok; const char *e; - if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) { + if (!(c >> 8) && c != 0 && (e = strchr(js_EscapeMap, (int)c)) != NULL) { ok = dontEscape ? Sprint(sp, "%c", (char)c) >= 0 : Sprint(sp, "\\%c", e[1]) >= 0; @@ -783,34 +1039,62 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) /* * If we haven't Sprint'd anything yet, Sprint an empty string so that - * the OFF2STR below gives a valid result. + * the return below gives a valid result. */ - if (off == sp->offset && Sprint(sp, "") < 0) + if (offset == sp->getOffset() && Sprint(sp, "") < 0) return NULL; - return OFF2STR(sp, off); + + return sp->stringAt(offset); } JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote) { - void *mark; - Sprinter sprinter; - char *bytes; - JSString *escstr; - - mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - bytes = QuoteString(&sprinter, str, quote); - escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; - JS_ARENA_RELEASE(&cx->tempPool, mark); + Sprinter sprinter(cx); + if (!sprinter.init()) + return NULL; + char *bytes = QuoteString(&sprinter, str, quote); + JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; return escstr; } /************************************************************************/ -struct JSPrinter { +/* + * Information for associating the decompilation of each opcode in a script + * with the place where it appears in the text for the decompilation of the + * entire script (or the function containing the script). + */ +struct DecompiledOpcode +{ + /* Decompiled text of this opcode. */ + const char *text; + + /* Bytecode into which this opcode was nested, or NULL. */ + jsbytecode *parent; + + /* + * Offset into the parent's decompiled text of the decompiled text of this + * opcode. For opcodes with a NULL parent, this was emitted directly into + * the permanent output at the given offset. + */ + int32_t parentOffset; + + /* + * Surrounded by parentheses when printed, which parentOffset does not + * account for. + */ + bool parenthesized; + + DecompiledOpcode() + : text(NULL), parent(NULL), parentOffset(-1), parenthesized(false) + {} +}; + +struct JSPrinter +{ Sprinter sprinter; /* base class state */ - JSArenaPool pool; /* string allocation pool */ + LifoAlloc pool; /* string allocation pool */ uintN indent; /* indentation in spaces */ bool pretty; /* pretty-print: indent, use newlines */ bool grouped; /* in parenthesized expression context */ @@ -819,20 +1103,26 @@ struct JSPrinter { jsbytecode *dvgfence; /* DecompileExpression fencepost */ jsbytecode **pcstack; /* DecompileExpression modeled stack */ JSFunction *fun; /* interpreted function */ - jsuword *localNames; /* argument and variable names */ + Vector *localNames; /* argument and variable names */ + Vector *decompiledOpcodes; /* optional state for decompiled ops */ + + DecompiledOpcode &decompiled(jsbytecode *pc) { + JS_ASSERT(decompiledOpcodes); + return (*decompiledOpcodes)[pc - script->code]; + } }; JSPrinter * js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, uintN indent, JSBool pretty, JSBool grouped, JSBool strict) { - JSPrinter *jp; - - jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); + JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); if (!jp) return NULL; - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - JS_InitArenaPool(&jp->pool, name, 256, 1, &cx->scriptStackQuota); + new (&jp->sprinter) Sprinter(cx); + if (!jp->sprinter.init()) + return NULL; + new (&jp->pool) LifoAlloc(1024); jp->indent = indent; jp->pretty = !!pretty; jp->grouped = !!grouped; @@ -842,9 +1132,10 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, jp->pcstack = NULL; jp->fun = fun; jp->localNames = NULL; + jp->decompiledOpcodes = NULL; if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) { - jp->localNames = fun->script()->bindings.getLocalNameArray(cx, &jp->pool); - if (!jp->localNames) { + jp->localNames = cx->new_ >(cx); + if (!jp->localNames || !fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) { js_DestroyPrinter(jp); return NULL; } @@ -855,29 +1146,30 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, void js_DestroyPrinter(JSPrinter *jp) { - JS_FinishArenaPool(&jp->pool); + jp->pool.freeAll(); + Foreground::delete_(jp->localNames); jp->sprinter.context->free_(jp); } JSString * js_GetPrinterOutput(JSPrinter *jp) { - JSContext *cx; - JSString *str; + JSContext *cx = jp->sprinter.context; + return JS_NewStringCopyZ(cx, jp->sprinter.string()); +} - cx = jp->sprinter.context; - if (!jp->sprinter.base) - return cx->runtime->emptyString; - str = JS_NewStringCopyZ(cx, jp->sprinter.base); - if (!str) - return NULL; - JS_FreeArenaPool(&jp->pool); - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - return str; +/* Mark the parent and offset into the parent's text for a printed opcode. */ +static inline void +UpdateDecompiledParent(JSPrinter *jp, jsbytecode *pc, jsbytecode *parent, size_t offset) +{ + if (jp->decompiledOpcodes && pc) { + jp->decompiled(pc).parent = parent; + jp->decompiled(pc).parentOffset = offset; + } } /* - * NB: Indexed by SRC_DECL_* defines from jsemit.h. + * NB: Indexed by SRC_DECL_* defines from frontend/BytecodeEmitter.h. */ static const char * const var_prefix[] = {"var ", "const ", "let "}; @@ -938,7 +1230,7 @@ js_printf(JSPrinter *jp, const char *format, ...) } cc = strlen(bp); - if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) + if (jp->sprinter.put(bp, (size_t)cc) < 0) cc = -1; jp->sprinter.context->free_(bp); @@ -949,20 +1241,120 @@ js_printf(JSPrinter *jp, const char *format, ...) JSBool js_puts(JSPrinter *jp, const char *s) { - return SprintCString(&jp->sprinter, s) >= 0; + return jp->sprinter.put(s) >= 0; } /************************************************************************/ -typedef struct SprintStack { +struct SprintStack +{ Sprinter sprinter; /* sprinter for postfix to infix buffering */ ptrdiff_t *offsets; /* stack of postfix string offsets */ jsbytecode *opcodes; /* parallel stack of JS opcodes */ + jsbytecode **bytecodes; /* actual script bytecode pushing the value */ uintN top; /* top of stack index */ uintN inArrayInit; /* array initialiser/comprehension level */ JSBool inGenExp; /* in generator expression */ JSPrinter *printer; /* permanent output goes here */ -} SprintStack; + + explicit SprintStack(JSContext *cx) + : sprinter(cx), offsets(NULL), + opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0), + inGenExp(JS_FALSE), printer(NULL) + { } +}; + +/* + * Set the decompiled text of an opcode, according to an offset into the + * print stack's sprinter buffer. + */ +static inline bool +UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo) +{ + JSPrinter *jp = ss->printer; + + if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) { + const char *text = ss->sprinter.stringAt(todo); + size_t len = strlen(text) + 1; + + char *ntext = ss->printer->pool.newArrayUninitialized(len); + if (!ntext) { + js_ReportOutOfMemory(ss->sprinter.context); + return false; + } + + js_memcpy(ntext, text, len); + jp->decompiled(pc).text = const_cast(ntext); + } + + return true; +} + +static inline const char * +SprintDupeStr(SprintStack *ss, const char *str) +{ + size_t len = strlen(str) + 1; + + const char *nstr = ss->printer->pool.newArrayUninitialized(len); + if (nstr) { + js_memcpy((char *) nstr, str, len); + } else { + js_ReportOutOfMemory(ss->sprinter.context); + nstr = ""; + } + + return nstr; +} + +/* Place an opcode's decompiled text into a printer's permanent output. */ +static inline void +SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc) +{ + ptrdiff_t offset = jp->sprinter.getOffset(); + UpdateDecompiledParent(jp, pc, NULL, offset); + js_printf(jp, "%s", str); +} + +/* + * Place an opcode's decompiled text into the printed output for another + * opcode parentpc, where startOffset indicates the printer offset for the + * start of parentpc. + */ +static inline void +SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc, + jsbytecode *parentpc, ptrdiff_t startOffset) +{ + if (startOffset < 0) { + JS_ASSERT(ss->sprinter.context->isExceptionPending()); + return; + } + ptrdiff_t offset = ss->sprinter.getOffset(); + UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset); + ss->sprinter.put(str); +} + +/* + * Copy the decompiled text for an opcode to all other ops which it was + * decomposed into. + */ +static inline void +CopyDecompiledTextForDecomposedOp(JSPrinter *jp, jsbytecode *pc) +{ + JS_ASSERT(js_CodeSpec[*pc].format & JOF_DECOMPOSE); + + if (jp->decompiledOpcodes) { + size_t len = GetDecomposeLength(pc, js_CodeSpec[*pc].length); + + const char *text = jp->decompiled(pc).text; + + jsbytecode *pc2 = pc + GetBytecodeLength(pc); + for (; pc2 < pc + len; pc2 += GetBytecodeLength(pc2)) { + jp->decompiled(pc2).text = text; + jp->decompiled(pc2).parent = pc; + jp->decompiled(pc2).parentOffset = 0; + } + } +} /* * Find the depth of the operand stack when the interpreter reaches the given @@ -977,7 +1369,7 @@ typedef struct SprintStack { */ static intN ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc, - jsbytecode **pcstack); + jsbytecode **pcstack, jsbytecode **lastDecomposedPC); #define FAILED_EXPRESSION_DECOMPILER ((char *) 1) @@ -1013,7 +1405,6 @@ GetOff(SprintStack *ss, uintN i) if (off >= 0) return off; - JS_ASSERT(off <= -2); JS_ASSERT(ss->printer->pcstack); if (off <= -2 && ss->printer->pcstack) { pc = ss->printer->pcstack[-2 - off]; @@ -1022,15 +1413,16 @@ GetOff(SprintStack *ss, uintN i) if (!bytes) return 0; if (bytes != FAILED_EXPRESSION_DECOMPILER) { - off = SprintCString(&ss->sprinter, bytes); + off = ss->sprinter.put(bytes); if (off < 0) off = 0; ss->offsets[i] = off; ss->sprinter.context->free_(bytes); return off; } - if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) { - memset(ss->sprinter.base, 0, ss->sprinter.offset); + + if (!*ss->sprinter.string()) { + memset(ss->sprinter.stringAt(0), 0, ss->sprinter.getOffset()); ss->offsets[i] = -1; } } @@ -1040,14 +1432,8 @@ GetOff(SprintStack *ss, uintN i) static const char * GetStr(SprintStack *ss, uintN i) { - ptrdiff_t off; - - /* - * Must call GetOff before using ss->sprinter.base, since it may be null - * until bootstrapped by GetOff. - */ - off = GetOff(ss, i); - return OFF2STR(&ss->sprinter, off); + ptrdiff_t off = GetOff(ss, i); + return ss->sprinter.stringAt(off); } /* @@ -1059,21 +1445,17 @@ GetStr(SprintStack *ss, uintN i) /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */ JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255); -static void +static inline void AddParenSlop(SprintStack *ss) { - memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); - ss->sprinter.offset += PAREN_SLOP; + ss->sprinter.reserveAndClear(PAREN_SLOP); } static JSBool -PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) +PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL) { uintN top; - if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP)) - return JS_FALSE; - /* ss->top points to the next free slot; be paranoid about overflow. */ top = ss->top; JS_ASSERT(top < StackDepth(ss->printer->script)); @@ -1087,18 +1469,32 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP : (op == JSOP_GETELEM2) ? JSOP_GETELEM : op); + ss->bytecodes[top] = pc; ss->top = ++top; + AddParenSlop(ss); return JS_TRUE; } +static bool +PushStr(SprintStack *ss, const char *str, JSOp op) +{ + ptrdiff_t off = ss->sprinter.put(str); + if (off < 0) + return false; + return PushOff(ss, off, op); +} + static ptrdiff_t -PopOffPrec(SprintStack *ss, uint8 prec) +PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) { uintN top; const JSCodeSpec *topcs; ptrdiff_t off; + if (ppc) + *ppc = NULL; + /* ss->top points to the next free slot; be paranoid about underflow. */ top = ss->top; JS_ASSERT(top != 0); @@ -1108,34 +1504,73 @@ PopOffPrec(SprintStack *ss, uint8 prec) ss->top = --top; off = GetOff(ss, top); topcs = &js_CodeSpec[ss->opcodes[top]]; + + jsbytecode *pc = ss->bytecodes[top]; + if (ppc) + *ppc = pc; + if (topcs->prec != 0 && topcs->prec < prec) { - ss->sprinter.offset = ss->offsets[top] = off - 2; - off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); + ss->offsets[top] = off - 2; + ss->sprinter.setOffset(off - 2); + off = Sprint(&ss->sprinter, "(%s)", ss->sprinter.stringAt(off)); + if (ss->printer->decompiledOpcodes && pc) + ss->printer->decompiled(pc).parenthesized = true; } else { - ss->sprinter.offset = off; + ss->sprinter.setOffset(off); } return off; } static const char * -PopStrPrec(SprintStack *ss, uint8 prec) +PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) { ptrdiff_t off; - off = PopOffPrec(ss, prec); - return OFF2STR(&ss->sprinter, off); + off = PopOffPrec(ss, prec, ppc); + return ss->sprinter.stringAt(off); +} + +/* + * As for PopStrPrec, but duplicates the string into the printer's arena. + * Strings returned by PopStrPrec are otherwise invalidated if any new text + * is printed into ss. + */ +static const char * +PopStrPrecDupe(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) +{ + const char *str = PopStrPrec(ss, prec, ppc); + return SprintDupeStr(ss, str); } static ptrdiff_t -PopOff(SprintStack *ss, JSOp op) +PopOff(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL) +{ + return PopOffPrec(ss, js_CodeSpec[op].prec, ppc); +} + +static const char * +PopStr(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL) +{ + return PopStrPrec(ss, js_CodeSpec[op].prec, ppc); +} + +static const char * +PopStrDupe(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL) { - return PopOffPrec(ss, js_CodeSpec[op].prec); + return PopStrPrecDupe(ss, js_CodeSpec[op].prec, ppc); } +/* + * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces + * extra parens around assignment, which avoids a strict-mode warning. + */ static const char * -PopStr(SprintStack *ss, JSOp op) +PopCondStr(SprintStack *ss, jsbytecode **ppc = NULL) { - return PopStrPrec(ss, js_CodeSpec[op].prec); + JSOp op = (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) + ? JSOP_IFEQ + : JSOP_NOP; + return PopStr(ss, op, ppc); } static inline bool @@ -1144,25 +1579,18 @@ IsInitializerOp(unsigned char op) return op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT; } -typedef struct TableEntry { +struct TableEntry { jsval key; ptrdiff_t offset; JSAtom *label; jsint order; /* source order for stable tableswitch sort */ -} TableEntry; +}; -static JSBool -CompareOffsets(void *arg, const void *v1, const void *v2, int *result) +inline bool +CompareTableEntries(const TableEntry &a, const TableEntry &b, bool *lessOrEqualp) { - ptrdiff_t offset_diff; - const TableEntry *te1 = (const TableEntry *) v1, - *te2 = (const TableEntry *) v2; - - offset_diff = te1->offset - te2->offset; - *result = (offset_diff == 0 ? te1->order - te2->order - : offset_diff < 0 ? -1 - : 1); - return JS_TRUE; + *lessOrEqualp = (a.offset != b.offset) ? a.offset <= b.offset : a.order <= b.order; + return true; } static ptrdiff_t @@ -1175,16 +1603,15 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp) JS_ASSERT(JSVAL_IS_DOUBLE(v)); d = JSVAL_TO_DOUBLE(v); if (JSDOUBLE_IS_NEGZERO(d)) { - todo = SprintCString(sp, "-0"); + todo = sp->put("-0"); *opp = JSOP_NEG; } else if (!JSDOUBLE_IS_FINITE(d)) { - /* Don't use Infinity and NaN, they're mutable. */ - todo = SprintCString(sp, - JSDOUBLE_IS_NaN(d) - ? "0 / 0" - : (d < 0) - ? "1 / -0" - : "1 / 0"); + /* Don't use Infinity and NaN, as local variables may shadow them. */ + todo = sp->put(JSDOUBLE_IS_NaN(d) + ? "0 / 0" + : (d < 0) + ? "1 / -0" + : "1 / 0"); *opp = JSOP_DIV; } else { ToCStringBuf cbuf; @@ -1203,7 +1630,7 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp) } static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop); +Decompile(SprintStack *ss, jsbytecode *pc, intN nb); static JSBool DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, @@ -1213,7 +1640,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, JSContext *cx; JSPrinter *jp; ptrdiff_t off, off2, diff, caseExprOff, todo; - char *lval, *rval; + const char *rval; uintN i; jsval key; JSString *str; @@ -1221,11 +1648,16 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, cx = ss->sprinter.context; jp = ss->printer; + jsbytecode *lvalpc; + const char *lval = PopStr(ss, JSOP_NOP, &lvalpc); + /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ - off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); - lval = OFF2STR(&ss->sprinter, off); + if (isCondSwitch) + ss->top++; - js_printf(jp, "\tswitch (%s) {\n", lval); + js_printf(jp, "\tswitch ("); + SprintOpcodePermanent(jp, lval, lvalpc); + js_printf(jp, ") {\n"); if (tableLength) { diff = table[0].offset - defaultOffset; @@ -1233,7 +1665,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, jp->indent += 2; js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; - if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP)) + if (!Decompile(ss, pc + defaultOffset, diff)) return JS_FALSE; jp->indent -= 4; } @@ -1256,10 +1688,8 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; jp->indent += 2; - if (!Decompile(ss, pc + caseExprOff, - nextCaseExprOff - caseExprOff, JSOP_NOP)) { + if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff)) return JS_FALSE; - } caseExprOff = nextCaseExprOff; /* Balance the stack as if this JSOP_CASE matched. */ @@ -1281,21 +1711,23 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, JSOp junk; todo = SprintDoubleValue(&ss->sprinter, key, &junk); + if (todo < 0) + return JS_FALSE; str = NULL; } else { - str = js_ValueToString(cx, Valueify(key)); + str = ToString(cx, key); if (!str) return JS_FALSE; } if (todo >= 0) { - rval = OFF2STR(&ss->sprinter, todo); + rval = ss->sprinter.stringAt(todo); } else { rval = QuoteString(&ss->sprinter, str, (jschar) (JSVAL_IS_STRING(key) ? '"' : 0)); if (!rval) return JS_FALSE; } - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); jp->indent += 2; js_printf(jp, "\tcase %s:\n", rval); } @@ -1304,7 +1736,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, if (off <= defaultOffset && defaultOffset < off2) { diff = defaultOffset - off; if (diff != 0) { - if (!Decompile(ss, pc + off, diff, JSOP_NOP)) + if (!Decompile(ss, pc + off, diff)) return JS_FALSE; off = defaultOffset; } @@ -1312,7 +1744,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; } - if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP)) + if (!Decompile(ss, pc + off, off2 - off)) return JS_FALSE; jp->indent -= 4; @@ -1347,25 +1779,47 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, static JSAtom * GetArgOrVarAtom(JSPrinter *jp, uintN slot) { - JSAtom *name; - LOCAL_ASSERT_RV(jp->fun, NULL); LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL); - name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]); + JSAtom *name = (*jp->localNames)[slot]; #if !JS_HAS_DESTRUCTURING LOCAL_ASSERT_RV(name, NULL); #endif return name; } +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") + +static const char * +GetLocalInSlot(SprintStack *ss, jsint i, jsint slot, JSObject *obj) +{ + for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { + const Shape &shape = r.front(); + + if (shape.shortid() == slot) { + /* Ignore the empty destructuring dummy. */ + if (!JSID_IS_ATOM(shape.propid())) + continue; + + JSAtom *atom = JSID_TO_ATOM(shape.propid()); + const char *rval = QuoteString(&ss->sprinter, atom, 0); + if (!rval) + return NULL; + + ss->sprinter.setOffset(rval); + return rval; + } + } + + return GetStr(ss, i); +} + const char * GetLocal(SprintStack *ss, jsint i) { -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") - ptrdiff_t off = ss->offsets[i]; if (off >= 0) - return OFF2STR(&ss->sprinter, off); + return ss->sprinter.stringAt(off); /* * We must be called from js_DecompileValueGenerator (via Decompile) when @@ -1381,40 +1835,43 @@ GetLocal(SprintStack *ss, jsint i) if (!JSScript::isValidOffset(script->objectsOffset)) return GetStr(ss, i); - for (jsatomid j = 0, n = script->objects()->length; j != n; j++) { - JSObject *obj = script->getObject(j); - if (obj->isBlock()) { - jsint depth = OBJ_BLOCK_DEPTH(cx, obj); - jsint count = OBJ_BLOCK_COUNT(cx, obj); - - if (jsuint(i - depth) < jsuint(count)) { - jsint slot = i - depth; + // In case of a let variable, the stack points to a JSOP_ENTERBLOCK opcode. + // Get the object number from the block instead of iterating all objects and + // hoping the right object is found. + if (off <= -2 && ss->printer->pcstack) { + jsbytecode *pc = ss->printer->pcstack[-2 - off]; - for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { - const Shape &shape = r.front(); + JS_ASSERT(ss->printer->script->code <= pc); + JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length)); - if (shape.shortid == slot) { - LOCAL_ASSERT(JSID_IS_ATOM(shape.id)); + if (JSOP_ENTERBLOCK == (JSOp)*pc) { + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); - JSAtom *atom = JSID_TO_ATOM(shape.id); - const char *rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval) - return NULL; + if (obj->isBlock()) { + uint32_t depth = obj->asBlock().stackDepth(); + uint32_t count = obj->asBlock().slotCount(); + if (jsuint(i - depth) < jsuint(count)) + return GetLocalInSlot(ss, i, jsint(i - depth), obj); + } + } + } - RETRACT(&ss->sprinter, rval); - return rval; - } - } + // Iterate over all objects. + for (jsatomid j = 0, n = script->objects()->length; j != n; j++) { + JSObject *obj = script->getObject(j); - break; - } + if (obj->isBlock()) { + uint32_t depth = obj->asBlock().stackDepth(); + uint32_t count = obj->asBlock().slotCount(); + if (jsuint(i - depth) < jsuint(count)) + return GetLocalInSlot(ss, i, jsint(i - depth), obj); } } return GetStr(ss, i); +} #undef LOCAL_ASSERT -} static JSBool IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp) @@ -1438,76 +1895,134 @@ IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp) #define LOAD_ATOM(PCOFF) \ GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom) +typedef Vector AtomVector; +typedef AtomVector::Range AtomRange; + #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length) static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + AtomRange *letNames = NULL); +/* + * Decompile a single element of a compound {}/[] destructuring lhs, sprinting + * the result in-place (without pushing/popping the stack) and advancing the pc + * to either the next element or the final pop. + * + * For normal (SRC_DESTRUCT) destructuring, the names of assigned/initialized + * variables are read from their slots. However, for SRC_DESTRUCTLET, the slots + * have not been pushed yet; the caller must pass the names to use via + * 'letNames'. Each variable initialized in this destructuring lhs results in + * popping a name from 'letNames'. + */ static jsbytecode * -DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - JSBool *hole) +DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole, + AtomRange *letNames = NULL) { - JSContext *cx; JSPrinter *jp; JSOp op; const JSCodeSpec *cs; uintN oplen; jsint i; const char *lval, *xval; - ptrdiff_t todo; JSAtom *atom; *hole = JS_FALSE; - cx = ss->sprinter.context; jp = ss->printer; LOAD_OP_DATA(pc); switch (op) { case JSOP_POP: *hole = JS_TRUE; - todo = SprintPut(&ss->sprinter, ", ", 2); + if (ss->sprinter.put(", ", 2) < 0) + return NULL; + break; + + case JSOP_PICK: + /* + * For 'let ([x, y] = y)', the emitter generates + * + * push evaluation of y + * dup + * 1 one + * 2 getelem + * 3 pick + * 4 two + * getelem + * pick + * pop + * + * Thus 'x' consists of 1 - 3. The caller (DecompileDestructuring or + * DecompileGroupAssignment) will have taken care of 1 - 2, so pc is + * now pointing at 3. The pick indicates a primitive let var init so + * pop a name and advance the pc to 4. + */ + LOCAL_ASSERT(letNames && !letNames->empty()); + if (!QuoteString(&ss->sprinter, letNames->popCopyFront(), 0)) + return NULL; break; case JSOP_DUP: - pc = DecompileDestructuring(ss, pc, endpc); + { + /* Compound lhs, e.g., '[x,y]' in 'let [[x,y], z] = a;'. */ + pc = DecompileDestructuring(ss, pc, endpc, letNames); if (!pc) return NULL; if (pc == endpc) return pc; LOAD_OP_DATA(pc); + + /* + * By its post-condition, DecompileDestructuring pushed one string + * containing the whole decompiled lhs. Our post-condition is to sprint + * in-place so pop/concat this pushed string. + */ lval = PopStr(ss, JSOP_NOP); - todo = SprintCString(&ss->sprinter, lval); - if (op == JSOP_POPN) - return pc; + if (ss->sprinter.put(lval) < 0) + return NULL; + LOCAL_ASSERT(*pc == JSOP_POP); + + /* + * To put block slots in the right place, the emitter follows a + * compound lhs with a pick (if at least one slot was pushed). The pick + * is not part of the compound lhs so DecompileDestructuring did not + * advance over it but it is part of the lhs so advance over it here. + */ + jsbytecode *nextpc = pc + JSOP_POP_LENGTH; + LOCAL_ASSERT(nextpc <= endpc); + if (letNames && *nextpc == JSOP_PICK) { + LOCAL_ASSERT(nextpc < endpc); + pc = nextpc; + LOAD_OP_DATA(pc); + } break; + } case JSOP_SETARG: case JSOP_SETLOCAL: + LOCAL_ASSERT(!letNames); LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN); /* FALL THROUGH */ - case JSOP_SETLOCALPOP: - atom = NULL; - lval = NULL; + LOCAL_ASSERT(!letNames); if (op == JSOP_SETARG) { atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc)); LOCAL_ASSERT(atom); + if (!QuoteString(&ss->sprinter, atom, 0)) + return NULL; } else if (IsVarSlot(jp, pc, &i)) { atom = GetArgOrVarAtom(jp, i); LOCAL_ASSERT(atom); + if (!QuoteString(&ss->sprinter, atom, 0)) + return NULL; } else { lval = GetLocal(ss, i); - } - { - JSAutoByteString bytes; - if (atom) - lval = js_AtomToPrintableString(cx, atom, &bytes); - LOCAL_ASSERT(lval); - todo = SprintCString(&ss->sprinter, lval); + if (!lval || ss->sprinter.put(lval) < 0) + return NULL; } if (op != JSOP_SETLOCALPOP) { pc += oplen; @@ -1520,7 +2035,8 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, } break; - default: + default: { + LOCAL_ASSERT(!letNames); /* * We may need to auto-parenthesize the left-most value decompiled * here, so add back PAREN_SLOP temporarily. Then decompile until the @@ -1528,9 +2044,9 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for * the nb parameter. */ - todo = ss->sprinter.offset; - ss->sprinter.offset = todo + PAREN_SLOP; - pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP); + ptrdiff_t todo = ss->sprinter.getOffset(); + ss->sprinter.reserve(PAREN_SLOP); + pc = Decompile(ss, pc, -((intN)ss->top)); if (!pc) return NULL; if (pc == endpc) @@ -1539,13 +2055,13 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); xval = PopStr(ss, JSOP_NOP); lval = PopStr(ss, JSOP_GETPROP); - ss->sprinter.offset = todo; + ss->sprinter.setOffset(todo); if (*lval == '\0') { /* lval is from JSOP_BINDNAME, so just print xval. */ - todo = SprintCString(&ss->sprinter, xval); + todo = ss->sprinter.put(xval); } else if (*xval == '\0') { /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ - todo = SprintCString(&ss->sprinter, lval); + todo = ss->sprinter.put(lval); } else { todo = Sprint(&ss->sprinter, (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME) @@ -1553,72 +2069,66 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, : "%s[%s]", lval, xval); } + if (todo < 0) + return NULL; break; + } } - if (todo < 0) - return NULL; - LOCAL_ASSERT(pc < endpc); pc += oplen; return pc; } /* - * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring - * left-hand side object or array initialiser, including nested destructuring - * initialisers. On successful return, the decompilation will be pushed on ss - * and the return value will point to the POP or GROUP bytecode following the - * destructuring expression. + * Decompile a destructuring lhs object or array initialiser, including nested + * destructuring initialisers. On return a single string is pushed containing + * the entire lhs (regardless of how many variables were bound). Thus, the + * caller must take care of fixing up the decompiler stack. * - * At any point, if pc is equal to endpc and would otherwise advance, we stop - * immediately and return endpc. + * See DecompileDestructuringLHS for description of 'letNames'. */ static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + AtomRange *letNames) { - ptrdiff_t head; - JSContext *cx; - JSPrinter *jp; - JSOp op, saveop; - const JSCodeSpec *cs; - uintN oplen; - jsint i, lasti; - jsdouble d; - const char *lval; - JSAtom *atom; - jssrcnote *sn; - JSBool hole; - LOCAL_ASSERT(*pc == JSOP_DUP); pc += JSOP_DUP_LENGTH; + JSContext *cx = ss->sprinter.context; + JSPrinter *jp = ss->printer; + jsbytecode *startpc = pc; + /* * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP * chars so the destructuring decompilation accumulates contiguously in * ss->sprinter starting with "[". */ - head = SprintPut(&ss->sprinter, "[", 1); + ptrdiff_t head = ss->sprinter.put("[", 1); if (head < 0 || !PushOff(ss, head, JSOP_NOP)) return NULL; - ss->sprinter.offset -= PAREN_SLOP; - LOCAL_ASSERT(head == ss->sprinter.offset - 1); - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); + LOCAL_ASSERT(head == ss->sprinter.getOffset() - 1); + LOCAL_ASSERT(ss->sprinter[head] == '['); - cx = ss->sprinter.context; - jp = ss->printer; - lasti = -1; + int lasti = -1; while (pc < endpc) { #if JS_HAS_DESTRUCTURING_SHORTHAND ptrdiff_t nameoff = -1; #endif + const JSCodeSpec *cs; + uintN oplen; + JSOp op; LOAD_OP_DATA(pc); - saveop = op; + int i; + double d; switch (op) { case JSOP_POP: + /* Empty destructuring lhs. */ + LOCAL_ASSERT(startpc == pc); pc += oplen; goto out; @@ -1636,7 +2146,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) i = (jsint)d; do_getelem: - sn = js_GetSrcNote(jp->script, pc); + { + jssrcnote *sn = js_GetSrcNote(jp->script, pc); pc += oplen; if (pc == endpc) return pc; @@ -1645,7 +2156,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) /* Distinguish object from array by opcode or source note. */ if (sn && SN_TYPE(sn) == SRC_INITPROP) { - *OFF2STR(&ss->sprinter, head) = '{'; + ss->sprinter[head] = '{'; if (Sprint(&ss->sprinter, "%g: ", d) < 0) return NULL; } else { @@ -1654,30 +2165,25 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) /* Fill in any holes (holes at the end don't matter). */ while (++lasti < i) { - if (SprintPut(&ss->sprinter, ", ", 2) < 0) + if (ss->sprinter.put(", ", 2) < 0) return NULL; } } break; + } - case JSOP_LENGTH: - atom = cx->runtime->atomState.lengthAtom; - goto do_destructure_atom; - - case JSOP_CALLPROP: case JSOP_GETPROP: - LOAD_ATOM(0); - do_destructure_atom: + case JSOP_LENGTH: { - *OFF2STR(&ss->sprinter, head) = '{'; + JSAtom *atom; + LOAD_ATOM(0); + ss->sprinter[head] = '{'; #if JS_HAS_DESTRUCTURING_SHORTHAND - nameoff = ss->sprinter.offset; + nameoff = ss->sprinter.getOffset(); #endif - if (!QuoteString(&ss->sprinter, atom, - js_IsIdentifier(atom) ? 0 : (jschar)'\'')) { + if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\'')) return NULL; - } - if (SprintPut(&ss->sprinter, ": ", 2) < 0) + if (ss->sprinter.put(": ", 2) < 0) return NULL; break; } @@ -1695,7 +2201,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) * and continues for a bounded number of bytecodes or stack operations * (and which in any event stops before endpc). */ - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); + JSBool hole; + pc = DecompileDestructuringLHS(ss, pc, endpc, &hole, letNames); if (!pc) return NULL; @@ -1703,8 +2210,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) if (nameoff >= 0) { ptrdiff_t offset, initlen; - offset = ss->sprinter.offset; - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0'); + offset = ss->sprinter.getOffset(); + LOCAL_ASSERT(ss->sprinter[offset] == '\0'); initlen = offset - nameoff; LOCAL_ASSERT(initlen >= 4); @@ -1718,12 +2225,12 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) * like, and apply the shorthand if we can. */ namelen = (size_t)(initlen - 2) >> 1; - name = OFF2STR(&ss->sprinter, nameoff); + name = ss->sprinter.stringAt(nameoff); if (!strncmp(name + namelen, ": ", 2) && !strncmp(name, name + namelen + 2, namelen)) { offset -= namelen + 2; - *OFF2STR(&ss->sprinter, offset) = '\0'; - ss->sprinter.offset = offset; + ss->sprinter[offset] = '\0'; + ss->sprinter.setOffset(offset); } } } @@ -1740,23 +2247,23 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) * means another destructuring initialiser abuts this one like in * '[a] = [b] = c'. */ - sn = js_GetSrcNote(jp->script, pc); + jssrcnote *sn = js_GetSrcNote(jp->script, pc); if (!sn) break; if (SN_TYPE(sn) != SRC_CONTINUE) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT || SN_TYPE(sn) == SRC_DESTRUCTLET); break; } - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + if (!hole && ss->sprinter.put(", ", 2) < 0) return NULL; pc += JSOP_DUP_LENGTH; } out: - lval = OFF2STR(&ss->sprinter, head); - if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0) + const char *lval = ss->sprinter.stringAt(head); + if (ss->sprinter.put((*lval == '[') ? "]" : "}", 1) < 0) return NULL; return pc; } @@ -1773,12 +2280,12 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, const char *rval; LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); + LOCAL_ASSERT(op == JSOP_GETLOCAL); todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) return NULL; - ss->sprinter.offset -= PAREN_SLOP; + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); for (;;) { pc += oplen; @@ -1790,14 +2297,14 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, if (pc == endpc) return pc; LOAD_OP_DATA(pc); - if (op != JSOP_PUSH && op != JSOP_GETLOCAL) + if (op != JSOP_GETLOCAL) break; - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + if (!hole && ss->sprinter.put(", ", 2) < 0) return NULL; } LOCAL_ASSERT(op == JSOP_POPN); - if (SprintPut(&ss->sprinter, "] = [", 5) < 0) + if (ss->sprinter.put("] = [", 5) < 0) return NULL; end = ss->top - 1; @@ -1811,9 +2318,9 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, } } - if (SprintPut(&ss->sprinter, "]", 1) < 0) + if (ss->sprinter.put("]", 1) < 0) return NULL; - ss->sprinter.offset = ss->offsets[i]; + ss->sprinter.setOffset(ss->offsets[i]); ss->top = start; *todop = todo; return pc; @@ -1824,24 +2331,229 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, #endif /* JS_HAS_DESTRUCTURING */ +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, false) + +/* + * The names of the vars of a let block/expr are stored as the ids of the + * shapes of the block object. Shapes are stored in a singly-linked list in + * reverse order of addition. This function takes care of putting the names + * back in declaration order. + */ +static bool +GetBlockNames(JSContext *cx, StaticBlockObject &blockObj, AtomVector *atoms) +{ + size_t numAtoms = blockObj.slotCount(); + LOCAL_ASSERT(numAtoms > 0); + if (!atoms->resize(numAtoms)) + return false; + + uintN i = numAtoms; + for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) { + const Shape &shape = r.front(); + LOCAL_ASSERT(shape.hasShortID()); + --i; + LOCAL_ASSERT((uintN)shape.shortid() == i); + (*atoms)[i] = JSID_IS_INT(shape.propid()) + ? cx->runtime->atomState.emptyAtom + : JSID_TO_ATOM(shape.propid()); + } + + LOCAL_ASSERT(i == 0); + return true; +} + +static bool +PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) +{ + for (size_t i = 0; i < atoms.length(); i++) { + const char *name = QuoteString(&ss->sprinter, atoms[i], 0); + if (!name || !PushOff(ss, ss->sprinter.getOffsetOf(name), JSOP_ENTERBLOCK)) + return false; + } + return true; +} + +/* + * In the scope of a let, the variables' (decompiler) stack slots must contain + * the corresponding variable's name. This function updates the N top slots + * with the N variable names stored in 'atoms'. + */ +static bool +AssignBlockNamesToPushedSlots(JSContext *cx, SprintStack *ss, const AtomVector &atoms) +{ + /* For simplicity, just pop and push. */ + LOCAL_ASSERT(atoms.length() <= (uintN)ss->top); + for (size_t i = 0; i < atoms.length(); ++i) + PopStr(ss, JSOP_NOP); + return PushBlockNames(cx, ss, atoms); +} + +static const char SkipString[] = "/*skip*/"; +static const char DestructuredString[] = "/*destructured*/"; +static const unsigned DestructuredStringLength = ArrayLength(DestructuredString) - 1; + +static ptrdiff_t +SprintLetBody(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength, + const char *headChars) +{ + if (pc[bodyLength] == JSOP_LEAVEBLOCK) { + js_printf(jp, "\tlet (%s) {\n", headChars); + jp->indent += 4; + if (!Decompile(ss, pc, bodyLength)) + return -1; + jp->indent -= 4; + js_printf(jp, "\t}\n"); + return -2; + } + + LOCAL_ASSERT_RV(pc[bodyLength] == JSOP_LEAVEBLOCKEXPR, -1); + if (!Decompile(ss, pc, bodyLength)) + return -1; + + const char *bodyChars = PopStr(ss, JSOP_SETNAME); + const char *format = *bodyChars == '{' ? "let (%s) (%s)" : "let (%s) %s"; + return Sprint(&ss->sprinter, format, headChars, bodyChars); +} + +/* + * Get the token to prefix the '=' in an assignment operation, checking whether + * the last operation was a getter, setter or compound assignment. For compound + * assignments, marks parents for the lhs and rhs of the operation in the + * compound assign. For an assignment such as 'a += b', the lhs will appear + * twice in the bytecode, in read and write operations. We defer generation of + * the offsets for the initial arithmetic operation until the entire compound + * assign has been processed. + */ +static const char * +GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop, + jsbytecode *pc, jsbytecode *rvalpc, + jsbytecode **lastlvalpc, jsbytecode **lastrvalpc) +{ + const char *token; + if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { + if (lastop == JSOP_GETTER) { + token = js_getter_str; + } else if (lastop == JSOP_SETTER) { + token = js_setter_str; + } else { + token = CodeToken[lastop]; + if (*lastlvalpc && *lastrvalpc) { + UpdateDecompiledParent(jp, *lastlvalpc, pc, 0); + UpdateDecompiledParent(jp, *lastrvalpc, rvalpc, 0); + } + } + } else { + token = ""; + } + *lastlvalpc = NULL; + *lastrvalpc = NULL; + return token; +} + +static ptrdiff_t +SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initPrefix, + const char *init, jsbytecode *initpc, jsbytecode **ppc, ptrdiff_t *plen) +{ + jsbytecode *pc = *ppc; + jssrcnote *sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(SN_TYPE(sn) == SRC_FOR); + + /* Print the keyword and the possibly empty init-part. */ + js_printf(jp, "\tfor (%s", initPrefix); + SprintOpcodePermanent(jp, init, initpc); + js_printf(jp, ";"); + + /* Skip the JSOP_NOP or JSOP_POP bytecode. */ + JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_POP); + pc += JSOP_NOP_LENGTH; + + /* Get the cond, next, and loop-closing tail offsets. */ + ptrdiff_t cond = js_GetSrcNoteOffset(sn, 0); + ptrdiff_t next = js_GetSrcNoteOffset(sn, 1); + ptrdiff_t tail = js_GetSrcNoteOffset(sn, 2); + + /* + * If this loop has a condition, then pc points at a goto + * targeting the condition. + */ + jsbytecode *pc2 = pc; + if (cond != tail) { + LOCAL_ASSERT(*pc == JSOP_GOTO); + pc2 += JSOP_GOTO_LENGTH; + } + LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == pc2 - pc); + + if (cond != tail) { + /* Decompile the loop condition. */ + if (!Decompile(ss, pc + cond, tail - cond)) + return -1; + js_printf(jp, " "); + jsbytecode *condpc; + const char *cond = PopStr(ss, JSOP_NOP, &condpc); + SprintOpcodePermanent(jp, cond, condpc); + } + + /* Need a semicolon whether or not there was a cond. */ + js_puts(jp, ";"); + + if (next != cond) { + /* + * Decompile the loop updater. It may end in a JSOP_POP + * that we skip; or in a JSOP_POPN that we do not skip, + * followed by a JSOP_NOP (skipped as if it's a POP). + * We cope with the difference between these two cases + * by checking for stack imbalance and popping if there + * is an rval. + */ + uintN saveTop = ss->top; + + if (!Decompile(ss, pc + next, cond - next - JSOP_POP_LENGTH)) + return -1; + LOCAL_ASSERT(ss->top - saveTop <= 1U); + jsbytecode *updatepc = NULL; + const char *update = (ss->top == saveTop) + ? ss->sprinter.stringEnd() + : PopStr(ss, JSOP_NOP, &updatepc); + js_printf(jp, " "); + SprintOpcodePermanent(jp, update, updatepc); + } + + /* Do the loop body. */ + js_printf(jp, ") {\n"); + jp->indent += 4; + next -= pc2 - pc; + if (!Decompile(ss, pc2, next)) + return -1; + jp->indent -= 4; + js_printf(jp, "\t}\n"); + + /* Set len so pc skips over the entire loop. */ + *ppc = pc; + *plen = tail + js_CodeSpec[pc[tail]].length; + return -2; +} + +#undef LOCAL_ASSERT + static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { - size_t offsetsz, opcodesz; - void *space; - - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); + if (!ss->sprinter.init()) + return JS_FALSE; + ss->sprinter.setOffset(PAREN_SLOP); - /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - offsetsz = depth * sizeof(ptrdiff_t); - opcodesz = depth * sizeof(jsbytecode); - JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); + /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */ + size_t offsetsz = depth * sizeof(ptrdiff_t); + size_t opcodesz = depth * sizeof(jsbytecode); + size_t bytecodesz = depth * sizeof(jsbytecode *); + void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz + bytecodesz); if (!space) { - js_ReportOutOfScriptQuota(cx); + js_ReportOutOfMemory(cx); return JS_FALSE; } ss->offsets = (ptrdiff_t *) space; ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); + ss->bytecodes = (jsbytecode **) ((char *)space + offsetsz + opcodesz); ss->top = ss->inArrayInit = 0; ss->inGenExp = JS_FALSE; @@ -1849,22 +2561,26 @@ InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) return JS_TRUE; } +template +void +Swap(T &a, T &b) +{ + T tmp = a; + a = b; + b = tmp; +} + /* * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise * the decompiler starts at pc and continues until it reaches an opcode for * which decompiling would result in the stack depth equaling -(nb + 1). - * - * The nextop parameter is either JSOP_NOP or the "next" opcode in order of - * abstract interpretation (not necessarily physically next in a bytecode - * vector). So nextop is JSOP_POP for the last operand in a comma expression, - * or JSOP_AND for the right operand of &&. */ static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) +Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { JSContext *cx; JSPrinter *jp, *jp2; - jsbytecode *startpc, *endpc, *pc2, *done; + jsbytecode *startpc, *endpc, *pc2, *done, *lvalpc, *rvalpc, *xvalpc; ptrdiff_t tail, todo, len, oplen, cond, next; JSOp op, lastop, saveop; const JSCodeSpec *cs; @@ -1872,7 +2588,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) const char *lval, *rval, *xval, *fmt, *token; uintN nuses; jsint i, argc; - char **argv; JSAtom *atom; JSObject *obj; JSFunction *fun = NULL; /* init to shut GCC up */ @@ -1905,23 +2620,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * Local macros */ #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) -#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL -#define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len]) +#define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb)) cleanup +#define DECOMPILE_CODE(pc,nb) DECOMPILE_CODE_CLEANUP(pc,nb,return NULL) #define TOP_STR() GetStr(ss, ss->top - 1) #define POP_STR() PopStr(ss, op) #define POP_STR_PREC(prec) PopStrPrec(ss, prec) -/* - * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces - * extra parens around assignment, which avoids a strict-mode warning. - */ -#define POP_COND_STR() \ - PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \ - ? JSOP_IFEQ \ - : JSOP_NOP) - -#define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(atom) - /* * Given an atom already fetched from jp->script's atom map, quote/escape its * string appropriately into rval, and select fmt from the quoted and unquoted @@ -1930,7 +2634,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \ JS_BEGIN_MACRO \ jschar quote_; \ - if (!ATOM_IS_IDENTIFIER(atom)) { \ + if (!IsIdentifier(atom)) { \ quote_ = '\''; \ fmt = qfmt; \ } else { \ @@ -1938,25 +2642,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) fmt = ufmt; \ } \ rval = QuoteString(&ss->sprinter, atom, quote_); \ + rval = SprintDupeStr(ss, rval); \ if (!rval) \ return NULL; \ JS_END_MACRO -#define LOAD_OBJECT(PCOFF) \ - GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj) - -#define LOAD_FUNCTION(PCOFF) \ - GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun) - -#define LOAD_REGEXP(PCOFF) \ - GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj) - #define GET_SOURCE_NOTE_ATOM(sn, atom) \ JS_BEGIN_MACRO \ jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \ \ - LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \ - (atom) = jp->script->atomMap.vector[atomIndex_]; \ + LOCAL_ASSERT(atomIndex_ < jp->script->natoms); \ + (atom) = jp->script->atoms[atomIndex_]; \ JS_END_MACRO /* @@ -1990,6 +2686,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } \ JS_END_MACRO + jsbytecode *lastlvalpc = NULL, *lastrvalpc = NULL; + cx = ss->sprinter.context; JS_CHECK_RECURSION(cx, return NULL); @@ -2001,6 +2699,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) saveop = JSOP_NOP; sn = NULL; rval = NULL; + bool forOf = false; #if JS_HAS_XML_SUPPORT foreach = inXML = quoteAttr = JS_FALSE; #endif @@ -2019,9 +2718,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) if (cs->format & JOF_INDEXBASE) { /* * The decompiler uses js_GetIndexFromBytecode to get atoms and - * objects and ignores these suffix/prefix bytecodes, thus - * simplifying code that must process JSOP_GETTER/JSOP_SETTER - * prefixes. + * ignores these suffix/prefix bytecodes, thus simplifying code + * that must process JSOP_GETTER/JSOP_SETTER prefixes. */ pc += cs->length; if (pc >= endpc) @@ -2031,7 +2729,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } saveop = op; len = oplen = cs->length; - nuses = js_GetStackUses(cs, op, pc); + nuses = StackUses(jp->script, pc); /* * Here it is possible that nuses > ss->top when the op has a hidden @@ -2040,7 +2738,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ if (nb < 0) { LOCAL_ASSERT(ss->top >= nuses); - uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc); + uintN ndefs = StackDefs(jp->script, pc); if ((uintN) -(nb + 1) == ss->top - nuses + ndefs) return pc; } @@ -2052,20 +2750,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) token = CodeToken[op]; if (pc + oplen == jp->dvgfence) { - StackFrame *fp; - uint32 format, mode, type; - /* * Rewrite non-get ops to their "get" format if the error is in - * the bytecode at pc, so we don't decompile more than the error + * the bytecode at pc, or if at an inner opcode of a 'fat' outer + * opcode at pc, so we don't decompile more than the error * expression. */ - fp = js_GetScriptedCaller(cx, NULL); - format = cs->format; - if (((fp && pc == fp->pc(cx)) || - (pc == startpc && nuses != 0)) && - format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) { - mode = JOF_MODE(format); + uint32_t format = cs->format; + bool matchPC = false; + if (StackFrame *fp = js_GetScriptedCaller(cx, NULL)) { + jsbytecode *npc = fp->pcQuadratic(cx); + if (pc == npc) { + matchPC = true; + } else if (format & JOF_DECOMPOSE) { + if (unsigned(npc - pc) < GetDecomposeLength(pc, js_CodeSpec[*pc].length)) + matchPC = true; + } + } + if ((matchPC || (pc == startpc && nuses != 0)) && + format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) { + uint32_t mode = JOF_MODE(format); if (mode == JOF_NAME) { /* * JOF_NAME does not imply JOF_ATOM, so we must check for @@ -2073,7 +2777,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of * to JSOP_NAME. */ - type = JOF_TYPE(format); + uint32_t type = JOF_TYPE(format); op = (type == JOF_QARG) ? JSOP_GETARG : (type == JOF_LOCAL) @@ -2112,28 +2816,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_ENUMCONSTELEM: op = JSOP_GETELEM; break; - case JSOP_GETTHISPROP: - /* - * NB: JSOP_GETTHISPROP can't fail due to |this| - * being null or undefined at runtime (beware that - * this may change for ES4). Therefore any error - * resulting from this op must be due to the value - * of the property accessed via |this|, so do not - * rewrite op to JSOP_THIS. - * - * The next two cases should not change op if - * js_DecompileValueGenerator was called from the - * the property getter. They should rewrite only - * if the base object in the arg/var/local is null - * or undefined. FIXME: bug 431569. - */ - break; - case JSOP_GETARGPROP: - op = JSOP_GETARG; - break; - case JSOP_GETLOCALPROP: - op = JSOP_GETLOCAL; - break; case JSOP_SETXMLNAME: op = JSOp(JSOP_GETELEM2); break; @@ -2151,12 +2833,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) else if (op == JSOP_GETELEM2) saveop = JSOP_GETELEM; } - LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen || - JOF_TYPE(format) == JOF_SLOTATOM); jp->dvgfence = NULL; } + jsbytecode *pushpc = pc; + if (token) { switch (nuses) { case 2: @@ -2168,16 +2850,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * problem). */ op = (JSOp) pc[oplen]; - rval = POP_STR(); - lval = POP_STR(); + rval = PopStr(ss, op, &lastrvalpc); + (void)PopStr(ss, op, &lastlvalpc); + /* Print only the right operand of the assignment-op. */ - todo = SprintCString(&ss->sprinter, rval); - op = saveop; + todo = ss->sprinter.put(rval); } else if (!inXML) { - rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC)); - lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC)); - todo = Sprint(&ss->sprinter, "%s %s %s", - lval, token, rval); + rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc); + lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); + Sprint(&ss->sprinter, " %s ", token); + SprintOpcode(ss, rval, rvalpc, pc, todo); } else { /* In XML, just concatenate the two operands. */ LOCAL_ASSERT(op == JSOP_ADD); @@ -2188,12 +2872,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case 1: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, ss_format, token, rval); + rval = PopStrDupe(ss, op, &rvalpc); + todo = ss->sprinter.put(token); + SprintOpcode(ss, rval, rvalpc, pc, todo); break; case 0: - todo = SprintCString(&ss->sprinter, token); + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_CONTINUE) { + /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */ + todo = ss->sprinter.put(SkipString); + break; + } + todo = ss->sprinter.put(token); break; default: @@ -2212,107 +2903,28 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = -2; switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_WHILE: + /* First instuction (NOP) contains offset to condition */ ++pc; - tail = js_GetSrcNoteOffset(sn, 0) - 1; - LOCAL_ASSERT(pc[tail] == JSOP_IFNE || - pc[tail] == JSOP_IFNEX); + /* Second instruction (TRACE) contains offset to JSOP_IFNE */ + sn = js_GetSrcNote(jp->script, pc); + tail = js_GetSrcNoteOffset(sn, 0); + LOCAL_ASSERT(pc[tail] == JSOP_IFNE); js_printf(jp, "\tdo {\n"); jp->indent += 4; DECOMPILE_CODE(pc, tail); jp->indent -= 4; - js_printf(jp, "\t} while (%s);\n", POP_COND_STR()); + js_printf(jp, "\t} while ("); + rval = PopCondStr(ss, &rvalpc); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ");\n"); pc += tail; len = js_CodeSpec[*pc].length; todo = -2; break; case SRC_FOR: - rval = ""; - - do_forloop: - JS_ASSERT(SN_TYPE(sn) == SRC_FOR); - - /* Skip the JSOP_NOP or JSOP_POP bytecode. */ - pc += JSOP_NOP_LENGTH; - - /* Get the cond, next, and loop-closing tail offsets. */ - cond = js_GetSrcNoteOffset(sn, 0); - next = js_GetSrcNoteOffset(sn, 1); - tail = js_GetSrcNoteOffset(sn, 2); - - /* - * If this loop has a condition, then pc points at a goto - * targeting the condition. - */ - pc2 = pc; - if (cond != tail) { - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH; - } - LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc); - - /* Print the keyword and the possibly empty init-part. */ - js_printf(jp, "\tfor (%s;", rval); - - if (cond != tail) { - /* Decompile the loop condition. */ - DECOMPILE_CODE(pc + cond, tail - cond); - js_printf(jp, " %s", POP_STR()); - } - - /* Need a semicolon whether or not there was a cond. */ - js_puts(jp, ";"); - - if (next != cond) { - /* - * Decompile the loop updater. It may end in a JSOP_POP - * that we skip; or in a JSOP_POPN that we do not skip, - * followed by a JSOP_NOP (skipped as if it's a POP). - * We cope with the difference between these two cases - * by checking for stack imbalance and popping if there - * is an rval. - */ - uintN saveTop = ss->top; - - DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH); - LOCAL_ASSERT(ss->top - saveTop <= 1U); - rval = (ss->top == saveTop) - ? ss->sprinter.base + ss->sprinter.offset - : POP_STR(); - js_printf(jp, " %s", rval); - } - - /* Do the loop body. */ - js_printf(jp, ") {\n"); - jp->indent += 4; - next -= pc2 - pc; - DECOMPILE_CODE(pc2, next); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - - /* Set len so pc skips over the entire loop. */ - len = tail + js_CodeSpec[pc[tail]].length; - break; - - case SRC_LABEL: - GET_SOURCE_NOTE_ATOM(sn, atom); - jp->indent -= 4; - rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\t%s:\n", rval); - jp->indent += 4; - break; - - case SRC_LABELBRACE: - GET_SOURCE_NOTE_ATOM(sn, atom); - rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\t%s: {\n", rval); - jp->indent += 4; + /* for loop with empty initializer. */ + todo = SprintNormalFor(cx, jp, ss, "", "", NULL, &pc, &len); break; case SRC_ENDBRACE: @@ -2330,8 +2942,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) if (!jp2) return NULL; ok = js_DecompileFunction(jp2); - if (ok && jp2->sprinter.base) - js_puts(jp, jp2->sprinter.base); + if (ok && !jp2->sprinter.empty()) + js_puts(jp, jp2->sprinter.string()); js_DestroyPrinter(jp2); if (!ok) return NULL; @@ -2351,19 +2963,36 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } break; - case JSOP_PUSH: -#if JS_HAS_DESTRUCTURING + case JSOP_LABEL: sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) + todo = -2; + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_LABEL: + GET_SOURCE_NOTE_ATOM(sn, atom); + jp->indent -= 4; + rval = QuoteString(&ss->sprinter, atom, 0); + if (!rval) return NULL; - LOCAL_ASSERT(*pc == JSOP_POPN); - len = oplen = JSOP_POPN_LENGTH; - goto end_groupassignment; + ss->sprinter.setOffset(rval); + js_printf(jp, "\t%s:\n", rval); + jp->indent += 4; + break; + + case SRC_LABELBRACE: + GET_SOURCE_NOTE_ATOM(sn, atom); + rval = QuoteString(&ss->sprinter, atom, 0); + if (!rval) + return NULL; + ss->sprinter.setOffset(rval); + js_printf(jp, "\t%s: {\n", rval); + jp->indent += 4; + break; + + default: + JS_NOT_REACHED("JSOP_LABEL without source note"); + break; } -#endif - /* FALL THROUGH */ + break; case JSOP_BINDNAME: case JSOP_BINDGNAME: @@ -2401,11 +3030,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_GOSUB: - case JSOP_GOSUBX: /* - * JSOP_GOSUB and GOSUBX have no effect on the decompiler's - * string stack because the next op in bytecode order finds - * the stack balanced by a JSOP_RETSUB executed elsewhere. + * JSOP_GOSUB has no effect on the decompiler's string stack + * because the next op in bytecode order finds the stack + * balanced by a JSOP_RETSUB executed elsewhere. */ todo = -2; break; @@ -2435,7 +3063,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) if (todo < 0) return NULL; for (uintN i = newtop; i < oldtop; i++) { - rval = OFF2STR(&ss->sprinter, ss->offsets[i]); + rval = ss->sprinter.stringAt(ss->offsets[i]); if (Sprint(&ss->sprinter, ss_format, (i == newtop) ? "" : ", ", (i == oldtop - 1 && *rval == '\0') @@ -2443,7 +3071,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) return NULL; } } - if (SprintPut(&ss->sprinter, "]", 1) < 0) + if (ss->sprinter.put("]", 1) < 0) return NULL; /* @@ -2453,16 +3081,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * empty group assignment decompilation. */ if (newtop == oldtop) { - ss->sprinter.offset = todo; + ss->sprinter.setOffset(todo); } else { /* * Kill newtop before the end_groupassignment: label by - * retracting/popping early. Control will either jump - * to do_forloop: or do_letheadbody: or else break from - * our case JSOP_POPN: after the switch (*pc2) below. + * retracting/popping early. */ LOCAL_ASSERT(newtop < oldtop); - ss->sprinter.offset = GetOff(ss, newtop); + ss->sprinter.setOffset(GetOff(ss, newtop)); ss->top = newtop; } @@ -2478,40 +3104,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * NB: todo at this point indexes space in ss->sprinter * that is liable to be overwritten. The code below knows * exactly how long rval lives, or else copies it down via - * SprintCString. + * Sprinter::put. */ - rval = OFF2STR(&ss->sprinter, todo); + rval = ss->sprinter.stringAt(todo); + rvalpc = NULL; todo = -2; pc2 = pc + oplen; + if (*pc2 == JSOP_NOP) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { if (SN_TYPE(sn) == SRC_FOR) { op = JSOP_NOP; pc = pc2; - goto do_forloop; - } - - if (SN_TYPE(sn) == SRC_DECL) { - if (ss->top == StackDepth(jp->script)) { - /* - * This must be an empty destructuring - * in the head of a let whose body block - * is also empty. - */ - pc = pc2 + JSOP_NOP_LENGTH; - len = js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); - js_printf(jp, "\tlet (%s) {\n", rval); - js_printf(jp, "\t}\n"); - break; - } - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - op = JSOP_POP; - pc = pc2 + JSOP_NOP_LENGTH; - goto do_letheadbody; + todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len); + break; } } else { /* @@ -2524,7 +3131,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ if (GET_UINT16(pc) == 0) break; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); saveop = JSOP_NOP; } } @@ -2539,7 +3146,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } #endif if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); + ss->sprinter.setOffset(GetOff(ss, newtop)); ss->top = newtop; } break; @@ -2567,36 +3174,35 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) /* Force parens around 'in' expression at 'for' front. */ if (ss->opcodes[ss->top-1] == JSOP_IN) op = JSOP_LSH; - rval = POP_STR(); - todo = -2; - goto do_forloop; + rval = PopStr(ss, op, &rvalpc); + todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len); + break; case SRC_PCDELTA: /* Comma operator: use JSOP_POP for correct precedence. */ op = JSOP_POP; /* Pop and save to avoid blowing stack depth budget. */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; + lval = PopStrDupe(ss, op, &lvalpc); /* * The offset tells distance to the end of the right-hand * operand of the comma operator. */ + pushpc = pc; done = pc + len; pc += js_GetSrcNoteOffset(sn, 0); len = 0; - if (!Decompile(ss, done, pc - done, JSOP_POP)) { - cx->free_((char *)lval); + if (!Decompile(ss, done, pc - done)) return NULL; - } /* Pop Decompile result and print comma expression. */ - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); - cx->free_((char *)lval); + rval = PopStrDupe(ss, op, &rvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pushpc, todo); + ss->sprinter.put(", "); + SprintOpcode(ss, rval, rvalpc, pushpc, todo); break; case SRC_HIDDEN: @@ -2604,49 +3210,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = -2; break; - case SRC_DECL: - /* This pop is at the end of the let block/expr head. */ - pc += JSOP_POP_LENGTH; -#if JS_HAS_DESTRUCTURING - do_letheadbody: -#endif - len = js_GetSrcNoteOffset(sn, 0); - if (pc[len] == JSOP_LEAVEBLOCK) { - js_printf(jp, "\tlet (%s) {\n", POP_STR()); - jp->indent += 4; - DECOMPILE_CODE(pc, len); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - } else { - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); - - lval = JS_strdup(cx, PopStr(ss, JSOP_NOP)); - if (!lval) - return NULL; - - /* Set saveop to reflect what we will push. */ - saveop = JSOP_LEAVEBLOCKEXPR; - if (!Decompile(ss, pc, len, saveop)) { - cx->free_((char *)lval); - return NULL; - } - rval = PopStr(ss, JSOP_SETNAME); - todo = Sprint(&ss->sprinter, - (*rval == '{') - ? "let (%s) (%s)" - : "let (%s) %s", - lval, rval); - cx->free_((char *)lval); - } + case SRC_CONTINUE: + /* Pop the stack, don't print: end of a for-let-in. */ + (void) PopOff(ss, op); + todo = -2; break; default: + { /* Turn off parens around a yield statement. */ if (ss->opcodes[ss->top-1] == JSOP_YIELD) op = JSOP_NOP; - rval = POP_STR(); + jsbytecode *rvalpc; + rval = PopStr(ss, op, &rvalpc); /* * Don't emit decompiler-pushed strings that are not @@ -2655,27 +3232,30 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * not appear in the decompiler's output. */ if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) { - js_printf(jp, - (*rval == '{' || - (strncmp(rval, js_function_str, 8) == 0 && - rval[8] == ' ')) - ? "\t(%s);\n" - : "\t%s;\n", - rval); + bool parens = + *rval == '{' || + (strncmp(rval, js_function_str, 8) == 0 && + rval[8] == ' '); + js_printf(jp, parens ? "\t(" : "\t"); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, parens ? ");\n" : ";\n"); } else { LOCAL_ASSERT(*rval == '\0' || strcmp(rval, exception_cookie) == 0); } todo = -2; break; + } } sn = NULL; break; case JSOP_ENTERWITH: LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); - rval = POP_STR(); - js_printf(jp, "\twith (%s) {\n", rval); + rval = PopStr(ss, op, &rvalpc); + js_printf(jp, "\twith ("); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ") {\n"); jp->indent += 4; todo = Sprint(&ss->sprinter, with_cookie); break; @@ -2693,39 +3273,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_ENTERBLOCK: { - JSAtom **atomv, *smallv[5]; - - LOAD_OBJECT(0); - argc = OBJ_BLOCK_COUNT(cx, obj); - if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) { - atomv = smallv; - } else { - atomv = (JSAtom **) cx->malloc_(argc * sizeof(JSAtom *)); - if (!atomv) - return NULL; - } + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); + AtomVector atoms(cx); + StaticBlockObject &blockObj = obj->asStaticBlock(); - MUST_FLOW_THROUGH("enterblock_out"); -#define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \ - goto enterblock_out) - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - const Shape &shape = r.front(); - - if (!shape.hasShortID()) - continue; - LOCAL_ASSERT_OUT(shape.shortid < argc); - atomv[shape.shortid] = JSID_TO_ATOM(shape.id); - } - ok = JS_TRUE; - for (i = 0; i < argc; i++) { - atom = atomv[i]; - rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval || - !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { - ok = JS_FALSE; - goto enterblock_out; - } - } + if (!GetBlockNames(cx, blockObj, &atoms) || !PushBlockNames(cx, ss, atoms)) + return NULL; sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { @@ -2734,10 +3287,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) js_printf(jp, "\t{\n"); jp->indent += 4; len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP) - != NULL; - if (!ok) - goto enterblock_out; + if (!Decompile(ss, pc + oplen, len - oplen)) + return NULL; jp->indent -= 4; js_printf(jp, "\t}\n"); break; @@ -2749,13 +3300,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) pc2 = pc; pc += oplen; - LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION); + LOCAL_ASSERT(*pc == JSOP_EXCEPTION); pc += JSOP_EXCEPTION_LENGTH; todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) { - ok = JS_FALSE; - goto enterblock_out; - } + if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) + return NULL; if (*pc == JSOP_DUP) { sn2 = js_GetSrcNote(jp->script, pc); @@ -2765,39 +3314,30 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * It is emitted only when the catch head contains * an exception guard. */ - LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0); + LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); pc += JSOP_DUP_LENGTH; todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || - !PushOff(ss, todo, JSOP_EXCEPTION)) { - ok = JS_FALSE; - goto enterblock_out; - } + if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) + return NULL; } } #if JS_HAS_DESTRUCTURING if (*pc == JSOP_DUP) { pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - goto enterblock_out; - } - LOCAL_ASSERT_OUT(*pc == JSOP_POP); + if (!pc) + return NULL; + LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(ss, JSOP_NOP); js_puts(jp, lval); } else { #endif - LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP); - i = GET_SLOTNO(pc) - jp->script->nfixed; + LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); pc += JSOP_SETLOCALPOP_LENGTH; - atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; - str = atom; - if (!QuoteString(&jp->sprinter, str, 0)) { - ok = JS_FALSE; - goto enterblock_out; - } + LOCAL_ASSERT(blockObj.slotCount() >= 1); + if (!QuoteString(&jp->sprinter, atoms[0], 0)) + return NULL; #if JS_HAS_DESTRUCTURING } #endif @@ -2807,19 +3347,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * guarded catch head) off the stack now. */ rval = PopStr(ss, JSOP_NOP); - LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0); + LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0); len = js_GetSrcNoteOffset(sn, 0); if (len) { len -= pc - pc2; - LOCAL_ASSERT_OUT(len > 0); + LOCAL_ASSERT(len > 0); js_printf(jp, " if "); - ok = Decompile(ss, pc, len, JSOP_NOP) != NULL; - if (!ok) - goto enterblock_out; + if (!Decompile(ss, pc, len)) + return NULL; js_printf(jp, "%s", POP_STR()); pc += len; - LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + LOCAL_ASSERT(*pc == JSOP_IFEQ); pc += js_CodeSpec[*pc].length; } @@ -2827,18 +3366,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) jp->indent += 4; len = 0; break; - default: - break; + default:; } todo = -2; - -#undef LOCAL_ASSERT_OUT - enterblock_out: - if (atomv != smallv) - cx->free_(atomv); - if (!ok) - return NULL; } break; @@ -2877,26 +3408,176 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) LOCAL_ASSERT(top >= depth); top -= depth; ss->top = top; - ss->sprinter.offset = GetOff(ss, top); + ss->sprinter.setOffset(GetOff(ss, top)); if (op == JSOP_LEAVEBLOCKEXPR) - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); + break; + } + + case JSOP_ENTERLET0: + { + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); + StaticBlockObject &blockObj = obj->asStaticBlock(); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, blockObj, &atoms)) + return NULL; + + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DECL); + ptrdiff_t letData = js_GetSrcNoteOffset(sn, 0); + bool groupAssign = LetDataToGroupAssign(letData); + uintN letDepth = blockObj.stackDepth(); + LOCAL_ASSERT(letDepth == (uintN)ss->top - blockObj.slotCount()); + LOCAL_ASSERT(atoms.length() == blockObj.slotCount()); + + /* + * Build the list of decompiled rhs expressions. Do this before + * sprinting the let-head since GetStr can inject stuff on top + * of the stack (in case js_DecompileValueGenerator). + */ + Vector rhsExprs(cx); + if (!rhsExprs.resize(atoms.length())) + return NULL; + for (size_t i = 0; i < rhsExprs.length(); ++i) + rhsExprs[i] = SprintDupeStr(ss, GetStr(ss, letDepth + i)); + + /* Build the let head starting at headBegin. */ + ptrdiff_t headBegin = ss->sprinter.getOffset(); + + /* + * For group assignment, prepend the '[lhs-vars] = [' here, + * append rhsExprs in the next loop and append ']' after. + */ + if (groupAssign) { + if (Sprint(&ss->sprinter, "[") < 0) + return NULL; + for (size_t i = 0; i < atoms.length(); ++i) { + if (i && Sprint(&ss->sprinter, ", ") < 0) + return NULL; + if (!QuoteString(&ss->sprinter, atoms[i], 0)) + return NULL; + } + if (Sprint(&ss->sprinter, "] = [") < 0) + return NULL; + } + + for (size_t i = 0; i < atoms.length(); ++i) { + const char *rhs = rhsExprs[i]; + if (!strcmp(rhs, SkipString)) + continue; + + if (i && Sprint(&ss->sprinter, ", ") < 0) + return NULL; + + if (groupAssign) { + if (ss->sprinter.put(rhs) < 0) + return NULL; + } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) { + if (ss->sprinter.put(rhs + DestructuredStringLength) < 0) + return NULL; + } else { + JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom); + if (!QuoteString(&ss->sprinter, atoms[i], 0)) + return NULL; + if (*rhs) { + uint8_t prec = js_CodeSpec[ss->opcodes[letDepth + i]].prec; + const char *fmt = prec && prec < js_CodeSpec[JSOP_SETLOCAL].prec + ? " = (%s)" + : " = %s"; + if (Sprint(&ss->sprinter, fmt, rhs) < 0) + return NULL; + } + } + } + + if (groupAssign && Sprint(&ss->sprinter, "]") < 0) + return NULL; + + /* Clone the let head chars before clobbering the stack. */ + DupBuffer head(cx); + if (!Dup(ss->sprinter.stringAt(headBegin), &head)) + return NULL; + if (!AssignBlockNamesToPushedSlots(cx, ss, atoms)) + return NULL; + + /* Detect 'for (let ...)' desugared into 'let (...) {for}'. */ + jsbytecode *nextpc = pc + JSOP_ENTERLET0_LENGTH; + if (*nextpc == JSOP_NOP) { + jssrcnote *nextsn = js_GetSrcNote(jp->script, nextpc); + if (nextsn && SN_TYPE(nextsn) == SRC_FOR) { + pc = nextpc; + todo = SprintNormalFor(cx, jp, ss, "let ", head.begin(), pc, &pc, &len); + break; + } + } + + /* Decompile the body and then complete the let block/expr. */ + len = LetDataToOffset(letData); + pc = nextpc; + saveop = (JSOp) pc[len]; + todo = SprintLetBody(cx, jp, ss, pc, len, head.begin()); + break; + } + + /* + * With 'for (let lhs in rhs)' and 'switch (c) { let-decl }', + * placeholder slots have already been pushed (by JSOP_UNDEFINED). + * In both the for-let-in and switch-hoisted-let cases: + * - there is a non-let slot on top of the stack (hence enterlet1) + * - there is no further special let-handling required: + * for-let-in will decompile the let head when it decompiles + * the loop body prologue; there is no let head to decompile + * with switch. + * Hence, the only thing to do is update the let vars' slots with + * their names, taking care to preserve the iter/condition value + * on top of the stack. + */ + case JSOP_ENTERLET1: + { + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); + StaticBlockObject &blockObj = obj->asStaticBlock(); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, blockObj, &atoms)) + return NULL; + + LOCAL_ASSERT(js_GetSrcNote(jp->script, pc) == NULL); + LOCAL_ASSERT(ss->top - 1 == blockObj.stackDepth() + blockObj.slotCount()); + jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH; + if (*nextpc == JSOP_GOTO) { + LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(jp->script, nextpc)) == SRC_FOR_IN); + } else { + LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH || + *nextpc == JSOP_TABLESWITCH || + *nextpc == JSOP_LOOKUPSWITCH); + } + + DupBuffer rhs(cx); + if (!Dup(PopStr(ss, JSOP_NOP), &rhs)) + return NULL; + if (!AssignBlockNamesToPushedSlots(cx, ss, atoms)) + return NULL; + if (!PushStr(ss, rhs.begin(), op)) + return NULL; + todo = -2; break; } - case JSOP_GETUPVAR_DBG: - case JSOP_CALLUPVAR_DBG: case JSOP_GETFCSLOT: case JSOP_CALLFCSLOT: { - if (!jp->fun) { - JS_ASSERT(jp->script->savedCallerFun); - jp->fun = jp->script->getFunction(0); - } + if (!jp->fun) + jp->fun = jp->script->getCallerFunction(); if (!jp->localNames) { JS_ASSERT(fun == jp->fun); - jp->localNames = - jp->fun->script()->bindings.getLocalNameArray(cx, &jp->pool); + jp->localNames = cx->new_ >(cx); + if (!jp->localNames || + !jp->fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) + { + return NULL; + } } uintN index = GET_UINT16(pc); @@ -2914,7 +3595,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * object that's not a constructor, causing us to be * called with an intervening frame on the stack. */ - StackFrame *fp = js_GetTopStackFrame(cx); + StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); if (fp) { while (!fp->isEvalFrame()) fp = fp->prev(); @@ -2976,7 +3657,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) goto do_setname; } lval = GetLocal(ss, i); - rval = POP_STR(); + rval = PopStrDupe(ss, op, &rvalpc); goto do_setlval; case JSOP_INCLOCAL: @@ -3009,9 +3690,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) if (fun->flags & JSFUN_EXPR_CLOSURE) { /* Turn on parens around comma-expression here. */ op = JSOP_SETNAME; - rval = POP_STR(); - js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format, - rval, + rval = PopStr(ss, op, &rvalpc); + bool parens = (*rval == '{'); + if (parens) + js_printf(jp, "("); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, parens ? ")%s" : "%s", ((fun->flags & JSFUN_LAMBDA) || !fun->atom) ? "" : ";"); @@ -3021,11 +3705,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) /* FALL THROUGH */ case JSOP_SETRVAL: - rval = POP_STR(); - if (*rval != '\0') - js_printf(jp, "\t%s %s;\n", js_return_str, rval); - else + rval = PopStr(ss, op, &rvalpc); + if (*rval != '\0') { + js_printf(jp, "\t%s ", js_return_str); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ";\n"); + } else { js_printf(jp, "\t%s;\n", js_return_str); + } todo = -2; break; @@ -3045,7 +3732,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) ? "%s (%s)" : "%s %s", js_yield_str, rval) - : SprintCString(&ss->sprinter, js_yield_str); + : ss->sprinter.put(js_yield_str); break; } @@ -3056,9 +3743,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_ARRAYPUSH: { - uintN pos, forpos; - ptrdiff_t start; - /* Turn off most parens. */ op = JSOP_SETNAME; @@ -3066,11 +3750,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = POP_STR(); /* - * Skip the for loop head stacked by JSOP_FORLOCAL until we hit - * a block local slot (note empty destructuring patterns result - * in unit-count blocks). + * Skip the for loop head stacked by JSOP_GOTO:SRC_FOR_IN until + * we hit a block local slot (note empty destructuring patterns + * result in unit-count blocks). */ - pos = ss->top; + uintN pos = ss->top; while (pos != 0) { op = (JSOp) ss->opcodes[--pos]; if (op == JSOP_ENTERBLOCK) @@ -3083,7 +3767,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * in the single string of accumulated |for| heads and optional * final |if (condition)|. */ - forpos = pos + 1; + uintN forpos = pos + 1; LOCAL_ASSERT(forpos < ss->top); /* @@ -3107,8 +3791,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * (so has neutral stack balance). */ LOCAL_ASSERT(pos == 0); - xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); - ss->sprinter.offset = PAREN_SLOP; + xval = ss->sprinter.stringAt(ss->offsets[forpos]); + ss->sprinter.setOffset(PAREN_SLOP); todo = Sprint(&ss->sprinter, ss_format, rval, xval); if (todo < 0) return NULL; @@ -3125,13 +3809,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc)); LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); - start = ss->offsets[pos]; - LOCAL_ASSERT(ss->sprinter.base[start] == '[' || - ss->sprinter.base[start] == '#'); + ptrdiff_t start = ss->offsets[pos]; + LOCAL_ASSERT(ss->sprinter[start] == '[' || + ss->sprinter[start] == '#'); LOCAL_ASSERT(forpos < ss->top); - xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); - lval = OFF2STR(&ss->sprinter, start); - RETRACT(&ss->sprinter, lval); + xval = ss->sprinter.stringAt(ss->offsets[forpos]); + lval = ss->sprinter.stringAt(start); + ss->sprinter.setOffset(lval); todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval); if (todo < 0) @@ -3151,12 +3835,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = -2; if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; - rval = POP_STR(); - js_printf(jp, "\t%s %s;\n", js_throw_str, rval); + rval = PopStr(ss, op, &rvalpc); + js_printf(jp, "\t%s ", js_throw_str); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ";\n"); break; case JSOP_ITER: - foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) == + forOf = (GET_UINT8(pc) == JSITER_FOR_OF); + foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) == JSITER_FOREACH; todo = -2; break; @@ -3174,81 +3861,122 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_GOTO: - case JSOP_GOTOX: sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_FOR_IN: /* - * The loop back-edge carries +1 stack balance, for the - * flag processed by JSOP_IFNE. We do not decompile the - * JSOP_IFNE, and instead push the left-hand side of 'in' - * after the loop edge in this stack slot (the JSOP_FOR* - * opcodes' decompilers do this pushing). + * The bytecode around pc looks like this: + * <> + * iter + * pc: goto/gotox C [src_for_in(B, D)] + * A: <> + * B: pop [maybe a src_decl_var/let] + * <> + * C: moreiter + * ifne/ifnex A + * enditer + * D: ... + * + * In an array comprehension or generator expression, we + * construct the for-head and store it in the slot pushed + * by JSOP_ITER, then recurse to decompile S. The + * culminating JSOP_ARRAYPUSH or JSOP_YIELD instruction + * (which S must contain, by construction) glues all the + * clauses together. + * + * Otherwise this is a for-in statement. We eagerly output + * the for-head and recurse to decompile the controlled + * statement S. + * + * We never decompile the obligatory JSOP_POP, + * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts + * to check that they are there. */ - cond = GetJumpOffset(pc, pc); + cond = GET_JUMP_OFFSET(pc); next = js_GetSrcNoteOffset(sn, 0); tail = js_GetSrcNoteOffset(sn, 1); + JS_ASSERT(pc[next] == JSOP_POP); + JS_ASSERT(pc[cond] == JSOP_LOOPENTRY); + cond += JSOP_LOOPENTRY_LENGTH; JS_ASSERT(pc[cond] == JSOP_MOREITER); DECOMPILE_CODE(pc + oplen, next - oplen); lval = POP_STR(); + + /* + * This string "" comes from jsopcode.tbl. It stands + * for the result pushed by JSOP_ITERNEXT. + */ + JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = ") == 0); + const_cast(lval)[strlen(lval) - 9] = '\0'; LOCAL_ASSERT(ss->top >= 1); if (ss->inArrayInit || ss->inGenExp) { rval = POP_STR(); if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) { - ss->sprinter.offset = ss->offsets[ss->top] - PAREN_SLOP; - if (Sprint(&ss->sprinter, " %s (%s in %s)", + ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP); + if (Sprint(&ss->sprinter, " %s (%s %s %s)", foreach ? js_for_each_str : js_for_str, - lval, rval) < 0) { + lval, + forOf ? "of" : "in", + rval) < 0) { return NULL; } /* - * Do not AddParentSlop here, as we will push the + * Do not AddParenSlop here, as we will push the * top-most offset again, which will add paren slop * for us. We must push to balance the stack budget * when nesting for heads in a comprehension. */ todo = ss->offsets[ss->top - 1]; } else { - todo = Sprint(&ss->sprinter, " %s (%s in %s)", + todo = Sprint(&ss->sprinter, " %s (%s %s %s)", foreach ? js_for_each_str : js_for_str, - lval, rval); + lval, + forOf ? "of" : "in", + rval); } if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL)) return NULL; - DECOMPILE_CODE(pc + next, cond - next); + DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH); } else { /* * As above, rval or an extension of it must remain * stacked during loop body decompilation. */ rval = GetStr(ss, ss->top - 1); - js_printf(jp, "\t%s (%s in %s) {\n", + xval = VarPrefix(js_GetSrcNote(jp->script, pc + next)); + js_printf(jp, "\t%s (%s%s %s %s) {\n", foreach ? js_for_each_str : js_for_str, - lval, rval); + xval, + lval, + forOf ? "of" : "in", + rval); jp->indent += 4; - DECOMPILE_CODE(pc + next, cond - next); + DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH); jp->indent -= 4; js_printf(jp, "\t}\n"); } pc += tail; - LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX); + LOCAL_ASSERT(*pc == JSOP_IFNE); len = js_CodeSpec[*pc].length; break; case SRC_WHILE: - cond = GetJumpOffset(pc, pc); + cond = GET_JUMP_OFFSET(pc); tail = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + cond, tail - cond); - js_printf(jp, "\twhile (%s) {\n", POP_COND_STR()); + js_printf(jp, "\twhile ("); + rval = PopCondStr(ss, &rvalpc); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ") {\n"); jp->indent += 4; DECOMPILE_CODE(pc + oplen, cond - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); pc += tail; - LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX); + LOCAL_ASSERT(*pc == JSOP_IFNE); len = js_CodeSpec[*pc].length; todo = -2; break; @@ -3258,7 +3986,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\tcontinue %s;\n", rval); break; @@ -3271,7 +3999,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\tbreak %s;\n", rval); break; @@ -3286,28 +4014,27 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_IFEQ: - case JSOP_IFEQX: { JSBool elseif = JS_FALSE; if_again: - len = GetJumpOffset(pc, pc); + len = GET_JUMP_OFFSET(pc); sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_IF: case SRC_IF_ELSE: - rval = POP_COND_STR(); + rval = PopCondStr(ss, &rvalpc); if (ss->inArrayInit || ss->inGenExp) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); - ss->sprinter.offset -= PAREN_SLOP; + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) return NULL; AddParenSlop(ss); } else { - js_printf(jp, - elseif ? " if (%s) {\n" : "\tif (%s) {\n", - rval); + js_printf(jp, elseif ? " if (" : "\tif ("); + SprintOpcodePermanent(jp, rval, rvalpc); + js_printf(jp, ") {\n"); jp->indent += 4; } @@ -3319,9 +4046,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) DECOMPILE_CODE(pc + oplen, tail - oplen); jp->indent -= 4; pc += tail; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + LOCAL_ASSERT(*pc == JSOP_GOTO); oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); + len = GET_JUMP_OFFSET(pc); js_printf(jp, "\t} else"); /* @@ -3355,26 +4082,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case SRC_COND: - xval = JS_strdup(cx, POP_STR()); - if (!xval) - return NULL; + xval = PopStrDupe(ss, op, &xvalpc); len = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, len - oplen); - lval = JS_strdup(cx, POP_STR()); - if (!lval) { - cx->free_((void *)xval); - return NULL; - } + lval = PopStrDupe(ss, op, &lvalpc); + pushpc = pc; pc += len; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + LOCAL_ASSERT(*pc == JSOP_GOTO); oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); + len = GET_JUMP_OFFSET(pc); DECOMPILE_CODE(pc + oplen, len - oplen); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s ? %s : %s", - xval, lval, rval); - cx->free_((void *)xval); - cx->free_((void *)lval); + rval = PopStrDupe(ss, op, &rvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, xval, xvalpc, pushpc, todo); + ss->sprinter.put(" ? "); + SprintOpcode(ss, lval, lvalpc, pushpc, todo); + ss->sprinter.put(" : "); + SprintOpcode(ss, rval, rvalpc, pushpc, todo); break; default: @@ -3384,108 +4108,42 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } case JSOP_IFNE: - case JSOP_IFNEX: LOCAL_ASSERT(0); break; case JSOP_OR: - case JSOP_ORX: xval = "||"; do_logical_connective: /* Top of stack is the first clause in a disjunction (||). */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - done = pc + GetJumpOffset(pc, pc); + lval = PopStrDupe(ss, op, &lvalpc); + done = pc + GET_JUMP_OFFSET(pc); + pushpc = pc; pc += len; + JS_ASSERT(*pc == JSOP_POP); + pc += JSOP_POP_LENGTH; len = done - pc; - if (!Decompile(ss, pc, len, op)) { - cx->free_((char *)lval); + if (!Decompile(ss, pc, len)) return NULL; - } - rval = POP_STR(); + rval = PopStrDupe(ss, op, &rvalpc); + if (!rval) + return NULL; + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pushpc, todo); if (jp->pretty && jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { - rval = JS_strdup(cx, rval); - if (!rval) { - tail = -1; - } else { - todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval); - tail = Sprint(&ss->sprinter, "%*s%s", - jp->indent + 4, "", rval); - cx->free_((char *)rval); - } - if (tail < 0) - todo = -1; + Sprint(&ss->sprinter, " %s\n", xval); + Sprint(&ss->sprinter, "%*s", jp->indent + 4, ""); } else { - todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); + Sprint(&ss->sprinter, " %s ", xval); } - cx->free_((char *)lval); + SprintOpcode(ss, rval, rvalpc, pushpc, todo); break; case JSOP_AND: - case JSOP_ANDX: xval = "&&"; goto do_logical_connective; - case JSOP_FORARG: - sn = NULL; - i = GET_ARGNO(pc); - goto do_forvarslot; - - case JSOP_FORLOCAL: - sn = js_GetSrcNote(jp->script, pc); - if (!IsVarSlot(jp, pc, &i)) { - JS_ASSERT(op == JSOP_FORLOCAL); - todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i)); - break; - } - - do_forvarslot: - atom = GetArgOrVarAtom(jp, i); - LOCAL_ASSERT(atom); - todo = SprintCString(&ss->sprinter, VarPrefix(sn)); - if (todo < 0 || !QuoteString(&ss->sprinter, atom, 0)) - return NULL; - break; - - case JSOP_FORNAME: - case JSOP_FORGNAME: - LOAD_ATOM(0); - - sn = js_GetSrcNote(jp->script, pc); - todo = SprintCString(&ss->sprinter, VarPrefix(sn)); - if (todo < 0 || !QuoteString(&ss->sprinter, atom, 0)) - return NULL; - break; - - case JSOP_FORPROP: - xval = NULL; - LOAD_ATOM(0); - if (!ATOM_IS_IDENTIFIER(atom)) { - xval = QuoteString(&ss->sprinter, atom, - (jschar)'\''); - if (!xval) - return NULL; - } - lval = POP_STR(); - if (xval) { - JS_ASSERT(*lval); - todo = Sprint(&ss->sprinter, index_format, lval, xval); - } else { - todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : ""); - if (todo < 0) - return NULL; - if (!QuoteString(&ss->sprinter, atom, 0)) - return NULL; - } - break; - - case JSOP_FORELEM: - todo = SprintCString(&ss->sprinter, forelem_cookie); - break; - case JSOP_ENUMELEM: case JSOP_ENUMCONSTELEM: /* @@ -3504,7 +4162,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = POP_STR(); LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); if (*xval == '\0') { - todo = SprintCString(&ss->sprinter, lval); + todo = ss->sprinter.put(lval); } else { todo = Sprint(&ss->sprinter, (JOF_OPMODE(lastop) == JOF_XMLNAME) @@ -3521,7 +4179,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_DUP2: rval = GetStr(ss, ss->top-2); - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); if (todo < 0 || !PushOff(ss, todo, (JSOp) ss->opcodes[ss->top-2])) { return NULL; @@ -3532,25 +4190,107 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #if JS_HAS_DESTRUCTURING sn = js_GetSrcNote(jp->script, pc); if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - len = 0; - lval = POP_STR(); - op = saveop = JSOP_ENUMELEM; - rval = POP_STR(); + if (SN_TYPE(sn) == SRC_DESTRUCT) { + pc = DecompileDestructuring(ss, pc, endpc); + if (!pc) + return NULL; + + lval = POP_STR(); /* Pop the decompiler result. */ + rval = POP_STR(); /* Pop the initializer expression. */ - if (strcmp(rval, forelem_cookie) == 0) { - todo = Sprint(&ss->sprinter, ss_format, - VarPrefix(sn), lval); + if (strcmp(rval, forelem_cookie) == 0) { + todo = Sprint(&ss->sprinter, ss_format, + VarPrefix(sn), lval); - // Skip POP so the SRC_FOR_IN code can pop for itself. - if (*pc == JSOP_POP) - len = JSOP_POP_LENGTH; + /* Skip POP so the SRC_FOR_IN code can pop for itself. */ + if (*pc == JSOP_POP) + len = JSOP_POP_LENGTH; + } else { + todo = Sprint(&ss->sprinter, "%s%s = %s", + VarPrefix(sn), lval, rval); + } + + op = saveop = JSOP_ENUMELEM; + len = 0; } else { - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCTLET); + + ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0); + LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0); + + obj = jp->script->getObject(GET_UINT32_INDEX(pc + offsetToLet)); + StaticBlockObject &blockObj = obj->asStaticBlock(); + + uint32_t blockDepth = blockObj.stackDepth(); + LOCAL_ASSERT(blockDepth < ss->top); + LOCAL_ASSERT(ss->top <= blockDepth + blockObj.slotCount()); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, blockObj, &atoms)) + return NULL; + + /* + * Skip any initializers preceding this one. E.g., in + * let (w=1, x=2, [y,z] = a) { ... } + * skip 'w' and 'x' for the JSOP_DUP of '[y,z] = a'. + */ + AtomRange letNames = atoms.all(); + uint32_t curDepth = ss->top - 1 /* initializer */; + for (uint32_t i = blockDepth; i < curDepth; ++i) + letNames.popFront(); + + /* + * Pop and copy the rhs before it gets clobbered. + * Use JSOP_SETLOCAL's precedence since this is =. + */ + DupBuffer rhs(cx); + if (!Dup(PopStr(ss, JSOP_SETLOCAL), &rhs)) + return NULL; + + /* Destructure, tracking how many vars were bound. */ + size_t remainBefore = letNames.remain(); + pc = DecompileDestructuring(ss, pc, endpc, &letNames); + if (!pc) + return NULL; + size_t remainAfter = letNames.remain(); + + /* + * Merge the lhs and rhs and prefix with a cookie to + * tell enterlet0 not to prepend "name = ". + */ + const char *lhs = PopStr(ss, JSOP_NOP); + ptrdiff_t off = Sprint(&ss->sprinter, "%s%s = %s", + DestructuredString, lhs, rhs.begin()); + if (off < 0 || !PushOff(ss, off, JSOP_NOP)) + return NULL; + + /* + * Only one slot has been pushed (holding the entire + * decompiled destructuring expression). However, the + * abstract depth needs one slot per bound var, so push + * empty strings for the remainder. We don't have to + * worry about empty destructuring because the parser + * ensures that there is always at least one pushed + * slot for each destructuring lhs. + */ + LOCAL_ASSERT(remainBefore >= remainAfter); + LOCAL_ASSERT(remainBefore > remainAfter || remainAfter > 0); + for (size_t i = remainBefore - 1; i > remainAfter; --i) { + if (!PushStr(ss, SkipString, JSOP_NOP)) + return NULL; + } + + LOCAL_ASSERT(*pc == JSOP_POP); + pc += JSOP_POP_LENGTH; + + /* Eat up the JSOP_UNDEFINED following empty destructuring. */ + if (remainBefore == remainAfter) { + LOCAL_ASSERT(*pc == JSOP_UNDEFINED); + pc += JSOP_UNDEFINED_LENGTH; + } + + len = 0; + todo = -2; } break; } @@ -3558,7 +4298,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = GetStr(ss, ss->top-1); saveop = (JSOp) ss->opcodes[ss->top-1]; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); + break; + + case JSOP_SWAP: + Swap(ss->offsets[ss->top-1], ss->offsets[ss->top-2]); + Swap(ss->opcodes[ss->top-1], ss->opcodes[ss->top-2]); + Swap(ss->bytecodes[ss->top-1], ss->bytecodes[ss->top-2]); + todo = -2; break; case JSOP_SETARG: @@ -3575,25 +4322,24 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - rval = POP_STR(); + rval = PopStrDupe(ss, op, &rvalpc); if (op == JSOP_SETNAME || op == JSOP_SETGNAME) (void) PopOff(ss, op); do_setlval: sn = js_GetSrcNote(jp->script, pc - 1); + todo = ss->sprinter.getOffset(); if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - todo = Sprint(&ss->sprinter, "%s %s= %s", - lval, - (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : CodeToken[lastop], - rval); + const char *token = + GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, + &lastlvalpc, &lastrvalpc); + Sprint(&ss->sprinter, "%s %s= ", lval, token); + SprintOpcode(ss, rval, rvalpc, pc, todo); } else { sn = js_GetSrcNote(jp->script, pc); - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); + const char *prefix = VarPrefix(sn); + Sprint(&ss->sprinter, "%s%s = ", prefix, lval); + SprintOpcode(ss, rval, rvalpc, pc, todo); } if (op == JSOP_SETLOCALPOP) { if (!PushOff(ss, todo, saveop)) @@ -3610,16 +4356,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_EVAL: case JSOP_FUNCALL: case JSOP_FUNAPPLY: + { argc = GET_ARGC(pc); - argv = (char **) + const char **argv = (const char **) cx->malloc_((size_t)(argc + 1) * sizeof *argv); if (!argv) return NULL; + jsbytecode **argbytecodes = (jsbytecode **) + cx->malloc_((size_t)(argc + 1) * sizeof *argbytecodes); + if (!argbytecodes) { + cx->free_(argv); + return NULL; + } op = JSOP_SETNAME; - ok = JS_TRUE; for (i = argc; i > 0; i--) - argv[i] = JS_strdup(cx, POP_STR()); + argv[i] = PopStrDupe(ss, op, &argbytecodes[i]); /* Skip the JSOP_PUSHOBJ-created empty string. */ LOCAL_ASSERT(ss->top >= 2); @@ -3631,51 +4383,41 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * See PROPAGATE_CALLNESS. */ op = (JSOp) ss->opcodes[ss->top - 1]; - lval = PopStr(ss, - (saveop == JSOP_NEW && - (op == JSOP_CALL || - op == JSOP_EVAL || - op == JSOP_FUNCALL || - op == JSOP_FUNAPPLY || - (js_CodeSpec[op].format & JOF_CALLOP))) - ? JSOP_NAME - : saveop); + argv[0] = PopStrDupe(ss, + (saveop == JSOP_NEW && + (op == JSOP_CALL || + op == JSOP_EVAL || + op == JSOP_FUNCALL || + op == JSOP_FUNAPPLY || + op == JSOP_CALLPROP || + op == JSOP_CALLELEM)) + ? JSOP_NAME + : saveop, + &lvalpc); op = saveop; - argv[0] = JS_strdup(cx, lval); - if (!argv[0]) - ok = JS_FALSE; - lval = "(", rval = ")"; + todo = ss->sprinter.getOffset(); if (op == JSOP_NEW) { if (argc == 0) lval = rval = ""; - todo = Sprint(&ss->sprinter, "%s %s%s", - js_new_str, argv[0], lval); - } else { - todo = Sprint(&ss->sprinter, ss_format, - argv[0], lval); + Sprint(&ss->sprinter, "%s ", js_new_str); } - if (todo < 0) - ok = JS_FALSE; + SprintOpcode(ss, argv[0], lvalpc, pc, todo); + ss->sprinter.put(lval); for (i = 1; i <= argc; i++) { - if (!argv[i] || - Sprint(&ss->sprinter, ss_format, - argv[i], (i < argc) ? ", " : "") < 0) { - ok = JS_FALSE; - break; - } + SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo); + if (i < argc) + ss->sprinter.put(", "); } - if (Sprint(&ss->sprinter, rval) < 0) - ok = JS_FALSE; + ss->sprinter.put(rval); - for (i = 0; i <= argc; i++) - cx->free_(argv[i]); cx->free_(argv); - if (!ok) - return NULL; + cx->free_(argbytecodes); + break; + } case JSOP_SETCALL: todo = Sprint(&ss->sprinter, ""); @@ -3686,7 +4428,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_delete_lval: todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); break; @@ -3725,11 +4467,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_TYPEOFEXPR: case JSOP_TYPEOF: case JSOP_VOID: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s", - (op == JSOP_VOID) ? js_void_str : js_typeof_str, - rval); + { + const char *prefix = (op == JSOP_VOID) ? js_void_str : js_typeof_str; + rval = PopStrDupe(ss, op, &rvalpc); + todo = ss->sprinter.getOffset(); + Sprint(&ss->sprinter, "%s ", prefix); + SprintOpcode(ss, rval, rvalpc, pc, todo); break; + } case JSOP_INCARG: case JSOP_DECARG: @@ -3746,10 +4491,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_inclval: todo = Sprint(&ss->sprinter, ss_format, js_incop_strs[!(cs->format & JOF_INC)], lval); + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + len += GetDecomposeLength(pc, js_CodeSpec[*pc].length); break; case JSOP_INCPROP: @@ -3766,6 +4513,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = Sprint(&ss->sprinter, fmt, js_incop_strs[!(cs->format & JOF_INC)], lval, rval); + len += GetDecomposeLength(pc, JSOP_INCPROP_LENGTH); break; case JSOP_INCELEM: @@ -3785,6 +4533,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = Sprint(&ss->sprinter, ss_format, js_incop_strs[!(cs->format & JOF_INC)], lval); } + len += GetDecomposeLength(pc, JSOP_INCELEM_LENGTH); break; case JSOP_ARGINC: @@ -3802,10 +4551,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_lvalinc: todo = Sprint(&ss->sprinter, ss_format, lval, js_incop_strs[!(cs->format & JOF_INC)]); + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + len += GetDecomposeLength(pc, js_CodeSpec[*pc].length); break; case JSOP_PROPINC: @@ -3821,6 +4572,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, lval, rval, js_incop_strs[!(cs->format & JOF_INC)]); + len += GetDecomposeLength(pc, JSOP_PROPINC_LENGTH); break; case JSOP_ELEMINC: @@ -3840,13 +4592,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) todo = Sprint(&ss->sprinter, ss_format, lval, js_incop_strs[!(cs->format & JOF_INC)]); } + len += GetDecomposeLength(pc, JSOP_ELEMINC_LENGTH); break; - case JSOP_LENGTH: - fmt = dot_format; - rval = js_length_str; - goto do_getprop_lval; - case JSOP_GETPROP2: op = JSOP_GETPROP; (void) PopOff(ss, lastop); @@ -3855,55 +4603,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_CALLPROP: case JSOP_GETPROP: case JSOP_GETXPROP: + case JSOP_LENGTH: LOAD_ATOM(0); - do_getprop: - GET_QUOTE_AND_FMT(index_format, dot_format, rval); - do_getprop_lval: + GET_QUOTE_AND_FMT("[%s]", ".%s", rval); PROPAGATE_CALLNESS(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, lval, rval); - break; - - case JSOP_GETTHISPROP: - LOAD_ATOM(0); - GET_QUOTE_AND_FMT(index_format, dot_format, rval); - todo = Sprint(&ss->sprinter, fmt, js_this_str, rval); + lval = PopStr(ss, op, &lvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); + Sprint(&ss->sprinter, fmt, rval); break; - case JSOP_GETARGPROP: - /* Get the name of the argument or variable. */ - i = GET_ARGNO(pc); - - do_getarg_prop: - atom = GetArgOrVarAtom(ss->printer, i); - LOCAL_ASSERT(atom); - lval = QuoteString(&ss->sprinter, atom, 0); - if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op)) - return NULL; - - /* Get the name of the property. */ - LOAD_ATOM(ARGNO_LEN); - goto do_getprop; - - case JSOP_GETLOCALPROP: - if (IsVarSlot(jp, pc, &i)) - goto do_getarg_prop; - LOCAL_ASSERT((uintN)i < ss->top); - lval = GetLocal(ss, i); - if (!lval) - return NULL; - todo = SprintCString(&ss->sprinter, lval); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - LOAD_ATOM(2); - goto do_getprop; - case JSOP_SETPROP: case JSOP_SETMETHOD: + { LOAD_ATOM(0); - GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); - rval = POP_STR(); + GET_QUOTE_AND_FMT("[%s] %s= ", ".%s %s= ", xval); + rval = PopStrDupe(ss, op, &rvalpc); /* * Force precedence below the numeric literal opcodes, so that @@ -3911,78 +4627,63 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * around the left-hand side of dot. */ op = JSOP_GETPROP; - lval = POP_STR(); + lval = PopStr(ss, op, &lvalpc); sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, fmt, lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : CodeToken[lastop] - : "", - rval); + const char *token = + GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, + &lastlvalpc, &lastrvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); + Sprint(&ss->sprinter, fmt, xval, token); + SprintOpcode(ss, rval, rvalpc, pc, todo); break; + } case JSOP_GETELEM2: - op = JSOP_GETELEM; (void) PopOff(ss, lastop); /* FALL THROUGH */ - case JSOP_CALLELEM: case JSOP_GETELEM: op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); + xval = PopStrDupe(ss, op, &xvalpc); op = saveop; PROPAGATE_CALLNESS(); - lval = POP_STR(); - if (*xval == '\0') { - todo = Sprint(&ss->sprinter, "%s", lval); - } else { - todo = Sprint(&ss->sprinter, - (JOF_OPMODE(lastop) == JOF_XMLNAME) - ? dot_format - : index_format, - lval, xval); + lval = PopStr(ss, op, &lvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); + if (*xval != '\0') { + bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME); + ss->sprinter.put(xml ? "." : "["); + SprintOpcode(ss, xval, xvalpc, pc, todo); + ss->sprinter.put(xml ? "" : "]"); } break; case JSOP_SETELEM: - rval = POP_STR(); + { + rval = PopStrDupe(ss, op, &rvalpc); op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); + xval = PopStrDupe(ss, op, &xvalpc); cs = &js_CodeSpec[ss->opcodes[ss->top]]; op = JSOP_GETELEM; /* lval must have high precedence */ - lval = POP_STR(); + lval = PopStr(ss, op, &lvalpc); op = saveop; if (*xval == '\0') goto do_setlval; sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, - (JOF_MODE(cs->format) == JOF_XMLNAME) - ? "%s.%s %s= %s" - : "%s[%s] %s= %s", - lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : CodeToken[lastop] - : "", - rval); - break; - - case JSOP_ARGSUB: - i = (jsint) GET_ARGNO(pc); - todo = Sprint(&ss->sprinter, "%s[%d]", - js_arguments_str, (int) i); - break; - - case JSOP_ARGCNT: - todo = Sprint(&ss->sprinter, dot_format, - js_arguments_str, js_length_str); + bool xml = (JOF_MODE(cs->format) == JOF_XMLNAME); + const char *token = + GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, + &lastlvalpc, &lastrvalpc); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); + ss->sprinter.put(xml ? "." : "["); + SprintOpcode(ss, xval, xvalpc, pc, todo); + ss->sprinter.put(xml ? "" : "]"); + Sprint(&ss->sprinter, " %s= ", token); + SprintOpcode(ss, rval, rvalpc, pc, todo); break; + } case JSOP_CALLARG: case JSOP_GETARG: @@ -3995,12 +4696,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } #else LOCAL_ASSERT(atom); -#endif - goto do_name; - - case JSOP_CALLGLOBAL: - case JSOP_GETGLOBAL: - atom = jp->script->getGlobalAtom(GET_SLOTNO(pc)); +#endif goto do_name; case JSOP_CALLNAME: @@ -4017,7 +4713,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); todo = Sprint(&ss->sprinter, sss_format, VarPrefix(sn), lval, rval); break; @@ -4054,43 +4750,43 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"'); if (!rval) return NULL; - todo = STR2OFF(&ss->sprinter, rval); + todo = ss->sprinter.getOffsetOf(rval); break; case JSOP_LAMBDA: case JSOP_LAMBDA_FC: - case JSOP_LAMBDA_DBGFC: #if JS_HAS_GENERATOR_EXPRS sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_GENEXP) { - void *mark; - jsuword *innerLocalNames, *outerLocalNames; + Vector *innerLocalNames; + Vector *outerLocalNames; JSScript *inner, *outer; - SprintStack ss2; + Vector *decompiledOpcodes; + SprintStack ss2(cx); JSFunction *outerfun; - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); /* - * All allocation when decompiling is LIFO, using malloc - * or, more commonly, arena-allocating from cx->tempPool. - * Therefore after InitSprintStack succeeds, we must - * release to mark before returning. + * All allocation when decompiling is LIFO, using malloc or, + * more commonly, arena-allocating from cx->tempLifoAlloc + * Therefore after InitSprintStack succeeds, we must release + * to mark before returning. */ - mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); if (fun->script()->bindings.hasLocalNames()) { - innerLocalNames = - fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool); - if (!innerLocalNames) + innerLocalNames = cx->new_ >(cx); + if (!innerLocalNames || + !fun->script()->bindings.getLocalNameArray(cx, innerLocalNames)) + { return NULL; + } } else { innerLocalNames = NULL; } inner = fun->script(); - if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) return NULL; - } ss2.inGenExp = JS_TRUE; /* @@ -4103,40 +4799,33 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) outer = jp->script; outerfun = jp->fun; outerLocalNames = jp->localNames; - LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length); + decompiledOpcodes = jp->decompiledOpcodes; + LOCAL_ASSERT(UnsignedPtrDiff(pc, outer->code) <= outer->length); jp->script = inner; jp->fun = fun; jp->localNames = innerLocalNames; + jp->decompiledOpcodes = NULL; /* * Decompile only the main bytecode, to avoid tripping over * new prolog ops that have stack effects. */ - ok = Decompile(&ss2, inner->main, - inner->length - (inner->main - inner->code), - JSOP_NOP) + ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset) != NULL; jp->script = outer; jp->fun = outerfun; jp->localNames = outerLocalNames; - if (!ok) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + jp->decompiledOpcodes = decompiledOpcodes; + if (!ok) return NULL; - } /* * Advance over this op and its global |this| push, and * arrange to advance over the call to this lambda. */ pc += len; - if (*pc == JSOP_BLOCKCHAIN) { - pc += JSOP_BLOCKCHAIN_LENGTH; - } else { - LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN); - pc += JSOP_NULLBLOCKCHAIN_LENGTH; - } - LOCAL_ASSERT(*pc == JSOP_PUSH); - pc += JSOP_PUSH_LENGTH; + LOCAL_ASSERT(*pc == JSOP_UNDEFINED); + pc += JSOP_UNDEFINED_LENGTH; LOCAL_ASSERT(*pc == JSOP_CALL); LOCAL_ASSERT(GET_ARGC(pc) == 0); len = JSOP_CALL_LENGTH; @@ -4159,7 +4848,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ pc2 = pc + len; op = JSOp(*pc2); - if (op == JSOP_TRACE || op == JSOP_NOP) + if (op == JSOP_LOOPHEAD || op == JSOP_NOP) pc2 += JSOP_NOP_LENGTH; LOCAL_ASSERT(pc2 < endpc || endpc < outer->code + outer->length); @@ -4187,22 +4876,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } /* - * Alas, we have to malloc a copy of the result left on - * the top of ss2 because both ss and ss2 arena-allocate - * from cx's tempPool. + * Alas, we have to malloc a copy of the result left on the + * top of ss2 because both ss and ss2 arena-allocate from + * cx's tempLifoAlloc */ rval = JS_strdup(cx, PopStr(&ss2, op)); - JS_ARENA_RELEASE(&cx->tempPool, mark); + las.releaseEarly(); if (!rval) return NULL; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); cx->free_((void *)rval); break; } #endif /* JS_HAS_GENERATOR_EXPRS */ /* FALL THROUGH */ - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); { /* * Always parenthesize expression closures. We can't force @@ -4219,43 +4908,39 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) return NULL; } sprint_string: - todo = SprintString(&ss->sprinter, str); + todo = ss->sprinter.putString(str); break; case JSOP_CALLEE: JS_ASSERT(jp->fun && jp->fun->atom); - todo = SprintString(&ss->sprinter, jp->fun->atom); + todo = ss->sprinter.putString(jp->fun->atom); break; case JSOP_OBJECT: - LOAD_OBJECT(0); + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); str = js_ValueToSource(cx, ObjectValue(*obj)); if (!str) return NULL; goto sprint_string; case JSOP_REGEXP: - GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj); - if (!js_regexp_toString(cx, obj, Valueify(&val))) + obj = jp->script->getRegExp(GET_UINT32_INDEX(pc)); + str = obj->asRegExp().toString(cx); + if (!str) return NULL; - str = JSVAL_TO_STRING(val); goto sprint_string; case JSOP_TABLESWITCH: - case JSOP_TABLESWITCHX: { - ptrdiff_t jmplen, off, off2; + ptrdiff_t off, off2; jsint j, n, low, high; TableEntry *table, *tmp; sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; + off = GET_JUMP_OFFSET(pc); + pc2 = pc + JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); @@ -4265,7 +4950,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) if (n == 0) { table = NULL; j = 0; - ok = JS_TRUE; + ok = true; } else { table = (TableEntry *) cx->malloc_((size_t)n * sizeof *table); @@ -4273,7 +4958,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) return NULL; for (i = j = 0; i < n; i++) { table[j].label = NULL; - off2 = GetJumpOffset(pc, pc2); + off2 = GET_JUMP_OFFSET(pc2); if (off2) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { @@ -4285,25 +4970,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) table[j].order = j; j++; } - pc2 += jmplen; + pc2 += JUMP_OFFSET_LEN; } tmp = (TableEntry *) cx->malloc_((size_t)j * sizeof *table); if (tmp) { - VOUCH_DOES_NOT_REQUIRE_STACK(); - ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry), - CompareOffsets, NULL, tmp, - JS_SORTING_GENERIC); - cx->free_(tmp); + MergeSort(table, size_t(j), tmp, CompareTableEntries); + Foreground::free_(tmp); + ok = true; } else { - ok = JS_FALSE; + ok = false; } } - if (ok) { - ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, - JS_FALSE); - } + if (ok) + ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, false); cx->free_(table); if (!ok) return NULL; @@ -4312,20 +4993,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } case JSOP_LOOKUPSWITCH: - case JSOP_LOOKUPSWITCHX: { - ptrdiff_t jmplen, off, off2; + ptrdiff_t off, off2; jsatomid npairs, k; TableEntry *table; sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; + off = GET_JUMP_OFFSET(pc); + pc2 = pc + JUMP_OFFSET_LEN; npairs = GET_UINT16(pc2); pc2 += UINT16_LEN; @@ -4341,11 +5018,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } else { table[k].label = NULL; } - uint16 constIndex = GET_INDEX(pc2); + uint16_t constIndex = GET_INDEX(pc2); pc2 += INDEX_LEN; - off2 = GetJumpOffset(pc, pc2); - pc2 += jmplen; - table[k].key = Jsvalify(jp->script->getConst(constIndex)); + off2 = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + table[k].key = jp->script->getConst(constIndex); table[k].offset = off2; } @@ -4377,9 +5054,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) off2 = off; for (ncases = 0; off2 != 0; ncases++) { pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); - if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { + LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT); + if (*pc2 == JSOP_DEFAULT) { /* End of cases, but count default as a case. */ off2 = 0; } else { @@ -4402,12 +5078,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) off2 = off; for (i = 0; i < ncases; i++) { pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); + LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT); caseOff = pc2 - pc; table[i].key = INT_TO_JSVAL((jsint) caseOff); - table[i].offset = caseOff + GetJumpOffset(pc2, pc2); - if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { + table[i].offset = caseOff + GET_JUMP_OFFSET(pc2); + if (*pc2 == JSOP_CASE) { sn = js_GetSrcNote(jp->script, pc2); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); off2 = js_GetSrcNoteOffset(sn, 0); @@ -4421,7 +5096,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ off = JSVAL_TO_INT(table[ncases-1].key); pc2 = pc + off; - off += GetJumpOffset(pc2, pc2); + off += GET_JUMP_OFFSET(pc2); ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off, JS_TRUE); @@ -4433,65 +5108,38 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } case JSOP_CASE: - case JSOP_CASEX: { - lval = POP_STR(); + lval = PopStr(ss, op, &lvalpc); if (!lval) return NULL; - js_printf(jp, "\tcase %s:\n", lval); + js_printf(jp, "\tcase "); + SprintOpcodePermanent(jp, lval, lvalpc); + js_printf(jp, ":\n"); todo = -2; break; } case JSOP_DEFFUN: - case JSOP_DEFFUN_FC: - case JSOP_DEFFUN_DBGFC: - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); todo = -2; goto do_function; - break; - - case JSOP_TRAP: - saveop = op = JS_GetTrapOpcode(cx, jp->script, pc); - *pc = op; - cs = &js_CodeSpec[op]; - len = cs->length; - DECOMPILE_CODE(pc, len); - *pc = JSOP_TRAP; - todo = -2; - break; case JSOP_HOLE: - todo = SprintPut(&ss->sprinter, "", 0); + todo = ss->sprinter.put("", 0); break; case JSOP_NEWINIT: { - i = pc[1]; + i = GET_UINT8(pc); LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); - todo = ss->sprinter.offset; -#if JS_HAS_SHARP_VARS - op = (JSOp)pc[len]; - if (op == JSOP_SHARPINIT) - op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH]; - if (op == JSOP_DEFSHARP) { - pc += len; - cs = &js_CodeSpec[op]; - len = cs->length; - if (Sprint(&ss->sprinter, "#%u=", - (unsigned) (jsint) GET_UINT16(pc + UINT16_LEN)) - < 0) { - return NULL; - } - } -#endif /* JS_HAS_SHARP_VARS */ + todo = ss->sprinter.getOffset(); if (i == JSProto_Array) { ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) + if (ss->sprinter.put("[") < 0) return NULL; } else { - if (SprintCString(&ss->sprinter, "{") < 0) + if (ss->sprinter.put("{") < 0) return NULL; } break; @@ -4499,17 +5147,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_NEWARRAY: { - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) + if (ss->sprinter.put("[") < 0) return NULL; break; } case JSOP_NEWOBJECT: { - todo = ss->sprinter.offset; - if (SprintCString(&ss->sprinter, "{") < 0) + todo = ss->sprinter.getOffset(); + if (ss->sprinter.put("{") < 0) return NULL; break; } @@ -4519,7 +5167,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) JSBool inArray; op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); + rval = PopStr(ss, op, &rvalpc); sn = js_GetSrcNote(jp->script, pc); /* Skip any #n= prefix to find the opening bracket. */ @@ -4528,10 +5176,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) inArray = (*xval == '['); if (inArray) --ss->inArrayInit; - todo = Sprint(&ss->sprinter, "%s%s%c", - rval, - (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", - inArray ? ']' : '}'); + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, rval, rvalpc, pc, todo); + Sprint(&ss->sprinter, "%s%c", + (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", + inArray ? ']' : '}'); break; } @@ -4543,13 +5192,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]); /* Turn off most parens. */ - op = JSOP_SETNAME; - rval = POP_STR(); + rval = PopStr(ss, JSOP_SETNAME, &rvalpc); /* Turn off all parens for xval and lval, which we control. */ - op = JSOP_NOP; - xval = POP_STR(); - lval = POP_STR(); + xval = PopStr(ss, JSOP_NOP); + lval = PopStr(ss, JSOP_NOP, &lvalpc); sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_INITPROP) { @@ -4557,25 +5204,24 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) goto do_initprop; } maybeComma = isFirst ? "" : ", "; - todo = Sprint(&ss->sprinter, sss_format, - lval, - maybeComma, - rval); + todo = Sprint(&ss->sprinter, "%s%s", lval, maybeComma); + SprintOpcode(ss, rval, rvalpc, pc, todo); break; case JSOP_INITPROP: case JSOP_INITMETHOD: LOAD_ATOM(0); - xval = QuoteString(&ss->sprinter, atom, - jschar(ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); + xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\'')); if (!xval) return NULL; isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]); - rval = POP_STR(); - lval = POP_STR(); + rval = PopStrDupe(ss, op, &rvalpc); + lval = PopStr(ss, op, &lvalpc); /* fall through */ do_initprop: + todo = ss->sprinter.getOffset(); + SprintOpcode(ss, lval, lvalpc, pc, todo); maybeComma = isFirst ? "" : ", "; if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { const char *end = rval + strlen(rval); @@ -4586,34 +5232,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) LOCAL_ASSERT(rval[8] == ' '); rval += 8 + 1; LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}'); - todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s", - lval, - maybeComma, - (lastop == JSOP_GETTER) - ? js_get_str : js_set_str, - xval, - (rval[0] != '(') ? " " : "", - end - rval, rval); + Sprint(&ss->sprinter, "%s%s %s%s%.*s", + maybeComma, + (lastop == JSOP_GETTER) + ? js_get_str : js_set_str, + xval, + (rval[0] != '(') ? " " : "", + end - rval, rval); } else { - todo = Sprint(&ss->sprinter, "%s%s%s: %s", - lval, maybeComma, xval, rval); + Sprint(&ss->sprinter, "%s%s: ", maybeComma, xval); + SprintOpcode(ss, rval, rvalpc, pc, todo); } break; } -#if JS_HAS_SHARP_VARS - case JSOP_DEFSHARP: - i = (jsint) GET_UINT16(pc + UINT16_LEN); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); - break; - - case JSOP_USESHARP: - i = (jsint) GET_UINT16(pc + UINT16_LEN); - todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); - break; -#endif /* JS_HAS_SHARP_VARS */ - case JSOP_DEBUGGER: js_printf(jp, "\tdebugger;\n"); todo = -2; @@ -4636,9 +5268,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_ANYNAME: if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { len += JSOP_TOATTRNAME_LENGTH; - todo = SprintPut(&ss->sprinter, "@*", 2); + todo = ss->sprinter.put("@*", 2); } else { - todo = SprintPut(&ss->sprinter, "*", 1); + todo = ss->sprinter.put("*", 1); } break; @@ -4657,7 +5289,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); break; @@ -4702,7 +5334,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_SETXMLNAME: /* Pop the r.h.s., the dummy string, and the name. */ - rval = POP_STR(); + rval = PopStrDupe(ss, op, &rvalpc); (void) PopOff(ss, op); lval = POP_STR(); goto do_setlval; @@ -4746,18 +5378,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_XMLCDATA: LOAD_ATOM(0); - todo = SprintPut(&ss->sprinter, "sprinter.put("sprinter, atom, DONT_ESCAPE)) return NULL; - SprintPut(&ss->sprinter, "]]>", 3); + ss->sprinter.put("]]>", 3); break; case JSOP_XMLCOMMENT: LOAD_ATOM(0); - todo = SprintPut(&ss->sprinter, "", 3); + ss->sprinter.put("-->", 3); break; case JSOP_XMLPI: @@ -4765,19 +5397,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) rval = JS_strdup(cx, POP_STR()); if (!rval) return NULL; - todo = SprintPut(&ss->sprinter, "sprinter.put("sprinter, atom, 0) && (*rval == '\0' || - (SprintPut(&ss->sprinter, " ", 1) >= 0 && - SprintCString(&ss->sprinter, rval))); + (ss->sprinter.put(" ", 1) >= 0 && + ss->sprinter.put(rval))); cx->free_((char *)rval); if (!ok) return NULL; - SprintPut(&ss->sprinter, "?>", 2); + ss->sprinter.put("?>", 2); break; case JSOP_GETFUNNS: - todo = SprintPut(&ss->sprinter, js_function_str, 8); + todo = ss->sprinter.put(js_function_str, 8); break; #endif /* JS_HAS_XML_SUPPORT */ @@ -4787,16 +5419,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } } + if (cx->isExceptionPending()) { + /* OOMs while printing to a string do not immediately return. */ + return NULL; + } + if (todo < 0) { /* -2 means "don't push", -1 means reported error. */ + JS_ASSERT(todo == -2); if (todo == -1) return NULL; } else { - if (!PushOff(ss, todo, saveop)) + if (!UpdateDecompiledText(ss, pushpc, todo)) return NULL; + if (!PushOff(ss, todo, saveop, pushpc)) + return NULL; + if (js_CodeSpec[*pc].format & JOF_DECOMPOSE) + CopyDecompiledTextForDecomposedOp(jp, pc); } - if (cs->format & JOF_CALLOP) { + if (op == JSOP_CALLXMLNAME) { todo = Sprint(&ss->sprinter, ""); if (todo < 0 || !PushOff(ss, todo, saveop)) return NULL; @@ -4810,12 +5452,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ #undef inXML #undef DECOMPILE_CODE -#undef NEXT_OP #undef TOP_STR #undef POP_STR #undef POP_STR_PREC #undef LOCAL_ASSERT -#undef ATOM_IS_IDENTIFIER #undef GET_QUOTE_AND_FMT #undef GET_ATOM_QUOTE_AND_FMT @@ -4826,25 +5466,16 @@ static JSBool DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, uintN pcdepth) { - uintN depth, i; - SprintStack ss; - JSContext *cx; - void *mark; - JSBool ok; - JSScript *oldscript; - char *last; + JSContext *cx = jp->sprinter.context; - depth = StackDepth(script); + uintN depth = StackDepth(script); JS_ASSERT(pcdepth <= depth); - cx = jp->sprinter.context; - - AutoScriptUntrapper untrapper(cx, script, &pc); /* Initialize a sprinter for use with the offset stack. */ - mark = JS_ARENA_MARK(&cx->tempPool); - ok = InitSprintStack(cx, &ss, jp, depth); - if (!ok) - goto out; + LifoAllocScope las(&cx->tempLifoAlloc()); + SprintStack ss(cx); + if (!InitSprintStack(cx, &ss, jp, depth)) + return false; /* * If we are called from js_DecompileValueGenerator with a portion of @@ -4859,29 +5490,27 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, */ ss.top = pcdepth; if (pcdepth != 0) { - for (i = 0; i < pcdepth; i++) { + for (uintN i = 0; i < pcdepth; i++) { ss.offsets[i] = -2 - (ptrdiff_t)i; ss.opcodes[i] = *jp->pcstack[i]; } } /* Call recursive subroutine to do the hard work. */ - oldscript = jp->script; + JSScript *oldscript = jp->script; jp->script = script; - ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; + bool ok = Decompile(&ss, pc, len) != NULL; jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ if (ok && ss.top) { + const char *last; do { - last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); + last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP)); } while (ss.top > pcdepth); js_printf(jp, "%s", last); } -out: - /* Free all temporary stuff allocated under this call. */ - JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } @@ -4945,25 +5574,19 @@ js_DecompileFunctionBody(JSPrinter *jp) JS_ASSERT(jp->fun); JS_ASSERT(!jp->script); - if (!FUN_INTERPRETED(jp->fun)) { + if (!jp->fun->isInterpreted()) { js_printf(jp, native_code_str); return JS_TRUE; } - script = jp->fun->u.i.script; + script = jp->fun->script(); return DecompileBody(jp, script, script->code); } JSBool js_DecompileFunction(JSPrinter *jp) { - JSFunction *fun; - uintN i; - JSAtom *param; - jsbytecode *pc, *endpc; - JSBool ok; - - fun = jp->fun; + JSFunction *fun = jp->fun; JS_ASSERT(fun); JS_ASSERT(!jp->script); @@ -4984,36 +5607,33 @@ js_DecompileFunction(JSPrinter *jp) return JS_FALSE; js_puts(jp, "("); - if (!FUN_INTERPRETED(fun)) { + if (!fun->isInterpreted()) { js_printf(jp, ") {\n"); jp->indent += 4; js_printf(jp, native_code_str); jp->indent -= 4; js_printf(jp, "\t}"); } else { - JSScript *script = fun->u.i.script; + JSScript *script = fun->script(); #if JS_HAS_DESTRUCTURING - SprintStack ss; - void *mark; + SprintStack ss(jp->sprinter.context); #endif /* Print the parameters. */ - pc = script->main; - AutoScriptUntrapper untrapper(jp->sprinter.context, script, &pc); - endpc = pc + script->length; - ok = JS_TRUE; + jsbytecode *pc = script->main(); + jsbytecode *endpc = pc + script->length; + JSBool ok = JS_TRUE; #if JS_HAS_DESTRUCTURING ss.printer = NULL; jp->script = script; - mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool); #endif - for (i = 0; i < fun->nargs; i++) { + for (uintN i = 0; i < fun->nargs; i++) { if (i > 0) js_puts(jp, ", "); - param = GetArgOrVarAtom(jp, i); + JSAtom *param = GetArgOrVarAtom(jp, i); #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) @@ -5038,7 +5658,7 @@ js_DecompileFunction(JSPrinter *jp) LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(&ss, JSOP_NOP); - todo = SprintCString(&jp->sprinter, lval); + todo = jp->sprinter.put(lval); if (todo < 0) { ok = JS_FALSE; break; @@ -5057,7 +5677,6 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING jp->script = NULL; - JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark); #endif if (!ok) return JS_FALSE; @@ -5084,30 +5703,26 @@ js_DecompileFunction(JSPrinter *jp) } char * -js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, +js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSString *fallback) { StackFrame *fp; JSScript *script; jsbytecode *pc; - Value v = Valueify(v_in); - JS_ASSERT(spindex < 0 || spindex == JSDVG_IGNORE_STACK || spindex == JSDVG_SEARCH_STACK); - LeaveTrace(cx); - - if (!cx->running() || !cx->fp()->isScriptFrame()) + if (!cx->hasfp() || !cx->fp()->isScriptFrame()) goto do_fallback; - fp = cx->fp(); + fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); script = fp->script(); - pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc; + pc = cx->regs().pc; JS_ASSERT(script->code <= pc && pc < script->code + script->length); - if (pc < script->main) + if (pc < script->main()) goto do_fallback; if (spindex != JSDVG_IGNORE_STACK) { @@ -5121,7 +5736,8 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, cx->malloc_(StackDepth(script) * sizeof *pcstack); if (!pcstack) return NULL; - intN pcdepth = ReconstructPCStack(cx, script, pc, pcstack); + jsbytecode *lastDecomposedPC = NULL; + intN pcdepth = ReconstructPCStack(cx, script, pc, pcstack, &lastDecomposedPC); if (pcdepth < 0) goto release_pcstack; @@ -5154,9 +5770,21 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, * produced by the current pc. Since it takes a fairly contrived * combination of calls to produce a situation where this is not * what we want, we just use the current pc. + * + * If we are in the middle of a decomposed opcode, use the outer + * 'fat' opcode itself. Any source notes for the operation which + * are needed during decompilation will be associated with the + * outer opcode. */ - if (sp < stackBase + pcdepth) + if (sp < stackBase + pcdepth) { pc = pcstack[sp - stackBase]; + if (lastDecomposedPC) { + size_t len = GetDecomposeLength(lastDecomposedPC, + js_CodeSpec[*lastDecomposedPC].length); + if (unsigned(pc - lastDecomposedPC) < len) + pc = lastDecomposedPC; + } + } } release_pcstack: @@ -5166,28 +5794,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, } { - jsbytecode* basepc = cx->regs().pc; - jsbytecode* savedImacropc = fp->maybeImacropc(); - if (savedImacropc) { - cx->regs().pc = savedImacropc; - fp->clearImacropc(); - } - - /* - * FIXME: bug 489843. Stack reconstruction may have returned a pc - * value *inside* an imacro; this would confuse the decompiler. - */ - char *name; - if (savedImacropc && size_t(pc - script->code) >= script->length) - name = FAILED_EXPRESSION_DECOMPILER; - else - name = DecompileExpression(cx, script, fp->maybeFun(), pc); - - if (savedImacropc) { - cx->regs().pc = basepc; - fp->setImacropc(savedImacropc); - } - + char *name = DecompileExpression(cx, script, fp->maybeFun(), pc); if (name != FAILED_EXPRESSION_DECOMPILER) return name; } @@ -5202,47 +5809,26 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, const jschar *chars = fallback->getChars(cx); if (!chars) return NULL; - return js_DeflateString(cx, chars, length); + return DeflateString(cx, chars, length); } static char * DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *pc) { - JSOp op; - const JSCodeSpec *cs; - jsbytecode *begin, *end; - jssrcnote *sn; - ptrdiff_t len; - jsbytecode **pcstack; - intN pcdepth; - JSPrinter *jp; - char *name; - JS_ASSERT(script->code <= pc && pc < script->code + script->length); - pcstack = NULL; - AutoScriptUntrapper untrapper(cx, script, &pc); - op = (JSOp) *pc; + JSOp op = (JSOp) *pc; /* None of these stack-writing ops generates novel values. */ - JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && - op != JSOP_DUP && op != JSOP_DUP2); - - /* JSOP_PUSH is used to generate undefined for group assignment holes. */ - if (op == JSOP_PUSH) { - name = JS_strdup(cx, js_undefined_str); - goto out; - } + JS_ASSERT(op != JSOP_CASE && op != JSOP_DUP && op != JSOP_DUP2); /* * |this| could convert to a very long object initialiser, so cite it by * its keyword name instead. */ - if (op == JSOP_THIS) { - name = JS_strdup(cx, js_this_str); - goto out; - } + if (op == JSOP_THIS) + return JS_strdup(cx, js_this_str); /* * JSOP_BINDNAME is special: it generates a value, the base object of a @@ -5250,25 +5836,21 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, * js_DecompileValueGenerator, the name being bound is irrelevant. Just * fall back to the base object. */ - if (op == JSOP_BINDNAME) { - name = FAILED_EXPRESSION_DECOMPILER; - goto out; - } + if (op == JSOP_BINDNAME) + return FAILED_EXPRESSION_DECOMPILER; /* NAME ops are self-contained, others require left or right context. */ - cs = &js_CodeSpec[op]; - begin = pc; - end = pc + cs->length; + const JSCodeSpec *cs = &js_CodeSpec[op]; + jsbytecode *begin = pc; + jsbytecode *end = pc + cs->length; switch (JOF_MODE(cs->format)) { case JOF_PROP: case JOF_ELEM: case JOF_XMLNAME: - case 0: - sn = js_GetSrcNote(script, pc); - if (!sn) { - name = FAILED_EXPRESSION_DECOMPILER; - goto out; - } + case 0: { + jssrcnote *sn = js_GetSrcNote(script, pc); + if (!sn) + return FAILED_EXPRESSION_DECOMPILER; switch (SN_TYPE(sn)) { case SRC_PCBASE: begin -= js_GetSrcNoteOffset(sn, 0); @@ -5278,54 +5860,61 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, begin += cs->length; break; default: - name = FAILED_EXPRESSION_DECOMPILER; - goto out; + return FAILED_EXPRESSION_DECOMPILER; } break; + } default:; } - len = end - begin; - if (len <= 0) { - name = FAILED_EXPRESSION_DECOMPILER; - goto out; - } - pcstack = (jsbytecode **) - cx->malloc_(StackDepth(script) * sizeof *pcstack); - if (!pcstack) { - name = NULL; - goto out; + /* + * Include the trailing SWAP when decompiling CALLPROP or CALLELEM ops, + * so that the result is the entire access rather than the lvalue. + */ + if (op == JSOP_CALLPROP || op == JSOP_CALLELEM) { + JS_ASSERT(*end == JSOP_SWAP); + end += JSOP_SWAP_LENGTH; } - MUST_FLOW_THROUGH("out"); - pcdepth = ReconstructPCStack(cx, script, begin, pcstack); - if (pcdepth < 0) { - name = FAILED_EXPRESSION_DECOMPILER; - goto out; - } + ptrdiff_t len = end - begin; + if (len <= 0) + return FAILED_EXPRESSION_DECOMPILER; - name = NULL; - jp = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, - false, false, false); - if (jp) { - jp->dvgfence = end; - jp->pcstack = pcstack; - if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) { - name = (jp->sprinter.base) ? jp->sprinter.base : (char *) ""; - name = JS_strdup(cx, name); + struct Guard { + jsbytecode **pcstack; + JSPrinter *printer; + Guard() : pcstack(NULL), printer(NULL) {} + ~Guard() { + if (printer) + js_DestroyPrinter(printer); + Foreground::free_(pcstack); } - js_DestroyPrinter(jp); - } + } g; -out: - cx->free_(pcstack); - return name; + g.pcstack = (jsbytecode **)OffTheBooks::malloc_(StackDepth(script) * sizeof *g.pcstack); + if (!g.pcstack) + return NULL; + + intN pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack, NULL); + if (pcdepth < 0) + return FAILED_EXPRESSION_DECOMPILER; + + g.printer = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, false, false, false); + if (!g.printer) + return NULL; + + g.printer->dvgfence = end; + g.printer->pcstack = g.pcstack; + if (!DecompileCode(g.printer, script, begin, (uintN) len, (uintN) pcdepth)) + return NULL; + + return JS_strdup(cx, g.printer->sprinter.string()); } uintN js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc) { - return ReconstructPCStack(cx, script, pc, NULL); + return ReconstructPCStack(cx, script, pc, NULL, NULL); } #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1); @@ -5334,8 +5923,8 @@ static intN SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs, jsbytecode *pc, jsbytecode **pcstack, uintN &pcdepth) { - uintN nuses = js_GetStackUses(cs, op, pc); - uintN ndefs = js_GetStackDefs(cx, cs, op, script, pc); + uintN nuses = StackUses(script, pc); + uintN ndefs = StackDefs(script, pc); LOCAL_ASSERT(pcdepth >= nuses); pcdepth -= nuses; LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script)); @@ -5354,7 +5943,6 @@ SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs, break; case JSOP_CASE: - case JSOP_CASEX: /* Keep the switch value. */ JS_ASSERT(ndefs == 1); break; @@ -5386,98 +5974,9 @@ SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs, return pcdepth; } -#ifdef JS_TRACER - -#undef LOCAL_ASSERT -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure); - -static intN -SimulateImacroCFG(JSContext *cx, JSScript *script, - uintN pcdepth, jsbytecode *pc, jsbytecode *target, - jsbytecode **pcstack) -{ - size_t nbytes = 0; - jsbytecode** tmp_pcstack = NULL; - if (pcstack) { - nbytes = StackDepth(script) * sizeof *pcstack; - tmp_pcstack = (jsbytecode **) cx->malloc_(nbytes); - if (!tmp_pcstack) - return -1; - memcpy(tmp_pcstack, pcstack, nbytes); - } - - ptrdiff_t oplen; - for (; pc < target; pc += oplen) { - JSOp op = js_GetOpcode(cx, script, pc); - const JSCodeSpec *cs = &js_CodeSpec[op]; - oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(pc); - - if (SimulateOp(cx, script, op, cs, pc, tmp_pcstack, pcdepth) < 0) - goto failure; - - uint32 type = cs->format & JOF_TYPEMASK; - if (type == JOF_JUMP || type == JOF_JUMPX) { - ptrdiff_t jmpoff = (type == JOF_JUMP) ? GET_JUMP_OFFSET(pc) - : GET_JUMPX_OFFSET(pc); - LOCAL_ASSERT(jmpoff >= 0); - intN tmp_pcdepth = SimulateImacroCFG(cx, script, pcdepth, pc + jmpoff, - target, tmp_pcstack); - if (tmp_pcdepth >= 0) { - pcdepth = uintN(tmp_pcdepth); - goto success; - } - - if (op == JSOP_GOTO || op == JSOP_GOTOX) - goto failure; - } - } - - if (pc > target) - goto failure; - - LOCAL_ASSERT(pc == target); - - success: - if (tmp_pcstack) { - memcpy(pcstack, tmp_pcstack, nbytes); - cx->free_(tmp_pcstack); - } - return pcdepth; - - failure: - if (tmp_pcstack) - cx->free_(tmp_pcstack); - return -1; -} - -#undef LOCAL_ASSERT -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1); - -static intN -ReconstructImacroPCStack(JSContext *cx, JSScript *script, - jsbytecode *imacstart, jsbytecode *target, - jsbytecode **pcstack) -{ - /* - * Begin with a recursive call back to ReconstructPCStack to pick up - * the state-of-the-world at the *start* of the imacro. - */ - StackFrame *fp = js_GetScriptedCaller(cx, NULL); - JS_ASSERT(fp->hasImacropc()); - intN pcdepth = ReconstructPCStack(cx, script, fp->imacropc(), pcstack); - if (pcdepth < 0) - return pcdepth; - return SimulateImacroCFG(cx, script, pcdepth, imacstart, target, pcstack); -} - -extern jsbytecode* js_GetImacroStart(jsbytecode* pc); -#endif - static intN ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, - jsbytecode **pcstack) + jsbytecode **pcstack, jsbytecode **lastDecomposedPC) { /* * Walk forward from script->main and compute the stack depth and stack of @@ -5486,42 +5985,42 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. * FIXME: Optimize to use last empty-stack sequence point. */ -#ifdef JS_TRACER - jsbytecode *imacstart = js_GetImacroStart(target); - - if (imacstart) - return ReconstructImacroPCStack(cx, script, imacstart, target, pcstack); -#endif LOCAL_ASSERT(script->code <= target && target < script->code + script->length); jsbytecode *pc = script->code; uintN pcdepth = 0; ptrdiff_t oplen; for (; pc < target; pc += oplen) { - JSOp op = js_GetOpcode(cx, script, pc); + JSOp op = JSOp(*pc); const JSCodeSpec *cs = &js_CodeSpec[op]; oplen = cs->length; if (oplen < 0) oplen = js_GetVariableBytecodeLength(pc); + if (cs->format & JOF_DECOMPOSE) { + if (lastDecomposedPC) + *lastDecomposedPC = pc; + continue; + } + /* * A (C ? T : E) expression requires skipping either T (if target is in * E) or both T and E (if target is after the whole expression) before - * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that - * tests condition C. We know that the stack depth can't change from - * what it was with C on top of stack. + * adjusting pcdepth based on the JSOP_IFEQ at pc that tests condition + * C. We know that the stack depth can't change from what it was with + * C on top of stack. */ jssrcnote *sn = js_GetSrcNote(script, pc); if (sn && SN_TYPE(sn) == SRC_COND) { ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0); if (pc + jmpoff < target) { pc += jmpoff; - op = js_GetOpcode(cx, script, pc); - JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); + op = JSOp(*pc); + JS_ASSERT(op == JSOP_GOTO); cs = &js_CodeSpec[op]; oplen = cs->length; JS_ASSERT(oplen > 0); - ptrdiff_t jmplen = GetJumpOffset(pc, pc); + ptrdiff_t jmplen = GET_JUMP_OFFSET(pc); if (pc + jmplen < target) { oplen = (uintN) jmplen; continue; @@ -5559,13 +6058,10 @@ CallResultEscapes(jsbytecode *pc) /* * If we see any of these sequences, the result is unused: * - call / pop - * - call / trace / pop * * If we see any of these sequences, the result is only tested for nullness: * - call / ifeq - * - call / trace / ifeq * - call / not / ifeq - * - call / trace / not / ifeq */ if (*pc != JSOP_CALL) @@ -5573,9 +6069,6 @@ CallResultEscapes(jsbytecode *pc) pc += JSOP_CALL_LENGTH; - if (*pc == JSOP_TRACE) - pc += JSOP_TRACE_LENGTH; - if (*pc == JSOP_POP) return false; @@ -5585,4 +6078,316 @@ CallResultEscapes(jsbytecode *pc) return (*pc != JSOP_IFEQ); } +extern bool +IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset) +{ + // This could be faster (by following jump instructions if the target is <= offset). + for (BytecodeRange r(script); !r.empty(); r.popFront()) { + size_t here = r.frontOffset(); + if (here >= offset) + return here == offset; + } + return false; +} + +JS_FRIEND_API(size_t) +GetPCCountScriptCount(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (!rt->scriptPCCounters) + return 0; + + return rt->scriptPCCounters->length(); +} + +enum MaybeComma {NO_COMMA, COMMA}; + +static void +AppendJSONProperty(StringBuffer &buf, const char *name, MaybeComma comma = COMMA) +{ + if (comma) + buf.append(','); + + buf.append('\"'); + buf.appendInflated(name, strlen(name)); + buf.appendInflated("\":", 2); +} + +static void +AppendArrayJSONProperties(JSContext *cx, StringBuffer &buf, + double *values, const char **names, unsigned count, MaybeComma &comma) +{ + for (unsigned i = 0; i < count; i++) { + if (values[i]) { + AppendJSONProperty(buf, names[i], comma); + comma = COMMA; + NumberValueToStringBuffer(cx, DoubleValue(values[i]), buf); + } + } +} + +JS_FRIEND_API(JSString *) +GetPCCountScriptSummary(JSContext *cx, size_t index) +{ + JSRuntime *rt = cx->runtime; + + if (!rt->scriptPCCounters || index >= rt->scriptPCCounters->length()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); + return NULL; + } + + ScriptOpcodeCountsPair info = (*rt->scriptPCCounters)[index]; + JSScript *script = info.script; + + /* + * OOM on buffer appends here will not be caught immediately, but since + * StringBuffer uses a ContextAllocPolicy will trigger an exception on the + * context if they occur, which we'll catch before returning. + */ + StringBuffer buf(cx); + + buf.append('{'); + + AppendJSONProperty(buf, "file", NO_COMMA); + JSString *str = JS_NewStringCopyZ(cx, script->filename); + if (!str || !(str = JS_ValueToSource(cx, StringValue(str)))) + return NULL; + buf.append(str); + + AppendJSONProperty(buf, "line"); + NumberValueToStringBuffer(cx, Int32Value(script->lineno), buf); + + if (script->function()) { + JSAtom *atom = script->function()->atom; + if (atom) { + AppendJSONProperty(buf, "name"); + if (!(str = JS_ValueToSource(cx, StringValue(atom)))) + return NULL; + buf.append(str); + } + } + + double baseTotals[OpcodeCounts::BASE_COUNT] = {0.0}; + double accessTotals[OpcodeCounts::ACCESS_COUNT - OpcodeCounts::BASE_COUNT] = {0.0}; + double elementTotals[OpcodeCounts::ELEM_COUNT - OpcodeCounts::ACCESS_COUNT] = {0.0}; + double propertyTotals[OpcodeCounts::PROP_COUNT - OpcodeCounts::ACCESS_COUNT] = {0.0}; + double arithTotals[OpcodeCounts::ARITH_COUNT - OpcodeCounts::BASE_COUNT] = {0.0}; + + for (unsigned i = 0; i < script->length; i++) { + OpcodeCounts &counts = info.getCounts(script->code + i); + if (!counts) + continue; + + JSOp op = (JSOp)script->code[i]; + unsigned numCounts = OpcodeCounts::numCounts(op); + + for (unsigned j = 0; j < numCounts; j++) { + double value = counts.get(j); + if (j < OpcodeCounts::BASE_COUNT) { + baseTotals[j] += value; + } else if (OpcodeCounts::accessOp(op)) { + if (j < OpcodeCounts::ACCESS_COUNT) + accessTotals[j - OpcodeCounts::BASE_COUNT] += value; + else if (OpcodeCounts::elementOp(op)) + elementTotals[j - OpcodeCounts::ACCESS_COUNT] += value; + else if (OpcodeCounts::propertyOp(op)) + propertyTotals[j - OpcodeCounts::ACCESS_COUNT] += value; + else + JS_NOT_REACHED("Bad opcode"); + } else if (OpcodeCounts::arithOp(op)) { + arithTotals[j - OpcodeCounts::BASE_COUNT] += value; + } else { + JS_NOT_REACHED("Bad opcode"); + } + } + } + + AppendJSONProperty(buf, "totals"); + buf.append('{'); + + MaybeComma comma = NO_COMMA; + + AppendArrayJSONProperties(cx, buf, baseTotals, countBaseNames, + JS_ARRAY_LENGTH(baseTotals), comma); + AppendArrayJSONProperties(cx, buf, accessTotals, countAccessNames, + JS_ARRAY_LENGTH(accessTotals), comma); + AppendArrayJSONProperties(cx, buf, elementTotals, countElementNames, + JS_ARRAY_LENGTH(elementTotals), comma); + AppendArrayJSONProperties(cx, buf, propertyTotals, countPropertyNames, + JS_ARRAY_LENGTH(propertyTotals), comma); + AppendArrayJSONProperties(cx, buf, arithTotals, countArithNames, + JS_ARRAY_LENGTH(arithTotals), comma); + + buf.append('}'); + buf.append('}'); + + if (cx->isExceptionPending()) + return NULL; + + return buf.finishString(); +} + +struct AutoDestroyPrinter +{ + JSPrinter *jp; + AutoDestroyPrinter(JSPrinter *jp) : jp(jp) {} + ~AutoDestroyPrinter() { js_DestroyPrinter(jp); } +}; + +static bool +GetPCCountJSON(JSContext *cx, const ScriptOpcodeCountsPair &info, StringBuffer &buf) +{ + JSScript *script = info.script; + + buf.append('{'); + AppendJSONProperty(buf, "text", NO_COMMA); + + Vector decompiledOpcodes(cx); + if (!decompiledOpcodes.reserve(script->length)) + return false; + + for (unsigned i = 0; i < script->length; i++) + decompiledOpcodes.infallibleAppend(DecompiledOpcode()); + + JSFunction *fun = script->function(); + JSPrinter *jp = js_NewPrinter(cx, "", fun, 4, true, false, false); + if (!jp) + return false; + AutoDestroyPrinter destroy(jp); + + jp->decompiledOpcodes = &decompiledOpcodes; + + if (fun) { + if (!js_DecompileFunction(jp)) + return false; + } else { + if (!js_DecompileScript(jp, script)) + return false; + } + JSString *str = js_GetPrinterOutput(jp); + if (!str || !(str = JS_ValueToSource(cx, StringValue(str)))) + return false; + + buf.append(str); + + AppendJSONProperty(buf, "opcodes"); + buf.append('['); + bool comma = false; + + SrcNoteLineScanner scanner(script->notes(), script->lineno); + + for (jsbytecode *pc = script->code; + pc < script->code + script->length; + pc += GetBytecodeLength(pc)) + { + size_t offset = pc - script->code; + + JSOp op = (JSOp) *pc; + + if (comma) + buf.append(','); + comma = true; + + buf.append('{'); + + AppendJSONProperty(buf, "id", NO_COMMA); + NumberValueToStringBuffer(cx, Int32Value(pc - script->code), buf); + + scanner.advanceTo(offset); + + AppendJSONProperty(buf, "line"); + NumberValueToStringBuffer(cx, Int32Value(scanner.getLine()), buf); + + { + const char *name = js_CodeName[op]; + AppendJSONProperty(buf, "name"); + buf.append('\"'); + buf.appendInflated(name, strlen(name)); + buf.append('\"'); + } + + DecompiledOpcode *search = &decompiledOpcodes[offset]; + size_t textBias = 0; + while (search->parent) { + textBias += search->parentOffset; + if (search->parenthesized) + textBias++; + search = &decompiledOpcodes[search->parent - script->code]; + } + + int32_t printedOffset = search->parentOffset; + if (printedOffset != -1) { + printedOffset += textBias; + if (search->parenthesized) + printedOffset++; + AppendJSONProperty(buf, "textOffset"); + NumberValueToStringBuffer(cx, Int32Value(printedOffset), buf); + } + + const char *text = decompiledOpcodes[offset].text; + if (text && *text != 0) { + AppendJSONProperty(buf, "text"); + JSString *str = JS_NewStringCopyZ(cx, text); + if (!str || !(str = JS_ValueToSource(cx, StringValue(str)))) + return false; + buf.append(str); + } + + OpcodeCounts &counts = info.getCounts(pc); + unsigned numCounts = OpcodeCounts::numCounts(op); + + AppendJSONProperty(buf, "counts"); + buf.append('{'); + + MaybeComma comma = NO_COMMA; + for (unsigned i = 0; i < numCounts; i++) { + double value = counts.get(i); + if (value > 0) { + AppendJSONProperty(buf, OpcodeCounts::countName(op, i), comma); + comma = COMMA; + NumberValueToStringBuffer(cx, DoubleValue(value), buf); + } + } + + buf.append('}'); + buf.append('}'); + } + + buf.append(']'); + buf.append('}'); + + return !cx->isExceptionPending(); +} + +JS_FRIEND_API(JSString *) +GetPCCountScriptContents(JSContext *cx, size_t index) +{ + JSRuntime *rt = cx->runtime; + + if (!rt->scriptPCCounters || index >= rt->scriptPCCounters->length()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); + return NULL; + } + + const ScriptOpcodeCountsPair &info = (*rt->scriptPCCounters)[index]; + JSScript *script = info.script; + + StringBuffer buf(cx); + + if (!script->function() && !script->compileAndGo) + return buf.finishString(); + + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, script->function() ? (JSObject *) script->function() : script->global())) + return NULL; + + if (!GetPCCountJSON(cx, info, buf)) + return NULL; + } + + return buf.finishString(); +} + } // namespace js diff --git a/deps/mozjs/js/src/jsopcode.h b/deps/mozjs/js/src/jsopcode.h index 49535b32660..b446324dc11 100644 --- a/deps/mozjs/js/src/jsopcode.h +++ b/deps/mozjs/js/src/jsopcode.h @@ -39,6 +39,7 @@ #ifndef jsopcode_h___ #define jsopcode_h___ + /* * JS bytecode definitions. */ @@ -46,11 +47,6 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsutil.h" -#include "jsarena.h" - -#ifdef __cplusplus -# include "jsvalue.h" -#endif JS_BEGIN_EXTERN_C @@ -65,13 +61,14 @@ typedef enum JSOp { JSOP_LIMIT, /* - * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, - * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in - * bytecode, so they don't preempt valid opcodes. + * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETPROP, + * JSOP_SETELEM, and comprehension-tails, respectively. They are never + * stored in bytecode, so they don't preempt valid opcodes. */ JSOP_GETPROP2 = JSOP_LIMIT, JSOP_GETELEM2 = JSOP_LIMIT + 1, - JSOP_FAKE_LIMIT = JSOP_GETELEM2 + JSOP_FORLOCAL = JSOP_LIMIT + 2, + JSOP_FAKE_LIMIT = JSOP_FORLOCAL } JSOp; /* @@ -85,21 +82,17 @@ typedef enum JSOp { #define JOF_LOOKUPSWITCH 5 /* lookup switch */ #define JOF_QARG 6 /* quickened get/set function argument ops */ #define JOF_LOCAL 7 /* var or block-local variable */ -#define JOF_SLOTATOM 8 /* uint16 slot + constant index */ -#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ -#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ -#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ +/* 8 is unused */ #define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ -#define JOF_UINT8 13 /* uint8 immediate, e.g. top 8 bits of 24-bit +#define JOF_UINT8 13 /* uint8_t immediate, e.g. top 8 bits of 24-bit atom index */ -#define JOF_INT32 14 /* int32 immediate operand */ +#define JOF_INT32 14 /* int32_t immediate operand */ #define JOF_OBJECT 15 /* unsigned 16-bit object index */ -#define JOF_SLOTOBJECT 16 /* uint16 slot index + object index */ -#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */ -#define JOF_INT8 18 /* int8 immediate operand */ -#define JOF_ATOMOBJECT 19 /* uint16 constant index + object index */ -#define JOF_UINT16PAIR 20 /* pair of uint16 immediates */ -#define JOF_GLOBAL 21 /* uint16 global array index */ +#define JOF_SLOTOBJECT 16 /* uint16_t slot index + object index */ +#define JOF_REGEXP 17 /* unsigned 32-bit regexp index */ +#define JOF_INT8 18 /* int8_t immediate operand */ +#define JOF_ATOMOBJECT 19 /* uint16_t constant index + object index */ +#define JOF_UINT16PAIR 20 /* pair of uint16_t immediates */ #define JOF_TYPEMASK 0x001f /* mask for above immediate types */ #define JOF_NAME (1U<<5) /* name operation */ @@ -114,7 +107,6 @@ typedef enum JSOp { #define JOF_INC (2U<<10) /* increment (++, not --) opcode */ #define JOF_INCDEC (3U<<10) /* increment or decrement opcode */ #define JOF_POST (1U<<12) /* postorder increment or decrement */ -#define JOF_FOR (1U<<13) /* for-in property op (akin to JOF_SET) */ #define JOF_ASSIGNING JOF_SET /* hint for Class.resolve, used for ops that do simplex assignment */ #define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */ @@ -122,8 +114,6 @@ typedef enum JSOp { #define JOF_LEFTASSOC (1U<<16) /* left-associative operator */ #define JOF_DECLARING (1U<<17) /* var, const, or function declaration op */ #define JOF_INDEXBASE (1U<<18) /* atom segment base setting prefix op */ -#define JOF_CALLOP (1U<<19) /* call operation that pushes function and - this */ #define JOF_PARENHEAD (1U<<20) /* opcode consumes value of expression in parenthesized statement head */ #define JOF_INVOKE (1U<<21) /* JSOP_CALL, JSOP_NEW, JSOP_EVAL */ @@ -137,10 +127,12 @@ typedef enum JSOp { #define JOF_TMPSLOT_SHIFT 22 #define JOF_TMPSLOT_MASK (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT) -#define JOF_SHARPSLOT (1U<<24) /* first immediate is uint16 stack slot no. - that needs fixup when in global code (see - Compiler::compileScript) */ +/* (1U<<24) is unused */ #define JOF_GNAME (1U<<25) /* predicted global name */ +#define JOF_TYPESET (1U<<26) /* has an entry in a script's type sets */ +#define JOF_DECOMPOSE (1U<<27) /* followed by an equivalent decomposed + * version of the opcode */ +#define JOF_ARITH (1U<<28) /* unary or binary arithmetic opcode */ /* Shorthands for type from format and type from opcode. */ #define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK) @@ -151,13 +143,25 @@ typedef enum JSOp { #define JOF_OPMODE(op) JOF_MODE(js_CodeSpec[op].format) #define JOF_TYPE_IS_EXTENDED_JUMP(t) \ - ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) + ((unsigned)((t) - JOF_JUMP) <= (unsigned)(JOF_LOOKUPSWITCH - JOF_JUMP)) /* * Immediate operand getters, setters, and bounds. */ -/* Common uint16 immediate format helpers. */ +static JS_ALWAYS_INLINE uint8_t +GET_UINT8(jsbytecode *pc) +{ + return (uint8_t) pc[1]; +} + +static JS_ALWAYS_INLINE void +SET_UINT8(jsbytecode *pc, uint8_t u) +{ + pc[1] = (jsbytecode) u; +} + +/* Common uint16_t immediate format helpers. */ #define UINT16_LEN 2 #define UINT16_HI(i) ((jsbytecode)((i) >> 8)) #define UINT16_LO(i) ((jsbytecode)(i)) @@ -165,54 +169,48 @@ typedef enum JSOp { #define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) #define UINT16_LIMIT ((uintN)1 << 16) -/* Short (2-byte signed offset) relative jump macros. */ -#define JUMP_OFFSET_LEN 2 -#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) -#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) -#define GET_JUMP_OFFSET(pc) ((int16)GET_UINT16(pc)) -#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off), \ - (pc)[2] = JUMP_OFFSET_LO(off)) -#define JUMP_OFFSET_MIN ((int16)0x8000) -#define JUMP_OFFSET_MAX ((int16)0x7fff) +/* Helpers for accessing the offsets of jump opcodes. */ +#define JUMP_OFFSET_LEN 4 +#define JUMP_OFFSET_MIN INT32_MIN +#define JUMP_OFFSET_MAX INT32_MAX -/* - * When a short jump won't hold a relative offset, its 2-byte immediate offset - * operand is an unsigned index of a span-dependency record, maintained until - * code generation finishes -- after which some (but we hope not nearly all) - * span-dependent jumps must be extended (see OptimizeSpanDeps in jsemit.c). - * - * If the span-dependency record index overflows SPANDEP_INDEX_MAX, the jump - * offset will contain SPANDEP_INDEX_HUGE, indicating that the record must be - * found (via binary search) by its "before span-dependency optimization" pc - * offset (from script main entry point). - */ -#define GET_SPANDEP_INDEX(pc) ((uint16)GET_UINT16(pc)) -#define SET_SPANDEP_INDEX(pc,i) ((pc)[1] = JUMP_OFFSET_HI(i), \ - (pc)[2] = JUMP_OFFSET_LO(i)) -#define SPANDEP_INDEX_MAX ((uint16)0xfffe) -#define SPANDEP_INDEX_HUGE ((uint16)0xffff) - -/* Ultimately, if short jumps won't do, emit long (4-byte signed) offsets. */ -#define JUMPX_OFFSET_LEN 4 -#define JUMPX_OFFSET_B3(off) ((jsbytecode)((off) >> 24)) -#define JUMPX_OFFSET_B2(off) ((jsbytecode)((off) >> 16)) -#define JUMPX_OFFSET_B1(off) ((jsbytecode)((off) >> 8)) -#define JUMPX_OFFSET_B0(off) ((jsbytecode)(off)) -#define GET_JUMPX_OFFSET(pc) ((int32)(((pc)[1] << 24) | ((pc)[2] << 16) \ - | ((pc)[3] << 8) | (pc)[4])) -#define SET_JUMPX_OFFSET(pc,off)((pc)[1] = JUMPX_OFFSET_B3(off), \ - (pc)[2] = JUMPX_OFFSET_B2(off), \ - (pc)[3] = JUMPX_OFFSET_B1(off), \ - (pc)[4] = JUMPX_OFFSET_B0(off)) -#define JUMPX_OFFSET_MIN ((int32)0x80000000) -#define JUMPX_OFFSET_MAX ((int32)0x7fffffff) +static JS_ALWAYS_INLINE int32_t +GET_JUMP_OFFSET(jsbytecode *pc) +{ + return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4]; +} + +static JS_ALWAYS_INLINE void +SET_JUMP_OFFSET(jsbytecode *pc, int32_t off) +{ + pc[1] = (jsbytecode)(off >> 24); + pc[2] = (jsbytecode)(off >> 16); + pc[3] = (jsbytecode)(off >> 8); + pc[4] = (jsbytecode)off; +} + +#define UINT32_INDEX_LEN 4 + +static JS_ALWAYS_INLINE uint32_t +GET_UINT32_INDEX(jsbytecode *pc) +{ + return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4]; +} + +static JS_ALWAYS_INLINE void +SET_UINT32_INDEX(jsbytecode *pc, uint32_t index) +{ + pc[1] = (jsbytecode)(index >> 24); + pc[2] = (jsbytecode)(index >> 16); + pc[3] = (jsbytecode)(index >> 8); + pc[4] = (jsbytecode)index; +} /* - * A literal is indexed by a per-script atom or object maps. Most scripts - * have relatively few literals, so the standard JOF_ATOM, JOF_OBJECT and - * JOF_REGEXP formats specifies a fixed 16 bits of immediate operand index. - * A script with more than 64K literals must wrap the bytecode into - * JSOP_INDEXBASE and JSOP_RESETBASE pair. + * A literal is indexed by a per-script atom map. Most scripts have relatively + * few literals, so the standard JOF_ATOM format specifies a fixed 16 bits of + * immediate operand index. A script with more than 64K literals must wrap the + * bytecode into an JSOP_INDEXBASE and JSOP_RESETBASE pair. */ #define INDEX_LEN 2 #define INDEX_HI(i) ((jsbytecode)((i) >> 8)) @@ -234,20 +232,20 @@ typedef enum JSOp { (pc)[2] = UINT24_MID(i), \ (pc)[3] = UINT24_LO(i)) -#define GET_INT8(pc) ((jsint)(int8)(pc)[1]) +#define GET_INT8(pc) ((jsint)int8_t((pc)[1])) -#define GET_INT32(pc) ((jsint)(((uint32)((pc)[1]) << 24) | \ - ((uint32)((pc)[2]) << 16) | \ - ((uint32)((pc)[3]) << 8) | \ - (uint32)(pc)[4])) -#define SET_INT32(pc,i) ((pc)[1] = (jsbytecode)((uint32)(i) >> 24), \ - (pc)[2] = (jsbytecode)((uint32)(i) >> 16), \ - (pc)[3] = (jsbytecode)((uint32)(i) >> 8), \ - (pc)[4] = (jsbytecode)(uint32)(i)) +#define GET_INT32(pc) ((jsint)((uint32_t((pc)[1]) << 24) | \ + (uint32_t((pc)[2]) << 16) | \ + (uint32_t((pc)[3]) << 8) | \ + uint32_t((pc)[4]))) +#define SET_INT32(pc,i) ((pc)[1] = (jsbytecode)(uint32_t(i) >> 24), \ + (pc)[2] = (jsbytecode)(uint32_t(i) >> 16), \ + (pc)[3] = (jsbytecode)(uint32_t(i) >> 8), \ + (pc)[4] = (jsbytecode)uint32_t(i)) -/* Index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ +/* Index limit is determined by SN_3BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */ #define INDEX_LIMIT_LOG2 23 -#define INDEX_LIMIT ((uint32)1 << INDEX_LIMIT_LOG2) +#define INDEX_LIMIT (uint32_t(1) << INDEX_LIMIT_LOG2) /* Actual argument count operand format helpers. */ #define ARGC_HI(argc) UINT16_HI(argc) @@ -267,15 +265,13 @@ typedef enum JSOp { #define SLOTNO_LIMIT UINT16_LIMIT struct JSCodeSpec { - int8 length; /* length including opcode byte */ - int8 nuses; /* arity, -1 if variadic */ - int8 ndefs; /* number of stack results */ - uint8 prec; /* operator precedence */ - uint32 format; /* immediate operand format */ - -#ifdef __cplusplus - uint32 type() const { return JOF_TYPE(format); } -#endif + int8_t length; /* length including opcode byte */ + int8_t nuses; /* arity, -1 if variadic */ + int8_t ndefs; /* number of stack results */ + uint8_t prec; /* operator precedence */ + uint32_t format; /* immediate operand format */ + + uint32_t type() const { return JOF_TYPE(format); } }; extern const JSCodeSpec js_CodeSpec[]; @@ -332,8 +328,7 @@ js_puts(JSPrinter *jp, const char *s); * lexical environments. */ uintN -js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, - ptrdiff_t pcoff); +js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff); /* * A slower version of GET_ATOM when the caller does not want to maintain @@ -342,88 +337,35 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, #define GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom) \ JS_BEGIN_MACRO \ JS_ASSERT(*(pc) != JSOP_DOUBLE); \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ - JS_GET_SCRIPT_ATOM(script, pc, index_, atom); \ - JS_END_MACRO - -/* - * Variant for getting a double atom when we might be in an imacro. Bytecodes - * with literals that are only ever doubles must use this macro, and never use - * GET_ATOM_FROM_BYTECODE or JS_GET_SCRIPT_ATOM. - * - * Unfortunately some bytecodes such as JSOP_LOOKUPSWITCH have immediates that - * might be string or double atoms. Those opcodes cannot be used from imacros. - * See the assertions in the JSOP_DOUBLE and JSOP_LOOKUPSWTICH* opcode cases in - * jsinterp.cpp. - */ -#define GET_DOUBLE_FROM_BYTECODE(script, pc, pcoff, dbl) \ - JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ - JS_ASSERT(index_ < (script)->consts()->length); \ - (dbl) = (script)->getConst(index_).toDouble(); \ + JS_ASSERT(js_CodeSpec[*(pc)].format & JOF_ATOM); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ + (atom) = (script)->getAtom(index_); \ JS_END_MACRO -#define GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj) \ +#define GET_NAME_FROM_BYTECODE(script, pc, pcoff, name) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ - obj = (script)->getObject(index_); \ + JSAtom *atom_; \ + GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom_); \ + JS_ASSERT(js_CodeSpec[*(pc)].format & (JOF_NAME | JOF_PROP)); \ + (name) = atom_->asPropertyName(); \ JS_END_MACRO -#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \ +#define GET_DOUBLE_FROM_BYTECODE(script, pc, pcoff, dbl) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ - fun = (script)->getFunction(index_); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ + JS_ASSERT(index_ < (script)->consts()->length); \ + (dbl) = (script)->getConst(index_).toDouble(); \ JS_END_MACRO -#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \ - JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ - obj = (script)->getRegExp(index_); \ - JS_END_MACRO +namespace js { -/* - * Get the length of variable-length bytecode like JSOP_TABLESWITCH. - */ extern uintN -js_GetVariableBytecodeLength(jsbytecode *pc); +StackUses(JSScript *script, jsbytecode *pc); -/* - * Find the number of stack slots used by a variadic opcode such as JSOP_CALL - * (for such ops, JSCodeSpec.nuses is -1). - */ extern uintN -js_GetVariableStackUses(JSOp op, jsbytecode *pc); +StackDefs(JSScript *script, jsbytecode *pc); -/* - * Find the number of stack slots defined by JSOP_ENTERBLOCK (for this op, - * JSCodeSpec.ndefs is -1). - */ -extern uintN -js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc); - -#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */ -static JS_INLINE uintN -js_GetStackUses(const JSCodeSpec *cs, JSOp op, jsbytecode *pc) -{ - JS_ASSERT(cs == &js_CodeSpec[op]); - if (cs->nuses >= 0) - return cs->nuses; - return js_GetVariableStackUses(op, pc); -} - -static JS_INLINE uintN -js_GetStackDefs(JSContext *cx, const JSCodeSpec *cs, JSOp op, JSScript *script, - jsbytecode *pc) -{ - JS_ASSERT(cs == &js_CodeSpec[op]); - if (cs->ndefs >= 0) - return cs->ndefs; - - /* Only JSOP_ENTERBLOCK has a variable number of stack defs. */ - JS_ASSERT(op == JSOP_ENTERBLOCK); - return js_GetEnterBlockStackDefs(cx, script, pc); -} -#endif +} /* namespace js */ /* * Decompilers, for script, function, and expression pretty-printing. @@ -467,51 +409,115 @@ extern char * js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSString *fallback); +/* + * Given bytecode address pc in script's main program code, return the operand + * stack depth just before (JSOp) *pc executes. + */ +extern uintN +js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +JS_END_EXTERN_C + #define JSDVG_IGNORE_STACK 0 #define JSDVG_SEARCH_STACK 1 -#ifdef __cplusplus +/* + * Get the length of variable-length bytecode like JSOP_TABLESWITCH. + */ +extern size_t +js_GetVariableBytecodeLength(jsbytecode *pc); + namespace js { static inline char * DecompileValueGenerator(JSContext *cx, intN spindex, const Value &v, JSString *fallback) { - return js_DecompileValueGenerator(cx, spindex, Jsvalify(v), fallback); + return js_DecompileValueGenerator(cx, spindex, v, fallback); } /* * Sprintf, but with unlimited and automatically allocated buffering. */ -struct Sprinter { - JSContext *context; /* context executing the decompiler */ - JSArenaPool *pool; /* string allocation pool */ - char *base; /* base address of buffer in pool */ - size_t size; /* size of buffer allocated at base */ - ptrdiff_t offset; /* offset of next free char in buffer */ -}; +class Sprinter +{ + public: + struct InvariantChecker + { + const Sprinter *parent; -#define INIT_SPRINTER(cx, sp, ap, off) \ - ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ - (sp)->offset = off) + explicit InvariantChecker(const Sprinter *p) : parent(p) { + parent->checkInvariants(); + } -/* - * Attempt to reserve len space in sp (including a trailing NULL byte). If the - * attempt succeeds, return a pointer to the start of that space and adjust the - * length of sp's contents. The caller *must* completely fill this space - * (including the space for the trailing NULL byte) on success. - */ -extern char * -SprintReserveAmount(Sprinter *sp, size_t len); + ~InvariantChecker() { + parent->checkInvariants(); + } + }; -extern ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len); + JSContext *context; /* context executing the decompiler */ -extern ptrdiff_t -SprintCString(Sprinter *sp, const char *s); + private: + static const size_t DefaultSize; +#ifdef DEBUG + bool initialized; /* true if this is initialized, use for debug builds */ +#endif + char *base; /* malloc'd buffer address */ + size_t size; /* size of buffer allocated at base */ + ptrdiff_t offset; /* offset of next free char in buffer */ -extern ptrdiff_t -SprintString(Sprinter *sp, JSString *str); + bool realloc_(size_t newSize); + + public: + explicit Sprinter(JSContext *cx); + ~Sprinter(); + + /* Initialize this sprinter, returns false on error */ + bool init(); + + void checkInvariants() const; + + const char *string() const; + const char *stringEnd() const; + /* Returns the string at offset |off| */ + char *stringAt(ptrdiff_t off) const; + /* Returns the char at offset |off| */ + char &operator[](size_t off); + /* Test if this Sprinter is empty */ + bool empty() const; + + /* + * Attempt to reserve len + 1 space (for a trailing NULL byte). If the + * attempt succeeds, return a pointer to the start of that space and adjust the + * internal content. The caller *must* completely fill this space on success. + */ + char *reserve(size_t len); + /* Like reserve, but memory is initialized to 0 */ + char *reserveAndClear(size_t len); + + /* + * Puts |len| characters from |s| at the current position and return an offset to + * the beginning of this new data + */ + ptrdiff_t put(const char *s, size_t len); + ptrdiff_t put(const char *s); + ptrdiff_t putString(JSString *str); + + /* Prints a formatted string into the buffer */ + int printf(const char *fmt, ...); + + /* Change the offset */ + void setOffset(const char *end); + void setOffset(ptrdiff_t off); + + /* Get the offset */ + ptrdiff_t getOffset() const; + ptrdiff_t getOffsetOf(const char *string) const; +}; extern ptrdiff_t Sprint(Sprinter *sp, const char *format, ...); @@ -519,35 +525,183 @@ Sprint(Sprinter *sp, const char *format, ...); extern bool CallResultEscapes(jsbytecode *pc); +static inline uintN +GetDecomposeLength(jsbytecode *pc, size_t len) +{ + /* + * The last byte of a DECOMPOSE op stores the decomposed length. This can + * vary across different instances of an opcode due to INDEXBASE ops. + */ + JS_ASSERT(size_t(js_CodeSpec[*pc].length) == len); + return (uintN) pc[len - 1]; +} + +static inline uintN +GetBytecodeLength(jsbytecode *pc) +{ + JSOp op = (JSOp)*pc; + JS_ASSERT(op < JSOP_LIMIT); + + if (js_CodeSpec[op].length != -1) + return js_CodeSpec[op].length; + return js_GetVariableBytecodeLength(pc); } -#endif +extern bool +IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset); + +inline bool +FlowsIntoNext(JSOp op) +{ + /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ + return op != JSOP_STOP && op != JSOP_RETURN && op != JSOP_RETRVAL && op != JSOP_THROW && + op != JSOP_GOTO && op != JSOP_RETSUB; +} + +/* + * Counts accumulated for a single opcode in a script. The counts tracked vary + * between opcodes, and this structure ensures that counts are accessed in + * a coherent fashion. + */ +class OpcodeCounts +{ + friend struct ::JSScript; + double *counts; #ifdef DEBUG -#ifdef __cplusplus + size_t capacity; +#endif + + public: + + enum BaseCounts { + BASE_INTERP = 0, + BASE_METHODJIT, + + BASE_METHODJIT_STUBS, + BASE_METHODJIT_CODE, + BASE_METHODJIT_PICS, + + BASE_COUNT + }; + + enum AccessCounts { + ACCESS_MONOMORPHIC = BASE_COUNT, + ACCESS_DIMORPHIC, + ACCESS_POLYMORPHIC, + + ACCESS_BARRIER, + ACCESS_NOBARRIER, + + ACCESS_UNDEFINED, + ACCESS_NULL, + ACCESS_BOOLEAN, + ACCESS_INT32, + ACCESS_DOUBLE, + ACCESS_STRING, + ACCESS_OBJECT, + + ACCESS_COUNT + }; + + static bool accessOp(JSOp op) { + /* + * Access ops include all name, element and property reads, as well as + * SETELEM and SETPROP (for ElementCounts/PropertyCounts alignment). + */ + if (op == JSOP_SETELEM || op == JSOP_SETPROP || op == JSOP_SETMETHOD) + return true; + int format = js_CodeSpec[op].format; + return !!(format & (JOF_NAME | JOF_GNAME | JOF_ELEM | JOF_PROP)) + && !(format & (JOF_SET | JOF_INCDEC)); + } + + enum ElementCounts { + ELEM_ID_INT = ACCESS_COUNT, + ELEM_ID_DOUBLE, + ELEM_ID_OTHER, + ELEM_ID_UNKNOWN, + + ELEM_OBJECT_TYPED, + ELEM_OBJECT_PACKED, + ELEM_OBJECT_DENSE, + ELEM_OBJECT_OTHER, + + ELEM_COUNT + }; + + static bool elementOp(JSOp op) { + return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_ELEM); + } + + enum PropertyCounts { + PROP_STATIC = ACCESS_COUNT, + PROP_DEFINITE, + PROP_OTHER, + + PROP_COUNT + }; + + static bool propertyOp(JSOp op) { + return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_PROP); + } + + enum ArithCounts { + ARITH_INT = BASE_COUNT, + ARITH_DOUBLE, + ARITH_OTHER, + ARITH_UNKNOWN, + + ARITH_COUNT + }; + + static bool arithOp(JSOp op) { + return !!(js_CodeSpec[op].format & (JOF_INCDEC | JOF_ARITH)); + } + + static size_t numCounts(JSOp op) + { + if (accessOp(op)) { + if (elementOp(op)) + return ELEM_COUNT; + if (propertyOp(op)) + return PROP_COUNT; + return ACCESS_COUNT; + } + if (arithOp(op)) + return ARITH_COUNT; + return BASE_COUNT; + } + + static const char *countName(JSOp op, size_t which); + + double *rawCounts() { return counts; } + + double& get(size_t which) { + JS_ASSERT(which < capacity); + return counts[which]; + } + + /* Boolean conversion, for 'if (counters) ...' */ + operator void*() const { + return counts; + } +}; + +} /* namespace js */ + +#if defined(DEBUG) /* * Disassemblers, for debugging only. */ -#include extern JS_FRIEND_API(JSBool) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, js::Sprinter *sp); extern JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, js::Sprinter *sp); -#endif /* __cplusplus */ -#endif /* DEBUG */ - -/* - * Given bytecode address pc in script's main program code, return the operand - * stack depth just before (JSOp) *pc executes. - */ -extern uintN -js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc); -#ifdef _MSC_VER -#pragma warning(pop) +extern JS_FRIEND_API(void) +js_DumpPCCounts(JSContext *cx, JSScript *script, js::Sprinter *sp); #endif -JS_END_EXTERN_C - #endif /* jsopcode_h___ */ diff --git a/deps/mozjs/js/src/jsopcode.tbl b/deps/mozjs/js/src/jsopcode.tbl index 6071482b61f..534e59f226f 100644 --- a/deps/mozjs/js/src/jsopcode.tbl +++ b/deps/mozjs/js/src/jsopcode.tbl @@ -70,9 +70,9 @@ * 2 , JSOP_POP with SRC_PCDELTA, JSOP_RETURN * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_SET); * let (...) ... and JSOP_LEAVEBLOCKEXPR - * 4 ?: JSOP_IFEQ, JSOP_IFEQX - * 5 || JSOP_OR, JSOP_ORX - * 6 && JSOP_AND, JSOP_ANDX + * 4 ?: JSOP_IFEQ + * 5 || JSOP_OR + * 6 && JSOP_AND * 7 | JSOP_BITOR * 8 ^ JSOP_BITXOR * 9 & JSOP_BITAND @@ -113,73 +113,72 @@ OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) /* Long-standing JavaScript bytecodes. */ -OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) +OPDEF(JSOP_UNDEFINED, 1, js_undefined_str, "", 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD) OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 2, JOF_BYTE) -OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_PARENHEAD) +OPDEF(JSOP_GOTO, 6, "goto", NULL, 5, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 5, 1, 0, 4, JOF_JUMP|JOF_DETECTING) +OPDEF(JSOP_IFNE, 8, "ifne", NULL, 5, 1, 0, 0, JOF_JUMP|JOF_PARENHEAD) /* Get the arguments object for the current, lightweight function activation. */ OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) -/* ECMA-compliant for-in loop with argument or local loop control. */ -OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 1, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR|JOF_TMPSLOT) -OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 1, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT) +OPDEF(JSOP_SWAP, 10, "swap", NULL, 1, 2, 2, 0, JOF_BYTE) +OPDEF(JSOP_POPN, 11, "popn", NULL, 3, -1, 0, 0, JOF_UINT16) /* More long-standing bytecodes. */ OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET) -OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_POS, 35, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) +OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH|JOF_DETECTING) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH|JOF_DETECTING) +OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_ARITH|JOF_DETECTING) +OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE|JOF_ARITH) +OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE|JOF_ARITH) +OPDEF(JSOP_POS, 35, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE|JOF_ARITH) OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEL) OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEL) OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEL) OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3) -OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT3) -OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_TMPSLOT2) -OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3) -OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_TMPSLOT3) -OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_TMPSLOT2) -OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST|JOF_TMPSLOT2) -OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT2) - -OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP) +OPDEF(JSOP_INCNAME, 41, "incname", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 4, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 2, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_TMPSLOT2|JOF_DECOMPOSE) +OPDEF(JSOP_DECNAME, 44, "decname", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 4, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 2, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_TMPSLOT2|JOF_DECOMPOSE) +OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 4, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 2, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST|JOF_TMPSLOT2|JOF_DECOMPOSE) +OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 4, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE) +OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 2, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT2|JOF_DECOMPOSE) + +OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET) OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) +OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP) -OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) -OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME) +OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET) +OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) +OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET) OPDEF(JSOP_DOUBLE, 60, "double", NULL, 3, 0, 1, 16, JOF_ATOM) OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_ATOM) OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) @@ -188,16 +187,16 @@ OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) -OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_OR, 68, "or", NULL, 5, 1, 1, 5, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_AND, 69, "and", NULL, 5, 1, 1, 6, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) /* The switch bytecodes have variable length. */ -OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD) +OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD) OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD) /* New, infallible/transitive identity ops. */ -OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) -OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH) +OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH) /* * Host object extension: given 'o.item(i) = j', the left-hand side compiles @@ -207,34 +206,33 @@ OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 1, 1, 2, 18, JOF_BYTE) /* * JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits - * in this op's uint8 immediate operand. It replaces the top of stack value + * in this op's uint8_t immediate operand. It replaces the top of stack value * with an iterator for that value. * * JSOP_MOREITER stores the next iterated value into cx->iterValue and pushes * true if another value is available, and false otherwise. It is followed - * immediately by JSOP_IFNE{,X}. + * immediately by JSOP_IFNE. * * JSOP_ENDITER cleans up after the loop. It uses the slot above the iterator * for temporary GC rooting. */ OPDEF(JSOP_ITER, 75, "iter", NULL, 2, 1, 1, 0, JOF_UINT8) OPDEF(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 77, "enditer", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ITERNEXT, 77, "iternext", "", 2, 0, 1, 0, JOF_UINT8) +OPDEF(JSOP_ENDITER, 78, "enditer", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_FUNAPPLY, 78, "funapply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) -OPDEF(JSOP_SWAP, 79, "swap", NULL, 1, 2, 2, 0, JOF_BYTE) +OPDEF(JSOP_FUNAPPLY, 79, "funapply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) /* Push object literal: either an XML object or initialiser object. */ -OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_OBJECT, 80, "object", NULL, 5, 0, 1, 19, JOF_OBJECT) /* Pop value and discard it. */ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) /* 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) +OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) -/* Trap into debugger for breakpoint, etc. */ -OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED0, 83, "unused0", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast get/set ops for function arguments and local variables. */ OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) @@ -253,14 +251,14 @@ OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16 * shape, which can be set at the start and slots then filled in directly. * NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 3, 0, 1, 19, JOF_UINT8) -OPDEF(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, 19, JOF_UINT24) -OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT8|JOF_TYPESET) +OPDEF(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, 19, JOF_UINT24|JOF_TYPESET) +OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, 19, JOF_OBJECT|JOF_TYPESET) OPDEF(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 93, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_DEFSHARP, 95, "defsharp", NULL, 5, 0, 0, 0, JOF_UINT16PAIR|JOF_SHARPSLOT) -OPDEF(JSOP_USESHARP, 96, "usesharp", NULL, 5, 0, 1, 0, JOF_UINT16PAIR|JOF_SHARPSLOT) +OPDEF(JSOP_UNUSED14, 95, "unused14", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED15, 96, "unused15", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast inc/dec ops for args and locals. */ OPDEF(JSOP_INCARG, 97, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3) @@ -273,13 +271,18 @@ OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL| OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3) OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_IMACOP, 105,"imacop", NULL, 1, 0, 0, 0, JOF_BYTE) +/* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */ +OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, 0, JOF_BYTE) -/* ECMA-compliant for/in ops. */ -OPDEF(JSOP_FORNAME, 106,"forname", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR|JOF_TMPSLOT3) -OPDEF(JSOP_FORPROP, 107,"forprop", NULL, 3, 2, 1, 18, JOF_ATOM|JOF_PROP|JOF_FOR|JOF_TMPSLOT3) -OPDEF(JSOP_FORELEM, 108,"forelem", NULL, 1, 1, 2, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) -OPDEF(JSOP_POPN, 109,"popn", NULL, 3, -1, 0, 0, JOF_UINT16) +/* The argument is the offset to the next statement and is used by IonMonkey. */ +OPDEF(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_UNUSED3, 107,"unused3", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ +OPDEF(JSOP_FUNCALL, 108,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) + +/* This opcode is the target of the backwards jump for some loop. */ +OPDEF(JSOP_LOOPHEAD, 109,"loophead", NULL, 1, 0, 0, 0, JOF_BYTE) /* ECMA-compliant assignment ops. */ OPDEF(JSOP_BINDNAME, 110,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET) @@ -296,7 +299,7 @@ OPDEF(JSOP_INSTANCEOF,114,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE| OPDEF(JSOP_DEBUGGER, 115,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) /* gosub/retsub for finally handling */ -OPDEF(JSOP_GOSUB, 116,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_GOSUB, 116,"gosub", NULL, 5, 0, 0, 0, JOF_JUMP) OPDEF(JSOP_RETSUB, 117,"retsub", NULL, 1, 2, 0, 0, JOF_BYTE) /* More exception handling ops. */ @@ -311,13 +314,13 @@ 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|JOF_TMPSLOT2) -OPDEF(JSOP_DEFAULT, 122,"default", NULL, 3, 1, 0, 0, JOF_JUMP) +OPDEF(JSOP_CASE, 121,"case", NULL, 5, 2, 1, 0, JOF_JUMP) +OPDEF(JSOP_DEFAULT, 122,"default", NULL, 5, 1, 0, 0, JOF_JUMP) /* * ECMA-compliant call to eval op */ -OPDEF(JSOP_EVAL, 123,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) +OPDEF(JSOP_EVAL, 123,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) /* * ECMA-compliant helper for 'for (x[i] in o)' loops. @@ -335,12 +338,12 @@ OPDEF(JSOP_SETTER, 126,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) /* * Prolog bytecodes for defining function, var, and const names. */ -OPDEF(JSOP_DEFFUN, 127,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFFUN, 127,"deffun", NULL, 5, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) OPDEF(JSOP_DEFCONST, 128,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) OPDEF(JSOP_DEFVAR, 129,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) /* Push a closure for a named or anonymous function expression. */ -OPDEF(JSOP_LAMBDA, 130, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_LAMBDA, 130, "lambda", NULL, 5, 0, 1, 19, JOF_OBJECT) /* Used for named function expression self-naming, if lightweight. */ OPDEF(JSOP_CALLEE, 131, "callee", NULL, 1, 0, 1, 19, JOF_BYTE) @@ -352,7 +355,7 @@ OPDEF(JSOP_CALLEE, 131, "callee", NULL, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_SETLOCALPOP, 132, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) /* Pick an element from the stack. */ -OPDEF(JSOP_PICK, 133, "pick", NULL, 2, 0, 0, 0, JOF_UINT8) +OPDEF(JSOP_PICK, 133, "pick", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_TMPSLOT2) /* * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN @@ -364,103 +367,89 @@ OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE) /* * Get a slot from a flat closure function object that contains a snapshot of * the closure-invariant upvar values. The immediate operand indexes the upvar - * in the function's u.i.script->upvars() array. The CALL variant computes the - * callee and this-object in preparation for a JSOP_CALL. - */ -OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) -OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) - -/* - * Bytecodes that avoid making an arguments object in most cases: - * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. - * JSOP_ARGCNT returns fp->argc. + * in the function's u.i.script->upvars() array. */ -OPDEF(JSOP_ARGSUB, 138,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) -OPDEF(JSOP_ARGCNT, 139,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) +OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET) +OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET) /* * Define a local function object as a local variable. * The local variable's slot number is the first immediate two-byte operand. * The function object's atom index is the second immediate operand. */ -OPDEF(JSOP_DEFLOCALFUN, 140,"deflocalfun",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) +OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 7, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) /* Extended jumps. */ -OPDEF(JSOP_GOTOX, 141,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_IFEQX, 142,"ifeqx", NULL, 5, 1, 0, 4, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_IFNEX, 143,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_PARENHEAD) -OPDEF(JSOP_ORX, 144,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_ANDX, 145,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_GOSUBX, 146,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_CASEX, 147,"casex", NULL, 5, 2, 1, 0, JOF_JUMPX) -OPDEF(JSOP_DEFAULTX, 148,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_TABLESWITCHX, 149,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD) -OPDEF(JSOP_LOOKUPSWITCHX, 150,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD) +OPDEF(JSOP_UNUSED4, 139,"unused4", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED5, 140,"unused5", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED6, 141,"unused6", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED7, 142,"unused7", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED8, 143,"unused8", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED9, 144,"unused9", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED10, 145,"unused10", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED11, 146,"unused11", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED12, 147,"unused12", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED13, 148,"unused13", NULL, 1, 0, 0, 0, JOF_BYTE) /* Placeholders for a real jump opcode set during backpatch chain fixup. */ -OPDEF(JSOP_BACKPATCH, 151,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) -OPDEF(JSOP_BACKPATCH_POP, 152,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) +OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 5, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) +OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 5, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) /* Set pending exception from the stack, to trigger rethrow. */ -OPDEF(JSOP_THROWING, 153,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) /* Set and get return value pseudo-register in stack frame. */ -OPDEF(JSOP_SETRVAL, 154,"setrval", NULL, 1, 1, 0, 2, JOF_BYTE) -OPDEF(JSOP_RETRVAL, 155,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) /* Free variable references that must either be found on the global or a ReferenceError */ -OPDEF(JSOP_GETGNAME, 156,"getgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_GNAME) -OPDEF(JSOP_SETGNAME, 157,"setgname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME) -OPDEF(JSOP_INCGNAME, 158,"incgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME) -OPDEF(JSOP_DECGNAME, 159,"decgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME) -OPDEF(JSOP_GNAMEINC, 160,"gnameinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME) -OPDEF(JSOP_GNAMEDEC, 161,"gnamedec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME) +OPDEF(JSOP_GETGNAME, 154,"getgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) +OPDEF(JSOP_SETGNAME, 155,"setgname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME) +OPDEF(JSOP_INCGNAME, 156,"incgname", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE) +OPDEF(JSOP_DECGNAME, 157,"decgname", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE) +OPDEF(JSOP_GNAMEINC, 158,"gnameinc", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE) +OPDEF(JSOP_GNAMEDEC, 159,"gnamedec", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE) /* Regular expression literal requiring special "fork on exec" handling. */ -OPDEF(JSOP_REGEXP, 162,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP) +OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 5, 0, 1, 19, JOF_REGEXP) /* XML (ECMA-357, a.k.a. "E4X") support. */ -OPDEF(JSOP_DEFXMLNS, 163,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_ANYNAME, 164,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_QNAMEPART, 165,"qnamepart", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_XMLNAME) -OPDEF(JSOP_QNAMECONST, 166,"qnameconst", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_XMLNAME) -OPDEF(JSOP_QNAME, 167,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRNAME, 168,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRVAL, 169,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_ADDATTRNAME, 170,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_ADDATTRVAL, 171,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_BINDXMLNAME, 172,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET) -OPDEF(JSOP_SETXMLNAME, 173,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_XMLNAME, 174,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_DESCENDANTS, 175,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) -OPDEF(JSOP_FILTER, 176,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) -OPDEF(JSOP_ENDFILTER, 177,"endfilter", NULL, 3, 2, 1, 18, JOF_JUMP) -OPDEF(JSOP_TOXML, 178,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_TOXMLLIST, 179,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_XMLTAGEXPR, 180,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_XMLELTEXPR, 181,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_NOTRACE, 182,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM) -OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM) -OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM) -OPDEF(JSOP_DELDESC, 186,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL) - -OPDEF(JSOP_CALLPROP, 187,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3) - -/* - * These opcodes contain a reference to the current blockChain object. - * They are emitted directly after instructions, such as DEFFUN, that need fast access to - * the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT - * does not permit NULL object references, since it stores an index into a table of - * objects. - */ -OPDEF(JSOP_BLOCKCHAIN, 188,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) -OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_XMLNAME) +OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_XMLNAME) +OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) +OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) +OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET) +OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) +OPDEF(JSOP_FILTER, 174,"filter", NULL, 5, 1, 1, 0, JOF_JUMP) +OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 5, 2, 1, 18, JOF_JUMP) +OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLCDATA, 180,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM) +OPDEF(JSOP_XMLCOMMENT, 181,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM) +OPDEF(JSOP_XMLPI, 182,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM) +OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL) + +OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) + +/* Enter a let block/expr whose slots are at the top of the stack. */ +OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 5, -1, -1, 0, JOF_OBJECT) + +/* Enter a let block/expr whose slots are 1 below the top of the stack. */ +OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 5, -1, -1, 0, JOF_OBJECT) /* * Opcode to hold 24-bit immediate integer operands. */ -OPDEF(JSOP_UINT24, 190,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) +OPDEF(JSOP_UINT24, 187,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) /* * Opcodes to allow 24-bit atom or object indexes. Whenever an index exceeds @@ -468,151 +457,122 @@ OPDEF(JSOP_UINT24, 190,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24 * JSOP_INDEXBASE and JSOP_RESETBASE to provide the upper bits of the index. * See jsemit.c, EmitIndexOp. */ -OPDEF(JSOP_INDEXBASE, 191,"indexbase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE) -OPDEF(JSOP_RESETBASE, 192,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_RESETBASE0, 193,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_INDEXBASE, 188,"indexbase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE) +OPDEF(JSOP_RESETBASE, 189,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_RESETBASE0, 190,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Opcodes to help the decompiler deal with XML. */ -OPDEF(JSOP_STARTXML, 194,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_STARTXMLEXPR, 195,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_STARTXML, 191,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_STARTXMLEXPR, 192,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_CALLELEM, 196, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP) +OPDEF(JSOP_CALLELEM, 193, "callelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) /* * Stop interpretation, emitted at end of script to save the threaded bytecode * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). */ -OPDEF(JSOP_STOP, 197,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_STOP, 194,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Get an extant property value, throwing ReferenceError if the identified * property does not exist. */ -OPDEF(JSOP_GETXPROP, 198,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP) +OPDEF(JSOP_GETXPROP, 195,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET) -OPDEF(JSOP_CALLXMLNAME, 199, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE|JOF_CALLOP) +OPDEF(JSOP_CALLXMLNAME, 196, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE) /* * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). */ -OPDEF(JSOP_TYPEOFEXPR, 200,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) /* * Block-local scope support. */ -OPDEF(JSOP_ENTERBLOCK, 201,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT) -OPDEF(JSOP_LEAVEBLOCK, 202,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16) +OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 5, 0, -1, 0, JOF_OBJECT) +OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16) -/* Jump to target if top of stack value is of primitive type. */ -OPDEF(JSOP_IFPRIMTOP, 203,"ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING) -/* Throws a TypeError if the value at the top of the stack is not primitive. */ -OPDEF(JSOP_PRIMTOP, 204,"primtop", NULL, 2, 1, 1, 0, JOF_INT8) +OPDEF(JSOP_UNUSED1, 200,"unused1", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED2, 201,"unused2", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Generator and array comprehension support. */ -OPDEF(JSOP_GENERATOR, 205,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_YIELD, 206,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) -OPDEF(JSOP_ARRAYPUSH, 207,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) +OPDEF(JSOP_GENERATOR, 202,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_YIELD, 203,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) +OPDEF(JSOP_ARRAYPUSH, 204,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) /* * Get the built-in function::foo namespace and push it. */ -OPDEF(JSOP_GETFUNNS, 208,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_GETFUNNS, 205,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) /* * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). */ -OPDEF(JSOP_ENUMCONSTELEM, 209,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET) +OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET) /* * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, * which must be moved down when the block pops. */ -OPDEF(JSOP_LEAVEBLOCKEXPR,210,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16) - -/* - * Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches. - */ -OPDEF(JSOP_GETTHISPROP, 211,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP) -OPDEF(JSOP_GETARGPROP, 212,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) -OPDEF(JSOP_GETLOCALPROP, 213,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) - +OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16) +\ /* * Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after * the opcode that they prefix. */ -OPDEF(JSOP_INDEXBASE1, 214,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) -OPDEF(JSOP_INDEXBASE2, 215,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) -OPDEF(JSOP_INDEXBASE3, 216,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE1, 208,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE2, 209,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE3, 210,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) -OPDEF(JSOP_CALLGNAME, 217, "callgname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP|JOF_GNAME) -OPDEF(JSOP_CALLLOCAL, 218, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP) -OPDEF(JSOP_CALLARG, 219, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP) -OPDEF(JSOP_BINDGNAME, 220, "bindgname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) +OPDEF(JSOP_CALLGNAME, 211, "callgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) +OPDEF(JSOP_CALLLOCAL, 212, "calllocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) +OPDEF(JSOP_CALLARG, 213, "callarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) +OPDEF(JSOP_BINDGNAME, 214, "bindgname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) /* * Opcodes to hold 8-bit and 32-bit immediate integer operands. */ -OPDEF(JSOP_INT8, 221, "int8", NULL, 2, 0, 1, 16, JOF_INT8) -OPDEF(JSOP_INT32, 222, "int32", NULL, 5, 0, 1, 16, JOF_INT32) +OPDEF(JSOP_INT8, 215, "int8", NULL, 2, 0, 1, 16, JOF_INT8) +OPDEF(JSOP_INT32, 216, "int32", NULL, 5, 0, 1, 16, JOF_INT32) /* * Get the value of the 'length' property from a stacked object. */ -OPDEF(JSOP_LENGTH, 223, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP) +OPDEF(JSOP_LENGTH, 217, "length", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET) /* * Push a JSVAL_HOLE value onto the stack, representing an omitted property in * an array literal (e.g. property 0 in the array [, 1]). This opcode is used * with the JSOP_NEWARRAY opcode. */ -OPDEF(JSOP_HOLE, 224, "hole", NULL, 1, 0, 1, 0, JOF_BYTE) - -/* - * Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case. - */ -OPDEF(JSOP_DEFFUN_FC, 225,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) -OPDEF(JSOP_DEFLOCALFUN_FC,226,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) -OPDEF(JSOP_LAMBDA_FC, 227,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, 0, JOF_BYTE) -/* - * Ensure that the value on the top of the stack is an object. The one - * argument is an error message, defined in js.msg, that takes one parameter - * (the decompilation of the primitive value). - */ -OPDEF(JSOP_OBJTOP, 228,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16) - -/* This opcode stores an index that is unique to the given loop. */ -OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16) +OPDEF(JSOP_UNUSED17, 219,"unused17", NULL, 1, 0, 0, 0, JOF_BYTE) /* - * Debugger versions of the flat closure (_FC) ops. + * Variants of JSOP_{DEFLOCALFUN,LAMBDA} optimized for the flat closure case. */ -OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) -OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) -OPDEF(JSOP_DEFFUN_DBGFC, 232,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) -OPDEF(JSOP_DEFLOCALFUN_DBGFC,233,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) -OPDEF(JSOP_LAMBDA_DBGFC, 234,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_DEFLOCALFUN_FC,220,"deflocalfun_fc",NULL, 7, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) +OPDEF(JSOP_LAMBDA_FC, 221,"lambda_fc", NULL, 5, 0, 1, 19, JOF_OBJECT) /* * Joined function object as method optimization support. */ -OPDEF(JSOP_SETMETHOD, 235,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_INITMETHOD, 236,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_UNBRAND, 237,"unbrand", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_UNBRANDTHIS, 238,"unbrandthis", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETMETHOD, 222,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_INITMETHOD, 223,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) -OPDEF(JSOP_SHARPINIT, 239,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT) +OPDEF(JSOP_UNUSED16, 224,"unused16", NULL, 1, 0, 0, 0, JOF_BYTE) -/* Static binding for globals. */ -OPDEF(JSOP_GETGLOBAL, 240,"getglobal", NULL, 3, 0, 1, 19, JOF_GLOBAL|JOF_NAME) -OPDEF(JSOP_CALLGLOBAL, 241,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP) +/* Pop the stack, convert to a jsid (int or string), and push back. */ +OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE) -/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ -OPDEF(JSOP_FUNCALL, 242,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) +/* Push the implicit 'this' value for calls to the associated name. */ +OPDEF(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 3, 0, 1, 0, JOF_ATOM) -OPDEF(JSOP_FORGNAME, 243,"forgname", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_GNAME|JOF_FOR|JOF_TMPSLOT3) +/* This opcode is the target of the entry jump for some loop. */ +OPDEF(JSOP_LOOPENTRY, 227, "loopentry", NULL, 1, 0, 0, 0, JOF_BYTE) diff --git a/deps/mozjs/js/src/jsopcodeinlines.h b/deps/mozjs/js/src/jsopcodeinlines.h index 0eb05d587c4..0178d73c938 100644 --- a/deps/mozjs/js/src/jsopcodeinlines.h +++ b/deps/mozjs/js/src/jsopcodeinlines.h @@ -38,20 +38,116 @@ #include "jsautooplen.h" +#include "frontend/BytecodeEmitter.h" + namespace js { -/* - * Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is - * useful only when checking for optimization opportunities. - */ -JS_ALWAYS_INLINE jsbytecode * -AdvanceOverBlockchainOp(jsbytecode *pc) +static inline PropertyName * +GetNameFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs) { - if (*pc == JSOP_NULLBLOCKCHAIN) - return pc + JSOP_NULLBLOCKCHAIN_LENGTH; - if (*pc == JSOP_BLOCKCHAIN) - return pc + JSOP_BLOCKCHAIN_LENGTH; - return pc; + if (op == JSOP_LENGTH) + return cx->runtime->atomState.lengthAtom; + + // The method JIT's implementation of instanceof contains an internal lookup + // of the prototype property. + if (op == JSOP_INSTANCEOF) + return cx->runtime->atomState.classPrototypeAtom; + + JSScript *script = cx->stack.currentScript(); + PropertyName *name; + GET_NAME_FROM_BYTECODE(script, pc, 0, name); + return name; } +class BytecodeRange { + public: + BytecodeRange(JSScript *script) + : script(script), pc(script->code), end(pc + script->length) {} + bool empty() const { return pc == end; } + jsbytecode *frontPC() const { return pc; } + JSOp frontOpcode() const { return JSOp(*pc); } + size_t frontOffset() const { return pc - script->code; } + void popFront() { pc += GetBytecodeLength(pc); } + + private: + JSScript *script; + jsbytecode *pc, *end; +}; + +class SrcNoteLineScanner +{ + /* offset of the current JSOp in the bytecode */ + ptrdiff_t offset; + + /* next src note to process */ + jssrcnote *sn; + + /* line number of the current JSOp */ + uint32_t lineno; + + /* + * Is the current op the first one after a line change directive? Note that + * multiple ops may be "first" if a line directive is used to return to a + * previous line (eg, with a for loop increment expression.) + */ + bool lineHeader; + +public: + SrcNoteLineScanner(jssrcnote *sn, uint32_t lineno) + : offset(0), sn(sn), lineno(lineno) + { + } + + /* + * This is called repeatedly with always-advancing relpc values. The src + * notes are tuples of . Scan + * through, updating the lineno, until the next src note is for a later + * bytecode. + * + * When looking at the desired PC offset ('relpc'), the op is first in that + * line iff there is a SRC_SETLINE or SRC_NEWLINE src note for that exact + * bytecode. + * + * Note that a single bytecode may have multiple line-modifying notes (even + * though only one should ever be needed.) + */ + void advanceTo(ptrdiff_t relpc) { + // Must always advance! If the same or an earlier PC is erroneously + // passed in, we will already be past the relevant src notes + JS_ASSERT_IF(offset > 0, relpc > offset); + + // Next src note should be for after the current offset + JS_ASSERT_IF(offset > 0, SN_IS_TERMINATOR(sn) || SN_DELTA(sn) > 0); + + // The first PC requested is always considered to be a line header + lineHeader = (offset == 0); + + if (SN_IS_TERMINATOR(sn)) + return; + + ptrdiff_t nextOffset; + while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) { + offset = nextOffset; + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE || type == SRC_NEWLINE) { + if (type == SRC_SETLINE) + lineno = js_GetSrcNoteOffset(sn, 0); + else + lineno++; + + if (offset == relpc) + lineHeader = true; + } + + sn = SN_NEXT(sn); + } + } + + bool isLineHeader() const { + return lineHeader; + } + + uint32_t getLine() const { return lineno; } +}; + } diff --git a/deps/mozjs/js/src/jsotypes.h b/deps/mozjs/js/src/jsotypes.h deleted file mode 100644 index 89d1b2135c0..00000000000 --- a/deps/mozjs/js/src/jsotypes.h +++ /dev/null @@ -1,181 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This section typedefs the old 'native' types to the new PRs. - * These definitions are scheduled to be eliminated at the earliest - * possible time. The NSPR API is implemented and documented using - * the new definitions. - */ - -/* - * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid - * double-definitions of scalar types such as uint32, if NSPR's - * protypes.h is also included. - */ -#ifndef PROTYPES_H -#define PROTYPES_H - -/* SVR4 typedef of uint is commonly found on UNIX machines. */ -#ifdef XP_UNIX -#include -#else -typedef JSUintn uint; -#endif - -typedef JSUintn uintn; -typedef JSUint64 uint64; -typedef JSUint32 uint32; -typedef JSUint16 uint16; -typedef JSUint8 uint8; - -#ifndef _XP_Core_ -typedef JSIntn intn; -#endif - -/* - * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very - * common header file) defines the types int8, int16, int32, and int64. - * So we don't define these four types here to avoid conflicts in case - * the code also includes sys/types.h. - */ -#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) -#include -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -typedef JSInt64 int64; - -/* Explicit signed keyword for bitfield types is required. */ -/* Some compilers may treat them as unsigned without it. */ -typedef signed int int32; -typedef signed short int16; -typedef signed char int8; -#else -typedef JSInt64 int64; - -/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ -typedef JSInt32 int32; -typedef JSInt16 int16; -typedef JSInt8 int8; -#endif /* AIX && HAVE_SYS_INTTYPES_H */ - -typedef JSFloat64 float64; - -/* Re: jsbit.h */ -#define TEST_BIT JS_TEST_BIT -#define SET_BIT JS_SET_BIT -#define CLEAR_BIT JS_CLEAR_BIT - -/* Re: prarena.h->plarena.h */ -#define PRArena PLArena -#define PRArenaPool PLArenaPool -#define PRArenaStats PLArenaStats -#define PR_ARENA_ALIGN PL_ARENA_ALIGN -#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL -#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE -#define PR_ARENA_GROW PL_ARENA_GROW -#define PR_ARENA_MARK PL_ARENA_MARK -#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED -#define PR_CLEAR_ARENA PL_CLEAR_ARENA -#define PR_ARENA_RELEASE PL_ARENA_RELEASE -#define PR_COUNT_ARENA PL_COUNT_ARENA -#define PR_ARENA_DESTROY PL_ARENA_DESTROY -#define PR_InitArenaPool PL_InitArenaPool -#define PR_FreeArenaPool PL_FreeArenaPool -#define PR_FinishArenaPool PL_FinishArenaPool -#define PR_CompactArenaPool PL_CompactArenaPool -#define PR_ArenaFinish PL_ArenaFinish -#define PR_ArenaAllocate PL_ArenaAllocate -#define PR_ArenaGrow PL_ArenaGrow -#define PR_ArenaRelease PL_ArenaRelease -#define PR_ArenaCountAllocation PL_ArenaCountAllocation -#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth -#define PR_ArenaCountGrowth PL_ArenaCountGrowth -#define PR_ArenaCountRelease PL_ArenaCountRelease -#define PR_ArenaCountRetract PL_ArenaCountRetract - -/* Re: prevent.h->plevent.h */ -#define PREvent PLEvent -#define PREventQueue PLEventQueue -#define PR_CreateEventQueue PL_CreateEventQueue -#define PR_DestroyEventQueue PL_DestroyEventQueue -#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor -#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR -#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR -#define PR_PostEvent PL_PostEvent -#define PR_PostSynchronousEvent PL_PostSynchronousEvent -#define PR_GetEvent PL_GetEvent -#define PR_EventAvailable PL_EventAvailable -#define PREventFunProc PLEventFunProc -#define PR_MapEvents PL_MapEvents -#define PR_RevokeEvents PL_RevokeEvents -#define PR_ProcessPendingEvents PL_ProcessPendingEvents -#define PR_WaitForEvent PL_WaitForEvent -#define PR_EventLoop PL_EventLoop -#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD -#define PRHandleEventProc PLHandleEventProc -#define PRDestroyEventProc PLDestroyEventProc -#define PR_InitEvent PL_InitEvent -#define PR_GetEventOwner PL_GetEventOwner -#define PR_HandleEvent PL_HandleEvent -#define PR_DestroyEvent PL_DestroyEvent -#define PR_DequeueEvent PL_DequeueEvent -#define PR_GetMainEventQueue PL_GetMainEventQueue - -/* Re: prhash.h->plhash.h */ -#define PRHashEntry PLHashEntry -#define PRHashTable PLHashTable -#define PRHashNumber PLHashNumber -#define PRHashFunction PLHashFunction -#define PRHashComparator PLHashComparator -#define PRHashEnumerator PLHashEnumerator -#define PRHashAllocOps PLHashAllocOps -#define PR_NewHashTable PL_NewHashTable -#define PR_HashTableDestroy PL_HashTableDestroy -#define PR_HashTableRawLookup PL_HashTableRawLookup -#define PR_HashTableRawAdd PL_HashTableRawAdd -#define PR_HashTableRawRemove PL_HashTableRawRemove -#define PR_HashTableAdd PL_HashTableAdd -#define PR_HashTableRemove PL_HashTableRemove -#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries -#define PR_HashTableLookup PL_HashTableLookup -#define PR_HashTableDump PL_HashTableDump -#define PR_HashString PL_HashString -#define PR_CompareStrings PL_CompareStrings -#define PR_CompareValues PL_CompareValues - -#endif /* !defined(PROTYPES_H) */ diff --git a/deps/mozjs/js/src/jsparse.cpp b/deps/mozjs/js/src/jsparse.cpp deleted file mode 100644 index b2a941c6447..00000000000 --- a/deps/mozjs/js/src/jsparse.cpp +++ /dev/null @@ -1,9669 +0,0 @@ -/* -*- 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 ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS parser. - * - * This is a recursive-descent parser for the JavaScript language specified by - * "The JavaScript 1.5 Language Specification". It uses lexical and semantic - * feedback to disambiguate non-LL(1) structures. It generates trees of nodes - * induced by the recursive parsing (not precise syntax trees, see jsparse.h). - * After tree construction, it rewrites trees to fold constants and evaluate - * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to - * generate bytecode. - * - * This parser attempts no error recovery. - */ -#include -#include -#include -#include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" -#include "jsutil.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsversion.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsgcmark.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsstaticcheck.h" -#include "jslibmath.h" -#include "jsvector.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_DESTRUCTURING -#include "jsdhash.h" -#endif - -#include "jsatominlines.h" -#include "jsobjinlines.h" -#include "jsregexpinlines.h" -#include "jsscriptinlines.h" - -// Grr, windows.h or something under it #defines CONST... -#ifdef CONST -#undef CONST -#endif - -using namespace js; -using namespace js::gc; - -/* - * Asserts to verify assumptions behind pn_ macros. - */ -#define pn_offsetof(m) offsetof(JSParseNode, m) - -JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses)); -JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom)); - -#undef pn_offsetof - -/* - * Insist that the next token be of type tt, or report errno and return null. - * NB: this macro uses cx and ts from its lexical environment. - */ -#define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \ - JS_BEGIN_MACRO \ - if (tokenStream.getToken((__flags)) != tt) { \ - reportErrorNumber(NULL, JSREPORT_ERROR, errno); \ - return NULL; \ - } \ - JS_END_MACRO -#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0) - -#ifdef METER_PARSENODES -static uint32 parsenodes = 0; -static uint32 maxparsenodes = 0; -static uint32 recyclednodes = 0; -#endif - -void -JSParseNode::become(JSParseNode *pn2) -{ - JS_ASSERT(!pn_defn); - JS_ASSERT(!pn2->pn_defn); - - JS_ASSERT(!pn_used); - if (pn2->pn_used) { - JSParseNode **pnup = &pn2->pn_lexdef->dn_uses; - while (*pnup != pn2) - pnup = &(*pnup)->pn_link; - *pnup = this; - pn_link = pn2->pn_link; - pn_used = true; - pn2->pn_link = NULL; - pn2->pn_used = false; - } - - pn_type = pn2->pn_type; - pn_op = pn2->pn_op; - pn_arity = pn2->pn_arity; - pn_parens = pn2->pn_parens; - pn_u = pn2->pn_u; - - /* - * If any pointers are pointing to pn2, change them to point to this - * instead, since pn2 will be cleared and probably recycled. - */ - if (PN_TYPE(this) == TOK_FUNCTION && pn_arity == PN_FUNC) { - /* Function node: fix up the pn_funbox->node back-pointer. */ - JS_ASSERT(pn_funbox->node == pn2); - pn_funbox->node = this; - } else if (pn_arity == PN_LIST && !pn_head) { - /* Empty list: fix up the pn_tail pointer. */ - JS_ASSERT(pn_count == 0); - JS_ASSERT(pn_tail == &pn2->pn_head); - pn_tail = &pn_head; - } - - pn2->clear(); -} - -void -JSParseNode::clear() -{ - pn_type = TOK_EOF; - pn_op = JSOP_NOP; - pn_used = pn_defn = false; - pn_arity = PN_NULLARY; - pn_parens = false; -} - -Parser::Parser(JSContext *cx, JSPrincipals *prin, StackFrame *cfp) - : js::AutoGCRooter(cx, PARSER), - context(cx), - aleFreeList(NULL), - tokenStream(cx), - principals(NULL), - callerFrame(cfp), - callerVarObj(cfp ? &cx->stack.space().varObjForFrame(cfp) : NULL), - nodeList(NULL), - functionCount(0), - traceListHead(NULL), - tc(NULL), - emptyCallShape(NULL), - keepAtoms(cx->runtime) -{ - js::PodArrayZero(tempFreeList); - setPrincipals(prin); - JS_ASSERT_IF(cfp, cfp->isScriptFrame()); -} - -bool -Parser::init(const jschar *base, size_t length, const char *filename, uintN lineno, - 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); - return false; - } - return true; -} - -Parser::~Parser() -{ - JSContext *cx = context; - - if (principals) - JSPRINCIPALS_DROP(cx, principals); - JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); -} - -void -Parser::setPrincipals(JSPrincipals *prin) -{ - JS_ASSERT(!principals); - if (prin) - JSPRINCIPALS_HOLD(context, prin); - principals = prin; -} - -JSObjectBox * -Parser::newObjectBox(JSObject *obj) -{ - JS_ASSERT(obj); - - /* - * We use JSContext.tempPool to allocate parsed objects and place them on - * a list in this Parser to ensure GC safety. Thus the tempPool arenas - * containing the entries must be alive until we are done with scanning, - * parsing and code generation for the whole script or top-level function. - */ - JSObjectBox *objbox; - JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool); - if (!objbox) { - js_ReportOutOfScriptQuota(context); - return NULL; - } - objbox->traceLink = traceListHead; - traceListHead = objbox; - objbox->emitLink = NULL; - objbox->object = obj; - objbox->isFunctionBox = false; - return objbox; -} - -JSFunctionBox * -Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc) -{ - JS_ASSERT(obj); - JS_ASSERT(obj->isFunction()); - - /* - * We use JSContext.tempPool to allocate parsed objects and place them on - * a list in this Parser to ensure GC safety. Thus the tempPool arenas - * containing the entries must be alive until we are done with scanning, - * parsing and code generation for the whole script or top-level function. - */ - JSFunctionBox *funbox; - JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool); - if (!funbox) { - js_ReportOutOfScriptQuota(context); - return NULL; - } - funbox->traceLink = traceListHead; - traceListHead = funbox; - funbox->emitLink = NULL; - funbox->object = obj; - funbox->isFunctionBox = true; - funbox->node = fn; - funbox->siblings = tc->functionList; - tc->functionList = funbox; - ++tc->parser->functionCount; - funbox->kids = NULL; - funbox->parent = tc->funbox; - funbox->methods = NULL; - new (&funbox->bindings) Bindings(context, emptyCallShape); - funbox->queued = false; - funbox->inLoop = false; - for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (STMT_IS_LOOP(stmt)) { - funbox->inLoop = true; - break; - } - } - funbox->level = tc->staticLevel; - funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE))); - if (tc->innermostWith) - funbox->tcflags |= TCF_IN_WITH; - return funbox; -} - -bool -JSFunctionBox::joinable() const -{ - return FUN_NULL_CLOSURE(function()) && - !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)); -} - -bool -JSFunctionBox::inAnyDynamicScope() const -{ - for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) { - if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_CALLS_EVAL)) - return true; - } - return false; -} - -bool -JSFunctionBox::scopeIsExtensible() const -{ - return tcflags & TCF_FUN_EXTENSIBLE_SCOPE; -} - -bool -JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const -{ - if (slowMethods != 0) { - for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) { - if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN)) - return true; - if (funbox->inLoop) - return true; - } - } - return false; -} - -void -Parser::trace(JSTracer *trc) -{ - JSObjectBox *objbox = traceListHead; - while (objbox) { - MarkObject(trc, *objbox->object, "parser.object"); - if (objbox->isFunctionBox) - static_cast(objbox)->bindings.trace(trc); - objbox = objbox->traceLink; - } - - if (emptyCallShape) - MarkShape(trc, emptyCallShape, "emptyCallShape"); - - for (JSTreeContext *tc = this->tc; tc; tc = tc->parent) - tc->trace(trc); -} - -/* Add |node| to |parser|'s free node list. */ -static inline void -AddNodeToFreeList(JSParseNode *pn, js::Parser *parser) -{ - /* Catch back-to-back dup recycles. */ - JS_ASSERT(pn != parser->nodeList); - - /* - * It's too hard to clear these nodes from the JSAtomLists, etc. that - * hold references to them, so we never free them. It's our caller's - * job to recognize and process these, since their children do need to - * be dealt with. - */ - JS_ASSERT(!pn->pn_used); - JS_ASSERT(!pn->pn_defn); - -#ifdef DEBUG - /* Poison the node, to catch attempts to use it without initializing it. */ - memset(pn, 0xab, sizeof(*pn)); -#endif - - pn->pn_next = parser->nodeList; - parser->nodeList = pn; - -#ifdef METER_PARSENODES - recyclednodes++; -#endif -} - -/* Add |node| to |tc|'s parser's free node list. */ -static inline void -AddNodeToFreeList(JSParseNode *pn, JSTreeContext *tc) -{ - AddNodeToFreeList(pn, tc->parser); -} - -/* - * Walk the function box list at |*funboxHead|, removing boxes for deleted - * functions and cleaning up method lists. We do this once, before - * performing function analysis, to avoid traversing possibly long function - * lists repeatedly when recycling nodes. - * - * There are actually three possible states for function boxes and their - * nodes: - * - * - Live: funbox->node points to the node, and funbox->node->pn_funbox - * points back to the funbox. - * - * - Recycled: funbox->node points to the node, but funbox->node->pn_funbox - * is NULL. When a function node is part of a tree that gets recycled, we - * must avoid corrupting any method list the node is on, so we leave the - * function node unrecycled until we call cleanFunctionList. At recycle - * time, we clear such nodes' pn_funbox pointers to indicate that they - * are deleted and should be recycled once we get here. - * - * - Mutated: funbox->node is NULL; the contents of the node itself could - * be anything. When we mutate a function node into some other kind of - * node, we lose all indication that the node was ever part of the - * function box tree; it could later be recycled, reallocated, and turned - * into anything at all. (Fortunately, method list members never get - * mutated, so we don't have to worry about that case.) - * PrepareNodeForMutation clears the node's function box's node pointer, - * disconnecting it entirely from the function box tree, and marking the - * function box to be trimmed out. - */ -void -Parser::cleanFunctionList(JSFunctionBox **funboxHead) -{ - JSFunctionBox **link = funboxHead; - while (JSFunctionBox *box = *link) { - if (!box->node) { - /* - * This funbox's parse node was mutated into something else. Drop the box, - * and stay at the same link. - */ - *link = box->siblings; - } else if (!box->node->pn_funbox) { - /* - * This funbox's parse node is ready to be recycled. Drop the box, recycle - * the node, and stay at the same link. - */ - *link = box->siblings; - AddNodeToFreeList(box->node, this); - } else { - /* The function is still live. */ - - /* First, remove nodes for deleted functions from our methods list. */ - { - JSParseNode **methodLink = &box->methods; - while (JSParseNode *method = *methodLink) { - /* Method nodes are never rewritten in place to be other kinds of nodes. */ - JS_ASSERT(method->pn_arity == PN_FUNC); - if (!method->pn_funbox) { - /* Deleted: drop the node, and stay on this link. */ - *methodLink = method->pn_link; - } else { - /* Live: keep the node, and move to the next link. */ - methodLink = &method->pn_link; - } - } - } - - /* Second, remove boxes for deleted functions from our kids list. */ - cleanFunctionList(&box->kids); - - /* Keep the box on the list, and move to the next link. */ - link = &box->siblings; - } - } -} - -namespace js { - -/* - * A work pool of JSParseNodes. The work pool is a stack, chained together - * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks - * when recycling deep parse trees. - * - * Since parse nodes are probably allocated in something close to the order - * they appear in a depth-first traversal of the tree, making the work pool - * a stack should give us pretty good locality. - */ -class NodeStack { - public: - NodeStack() : top(NULL) { } - bool empty() { return top == NULL; } - void push(JSParseNode *pn) { - pn->pn_next = top; - top = pn; - } - void pushUnlessNull(JSParseNode *pn) { if (pn) push(pn); } - /* Push the children of the PN_LIST node |pn| on the stack. */ - void pushList(JSParseNode *pn) { - /* This clobbers pn->pn_head if the list is empty; should be okay. */ - *pn->pn_tail = top; - top = pn->pn_head; - } - JSParseNode *pop() { - JS_ASSERT(!empty()); - JSParseNode *hold = top; /* my kingdom for a prog1 */ - top = top->pn_next; - return hold; - } - private: - JSParseNode *top; -}; - -} /* namespace js */ - -/* - * Push the children of |pn| on |stack|. Return true if |pn| itself could be - * safely recycled, or false if it must be cleaned later (pn_used and pn_defn - * nodes, and all function nodes; see comments for - * js::Parser::cleanFunctionList). Some callers want to free |pn|; others - * (PrepareNodeForMutation) don't care about |pn|, and just need to take care of - * its children. - */ -static bool -PushNodeChildren(JSParseNode *pn, NodeStack *stack) -{ - switch (pn->pn_arity) { - case PN_FUNC: - /* - * Function nodes are linked into the function box tree, and may - * appear on method lists. Both of those lists are singly-linked, - * so trying to update them now could result in quadratic behavior - * when recycling trees containing many functions; and the lists - * can be very long. So we put off cleaning the lists up until just - * before function analysis, when we call - * js::Parser::cleanFunctionList. - * - * In fact, we can't recycle the parse node yet, either: it may - * appear on a method list, and reusing the node would corrupt - * that. Instead, we clear its pn_funbox pointer to mark it as - * deleted; js::Parser::cleanFunctionList recycles it as well. - * - * We do recycle the nodes around it, though, so we must clear - * pointers to them to avoid leaving dangling references where - * someone can find them. - */ - pn->pn_funbox = NULL; - stack->pushUnlessNull(pn->pn_body); - pn->pn_body = NULL; - return false; - - case PN_NAME: - /* - * Because used/defn nodes appear in JSAtomLists and elsewhere, we - * don't recycle them. (We'll recover their storage when we free - * the temporary arena.) However, we do recycle the nodes around - * them, so clean up the pointers to avoid dangling references. The - * top-level decls table carries references to them that later - * iterations through the compileScript loop may find, so they need - * to be neat. - * - * pn_expr and pn_lexdef share storage; the latter isn't an owning - * reference. - */ - if (!pn->pn_used) { - stack->pushUnlessNull(pn->pn_expr); - pn->pn_expr = NULL; - } - return !pn->pn_used && !pn->pn_defn; - - case PN_LIST: - stack->pushList(pn); - break; - case PN_TERNARY: - stack->pushUnlessNull(pn->pn_kid1); - stack->pushUnlessNull(pn->pn_kid2); - stack->pushUnlessNull(pn->pn_kid3); - break; - case PN_BINARY: - if (pn->pn_left != pn->pn_right) - stack->pushUnlessNull(pn->pn_left); - stack->pushUnlessNull(pn->pn_right); - break; - case PN_UNARY: - stack->pushUnlessNull(pn->pn_kid); - break; - case PN_NULLARY: - /* - * E4X function namespace nodes are PN_NULLARY, but can appear on use - * lists. - */ - return !pn->pn_used && !pn->pn_defn; - } - - return true; -} - -/* - * Prepare |pn| to be mutated in place into a new kind of node. Recycle all - * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from - * metadata structures (the function box tree). - */ -static void -PrepareNodeForMutation(JSParseNode *pn, JSTreeContext *tc) -{ - if (pn->pn_arity != PN_NULLARY) { - if (pn->pn_arity == PN_FUNC) { - /* - * Since this node could be turned into anything, we can't - * ensure it won't be subsequently recycled, so we must - * disconnect it from the funbox tree entirely. - * - * Note that pn_funbox may legitimately be NULL. functionDef - * applies MakeDefIntoUse to definition nodes, which can come - * from prior iterations of the big loop in compileScript. In - * such cases, the defn nodes have been visited by the recycler - * (but not actually recycled!), and their funbox pointers - * cleared. But it's fine to mutate them into uses of some new - * definition. - */ - if (pn->pn_funbox) - pn->pn_funbox->node = NULL; - } - - /* Put |pn|'s children (but not |pn| itself) on a work stack. */ - NodeStack stack; - PushNodeChildren(pn, &stack); - /* - * For each node on the work stack, push its children on the work stack, - * and free the node if we can. - */ - while (!stack.empty()) { - pn = stack.pop(); - if (PushNodeChildren(pn, &stack)) - AddNodeToFreeList(pn, tc); - } - } -} - -/* - * Return the nodes in the subtree |pn| to the parser's free node list, for - * reallocation. - * - * Note that all functions in |pn| that are not enclosed by other functions - * in |pn| must be direct children of |tc|, because we only clean up |tc|'s - * function and method lists. You must not reach into a function and - * recycle some part of it (unless you've updated |tc|->functionList, the - * way js_FoldConstants does). - */ -static JSParseNode * -RecycleTree(JSParseNode *pn, JSTreeContext *tc) -{ - if (!pn) - return NULL; - - JSParseNode *savedNext = pn->pn_next; - - NodeStack stack; - for (;;) { - if (PushNodeChildren(pn, &stack)) - AddNodeToFreeList(pn, tc); - if (stack.empty()) - break; - pn = stack.pop(); - } - - return savedNext; -} - -/* - * Allocate a JSParseNode from tc's node freelist or, failing that, from - * cx's temporary arena. - */ -static JSParseNode * -NewOrRecycledNode(JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = tc->parser->nodeList; - if (!pn) { - JSContext *cx = tc->parser->context; - - JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); - if (!pn) - js_ReportOutOfScriptQuota(cx); - } else { - tc->parser->nodeList = pn->pn_next; - } - - if (pn) { -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; -#endif - pn->pn_used = pn->pn_defn = false; - memset(&pn->pn_u, 0, sizeof pn->pn_u); - pn->pn_next = NULL; - } - return pn; -} - -/* used only by static create methods of subclasses */ - -JSParseNode * -JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc) -{ - JSParseNode *pn = NewOrRecycledNode(tc); - if (!pn) - return NULL; - const Token &tok = tc->parser->tokenStream.currentToken(); - pn->init(tok.type, JSOP_NOP, arity); - pn->pn_pos = tok.pos; - return pn; -} - -JSParseNode * -JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSParseNode *pn, *pn1, *pn2; - - if (!left || !right) - return NULL; - - /* - * Flatten a left-associative (left-heavy) tree of a given operator into - * a list, to reduce js_FoldConstants and js_EmitTree recursion. - */ - if (PN_TYPE(left) == tt && - PN_OP(left) == op && - (js_CodeSpec[op].format & JOF_LEFTASSOC)) { - if (left->pn_arity != PN_LIST) { - pn1 = left->pn_left, pn2 = left->pn_right; - left->pn_arity = PN_LIST; - left->pn_parens = false; - left->initList(pn1); - left->append(pn2); - if (tt == TOK_PLUS) { - if (pn1->pn_type == TOK_STRING) - left->pn_xflags |= PNX_STRCAT; - else if (pn1->pn_type != TOK_NUMBER) - left->pn_xflags |= PNX_CANTFOLD; - if (pn2->pn_type == TOK_STRING) - left->pn_xflags |= PNX_STRCAT; - else if (pn2->pn_type != TOK_NUMBER) - left->pn_xflags |= PNX_CANTFOLD; - } - } - left->append(right); - left->pn_pos.end = right->pn_pos.end; - if (tt == TOK_PLUS) { - if (right->pn_type == TOK_STRING) - left->pn_xflags |= PNX_STRCAT; - else if (right->pn_type != TOK_NUMBER) - left->pn_xflags |= PNX_CANTFOLD; - } - return left; - } - - /* - * Fold constant addition immediately, to conserve node space and, what's - * more, so js_FoldConstants never sees mixed addition and concatenation - * operations with more than one leading non-string operand in a PN_LIST - * generated for expressions such as 1 + 2 + "pt" (which should evaluate - * to "3pt", not "12pt"). - */ - if (tt == TOK_PLUS && - left->pn_type == TOK_NUMBER && - right->pn_type == TOK_NUMBER) { - left->pn_dval += right->pn_dval; - left->pn_pos.end = right->pn_pos.end; - RecycleTree(right, tc); - return left; - } - - pn = NewOrRecycledNode(tc); - if (!pn) - return NULL; - pn->init(tt, op, PN_BINARY); - pn->pn_pos.begin = left->pn_pos.begin; - pn->pn_pos.end = right->pn_pos.end; - pn->pn_left = left; - pn->pn_right = right; - return (BinaryNode *)pn; -} - -namespace js { - -inline void -NameNode::initCommon(JSTreeContext *tc) -{ - pn_expr = NULL; - pn_cookie.makeFree(); - pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK) - ? PND_BLOCKCHILD - : 0; - pn_blockid = tc->blockid(); -} - -NameNode * -NameNode::create(JSAtom *atom, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = JSParseNode::create(PN_NAME, tc); - if (pn) { - pn->pn_atom = atom; - ((NameNode *)pn)->initCommon(tc); - } - return (NameNode *)pn; -} - -} /* namespace js */ - -static bool -GenerateBlockId(JSTreeContext *tc, uint32& blockid) -{ - if (tc->blockidGen == JS_BIT(20)) { - JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, - JSMSG_NEED_DIET, "program"); - return false; - } - blockid = tc->blockidGen++; - return true; -} - -static bool -GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc) -{ - JS_ASSERT(tc->topStmt); - JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt)); - JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE); - if (!GenerateBlockId(tc, tc->topStmt->blockid)) - return false; - pn->pn_blockid = tc->topStmt->blockid; - return true; -} - -/* - * Parse a top-level JS script. - */ -JSParseNode * -Parser::parse(JSObject *chain) -{ - /* - * Protect atoms from being collected by a GC activation, which might - * - nest on this thread due to out of memory (the so-called "last ditch" - * GC attempted within js_NewGCThing), or - * - run for any reason on another thread if this thread is suspended on - * an object lock before it finishes generating bytecode into a script - * protected from the GC by a root or a stack frame reference. - */ - JSTreeContext globaltc(this); - globaltc.setScopeChain(chain); - if (!GenerateBlockId(&globaltc, globaltc.bodyid)) - return NULL; - - JSParseNode *pn = statements(); - if (pn) { - if (!tokenStream.matchToken(TOK_EOF)) { - reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); - pn = NULL; - } else { - if (!js_FoldConstants(context, pn, &globaltc)) - pn = NULL; - } - } - return pn; -} - -JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS)); - -static inline bool -SetStaticLevel(JSTreeContext *tc, uintN staticLevel) -{ - /* - * This is a lot simpler than error-checking every UpvarCookie::set, and - * practically speaking it leaves more than enough room for upvars. - */ - if (UpvarCookie::isLevelReserved(staticLevel)) { - JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, - JSMSG_TOO_DEEP, js_function_str); - return false; - } - tc->staticLevel = staticLevel; - return true; -} - -/* - * Compile a top-level script. - */ -Compiler::Compiler(JSContext *cx, JSPrincipals *prin, StackFrame *cfp) - : parser(cx, prin, cfp) -{ -} - -JSScript * -Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, - JSPrincipals *principals, uint32 tcflags, - const jschar *chars, size_t length, - const char *filename, uintN lineno, JSVersion version, - JSString *source /* = NULL */, - uintN staticLevel /* = 0 */) -{ - JSArenaPool codePool, notePool; - TokenKind tt; - JSParseNode *pn; - JSScript *script; - bool inDirectivePrologue; -#ifdef METER_PARSENODES - void *sbrk(ptrdiff_t), *before = sbrk(0); -#endif - - JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT | - TCF_COMPILE_FOR_EVAL))); - - /* - * The scripted callerFrame can only be given for compile-and-go scripts - * and non-zero static level requires callerFrame. - */ - JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); - JS_ASSERT_IF(staticLevel != 0, callerFrame); - - Compiler compiler(cx, principals, callerFrame); - if (!compiler.init(chars, length, filename, lineno, version)) - return NULL; - - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode), - &cx->scriptStackQuota); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote), - &cx->scriptStackQuota); - - Parser &parser = compiler.parser; - TokenStream &tokenStream = parser.tokenStream; - - JSCodeGenerator cg(&parser, &codePool, ¬ePool, tokenStream.getLineno()); - if (!cg.init()) - return NULL; - - MUST_FLOW_THROUGH("out"); - - // We can specialize a bit for the given scope chain if that scope chain is the global object. - JSObject *globalObj = scopeChain && scopeChain == scopeChain->getGlobal() - ? scopeChain->getGlobal() - : NULL; - js::GlobalScope globalScope(cx, globalObj, &cg); - if (globalObj) { - JS_ASSERT(globalObj->isNative()); - JS_ASSERT((globalObj->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS); - } - - /* Null script early in case of error, to reduce our code footprint. */ - script = NULL; - - globalScope.cg = &cg; - cg.flags |= tcflags; - cg.setScopeChain(scopeChain); - compiler.globalScope = &globalScope; - if (!SetStaticLevel(&cg, staticLevel)) - goto out; - - /* If this is a direct call to eval, inherit the caller's strictness. */ - if (callerFrame && - callerFrame->isScriptFrame() && - callerFrame->script()->strictModeCode) { - cg.flags |= TCF_STRICT_MODE_CODE; - tokenStream.setStrictMode(); - } - - /* - * If funbox is non-null after we create the new script, callerFrame->fun - * was saved in the 0th object table entry. - */ - JSObjectBox *funbox; - funbox = NULL; - - if (tcflags & TCF_COMPILE_N_GO) { - if (source) { - /* - * Save eval program source in script->atomMap.vector[0] for the - * eval cache (see EvalCacheLookup in jsobj.cpp). - */ - JSAtom *atom = js_AtomizeString(cx, source, 0); - if (!atom || !cg.atomList.add(&parser, atom)) - goto out; - } - - if (callerFrame && callerFrame->isFunctionFrame()) { - /* - * An eval script in a caller frame needs to have its enclosing - * function captured in case it refers to an upvar, and someone - * wishes to decompile it while it's running. - */ - funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun())); - if (!funbox) - goto out; - funbox->emitLink = cg.objectList.lastbox; - cg.objectList.lastbox = funbox; - cg.objectList.length++; - } - } - - /* - * Inline this->statements to emit as we go to save AST space. We must - * generate our script-body blockid since we aren't calling Statements. - */ - uint32 bodyid; - if (!GenerateBlockId(&cg, bodyid)) - goto out; - cg.bodyid = bodyid; - -#if JS_HAS_XML_SUPPORT - pn = NULL; - bool onlyXML; - onlyXML = true; -#endif - - inDirectivePrologue = true; - tokenStream.setOctalCharacterEscape(false); - for (;;) { - tt = tokenStream.peekToken(TSF_OPERAND); - if (tt <= TOK_EOF) { - if (tt == TOK_EOF) - break; - JS_ASSERT(tt == TOK_ERROR); - goto out; - } - - pn = parser.statement(); - if (!pn) - goto out; - JS_ASSERT(!cg.blockNode); - - if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) - goto out; - - if (!js_FoldConstants(cx, pn, &cg)) - goto out; - - if (!parser.analyzeFunctions(&cg)) - goto out; - cg.functionList = NULL; - - if (!js_EmitTree(cx, &cg, pn)) - goto out; - -#if JS_HAS_XML_SUPPORT - if (PN_TYPE(pn) != TOK_SEMI || - !pn->pn_kid || - !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) { - onlyXML = false; - } -#endif - RecycleTree(pn, &cg); - } - -#if JS_HAS_XML_SUPPORT - /* - * Prevent XML data theft via - * - * It does not cope with malformed comment hiding hacks where --> is hidden - * by C-style comments, or on a dirty line. Such cases are already broken. - */ - TSF_IN_HTML_COMMENT = 0x2000 -}; - -#define t_op u.s.op -#define t_reflags u.reflags -#define t_atom u.s.atom -#define t_atom2 u.p.atom2 -#define t_dval u.dval - -class TokenStream -{ - /* Unicode separators that are treated as line terminators, in addition to \n, \r */ - enum { - LINE_SEPARATOR = 0x2028, - PARA_SEPARATOR = 0x2029 - }; - - static const size_t ntokens = 4; /* 1 current + 2 lookahead, rounded - to power of 2 to avoid divmod by 3 */ - static const uintN ntokensMask = ntokens - 1; - - public: - typedef Vector CharBuffer; - - /* - * To construct a TokenStream, first call the constructor, which is - * infallible, then call |init|, which can fail. To destroy a TokenStream, - * first call |close| then call the destructor. If |init| fails, do not call - * |close|. - * - * This class uses JSContext.tempPool to allocate internal buffers. The - * caller should JS_ARENA_MARK before calling |init| and JS_ARENA_RELEASE - * after calling |close|. - */ - TokenStream(JSContext *); - - /* - * Create a new token stream from an input buffer. - * Return false on memory-allocation failure. - */ - bool init(const jschar *base, size_t length, const char *filename, uintN lineno, - JSVersion version); - ~TokenStream(); - - /* Accessors. */ - JSContext *getContext() const { return cx; } - bool onCurrentLine(const TokenPos &pos) const { return lineno == pos.end.lineno; } - const Token ¤tToken() const { return tokens[cursor]; } - bool isCurrentTokenType(TokenKind type) const { - return currentToken().type == type; - } - bool isCurrentTokenType(TokenKind type1, TokenKind type2) const { - TokenKind type = currentToken().type; - return type == type1 || type == type2; - } - const CharBuffer &getTokenbuf() const { return tokenbuf; } - const char *getFilename() const { return filename; } - uintN getLineno() const { return lineno; } - /* Note that the version and hasXML can get out of sync via setXML. */ - JSVersion versionNumber() const { return VersionNumber(version); } - JSVersion versionWithFlags() const { return version; } - bool hasAnonFunFix() const { return VersionHasAnonFunFix(version); } - bool hasXML() const { return xml || VersionShouldParseXML(versionNumber()); } - void setXML(bool enabled) { xml = enabled; } - - /* Flag methods. */ - void setStrictMode(bool enabled = true) { setFlag(enabled, TSF_STRICT_MODE_CODE); } - void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); } - void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); } - void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); } - void setOctalCharacterEscape(bool enabled = true) { setFlag(enabled, TSF_OCTAL_CHAR); } - - bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); } - bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); } - bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); } - bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); } - bool isEOF() const { return !!(flags & TSF_EOF); } - bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; } - - /* Mutators. */ - bool reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN errorNumber, va_list ap); - void mungeCurrentToken(TokenKind newKind) { tokens[cursor].type = newKind; } - void mungeCurrentToken(JSOp newOp) { tokens[cursor].t_op = newOp; } - void mungeCurrentToken(TokenKind newKind, JSOp newOp) { - mungeCurrentToken(newKind); - mungeCurrentToken(newOp); - } - - private: - static JSAtom *atomize(JSContext *cx, CharBuffer &cb); - bool putIdentInTokenbuf(const jschar *identStart); - - /* - * Enables flags in the associated tokenstream for the object lifetime. - * Useful for lexically-scoped flag toggles. - */ - class Flagger { - TokenStream * const parent; - uintN flags; - public: - Flagger(TokenStream *parent, uintN withFlags) : parent(parent), flags(withFlags) { - parent->flags |= flags; - } - - ~Flagger() { parent->flags &= ~flags; } - }; - friend class Flagger; - - void setFlag(bool enabled, TokenStreamFlags flag) { - if (enabled) - flags |= flag; - else - flags &= ~flag; - } - - public: - /* - * Get the next token from the stream, make it the current token, and - * return its kind. - */ - TokenKind getToken() { - /* Check for a pushed-back token resulting from mismatching lookahead. */ - if (lookahead != 0) { - JS_ASSERT(!(flags & TSF_XMLTEXTMODE)); - lookahead--; - cursor = (cursor + 1) & ntokensMask; - TokenKind tt = currentToken().type; - JS_ASSERT(tt != TOK_EOL); - return tt; - } - - return getTokenInternal(); - } - - /* Similar, but also sets flags. */ - TokenKind getToken(uintN withFlags) { - Flagger flagger(this, withFlags); - return getToken(); - } - - /* - * Push the last scanned token back into the stream. - */ - void ungetToken() { - JS_ASSERT(lookahead < ntokensMask); - lookahead++; - cursor = (cursor - 1) & ntokensMask; - } - - TokenKind peekToken(uintN withFlags = 0) { - Flagger flagger(this, withFlags); - if (lookahead != 0) { - JS_ASSERT(lookahead == 1); - return tokens[(cursor + lookahead) & ntokensMask].type; - } - TokenKind tt = getToken(); - ungetToken(); - return tt; - } - - TokenKind peekTokenSameLine(uintN withFlags = 0) { - if (!onCurrentLine(currentToken().pos)) - return TOK_EOL; - - if (lookahead != 0) { - JS_ASSERT(lookahead == 1); - return tokens[(cursor + lookahead) & ntokensMask].type; - } - - /* - * This is the only place TOK_EOL is produced. No token with TOK_EOL - * is created, just a TOK_EOL TokenKind is returned. - */ - flags &= ~TSF_EOL; - TokenKind tt = getToken(withFlags); - if (flags & TSF_EOL) { - tt = TOK_EOL; - flags &= ~TSF_EOL; - } - ungetToken(); - return tt; - } - - /* - * Get the next token from the stream if its kind is |tt|. - */ - bool matchToken(TokenKind tt, uintN withFlags = 0) { - Flagger flagger(this, withFlags); - if (getToken() == tt) - return true; - ungetToken(); - return false; - } - - private: - /* - * This is the low-level interface to the JS source code buffer. It just - * gets raw chars, basically. TokenStreams functions are layered on top - * and do some extra stuff like converting all EOL sequences to '\n', - * tracking the line number, and setting the TSF_EOF flag. (The "raw" in - * "raw chars" refers to the lack of EOL sequence normalization.) - */ - class TokenBuf { - public: - TokenBuf() : base(NULL), limit(NULL), ptr(NULL) { } - - void init(const jschar *buf, size_t length) { - base = ptr = buf; - limit = base + length; - } - - bool hasRawChars() const { - return ptr < limit; - } - - bool atStart() const { - return ptr == base; - } - - int32 getRawChar() { - return *ptr++; /* this will NULL-crash if poisoned */ - } - - int32 peekRawChar() const { - return *ptr; /* this will NULL-crash if poisoned */ - } - - bool matchRawChar(int32 c) { - if (*ptr == c) { /* this will NULL-crash if poisoned */ - ptr++; - return true; - } - return false; - } - - bool matchRawCharBackwards(int32 c) { - JS_ASSERT(ptr); /* make sure haven't been poisoned */ - if (*(ptr - 1) == c) { - ptr--; - return true; - } - return false; - } - - void ungetRawChar() { - JS_ASSERT(ptr); /* make sure haven't been poisoned */ - ptr--; - } - - const jschar *addressOfNextRawChar() { - JS_ASSERT(ptr); /* make sure haven't been poisoned */ - return ptr; - } - - /* Use this with caution! */ - void setAddressOfNextRawChar(const jschar *a) { - JS_ASSERT(a); - ptr = a; - } - -#ifdef DEBUG - /* - * Poison the TokenBuf so it cannot be accessed again. There's one - * exception to this rule -- see findEOL() -- which is why - * ptrWhenPoisoned exists. - */ - void poison() { - ptrWhenPoisoned = ptr; - ptr = NULL; - } -#endif - - static bool isRawEOLChar(int32 c) { - return (c == '\n' || c == '\r' || c == LINE_SEPARATOR || c == PARA_SEPARATOR); - } - - const jschar *findEOL(); - - private: - const jschar *base; /* base of buffer */ - const jschar *limit; /* limit for quick bounds check */ - const jschar *ptr; /* next char to get */ - const jschar *ptrWhenPoisoned; /* |ptr| when poison() was called */ - }; - - TokenKind getTokenInternal(); /* doesn't check for pushback or error flag. */ - - int32 getChar(); - int32 getCharIgnoreEOL(); - void ungetChar(int32 c); - void ungetCharIgnoreEOL(int32 c); - Token *newToken(ptrdiff_t adjust); - bool peekUnicodeEscape(int32 *c); - bool matchUnicodeEscapeIdStart(int32 *c); - bool matchUnicodeEscapeIdent(int32 *c); - bool peekChars(intN n, jschar *cp); - bool getAtLine(); - - bool getXMLEntity(); - bool getXMLTextOrTag(TokenKind *ttp, Token **tpp); - bool getXMLMarkup(TokenKind *ttp, Token **tpp); - - bool matchChar(int32 expect) { - int32 c = getChar(); - if (c == expect) - return true; - ungetChar(c); - return false; - } - - int32 peekChar() { - int32 c = getChar(); - ungetChar(c); - return c; - } - - void skipChars(intN n) { - while (--n >= 0) - getChar(); - } - - JSContext * const cx; - Token tokens[ntokens];/* circular token buffer */ - uintN cursor; /* index of last parsed token */ - uintN lookahead; /* count of lookahead tokens */ - uintN lineno; /* current line number */ - uintN flags; /* flags -- see above */ - const jschar *linebase; /* start of current line; points into userbuf */ - const jschar *prevLinebase; /* start of previous line; NULL if on the first line */ - TokenBuf userbuf; /* user input buffer */ - const char *filename; /* input filename or null */ - JSSourceHandler listener; /* callback for source; eg debugger */ - void *listenerData; /* listener 'this' data */ - void *listenerTSData;/* listener data for this TokenStream */ - CharBuffer tokenbuf; /* current token string buffer */ - int8 oneCharTokens[128]; /* table of one-char tokens */ - JSPackedBool maybeEOL[256]; /* probabilistic EOL lookup table */ - JSPackedBool maybeStrSpecial[256];/* speeds up string scanning */ - JSVersion version; /* (i.e. to identify keywords) */ - bool xml; /* see JSOPTION_XML */ -}; - -} /* namespace js */ - -extern void -js_CloseTokenStream(JSContext *cx, js::TokenStream *ts); - -extern JS_FRIEND_API(int) -js_fgets(char *buf, int size, FILE *file); - -namespace js { - -struct KeywordInfo { - const char *chars; /* C string with keyword text */ - TokenKind tokentype; - JSOp op; /* JSOp */ - JSVersion version; /* JSVersion */ -}; - -/* - * Returns a KeywordInfo for the specified characters, or NULL if the string is - * not a keyword. - */ -extern const KeywordInfo * -FindKeyword(const jschar *s, size_t length); - -} // namespace js - -/* - * Friend-exported API entry point to call a mapping function on each reserved - * identifier in the scanner's keyword table. - */ -typedef void (*JSMapKeywordFun)(const char *); - -/* - * Check that str forms a valid JS identifier name. The function does not - * check if str is a JS keyword. - */ -extern JSBool -js_IsIdentifier(JSLinearString *str); - -/* - * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error - * message have const jschar* type, not const char*. - */ -#define JSREPORT_UC 0x100 - -namespace js { - -/* - * Report a compile-time error by its number. Return true for a warning, false - * for an error. When pn is not null, use it to report error's location. - * Otherwise use ts, which must not be null. - */ -bool -ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, JSParseNode *pn, uintN flags, - uintN errorNumber, ...); - -/* - * Report a condition that should elicit a warning with JSOPTION_STRICT, - * or an error if ts or tc is handling strict mode code. This function - * defers to ReportCompileErrorNumber to do the real work. Either tc - * or ts may be NULL, if there is no tree context or token stream state - * whose strictness should affect the report. - * - * One could have ReportCompileErrorNumber recognize the - * JSREPORT_STRICT_MODE_ERROR flag instead of having a separate function - * like this one. However, the strict mode code flag we need to test is - * in the JSTreeContext structure for that code; we would have to change - * the ~120 ReportCompileErrorNumber calls to pass the additional - * argument, even though many of those sites would never use it. Using - * ts's TSF_STRICT_MODE_CODE flag instead of tc's would be brittle: at some - * points ts's flags don't correspond to those of the tc relevant to the - * error. - */ -bool -ReportStrictModeError(JSContext *cx, TokenStream *ts, JSTreeContext *tc, JSParseNode *pn, - uintN errorNumber, ...); - -} /* namespace js */ - -#endif /* jsscan_h___ */ diff --git a/deps/mozjs/js/src/jsscope.cpp b/deps/mozjs/js/src/jsscope.cpp index b0f449bf6f6..d4ca4446d40 100644 --- a/deps/mozjs/js/src/jsscope.cpp +++ b/deps/mozjs/js/src/jsscope.cpp @@ -45,9 +45,6 @@ #include #include #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" -#include "jsbit.h" #include "jsclist.h" #include "jsdhash.h" #include "jsutil.h" @@ -55,93 +52,21 @@ #include "jsatom.h" #include "jscntxt.h" #include "jsdbgapi.h" -#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsscope.h" #include "jsstr.h" -#include "jstracer.h" -#include "jsdbgapiinlines.h" +#include "js/MemoryMetrics.h" + +#include "jsatominlines.h" #include "jsobjinlines.h" #include "jsscopeinlines.h" using namespace js; using namespace js::gc; -uint32 -js_GenerateShape(JSRuntime *rt) -{ - uint32 shape; - - shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); - JS_ASSERT(shape != 0); - if (shape >= SHAPE_OVERFLOW_BIT) { - /* - * FIXME bug 440834: The shape id space has overflowed. Currently we - * cope badly with this and schedule the GC on the every call. But - * first we make sure that increments from other threads would not - * have a chance to wrap around shapeGen to zero. - */ - rt->shapeGen = SHAPE_OVERFLOW_BIT; - shape = SHAPE_OVERFLOW_BIT; - -#ifdef JS_THREADSAFE - AutoLockGC lockIf(rt); -#endif - TriggerGC(rt); - } - return shape; -} - -uint32 -js_GenerateShape(JSContext *cx) -{ - return js_GenerateShape(cx->runtime); -} - -bool -JSObject::ensureClassReservedSlotsForEmptyObject(JSContext *cx) -{ - JS_ASSERT(nativeEmpty()); - - /* - * Subtle rule: objects that call JSObject::ensureInstanceReservedSlots - * must either: - * - * (a) never escape anywhere an ad-hoc property could be set on them; or - * - * (b) protect their instance-reserved slots with shapes, at least a custom - * empty shape with the right slotSpan member. - * - * Block objects are the only objects that fall into category (a). While - * Call objects cannot escape, they can grow ad-hoc properties via eval - * of a var declaration, or due to a function statement being evaluated, - * but they have slots mapped by compiler-created shapes, and thus (b) no - * problem predicting first ad-hoc property slot. Bound Function objects - * have a custom empty shape. - * - * (Note that Block, Call, and bound Function objects are the only native - * class objects that are allowed to call ensureInstanceReservedSlots.) - */ - uint32 nfixed = JSSLOT_FREE(getClass()); - if (nfixed > numSlots() && !allocSlots(cx, nfixed)) - return false; - - return true; -} - -#define PROPERTY_TABLE_NBYTES(n) ((n) * sizeof(Shape *)) - -#ifdef DEBUG -JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0}; - -# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) -#else -# define METER(x) ((void) 0) -#endif - bool PropertyTable::init(JSRuntime *rt, Shape *lastProp) { @@ -152,7 +77,7 @@ PropertyTable::init(JSRuntime *rt, Shape *lastProp) * event, let's try to grow, overallocating to hold at least twice the * current population. */ - uint32 sizeLog2 = JS_CeilingLog2(2 * entryCount); + uint32_t sizeLog2 = JS_CEILING_LOG2W(2 * entryCount); if (sizeLog2 < MIN_SIZE_LOG2) sizeLog2 = MIN_SIZE_LOG2; @@ -160,18 +85,14 @@ PropertyTable::init(JSRuntime *rt, Shape *lastProp) * Use rt->calloc_ for memory accounting and overpressure handling * without OOM reporting. See PropertyTable::change. */ - entries = (Shape **) rt->calloc_(JS_BIT(sizeLog2) * sizeof(Shape *)); - if (!entries) { - METER(tableAllocFails); + entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2))); + if (!entries) return false; - } hashShift = JS_DHASH_BITS - sizeLog2; for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) { const Shape &shape = r.front(); - METER(searches); - METER(initSearches); - Shape **spp = search(shape.id, true); + Shape **spp = search(shape.propid(), true); /* * Beware duplicate args and arg vs. var conflicts: the youngest shape @@ -184,41 +105,72 @@ PropertyTable::init(JSRuntime *rt, Shape *lastProp) } bool -Shape::hashify(JSRuntime *rt) +Shape::makeOwnBaseShape(JSContext *cx) { - JS_ASSERT(!hasTable()); - PropertyTable *table = rt->new_(entryCount()); - if (!table) + JS_ASSERT(!base()->isOwned()); + + RootedVarShape self(cx, this); + + BaseShape *nbase = js_NewGCBaseShape(cx); + if (!nbase) return false; - setTable(table); - return getTable()->init(rt, this); + + new (nbase) BaseShape(*self->base()); + nbase->setOwned(self->base()->toUnowned()); + + self->base_ = nbase; + + return true; } -#ifdef DEBUG -# include "jsprf.h" -# define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr) -#else -# define LIVE_SCOPE_METER(cx,expr) /* nothing */ -#endif +void +Shape::handoffTableTo(Shape *shape) +{ + JS_ASSERT(inDictionary() && shape->inDictionary()); + + if (this == shape) + return; -JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); -JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); + JS_ASSERT(base()->isOwned() && !shape->base()->isOwned()); -#if JS_BYTES_PER_WORD == 4 -# define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id))) -#elif JS_BYTES_PER_WORD == 8 -# define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id)) ^ (JSHashNumber)((JSID_BITS(id)) >> 32)) -#else -# error "Unsupported configuration" -#endif + BaseShape *nbase = base(); + + JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot()); + + this->base_ = nbase->baseUnowned(); + nbase->adoptUnowned(shape->base()->toUnowned()); + + shape->base_ = nbase; +} + +bool +Shape::hashify(JSContext *cx) +{ + JS_ASSERT(!hasTable()); + + RootedVarShape self(cx, this); + + if (!ensureOwnBaseShape(cx)) + return false; + + JSRuntime *rt = cx->runtime; + PropertyTable *table = rt->new_(self->entryCount()); + if (!table) + return false; + + if (!table->init(rt, self)) { + rt->free_(table); + return false; + } + + self->base()->setTable(table); + return true; +} /* * Double hashing needs the second hash code to be relatively prime to table - * size, so we simply make hash2 odd. The inputs to multiplicative hash are - * the golden ratio, expressed as a fixed-point 32 bit fraction, and the id - * itself. + * size, so we simply make hash2 odd. */ -#define HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) #define HASH1(hash0,shift) ((hash0) >> (shift)) #define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) @@ -228,32 +180,25 @@ PropertyTable::search(jsid id, bool adding) JSHashNumber hash0, hash1, hash2; int sizeLog2; Shape *stored, *shape, **spp, **firstRemoved; - uint32 sizeMask; + uint32_t sizeMask; JS_ASSERT(entries); - JS_ASSERT(!JSID_IS_VOID(id)); + JS_ASSERT(!JSID_IS_EMPTY(id)); /* Compute the primary hash address. */ - METER(hashes); - hash0 = HASH0(id); + hash0 = HashId(id); hash1 = HASH1(hash0, hashShift); spp = entries + hash1; /* Miss: return space for a new entry. */ stored = *spp; - if (SHAPE_IS_FREE(stored)) { - METER(misses); - METER(hashMisses); + if (SHAPE_IS_FREE(stored)) return spp; - } /* Hit: return entry. */ shape = SHAPE_CLEAR_COLLISION(stored); - if (shape && shape->id == id) { - METER(hits); - METER(hashHits); + if (shape && shape->propid() == id) return spp; - } /* Collision: double hash. */ sizeLog2 = JS_DHASH_BITS - hashShift; @@ -261,7 +206,7 @@ PropertyTable::search(jsid id, bool adding) sizeMask = JS_BITMASK(sizeLog2); #ifdef DEBUG - jsuword collision_flag = SHAPE_COLLISION; + uintptr_t collision_flag = SHAPE_COLLISION; #endif /* Save the first removed entry pointer so we can recycle it if adding. */ @@ -272,27 +217,21 @@ PropertyTable::search(jsid id, bool adding) if (adding && !SHAPE_HAD_COLLISION(stored)) SHAPE_FLAG_COLLISION(spp, shape); #ifdef DEBUG - collision_flag &= jsuword(*spp) & SHAPE_COLLISION; + collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION; #endif } for (;;) { - METER(steps); hash1 -= hash2; hash1 &= sizeMask; spp = entries + hash1; stored = *spp; - if (SHAPE_IS_FREE(stored)) { - METER(misses); - METER(stepMisses); + if (SHAPE_IS_FREE(stored)) return (adding && firstRemoved) ? firstRemoved : spp; - } shape = SHAPE_CLEAR_COLLISION(stored); - if (shape && shape->id == id) { - METER(hits); - METER(stepHits); + if (shape && shape->propid() == id) { JS_ASSERT(collision_flag); return spp; } @@ -304,7 +243,7 @@ PropertyTable::search(jsid id, bool adding) if (adding && !SHAPE_HAD_COLLISION(stored)) SHAPE_FLAG_COLLISION(spp, shape); #ifdef DEBUG - collision_flag &= jsuword(*spp) & SHAPE_COLLISION; + collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION; #endif } } @@ -316,39 +255,30 @@ PropertyTable::search(jsid id, bool adding) bool PropertyTable::change(int log2Delta, JSContext *cx) { - int oldlog2, newlog2; - uint32 oldsize, newsize, nbytes; - Shape **newTable, **oldTable, **spp, **oldspp, *shape; - JS_ASSERT(entries); /* * Grow, shrink, or compress by changing this->entries. */ - oldlog2 = JS_DHASH_BITS - hashShift; - newlog2 = oldlog2 + log2Delta; - oldsize = JS_BIT(oldlog2); - newsize = JS_BIT(newlog2); - nbytes = PROPERTY_TABLE_NBYTES(newsize); - newTable = (Shape **) cx->calloc_(nbytes); - if (!newTable) { - METER(tableAllocFails); + int oldlog2 = JS_DHASH_BITS - hashShift; + int newlog2 = oldlog2 + log2Delta; + uint32_t oldsize = JS_BIT(oldlog2); + uint32_t newsize = JS_BIT(newlog2); + Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize)); + if (!newTable) return false; - } /* Now that we have newTable allocated, update members. */ hashShift = JS_DHASH_BITS - newlog2; removedCount = 0; - oldTable = entries; + Shape **oldTable = entries; entries = newTable; /* Copy only live entries, leaving removed and free ones behind. */ - for (oldspp = oldTable; oldsize != 0; oldspp++) { - shape = SHAPE_FETCH(oldspp); + for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) { + Shape *shape = SHAPE_FETCH(oldspp); if (shape) { - METER(searches); - METER(changeSearches); - spp = search(shape->id, true); + Shape **spp = search(shape->propid(), true); JS_ASSERT(SHAPE_IS_FREE(*spp)); *spp = shape; } @@ -365,12 +295,8 @@ PropertyTable::grow(JSContext *cx) { JS_ASSERT(needsToGrow()); - uint32 size = capacity(); + uint32_t size = capacity(); int delta = removedCount < size >> 2; - if (!delta) - METER(compresses); - else - METER(grows); if (!change(delta, cx) && entryCount + removedCount == size - 1) { JS_ReportOutOfMemory(cx); @@ -380,197 +306,176 @@ PropertyTable::grow(JSContext *cx) } Shape * -Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp) +Shape::getChildBinding(JSContext *cx, const StackShape &child) { - JS_ASSERT(!JSID_IS_VOID(child.id)); - JS_ASSERT(!child.inDictionary()); + JS_ASSERT(!inDictionary()); - if (inDictionary()) { - Shape *oldShape = *listp; - PropertyTable *table = (oldShape && oldShape->hasTable()) ? oldShape->getTable() : NULL; + Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child); + if (shape) { + JS_ASSERT(shape->parent == this); /* - * Attempt to grow table if needed before extending *listp, rather than - * risking OOM under table->grow after newDictionaryShape succeeds, and - * then have to fix up *listp. + * Update the number of fixed slots which bindings of this shape will + * have. Bindings are constructed as new properties come in, so the + * call object allocation class is not known ahead of time. Compute + * the fixed slot count here, which will feed into call objects created + * off of the bindings. */ - if (table && table->needsToGrow() && !table->grow(cx)) - return NULL; + uint32_t slots = child.slotSpan() + 1; /* Add one for private data. */ + gc::AllocKind kind = gc::GetGCObjectKind(slots); - if (newDictionaryShape(cx, child, listp)) { - Shape *newShape = *listp; - - JS_ASSERT(oldShape == newShape->parent); - if (table) { - /* Add newShape to the property table. */ - METER(searches); - Shape **spp = table->search(newShape->id, true); - - /* - * Beware duplicate formal parameters, allowed by ECMA-262 in - * non-strict mode. Otherwise we know that Bindings::add (our - * caller) won't pass an id already in the table to us. In the - * case of duplicate formals, the last one wins, so while we - * must not overcount entries, we must store newShape. - */ - if (!SHAPE_FETCH(spp)) - ++table->entryCount; - SHAPE_STORE_PRESERVING_COLLISION(spp, newShape); - - /* Hand the table off from oldShape to newShape. */ - oldShape->setTable(NULL); - newShape->setTable(table); - } else { - if (!newShape->hasTable()) - newShape->hashify(cx->runtime); - } - return newShape; + /* + * Make sure that the arguments and variables in the call object all + * end up in a contiguous range of slots. We need this to be able to + * embed the args/vars arrays in the TypeScriptNesting for the function + * after the call object's frame has finished. + */ + uint32_t nfixed = gc::GetGCKindSlots(kind); + if (nfixed < slots) { + nfixed = CallObject::RESERVED_SLOTS + 1; + JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1); } - return NULL; + shape->setNumFixedSlots(nfixed - 1); } + return shape; +} - if ((*listp)->entryCount() >= PropertyTree::MAX_HEIGHT) { - Shape *dprop = Shape::newDictionaryList(cx, listp); - if (!dprop) - return NULL; - return dprop->getChild(cx, child, listp); +/* static */ Shape * +Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape) +{ + JS_ASSERT(!shape->inDictionary()); + + if (!shape->parent) { + /* Treat as resetting the initial property of the shape hierarchy. */ + AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); + return EmptyShape::getInitialShape(cx, base.clasp, proto, + base.parent, kind, + base.flags & BaseShape::OBJECT_FLAG_MASK); } - Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, child); - if (shape) { - JS_ASSERT(shape->parent == this); - JS_ASSERT(this == *listp); - *listp = shape; - } - return shape; + RootShape root(cx, &shape); + + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + + StackShape child(shape); + child.base = nbase; + + return JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, shape->numFixedSlots(), child); } /* - * Get or create a property-tree or dictionary child property of parent, which - * must be lastProp if inDictionaryMode(), else parent must be one of lastProp - * or lastProp->parent. + * Get or create a property-tree or dictionary child property of |parent|, + * which must be lastProperty() if inDictionaryMode(), else parent must be + * one of lastProperty() or lastProperty()->parent. */ Shape * -JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child) +JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child) { - JS_ASSERT(!JSID_IS_VOID(child.id)); - JS_ASSERT(!child.inDictionary()); - /* - * Aliases share another property's slot, passed in the |slot| parameter. - * Shared properties have no slot. Unshared properties that do not alias - * another property's slot allocate a slot here, but may lose it due to a + * Shared properties have no slot, but slot_ will reflect that of parent. + * Unshared properties allocate a slot here but may lose it due to a * JS_ClearScope call. */ - if (!child.isAlias()) { - if (child.attrs & JSPROP_SHARED) { - child.slot = SHAPE_INVALID_SLOT; - } else { - /* - * We may have set slot from a nearly-matching shape, above. If so, - * we're overwriting that nearly-matching shape, so we can reuse - * its slot -- we don't need to allocate a new one. Similarly, we - * use a specific slot if provided by the caller. - */ - if (child.slot == SHAPE_INVALID_SLOT && !allocSlot(cx, &child.slot)) + if (!child.hasSlot()) { + child.setSlot(parent->maybeSlot()); + } else { + if (child.hasMissingSlot()) { + uint32_t slot; + if (!allocSlot(cx, &slot)) return NULL; + child.setSlot(slot); + } else { + /* Slots can only be allocated out of order on objects in dictionary mode. */ + JS_ASSERT(inDictionaryMode() || + parent->hasMissingSlot() || + child.slot() == parent->maybeSlot() + 1); } } Shape *shape; + RootedVarObject self(cx, this); + if (inDictionaryMode()) { - JS_ASSERT(parent == lastProp); - if (parent->frozen()) { - parent = Shape::newDictionaryList(cx, &lastProp); - if (!parent) - return NULL; - JS_ASSERT(!parent->frozen()); - } - shape = Shape::newDictionaryShape(cx, child, &lastProp); + JS_ASSERT(parent == lastProperty()); + RootStackShape childRoot(cx, &child); + shape = js_NewGCShape(cx); if (!shape) return NULL; + if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) { + if (!self->setSlotSpan(cx, child.slot() + 1)) + return NULL; + } + shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_); } else { - shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child); + shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, self->numFixedSlots(), child); if (!shape) return NULL; - JS_ASSERT(shape->parent == parent); - JS_ASSERT_IF(parent != lastProp, parent == lastProp->parent); - setLastProperty(shape); + //JS_ASSERT(shape->parent == parent); + //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent); + if (!self->setLastProperty(cx, shape)) + return NULL; } - updateFlags(shape); - updateShape(cx); return shape; } -Shape * -Shape::newDictionaryShape(JSContext *cx, const Shape &child, Shape **listp) +bool +JSObject::toDictionaryMode(JSContext *cx) { - Shape *dprop = JS_PROPERTY_TREE(cx).newShape(cx); - if (!dprop) - return NULL; - - new (dprop) Shape(child.id, child.rawGetter, child.rawSetter, child.slot, child.attrs, - (child.flags & ~FROZEN) | IN_DICTIONARY, child.shortid, - js_GenerateShape(cx), child.slotSpan); + JS_ASSERT(!inDictionaryMode()); - dprop->listp = NULL; - dprop->insertIntoDictionary(listp); + /* We allocate the shapes from cx->compartment, so make sure it's right. */ + JS_ASSERT(compartment() == cx->compartment); - JS_COMPARTMENT_METER(cx->compartment->liveDictModeNodes++); - return dprop; -} + uint32_t span = slotSpan(); -Shape * -Shape::newDictionaryList(JSContext *cx, Shape **listp) -{ - Shape *shape = *listp; - Shape *list = shape; + RootedVarObject self(cx, this); /* - * We temporarily create the dictionary shapes using a root located on the - * stack. This way, the GC doesn't see any intermediate state until we - * switch listp at the end. + * Clone the shapes into a new dictionary list. Don't update the + * last property of this object until done, otherwise a GC + * triggered while creating the dictionary will get the wrong + * slot span for this object. */ - Shape *root = NULL; - Shape **childp = &root; + RootedVarShape root(cx); + RootedVarShape dictionaryShape(cx); + + RootedVarShape shape(cx); + shape = lastProperty(); while (shape) { - JS_ASSERT_IF(!shape->frozen(), !shape->inDictionary()); + JS_ASSERT(!shape->inDictionary()); - Shape *dprop = Shape::newDictionaryShape(cx, *shape, childp); - if (!dprop) { - METER(toDictFails); - *listp = list; - return NULL; - } + Shape *dprop = js_NewGCShape(cx); + if (!dprop) + return false; + + HeapPtrShape *listp = dictionaryShape + ? &dictionaryShape->parent + : (HeapPtrShape *) root.address(); + + StackShape child(shape); + dprop->initDictionaryShape(child, self->numFixedSlots(), listp); JS_ASSERT(!dprop->hasTable()); - childp = &dprop->parent; - shape = shape->parent; + dictionaryShape = dprop; + shape = shape->previous(); } - *listp = root; - root->listp = listp; - - JS_ASSERT(root->inDictionary()); - root->hashify(cx->runtime); - return root; -} + if (!root->hashify(cx)) + return false; -bool -JSObject::toDictionaryMode(JSContext *cx) -{ - JS_ASSERT(!inDictionaryMode()); + JS_ASSERT((Shape **) root->listp == root.address()); + root->listp = &self->shape_; + self->shape_ = root; - /* We allocate the shapes from cx->compartment, so make sure it's right. */ - JS_ASSERT(compartment() == cx->compartment); - if (!Shape::newDictionaryList(cx, &lastProp)) - return false; + JS_ASSERT(self->inDictionaryMode()); + root->base()->setSlotSpan(span); - clearOwnShape(); return true; } @@ -584,17 +489,17 @@ NormalizeGetterAndSetter(JSContext *cx, JSObject *obj, PropertyOp &getter, StrictPropertyOp &setter) { - if (setter == StrictPropertyStub) { + if (setter == JS_StrictPropertyStub) { JS_ASSERT(!(attrs & JSPROP_SETTER)); setter = NULL; } if (flags & Shape::METHOD) { - /* Here, getter is the method, a function object reference. */ - JS_ASSERT(getter); - JS_ASSERT(!setter || setter == js_watch_set); + JS_ASSERT_IF(getter, getter == JS_PropertyStub); + JS_ASSERT(!setter); JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + getter = NULL; } else { - if (getter == PropertyStub) { + if (getter == JS_PropertyStub) { JS_ASSERT(!(attrs & JSPROP_GETTER)); getter = NULL; } @@ -620,58 +525,49 @@ JSObject::checkShapeConsistency() return; JS_ASSERT(isNative()); - if (hasOwnShape()) - JS_ASSERT(objShape != lastProp->shape); - else - JS_ASSERT(objShape == lastProp->shape); - Shape *shape = lastProp; + Shape *shape = lastProperty(); Shape *prev = NULL; if (inDictionaryMode()) { - if (shape->hasTable()) { - PropertyTable *table = shape->getTable(); - for (uint32 fslot = table->freelist; fslot != SHAPE_INVALID_SLOT; - fslot = getSlotRef(fslot).toPrivateUint32()) { - JS_ASSERT(fslot < shape->slotSpan); - } + JS_ASSERT(shape->hasTable()); - for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) { - JS_ASSERT_IF(shape != lastProp, !shape->hasTable()); + PropertyTable &table = shape->table(); + for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT; + fslot = getSlot(fslot).toPrivateUint32()) { + JS_ASSERT(fslot < slotSpan()); + } - Shape **spp = table->search(shape->id, false); - JS_ASSERT(SHAPE_FETCH(spp) == shape); - } - } else { - shape = shape->parent; - for (int n = throttle; --n >= 0 && shape; shape = shape->parent) - JS_ASSERT(!shape->hasTable()); + for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) { + JS_ASSERT_IF(shape != lastProperty(), !shape->hasTable()); + + Shape **spp = table.search(shape->propid(), false); + JS_ASSERT(SHAPE_FETCH(spp) == shape); } - shape = lastProp; + shape = lastProperty(); for (int n = throttle; --n >= 0 && shape; shape = shape->parent) { - JS_ASSERT_IF(shape->slot != SHAPE_INVALID_SLOT, shape->slot < shape->slotSpan); + JS_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan()); if (!prev) { - JS_ASSERT(shape == lastProp); - JS_ASSERT(shape->listp == &lastProp); + JS_ASSERT(shape == lastProperty()); + JS_ASSERT(shape->listp == &shape_); } else { JS_ASSERT(shape->listp == &prev->parent); - JS_ASSERT(prev->slotSpan >= shape->slotSpan); } prev = shape; } } else { for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) { if (shape->hasTable()) { - PropertyTable *table = shape->getTable(); + PropertyTable &table = shape->table(); JS_ASSERT(shape->parent); for (Shape::Range r(shape); !r.empty(); r.popFront()) { - Shape **spp = table->search(r.front().id, false); + Shape **spp = table.search(r.front().propid(), false); JS_ASSERT(SHAPE_FETCH(spp) == &r.front()); } } if (prev) { - JS_ASSERT(prev->slotSpan >= shape->slotSpan); + JS_ASSERT(prev->maybeSlot() >= shape->maybeSlot()); shape->kids.checkConsistency(prev); } prev = shape; @@ -682,11 +578,11 @@ JSObject::checkShapeConsistency() # define CHECK_SHAPE_CONSISTENCY(obj) ((void)0) #endif -const Shape * +Shape * JSObject::addProperty(JSContext *cx, jsid id, PropertyOp getter, StrictPropertyOp setter, - uint32 slot, uintN attrs, - uintN flags, intN shortid) + uint32_t slot, uintN attrs, + uintN flags, intN shortid, bool allowDictionary) { JS_ASSERT(!JSID_IS_VOID(id)); @@ -697,80 +593,94 @@ JSObject::addProperty(JSContext *cx, jsid id, NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter); - /* Search for id with adding = true in order to claim its entry. */ - Shape **spp = nativeSearch(id, true); - JS_ASSERT(!SHAPE_FETCH(spp)); - const Shape *shape = addPropertyInternal(cx, id, getter, setter, slot, attrs, - flags, shortid, spp); - if (!shape) - return NULL; - - /* Update any watchpoints referring to this property. */ - shape = js_UpdateWatchpointsForShape(cx, this, shape); - if (!shape) - METER(wrapWatchFails); + Shape **spp = NULL; + if (inDictionaryMode()) + spp = lastProperty()->table().search(id, true); - return shape; + return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, + spp, allowDictionary); } -const Shape * +Shape * JSObject::addPropertyInternal(JSContext *cx, jsid id, PropertyOp getter, StrictPropertyOp setter, - uint32 slot, uintN attrs, - uintN flags, intN shortid, - Shape **spp) + uint32_t slot, uintN attrs, + uintN flags, intN shortid, Shape **spp, + bool allowDictionary) { - JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen()); + JS_ASSERT_IF(!allowDictionary, !inDictionaryMode()); + + RootId idRoot(cx, &id); + RootedVarObject self(cx, this); PropertyTable *table = NULL; if (!inDictionaryMode()) { - if (lastProp->entryCount() >= PropertyTree::MAX_HEIGHT) { + bool stableSlot = + (slot == SHAPE_INVALID_SLOT) || + lastProperty()->hasMissingSlot() || + (slot == lastProperty()->maybeSlot() + 1); + JS_ASSERT_IF(!allowDictionary, stableSlot); + if (allowDictionary && + (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) { if (!toDictionaryMode(cx)) return NULL; - spp = nativeSearch(id, true); - table = lastProp->getTable(); + table = &lastProperty()->table(); + spp = table->search(id, true); } - } else if (lastProp->hasTable()) { - table = lastProp->getTable(); + } else { + table = &lastProperty()->table(); if (table->needsToGrow()) { if (!table->grow(cx)) return NULL; - - METER(searches); - METER(changeSearches); spp = table->search(id, true); JS_ASSERT(!SHAPE_FETCH(spp)); } } + JS_ASSERT(!!table == !!spp); + /* Find or create a property tree node labeled by our arguments. */ - const Shape *shape; + Shape *shape; { - Shape child(id, getter, setter, slot, attrs, flags, shortid); - shape = getChildProperty(cx, lastProp, child); + shape = self->lastProperty(); + + jsuint index; + bool indexed = js_IdIsIndex(id, &index); + UnownedBaseShape *nbase; + if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) { + nbase = shape->base()->unowned(); + } else { + StackBaseShape base(shape->base()); + base.updateGetterSetter(attrs, getter, setter); + if (indexed) + base.flags |= BaseShape::INDEXED; + nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + } + + StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid); + shape = self->getChildProperty(cx, self->lastProperty(), child); } if (shape) { - JS_ASSERT(shape == lastProp); + JS_ASSERT(shape == self->lastProperty()); if (table) { /* Store the tree node pointer in the table entry for id. */ SHAPE_STORE_PRESERVING_COLLISION(spp, shape); ++table->entryCount; - /* Pass the table along to the new lastProp, namely shape. */ - JS_ASSERT(shape->parent->getTable() == table); - shape->parent->setTable(NULL); - shape->setTable(table); + /* Pass the table along to the new last property, namely shape. */ + JS_ASSERT(&shape->parent->table() == table); + shape->parent->handoffTableTo(shape); } - CHECK_SHAPE_CONSISTENCY(this); - METER(adds); + CHECK_SHAPE_CONSISTENCY(self); return shape; } - CHECK_SHAPE_CONSISTENCY(this); - METER(addFails); + CHECK_SHAPE_CONSISTENCY(self); return NULL; } @@ -791,60 +701,49 @@ CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, uintN *att /* Reject attempts to remove a slot from the permanent data property. */ if (shape->isDataDescriptor() && shape->hasSlot() && (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) { - obj->reportNotConfigurable(cx, shape->id); + obj->reportNotConfigurable(cx, shape->propid()); return false; } return true; } -const Shape * +Shape * JSObject::putProperty(JSContext *cx, jsid id, PropertyOp getter, StrictPropertyOp setter, - uint32 slot, uintN attrs, + uint32_t slot, uintN attrs, uintN flags, intN shortid) { JS_ASSERT(!JSID_IS_VOID(id)); - /* - * Horrid non-strict eval, debuggers, and |default xml namespace ...| may - * extend Call objects. - */ - if (lastProp->frozen()) { - if (!Shape::newDictionaryList(cx, &lastProp)) - return NULL; - JS_ASSERT(!lastProp->frozen()); - } + RootId idRoot(cx, &id); NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter); + RootedVarObject self(cx, this); + /* Search for id in order to claim its entry if table has been allocated. */ - Shape **spp = nativeSearch(id, true); - Shape *shape = SHAPE_FETCH(spp); + Shape **spp; + Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true); if (!shape) { /* * You can't add properties to a non-extensible object, but you can change * attributes of properties in such objects. */ - if (!isExtensible()) { - reportNotExtensible(cx); + if (!self->isExtensible()) { + self->reportNotExtensible(cx); return NULL; } - const Shape *newShape = - addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp); - if (!newShape) - return NULL; - newShape = js_UpdateWatchpointsForShape(cx, this, newShape); - if (!newShape) - METER(wrapWatchFails); - return newShape; + return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true); } /* Property exists: search must have returned a valid *spp. */ - JS_ASSERT(!SHAPE_IS_REMOVED(*spp)); + JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp)); - if (!CheckCanChangeAttrs(cx, this, shape, &attrs)) + RootShape shapeRoot(cx, &shape); + + if (!CheckCanChangeAttrs(cx, self, shape, &attrs)) return NULL; /* @@ -852,103 +751,93 @@ JSObject::putProperty(JSContext *cx, jsid id, * copy the existing shape's slot into slot so we can match shape, if all * other members match. */ - bool hadSlot = !shape->isAlias() && shape->hasSlot(); - uint32 oldSlot = shape->slot; + bool hadSlot = shape->hasSlot(); + uint32_t oldSlot = shape->maybeSlot(); if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot) slot = oldSlot; + RootedVar nbase(cx); + { + jsuint index; + bool indexed = js_IdIsIndex(id, &index); + StackBaseShape base(self->lastProperty()->base()); + base.updateGetterSetter(attrs, getter, setter); + if (indexed) + base.flags |= BaseShape::INDEXED; + nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + } + /* * Now that we've possibly preserved slot, check whether all members match. * If so, this is a redundant "put" and we can return without more work. */ - if (shape->matchesParamsAfterId(getter, setter, slot, attrs, flags, shortid)) { - METER(redundantPuts); + if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid)) return shape; - } /* * Overwriting a non-last property requires switching to dictionary mode. * The shape tree is shared immutable, and we can't removeProperty and then * addPropertyInternal because a failure under add would lose data. */ - if (shape != lastProp && !inDictionaryMode()) { - if (!toDictionaryMode(cx)) + if (shape != self->lastProperty() && !self->inDictionaryMode()) { + if (!self->toDictionaryMode(cx)) return NULL; - spp = nativeSearch(shape->id); + spp = self->lastProperty()->table().search(shape->propid(), false); shape = SHAPE_FETCH(spp); } - /* - * Now that we have passed the lastProp->frozen() check at the top of this - * method, and the non-last-property conditioning just above, we are ready - * to overwrite. - * - * Optimize the case of a non-frozen dictionary-mode object based on the - * property that dictionaries exclusively own their mutable shape structs, - * each of which has a unique shape number (not shared via a shape tree). - * - * This is more than an optimization: it is required to preserve for-in - * enumeration order (see bug 601399). - */ - if (inDictionaryMode()) { + JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot); + + if (self->inDictionaryMode()) { + /* + * Updating some property in a dictionary-mode object. Create a new + * shape for the existing property, and also generate a new shape for + * the last property of the dictionary (unless the modified property + * is also the last property). + */ + bool updateLast = (shape == self->lastProperty()); + shape = self->replaceWithNewEquivalentShape(cx, shape); + if (!shape) + return NULL; + if (!updateLast && !self->generateOwnShape(cx)) + return NULL; + /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */ - if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) { - if (!allocSlot(cx, &slot)) + if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) { + if (!self->allocSlot(cx, &slot)) return NULL; } - shape->slot = slot; - if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) { - shape->slotSpan = slot + 1; - - for (Shape *temp = lastProp; temp != shape; temp = temp->parent) { - if (temp->slotSpan <= slot) - temp->slotSpan = slot + 1; - } - } + if (updateLast) + shape->base()->adoptUnowned(nbase); + else + shape->base_ = nbase; - shape->rawGetter = getter; - shape->rawSetter = setter; - shape->attrs = uint8(attrs); + shape->setSlot(slot); + shape->attrs = uint8_t(attrs); shape->flags = flags | Shape::IN_DICTIONARY; - shape->shortid = int16(shortid); - - /* - * We are done updating shape and lastProp. Now we may need to update - * flags and we will need to update objShape, which is no longer "own". - * In the last non-dictionary property case in the else clause just - * below, getChildProperty handles this for us. First update flags. - */ - updateFlags(shape); - - /* - * We have just mutated shape in place, but nothing caches it based on - * shape->shape unless shape is lastProp and !hasOwnShape()). Therefore - * we regenerate only lastProp->shape. We will clearOwnShape(), which - * sets objShape to lastProp->shape. - */ - lastProp->shape = js_GenerateShape(cx); - clearOwnShape(); + shape->shortid_ = int16_t(shortid); } else { /* - * Updating lastProp in a non-dictionary-mode object. Such objects - * share their shapes via a tree rooted at a prototype emptyShape, or - * perhaps a well-known compartment-wide singleton emptyShape. - * - * If any shape in the tree has a property hashtable, it is shared and - * immutable too, therefore we must not update *spp. + * Updating the last property in a non-dictionary-mode object. Find an + * alternate shared child of the last property's previous shape. */ - JS_ASSERT(shape == lastProp); - removeLastProperty(); + StackBaseShape base(self->lastProperty()->base()); + base.updateGetterSetter(attrs, getter, setter); + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + + JS_ASSERT(shape == self->lastProperty()); /* Find or create a property tree node labeled by our arguments. */ - Shape child(id, getter, setter, slot, attrs, flags, shortid); + StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid); + Shape *newShape = self->getChildProperty(cx, shape->parent, child); - Shape *newShape = getChildProperty(cx, lastProp, child); if (!newShape) { - setLastProperty(shape); - CHECK_SHAPE_CONSISTENCY(this); - METER(putFails); + CHECK_SHAPE_CONSISTENCY(self); return NULL; } @@ -958,33 +847,25 @@ JSObject::putProperty(JSContext *cx, jsid id, /* * Can't fail now, so free the previous incarnation's slot if the new shape * has no slot. But we do not need to free oldSlot (and must not, as trying - * to will botch an assertion in JSObject::freeSlot) if the new lastProp - * (shape here) has a slotSpan that does not cover it. + * to will botch an assertion in JSObject::freeSlot) if the new last + * property (shape here) has a slotSpan that does not cover it. */ if (hadSlot && !shape->hasSlot()) { - if (oldSlot < shape->slotSpan) - freeSlot(cx, oldSlot); - else - getSlotRef(oldSlot).setUndefined(); + if (oldSlot < self->slotSpan()) + self->freeSlot(cx, oldSlot); JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); } - CHECK_SHAPE_CONSISTENCY(this); - METER(puts); + CHECK_SHAPE_CONSISTENCY(self); - const Shape *newShape = js_UpdateWatchpointsForShape(cx, this, shape); - if (!newShape) - METER(wrapWatchFails); - return newShape; + return shape; } -const Shape * -JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask, +Shape * +JSObject::changeProperty(JSContext *cx, Shape *shape, uintN attrs, uintN mask, PropertyOp getter, StrictPropertyOp setter) { - JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen()); - JS_ASSERT(!JSID_IS_VOID(shape->id)); - JS_ASSERT(nativeContains(*shape)); + JS_ASSERT(nativeContains(cx, *shape)); attrs |= shape->attrs & mask; @@ -992,12 +873,16 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) || !(attrs & JSPROP_SHARED)); - /* Don't allow method properties to be changed to have a getter. */ - JS_ASSERT_IF(getter != shape->rawGetter, !shape->isMethod()); + /* Don't allow method properties to be changed to have a getter or setter. */ + JS_ASSERT_IF(shape->isMethod(), !getter && !setter); - if (getter == PropertyStub) + types::MarkTypePropertyConfigured(cx, this, shape->propid()); + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) + types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType()); + + if (getter == JS_PropertyStub) getter = NULL; - if (setter == StrictPropertyStub) + if (setter == JS_StrictPropertyStub) setter = NULL; if (!CheckCanChangeAttrs(cx, this, shape, &attrs)) @@ -1006,235 +891,140 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter) return shape; - const Shape *newShape; - /* - * Dictionary-mode objects exclusively own their mutable shape structs, so - * we simply modify in place. + * Let JSObject::putProperty handle this |overwriting| case, including + * the conservation of shape->slot (if it's valid). We must not call + * removeProperty because it will free an allocated shape->slot, and + * putProperty won't re-allocate it. */ - if (inDictionaryMode()) { - /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */ - uint32 slot = shape->slot; - if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) { - if (!allocSlot(cx, &slot)) - return NULL; - } - - Shape *mutableShape = const_cast(shape); - mutableShape->slot = slot; - if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) { - mutableShape->slotSpan = slot + 1; - - for (Shape *temp = lastProp; temp != shape; temp = temp->parent) { - if (temp->slotSpan <= slot) - temp->slotSpan = slot + 1; - } - } - - mutableShape->rawGetter = getter; - mutableShape->rawSetter = setter; - mutableShape->attrs = uint8(attrs); - - updateFlags(shape); + Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(), + attrs, shape->flags, shape->maybeShortid()); - /* See the corresponding code in putProperty. */ - lastProp->shape = js_GenerateShape(cx); - clearOwnShape(); - - shape = js_UpdateWatchpointsForShape(cx, this, shape); - if (!shape) { - METER(wrapWatchFails); - return NULL; - } - JS_ASSERT(shape == mutableShape); - newShape = mutableShape; - } else if (shape == lastProp) { - Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid); - - newShape = getChildProperty(cx, shape->parent, child); -#ifdef DEBUG - if (newShape) { - JS_ASSERT(newShape == lastProp); - if (newShape->hasTable()) { - Shape **spp = nativeSearch(shape->id); - JS_ASSERT(SHAPE_FETCH(spp) == newShape); - } - } -#endif - } else { - /* - * Let JSObject::putProperty handle this |overwriting| case, including - * the conservation of shape->slot (if it's valid). We must not call - * removeProperty because it will free an allocated shape->slot, and - * putProperty won't re-allocate it. - */ - Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid); - newShape = putProperty(cx, child.id, child.rawGetter, child.rawSetter, child.slot, - child.attrs, child.flags, child.shortid); -#ifdef DEBUG - if (newShape) - METER(changePuts); -#endif - } - -#ifdef DEBUG CHECK_SHAPE_CONSISTENCY(this); - if (newShape) - METER(changes); - else - METER(changeFails); -#endif return newShape; } bool JSObject::removeProperty(JSContext *cx, jsid id) { - Shape **spp = nativeSearch(id); - Shape *shape = SHAPE_FETCH(spp); - if (!shape) { - METER(uselessRemoves); - return true; - } + RootedVarObject self(cx, this); - /* First, if shape is unshared and not has a slot, free its slot number. */ - bool addedToFreelist = false; - bool hadSlot = !shape->isAlias() && shape->hasSlot(); - if (hadSlot) { - addedToFreelist = freeSlot(cx, shape->slot); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); - } + RootId idRoot(cx, &id); + RootedVarShape shape(cx); + Shape **spp; + shape = Shape::search(cx, lastProperty(), id, &spp); + if (!shape) + return true; - /* If shape is not the last property added, switch to dictionary mode. */ - if (shape != lastProp && !inDictionaryMode()) { - if (!toDictionaryMode(cx)) + /* + * If shape is not the last property added, or the last property cannot + * be removed, switch to dictionary mode. + */ + if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) { + if (!self->toDictionaryMode(cx)) return false; - spp = nativeSearch(shape->id); + spp = self->lastProperty()->table().search(shape->propid(), false); shape = SHAPE_FETCH(spp); } + /* + * If in dictionary mode, get a new shape for the last property after the + * removal. We need a fresh shape for all dictionary deletions, even of + * the last property. Otherwise, a shape could replay and caches might + * return deleted DictionaryShapes! See bug 595365. Do this before changing + * the object or table, so the remaining removal is infallible. + */ + Shape *spare = NULL; + if (self->inDictionaryMode()) { + spare = js_NewGCShape(cx); + if (!spare) + return false; + new (spare) Shape(shape->base()->unowned(), 0); + if (shape == lastProperty()) { + /* + * Get an up to date unowned base shape for the new last property + * when removing the dictionary's last property. Information in + * base shapes for non-last properties may be out of sync with the + * object's state. + */ + Shape *previous = lastProperty()->parent; + StackBaseShape base(lastProperty()->base()); + base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter()); + BaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return false; + previous->base_ = nbase; + } + } + + /* If shape has a slot, free its slot number. */ + if (shape->hasSlot()) { + self->freeSlot(cx, shape->slot()); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); + } + /* * A dictionary-mode object owns mutable, unique shapes on a non-circular - * doubly linked list, optionally hashed by lastProp->table. So we can edit - * the list and hash in place. + * doubly linked list, hashed by lastProperty()->table. So we can edit the + * list and hash in place. */ - if (inDictionaryMode()) { - PropertyTable *table = lastProp->hasTable() ? lastProp->getTable() : NULL; + if (self->inDictionaryMode()) { + PropertyTable &table = self->lastProperty()->table(); if (SHAPE_HAD_COLLISION(*spp)) { - JS_ASSERT(table); *spp = SHAPE_REMOVED; - ++table->removedCount; - --table->entryCount; + ++table.removedCount; + --table.entryCount; } else { - METER(removeFrees); - if (table) { - *spp = NULL; - --table->entryCount; + *spp = NULL; + --table.entryCount; #ifdef DEBUG - /* - * Check the consistency of the table but limit the number of - * checks not to alter significantly the complexity of the - * delete in debug builds, see bug 534493. - */ - const Shape *aprop = lastProp; - for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent) - JS_ASSERT_IF(aprop != shape, nativeContains(*aprop)); + /* + * Check the consistency of the table but limit the number of + * checks not to alter significantly the complexity of the + * delete in debug builds, see bug 534493. + */ + const Shape *aprop = self->lastProperty(); + for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent) + JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop)); #endif - } } - /* - * Remove shape from its non-circular doubly linked list, setting this - * object's OWN_SHAPE flag so the updateShape(cx) further below will - * generate a fresh shape id for this object, distinct from the id of - * any shape in the list. We need a fresh shape for all deletions, even - * of lastProp. Otherwise, a shape number could replay and caches might - * return get deleted DictionaryShapes! See bug 595365. - */ - flags |= OWN_SHAPE; + /* Remove shape from its non-circular doubly linked list. */ + Shape *oldLastProp = self->lastProperty(); + shape->removeFromDictionary(self); - Shape *oldLastProp = lastProp; - shape->removeFromDictionary(this); - if (table) { - if (shape == oldLastProp) { - JS_ASSERT(shape->getTable() == table); - JS_ASSERT(shape->parent == lastProp); - JS_ASSERT(shape->slotSpan >= lastProp->slotSpan); - JS_ASSERT_IF(hadSlot, shape->slot + 1 <= shape->slotSpan); - - /* - * Maintain slot freelist consistency. The only constraint we - * have is that slot numbers on the freelist are less than - * lastProp->slotSpan. Thus, if the freelist is non-empty, - * then lastProp->slotSpan may not decrease. - */ - if (table->freelist != SHAPE_INVALID_SLOT) { - lastProp->slotSpan = shape->slotSpan; - - /* Add the slot to the freelist if it wasn't added in freeSlot. */ - if (hadSlot && !addedToFreelist) { - getSlotRef(shape->slot).setPrivateUint32(table->freelist); - table->freelist = shape->slot; - } - } - } + /* Hand off table from the old to new last property. */ + oldLastProp->handoffTableTo(self->lastProperty()); - /* Hand off table from old to new lastProp. */ - oldLastProp->setTable(NULL); - lastProp->setTable(table); - } + /* Generate a new shape for the object, infallibly. */ + JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare)); + + /* Consider shrinking table if its load factor is <= .25. */ + uint32_t size = table.capacity(); + if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2) + (void) table.change(-1, cx); } else { /* * Non-dictionary-mode property tables are shared immutables, so all we - * need do is retract lastProp and we'll either get or else lazily make - * via a later hashify the exact table for the new property lineage. - */ - JS_ASSERT(shape == lastProp); - removeLastProperty(); - - /* - * Revert to fixed slots if this was the first dynamically allocated slot, - * preserving invariant that objects with the same shape use the fixed - * slots in the same way. + * need do is retract the last property and we'll either get or else + * lazily make via a later hashify the exact table for the new property + * lineage. */ - size_t fixed = numFixedSlots(); - if (shape->slot == fixed) { - JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(), - lastProp->slot == fixed - 1); - revertToFixedSlots(cx); - } - } - updateShape(cx); - - /* On the way out, consider shrinking table if its load factor is <= .25. */ - if (lastProp->hasTable()) { - PropertyTable *table = lastProp->getTable(); - uint32 size = table->capacity(); - if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2) { - METER(shrinks); - (void) table->change(-1, cx); - } + JS_ASSERT(shape == self->lastProperty()); + self->removeLastProperty(cx); } - /* Also, consider shrinking object slots if 25% or more are unused. */ - if (hasSlotsArray()) { - JS_ASSERT(slotSpan() <= numSlots()); - if ((slotSpan() + (slotSpan() >> 2)) < numSlots()) - shrinkSlots(cx, slotSpan()); - } - - CHECK_SHAPE_CONSISTENCY(this); - METER(removes); + CHECK_SHAPE_CONSISTENCY(self); return true; } void JSObject::clear(JSContext *cx) { - Shape *shape = lastProp; + Shape *shape = lastProperty(); JS_ASSERT(inDictionaryMode() == shape->inDictionary()); while (shape->parent) { @@ -1244,131 +1034,490 @@ JSObject::clear(JSContext *cx) JS_ASSERT(shape->isEmptyShape()); if (inDictionaryMode()) - shape->listp = &lastProp; + shape->listp = &shape_; + + JS_ALWAYS_TRUE(setLastProperty(cx, shape)); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); + CHECK_SHAPE_CONSISTENCY(this); +} + +void +JSObject::rollbackProperties(JSContext *cx, uint32_t slotSpan) +{ /* - * Revert to fixed slots if we have cleared below the first dynamically - * allocated slot, preserving invariant that objects with the same shape - * use the fixed slots in the same way. + * Remove properties from this object until it has a matching slot span. + * The object cannot have escaped in a way which would prevent safe + * removal of the last properties. */ - if (hasSlotsArray() && JSSLOT_FREE(getClass()) <= numFixedSlots()) - revertToFixedSlots(cx); + JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan()); + while (this->slotSpan() != slotSpan) { + JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined()); + removeLastProperty(cx); + } +} + +Shape * +JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape) +{ + JS_ASSERT_IF(oldShape != lastProperty(), + inDictionaryMode() && + nativeLookup(cx, oldShape->maybePropid()) == oldShape); + + JSObject *self = this; + + if (!inDictionaryMode()) { + RootObject selfRoot(cx, &self); + RootShape newRoot(cx, &newShape); + if (!toDictionaryMode(cx)) + return NULL; + oldShape = lastProperty(); + } + + if (!newShape) { + RootObject selfRoot(cx, &self); + RootShape oldRoot(cx, &oldShape); + newShape = js_NewGCShape(cx); + if (!newShape) + return NULL; + new (newShape) Shape(oldShape->base()->unowned(), 0); + } + + PropertyTable &table = self->lastProperty()->table(); + Shape **spp = oldShape->isEmptyShape() + ? NULL + : table.search(oldShape->maybePropid(), false); /* - * We have rewound to a uniquely-shaped empty scope, so we don't need an - * override for this object's shape. + * Splice the new shape into the same position as the old shape, preserving + * enumeration order (see bug 601399). */ - clearOwnShape(); - setMap(shape); + StackShape nshape(oldShape); + newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp); - LeaveTraceIfGlobalObject(cx, this); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); - CHECK_SHAPE_CONSISTENCY(this); + JS_ASSERT(newShape->parent == oldShape); + oldShape->removeFromDictionary(self); + + if (newShape == lastProperty()) + oldShape->handoffTableTo(newShape); + + if (spp) + SHAPE_STORE_PRESERVING_COLLISION(spp, newShape); + return newShape; } -void -JSObject::generateOwnShape(JSContext *cx) +Shape * +JSObject::methodShapeChange(JSContext *cx, const Shape &shape) { -#ifdef JS_TRACER - JS_ASSERT_IF(!parent && JS_ON_TRACE(cx), JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit); - LeaveTraceIfGlobalObject(cx, this); + JS_ASSERT(shape.isMethod()); + + if (!inDictionaryMode() && !toDictionaryMode(cx)) + return NULL; + + Shape *spare = js_NewGCShape(cx); + if (!spare) + return NULL; + new (spare) Shape(shape.base()->unowned(), 0); + +#ifdef DEBUG + JS_ASSERT(canHaveMethodBarrier()); + JS_ASSERT(!shape.setter()); + JS_ASSERT(!shape.hasShortID()); +#endif /* - * If we are recording, here is where we forget already-guarded shapes. - * Any subsequent property operation upon object on the trace currently - * being recorded will re-guard (and re-memoize). + * Clear Shape::METHOD from flags as we are despecializing from a + * method memoized in the property tree to a plain old function-valued + * property. */ - if (TraceRecorder *tr = TRACE_RECORDER(cx)) - tr->forgetGuardedShapesForObject(this); -#endif + Shape *result = + putProperty(cx, shape.propid(), NULL, NULL, shape.slot(), + shape.attrs, + shape.getFlags() & ~Shape::METHOD, + 0); + if (!result) + return NULL; - setOwnShape(js_GenerateShape(cx)); + if (result != lastProperty()) + JS_ALWAYS_TRUE(generateOwnShape(cx, spare)); + + return result; } -void -JSObject::deletingShapeChange(JSContext *cx, const Shape &shape) +bool +JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape) { - JS_ASSERT(!JSID_IS_VOID(shape.id)); - generateOwnShape(cx); + return generateOwnShape(cx); } -const Shape * -JSObject::methodShapeChange(JSContext *cx, const Shape &shape) +bool +JSObject::clearParent(JSContext *cx) { - const Shape *result = &shape; + return setParent(cx, NULL); +} - JS_ASSERT(!JSID_IS_VOID(shape.id)); - if (shape.isMethod()) { -#ifdef DEBUG - const Value &prev = nativeGetSlot(shape.slot); - JS_ASSERT(shape.methodObject() == prev.toObject()); - JS_ASSERT(canHaveMethodBarrier()); - JS_ASSERT(hasMethodBarrier()); - JS_ASSERT(!shape.rawSetter || shape.rawSetter == js_watch_set); -#endif +bool +JSObject::setParent(JSContext *cx, JSObject *parent) +{ + if (parent && !parent->setDelegate(cx)) + return false; - /* - * Pass null to make a stub getter, but pass along shape.rawSetter to - * preserve watchpoints. Clear Shape::METHOD from flags as we are - * despecializing from a method memoized in the property tree to a - * plain old function-valued property. - */ - result = putProperty(cx, shape.id, NULL, shape.rawSetter, shape.slot, - shape.attrs, - shape.getFlags() & ~Shape::METHOD, - shape.shortid); - if (!result) - return NULL; + if (inDictionaryMode()) { + StackBaseShape base(lastProperty()); + base.parent = parent; + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return false; + + lastProperty()->base()->adoptUnowned(nbase); + return true; } - if (branded()) { - uintN thrashCount = getMethodThrashCount(); - if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) { - ++thrashCount; - setMethodThrashCount(thrashCount); - if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) { - unbrand(cx); - return result; + Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_); + if (!newShape) + return false; + + shape_ = newShape; + return true; +} + +/* static */ Shape * +Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last) +{ + if (last->getObjectParent() == parent) + return last; + + StackBaseShape base(last); + base.parent = parent; + + return replaceLastProperty(cx, base, proto, last); +} + +bool +JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props) +{ + JS_ASSERT(isExtensible()); + + RootedVarObject self(cx, this); + + if (props) { + if (js::FixOp fix = getOps()->fix) { + bool success; + if (!fix(cx, this, &success, props)) + return false; + if (!success) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY); + return false; } + } else { + if (!js::GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props)) + return false; } } - generateOwnShape(cx); - return result; + return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE); } bool -JSObject::methodShapeChange(JSContext *cx, uint32 slot) +JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape) { - if (!hasMethodBarrier()) { - generateOwnShape(cx); - } else { - for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) { - const Shape &shape = r.front(); - JS_ASSERT(!JSID_IS_VOID(shape.id)); - if (shape.slot == slot) - return methodShapeChange(cx, shape) != NULL; - } + BaseShape::Flag flag = (BaseShape::Flag) flag_; + + if (lastProperty()->getObjectFlags() & flag) + return true; + + RootedVarObject self(cx, this); + + if (inDictionaryMode()) { + if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx)) + return false; + StackBaseShape base(self->lastProperty()); + base.flags |= flag; + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return false; + + self->lastProperty()->base()->adoptUnowned(nbase); + return true; } + + Shape *newShape = Shape::setObjectFlag(cx, flag, getProto(), lastProperty()); + if (!newShape) + return false; + + self->shape_ = newShape; return true; } +/* static */ Shape * +Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last) +{ + if (last->getObjectFlags() & flag) + return last; + + StackBaseShape base(last); + base.flags |= flag; + + return replaceLastProperty(cx, base, proto, last); +} + +/* static */ inline HashNumber +StackBaseShape::hash(const StackBaseShape *base) +{ + JSDHashNumber hash = base->flags; + hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3); + hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3); + hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter); + hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter); + return hash; +} + +/* static */ inline bool +StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup) +{ + return key->flags == lookup->flags + && key->clasp == lookup->clasp + && key->parent == lookup->parent + && key->rawGetter == lookup->rawGetter + && key->rawSetter == lookup->rawSetter; +} + +/* Root for stack allocated base shapes. */ +class RootStackBaseShape +{ + Root parentRoot; + Maybe getterRoot; + Maybe setterRoot; + + public: + RootStackBaseShape(JSContext *cx, const StackBaseShape *base) + : parentRoot(cx, &base->parent) + { + if (base->flags & BaseShape::HAS_GETTER_OBJECT) + getterRoot.construct(cx, (JSObject **) &base->rawGetter); + if (base->flags & BaseShape::HAS_SETTER_OBJECT) + setterRoot.construct(cx, (JSObject **) &base->rawSetter); + } +}; + +/* static */ UnownedBaseShape * +BaseShape::getUnowned(JSContext *cx, const StackBaseShape &base) +{ + BaseShapeSet &table = cx->compartment->baseShapes; + + if (!table.initialized() && !table.init()) + return NULL; + + BaseShapeSet::AddPtr p = table.lookupForAdd(&base); + + if (p) + return *p; + + RootStackBaseShape root(cx, &base); + + BaseShape *nbase_ = js_NewGCBaseShape(cx); + if (!nbase_) + return NULL; + new (nbase_) BaseShape(base); + + UnownedBaseShape *nbase = static_cast(nbase_); + + if (!table.relookupOrAdd(p, &base, nbase)) + return NULL; + + return nbase; +} + void -JSObject::protoShapeChange(JSContext *cx) +JSCompartment::sweepBaseShapeTable(JSContext *cx) { - generateOwnShape(cx); + if (baseShapes.initialized()) { + for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { + UnownedBaseShape *base = e.front(); + if (!base->isMarked()) + e.removeFront(); + } + } } void -JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape) +BaseShape::finalize(JSContext *cx, bool background) { - JS_ASSERT(!JSID_IS_VOID(shape.id)); - generateOwnShape(cx); + if (table_) { + cx->delete_(table_); + table_ = NULL; + } +} + +/* static */ Shape * +Shape::setExtensibleParents(JSContext *cx, Shape *shape) +{ + JS_ASSERT(!shape->inDictionary()); + + StackBaseShape base(shape); + base.flags |= BaseShape::EXTENSIBLE_PARENTS; + + /* This is only used for Block and Call objects, which have a NULL proto. */ + return replaceLastProperty(cx, base, NULL, shape); } bool -JSObject::globalObjectOwnShapeChange(JSContext *cx) +Bindings::setExtensibleParents(JSContext *cx) { - generateOwnShape(cx); - return !js_IsPropertyCacheDisabled(cx); + if (!ensureShape(cx)) + return false; + Shape *newShape = Shape::setExtensibleParents(cx, lastBinding); + if (!newShape) + return false; + lastBinding = newShape; + return true; +} + +bool +Bindings::setParent(JSContext *cx, JSObject *obj) +{ + /* + * This may be invoked on GC heap allocated bindings, in which case this + * is pointing to an internal value of a JSScript that can't itself be + * relocated. The script itself will be rooted, and will not be moved, so + * mark the stack value as non-relocatable for the stack root analysis. + */ + Bindings *self = this; + CheckRoot root(cx, &self); + + RootObject rootObj(cx, &obj); + + if (!ensureShape(cx)) + return false; + + /* This is only used for Block objects, which have a NULL proto. */ + Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding); + if (!newShape) + return false; + self->lastBinding = newShape; + return true; +} + +/* static */ inline HashNumber +InitialShapeEntry::hash(const Lookup &lookup) +{ + JSDHashNumber hash = uintptr_t(lookup.clasp) >> 3; + hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.proto) >> 3); + hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3); + return hash + lookup.nfixed; +} + +/* static */ inline bool +InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup) +{ + return lookup.clasp == key.shape->getObjectClass() + && lookup.proto == key.proto + && lookup.parent == key.shape->getObjectParent() + && lookup.nfixed == key.shape->numFixedSlots() + && lookup.baseFlags == key.shape->getObjectFlags(); +} + +/* static */ Shape * +EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent, + AllocKind kind, uint32_t objectFlags) +{ + InitialShapeSet &table = cx->compartment->initialShapes; + + if (!table.initialized() && !table.init()) + return NULL; + + size_t nfixed = GetGCKindSlots(kind, clasp); + InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags); + + InitialShapeSet::AddPtr p = table.lookupForAdd(lookup); + + if (p) + return p->shape; + + RootedVar nbase(cx); + + StackBaseShape base(clasp, parent, objectFlags); + nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + + Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx); + if (!shape) + return NULL; + new (shape) EmptyShape(nbase, nfixed); + + InitialShapeEntry entry; + entry.shape = shape; + entry.proto = proto; + + if (!table.relookupOrAdd(p, lookup, entry)) + return NULL; + + return shape; +} + +void +NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto) +{ + Class *clasp = shape->getObjectClass(); + + gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); + if (CanBeFinalizedInBackground(kind, clasp)) + kind = GetBackgroundAllocKind(kind); + + GlobalObject *global = &shape->getObjectParent()->global(); + types::TypeObject *type = proto->getNewType(cx); + + EntryIndex entry; + if (lookupGlobal(clasp, global, kind, &entry)) + PodZero(&entries[entry]); + if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry)) + PodZero(&entries[entry]); + if (lookupType(clasp, type, kind, &entry)) + PodZero(&entries[entry]); +} + +/* static */ void +EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto) +{ + InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(), + shape->numFixedSlots(), shape->getObjectFlags()); + + InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup); + JS_ASSERT(p); + + InitialShapeEntry &entry = const_cast(*p); + JS_ASSERT(entry.shape->isEmptyShape()); + + /* The new shape had better be rooted at the old one. */ +#ifdef DEBUG + const Shape *nshape = shape; + while (!nshape->isEmptyShape()) + nshape = nshape->previous(); + JS_ASSERT(nshape == entry.shape); +#endif + + entry.shape = shape; + + /* + * This affects the shape that will be produced by the various NewObject + * methods, so clear any cache entry referring to the old shape. This is + * not required for correctness (though it may bust on the above asserts): + * the NewObject must always check for a nativeEmpty() result and generate + * the appropriate properties if found. Clearing the cache entry avoids + * this duplicate regeneration. + */ + cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto); +} + +void +JSCompartment::sweepInitialShapeTable(JSContext *cx) +{ + if (initialShapes.initialized()) { + for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { + const InitialShapeEntry &entry = e.front(); + if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked())) + e.removeFront(); + } + } } diff --git a/deps/mozjs/js/src/jsscope.h b/deps/mozjs/js/src/jsscope.h index c8d0fa41c3f..025ef99f5f0 100644 --- a/deps/mozjs/js/src/jsscope.h +++ b/deps/mozjs/js/src/jsscope.h @@ -48,15 +48,14 @@ #include #endif -#include "jstypes.h" -#include "jscntxt.h" -#include "jscompartment.h" -#include "jshashtable.h" -#include "jsiter.h" +#include "jsdhash.h" #include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" #include "jspropertytree.h" +#include "jstypes.h" + +#include "js/HashTable.h" +#include "gc/Root.h" +#include "mozilla/Attributes.h" #ifdef _MSC_VER #pragma warning(push) @@ -66,173 +65,95 @@ #endif /* - * Given P independent, non-unique properties each of size S words mapped by - * all scopes in a runtime, construct a property tree of N nodes each of size - * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child - * and right-sibling links. We hope that the N < P by enough that the space - * overhead of L, and the overhead of scope entries pointing at property tree - * nodes, is worth it. - * - * The tree construction goes as follows. If any empty scope in the runtime - * has a property X added to it, find or create a node under the tree root - * labeled X, and set obj->lastProp to point at that node. If any non-empty - * scope whose most recently added property is labeled Y has another property - * labeled Z added, find or create a node for Z under the node that was added - * for Y, and set obj->lastProp to point at that node. - * - * A property is labeled by its members' values: id, getter, setter, slot, - * attributes, tiny or short id, and a field telling for..in order. Note that - * labels are not unique in the tree, but they are unique among a node's kids - * (barring rare and benign multi-threaded race condition outcomes, see below) - * and along any ancestor line from the tree root to a given leaf node (except - * for the hard case of duplicate formal parameters to a function). - * - * Thus the root of the tree represents all empty scopes, and the first ply - * of the tree represents all scopes containing one property, etc. Each node - * in the tree can stand for any number of scopes having the same ordered set - * of properties, where that node was the last added to the scope. (We need - * not store the root of the tree as a node, and do not -- all we need are - * links to its kids.) - * - * Sidebar on for..in loop order: ECMA requires no particular order, but this - * implementation has promised and delivered property definition order, and - * compatibility is king. We could use an order number per property, which - * would require a sort in js_Enumerate, and an entry order generation number - * per scope. An order number beats a list, which should be doubly-linked for - * O(1) delete. An even better scheme is to use a parent link in the property - * tree, so that the ancestor line can be iterated from obj->lastProp when - * filling in a JSIdArray from back to front. This parent link also helps the - * GC to sweep properties iteratively. - * - * What if a property Y is deleted from a scope? If Y is the last property in - * the scope, we simply adjust the scope's lastProp member after we remove the - * scope's hash-table entry pointing at that property node. The parent link - * mentioned in the for..in sidebar above makes this adjustment O(1). But if - * Y comes between X and Z in the scope, then we might have to "fork" the tree - * at X, leaving X->Y->Z in case other scopes have those properties added in - * that order; and to finish the fork, we'd add a node labeled Z with the path - * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to - * O(n^2) growth when deleting lots of properties. - * - * Rather, for O(1) growth all around, we should share the path X->Y->Z among - * scopes having those three properties added in that order, and among scopes - * having only X->Z where Y was deleted. All such scopes have a lastProp that - * points to the Z child of Y. But a scope in which Y was deleted does not - * have a table entry for Y, and when iterating that scope by traversing the - * ancestor line from Z, we will have to test for a table entry for each node, - * skipping nodes that lack entries. - * - * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. - * Therefore we must fork in such a case if not earlier, or do something else. - * We used to fork on the theory that set after delete is rare, but the Web is - * a harsh mistress, and we now convert the scope to a "dictionary" on first - * delete, to avoid O(n^2) growth in the property tree. - * - * What about thread safety? If the property tree operations done by requests - * are find-node and insert-node, then the only hazard is duplicate insertion. - * This is harmless except for minor bloat. When all requests have ended or - * been suspended, the GC is free to sweep the tree after marking all nodes - * reachable from scopes, performing remove-node operations as needed. - * - * Is the property tree worth it compared to property storage in each table's - * entries? To decide, we must find the relation <> between the words used - * with a property tree and the words required without a tree. - * - * Model all scopes as one super-scope of capacity T entries (T a power of 2). - * Let alpha be the load factor of this double hash-table. With the property - * tree, each entry in the table is a word-sized pointer to a node that can be - * shared by many scopes. But all such pointers are overhead compared to the - * situation without the property tree, where the table stores property nodes - * directly, as entries each of size S words. With the property tree, we need - * L=2 extra words per node for siblings and kids pointers. Without the tree, - * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required - * by double hashing. - * - * Therefore, - * - * (property tree) <> (no property tree) - * N*(S+L) + T <> S*T - * N*(S+L) + T <> P*S + (1-alpha)*S*T - * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T - * - * Note that P is alpha*T by definition, so - * - * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T - * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T - * N*(S+L) <> (P + (1-alpha)*T) * (S-1) - * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) - * N*(S+L) <> P * (1/alpha) * (S-1) + * In isolation, a Shape represents a property that exists in one or more + * objects; it has an id, flags, etc. (But it doesn't represent the property's + * value.) However, Shapes are always stored in linked linear sequence of + * Shapes, called "shape lineages". Each shape lineage represents the layout of + * an entire object. + * + * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to + * the last Shape in a shape lineage, which identifies the property most + * recently added to the object. This pointer permits fast object layout + * tests. The shape lineage order also dictates the enumeration order for the + * object; ECMA requires no particular order but this implementation has + * promised and delivered property definition order. + * + * Shape lineages occur in two kinds of data structure. + * + * 1. N-ary property trees. Each path from a non-root node to the root node in + * a property tree is a shape lineage. Property trees permit full (or + * partial) sharing of Shapes between objects that have fully (or partly) + * identical layouts. The root is an EmptyShape whose identity is determined + * by the object's class, compartment and prototype. These Shapes are shared + * and immutable. + * + * 2. Dictionary mode lists. Shapes in such lists are said to be "in + * dictionary mode", as are objects that point to such Shapes. These Shapes + * are unshared, private to a single object, and immutable except for their + * links in the dictionary list. + * + * All shape lineages are bi-directionally linked, via the |parent| and + * |kids|/|listp| members. + * + * Shape lineages start out life in the property tree. They can be converted + * (by copying) to dictionary mode lists in the following circumstances. + * + * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids + * potential worst cases involving shape lineage mutations. + * + * 2. A property represented by a non-last Shape in a shape lineage is removed + * from an object. (In the last Shape case, obj->shape_ can be easily + * adjusted to point to obj->shape_->parent.) We originally tried lazy + * forking of the property tree, but this blows up for delete/add + * repetitions. + * + * 3. A property represented by a non-last Shape in a shape lineage has its + * attributes modified. + * + * To find the Shape for a particular property of an object initially requires + * a linear search. But if the number of searches starting at any particular + * Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's + * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an + * auxiliary hash table -- the PropertyTable -- that allows faster lookup. + * Furthermore, a PropertyTable is always created for dictionary mode lists, + * and it is attached to the last Shape in the lineage. Property tables for + * property tree Shapes never change, but property tables for dictionary mode + * Shapes can grow and shrink. * - * Let N = P*beta for a compression ratio beta, beta <= 1: + * There used to be a long, math-heavy comment here explaining why property + * trees are more space-efficient than alternatives. This was removed in bug + * 631138; see that bug for the full details. * - * P*beta*(S+L) <> P * (1/alpha) * (S-1) - * beta*(S+L) <> (S-1)/alpha - * beta <> (S-1)/((S+L)*alpha) - * - * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff - * - * beta < 5/(8*alpha) - * - * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An - * average beta from recent Mozilla browser startups was around .6. - * - * Can we reduce L? Observe that the property tree degenerates into a list of - * lists if at most one property Y follows X in all scopes. In or near such a - * case, we waste a word on the right-sibling link outside of the root ply of - * the tree. Note also that the root ply tends to be large, so O(n^2) growth - * searching it is likely, indicating the need for hashing (but with increased - * thread safety costs). - * - * If only K out of N nodes in the property tree have more than one child, we - * could eliminate the sibling link and overlay a children list or hash-table - * pointer on the leftmost-child link (which would then be either null or an - * only-child link; the overlay could be tagged in the low bit of the pointer, - * or flagged elsewhere in the property tree node, although such a flag must - * not be considered when comparing node labels during tree search). - * - * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. - * If K << N, L approaches 1 and the property tree wins if beta < .95. - * - * We observe that fan-out below the root ply of the property tree appears to - * have extremely low degree (see the MeterPropertyTree code that histograms - * child-counts in jsscope.c), so instead of a hash-table we use a linked list - * of child node pointer arrays ("kid chunks"). The details are isolated in - * jspropertytree.h/.cpp; others must treat js::Shape.kids as opaque. - * - * One final twist (can you stand it?): the vast majority (~95% or more) of - * scopes are looked up fewer than three times; in these cases, initializing - * scope->table isn't worth it. So instead of always allocating scope->table, - * we leave it null while initializing all the other scope members as if it - * were non-null and minimal-length. Until a scope is searched - * MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a - * given id, and save on the time and space overhead of creating a hash table. + * Because many Shapes have similar data, there is actually a secondary type + * called a BaseShape that holds some of a Shape's data. Many shapes can share + * a single BaseShape. */ -#define SHAPE_INVALID_SLOT 0xffffffff - namespace js { +/* Limit on the number of slotful properties in an object. */ +static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1; +static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2; + /* * Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to - * minimize footprint. But if a Shape lineage has been searched fewer than - * MAX_LINEAR_SEARCHES times, we use linear search and avoid allocating - * scope->table. + * minimize footprint. */ struct PropertyTable { - static const uint32 MAX_LINEAR_SEARCHES = 7; - static const uint32 MIN_SIZE_LOG2 = 4; - static const uint32 MIN_SIZE = JS_BIT(MIN_SIZE_LOG2); + static const uint32_t MIN_ENTRIES = 7; + static const uint32_t MIN_SIZE_LOG2 = 4; + static const uint32_t MIN_SIZE = JS_BIT(MIN_SIZE_LOG2); int hashShift; /* multiplicative hash shift */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - uint32 freelist; /* SHAPE_INVALID_SLOT or head of slot + uint32_t entryCount; /* number of entries in table */ + uint32_t removedCount; /* removed entry sentinels in table */ + uint32_t freelist; /* SHAPE_INVALID_SLOT or head of slot freelist in owning dictionary-mode object */ js::Shape **entries; /* table of ptrs to shared tree nodes */ - PropertyTable(uint32 nentries) + PropertyTable(uint32_t nentries) : hashShift(JS_DHASH_BITS - MIN_SIZE_LOG2), entryCount(nentries), removedCount(0), @@ -246,11 +167,22 @@ struct PropertyTable { } /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ - uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); } + uint32_t capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); } + + /* Computes the size of the entries array for a given capacity. */ + static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); } + + /* + * This counts the PropertyTable object itself (which must be + * heap-allocated) and its |entries| array. + */ + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const { + return mallocSizeOf(this) + mallocSizeOf(entries); + } /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */ bool needsToGrow() const { - uint32 size = capacity(); + uint32_t size = capacity(); return entryCount + removedCount >= size - (size >> 2); } @@ -279,179 +211,367 @@ namespace js { class PropertyTree; -static inline PropertyOp -CastAsPropertyOp(js::Class *clasp) -{ - return JS_DATA_TO_FUNC_PTR(PropertyOp, clasp); -} - /* * Reuse the API-only JSPROP_INDEX attribute to mean shadowability. */ #define JSPROP_SHADOWABLE JSPROP_INDEX -struct Shape : public js::gc::Cell +/* + * Shapes encode information about both a property lineage *and* a particular + * property. This information is split across the Shape and the BaseShape + * at shape->base(). Both Shape and BaseShape can be either owned or unowned + * by, respectively, the Object or Shape referring to them. + * + * Owned Shapes are used in dictionary objects, and form a doubly linked list + * whose entries are all owned by that dictionary. Unowned Shapes are all in + * the property tree. + * + * Owned BaseShapes are used for shapes which have property tables, including + * the last properties in all dictionaries. Unowned BaseShapes compactly store + * information common to many shapes. In a given compartment there is a single + * BaseShape for each combination of BaseShape information. This information + * is cloned in owned BaseShapes so that information can be quickly looked up + * for a given object or shape without regard to whether the base shape is + * owned or not. + * + * All combinations of owned/unowned Shapes/BaseShapes are possible: + * + * Owned Shape, Owned BaseShape: + * + * Last property in a dictionary object. The BaseShape is transferred from + * property to property as the object's last property changes. + * + * Owned Shape, Unowned BaseShape: + * + * Property in a dictionary object other than the last one. + * + * Unowned Shape, Owned BaseShape: + * + * Property in the property tree which has a property table. + * + * Unowned Shape, Unowned BaseShape: + * + * Property in the property tree which does not have a property table. + * + * BaseShapes additionally encode some information about the referring object + * itself. This includes the object's class, parent and various flags that may + * be set for the object. Except for the class, this information is mutable and + * may change when the object has an established property lineage. On such + * changes the entire property lineage is not updated, but rather only the + * last property (and its base shape). This works because only the object's + * last property is used to query information about the object. Care must be + * taken to call JSObject::canRemoveLastProperty when unwinding an object to + * an earlier property, however. + */ + +class UnownedBaseShape; + +class BaseShape : public js::gc::Cell { - friend struct ::JSObject; - friend struct ::JSFunction; - friend class js::PropertyTree; - friend class js::Bindings; - friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape); - - mutable uint32 shape; /* shape identifier */ - uint32 slotSpan; /* one more than maximum live slot number */ - - /* - * numLinearSearches starts at zero and is incremented initially on each - * search() call. Once numLinearSearches reaches MAX_LINEAR_SEARCHES - * (which is a small integer), the table is created on the next search() - * call, and the table pointer will be easily distinguishable from a small - * integer. The table can also be created when hashifying for dictionary - * mode. - */ - union { - mutable size_t numLinearSearches; - mutable js::PropertyTable *table; + public: + friend struct Shape; + friend struct StackBaseShape; + friend struct StackShape; + + enum Flag { + /* Owned by the referring shape. */ + OWNED_SHAPE = 0x1, + + /* getterObj/setterObj are active in unions below. */ + HAS_GETTER_OBJECT = 0x2, + HAS_SETTER_OBJECT = 0x4, + + /* + * Flags set which describe the referring object. Once set these cannot + * be unset, and are transferred from shape to shape as the object's + * last property changes. + */ + + EXTENSIBLE_PARENTS = 0x8, + DELEGATE = 0x10, + SYSTEM = 0x20, + NOT_EXTENSIBLE = 0x40, + INDEXED = 0x80, + BOUND_FUNCTION = 0x100, + VAROBJ = 0x200, + WATCHED = 0x400, + ITERATED_SINGLETON = 0x800, + NEW_TYPE_UNKNOWN = 0x1000, + UNCACHEABLE_PROTO = 0x2000, + + OBJECT_FLAG_MASK = 0x3ff8 }; - inline void freeTable(JSContext *cx); - - jsid id; + private: + Class *clasp; /* Class of referring object. */ + HeapPtrObject parent; /* Parent of referring object. */ + uint32_t flags; /* Vector of above flags. */ + uint32_t slotSpan_; /* Object slot span for BaseShapes at + * dictionary last properties. */ - protected: union { - js::PropertyOp rawGetter; /* getter and setter hooks or objects */ + js::PropertyOp rawGetter; /* getter hook for shape */ JSObject *getterObj; /* user-defined callable "get" object or - null if shape->hasGetterValue(); or - joined function object if METHOD flag - is set. */ - js::Class *clasp; /* prototype class for empty scope */ + null if shape->hasGetterValue() */ }; union { - js::StrictPropertyOp rawSetter;/* getter is JSObject* and setter is 0 - if shape->isMethod() */ + js::StrictPropertyOp rawSetter; /* setter hook for shape */ JSObject *setterObj; /* user-defined callable "set" object or null if shape->hasSetterValue() */ }; + /* For owned BaseShapes, the canonical unowned BaseShape. */ + HeapPtr unowned_; + + /* For owned BaseShapes, the shape's property table. */ + PropertyTable *table_; + public: - uint32 slot; /* abstract index in object slots */ + void finalize(JSContext *cx, bool background); + + inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags); + inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags, + uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter); + inline BaseShape(const StackBaseShape &base); + + /* Not defined: BaseShapes must not be stack allocated. */ + ~BaseShape(); + + inline BaseShape &operator=(const BaseShape &other); + + bool isOwned() const { return !!(flags & OWNED_SHAPE); } + + inline bool matchesGetterSetter(PropertyOp rawGetter, + StrictPropertyOp rawSetter) const; + + inline void adoptUnowned(UnownedBaseShape *other); + inline void setOwned(UnownedBaseShape *unowned); + + JSObject *getObjectParent() const { return parent; } + uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; } + + bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); } + JSObject *getterObject() const { JS_ASSERT(hasGetterObject()); return getterObj; } + + bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); } + JSObject *setterObject() const { JS_ASSERT(hasSetterObject()); return setterObj; } + + bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != NULL; } + PropertyTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; } + void setTable(PropertyTable *table) { JS_ASSERT(isOwned()); table_ = table; } + + uint32_t slotSpan() const { JS_ASSERT(isOwned()); return slotSpan_; } + void setSlotSpan(uint32_t slotSpan) { JS_ASSERT(isOwned()); slotSpan_ = slotSpan; } + + /* Lookup base shapes from the compartment's baseShapes table. */ + static UnownedBaseShape *getUnowned(JSContext *cx, const StackBaseShape &base); + + /* Get the canonical base shape. */ + inline UnownedBaseShape *unowned(); + + /* Get the canonical base shape for an owned one. */ + inline UnownedBaseShape *baseUnowned(); + + /* Get the canonical base shape for an unowned one (i.e. identity). */ + inline UnownedBaseShape *toUnowned(); + + /* Check that an owned base shape is consistent with its unowned base. */ + inline void assertConsistency(); + + /* For JIT usage */ + static inline size_t offsetOfClass() { return offsetof(BaseShape, clasp); } + static inline size_t offsetOfParent() { return offsetof(BaseShape, parent); } + static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); } + + static inline void writeBarrierPre(BaseShape *shape); + static inline void writeBarrierPost(BaseShape *shape, void *addr); + static inline void readBarrier(BaseShape *shape); + + static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; } + private: - uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ - mutable uint8 flags; /* flags, see below for defines */ - public: - int16 shortid; /* tinyid, or local arg/var index */ + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp)); + } +}; + +class UnownedBaseShape : public BaseShape {}; + +UnownedBaseShape * +BaseShape::unowned() +{ + return isOwned() ? baseUnowned() : toUnowned(); +} + +UnownedBaseShape * +BaseShape::toUnowned() +{ + JS_ASSERT(!isOwned() && !unowned_); return static_cast(this); +} + +UnownedBaseShape * +BaseShape::baseUnowned() +{ + JS_ASSERT(isOwned() && unowned_); return unowned_; +} + +/* Entries for the per-compartment baseShapes set of unowned base shapes. */ +struct StackBaseShape +{ + typedef const StackBaseShape *Lookup; + + uint32_t flags; + Class *clasp; + JSObject *parent; + PropertyOp rawGetter; + StrictPropertyOp rawSetter; + + StackBaseShape(BaseShape *base) + : flags(base->flags & BaseShape::OBJECT_FLAG_MASK), + clasp(base->clasp), + parent(base->parent), + rawGetter(NULL), + rawSetter(NULL) + {} + + StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags) + : flags(objectFlags), + clasp(clasp), + parent(parent), + rawGetter(NULL), + rawSetter(NULL) + {} + + inline StackBaseShape(Shape *shape); + + inline void updateGetterSetter(uint8_t attrs, + PropertyOp rawGetter, + StrictPropertyOp rawSetter); + + static inline HashNumber hash(const StackBaseShape *lookup); + static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup); +}; + +typedef HashSet, + StackBaseShape, + SystemAllocPolicy> BaseShapeSet; + +struct Shape : public js::gc::Cell +{ + friend struct ::JSObject; + friend struct ::JSFunction; + friend class js::StaticBlockObject; + friend class js::PropertyTree; + friend class js::Bindings; + friend struct js::StackShape; + friend struct js::StackBaseShape; protected: - mutable js::Shape *parent; /* parent node, reverse for..in order */ + HeapPtrBaseShape base_; + HeapId propid_; + + JS_ENUM_HEADER(SlotInfo, uint32_t) + { + /* Number of fixed slots in objects with this shape. */ + FIXED_SLOTS_MAX = 0x1f, + FIXED_SLOTS_SHIFT = 27, + FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT), + + /* + * numLinearSearches starts at zero and is incremented initially on + * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX, + * the table is created on the next search() call. The table can also + * be created when hashifying for dictionary mode. + */ + LINEAR_SEARCHES_MAX = 0x7, + LINEAR_SEARCHES_SHIFT = 24, + LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT, + + /* + * Mask to get the index in object slots for shapes which hasSlot(). + * For !hasSlot() shapes in the property tree with a parent, stores the + * parent's slot index (which may be invalid), and invalid for all + * other shapes. + */ + SLOT_MASK = JS_BIT(24) - 1 + } JS_ENUM_FOOTER(SlotInfo); + + uint32_t slotInfo; /* mask of above info */ + uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */ + uint8_t flags; /* flags, see below for defines */ + int16_t shortid_; /* tinyid, or local arg/var index */ + + HeapPtrShape parent; /* parent node, reverse for..in order */ + /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */ union { - mutable js::KidsPointer kids; /* null, single child, or a tagged ptr - to many-kids data structure */ - mutable js::Shape **listp; /* dictionary list starting at lastProp - has a double-indirect back pointer, - either to shape->parent if not last, - else to obj->lastProp */ + KidsPointer kids; /* null, single child, or a tagged ptr + to many-kids data structure */ + HeapPtrShape *listp; /* dictionary list starting at shape_ + has a double-indirect back pointer, + either to the next shape's parent if not + last, else to obj->shape_ */ }; - static inline js::Shape **search(JSRuntime *rt, js::Shape **startp, jsid id, - bool adding = false); - static js::Shape *newDictionaryShape(JSContext *cx, const js::Shape &child, js::Shape **listp); - static js::Shape *newDictionaryList(JSContext *cx, js::Shape **listp); + static inline Shape *search(JSContext *cx, Shape *start, jsid id, + Shape ***pspp, bool adding = false); - inline void removeFromDictionary(JSObject *obj) const; - inline void insertIntoDictionary(js::Shape **dictp); + inline void removeFromDictionary(JSObject *obj); + inline void insertIntoDictionary(HeapPtrShape *dictp); - js::Shape *getChild(JSContext *cx, const js::Shape &child, js::Shape **listp); + inline void initDictionaryShape(const StackShape &child, uint32_t nfixed, + HeapPtrShape *dictp); - bool hashify(JSRuntime *rt); + Shape *getChildBinding(JSContext *cx, const StackShape &child); - bool hasTable() const { - /* A valid pointer should be much bigger than MAX_LINEAR_SEARCHES. */ - return numLinearSearches > PropertyTable::MAX_LINEAR_SEARCHES; - } + /* Replace the base shape of the last shape in a non-dictionary lineage with base. */ + static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base, + JSObject *proto, Shape *shape); - js::PropertyTable *getTable() const { - JS_ASSERT(hasTable()); - return table; - } + bool hashify(JSContext *cx); + void handoffTableTo(Shape *newShape); - void setTable(js::PropertyTable *t) const { - JS_ASSERT_IF(t && t->freelist != SHAPE_INVALID_SLOT, t->freelist < slotSpan); - table = t; - } + inline void setParent(js::Shape *p); - /* - * Setter for parent. The challenge is to maintain JSObjectMap::slotSpan in - * the face of arbitrary slot order. - * - * By induction, an empty shape has a slotSpan member correctly computed as - * JSCLASS_FREE(clasp) -- see EmptyShape's constructor in jsscopeinlines.h. - * This is the basis case, where p is null. - * - * Any child shape, whether in a shape tree or in a dictionary list, must - * have a slotSpan either one greater than its slot value (if the child's - * slot is SHAPE_INVALID_SLOT, this will yield 0; the static assertion - * below enforces this), or equal to its parent p's slotSpan, whichever is - * greater. This is the inductive step. - * - * If we maintained shape paths such that parent slot was always one less - * than child slot, possibly with an exception for SHAPE_INVALID_SLOT slot - * values where we would use another way of computing slotSpan based on the - * PropertyTable (as JSC does), then we would not need to store slotSpan in - * Shape (to be precise, in its base struct, JSobjectMap). - * - * But we currently scramble slots along shape paths due to resolve-based - * creation of shapes mapping reserved slots, and we do not have the needed - * PropertyTable machinery to use as an alternative when parent slot is not - * one less than child slot. This machinery is neither simple nor free, as - * it must involve creating a table for any slot-less transition and then - * pinning the table to its shape. - * - * Use of 'delete' can scramble slots along the shape lineage too, although - * it always switches the target object to dictionary mode, so the cost of - * a pinned table is less onerous. - * - * Note that allocating a uint32 slotSpan member in JSObjectMap takes no - * net extra space on 64-bit targets (it packs with shape). And on 32-bit - * targets, adding slotSpan to JSObjectMap takes no gross extra space, - * because Shape rounds up to an even number of 32-bit words (required for - * GC-thing and js::Value allocation in any event) on 32-bit targets. - * - * So in terms of space, we can afford to maintain both slotSpan and slot, - * but it might be better if we eliminated slotSpan using slot combined - * with an auxiliary mechanism based on table. - */ - void setParent(js::Shape *p) { - JS_STATIC_ASSERT(uint32(SHAPE_INVALID_SLOT) == ~uint32(0)); - if (p) - slotSpan = JS_MAX(p->slotSpan, slot + 1); - JS_ASSERT(slotSpan < JSObject::NSLOTS_LIMIT); - parent = p; + bool ensureOwnBaseShape(JSContext *cx) { + if (base()->isOwned()) + return true; + return makeOwnBaseShape(cx); } + bool makeOwnBaseShape(JSContext *cx); + public: - static JS_FRIEND_DATA(Shape) sharedNonNative; + bool hasTable() const { return base()->hasTable(); } + js::PropertyTable &table() const { return base()->table(); } + + void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, + size_t *propTableSize, size_t *kidsSize) const { + *propTableSize = hasTable() ? table().sizeOfIncludingThis(mallocSizeOf) : 0; + *kidsSize = !inDictionary() && kids.isHash() + ? kids.toHash()->sizeOfIncludingThis(mallocSizeOf) + : 0; + } - bool isNative() const { return this != &sharedNonNative; } + bool isNative() const { + JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative()); + return !(flags & NON_NATIVE); + } - const js::Shape *previous() const { + const HeapPtrShape &previous() const { return parent; } class Range { protected: friend struct Shape; - const Shape *cursor; - const Shape *end; public: Range(const Shape *shape) : cursor(shape) { } bool empty() const { - JS_ASSERT_IF(!cursor->parent, JSID_IS_EMPTY(cursor->id)); - return !cursor->parent; + return cursor->isEmptyShape(); } const Shape &front() const { @@ -463,12 +583,32 @@ struct Shape : public js::gc::Cell JS_ASSERT(!empty()); cursor = cursor->parent; } + + class Root { + js::Root cursorRoot; + public: + Root(JSContext *cx, Range *range) + : cursorRoot(cx, &range->cursor) + {} + }; }; Range all() const { return Range(this); } + Class *getObjectClass() const { return base()->clasp; } + JSObject *getObjectParent() const { return base()->parent; } + + static Shape *setObjectParent(JSContext *cx, JSObject *obj, JSObject *proto, Shape *last); + static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last); + + uint32_t getObjectFlags() const { return base()->getObjectFlags(); } + bool hasObjectFlag(BaseShape::Flag flag) const { + JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK)); + return !!(base()->flags & flag); + } + protected: /* * Implementation-private bits stored in shape->flags. See public: enum {} @@ -476,90 +616,175 @@ struct Shape : public js::gc::Cell * with these bits. */ enum { - SHARED_EMPTY = 0x01, + /* Property is placeholder for a non-native class. */ + NON_NATIVE = 0x01, /* Property stored in per-object dictionary, not shared property tree. */ IN_DICTIONARY = 0x02, - /* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */ - FROZEN = 0x04 + UNUSED_BITS = 0x3C }; - Shape(jsid id, js::PropertyOp getter, js::StrictPropertyOp setter, uint32 slot, uintN attrs, - uintN flags, intN shortid, uint32 shape = INVALID_SHAPE, uint32 slotSpan = 0); + /* Get a shape identical to this one, without parent/kids information. */ + Shape(const StackShape &other, uint32_t nfixed); /* Used by EmptyShape (see jsscopeinlines.h). */ - Shape(JSCompartment *comp, Class *aclasp); - - /* Used by sharedNonNative. */ - explicit Shape(uint32 shape); + Shape(UnownedBaseShape *base, uint32_t nfixed); - bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; } - bool frozen() const { return (flags & FROZEN) != 0; } - void setFrozen() { flags |= FROZEN; } + /* Copy constructor disabled, to avoid misuse of the above form. */ + Shape(const Shape &other) MOZ_DELETE; - bool isEmptyShape() const { JS_ASSERT_IF(!parent, JSID_IS_EMPTY(id)); return !parent; } + /* + * Whether this shape has a valid slot value. This may be true even if + * !hasSlot() (see SlotInfo comment above), and may be false even if + * hasSlot() if the shape is being constructed and has not had a slot + * assigned yet. After construction, hasSlot() implies !hasMissingSlot(). + */ + bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; } public: /* Public bits stored in shape->flags. */ enum { - ALIAS = 0x20, HAS_SHORTID = 0x40, METHOD = 0x80, - PUBLIC_FLAGS = ALIAS | HAS_SHORTID | METHOD + PUBLIC_FLAGS = HAS_SHORTID | METHOD }; + bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; } uintN getFlags() const { return flags & PUBLIC_FLAGS; } - bool isAlias() const { return (flags & ALIAS) != 0; } bool hasShortID() const { return (flags & HAS_SHORTID) != 0; } - bool isMethod() const { return (flags & METHOD) != 0; } - JSObject &methodObject() const { JS_ASSERT(isMethod()); return *getterObj; } + /* + * A shape has a method barrier when some compiler-created "null closure" + * function objects (functions that do not use lexical bindings above their + * scope, only free variable names) that have a correct JSSLOT_PARENT value + * thanks to the COMPILE_N_GO optimization are stored in objects without + * cloning. + * + * The de-facto standard JS language requires each evaluation of such a + * closure to result in a unique (according to === and observable effects) + * function object. When storing a function to a property, we use method + * shapes to speculate that these effects will never be observed: the + * property will only be used in calls, and f.callee will not be used + * to get a handle on the object. + * + * If either a non-call use or callee access occurs, then the function is + * cloned and the object is reshaped with a non-method property. + * + * Note that method shapes do not imply the object has a particular + * uncloned function, just that the object has *some* uncloned function + * in the shape's slot. + */ + bool isMethod() const { + JS_ASSERT_IF(flags & METHOD, !base()->rawGetter); + return (flags & METHOD) != 0; + } - js::PropertyOp getter() const { return rawGetter; } - bool hasDefaultGetter() const { return !rawGetter; } - js::PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return rawGetter; } - JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj; } + PropertyOp getter() const { return base()->rawGetter; } + bool hasDefaultGetterOrIsMethod() const { return !base()->rawGetter; } + bool hasDefaultGetter() const { return !base()->rawGetter && !isMethod(); } + PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; } + JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; } // Per ES5, decode null getterObj as the undefined value, which encodes as null. - js::Value getterValue() const { + Value getterValue() const { JS_ASSERT(hasGetterValue()); - return getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue(); + return base()->getterObj ? js::ObjectValue(*base()->getterObj) : js::UndefinedValue(); } - js::Value getterOrUndefined() const { - return hasGetterValue() && getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue(); + Value getterOrUndefined() const { + return (hasGetterValue() && base()->getterObj) + ? ObjectValue(*base()->getterObj) + : UndefinedValue(); } - js::StrictPropertyOp setter() const { return rawSetter; } - bool hasDefaultSetter() const { return !rawSetter; } - js::StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return rawSetter; } - JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj; } + StrictPropertyOp setter() const { return base()->rawSetter; } + bool hasDefaultSetter() const { return !base()->rawSetter; } + StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; } + JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; } // Per ES5, decode null setterObj as the undefined value, which encodes as null. - js::Value setterValue() const { + Value setterValue() const { JS_ASSERT(hasSetterValue()); - return setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue(); + return base()->setterObj ? js::ObjectValue(*base()->setterObj) : js::UndefinedValue(); } - js::Value setterOrUndefined() const { - return hasSetterValue() && setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue(); + Value setterOrUndefined() const { + return (hasSetterValue() && base()->setterObj) + ? ObjectValue(*base()->setterObj) + : UndefinedValue(); } - inline JSDHashNumber hash() const; - inline bool matches(const js::Shape *p) const; - inline bool matchesParamsAfterId(js::PropertyOp agetter, js::StrictPropertyOp asetter, - uint32 aslot, uintN aattrs, uintN aflags, + void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs); + + inline bool matches(const Shape *other) const; + inline bool matches(const StackShape &other) const; + inline bool matchesParamsAfterId(BaseShape *base, + uint32_t aslot, uintN aattrs, uintN aflags, intN ashortid) const; bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const; bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const; - inline bool isSharedPermanent() const; + BaseShape *base() const { return base_; } bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } + uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); } + uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; } + + bool isEmptyShape() const { + JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot()); + return JSID_IS_EMPTY(propid_); + } - uint8 attributes() const { return attrs; } + uint32_t slotSpan() const { + JS_ASSERT(!inDictionary()); + uint32_t free = JSSLOT_FREE(getObjectClass()); + return hasMissingSlot() ? free : Max(free, maybeSlot() + 1); + } + + void setSlot(uint32_t slot) { + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); + slotInfo = slotInfo & ~Shape::SLOT_MASK; + slotInfo = slotInfo | slot; + } + + uint32_t numFixedSlots() const { + return (slotInfo >> FIXED_SLOTS_SHIFT); + } + + void setNumFixedSlots(uint32_t nfixed) { + JS_ASSERT(nfixed < FIXED_SLOTS_MAX); + slotInfo = slotInfo & ~FIXED_SLOTS_MASK; + slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT); + } + + uint32_t numLinearSearches() const { + return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT; + } + + void incrementNumLinearSearches() { + uint32_t count = numLinearSearches(); + JS_ASSERT(count < LINEAR_SEARCHES_MAX); + slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK; + slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT); + } + + jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); } + const HeapId &maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } + + int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); } + int16_t maybeShortid() const { return shortid_; } + + /* + * If SHORTID is set in shape->flags, we use shape->shortid rather + * than id when calling shape's getter or setter. + */ + jsid getUserId() const { + return hasShortID() ? INT_TO_JSID(shortid()) : propid(); + } + + uint8_t attributes() const { return attrs; } bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; } bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; } bool writable() const { @@ -569,10 +794,6 @@ struct Shape : public js::gc::Cell bool hasGetterValue() const { return attrs & JSPROP_GETTER; } bool hasSetterValue() const { return attrs & JSPROP_SETTER; } - bool hasDefaultGetterOrIsMethod() const { - return hasDefaultGetter() || isMethod(); - } - bool isDataDescriptor() const { return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0; } @@ -581,7 +802,7 @@ struct Shape : public js::gc::Cell } /* - * For ES5 compatibility, we allow properties with JSPropertyOp-flavored + * For ES5 compatibility, we allow properties with PropertyOp-flavored * setters to be shadowed when set. The "own" property thereby created in * the directly referenced object will have the same getter and setter as * the prototype property. See bug 552432. @@ -591,78 +812,232 @@ struct Shape : public js::gc::Cell return hasSlot() || (attrs & JSPROP_SHADOWABLE); } - uint32 entryCount() const { + /* + * Sometimes call objects and run-time block objects need unique shapes, but + * sometimes they don't. + * + * Property cache entries only record the shapes of the first and last + * objects along the search path, so if the search traverses more than those + * two objects, then those first and last shapes must determine the shapes + * of everything else along the path. The js_PurgeScopeChain stuff takes + * care of making this work, but that suffices only because we require that + * start points with the same shape have the same successor object in the + * search path --- a cache hit means the starting shapes were equal, which + * means the seach path tail (everything but the first object in the path) + * was shared, which in turn means the effects of a purge will be seen by + * all affected starting search points. + * + * For call and run-time block objects, the "successor object" is the scope + * chain parent. Unlike prototype objects (of which there are usually few), + * scope chain parents are created frequently (possibly on every call), so + * following the shape-implies-parent rule blindly would lead one to give + * every call and block its own shape. + * + * In many cases, however, it's not actually necessary to give call and + * block objects their own shapes, and we can do better. If the code will + * always be used with the same global object, and none of the enclosing + * call objects could have bindings added to them at runtime (by direct eval + * calls or function statements), then we can use a fixed set of shapes for + * those objects. You could think of the shapes in the functions' bindings + * and compile-time blocks as uniquely identifying the global object(s) at + * the end of the scope chain. + * + * (In fact, some JSScripts we do use against multiple global objects (see + * bug 618497), and using the fixed shapes isn't sound there.) + * + * In deciding whether a call or block has any extensible parents, we + * actually only need to consider enclosing calls; blocks are never + * extensible, and the other sorts of objects that appear in the scope + * chains ('with' blocks, say) are not CacheableNonGlobalScopes. + * + * If the hasExtensibleParents flag is set for the last property in a + * script's bindings or a compiler-generated Block object, then created + * Call or Block objects need unique shapes. If the flag is clear, then we + * can use lastBinding's shape. + */ + static Shape *setExtensibleParents(JSContext *cx, Shape *shape); + bool extensibleParents() const { return !!(base()->flags & BaseShape::EXTENSIBLE_PARENTS); } + + uint32_t entryCount() const { if (hasTable()) - return getTable()->entryCount; + return table().entryCount; const js::Shape *shape = this; - uint32 count = 0; + uint32_t count = 0; for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) ++count; return count; } + bool isBigEnoughForAPropertyTable() const { + JS_ASSERT(!hasTable()); + const js::Shape *shape = this; + uint32_t count = 0; + for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) { + ++count; + if (count >= PropertyTable::MIN_ENTRIES) + return true; + } + return false; + } + #ifdef DEBUG void dump(JSContext *cx, FILE *fp) const; void dumpSubtree(JSContext *cx, int level, FILE *fp) const; #endif - void finalize(JSContext *cx); + void finalize(JSContext *cx, bool background); void removeChild(js::Shape *child); + + static inline void writeBarrierPre(const Shape *shape); + static inline void writeBarrierPost(const Shape *shape, void *addr); + + /* + * All weak references need a read barrier for incremental GC. This getter + * method implements the read barrier. It's used to obtain initial shapes + * from the compartment. + */ + static inline void readBarrier(const Shape *shape); + + static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; } + + /* For JIT usage */ + static inline size_t offsetOfBase() { return offsetof(Shape, base_); } + + private: + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base)); + JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo)); + JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT); + } }; struct EmptyShape : public js::Shape { - EmptyShape(JSCompartment *comp, js::Class *aclasp); + EmptyShape(UnownedBaseShape *base, uint32_t nfixed); - js::Class *getClass() const { return clasp; }; + /* + * Lookup an initial shape matching the given parameters, creating an empty + * shape if none was found. + */ + static Shape *getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, + JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0); - static EmptyShape *create(JSContext *cx, js::Class *clasp) { - js::Shape *eprop = JS_PROPERTY_TREE(cx).newShape(cx); - if (!eprop) - return NULL; - return new (eprop) EmptyShape(cx->compartment, clasp); - } + /* + * Reinsert an alternate initial shape, to be returned by future + * getInitialShape calls, until the new shape becomes unreachable in a GC + * and the table entry is purged. + */ + static void insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto); +}; - static EmptyShape *ensure(JSContext *cx, js::Class *clasp, EmptyShape **shapep) { - EmptyShape *shape = *shapep; - if (!shape) { - if (!(shape = create(cx, clasp))) - return NULL; - return *shapep = shape; - } - return shape; - } +/* + * Entries for the per-compartment initialShapes set indexing initial shapes + * for objects in the compartment and the associated types. + */ +struct InitialShapeEntry +{ + /* + * Initial shape to give to the object. This is an empty shape, except for + * certain classes (e.g. String, RegExp) which may add certain baked-in + * properties. + */ + ReadBarriered shape; - static EmptyShape *getEmptyArgumentsShape(JSContext *cx) { - return ensure(cx, &js_ArgumentsClass, &cx->compartment->emptyArgumentsShape); - } + /* + * Matching prototype for the entry. The shape of an object determines its + * prototype, but the prototype cannot be determined from the shape itself. + */ + JSObject *proto; + + /* State used to determine a match on an initial shape. */ + struct Lookup { + Class *clasp; + JSObject *proto; + JSObject *parent; + uint32_t nfixed; + uint32_t baseFlags; + Lookup(Class *clasp, JSObject *proto, JSObject *parent, uint32_t nfixed, + uint32_t baseFlags) + : clasp(clasp), proto(proto), parent(parent), + nfixed(nfixed), baseFlags(baseFlags) + {} + }; - static EmptyShape *getEmptyBlockShape(JSContext *cx) { - return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape); - } + static inline HashNumber hash(const Lookup &lookup); + static inline bool match(const InitialShapeEntry &key, const Lookup &lookup); +}; - static EmptyShape *getEmptyCallShape(JSContext *cx) { - return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape); - } +typedef HashSet InitialShapeSet; - static EmptyShape *getEmptyDeclEnvShape(JSContext *cx) { - return ensure(cx, &js_DeclEnvClass, &cx->compartment->emptyDeclEnvShape); +struct StackShape +{ + UnownedBaseShape *base; + jsid propid; + uint32_t slot_; + uint8_t attrs; + uint8_t flags; + int16_t shortid; + + StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot, + uint32_t nfixed, uintN attrs, uintN flags, intN shortid) + : base(base), + propid(propid), + slot_(slot), + attrs(uint8_t(attrs)), + flags(uint8_t(flags)), + shortid(int16_t(shortid)) + { + JS_ASSERT(base); + JS_ASSERT(!JSID_IS_VOID(propid)); + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); } - static EmptyShape *getEmptyEnumeratorShape(JSContext *cx) { - return ensure(cx, &js_IteratorClass, &cx->compartment->emptyEnumeratorShape); + StackShape(const Shape *shape) + : base(shape->base()->unowned()), + propid(shape->maybePropid()), + slot_(shape->slotInfo & Shape::SLOT_MASK), + attrs(shape->attrs), + flags(shape->flags), + shortid(shape->shortid_) + {} + + bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } + bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; } + + uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; } + uint32_t maybeSlot() const { return slot_; } + + uint32_t slotSpan() const { + uint32_t free = JSSLOT_FREE(base->clasp); + return hasMissingSlot() ? free : (maybeSlot() + 1); } - static EmptyShape *getEmptyWithShape(JSContext *cx) { - return ensure(cx, &js_WithClass, &cx->compartment->emptyWithShape); + void setSlot(uint32_t slot) { + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); + slot_ = slot; } + + inline JSDHashNumber hash() const; +}; + +/* Rooter for stack allocated shapes. */ +class RootStackShape +{ + Root baseShapeRoot; + Root propidRoot; + + public: + RootStackShape(JSContext *cx, const StackShape *shape) + : baseShapeRoot(cx, &shape->base), + propidRoot(cx, &shape->propid) + {} }; } /* namespace js */ /* js::Shape pointer tag bit indicating a collision. */ -#define SHAPE_COLLISION (jsuword(1)) +#define SHAPE_COLLISION (uintptr_t(1)) #define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION) /* Macros to get and set shape pointer values and collision flags. */ @@ -670,115 +1045,57 @@ struct EmptyShape : public js::Shape #define SHAPE_IS_REMOVED(shape) ((shape) == SHAPE_REMOVED) #define SHAPE_IS_LIVE(shape) ((shape) > SHAPE_REMOVED) #define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (js::Shape *) \ - (jsuword(shape) | SHAPE_COLLISION)) -#define SHAPE_HAD_COLLISION(shape) (jsuword(shape) & SHAPE_COLLISION) + (uintptr_t(shape) | SHAPE_COLLISION)) +#define SHAPE_HAD_COLLISION(shape) (uintptr_t(shape) & SHAPE_COLLISION) #define SHAPE_FETCH(spp) SHAPE_CLEAR_COLLISION(*(spp)) #define SHAPE_CLEAR_COLLISION(shape) \ - ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION)) + ((js::Shape *) (uintptr_t(shape) & ~SHAPE_COLLISION)) #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape) \ - (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp)))) - -/* - * If SHORTID is set in shape->flags, we use shape->shortid rather - * than id when calling shape's getter or setter. - */ -#define SHAPE_USERID(shape) \ - ((shape)->hasShortID() ? INT_TO_JSID((shape)->shortid) \ - : (shape)->id) - -extern uint32 -js_GenerateShape(JSRuntime *rt); - -extern uint32 -js_GenerateShape(JSContext *cx); - -#ifdef DEBUG -struct JSScopeStats { - jsrefcount searches; - jsrefcount hits; - jsrefcount misses; - jsrefcount hashes; - jsrefcount hashHits; - jsrefcount hashMisses; - jsrefcount steps; - jsrefcount stepHits; - jsrefcount stepMisses; - jsrefcount initSearches; - jsrefcount changeSearches; - jsrefcount tableAllocFails; - jsrefcount toDictFails; - jsrefcount wrapWatchFails; - jsrefcount adds; - jsrefcount addFails; - jsrefcount puts; - jsrefcount redundantPuts; - jsrefcount putFails; - jsrefcount changes; - jsrefcount changePuts; - jsrefcount changeFails; - jsrefcount compresses; - jsrefcount grows; - jsrefcount removes; - jsrefcount removeFrees; - jsrefcount uselessRemoves; - jsrefcount shrinks; -}; - -extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats; - -# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) -#else -# define METER(x) /* nothing */ -#endif + (*(spp) = (js::Shape *) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp)))) namespace js { -JS_ALWAYS_INLINE js::Shape ** -Shape::search(JSRuntime *rt, js::Shape **startp, jsid id, bool adding) +inline Shape * +Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding) { - js::Shape *start = *startp; - METER(searches); + if (start->inDictionary()) { + *pspp = start->table().search(id, adding); + return SHAPE_FETCH(*pspp); + } - if (start->hasTable()) - return start->getTable()->search(id, adding); + *pspp = NULL; - if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) { - if (start->hashify(rt)) - return start->getTable()->search(id, adding); - /* OOM! Don't increment numLinearSearches, to keep hasTable() false. */ - JS_ASSERT(!start->hasTable()); - } else { - JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES); - start->numLinearSearches++; + if (start->hasTable()) { + Shape **spp = start->table().search(id, adding); + return SHAPE_FETCH(spp); } - /* - * Not enough searches done so far to justify hashing: search linearly - * from *startp. - * - * We don't use a Range here, or stop at null parent (the empty shape - * at the end), to avoid an extra load per iteration just to save a - * load and id test at the end (when missing). - */ - js::Shape **spp; - for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) { - if (shape->id == id) { - METER(hits); - return spp; + if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) { + if (start->isBigEnoughForAPropertyTable()) { + RootShape startRoot(cx, &start); + RootId idRoot(cx, &id); + if (start->hashify(cx)) { + Shape **spp = start->table().search(id, adding); + return SHAPE_FETCH(spp); + } } + /* + * No table built -- there weren't enough entries, or OOM occurred. + * Don't increment numLinearSearches, to keep hasTable() false. + */ + JS_ASSERT(!start->hasTable()); + } else { + start->incrementNumLinearSearches(); } - METER(misses); - return spp; -} -#undef METER + for (Shape *shape = start; shape; shape = shape->parent) { + if (shape->maybePropid() == id) + return shape; + } -inline bool -Shape::isSharedPermanent() const -{ - return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0; + return NULL; } } // namespace js @@ -788,4 +1105,33 @@ Shape::isSharedPermanent() const #pragma warning(pop) #endif +inline js::Class * +JSObject::getClass() const +{ + return lastProperty()->getObjectClass(); +} + +inline JSClass * +JSObject::getJSClass() const +{ + return Jsvalify(getClass()); +} + +inline bool +JSObject::hasClass(const js::Class *c) const +{ + return getClass() == c; +} + +inline const js::ObjectOps * +JSObject::getOps() const +{ + return &getClass()->ops; +} + +namespace JS { + template<> class AnchorPermitted { }; + template<> class AnchorPermitted { }; +} + #endif /* jsscope_h___ */ diff --git a/deps/mozjs/js/src/jsscopeinlines.h b/deps/mozjs/js/src/jsscopeinlines.h index 851e152b3f7..8fe3cf8e49e 100644 --- a/deps/mozjs/js/src/jsscopeinlines.h +++ b/deps/mozjs/js/src/jsscopeinlines.h @@ -41,6 +41,8 @@ #define jsscopeinlines_h___ #include + +#include "jsarray.h" #include "jsbool.h" #include "jscntxt.h" #include "jsdbgapi.h" @@ -48,228 +50,245 @@ #include "jsobj.h" #include "jsscope.h" #include "jsgc.h" +#include "jsgcmark.h" +#include "vm/ArgumentsObject.h" +#include "vm/ScopeObject.h" #include "vm/StringObject.h" #include "jscntxtinlines.h" #include "jsgcinlines.h" #include "jsobjinlines.h" -inline void -js::Shape::freeTable(JSContext *cx) +#include "vm/ScopeObject-inl.h" + +namespace js { + +inline +BaseShape::BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags) { - if (hasTable()) { - cx->delete_(getTable()); - setTable(NULL); - } + JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK)); + PodZero(this); + this->clasp = clasp; + this->parent = parent; + this->flags = objectFlags; } -inline js::EmptyShape * -JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind) +inline +BaseShape::BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags, + uint8_t attrs, js::PropertyOp rawGetter, js::StrictPropertyOp rawSetter) { - JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); - int i = kind - js::gc::FINALIZE_OBJECT0; - - if (!emptyShapes) { - emptyShapes = (js::EmptyShape**) - cx->calloc_(sizeof(js::EmptyShape*) * js::gc::FINALIZE_FUNCTION_AND_OBJECT_LAST); - if (!emptyShapes) - return NULL; - - /* - * Always fill in emptyShapes[0], so canProvideEmptyShape works. - * Other empty shapes are filled in lazily. - */ - emptyShapes[0] = js::EmptyShape::create(cx, aclasp); - if (!emptyShapes[0]) { - cx->free_(emptyShapes); - emptyShapes = NULL; - return NULL; - } + JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK)); + PodZero(this); + this->clasp = clasp; + this->parent = parent; + this->flags = objectFlags; + this->rawGetter = rawGetter; + this->rawSetter = rawSetter; + if ((attrs & JSPROP_GETTER) && rawGetter) { + this->flags |= HAS_GETTER_OBJECT; + JSObject::writeBarrierPost(this->getterObj, &this->getterObj); } + if ((attrs & JSPROP_SETTER) && rawSetter) { + this->flags |= HAS_SETTER_OBJECT; + JSObject::writeBarrierPost(this->setterObj, &this->setterObj); + } +} - JS_ASSERT(aclasp == emptyShapes[0]->getClass()); - - if (!emptyShapes[i]) { - emptyShapes[i] = js::EmptyShape::create(cx, aclasp); - if (!emptyShapes[i]) - return NULL; +inline +BaseShape::BaseShape(const StackBaseShape &base) +{ + PodZero(this); + this->clasp = base.clasp; + this->parent = base.parent; + this->flags = base.flags; + this->rawGetter = base.rawGetter; + this->rawSetter = base.rawSetter; + if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter) { + JSObject::writeBarrierPost(this->getterObj, &this->getterObj); + } + if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter) { + JSObject::writeBarrierPost(this->setterObj, &this->setterObj); } +} - return emptyShapes[i]; +inline BaseShape & +BaseShape::operator=(const BaseShape &other) +{ + clasp = other.clasp; + parent = other.parent; + flags = other.flags; + slotSpan_ = other.slotSpan_; + if (flags & HAS_GETTER_OBJECT) { + getterObj = other.getterObj; + JSObject::writeBarrierPost(getterObj, &getterObj); + } else { + rawGetter = other.rawGetter; + } + if (flags & HAS_SETTER_OBJECT) { + setterObj = other.setterObj; + JSObject::writeBarrierPost(setterObj, &setterObj); + } else { + rawSetter = other.rawSetter; + } + return *this; } inline bool -JSObject::canProvideEmptyShape(js::Class *aclasp) +BaseShape::matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const { - return !emptyShapes || emptyShapes[0]->getClass() == aclasp; + return rawGetter == this->rawGetter && rawSetter == this->rawSetter; } -inline void -JSObject::updateShape(JSContext *cx) +inline +StackBaseShape::StackBaseShape(Shape *shape) + : flags(shape->getObjectFlags()), + clasp(shape->getObjectClass()), + parent(shape->getObjectParent()) { - JS_ASSERT(isNative()); - js::LeaveTraceIfGlobalObject(cx, this); - if (hasOwnShape()) - setOwnShape(js_GenerateShape(cx)); - else - objShape = lastProp->shape; + updateGetterSetter(shape->attrs, shape->getter(), shape->setter()); } inline void -JSObject::updateFlags(const js::Shape *shape, bool isDefinitelyAtom) +StackBaseShape::updateGetterSetter(uint8_t attrs, + PropertyOp rawGetter, + StrictPropertyOp rawSetter) { - jsuint index; - if (!isDefinitelyAtom && js_IdIsIndex(shape->id, &index)) - setIndexed(); - - if (shape->isMethod()) - setMethodBarrier(); + flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT); + if ((attrs & JSPROP_GETTER) && rawGetter) + flags |= BaseShape::HAS_GETTER_OBJECT; + if ((attrs & JSPROP_SETTER) && rawSetter) + flags |= BaseShape::HAS_SETTER_OBJECT; + + this->rawGetter = rawGetter; + this->rawSetter = rawSetter; } inline void -JSObject::extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom) +BaseShape::adoptUnowned(UnownedBaseShape *other) { - setLastProperty(shape); - updateFlags(shape, isDefinitelyAtom); - updateShape(cx); -} + /* + * This is a base shape owned by a dictionary object, update it to reflect the + * unowned base shape of a new last property. + */ + JS_ASSERT(isOwned()); + DebugOnly flags = getObjectFlags(); + JS_ASSERT((flags & other->getObjectFlags()) == flags); -namespace js { + uint32_t span = slotSpan(); + PropertyTable *table = &this->table(); -inline bool -StringObject::init(JSContext *cx, JSString *str) -{ - JS_ASSERT(nativeEmpty()); + *this = *other; + setOwned(other); + setTable(table); + setSlotSpan(span); - const Shape **shapep = &cx->compartment->initialStringShape; - if (*shapep) { - setLastProperty(*shapep); - } else { - *shapep = assignInitialShape(cx); - if (!*shapep) - return false; - } - JS_ASSERT(*shapep == lastProperty()); - JS_ASSERT(!nativeEmpty()); - JS_ASSERT(nativeLookup(ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot == LENGTH_SLOT); + assertConsistency(); +} - setStringThis(str); - return true; +inline void +BaseShape::setOwned(UnownedBaseShape *unowned) +{ + flags |= OWNED_SHAPE; + this->unowned_ = unowned; } -inline -Shape::Shape(jsid id, js::PropertyOp getter, js::StrictPropertyOp setter, uint32 slot, uintN attrs, - uintN flags, intN shortid, uint32 shape, uint32 slotSpan) - : shape(shape), - slotSpan(slotSpan), - numLinearSearches(0), - id(id), - rawGetter(getter), - rawSetter(setter), - slot(slot), - attrs(uint8(attrs)), - flags(uint8(flags)), - shortid(int16(shortid)), - parent(NULL) +inline void +BaseShape::assertConsistency() { - JS_ASSERT_IF(slotSpan != SHAPE_INVALID_SLOT, slotSpan < JSObject::NSLOTS_LIMIT); - JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable()); - JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable()); - kids.setNull(); +#ifdef DEBUG + if (isOwned()) { + UnownedBaseShape *unowned = baseUnowned(); + JS_ASSERT(hasGetterObject() == unowned->hasGetterObject()); + JS_ASSERT(hasSetterObject() == unowned->hasSetterObject()); + JS_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject()); + JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject()); + JS_ASSERT(getObjectParent() == unowned->getObjectParent()); + JS_ASSERT(getObjectFlags() == unowned->getObjectFlags()); + } +#endif } inline -Shape::Shape(JSCompartment *comp, Class *aclasp) - : shape(js_GenerateShape(comp->rt)), - slotSpan(JSSLOT_FREE(aclasp)), - numLinearSearches(0), - id(JSID_EMPTY), - clasp(aclasp), - rawSetter(NULL), - slot(SHAPE_INVALID_SLOT), - attrs(0), - flags(SHARED_EMPTY), - shortid(0), +Shape::Shape(const StackShape &other, uint32_t nfixed) + : base_(other.base), + propid_(other.propid), + slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)), + attrs(other.attrs), + flags(other.flags), + shortid_(other.shortid), parent(NULL) { kids.setNull(); } inline -Shape::Shape(uint32 shape) - : shape(shape), - slotSpan(0), - numLinearSearches(0), - id(JSID_EMPTY), - clasp(NULL), - rawSetter(NULL), - slot(SHAPE_INVALID_SLOT), - attrs(0), - flags(SHARED_EMPTY), - shortid(0), +Shape::Shape(UnownedBaseShape *base, uint32_t nfixed) + : base_(base), + propid_(JSID_EMPTY), + slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)), + attrs(JSPROP_SHARED), + flags(0), + shortid_(0), parent(NULL) { + JS_ASSERT(base); kids.setNull(); } inline JSDHashNumber -Shape::hash() const +StackShape::hash() const { - JSDHashNumber hash = 0; + JSDHashNumber hash = uintptr_t(base); /* Accumulate from least to most random so the low bits are most random. */ - JS_ASSERT_IF(isMethod(), !rawSetter || rawSetter == js_watch_set); - if (rawGetter) - hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawGetter); - if (rawSetter) - hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawSetter); - hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & PUBLIC_FLAGS); + hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & Shape::PUBLIC_FLAGS); hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs; hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid; - hash = JS_ROTATE_LEFT32(hash, 4) ^ slot; - hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(id); + hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_; + hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid); return hash; } inline bool Shape::matches(const js::Shape *other) const { - JS_ASSERT(!JSID_IS_VOID(id)); - JS_ASSERT(!JSID_IS_VOID(other->id)); - return id == other->id && - matchesParamsAfterId(other->rawGetter, other->rawSetter, other->slot, other->attrs, - other->flags, other->shortid); + return propid_.get() == other->propid_.get() && + matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, + other->flags, other->shortid_); } inline bool -Shape::matchesParamsAfterId(js::PropertyOp agetter, js::StrictPropertyOp asetter, uint32 aslot, +Shape::matches(const StackShape &other) const +{ + return propid_.get() == other.propid && + matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags, other.shortid); +} + +inline bool +Shape::matchesParamsAfterId(BaseShape *base, uint32_t aslot, uintN aattrs, uintN aflags, intN ashortid) const { - JS_ASSERT(!JSID_IS_VOID(id)); - return rawGetter == agetter && - rawSetter == asetter && - slot == aslot && + return base->unowned() == this->base()->unowned() && + maybeSlot() == aslot && attrs == aattrs && ((flags ^ aflags) & PUBLIC_FLAGS) == 0 && - shortid == ashortid; + shortid_ == ashortid; } inline bool Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js::Value* vp) const { - JS_ASSERT(!JSID_IS_VOID(this->id)); JS_ASSERT(!hasDefaultGetter()); if (hasGetterValue()) { JS_ASSERT(!isMethod()); js::Value fval = getterValue(); - return js::ExternalGetOrSet(cx, receiver, id, fval, JSACC_READ, 0, 0, vp); + return js::InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp); } if (isMethod()) { - vp->setObject(methodObject()); + vp->setObject(*pobj->nativeGetMethod(this)); return pobj->methodReadBarrier(cx, *this, vp); } @@ -277,9 +296,9 @@ Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js: * |with (it) color;| ends up here, as do XML filter-expressions. * Avoid exposing the With object to native getters. */ - if (obj->getClass() == &js_WithClass) - obj = js_UnwrapWithObject(cx, obj); - return js::CallJSPropertyOp(cx, getterOp(), receiver, SHAPE_USERID(this), vp); + if (obj->isWith()) + obj = &obj->asWith().object(); + return js::CallJSPropertyOp(cx, getterOp(), receiver, getUserId(), vp); } inline bool @@ -289,31 +308,37 @@ Shape::set(JSContext* cx, JSObject* obj, bool strict, js::Value* vp) const if (attrs & JSPROP_SETTER) { js::Value fval = setterValue(); - return js::ExternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp); + return js::InvokeGetterOrSetter(cx, obj, fval, 1, vp, vp); } if (attrs & JSPROP_GETTER) return js_ReportGetterOnlyAssignment(cx); /* See the comment in js::Shape::get as to why we check for With. */ - if (obj->getClass() == &js_WithClass) - obj = js_UnwrapWithObject(cx, obj); - return js::CallJSPropertyOpSetter(cx, setterOp(), obj, SHAPE_USERID(this), strict, vp); + if (obj->isWith()) + obj = &obj->asWith().object(); + return js::CallJSPropertyOpSetter(cx, setterOp(), obj, getUserId(), strict, vp); } inline void -Shape::removeFromDictionary(JSObject *obj) const +Shape::setParent(js::Shape *p) +{ + JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(), + p->maybeSlot() <= maybeSlot()); + JS_ASSERT_IF(p && !inDictionary(), + hasSlot() == (p->maybeSlot() != maybeSlot())); + parent = p; +} + +inline void +Shape::removeFromDictionary(JSObject *obj) { - JS_ASSERT(!frozen()); JS_ASSERT(inDictionary()); JS_ASSERT(obj->inDictionaryMode()); JS_ASSERT(listp); - JS_ASSERT(!JSID_IS_VOID(id)); - JS_ASSERT(obj->lastProp->inDictionary()); - JS_ASSERT(obj->lastProp->listp == &obj->lastProp); - JS_ASSERT_IF(obj->lastProp != this, !JSID_IS_VOID(obj->lastProp->id)); - JS_ASSERT_IF(obj->lastProp->parent, !JSID_IS_VOID(obj->lastProp->parent->id)); + JS_ASSERT(obj->shape_->inDictionary()); + JS_ASSERT(obj->shape_->listp == &obj->shape_); if (parent) parent->listp = listp; @@ -322,7 +347,7 @@ Shape::removeFromDictionary(JSObject *obj) const } inline void -Shape::insertIntoDictionary(js::Shape **dictp) +Shape::insertIntoDictionary(HeapPtrShape *dictp) { /* * Don't assert inDictionaryMode() here because we may be called from @@ -330,28 +355,90 @@ Shape::insertIntoDictionary(js::Shape **dictp) */ JS_ASSERT(inDictionary()); JS_ASSERT(!listp); - JS_ASSERT(!JSID_IS_VOID(id)); - JS_ASSERT_IF(*dictp, !(*dictp)->frozen()); JS_ASSERT_IF(*dictp, (*dictp)->inDictionary()); JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp); - JS_ASSERT_IF(*dictp, !JSID_IS_VOID((*dictp)->id)); JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment()); setParent(*dictp); if (parent) parent->listp = &parent; - listp = dictp; + listp = (HeapPtrShape *) dictp; *dictp = this; } +void +Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp) +{ + new (this) Shape(child, nfixed); + this->flags |= IN_DICTIONARY; + + this->listp = NULL; + insertIntoDictionary(dictp); +} + inline -EmptyShape::EmptyShape(JSCompartment *comp, js::Class *aclasp) - : js::Shape(comp, aclasp) +EmptyShape::EmptyShape(UnownedBaseShape *base, uint32_t nfixed) + : js::Shape(base, nfixed) { -#ifdef DEBUG - if (comp->rt->meterEmptyShapes()) - comp->emptyShapes.put(this); + /* Only empty shapes can be NON_NATIVE. */ + if (!getObjectClass()->isNative()) + flags |= NON_NATIVE; +} + +inline void +Shape::writeBarrierPre(const js::Shape *shape) +{ +#ifdef JSGC_INCREMENTAL + if (!shape) + return; + + JSCompartment *comp = shape->compartment(); + if (comp->needsBarrier()) + MarkShapeUnbarriered(comp->barrierTracer(), const_cast(shape), "write barrier"); +#endif +} + +inline void +Shape::writeBarrierPost(const js::Shape *shape, void *addr) +{ +} + +inline void +Shape::readBarrier(const Shape *shape) +{ +#ifdef JSGC_INCREMENTAL + JSCompartment *comp = shape->compartment(); + if (comp->needsBarrier()) + MarkShapeUnbarriered(comp->barrierTracer(), const_cast(shape), "read barrier"); +#endif +} + +inline void +BaseShape::writeBarrierPre(BaseShape *base) +{ +#ifdef JSGC_INCREMENTAL + if (!base) + return; + + JSCompartment *comp = base->compartment(); + if (comp->needsBarrier()) + MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "write barrier"); +#endif +} + +inline void +BaseShape::writeBarrierPost(BaseShape *shape, void *addr) +{ +} + +inline void +BaseShape::readBarrier(BaseShape *base) +{ +#ifdef JSGC_INCREMENTAL + JSCompartment *comp = base->compartment(); + if (comp->needsBarrier()) + MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier"); #endif } diff --git a/deps/mozjs/js/src/jsscript.cpp b/deps/mozjs/js/src/jsscript.cpp index 0dd911e698a..a7b8ca0e871 100644 --- a/deps/mozjs/js/src/jsscript.cpp +++ b/deps/mozjs/js/src/jsscript.cpp @@ -23,6 +23,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Nick Fitzgerald * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -41,17 +42,17 @@ /* * JS script operations. */ + #include #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" +#include "jscrashreport.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" -#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" @@ -59,36 +60,43 @@ #include "jslock.h" #include "jsnum.h" #include "jsopcode.h" -#include "jsparse.h" #include "jsscope.h" #include "jsscript.h" -#include "jstracer.h" #if JS_HAS_XDR #include "jsxdrapi.h" #endif + +#include "frontend/BytecodeEmitter.h" +#include "frontend/Parser.h" +#include "js/MemoryMetrics.h" #include "methodjit/MethodJIT.h" +#include "methodjit/Retcon.h" +#include "vm/Debugger.h" +#include "jsinferinlines.h" +#include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" using namespace js; using namespace js::gc; +using namespace js::frontend; namespace js { BindingKind Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const { - JS_ASSERT(lastBinding); + if (!lastBinding) + return NONE; - Shape *shape = - SHAPE_FETCH(Shape::search(cx->runtime, const_cast(&lastBinding), - ATOM_TO_JSID(name))); + Shape **spp; + Shape *shape = Shape::search(cx, lastBinding, ATOM_TO_JSID(name), &spp); if (!shape) return NONE; if (indexp) - *indexp = shape->shortid; + *indexp = shape->shortid(); if (shape->getter() == GetCallArg) return ARGUMENT; @@ -101,19 +109,20 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const bool Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) { - JS_ASSERT(lastBinding); + if (!ensureShape(cx)) + return false; /* * We still follow 10.2.3 of ES3 and make argument and variable properties * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to * avoid objects as activations, something we should do too. */ - uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED; + uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT; - uint16 *indexp; + uint16_t *indexp; PropertyOp getter; StrictPropertyOp setter; - uint32 slot = JSObject::CALL_RESERVED_SLOTS; + uint32_t slot = CallObject::RESERVED_SLOTS; if (kind == ARGUMENT) { JS_ASSERT(nvars == 0); @@ -126,7 +135,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) indexp = &nupvars; getter = GetCallUpvar; setter = SetCallUpvar; - slot = SHAPE_INVALID_SLOT; + slot = lastBinding->maybeSlot(); + attrs |= JSPROP_SHARED; } else { JS_ASSERT(kind == VARIABLE || kind == CONSTANT); JS_ASSERT(nupvars == 0); @@ -155,43 +165,88 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) id = ATOM_TO_JSID(name); } - Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp); + StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ); + base.updateGetterSetter(attrs, getter, setter); + + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); + if (!nbase) + return NULL; + + StackShape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp); - Shape *shape = lastBinding->getChild(cx, child, &lastBinding); + /* Shapes in bindings cannot be dictionaries. */ + Shape *shape = lastBinding->getChildBinding(cx, child); if (!shape) return false; - JS_ASSERT(lastBinding == shape); + lastBinding = shape; ++*indexp; return true; } -jsuword * -Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool) +Shape * +Bindings::callObjectShape(JSContext *cx) const { - JS_ASSERT(lastBinding); + if (!hasDup()) + return lastShape(); - JS_ASSERT(hasLocalNames()); + /* + * Build a vector of non-duplicate properties in order from last added + * to first (i.e., the order we normally have iterate over Shapes). Choose + * the last added property in each set of dups. + */ + Vector shapes(cx); + HashSet seen(cx); + if (!seen.init()) + return NULL; - uintN n = countLocalNames(); - jsuword *names; + for (Shape::Range r = lastShape()->all(); !r.empty(); r.popFront()) { + const Shape &s = r.front(); + HashSet::AddPtr p = seen.lookupForAdd(s.propid()); + if (!p) { + if (!seen.add(p, s.propid())) + return NULL; + if (!shapes.append(&s)) + return NULL; + } + } - JS_ASSERT(SIZE_MAX / size_t(n) > sizeof *names); - JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names); - if (!names) { - js_ReportOutOfScriptQuota(cx); - return NULL; + /* + * Now build the Shape without duplicate properties. + */ + RootedVarShape shape(cx); + shape = initialShape(cx); + for (int i = shapes.length() - 1; i >= 0; --i) { + shape = shape->getChildBinding(cx, shapes[i]); + if (!shape) + return NULL; } + return shape; +} + +bool +Bindings::getLocalNameArray(JSContext *cx, Vector *namesp) +{ + JS_ASSERT(lastBinding); + JS_ASSERT(hasLocalNames()); + + Vector &names = *namesp; + JS_ASSERT(names.empty()); + + uintN n = countLocalNames(); + if (!names.growByUninitialized(n)) + return false; + #ifdef DEBUG - for (uintN i = 0; i != n; i++) - names[i] = 0xdeadbeef; + JSAtom * const POISON = reinterpret_cast(0xdeadbeef); + for (uintN i = 0; i < n; i++) + names[i] = POISON; #endif - for (Shape::Range r = lastBinding; !r.empty(); r.popFront()) { + for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) { const Shape &shape = r.front(); - uintN index = uint16(shape.shortid); - jsuword constFlag = 0; + uintN index = uint16_t(shape.shortid()); if (shape.getter() == GetCallArg) { JS_ASSERT(index < nargs); @@ -201,27 +256,23 @@ Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool) } else { JS_ASSERT(index < nvars); index += nargs; - if (!shape.writable()) - constFlag = 1; } - JSAtom *atom; - if (JSID_IS_ATOM(shape.id)) { - atom = JSID_TO_ATOM(shape.id); + if (JSID_IS_ATOM(shape.propid())) { + names[index] = JSID_TO_ATOM(shape.propid()); } else { - JS_ASSERT(JSID_IS_INT(shape.id)); + JS_ASSERT(JSID_IS_INT(shape.propid())); JS_ASSERT(shape.getter() == GetCallArg); - atom = NULL; + names[index] = NULL; } - - names[index] = jsuword(atom); } #ifdef DEBUG - for (uintN i = 0; i != n; i++) - JS_ASSERT(names[i] != 0xdeadbeef); + for (uintN i = 0; i < n; i++) + JS_ASSERT(names[i] != POISON); #endif - return names; + + return true; } const Shape * @@ -257,32 +308,11 @@ Bindings::lastUpvar() const return lastBinding; } -int -Bindings::sharpSlotBase(JSContext *cx) -{ - JS_ASSERT(lastBinding); -#if JS_HAS_SHARP_VARS - if (JSAtom *name = js_Atomize(cx, "#array", 6, 0)) { - uintN index = uintN(-1); - DebugOnly kind = lookup(cx, name, &index); - JS_ASSERT(kind == VARIABLE); - return int(index); - } -#endif - return -1; -} - void Bindings::makeImmutable() { JS_ASSERT(lastBinding); - Shape *shape = lastBinding; - if (shape->inDictionary()) { - do { - JS_ASSERT(!shape->frozen()); - shape->setFrozen(); - } while ((shape = shape->parent) != NULL); - } + JS_ASSERT(!lastBinding->inDictionary()); } void @@ -292,6 +322,20 @@ Bindings::trace(JSTracer *trc) MarkShape(trc, lastBinding, "shape"); } +#ifdef JS_CRASH_DIAGNOSTICS + +void +CheckScript(JSScript *script, JSScript *prev) +{ + if (script->cookie1[0] != JS_SCRIPT_COOKIE || script->cookie2[0] != JS_SCRIPT_COOKIE) { + crash::StackBuffer buf1(script); + crash::StackBuffer buf2(prev); + JS_OPT_ASSERT(false); + } +} + +#endif /* JS_CRASH_DIAGNOSTICS */ + } /* namespace js */ #if JS_HAS_XDR @@ -299,7 +343,6 @@ Bindings::trace(JSTracer *trc) enum ScriptBits { NoScriptRval, SavedCallerFun, - HasSharps, StrictModeCode, UsesEval, UsesArguments @@ -313,16 +356,14 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) { JSScript *oldscript; JSBool ok; - jsbytecode *code; - uint32 length, lineno, nslots; - uint32 natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i; - uint32 prologLength, version, encodedClosedCount; - uint16 nClosedArgs = 0, nClosedVars = 0; - JSPrincipals *principals; - uint32 encodeable; - jssrcnote *sn; + uint32_t length, lineno, nslots; + uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i; + uint32_t prologLength, version, encodedClosedCount; + uint16_t nClosedArgs = 0, nClosedVars = 0; + uint32_t nTypeSets = 0; + uint32_t encodeable, sameOriginPrincipals; JSSecurityCallbacks *callbacks; - uint32 scriptBits = 0; + uint32_t scriptBits = 0; JSContext *cx = xdr->cx; JSScript *script = *scriptp; @@ -336,11 +377,11 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset)); /* XDR arguments, local vars, and upvars. */ - uint16 nargs, nvars, nupvars; + uint16_t nargs, nvars, nupvars; #if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */ nargs = nvars = nupvars = Bindings::BINDING_COUNT_LIMIT; #endif - uint32 argsVars, paddingUpvars; + uint32_t argsVars, paddingUpvars; if (xdr->mode == JSXDR_ENCODE) { nargs = script->bindings.countArgs(); nvars = script->bindings.countVars(); @@ -360,23 +401,10 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT); JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT); - EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx); - if (!emptyCallShape) - return false; - AutoShapeRooter shapeRoot(cx, emptyCallShape); - - Bindings bindings(cx, emptyCallShape); - AutoBindingsRooter rooter(cx, bindings); - uint32 nameCount = nargs + nvars + nupvars; + Bindings bindings(cx); + uint32_t nameCount = nargs + nvars + nupvars; if (nameCount > 0) { - struct AutoMark { - JSArenaPool * const pool; - void * const mark; - AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { } - ~AutoMark() { - JS_ARENA_RELEASE(pool, mark); - } - } automark(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); /* * To xdr the names we prefix the names with a bitmap descriptor and @@ -387,34 +415,22 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * name is declared as const, not as ordinary var. * */ uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32); - uint32 *bitmap; - JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool, - bitmapLength * sizeof *bitmap); + uint32_t *bitmap = cx->tempLifoAlloc().newArray(bitmapLength); if (!bitmap) { - js_ReportOutOfScriptQuota(cx); + js_ReportOutOfMemory(cx); return false; } - jsuword *names; + Vector names(cx); if (xdr->mode == JSXDR_ENCODE) { - names = script->bindings.getLocalNameArray(cx, &cx->tempPool); - if (!names) + if (!script->bindings.getLocalNameArray(cx, &names)) return false; PodZero(bitmap, bitmapLength); for (uintN i = 0; i < nameCount; i++) { - if (i < nargs - ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL - : JS_LOCAL_NAME_IS_CONST(names[i])) - { + if (i < nargs && names[i]) bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1)); - } } } -#ifdef __GNUC__ - else { - names = NULL; /* quell GCC uninitialized warning */ - } -#endif for (uintN i = 0; i < bitmapLength; ++i) { if (!JS_XDRUint32(xdr, &bitmap[i])) return false; @@ -425,18 +441,18 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) { if (xdr->mode == JSXDR_DECODE) { - uint16 dummy; + uint16_t dummy; if (!bindings.addDestructuring(cx, &dummy)) return false; } else { - JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i])); + JS_ASSERT(!names[i]); } continue; } JSAtom *name; if (xdr->mode == JSXDR_ENCODE) - name = JS_LOCAL_NAME_TO_ATOM(names[i]); + name = names[i]; if (!js_XDRAtom(xdr, &name)) return false; if (xdr->mode == JSXDR_DECODE) { @@ -452,9 +468,12 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) return false; } } + } - if (xdr->mode == JSXDR_DECODE) - bindings.makeImmutable(); + if (xdr->mode == JSXDR_DECODE) { + if (!bindings.ensureShape(cx)) + return false; + bindings.makeImmutable(); } if (xdr->mode == JSXDR_ENCODE) @@ -463,20 +482,16 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) return JS_FALSE; if (xdr->mode == JSXDR_ENCODE) { - prologLength = script->main - script->code; + prologLength = script->mainOffset; JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN); - version = (uint32)script->getVersion() | (script->nfixed << 16); - lineno = (uint32)script->lineno; - nslots = (uint32)script->nslots; - nslots = (uint32)((script->staticLevel << 16) | script->nslots); - natoms = (uint32)script->atomMap.length; + version = (uint32_t)script->getVersion() | (script->nfixed << 16); + lineno = (uint32_t)script->lineno; + nslots = (uint32_t)script->nslots; + nslots = (uint32_t)((script->staticLevel << 16) | script->nslots); + natoms = script->natoms; - /* Count the srcnotes, keeping notes pointing at the first one. */ notes = script->notes(); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nsrcnotes = sn - notes; - nsrcnotes++; /* room for the terminator */ + nsrcnotes = script->numNotes(); if (JSScript::isValidOffset(script->objectsOffset)) nobjects = script->objects()->length; @@ -493,12 +508,12 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) nClosedVars = script->nClosedVars; encodedClosedCount = (nClosedArgs << 16) | nClosedVars; + nTypeSets = script->nTypeSets; + if (script->noScriptRval) scriptBits |= (1 << NoScriptRval); if (script->savedCallerFun) scriptBits |= (1 << SavedCallerFun); - if (script->hasSharps) - scriptBits |= (1 << HasSharps); if (script->strictModeCode) scriptBits |= (1 << StrictModeCode); if (script->usesEval) @@ -532,11 +547,11 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) return JS_FALSE; if (!JS_XDRUint32(xdr, &encodedClosedCount)) return JS_FALSE; + if (!JS_XDRUint32(xdr, &nTypeSets)) + return JS_FALSE; if (!JS_XDRUint32(xdr, &scriptBits)) return JS_FALSE; - AutoScriptRooter tvr(cx, NULL); - if (xdr->mode == JSXDR_DECODE) { nClosedArgs = encodedClosedCount >> 16; nClosedVars = encodedClosedCount & 0xFFFF; @@ -546,26 +561,23 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_ASSERT((version_ & VersionFlags::FULL_MASK) == uintN(version_)); script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars, nregexps, ntrynotes, nconsts, 0, nClosedArgs, - nClosedVars, version_); + nClosedVars, nTypeSets, version_); if (!script) return JS_FALSE; script->bindings.transfer(cx, &bindings); - - script->main += prologLength; - script->nfixed = uint16(version >> 16); + JS_ASSERT(!script->mainOffset); + script->mainOffset = prologLength; + script->nfixed = uint16_t(version >> 16); /* If we know nsrcnotes, we allocated space for notes in script. */ notes = script->notes(); *scriptp = script; - tvr.setScript(script); if (scriptBits & (1 << NoScriptRval)) script->noScriptRval = true; if (scriptBits & (1 << SavedCallerFun)) script->savedCallerFun = true; - if (scriptBits & (1 << HasSharps)) - script->hasSharps = true; if (scriptBits & (1 << StrictModeCode)) script->strictModeCode = true; if (scriptBits & (1 << UsesEval)) @@ -579,18 +591,9 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * DECODE case to destroy script. */ oldscript = xdr->script; - code = script->code; - if (xdr->mode == JSXDR_ENCODE) { - code = js_UntrapScriptCode(cx, script); - if (!code) - goto error; - } xdr->script = script; - ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode)); - - if (code != script->code) - cx->free_(code); + ok = JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)); if (!ok) goto error; @@ -617,38 +620,47 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_ASSERT_IF(xdr->mode == JSXDR_ENCODE, state->filename == script->filename); callbacks = JS_GetSecurityCallbacks(cx); - if (xdr->mode == JSXDR_ENCODE) { - principals = script->principals; - encodeable = callbacks && callbacks->principalsTranscoder; - if (!JS_XDRUint32(xdr, &encodeable)) - goto error; - if (encodeable && - !callbacks->principalsTranscoder(xdr, &principals)) { + if (xdr->mode == JSXDR_ENCODE) + encodeable = script->principals && callbacks && callbacks->principalsTranscoder; + + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + + if (encodeable) { + if (!callbacks || !callbacks->principalsTranscoder) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DECODE_PRINCIPALS); goto error; } - } else { - if (!JS_XDRUint32(xdr, &encodeable)) + + if (!callbacks->principalsTranscoder(xdr, &script->principals)) goto error; - if (encodeable) { - if (!(callbacks && callbacks->principalsTranscoder)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_DECODE_PRINCIPALS); - goto error; + + if (xdr->mode == JSXDR_ENCODE) + sameOriginPrincipals = script->principals == script->originPrincipals; + + if (!JS_XDRUint32(xdr, &sameOriginPrincipals)) + goto error; + + if (sameOriginPrincipals) { + if (xdr->mode == JSXDR_DECODE) { + script->originPrincipals = script->principals; + JSPRINCIPALS_HOLD(cx, script->originPrincipals); } - if (!callbacks->principalsTranscoder(xdr, &principals)) + } else { + if (!callbacks->principalsTranscoder(xdr, &script->originPrincipals)) goto error; - script->principals = principals; } } if (xdr->mode == JSXDR_DECODE) { script->lineno = (uintN)lineno; - script->nslots = (uint16)nslots; - script->staticLevel = (uint16)(nslots >> 16); + script->nslots = uint16_t(nslots); + script->staticLevel = uint16_t(nslots >> 16); } for (i = 0; i != natoms; ++i) { - if (!js_XDRAtom(xdr, &script->atomMap.vector[i])) + if (!js_XDRAtom(xdr, &script->atoms[i])) goto error; } @@ -659,32 +671,37 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * to restore the parent chain. */ for (i = 0; i != nobjects; ++i) { - JSObject **objp = &script->objects()->vector[i]; - uint32 isBlock; + HeapPtr *objp = &script->objects()->vector[i]; + uint32_t isBlock; if (xdr->mode == JSXDR_ENCODE) { - Class *clasp = (*objp)->getClass(); - JS_ASSERT(clasp == &js_FunctionClass || - clasp == &js_BlockClass); - isBlock = (clasp == &js_BlockClass) ? 1 : 0; + JSObject *obj = *objp; + JS_ASSERT(obj->isFunction() || obj->isStaticBlock()); + isBlock = obj->isBlock() ? 1 : 0; } if (!JS_XDRUint32(xdr, &isBlock)) goto error; if (isBlock == 0) { - if (!js_XDRFunctionObject(xdr, objp)) + JSObject *tmp = *objp; + if (!js_XDRFunctionObject(xdr, &tmp)) goto error; + *objp = tmp; } else { JS_ASSERT(isBlock == 1); - if (!js_XDRBlockObject(xdr, objp)) + StaticBlockObject *tmp = static_cast(objp->get()); + if (!js_XDRStaticBlockObject(xdr, &tmp)) goto error; + *objp = tmp; } } for (i = 0; i != nupvars; ++i) { - if (!JS_XDRUint32(xdr, reinterpret_cast(&script->upvars()->vector[i]))) + if (!JS_XDRUint32(xdr, reinterpret_cast(&script->upvars()->vector[i]))) goto error; } for (i = 0; i != nregexps; ++i) { - if (!js_XDRRegExpObject(xdr, &script->regexps()->vector[i])) + JSObject *tmp = script->regexps()->vector[i]; + if (!js_XDRRegExpObject(xdr, &tmp)) goto error; + script->regexps()->vector[i] = tmp; } for (i = 0; i != nClosedArgs; ++i) { if (!JS_XDRUint32(xdr, &script->closedSlots[i])) @@ -701,9 +718,9 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * efficient when serializing small integer types. */ JSTryNote *tn, *tnfirst; - uint32 kindAndDepth; - JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8)); - JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16)); + uint32_t kindAndDepth; + JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8_t)); + JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16_t)); tnfirst = script->trynotes()->vector; JS_ASSERT(script->trynotes()->length == ntrynotes); @@ -711,8 +728,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) do { --tn; if (xdr->mode == JSXDR_ENCODE) { - kindAndDepth = ((uint32)tn->kind << 16) - | (uint32)tn->stackDepth; + kindAndDepth = (uint32_t(tn->kind) << 16) + | uint32_t(tn->stackDepth); } if (!JS_XDRUint32(xdr, &kindAndDepth) || !JS_XDRUint32(xdr, &tn->start) || @@ -720,197 +737,113 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) goto error; } if (xdr->mode == JSXDR_DECODE) { - tn->kind = (uint8)(kindAndDepth >> 16); - tn->stackDepth = (uint16)kindAndDepth; + tn->kind = uint8_t(kindAndDepth >> 16); + tn->stackDepth = uint16_t(kindAndDepth); } } while (tn != tnfirst); } for (i = 0; i != nconsts; ++i) { - if (!JS_XDRValue(xdr, Jsvalify(&script->consts()->vector[i]))) + Value tmp = script->consts()->vector[i]; + if (!JS_XDRValue(xdr, &tmp)) goto error; + script->consts()->vector[i] = tmp; } + if (xdr->mode == JSXDR_DECODE && cx->hasRunOption(JSOPTION_PCCOUNT)) + (void) script->initCounts(cx); + xdr->script = oldscript; return JS_TRUE; error: - if (xdr->mode == JSXDR_DECODE) { - js_DestroyScript(cx, script); + if (xdr->mode == JSXDR_DECODE) *scriptp = NULL; - } xdr->script = oldscript; return JS_FALSE; } #endif /* JS_HAS_XDR */ -static void -script_finalize(JSContext *cx, JSObject *obj) +bool +JSScript::initCounts(JSContext *cx) { - JSScript *script = (JSScript *) obj->getPrivate(); - if (script) - js_DestroyScriptFromGC(cx, script); -} + JS_ASSERT(!pcCounters); -static void -script_trace(JSTracer *trc, JSObject *obj) -{ - JSScript *script = (JSScript *) obj->getPrivate(); - if (script) - js_TraceScript(trc, script); -} + size_t count = 0; -Class js_ScriptClass = { - "Script", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - script_finalize, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - script_trace -}; + jsbytecode *pc, *next; + for (pc = code; pc < code + length; pc = next) { + count += OpcodeCounts::numCounts(JSOp(*pc)); + next = pc + GetBytecodeLength(pc); + } -/* - * Shared script filename management. - */ -static int -js_compare_strings(const void *k1, const void *k2) -{ - return strcmp((const char *) k1, (const char *) k2) == 0; -} + size_t bytes = (length * sizeof(OpcodeCounts)) + (count * sizeof(double)); + char *cursor = (char *) cx->calloc_(bytes); + if (!cursor) + return false; -/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ -typedef struct ScriptFilenameEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to filename, below */ - JSPackedBool mark; /* GC mark flag */ - char filename[3]; /* two or more bytes, NUL-terminated */ -} ScriptFilenameEntry; - -static void * -js_alloc_table_space(void *priv, size_t size) -{ - return OffTheBooks::malloc_(size); -} + DebugOnly base = cursor; -static void -js_free_table_space(void *priv, void *item, size_t size) -{ - UnwantedForeground::free_(item); -} + pcCounters.counts = (OpcodeCounts *) cursor; + cursor += length * sizeof(OpcodeCounts); -static JSHashEntry * -js_alloc_sftbl_entry(void *priv, const void *key) -{ - size_t nbytes = offsetof(ScriptFilenameEntry, filename) + - strlen((const char *) key) + 1; + for (pc = code; pc < code + length; pc = next) { + pcCounters.counts[pc - code].counts = (double *) cursor; + size_t capacity = OpcodeCounts::numCounts(JSOp(*pc)); +#ifdef DEBUG + pcCounters.counts[pc - code].capacity = capacity; +#endif + cursor += capacity * sizeof(double); + next = pc + GetBytecodeLength(pc); + } - return (JSHashEntry *) OffTheBooks::malloc_(JS_MAX(nbytes, sizeof(JSHashEntry))); -} + JS_ASSERT(size_t(cursor - base) == bytes); -static void -js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; - UnwantedForeground::free_(he); -} + /* Enable interrupts in any interpreter frames running on this script. */ + InterpreterFrames *frames; + for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older) + frames->enableInterruptsIfRunning(this); -static JSHashAllocOps sftbl_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_sftbl_entry, js_free_sftbl_entry -}; - -static void -FinishRuntimeScriptState(JSRuntime *rt) -{ - if (rt->scriptFilenameTable) { - JS_HashTableDestroy(rt->scriptFilenameTable); - rt->scriptFilenameTable = NULL; - } -#ifdef JS_THREADSAFE - if (rt->scriptFilenameTableLock) { - JS_DESTROY_LOCK(rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = NULL; - } -#endif + return true; } -JSBool -js_InitRuntimeScriptState(JSRuntime *rt) +void +JSScript::destroyCounts(JSContext *cx) { -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = JS_NEW_LOCK(); - if (!rt->scriptFilenameTableLock) - return JS_FALSE; -#endif - JS_ASSERT(!rt->scriptFilenameTable); - rt->scriptFilenameTable = - JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, - &sftbl_alloc_ops, NULL); - if (!rt->scriptFilenameTable) { - FinishRuntimeScriptState(rt); /* free lock if threadsafe */ - return JS_FALSE; + if (pcCounters) { + cx->free_(pcCounters.counts); + pcCounters.counts = NULL; } - return JS_TRUE; } -void -js_FreeRuntimeScriptState(JSRuntime *rt) -{ - if (!rt->scriptFilenameTable) - return; - FinishRuntimeScriptState(rt); -} +/* + * Shared script filename management. + */ static const char * SaveScriptFilename(JSContext *cx, const char *filename) { - JSRuntime *rt = cx->runtime; - JS_AUTO_LOCK_GUARD(g, rt->scriptFilenameTableLock); - - JSHashTable *table = rt->scriptFilenameTable; - JSHashNumber hash = JS_HashString(filename); - JSHashEntry **hep = JS_HashTableRawLookup(table, hash, filename); - - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) *hep; - if (!sfe) { - sfe = (ScriptFilenameEntry *) - JS_HashTableRawAdd(table, hep, hash, filename, NULL); - if (!sfe) { + JSCompartment *comp = cx->compartment; + + ScriptFilenameTable::AddPtr p = comp->scriptFilenameTable.lookupForAdd(filename); + if (!p) { + size_t size = offsetof(ScriptFilenameEntry, filename) + strlen(filename) + 1; + ScriptFilenameEntry *entry = (ScriptFilenameEntry *) cx->malloc_(size); + if (!entry) + return NULL; + entry->marked = false; + strcpy(entry->filename, filename); + + if (!comp->scriptFilenameTable.add(p, entry)) { + Foreground::free_(entry); JS_ReportOutOfMemory(cx); return NULL; } - sfe->key = strcpy(sfe->filename, filename); - sfe->mark = JS_FALSE; - } - -#ifdef DEBUG - if (rt->functionMeterFilename) { - size_t len = strlen(sfe->filename); - if (len >= sizeof rt->lastScriptFilename) - len = sizeof rt->lastScriptFilename - 1; - memcpy(rt->lastScriptFilename, sfe->filename, len); - rt->lastScriptFilename[len] = '\0'; } -#endif - return sfe->filename; + return (*p)->filename; } /* @@ -919,72 +852,28 @@ SaveScriptFilename(JSContext *cx, const char *filename) #define FILENAME_TO_SFE(fn) \ ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) -/* - * The sfe->key member, redundant given sfe->filename but required by the old - * jshash.c code, here gives us a useful sanity check. This assertion will - * very likely botch if someone tries to mark a string that wasn't allocated - * as an sfe->filename. - */ -#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) - void js_MarkScriptFilename(const char *filename) { - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - sfe->mark = JS_TRUE; -} - -static intN -js_script_filename_marker(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - sfe->mark = JS_TRUE; - return HT_ENUMERATE_NEXT; + ScriptFilenameEntry *sfe = FILENAME_TO_SFE(filename); + sfe->marked = true; } void -js_MarkScriptFilenames(JSRuntime *rt) +js_SweepScriptFilenames(JSCompartment *comp) { - if (!rt->scriptFilenameTable) - return; - - if (rt->gcKeepAtoms) { - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_marker, - rt); + ScriptFilenameTable &table = comp->scriptFilenameTable; + for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront()) { + ScriptFilenameEntry *entry = e.front(); + if (entry->marked) { + entry->marked = false; + } else if (!comp->rt->gcKeepAtoms) { + Foreground::free_(entry); + e.removeFront(); + } } } -static intN -js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - if (!sfe->mark) - return HT_ENUMERATE_REMOVE; - sfe->mark = JS_FALSE; - return HT_ENUMERATE_NEXT; -} - -void -js_SweepScriptFilenames(JSRuntime *rt) -{ - if (!rt->scriptFilenameTable) - return; - - /* - * JS_HashTableEnumerateEntries shrinks the table if many entries are - * removed preventing wasting memory on a too sparse table. - */ - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_sweeper, - rt); -} - /* * JSScript data structures memory alignment: * @@ -995,8 +884,8 @@ js_SweepScriptFilenames(JSRuntime *rt) * use script->regexps() to access it. * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset * != 0, use script->trynotes() to access it. - * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by - * JSScript.atomMap.vector if any. + * JSAtom *a[] array of JSScript.natoms atoms pointed by + * JSScript.atoms if any. * JSObject *o[] array of script->objects()->length objects if any * pointed by script->objects()->vector. * JSObject *r[] array of script->regexps()->length regexps if any @@ -1012,19 +901,19 @@ js_SweepScriptFilenames(JSRuntime *rt) * * The followings asserts checks that assuming that the alignment requirement * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote - * it is sizeof(uint32) as the structure consists of 3 uint32 fields. + * it is sizeof(uint32_t) as the structure consists of 3 uint32_t fields. */ JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0); JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0); JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray)); JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *)); -JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0); -JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32)); -JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0); +JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32_t) == 0); +JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32_t)); +JS_STATIC_ASSERT(sizeof(uint32_t) % sizeof(jsbytecode) == 0); JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0); /* - * Check that uint8 offsets is enough to reach any optional array allocated + * Check that uint8_t offsets is enough to reach any optional array allocated * after JSScript. For that we check that the maximum possible offset for * JSConstArray, that last optional array, still fits 1 byte and do not * coincide with INVALID_OFFSET. @@ -1038,97 +927,119 @@ JS_STATIC_ASSERT(sizeof(JSObjectArray) + JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255); JSScript * -JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, - uint32 nobjects, uint32 nupvars, uint32 nregexps, - uint32 ntrynotes, uint32 nconsts, uint32 nglobals, - uint16 nClosedArgs, uint16 nClosedVars, JSVersion version) +JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms, + uint32_t nobjects, uint32_t nupvars, uint32_t nregexps, + uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals, + uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version) { - EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx); - if (!emptyCallShape) - return NULL; - AutoShapeRooter shapeRoot(cx, emptyCallShape); - - size_t size, vectorSize; - JSScript *script; - uint8 *cursor; - unsigned constPadding = 0; - - uint32 totalClosed = nClosedArgs + nClosedVars; - - size = sizeof(JSScript) + - sizeof(JSAtom *) * natoms; - + size_t size = sizeof(JSAtom *) * natoms; if (nobjects != 0) size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *); if (nupvars != 0) - size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32); + size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32_t); if (nregexps != 0) size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *); if (ntrynotes != 0) size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote); if (nglobals != 0) size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry); + uint32_t totalClosed = nClosedArgs + nClosedVars; if (totalClosed != 0) - size += totalClosed * sizeof(uint32); + size += totalClosed * sizeof(uint32_t); - if (nconsts != 0) { - size += sizeof(JSConstArray); + /* + * To esnure jsval alignment for the const array we place it immediately + * after JSSomethingArray structs as their sizes all divide sizeof(jsval). + * This works as long as the data itself is allocated with proper + * alignment which we ensure below. + */ + JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(jsval) == 0); + JS_STATIC_ASSERT(sizeof(JSUpvarArray) % sizeof(jsval) == 0); + JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0); + JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0); + JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0); + if (nconsts != 0) + size += sizeof(JSConstArray) + nconsts * sizeof(Value); + + size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote); + + uint8_t *data = NULL; +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) { + /* + * Check that if inlineData is big enough to store const values, we + * can do that without any special alignment requirements given that + * the script as a GC thing is always aligned on Cell::CellSize. + */ + JS_STATIC_ASSERT(Cell::CellSize % sizeof(Value) == 0); + JS_STATIC_ASSERT(JS_SCRIPT_INLINE_DATA_LIMIT < sizeof(Value) || + offsetof(JSScript, inlineData) % sizeof(Value) == 0); + } else +#endif + { /* - * Calculate padding assuming that consts go after the other arrays, - * but before the bytecode and source notes. + * We assume that calloc aligns on sizeof(Value) if the size we ask to + * allocate divides sizeof(Value). */ - constPadding = (8 - (size % 8)) % 8; - size += constPadding + nconsts * sizeof(Value); + JS_STATIC_ASSERT(sizeof(Value) == sizeof(jsdouble)); + data = static_cast(cx->calloc_(JS_ROUNDUP(size, sizeof(Value)))); + if (!data) + return NULL; } - size += length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote); - - script = (JSScript *) cx->malloc_(size); - if (!script) + JSScript *script = js_NewGCScript(cx); + if (!script) { + Foreground::free_(data); return NULL; + } PodZero(script); +#ifdef JS_CRASH_DIAGNOSTICS + script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE; +#endif +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (!data) + data = script->inlineData; +#endif + script->data = data; script->length = length; script->version = version; - new (&script->bindings) Bindings(cx, emptyCallShape); - - uint8 *scriptEnd = reinterpret_cast(script + 1); + new (&script->bindings) Bindings(cx); - cursor = scriptEnd; + uint8_t *cursor = data; if (nobjects != 0) { - script->objectsOffset = (uint8)(cursor - scriptEnd); + script->objectsOffset = uint8_t(cursor - data); cursor += sizeof(JSObjectArray); } else { script->objectsOffset = JSScript::INVALID_OFFSET; } if (nupvars != 0) { - script->upvarsOffset = (uint8)(cursor - scriptEnd); + script->upvarsOffset = uint8_t(cursor - data); cursor += sizeof(JSUpvarArray); } else { script->upvarsOffset = JSScript::INVALID_OFFSET; } if (nregexps != 0) { - script->regexpsOffset = (uint8)(cursor - scriptEnd); + script->regexpsOffset = uint8_t(cursor - data); cursor += sizeof(JSObjectArray); } else { script->regexpsOffset = JSScript::INVALID_OFFSET; } if (ntrynotes != 0) { - script->trynotesOffset = (uint8)(cursor - scriptEnd); + script->trynotesOffset = uint8_t(cursor - data); cursor += sizeof(JSTryNoteArray); } else { script->trynotesOffset = JSScript::INVALID_OFFSET; } if (nglobals != 0) { - script->globalsOffset = (uint8)(cursor - scriptEnd); + script->globalsOffset = uint8_t(cursor - data); cursor += sizeof(GlobalSlotArray); } else { script->globalsOffset = JSScript::INVALID_OFFSET; } - JS_ASSERT(cursor - scriptEnd < 0xFF); + JS_ASSERT(cursor - data < 0xFF); if (nconsts != 0) { - script->constOffset = (uint8)(cursor - scriptEnd); + script->constOffset = uint8_t(cursor - data); cursor += sizeof(JSConstArray); } else { script->constOffset = JSScript::INVALID_OFFSET; @@ -1140,39 +1051,36 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom sizeof(JSTryNoteArray) + sizeof(GlobalSlotArray) < 0xFF); - if (natoms != 0) { - script->atomMap.length = natoms; - script->atomMap.vector = (JSAtom **)cursor; - vectorSize = natoms * sizeof(script->atomMap.vector[0]); - /* - * Clear object map's vector so the GC tracing can run when not yet - * all atoms are copied to the array. - */ - memset(cursor, 0, vectorSize); - cursor += vectorSize; + if (nconsts != 0) { + JS_ASSERT(reinterpret_cast(cursor) % sizeof(jsval) == 0); + script->consts()->length = nconsts; + script->consts()->vector = (HeapValue *)cursor; + cursor += nconsts * sizeof(script->consts()->vector[0]); + } + + if (natoms != 0) { + script->natoms = natoms; + script->atoms = reinterpret_cast(cursor); + cursor += natoms * sizeof(script->atoms[0]); } if (nobjects != 0) { script->objects()->length = nobjects; - script->objects()->vector = (JSObject **)cursor; - vectorSize = nobjects * sizeof(script->objects()->vector[0]); - memset(cursor, 0, vectorSize); - cursor += vectorSize; + script->objects()->vector = (HeapPtr *)cursor; + cursor += nobjects * sizeof(script->objects()->vector[0]); } if (nregexps != 0) { script->regexps()->length = nregexps; - script->regexps()->vector = (JSObject **)cursor; - vectorSize = nregexps * sizeof(script->regexps()->vector[0]); - memset(cursor, 0, vectorSize); - cursor += vectorSize; + script->regexps()->vector = (HeapPtr *)cursor; + cursor += nregexps * sizeof(script->regexps()->vector[0]); } if (ntrynotes != 0) { script->trynotes()->length = ntrynotes; - script->trynotes()->vector = (JSTryNote *)cursor; - vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); + script->trynotes()->vector = reinterpret_cast(cursor); + size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); #ifdef DEBUG memset(cursor, 0, vectorSize); #endif @@ -1181,170 +1089,170 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom if (nglobals != 0) { script->globals()->length = nglobals; - script->globals()->vector = (GlobalSlotArray::Entry *)cursor; - vectorSize = nglobals * sizeof(script->globals()->vector[0]); - cursor += vectorSize; + script->globals()->vector = reinterpret_cast(cursor); + cursor += nglobals * sizeof(script->globals()->vector[0]); } if (totalClosed != 0) { script->nClosedArgs = nClosedArgs; script->nClosedVars = nClosedVars; - script->closedSlots = (uint32 *)cursor; - cursor += totalClosed * sizeof(uint32); + script->closedSlots = reinterpret_cast(cursor); + cursor += totalClosed * sizeof(uint32_t); } + JS_ASSERT(nTypeSets <= UINT16_MAX); + script->nTypeSets = uint16_t(nTypeSets); + /* - * NB: We allocate the vector of uint32 upvar cookies after all vectors of + * NB: We allocate the vector of uint32_t upvar cookies after all vectors of * pointers, to avoid misalignment on 64-bit platforms. See bug 514645. */ if (nupvars != 0) { script->upvars()->length = nupvars; script->upvars()->vector = reinterpret_cast(cursor); - vectorSize = nupvars * sizeof(script->upvars()->vector[0]); - memset(cursor, 0, vectorSize); - cursor += vectorSize; - } - - /* Must go after other arrays; see constPadding definition. */ - if (nconsts != 0) { - cursor += constPadding; - script->consts()->length = nconsts; - script->consts()->vector = (Value *)cursor; - JS_ASSERT((size_t)cursor % sizeof(double) == 0); - vectorSize = nconsts * sizeof(script->consts()->vector[0]); - memset(cursor, 0, vectorSize); - cursor += vectorSize; + cursor += nupvars * sizeof(script->upvars()->vector[0]); } - script->code = script->main = (jsbytecode *)cursor; - JS_ASSERT(cursor + - length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote) == - (uint8 *)script + size); + script->code = (jsbytecode *)cursor; + JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size); - script->compartment = cx->compartment; -#ifdef CHECK_SCRIPT_OWNER - script->owner = cx->thread(); +#ifdef DEBUG + script->id_ = 0; #endif - JS_APPEND_LINK(&script->links, &cx->compartment->scripts); JS_ASSERT(script->getVersion() == version); return script; } JSScript * -JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) +JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce) { - uint32 mainLength, prologLength, nsrcnotes, nfixed; + uint32_t mainLength, prologLength, nfixed; JSScript *script; const char *filename; JSFunction *fun; /* The counts of indexed things must be checked during code generation. */ - JS_ASSERT(cg->atomList.count <= INDEX_LIMIT); - JS_ASSERT(cg->objectList.length <= INDEX_LIMIT); - JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT); - - mainLength = CG_OFFSET(cg); - prologLength = CG_PROLOG_OFFSET(cg); - - CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); - uint16 nClosedArgs = uint16(cg->closedArgs.length()); - JS_ASSERT(nClosedArgs == cg->closedArgs.length()); - uint16 nClosedVars = uint16(cg->closedVars.length()); - JS_ASSERT(nClosedVars == cg->closedVars.length()); + JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT); + JS_ASSERT(bce->objectList.length <= INDEX_LIMIT); + JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT); + + mainLength = bce->offset(); + prologLength = bce->prologOffset(); + + if (!bce->bindings.ensureShape(cx)) + return NULL; + + uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes()); + uint16_t nClosedArgs = uint16_t(bce->closedArgs.length()); + JS_ASSERT(nClosedArgs == bce->closedArgs.length()); + uint16_t nClosedVars = uint16_t(bce->closedVars.length()); + JS_ASSERT(nClosedVars == bce->closedVars.length()); + size_t upvarIndexCount = bce->upvarIndices.hasMap() ? bce->upvarIndices->count() : 0; script = NewScript(cx, prologLength + mainLength, nsrcnotes, - cg->atomList.count, cg->objectList.length, - cg->upvarList.count, cg->regexpList.length, - cg->ntrynotes, cg->constList.length(), - cg->globalUses.length(), nClosedArgs, nClosedVars, cg->version()); + bce->atomIndices->count(), bce->objectList.length, + upvarIndexCount, bce->regexpList.length, + bce->ntrynotes, bce->constList.length(), + bce->globalUses.length(), nClosedArgs, nClosedVars, + bce->typesetCount, bce->version()); if (!script) return NULL; - /* Now that we have script, error control flow must go to label bad. */ - script->main += prologLength; - memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); - memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); - nfixed = cg->inFunction() - ? cg->bindings.countVars() - : cg->sharpSlots(); + bce->bindings.makeImmutable(); + + JS_ASSERT(script->mainOffset == 0); + script->mainOffset = prologLength; + PodCopy(script->code, bce->prologBase(), prologLength); + PodCopy(script->main(), bce->base(), mainLength); + nfixed = bce->inFunction() ? bce->bindings.countVars() : 0; JS_ASSERT(nfixed < SLOTNO_LIMIT); - script->nfixed = (uint16) nfixed; - js_InitAtomMap(cx, &script->atomMap, &cg->atomList); + script->nfixed = uint16_t(nfixed); + js_InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms); - filename = cg->parser->tokenStream.getFilename(); + filename = bce->parser->tokenStream.getFilename(); if (filename) { script->filename = SaveScriptFilename(cx, filename); if (!script->filename) - goto bad; + return NULL; } - script->lineno = cg->firstLine; - if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) { - ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script"); - goto bad; + script->lineno = bce->firstLine; + if (script->nfixed + bce->maxStackDepth >= JS_BIT(16)) { + ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, + "script"); + return NULL; } - script->nslots = script->nfixed + cg->maxStackDepth; - script->staticLevel = uint16(cg->staticLevel); - script->principals = cg->parser->principals; + script->nslots = script->nfixed + bce->maxStackDepth; + script->staticLevel = uint16_t(bce->staticLevel); + script->principals = bce->parser->principals; + if (script->principals) JSPRINCIPALS_HOLD(cx, script->principals); - if (!js_FinishTakingSrcNotes(cx, cg, script->notes())) - goto bad; - if (cg->ntrynotes != 0) - js_FinishTakingTryNotes(cg, script->trynotes()); - if (cg->objectList.length != 0) - cg->objectList.finish(script->objects()); - if (cg->regexpList.length != 0) - cg->regexpList.finish(script->regexps()); - if (cg->constList.length() != 0) - cg->constList.finish(script->consts()); - if (cg->flags & TCF_NO_SCRIPT_RVAL) + /* Establish invariant: principals implies originPrincipals. */ + script->originPrincipals = bce->parser->originPrincipals; + if (!script->originPrincipals) + script->originPrincipals = script->principals; + if (script->originPrincipals) + JSPRINCIPALS_HOLD(cx, script->originPrincipals); + + script->sourceMap = (jschar *) bce->parser->tokenStream.releaseSourceMap(); + + if (!FinishTakingSrcNotes(cx, bce, script->notes())) + return NULL; + if (bce->ntrynotes != 0) + FinishTakingTryNotes(bce, script->trynotes()); + if (bce->objectList.length != 0) + bce->objectList.finish(script->objects()); + if (bce->regexpList.length != 0) + bce->regexpList.finish(script->regexps()); + if (bce->constList.length() != 0) + bce->constList.finish(script->consts()); + if (bce->flags & TCF_NO_SCRIPT_RVAL) script->noScriptRval = true; - if (cg->hasSharps()) - script->hasSharps = true; - if (cg->flags & TCF_STRICT_MODE_CODE) + if (bce->flags & TCF_STRICT_MODE_CODE) script->strictModeCode = true; - if (cg->flags & TCF_COMPILE_N_GO) + if (bce->flags & TCF_COMPILE_N_GO) { script->compileAndGo = true; - if (cg->callsEval()) + const StackFrame *fp = bce->parser->callerFrame; + if (fp && fp->isFunctionFrame()) + script->savedCallerFun = true; + } + if (bce->callsEval()) script->usesEval = true; - if (cg->flags & TCF_FUN_USES_ARGUMENTS) + if (bce->flags & TCF_FUN_USES_ARGUMENTS) script->usesArguments = true; - if (cg->flags & TCF_HAS_SINGLETONS) + if (bce->flags & TCF_HAS_SINGLETONS) script->hasSingletons = true; - if (cg->upvarList.count != 0) { - JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length); - memcpy(script->upvars()->vector, cg->upvarMap.vector, - cg->upvarList.count * sizeof(uint32)); - cg->upvarList.clear(); - cx->free_(cg->upvarMap.vector); - cg->upvarMap.vector = NULL; + if (bce->hasUpvarIndices()) { + JS_ASSERT(bce->upvarIndices->count() <= bce->upvarMap.length()); + PodCopy(script->upvars()->vector, bce->upvarMap.begin(), + bce->upvarIndices->count()); + bce->upvarIndices->clear(); + bce->upvarMap.clear(); } - if (cg->globalUses.length()) { - memcpy(script->globals()->vector, &cg->globalUses[0], - cg->globalUses.length() * sizeof(GlobalSlotArray::Entry)); + if (bce->globalUses.length()) { + PodCopy(script->globals()->vector, &bce->globalUses[0], + bce->globalUses.length()); } if (script->nClosedArgs) - memcpy(script->closedSlots, &cg->closedArgs[0], script->nClosedArgs * sizeof(uint32)); + PodCopy(script->closedSlots, &bce->closedArgs[0], script->nClosedArgs); if (script->nClosedVars) { - memcpy(&script->closedSlots[script->nClosedArgs], &cg->closedVars[0], - script->nClosedVars * sizeof(uint32)); + PodCopy(&script->closedSlots[script->nClosedArgs], &bce->closedVars[0], + script->nClosedVars); } - cg->bindings.makeImmutable(); - script->bindings.transfer(cx, &cg->bindings); + script->bindings.transfer(cx, &bce->bindings); - /* - * We initialize fun->u.script to be the script constructed above - * so that the debugger has a valid FUN_SCRIPT(fun). - */ fun = NULL; - if (cg->inFunction()) { - fun = cg->fun(); + if (bce->inFunction()) { + /* + * We initialize fun->script() to be the script constructed above + * so that the debugger has a valid fun->script(). + */ + fun = bce->fun(); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->script()); #ifdef DEBUG @@ -1353,227 +1261,165 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) else JS_ASSERT(script->bindings.countUpvars() == 0); #endif - fun->u.i.script = script; -#ifdef CHECK_SCRIPT_OWNER - script->owner = NULL; -#endif - if (cg->flags & TCF_FUN_HEAVYWEIGHT) + if (bce->flags & TCF_FUN_HEAVYWEIGHT) fun->flags |= JSFUN_HEAVYWEIGHT; + + /* + * Mark functions which will only be executed once as singletons. + * Skip this for flat closures, which must be copied on executing. + */ + bool singleton = + cx->typeInferenceEnabled() && + bce->parent && + bce->parent->compiling() && + bce->parent->asBytecodeEmitter()->checkSingletonContext() && + !fun->isFlatClosure(); + + if (!script->typeSetFunction(cx, fun, singleton)) + return NULL; + + fun->setScript(script); + script->globalObject = fun->getParent() ? &fun->getParent()->global() : NULL; + } else { + /* + * Initialize script->object, if necessary, so that the debugger has a + * valid holder object. + */ + if (bce->flags & TCF_NEED_SCRIPT_GLOBAL) + script->globalObject = GetCurrentGlobal(cx); } /* Tell the debugger about this compiled script. */ js_CallNewScriptHook(cx, script, fun); -#ifdef DEBUG - { - jsrefcount newEmptyLive, newLive, newTotal; - if (script->isEmpty()) { - newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts); - newLive = cx->runtime->liveScripts; - newTotal = - JS_RUNTIME_METER(cx->runtime, totalEmptyScripts) + cx->runtime->totalScripts; - } else { - newEmptyLive = cx->runtime->liveEmptyScripts; - newLive = JS_RUNTIME_METER(cx->runtime, liveScripts); - newTotal = - cx->runtime->totalEmptyScripts + JS_RUNTIME_METER(cx->runtime, totalScripts); - } - - jsrefcount oldHigh = cx->runtime->highWaterLiveScripts; - if (newEmptyLive + newLive > oldHigh) { - JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive); - if (getenv("JS_DUMP_LIVE_SCRIPTS")) { - fprintf(stderr, "high water script count: %d empty, %d not (total %d)\n", - newEmptyLive, newLive, newTotal); - } + if (!bce->parent) { + GlobalObject *compileAndGoGlobal = NULL; + if (script->compileAndGo) { + compileAndGoGlobal = script->globalObject; + if (!compileAndGoGlobal) + compileAndGoGlobal = &bce->scopeChain()->global(); } + Debugger::onNewScript(cx, script, compileAndGoGlobal); } -#endif - return script; - -bad: - js_DestroyScript(cx, script); - return NULL; -} + if (cx->hasRunOption(JSOPTION_PCCOUNT)) + (void) script->initCounts(cx); -JS_FRIEND_API(void) -js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) -{ - JSNewScriptHook hook; - - hook = cx->debugHooks->newScriptHook; - if (hook) { - AutoKeepAtoms keep(cx->runtime); - hook(cx, script->filename, script->lineno, script, fun, - cx->debugHooks->newScriptHookData); - } + return script; } -void -js_CallDestroyScriptHook(JSContext *cx, JSScript *script) +size_t +JSScript::computedSizeOfData() { - JSDestroyScriptHook hook; +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (data == inlineData) + return 0; +#endif - hook = cx->debugHooks->destroyScriptHook; - if (hook) - hook(cx, script, cx->debugHooks->destroyScriptHookData); - JS_ClearScriptTraps(cx, script); + uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote); + JS_ASSERT(dataEnd >= data); + return dataEnd - data; } -static void -DestroyScript(JSContext *cx, JSScript *script) +size_t +JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf) { -#ifdef DEBUG - if (script->isEmpty()) - JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts); - else - JS_RUNTIME_UNMETER(cx->runtime, liveScripts); -#endif - - if (script->principals) - JSPRINCIPALS_DROP(cx, script->principals); - - GSNCache *gsnCache = GetGSNCache(cx); - if (gsnCache->code == script->code) - gsnCache->purge(); - - /* - * Worry about purging the property cache and any compiled traces related - * to its bytecode if this script is being destroyed from JS_DestroyScript - * or equivalent according to a mandatory "New/Destroy" protocol. - * - * The GC purges all property caches when regenerating shapes upon shape - * generator overflow, so no need in that event to purge just the entries - * for this script. - * - * The GC purges trace-JITted code on every GC activation, not just when - * regenerating shapes, so we don't have to purge fragments if the GC is - * currently running. - * - * JS_THREADSAFE note: The code below purges only the current thread's - * property cache, so a script not owned by a function or object, which - * hands off lifetime management for that script to the GC, must be used by - * only one thread over its lifetime. - * - * This should be an API-compatible change, since a script is never safe - * against premature GC if shared among threads without a rooted object - * wrapping it to protect the script's mapped atoms against GC. We use - * script->owner to enforce this requirement via assertions. - */ -#ifdef CHECK_SCRIPT_OWNER - JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner); -#endif - - /* FIXME: bug 506341; would like to do this only if regenerating shapes. */ - if (!cx->runtime->gcRunning) { - JS_PROPERTY_CACHE(cx).purgeForScript(cx, script); - -#ifdef CHECK_SCRIPT_OWNER - JS_ASSERT(script->owner == cx->thread()); -#endif - } - -#ifdef JS_TRACER - PurgeScriptFragments(&script->compartment->traceMonitor, script); -#endif - -#if defined(JS_METHODJIT) - mjit::ReleaseScriptCode(cx, script); +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (data == inlineData) + return 0; #endif - JS_REMOVE_LINK(&script->links); - cx->free_(script); + return mallocSizeOf(data); } -void -js_DestroyScript(JSContext *cx, JSScript *script) +/* + * Nb: srcnotes are variable-length. This function computes the number of + * srcnote *slots*, which may be greater than the number of srcnotes. + */ +uint32_t +JSScript::numNotes() { - JS_ASSERT(!cx->runtime->gcRunning); - js_CallDestroyScriptHook(cx, script); - DestroyScript(cx, script); + jssrcnote *sn; + jssrcnote *notes_ = notes(); + for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + return sn - notes_ + 1; /* +1 for the terminator */ } -void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script) +JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) { - JS_ASSERT(cx->runtime->gcRunning); - js_CallDestroyScriptHook(cx, script); - DestroyScript(cx, script); + JS_ASSERT(!script->callDestroyHook); + if (JSNewScriptHook hook = cx->debugHooks->newScriptHook) { + AutoKeepAtoms keep(cx->runtime); + hook(cx, script->filename, script->lineno, script, fun, + cx->debugHooks->newScriptHookData); + } + script->callDestroyHook = true; } void -js_DestroyCachedScript(JSContext *cx, JSScript *script) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script) { - JS_ASSERT(cx->runtime->gcRunning); - DestroyScript(cx, script); + if (!script->callDestroyHook) + return; + + if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook) + hook(cx, script, cx->debugHooks->destroyScriptHookData); + script->callDestroyHook = false; + JS_ClearScriptTraps(cx, script); } void -js_TraceScript(JSTracer *trc, JSScript *script) +JSScript::finalize(JSContext *cx, bool background) { - JSAtomMap *map = &script->atomMap; - MarkAtomRange(trc, map->length, map->vector, "atomMap"); + CheckScript(this, NULL); - if (JSScript::isValidOffset(script->objectsOffset)) { - JSObjectArray *objarray = script->objects(); - MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); - } + js_CallDestroyScriptHook(cx, this); - if (JSScript::isValidOffset(script->regexpsOffset)) { - JSObjectArray *objarray = script->regexps(); - MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); - } - - if (JSScript::isValidOffset(script->constOffset)) { - JSConstArray *constarray = script->consts(); - MarkValueRange(trc, constarray->length, constarray->vector, "consts"); - } + JS_ASSERT_IF(principals, originPrincipals); + if (principals) + JSPRINCIPALS_DROP(cx, principals); + if (originPrincipals) + JSPRINCIPALS_DROP(cx, originPrincipals); - if (script->u.object) - MarkObject(trc, *script->u.object, "object"); - - if (IS_GC_MARKING_TRACER(trc) && script->filename) - js_MarkScriptFilename(script->filename); - - script->bindings.trace(trc); -} + if (types) + types->destroy(); -JSObject * -js_NewScriptObject(JSContext *cx, JSScript *script) -{ - AutoScriptRooter root(cx, script); +#ifdef JS_METHODJIT + mjit::ReleaseScriptCode(cx, this); +#endif - JS_ASSERT(!script->u.object); + destroyCounts(cx); - JSObject *obj = NewNonFunction(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return NULL; - obj->setPrivate(script); - script->u.object = obj; + if (sourceMap) + cx->free_(sourceMap); - /* - * Clear the object's proto, to avoid entraining stuff. Once we no longer use the parent - * for security checks, then we can clear the parent, too. - */ - obj->clearProto(); + if (debug) { + jsbytecode *end = code + length; + for (jsbytecode *pc = code; pc < end; pc++) { + if (BreakpointSite *site = getBreakpointSite(pc)) { + /* Breakpoints are swept before finalization. */ + JS_ASSERT(site->firstBreakpoint() == NULL); + site->clearTrap(cx, NULL, NULL); + JS_ASSERT(getBreakpointSite(pc) == NULL); + } + } + cx->free_(debug); + } -#ifdef CHECK_SCRIPT_OWNER - script->owner = NULL; +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (data != inlineData) #endif - - return obj; + { + JS_POISON(data, 0xdb, computedSizeOfData()); + cx->free_(data); + } } namespace js { -static const uint32 GSN_CACHE_THRESHOLD = 100; -static const uint32 GSN_CACHE_MAP_INIT_SIZE = 20; - -#ifdef JS_GSNMETER -# define GSN_CACHE_METER(cache,cnt) (++(cache)->stats.cnt) -#else -# define GSN_CACHE_METER(cache,cnt) ((void) 0) -#endif +static const uint32_t GSN_CACHE_THRESHOLD = 100; +static const uint32_t GSN_CACHE_MAP_INIT_SIZE = 20; void GSNCache::purge() @@ -1581,7 +1427,6 @@ GSNCache::purge() code = NULL; if (map.initialized()) map.finish(); - GSN_CACHE_METER(this, purges); } } /* namespace js */ @@ -1595,13 +1440,11 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) GSNCache *cache = GetGSNCache(cx); if (cache->code == script->code) { - GSN_CACHE_METER(cache, hits); JS_ASSERT(cache->map.initialized()); GSNCache::Map::Ptr p = cache->map.lookup(pc); return p ? p->value : NULL; } - GSN_CACHE_METER(cache, misses); size_t offset = 0; jssrcnote *result; for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) { @@ -1637,30 +1480,15 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) JS_ALWAYS_TRUE(cache->map.put(pc, sn)); } cache->code = script->code; - GSN_CACHE_METER(cache, fills); } } return result; } -uintN -js_FramePCToLineNumber(JSContext *cx, StackFrame *fp) -{ - return js_PCToLineNumber(cx, fp->script(), - fp->hasImacropc() ? fp->imacropc() : fp->pc(cx)); -} - uintN js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) { - JSOp op; - JSFunction *fun; - uintN lineno; - ptrdiff_t offset, target; - jssrcnote *sn; - JSSrcNoteType type; - /* Cope with StackFrame.pc value prior to entering js_Interpret. */ if (!pc) return 0; @@ -1669,25 +1497,23 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) * Special case: function definition needs no line number note because * the function's script contains its starting line number. */ - op = js_GetOpcode(cx, script, pc); + JSOp op = JSOp(*pc); if (js_CodeSpec[op].format & JOF_INDEXBASE) pc += js_CodeSpec[op].length; - if (*pc == JSOP_DEFFUN) { - GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun); - return fun->u.i.script->lineno; - } + if (*pc == JSOP_DEFFUN) + return script->getFunction(GET_UINT32_INDEX(pc))->script()->lineno; /* * General case: walk through source notes accumulating their deltas, * keeping track of line-number notes, until we pass the note for pc's * offset within script->code. */ - lineno = script->lineno; - offset = 0; - target = pc - script->code; - for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + uintN lineno = script->lineno; + ptrdiff_t offset = 0; + ptrdiff_t target = pc - script->code; + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { if (offset <= target) lineno = (uintN) js_GetSrcNoteOffset(sn, 0); @@ -1707,31 +1533,26 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) jsbytecode * js_LineNumberToPC(JSScript *script, uintN target) { - ptrdiff_t offset, best; - uintN lineno, bestdiff, diff; - jssrcnote *sn; - JSSrcNoteType type; - - offset = 0; - best = -1; - lineno = script->lineno; - bestdiff = SN_LINE_LIMIT; - for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + ptrdiff_t offset = 0; + ptrdiff_t best = -1; + uintN lineno = script->lineno; + uintN bestdiff = SN_LINE_LIMIT; + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { /* * Exact-match only if offset is not in the prolog; otherwise use * nearest greater-or-equal line number match. */ - if (lineno == target && script->code + offset >= script->main) + if (lineno == target && offset >= ptrdiff_t(script->mainOffset)) goto out; if (lineno >= target) { - diff = lineno - target; + uintN diff = lineno - target; if (diff < bestdiff) { bestdiff = diff; best = offset; } } offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { lineno = (uintN) js_GetSrcNoteOffset(sn, 0); } else if (type == SRC_NEWLINE) { @@ -1747,38 +1568,63 @@ js_LineNumberToPC(JSScript *script, uintN target) JS_FRIEND_API(uintN) js_GetScriptLineExtent(JSScript *script) { - uintN lineno; - jssrcnote *sn; - JSSrcNoteType type; - - lineno = script->lineno; - for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - type = (JSSrcNoteType) SN_TYPE(sn); + uintN lineno = script->lineno; + uintN maxLineNo = 0; + bool counting = true; + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { + if (maxLineNo < lineno) + maxLineNo = lineno; lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + counting = true; + if (maxLineNo < lineno) + maxLineNo = lineno; + else + counting = false; } else if (type == SRC_NEWLINE) { - lineno++; + if (counting) + lineno++; } } + + if (maxLineNo > lineno) + lineno = maxLineNo; + return 1 + lineno - script->lineno; } -const char * -js::CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop) +namespace js { + +uintN +CurrentLine(JSContext *cx) +{ + return js_PCToLineNumber(cx, cx->fp()->script(), cx->regs().pc); +} + +void +CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, uintN *linenop, + JSPrincipals **origin) { - if (!cx->running()) { + FrameRegsIter iter(cx); + while (!iter.done() && !iter.fp()->isScriptFrame()) + ++iter; + + if (iter.done()) { + *file = NULL; *linenop = 0; - return NULL; + *origin = NULL; + return; } - StackFrame *fp = cx->fp(); - while (fp->isDummyFrame()) - fp = fp->prev(); - - *linenop = js_FramePCToLineNumber(cx, fp); - return fp->script()->filename; + JSScript *script = iter.fp()->script(); + *file = script->filename; + *linenop = js_PCToLineNumber(cx, iter.fp()->script(), iter.pc()); + *origin = script->originPrincipals; } +} /* namespace js */ + class DisablePrincipalsTranscoding { JSSecurityCallbacks *callbacks; JSPrincipalsTranscoder temp; @@ -1826,8 +1672,7 @@ class AutoJSXDRState { JSScript * js_CloneScript(JSContext *cx, JSScript *script) { - JS_ASSERT(cx->compartment != script->compartment); - JS_ASSERT(script->compartment); + JS_ASSERT(cx->compartment != script->compartment()); // serialize script AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE)); @@ -1844,7 +1689,7 @@ js_CloneScript(JSContext *cx, JSScript *script) if (!js_XDRScript(w, &script)) return NULL; - uint32 nbytes; + uint32_t nbytes; void *p = JS_XDRMemGetData(w, &nbytes); if (!p) return NULL; @@ -1863,19 +1708,201 @@ js_CloneScript(JSContext *cx, JSScript *script) rstate.filename = script->filename; rstate.filenameSaved = true; - if (!js_XDRScript(r, &script)) + JSScript *newScript = NULL; + if (!js_XDRScript(r, &newScript)) return NULL; - // set the proper principals for the script - script->principals = script->compartment->principals; - if (script->principals) - JSPRINCIPALS_HOLD(cx, script->principals); + // set the proper principals for the script's new compartment + // the originPrincipals are not related to compartment, so just copy + newScript->principals = newScript->compartment()->principals; + newScript->originPrincipals = script->originPrincipals; + if (!newScript->originPrincipals) + newScript->originPrincipals = newScript->principals; + if (newScript->principals) { + JSPRINCIPALS_HOLD(cx, newScript->principals); + JSPRINCIPALS_HOLD(cx, newScript->originPrincipals); + } - return script; + return newScript; } void JSScript::copyClosedSlotsTo(JSScript *other) { - memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars); + js_memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars); +} + +bool +JSScript::ensureHasDebug(JSContext *cx) +{ + if (debug) + return true; + + size_t nbytes = offsetof(DebugScript, breakpoints) + length * sizeof(BreakpointSite*); + debug = (DebugScript *) cx->calloc_(nbytes); + if (!debug) + return false; + + /* + * Ensure that any Interpret() instances running on this script have + * interrupts enabled. The interrupts must stay enabled until the + * debug state is destroyed. + */ + InterpreterFrames *frames; + for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older) + frames->enableInterruptsIfRunning(this); + + return true; +} + +bool +JSScript::recompileForStepMode(JSContext *cx) +{ +#ifdef JS_METHODJIT + if (jitNormal || jitCtor) { + mjit::Recompiler::clearStackReferences(cx, this); + mjit::ReleaseScriptCode(cx, this); + } +#endif + return true; +} + +bool +JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue) +{ + JS_ASSERT(debug); + + uint32_t prior = debug->stepMode; + debug->stepMode = newValue; + + if (!prior != !newValue) { + /* Step mode has been enabled or disabled. Alert the methodjit. */ + if (!recompileForStepMode(cx)) { + debug->stepMode = prior; + return false; + } + + if (!stepModeEnabled() && !debug->numSites) { + cx->free_(debug); + debug = NULL; + } + } + + return true; +} + +bool +JSScript::setStepModeFlag(JSContext *cx, bool step) +{ + if (!ensureHasDebug(cx)) + return false; + + return tryNewStepMode(cx, (debug->stepMode & stepCountMask) | (step ? stepFlagMask : 0)); +} + +bool +JSScript::changeStepModeCount(JSContext *cx, int delta) +{ + if (!ensureHasDebug(cx)) + return false; + + assertSameCompartment(cx, this); + JS_ASSERT_IF(delta > 0, cx->compartment->debugMode()); + + uint32_t count = debug->stepMode & stepCountMask; + JS_ASSERT(((count + delta) & stepCountMask) == count + delta); + return tryNewStepMode(cx, + (debug->stepMode & stepFlagMask) | + ((count + delta) & stepCountMask)); +} + +BreakpointSite * +JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc, + GlobalObject *scriptGlobal) +{ + JS_ASSERT(size_t(pc - code) < length); + + if (!ensureHasDebug(cx)) + return NULL; + + BreakpointSite *&site = debug->breakpoints[pc - code]; + + if (!site) { + site = cx->runtime->new_(this, pc); + if (!site) { + js_ReportOutOfMemory(cx); + return NULL; + } + debug->numSites++; + } + + if (site->scriptGlobal) + JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal); + else + site->scriptGlobal = scriptGlobal; + + return site; +} + +void +JSScript::destroyBreakpointSite(JSRuntime *rt, jsbytecode *pc) +{ + JS_ASSERT(unsigned(pc - code) < length); + + BreakpointSite *&site = debug->breakpoints[pc - code]; + JS_ASSERT(site); + + rt->delete_(site); + site = NULL; + + if (--debug->numSites == 0 && !stepModeEnabled()) { + rt->free_(debug); + debug = NULL; + } +} + +void +JSScript::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler) +{ + if (!hasAnyBreakpointsOrStepMode()) + return; + + jsbytecode *end = code + length; + for (jsbytecode *pc = code; pc < end; pc++) { + BreakpointSite *site = getBreakpointSite(pc); + if (site) { + Breakpoint *nextbp; + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { + nextbp = bp->nextInSite(); + if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) + bp->destroy(cx); + } + } + } +} + +void +JSScript::clearTraps(JSContext *cx) +{ + if (!hasAnyBreakpointsOrStepMode()) + return; + + jsbytecode *end = code + length; + for (jsbytecode *pc = code; pc < end; pc++) { + BreakpointSite *site = getBreakpointSite(pc); + if (site) + site->clearTrap(cx); + } +} + +void +JSScript::markTrapClosures(JSTracer *trc) +{ + JS_ASSERT(hasAnyBreakpointsOrStepMode()); + + for (unsigned i = 0; i < length; i++) { + BreakpointSite *site = debug->breakpoints[i]; + if (site && site->trapHandler) + MarkValue(trc, site->trapClosure, "trap closure"); + } } diff --git a/deps/mozjs/js/src/jsscript.h b/deps/mozjs/js/src/jsscript.h index ed7dd8dad76..d957143ba59 100644 --- a/deps/mozjs/js/src/jsscript.h +++ b/deps/mozjs/js/src/jsscript.h @@ -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=4 sw=4 et tw=79 ft=cpp: * * ***** BEGIN LICENSE BLOCK ***** @@ -47,6 +47,11 @@ #include "jsprvtd.h" #include "jsdbgapi.h" #include "jsclist.h" +#include "jsinfer.h" +#include "jsopcode.h" +#include "jsscope.h" + +#include "gc/Barrier.h" /* * Type of try note associated with each catch or finally block, and also with @@ -75,12 +80,12 @@ namespace js { */ class UpvarCookie { - uint32 value; + uint32_t value; - static const uint32 FREE_VALUE = 0xfffffffful; + static const uint32_t FREE_VALUE = 0xfffffffful; void checkInvariants() { - JS_STATIC_ASSERT(sizeof(UpvarCookie) == sizeof(uint32)); + JS_STATIC_ASSERT(sizeof(UpvarCookie) == sizeof(uint32_t)); JS_STATIC_ASSERT(UPVAR_LEVEL_LIMIT < FREE_LEVEL); } @@ -89,26 +94,26 @@ class UpvarCookie * All levels above-and-including FREE_LEVEL are reserved so that * FREE_VALUE can be used as a special value. */ - static const uint16 FREE_LEVEL = 0x3fff; + static const uint16_t FREE_LEVEL = 0x3fff; /* * If a function has a higher static level than this limit, we will not * optimize it using UPVAR opcodes. */ - static const uint16 UPVAR_LEVEL_LIMIT = 16; - static const uint16 CALLEE_SLOT = 0xffff; - static bool isLevelReserved(uint16 level) { return level >= FREE_LEVEL; } + static const uint16_t UPVAR_LEVEL_LIMIT = 16; + static const uint16_t CALLEE_SLOT = 0xffff; + static bool isLevelReserved(uint16_t level) { return level >= FREE_LEVEL; } bool isFree() const { return value == FREE_VALUE; } - uint32 asInteger() const { return value; } + uint32_t asInteger() const { return value; } /* isFree check should be performed before using these accessors. */ - uint16 level() const { JS_ASSERT(!isFree()); return uint16(value >> 16); } - uint16 slot() const { JS_ASSERT(!isFree()); return uint16(value); } + uint16_t level() const { JS_ASSERT(!isFree()); return uint16_t(value >> 16); } + uint16_t slot() const { JS_ASSERT(!isFree()); return uint16_t(value); } void set(const UpvarCookie &other) { set(other.level(), other.slot()); } - void set(uint16 newLevel, uint16 newSlot) { value = (uint32(newLevel) << 16) | newSlot; } + void set(uint16_t newLevel, uint16_t newSlot) { value = (uint32_t(newLevel) << 16) | newSlot; } void makeFree() { set(0xffff, 0xffff); JS_ASSERT(isFree()); } - void fromInteger(uint32 u32) { value = u32; } + void fromInteger(uint32_t u32) { value = u32; } }; } @@ -117,45 +122,43 @@ class UpvarCookie * Exception handling record. */ struct JSTryNote { - uint8 kind; /* one of JSTryNoteKind */ - uint8 padding; /* explicit padding on uint16 boundary */ - uint16 stackDepth; /* stack depth upon exception handler entry */ - uint32 start; /* start of the try statement or for-in loop + uint8_t kind; /* one of JSTryNoteKind */ + uint8_t padding; /* explicit padding on uint16_t boundary */ + uint16_t stackDepth; /* stack depth upon exception handler entry */ + uint32_t start; /* start of the try statement or for-in loop relative to script->main */ - uint32 length; /* length of the try statement or for-in loop */ + uint32_t length; /* length of the try statement or for-in loop */ }; typedef struct JSTryNoteArray { JSTryNote *vector; /* array of indexed try notes */ - uint32 length; /* count of indexed try notes */ + uint32_t length; /* count of indexed try notes */ } JSTryNoteArray; typedef struct JSObjectArray { - JSObject **vector; /* array of indexed objects */ - uint32 length; /* count of indexed objects */ + js::HeapPtrObject *vector; /* array of indexed objects */ + uint32_t length; /* count of indexed objects */ } JSObjectArray; typedef struct JSUpvarArray { js::UpvarCookie *vector; /* array of indexed upvar cookies */ - uint32 length; /* count of indexed upvar cookies */ + uint32_t length; /* count of indexed upvar cookies */ } JSUpvarArray; typedef struct JSConstArray { - js::Value *vector; /* array of indexed constant values */ - uint32 length; + js::HeapValue *vector; /* array of indexed constant values */ + uint32_t length; } JSConstArray; -struct JSArenaPool; - namespace js { struct GlobalSlotArray { struct Entry { - uint32 atomIndex; /* index into atom table */ - uint32 slot; /* global obj slot number */ + uint32_t atomIndex; /* index into atom table */ + uint32_t slot; /* global obj slot number */ }; Entry *vector; - uint32 length; + uint32_t length; }; struct Shape; @@ -169,14 +172,15 @@ enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT, UPVAR }; * strict mode eval code, to give such code its own lexical environment). */ class Bindings { - js::Shape *lastBinding; - uint16 nargs; - uint16 nvars; - uint16 nupvars; - bool hasExtensibleParents; + HeapPtr lastBinding; + uint16_t nargs; + uint16_t nvars; + uint16_t nupvars; + bool hasDup_:1; // true if there are duplicate argument names + inline Shape *initialShape(JSContext *cx) const; public: - inline Bindings(JSContext *cx, EmptyShape *emptyCallShape); + inline Bindings(JSContext *cx); /* * Transfers ownership of bindings data from bindings into this fresh @@ -192,9 +196,9 @@ class Bindings { */ inline void clone(JSContext *cx, Bindings *bindings); - uint16 countArgs() const { return nargs; } - uint16 countVars() const { return nvars; } - uint16 countUpvars() const { return nupvars; } + uint16_t countArgs() const { return nargs; } + uint16_t countVars() const { return nvars; } + uint16_t countUpvars() const { return nupvars; } uintN countArgsAndVars() const { return nargs + nvars; } @@ -203,8 +207,23 @@ class Bindings { bool hasUpvars() const { return nupvars > 0; } bool hasLocalNames() const { return countLocalNames() > 0; } - /* Returns the shape lineage generated for these bindings. */ - inline js::Shape *lastShape() const; + /* Ensure these bindings have a shape lineage. */ + inline bool ensureShape(JSContext *cx); + + /* Return the shape lineage generated for these bindings. */ + inline Shape *lastShape() const; + + /* + * Return the shape to use to create a call object for these bindings. + * The result is guaranteed not to have duplicate property names. + */ + Shape *callObjectShape(JSContext *cx) const; + + /* See Scope::extensibleParents */ + inline bool extensibleParents(); + bool setExtensibleParents(JSContext *cx); + + bool setParent(JSContext *cx, JSObject *obj); enum { /* @@ -242,16 +261,19 @@ class Bindings { bool addUpvar(JSContext *cx, JSAtom *name) { return add(cx, name, UPVAR); } - bool addArgument(JSContext *cx, JSAtom *name, uint16 *slotp) { + bool addArgument(JSContext *cx, JSAtom *name, uint16_t *slotp) { JS_ASSERT(name != NULL); /* not destructuring */ *slotp = nargs; return add(cx, name, ARGUMENT); } - bool addDestructuring(JSContext *cx, uint16 *slotp) { + bool addDestructuring(JSContext *cx, uint16_t *slotp) { *slotp = nargs; return add(cx, NULL, ARGUMENT); } + void noteDup() { hasDup_ = true; } + bool hasDup() const { return hasDup_; } + /* * Look up an argument or variable name, returning its kind when found or * NONE when no such name exists. When indexp is not null and the name @@ -266,30 +288,15 @@ class Bindings { } /* - * Function and macros to work with local names as an array of words. - * getLocalNameArray returns the array, or null if we are out of memory. - * This function must be called only when hasLocalNames(). - * - * The supplied pool is used to allocate the returned array, so the caller - * is obligated to mark and release to free it. + * This method returns the local variable, argument, etc. names used by a + * script. This function must be called only when hasLocalNames(). * - * The elements of the array with index less than nargs correspond to the - * the names of arguments. An index >= nargs addresses a var binding. Use - * JS_LOCAL_NAME_TO_ATOM to convert array's element to an atom pointer. - * This pointer can be null when the element is for an argument + * The elements of the vector with index less than nargs correspond to the + * the names of arguments. An index >= nargs addresses a var binding. + * The name at an element will be null when the element is for an argument * corresponding to a destructuring pattern. - * - * If nameWord does not name an argument, use JS_LOCAL_NAME_IS_CONST to - * check if nameWord corresponds to the const declaration. - */ - jsuword * - getLocalNameArray(JSContext *cx, JSArenaPool *pool); - - /* - * Returns the slot where the sharp array is stored, or a value < 0 if no - * sharps are present or in case of failure. */ - int sharpSlotBase(JSContext *cx); + bool getLocalNameArray(JSContext *cx, Vector *namesp); /* * Protect stored bindings from mutation. Subsequent attempts to add @@ -298,54 +305,6 @@ class Bindings { */ void makeImmutable(); - /* - * Sometimes call objects and run-time block objects need unique shapes, but - * sometimes they don't. - * - * Property cache entries only record the shapes of the first and last - * objects along the search path, so if the search traverses more than those - * two objects, then those first and last shapes must determine the shapes - * of everything else along the path. The js_PurgeScopeChain stuff takes - * care of making this work, but that suffices only because we require that - * start points with the same shape have the same successor object in the - * search path --- a cache hit means the starting shapes were equal, which - * means the seach path tail (everything but the first object in the path) - * was shared, which in turn means the effects of a purge will be seen by - * all affected starting search points. - * - * For call and run-time block objects, the "successor object" is the scope - * chain parent. Unlike prototype objects (of which there are usually few), - * scope chain parents are created frequently (possibly on every call), so - * following the shape-implies-parent rule blindly would lead one to give - * every call and block its own shape. - * - * In many cases, however, it's not actually necessary to give call and - * block objects their own shapes, and we can do better. If the code will - * always be used with the same global object, and none of the enclosing - * call objects could have bindings added to them at runtime (by direct eval - * calls or function statements), then we can use a fixed set of shapes for - * those objects. You could think of the shapes in the functions' bindings - * and compile-time blocks as uniquely identifying the global object(s) at - * the end of the scope chain. - * - * (In fact, some JSScripts we do use against multiple global objects (see - * bug 618497), and using the fixed shapes isn't sound there.) - * - * In deciding whether a call or block has any extensible parents, we - * actually only need to consider enclosing calls; blocks are never - * extensible, and the other sorts of objects that appear in the scope - * chains ('with' blocks, say) are not CacheableNonGlobalScopes. - * - * If the hasExtensibleParents flag is set, then Call objects created for - * the function this Bindings describes need unique shapes. If the flag is - * clear, then we can use lastBinding's shape. - * - * For blocks, we set the the OWN_SHAPE flag on the compiler-generated - * blocksto indicate that their clones need unique shapes. - */ - void setExtensibleParents() { hasExtensibleParents = true; } - bool extensibleParents() const { return hasExtensibleParents; } - /* * These methods provide direct access to the shape path normally * encapsulated by js::Bindings. These methods may be used to make a @@ -353,15 +312,21 @@ class Bindings { * oldest (i.e., last or right-most to first or left-most in source order). * * Sometimes iteration order must be from oldest to youngest, however. For - * such cases, use js::Bindings::getLocalNameArray. The RAII class - * js::AutoLocalNameArray, defined in jscntxt.h, should be used where - * possible instead of direct calls to getLocalNameArray. + * such cases, use js::Bindings::getLocalNameArray. */ const js::Shape *lastArgument() const; const js::Shape *lastVariable() const; const js::Shape *lastUpvar() const; void trace(JSTracer *trc); + + /* Rooter for stack allocated Bindings. */ + struct StackRoot { + RootShape root; + StackRoot(JSContext *cx, Bindings *bindings) + : root(cx, (Shape **) &bindings->lastBinding) + {} + }; }; } /* namespace js */ @@ -369,10 +334,6 @@ class Bindings { #define JS_OBJECT_ARRAY_SIZE(length) \ (offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length)) -#if defined DEBUG && defined JS_THREADSAFE -# define CHECK_SCRIPT_OWNER 1 -#endif - #ifdef JS_METHODJIT namespace JSC { class ExecutablePool; @@ -380,26 +341,69 @@ namespace JSC { #define JS_UNJITTABLE_SCRIPT (reinterpret_cast(1)) -enum JITScriptStatus { - JITScript_None, - JITScript_Invalid, - JITScript_Valid -}; +namespace js { namespace mjit { struct JITScript; } } +#endif namespace js { -namespace mjit { -struct JITScript; +namespace analyze { class ScriptAnalysis; } -} -} -#endif +class ScriptOpcodeCounts +{ + friend struct ::JSScript; + friend struct ScriptOpcodeCountsPair; + OpcodeCounts *counts; + + public: + + ScriptOpcodeCounts() : counts(NULL) { + } + + inline void destroy(JSContext *cx); + + void steal(ScriptOpcodeCounts &other) { + *this = other; + js::PodZero(&other); + } + + // Boolean conversion, for 'if (counters) ...' + operator void*() const { + return counts; + } +}; + +class DebugScript +{ + friend struct ::JSScript; -struct JSScript { + /* + * When non-zero, compile script in single-step mode. The top bit is set and + * cleared by setStepMode, as used by JSD. The lower bits are a count, + * adjusted by changeStepModeCount, used by the Debugger object. Only + * when the bit is clear and the count is zero may we compile the script + * without single-step support. + */ + uint32_t stepMode; + + /* Number of breakpoint sites at opcodes in the script. */ + uint32_t numSites; + + /* + * Array with all breakpoints installed at opcodes in the script, indexed + * by the offset of the opcode into the script. + */ + BreakpointSite *breakpoints[1]; +}; + +} /* namespace js */ + +static const uint32_t JS_SCRIPT_COOKIE = 0xc00cee; + +struct JSScript : public js::gc::Cell { /* * Two successively less primitive ways to make a new JSScript. The first * does *not* call a non-null cx->runtime->newScriptHook -- only the second, - * NewScriptFromCG, calls this optional debugger hook. + * NewScriptFromEmitter, calls this optional debugger hook. * * The NewScript function can't know whether the script it creates belongs * to a function, or is top-level or eval code, but the debugger wants access @@ -407,46 +411,56 @@ struct JSScript { * are responsible for notifying the debugger after successfully creating any * kind (function or other) of new JSScript. */ - static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, - uint32 nobjects, uint32 nupvars, uint32 nregexps, - uint32 ntrynotes, uint32 nconsts, uint32 nglobals, - uint16 nClosedArgs, uint16 nClosedVars, JSVersion version); + static JSScript *NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms, + uint32_t nobjects, uint32_t nupvars, uint32_t nregexps, + uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals, + uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, + JSVersion version); - static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg); + static JSScript *NewScriptFromEmitter(JSContext *cx, js::BytecodeEmitter *bce); - /* FIXME: bug 586181 */ - JSCList links; /* Links for compartment script list */ +#ifdef JS_CRASH_DIAGNOSTICS + /* + * Make sure that the cookie size does not affect the GC alignment + * requirements. + */ + uint32_t cookie1[Cell::CellSize / sizeof(uint32_t)]; +#endif jsbytecode *code; /* bytecodes and their immediate operands */ - uint32 length; /* length of code vector */ + uint8_t *data; /* pointer to variable-length data array */ + uint32_t length; /* length of code vector */ private: - uint16 version; /* JS version under which script was compiled */ - - size_t callCount_; /* Number of times the script has been called. */ + uint16_t version; /* JS version under which script was compiled */ public: - uint16 nfixed; /* number of slots besides stack operands in + uint16_t nfixed; /* number of slots besides stack operands in slot array */ - /* * Offsets to various array structures from the end of this script, or * JSScript::INVALID_OFFSET if the array has length 0. */ - uint8 objectsOffset; /* offset to the array of nested function, + uint8_t objectsOffset; /* offset to the array of nested function, block, scope, xml and one-time regexps objects */ - uint8 upvarsOffset; /* offset of the array of display ("up") + uint8_t upvarsOffset; /* offset of the array of display ("up") closure vars */ - uint8 regexpsOffset; /* offset to the array of to-be-cloned + uint8_t regexpsOffset; /* offset to the array of to-be-cloned regexps */ - uint8 trynotesOffset; /* offset to the array of try notes */ - uint8 globalsOffset; /* offset to the array of global slots */ - uint8 constOffset; /* offset to the array of constants */ + uint8_t trynotesOffset; /* offset to the array of try notes */ + uint8_t globalsOffset; /* offset to the array of global slots */ + uint8_t constOffset; /* offset to the array of constants */ + + uint16_t nTypeSets; /* number of type sets used in this script for + dynamic type monitoring */ + + uint32_t lineno; /* base line number of script */ + uint32_t mainOffset; /* offset of main entry point from code, after + predef'ing prolog */ bool noScriptRval:1; /* no need for result value of last expression statement */ - bool savedCallerFun:1; /* object 0 is caller function */ - bool hasSharps:1; /* script uses sharp variables */ + bool savedCallerFun:1; /* can call getCallerFunction() */ bool strictModeCode:1; /* code is in strict mode */ bool compileAndGo:1; /* script was compiled with TCF_COMPILE_N_GO */ bool usesEval:1; /* script uses eval() */ @@ -454,52 +468,157 @@ struct JSScript { bool warnedAboutTwoArgumentEval:1; /* have warned about use of obsolete eval(s, o) in this script */ + bool warnedAboutUndefinedProp:1; /* have warned about uses of + undefined properties in this + script */ bool hasSingletons:1; /* script has singleton objects */ + bool isOuterFunction:1; /* function is heavyweight, with inner functions */ + bool isInnerFunction:1; /* function is directly nested in a heavyweight + * outer function */ + bool isActiveEval:1; /* script came from eval(), and is still active */ + bool isCachedEval:1; /* script came from eval(), and is in eval cache */ + bool usedLazyArgs:1; /* script has used lazy arguments at some point */ + bool createdArgs:1; /* script has had arguments objects created */ + bool uninlineable:1; /* script is considered uninlineable by analysis */ + bool reentrantOuterFunction:1; /* outer function marked reentrant */ + bool typesPurged:1; /* TypeScript has been purged at some point */ #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ - bool singleStepMode:1; /* compile script in single-step mode */ + bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */ +#endif + bool callDestroyHook:1;/* need to call destroy hook */ + + uint32_t natoms; /* length of atoms array */ + uint16_t nslots; /* vars plus maximum stack depth */ + uint16_t staticLevel;/* static level for display maintenance */ + + uint16_t nClosedArgs; /* number of args which are closed over. */ + uint16_t nClosedVars; /* number of vars which are closed over. */ + + /* + * To ensure sizeof(JSScript) % gc::Cell::CellSize == 0 on we must pad + * the script with 4 bytes. We use them to store tiny scripts like empty + * scripts. + */ +#if JS_BITS_PER_WORD == 64 +#define JS_SCRIPT_INLINE_DATA_LIMIT 4 + uint8_t inlineData[JS_SCRIPT_INLINE_DATA_LIMIT]; #endif - jsbytecode *main; /* main entry point, after predef'ing prolog */ - JSAtomMap atomMap; /* maps immediate index to literal struct */ - JSCompartment *compartment; /* compartment the script was compiled for */ const char *filename; /* source filename or null */ - uint32 lineno; /* base line number of script */ - uint16 nslots; /* vars plus maximum stack depth */ - uint16 staticLevel;/* static level for display maintenance */ - uint16 nClosedArgs; /* number of args which are closed over. */ - uint16 nClosedVars; /* number of vars which are closed over. */ + JSAtom **atoms; /* maps immediate index to literal struct */ + private: + size_t useCount; /* Number of times the script has been called + * or has had backedges taken. Reset if the + * script's JIT code is forcibly discarded. */ + public: js::Bindings bindings; /* names of top-level variables in this script (and arguments if this is a function script) */ JSPrincipals *principals;/* principals for this script */ - union { - /* - * A script object of class js_ScriptClass, to ensure the script is GC'd. - * - All scripts returned by JSAPI functions (JS_CompileScript, - * JS_CompileFile, etc.) have these objects. - * - Function scripts never have script objects; such scripts are owned - * by their function objects. - * - Temporary scripts created by obj_eval, JS_EvaluateScript, and - * similar functions never have these objects; such scripts are - * explicitly destroyed by the code that created them. - * Debugging API functions (JSDebugHooks::newScriptHook; - * JS_GetFunctionScript) may reveal sans-script-object Function and - * temporary scripts to clients, but clients must never call - * JS_NewScriptObject on such scripts: doing so would double-free them, - * once from the explicit call to js_DestroyScript, and once when the - * script object is garbage collected. - */ - JSObject *object; - JSScript *nextToGC; /* next to GC in rt->scriptsToGC list */ - } u; + JSPrincipals *originPrincipals; /* see jsapi.h 'originPrincipals' comment */ + jschar *sourceMap; /* source map file or null */ + + /* + * A global object for the script. + * - All scripts returned by JSAPI functions (JS_CompileScript, + * JS_CompileUTF8File, etc.) have a non-null globalObject. + * - A function script has a globalObject if the function comes from a + * compile-and-go script. + * - Temporary scripts created by obj_eval, JS_EvaluateScript, and + * similar functions never have the globalObject field set; for such + * scripts the global should be extracted from the JS frame that + * execute scripts. + */ + js::HeapPtr globalObject; + + /* Hash table chaining for JSCompartment::evalCache. */ + JSScript *&evalHashLink() { return *globalObject.unsafeGetUnioned(); } + + uint32_t *closedSlots; /* vector of closed slots; args first, then vars. */ + + /* Execution and profiling information for JIT code in the script. */ + js::ScriptOpcodeCounts pcCounters; -#ifdef CHECK_SCRIPT_OWNER - JSThread *owner; /* for thread-safe life-cycle assertions */ + private: + js::DebugScript *debug; + js::HeapPtrFunction function_; + public: + + /* + * Original compiled function for the script, if it has a function. + * NULL for global and eval scripts. + */ + JSFunction *function() const { return function_; } + void setFunction(JSFunction *fun); + +#ifdef JS_CRASH_DIAGNOSTICS + /* All diagnostic fields must be multiples of Cell::CellSize. */ + uint32_t cookie2[Cell::CellSize / sizeof(uint32_t)]; #endif - uint32 *closedSlots; /* vector of closed slots; args first, then vars. */ +#ifdef DEBUG + /* + * Unique identifier within the compartment for this script, used for + * printing analysis information. + */ + uint32_t id_; + uint32_t idpad; + unsigned id(); +#else + unsigned id() { return 0; } +#endif + + /* Persistent type information retained across GCs. */ + js::types::TypeScript *types; + +#if JS_BITS_PER_WORD == 32 + private: + void *padding_; + public: +#endif + + /* Ensure the script has a TypeScript. */ + inline bool ensureHasTypes(JSContext *cx); + + /* + * Ensure the script has scope and bytecode analysis information. + * Performed when the script first runs, or first runs after a TypeScript + * GC purge. If scope is NULL then the script must already have types with + * scope information. + */ + inline bool ensureRanAnalysis(JSContext *cx, JSObject *scope); + + /* Ensure the script has type inference analysis information. */ + inline bool ensureRanInference(JSContext *cx); + + inline bool hasAnalysis(); + inline void clearAnalysis(); + inline js::analyze::ScriptAnalysis *analysis(); + + /* + * Associates this script with a specific function, constructing a new type + * object for the function if necessary. + */ + bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false); + + inline bool hasGlobal() const; + inline bool hasClearedGlobal() const; + + inline js::GlobalObject *global() const; + inline js::types::TypeScriptNesting *nesting() const; + inline void clearNesting(); + + /* Return creation time global or null. */ + js::GlobalObject *getGlobalObjectOrNull() const { + return isCachedEval ? NULL : globalObject.get(); + } + + private: + bool makeTypes(JSContext *cx); + bool makeAnalysis(JSContext *cx); public: + #ifdef JS_METHODJIT // Fast-cached pointers to make calls faster. These are also used to // quickly test whether there is JIT code; a NULL value means no @@ -510,72 +629,101 @@ struct JSScript { js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ +#endif +#ifdef JS_METHODJIT bool hasJITCode() { return jitNormal || jitCtor; } // These methods are implemented in MethodJIT.h. inline void **nativeMap(bool constructing); - inline void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc); inline void *nativeCodeForPC(bool constructing, jsbytecode *pc); js::mjit::JITScript *getJIT(bool constructing) { return constructing ? jitCtor : jitNormal; } - size_t callCount() const { return callCount_; } - size_t incCallCount() { return ++callCount_; } + size_t getUseCount() const { return useCount; } + size_t incUseCount() { return ++useCount; } + size_t *addressOfUseCount() { return &useCount; } + void resetUseCount() { useCount = 0; } + + /* + * Size of the JITScript and all sections. If |mallocSizeOf| is NULL, the + * size is computed analytically. (This method is implemented in + * MethodJIT.cpp.) + */ + size_t sizeOfJitScripts(JSMallocSizeOfFun mallocSizeOf); - JITScriptStatus getJITStatus(bool constructing) { - void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; - if (addr == NULL) - return JITScript_None; - if (addr == JS_UNJITTABLE_SCRIPT) - return JITScript_Invalid; - return JITScript_Valid; - } #endif + /* Counter accessors. */ + js::OpcodeCounts getCounts(jsbytecode *pc) { + JS_ASSERT(size_t(pc - code) < length); + return pcCounters.counts[pc - code]; + } + + bool initCounts(JSContext *cx); + void destroyCounts(JSContext *cx); + + jsbytecode *main() { + return code + mainOffset; + } + + /* + * computedSizeOfData() is the in-use size of all the data sections. + * sizeOfData() is the size of the block allocated to hold all the data sections + * (which can be larger than the in-use size). + */ + size_t computedSizeOfData(); + size_t sizeOfData(JSMallocSizeOfFun mallocSizeOf); + + uint32_t numNotes(); /* Number of srcnote slots in the srcnotes section */ + /* Script notes are allocated right after the code. */ jssrcnote *notes() { return (jssrcnote *)(code + length); } - static const uint8 INVALID_OFFSET = 0xFF; - static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; } + static const uint8_t INVALID_OFFSET = 0xFF; + static bool isValidOffset(uint8_t offset) { return offset != INVALID_OFFSET; } JSObjectArray *objects() { JS_ASSERT(isValidOffset(objectsOffset)); - return (JSObjectArray *)((uint8 *) (this + 1) + objectsOffset); + return reinterpret_cast(data + objectsOffset); } JSUpvarArray *upvars() { JS_ASSERT(isValidOffset(upvarsOffset)); - return (JSUpvarArray *) ((uint8 *) (this + 1) + upvarsOffset); + return reinterpret_cast(data + upvarsOffset); } JSObjectArray *regexps() { JS_ASSERT(isValidOffset(regexpsOffset)); - return (JSObjectArray *) ((uint8 *) (this + 1) + regexpsOffset); + return reinterpret_cast(data + regexpsOffset); } JSTryNoteArray *trynotes() { JS_ASSERT(isValidOffset(trynotesOffset)); - return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset); + return reinterpret_cast(data + trynotesOffset); } js::GlobalSlotArray *globals() { JS_ASSERT(isValidOffset(globalsOffset)); - return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset); + return reinterpret_cast(data + globalsOffset); } JSConstArray *consts() { JS_ASSERT(isValidOffset(constOffset)); - return (JSConstArray *) ((uint8 *) (this + 1) + constOffset); + return reinterpret_cast(data + constOffset); } JSAtom *getAtom(size_t index) { - JS_ASSERT(index < atomMap.length); - return atomMap.vector[index]; + JS_ASSERT(index < natoms); + return atoms[index]; + } + + js::PropertyName *getName(size_t index) { + return getAtom(index)->asPropertyName(); } JSObject *getObject(size_t index) { @@ -584,23 +732,12 @@ struct JSScript { return arr->vector[index]; } - uint32 getGlobalSlot(size_t index) { - js::GlobalSlotArray *arr = globals(); - JS_ASSERT(index < arr->length); - return arr->vector[index].slot; - } - - JSAtom *getGlobalAtom(size_t index) { - js::GlobalSlotArray *arr = globals(); - JS_ASSERT(index < arr->length); - return getAtom(arr->vector[index].atomIndex); - } - JSVersion getVersion() const { return JSVersion(version); } inline JSFunction *getFunction(size_t index); + inline JSFunction *getCallerFunction(); inline JSObject *getRegExp(size_t index); @@ -617,21 +754,85 @@ struct JSScript { */ inline bool isEmpty() const; - uint32 getClosedArg(uint32 index) { + uint32_t getClosedArg(uint32_t index) { JS_ASSERT(index < nClosedArgs); return closedSlots[index]; } - uint32 getClosedVar(uint32 index) { + uint32_t getClosedVar(uint32_t index) { JS_ASSERT(index < nClosedVars); return closedSlots[nClosedArgs + index]; } void copyClosedSlotsTo(JSScript *other); + + private: + static const uint32_t stepFlagMask = 0x80000000U; + static const uint32_t stepCountMask = 0x7fffffffU; + + /* + * Attempt to recompile with or without single-stepping support, as directed + * by stepModeEnabled(). + */ + bool recompileForStepMode(JSContext *cx); + + /* Attempt to change this->stepMode to |newValue|. */ + bool tryNewStepMode(JSContext *cx, uint32_t newValue); + + bool ensureHasDebug(JSContext *cx); + + public: + bool hasBreakpointsAt(jsbytecode *pc) { return !!getBreakpointSite(pc); } + bool hasAnyBreakpointsOrStepMode() { return !!debug; } + + js::BreakpointSite *getBreakpointSite(jsbytecode *pc) + { + JS_ASSERT(size_t(pc - code) < length); + return debug ? debug->breakpoints[pc - code] : NULL; + } + + js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc, + js::GlobalObject *scriptGlobal); + + void destroyBreakpointSite(JSRuntime *rt, jsbytecode *pc); + + void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler); + void clearTraps(JSContext *cx); + + void markTrapClosures(JSTracer *trc); + + /* + * Set or clear the single-step flag. If the flag is set or the count + * (adjusted by changeStepModeCount) is non-zero, then the script is in + * single-step mode. (JSD uses an on/off-style interface; Debugger uses a + * count-style interface.) + */ + bool setStepModeFlag(JSContext *cx, bool step); + + /* + * Increment or decrement the single-step count. If the count is non-zero or + * the flag (set by setStepModeFlag) is set, then the script is in + * single-step mode. (JSD uses an on/off-style interface; Debugger uses a + * count-style interface.) + */ + bool changeStepModeCount(JSContext *cx, int delta); + + bool stepModeEnabled() { return debug && !!debug->stepMode; } + +#ifdef DEBUG + uint32_t stepModeCount() { return debug ? (debug->stepMode & stepCountMask) : 0; } +#endif + + void finalize(JSContext *cx, bool background); + + static inline void writeBarrierPre(JSScript *script); + static inline void writeBarrierPost(JSScript *script, void *addr); + + static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; } }; -#define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script - uses sharp variables */ +/* If this fails, padding_ can be removed. */ +JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0); static JS_INLINE uintN StackDepth(JSScript *script) @@ -639,54 +840,14 @@ StackDepth(JSScript *script) return script->nslots - script->nfixed; } -/* - * If pc_ does not point within script_'s bytecode, then it must point into an - * imacro body, so we use cx->runtime common atoms instead of script_'s atoms. - * This macro uses cx from its callers' environments in the pc-in-imacro case. - */ -#define JS_GET_SCRIPT_ATOM(script_, pc_, index, atom) \ - JS_BEGIN_MACRO \ - if ((pc_) < (script_)->code || \ - (script_)->code + (script_)->length <= (pc_)) { \ - JS_ASSERT((size_t)(index) < js_common_atom_count); \ - (atom) = COMMON_ATOMS_START(&cx->runtime->atomState)[index]; \ - } else { \ - (atom) = script_->getAtom(index); \ - } \ - JS_END_MACRO - -extern JS_FRIEND_DATA(js::Class) js_ScriptClass; - -extern JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj); - -/* - * On first new context in rt, initialize script runtime state, specifically - * the script filename table and its lock. - */ -extern JSBool -js_InitRuntimeScriptState(JSRuntime *rt); - -/* - * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any - * script filename table entries that have not been GC'd. - * - * This allows script filename prefixes to outlive any context in rt. - */ -extern void -js_FreeRuntimeScriptState(JSRuntime *rt); - extern void js_MarkScriptFilename(const char *filename); extern void -js_MarkScriptFilenames(JSRuntime *rt); - -extern void -js_SweepScriptFilenames(JSRuntime *rt); +js_SweepScriptFilenames(JSCompartment *comp); /* - * New-script-hook calling is factored from js_NewScriptFromCG so that it + * New-script-hook calling is factored from NewScriptFromEmitter so that it * and callers of js_XDRScript can share this code. In the case of callers * of js_XDRScript, the hook should be invoked only after successful decode * of any owning function (the fun parameter) or script object (null fun). @@ -697,30 +858,34 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); extern void js_CallDestroyScriptHook(JSContext *cx, JSScript *script); -/* - * The function must be used only outside the GC for a script that was run - * only on the current thread. - */ -extern void -js_DestroyScript(JSContext *cx, JSScript *script); +namespace js { -extern void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script); +struct ScriptOpcodeCountsPair +{ + JSScript *script; + ScriptOpcodeCounts counters; -/* - * Script objects may be cached and reused, in which case their JSD-visible - * lifetimes may be shorter than their actual lifetimes. Destroy one such - * script for real as part of a GC pass. From JSD's point of view, the script - * is already dead. - */ -extern void -js_DestroyCachedScript(JSContext *cx, JSScript *script); + OpcodeCounts &getCounts(jsbytecode *pc) const { + JS_ASSERT(unsigned(pc - script->code) < script->length); + return counters.counts[pc - script->code]; + } +}; -extern void -js_TraceScript(JSTracer *trc, JSScript *script); +#ifdef JS_CRASH_DIAGNOSTICS + +void +CheckScript(JSScript *script, JSScript *prev); -extern JSObject * -js_NewScriptObject(JSContext *cx, JSScript *script); +#else + +inline void +CheckScript(JSScript *script, JSScript *prev) +{ +} + +#endif /* !JS_CRASH_DIAGNOSTICS */ + +} /* namespace js */ /* * To perturb as little code as possible, we introduce a js_GetSrcNote lookup @@ -732,14 +897,6 @@ js_NewScriptObject(JSContext *cx, JSScript *script); extern jssrcnote * js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); -/* - * NOTE: use js_FramePCToLineNumber(cx, fp) when you have an active fp, in - * preference to js_PCToLineNumber (cx, fp->script fp->regs->pc), because - * fp->imacpc may be non-null, indicating an active imacro. - */ -extern uintN -js_FramePCToLineNumber(JSContext *cx, js::StackFrame *fp); - extern uintN js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); @@ -751,6 +908,9 @@ js_GetScriptLineExtent(JSScript *script); namespace js { +extern uintN +CurrentLine(JSContext *cx); + /* * This function returns the file and line number of the script currently * executing on cx. If there is no current script executing on cx (e.g., a @@ -765,20 +925,11 @@ enum LineOption { NOT_CALLED_FROM_JSOP_EVAL }; -inline const char * -CurrentScriptFileAndLine(JSContext *cx, uintN *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL); +inline void +CurrentScriptFileLineOrigin(JSContext *cx, uintN *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL); } -static JS_INLINE JSOp -js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSOp op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - return op; -} - extern JSScript * js_CloneScript(JSContext *cx, JSScript *script); @@ -790,17 +941,4 @@ js_CloneScript(JSContext *cx, JSScript *script); extern JSBool js_XDRScript(JSXDRState *xdr, JSScript **scriptp); -inline bool -JSObject::isScript() const -{ - return getClass() == &js_ScriptClass; -} - -inline JSScript * -JSObject::getScript() const -{ - JS_ASSERT(isScript()); - return static_cast(getPrivate()); -} - #endif /* jsscript_h___ */ diff --git a/deps/mozjs/js/src/jsscriptinlines.h b/deps/mozjs/js/src/jsscriptinlines.h index 8af35211b36..005f326b79d 100644 --- a/deps/mozjs/js/src/jsscriptinlines.h +++ b/deps/mozjs/js/src/jsscriptinlines.h @@ -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=79 ft=cpp: * * ***** BEGIN LICENSE BLOCK ***** @@ -45,44 +45,39 @@ #include "jscntxt.h" #include "jsfun.h" #include "jsopcode.h" -#include "jsregexp.h" #include "jsscript.h" #include "jsscope.h" +#include "vm/ScopeObject.h" +#include "vm/GlobalObject.h" +#include "vm/RegExpObject.h" + +#include "jsscopeinlines.h" + namespace js { inline -Bindings::Bindings(JSContext *cx, EmptyShape *emptyCallShape) - : lastBinding(emptyCallShape), nargs(0), nvars(0), nupvars(0), - hasExtensibleParents(false) -{ -} +Bindings::Bindings(JSContext *cx) + : lastBinding(NULL), nargs(0), nvars(0), nupvars(0), hasDup_(false) +{} inline void Bindings::transfer(JSContext *cx, Bindings *bindings) { - JS_ASSERT(lastBinding == cx->compartment->emptyCallShape); + JS_ASSERT(!lastBinding); + JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary()); *this = *bindings; #ifdef DEBUG bindings->lastBinding = NULL; #endif - - /* Preserve back-pointer invariants across the lastBinding transfer. */ - if (lastBinding->inDictionary()) - lastBinding->listp = &this->lastBinding; } inline void Bindings::clone(JSContext *cx, Bindings *bindings) { - JS_ASSERT(lastBinding == cx->compartment->emptyCallShape); - - /* - * Non-dictionary bindings are fine to share, as are dictionary bindings if - * they're copy-on-modification. - */ - JS_ASSERT(!bindings->lastBinding->inDictionary() || bindings->lastBinding->frozen()); + JS_ASSERT(!lastBinding); + JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary()); *this = *bindings; } @@ -91,46 +86,95 @@ Shape * Bindings::lastShape() const { JS_ASSERT(lastBinding); - JS_ASSERT_IF(lastBinding->inDictionary(), lastBinding->frozen()); + JS_ASSERT(!lastBinding->inDictionary()); return lastBinding; } -extern const char * -CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop); +Shape * +Bindings::initialShape(JSContext *cx) const +{ + /* Get an allocation kind to match an empty call object. */ + gc::AllocKind kind = gc::FINALIZE_OBJECT4; + JS_ASSERT(gc::GetGCKindSlots(kind) == CallObject::RESERVED_SLOTS + 1); + + return EmptyShape::getInitialShape(cx, &CallClass, NULL, NULL, kind, + BaseShape::VAROBJ); +} + +bool +Bindings::ensureShape(JSContext *cx) +{ + if (!lastBinding) { + lastBinding = initialShape(cx); + if (!lastBinding) + return false; + } + return true; +} -inline const char * -CurrentScriptFileAndLine(JSContext *cx, uintN *linenop, LineOption opt) +bool +Bindings::extensibleParents() +{ + return lastBinding && lastBinding->extensibleParents(); +} + +extern void +CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, uintN *linenop, JSPrincipals **origin); + +inline void +CurrentScriptFileLineOrigin(JSContext *cx, const char **file, uintN *linenop, JSPrincipals **origin, + LineOption opt = NOT_CALLED_FROM_JSOP_EVAL) { if (opt == CALLED_FROM_JSOP_EVAL) { - JS_ASSERT(*cx->regs().pc == JSOP_EVAL); + JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL); JS_ASSERT(*(cx->regs().pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); + JSScript *script = cx->fp()->script(); + *file = script->filename; *linenop = GET_UINT16(cx->regs().pc + JSOP_EVAL_LENGTH); - return cx->fp()->script()->filename; + *origin = script->originPrincipals; + return; } - return CurrentScriptFileAndLineSlow(cx, linenop); + CurrentScriptFileLineOriginSlow(cx, file, linenop, origin); +} + +inline void +ScriptOpcodeCounts::destroy(JSContext *cx) +{ + if (counts) + cx->free_(counts); } } // namespace js +inline void +JSScript::setFunction(JSFunction *fun) +{ + function_ = fun; +} + inline JSFunction * JSScript::getFunction(size_t index) { JSObject *funobj = getObject(index); - JS_ASSERT(funobj->isFunction()); - JS_ASSERT(funobj == (JSObject *) funobj->getPrivate()); - JSFunction *fun = (JSFunction *) funobj; - JS_ASSERT(FUN_INTERPRETED(fun)); - return fun; + JS_ASSERT(funobj->isFunction() && funobj->toFunction()->isInterpreted()); + return funobj->toFunction(); +} + +inline JSFunction * +JSScript::getCallerFunction() +{ + JS_ASSERT(savedCallerFun); + return getFunction(0); } inline JSObject * JSScript::getRegExp(size_t index) { JSObjectArray *arr = regexps(); - JS_ASSERT((uint32) index < arr->length); + JS_ASSERT(uint32_t(index) < arr->length); JSObject *obj = arr->vector[index]; - JS_ASSERT(obj->getClass() == &js_RegExpClass); + JS_ASSERT(obj->isRegExp()); return obj; } @@ -146,4 +190,69 @@ JSScript::isEmpty() const return JSOp(*pc) == JSOP_STOP; } +inline bool +JSScript::hasGlobal() const +{ + /* + * Make sure that we don't try to query information about global objects + * which have had their scopes cleared. compileAndGo code should not run + * anymore against such globals. + */ + JS_ASSERT(types && types->hasScope()); + js::GlobalObject *obj = types->global; + return obj && !obj->isCleared(); +} + +inline js::GlobalObject * +JSScript::global() const +{ + JS_ASSERT(hasGlobal()); + return types->global; +} + +inline bool +JSScript::hasClearedGlobal() const +{ + JS_ASSERT(types && types->hasScope()); + js::GlobalObject *obj = types->global; + return obj && obj->isCleared(); +} + +inline js::types::TypeScriptNesting * +JSScript::nesting() const +{ + JS_ASSERT(function() && types && types->hasScope()); + return types->nesting; +} + +inline void +JSScript::clearNesting() +{ + js::types::TypeScriptNesting *nesting = this->nesting(); + if (nesting) { + js::Foreground::delete_(nesting); + types->nesting = NULL; + } +} + +inline void +JSScript::writeBarrierPre(JSScript *script) +{ +#ifdef JSGC_INCREMENTAL + if (!script) + return; + + JSCompartment *comp = script->compartment(); + if (comp->needsBarrier()) { + JS_ASSERT(!comp->rt->gcRunning); + MarkScriptUnbarriered(comp->barrierTracer(), script, "write barrier"); + } +#endif +} + +inline void +JSScript::writeBarrierPost(JSScript *script, void *addr) +{ +} + #endif /* jsscriptinlines_h___ */ diff --git a/deps/mozjs/js/src/jsstack.js b/deps/mozjs/js/src/jsstack.js deleted file mode 100644 index 16ab957cc2e..00000000000 --- a/deps/mozjs/js/src/jsstack.js +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Check that only JS_REQUIRES_STACK/JS_FORCES_STACK functions, and functions - * that have called a JS_FORCES_STACK function, access cx->fp directly or - * indirectly. - */ - -require({ after_gcc_pass: 'cfg' }); -include('gcc_util.js'); -include('unstable/adts.js'); -include('unstable/analysis.js'); -include('unstable/lazy_types.js'); -include('unstable/esp.js'); - -var Zero_NonZero = {}; -include('unstable/zero_nonzero.js', Zero_NonZero); - -// Tell MapFactory we don't need multimaps (a speed optimization). -MapFactory.use_injective = true; - -/* - * There are two regions in the program: RED and GREEN. Functions and member - * variables may be declared RED in the C++ source. GREEN is the default. - * - * RED signals danger. A GREEN part of a function must not call a RED function - * or access a RED member. - * - * The body of a RED function is all red. The body of a GREEN function is all - * GREEN by default, but parts dominated by a call to a TURN_RED function are - * red. This way GREEN functions can safely access RED stuff by calling a - * TURN_RED function as preparation. - * - * The analysis does not attempt to prove anything about the body of a TURN_RED - * function. (Both annotations are trusted; only unannotated code is checked - * for errors.) - */ -const RED = 'JS_REQUIRES_STACK'; -const TURN_RED = 'JS_FORCES_STACK'; -const IGNORE_ERRORS = 'JS_IGNORE_STACK'; - -function attrs(tree) { - let a = DECL_P(tree) ? DECL_ATTRIBUTES(tree) : TYPE_ATTRIBUTES(tree); - return translate_attributes(a); -} - -function hasUserAttribute(tree, attrname) { - let attributes = attrs(tree); - if (attributes) { - for (let i = 0; i < attributes.length; i++) { - let attr = attributes[i]; - if (attr.name == 'user' && attr.value.length == 1 && attr.value[0] == attrname) - return true; - } - } - return false; -} - -function process_tree_type(d) -{ - let t = dehydra_convert(d); - if (t.isFunction) - return; - - if (t.typedef !== undefined) - if (isRed(TYPE_NAME(d))) - warning("Typedef declaration is annotated JS_REQUIRES_STACK: the annotation should be on the type itself", t.loc); - - if (hasAttribute(t, RED)) { - warning("Non-function is annotated JS_REQUIRES_STACK", t.loc); - return; - } - - for (let st = t; st !== undefined && st.isPointer; st = st.type) { - if (hasAttribute(st, RED)) { - warning("Non-function is annotated JS_REQUIRES_STACK", t.loc); - return; - } - - if (st.parameters) - return; - } -} - -function process_tree_decl(d) -{ - // For VAR_DECLs, walk the DECL_INITIAL looking for bad assignments - if (TREE_CODE(d) != VAR_DECL) - return; - - let i = DECL_INITIAL(d); - if (!i) - return; - - assignCheck(i, TREE_TYPE(d), function() { return location_of(d); }); - - functionPointerWalk(i, d); -} - -/* - * x is an expression or decl. These functions assume that - */ -function isRed(x) { return hasUserAttribute(x, RED); } -function isTurnRed(x) { return hasUserAttribute(x, TURN_RED); } - -function process_tree(fndecl) -{ - if (hasUserAttribute(fndecl, IGNORE_ERRORS)) - return; - - if (!(isRed(fndecl) || isTurnRed(fndecl))) { - // Ordinarily a user of ESP runs the analysis, then generates output based - // on the results. But in our case (a) we need sub-basic-block resolution, - // which ESP doesn't keep; (b) it so happens that even though ESP can - // iterate over blocks multiple times, in our case that won't cause - // spurious output. (It could cause us to the same error message each time - // through--but that's easily avoided.) Therefore we generate the output - // while the ESP analysis is running. - let a = new RedGreenCheck(fndecl, 0); - if (a.hasRed) - a.run(); - } - - functionPointerCheck(fndecl); -} - -function RedGreenCheck(fndecl, trace) { - //print("RedGreenCheck: " + fndecl.toCString()); - this._fndecl = fndecl; - - // Tell ESP that fndecl is a "property variable". This makes ESP track it in - // a flow-sensitive way. The variable will be 1 in RED regions and "don't - // know" in GREEN regions. (We are technically lying to ESP about fndecl - // being a variable--what we really want is a synthetic variable indicating - // RED/GREEN state, but ESP operates on GCC decl nodes.) - this._state_var_decl = fndecl; - let state_var = new ESP.PropVarSpec(this._state_var_decl, true, undefined); - - // Call base class constructor. - let cfg = function_decl_cfg(fndecl); - ESP.Analysis.apply(this, [cfg, [state_var], Zero_NonZero.meet, trace]); - this.join = Zero_NonZero.join; - - // Preprocess all instructions in the cfg to determine whether this analysis - // is necessary and gather some information we'll use later. - // - // Each isn may include a function call, an assignment, and/or some reads. - // Using walk_tree to walk the isns is a little crazy but robust. - // - this.hasRed = false; - let self = this; // Allow our 'this' to be accessed inside closure - for (let bb in cfg_bb_iterator(cfg)) { - for (let isn in bb_isn_iterator(bb)) { - // Treehydra objects don't support reading never-defined properties - // as undefined, so we have to explicitly initialize anything we want - // to check for later. - isn.redInfo = undefined; - walk_tree(isn, function(t, stack) { - function getLocation(skiptop) { - if (!skiptop) { - let loc = location_of(t); - if (loc !== undefined) - return loc; - } - - for (let i = stack.length - 1; i >= 0; --i) { - let loc = location_of(stack[i]); - if (loc !== undefined) - return loc; - } - return location_of(fndecl); - } - - switch (TREE_CODE(t)) { - case FIELD_DECL: - if (isRed(t)) { - let varName = dehydra_convert(t).name; - // location_of(t) is the location of the declaration. - isn.redInfo = ["cannot access JS_REQUIRES_STACK variable " + varName, - getLocation(true)]; - self.hasRed = true; - } - break; - case GIMPLE_CALL: - { - let callee = gimple_call_fndecl(t); - if (callee) { - if (isRed(callee)) { - let calleeName = dehydra_convert(callee).name; - isn.redInfo = ["cannot call JS_REQUIRES_STACK function " + calleeName, - getLocation(false)]; - self.hasRed = true; - } else if (isTurnRed(callee)) { - isn.turnRed = true; - } - } - else { - let fntype = TREE_CHECK( - TREE_TYPE( // the function type - TREE_TYPE( // the function pointer type - gimple_call_fn(t) - ) - ), - FUNCTION_TYPE, METHOD_TYPE); - if (isRed(fntype)) { - isn.redInfo = ["cannot call JS_REQUIRES_STACK function pointer", - getLocation(false)]; - self.hasRed = true; - } - else if (isTurnRed(fntype)) { - isn.turnRed = true; - } - } - } - break; - } - }); - } - } - - // Initialize mixin for infeasible-path elimination. - this._zeroNonzero = new Zero_NonZero.Zero_NonZero(); -} - -RedGreenCheck.prototype = new ESP.Analysis; - -RedGreenCheck.prototype.flowStateCond = function(isn, truth, state) { - // forward event to mixin - this._zeroNonzero.flowStateCond(isn, truth, state); -}; - -RedGreenCheck.prototype.flowState = function(isn, state) { - // forward event to mixin - //try { // The try/catch here is a workaround for some baffling bug in zero_nonzero. - this._zeroNonzero.flowState(isn, state); - //} catch (exc) { - // warning(exc, location_of(isn)); - // warning("(Remove the workaround in jsstack.js and recompile to get a JS stack trace.)", - // location_of(isn)); - //} - let stackState = state.get(this._state_var_decl); - let green = stackState != 1 && stackState != ESP.NOT_REACHED; - let redInfo = isn.redInfo; - if (green && redInfo) { - warning(redInfo[0], redInfo[1]); - isn.redInfo = undefined; // avoid duplicate messages about this instruction - } - - // If we call a TURNS_RED function, it doesn't take effect until after the - // whole isn finishes executing (the most conservative rule). - if (isn.turnRed) - state.assignValue(this._state_var_decl, 1, isn); -}; - -function followTypedefs(type) -{ - while (type.typedef !== undefined) - type = type.typedef; - return type; -} - -function assignCheck(source, destType, locfunc) -{ - if (TREE_CODE(destType) != POINTER_TYPE) - return; - - let destCode = TREE_CODE(TREE_TYPE(destType)); - if (destCode != FUNCTION_TYPE && destCode != METHOD_TYPE) - return; - - if (isRed(TREE_TYPE(destType))) - return; - - while (TREE_CODE(source) == NOP_EXPR) - source = source.operands()[0]; - - // The destination is a green function pointer - - if (TREE_CODE(source) == ADDR_EXPR) { - let sourcefn = source.operands()[0]; - - // oddly enough, SpiderMonkey assigns the address of something that's not - // a function to a function pointer as part of the API! See JS_TN - if (TREE_CODE(sourcefn) != FUNCTION_DECL) - return; - - if (isRed(sourcefn)) - warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function " + dehydra_convert(sourcefn).name, locfunc()); - } else if (TREE_TYPE(source).tree_code() == POINTER_TYPE) { - let sourceType = TREE_TYPE(TREE_TYPE(source)); - switch (TREE_CODE(sourceType)) { - case FUNCTION_TYPE: - case METHOD_TYPE: - if (isRed(sourceType)) - warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function pointer", locfunc()); - break; - } - } -} - -/** - * A type checker which verifies that a red function pointer is never converted - * to a green function pointer. - */ - -function functionPointerWalk(t, baseloc) -{ - walk_tree(t, function(t, stack) { - function getLocation(skiptop) { - if (!skiptop) { - let loc = location_of(t); - if (loc !== undefined) - return loc; - } - - for (let i = stack.length - 1; i >= 0; --i) { - let loc = location_of(stack[i]); - if (loc !== undefined) - return loc; - } - return location_of(baseloc); - } - - switch (TREE_CODE(t)) { - case GIMPLE_ASSIGN: { - let [dest, source] = t.operands(); - assignCheck(source, TREE_TYPE(dest), getLocation); - break; - } - case CONSTRUCTOR: { - let ttype = TREE_TYPE(t); - switch (TREE_CODE(ttype)) { - case RECORD_TYPE: - case UNION_TYPE: { - for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t))) - assignCheck(ce.value, TREE_TYPE(ce.index), getLocation); - break; - } - case ARRAY_TYPE: { - let eltype = TREE_TYPE(ttype); - for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t))) - assignCheck(ce.value, eltype, getLocation); - break; - } - case LANG_TYPE: - // these can be safely ignored - break; - default: - warning("Unexpected type in initializer: " + TREE_CODE(TREE_TYPE(t)), getLocation()); - } - break; - } - case GIMPLE_CALL: { - // Check that the arguments to a function and the declared types - // of those arguments are compatible. - let ops = t.operands(); - let funcType = TREE_TYPE( // function type - TREE_TYPE(ops[1])); // function pointer type - let argTypes = [t for (t in function_type_args(funcType))]; - for (let i = argTypes.length - 1; i >= 0; --i) { - let destType = argTypes[i]; - let source = ops[i + 3]; - assignCheck(source, destType, getLocation); - } - break; - } - } - }); -} - -function functionPointerCheck(fndecl) -{ - let cfg = function_decl_cfg(fndecl); - for (let bb in cfg_bb_iterator(cfg)) - for (let isn in bb_isn_iterator(bb)) - functionPointerWalk(isn, fndecl); -} diff --git a/deps/mozjs/js/src/jsstaticcheck.h b/deps/mozjs/js/src/jsstaticcheck.h deleted file mode 100644 index c605d2f0848..00000000000 --- a/deps/mozjs/js/src/jsstaticcheck.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsstaticcheck_h___ -#define jsstaticcheck_h___ - -#ifdef NS_STATIC_CHECKING -/* - * Trigger a control flow check to make sure that code flows through label - */ -inline __attribute__ ((unused)) void MUST_FLOW_THROUGH(const char *label) { -} - -/* avoid unused goto-label warnings */ -#define MUST_FLOW_LABEL(label) goto label; label: - -inline JS_FORCES_STACK void VOUCH_DOES_NOT_REQUIRE_STACK() {} - -inline JS_FORCES_STACK void -JS_ASSERT_NOT_ON_TRACE(JSContext *cx) -{ - JS_ASSERT(!JS_ON_TRACE(cx)); -} - -#else -#define MUST_FLOW_THROUGH(label) ((void) 0) -#define MUST_FLOW_LABEL(label) -#define VOUCH_DOES_NOT_REQUIRE_STACK() ((void) 0) -#define JS_ASSERT_NOT_ON_TRACE(cx) JS_ASSERT(!JS_ON_TRACE(cx)) -#endif -#define VOUCH_HAVE_STACK VOUCH_DOES_NOT_REQUIRE_STACK - -/* sixgill annotation defines */ - -/* Avoid name collision if included with other headers defining annotations. */ -#ifndef HAVE_STATIC_ANNOTATIONS -#define HAVE_STATIC_ANNOTATIONS - -#ifdef XGILL_PLUGIN - -#define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND))) -#define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND))) -#define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND))) -#define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND))) -#define STATIC_INVARIANT(COND) __attribute__((invariant(#COND))) -#define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND))) - -/* Used to make identifiers for assert/assume annotations in a function. */ -#define STATIC_PASTE2(X,Y) X ## Y -#define STATIC_PASTE1(X,Y) STATIC_PASTE2(X,Y) - -#define STATIC_ASSERT(COND) \ - JS_BEGIN_MACRO \ - __attribute__((assert_static(#COND), unused)) \ - int STATIC_PASTE1(assert_static_, __COUNTER__); \ - JS_END_MACRO - -#define STATIC_ASSUME(COND) \ - JS_BEGIN_MACRO \ - __attribute__((assume_static(#COND), unused)) \ - int STATIC_PASTE1(assume_static_, __COUNTER__); \ - JS_END_MACRO - -#define STATIC_ASSERT_RUNTIME(COND) \ - JS_BEGIN_MACRO \ - __attribute__((assert_static_runtime(#COND), unused)) \ - int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \ - JS_END_MACRO - -#else /* XGILL_PLUGIN */ - -#define STATIC_PRECONDITION(COND) /* nothing */ -#define STATIC_PRECONDITION_ASSUME(COND) /* nothing */ -#define STATIC_POSTCONDITION(COND) /* nothing */ -#define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */ -#define STATIC_INVARIANT(COND) /* nothing */ -#define STATIC_INVARIANT_ASSUME(COND) /* nothing */ - -#define STATIC_ASSERT(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO -#define STATIC_ASSUME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO -#define STATIC_ASSERT_RUNTIME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO - -#endif /* XGILL_PLUGIN */ - -#define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference()) - -#endif /* HAVE_STATIC_ANNOTATIONS */ - -#endif /* jsstaticcheck_h___ */ diff --git a/deps/mozjs/js/src/jsstdint.h b/deps/mozjs/js/src/jsstdint.h deleted file mode 100644 index 446a87319ac..00000000000 --- a/deps/mozjs/js/src/jsstdint.h +++ /dev/null @@ -1,122 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Jim Blandy - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This header provides definitions for the types we use, - * even on systems that lack . - * - * NOTE: This header should only be included in private SpiderMonkey - * code; public headers should use only the JS{Int,Uint}N types; see - * the comment for them in "jsinttypes.h". - * - * At the moment, these types are not widely used within SpiderMonkey; - * this file is meant to make existing uses portable, and to allow us - * to transition portably to using them more, if desired. - */ - -#ifndef jsstdint_h___ -#define jsstdint_h___ - -#include "jsinttypes.h" - -/* If we have a working stdint.h, then jsinttypes.h has already - defined the standard integer types. Otherwise, define the standard - names in terms of the 'JS' types. */ -#if ! defined(JS_HAVE_STDINT_H) && \ - ! defined(JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES) - -typedef JSInt8 int8_t; -typedef JSInt16 int16_t; -typedef JSInt32 int32_t; -typedef JSInt64 int64_t; - -typedef JSUint8 uint8_t; -typedef JSUint16 uint16_t; -typedef JSUint32 uint32_t; -typedef JSUint64 uint64_t; - -/* Suppress other, conflicting attempts to define stdint-bits. */ -#define _STDINT_H - -/* If JS_STDDEF_H_HAS_INTPTR_T or JS_CRTDEFS_H_HAS_INTPTR_T are - defined, then jsinttypes.h included the given header, which - introduced definitions for intptr_t and uintptr_t. Otherwise, - define the standard names in terms of the 'JS' types. */ -#if !defined(JS_STDDEF_H_HAS_INTPTR_T) && !defined(JS_CRTDEFS_H_HAS_INTPTR_T) -typedef JSIntPtr intptr_t; -typedef JSUintPtr uintptr_t; -#endif - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) - -#define INT8_MAX 127 -#define INT8_MIN (-INT8_MAX - 1) -#define INT16_MAX 32767 -#define INT16_MIN (-INT16_MAX - 1) -#define INT32_MAX 2147483647 -#define INT32_MIN (-INT32_MAX - 1) -#define INT64_MAX 9223372036854775807LL -#define INT64_MIN (-INT64_MAX - 1) - -#define UINT8_MAX 255 -#define UINT16_MAX 65535 -#define UINT32_MAX 4294967295U -#define UINT64_MAX 18446744073709551615ULL - -/* - * These are technically wrong as they can't be used in the preprocessor, but - * we would require compiler assistance, and at the moment we don't need - * preprocessor-correctness. - */ -#ifdef _MSC_VER -#undef SIZE_MAX -#endif - -#define INTPTR_MAX ((intptr_t) (UINTPTR_MAX >> 1)) -#define INTPTR_MIN (intptr_t(uintptr_t(INTPTR_MAX) + uintptr_t(1))) -#define UINTPTR_MAX ((uintptr_t) -1) -#define SIZE_MAX UINTPTR_MAX -#define PTRDIFF_MAX INTPTR_MAX -#define PTRDIFF_MIN INTPTR_MIN - -#endif /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */ - -#endif /* JS_HAVE_STDINT_H */ - -#endif /* jsstdint_h___ */ diff --git a/deps/mozjs/js/src/jsstr.cpp b/deps/mozjs/js/src/jsstr.cpp index c73282ebccb..a877b259ecf 100644 --- a/deps/mozjs/js/src/jsstr.cpp +++ b/deps/mozjs/js/src/jsstr.cpp @@ -48,10 +48,12 @@ * of rooting things that might lose their newborn root due to subsequent GC * allocations in the same native method. */ + +#include "mozilla/Attributes.h" + #include #include #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jshash.h" #include "jsprf.h" @@ -59,399 +61,49 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" -#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */ #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsregexp.h" +#include "jsprobes.h" #include "jsscope.h" -#include "jsstaticcheck.h" #include "jsstr.h" -#include "jsbit.h" -#include "jsvector.h" #include "jsversion.h" -#include "jsinterpinlines.h" +#include "builtin/RegExp.h" +#include "vm/GlobalObject.h" +#include "vm/RegExpObject.h" + +#include "jsinferinlines.h" #include "jsobjinlines.h" -#include "jsregexpinlines.h" -#include "jsstrinlines.h" #include "jsautooplen.h" // generated headers last +#include "vm/RegExpObject-inl.h" +#include "vm/RegExpStatics-inl.h" #include "vm/StringObject-inl.h" +#include "vm/String-inl.h" using namespace js; using namespace js::gc; - -#ifdef DEBUG -bool -JSString::isShort() const -{ - bool is_short = arenaHeader()->getThingKind() == FINALIZE_SHORT_STRING; - JS_ASSERT_IF(is_short, isFlat()); - return is_short; -} - -bool -JSString::isFixed() const -{ - return isFlat() && !isExtensible(); -} -#endif - -bool -JSString::isExternal() const -{ - bool is_external = arenaHeader()->getThingKind() == FINALIZE_EXTERNAL_STRING; - JS_ASSERT_IF(is_external, isFixed()); - return is_external; -} - -static JS_ALWAYS_INLINE JSString * -Tag(JSRope *str) -{ - JS_ASSERT(!(size_t(str) & 1)); - return (JSString *)(size_t(str) | 1); -} - -static JS_ALWAYS_INLINE bool -Tagged(JSString *str) -{ - return (size_t(str) & 1) != 0; -} - -static JS_ALWAYS_INLINE JSRope * -Untag(JSString *str) -{ - JS_ASSERT((size_t(str) & 1) == 1); - return (JSRope *)(size_t(str) & ~size_t(1)); -} - -void -JSLinearString::mark(JSTracer *) -{ - JSLinearString *str = this; - while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent()) - str = str->asDependent().base(); -} - -void -JSString::mark(JSTracer *trc) -{ - if (isLinear()) { - asLinear().mark(trc); - return; - } - - /* - * This function must not fail, so a simple stack-based traversal must not - * be used (since it may oom if the stack grows large). Instead, strings - * are temporarily mutated to embed parent pointers as they are traversed. - * This algorithm is homomorphic to JSString::flatten. - */ - JSRope *str = &asRope(); - JSRope *parent = NULL; - first_visit_node: { - if (!str->markIfUnmarked()) - goto finish_node; - JS_ASSERT(!Tagged(str->d.u1.left) && !Tagged(str->d.s.u2.right)); - JSString *left = str->d.u1.left; - if (left->isRope()) { - str->d.u1.left = Tag(parent); - parent = str; - str = &left->asRope(); - goto first_visit_node; - } - left->asLinear().mark(trc); - } - visit_right_child: { - JSString *right = str->d.s.u2.right; - if (right->isRope()) { - str->d.s.u2.right = Tag(parent); - parent = str; - str = &right->asRope(); - goto first_visit_node; - } - right->asLinear().mark(trc); - } - finish_node: { - if (!parent) - return; - if (Tagged(parent->d.u1.left)) { - JS_ASSERT(!Tagged(parent->d.s.u2.right)); - JSRope *nextParent = Untag(parent->d.u1.left); - parent->d.u1.left = str; - str = parent; - parent = nextParent; - goto visit_right_child; - } - JSRope *nextParent = Untag(parent->d.s.u2.right); - parent->d.s.u2.right = str; - str = parent; - parent = nextParent; - goto finish_node; - } -} - -static JS_ALWAYS_INLINE size_t -RopeCapacityFor(size_t length) -{ - static const size_t ROPE_DOUBLING_MAX = 1024 * 1024; - - /* - * Grow by 12.5% if the buffer is very large. Otherwise, round up to the - * next power of 2. This is similar to what we do with arrays; see - * JSObject::ensureDenseArrayElements. - */ - if (length > ROPE_DOUBLING_MAX) - return length + (length / 8); - return RoundUpPow2(length); -} - -static JS_ALWAYS_INLINE jschar * -AllocChars(JSContext *maybecx, size_t wholeCapacity) -{ - /* +1 for the null char at the end. */ - JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX); - size_t bytes = (wholeCapacity + 1) * sizeof(jschar); - if (maybecx) - return (jschar *)maybecx->malloc_(bytes); - return (jschar *)OffTheBooks::malloc_(bytes); -} - -JSFlatString * -JSRope::flatten(JSContext *maybecx) -{ - /* - * Perform a depth-first dag traversal, splatting each node's characters - * into a contiguous buffer. Visit each rope node three times: - * 1. record position in the buffer and recurse into left child; - * 2. recurse into the right child; - * 3. transform the node into a dependent string. - * To avoid maintaining a stack, tree nodes are mutated to indicate how many - * times they have been visited. Since ropes can be dags, a node may be - * encountered multiple times during traversal. However, step 3 above leaves - * a valid dependent string, so everything works out. This algorithm is - * homomorphic to marking code. - * - * While ropes avoid all sorts of quadratic cases with string - * concatenation, they can't help when ropes are immediately flattened. - * One idiomatic case that we'd like to keep linear (and has traditionally - * been linear in SM and other JS engines) is: - * - * while (...) { - * s += ... - * s.flatten - * } - * - * To do this, when the buffer for a to-be-flattened rope is allocated, the - * allocation size is rounded up. Then, if the resulting flat string is the - * left-hand side of a new rope that gets flattened and there is enough - * capacity, the rope is flattened into the same buffer, thereby avoiding - * copying the left-hand side. Clearing the 'extensible' bit turns off this - * optimization. This is necessary, e.g., when the JSAPI hands out the raw - * null-terminated char array of a flat string. - * - * N.B. This optimization can create chains of dependent strings. - */ - const size_t wholeLength = length(); - size_t wholeCapacity; - jschar *wholeChars; - JSString *str = this; - jschar *pos; - - if (this->leftChild()->isExtensible()) { - JSExtensibleString &left = this->leftChild()->asExtensible(); - size_t capacity = left.capacity(); - if (capacity >= wholeLength) { - wholeCapacity = capacity; - wholeChars = const_cast(left.chars()); - size_t bits = left.d.lengthAndFlags; - pos = wholeChars + (bits >> LENGTH_SHIFT); - left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_BIT); - left.d.s.u2.base = (JSLinearString *)this; /* will be true on exit */ - goto visit_right_child; - } - } - - wholeCapacity = RopeCapacityFor(wholeLength); - wholeChars = AllocChars(maybecx, wholeCapacity); - if (!wholeChars) - return NULL; - - if (maybecx) - maybecx->runtime->stringMemoryUsed += wholeLength * 2; - - pos = wholeChars; - first_visit_node: { - JSString &left = *str->d.u1.left; - str->d.u1.chars = pos; - if (left.isRope()) { - left.d.s.u3.parent = str; /* Return to this when 'left' done, */ - left.d.lengthAndFlags = 0x200; /* but goto visit_right_child. */ - str = &left; - goto first_visit_node; - } - size_t len = left.length(); - PodCopy(pos, left.d.u1.chars, len); - pos += len; - } - visit_right_child: { - JSString &right = *str->d.s.u2.right; - if (right.isRope()) { - right.d.s.u3.parent = str; /* Return to this node when 'right' done, */ - right.d.lengthAndFlags = 0x300; /* but goto finish_node. */ - str = &right; - goto first_visit_node; - } - size_t len = right.length(); - PodCopy(pos, right.d.u1.chars, len); - pos += len; - } - finish_node: { - if (str == this) { - JS_ASSERT(pos == wholeChars + wholeLength); - *pos = '\0'; - str->d.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS); - str->d.u1.chars = wholeChars; - str->d.s.u2.capacity = wholeCapacity; - return &this->asFlat(); - } - size_t progress = str->d.lengthAndFlags; - str->d.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_BIT); - str->d.s.u2.base = (JSLinearString *)this; /* will be true on exit */ - str = str->d.s.u3.parent; - if (progress == 0x200) - goto visit_right_child; - JS_ASSERT(progress == 0x300); - goto finish_node; - } -} - -JSString * JS_FASTCALL -js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) -{ - JS_ASSERT_IF(!left->isAtom(), left->compartment() == cx->compartment); - JS_ASSERT_IF(!right->isAtom(), right->compartment() == cx->compartment); - - size_t leftLen = left->length(); - if (leftLen == 0) - return right; - - size_t rightLen = right->length(); - if (rightLen == 0) - return left; - - size_t wholeLength = leftLen + rightLen; - - if (JSShortString::lengthFits(wholeLength)) { - JSShortString *str = js_NewGCShortString(cx); - if (!str) - return NULL; - const jschar *leftChars = left->getChars(cx); - if (!leftChars) - return NULL; - const jschar *rightChars = right->getChars(cx); - if (!rightChars) - return NULL; - - jschar *buf = str->init(wholeLength); - PodCopy(buf, leftChars, leftLen); - PodCopy(buf + leftLen, rightChars, rightLen); - buf[wholeLength] = 0; - return str; - } - - if (wholeLength > JSString::MAX_LENGTH) { - if (JS_ON_TRACE(cx)) { - if (!CanLeaveTrace(cx)) - return NULL; - LeaveTrace(cx); - } - js_ReportAllocationOverflow(cx); - return NULL; - } - - return JSRope::new_(cx, left, right, wholeLength); -} - -JSFixedString * -JSDependentString::undepend(JSContext *cx) -{ - JS_ASSERT(isDependent()); - - size_t n = length(); - size_t size = (n + 1) * sizeof(jschar); - jschar *s = (jschar *) cx->malloc_(size); - if (!s) - return NULL; - - cx->runtime->stringMemoryUsed += size; - - PodCopy(s, chars(), n); - s[n] = 0; - - d.lengthAndFlags = buildLengthAndFlags(n, FIXED_FLAGS); - d.u1.chars = s; - -#ifdef DEBUG - JSRuntime *rt = cx->runtime; - JS_RUNTIME_UNMETER(rt, liveDependentStrings); - JS_RUNTIME_UNMETER(rt, totalDependentStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum -= (double)n, - rt->strdepLengthSquaredSum -= (double)n * (double)n)); -#endif - - return &this->asFixed(); -} - -JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; - -#ifdef JS_TRACER - -JSBool JS_FASTCALL -js_FlattenOnTrace(JSContext *cx, JSString* str) -{ - return !!str->ensureLinear(cx); -} -JS_DEFINE_CALLINFO_2(extern, BOOL, js_FlattenOnTrace, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY) - -#endif /* !JS_TRACER */ +using namespace js::types; +using namespace js::unicode; static JSLinearString * -ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) +ArgToRootedString(JSContext *cx, CallArgs &args, uintN argno) { - if (arg >= argc) + if (argno >= args.length()) return cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; - vp += 2 + arg; - if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp)) + Value &arg = args[argno]; + JSString *str = ToString(cx, arg); + if (!str) return NULL; - JSLinearString *str; - if (vp->isString()) { - str = vp->toString()->ensureLinear(cx); - } else if (vp->isBoolean()) { - str = cx->runtime->atomState.booleanAtoms[(int)vp->toBoolean()]; - } else if (vp->isNull()) { - str = cx->runtime->atomState.nullAtom; - } else if (vp->isUndefined()) { - str = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; - } - else { - str = NumberToString(cx, vp->toNumber()); - if (!str) - return NULL; - vp->setString(str); - } - return str; + arg = StringValue(str); + return str->ensureLinear(cx); } /* @@ -469,94 +121,64 @@ str_encodeURI(JSContext *cx, uintN argc, Value *vp); static JSBool str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp); -static const uint32 OVERLONG_UTF8 = UINT32_MAX; +static const uint32_t INVALID_UTF8 = UINT32_MAX; -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); - -/* - * Contributions from the String class to the set of methods defined for the - * global object. escape and unescape used to be defined in the Mocha library, - * but as ECMA decided to spec them, they've been moved to the core engine - * and made ECMA-compliant. (Incomplete escapes are interpreted as literal - * characters by unescape.) - */ +static uint32_t +Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length); /* - * Stuff to emulate the old libmocha escape, which took a second argument - * giving the type of escape to perform. Retained for compatibility, and - * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. + * Global string methods */ -#define URL_XALPHAS ((uint8) 1) -#define URL_XPALPHAS ((uint8) 2) -#define URL_PATH ((uint8) 4) -static const uint8 urlCharType[256] = -/* Bit 0 xalpha -- the alphas - * Bit 1 xpalpha -- as xalpha but - * converts spaces to plus and plus to %20 - * Bit 2 ... path -- as xalphas but doesn't escape '/' - */ - /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ - 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ - 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ - 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ - 0, }; - -/* This matches the ECMA escape set when mask is 7 (default.) */ - -#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) - -/* See ECMA-262 Edition 3 B.2.1 */ -JSBool -js_str_escape(JSContext *cx, uintN argc, Value *vp, Value *rval) +/* ES5 B.2.1 */ +static JSBool +str_escape(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - jsint mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; - if (argc > 1) { - double d; - if (!ValueToNumber(cx, vp[3], &d)) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(d) || - (mask = (jsint)d) != d || - mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) - { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_STRING_MASK, numBuf); - return JS_FALSE; - } - } - - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) - return JS_FALSE; + return false; size_t length = str->length(); const jschar *chars = str->chars(); + static const uint8_t shouldPassThrough[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1, /* !"#$%&'()*+,-./ */ + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 0123456789:;<=>? */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* @ABCDEFGHIJKLMNO */ + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* PQRSTUVWXYZ[\]^_ */ + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* `abcdefghijklmno */ + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* pqrstuvwxyz{\}~ DEL */ + }; + + /* In step 7, exactly 69 characters should pass through unencoded. */ +#ifdef DEBUG + size_t count = 0; + for (size_t i = 0; i < sizeof(shouldPassThrough); i++) { + if (shouldPassThrough[i]) { + count++; + } + } + JS_ASSERT(count == 69); +#endif + + /* Take a first pass and see how big the result string will need to be. */ size_t newlength = length; for (size_t i = 0; i < length; i++) { - jschar ch; - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) + jschar ch = chars[i]; + if (ch < 128 && shouldPassThrough[ch]) continue; - if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') - continue; /* The character will be encoded as '+' */ - newlength += 2; /* The character will be encoded as %XX */ - } else { - newlength += 5; /* The character will be encoded as %uXXXX */ - } + + /* The character will be encoded as %XX or %uXXXX. */ + newlength += (ch < 256) ? 2 : 5; /* * This overflow test works because newlength is incremented by at @@ -564,31 +186,27 @@ js_str_escape(JSContext *cx, uintN argc, Value *vp, Value *rval) */ if (newlength < length) { js_ReportAllocationOverflow(cx); - return JS_FALSE; + return false; } } if (newlength >= ~(size_t)0 / sizeof(jschar)) { js_ReportAllocationOverflow(cx); - return JS_FALSE; + return false; } jschar *newchars = (jschar *) cx->malloc_((newlength + 1) * sizeof(jschar)); if (!newchars) - return JS_FALSE; + return false; size_t i, ni; for (i = 0, ni = 0; i < length; i++) { - jschar ch; - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { + jschar ch = chars[i]; + if (ch < 128 && shouldPassThrough[ch]) { newchars[ni++] = ch; } else if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') { - newchars[ni++] = '+'; /* convert spaces to pluses */ - } else { - newchars[ni++] = '%'; - newchars[ni++] = digits[ch >> 4]; - newchars[ni++] = digits[ch & 0xF]; - } + newchars[ni++] = '%'; + newchars[ni++] = digits[ch >> 4]; + newchars[ni++] = digits[ch & 0xF]; } else { newchars[ni++] = '%'; newchars[ni++] = 'u'; @@ -604,78 +222,148 @@ js_str_escape(JSContext *cx, uintN argc, Value *vp, Value *rval) JSString *retstr = js_NewString(cx, newchars, newlength); if (!retstr) { cx->free_(newchars); - return JS_FALSE; + return false; } - rval->setString(retstr); - return JS_TRUE; + + args.rval() = StringValue(retstr); + return true; } -#undef IS_OK -static JSBool -str_escape(JSContext *cx, uintN argc, Value *vp) +static inline bool +Unhex4(const jschar *chars, jschar *result) +{ + jschar a = chars[0], + b = chars[1], + c = chars[2], + d = chars[3]; + + if (!(JS7_ISHEX(a) && JS7_ISHEX(b) && JS7_ISHEX(c) && JS7_ISHEX(d))) + return false; + + *result = (((((JS7_UNHEX(a) << 4) + JS7_UNHEX(b)) << 4) + JS7_UNHEX(c)) << 4) + JS7_UNHEX(d); + return true; +} + +static inline bool +Unhex2(const jschar *chars, jschar *result) { - return js_str_escape(cx, argc, vp, vp); + jschar a = chars[0], + b = chars[1]; + + if (!(JS7_ISHEX(a) && JS7_ISHEX(b))) + return false; + + *result = (JS7_UNHEX(a) << 4) + JS7_UNHEX(b); + return true; } -/* See ECMA-262 Edition 3 B.2.2 */ +/* ES5 B.2.2 */ static JSBool str_unescape(JSContext *cx, uintN argc, Value *vp) { - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) return false; + /* Step 2. */ size_t length = str->length(); const jschar *chars = str->chars(); - /* Don't bother allocating less space for the new string. */ - jschar *newchars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar)); - if (!newchars) - return false; - size_t ni = 0, i = 0; - while (i < length) { - jschar ch = chars[i++]; - if (ch == '%') { - if (i + 1 < length && - JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) - { - ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); - i += 2; - } else if (i + 4 < length && chars[i] == 'u' && - JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && - JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) - { - ch = (((((JS7_UNHEX(chars[i + 1]) << 4) - + JS7_UNHEX(chars[i + 2])) << 4) - + JS7_UNHEX(chars[i + 3])) << 4) - + JS7_UNHEX(chars[i + 4]); - i += 5; + /* Step 3. */ + StringBuffer sb(cx); + + /* + * Note that the spec algorithm has been optimized to avoid building + * a string in the case where no escapes are present. + */ + + /* Step 4. */ + size_t k = 0; + bool building = false; + + while (true) { + /* Step 5. */ + if (k == length) { + JSLinearString *result; + if (building) { + result = sb.finishString(); + if (!result) + return false; + } else { + result = str; } + + args.rval() = StringValue(result); + return true; } - newchars[ni++] = ch; - } - newchars[ni] = 0; - JSString *retstr = js_NewString(cx, newchars, ni); - if (!retstr) { - cx->free_(newchars); - return JS_FALSE; + /* Step 6. */ + jschar c = chars[k]; + + /* Step 7. */ + if (c != '%') + goto step_18; + + /* Step 8. */ + if (k > length - 6) + goto step_14; + + /* Step 9. */ + if (chars[k + 1] != 'u') + goto step_14; + +#define ENSURE_BUILDING \ + JS_BEGIN_MACRO \ + if (!building) { \ + building = true; \ + if (!sb.reserve(length)) \ + return false; \ + sb.infallibleAppend(chars, chars + k); \ + } \ + JS_END_MACRO + + /* Step 10-13. */ + if (Unhex4(&chars[k + 2], &c)) { + ENSURE_BUILDING; + k += 5; + goto step_18; + } + + step_14: + /* Step 14. */ + if (k > length - 3) + goto step_18; + + /* Step 15-17. */ + if (Unhex2(&chars[k + 1], &c)) { + ENSURE_BUILDING; + k += 2; + } + + step_18: + if (building) + sb.infallibleAppend(c); + + /* Step 19. */ + k += 1; } - vp->setString(retstr); - return JS_TRUE; +#undef ENSURE_BUILDING } #if JS_HAS_UNEVAL static JSBool str_uneval(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = js_ValueToSource(cx, argc != 0 ? vp[2] : UndefinedValue()); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = js_ValueToSource(cx, args.length() != 0 ? args[0] : UndefinedValue()); if (!str) - return JS_FALSE; - vp->setString(str); - return JS_TRUE; + return false; + + args.rval() = StringValue(str); + return true; } #endif @@ -706,7 +394,7 @@ static JSFunctionSpec string_functions[] = { jschar js_empty_ucstr[] = {0}; JSSubString js_EmptySubString = {0, js_empty_ucstr}; -#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) +static const uintN STRING_ELEMENT_ATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; static JSBool str_enumerate(JSContext *cx, JSObject *obj) @@ -716,9 +404,9 @@ str_enumerate(JSContext *cx, JSObject *obj) JSString *str1 = js_NewDependentString(cx, str, i, 1); if (!str1) return false; - if (!obj->defineProperty(cx, INT_TO_JSID(i), StringValue(str1), - PropertyStub, StrictPropertyStub, - STRING_ELEMENT_ATTRS)) { + if (!obj->defineElement(cx, i, StringValue(str1), + JS_PropertyStub, JS_StrictPropertyStub, + STRING_ELEMENT_ATTRS)) { return false; } } @@ -737,11 +425,11 @@ str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, jsint slot = JSID_TO_INT(id); if ((size_t)slot < str->length()) { - JSString *str1 = JSAtom::getUnitStringForElement(cx, str, size_t(slot)); + JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot)); if (!str1) return JS_FALSE; - if (!obj->defineProperty(cx, id, StringValue(str1), NULL, NULL, - STRING_ELEMENT_ATTRS)) { + if (!obj->defineElement(cx, uint32_t(slot), StringValue(str1), NULL, NULL, + STRING_ELEMENT_ATTRS)) { return JS_FALSE; } *objp = obj; @@ -749,52 +437,55 @@ str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, return JS_TRUE; } -Class js_StringClass = { +Class js::StringClass = { js_String_str, JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ str_enumerate, (JSResolveOp)str_resolve, - ConvertStub + JS_ConvertStub }; /* - * Returns a JSString * for the |this| value associated with vp, or throws a - * TypeError if |this| is null or undefined. This algorithm is the same as + * Returns a JSString * for the |this| value associated with 'call', or throws + * a TypeError if |this| is null or undefined. This algorithm is the same as * calling CheckObjectCoercible(this), then returning ToString(this), as all - * String.prototype.* methods do. + * String.prototype.* methods do (other than toString and valueOf). */ static JS_ALWAYS_INLINE JSString * -ThisToStringForStringProto(JSContext *cx, Value *vp) +ThisToStringForStringProto(JSContext *cx, CallReceiver call) { - if (vp[1].isString()) - return vp[1].toString(); + JS_CHECK_RECURSION(cx, return NULL); + + if (call.thisv().isString()) + return call.thisv().toString(); - if (vp[1].isObject()) { - JSObject *obj = &vp[1].toObject(); - if (obj->getClass() == &js_StringClass && + if (call.thisv().isObject()) { + JSObject *obj = &call.thisv().toObject(); + if (obj->isString() && ClassMethodIsNative(cx, obj, - &js_StringClass, + &StringClass, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { - vp[1] = obj->getPrimitiveThis(); - return vp[1].toString(); + call.thisv() = obj->getPrimitiveThis(); + return call.thisv().toString(); } - } else if (vp[1].isNullOrUndefined()) { + } else if (call.thisv().isNullOrUndefined()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, - vp[1].isNull() ? "null" : "undefined", "object"); + call.thisv().isNull() ? "null" : "undefined", "object"); return NULL; } - JSString *str = js_ValueToString(cx, vp[1]); + JSString *str = ToStringSlow(cx, call.thisv()); if (!str) return NULL; - vp[1].setString(str); + + call.thisv().setString(str); return str; } @@ -807,56 +498,39 @@ ThisToStringForStringProto(JSContext *cx, Value *vp) static JSBool str_quote(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; str = js_QuoteString(cx, str, '"'); if (!str) return false; - vp->setString(str); + args.rval() = StringValue(str); return true; } static JSBool str_toSource(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str; - if (!GetPrimitiveThis(cx, vp, &str)) - return false; + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, str_toSource, &str, &ok)) + return ok; str = js_QuoteString(cx, str, '"'); if (!str) return false; - char buf[16]; - size_t j = JS_snprintf(buf, sizeof buf, "(new String("); - - JS::Anchor anchor(str); - size_t k = str->length(); - const jschar *s = str->getChars(cx); - if (!s) - return false; - - size_t n = j + k + 2; - jschar *t = (jschar *) cx->malloc_((n + 1) * sizeof(jschar)); - if (!t) + StringBuffer sb(cx); + if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))")) return false; - size_t i; - for (i = 0; i < j; i++) - t[i] = buf[i]; - for (j = 0; j < k; i++, j++) - t[i] = s[j]; - t[i++] = ')'; - t[i++] = ')'; - t[i] = 0; - - str = js_NewString(cx, t, n); - if (!str) { - cx->free_(t); + str = sb.finishString(); + if (!str) return false; - } - vp->setString(str); + args.rval() = StringValue(str); return true; } @@ -865,19 +539,23 @@ str_toSource(JSContext *cx, uintN argc, Value *vp) JSBool js_str_toString(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str; - if (!GetPrimitiveThis(cx, vp, &str)) - return false; - vp->setString(str); + bool ok; + if (!BoxedPrimitiveMethodGuard(cx, args, js_str_toString, &str, &ok)) + return ok; + + args.rval() = StringValue(str); return true; } /* * Java-like string native methods. */ - + JS_ALWAYS_INLINE bool -ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out) +ValueToIntegerRange(JSContext *cx, const Value &v, int32_t *out) { if (v.isInt32()) { *out = v.toInt32(); @@ -889,8 +567,8 @@ ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out) *out = INT32_MAX; else if (d < INT32_MIN) *out = INT32_MIN; - else - *out = int32(d); + else + *out = int32_t(d); } return true; @@ -899,15 +577,17 @@ ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out) static JSBool str_substring(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - int32 length, begin, end; - if (argc > 0) { - end = length = int32(str->length()); + int32_t length, begin, end; + if (args.length() > 0) { + end = length = int32_t(str->length()); - if (!ValueToIntegerRange(cx, vp[2], &begin)) + if (!ValueToIntegerRange(cx, args[0], &begin)) return false; if (begin < 0) @@ -915,8 +595,8 @@ str_substring(JSContext *cx, uintN argc, Value *vp) else if (begin > length) begin = length; - if (argc > 1 && !vp[3].isUndefined()) { - if (!ValueToIntegerRange(cx, vp[3], &end)) + if (args.length() > 1 && !args[1].isUndefined()) { + if (!ValueToIntegerRange(cx, args[1], &end)) return false; if (end > length) { @@ -937,7 +617,7 @@ str_substring(JSContext *cx, uintN argc, Value *vp) return false; } - vp->setString(str); + args.rval() = StringValue(str); return true; } @@ -953,7 +633,7 @@ js_toLowerCase(JSContext *cx, JSString *str) if (!news) return NULL; for (size_t i = 0; i < n; i++) - news[i] = JS_TOLOWER(s[i]); + news[i] = unicode::ToLowerCase(s[i]); news[n] = 0; str = js_NewString(cx, news, n); if (!str) { @@ -963,34 +643,50 @@ js_toLowerCase(JSContext *cx, JSString *str) return str; } -static JSBool -str_toLowerCase(JSContext *cx, uintN argc, Value *vp) +static inline bool +ToLowerCaseHelper(JSContext *cx, CallReceiver call) { - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, call); if (!str) return false; + str = js_toLowerCase(cx, str); if (!str) return false; - vp->setString(str); + + call.rval() = StringValue(str); return true; } +static JSBool +str_toLowerCase(JSContext *cx, uintN argc, Value *vp) +{ + return ToLowerCaseHelper(cx, CallArgsFromVp(argc, vp)); +} + static JSBool str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* * Forcefully ignore the first (or any) argument and return toLowerCase(), * ECMA has reserved that argument, presumably for defining the locale. */ if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - return cx->localeCallbacks->localeToLowerCase(cx, str, Jsvalify(vp)); + + Value result; + if (!cx->localeCallbacks->localeToLowerCase(cx, str, &result)) + return false; + + args.rval() = result; + return true; } - return str_toLowerCase(cx, 0, vp); + return ToLowerCaseHelper(cx, args); } JSString* JS_FASTCALL @@ -1004,7 +700,7 @@ js_toUpperCase(JSContext *cx, JSString *str) if (!news) return NULL; for (size_t i = 0; i < n; i++) - news[i] = JS_TOUPPER(s[i]); + news[i] = unicode::ToUpperCase(s[i]); news[n] = 0; str = js_NewString(cx, news, n); if (!str) { @@ -1015,56 +711,82 @@ js_toUpperCase(JSContext *cx, JSString *str) } static JSBool -str_toUpperCase(JSContext *cx, uintN argc, Value *vp) +ToUpperCaseHelper(JSContext *cx, CallReceiver call) { - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, call); if (!str) return false; + str = js_toUpperCase(cx, str); if (!str) return false; - vp->setString(str); + + call.rval() = StringValue(str); return true; } +static JSBool +str_toUpperCase(JSContext *cx, uintN argc, Value *vp) +{ + return ToUpperCaseHelper(cx, CallArgsFromVp(argc, vp)); +} + static JSBool str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* * Forcefully ignore the first (or any) argument and return toUpperCase(), * ECMA has reserved that argument, presumably for defining the locale. */ if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - return cx->localeCallbacks->localeToUpperCase(cx, str, Jsvalify(vp)); + + Value result; + if (!cx->localeCallbacks->localeToUpperCase(cx, str, &result)) + return false; + + args.rval() = result; + return true; } - return str_toUpperCase(cx, 0, vp); + return ToUpperCaseHelper(cx, args); } static JSBool str_localeCompare(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - if (argc == 0) { - vp->setInt32(0); + if (args.length() == 0) { + args.rval() = Int32Value(0); } else { - JSString *thatStr = js_ValueToString(cx, vp[2]); + JSString *thatStr = ToString(cx, args[0]); if (!thatStr) return false; + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { - vp[2].setString(thatStr); - return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp)); + args[0].setString(thatStr); + + Value result; + if (!cx->localeCallbacks->localeCompare(cx, str, thatStr, &result)) + return true; + + args.rval() = result; + return true; } - int32 result; + + int32_t result; if (!CompareStrings(cx, str, thatStr, &result)) return false; - vp->setInt32(result); + + args.rval() = Int32Value(result); } return true; } @@ -1072,60 +794,64 @@ str_localeCompare(JSContext *cx, uintN argc, Value *vp) JSBool js_str_charAt(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str; - jsint i; - if (vp[1].isString() && argc != 0 && vp[2].isInt32()) { - str = vp[1].toString(); - i = vp[2].toInt32(); - if ((size_t)i >= str->length()) + size_t i; + if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) { + str = args.thisv().toString(); + i = size_t(args[0].toInt32()); + if (i >= str->length()) goto out_of_range; } else { - str = ThisToStringForStringProto(cx, vp); + str = ThisToStringForStringProto(cx, args); if (!str) return false; double d = 0.0; - if (argc > 0 && !ToInteger(cx, vp[2], &d)) + if (args.length() > 0 && !ToInteger(cx, args[0], &d)) return false; if (d < 0 || str->length() <= d) goto out_of_range; - i = (jsint) d; + i = size_t(d); } - str = JSAtom::getUnitStringForElement(cx, str, size_t(i)); + str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, i); if (!str) return false; - vp->setString(str); + args.rval() = StringValue(str); return true; out_of_range: - vp->setString(cx->runtime->emptyString); + args.rval() = StringValue(cx->runtime->emptyString); return true; } JSBool js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str; - jsint i; - if (vp[1].isString() && argc != 0 && vp[2].isInt32()) { - str = vp[1].toString(); - i = vp[2].toInt32(); - if ((size_t)i >= str->length()) + size_t i; + if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) { + str = args.thisv().toString(); + i = size_t(args[0].toInt32()); + if (i >= str->length()) goto out_of_range; } else { - str = ThisToStringForStringProto(cx, vp); + str = ThisToStringForStringProto(cx, args); if (!str) return false; double d = 0.0; - if (argc > 0 && !ToInteger(cx, vp[2], &d)) + if (args.length() > 0 && !ToInteger(cx, args[0], &d)) return false; if (d < 0 || str->length() <= d) goto out_of_range; - i = (jsint) d; + i = size_t(d); } const jschar *chars; @@ -1133,11 +859,11 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) if (!chars) return false; - vp->setInt32(chars[i]); + args.rval() = Int32Value(chars[i]); return true; out_of_range: - vp->setDouble(js_NaN); + args.rval() = DoubleValue(js_NaN); return true; } @@ -1148,24 +874,24 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) * Return the index of pat in text, or -1 if not found. */ static const jsuint sBMHCharSetSize = 256; /* ISO-Latin-1 */ -static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8 */ +static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8_t */ static const jsint sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */ jsint js_BoyerMooreHorspool(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen) { - uint8 skip[sBMHCharSetSize]; + uint8_t skip[sBMHCharSetSize]; JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax); for (jsuint i = 0; i < sBMHCharSetSize; i++) - skip[i] = (uint8)patlen; + skip[i] = (uint8_t)patlen; jsuint m = patlen - 1; for (jsuint i = 0; i < m; i++) { jschar c = pat[i]; if (c >= sBMHCharSetSize) return sBMHBadPattern; - skip[c] = (uint8)(m - i); + skip[c] = (uint8_t)(m - i); } jschar c; for (jsuint k = m; @@ -1214,7 +940,7 @@ UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patl const jschar p0 = *pat; const jschar *const patNext = pat + 1; const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen); - uint8 fixup; + uint8_t fixup; const jschar *t = text; switch ((textend - t) & 7) { @@ -1364,26 +1090,19 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js /* Absolute offset from the beginning of the logical string textstr. */ jsint pos = 0; - // TODO: consider branching to a simple loop if patlen == 1 - for (JSLinearString **outerp = strs.begin(); outerp != strs.end(); ++outerp) { - /* First try to match without spanning two nodes. */ + /* Try to find a match within 'outer'. */ JSLinearString *outer = *outerp; const jschar *chars = outer->chars(); size_t len = outer->length(); jsint matchResult = StringMatch(chars, len, pat, patlen); if (matchResult != -1) { + /* Matched! */ *match = pos + matchResult; return true; } - /* Test the overlap. */ - JSLinearString **innerp = outerp; - - /* - * Start searching at the first place where StringMatch wouldn't have - * found the match. - */ + /* Try to find a match starting in 'outer' and running into other nodes. */ const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1); const jschar *const textend = chars + len; const jschar p0 = *pat; @@ -1392,6 +1111,7 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js for (const jschar *t = text; t != textend; ) { if (*t++ != p0) continue; + JSLinearString **innerp = outerp; const jschar *ttend = textend; for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) { while (tt == ttend) { @@ -1424,11 +1144,12 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js static JSBool str_indexOf(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *patstr = ArgToRootedString(cx, args, 0); if (!patstr) return false; @@ -1441,9 +1162,9 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) const jschar *pat = patstr->chars(); jsuint start; - if (argc > 1) { - if (vp[3].isInt32()) { - jsint i = vp[3].toInt32(); + if (args.length() > 1) { + if (args[1].isInt32()) { + jsint i = args[1].toInt32(); if (i <= 0) { start = 0; } else if (jsuint(i) > textlen) { @@ -1456,7 +1177,7 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) } } else { jsdouble d; - if (!ToInteger(cx, vp[3], &d)) + if (!ToInteger(cx, args[1], &d)) return false; if (d <= 0) { start = 0; @@ -1474,22 +1195,24 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) } jsint match = StringMatch(text, textlen, pat, patlen); - vp->setInt32((match == -1) ? -1 : start + match); + args.rval() = Int32Value((match == -1) ? -1 : start + match); return true; } static JSBool str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) { - JSString *textstr = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *textstr = ThisToStringForStringProto(cx, args); if (!textstr) return false; + size_t textlen = textstr->length(); const jschar *text = textstr->getChars(cx); if (!text) return false; - JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *patstr = ArgToRootedString(cx, args, 0); if (!patstr) return false; @@ -1498,20 +1221,20 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) jsint i = textlen - patlen; // Start searching here if (i < 0) { - vp->setInt32(-1); + args.rval() = Int32Value(-1); return true; } - if (argc > 1) { - if (vp[3].isInt32()) { - jsint j = vp[3].toInt32(); + if (args.length() > 1) { + if (args[1].isInt32()) { + jsint j = args[1].toInt32(); if (j <= 0) i = 0; else if (j < i) i = j; } else { double d; - if (!ValueToNumber(cx, vp[3], &d)) + if (!ToNumber(cx, args[1], &d)) return false; if (!JSDOUBLE_IS_NaN(d)) { d = js_DoubleToInteger(d); @@ -1524,7 +1247,7 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) } if (patlen == 0) { - vp->setInt32(i); + args.rval() = Int32Value(i); return true; } @@ -1541,20 +1264,21 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) if (*t1 != *p1) goto break_continue; } - vp->setInt32(t - text); + args.rval() = Int32Value(t - text); return true; } break_continue:; } - vp->setInt32(-1); + args.rval() = Int32Value(-1); return true; } static JSBool js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallReceiver call = CallReceiverFromVp(vp); + JSString *str = ThisToStringForStringProto(cx, call); if (!str) return false; size_t length = str->length(); @@ -1566,12 +1290,12 @@ js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) size_t end = length; if (trimLeft) { - while (begin < length && JS_ISSPACE(chars[begin])) + while (begin < length && unicode::IsSpace(chars[begin])) ++begin; } if (trimRight) { - while (end > begin && JS_ISSPACE(chars[end-1])) + while (end > begin && unicode::IsSpace(chars[end - 1])) --end; } @@ -1579,7 +1303,7 @@ js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) if (!str) return false; - vp->setString(str); + call.rval() = StringValue(str); return true; } @@ -1608,69 +1332,62 @@ str_trimRight(JSContext *cx, uintN argc, Value *vp) /* Result of a successfully performed flat match. */ class FlatMatch { - JSLinearString *patstr; - const jschar *pat; - size_t patlen; - int32 match_; + JSAtom *patstr; + const jschar *pat; + size_t patlen; + int32_t match_; friend class RegExpGuard; public: FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */ - JSString *pattern() const { return patstr; } + JSLinearString *pattern() const { return patstr; } size_t patternLength() const { return patlen; } /* * Note: The match is -1 when the match is performed successfully, * but no match is found. */ - int32 match() const { return match_; } + int32_t match() const { return match_; } }; -/* A regexp and optional associated object. */ -class RegExpPair +static inline bool +IsRegExpMetaChar(jschar c) { - AutoRefCount re_; - JSObject *reobj_; - - explicit RegExpPair(RegExpPair &); - - public: - explicit RegExpPair(JSContext *cx) : re_(cx) {} - - void reset(JSObject &obj) { - reobj_ = &obj; - RegExp *re = RegExp::extractFrom(reobj_); - JS_ASSERT(re); - re_.reset(NeedsIncRef(re)); + switch (c) { + /* Taken from the PatternCharacter production in 15.10.1. */ + case '^': case '$': case '\\': case '.': case '*': case '+': + case '?': case '(': case ')': case '[': case ']': case '{': + case '}': case '|': + return true; + default: + return false; } +} - void reset(AlreadyIncRefed re) { - reobj_ = NULL; - re_.reset(re); +static inline bool +HasRegExpMetaChars(const jschar *chars, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + if (IsRegExpMetaChar(chars[i])) + return true; } - - /* Note: May be null. */ - JSObject *reobj() const { return reobj_; } - bool hasRegExp() const { return !re_.null(); } - RegExp &re() const { JS_ASSERT(hasRegExp()); return *re_; } -}; + return false; +} /* * RegExpGuard factors logic out of String regexp operations. * - * @param optarg Indicates in which argument position RegExp - * flags will be found, if present. This is a Mozilla - * extension and not part of any ECMA spec. + * |optarg| indicates in which argument position RegExp flags will be found, if + * present. This is a Mozilla extension and not part of any ECMA spec. */ class RegExpGuard { - RegExpGuard(const RegExpGuard &); - void operator=(const RegExpGuard &); + RegExpGuard(const RegExpGuard &) MOZ_DELETE; + void operator=(const RegExpGuard &) MOZ_DELETE; - JSContext *cx; - RegExpPair rep; - FlatMatch fm; + RegExpShared::Guard re_; + FlatMatch fm; /* * Upper bound on the number of characters we are willing to potentially @@ -1678,7 +1395,9 @@ class RegExpGuard */ static const size_t MAX_FLAT_PAT_LEN = 256; - static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) { + static JSAtom * + flattenPattern(JSContext *cx, JSAtom *patstr) + { StringBuffer sb(cx); if (!sb.reserve(patstr->length())) return NULL; @@ -1687,7 +1406,7 @@ class RegExpGuard const jschar *chars = patstr->chars(); size_t len = patstr->length(); for (const jschar *it = chars; it != chars + len; ++it) { - if (RegExp::isMetaChar(*it)) { + if (IsRegExpMetaChar(*it)) { if (!sb.append(ESCAPE_CHAR) || !sb.append(*it)) return NULL; } else { @@ -1695,21 +1414,31 @@ class RegExpGuard return NULL; } } - return sb.finishString(); + return sb.finishAtom(); } public: - explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx) {} - ~RegExpGuard() {} + RegExpGuard() {} /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */ - bool - init(uintN argc, Value *vp) + bool init(JSContext *cx, CallArgs args, bool convertVoid = false) { - if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) { - rep.reset(vp[2].toObject()); + if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) { + RegExpShared *shared = RegExpToShared(cx, args[0].toObject()); + if (!shared) + return false; + re_.init(*shared); } else { - fm.patstr = ArgToRootedString(cx, argc, vp, 0); + if (convertVoid && (args.length() == 0 || args[0].isUndefined())) { + fm.patstr = cx->runtime->emptyString; + return true; + } + + JSString *arg = ArgToRootedString(cx, args, 0); + if (!arg) + return false; + + fm.patstr = js_AtomizeString(cx, arg); if (!fm.patstr) return false; } @@ -1720,8 +1449,9 @@ class RegExpGuard * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the * pattern string, or a lengthy pattern string can thwart this process. * - * @param checkMetaChars Look for regexp metachars in the pattern string. - * @return Whether flat matching could be used. + * |checkMetaChars| looks for regexp metachars in the pattern string. + * + * Return whether flat matching could be used. * * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending(). */ @@ -1729,7 +1459,7 @@ class RegExpGuard tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc, bool checkMetaChars = true) { - if (rep.hasRegExp()) + if (re_.initialized()) return NULL; fm.pat = fm.patstr->chars(); @@ -1739,7 +1469,7 @@ class RegExpGuard return NULL; if (checkMetaChars && - (fm.patlen > MAX_FLAT_PAT_LEN || RegExp::hasMetaChars(fm.pat, fm.patlen))) { + (fm.patlen > MAX_FLAT_PAT_LEN || HasRegExpMetaChars(fm.pat, fm.patlen))) { return NULL; } @@ -1759,49 +1489,47 @@ class RegExpGuard } /* If the pattern is not already a regular expression, make it so. */ - const RegExpPair * - normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp) + bool normalizeRegExp(JSContext *cx, bool flat, uintN optarg, CallArgs args) { - if (rep.hasRegExp()) - return &rep; + if (re_.initialized()) + return true; /* Build RegExp from pattern string. */ JSString *opt; - if (optarg < argc) { - opt = js_ValueToString(cx, vp[2 + optarg]); + if (optarg < args.length()) { + opt = ToString(cx, args[optarg]); if (!opt) - return NULL; + return false; } else { opt = NULL; } - JSString *patstr; + JSAtom *patstr; if (flat) { patstr = flattenPattern(cx, fm.patstr); if (!patstr) - return NULL; + return false; } else { patstr = fm.patstr; } JS_ASSERT(patstr); - AlreadyIncRefed re = RegExp::createFlagged(cx, patstr, opt); + RegExpShared *re = cx->compartment->regExps.get(cx, patstr, opt); if (!re) - return NULL; - rep.reset(re); - return &rep; + return false; + + re_.init(*re); + return true; } -#if DEBUG - bool hasRegExpPair() const { return rep.hasRegExp(); } -#endif + RegExpShared ®Exp() { return *re_; } }; -/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */ +/* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */ static JS_ALWAYS_INLINE bool -Matched(bool test, const Value &v) +Matched(RegExpExecType type, const Value &v) { - return test ? v.isTrue() : !v.isNull(); + return (type == RegExpTest) ? v.isTrue() : !v.isNull(); } typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data); @@ -1822,19 +1550,22 @@ enum MatchControlFlags { /* Factor out looping and matching logic. */ static bool -DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep, - DoMatchCallback callback, void *data, MatchControlFlags flags) +DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re, + DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval) { - RegExp &re = rep.re(); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); + if (re.global()) { - /* global matching ('g') */ - bool testGlobal = flags & TEST_GLOBAL_BIT; - if (rep.reobj()) - rep.reobj()->zeroRegExpLastIndex(); + RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec; for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) { - if (!re.execute(cx, res, str, &i, testGlobal, vp)) + if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval)) return false; - if (!Matched(testGlobal, *vp)) + if (!Matched(type, *rval)) break; if (!callback(cx, res, count, data)) return false; @@ -1842,23 +1573,22 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx ++i; } } else { - /* single match */ - bool testSingle = !!(flags & TEST_SINGLE_BIT), - callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT); + RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec; + bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT); size_t i = 0; - if (!re.execute(cx, res, str, &i, testSingle, vp)) + if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval)) return false; - if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data)) + if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data)) return false; } return true; } static bool -BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp) +BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, CallArgs *args) { if (fm.match() < 0) { - vp->setNull(); + args->rval() = NullValue(); return true; } @@ -1866,13 +1596,16 @@ BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value JSObject *obj = NewSlowEmptyArray(cx); if (!obj) return false; - vp->setObject(*obj); - return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) && - obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom), - Int32Value(fm.match())) && - obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom), - StringValue(textstr)); + if (!obj->defineElement(cx, 0, StringValue(fm.pattern())) || + !obj->defineProperty(cx, cx->runtime->atomState.indexAtom, Int32Value(fm.match())) || + !obj->defineProperty(cx, cx->runtime->atomState.inputAtom, StringValue(textstr))) + { + return false; + } + + args->rval() = ObjectValue(*obj); + return true; } typedef JSObject **MatchArgType; @@ -1894,80 +1627,92 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) } Value v; - if (!res->createLastMatch(cx, &v)) - return false; - - JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); - return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false); + return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v); } -static JSBool -str_match(JSContext *cx, uintN argc, Value *vp) +JSBool +js::str_match(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - RegExpGuard g(cx); - if (!g.init(argc, vp)) + RegExpGuard g; + if (!g.init(cx, args, true)) return false; - if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) - return BuildFlatMatchArray(cx, str, *fm, vp); - if (cx->isExceptionPending()) /* from tryFlatMatch */ + + if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) + return BuildFlatMatchArray(cx, str, *fm, &args); + + /* Return if there was an error in tryFlatMatch. */ + if (cx->isExceptionPending()) return false; - const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp); - if (!rep) + if (!g.normalizeRegExp(cx, false, 1, args)) return false; - AutoObjectRooter array(cx); - MatchArgType arg = array.addr(); + JSObject *array = NULL; + MatchArgType arg = &array; RegExpStatics *res = cx->regExpStatics(); - if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS)) + Value rval; + if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval)) return false; - /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */ - if (rep->re().global()) - vp->setObjectOrNull(array.object()); + if (g.regExp().global()) + args.rval() = ObjectOrNullValue(array); + else + args.rval() = rval; return true; } -static JSBool -str_search(JSContext *cx, uintN argc, Value *vp) +JSBool +js::str_search(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - RegExpGuard g(cx); - if (!g.init(argc, vp)) + RegExpGuard g; + if (!g.init(cx, args, true)) return false; - if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) { - vp->setInt32(fm->match()); + if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) { + args.rval() = Int32Value(fm->match()); return true; } + if (cx->isExceptionPending()) /* from tryFlatMatch */ return false; - const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp); - if (!rep) + + if (!g.normalizeRegExp(cx, false, 1, args)) return false; + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); RegExpStatics *res = cx->regExpStatics(); + + /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */ size_t i = 0; - if (!rep->re().execute(cx, res, str, &i, true, vp)) + Value result; + if (!ExecuteRegExp(cx, res, g.regExp(), linearStr, chars, length, &i, RegExpTest, &result)) return false; - if (vp->isTrue()) - vp->setInt32(res->matchStart()); + if (result.isTrue()) + args.rval() = Int32Value(res->matchStart()); else - vp->setInt32(-1); + args.rval() = Int32Value(-1); return true; } struct ReplaceData { ReplaceData(JSContext *cx) - : g(cx), sb(cx) + : sb(cx) {} JSString *str; /* 'this' parameter object as a string */ @@ -1980,8 +1725,7 @@ struct ReplaceData jsint leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ bool calledBack; /* record whether callback has been called */ - InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */ - InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */ + InvokeArgsGuard args; /* arguments for lambda call */ StringBuffer sb; /* buffer built during DoMatch */ }; @@ -2018,7 +1762,7 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jscha JS_ASSERT(num <= res->parenCount()); - /* + /* * Note: we index to get the paren with the (1-indexed) pair * number, as opposed to a (0-indexed) paren number. */ @@ -2073,7 +1817,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t if (str->isAtom()) { atom = &str->asAtom(); } else { - atom = js_AtomizeString(cx, str, 0); + atom = js_AtomizeString(cx, str); if (!atom) return false; } @@ -2081,14 +1825,14 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t JSObject *holder; JSProperty *prop = NULL; - if (js_LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop) < 0) + if (!LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop)) return false; /* Only handle the case where the property exists and is on this object. */ if (prop && holder == base) { Shape *shape = (Shape *) prop; - if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) { - Value value = base->getSlot(shape->slot); + if (shape->hasSlot() && shape->hasDefaultGetter()) { + Value value = base->getSlot(shape->slot()); if (value.isString()) { rdata.repstr = value.toString()->ensureLinear(cx); if (!rdata.repstr) @@ -2108,6 +1852,10 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t JSObject *lambda = rdata.lambda; if (lambda) { + PreserveRegExpStatics staticsGuard(res); + if (!staticsGuard.init(cx)) + return false; + /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within @@ -2119,36 +1867,32 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t uintN p = res->parenCount(); uintN argc = 1 + p + 2; - InvokeSessionGuard &session = rdata.session; - if (!session.started()) { - Value lambdav = ObjectValue(*lambda); - if (!session.start(cx, lambdav, UndefinedValue(), argc)) - return false; - } - - PreserveRegExpStatics staticsGuard(res); - if (!staticsGuard.init(cx)) + InvokeArgsGuard &args = rdata.args; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) return false; + args.setCallee(ObjectValue(*lambda)); + args.thisv() = UndefinedValue(); + /* Push $&, $1, $2, ... */ uintN argi = 0; - if (!res->createLastMatch(cx, &session[argi++])) + if (!res->createLastMatch(cx, &args[argi++])) return false; for (size_t i = 0; i < res->parenCount(); ++i) { - if (!res->createParen(cx, i + 1, &session[argi++])) + if (!res->createParen(cx, i + 1, &args[argi++])) return false; } /* Push match index and input string. */ - session[argi++].setInt32(res->matchStart()); - session[argi].setString(rdata.str); + args[argi++].setInt32(res->matchStart()); + args[argi].setString(rdata.str); - if (!session.invoke(cx)) + if (!Invoke(cx, args)) return false; /* root repstr: rdata is on the stack, so scanned by conservative gc. */ - JSString *repstr = ValueToString_TestForStringInline(cx, session.rval()); + JSString *repstr = ToString(cx, args.rval()); if (!repstr) return false; rdata.repstr = repstr->ensureLinear(cx); @@ -2175,7 +1919,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t return true; } -/* +/* * Precondition: |rdata.sb| already has necessary growth space reserved (as * derived from FindReplaceLength). */ @@ -2205,7 +1949,7 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata) dp++; } } - JS_ALWAYS_TRUE(rdata.sb.append(cp, repstr->length() - (cp - bp))); + rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp)); } static bool @@ -2234,7 +1978,7 @@ ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) static bool BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, - const FlatMatch &fm, Value *vp) + const FlatMatch &fm, CallArgs *args) { RopeBuilder builder(cx); size_t match = fm.match(); @@ -2305,7 +2049,7 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, } } - vp->setString(builder.result()); + args->rval() = StringValue(builder.result()); return true; } @@ -2317,7 +2061,7 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, */ static inline bool BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr, - const jschar *firstDollar, const FlatMatch &fm, Value *vp) + const jschar *firstDollar, const FlatMatch &fm, CallArgs *args) { JSLinearString *textstr = textstrArg->ensureLinear(cx); if (!textstr) @@ -2389,27 +2133,29 @@ BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *reps builder.append(rightSide)); #undef ENSURE - vp->setString(builder.result()); + args->rval() = StringValue(builder.result()); return true; } static inline bool -str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) +str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata) { - const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp); - if (!rep) + if (!rdata.g.normalizeRegExp(cx, true, 2, args)) return false; rdata.leftIndex = 0; rdata.calledBack = false; RegExpStatics *res = cx->regExpStatics(); - if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS)) + RegExpShared &re = rdata.g.regExp(); + + Value tmp; + if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp)) return false; if (!rdata.calledBack) { /* Didn't match, so the string is unmodified. */ - vp->setString(rdata.str); + args.rval() = StringValue(rdata.str); return true; } @@ -2422,39 +2168,37 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) if (!retstr) return false; - vp->setString(retstr); + args.rval() = StringValue(retstr); return true; } static inline bool -str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata, - const FlatMatch &fm) +str_replace_flat_lambda(JSContext *cx, CallArgs outerArgs, ReplaceData &rdata, const FlatMatch &fm) { JS_ASSERT(fm.match() >= 0); - LeaveTrace(cx); JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength()); if (!matchStr) return false; /* lambda(matchStr, matchStart, textstr) */ - static const uint32 lambdaArgc = 3; - if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot)) + static const uint32_t lambdaArgc = 3; + if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.args)) return false; - CallArgs &args = rdata.singleShot; + CallArgs &args = rdata.args; args.calleev().setObject(*rdata.lambda); args.thisv().setUndefined(); - Value *sp = args.argv(); + Value *sp = args.array(); sp[0].setString(matchStr); sp[1].setInt32(fm.match()); sp[2].setString(rdata.str); - if (!Invoke(cx, rdata.singleShot)) + if (!Invoke(cx, rdata.args)) return false; - JSString *repstr = js_ValueToString(cx, args.rval()); + JSString *repstr = ToString(cx, args.rval()); if (!repstr) return false; @@ -2475,28 +2219,34 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata return false; } - vp->setString(builder.result()); + outerArgs.rval() = StringValue(builder.result()); return true; } +static const uint32_t ReplaceOptArg = 2; + JSBool js::str_replace(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + ReplaceData rdata(cx); - rdata.str = ThisToStringForStringProto(cx, vp); + rdata.str = ThisToStringForStringProto(cx, args); if (!rdata.str) return false; - static const uint32 optarg = 2; + + if (!rdata.g.init(cx, args)) + return false; /* Extract replacement string/function. */ - if (argc >= optarg && js_IsCallable(vp[3])) { - rdata.lambda = &vp[3].toObject(); + if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) { + rdata.lambda = &args[1].toObject(); rdata.elembase = NULL; rdata.repstr = NULL; rdata.dollar = rdata.dollarEnd = NULL; if (rdata.lambda->isFunction()) { - JSFunction *fun = rdata.lambda->getFunctionPrivate(); + JSFunction *fun = rdata.lambda->toFunction(); if (fun->isInterpreted()) { /* * Pattern match the script to check if it is is indexing into a @@ -2506,19 +2256,19 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) * encode large scripts. We only handle the code patterns generated * by such packers here. */ - JSScript *script = fun->u.i.script; + JSScript *script = fun->script(); jsbytecode *pc = script->code; Value table = UndefinedValue(); if (JSOp(*pc) == JSOP_GETFCSLOT) { - table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc)); + table = fun->getFlatClosureUpvar(GET_UINT16(pc)); pc += JSOP_GETFCSLOT_LENGTH; } if (table.isObject() && JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 && - JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM && - JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) { + JSOp(pc[JSOP_GETARG_LENGTH]) == JSOP_GETELEM && + JSOp(pc[JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH]) == JSOP_RETURN) { Class *clasp = table.toObject().getClass(); if (clasp->isNative() && !clasp->ops.lookupProperty && @@ -2531,7 +2281,7 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) } else { rdata.lambda = NULL; rdata.elembase = NULL; - rdata.repstr = ArgToRootedString(cx, argc, vp, 1); + rdata.repstr = ArgToRootedString(cx, args, 1); if (!rdata.repstr) return false; @@ -2543,9 +2293,6 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) rdata.dollar = js_strchr_limit(fixed->chars(), '$', rdata.dollarEnd); } - if (!rdata.g.init(argc, vp)) - return false; - /* * Unlike its |String.prototype| brethren, |replace| doesn't convert * its input to a regular expression. (Even if it contains metachars.) @@ -2556,30 +2303,29 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) * |RegExp| statics. */ - const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false); + const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, args.length(), false); if (!fm) { if (cx->isExceptionPending()) /* oom in RopeMatch in tryFlatMatch */ return false; - JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg); - return str_replace_regexp(cx, argc, vp, rdata); + return str_replace_regexp(cx, args, rdata); } if (fm->match() < 0) { - vp->setString(rdata.str); + args.rval() = StringValue(rdata.str); return true; } if (rdata.lambda) - return str_replace_flat_lambda(cx, argc, vp, rdata, *fm); + return str_replace_flat_lambda(cx, args, rdata, *fm); - /* + /* * Note: we could optimize the text.length == pattern.length case if we wanted, * even in the presence of dollar metachars. */ if (rdata.dollar) - return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp); + return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, &args); - return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp); + return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, &args); } class SplitMatchResult { @@ -2610,7 +2356,7 @@ class SplitMatchResult { template static JSObject * -SplitHelper(JSContext *cx, JSLinearString *str, uint32 limit, Matcher splitMatch) +SplitHelper(JSContext *cx, JSLinearString *str, uint32_t limit, Matcher splitMatch, TypeObject *type) { size_t strLength = str->length(); SplitMatchResult result; @@ -2709,6 +2455,8 @@ SplitHelper(JSContext *cx, JSLinearString *str, uint32 limit, Matcher splitMatch if (!sub || !splits.append(StringValue(sub))) return NULL; } else { + /* Only string entries have been accounted for so far. */ + AddTypeProperty(cx, type, NULL, UndefinedValue()); if (!splits.append(UndefinedValue())) return NULL; } @@ -2734,28 +2482,27 @@ SplitHelper(JSContext *cx, JSLinearString *str, uint32 limit, Matcher splitMatch /* * The SplitMatch operation from ES5 15.5.4.14 is implemented using different - * matchers for regular expression and string separators. + * paths for regular expression and string separators. * - * The algorithm differs from the spec in that the matchers return the next - * index at which a match happens. + * The algorithm differs from the spec in that the we return the next index at + * which a match happens. */ -class SplitRegExpMatcher { +class SplitRegExpMatcher +{ + RegExpShared &re; RegExpStatics *res; - RegExp *re; public: + SplitRegExpMatcher(RegExpShared &re, RegExpStatics *res) : re(re), res(res) {} + static const bool returnsCaptures = true; - SplitRegExpMatcher(RegExp *re, RegExpStatics *res) : res(res), re(re) { - } - inline bool operator()(JSContext *cx, JSLinearString *str, size_t index, - SplitMatchResult *result) { - Value rval -#ifdef __GNUC__ /* quell GCC overwarning */ - = UndefinedValue() -#endif - ; - if (!re->execute(cx, res, str, &index, true, &rval)) + bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *result) + { + Value rval = UndefinedValue(); + const jschar *chars = str->chars(); + size_t length = str->length(); + if (!ExecuteRegExp(cx, res, re, str, chars, length, &index, RegExpTest, &rval)) return false; if (!rval.isTrue()) { result->setFailure(); @@ -2769,19 +2516,21 @@ class SplitRegExpMatcher { } }; -class SplitStringMatcher { +class SplitStringMatcher +{ const jschar *sepChars; size_t sepLength; public: - static const bool returnsCaptures = false; SplitStringMatcher(JSLinearString *sep) { sepChars = sep->chars(); sepLength = sep->length(); } - inline bool operator()(JSContext *cx, JSLinearString *str, size_t index, - SplitMatchResult *res) { + static const bool returnsCaptures = false; + + bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res) + { JS_ASSERT(index == 0 || index < str->length()); const jschar *chars = str->chars(); jsint match = StringMatch(chars + index, str->length() - index, sepChars, sepLength); @@ -2794,19 +2543,26 @@ class SplitStringMatcher { }; /* ES5 15.5.4.14 */ -static JSBool -str_split(JSContext *cx, uintN argc, Value *vp) +JSBool +js::str_split(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* Steps 1-2. */ - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; + TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array); + if (!type) + return false; + AddTypeProperty(cx, type, NULL, Type::StringType()); + /* Step 5: Use the second argument as the split limit, if given. */ - uint32 limit; - if (argc > 1 && !vp[3].isUndefined()) { + uint32_t limit; + if (args.length() > 1 && !args[1].isUndefined()) { jsdouble d; - if (!ValueToNumber(cx, vp[3], &d)) + if (!ToNumber(cx, args[1], &d)) return false; limit = js_DoubleToECMAUint32(d); } else { @@ -2814,19 +2570,17 @@ str_split(JSContext *cx, uintN argc, Value *vp) } /* Step 8. */ - RegExp *re = NULL; + RegExpShared::Guard re; JSLinearString *sepstr = NULL; - bool sepUndefined = (argc == 0 || vp[2].isUndefined()); + bool sepUndefined = (args.length() == 0 || args[0].isUndefined()); if (!sepUndefined) { - if (VALUE_IS_REGEXP(cx, vp[2])) { - re = static_cast(vp[2].toObject().getPrivate()); - } else { - JSString *sep = js_ValueToString(cx, vp[2]); - if (!sep) + if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) { + RegExpShared *shared = RegExpToShared(cx, args[0].toObject()); + if (!shared) return false; - vp[2].setString(sep); - - sepstr = sep->ensureLinear(cx); + re.init(*shared); + } else { + sepstr = ArgToRootedString(cx, args, 0); if (!sepstr) return false; } @@ -2837,7 +2591,8 @@ str_split(JSContext *cx, uintN argc, Value *vp) JSObject *aobj = NewDenseEmptyArray(cx); if (!aobj) return false; - vp->setObject(*aobj); + aobj->setType(type); + args.rval() = ObjectValue(*aobj); return true; } @@ -2847,7 +2602,8 @@ str_split(JSContext *cx, uintN argc, Value *vp) JSObject *aobj = NewDenseCopiedArray(cx, 1, &v); if (!aobj) return false; - vp->setObject(*aobj); + aobj->setType(type); + args.rval() = ObjectValue(*aobj); return true; } JSLinearString *strlin = str->ensureLinear(cx); @@ -2856,17 +2612,16 @@ str_split(JSContext *cx, uintN argc, Value *vp) /* Steps 11-15. */ JSObject *aobj; - if (re) { - aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(re, cx->regExpStatics())); - } else { - // NB: sepstr is anchored through its storage in vp[2]. - aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr)); - } + if (!re.initialized()) + aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type); + else + aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(*re, cx->regExpStatics()), type); if (!aobj) return false; /* Step 16. */ - vp->setObject(*aobj); + aobj->setType(type); + args.rval() = ObjectValue(*aobj); return true; } @@ -2874,14 +2629,15 @@ str_split(JSContext *cx, uintN argc, Value *vp) static JSBool str_substr(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - int32 length, len, begin; - if (argc > 0) { - length = int32(str->length()); - if (!ValueToIntegerRange(cx, vp[2], &begin)) + int32_t length, len, begin; + if (args.length() > 0) { + length = int32_t(str->length()); + if (!ValueToIntegerRange(cx, args[0], &begin)) return false; if (begin >= length) { @@ -2889,15 +2645,15 @@ str_substr(JSContext *cx, uintN argc, Value *vp) goto out; } if (begin < 0) { - begin += length; /* length + INT_MIN will always be less then 0 */ + begin += length; /* length + INT_MIN will always be less than 0 */ if (begin < 0) begin = 0; } - if (argc == 1 || vp[3].isUndefined()) { + if (args.length() == 1 || args[1].isUndefined()) { len = length - begin; } else { - if (!ValueToIntegerRange(cx, vp[3], &len)) + if (!ValueToIntegerRange(cx, args[1], &len)) return false; if (len <= 0) { @@ -2905,7 +2661,7 @@ str_substr(JSContext *cx, uintN argc, Value *vp) goto out; } - if (uint32(length) < uint32(begin + len)) + if (uint32_t(length) < uint32_t(begin + len)) len = length - begin; } @@ -2915,7 +2671,7 @@ str_substr(JSContext *cx, uintN argc, Value *vp) } out: - vp->setString(str); + args.rval() = StringValue(str); return true; } #endif /* JS_HAS_PERL_SUBSTR */ @@ -2926,38 +2682,35 @@ str_substr(JSContext *cx, uintN argc, Value *vp) static JSBool str_concat(JSContext *cx, uintN argc, Value *vp) { - JSString *str = ThisToStringForStringProto(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - /* Set vp (aka rval) early to handle the argc == 0 case. */ - vp->setString(str); - - Value *argv; - uintN i; - for (i = 0, argv = vp + 2; i < argc; i++) { - JSString *str2 = js_ValueToString(cx, argv[i]); - if (!str2) + for (uintN i = 0; i < args.length(); i++) { + JSString *argStr = ToString(cx, args[i]); + if (!argStr) return false; - argv[i].setString(str2); - str = js_ConcatStrings(cx, str, str2); + str = js_ConcatStrings(cx, str, argStr); if (!str) return false; - vp->setString(str); } + args.rval() = StringValue(str); return true; } static JSBool str_slice(JSContext *cx, uintN argc, Value *vp) { - if (argc == 1 && vp[1].isString() && vp[2].isInt32()) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() == 1 && args.thisv().isString() && args[0].isInt32()) { size_t begin, end, length; - JSString *str = vp[1].toString(); - begin = vp[2].toInt32(); + JSString *str = args.thisv().toString(); + begin = args[0].toInt32(); end = str->length(); if (begin <= end) { length = end - begin; @@ -2965,24 +2718,24 @@ str_slice(JSContext *cx, uintN argc, Value *vp) str = cx->runtime->emptyString; } else { str = (length == 1) - ? JSAtom::getUnitStringForElement(cx, str, begin) + ? cx->runtime->staticStrings.getUnitStringForElement(cx, str, begin) : js_NewDependentString(cx, str, begin, length); if (!str) - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + args.rval() = StringValue(str); + return true; } } - JSString *str = ThisToStringForStringProto(cx, vp); + JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - if (argc != 0) { + if (args.length() != 0) { double begin, end, length; - if (!ToInteger(cx, vp[2], &begin)) + if (!ToInteger(cx, args[0], &begin)) return false; length = str->length(); if (begin < 0) { @@ -2993,10 +2746,10 @@ str_slice(JSContext *cx, uintN argc, Value *vp) begin = length; } - if (argc == 1 || vp[3].isUndefined()) { + if (args.length() == 1 || args[1].isUndefined()) { end = length; } else { - if (!ToInteger(cx, vp[3], &end)) + if (!ToInteger(cx, args[1], &end)) return false; if (end < 0) { end += length; @@ -3013,10 +2766,10 @@ str_slice(JSContext *cx, uintN argc, Value *vp) (size_t)begin, (size_t)(end - begin)); if (!str) - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + args.rval() = StringValue(str); + return true; } #if JS_HAS_STR_HTML_HELPERS @@ -3025,11 +2778,12 @@ str_slice(JSContext *cx, uintN argc, Value *vp) */ static bool tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end, - Value *vp) + CallReceiver call) { - JSString *thisstr = ThisToStringForStringProto(cx, vp); + JSString *thisstr = ThisToStringForStringProto(cx, call); if (!thisstr) return false; + JSLinearString *str = thisstr->ensureLinear(cx); if (!str) return false; @@ -3084,113 +2838,99 @@ tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end, Foreground::free_((char *)tagbuf); return false; } - vp->setString(retstr); + call.rval() = StringValue(retstr); return true; } static JSBool -tagify_value(JSContext *cx, uintN argc, Value *vp, - const char *begin, const char *end) +tagify_value(JSContext *cx, CallArgs args, const char *begin, const char *end) { - JSLinearString *param = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *param = ArgToRootedString(cx, args, 0); if (!param) - return JS_FALSE; - return tagify(cx, begin, param, end, vp); + return false; + + return tagify(cx, begin, param, end, args); } static JSBool str_bold(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "b", NULL, NULL, vp); + return tagify(cx, "b", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_italics(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "i", NULL, NULL, vp); + return tagify(cx, "i", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_fixed(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "tt", NULL, NULL, vp); + return tagify(cx, "tt", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_fontsize(JSContext *cx, uintN argc, Value *vp) { - return tagify_value(cx, argc, vp, "font size", "font"); + return tagify_value(cx, CallArgsFromVp(argc, vp), "font size", "font"); } static JSBool str_fontcolor(JSContext *cx, uintN argc, Value *vp) { - return tagify_value(cx, argc, vp, "font color", "font"); + return tagify_value(cx, CallArgsFromVp(argc, vp), "font color", "font"); } static JSBool str_link(JSContext *cx, uintN argc, Value *vp) { - return tagify_value(cx, argc, vp, "a href", "a"); + return tagify_value(cx, CallArgsFromVp(argc, vp), "a href", "a"); } static JSBool str_anchor(JSContext *cx, uintN argc, Value *vp) { - return tagify_value(cx, argc, vp, "a name", "a"); + return tagify_value(cx, CallArgsFromVp(argc, vp), "a name", "a"); } static JSBool str_strike(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "strike", NULL, NULL, vp); + return tagify(cx, "strike", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_small(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "small", NULL, NULL, vp); + return tagify(cx, "small", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_big(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "big", NULL, NULL, vp); + return tagify(cx, "big", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_blink(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "blink", NULL, NULL, vp); + return tagify(cx, "blink", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_sup(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "sup", NULL, NULL, vp); + return tagify(cx, "sup", NULL, NULL, CallReceiverFromVp(vp)); } static JSBool str_sub(JSContext *cx, uintN argc, Value *vp) { - return tagify(cx, "sub", NULL, NULL, vp); + return tagify(cx, "sub", NULL, NULL, CallReceiverFromVp(vp)); } #endif /* JS_HAS_STR_HTML_HELPERS */ -#ifdef JS_TRACER -JSString* FASTCALL -js_String_getelem(JSContext* cx, JSString* str, int32 i) -{ - if ((size_t)i >= str->length()) - return NULL; - return JSAtom::getUnitStringForElement(cx, str, size_t(i)); -} -#endif - -JS_DEFINE_TRCINFO_1(str_concat, - (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, - 1, nanojit::ACCSET_NONE))) - static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE), @@ -3224,7 +2964,7 @@ static JSFunctionSpec string_methods[] = { #endif /* Python-esque sequence methods. */ - JS_TN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE, &str_concat_trcinfo), + JS_FN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE), JS_FN("slice", str_slice, 2,JSFUN_GENERIC_NATIVE), /* HTML string methods. */ @@ -3247,285 +2987,78 @@ static JSFunctionSpec string_methods[] = { JS_FS_END }; -/* - * Set up some tools to make it easier to generate large tables. After constant - * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1). - * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1). - * To use this, define R appropriately, then use Rn(0) (for some value of n), then - * undefine R. - */ -#define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0)) -#define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2)) -#define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4)) -#define R8(n) R6(n), R6((n) + (1 << 6)), R6((n) + (2 << 6)), R6((n) + (3 << 6)) -#define R10(n) R8(n), R8((n) + (1 << 8)), R8((n) + (2 << 8)), R8((n) + (3 << 8)) -#define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10)) - -#define R3(n) R2(n), R2((n) + (1 << 2)) -#define R7(n) R6(n), R6((n) + (1 << 6)) - -#define BUILD_LENGTH_AND_FLAGS(length, flags) \ - (((length) << JSString::LENGTH_SHIFT) | (flags)) - -/* - * Declare unit strings. Pack the string data itself into the mInlineChars - * place in the header. - */ -#define R(c) { \ - BUILD_LENGTH_AND_FLAGS(1, JSString::STATIC_ATOM_FLAGS), \ - { (jschar *)(((char *)(unitStaticTable + (c))) + \ - offsetof(JSString::Data, inlineStorage)) }, \ - { {(c), 0x00} } } - -/* - * For all the pragma pack usage in this file, the following logic applies: - * To apply: To reset: - * Sun CC: pack(#) / pack(0) - * IBM xlC: pack(#) / pack(pop) - * HP aCC: pack # / pack - * Others: pack(push, #) / pack(pop) - * The -Dlint case is explicitly excluded because GCC will error out when - * pack pragmas are used on unsupported platforms. If GCC is being used - * simply for error checking, these errors will be avoided. - */ - -#if defined(__SUNPRO_CC) || defined(__xlC__) -#pragma pack(8) -#elif defined(__HP_aCC) -#pragma pack 8 -#elif !defined(lint) -#pragma pack(push, 8) -#endif - -const JSString::Data JSAtom::unitStaticTable[] -#if defined(__GNUC__) || defined(__xlC__) -__attribute__ ((aligned (8))) -#endif -= { R8(0) }; - -#if defined(__SUNPRO_CC) -#pragma pack(0) -#elif defined(__HP_aCC) -#pragma pack -#elif !defined(lint) -#pragma pack(pop) -#endif - -#undef R - -/* - * Declare length-2 strings. We only store strings where both characters are - * alphanumeric. The lower 10 short chars are the numerals, the next 26 are - * the lowercase letters, and the next 26 are the uppercase letters. - */ -#define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \ - (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \ - (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \ - JSAtom::INVALID_SMALL_CHAR) - -#define R TO_SMALL_CHAR - -const JSAtom::SmallChar JSAtom::toSmallChar[] = { R7(0) }; - -#undef R - -/* - * This is used when we generate our table of short strings, so the compiler is - * happier if we use |c| as few times as possible. - */ -#define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' : \ - (c) < 36 ? 'a' - 10 : \ - 'A' - 36)) -#define R FROM_SMALL_CHAR - -const jschar JSAtom::fromSmallChar[] = { R6(0) }; - -#undef R - -/* - * For code-generation ease, length-2 strings are encoded as 12-bit int values, - * where the upper 6 bits is the first character and the lower 6 bits is the - * second character. - */ -#define R(c) { \ - BUILD_LENGTH_AND_FLAGS(2, JSString::STATIC_ATOM_FLAGS), \ - { (jschar *)(((char *)(length2StaticTable + (c))) + \ - offsetof(JSString::Data, inlineStorage)) }, \ - { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } } - -#if defined(__SUNPRO_CC) || defined(__xlC__) -#pragma pack(8) -#elif defined(__HP_aCC) -#pragma pack 8 -#elif !defined(lint) -#pragma pack(push, 8) -#endif - -const JSString::Data JSAtom::length2StaticTable[] -#if defined(__GNUC__) || defined(__xlC__) -__attribute__ ((aligned (8))) -#endif -= { R12(0) }; - -#if defined(__SUNPRO_CC) -#pragma pack(0) -#elif defined(__HP_aCC) -#pragma pack -#elif !defined(lint) -#pragma pack(pop) -#endif - -#undef R - -/* - * Declare int strings. Only int strings from 100 to 255 actually have to be - * generated, since the rest are either unit strings or length-2 strings. To - * avoid the runtime cost of figuring out where to look for the string for a - * particular integer, we precompute a table of JSString*s which refer to the - * correct location of the int string. - */ -#define R(c) { \ - BUILD_LENGTH_AND_FLAGS(3, JSString::STATIC_ATOM_FLAGS), \ - { (jschar *)(((char *)(hundredStaticTable + ((c) - 100))) + \ - offsetof(JSString::Data, inlineStorage)) }, \ - { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } } - - -JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256); - -#if defined(__SUNPRO_CC) || defined(__xlC__) -#pragma pack(8) -#elif defined(__HP_aCC) -#pragma pack 8 -#elif !defined(lint) -#pragma pack(push, 8) -#endif - -const JSString::Data JSAtom::hundredStaticTable[] -#if defined(__GNUC__) || defined(__xlC__) -__attribute__ ((aligned (8))) -#endif -= { R7(100), /* 100 through 227 */ - R4(100 + (1 << 7)), /* 228 through 243 */ - R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */ - R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */ -}; - -#undef R - -#define R(c) ((c) < 10 ? JSAtom::unitStaticTable + ((c) + '0') : \ - (c) < 100 ? JSAtom::length2StaticTable + \ - ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) + \ - TO_SMALL_CHAR(((c) % 10) + '0') : \ - JSAtom::hundredStaticTable + ((c) - 100)) - -const JSString::Data *const JSAtom::intStaticTable[] = { R8(0) }; - -#undef R - -#if defined(__SUNPRO_CC) -#pragma pack(0) -#elif defined(__HP_aCC) -#pragma pack -#elif !defined(lint) -#pragma pack(pop) -#endif - -#undef R2 -#undef R4 -#undef R6 -#undef R8 -#undef R10 -#undef R12 - -#undef R3 -#undef R7 - JSBool js_String(JSContext *cx, uintN argc, Value *vp) { - Value *argv = vp + 2; + CallArgs args = CallArgsFromVp(argc, vp); JSString *str; - if (argc > 0) { - str = js_ValueToString(cx, argv[0]); + if (args.length() > 0) { + str = ToString(cx, args[0]); if (!str) return false; } else { str = cx->runtime->emptyString; } - if (IsConstructing(vp)) { + if (IsConstructing(args)) { StringObject *strobj = StringObject::create(cx, str); if (!strobj) return false; - vp->setObject(*strobj); - } else { - vp->setString(str); + args.rval() = ObjectValue(*strobj); + return true; } + + args.rval() = StringValue(str); return true; } -static JSBool -str_fromCharCode(JSContext *cx, uintN argc, Value *vp) +JSBool +js::str_fromCharCode(JSContext *cx, uintN argc, Value *vp) { - Value *argv = JS_ARGV(cx, vp); - JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); - if (argc == 1) { + CallArgs args = CallArgsFromVp(argc, vp); + + JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX); + if (args.length() == 1) { uint16_t code; - if (!ValueToUint16(cx, argv[0], &code)) + if (!ValueToUint16(cx, args[0], &code)) return JS_FALSE; - if (JSAtom::hasUnitStatic(code)) { - vp->setString(&JSAtom::unitStatic(code)); + if (StaticStrings::hasUnit(code)) { + args.rval() = StringValue(cx->runtime->staticStrings.getUnit(code)); return JS_TRUE; } - argv[0].setInt32(code); + args[0].setInt32(code); } - jschar *chars = (jschar *) cx->malloc_((argc + 1) * sizeof(jschar)); + jschar *chars = (jschar *) cx->malloc_((args.length() + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; - for (uintN i = 0; i < argc; i++) { + for (uintN i = 0; i < args.length(); i++) { uint16_t code; - if (!ValueToUint16(cx, argv[i], &code)) { + if (!ValueToUint16(cx, args[i], &code)) { cx->free_(chars); return JS_FALSE; } chars[i] = (jschar)code; } - chars[argc] = 0; - JSString *str = js_NewString(cx, chars, argc); + chars[args.length()] = 0; + JSString *str = js_NewString(cx, chars, args.length()); if (!str) { cx->free_(chars); return JS_FALSE; } - vp->setString(str); - return JS_TRUE; -} -#ifdef JS_TRACER -static JSString* FASTCALL -String_fromCharCode(JSContext* cx, int32 i) -{ - JS_ASSERT(JS_ON_TRACE(cx)); - jschar c = (jschar)i; - if (JSAtom::hasUnitStatic(c)) - return &JSAtom::unitStatic(c); - return js_NewStringCopyN(cx, &c, 1); + args.rval() = StringValue(str); + return JS_TRUE; } -#endif - -JS_DEFINE_TRCINFO_1(str_fromCharCode, - (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE))) static JSFunctionSpec string_static_methods[] = { - JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo), + JS_FN("fromCharCode", js::str_fromCharCode, 1, 0), JS_FS_END }; -const Shape * +Shape * StringObject::assignInitialShape(JSContext *cx) { - JS_ASSERT(!cx->compartment->initialStringShape); JS_ASSERT(nativeEmpty()); return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), @@ -3533,70 +3066,45 @@ StringObject::assignInitialShape(JSContext *cx) } JSObject * -js_InitStringClass(JSContext *cx, JSObject *global) +js_InitStringClass(JSContext *cx, JSObject *obj) { - JS_ASSERT(global->isGlobal()); - JS_ASSERT(global->isNative()); - - /* - * Define escape/unescape, the URI encode/decode functions, and maybe - * uneval on the global object. - */ - if (!JS_DefineFunctions(cx, global, string_functions)) - return NULL; + JS_ASSERT(obj->isNative()); - /* Create and initialize String.prototype. */ - JSObject *objectProto; - if (!js_GetClassPrototype(cx, global, JSProto_Object, &objectProto)) - return NULL; + GlobalObject *global = &obj->asGlobal(); - JSObject *proto = NewObject(cx, &js_StringClass, objectProto, global); - if (!proto || !proto->asString()->init(cx, cx->runtime->emptyString)) + JSObject *proto = global->createBlankPrototype(cx, &StringClass); + if (!proto || !proto->asString().init(cx, cx->runtime->emptyString)) return NULL; /* Now create the String function. */ - JSAtom *atom = CLASS_ATOM(cx, String); - JSFunction *ctor = js_NewFunction(cx, NULL, js_String, 1, JSFUN_CONSTRUCTOR, global, atom); + JSFunction *ctor = global->createConstructor(cx, js_String, &StringClass, + CLASS_ATOM(cx, String), 1); if (!ctor) return NULL; - /* String creates string objects. */ - FUN_CLASP(ctor) = &js_StringClass; + if (!LinkConstructorAndPrototype(cx, ctor, proto)) + return NULL; - /* Define String.prototype and String.prototype.constructor. */ - if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - ObjectValue(*proto), PropertyStub, StrictPropertyStub, - JSPROP_PERMANENT | JSPROP_READONLY) || - !proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - ObjectValue(*ctor), PropertyStub, StrictPropertyStub, 0)) + if (!DefinePropertiesAndBrand(cx, proto, NULL, string_methods) || + !DefinePropertiesAndBrand(cx, ctor, NULL, string_static_methods)) { return NULL; } - /* Add properties and methods to the prototype and the constructor. */ - if (!JS_DefineFunctions(cx, proto, string_methods) || - !JS_DefineFunctions(cx, ctor, string_static_methods)) - { + /* Capture normal data properties pregenerated for String objects. */ + TypeObject *type = proto->getNewType(cx); + if (!type) return NULL; - } + AddTypeProperty(cx, type, "length", Type::Int32Type()); - /* Pre-brand String and String.prototype for trace-jitted code. */ - proto->brand(cx); - ctor->brand(cx); + if (!DefineConstructorAndPrototype(cx, global, JSProto_String, ctor, proto)) + return NULL; /* - * Make sure proto's emptyShape is available to be shared by String - * objects. JSObject::emptyShape is a one-slot cache. If we omit this, some - * other class could snap it up. (The risk is particularly great for - * Object.prototype.) - * - * All callers of JSObject::initSharingEmptyShape depend on this. + * Define escape/unescape, the URI encode/decode functions, and maybe + * uneval on the global object. */ - if (!proto->getEmptyShape(cx, &js_StringClass, FINALIZE_OBJECT0)) - return NULL; - - /* Install the fully-constructed String and String.prototype. */ - if (!DefineConstructorAndPrototype(cx, global, JSProto_String, ctor, proto)) + if (!JS_DefineFunctions(cx, global, string_functions)) return NULL; return proto; @@ -3605,10 +3113,10 @@ js_InitStringClass(JSContext *cx, JSObject *global) JSFixedString * js_NewString(JSContext *cx, jschar *chars, size_t length) { - if (!CheckStringLength(cx, length)) - return NULL; - - return JSFixedString::new_(cx, chars, length); + JSFixedString *s = JSFixedString::new_(cx, chars, length); + if (s) + Probes::createString(cx, s, length); + return s; } static JS_ALWAYS_INLINE JSFixedString * @@ -3629,6 +3137,7 @@ NewShortString(JSContext *cx, const jschar *chars, size_t length) jschar *storage = str->init(length); PodCopy(storage, chars, length); storage[length] = 0; + Probes::createString(cx, str, length); return str; } @@ -3647,7 +3156,7 @@ NewShortString(JSContext *cx, const char *chars, size_t length) #ifdef DEBUG size_t oldLength = length; #endif - if (!js_InflateUTF8StringToBuffer(cx, chars, length, storage, &length)) + if (!InflateUTF8StringToBuffer(cx, chars, length, storage, &length)) return NULL; JS_ASSERT(length <= oldLength); storage[length] = 0; @@ -3659,6 +3168,7 @@ NewShortString(JSContext *cx, const char *chars, size_t length) *p++ = (unsigned char)*chars++; *p = 0; } + Probes::createString(cx, str, length); return str; } @@ -3674,8 +3184,7 @@ StringBuffer::extractWellSized() /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */ JS_ASSERT(capacity >= length); - if (length > CharBuffer::sMaxInlineStorage && - capacity - length > (length >> 2)) { + if (length > CharBuffer::sMaxInlineStorage && capacity - length > length / 4) { size_t bytes = sizeof(jschar) * (length + 1); JSContext *cx = context(); jschar *tmp = (jschar *)cx->realloc_(buf, bytes); @@ -3726,7 +3235,7 @@ StringBuffer::finishAtom() if (length == 0) return cx->runtime->atomState.emptyAtom; - JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length, 0); + JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length); cb.clear(); return atom; } @@ -3746,32 +3255,13 @@ js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t len const jschar *chars = base->chars() + start; - if (JSLinearString *staticStr = JSAtom::lookupStatic(chars, length)) + if (JSLinearString *staticStr = cx->runtime->staticStrings.lookup(chars, length)) return staticStr; - return JSDependentString::new_(cx, base, chars, length); -} - -#ifdef DEBUG -#include - -void printJSStringStats(JSRuntime *rt) -{ - double mean, sigma; - - mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum, - rt->lengthSquaredSum, &sigma); - - fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", - (unsigned long)rt->totalStrings, mean, sigma); - - mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum, - rt->strdepLengthSquaredSum, &sigma); - - fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", - (unsigned long)rt->totalDependentStrings, mean, sigma); + JSLinearString *s = JSDependentString::new_(cx, base, chars, length); + Probes::createString(cx, s, length); + return s; } -#endif JSFixedString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n) @@ -3796,7 +3286,7 @@ js_NewStringCopyN(JSContext *cx, const char *s, size_t n) if (JSShortString::lengthFits(n)) return NewShortString(cx, s, n); - jschar *chars = js_InflateString(cx, s, &n); + jschar *chars = InflateString(cx, s, &n); if (!chars) return NULL; JSFixedString *str = js_NewString(cx, chars, n); @@ -3816,7 +3306,7 @@ js_NewStringCopyZ(JSContext *cx, const jschar *s) jschar *news = (jschar *) cx->malloc_(m); if (!news) return NULL; - memcpy(news, s, m); + js_memcpy(news, s, m); JSFixedString *str = js_NewString(cx, news, n); if (!str) cx->free_(news); @@ -3834,7 +3324,7 @@ js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool { JSString *str; - str = (asSource ? js_ValueToSource : js_ValueToString)(cx, v); + str = (asSource ? js_ValueToSource : ToString)(cx, v); if (!str) return NULL; str = js_QuoteString(cx, str, 0); @@ -3844,10 +3334,13 @@ js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool } JSString * -js_ValueToString(JSContext *cx, const Value &arg) +js::ToStringSlow(JSContext *cx, const Value &arg) { + /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */ + JS_ASSERT(!arg.isString()); + Value v = arg; - if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v)) + if (!ToPrimitive(cx, JSTYPE_STRING, &v)) return NULL; JSString *str; @@ -3872,7 +3365,7 @@ bool js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb) { Value v = arg; - if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v)) + if (!ToPrimitive(cx, JSTYPE_STRING, &v)) return false; if (v.isString()) @@ -3904,26 +3397,26 @@ js_ValueToSource(JSContext *cx, const Value &v) return js_NewStringCopyN(cx, js_negzero_ucNstr, 2); } - return js_ValueToString(cx, v); + return ToString(cx, v); } Value rval = NullValue(); Value fval; jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom); if (!js_GetMethod(cx, &v.toObject(), id, JSGET_NO_METHOD_BARRIER, &fval)) - return false; + return NULL; if (js_IsCallable(fval)) { - if (!ExternalInvoke(cx, v, fval, 0, NULL, &rval)) - return false; + if (!Invoke(cx, v, fval, 0, NULL, &rval)) + return NULL; } - return js_ValueToString(cx, rval); + return ToString(cx, rval); } namespace js { bool -EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result) +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result) { if (str1 == str2) { *result = true; @@ -3962,19 +3455,10 @@ EqualStrings(JSLinearString *str1, JSLinearString *str2) } /* namespace js */ -JSBool JS_FASTCALL -js_EqualStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) -{ - JSBool result; - return EqualStrings(cx, str1, str2, &result) ? result : JS_NEITHER; -} -JS_DEFINE_CALLINFO_3(extern, BOOL, js_EqualStringsOnTrace, - CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) - namespace js { static bool -CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *result) { JS_ASSERT(str1); JS_ASSERT(str2); @@ -3984,47 +3468,25 @@ CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result) return true; } - size_t l1 = str1->length(); const jschar *s1 = str1->getChars(cx); if (!s1) return false; - size_t l2 = str2->length(); const jschar *s2 = str2->getChars(cx); if (!s2) return false; - size_t n = JS_MIN(l1, l2); - for (size_t i = 0; i < n; i++) { - if (int32 cmp = s1[i] - s2[i]) { - *result = cmp; - return true; - } - } - *result = (int32)(l1 - l2); - return true; + return CompareChars(s1, str1->length(), s2, str2->length(), result); } bool -CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result) { return CompareStringsImpl(cx, str1, str2, result); } } /* namespace js */ -int32 JS_FASTCALL -js_CompareStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) -{ - int32 result; - if (!CompareStringsImpl(cx, str1, str2, &result)) - return INT32_MIN; - JS_ASSERT(result != INT32_MIN); - return result; -} -JS_DEFINE_CALLINFO_3(extern, INT32, js_CompareStringsOnTrace, - CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) - namespace js { bool @@ -4079,35 +3541,28 @@ js_strchr_limit(const jschar *s, jschar c, const jschar *limit) return NULL; } +namespace js { + jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp, bool useCESU8) +InflateString(JSContext *cx, const char *bytes, size_t *lengthp, FlationCoding fc) { - size_t nbytes, nchars, i; + size_t nchars; jschar *chars; -#ifdef DEBUG - JSBool ok; -#endif + size_t nbytes = *lengthp; - nbytes = *lengthp; - if (js_CStringsAreUTF8 || useCESU8) { - if (!js_InflateUTF8StringToBuffer(cx, bytes, nbytes, NULL, &nchars, - useCESU8)) + if (js_CStringsAreUTF8 || fc == CESU8Encoding) { + if (!InflateUTF8StringToBuffer(cx, bytes, nbytes, NULL, &nchars, fc)) goto bad; chars = (jschar *) cx->malloc_((nchars + 1) * sizeof (jschar)); if (!chars) goto bad; -#ifdef DEBUG - ok = -#endif - js_InflateUTF8StringToBuffer(cx, bytes, nbytes, chars, &nchars, - useCESU8); - JS_ASSERT(ok); + JS_ALWAYS_TRUE(InflateUTF8StringToBuffer(cx, bytes, nbytes, chars, &nchars, fc)); } else { nchars = nbytes; chars = (jschar *) cx->malloc_((nchars + 1) * sizeof(jschar)); if (!chars) goto bad; - for (i = 0; i < nchars; i++) + for (size_t i = 0; i < nchars; i++) chars[i] = (unsigned char) bytes[i]; } *lengthp = nchars; @@ -4127,26 +3582,19 @@ js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp, bool useCESU * May be called with null cx. */ char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars) +DeflateString(JSContext *cx, const jschar *chars, size_t nchars) { size_t nbytes, i; char *bytes; -#ifdef DEBUG - JSBool ok; -#endif if (js_CStringsAreUTF8) { - nbytes = js_GetDeflatedStringLength(cx, chars, nchars); + nbytes = GetDeflatedStringLength(cx, chars, nchars); if (nbytes == (size_t) -1) return NULL; bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1)); if (!bytes) return NULL; -#ifdef DEBUG - ok = -#endif - js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes); - JS_ASSERT(ok); + JS_ALWAYS_TRUE(DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes)); } else { nbytes = nchars; bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1)); @@ -4160,25 +3608,26 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars) } size_t -js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars) +GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars) { if (!js_CStringsAreUTF8) return nchars; - return js_GetDeflatedUTF8StringLength(cx, chars, nchars); + return GetDeflatedUTF8StringLength(cx, chars, nchars); } /* * May be called with null cx through public API, see below. */ size_t -js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, - size_t nchars, bool useCESU8) +GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, + size_t nchars, FlationCoding fc) { size_t nbytes; const jschar *end; uintN c, c2; char buffer[10]; + bool useCESU8 = fc == CESU8Encoding; nbytes = nchars; for (end = chars + nchars; chars != end; chars++) { @@ -4216,9 +3665,9 @@ js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, return (size_t) -1; } -JSBool -js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, - char *dst, size_t *dstlenp) +bool +DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp) { size_t dstlen, i; @@ -4239,20 +3688,22 @@ js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, return JS_TRUE; } - return js_DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp); + return DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp); } -JSBool -js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen, - char *dst, size_t *dstlenp, bool useCESU8) +bool +DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp, FlationCoding fc) { - size_t dstlen, i, origDstlen, utf8Len; + size_t i, utf8Len; jschar c, c2; - uint32 v; - uint8 utf8buf[6]; + uint32_t v; + uint8_t utf8buf[6]; + + bool useCESU8 = fc == CESU8Encoding; + size_t dstlen = *dstlenp; + size_t origDstlen = dstlen; - dstlen = *dstlenp; - origDstlen = dstlen; while (srclen) { c = *src++; srclen--; @@ -4292,7 +3743,7 @@ js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen, *dstlenp = (origDstlen - dstlen); /* Delegate error reporting to the measurement function. */ if (cx) - js_GetDeflatedStringLength(cx, src - 1, srclen + 1); + GetDeflatedStringLength(cx, src - 1, srclen + 1); return JS_FALSE; bufferTooSmall: @@ -4304,47 +3755,47 @@ js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen, return JS_FALSE; } -JSBool -js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, - jschar *dst, size_t *dstlenp) +bool +InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp) { size_t dstlen, i; - if (!js_CStringsAreUTF8) { - if (dst) { - dstlen = *dstlenp; - if (srclen > dstlen) { - for (i = 0; i < dstlen; i++) - dst[i] = (unsigned char) src[i]; - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; - } - for (i = 0; i < srclen; i++) + if (js_CStringsAreUTF8) + return InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp); + + if (dst) { + dstlen = *dstlenp; + if (srclen > dstlen) { + for (i = 0; i < dstlen; i++) dst[i] = (unsigned char) src[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; } - *dstlenp = srclen; - return JS_TRUE; + for (i = 0; i < srclen; i++) + dst[i] = (unsigned char) src[i]; } - - return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp); + *dstlenp = srclen; + return JS_TRUE; } -JSBool -js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen, - jschar *dst, size_t *dstlenp, bool useCESU8) +bool +InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp, FlationCoding fc) { size_t dstlen, origDstlen, offset, j, n; - uint32 v; + uint32_t v; dstlen = dst ? *dstlenp : (size_t) -1; origDstlen = dstlen; offset = 0; + bool useCESU8 = fc == CESU8Encoding; while (srclen) { - v = (uint8) *src; + v = (uint8_t) *src; n = 1; if (v & 0x80) { while (v & (0x80 >> n)) @@ -4357,7 +3808,7 @@ js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen, if ((src[j] & 0xC0) != 0x80) goto badCharacter; } - v = Utf8ToOneUcs4Char((uint8 *)src, n); + v = Utf8ToOneUcs4Char((uint8_t *)src, n); if (v >= 0x10000 && !useCESU8) { v -= 0x10000; if (v > 0xFFFFF || dstlen < 2) { @@ -4413,1293 +3864,7 @@ js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen, return JS_FALSE; } -/* - * From java.lang.Character.java: - * - * The character properties are currently encoded into 32 bits in the - * following manner: - * - * 10 bits signed offset used for converting case - * 1 bit if 1, adding the signed offset converts the character to - * lowercase - * 1 bit if 1, subtracting the signed offset converts the character to - * uppercase - * 1 bit if 1, character has a titlecase equivalent (possibly itself) - * 3 bits 0 may not be part of an identifier - * 1 ignorable control; may continue a Unicode identifier or JS - * identifier - * 2 may continue a JS identifier but not a Unicode identifier - * (unused) - * 3 may continue a Unicode identifier or JS identifier - * 4 is a JS whitespace character - * 5 may start or continue a JS identifier; - * may continue but not start a Unicode identifier (_) - * 6 may start or continue a JS identifier but not a Unicode - * identifier ($) - * 7 may start or continue a Unicode identifier or JS identifier - * Thus: - * 5, 6, 7 may start a JS identifier - * 1, 2, 3, 5, 6, 7 may continue a JS identifier - * 7 may start a Unicode identifier - * 1, 3, 5, 7 may continue a Unicode identifier - * 1 is ignorable within an identifier - * 4 is JS whitespace - * 2 bits 0 this character has no numeric property - * 1 adding the digit offset to the character code and then - * masking with 0x1F will produce the desired numeric value - * 2 this character has a "strange" numeric value - * 3 a JS supradecimal digit: adding the digit offset to the - * character code, then masking with 0x1F, then adding 10 - * will produce the desired numeric value - * 5 bits digit offset - * 1 bit XML 1.0 name start character - * 1 bit XML 1.0 name character - * 2 bits reserved for future use - * 5 bits character type - */ - -/* The X table has 1024 entries for a total of 1024 bytes. */ - -const uint8 js_X[] = { - 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ - 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ - 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ - 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ - 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ - 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ - 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ - 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ - 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ - 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ - 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ - 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ - 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ - 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ - 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ - 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ - 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ - 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ -105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ -106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ - 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ -115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ -}; - -/* The Y table has 7808 entries for a total of 7808 bytes. */ - -const uint8 js_Y[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ - 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ - 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ - 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ - 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ - 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ - 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ - 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ - 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ - 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ - 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ - 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ - 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ - 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ - 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ - 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ - 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ - 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ - 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ - 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ - 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ - 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ - 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ - 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ - 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ - 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ - 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ - 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ - 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ - 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ - 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ - 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ - 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ - 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ - 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ - 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ - 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ - 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ - 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ - 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ - 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ - 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ - 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ - 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ - 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ - 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ - 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ - 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ - 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ - 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ - 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ - 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ - 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ - 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ - 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ - 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ - 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ - 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ - 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ - 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ - 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ - 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ - 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ - 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ - 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ - 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ - 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ - 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ - 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ - 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ - 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ - 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ - 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ - 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ - 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ - 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ - 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ - 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ - 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ - 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ - 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ - 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ - 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ - 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ - 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ - 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ - 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ - 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ - 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ - 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ - 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ - 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ - 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ - 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ - 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ - 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ - 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ - 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ - 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ - 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ - 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ - 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ - 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ - 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ - 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ - 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ - 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ - 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ - 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ - 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ - 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ -102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ - 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ - 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ - 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ - 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ -105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ - 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ - 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ - 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ - 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ -107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ -107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ - 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ - 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ - 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ - 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ - 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ - 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ - 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ - 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ - 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ -109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ -109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ -111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ -111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ -113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ - 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ - 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ - 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ - 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ - 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ - 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ -119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ -114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ - 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ - 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ - 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ - 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ - 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ - 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ -121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ - 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ - 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ - 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ - 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ - 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ - 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ - 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ -114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ - 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ - 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ - 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ - 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ - 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ - 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ - 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ - 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ - 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ - 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ - 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ - 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ - 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ - 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ - 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ - 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ -}; - -/* The A table has 124 entries for a total of 496 bytes. */ - -const uint32 js_A[] = { -0x0001000F, /* 0 Cc, ignorable */ -0x0004000F, /* 1 Cc, whitespace */ -0x0004000C, /* 2 Zs, whitespace */ -0x00000018, /* 3 Po */ -0x0006001A, /* 4 Sc, currency */ -0x00000015, /* 5 Ps */ -0x00000016, /* 6 Pe */ -0x00000019, /* 7 Sm */ -0x00000014, /* 8 Pd */ -0x00036089, /* 9 Nd, identifier part, decimal 16 */ -0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ -0x0000001B, /* 11 Sk */ -0x00050017, /* 12 Pc, underscore */ -0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ -0x0000000C, /* 14 Zs */ -0x0000001C, /* 15 So */ -0x00070182, /* 16 Ll, identifier start */ -0x0000600B, /* 17 No, decimal 16 */ -0x0000500B, /* 18 No, decimal 8 */ -0x0000800B, /* 19 No, strange */ -0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ -0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ -0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ -0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ -0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ -0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ -0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ -0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ -0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ -0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ -0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ -0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ -0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ -0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ -0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ -0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ -0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ -0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ -0x00070181, /* 38 Lu, identifier start */ -0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ -0x00070185, /* 40 Lo, identifier start */ -0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ -0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ -0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ -0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ -0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ -0x00000000, /* 46 unassigned */ -0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ -0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ -0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ -0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ -0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ -0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ -0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ -0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ -0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ -0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ -0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ -0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ -0x00070084, /* 59 Lm, identifier start */ -0x00030086, /* 60 Mn, identifier part */ -0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ -0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ -0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ -0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ -0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ -0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ -0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ -0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ -0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ -0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ -0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ -0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ -0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ -0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ -0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ -0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ -0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ -0x00034089, /* 78 Nd, identifier part, decimal 0 */ -0x00000087, /* 79 Me */ -0x00030088, /* 80 Mc, identifier part */ -0x00037489, /* 81 Nd, identifier part, decimal 26 */ -0x00005A0B, /* 82 No, decimal 13 */ -0x00006E0B, /* 83 No, decimal 23 */ -0x0000740B, /* 84 No, decimal 26 */ -0x0000000B, /* 85 No */ -0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ -0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ -0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ -0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ -0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ -0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ -0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ -0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ -0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ -0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ -0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ -0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ -0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ -0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ -0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ -0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ -0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ -0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ -0x00010010, /* 104 Cf, ignorable */ -0x0004000D, /* 105 Zl, whitespace */ -0x0004000E, /* 106 Zp, whitespace */ -0x0000400B, /* 107 No, decimal 0 */ -0x0000440B, /* 108 No, decimal 2 */ -0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ -0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ -0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ -0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ -0x0007818A, /* 113 Nl, identifier start, strange */ -0x0000420B, /* 114 No, decimal 1 */ -0x0000720B, /* 115 No, decimal 25 */ -0x06A0001C, /* 116 So, hasLower (add 26) */ -0x0690001C, /* 117 So, hasUpper (subtract 26) */ -0x00006C0B, /* 118 No, decimal 22 */ -0x0000560B, /* 119 No, decimal 11 */ -0x0007738A, /* 120 Nl, identifier start, decimal 25 */ -0x0007418A, /* 121 Nl, identifier start, decimal 0 */ -0x00000013, /* 122 Cs */ -0x00000012 /* 123 Co */ -}; +} /* namepsace js */ const jschar js_uriReservedPlusPound_ucstr[] = {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; @@ -5713,27 +3878,6 @@ const jschar js_uriUnescaped_ucstr[] = #define ____ false -/* - * This table allows efficient testing for the regular expression \w which is - * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z]. - */ -const bool js_alnum[] = { -/* 0 1 2 3 4 5 6 7 8 9 */ -/* 0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, -/* 1 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, -/* 2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, -/* 3 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, -/* 4 */ ____, ____, ____, ____, ____, ____, ____, ____, true, true, -/* 5 */ true, true, true, true, true, true, true, true, ____, ____, -/* 6 */ ____, ____, ____, ____, ____, true, true, true, true, true, -/* 7 */ true, true, true, true, true, true, true, true, true, true, -/* 8 */ true, true, true, true, true, true, true, true, true, true, -/* 9 */ true, ____, ____, ____, ____, true, ____, true, true, true, -/* 10 */ true, true, true, true, true, true, true, true, true, true, -/* 11 */ true, true, true, true, true, true, true, true, true, true, -/* 12 */ true, true, true, ____, ____, ____, ____, ____ -}; - /* * Identifier start chars: * - 36: $ @@ -5854,7 +3998,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } - uint32 v; + uint32_t v; if (c < 0xD800 || c > 0xDBFF) { v = c; } else { @@ -5872,7 +4016,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, } v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } - uint8 utf8buf[4]; + uint8_t utf8buf[4]; size_t L = js_OneUcs4ToUtf8Char(utf8buf, v); for (size_t j = 0; j < L; j++) { hexBuf[1] = HexDigits[utf8buf[j] >> 4]; @@ -5918,8 +4062,8 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) n++; if (n == 1 || n > 4) goto report_bad_uri; - uint8 octets[4]; - octets[0] = (uint8)B; + uint8_t octets[4]; + octets[0] = (uint8_t)B; if (k + 3 * (n - 1) >= length) goto report_bad_uri; for (intN j = 1; j < n; j++) { @@ -5934,7 +4078,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) k += 2; octets[j] = (char)B; } - uint32 v = Utf8ToOneUcs4Char(octets, n); + uint32_t v = Utf8ToOneUcs4Char(octets, n); if (v >= 0x10000) { v -= 0x10000; if (v > 0xFFFFF) @@ -5972,38 +4116,65 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) static JSBool str_decodeURI(JSContext *cx, uintN argc, Value *vp) { - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + CallArgs args = CallArgsFromVp(argc, vp); + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) - return JS_FALSE; - return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp); + return false; + + Value result; + if (!Decode(cx, str, js_uriReservedPlusPound_ucstr, &result)) + return false; + + args.rval() = result; + return true; } static JSBool str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + CallArgs args = CallArgsFromVp(argc, vp); + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) - return JS_FALSE; - return Decode(cx, str, js_empty_ucstr, vp); + return false; + + Value result; + if (!Decode(cx, str, js_empty_ucstr, &result)) + return false; + + args.rval() = result; + return true; } static JSBool str_encodeURI(JSContext *cx, uintN argc, Value *vp) { - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + CallArgs args = CallArgsFromVp(argc, vp); + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) - return JS_FALSE; - return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, - vp); + return false; + + Value result; + if (!Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, &result)) + return false; + + args.rval() = result; + return true; } static JSBool str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); + CallArgs args = CallArgsFromVp(argc, vp); + JSLinearString *str = ArgToRootedString(cx, args, 0); if (!str) - return JS_FALSE; - return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp); + return false; + + Value result; + if (!Encode(cx, str, js_uriUnescaped_ucstr, NULL, &result)) + return false; + + args.rval() = result; + return true; } /* @@ -6011,16 +4182,16 @@ str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp) * least 4 bytes long. Return the number of UTF-8 bytes of data written. */ int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) +js_OneUcs4ToUtf8Char(uint8_t *utf8Buffer, uint32_t ucs4Char) { int utf8Length = 1; JS_ASSERT(ucs4Char <= 0x10FFFF); if (ucs4Char < 0x80) { - *utf8Buffer = (uint8)ucs4Char; + *utf8Buffer = (uint8_t)ucs4Char; } else { int i; - uint32 a = ucs4Char >> 11; + uint32_t a = ucs4Char >> 11; utf8Length = 2; while (a) { a >>= 5; @@ -6028,10 +4199,10 @@ js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) } i = utf8Length; while (--i) { - utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); + utf8Buffer[i] = (uint8_t)((ucs4Char & 0x3F) | 0x80); ucs4Char >>= 6; } - *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + *utf8Buffer = (uint8_t)(0x100 - (1 << (8-utf8Length)) + ucs4Char); } return utf8Length; } @@ -6041,42 +4212,38 @@ js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) * character. It is assumed that the caller already checked that the sequence * is valid. */ -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) +static uint32_t +Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length) { - uint32 ucs4Char; - uint32 minucs4Char; - /* from Unicode 3.1, non-shortest form is illegal */ - static const uint32 minucs4Table[] = { - 0x00000080, 0x00000800, 0x00010000 - }; + JS_ASSERT(1 <= utf8Length && utf8Length <= 4); - JS_ASSERT(utf8Length >= 1 && utf8Length <= 4); if (utf8Length == 1) { - ucs4Char = *utf8Buffer; - JS_ASSERT(!(ucs4Char & 0x80)); - } else { - JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == - (0x100 - (1 << (8-utf8Length)))); - ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); - minucs4Char = minucs4Table[utf8Length-2]; - while (--utf8Length) { - JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); - ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); - } - if (JS_UNLIKELY(ucs4Char < minucs4Char)) { - ucs4Char = OVERLONG_UTF8; - } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { - ucs4Char = 0xFFFD; - } + JS_ASSERT(!(*utf8Buffer & 0x80)); + return *utf8Buffer; + } + + /* from Unicode 3.1, non-shortest form is illegal */ + static const uint32_t minucs4Table[] = { 0x80, 0x800, 0x10000 }; + + JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7 - utf8Length)))) == + (0x100 - (1 << (8 - utf8Length)))); + uint32_t ucs4Char = *utf8Buffer++ & ((1 << (7 - utf8Length)) - 1); + uint32_t minucs4Char = minucs4Table[utf8Length - 2]; + while (--utf8Length) { + JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); + ucs4Char = (ucs4Char << 6) | (*utf8Buffer++ & 0x3F); } + + if (JS_UNLIKELY(ucs4Char < minucs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF))) + return INVALID_UTF8; + return ucs4Char; } namespace js { size_t -PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32 quote) +PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32_t quote) { enum { STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE diff --git a/deps/mozjs/js/src/jsstr.h b/deps/mozjs/js/src/jsstr.h index 35b846d174e..bca9bacc58d 100644 --- a/deps/mozjs/js/src/jsstr.h +++ b/deps/mozjs/js/src/jsstr.h @@ -42,702 +42,14 @@ #include #include "jsapi.h" +#include "jsatom.h" #include "jsprvtd.h" -#include "jshashtable.h" #include "jslock.h" -#include "jsobj.h" -#include "jsvalue.h" #include "jscell.h" +#include "jsutil.h" -/* - * JS strings - * - * Conceptually, a JS string is just an array of chars and a length. To improve - * performance of common string operations, the following optimizations are - * made which affect the engine's representation of strings: - * - * - The plain vanilla representation is a "flat" string which consists of a - * string header in the GC heap and a malloc'd null terminated char array. - * - * - To avoid copying a substring of an existing "base" string , a "dependent" - * string (JSDependentString) can be created which points into the base - * string's char array. - * - * - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created - * to represent a delayed string concatenation. Concatenation (called - * flattening) is performed if and when a linear char array is requested. In - * general, ropes form a binary dag whose internal nodes are JSRope string - * headers with no associated char array and whose leaf nodes are either flat - * or dependent strings. - * - * - To avoid copying the left-hand side when flattening, the left-hand side's - * buffer may be grown to make space for a copy of the right-hand side (see - * comment in JSString::flatten). This optimization requires that there are - * no external pointers into the char array. We conservatively maintain this - * property via a flat string's "extensible" property. - * - * - To avoid allocating small char arrays, short strings can be stored inline - * in the string header (JSInlineString). To increase the max size of such - * inline strings, extra-large string headers can be used (JSShortString). - * - * - To avoid comparing O(n) string equality comparison, strings can be - * canonicalized to "atoms" (JSAtom) such that there is a single atom with a - * given (length,chars). - * - * - To avoid dynamic creation of common short strings (e.g., single-letter - * alphanumeric strings, numeric strings up to 999) headers and char arrays - * for such strings are allocated in static memory (JSStaticAtom) and used - * as atoms. - * - * - To avoid copying all strings created through the JSAPI, an "external" - * string (JSExternalString) can be created whose chars are managed by the - * JSAPI client. - * - * Although all strings share the same basic memory layout, we can conceptually - * arrange them into a hierarchy of operations/invariants and represent this - * hierarchy in C++ with classes: - * - * C++ type operations+fields / invariants+properties - * - * JSString (abstract) getCharsZ, getChars, length / - - * | \ - * | JSRope leftChild, rightChild / - - * | - * JSLinearString (abstract) chars / not null-terminated - * | \ - * | JSDependentString base / - - * | - * JSFlatString (abstract) chars / not null-terminated - * | \ - * | JSExtensibleString capacity / no external pointers into char array - * | - * JSFixedString - / may have external pointers into char array - * | \ \ - * | \ JSExternalString - / char array memory managed by embedding - * | \ - * | JSInlineString - / chars stored in header - * | | \ - * | | JSShortString - / header is fat - * | | | - * JSAtom | | - / string equality === pointer equality - * | \ | | - * | JSInlineAtom | - / atomized JSInlineString - * | \ | - * | JSShortAtom - / atomized JSShortString - * | - * JSStaticAtom - / header and chars statically allocated - * - * Classes marked with (abstract) above are not literally C++ Abstract Base - * Classes (since there are no virtual functions, pure or not, in this - * hierarchy), but have the same meaning: there are no strings with this type as - * its most-derived type. - * - * Derived string types can be queried from ancestor types via isX() and - * retrieved with asX() debug-only-checked casts. - * - * The ensureX() operations mutate 'this' in place to effectively the type to be - * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString). - */ - -class JSString : public js::gc::Cell -{ - protected: - static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar); - - /* Fields only apply to string types commented on the right. */ - struct Data - { - size_t lengthAndFlags; /* JSString */ - union { - const jschar *chars; /* JSLinearString */ - JSString *left; /* JSRope */ - } u1; - union { - jschar inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|Short)String */ - struct { - union { - JSLinearString *base; /* JSDependentString */ - JSString *right; /* JSRope */ - size_t capacity; /* JSFlatString (extensible) */ - size_t externalType; /* JSExternalString */ - } u2; - union { - JSString *parent; /* JSRope (temporary) */ - void *externalClosure; /* JSExternalString */ - size_t reserved; /* may use for bug 615290 */ - } u3; - } s; - }; - } d; - - public: - /* Flags exposed only for jits */ - - static const size_t LENGTH_SHIFT = 4; - static const size_t FLAGS_MASK = JS_BITMASK(LENGTH_SHIFT); - static const size_t MAX_LENGTH = JS_BIT(32 - LENGTH_SHIFT) - 1; - - /* - * The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type - * of the string. The remaining bits store the string length (which must be - * less or equal than MAX_LENGTH). - * - * Instead of using a dense index to represent the most-derived type, string - * types are encoded to allow single-op tests for hot queries (isRope, - * isDependent, isFlat, isAtom, isStaticAtom): - * - * JSRope xxx1 - * JSLinearString xxx0 - * JSDependentString xx1x - * JSFlatString xx00 - * JSExtensibleString 1100 - * JSFixedString xy00 where xy != 11 - * JSInlineString 0100 and chars == inlineStorage - * JSShortString 0100 and in FINALIZE_SHORT_STRING arena - * JSExternalString 0100 and in FINALIZE_EXTERNAL_STRING arena - * JSAtom x000 - * JSStaticAtom 0000 - * - * NB: this scheme takes advantage of the fact that there are no string - * instances whose most-derived type is JSString, JSLinearString, or - * JSFlatString. - */ - - static const size_t ROPE_BIT = JS_BIT(0); - - static const size_t LINEAR_MASK = JS_BITMASK(1); - static const size_t LINEAR_FLAGS = 0x0; - - static const size_t DEPENDENT_BIT = JS_BIT(1); - - static const size_t FLAT_MASK = JS_BITMASK(2); - static const size_t FLAT_FLAGS = 0x0; - - static const size_t FIXED_FLAGS = JS_BIT(2); - - static const size_t ATOM_MASK = JS_BITMASK(3); - static const size_t ATOM_FLAGS = 0x0; - - static const size_t STATIC_ATOM_MASK = JS_BITMASK(4); - static const size_t STATIC_ATOM_FLAGS = 0x0; - - static const size_t EXTENSIBLE_FLAGS = JS_BIT(2) | JS_BIT(3); - static const size_t NON_STATIC_ATOM = JS_BIT(3); - - size_t buildLengthAndFlags(size_t length, size_t flags) { - return (length << LENGTH_SHIFT) | flags; - } - - static void staticAsserts() { - JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX)); - JS_STATIC_ASSERT(JSString::MAX_LENGTH <= JSVAL_INT_MAX); - JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32); - JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >> - JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH); - JS_STATIC_ASSERT(sizeof(JSString) == - offsetof(JSString, d.inlineStorage) + - NUM_INLINE_CHARS * sizeof(jschar)); - } - - /* Avoid lame compile errors in JSRope::flatten */ - friend class JSRope; - - public: - /* All strings have length. */ - - JS_ALWAYS_INLINE - size_t length() const { - return d.lengthAndFlags >> LENGTH_SHIFT; - } - - JS_ALWAYS_INLINE - bool empty() const { - return d.lengthAndFlags <= FLAGS_MASK; - } - - /* - * All strings have a fallible operation to get an array of chars. - * getCharsZ additionally ensures the array is null terminated. - */ - - inline const jschar *getChars(JSContext *cx); - inline const jschar *getCharsZ(JSContext *cx); - - /* Fallible conversions to more-derived string types. */ - - inline JSLinearString *ensureLinear(JSContext *cx); - inline JSFlatString *ensureFlat(JSContext *cx); - inline JSFixedString *ensureFixed(JSContext *cx); - - /* Type query and debug-checked casts */ - - JS_ALWAYS_INLINE - bool isRope() const { - bool rope = d.lengthAndFlags & ROPE_BIT; - JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT); - return rope; - } - - JS_ALWAYS_INLINE - JSRope &asRope() { - JS_ASSERT(isRope()); - return *(JSRope *)this; - } - - JS_ALWAYS_INLINE - bool isLinear() const { - return (d.lengthAndFlags & LINEAR_MASK) == LINEAR_FLAGS; - } - - JS_ALWAYS_INLINE - JSLinearString &asLinear() { - JS_ASSERT(isLinear()); - return *(JSLinearString *)this; - } - - JS_ALWAYS_INLINE - bool isDependent() const { - bool dependent = d.lengthAndFlags & DEPENDENT_BIT; - JS_ASSERT_IF(dependent, (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_BIT); - return dependent; - } - - JS_ALWAYS_INLINE - JSDependentString &asDependent() { - JS_ASSERT(isDependent()); - return *(JSDependentString *)this; - } - - JS_ALWAYS_INLINE - bool isFlat() const { - return (d.lengthAndFlags & FLAT_MASK) == FLAT_FLAGS; - } - - JS_ALWAYS_INLINE - JSFlatString &asFlat() { - JS_ASSERT(isFlat()); - return *(JSFlatString *)this; - } - - JS_ALWAYS_INLINE - bool isExtensible() const { - return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS; - } - - JS_ALWAYS_INLINE - JSExtensibleString &asExtensible() const { - JS_ASSERT(isExtensible()); - return *(JSExtensibleString *)this; - } - -#ifdef DEBUG - bool isShort() const; - bool isFixed() const; -#endif - - JS_ALWAYS_INLINE - JSFixedString &asFixed() { - JS_ASSERT(isFixed()); - return *(JSFixedString *)this; - } - - bool isExternal() const; - - JS_ALWAYS_INLINE - JSExternalString &asExternal() { - JS_ASSERT(isExternal()); - return *(JSExternalString *)this; - } - - JS_ALWAYS_INLINE - bool isAtom() const { - bool atomized = (d.lengthAndFlags & ATOM_MASK) == ATOM_FLAGS; - JS_ASSERT_IF(atomized, isFlat()); - return atomized; - } - - JS_ALWAYS_INLINE - JSAtom &asAtom() const { - JS_ASSERT(isAtom()); - return *(JSAtom *)this; - } - - JS_ALWAYS_INLINE - bool isStaticAtom() const { - return (d.lengthAndFlags & FLAGS_MASK) == STATIC_ATOM_FLAGS; - } - - /* Only called by the GC for strings with the FINALIZE_STRING kind. */ - - inline void finalize(JSContext *cx); - - /* Called during GC for any string. */ - - void mark(JSTracer *trc); - - /* Offsets for direct field from jit code. */ - - static size_t offsetOfLengthAndFlags() { - return offsetof(JSString, d.lengthAndFlags); - } - - static size_t offsetOfChars() { - return offsetof(JSString, d.u1.chars); - } -}; - -class JSRope : public JSString -{ - friend class JSString; - JSFlatString *flatten(JSContext *cx); - - void init(JSString *left, JSString *right, size_t length); - - public: - static inline JSRope *new_(JSContext *cx, JSString *left, - JSString *right, size_t length); - - inline JSString *leftChild() const { - JS_ASSERT(isRope()); - return d.u1.left; - } - - inline JSString *rightChild() const { - JS_ASSERT(isRope()); - return d.s.u2.right; - } -}; - -JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString)); - -class JSLinearString : public JSString -{ - friend class JSString; - void mark(JSTracer *trc); - - public: - JS_ALWAYS_INLINE - const jschar *chars() const { - JS_ASSERT(isLinear()); - return d.u1.chars; - } -}; - -JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString)); - -class JSDependentString : public JSLinearString -{ - friend class JSString; - JSFixedString *undepend(JSContext *cx); - - void init(JSLinearString *base, const jschar *chars, size_t length); - - public: - static inline JSDependentString *new_(JSContext *cx, JSLinearString *base, - const jschar *chars, size_t length); - - JSLinearString *base() const { - JS_ASSERT(isDependent()); - return d.s.u2.base; - } -}; - -JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString)); - -class JSFlatString : public JSLinearString -{ - friend class JSRope; - void morphExtensibleIntoDependent(JSLinearString *base) { - d.lengthAndFlags = buildLengthAndFlags(length(), DEPENDENT_BIT); - d.s.u2.base = base; - } - - public: - JS_ALWAYS_INLINE - const jschar *charsZ() const { - JS_ASSERT(isFlat()); - return chars(); - } - - /* Only called by the GC for strings with the FINALIZE_STRING kind. */ - - inline void finalize(JSRuntime *rt); -}; - -JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString)); - -class JSExtensibleString : public JSFlatString -{ - public: - JS_ALWAYS_INLINE - size_t capacity() const { - JS_ASSERT(isExtensible()); - return d.s.u2.capacity; - } -}; - -JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString)); - -class JSFixedString : public JSFlatString -{ - void init(const jschar *chars, size_t length); - - public: - static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length); - - /* - * Once a JSFixedString has been added to the atom table, this operation - * changes the type (in place) of the JSFixedString into a JSAtom. - */ - inline JSAtom *morphInternedStringIntoAtom(); -}; - -JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString)); - -class JSInlineString : public JSFixedString -{ - static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1; - - public: - static inline JSInlineString *new_(JSContext *cx); - - inline jschar *init(size_t length); - - inline void resetLength(size_t length); - - static bool lengthFits(size_t length) { - return length <= MAX_INLINE_LENGTH; - } - -}; - -JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString)); - -class JSShortString : public JSInlineString -{ - /* This can be any value that is a multiple of sizeof(gc::FreeCell). */ - static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar); - - static void staticAsserts() { - JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % sizeof(js::gc::FreeCell) == 0); - JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 == - (sizeof(JSShortString) - - offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar)); - } - - jschar inlineStorageExtension[INLINE_EXTENSION_CHARS]; - - public: - static inline JSShortString *new_(JSContext *cx); - - jschar *inlineStorageBeforeInit() { - return d.inlineStorage; - } - - inline void initAtOffsetInBuffer(const jschar *chars, size_t length); - - static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS + - INLINE_EXTENSION_CHARS - -1 /* null terminator */; - - static bool lengthFits(size_t length) { - return length <= MAX_SHORT_LENGTH; - } - - /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */ - - JS_ALWAYS_INLINE void finalize(JSContext *cx); -}; - -JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString)); - -/* - * The externalClosure stored in an external string is a black box to the JS - * engine; see JS_NewExternalStringWithClosure. - */ -class JSExternalString : public JSFixedString -{ - static void staticAsserts() { - JS_STATIC_ASSERT(TYPE_LIMIT == 8); - } - - void init(const jschar *chars, size_t length, intN type, void *closure); - - public: - static inline JSExternalString *new_(JSContext *cx, const jschar *chars, - size_t length, intN type, void *closure); - - intN externalType() const { - JS_ASSERT(isExternal()); - JS_ASSERT(d.s.u2.externalType < TYPE_LIMIT); - return d.s.u2.externalType; - } - - void *externalClosure() const { - JS_ASSERT(isExternal()); - return d.s.u3.externalClosure; - } - - static const uintN TYPE_LIMIT = 8; - static JSStringFinalizeOp str_finalizers[TYPE_LIMIT]; - - static intN changeFinalizer(JSStringFinalizeOp oldop, - JSStringFinalizeOp newop) { - for (uintN i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) { - if (str_finalizers[i] == oldop) { - str_finalizers[i] = newop; - return intN(i); - } - } - return -1; - } - - /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */ - - void finalize(JSContext *cx); - void finalize(); -}; - -JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString)); - -class JSAtom : public JSFixedString -{ - public: - /* Exposed only for jits. */ - - static const size_t UNIT_STATIC_LIMIT = 256U; - static const size_t SMALL_CHAR_LIMIT = 128U; /* Bigger chars cannot be in a length-2 string. */ - static const size_t NUM_SMALL_CHARS = 64U; - static const size_t INT_STATIC_LIMIT = 256U; - static const size_t NUM_HUNDRED_STATICS = 156U; - -#ifdef __SUNPRO_CC -# pragma align 8 (__1cGJSAtomPunitStaticTable_, __1cGJSAtomSlength2StaticTable_, __1cGJSAtomShundredStaticTable_) -#endif - static const JSString::Data unitStaticTable[]; - static const JSString::Data length2StaticTable[]; - static const JSString::Data hundredStaticTable[]; - static const JSString::Data *const intStaticTable[]; - - private: - /* Defined in jsgcinlines.h */ - static inline bool isUnitString(const void *ptr); - static inline bool isLength2String(const void *ptr); - static inline bool isHundredString(const void *ptr); - - typedef uint8 SmallChar; - static const SmallChar INVALID_SMALL_CHAR = -1; - - static inline bool fitsInSmallChar(jschar c); - - static const jschar fromSmallChar[]; - static const SmallChar toSmallChar[]; - - static void staticAsserts() { - JS_STATIC_ASSERT(sizeof(JSString::Data) == sizeof(JSString)); - } - - static JSStaticAtom &length2Static(jschar c1, jschar c2); - static JSStaticAtom &length2Static(uint32 i); - - public: - /* - * While this query can be used for any pointer to GC thing, given a - * JSString 'str', it is more efficient to use 'str->isStaticAtom()'. - */ - static inline bool isStatic(const void *ptr); - - static inline bool hasIntStatic(int32 i); - static inline JSStaticAtom &intStatic(jsint i); - - static inline bool hasUnitStatic(jschar c); - static JSStaticAtom &unitStatic(jschar c); - - /* May not return atom, returns null on (reported) failure. */ - static inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index); - - /* Return null if no static atom exists for the given (chars, length). */ - static inline JSStaticAtom *lookupStatic(const jschar *chars, size_t length); - - inline void finalize(JSRuntime *rt); -}; - -JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString)); - -class JSInlineAtom : public JSInlineString /*, JSAtom */ -{ - /* - * JSInlineAtom is not explicitly used and is only present for consistency. - * See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms. - */ -}; - -JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString)); - -class JSShortAtom : public JSShortString /*, JSInlineAtom */ -{ - /* - * JSShortAtom is not explicitly used and is only present for consistency. - * See Atomize() for how JSShortStrings get morphed into JSShortAtoms. - */ -}; - -JS_STATIC_ASSERT(sizeof(JSShortAtom) == sizeof(JSShortString)); - -class JSStaticAtom : public JSAtom -{}; - -JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString)); - -/* Avoid requring jsstrinlines.h just to call getChars. */ - -JS_ALWAYS_INLINE const jschar * -JSString::getChars(JSContext *cx) -{ - if (JSLinearString *str = ensureLinear(cx)) - return str->chars(); - return NULL; -} - -JS_ALWAYS_INLINE const jschar * -JSString::getCharsZ(JSContext *cx) -{ - if (JSFlatString *str = ensureFlat(cx)) - return str->chars(); - return NULL; -} - -JS_ALWAYS_INLINE JSLinearString * -JSString::ensureLinear(JSContext *cx) -{ - return isLinear() - ? &asLinear() - : asRope().flatten(cx); -} - -JS_ALWAYS_INLINE JSFlatString * -JSString::ensureFlat(JSContext *cx) -{ - return isFlat() - ? &asFlat() - : isDependent() - ? asDependent().undepend(cx) - : asRope().flatten(cx); -} - -JS_ALWAYS_INLINE JSFixedString * -JSString::ensureFixed(JSContext *cx) -{ - if (!ensureFlat(cx)) - return NULL; - if (isExtensible()) { - JS_ASSERT((d.lengthAndFlags & FLAT_MASK) == 0); - JS_STATIC_ASSERT(EXTENSIBLE_FLAGS == (JS_BIT(2) | JS_BIT(3))); - JS_STATIC_ASSERT(FIXED_FLAGS == JS_BIT(2)); - d.lengthAndFlags ^= JS_BIT(3); - } - return &asFixed(); -} +#include "js/HashTable.h" +#include "vm/Unicode.h" namespace js { @@ -778,193 +90,17 @@ struct JSSubString { extern jschar js_empty_ucstr[]; extern JSSubString js_EmptySubString; -/* Unicode character attribute lookup tables. */ -extern const uint8 js_X[]; -extern const uint8 js_Y[]; -extern const uint32 js_A[]; - -/* Enumerated Unicode general category types. */ -typedef enum JSCharType { - JSCT_UNASSIGNED = 0, - JSCT_UPPERCASE_LETTER = 1, - JSCT_LOWERCASE_LETTER = 2, - JSCT_TITLECASE_LETTER = 3, - JSCT_MODIFIER_LETTER = 4, - JSCT_OTHER_LETTER = 5, - JSCT_NON_SPACING_MARK = 6, - JSCT_ENCLOSING_MARK = 7, - JSCT_COMBINING_SPACING_MARK = 8, - JSCT_DECIMAL_DIGIT_NUMBER = 9, - JSCT_LETTER_NUMBER = 10, - JSCT_OTHER_NUMBER = 11, - JSCT_SPACE_SEPARATOR = 12, - JSCT_LINE_SEPARATOR = 13, - JSCT_PARAGRAPH_SEPARATOR = 14, - JSCT_CONTROL = 15, - JSCT_FORMAT = 16, - JSCT_PRIVATE_USE = 18, - JSCT_SURROGATE = 19, - JSCT_DASH_PUNCTUATION = 20, - JSCT_START_PUNCTUATION = 21, - JSCT_END_PUNCTUATION = 22, - JSCT_CONNECTOR_PUNCTUATION = 23, - JSCT_OTHER_PUNCTUATION = 24, - JSCT_MATH_SYMBOL = 25, - JSCT_CURRENCY_SYMBOL = 26, - JSCT_MODIFIER_SYMBOL = 27, - JSCT_OTHER_SYMBOL = 28 -} JSCharType; - -/* Character classifying and mapping macros, based on java.lang.Character. */ -#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) -#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) - -#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER)) \ - >> JS_CTYPE(c)) & 1) - -#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* A unicode letter, suitable for use in an identifier. */ -#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* - * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or - * digit or connector punctuation. - */ -#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER) | \ - (1 << JSCT_NON_SPACING_MARK) | \ - (1 << JSCT_COMBINING_SPACING_MARK) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ - (1 << JSCT_CONNECTOR_PUNCTUATION)) \ - >> JS_CTYPE(c)) & 1) - -/* Unicode control-format characters, ignored in input */ -#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) - -/* - * This table is used in JS_ISWORD. The definition has external linkage to - * allow the raw table data to be used in the regular expression compiler. - */ -extern const bool js_alnum[]; - -/* - * This macro performs testing for the regular expression word class \w, which - * is defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z]. If we want a - * Unicode-friendlier definition of "word", we should rename this macro to - * something regexp-y. - */ -#define JS_ISWORD(c) ((c) < 128 && js_alnum[(c)]) - -extern const bool js_isidstart[]; -extern const bool js_isident[]; - -static inline bool -JS_ISIDSTART(int c) -{ - unsigned w = c; - - return (w < 128) ? js_isidstart[w] : JS_ISLETTER(c); -} - -static inline bool -JS_ISIDENT(int c) -{ - unsigned w = c; - - return (w < 128) ? js_isident[w] : JS_ISIDPART(c); -} - -#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ - (c) == '\n') -#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') -#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ - (c) == '-' || (c) == '_') -#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') -#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') - -#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) - -const jschar BYTE_ORDER_MARK = 0xFEFF; -const jschar NO_BREAK_SPACE = 0x00A0; - -extern const bool js_isspace[]; - -static inline bool -JS_ISSPACE(int c) -{ - unsigned w = c; - - return (w < 128) - ? js_isspace[w] - : w == NO_BREAK_SPACE || w == BYTE_ORDER_MARK || - (JS_CCODE(w) & 0x00070000) == 0x00040000; -} - -static inline bool -JS_ISSPACE_OR_BOM(int c) -{ - unsigned w = c; - - /* Treat little- and big-endian BOMs as whitespace for compatibility. */ - return (w < 128) - ? js_isspace[w] - : w == NO_BREAK_SPACE || w == BYTE_ORDER_MARK || - (JS_CCODE(w) & 0x00070000) == 0x00040000 || w == 0xfffe || w == 0xfeff; -} - -#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) - -#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) -#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) - -#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ - ? (c) - ((int32)JS_CCODE(c) >> 22) \ - : (c))) -#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ - ? (c) + ((int32)JS_CCODE(c) >> 22) \ - : (c))) - /* * Shorthands for ASCII (7-bit) decimal and hex conversion. * Manually inline isdigit for performance; MSVC doesn't do this for us. */ #define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) -#define JS7_ISDECNZ(c) ((((unsigned)(c)) - '1') <= 8) #define JS7_UNDEC(c) ((c) - '0') #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) #define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') #define JS7_ISLET(c) ((c) < 128 && isalpha(c)) /* Initialize the String class, returning its prototype object. */ -extern js::Class js_StringClass; - -inline bool -JSObject::isString() const -{ - return getClass() == &js_StringClass; -} - extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); @@ -1004,26 +140,26 @@ extern const char * js_ValueToPrintable(JSContext *cx, const js::Value &, JSAutoByteString *bytes, bool asSource = false); +namespace js { + /* - * Convert a value to a string, returning null after reporting an error, - * otherwise returning a new string reference. + * Convert a non-string value to a string, returning null after reporting an + * error, otherwise returning a new string reference. */ extern JSString * -js_ValueToString(JSContext *cx, const js::Value &v); - -namespace js { +ToStringSlow(JSContext *cx, const Value &v); /* - * Most code that calls js_ValueToString knows the value is (probably) not a - * string, so it does not make sense to put this inline fast path into - * js_ValueToString. + * Convert the given value to a string. This method includes an inline + * fast-path for the case where the value is already a string; if the value is + * known not to be a string, use ToStringSlow instead. */ static JS_ALWAYS_INLINE JSString * -ValueToString_TestForStringInline(JSContext *cx, const Value &v) +ToString(JSContext *cx, const js::Value &v) { if (v.isString()) return v.toString(); - return js_ValueToString(cx, v); + return ToStringSlow(cx, v); } /* @@ -1045,25 +181,16 @@ js_ValueToSource(JSContext *cx, const js::Value &v); namespace js { -/* - * Compute a hash function from str. The caller can call this function even if - * str is not a GC-allocated thing. - */ -inline uint32 -HashChars(const jschar *chars, size_t length) -{ - uint32 h = 0; - for (; length; chars++, length--) - h = JS_ROTATE_LEFT32(h, 4) ^ *chars; - return h; -} - /* * Test if strings are equal. The caller can call the function even if str1 * or str2 are not GC-allocated things. */ extern bool -EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result); +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result); + +/* Use the infallible method instead! */ +extern bool +EqualStrings(JSContext *cx, JSLinearString *str1, JSLinearString *str2, bool *result) MOZ_DELETE; /* EqualStrings is infallible on linear strings. */ extern bool @@ -1074,7 +201,7 @@ EqualStrings(JSLinearString *str1, JSLinearString *str2); * str1 is less than, equal to, or greater than str2. */ extern bool -CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result); /* * Return true if the string matches the given sequence of ASCII bytes. @@ -1082,7 +209,7 @@ CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); extern bool StringEqualsAscii(JSLinearString *str, const char *asciiBytes); -} /* namespacejs */ +} /* namespace js */ extern size_t js_strlen(const jschar *s); @@ -1093,74 +220,51 @@ js_strchr(const jschar *s, jschar c); extern jschar * js_strchr_limit(const jschar *s, jschar c, const jschar *limit); -#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) - -/* - * Return s advanced past any Unicode white space characters. - */ -static inline const jschar * -js_SkipWhiteSpace(const jschar *s, const jschar *end) +static JS_ALWAYS_INLINE void +js_strncpy(jschar *dst, const jschar *src, size_t nelem) { - JS_ASSERT(s <= end); - while (s != end && JS_ISSPACE(*s)) - s++; - return s; + return js::PodCopy(dst, src, nelem); } -/* - * Some string functions have an optional bool useCESU8 argument. - * CESU-8 (Compatibility Encoding Scheme for UTF-16: 8-bit) is a - * variant of UTF-8 that allows us to store any wide character - * string as a narrow character string. For strings containing - * mostly ascii, it saves space. - * http://www.unicode.org/reports/tr26/ - */ +namespace js { /* - * Inflate bytes to JS chars and vice versa. Report out of memory via cx and - * return null on error, otherwise return the jschar or byte vector that was - * JS_malloc'ed. length is updated to the length of the new string in jschars. - * Using useCESU8 = true treats 'bytes' as CESU-8. + * Inflate bytes to jschars. Return null on error, otherwise return the jschar + * or byte vector that was malloc'ed. length is updated to the length of the + * new string (in jschars). */ extern jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *length, bool useCESU8 = false); +InflateString(JSContext *cx, const char *bytes, size_t *length, + FlationCoding fc = NormalEncoding); extern char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length); +DeflateString(JSContext *cx, const jschar *chars, size_t length); /* - * Inflate bytes to JS chars into a buffer. 'chars' must be large enough for - * 'length' jschars. The buffer is NOT null-terminated. The destination length - * must be be initialized with the buffer size and will contain on return the - * number of copied chars. Conversion behavior depends on js_CStringsAreUTF8. + * Inflate bytes to JS chars in an existing buffer. 'chars' must be large + * enough for 'length' jschars. The buffer is NOT null-terminated. + * + * charsLength must be be initialized with the destination buffer size and, on + * return, will contain on return the number of copied chars. */ -extern JSBool -js_InflateStringToBuffer(JSContext *cx, const char *bytes, size_t length, - jschar *chars, size_t *charsLength); +extern bool +InflateStringToBuffer(JSContext *cx, const char *bytes, size_t length, + jschar *chars, size_t *charsLength); -/* - * Same as js_InflateStringToBuffer, but treats 'bytes' as UTF-8 or CESU-8. - */ -extern JSBool -js_InflateUTF8StringToBuffer(JSContext *cx, const char *bytes, size_t length, - jschar *chars, size_t *charsLength, - bool useCESU8 = false); +extern bool +InflateUTF8StringToBuffer(JSContext *cx, const char *bytes, size_t length, + jschar *chars, size_t *charsLength, + FlationCoding fc = NormalEncoding); -/* - * Get number of bytes in the deflated sequence of characters. Behavior depends - * on js_CStringsAreUTF8. - */ +/* Get number of bytes in the deflated sequence of characters. */ extern size_t -js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, - size_t charsLength); +GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t charsLength); -/* - * Same as js_GetDeflatedStringLength, but treats the result as UTF-8 or CESU-8. - * This function will never fail (return -1) in CESU-8 mode. - */ +/* This function will never fail (return -1) in CESU-8 mode. */ extern size_t -js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, - size_t charsLength, bool useCESU8 = false); +GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, + size_t charsLength, + FlationCoding fc = NormalEncoding); /* * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for @@ -1168,30 +272,29 @@ js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, * must to be initialized with the buffer size and will contain on return the * number of copied bytes. Conversion behavior depends on js_CStringsAreUTF8. */ -extern JSBool -js_DeflateStringToBuffer(JSContext *cx, const jschar *chars, - size_t charsLength, char *bytes, size_t *length); +extern bool +DeflateStringToBuffer(JSContext *cx, const jschar *chars, + size_t charsLength, char *bytes, size_t *length); /* - * Same as js_DeflateStringToBuffer, but treats 'bytes' as UTF-8 or CESU-8. + * Same as DeflateStringToBuffer, but treats 'bytes' as UTF-8 or CESU-8. */ -extern JSBool -js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *chars, - size_t charsLength, char *bytes, size_t *length, - bool useCESU8 = false); - -/* Export a few natives and a helper to other files in SpiderMonkey. */ -extern JSBool -js_str_escape(JSContext *cx, uintN argc, js::Value *argv, js::Value *rval); +extern bool +DeflateStringToUTF8Buffer(JSContext *cx, const jschar *chars, + size_t charsLength, char *bytes, size_t *length, + FlationCoding fc = NormalEncoding); /* * The String.prototype.replace fast-native entry point is exported for joined * function optimization in js{interp,tracer}.cpp. */ -namespace js { extern JSBool str_replace(JSContext *cx, uintN argc, js::Value *vp); -} + +extern JSBool +str_fromCharCode(JSContext *cx, uintN argc, Value *vp); + +} /* namespace js */ extern JSBool js_str_toString(JSContext *cx, uintN argc, js::Value *vp); @@ -1207,12 +310,12 @@ js_str_charCodeAt(JSContext *cx, uintN argc, js::Value *vp); * least 6 bytes long. Return the number of UTF-8 bytes of data written. */ extern int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); +js_OneUcs4ToUtf8Char(uint8_t *utf8Buffer, uint32_t ucs4Char); namespace js { extern size_t -PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSLinearString *str, uint32 quote); +PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSLinearString *str, uint32_t quote); /* * Write str into buffer escaping any non-printable or non-ASCII character @@ -1224,7 +327,7 @@ PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSLinearString *str, u * be a single or double quote character that will quote the output. */ inline size_t -PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32 quote) +PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32_t quote) { size_t n = PutEscapedStringImpl(buffer, size, NULL, str, quote); @@ -1239,11 +342,20 @@ PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32 quote) * will quote the output. */ inline bool -FileEscapedString(FILE *fp, JSLinearString *str, uint32 quote) +FileEscapedString(FILE *fp, JSLinearString *str, uint32_t quote) { return PutEscapedStringImpl(NULL, 0, fp, str, quote) != size_t(-1); } +JSBool +str_match(JSContext *cx, uintN argc, Value *vp); + +JSBool +str_search(JSContext *cx, uintN argc, Value *vp); + +JSBool +str_split(JSContext *cx, uintN argc, Value *vp); + } /* namespace js */ extern JSBool diff --git a/deps/mozjs/js/src/jsstrinlines.h b/deps/mozjs/js/src/jsstrinlines.h index 6bd691ac7db..244e291dd6b 100644 --- a/deps/mozjs/js/src/jsstrinlines.h +++ b/deps/mozjs/js/src/jsstrinlines.h @@ -40,46 +40,37 @@ #ifndef jsstrinlines_h___ #define jsstrinlines_h___ +#include "mozilla/Attributes.h" + #include "jsatom.h" #include "jsstr.h" #include "jscntxtinlines.h" #include "jsgcinlines.h" +#include "vm/String-inl.h" namespace js { -static inline bool -CheckStringLength(JSContext *cx, size_t length) -{ - if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) { - if (JS_ON_TRACE(cx)) { - /* - * If we can't leave the trace, signal OOM condition, otherwise - * exit from trace before throwing. - */ - if (!CanLeaveTrace(cx)) - return NULL; - - LeaveTrace(cx); - } - js_ReportAllocationOverflow(cx); - return false; - } - - return true; -} - /* * String builder that eagerly checks for over-allocation past the maximum * string length. * + * Any operation which would exceed the maximum string length causes an + * exception report on the context and results in a failed return value. + * + * Well-sized extractions (which waste no more than 1/4 of their char + * buffer space) are guaranteed for strings built by this interface. + * See |extractWellSized|. + * * Note: over-allocation is not checked for when using the infallible * |replaceRawBuffer|, so the implementation of |finishString| also must check * for over-allocation. */ class StringBuffer { - typedef Vector CharBuffer; + /* cb's buffer is taken by the new string so use ContextAllocPolicy. */ + typedef Vector CharBuffer; + CharBuffer cb; static inline bool checkLength(JSContext *cx, size_t length); @@ -87,6 +78,9 @@ class StringBuffer JSContext *context() const { return cb.allocPolicy().context(); } jschar *extractWellSized(); + StringBuffer(const StringBuffer &other) MOZ_DELETE; + void operator=(const StringBuffer &other) MOZ_DELETE; + public: explicit inline StringBuffer(JSContext *cx); bool reserve(size_t len); @@ -95,7 +89,7 @@ class StringBuffer bool append(const jschar *chars, size_t len); bool append(const jschar *begin, const jschar *end); bool append(JSString *str); - bool append(JSAtom *atom); + bool append(JSLinearString *str); bool appendN(const jschar c, size_t n); bool appendInflated(const char *cstr, size_t len); @@ -191,19 +185,14 @@ StringBuffer::append(JSString *str) JSLinearString *linear = str->ensureLinear(context()); if (!linear) return false; - size_t strLen = linear->length(); - if (!checkLength(cb.length() + strLen)) - return false; - return cb.append(linear->chars(), strLen); + return append(linear); } inline bool -StringBuffer::append(JSAtom *atom) +StringBuffer::append(JSLinearString *str) { - size_t strLen = atom->length(); - if (!checkLength(cb.length() + strLen)) - return false; - return cb.append(atom->chars(), strLen); + JS::Anchor anch(str); + return cb.append(str->chars(), str->length()); } inline bool @@ -220,11 +209,9 @@ StringBuffer::appendInflated(const char *cstr, size_t cstrlen) size_t lengthBefore = length(); if (!cb.growByUninitialized(cstrlen)) return false; -#if DEBUG - size_t oldcstrlen = cstrlen; - bool ok = -#endif - js_InflateStringToBuffer(context(), cstr, cstrlen, begin() + lengthBefore, &cstrlen); + DebugOnly oldcstrlen = cstrlen; + DebugOnly ok = InflateStringToBuffer(context(), cstr, cstrlen, + begin() + lengthBefore, &cstrlen); JS_ASSERT(ok && oldcstrlen == cstrlen); return true; } @@ -240,7 +227,7 @@ StringBuffer::length() const inline bool StringBuffer::checkLength(size_t length) { - return CheckStringLength(context(), length); + return JSString::validateLength(context(), length); } extern bool @@ -255,335 +242,13 @@ ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) return ValueToStringBufferSlow(cx, v, sb); } -} /* namespace js */ - -JS_ALWAYS_INLINE void -JSRope::init(JSString *left, JSString *right, size_t length) -{ - d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT); - d.u1.left = left; - d.s.u2.right = right; -} - -JS_ALWAYS_INLINE JSRope * -JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length) -{ - JSRope *str = (JSRope *)js_NewGCString(cx); - if (!str) - return NULL; - str->init(left, right, length); - return str; -} - -JS_ALWAYS_INLINE void -JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length) -{ - d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT); - d.u1.chars = chars; - d.s.u2.base = base; -} - -JS_ALWAYS_INLINE JSDependentString * -JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars, size_t length) -{ - /* Try to avoid long chains of dependent strings. */ - while (base->isDependent()) - base = base->asDependent().base(); - - JS_ASSERT(base->isFlat()); - JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length()); - JS_ASSERT(length <= base->length() - (chars - base->chars())); - - JSDependentString *str = (JSDependentString *)js_NewGCString(cx); - if (!str) - return NULL; - str->init(base, chars, length); -#ifdef DEBUG - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveDependentStrings); - JS_RUNTIME_METER(rt, totalDependentStrings); - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum += (double)length, - rt->strdepLengthSquaredSum += (double)length * (double)length)); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); -#endif - return str; -} - -JS_ALWAYS_INLINE void -JSFixedString::init(const jschar *chars, size_t length) -{ - d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); - d.u1.chars = chars; -} - -JS_ALWAYS_INLINE JSFixedString * -JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length) -{ - JS_ASSERT(length <= MAX_LENGTH); - JS_ASSERT(chars[length] == jschar(0)); - - JSFixedString *str = (JSFixedString *)js_NewGCString(cx); - if (!str) - return NULL; - str->init(chars, length); - - cx->runtime->stringMemoryUsed += length * 2; -#ifdef DEBUG - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); -#endif - return str; -} - -JS_ALWAYS_INLINE JSAtom * -JSFixedString::morphInternedStringIntoAtom() -{ - JS_ASSERT((d.lengthAndFlags & FLAGS_MASK) == JS_BIT(2)); - JS_STATIC_ASSERT(NON_STATIC_ATOM == JS_BIT(3)); - d.lengthAndFlags ^= (JS_BIT(2) | JS_BIT(3)); - return &asAtom(); -} - -JS_ALWAYS_INLINE JSInlineString * -JSInlineString::new_(JSContext *cx) -{ - return (JSInlineString *)js_NewGCString(cx); -} - -JS_ALWAYS_INLINE jschar * -JSInlineString::init(size_t length) -{ - d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); - d.u1.chars = d.inlineStorage; - JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length))); - return d.inlineStorage; -} - -JS_ALWAYS_INLINE void -JSInlineString::resetLength(size_t length) -{ - d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); - JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length))); -} - -JS_ALWAYS_INLINE JSShortString * -JSShortString::new_(JSContext *cx) -{ - return js_NewGCShortString(cx); -} - -JS_ALWAYS_INLINE void -JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length) -{ - JS_ASSERT(lengthFits(length + (chars - d.inlineStorage))); - JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH); - d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); - d.u1.chars = chars; -} - -JS_ALWAYS_INLINE void -JSExternalString::init(const jschar *chars, size_t length, intN type, void *closure) -{ - d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); - d.u1.chars = chars; - d.s.u2.externalType = type; - d.s.u3.externalClosure = closure; -} - -JS_ALWAYS_INLINE JSExternalString * -JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN type, void *closure) -{ - JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT); - JS_ASSERT(chars[length] == 0); - - JSExternalString *str = (JSExternalString *)js_NewGCExternalString(cx, type); - if (!str) - return NULL; - str->init(chars, length, type, closure); - cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar)); - return str; -} - -inline bool -JSAtom::fitsInSmallChar(jschar c) -{ - return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR; -} - -inline bool -JSAtom::hasUnitStatic(jschar c) -{ - return c < UNIT_STATIC_LIMIT; -} - -inline JSStaticAtom & -JSAtom::unitStatic(jschar c) -{ - JS_ASSERT(hasUnitStatic(c)); - return (JSStaticAtom &)unitStaticTable[c]; -} - -inline bool -JSAtom::hasIntStatic(int32 i) -{ - return uint32(i) < INT_STATIC_LIMIT; -} - -inline JSStaticAtom & -JSAtom::intStatic(jsint i) -{ - JS_ASSERT(hasIntStatic(i)); - return (JSStaticAtom &)*intStaticTable[i]; -} - -inline JSLinearString * -JSAtom::getUnitStringForElement(JSContext *cx, JSString *str, size_t index) -{ - JS_ASSERT(index < str->length()); - const jschar *chars = str->getChars(cx); - if (!chars) - return NULL; - jschar c = chars[index]; - if (c < UNIT_STATIC_LIMIT) - return &unitStatic(c); - return js_NewDependentString(cx, str, index, 1); -} - -inline JSStaticAtom & -JSAtom::length2Static(jschar c1, jschar c2) -{ - JS_ASSERT(fitsInSmallChar(c1)); - JS_ASSERT(fitsInSmallChar(c2)); - size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]; - return (JSStaticAtom &)length2StaticTable[index]; -} - -inline JSStaticAtom & -JSAtom::length2Static(uint32 i) -{ - JS_ASSERT(i < 100); - return length2Static('0' + i / 10, '0' + i % 10); -} - -/* Get a static atomized string for chars if possible. */ -inline JSStaticAtom * -JSAtom::lookupStatic(const jschar *chars, size_t length) -{ - switch (length) { - case 1: - if (chars[0] < UNIT_STATIC_LIMIT) - return &unitStatic(chars[0]); - return NULL; - case 2: - if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1])) - return &length2Static(chars[0], chars[1]); - return NULL; - case 3: - /* - * Here we know that JSString::intStringTable covers only 256 (or at least - * not 1000 or more) chars. We rely on order here to resolve the unit vs. - * int string/length-2 string atom identity issue by giving priority to unit - * strings for "0" through "9" and length-2 strings for "10" through "99". - */ - JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999); - if ('1' <= chars[0] && chars[0] <= '9' && - '0' <= chars[1] && chars[1] <= '9' && - '0' <= chars[2] && chars[2] <= '9') { - jsint i = (chars[0] - '0') * 100 + - (chars[1] - '0') * 10 + - (chars[2] - '0'); - - if (jsuint(i) < INT_STATIC_LIMIT) - return &intStatic(i); - } - return NULL; - } - - return NULL; -} - -JS_ALWAYS_INLINE void -JSString::finalize(JSContext *cx) -{ - JS_ASSERT(!isStaticAtom() && !isShort()); - - JS_RUNTIME_UNMETER(cx->runtime, liveStrings); - - if (isDependent()) - JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings); - else if (isFlat()) - asFlat().finalize(cx->runtime); - else - JS_ASSERT(isRope()); -} - -inline void -JSFlatString::finalize(JSRuntime *rt) -{ - JS_ASSERT(!isShort()); - rt->stringMemoryUsed -= length() * 2; - - /* - * This check depends on the fact that 'chars' is only initialized to the - * beginning of inlineStorage. E.g., this is not the case for short strings. - */ - if (chars() != d.inlineStorage) - rt->free_(const_cast(chars())); -} - -inline void -JSShortString::finalize(JSContext *cx) -{ - JS_ASSERT(isShort()); - JS_RUNTIME_UNMETER(cx->runtime, liveStrings); -} - -inline void -JSAtom::finalize(JSRuntime *rt) -{ - JS_ASSERT(isAtom()); - if (arenaHeader()->getThingKind() == js::gc::FINALIZE_STRING) - asFlat().finalize(rt); - else - JS_ASSERT(arenaHeader()->getThingKind() == js::gc::FINALIZE_SHORT_STRING); -} - -inline void -JSExternalString::finalize(JSContext *cx) -{ - JS_RUNTIME_UNMETER(cx->runtime, liveStrings); - if (JSStringFinalizeOp finalizer = str_finalizers[externalType()]) - finalizer(cx, this); -} - -inline void -JSExternalString::finalize() -{ - JSStringFinalizeOp finalizer = str_finalizers[externalType()]; - if (finalizer) { - /* - * Assume that the finalizer for the permanently interned - * string knows how to deal with null context. - */ - finalizer(NULL, this); - } -} - -namespace js { - class RopeBuilder { JSContext *cx; JSString *res; + RopeBuilder(const RopeBuilder &other) MOZ_DELETE; + void operator=(const RopeBuilder &other) MOZ_DELETE; + public: RopeBuilder(JSContext *cx) : cx(cx), res(cx->runtime->emptyString) @@ -648,6 +313,39 @@ class StringSegmentRange } }; +/* + * Return s advanced past any Unicode white space characters. + */ +static inline const jschar * +SkipSpace(const jschar *s, const jschar *end) +{ + JS_ASSERT(s <= end); + + while (s < end && unicode::IsSpace(*s)) + s++; + + return s; +} + +/* + * Return less than, equal to, or greater than zero depending on whether + * s1 is less than, equal to, or greater than s2. + */ +inline bool +CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result) +{ + size_t n = JS_MIN(l1, l2); + for (size_t i = 0; i < n; i++) { + if (int32_t cmp = s1[i] - s2[i]) { + *result = cmp; + return true; + } + } + + *result = (int32_t)(l1 - l2); + return true; +} + } /* namespace js */ #endif /* jsstrinlines_h___ */ diff --git a/deps/mozjs/js/src/jstl.h b/deps/mozjs/js/src/jstl.h deleted file mode 100644 index 52f1efde67c..00000000000 --- a/deps/mozjs/js/src/jstl.h +++ /dev/null @@ -1,508 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * July 16, 2009. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * - * Contributor(s): - * Luke Wagner - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jstl_h_ -#define jstl_h_ - -#include "jspubtd.h" -#include "jsbit.h" -#include "jsstaticcheck.h" -#include "jsstdint.h" - -#include -#include - -namespace js { - -/* JavaScript Template Library. */ -namespace tl { - -/* Compute min/max/clamp. */ -template struct Min { - static const size_t result = i < j ? i : j; -}; -template struct Max { - static const size_t result = i > j ? i : j; -}; -template struct Clamp { - static const size_t result = i < min ? min : (i > max ? max : i); -}; - -/* Compute x^y. */ -template struct Pow { - static const size_t result = x * Pow::result; -}; -template struct Pow { - static const size_t result = 1; -}; - -/* Compute floor(log2(i)). */ -template struct FloorLog2 { - static const size_t result = 1 + FloorLog2::result; -}; -template <> struct FloorLog2<0> { /* Error */ }; -template <> struct FloorLog2<1> { static const size_t result = 0; }; - -/* Compute ceiling(log2(i)). */ -template struct CeilingLog2 { - static const size_t result = FloorLog2<2 * i - 1>::result; -}; - -/* Round up to the nearest power of 2. */ -template struct RoundUpPow2 { - static const size_t result = 1u << CeilingLog2::result; -}; -template <> struct RoundUpPow2<0> { - static const size_t result = 1; -}; - -/* Compute the number of bits in the given unsigned type. */ -template struct BitSize { - static const size_t result = sizeof(T) * JS_BITS_PER_BYTE; -}; - -/* Allow Assertions by only including the 'result' typedef if 'true'. */ -template struct StaticAssert {}; -template <> struct StaticAssert { typedef int result; }; - -/* Boolean test for whether two types are the same. */ -template struct IsSameType { - static const bool result = false; -}; -template struct IsSameType { - static const bool result = true; -}; - -/* - * Produce an N-bit mask, where N <= BitSize::result. Handle the - * language-undefined edge case when N = BitSize::result. - */ -template struct NBitMask { - typedef typename StaticAssert::result>::result _; - static const size_t result = (size_t(1) << N) - 1; -}; -template <> struct NBitMask::result> { - static const size_t result = size_t(-1); -}; - -/* - * For the unsigned integral type size_t, compute a mask M for N such that - * for all X, !(X & M) implies X * N will not overflow (w.r.t size_t) - */ -template struct MulOverflowMask { - static const size_t result = - ~NBitMask::result - CeilingLog2::result>::result; -}; -template <> struct MulOverflowMask<0> { /* Error */ }; -template <> struct MulOverflowMask<1> { static const size_t result = 0; }; - -/* - * Generate a mask for T such that if (X & sUnsafeRangeSizeMask), an X-sized - * array of T's is big enough to cause a ptrdiff_t overflow when subtracting - * a pointer to the end of the array from the beginning. - */ -template struct UnsafeRangeSizeMask { - /* - * The '2' factor means the top bit is clear, sizeof(T) converts from - * units of elements to bytes. - */ - static const size_t result = MulOverflowMask<2 * sizeof(T)>::result; -}; - -/* Return T stripped of any const-ness. */ -template struct StripConst { typedef T result; }; -template struct StripConst { typedef T result; }; - -/* - * Traits class for identifying POD types. Until C++0x, there is no automatic - * way to detect PODs, so for the moment it is done manually. - */ -template struct IsPodType { static const bool result = false; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; - -/* Return the size/end of an array without using macros. */ -template inline T *ArraySize(T (&)[N]) { return N; } -template inline T *ArrayEnd(T (&arr)[N]) { return arr + N; } - -template struct If { static const T result = v1; }; -template struct If { static const T result = v2; }; - -} /* namespace tl */ - -/* Useful for implementing containers that assert non-reentrancy */ -class ReentrancyGuard -{ - /* ReentrancyGuard is not copyable. */ - ReentrancyGuard(const ReentrancyGuard &); - void operator=(const ReentrancyGuard &); - -#ifdef DEBUG - bool &entered; -#endif - public: - template -#ifdef DEBUG - ReentrancyGuard(T &obj) - : entered(obj.entered) -#else - ReentrancyGuard(T &/*obj*/) -#endif - { -#ifdef DEBUG - JS_ASSERT(!entered); - entered = true; -#endif - } - ~ReentrancyGuard() - { -#ifdef DEBUG - entered = false; -#endif - } -}; - -/* - * Round x up to the nearest power of 2. This function assumes that the most - * significant bit of x is not set, which would lead to overflow. - */ -STATIC_POSTCONDITION_ASSUME(return >= x) -JS_ALWAYS_INLINE size_t -RoundUpPow2(size_t x) -{ - size_t log2 = JS_CEILING_LOG2W(x); - JS_ASSERT(log2 < tl::BitSize::result); - size_t result = size_t(1) << log2; - return result; -} - -/* - * Safely subtract two pointers when it is known that end > begin. This avoids - * the common compiler bug that if (size_t(end) - size_t(begin)) has the MSB - * set, the unsigned subtraction followed by right shift will produce -1, or - * size_t(-1), instead of the real difference. - */ -template -JS_ALWAYS_INLINE size_t -PointerRangeSize(T *begin, T *end) -{ - return (size_t(end) - size_t(begin)) / sizeof(T); -} - -template -class AlignedPtrAndFlag -{ - uintptr_t bits; - - public: - AlignedPtrAndFlag(T *t, bool flag) { - JS_ASSERT((uintptr_t(t) & 1) == 0); - bits = uintptr_t(t) | uintptr_t(flag); - } - - T *ptr() const { - return (T *)(bits & ~uintptr_t(1)); - } - - bool flag() const { - return (bits & 1) != 0; - } - - void setPtr(T *t) { - JS_ASSERT((uintptr_t(t) & 1) == 0); - bits = uintptr_t(t) | uintptr_t(flag()); - } - - void setFlag() { - bits |= 1; - } - - void unsetFlag() { - bits &= ~uintptr_t(1); - } - - void set(T *t, bool flag) { - JS_ASSERT((uintptr_t(t) & 1) == 0); - bits = uintptr_t(t) | flag; - } -}; - -template -static inline void -Reverse(T *beg, T *end) -{ - while (beg != end) { - if (--end == beg) - return; - T tmp = *beg; - *beg = *end; - *end = tmp; - ++beg; - } -} - -template -static inline T * -Find(T *beg, T *end, const T &v) -{ - for (T *p = beg; p != end; ++p) { - if (*p == v) - return p; - } - return end; -} - -template -static inline typename Container::ElementType * -Find(Container &c, const typename Container::ElementType &v) -{ - return Find(c.begin(), c.end(), v); -} - -template -void -ForEach(InputIterT begin, InputIterT end, CallableT f) -{ - for (; begin != end; ++begin) - f(*begin); -} - -template -static inline T -Min(T t1, T t2) -{ - return t1 < t2 ? t1 : t2; -} - -template -static inline T -Max(T t1, T t2) -{ - return t1 > t2 ? t1 : t2; -} - -/* Allows a const variable to be initialized after its declaration. */ -template -static T& -InitConst(const T &t) -{ - return const_cast(t); -} - -/* Smart pointer, restricted to a range defined at construction. */ -template -class RangeCheckedPointer -{ - T *ptr; - -#ifdef DEBUG - T * const rangeStart; - T * const rangeEnd; -#endif - - void sanityChecks() { - JS_ASSERT(rangeStart <= ptr); - JS_ASSERT(ptr <= rangeEnd); - } - - /* Creates a new pointer for |ptr|, restricted to this pointer's range. */ - RangeCheckedPointer create(T *ptr) const { -#ifdef DEBUG - return RangeCheckedPointer(ptr, rangeStart, rangeEnd); -#else - return RangeCheckedPointer(ptr, NULL, size_t(0)); -#endif - } - - public: - RangeCheckedPointer(T *p, T *start, T *end) - : ptr(p) -#ifdef DEBUG - , rangeStart(start), rangeEnd(end) -#endif - { - JS_ASSERT(rangeStart <= rangeEnd); - sanityChecks(); - } - RangeCheckedPointer(T *p, T *start, size_t length) - : ptr(p) -#ifdef DEBUG - , rangeStart(start), rangeEnd(start + length) -#endif - { - JS_ASSERT(length <= size_t(-1) / sizeof(T)); - JS_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart)); - sanityChecks(); - } - - RangeCheckedPointer &operator=(const RangeCheckedPointer &other) { - JS_ASSERT(rangeStart == other.rangeStart); - JS_ASSERT(rangeEnd == other.rangeEnd); - ptr = other.ptr; - sanityChecks(); - return *this; - } - - RangeCheckedPointer operator+(size_t inc) { - JS_ASSERT(inc <= size_t(-1) / sizeof(T)); - JS_ASSERT(ptr + inc > ptr); - return create(ptr + inc); - } - - RangeCheckedPointer operator-(size_t dec) { - JS_ASSERT(dec <= size_t(-1) / sizeof(T)); - JS_ASSERT(ptr - dec < ptr); - return create(ptr - dec); - } - - template - RangeCheckedPointer &operator=(U *p) { - *this = create(p); - return *this; - } - - template - RangeCheckedPointer &operator=(const RangeCheckedPointer &p) { - JS_ASSERT(rangeStart <= p.ptr); - JS_ASSERT(p.ptr <= rangeEnd); - ptr = p.ptr; - sanityChecks(); - return *this; - } - - RangeCheckedPointer &operator++() { - return (*this += 1); - } - - RangeCheckedPointer operator++(int) { - RangeCheckedPointer rcp = *this; - ++*this; - return rcp; - } - - RangeCheckedPointer &operator--() { - return (*this -= 1); - } - - RangeCheckedPointer operator--(int) { - RangeCheckedPointer rcp = *this; - --*this; - return rcp; - } - - RangeCheckedPointer &operator+=(size_t inc) { - this->operator=(*this + inc); - return *this; - } - - RangeCheckedPointer &operator-=(size_t dec) { - this->operator=(*this - dec); - return *this; - } - - T &operator[](int index) const { - JS_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T)); - return *create(ptr + index); - } - - T &operator*() const { - return *ptr; - } - - operator T*() const { - return ptr; - } - - template - bool operator==(const RangeCheckedPointer &other) const { - return ptr == other.ptr; - } - template - bool operator!=(const RangeCheckedPointer &other) const { - return !(*this == other); - } - - template - bool operator<(const RangeCheckedPointer &other) const { - return ptr < other.ptr; - } - template - bool operator<=(const RangeCheckedPointer &other) const { - return ptr <= other.ptr; - } - - template - bool operator>(const RangeCheckedPointer &other) const { - return ptr > other.ptr; - } - template - bool operator>=(const RangeCheckedPointer &other) const { - return ptr >= other.ptr; - } - - size_t operator-(const RangeCheckedPointer &other) const { - JS_ASSERT(ptr >= other.ptr); - return PointerRangeSize(other.ptr, ptr); - } - - private: - RangeCheckedPointer(); - T *operator&(); -}; - -template -JS_ALWAYS_INLINE T & -ImplicitCast(U &u) -{ - T &t = u; - return t; -} - -} /* namespace js */ - -#endif /* jstl_h_ */ diff --git a/deps/mozjs/js/src/jstracer.cpp b/deps/mozjs/js/src/jstracer.cpp deleted file mode 100644 index 0567f51d05b..00000000000 --- a/deps/mozjs/js/src/jstracer.cpp +++ /dev/null @@ -1,17438 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * May 28, 2008. - * - * The Initial Developer of the Original Code is - * Brendan Eich - * - * Contributor(s): - * Andreas Gal - * Mike Shaver - * David Anderson - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstdint.h" -#include "jsbit.h" // low-level (NSPR-based) headers next -#include "jsprf.h" -#include // standard headers next - -#if defined(_MSC_VER) || defined(__MINGW32__) -#include -#ifdef _MSC_VER -#define alloca _alloca -#endif -#endif -#ifdef SOLARIS -#include -#endif -#include - -#include "nanojit/nanojit.h" -#include "jsapi.h" // higher-level library and API headers -#include "jsarray.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jscompartment.h" -#include "jsdate.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsgcmark.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jsmath.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstaticcheck.h" -#include "jstl.h" -#include "jstracer.h" -#include "jsxml.h" -#include "jstypedarray.h" - -#include "jsatominlines.h" -#include "jscntxtinlines.h" -#include "jsfuninlines.h" -#include "jsinterpinlines.h" -#include "jspropertycacheinlines.h" -#include "jsobjinlines.h" -#include "jsscopeinlines.h" -#include "jsscriptinlines.h" -#include "jscntxtinlines.h" -#include "jsopcodeinlines.h" - -#include "vm/Stack-inl.h" - -#ifdef JS_METHODJIT -#include "methodjit/MethodJIT.h" -#endif - -#include "jsautooplen.h" // generated headers last -#include "imacros.c.out" - -#if defined(NANOJIT_ARM) && defined(__GNUC__) && defined(AVMPLUS_LINUX) -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef DEBUG -namespace js { -static const char* -getExitName(ExitType type) -{ - static const char* exitNames[] = - { - #define MAKE_EXIT_STRING(x) #x, - JS_TM_EXITCODES(MAKE_EXIT_STRING) - #undef MAKE_EXIT_STRING - NULL - }; - - JS_ASSERT(type < TOTAL_EXIT_TYPES); - - return exitNames[type]; -} -} -#endif /* DEBUG */ - -namespace nanojit { -using namespace js; -using namespace js::gc; -using namespace js::tjit; - -/* - * This macro is just like JS_NOT_REACHED but it exists in non-debug builds - * too. Its presence indicates shortcomings in jstracer's handling of some - * OOM situations: - * - OOM failures in constructors, which lack a return value to pass back a - * failure code (though it can and should be done indirectly). - * - OOM failures in the "infallible" allocators used for Nanojit. - * - * FIXME: bug 624590 is open to fix these problems. - */ -#define OUT_OF_MEMORY_ABORT(msg) JS_Assert(msg, __FILE__, __LINE__); - -/* Implement embedder-specific nanojit members. */ - -/* - * Nanojit requires infallible allocations most of the time. We satisfy this by - * reserving some space in each allocator which is used as a fallback if - * rt->calloc_() fails. Ideally this reserve space should be big enough to allow - * for all infallible requests made to the allocator until the next OOM check - * occurs, but it turns out that's impossible to guarantee (though it should be - * unlikely). So we abort if the reserve runs out; this is better than - * allowing memory errors to occur. - * - * The space calculations are as follows... between OOM checks, each - * VMAllocator can do (ie. has been seen to do) the following maximum - * allocations on 64-bits: - * - * - dataAlloc: 31 minimum-sized chunks (MIN_CHUNK_SZB) in assm->compile() - * (though arbitrarily more could occur due to LabelStateMap additions done - * when handling labels): 62,248 bytes. This one is the most likely to - * overflow. - * - * - traceAlloc: 1 minimum-sized chunk: 2,008 bytes. - * - * - tempAlloc: 1 LIR code chunk (CHUNK_SZB) and 5 minimum-sized chunks for - * sundry small allocations: 18,048 bytes. - * - * The reserve sizes are chosen by exceeding this by a reasonable amount. - * Reserves for 32-bits are slightly more than half, because most of the - * allocated space is used to hold pointers. - * - * FIXME: Bug 624590 is open to get rid of all this. - */ -static const size_t DataReserveSize = 12500 * sizeof(uintptr_t); -static const size_t TraceReserveSize = 5000 * sizeof(uintptr_t); -static const size_t TempReserveSize = 1000 * sizeof(uintptr_t); - -void* -nanojit::Allocator::allocChunk(size_t nbytes, bool fallible) -{ - VMAllocator *vma = (VMAllocator*)this; - /* - * Nb: it's conceivable that request 1 might fail (in which case - * mOutOfMemory will be set) and then request 2 succeeds. The subsequent - * OOM check will still fail, which is what we want, and the success of - * request 2 makes it less likely that the reserve space will overflow. - */ - void *p = vma->mRt->calloc_(nbytes); - if (p) { - vma->mSize += nbytes; - } else { - vma->mOutOfMemory = true; - if (!fallible) { - p = (void *)vma->mReserveCurr; - vma->mReserveCurr += nbytes; - if (vma->mReserveCurr > vma->mReserveLimit) - OUT_OF_MEMORY_ABORT("nanojit::Allocator::allocChunk: out of memory"); - memset(p, 0, nbytes); - vma->mSize += nbytes; - } - } - return p; -} - -void -nanojit::Allocator::freeChunk(void *p) { - VMAllocator *vma = (VMAllocator*)this; - if (p < vma->mReserve || uintptr_t(p) >= vma->mReserveLimit) - UnwantedForeground::free_(p); -} - -void -nanojit::Allocator::postReset() { - VMAllocator *vma = (VMAllocator*)this; - vma->mOutOfMemory = false; - vma->mSize = 0; - vma->mReserveCurr = uintptr_t(vma->mReserve); -} - -int -StackFilter::getTop(LIns* guard) -{ - VMSideExit* e = (VMSideExit*)guard->record()->exit; - return e->sp_adj; -} - -#if defined NJ_VERBOSE -static void -formatGuardExit(InsBuf *buf, LIns *ins) -{ - VMSideExit *x = (VMSideExit *)ins->record()->exit; - RefBuf b1; - if (LogController.lcbits & LC_FragProfile) - VMPI_snprintf(b1.buf, b1.len, " (GuardID=%03d)", ins->record()->profGuardID); - else - b1.buf[0] = '\0'; - VMPI_snprintf(buf->buf, buf->len, - " -> exit=%p pc=%p imacpc=%p sp%+ld rp%+ld %s%s", - (void *)x, - (void *)x->pc, - (void *)x->imacpc, - (long int)x->sp_adj, - (long int)x->rp_adj, - getExitName(x->exitType), - b1.buf); -} - -void -LInsPrinter::formatGuard(InsBuf *buf, LIns *ins) -{ - RefBuf b1, b2; - InsBuf b3; - formatGuardExit(&b3, ins); - VMPI_snprintf(buf->buf, buf->len, - "%s: %s %s%s", - formatRef(&b1, ins), - lirNames[ins->opcode()], - ins->oprnd1() ? formatRef(&b2, ins->oprnd1()) : "", - b3.buf); -} - -void -LInsPrinter::formatGuardXov(InsBuf *buf, LIns *ins) -{ - RefBuf b1, b2, b3; - InsBuf b4; - formatGuardExit(&b4, ins); - VMPI_snprintf(buf->buf, buf->len, - "%s = %s %s, %s%s", - formatRef(&b1, ins), - lirNames[ins->opcode()], - formatRef(&b2, ins->oprnd1()), - formatRef(&b3, ins->oprnd2()), - b4.buf); -} - -const char* -nanojit::LInsPrinter::accNames[] = { - "state", // (1 << 0) == ACCSET_STATE - "sp", // (1 << 1) == ACCSET_STACK - "rp", // (1 << 2) == ACCSET_RSTACK - "cx", // (1 << 3) == ACCSET_CX - "tm", // (1 << 4) == ACCSET_TM - "eos", // (1 << 5) == ACCSET_EOS - "alloc", // (1 << 6) == ACCSET_ALLOC - "regs", // (1 << 7) == ACCSET_FRAMEREGS - "sf", // (1 << 8) == ACCSET_STACKFRAME - "rt", // (1 << 9) == ACCSET_RUNTIME - - "objclasp", // (1 << 10) == ACCSET_OBJ_CLASP - "objflags", // (1 << 11) == ACCSET_OBJ_FLAGS - "objshape", // (1 << 12) == ACCSET_OBJ_SHAPE - "objproto", // (1 << 13) == ACCSET_OBJ_PROTO - "objparent", // (1 << 14) == ACCSET_OBJ_PARENT - "objprivate", // (1 << 15) == ACCSET_OBJ_PRIVATE - "objcapacity", // (1 << 16) == ACCSET_OBJ_CAPACITY - "objslots", // (1 << 17) == ACCSET_OBJ_SLOTS - - "slots", // (1 << 18) == ACCSET_SLOTS - "tarray", // (1 << 19) == ACCSET_TARRAY - "tdata", // (1 << 20) == ACCSET_TARRAY_DATA - "iter", // (1 << 21) == ACCSET_ITER - "iterprops", // (1 << 22) == ACCSET_ITER_PROPS - "str", // (1 << 23) == ACCSET_STRING - "strmchars", // (1 << 24) == ACCSET_STRING_MCHARS - "typemap", // (1 << 25) == ACCSET_TYPEMAP - "fcslots", // (1 << 26) == ACCSET_FCSLOTS - "argsdata", // (1 << 27) == ACCSET_ARGS_DATA - - "?!" // this entry should never be used, have it just in case -}; - -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(nanojit::LInsPrinter::accNames) == TM_NUM_USED_ACCS + 1); -#endif - -} /* namespace nanojit */ - -JS_DEFINE_CALLINFO_2(extern, STRING, js_IntToString, CONTEXT, INT32, 1, nanojit::ACCSET_NONE) - -namespace js { - -using namespace nanojit; - -#if JS_HAS_XML_SUPPORT -#define RETURN_VALUE_IF_XML(val, ret) \ - JS_BEGIN_MACRO \ - if (!val.isPrimitive() && val.toObject().isXML()) \ - RETURN_VALUE("xml detected", ret); \ - JS_END_MACRO -#else -#define RETURN_IF_XML(val, ret) ((void) 0) -#endif - -#define RETURN_IF_XML_A(val) RETURN_VALUE_IF_XML(val, ARECORD_STOP) -#define RETURN_IF_XML(val) RETURN_VALUE_IF_XML(val, RECORD_STOP) - -JS_STATIC_ASSERT(sizeof(JSValueType) == 1); -JS_STATIC_ASSERT(offsetof(TraceNativeStorage, stack_global_buf) % 16 == 0); - -/* Map to translate a type tag into a printable representation. */ -#ifdef DEBUG -static char -TypeToChar(JSValueType type) -{ - switch (type) { - case JSVAL_TYPE_DOUBLE: return 'D'; - case JSVAL_TYPE_INT32: return 'I'; - case JSVAL_TYPE_STRING: return 'S'; - case JSVAL_TYPE_OBJECT: return '!'; - case JSVAL_TYPE_BOOLEAN: return 'B'; - case JSVAL_TYPE_NULL: return 'N'; - case JSVAL_TYPE_UNDEFINED: return 'U'; - case JSVAL_TYPE_MAGIC: return 'M'; - case JSVAL_TYPE_FUNOBJ: return 'F'; - case JSVAL_TYPE_NONFUNOBJ: return 'O'; - case JSVAL_TYPE_BOXED: return '#'; - case JSVAL_TYPE_STRORNULL: return 's'; - case JSVAL_TYPE_OBJORNULL: return 'o'; - } - return '?'; -} - -static char -ValueToTypeChar(const Value &v) -{ - if (v.isInt32()) return 'I'; - if (v.isDouble()) return 'D'; - if (v.isString()) return 'S'; - if (v.isObject()) return v.toObject().isFunction() ? 'F' : 'O'; - if (v.isBoolean()) return 'B'; - if (v.isNull()) return 'N'; - if (v.isUndefined()) return 'U'; - if (v.isMagic()) return 'M'; - return '?'; -} -#endif - - -/* Blacklist parameters. */ - -/* - * Number of iterations of a loop where we start tracing. That is, we don't - * start tracing until the beginning of the HOTLOOP-th iteration. - */ -#define HOTLOOP 8 - -/* Attempt recording this many times before blacklisting permanently. */ -#define BL_ATTEMPTS 2 - -/* Skip this many hits before attempting recording again, after an aborted attempt. */ -#define BL_BACKOFF 32 - -/* - * If, after running a trace CHECK_LOOP_ITERS times, it hasn't done MIN_LOOP_ITERS - * iterations, we blacklist it. -*/ -#define MIN_LOOP_ITERS 200 -#define LOOP_CHECK_ITERS 10 - -#ifdef DEBUG -#define LOOP_COUNT_MAX 100000000 -#else -#define LOOP_COUNT_MAX MIN_LOOP_ITERS -#endif - -/* Number of times we wait to exit on a side exit before we try to extend the tree. */ -#define HOTEXIT 1 - -/* Number of times we try to extend the tree along a side exit. */ -#define MAXEXIT 3 - -/* Maximum number of peer trees allowed. */ -#define MAXPEERS 9 - -/* Max call depths for inlining. */ -#define MAX_CALLDEPTH 10 - -/* Max number of slots in a table-switch. */ -#define MAX_TABLE_SWITCH 256 - -/* Max number of branches per tree. */ -#define MAX_BRANCHES 32 - -#define CHECK_STATUS(expr) \ - JS_BEGIN_MACRO \ - RecordingStatus _status = (expr); \ - if (_status != RECORD_CONTINUE) \ - return _status; \ - JS_END_MACRO - -#define CHECK_STATUS_A(expr) \ - JS_BEGIN_MACRO \ - AbortableRecordingStatus _status = InjectStatus((expr)); \ - if (_status != ARECORD_CONTINUE) \ - return _status; \ - JS_END_MACRO - -#ifdef JS_JIT_SPEW -#define RETURN_VALUE(msg, value) \ - JS_BEGIN_MACRO \ - debug_only_printf(LC_TMAbort, "trace stopped: %d: %s\n", __LINE__, (msg)); \ - return (value); \ - JS_END_MACRO -#else -#define RETURN_VALUE(msg, value) return (value) -#endif - -#define RETURN_STOP(msg) RETURN_VALUE(msg, RECORD_STOP) -#define RETURN_STOP_A(msg) RETURN_VALUE(msg, ARECORD_STOP) -#define RETURN_ERROR(msg) RETURN_VALUE(msg, RECORD_ERROR) -#define RETURN_ERROR_A(msg) RETURN_VALUE(msg, ARECORD_ERROR) - -#ifdef JS_JIT_SPEW -struct __jitstats { -#define JITSTAT(x) uint64 x; -#include "jitstats.tbl" -#undef JITSTAT -} jitstats = { 0LL, }; - -JS_STATIC_ASSERT(sizeof(jitstats) % sizeof(uint64) == 0); - -enum jitstat_ids { -#define JITSTAT(x) STAT ## x ## ID, -#include "jitstats.tbl" -#undef JITSTAT - STAT_IDS_TOTAL -}; - -static JSBool -jitstats_getOnTrace(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - *vp = BOOLEAN_TO_JSVAL(JS_ON_TRACE(cx)); - return true; -} - -static JSPropertySpec jitstats_props[] = { -#define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT }, -#include "jitstats.tbl" -#undef JITSTAT - { "onTrace", 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, jitstats_getOnTrace, NULL }, - { 0 } -}; - -static JSBool -jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - int index = -1; - - if (JSID_IS_STRING(id)) { - JSAtom* str = JSID_TO_ATOM(id); - if (StringEqualsAscii(str, "HOTLOOP")) { - *vp = INT_TO_JSVAL(HOTLOOP); - return JS_TRUE; - } - - if (StringEqualsAscii(str, "adaptive")) { -#ifdef JS_METHODJIT - *vp = BOOLEAN_TO_JSVAL(cx->profilingEnabled || - (cx->methodJitEnabled && - !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))); -#else - *vp = BOOLEAN_TO_JSVAL(false); -#endif - return JS_TRUE; - } - } - - if (JSID_IS_INT(id)) - index = JSID_TO_INT(id); - - uint64 result = 0; - switch (index) { -#define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break; -#include "jitstats.tbl" -#undef JITSTAT - default: - *vp = JSVAL_VOID; - return JS_TRUE; - } - - if (result < JSVAL_INT_MAX) { - *vp = INT_TO_JSVAL(jsint(result)); - return JS_TRUE; - } - char retstr[64]; - JS_snprintf(retstr, sizeof retstr, "%llu", result); - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr)); - return JS_TRUE; -} - -JSClass jitstats_class = { - "jitstats", - 0, - JS_PropertyStub, JS_PropertyStub, - jitstats_getProperty, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, NULL, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -void -InitJITStatsClass(JSContext *cx, JSObject *glob) -{ - JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL); -} - -#define AUDIT(x) (jitstats.x++) -#else -#define AUDIT(x) ((void)0) -#endif /* JS_JIT_SPEW */ - -#ifdef JS_JIT_SPEW -static void -DumpPeerStability(TraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, uint32 argc); -#endif - -/* - * We really need a better way to configure the JIT. Shaver, where is - * my fancy JIT object? - * - * NB: this is raced on, if jstracer.cpp should ever be running MT. - * I think it's harmless tho. - */ -static bool did_we_check_processor_features = false; - -nanojit::Config NJConfig; - -/* ------ Debug logging control ------ */ - -/* - * All the logging control stuff lives in here. It is shared between - * all threads, but I think that's OK. - */ -LogControl LogController; - -#ifdef JS_JIT_SPEW - -/* - * NB: this is raced on too, if jstracer.cpp should ever be running MT. - * Also harmless. - */ -static bool did_we_set_up_debug_logging = false; - -static void -InitJITLogController() -{ - char *tm, *tmf; - uint32_t bits; - - LogController.lcbits = 0; - - tm = getenv("TRACEMONKEY"); - if (tm) { - fflush(NULL); - printf( - "The environment variable $TRACEMONKEY has been replaced by $TMFLAGS.\n" - "Try 'TMFLAGS=help js -j' for a list of options.\n" - ); - exit(0); - } - - tmf = getenv("TMFLAGS"); - if (!tmf) return; - - /* Using strstr() is really a cheap hack as far as flag decoding goes. */ - if (strstr(tmf, "help")) { - fflush(NULL); - printf( - "usage: TMFLAGS=option,option,option,... where options can be:\n" - "\n" - " help show this message\n" - " ------ options for jstracer & jsregexp ------\n" - " minimal ultra-minimalist output; try this first\n" - " full everything except 'treevis' and 'fragprofile'\n" - " tracer tracer lifetime (FIXME:better description)\n" - " recorder trace recording stuff (FIXME:better description)\n" - " abort show trace recording aborts\n" - " stats show trace recording stats\n" - " regexp show compilation & entry for regexps\n" - " profiler show loop profiles as they are profiled\n" - " treevis spew that tracevis/tree.py can parse\n" - " ------ options for Nanojit ------\n" - " fragprofile count entries and exits for each fragment\n" - " liveness show LIR liveness at start of reader pipeline\n" - " readlir show LIR as it enters the reader pipeline\n" - " aftersf show LIR after StackFilter\n" - " afterdce show LIR after dead code elimination\n" - " native show native code (interleaved with 'afterdce')\n" - " nativebytes show native code bytes in 'native' output\n" - " regalloc show regalloc state in 'native' output\n" - " activation show activation state in 'native' output\n" - "\n" - ); - exit(0); - /*NOTREACHED*/ - } - - bits = 0; - - /* flags for jstracer.cpp */ - if (strstr(tmf, "minimal") || strstr(tmf, "full")) bits |= LC_TMMinimal; - if (strstr(tmf, "tracer") || strstr(tmf, "full")) bits |= LC_TMTracer; - if (strstr(tmf, "recorder") || strstr(tmf, "full")) bits |= LC_TMRecorder; - if (strstr(tmf, "abort") || strstr(tmf, "full")) bits |= LC_TMAbort; - if (strstr(tmf, "stats") || strstr(tmf, "full")) bits |= LC_TMStats; - if (strstr(tmf, "profiler") || strstr(tmf, "full")) bits |= LC_TMProfiler; - if (strstr(tmf, "treevis")) bits |= LC_TMTreeVis; - - /* flags for nanojit */ - if (strstr(tmf, "fragprofile")) bits |= LC_FragProfile; - if (strstr(tmf, "liveness") || strstr(tmf, "full")) bits |= LC_Liveness; - if (strstr(tmf, "readlir") || strstr(tmf, "full")) bits |= LC_ReadLIR; - if (strstr(tmf, "aftersf") || strstr(tmf, "full")) bits |= LC_AfterSF; - if (strstr(tmf, "afterdce") || strstr(tmf, "full")) bits |= LC_AfterDCE; - if (strstr(tmf, "native") || strstr(tmf, "full")) bits |= LC_Native; - if (strstr(tmf, "nativebytes")|| strstr(tmf, "full")) bits |= LC_Bytes; - if (strstr(tmf, "regalloc") || strstr(tmf, "full")) bits |= LC_RegAlloc; - if (strstr(tmf, "activation") || strstr(tmf, "full")) bits |= LC_Activation; - - LogController.lcbits = bits; - return; - -} -#endif - -/* ------------------ Frag-level profiling support ------------------ */ - -#ifdef JS_JIT_SPEW - -/* - * All the allocations done by this profile data-collection and - * display machinery, are done in TraceMonitor::profAlloc. That is - * emptied out at the end of FinishJIT. It has a lifetime from - * InitJIT to FinishJIT, which exactly matches the span - * js_FragProfiling_init to js_FragProfiling_showResults. - */ -template -static -Seq* reverseInPlace(Seq* seq) -{ - Seq* prev = NULL; - Seq* curr = seq; - while (curr) { - Seq* next = curr->tail; - curr->tail = prev; - prev = curr; - curr = next; - } - return prev; -} - -// The number of top blocks to show in the profile -#define N_TOP_BLOCKS 50 - -// Contains profile info for a single guard -struct GuardPI { - uint32_t guardID; // identifying number - uint32_t count; // count. -}; - -struct FragPI { - uint32_t count; // entry count for this Fragment - uint32_t nStaticExits; // statically: the number of exits - size_t nCodeBytes; // statically: the number of insn bytes in the main fragment - size_t nExitBytes; // statically: the number of insn bytes in the exit paths - Seq* guards; // guards, each with its own count - uint32_t largestGuardID; // that exists in .guards -}; - -void -FragProfiling_FragFinalizer(Fragment* f, TraceMonitor* tm) -{ - // Recover profiling data from 'f', which is logically at the end - // of its useful lifetime. - if (!(LogController.lcbits & LC_FragProfile)) - return; - - NanoAssert(f); - // Valid profFragIDs start at 1 - NanoAssert(f->profFragID >= 1); - // Should be called exactly once per Fragment. This will assert if - // you issue the same FragID to more than one Fragment. - NanoAssert(!tm->profTab->containsKey(f->profFragID)); - - FragPI pi = { f->profCount, - f->nStaticExits, - f->nCodeBytes, - f->nExitBytes, - NULL, 0 }; - - // Begin sanity check on the guards - SeqBuilder guardsBuilder(*tm->profAlloc); - GuardRecord* gr; - uint32_t nGs = 0; - uint32_t sumOfDynExits = 0; - for (gr = f->guardsForFrag; gr; gr = gr->nextInFrag) { - nGs++; - // Also copy the data into our auxiliary structure. - // f->guardsForFrag is in reverse order, and so this - // copy preserves that ordering (->add adds at end). - // Valid profGuardIDs start at 1. - NanoAssert(gr->profGuardID > 0); - sumOfDynExits += gr->profCount; - GuardPI gpi = { gr->profGuardID, gr->profCount }; - guardsBuilder.add(gpi); - if (gr->profGuardID > pi.largestGuardID) - pi.largestGuardID = gr->profGuardID; - } - pi.guards = guardsBuilder.get(); - // And put the guard list in forwards order - pi.guards = reverseInPlace(pi.guards); - - // Why is this so? Because nGs is the number of guards - // at the time the LIR was generated, whereas f->nStaticExits - // is the number of them observed by the time it makes it - // through to the assembler. It can be the case that LIR - // optimisation removes redundant guards; hence we expect - // nGs to always be the same or higher. - NanoAssert(nGs >= f->nStaticExits); - - // Also we can assert that the sum of the exit counts - // can't exceed the entry count. It'd be nice to assert that - // they are exactly equal, but we can't because we don't know - // how many times we got to the end of the trace. - NanoAssert(f->profCount >= sumOfDynExits); - - // End sanity check on guards - - tm->profTab->put(f->profFragID, pi); -} - -static void -FragProfiling_showResults(TraceMonitor* tm) -{ - uint32_t topFragID[N_TOP_BLOCKS]; - FragPI topPI[N_TOP_BLOCKS]; - uint64_t totCount = 0, cumulCount; - uint32_t totSE = 0; - size_t totCodeB = 0, totExitB = 0; - PodArrayZero(topFragID); - PodArrayZero(topPI); - FragStatsMap::Iter iter(*tm->profTab); - while (iter.next()) { - uint32_t fragID = iter.key(); - FragPI pi = iter.value(); - uint32_t count = pi.count; - totCount += (uint64_t)count; - /* Find the rank for this entry, in tops */ - int r = N_TOP_BLOCKS-1; - while (true) { - if (r == -1) - break; - if (topFragID[r] == 0) { - r--; - continue; - } - if (count > topPI[r].count) { - r--; - continue; - } - break; - } - r++; - NanoAssert(r >= 0 && r <= N_TOP_BLOCKS); - /* This entry should be placed at topPI[r], and entries - at higher numbered slots moved up one. */ - if (r < N_TOP_BLOCKS) { - for (int s = N_TOP_BLOCKS-1; s > r; s--) { - topFragID[s] = topFragID[s-1]; - topPI[s] = topPI[s-1]; - } - topFragID[r] = fragID; - topPI[r] = pi; - } - } - - LogController.printf( - "\n----------------- Per-fragment execution counts ------------------\n"); - LogController.printf( - "\nTotal count = %llu\n\n", (unsigned long long int)totCount); - - LogController.printf( - " Entry counts Entry counts ----- Static -----\n"); - LogController.printf( - " ------Self------ ----Cumulative--- Exits Cbytes Xbytes FragID\n"); - LogController.printf("\n"); - - if (totCount == 0) - totCount = 1; /* avoid division by zero */ - cumulCount = 0; - int r; - for (r = 0; r < N_TOP_BLOCKS; r++) { - if (topFragID[r] == 0) - break; - cumulCount += (uint64_t)topPI[r].count; - LogController.printf("%3d: %5.2f%% %9u %6.2f%% %9llu" - " %3d %5u %5u %06u\n", - r, - (double)topPI[r].count * 100.0 / (double)totCount, - topPI[r].count, - (double)cumulCount * 100.0 / (double)totCount, - (unsigned long long int)cumulCount, - topPI[r].nStaticExits, - (unsigned int)topPI[r].nCodeBytes, - (unsigned int)topPI[r].nExitBytes, - topFragID[r]); - totSE += (uint32_t)topPI[r].nStaticExits; - totCodeB += topPI[r].nCodeBytes; - totExitB += topPI[r].nExitBytes; - } - LogController.printf("\nTotal displayed code bytes = %u, " - "exit bytes = %u\n" - "Total displayed static exits = %d\n\n", - (unsigned int)totCodeB, (unsigned int)totExitB, totSE); - - LogController.printf("Analysis by exit counts\n\n"); - - for (r = 0; r < N_TOP_BLOCKS; r++) { - if (topFragID[r] == 0) - break; - LogController.printf("FragID=%06u, total count %u:\n", topFragID[r], - topPI[r].count); - uint32_t madeItToEnd = topPI[r].count; - uint32_t totThisFrag = topPI[r].count; - if (totThisFrag == 0) - totThisFrag = 1; - GuardPI gpi; - // visit the guards, in forward order - for (Seq* guards = topPI[r].guards; guards; guards = guards->tail) { - gpi = (*guards).head; - if (gpi.count == 0) - continue; - madeItToEnd -= gpi.count; - LogController.printf(" GuardID=%03u %7u (%5.2f%%)\n", - gpi.guardID, gpi.count, - 100.0 * (double)gpi.count / (double)totThisFrag); - } - LogController.printf(" Looped (%03u) %7u (%5.2f%%)\n", - topPI[r].largestGuardID+1, - madeItToEnd, - 100.0 * (double)madeItToEnd / (double)totThisFrag); - NanoAssert(madeItToEnd <= topPI[r].count); // else unsigned underflow - LogController.printf("\n"); - } - - tm->profTab = NULL; -} - -#endif - -/* ----------------------------------------------------------------- */ - -#ifdef DEBUG -JSBool FASTCALL -PrintOnTrace(char* format, uint32 argc, double *argv) -{ - union { - struct { - uint32 lo; - uint32 hi; - } i; - double d; - char *cstr; - JSObject *o; - JSString *s; - } u; - -#define GET_ARG() JS_BEGIN_MACRO \ - if (argi >= argc) { \ - fprintf(out, "[too few args for format]"); \ - break; \ -} \ - u.d = argv[argi++]; \ - JS_END_MACRO - - FILE *out = stderr; - - uint32 argi = 0; - for (char *p = format; *p; ++p) { - if (*p != '%') { - putc(*p, out); - continue; - } - char ch = *++p; - if (!ch) { - fprintf(out, "[trailing %%]"); - continue; - } - - switch (ch) { - case 'a': - GET_ARG(); - fprintf(out, "[%u:%u 0x%x:0x%x %f]", u.i.lo, u.i.hi, u.i.lo, u.i.hi, u.d); - break; - case 'd': - GET_ARG(); - fprintf(out, "%d", u.i.lo); - break; - case 'u': - GET_ARG(); - fprintf(out, "%u", u.i.lo); - break; - case 'x': - GET_ARG(); - fprintf(out, "%x", u.i.lo); - break; - case 'f': - GET_ARG(); - fprintf(out, "%f", u.d); - break; - case 'o': - GET_ARG(); - js_DumpObject(u.o); - break; - case 's': - GET_ARG(); - { - size_t length = u.s->length(); - // protect against massive spew if u.s is a bad pointer. - if (length > 1 << 16) - length = 1 << 16; - if (u.s->isRope()) { - fprintf(out, ""); - break; - } - if (u.s->isRope()) { - fprintf(out, "", (int)u.s->asRope().length()); - } else { - const jschar *chars = u.s->asLinear().chars(); - for (unsigned i = 0; i < length; ++i) { - jschar co = chars[i]; - if (co < 128) - putc(co, out); - else if (co < 256) - fprintf(out, "\\u%02x", co); - else - fprintf(out, "\\u%04x", co); - } - } - } - break; - case 'S': - GET_ARG(); - fprintf(out, "%s", u.cstr); - break; - case 'v': { - GET_ARG(); - Value *v = (Value *) u.i.lo; - js_DumpValue(*v); - break; - } - default: - fprintf(out, "[invalid %%%c]", *p); - } - } - -#undef GET_ARG - - return JS_TRUE; -} - -JS_DEFINE_CALLINFO_3(extern, BOOL, PrintOnTrace, CHARPTR, UINT32, DOUBLEPTR, 0, ACCSET_STORE_ANY) - -// This version is not intended to be called directly: usually it is easier to -// use one of the other overloads. -void -TraceRecorder::tprint(const char *format, int count, nanojit::LIns *insa[]) -{ - size_t size = strlen(format) + 1; - char* data = (char*) traceMonitor->traceAlloc->alloc(size); - memcpy(data, format, size); - - double *args = (double*) traceMonitor->traceAlloc->alloc(count * sizeof(double)); - LIns* argsp_ins = w.nameImmpNonGC(args); - for (int i = 0; i < count; ++i) - w.stTprintArg(insa, argsp_ins, i); - - LIns* args_ins[] = { w.nameImmpNonGC(args), w.nameImmi(count), w.nameImmpNonGC(data) }; - LIns* call_ins = w.call(&PrintOnTrace_ci, args_ins); - guard(false, w.eqi0(call_ins), MISMATCH_EXIT); -} - -// Generate a 'printf'-type call from trace for debugging. -void -TraceRecorder::tprint(const char *format) -{ - LIns* insa[] = { NULL }; - tprint(format, 0, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins) -{ - LIns* insa[] = { ins }; - tprint(format, 1, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2) -{ - LIns* insa[] = { ins1, ins2 }; - tprint(format, 2, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3) -{ - LIns* insa[] = { ins1, ins2, ins3 }; - tprint(format, 3, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4) -{ - LIns* insa[] = { ins1, ins2, ins3, ins4 }; - tprint(format, 4, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4, - LIns *ins5) -{ - LIns* insa[] = { ins1, ins2, ins3, ins4, ins5 }; - tprint(format, 5, insa); -} - -void -TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4, - LIns *ins5, LIns *ins6) -{ - LIns* insa[] = { ins1, ins2, ins3, ins4, ins5, ins6 }; - tprint(format, 6, insa); -} -#endif - -Tracker::Tracker(JSContext *cx) - : cx(cx) -{ - pagelist = NULL; -} - -Tracker::~Tracker() -{ - clear(); -} - -inline jsuword -Tracker::getTrackerPageBase(const void* v) const -{ - return jsuword(v) & ~TRACKER_PAGE_MASK; -} - -inline jsuword -Tracker::getTrackerPageOffset(const void* v) const -{ - return (jsuword(v) & TRACKER_PAGE_MASK) >> 2; -} - -struct Tracker::TrackerPage* -Tracker::findTrackerPage(const void* v) const -{ - jsuword base = getTrackerPageBase(v); - struct Tracker::TrackerPage* p = pagelist; - while (p) { - if (p->base == base) - return p; - p = p->next; - } - return NULL; -} - -struct Tracker::TrackerPage* -Tracker::addTrackerPage(const void* v) -{ - jsuword base = getTrackerPageBase(v); - struct TrackerPage* p = (struct TrackerPage*) cx->calloc_(sizeof(*p)); - p->base = base; - p->next = pagelist; - pagelist = p; - return p; -} - -void -Tracker::clear() -{ - while (pagelist) { - TrackerPage* p = pagelist; - pagelist = pagelist->next; - cx->free_(p); - } -} - -bool -Tracker::has(const void *v) const -{ - return get(v) != NULL; -} - -LIns* -Tracker::get(const void* v) const -{ - struct Tracker::TrackerPage* p = findTrackerPage(v); - if (!p) - return NULL; - return p->map[getTrackerPageOffset(v)]; -} - -void -Tracker::set(const void* v, LIns* i) -{ - struct Tracker::TrackerPage* p = findTrackerPage(v); - if (!p) - p = addTrackerPage(v); - p->map[getTrackerPageOffset(v)] = i; -} - -static inline bool -hasInt32Repr(const Value &v) -{ - if (!v.isNumber()) - return false; - if (v.isInt32()) - return true; - int32_t _; - return JSDOUBLE_IS_INT32(v.toDouble(), &_); -} - -static inline jsint -asInt32(const Value &v) -{ - JS_ASSERT(v.isNumber()); - if (v.isInt32()) - return v.toInt32(); -#ifdef DEBUG - int32_t _; - JS_ASSERT(JSDOUBLE_IS_INT32(v.toDouble(), &_)); -#endif - return jsint(v.toDouble()); -} - -/* - * Return JSVAL_TYPE_DOUBLE for all numbers (int and double). Split - * JSVAL_TYPE_OBJECT into JSVAL_TYPE_FUNOBJ and JSVAL_TYPE_NONFUNOBJ. - * Otherwise, just return the value's type. - */ -static inline JSValueType -getPromotedType(const Value &v) -{ - if (v.isNumber()) - return JSVAL_TYPE_DOUBLE; - if (v.isObject()) - return v.toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ; - return v.extractNonDoubleObjectTraceType(); -} - -/* - * Return JSVAL_TYPE_INT32 for all whole numbers that fit into signed 32-bit. - * Split JSVAL_TYPE_OBJECT into JSVAL_TYPE_FUNOBJ and JSVAL_TYPE_NONFUNOBJ. - * Otherwise, just return the value's type. - */ -static inline JSValueType -getCoercedType(const Value &v) -{ - if (v.isNumber()) { - int32_t _; - return (v.isInt32() || JSDOUBLE_IS_INT32(v.toDouble(), &_)) - ? JSVAL_TYPE_INT32 - : JSVAL_TYPE_DOUBLE; - } - if (v.isObject()) - return v.toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ; - return v.extractNonDoubleObjectTraceType(); -} - -static inline JSValueType -getFrameObjPtrTraceType(void *p, StackFrame *fp) -{ - if (p == fp->addressOfScopeChain()) { - JS_ASSERT(*(JSObject **)p != NULL); - return JSVAL_TYPE_NONFUNOBJ; - } - JS_ASSERT(p == fp->addressOfArgs()); - return fp->hasArgsObj() ? JSVAL_TYPE_NONFUNOBJ : JSVAL_TYPE_NULL; -} - -static inline bool -isFrameObjPtrTraceType(JSValueType t) -{ - return t == JSVAL_TYPE_NULL || t == JSVAL_TYPE_NONFUNOBJ; -} - -/* Constant seed and accumulate step borrowed from the DJB hash. */ - -const uintptr_t ORACLE_MASK = ORACLE_SIZE - 1; -JS_STATIC_ASSERT((ORACLE_MASK & ORACLE_SIZE) == 0); - -const uintptr_t FRAGMENT_TABLE_MASK = FRAGMENT_TABLE_SIZE - 1; -JS_STATIC_ASSERT((FRAGMENT_TABLE_MASK & FRAGMENT_TABLE_SIZE) == 0); - -const uintptr_t HASH_SEED = 5381; - -static inline void -HashAccum(uintptr_t& h, uintptr_t i, uintptr_t mask) -{ - h = ((h << 5) + h + (mask & i)) & mask; -} - -static JS_REQUIRES_STACK inline int -StackSlotHash(JSContext* cx, unsigned slot, const void* pc) -{ - uintptr_t h = HASH_SEED; - HashAccum(h, uintptr_t(cx->fp()->script()), ORACLE_MASK); - HashAccum(h, uintptr_t(pc), ORACLE_MASK); - HashAccum(h, uintptr_t(slot), ORACLE_MASK); - return int(h); -} - -static JS_REQUIRES_STACK inline int -GlobalSlotHash(JSContext* cx, unsigned slot) -{ - uintptr_t h = HASH_SEED; - StackFrame* fp = cx->fp(); - - while (fp->prev()) - fp = fp->prev(); - - HashAccum(h, uintptr_t(fp->maybeScript()), ORACLE_MASK); - HashAccum(h, uintptr_t(fp->scopeChain().getGlobal()->shape()), ORACLE_MASK); - HashAccum(h, uintptr_t(slot), ORACLE_MASK); - return int(h); -} - -static inline int -PCHash(jsbytecode* pc) -{ - return int(uintptr_t(pc) & ORACLE_MASK); -} - -Oracle::Oracle() -{ - /* Grow the oracle bitsets to their (fixed) size here, once. */ - _stackDontDemote.set(ORACLE_SIZE-1); - _globalDontDemote.set(ORACLE_SIZE-1); - clear(); -} - -/* Tell the oracle that a certain global variable should not be demoted. */ -JS_REQUIRES_STACK void -Oracle::markGlobalSlotUndemotable(JSContext* cx, unsigned slot) -{ - _globalDontDemote.set(GlobalSlotHash(cx, slot)); -} - -/* Consult with the oracle whether we shouldn't demote a certain global variable. */ -JS_REQUIRES_STACK bool -Oracle::isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const -{ - return _globalDontDemote.get(GlobalSlotHash(cx, slot)); -} - -/* Tell the oracle that a certain slot at a certain stack slot should not be demoted. */ -JS_REQUIRES_STACK void -Oracle::markStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) -{ - _stackDontDemote.set(StackSlotHash(cx, slot, pc)); -} - -JS_REQUIRES_STACK void -Oracle::markStackSlotUndemotable(JSContext* cx, unsigned slot) -{ - markStackSlotUndemotable(cx, slot, cx->regs().pc); -} - -/* Consult with the oracle whether we shouldn't demote a certain slot. */ -JS_REQUIRES_STACK bool -Oracle::isStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) const -{ - return _stackDontDemote.get(StackSlotHash(cx, slot, pc)); -} - -JS_REQUIRES_STACK bool -Oracle::isStackSlotUndemotable(JSContext* cx, unsigned slot) const -{ - return isStackSlotUndemotable(cx, slot, cx->regs().pc); -} - -/* Tell the oracle that a certain slot at a certain bytecode location should not be demoted. */ -void -Oracle::markInstructionUndemotable(jsbytecode* pc) -{ - _pcDontDemote.set(PCHash(pc)); -} - -/* Consult with the oracle whether we shouldn't demote a certain bytecode location. */ -bool -Oracle::isInstructionUndemotable(jsbytecode* pc) const -{ - return _pcDontDemote.get(PCHash(pc)); -} - -/* Tell the oracle that the instruction at bytecode location should use a stronger (slower) test for -0. */ -void -Oracle::markInstructionSlowZeroTest(jsbytecode* pc) -{ - _pcSlowZeroTest.set(PCHash(pc)); -} - -/* Consult with the oracle whether we should use a stronger (slower) test for -0. */ -bool -Oracle::isInstructionSlowZeroTest(jsbytecode* pc) const -{ - return _pcSlowZeroTest.get(PCHash(pc)); -} - -void -Oracle::clearDemotability() -{ - _stackDontDemote.reset(); - _globalDontDemote.reset(); - _pcDontDemote.reset(); - _pcSlowZeroTest.reset(); -} - -JS_REQUIRES_STACK void -TraceRecorder::markSlotUndemotable(LinkableFragment* f, unsigned slot) -{ - if (slot < f->nStackTypes) { - traceMonitor->oracle->markStackSlotUndemotable(cx, slot); - return; - } - - uint16* gslots = f->globalSlots->data(); - traceMonitor->oracle->markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); -} - -JS_REQUIRES_STACK void -TraceRecorder::markSlotUndemotable(LinkableFragment* f, unsigned slot, const void* pc) -{ - if (slot < f->nStackTypes) { - traceMonitor->oracle->markStackSlotUndemotable(cx, slot, pc); - return; - } - - uint16* gslots = f->globalSlots->data(); - traceMonitor->oracle->markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); -} - -static JS_REQUIRES_STACK bool -IsSlotUndemotable(Oracle* oracle, JSContext* cx, LinkableFragment* f, unsigned slot, const void* ip) -{ - if (slot < f->nStackTypes) - return !oracle || oracle->isStackSlotUndemotable(cx, slot, ip); - - uint16* gslots = f->globalSlots->data(); - return !oracle || oracle->isGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); -} - -class FrameInfoCache -{ - struct HashPolicy - { - typedef FrameInfo *Lookup; - static HashNumber hash(const FrameInfo* fi) { - size_t len = sizeof(FrameInfo) + fi->callerHeight * sizeof(JSValueType); - HashNumber h = 0; - const unsigned char *s = (const unsigned char*)fi; - for (size_t i = 0; i < len; i++, s++) - h = JS_ROTATE_LEFT32(h, 4) ^ *s; - return h; - } - - static bool match(const FrameInfo* fi1, const FrameInfo* fi2) { - if (memcmp(fi1, fi2, sizeof(FrameInfo)) != 0) - return false; - return memcmp(fi1->get_typemap(), fi2->get_typemap(), - fi1->callerHeight * sizeof(JSValueType)) == 0; - } - }; - - typedef HashSet FrameSet; - - FrameSet set; - VMAllocator *allocator; - - public: - - FrameInfoCache(VMAllocator *allocator); - - void reset() { - set.clear(); - } - - FrameInfo *memoize(FrameInfo *fi) { - FrameSet::AddPtr p = set.lookupForAdd(fi); - if (!p) { - FrameInfo* n = (FrameInfo*) - allocator->alloc(sizeof(FrameInfo) + fi->callerHeight * sizeof(JSValueType)); - memcpy(n, fi, sizeof(FrameInfo) + fi->callerHeight * sizeof(JSValueType)); - if (!set.add(p, n)) - return NULL; - } - - return *p; - } -}; - -FrameInfoCache::FrameInfoCache(VMAllocator *allocator) - : allocator(allocator) -{ - if (!set.init()) - OUT_OF_MEMORY_ABORT("FrameInfoCache::FrameInfoCache(): out of memory"); -} - -#define PC_HASH_COUNT 1024 - -static void -Blacklist(jsbytecode* pc) -{ - AUDIT(blacklisted); - JS_ASSERT(*pc == JSOP_TRACE || *pc == JSOP_NOTRACE); - *pc = JSOP_NOTRACE; -} - -static void -Unblacklist(JSScript *script, jsbytecode *pc) -{ - JS_ASSERT(*pc == JSOP_NOTRACE || *pc == JSOP_TRACE); - if (*pc == JSOP_NOTRACE) { - *pc = JSOP_TRACE; - -#ifdef JS_METHODJIT - /* This code takes care of unblacklisting in the method JIT. */ - js::mjit::ResetTraceHint(script, pc, GET_UINT16(pc), false); -#endif - } -} - -#ifdef JS_METHODJIT -static bool -IsBlacklisted(jsbytecode* pc) -{ - if (*pc == JSOP_NOTRACE) - return true; - if (*pc == JSOP_CALL) - return *(pc + JSOP_CALL_LENGTH) == JSOP_NOTRACE; - return false; -} -#endif - -static void -Backoff(TraceMonitor *tm, jsbytecode* pc, Fragment* tree = NULL) -{ - /* N.B. This code path cannot assume the recorder is/is not alive. */ - RecordAttemptMap &table = *tm->recordAttempts; - if (RecordAttemptMap::AddPtr p = table.lookupForAdd(pc)) { - if (p->value++ > (BL_ATTEMPTS * MAXPEERS)) { - p->value = 0; - Blacklist(pc); - return; - } - } else { - table.add(p, pc, 0); - } - - if (tree) { - tree->hits() -= BL_BACKOFF; - - /* - * In case there is no entry or no table (due to OOM) or some - * serious imbalance in the recording-attempt distribution on a - * multitree, give each tree another chance to blacklist here as - * well. - */ - if (++tree->recordAttempts > BL_ATTEMPTS) - Blacklist(pc); - } -} - -static void -ResetRecordingAttempts(TraceMonitor *tm, jsbytecode* pc) -{ - RecordAttemptMap &table = *tm->recordAttempts; - if (RecordAttemptMap::Ptr p = table.lookup(pc)) - p->value = 0; -} - -static inline size_t -FragmentHash(const void *ip, JSObject* globalObj, uint32 globalShape, uint32 argc) -{ - uintptr_t h = HASH_SEED; - HashAccum(h, uintptr_t(ip), FRAGMENT_TABLE_MASK); - HashAccum(h, uintptr_t(globalObj), FRAGMENT_TABLE_MASK); - HashAccum(h, uintptr_t(globalShape), FRAGMENT_TABLE_MASK); - HashAccum(h, uintptr_t(argc), FRAGMENT_TABLE_MASK); - return size_t(h); -} - -static void -RawLookupFirstPeer(TraceMonitor* tm, const void *ip, JSObject* globalObj, - uint32 globalShape, uint32 argc, - TreeFragment*& firstInBucket, TreeFragment**& prevTreeNextp) -{ - size_t h = FragmentHash(ip, globalObj, globalShape, argc); - TreeFragment** ppf = &tm->vmfragments[h]; - firstInBucket = *ppf; - for (; TreeFragment* pf = *ppf; ppf = &pf->next) { - if (pf->globalObj == globalObj && - pf->globalShape == globalShape && - pf->ip == ip && - pf->argc == argc) { - prevTreeNextp = ppf; - return; - } - } - prevTreeNextp = ppf; - return; -} - -static TreeFragment* -LookupLoop(TraceMonitor* tm, const void *ip, JSObject* globalObj, - uint32 globalShape, uint32 argc) -{ - TreeFragment *_, **prevTreeNextp; - RawLookupFirstPeer(tm, ip, globalObj, globalShape, argc, _, prevTreeNextp); - return *prevTreeNextp; -} - -static TreeFragment* -LookupOrAddLoop(TraceMonitor* tm, const void *ip, JSObject* globalObj, - uint32 globalShape, uint32 argc) -{ - TreeFragment *firstInBucket, **prevTreeNextp; - RawLookupFirstPeer(tm, ip, globalObj, globalShape, argc, firstInBucket, prevTreeNextp); - if (TreeFragment *f = *prevTreeNextp) - return f; - - verbose_only( - uint32_t profFragID = (LogController.lcbits & LC_FragProfile) - ? (++(tm->lastFragID)) : 0; - ) - TreeFragment* f = new (*tm->dataAlloc) TreeFragment(ip, tm->dataAlloc, tm->oracle, - globalObj, globalShape, - argc verbose_only(, profFragID)); - f->root = f; /* f is the root of a new tree */ - *prevTreeNextp = f; /* insert f at the end of the vmfragments bucket-list */ - f->next = NULL; - f->first = f; /* initialize peer-list at f */ - f->peer = NULL; - return f; -} - -static TreeFragment* -AddNewPeerToPeerList(TraceMonitor* tm, TreeFragment* peer) -{ - JS_ASSERT(peer); - verbose_only( - uint32_t profFragID = (LogController.lcbits & LC_FragProfile) - ? (++(tm->lastFragID)) : 0; - ) - TreeFragment* f = new (*tm->dataAlloc) TreeFragment(peer->ip, tm->dataAlloc, tm->oracle, - peer->globalObj, peer->globalShape, - peer->argc verbose_only(, profFragID)); - f->root = f; /* f is the root of a new tree */ - f->first = peer->first; /* add f to peer list */ - f->peer = peer->peer; - peer->peer = f; - /* only the |first| Fragment of a peer list needs a valid |next| field */ - debug_only(f->next = (TreeFragment*)0xcdcdcdcd); - return f; -} - -JS_REQUIRES_STACK void -TreeFragment::initialize(JSContext* cx, SlotList *globalSlots, bool speculate) -{ - this->dependentTrees.clear(); - this->linkedTrees.clear(); - this->globalSlots = globalSlots; - - /* Capture the coerced type of each active slot in the type map. */ - this->typeMap.captureTypes(cx, globalObj, *globalSlots, 0 /* callDepth */, speculate); - this->nStackTypes = this->typeMap.length() - globalSlots->length(); - this->spOffsetAtEntry = cx->regs().sp - cx->fp()->base(); - -#ifdef DEBUG - this->treeFileName = cx->fp()->script()->filename; - this->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp()); - this->treePCOffset = FramePCOffset(cx, cx->fp()); -#endif - this->script = cx->fp()->script(); - this->gcthings.clear(); - this->shapes.clear(); - this->unstableExits = NULL; - this->sideExits.clear(); - - /* Determine the native frame layout at the entry point. */ - this->nativeStackBase = (nStackTypes - (cx->regs().sp - cx->fp()->base())) * - sizeof(double); - this->maxNativeStackSlots = nStackTypes; - this->maxCallDepth = 0; - this->execs = 0; - this->iters = 0; -} - -UnstableExit* -TreeFragment::removeUnstableExit(VMSideExit* exit) -{ - /* Now erase this exit from the unstable exit list. */ - UnstableExit** tail = &this->unstableExits; - for (UnstableExit* uexit = this->unstableExits; uexit != NULL; uexit = uexit->next) { - if (uexit->exit == exit) { - *tail = uexit->next; - return *tail; - } - tail = &uexit->next; - } - JS_NOT_REACHED("exit not in unstable exit list"); - return NULL; -} - -#ifdef DEBUG -static void -AssertTreeIsUnique(TraceMonitor* tm, TreeFragment* f) -{ - JS_ASSERT(f->root == f); - - /* - * Check for duplicate entry type maps. This is always wrong and hints at - * trace explosion since we are trying to stabilize something without - * properly connecting peer edges. - */ - for (TreeFragment* peer = LookupLoop(tm, f->ip, f->globalObj, f->globalShape, f->argc); - peer != NULL; - peer = peer->peer) { - if (!peer->code() || peer == f) - continue; - JS_ASSERT(!f->typeMap.matches(peer->typeMap)); - } -} -#endif - -static void -AttemptCompilation(TraceMonitor *tm, JSObject* globalObj, - JSScript* script, jsbytecode* pc, uint32 argc) -{ - /* If we already permanently blacklisted the location, undo that. */ - Unblacklist(script, pc); - ResetRecordingAttempts(tm, pc); - - /* Breathe new life into all peer fragments at the designated loop header. */ - TreeFragment* f = LookupLoop(tm, pc, globalObj, globalObj->shape(), argc); - if (!f) { - /* - * If the global object's shape changed, we can't easily find the - * corresponding loop header via a hash table lookup. In this - * we simply bail here and hope that the fragment has another - * outstanding compilation attempt. This case is extremely rare. - */ - return; - } - JS_ASSERT(f->root == f); - f = f->first; - while (f) { - JS_ASSERT(f->root == f); - --f->recordAttempts; - f->hits() = HOTLOOP; - f = f->peer; - } -} - -static const CallInfo * -fcallinfo(LIns *ins) -{ - return ins->isop(LIR_calld) ? ins->callInfo() : NULL; -} - -/* - * StackFrame::numActualArgs is only defined for function frames. Since the - * actual arguments of the entry frame are kept on trace, argc is included in - * the tuple identifying a fragment so that two fragments for the same loop but - * recorded with different number of actual arguments are treated as two - * completely separate trees. For this particular use, we define the number of - * actuals for global and eval frames to be 0. - */ -static inline uintN -entryFrameArgc(JSContext *cx) -{ - StackFrame *fp = cx->fp(); - return fp->hasArgs() ? fp->numActualArgs() : 0; -} - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool -VisitStackAndArgs(Visitor &visitor, StackFrame *fp, StackFrame *next, Value *stack) -{ - if (JS_LIKELY(!next->hasOverflowArgs())) - return visitor.visitStackSlots(stack, next->formalArgsEnd() - stack, fp); - - /* - * In the case of nactual > nformal, the formals are copied by the VM onto - * the top of the stack. We only want to mark the formals once, so we - * carefully mark only the canonical actual arguments (as defined by - * StackFrame::canonicalActualArg). - */ - uintN nactual = next->numActualArgs(); - Value *actuals = next->actualArgs(); - size_t nstack = (actuals - 2 /* callee,this */) - stack; - if (!visitor.visitStackSlots(stack, nstack, fp)) - return false; - uintN nformal = next->numFormalArgs(); - Value *formals = next->formalArgs(); - if (!visitor.visitStackSlots(formals - 2, 2 + nformal, fp)) - return false; - return visitor.visitStackSlots(actuals + nformal, nactual - nformal, fp); -} - -/* - * Visit the values in the given StackFrame that the tracer cares about. This - * visitor function is (implicitly) the primary definition of the native stack - * area layout. There are a few other independent pieces of code that must be - * maintained to assume the same layout. They are marked like this: - * - * Duplicate native stack layout computation: see VisitFrameSlots header comment. - */ -template -static JS_REQUIRES_STACK bool -VisitFrameSlots(Visitor &visitor, JSContext *cx, unsigned depth, StackFrame *fp, - StackFrame *next) -{ - JS_ASSERT_IF(!next, cx->fp() == fp); - - if (depth > 0 && !VisitFrameSlots(visitor, cx, depth-1, fp->prev(), fp)) - return false; - - if (depth == 0) { - if (fp->isGlobalFrame()) { - visitor.setStackSlotKind("global"); - Value *base = fp->slots() + fp->globalScript()->nfixed; - if (next) - return VisitStackAndArgs(visitor, fp, next, base); - return visitor.visitStackSlots(base, cx->regs().sp - base, fp); - } - - if (JS_UNLIKELY(fp->isEvalFrame())) { - visitor.setStackSlotKind("eval"); - if (!visitor.visitStackSlots(&fp->calleev(), 2, fp)) - return false; - } else { - /* - * Only the bottom function frame must visit its arguments; for all - * other frames, arguments are visited by the prev-frame. - */ - visitor.setStackSlotKind("args"); - uintN nformal = fp->numFormalArgs(); - if (!visitor.visitStackSlots(fp->formalArgs() - 2, 2 + nformal, fp)) - return false; - if (JS_UNLIKELY(fp->hasOverflowArgs())) { - if (!visitor.visitStackSlots(fp->actualArgs() + nformal, - fp->numActualArgs() - nformal, fp)) - return false; - } - } - } - - JS_ASSERT(fp->isFunctionFrame()); - - /* - * We keep two members of StackFrame on trace: the args obj pointer and - * the scope chain pointer. The visitor must take care not to treat these - * as js::Value-typed variables, since they are unboxed pointers. - * Moreover, StackFrame compresses the args obj pointer with nactual, so - * fp->addressOfArgs() is not really a JSObject**: the visitor must treat - * !fp->hasArgsObj() as a null args obj pointer. Hence, visitFrameObjPtr - * is only passed a void *. - */ - visitor.setStackSlotKind("arguments"); - if (!visitor.visitFrameObjPtr(fp->addressOfArgs(), fp)) - return false; - visitor.setStackSlotKind("scopeChain"); - if (!visitor.visitFrameObjPtr(fp->addressOfScopeChain(), fp)) - return false; - - visitor.setStackSlotKind("slots"); - if (next) - return VisitStackAndArgs(visitor, fp, next, fp->slots()); - return visitor.visitStackSlots(fp->slots(), cx->regs().sp - fp->slots(), fp); -} - -// Number of native frame slots used for 'special' values between args and vars. -// Currently the two values are |arguments| (args object) and |scopeChain|. -const int SPECIAL_FRAME_SLOTS = 2; - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool -VisitStackSlots(Visitor &visitor, JSContext *cx, unsigned callDepth) -{ - return VisitFrameSlots(visitor, cx, callDepth, cx->fp(), NULL); -} - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitGlobalSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj, - unsigned ngslots, uint16 *gslots) -{ - for (unsigned n = 0; n < ngslots; ++n) { - unsigned slot = gslots[n]; - visitor.visitGlobalSlot(&globalObj->getSlotRef(slot), n, slot); - } -} - -class AdjustCallerTypeVisitor; - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitGlobalSlots(Visitor &visitor, JSContext *cx, SlotList &gslots) -{ - VisitGlobalSlots(visitor, cx, cx->fp()->scopeChain().getGlobal(), - gslots.length(), gslots.data()); -} - - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitSlots(Visitor& visitor, JSContext* cx, JSObject* globalObj, - unsigned callDepth, unsigned ngslots, uint16* gslots) -{ - if (VisitStackSlots(visitor, cx, callDepth)) - VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots); -} - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitSlots(Visitor& visitor, JSContext* cx, unsigned callDepth, - unsigned ngslots, uint16* gslots) -{ - VisitSlots(visitor, cx, cx->fp()->scopeChain().getGlobal(), - callDepth, ngslots, gslots); -} - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj, - unsigned callDepth, const SlotList& slots) -{ - VisitSlots(visitor, cx, globalObj, callDepth, slots.length(), - slots.data()); -} - -template -static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitSlots(Visitor &visitor, JSContext *cx, unsigned callDepth, - const SlotList& slots) -{ - VisitSlots(visitor, cx, cx->fp()->scopeChain().getGlobal(), - callDepth, slots.length(), slots.data()); -} - - -class SlotVisitorBase { -#if defined JS_JIT_SPEW -protected: - char const *mStackSlotKind; -public: - SlotVisitorBase() : mStackSlotKind(NULL) {} - JS_ALWAYS_INLINE const char *stackSlotKind() { return mStackSlotKind; } - JS_ALWAYS_INLINE void setStackSlotKind(char const *k) { - mStackSlotKind = k; - } -#else -public: - JS_ALWAYS_INLINE const char *stackSlotKind() { return NULL; } - JS_ALWAYS_INLINE void setStackSlotKind(char const *k) {} -#endif -}; - -struct CountSlotsVisitor : public SlotVisitorBase -{ - unsigned mCount; - bool mDone; - const void* mStop; -public: - JS_ALWAYS_INLINE CountSlotsVisitor(const void* stop = NULL) : - mCount(0), - mDone(false), - mStop(stop) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - if (mDone) - return false; - if (mStop && size_t(((const Value *)mStop) - vp) < count) { - mCount += size_t(((const Value *)mStop) - vp); - mDone = true; - return false; - } - mCount += count; - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - if (mDone) - return false; - if (mStop && mStop == p) { - mDone = true; - return false; - } - mCount++; - return true; - } - - JS_ALWAYS_INLINE unsigned count() { - return mCount; - } - - JS_ALWAYS_INLINE bool stopped() { - return mDone; - } -}; - -static JS_REQUIRES_STACK JS_ALWAYS_INLINE unsigned -CountStackAndArgs(StackFrame *next, Value *stack) -{ - if (JS_LIKELY(!next->hasOverflowArgs())) - return (Value *)next - stack; - size_t nvals = (next->formalArgs() - 2 /* callee, this */) - stack; - JS_ASSERT(nvals == unsigned((next->actualArgs() - 2) - stack) + (2 + next->numActualArgs())); - return nvals; -} - -static JS_ALWAYS_INLINE uintN -NumSlotsBeforeFixed(StackFrame *fp) -{ - uintN numArgs = fp->isEvalFrame() ? 0 : Max(fp->numActualArgs(), fp->numFormalArgs()); - return 2 + numArgs + SPECIAL_FRAME_SLOTS; -} - -/* - * Calculate the total number of native frame slots we need from this frame all - * the way back to the entry frame, including the current stack usage. - * - * Duplicate native stack layout computation: see VisitFrameSlots header comment. - */ -JS_REQUIRES_STACK unsigned -NativeStackSlots(JSContext *cx, unsigned callDepth) -{ - StackFrame *fp = cx->fp(); - StackFrame *next = NULL; - unsigned slots = 0; - unsigned depth = callDepth; - - for (; depth > 0; --depth, next = fp, fp = fp->prev()) { - JS_ASSERT(fp->isNonEvalFunctionFrame()); - slots += SPECIAL_FRAME_SLOTS; - if (next) - slots += CountStackAndArgs(next, fp->slots()); - else - slots += cx->regs().sp - fp->slots(); - } - - Value *start; - if (fp->isGlobalFrame()) { - start = fp->slots() + fp->globalScript()->nfixed; - } else { - start = fp->slots(); - slots += NumSlotsBeforeFixed(fp); - } - if (next) - slots += CountStackAndArgs(next, start); - else - slots += cx->regs().sp - start; - -#ifdef DEBUG - CountSlotsVisitor visitor; - VisitStackSlots(visitor, cx, callDepth); - JS_ASSERT(visitor.count() == slots && !visitor.stopped()); -#endif - return slots; -} - -class CaptureTypesVisitor : public SlotVisitorBase -{ - JSContext* mCx; - JSValueType* mTypeMap; - JSValueType* mPtr; - Oracle * mOracle; - -public: - JS_ALWAYS_INLINE CaptureTypesVisitor(JSContext* cx, Oracle *oracle, - JSValueType* typeMap, bool speculate) - : mCx(cx), - mTypeMap(typeMap), - mPtr(typeMap), - mOracle(speculate ? oracle : NULL) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - JSValueType type = getCoercedType(*vp); - if (type == JSVAL_TYPE_INT32 && (!mOracle || mOracle->isGlobalSlotUndemotable(mCx, slot))) - type = JSVAL_TYPE_DOUBLE; - JS_ASSERT(type != JSVAL_TYPE_BOXED); - debug_only_printf(LC_TMTracer, - "capture type global%d: %c\n", - n, TypeToChar(type)); - *mPtr++ = type; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, int count, StackFrame* fp) { - for (int i = 0; i < count; ++i) { - JSValueType type = getCoercedType(vp[i]); - if (type == JSVAL_TYPE_INT32 && (!mOracle || mOracle->isStackSlotUndemotable(mCx, length()))) - type = JSVAL_TYPE_DOUBLE; - JS_ASSERT(type != JSVAL_TYPE_BOXED); - debug_only_printf(LC_TMTracer, - "capture type %s%d: %c\n", - stackSlotKind(), i, TypeToChar(type)); - *mPtr++ = type; - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - JSValueType type = getFrameObjPtrTraceType(p, fp); - debug_only_printf(LC_TMTracer, - "capture type %s%d: %c\n", - stackSlotKind(), 0, TypeToChar(type)); - *mPtr++ = type; - return true; - } - - JS_ALWAYS_INLINE uintptr_t length() { - return mPtr - mTypeMap; - } -}; - -void -TypeMap::set(unsigned stackSlots, unsigned ngslots, - const JSValueType* stackTypeMap, const JSValueType* globalTypeMap) -{ - setLength(ngslots + stackSlots); - memcpy(data(), stackTypeMap, stackSlots * sizeof(JSValueType)); - memcpy(data() + stackSlots, globalTypeMap, ngslots * sizeof(JSValueType)); -} - -/* - * Capture the type map for the selected slots of the global object and currently pending - * stack frames. - */ -JS_REQUIRES_STACK void -TypeMap::captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth, - bool speculate) -{ - setLength(NativeStackSlots(cx, callDepth) + slots.length()); - CaptureTypesVisitor visitor(cx, oracle, data(), speculate); - VisitSlots(visitor, cx, globalObj, callDepth, slots); - JS_ASSERT(visitor.length() == length()); -} - -JS_REQUIRES_STACK void -TypeMap::captureMissingGlobalTypes(JSContext* cx, - JSObject* globalObj, SlotList& slots, unsigned stackSlots, - bool speculate) -{ - unsigned oldSlots = length() - stackSlots; - int diff = slots.length() - oldSlots; - JS_ASSERT(diff >= 0); - setLength(length() + diff); - CaptureTypesVisitor visitor(cx, oracle, data() + stackSlots + oldSlots, speculate); - VisitGlobalSlots(visitor, cx, globalObj, diff, slots.data() + oldSlots); -} - -/* Compare this type map to another one and see whether they match. */ -bool -TypeMap::matches(TypeMap& other) const -{ - if (length() != other.length()) - return false; - return !memcmp(data(), other.data(), length()); -} - -void -TypeMap::fromRaw(JSValueType* other, unsigned numSlots) -{ - unsigned oldLength = length(); - setLength(length() + numSlots); - for (unsigned i = 0; i < numSlots; i++) - get(oldLength + i) = other[i]; -} - -/* - * Use the provided storage area to create a new type map that contains the - * partial type map with the rest of it filled up from the complete type - * map. - */ -static void -MergeTypeMaps(JSValueType** partial, unsigned* plength, JSValueType* complete, unsigned clength, JSValueType* mem) -{ - unsigned l = *plength; - JS_ASSERT(l < clength); - memcpy(mem, *partial, l * sizeof(JSValueType)); - memcpy(mem + l, complete + l, (clength - l) * sizeof(JSValueType)); - *partial = mem; - *plength = clength; -} - -/* - * Specializes a tree to any specifically missing globals, including any - * dependent trees. - */ -static JS_REQUIRES_STACK void -SpecializeTreesToLateGlobals(JSContext* cx, TreeFragment* root, JSValueType* globalTypeMap, - unsigned numGlobalSlots) -{ - for (unsigned i = root->nGlobalTypes(); i < numGlobalSlots; i++) - root->typeMap.add(globalTypeMap[i]); - - JS_ASSERT(root->nGlobalTypes() == numGlobalSlots); - - for (unsigned i = 0; i < root->dependentTrees.length(); i++) { - TreeFragment* tree = root->dependentTrees[i]; - if (tree->code() && tree->nGlobalTypes() < numGlobalSlots) - SpecializeTreesToLateGlobals(cx, tree, globalTypeMap, numGlobalSlots); - } - for (unsigned i = 0; i < root->linkedTrees.length(); i++) { - TreeFragment* tree = root->linkedTrees[i]; - if (tree->code() && tree->nGlobalTypes() < numGlobalSlots) - SpecializeTreesToLateGlobals(cx, tree, globalTypeMap, numGlobalSlots); - } -} - -/* Specializes a tree to any missing globals, including any dependent trees. */ -static JS_REQUIRES_STACK void -SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeFragment* root) -{ - /* If we already have a bunch of peer trees, try to be as generic as possible. */ - size_t count = 0; - for (TreeFragment *f = root->first; f; f = f->peer, ++count); - bool speculate = count < MAXPEERS-1; - - root->typeMap.captureMissingGlobalTypes(cx, globalObj, *root->globalSlots, root->nStackTypes, - speculate); - JS_ASSERT(root->globalSlots->length() == root->typeMap.length() - root->nStackTypes); - - SpecializeTreesToLateGlobals(cx, root, root->globalTypeMap(), root->nGlobalTypes()); -} - -static void -ResetJITImpl(JSContext* cx, TraceMonitor *tm); - -#ifdef MOZ_TRACEVIS -static JS_INLINE void -ResetJIT(JSContext* cx, TraceMonitor *tm, TraceVisFlushReason r) -{ - LogTraceVisEvent(cx, S_RESET, r); - ResetJITImpl(cx, tm); -} -#else -# define ResetJIT(cx, tm, reason) ResetJITImpl(cx, tm) -#endif - -void -FlushJITCache(JSContext *cx, TraceMonitor *tm) -{ - ResetJIT(cx, tm, FR_OOM); -} - -static void -TrashTree(TreeFragment* f); - -JS_REQUIRES_STACK -TraceRecorder::TraceRecorder(JSContext* cx, TraceMonitor *tm, - VMSideExit* anchor, VMFragment* fragment, - unsigned stackSlots, unsigned ngslots, JSValueType* typeMap, - VMSideExit* innermost, JSScript* outerScript, jsbytecode* outerPC, - uint32 outerArgc, bool speculate) - : cx(cx), - traceMonitor(tm), - oracle(speculate ? tm->oracle : NULL), - fragment(fragment), - tree(fragment->root), - globalObj(tree->globalObj), - outerScript(outerScript), - outerPC(outerPC), - outerArgc(outerArgc), - anchor(anchor), - cx_ins(NULL), - eos_ins(NULL), - eor_ins(NULL), - loopLabel(NULL), - importTypeMap(&tempAlloc(), tm->oracle), - lirbuf(new (tempAlloc()) LirBuffer(tempAlloc())), - mark(*traceMonitor->traceAlloc), - numSideExitsBefore(tree->sideExits.length()), - tracker(cx), - nativeFrameTracker(cx), - global_slots(NULL), - callDepth(anchor ? anchor->calldepth : 0), - atoms(FrameAtomBase(cx, cx->fp())), - consts(JSScript::isValidOffset(cx->fp()->script()->constOffset) - ? cx->fp()->script()->consts()->vector - : NULL), - strictModeCode_ins(NULL), - cfgMerges(&tempAlloc()), - trashSelf(false), - whichTreesToTrash(&tempAlloc()), - guardedShapeTable(cx), - initDepth(0), - hadNewInit(false), -#ifdef DEBUG - addPropShapeBefore(NULL), -#endif - rval_ins(NULL), - native_rval_ins(NULL), - newobj_ins(NULL), - pendingSpecializedNative(NULL), - pendingUnboxSlot(NULL), - pendingGuardCondition(NULL), - pendingGlobalSlotsToSet(cx), - pendingLoop(true), - generatedSpecializedNative(), - tempTypeMap(cx), - w(&tempAlloc(), lirbuf) -{ - JS_ASSERT(globalObj == cx->fp()->scopeChain().getGlobal()); - JS_ASSERT(globalObj->hasOwnShape()); - JS_ASSERT(cx->regs().pc == (jsbytecode*)fragment->ip); - -#ifdef JS_METHODJIT - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); -#endif - - JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL); - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL); - JS_THREAD_DATA(cx)->recordingCompartment = cx->compartment; - -#ifdef DEBUG - lirbuf->printer = new (tempAlloc()) LInsPrinter(tempAlloc(), TM_NUM_USED_ACCS); -#endif - - /* - * Reset the fragment state we care about in case we got a recycled - * fragment. This includes resetting any profiling data we might have - * accumulated. - */ - fragment->lastIns = NULL; - fragment->setCode(NULL); - fragment->lirbuf = lirbuf; - verbose_only( fragment->profCount = 0; ) - verbose_only( fragment->nStaticExits = 0; ) - verbose_only( fragment->nCodeBytes = 0; ) - verbose_only( fragment->nExitBytes = 0; ) - verbose_only( fragment->guardNumberer = 1; ) - verbose_only( fragment->guardsForFrag = NULL; ) - verbose_only( fragment->loopLabel = NULL; ) - - /* - * Don't change fragment->profFragID, though. Once the identity of the - * Fragment is set up (for profiling purposes), we can't change it. - */ - - if (!guardedShapeTable.init()) - OUT_OF_MEMORY_ABORT("TraceRecorder::TraceRecorder: out of memory"); - -#ifdef JS_JIT_SPEW - debug_only_print0(LC_TMMinimal, "\n"); - debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n", - tree->treeFileName, tree->treeLineNumber, tree->treePCOffset, - fragment->profFragID); - - debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n", - (void*)this->globalObj, this->globalObj->shape()); - debug_only_printf(LC_TMTreeVis, "TREEVIS RECORD FRAG=%p ANCHOR=%p\n", (void*)fragment, - (void*)anchor); -#endif - - /* This creates the LIR writer pipeline. */ - w.init(&LogController, &NJConfig); - - w.start(); - - for (int i = 0; i < NumSavedRegs; ++i) - w.paramp(i, 1); -#ifdef DEBUG - for (int i = 0; i < NumSavedRegs; ++i) - w.name(lirbuf->savedRegs[i], regNames[REGNUM(Assembler::savedRegs[i])]); -#endif - - lirbuf->state = w.name(w.paramp(0, 0), "state"); - - if (fragment == fragment->root) { - w.comment("begin-loop"); - InitConst(loopLabel) = w.label(); - } - w.comment("begin-setup"); - - // if profiling, drop a label, so the assembler knows to put a - // frag-entry-counter increment at this point. If there's a - // loopLabel, use that; else we'll have to make a dummy label - // especially for this purpose. - verbose_only( if (LogController.lcbits & LC_FragProfile) { - LIns* entryLabel = NULL; - if (fragment == fragment->root) { - entryLabel = loopLabel; - } else { - entryLabel = w.label(); - } - NanoAssert(entryLabel); - NanoAssert(!fragment->loopLabel); - fragment->loopLabel = entryLabel; - }) - - lirbuf->sp = w.name(w.ldpStateField(sp), "sp"); - lirbuf->rp = w.name(w.ldpStateField(rp), "rp"); - InitConst(cx_ins) = w.name(w.ldpStateField(cx), "cx"); - InitConst(eos_ins) = w.name(w.ldpStateField(eos), "eos"); - InitConst(eor_ins) = w.name(w.ldpStateField(eor), "eor"); - - strictModeCode_ins = w.name(w.immi(cx->fp()->script()->strictModeCode), "strict"); - - /* If we came from exit, we might not have enough global types. */ - if (tree->globalSlots->length() > tree->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, tree); - - /* read into registers all values on the stack and all globals we know so far */ - import(tree, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap); - - if (fragment == fragment->root) { - /* - * We poll the operation callback request flag. It is updated - * asynchronously whenever the callback is to be invoked. We can use - * w.nameImmpNonGC here as JIT-ed code is per thread and cannot - * outlive the corresponding ThreadData. - */ - w.comment("begin-interruptFlags-check"); - /* FIXME: See bug 621140 for moving interruptCounter to the compartment. */ -#ifdef JS_THREADSAFE - void *interrupt = (void*) &cx->runtime->interruptCounter; -#else - void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags; -#endif - LIns* flagptr = w.nameImmpNonGC(interrupt); - LIns* x = w.ldiVolatile(flagptr); - guard(true, w.eqi0(x), TIMEOUT_EXIT); - w.comment("end-interruptFlags-check"); - - /* - * Count the number of iterations run by a trace, so that we can blacklist if - * the trace runs too few iterations to be worthwhile. Do this only if the methodjit - * is on--otherwise we must try to trace as much as possible. - */ -#ifdef JS_METHODJIT - if (cx->methodJitEnabled) { - w.comment("begin-count-loop-iterations"); - LIns* counterPtr = w.nameImmpNonGC((void *) &traceMonitor->iterationCounter); - LIns* counterValue = w.ldiVolatile(counterPtr); - LIns* test = w.ltiN(counterValue, LOOP_COUNT_MAX); - LIns *branch = w.jfUnoptimizable(test); - /* - * stiVolatile() uses ACCSET_STORE_ANY; If LICM is implemented - * (bug 545406) this counter will need its own region. - */ - w.stiVolatile(w.addi(counterValue, w.immi(1)), counterPtr); - w.label(branch); - w.comment("end-count-loop-iterations"); - } -#endif - } - - /* - * If we are attached to a tree call guard, make sure the guard the inner - * tree exited from is what we expect it to be. - */ - if (anchor && anchor->exitType == NESTED_EXIT) { - LIns* nested_ins = w.ldpStateField(outermostTreeExitGuard); - guard(true, w.eqp(nested_ins, w.nameImmpNonGC(innermost)), NESTED_EXIT); - } - - w.comment("end-setup"); -} - -TraceRecorder::~TraceRecorder() -{ - /* Should already have been adjusted by callers before calling delete. */ - JS_ASSERT(traceMonitor->recorder != this); - - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_ASSERT(&JS_THREAD_DATA(cx)->recordingCompartment->traceMonitor == traceMonitor); - JS_THREAD_DATA(cx)->recordingCompartment = NULL; - - if (trashSelf) - TrashTree(fragment->root); - - for (unsigned int i = 0; i < whichTreesToTrash.length(); i++) - TrashTree(whichTreesToTrash[i]); - - /* Purge the tempAlloc used during recording. */ - tempAlloc().reset(); - - forgetGuardedShapes(); -} - -inline bool -TraceMonitor::outOfMemory() const -{ - return dataAlloc->outOfMemory() || - tempAlloc->outOfMemory() || - traceAlloc->outOfMemory(); -} - -void -TraceMonitor::getCodeAllocStats(size_t &total, size_t &frag_size, size_t &free_size) const -{ - if (codeAlloc) { - codeAlloc->getStats(total, frag_size, free_size); - } else { - total = 0; - frag_size = 0; - free_size = 0; - } -} - -size_t -TraceMonitor::getVMAllocatorsMainSize() const -{ - return dataAlloc->getBytesAllocated() + - traceAlloc->getBytesAllocated() + - tempAlloc->getBytesAllocated(); -} - -size_t -TraceMonitor::getVMAllocatorsReserveSize() const -{ - return dataAlloc->mReserveSize + - traceAlloc->mReserveSize + - tempAlloc->mReserveSize; -} - -/* - * This function destroys the recorder after a successful recording, possibly - * starting a suspended outer recorder. - */ -AbortableRecordingStatus -TraceRecorder::finishSuccessfully() -{ - JS_ASSERT(!traceMonitor->profile); - JS_ASSERT(traceMonitor->recorder == this); - JS_ASSERT(fragment->lastIns && fragment->code()); - - AUDIT(traceCompleted); - mark.commit(); - - /* Grab local copies of members needed after destruction of |this|. */ - JSContext* localcx = cx; - TraceMonitor* localtm = traceMonitor; - - localtm->recorder = NULL; - cx->delete_(this); - - /* Catch OOM that occurred during recording. */ - if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) { - ResetJIT(localcx, localtm, FR_OOM); - return ARECORD_ABORTED; - } - return ARECORD_COMPLETED; -} - -/* This function aborts a recorder and any pending outer recorders. */ -JS_REQUIRES_STACK TraceRecorder::AbortResult -TraceRecorder::finishAbort(const char* reason) -{ - JS_ASSERT(!traceMonitor->profile); - JS_ASSERT(traceMonitor->recorder == this); - - AUDIT(recorderAborted); -#ifdef DEBUG - debug_only_printf(LC_TMMinimal | LC_TMAbort, - "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n", - tree->treeFileName, - tree->treeLineNumber, - tree->treePCOffset, - cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), - reason); -#endif - Backoff(traceMonitor, (jsbytecode*) fragment->root->ip, fragment->root); - - /* - * If this is the primary trace and we didn't succeed compiling, trash the - * tree. Otherwise, remove the VMSideExits we added while recording, which - * are about to be invalid. - * - * BIG FAT WARNING: resetting the length is only a valid strategy as long as - * there may be only one recorder active for a single TreeInfo at a time. - * Otherwise, we may be throwing away another recorder's valid side exits. - */ - if (fragment->root == fragment) { - TrashTree(fragment->toTreeFragment()); - } else { - JS_ASSERT(numSideExitsBefore <= fragment->root->sideExits.length()); - fragment->root->sideExits.setLength(numSideExitsBefore); - } - - /* Grab local copies of members needed after destruction of |this|. */ - JSContext* localcx = cx; - TraceMonitor* localtm = traceMonitor; - - localtm->recorder = NULL; - cx->delete_(this); - - /* Catch OOM that occurred during recording. */ - if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) { - ResetJIT(localcx, localtm, FR_OOM); - return JIT_RESET; - } - return NORMAL_ABORT; -} - -inline LIns* -TraceRecorder::w_immpObjGC(JSObject* obj) -{ - JS_ASSERT(obj); - tree->gcthings.addUnique(ObjectValue(*obj)); - return w.immpNonGC((void*)obj); -} - -inline LIns* -TraceRecorder::w_immpFunGC(JSFunction* fun) -{ - JS_ASSERT(fun); - tree->gcthings.addUnique(ObjectValue(*fun)); - return w.immpNonGC((void*)fun); -} - -inline LIns* -TraceRecorder::w_immpStrGC(JSString* str) -{ - JS_ASSERT(str); - tree->gcthings.addUnique(StringValue(str)); - return w.immpNonGC((void*)str); -} - -inline LIns* -TraceRecorder::w_immpShapeGC(const Shape* shape) -{ - JS_ASSERT(shape); - tree->shapes.addUnique(shape); - return w.immpNonGC((void*)shape); -} - -inline LIns* -TraceRecorder::w_immpIdGC(jsid id) -{ - if (JSID_IS_GCTHING(id)) - tree->gcthings.addUnique(IdToValue(id)); - return w.immpNonGC((void*)JSID_BITS(id)); -} - -ptrdiff_t -TraceRecorder::nativeGlobalSlot(const Value* p) const -{ - JS_ASSERT(isGlobal(p)); - return ptrdiff_t(p - globalObj->slots); -} - -/* Determine the offset in the native global frame for a jsval we track. */ -ptrdiff_t -TraceRecorder::nativeGlobalOffset(const Value* p) const -{ - return nativeGlobalSlot(p) * sizeof(double); -} - -/* Determine whether a value is a global stack slot. */ -bool -TraceRecorder::isGlobal(const Value* p) const -{ - return (size_t(p - globalObj->slots) < globalObj->numSlots()); -} - -bool -TraceRecorder::isVoidPtrGlobal(const void* p) const -{ - return isGlobal((const Value *)p); -} - -/* - * Return the offset in the native stack for the given jsval. More formally, - * |p| must be the address of a jsval that is represented in the native stack - * area. The return value is the offset, from TracerState::stackBase, in bytes, - * where the native representation of |*p| is stored. To get the offset - * relative to TracerState::sp, subtract TreeFragment::nativeStackBase. - */ -JS_REQUIRES_STACK ptrdiff_t -TraceRecorder::nativeStackOffsetImpl(const void* p) const -{ - CountSlotsVisitor visitor(p); - VisitStackSlots(visitor, cx, callDepth); - size_t offset = visitor.count() * sizeof(double); - - /* - * If it's not in a pending frame, it must be on the stack of the current - * frame above sp but below fp->slots() + script->nslots. - */ - if (!visitor.stopped()) { - const Value *vp = (const Value *)p; - JS_ASSERT(size_t(vp - cx->fp()->slots()) < cx->fp()->numSlots()); - offset += size_t(vp - cx->regs().sp) * sizeof(double); - } - return offset; -} - -JS_REQUIRES_STACK inline ptrdiff_t -TraceRecorder::nativeStackOffset(const Value* p) const -{ - return nativeStackOffsetImpl(p); -} - -JS_REQUIRES_STACK inline ptrdiff_t -TraceRecorder::nativeStackSlotImpl(const void* p) const -{ - return nativeStackOffsetImpl(p) / sizeof(double); -} - -JS_REQUIRES_STACK inline ptrdiff_t -TraceRecorder::nativeStackSlot(const Value* p) const -{ - return nativeStackSlotImpl(p); -} - -/* - * Return the offset, from TracerState:sp, for the given jsval. Shorthand for: - * -TreeFragment::nativeStackBase + nativeStackOffset(p). - */ -inline JS_REQUIRES_STACK ptrdiff_t -TraceRecorder::nativespOffsetImpl(const void* p) const -{ - return -tree->nativeStackBase + nativeStackOffsetImpl(p); -} - -inline JS_REQUIRES_STACK ptrdiff_t -TraceRecorder::nativespOffset(const Value* p) const -{ - return nativespOffsetImpl(p); -} - -/* Track the maximum number of native frame slots we need during execution. */ -inline void -TraceRecorder::trackNativeStackUse(unsigned slots) -{ - if (slots > tree->maxNativeStackSlots) - tree->maxNativeStackSlots = slots; -} - -/* - * Unbox a jsval into a slot. Slots are wide enough to hold double values - * directly (instead of storing a pointer to them). We assert instead of - * type checking. The caller must ensure the types are compatible. - */ -static inline void -ValueToNative(const Value &v, JSValueType type, double* slot) -{ - JS_ASSERT(type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET); - if (type > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) - v.unboxNonDoubleTo((uint64 *)slot); - else if (type == JSVAL_TYPE_INT32) - *(int32_t *)slot = v.isInt32() ? v.toInt32() : (int32_t)v.toDouble(); - else - *(double *)slot = v.toNumber(); - -#ifdef DEBUG - int32_t _; - switch (type) { - case JSVAL_TYPE_NONFUNOBJ: { - JS_ASSERT(!IsFunctionObject(v)); - debug_only_printf(LC_TMTracer, - "object<%p:%s> ", (void*)*(JSObject **)slot, - v.toObject().getClass()->name); - return; - } - - case JSVAL_TYPE_INT32: - JS_ASSERT(v.isInt32() || (v.isDouble() && JSDOUBLE_IS_INT32(v.toDouble(), &_))); - debug_only_printf(LC_TMTracer, "int<%d> ", *(jsint *)slot); - return; - - case JSVAL_TYPE_DOUBLE: - JS_ASSERT(v.isNumber()); - debug_only_printf(LC_TMTracer, "double<%g> ", *(jsdouble *)slot); - return; - - case JSVAL_TYPE_BOXED: - JS_NOT_REACHED("found jsval type in an entry type map"); - return; - - case JSVAL_TYPE_STRING: - JS_ASSERT(v.isString()); - debug_only_printf(LC_TMTracer, "string<%p> ", (void*)*(JSString**)slot); - return; - - case JSVAL_TYPE_NULL: - JS_ASSERT(v.isNull()); - debug_only_print0(LC_TMTracer, "null "); - return; - - case JSVAL_TYPE_BOOLEAN: - JS_ASSERT(v.isBoolean()); - debug_only_printf(LC_TMTracer, "special<%d> ", *(JSBool*)slot); - return; - - case JSVAL_TYPE_UNDEFINED: - JS_ASSERT(v.isUndefined()); - debug_only_print0(LC_TMTracer, "undefined "); - return; - - case JSVAL_TYPE_MAGIC: - JS_ASSERT(v.isMagic()); - debug_only_print0(LC_TMTracer, "hole "); - return; - - case JSVAL_TYPE_FUNOBJ: { - JS_ASSERT(IsFunctionObject(v)); - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &v.toObject()); -#if defined JS_JIT_SPEW - if (LogController.lcbits & LC_TMTracer) { - char funName[40]; - if (fun->atom) - JS_PutEscapedFlatString(funName, sizeof funName, fun->atom, 0); - else - strcpy(funName, "unnamed"); - LogController.printf("function<%p:%s> ", (void*)*(JSObject **)slot, funName); - } -#endif - return; - } - default: - JS_NOT_REACHED("unexpected type"); - break; - } -#endif -} - -void -TraceMonitor::flush() -{ - /* flush should only be called after all recorders have been aborted. */ - JS_ASSERT(!recorder); - JS_ASSERT(!profile); - AUDIT(cacheFlushed); - - // recover profiling data from expiring Fragments - verbose_only( - for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { - for (TreeFragment *f = vmfragments[i]; f; f = f->next) { - JS_ASSERT(f->root == f); - for (TreeFragment *p = f; p; p = p->peer) - FragProfiling_FragFinalizer(p, this); - } - } - ) - - verbose_only( - for (Seq* f = branches; f; f = f->tail) - FragProfiling_FragFinalizer(f->head, this); - ) - - flushEpoch++; - -#ifdef JS_METHODJIT - if (loopProfiles) { - for (LoopProfileMap::Enum e(*loopProfiles); !e.empty(); e.popFront()) { - jsbytecode *pc = e.front().key; - LoopProfile *prof = e.front().value; - /* This code takes care of resetting all methodjit state. */ - js::mjit::ResetTraceHint(prof->entryScript, pc, GET_UINT16(pc), true); - } - } -#endif - - frameCache->reset(); - dataAlloc->reset(); - traceAlloc->reset(); - codeAlloc->reset(); - tempAlloc->reset(); - oracle->clear(); - loopProfiles->clear(); - - for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) { - globalStates[i].globalShape = -1; - globalStates[i].globalSlots = new (*dataAlloc) SlotList(dataAlloc); - } - - assembler = new (*dataAlloc) Assembler(*codeAlloc, *dataAlloc, *dataAlloc, - &LogController, NJConfig); - verbose_only( branches = NULL; ) - - PodArrayZero(vmfragments); - tracedScripts.clear(); - - needFlush = JS_FALSE; -} - -inline bool -HasUnreachableGCThings(JSContext *cx, TreeFragment *f) -{ - /* - * We do not check here for dead scripts as JSScript is not a GC thing. - * Instead PurgeScriptFragments is used to remove dead script fragments. - * See bug 584860. - */ - if (IsAboutToBeFinalized(cx, f->globalObj)) - return true; - Value* vp = f->gcthings.data(); - for (unsigned len = f->gcthings.length(); len; --len) { - Value &v = *vp++; - JS_ASSERT(v.isMarkable()); - if (IsAboutToBeFinalized(cx, v.toGCThing())) - return true; - } - const Shape** shapep = f->shapes.data(); - for (unsigned len = f->shapes.length(); len; --len) { - const Shape* shape = *shapep++; - if (IsAboutToBeFinalized(cx, shape)) - return true; - } - return false; -} - -void -TraceMonitor::sweep(JSContext *cx) -{ - JS_ASSERT(!ontrace()); - debug_only_print0(LC_TMTracer, "Purging fragments with dead things"); - - bool shouldAbortRecording = false; - TreeFragment *recorderTree = NULL; - if (recorder) { - recorderTree = recorder->getTree(); - shouldAbortRecording = HasUnreachableGCThings(cx, recorderTree); - } - - for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { - TreeFragment** fragp = &vmfragments[i]; - while (TreeFragment* frag = *fragp) { - TreeFragment* peer = frag; - do { - if (HasUnreachableGCThings(cx, peer)) - break; - peer = peer->peer; - } while (peer); - if (peer) { - debug_only_printf(LC_TMTracer, - "TreeFragment peer %p has dead gc thing." - "Disconnecting tree %p with ip %p\n", - (void *) peer, (void *) frag, frag->ip); - JS_ASSERT(frag->root == frag); - *fragp = frag->next; - do { - verbose_only( FragProfiling_FragFinalizer(frag, this); ); - if (recorderTree == frag) - shouldAbortRecording = true; - TrashTree(frag); - frag = frag->peer; - } while (frag); - } else { - fragp = &frag->next; - } - } - } - - if (shouldAbortRecording) - recorder->finishAbort("dead GC things"); -} - -void -TraceMonitor::mark(JSTracer *trc) -{ - TracerState* state = tracerState; - while (state) { - if (state->nativeVp) - MarkValueRange(trc, state->nativeVpLen, state->nativeVp, "nativeVp"); - state = state->prev; - } -} - -/* - * Box a value from the native stack back into the Value format. - */ -static inline void -NativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot) -{ - if (type == JSVAL_TYPE_DOUBLE) { - v.setNumber(*slot); - } else if (JS_LIKELY(type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET)) { - v.boxNonDoubleFrom(type, (uint64 *)slot); - } else if (type == JSVAL_TYPE_STRORNULL) { - JSString *str = *(JSString **)slot; - v = str ? StringValue(str) : NullValue(); - } else if (type == JSVAL_TYPE_OBJORNULL) { - JSObject *obj = *(JSObject **)slot; - v = obj ? ObjectValue(*obj) : NullValue(); - } else { - JS_ASSERT(type == JSVAL_TYPE_BOXED); - JS_STATIC_ASSERT(sizeof(Value) == sizeof(double)); - v = *(Value *)slot; - } - -#ifdef DEBUG - switch (type) { - case JSVAL_TYPE_NONFUNOBJ: - JS_ASSERT(!IsFunctionObject(v)); - debug_only_printf(LC_TMTracer, - "object<%p:%s> ", - (void*) &v.toObject(), - v.toObject().getClass()->name); - break; - case JSVAL_TYPE_INT32: - debug_only_printf(LC_TMTracer, "int<%d> ", v.toInt32()); - break; - case JSVAL_TYPE_DOUBLE: - debug_only_printf(LC_TMTracer, "double<%g> ", v.toNumber()); - break; - case JSVAL_TYPE_STRING: - debug_only_printf(LC_TMTracer, "string<%p> ", (void*)v.toString()); - break; - case JSVAL_TYPE_NULL: - JS_ASSERT(v.isNull()); - debug_only_print0(LC_TMTracer, "null "); - break; - case JSVAL_TYPE_BOOLEAN: - debug_only_printf(LC_TMTracer, "bool<%d> ", v.toBoolean()); - break; - case JSVAL_TYPE_UNDEFINED: - JS_ASSERT(v.isUndefined()); - debug_only_print0(LC_TMTracer, "undefined "); - break; - case JSVAL_TYPE_MAGIC: - debug_only_printf(LC_TMTracer, "magic<%d> ", v.whyMagic()); - break; - case JSVAL_TYPE_FUNOBJ: - JS_ASSERT(IsFunctionObject(v)); -#if defined JS_JIT_SPEW - if (LogController.lcbits & LC_TMTracer) { - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &v.toObject()); - char funName[40]; - if (fun->atom) - JS_PutEscapedFlatString(funName, sizeof funName, fun->atom, 0); - else - strcpy(funName, "unnamed"); - LogController.printf("function<%p:%s> ", (void*) &v.toObject(), funName); - } -#endif - break; - case JSVAL_TYPE_STRORNULL: - debug_only_printf(LC_TMTracer, "nullablestr<%p> ", v.isNull() ? NULL : (void *)v.toString()); - break; - case JSVAL_TYPE_OBJORNULL: - debug_only_printf(LC_TMTracer, "nullablestr<%p> ", v.isNull() ? NULL : (void *)&v.toObject()); - break; - case JSVAL_TYPE_BOXED: - debug_only_printf(LC_TMTracer, "box<%llx> ", (long long unsigned int)v.asRawBits()); - break; - default: - JS_NOT_REACHED("unexpected type"); - break; - } -#endif -} - -void -ExternNativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot) -{ - return NativeToValue(cx, v, type, slot); -} - -class BuildNativeFrameVisitor : public SlotVisitorBase -{ - JSContext *mCx; - JSValueType *mTypeMap; - double *mGlobal; - double *mStack; -public: - BuildNativeFrameVisitor(JSContext *cx, - JSValueType *typemap, - double *global, - double *stack) : - mCx(cx), - mTypeMap(typemap), - mGlobal(global), - mStack(stack) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - debug_only_printf(LC_TMTracer, "global%d: ", n); - ValueToNative(*vp, *mTypeMap++, &mGlobal[slot]); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, int count, StackFrame* fp) { - for (int i = 0; i < count; ++i) { - debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), i); - ValueToNative(*vp++, *mTypeMap++, mStack++); - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), 0); - if (p == fp->addressOfScopeChain()) - *(JSObject **)mStack = &fp->scopeChain(); - else - *(JSObject **)mStack = fp->hasArgsObj() ? &fp->argsObj() : NULL; -#ifdef DEBUG - if (*mTypeMap == JSVAL_TYPE_NULL) { - JS_ASSERT(*(JSObject **)mStack == NULL); - debug_only_print0(LC_TMTracer, "null "); - } else { - JS_ASSERT(*mTypeMap == JSVAL_TYPE_NONFUNOBJ); - JS_ASSERT(!(*(JSObject **)p)->isFunction()); - debug_only_printf(LC_TMTracer, - "object<%p:%s> ", *(void **)p, - (*(JSObject **)p)->getClass()->name); - } -#endif - mTypeMap++; - mStack++; - return true; - } -}; - -static JS_REQUIRES_STACK void -BuildNativeFrame(JSContext *cx, JSObject *globalObj, unsigned callDepth, - unsigned ngslots, uint16 *gslots, - JSValueType *typeMap, double *global, double *stack) -{ - BuildNativeFrameVisitor visitor(cx, typeMap, global, stack); - VisitSlots(visitor, cx, globalObj, callDepth, ngslots, gslots); - debug_only_print0(LC_TMTracer, "\n"); -} - -class FlushNativeGlobalFrameVisitor : public SlotVisitorBase -{ - JSContext *mCx; - JSValueType *mTypeMap; - double *mGlobal; -public: - FlushNativeGlobalFrameVisitor(JSContext *cx, - JSValueType *typeMap, - double *global) : - mCx(cx), - mTypeMap(typeMap), - mGlobal(global) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - debug_only_printf(LC_TMTracer, "global%d=", n); - JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota); - NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]); - } -}; - -class FlushNativeStackFrameVisitor : public SlotVisitorBase -{ - JSContext *mCx; - const JSValueType *mInitTypeMap; - const JSValueType *mTypeMap; - double *mStack; -public: - FlushNativeStackFrameVisitor(JSContext *cx, - const JSValueType *typeMap, - double *stack) : - mCx(cx), - mInitTypeMap(typeMap), - mTypeMap(typeMap), - mStack(stack) - {} - - const JSValueType* getTypeMap() - { - return mTypeMap; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota); - for (size_t i = 0; i < count; ++i) { - debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i)); - NativeToValue(mCx, *vp, *mTypeMap, mStack); - vp++; - mTypeMap++; - mStack++; - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota); - debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0); - JSObject *frameobj = *(JSObject **)mStack; - JS_ASSERT((frameobj == NULL) == (*mTypeMap == JSVAL_TYPE_NULL)); - if (p == fp->addressOfArgs()) { - if (frameobj) { - JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj()); - fp->setArgsObj(*frameobj); - JS_ASSERT(frameobj->isArguments()); - if (frameobj->isNormalArguments()) - frameobj->setPrivate(fp); - else - JS_ASSERT(!frameobj->getPrivate()); - debug_only_printf(LC_TMTracer, - "argsobj<%p> ", - (void *)frameobj); - } else { - JS_ASSERT(!fp->hasArgsObj()); - debug_only_print0(LC_TMTracer, - "argsobj "); - } - /* else, SynthesizeFrame has initialized fp->args.nactual */ - } else { - JS_ASSERT(p == fp->addressOfScopeChain()); - if (frameobj->isCall() && - !frameobj->getPrivate() && - fp->maybeCallee() == frameobj->getCallObjCallee()) - { - JS_ASSERT(&fp->scopeChain() == StackFrame::sInvalidScopeChain); - frameobj->setPrivate(fp); - fp->setScopeChainWithOwnCallObj(*frameobj); - } else { - fp->setScopeChainNoCallObj(*frameobj); - } - debug_only_printf(LC_TMTracer, - "scopechain<%p> ", - (void *)frameobj); - } -#ifdef DEBUG - JSValueType type = *mTypeMap; - if (type == JSVAL_TYPE_NULL) { - debug_only_print0(LC_TMTracer, "null "); - } else { - JS_ASSERT(type == JSVAL_TYPE_NONFUNOBJ); - JS_ASSERT(!frameobj->isFunction()); - debug_only_printf(LC_TMTracer, - "object<%p:%s> ", - *(void **)p, - frameobj->getClass()->name); - } -#endif - mTypeMap++; - mStack++; - return true; - } -}; - -/* Box the given native frame into a JS frame. This is infallible. */ -static JS_REQUIRES_STACK void -FlushNativeGlobalFrame(JSContext *cx, JSObject *globalObj, double *global, unsigned ngslots, - uint16 *gslots, JSValueType *typemap) -{ - FlushNativeGlobalFrameVisitor visitor(cx, typemap, global); - VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots); - debug_only_print0(LC_TMTracer, "\n"); -} - -/* - * Returns the number of values on the native stack, excluding the innermost - * frame. This walks all FrameInfos on the native frame stack and sums the - * slot usage of each frame. - */ -static int32 -StackDepthFromCallStack(TracerState* state, uint32 callDepth) -{ - int32 nativeStackFramePos = 0; - - // Duplicate native stack layout computation: see VisitFrameSlots header comment. - for (FrameInfo** fip = state->callstackBase; fip < state->rp + callDepth; fip++) - nativeStackFramePos += (*fip)->callerHeight; - return nativeStackFramePos; -} - -/* - * Generic function to read upvars on trace from slots of active frames. - * T Traits type parameter. Must provide static functions: - * interp_get(fp, slot) Read the value out of an interpreter frame. - * native_slot(argc, slot) Return the position of the desired value in the on-trace - * stack frame (with position 0 being callee). - * - * upvarLevel Static level of the function containing the upvar definition - * slot Identifies the value to get. The meaning is defined by the traits type. - * callDepth Call depth of current point relative to trace entry - */ -template -inline JSValueType -GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) -{ - TracerState* state = JS_TRACE_MONITOR_ON_TRACE(cx)->tracerState; - FrameInfo** fip = state->rp + callDepth; - - /* - * First search the FrameInfo call stack for an entry containing our - * upvar, namely one with level == upvarLevel. The first FrameInfo is a - * transition from the entry frame to some callee. However, it is not - * known (from looking at the FrameInfo) whether the entry frame had a - * callee. Rather than special-case this or insert more logic into the - * loop, instead just stop before that FrameInfo (i.e. |> base| instead of - * |>= base|), and let the code after the loop handle it. - */ - int32 stackOffset = StackDepthFromCallStack(state, callDepth); - while (--fip > state->callstackBase) { - FrameInfo* fi = *fip; - - /* - * The loop starts aligned to the top of the stack, so move down to the first meaningful - * callee. Then read the callee directly from the frame. - */ - stackOffset -= fi->callerHeight; - JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]); - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, callee); - uintN calleeLevel = fun->u.i.script->staticLevel; - if (calleeLevel == upvarLevel) { - /* - * Now find the upvar's value in the native stack. stackOffset is - * the offset of the start of the activation record corresponding - * to *fip in the native stack. - */ - uint32 native_slot = T::native_slot(fi->callerArgc, slot); - *result = state->stackBase[stackOffset + native_slot]; - return fi->get_typemap()[native_slot]; - } - } - - // Next search the trace entry frame, which is not in the FrameInfo stack. - if (state->outermostTree->script->staticLevel == upvarLevel) { - uint32 argc = state->outermostTree->argc; - uint32 native_slot = T::native_slot(argc, slot); - *result = state->stackBase[native_slot]; - return state->callstackBase[0]->get_typemap()[native_slot]; - } - - /* - * If we did not find the upvar in the frames for the active traces, - * then we simply get the value from the interpreter state. - */ - JS_ASSERT(upvarLevel < UpvarCookie::UPVAR_LEVEL_LIMIT); - StackFrame* fp = cx->stack.findFrameAtLevel(upvarLevel); - Value v = T::interp_get(fp, slot); - JSValueType type = getCoercedType(v); - ValueToNative(v, type, result); - return type; -} - -// For this traits type, 'slot' is the argument index, which may be -2 for callee. -struct UpvarArgTraits { - static Value interp_get(StackFrame* fp, int32 slot) { - return fp->formalArg(slot); - } - - static uint32 native_slot(uint32 argc, int32 slot) { - return 2 /*callee,this*/ + slot; - } -}; - -uint32 JS_FASTCALL -GetUpvarArgOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) -{ - return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); -} - -// For this traits type, 'slot' is an index into the local slots array. -struct UpvarVarTraits { - static Value interp_get(StackFrame* fp, int32 slot) { - return fp->slots()[slot]; - } - - static uint32 native_slot(uint32 argc, int32 slot) { - return 4 /*callee,this,arguments,scopeChain*/ + argc + slot; - } -}; - -uint32 JS_FASTCALL -GetUpvarVarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) -{ - return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); -} - -/* - * For this traits type, 'slot' is an index into the stack area (within slots, - * after nfixed) of a frame with no function. (On trace, the top-level frame is - * the only one that can have no function.) - */ -struct UpvarStackTraits { - static Value interp_get(StackFrame* fp, int32 slot) { - return fp->slots()[slot + fp->numFixed()]; - } - - static uint32 native_slot(uint32 argc, int32 slot) { - /* - * Locals are not imported by the tracer when the frame has no - * function, so we do not add fp->getFixedCount(). - */ - JS_ASSERT(argc == 0); - return slot; - } -}; - -uint32 JS_FASTCALL -GetUpvarStackOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, - double* result) -{ - return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); -} - -// Parameters needed to access a value from a closure on trace. -struct ClosureVarInfo -{ - uint32 slot; -#ifdef DEBUG - uint32 callDepth; -#endif -}; - -/* - * Generic function to read upvars from Call objects of active heavyweight functions. - * call Callee Function object in which the upvar is accessed. - */ -template -inline uint32 -GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double* result) -{ - JS_ASSERT(call->isCall()); - -#ifdef DEBUG - TracerState* state = JS_TRACE_MONITOR_ON_TRACE(cx)->tracerState; - FrameInfo** fip = state->rp + cv->callDepth; - int32 stackOffset = StackDepthFromCallStack(state, cv->callDepth); - while (--fip > state->callstackBase) { - FrameInfo* fi = *fip; - - /* - * The loop starts aligned to the top of the stack, so move down to the first meaningful - * callee. Then read the callee directly from the frame. - */ - stackOffset -= fi->callerHeight; - JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]); - if (callee == call) { - // This is not reachable as long as the tracer guards on the identity of the callee's - // parent when making a call: - // - // - We can only reach this point if we execute JSOP_LAMBDA on trace, then call the - // function created by the lambda, and then execute a JSOP_NAME on trace. - // - Each time we execute JSOP_LAMBDA we get a function with a different parent. - // - When we execute the call to the new function, we exit trace because the parent - // is different. - JS_NOT_REACHED("JSOP_NAME variable found in outer trace"); - } - } -#endif - - // We already guarded on trace that we aren't touching an outer tree's entry frame - VOUCH_DOES_NOT_REQUIRE_STACK(); - StackFrame* fp = (StackFrame*) call->getPrivate(); - JS_ASSERT(fp != cx->fp()); - - Value v; - if (fp) { - v = T::get_slot(fp, cv->slot); - } else { - /* - * Get the value from the object. We know we have a Call object, and - * that our slot index is fine, so don't monkey around with calling the - * property getter (which just looks in the slot) or calling - * js_GetReservedSlot. Just get the slot directly. Note the static - * asserts in jsfun.cpp which make sure Call objects use slots. - */ - JS_ASSERT(cv->slot < T::slot_count(call)); - v = T::get_slot(call, cv->slot); - } - JSValueType type = getCoercedType(v); - ValueToNative(v, type, result); - return type; -} - -struct ArgClosureTraits -{ - // Get the right frame slots to use our slot index with. - // See also UpvarArgTraits. - static inline Value get_slot(StackFrame* fp, unsigned slot) { - JS_ASSERT(slot < fp->numFormalArgs()); - return fp->formalArg(slot); - } - - // Get the right object slots to use our slot index with. - static inline Value get_slot(JSObject* obj, unsigned slot) { - return obj->getSlot(slot_offset(obj) + slot); - } - - // Get the offset of our object slots from the object's slots pointer. - static inline uint32 slot_offset(JSObject* obj) { - return JSObject::CALL_RESERVED_SLOTS; - } - - // Get the maximum slot index of this type that should be allowed - static inline uint16 slot_count(JSObject* obj) { - return obj->getCallObjCalleeFunction()->nargs; - } - -private: - ArgClosureTraits(); -}; - -uint32 JS_FASTCALL -GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result) -{ - return GetFromClosure(cx, callee, cv, result); -} - -struct VarClosureTraits -{ - // See also UpvarVarTraits. - static inline Value get_slot(StackFrame* fp, unsigned slot) { - JS_ASSERT(slot < fp->fun()->script()->bindings.countVars()); - return fp->slots()[slot]; - } - - static inline Value get_slot(JSObject* obj, unsigned slot) { - return obj->getSlot(slot_offset(obj) + slot); - } - - static inline uint32 slot_offset(JSObject* obj) { - return JSObject::CALL_RESERVED_SLOTS + - obj->getCallObjCalleeFunction()->nargs; - } - - static inline uint16 slot_count(JSObject* obj) { - return obj->getCallObjCalleeFunction()->script()->bindings.countVars(); - } - -private: - VarClosureTraits(); -}; - -uint32 JS_FASTCALL -GetClosureVar(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result) -{ - return GetFromClosure(cx, callee, cv, result); -} - -/** - * Box the given native stack frame into the virtual machine stack. This - * is infallible. - * - * @param callDepth the distance between the entry frame into our trace and - * cx->fp() when we make this call. If this is not called as a - * result of a nested exit, callDepth is 0. - * @param mp an array of JSValueType that indicate what the types of the things - * on the stack are. - * @param np pointer to the native stack. We want to copy values from here to - * the JS stack as needed. - * @return the number of things we popped off of np. - */ -static JS_REQUIRES_STACK int -FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const JSValueType* mp, double* np) -{ - /* Root all string and object references first (we don't need to call the GC for this). */ - FlushNativeStackFrameVisitor visitor(cx, mp, np); - VisitStackSlots(visitor, cx, callDepth); - - debug_only_print0(LC_TMTracer, "\n"); - return visitor.getTypeMap() - mp; -} - -/* Emit load instructions onto the trace that read the initial stack state. */ -JS_REQUIRES_STACK void -TraceRecorder::importImpl(Address addr, const void* p, JSValueType t, - const char *prefix, uintN index, StackFrame *fp) -{ - LIns* ins; - if (t == JSVAL_TYPE_INT32) { /* demoted */ - JS_ASSERT(hasInt32Repr(*(const Value *)p)); - - /* - * Ok, we have a valid demotion attempt pending, so insert an integer - * read and promote it to double since all arithmetic operations expect - * to see doubles on entry. The first op to use this slot will emit a - * d2i cast which will cancel out the i2d we insert here. - */ - ins = w.ldi(addr); - ins = w.i2d(ins); - } else { - JS_ASSERT_IF(t != JSVAL_TYPE_BOXED && !isFrameObjPtrTraceType(t), - ((const Value *)p)->isNumber() == (t == JSVAL_TYPE_DOUBLE)); - if (t == JSVAL_TYPE_DOUBLE) { - ins = w.ldd(addr); - } else if (t == JSVAL_TYPE_BOOLEAN) { - ins = w.ldi(addr); - } else if (t == JSVAL_TYPE_UNDEFINED) { - ins = w.immiUndefined(); - } else if (t == JSVAL_TYPE_MAGIC) { - ins = w.ldi(addr); - } else { - ins = w.ldp(addr); - } - } - checkForGlobalObjectReallocation(); - tracker.set(p, ins); - -#ifdef DEBUG - char name[64]; - JS_ASSERT(strlen(prefix) < 11); - void* mark = NULL; - jsuword* localNames = NULL; - const char* funName = NULL; - JSAutoByteString funNameBytes; - if (*prefix == 'a' || *prefix == 'v') { - mark = JS_ARENA_MARK(&cx->tempPool); - JSFunction *fun = fp->fun(); - Bindings &bindings = fun->script()->bindings; - if (bindings.hasLocalNames()) - localNames = bindings.getLocalNameArray(cx, &cx->tempPool); - funName = fun->atom - ? js_AtomToPrintableString(cx, fun->atom, &funNameBytes) - : ""; - } - if (!strcmp(prefix, "argv")) { - if (index < fp->numFormalArgs()) { - JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[index]); - JSAutoByteString atomBytes; - JS_snprintf(name, sizeof name, "$%s.%s", funName, - js_AtomToPrintableString(cx, atom, &atomBytes)); - } else { - JS_snprintf(name, sizeof name, "$%s.", funName, index); - } - } else if (!strcmp(prefix, "vars")) { - JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[fp->numFormalArgs() + index]); - JSAutoByteString atomBytes; - JS_snprintf(name, sizeof name, "$%s.%s", funName, - js_AtomToPrintableString(cx, atom, &atomBytes)); - } else { - JS_snprintf(name, sizeof name, "$%s%d", prefix, index); - } - - if (mark) - JS_ARENA_RELEASE(&cx->tempPool, mark); - w.name(ins, name); - - debug_only_printf(LC_TMTracer, "import vp=%p name=%s type=%c\n", - p, name, TypeToChar(t)); -#endif -} - -JS_REQUIRES_STACK void -TraceRecorder::import(Address addr, const Value* p, JSValueType t, - const char *prefix, uintN index, StackFrame *fp) -{ - return importImpl(addr, p, t, prefix, index, fp); -} - -class ImportBoxedStackSlotVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - LIns *mBase; - ptrdiff_t mStackOffset; - JSValueType *mTypemap; - StackFrame *mFp; -public: - ImportBoxedStackSlotVisitor(TraceRecorder &recorder, - LIns *base, - ptrdiff_t stackOffset, - JSValueType *typemap) : - mRecorder(recorder), - mBase(base), - mStackOffset(stackOffset), - mTypemap(typemap) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - for (size_t i = 0; i < count; ++i) { - if (*mTypemap == JSVAL_TYPE_BOXED) { - mRecorder.import(StackAddress(mBase, mStackOffset), vp, JSVAL_TYPE_BOXED, - "jsval", i, fp); - LIns *vp_ins = mRecorder.unbox_value(*vp, - StackAddress(mBase, mStackOffset), - mRecorder.copy(mRecorder.anchor)); - mRecorder.set(vp, vp_ins); - } - vp++; - mTypemap++; - mStackOffset += sizeof(double); - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame *fp) { - JS_ASSERT(*mTypemap != JSVAL_TYPE_BOXED); - mTypemap++; - mStackOffset += sizeof(double); - return true; - } -}; - -JS_REQUIRES_STACK void -TraceRecorder::import(TreeFragment* tree, LIns* sp, unsigned stackSlots, unsigned ngslots, - unsigned callDepth, JSValueType* typeMap) -{ - /* - * If we get a partial list that doesn't have all the types (i.e. recording - * from a side exit that was recorded but we added more global slots - * later), merge the missing types from the entry type map. This is safe - * because at the loop edge we verify that we have compatible types for all - * globals (entry type and loop edge type match). While a different trace - * of the tree might have had a guard with a different type map for these - * slots we just filled in here (the guard we continue from didn't know - * about them), since we didn't take that particular guard the only way we - * could have ended up here is if that other trace had at its end a - * compatible type distribution with the entry map. Since that's exactly - * what we used to fill in the types our current side exit didn't provide, - * this is always safe to do. - */ - - JSValueType* globalTypeMap = typeMap + stackSlots; - unsigned length = tree->nGlobalTypes(); - - /* - * This is potentially the typemap of the side exit and thus shorter than - * the tree's global type map. - */ - if (ngslots < length) { - MergeTypeMaps(&globalTypeMap /* out param */, &ngslots /* out param */, - tree->globalTypeMap(), length, - (JSValueType*)alloca(sizeof(JSValueType) * length)); - } - JS_ASSERT(ngslots == tree->nGlobalTypes()); - - /* - * Check whether there are any values on the stack we have to unbox and do - * that first before we waste any time fetching the state from the stack. - */ - ImportBoxedStackSlotVisitor boxedStackVisitor(*this, sp, -tree->nativeStackBase, typeMap); - VisitStackSlots(boxedStackVisitor, cx, callDepth); - - /* - * Remember the import type map so we can lazily import later whatever - * we need. - */ - importTypeMap.set(importStackSlots = stackSlots, - importGlobalSlots = ngslots, - typeMap, globalTypeMap); -} - -JS_REQUIRES_STACK bool -TraceRecorder::isValidSlot(JSObject *obj, const Shape* shape) -{ - uint32 setflags = (js_CodeSpec[*cx->regs().pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR)); - - if (setflags) { - if (!shape->hasDefaultSetter()) - RETURN_VALUE("non-stub setter", false); - if (!shape->writable()) - RETURN_VALUE("writing to a read-only property", false); - } - - /* This check applies even when setflags == 0. */ - if (setflags != JOF_SET && !shape->hasDefaultGetter()) { - JS_ASSERT(!shape->isMethod()); - RETURN_VALUE("non-stub getter", false); - } - - if (!obj->containsSlot(shape->slot)) - RETURN_VALUE("invalid-slot obj property", false); - - return true; -} - -/* Lazily import a global slot if we don't already have it in the tracker. */ -JS_REQUIRES_STACK void -TraceRecorder::importGlobalSlot(unsigned slot) -{ - JS_ASSERT(slot == uint16(slot)); - JS_ASSERT(globalObj->numSlots() <= MAX_GLOBAL_SLOTS); - - Value* vp = &globalObj->getSlotRef(slot); - JS_ASSERT(!known(vp)); - - /* Add the slot to the list of interned global slots. */ - JSValueType type; - int index = tree->globalSlots->offsetOf(uint16(slot)); - if (index == -1) { - type = getCoercedType(*vp); - if (type == JSVAL_TYPE_INT32 && (!oracle || oracle->isGlobalSlotUndemotable(cx, slot))) - type = JSVAL_TYPE_DOUBLE; - index = (int)tree->globalSlots->length(); - tree->globalSlots->add(uint16(slot)); - tree->typeMap.add(type); - SpecializeTreesToMissingGlobals(cx, globalObj, tree); - JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length()); - } else { - type = importTypeMap[importStackSlots + index]; - } - import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL); -} - -/* Lazily import a global slot if we don't already have it in the tracker. */ -JS_REQUIRES_STACK bool -TraceRecorder::lazilyImportGlobalSlot(unsigned slot) -{ - if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */ - return false; - /* - * If the global object grows too large, alloca in ExecuteTree might fail, - * so abort tracing on global objects with unreasonably many slots. - */ - if (globalObj->numSlots() > MAX_GLOBAL_SLOTS) - return false; - Value* vp = &globalObj->getSlotRef(slot); - if (known(vp)) - return true; /* we already have it */ - importGlobalSlot(slot); - return true; -} - -/* Write back a value onto the stack or global frames. */ -LIns* -TraceRecorder::writeBack(LIns* ins, LIns* base, ptrdiff_t offset, bool shouldDemoteToInt32) -{ - /* - * Sink all type casts targeting the stack into the side exit by simply storing the original - * (uncasted) value. Each guard generates the side exit map based on the types of the - * last stores to every stack location, so it's safe to not perform them on-trace. - */ - JS_ASSERT(base == lirbuf->sp || base == eos_ins); - if (shouldDemoteToInt32 && IsPromotedInt32(ins)) - ins = w.demoteToInt32(ins); - - Address addr; - if (base == lirbuf->sp) { - addr = StackAddress(base, offset); - } else { - addr = EosAddress(base, offset); - unsigned slot = unsigned(offset / sizeof(double)); - (void)pendingGlobalSlotsToSet.append(slot); /* OOM is safe. */ - } - return w.st(ins, addr); -} - -/* Update the tracker, then issue a write back store. */ -JS_REQUIRES_STACK void -TraceRecorder::setImpl(void* p, LIns* i, bool shouldDemoteToInt32) -{ - JS_ASSERT(i != NULL); - checkForGlobalObjectReallocation(); - tracker.set(p, i); - - /* - * If we are writing to this location for the first time, calculate the - * offset into the native frame manually. Otherwise just look up the last - * load or store associated with the same source address (p) and use the - * same offset/base. - */ - LIns* x = nativeFrameTracker.get(p); - if (!x) { - if (isVoidPtrGlobal(p)) - x = writeBack(i, eos_ins, nativeGlobalOffset((Value *)p), shouldDemoteToInt32); - else - x = writeBack(i, lirbuf->sp, nativespOffsetImpl(p), shouldDemoteToInt32); - nativeFrameTracker.set(p, x); - } else { -#if defined NANOJIT_64BIT - JS_ASSERT( x->isop(LIR_stq) || x->isop(LIR_sti) || x->isop(LIR_std)); -#else - JS_ASSERT( x->isop(LIR_sti) || x->isop(LIR_std)); -#endif - - ptrdiff_t disp; - LIns *base = x->oprnd2(); - if (base->isop(LIR_addp) && base->oprnd2()->isImmP()) { - disp = ptrdiff_t(base->oprnd2()->immP()); - base = base->oprnd1(); - } else { - disp = x->disp(); - } - - JS_ASSERT(base == lirbuf->sp || base == eos_ins); - JS_ASSERT(disp == ((base == lirbuf->sp) - ? nativespOffsetImpl(p) - : nativeGlobalOffset((Value *)p))); - - writeBack(i, base, disp, shouldDemoteToInt32); - } -} - -JS_REQUIRES_STACK inline void -TraceRecorder::set(Value* p, LIns* i, bool shouldDemoteToInt32) -{ - return setImpl(p, i, shouldDemoteToInt32); -} - -JS_REQUIRES_STACK void -TraceRecorder::setFrameObjPtr(void* p, LIns* i, bool shouldDemoteToInt32) -{ - JS_ASSERT(isValidFrameObjPtr(p)); - return setImpl(p, i, shouldDemoteToInt32); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::attemptImport(const Value* p) -{ - if (LIns* i = getFromTracker(p)) - return i; - - /* If the variable was not known, it could require a lazy import. */ - CountSlotsVisitor countVisitor(p); - VisitStackSlots(countVisitor, cx, callDepth); - - if (countVisitor.stopped() || size_t(p - cx->fp()->slots()) < cx->fp()->numSlots()) - return get(p); - - return NULL; -} - -inline nanojit::LIns* -TraceRecorder::getFromTrackerImpl(const void* p) -{ - checkForGlobalObjectReallocation(); - return tracker.get(p); -} - -inline nanojit::LIns* -TraceRecorder::getFromTracker(const Value* p) -{ - return getFromTrackerImpl(p); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::getImpl(const void *p) -{ - LIns* x = getFromTrackerImpl(p); - if (x) - return x; - if (isVoidPtrGlobal(p)) { - unsigned slot = nativeGlobalSlot((const Value *)p); - JS_ASSERT(tree->globalSlots->offsetOf(uint16(slot)) != -1); - importGlobalSlot(slot); - } else { - unsigned slot = nativeStackSlotImpl(p); - JSValueType type = importTypeMap[slot]; - importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)), - p, type, "stack", slot, cx->fp()); - } - JS_ASSERT(knownImpl(p)); - return tracker.get(p); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::get(const Value *p) -{ - return getImpl(p); -} - -#ifdef DEBUG -bool -TraceRecorder::isValidFrameObjPtr(void *p) -{ - StackFrame *fp = cx->fp(); - for (; fp; fp = fp->prev()) { - if (fp->addressOfScopeChain() == p || fp->addressOfArgs() == p) - return true; - } - return false; -} -#endif - -JS_REQUIRES_STACK LIns* -TraceRecorder::getFrameObjPtr(void *p) -{ - JS_ASSERT(isValidFrameObjPtr(p)); - return getImpl(p); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::addr(Value* p) -{ - return isGlobal(p) - ? w.addp(eos_ins, w.nameImmw(nativeGlobalOffset(p))) - : w.addp(lirbuf->sp, w.nameImmw(nativespOffset(p))); -} - -JS_REQUIRES_STACK inline bool -TraceRecorder::knownImpl(const void* p) -{ - checkForGlobalObjectReallocation(); - return tracker.has(p); -} - -JS_REQUIRES_STACK inline bool -TraceRecorder::known(const Value* vp) -{ - return knownImpl(vp); -} - -JS_REQUIRES_STACK inline bool -TraceRecorder::known(JSObject** p) -{ - return knownImpl(p); -} - -/* - * The slots of the global object are sometimes reallocated by the interpreter. - * This function check for that condition and re-maps the entries of the tracker - * accordingly. - */ -JS_REQUIRES_STACK void -TraceRecorder::checkForGlobalObjectReallocationHelper() -{ - debug_only_print0(LC_TMTracer, "globalObj->slots relocated, updating tracker\n"); - Value* src = global_slots; - Value* dst = globalObj->getSlots(); - jsuint length = globalObj->capacity; - LIns** map = (LIns**)alloca(sizeof(LIns*) * length); - for (jsuint n = 0; n < length; ++n) { - map[n] = tracker.get(src); - tracker.set(src++, NULL); - } - for (jsuint n = 0; n < length; ++n) - tracker.set(dst++, map[n]); - global_slots = globalObj->getSlots(); -} - -/* Determine whether the current branch is a loop edge (taken or not taken). */ -static JS_REQUIRES_STACK bool -IsLoopEdge(jsbytecode* pc, jsbytecode* header) -{ - switch (*pc) { - case JSOP_IFEQ: - case JSOP_IFNE: - return ((pc + GET_JUMP_OFFSET(pc)) == header); - case JSOP_IFEQX: - case JSOP_IFNEX: - return ((pc + GET_JUMPX_OFFSET(pc)) == header); - default: - JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) || - (*pc == JSOP_OR) || (*pc == JSOP_ORX)); - } - return false; -} - -class AdjustCallerGlobalTypesVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - JSContext *mCx; - nanojit::LirBuffer *mLirbuf; - JSValueType *mTypeMap; -public: - AdjustCallerGlobalTypesVisitor(TraceRecorder &recorder, - JSValueType *typeMap) : - mRecorder(recorder), - mCx(mRecorder.cx), - mLirbuf(mRecorder.lirbuf), - mTypeMap(typeMap) - {} - - JSValueType* getTypeMap() - { - return mTypeMap; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - LIns *ins = mRecorder.get(vp); - bool isPromote = IsPromotedInt32(ins); - if (isPromote && *mTypeMap == JSVAL_TYPE_DOUBLE) { - mRecorder.w.st(mRecorder.get(vp), - EosAddress(mRecorder.eos_ins, mRecorder.nativeGlobalOffset(vp))); - /* - * Aggressively undo speculation so the inner tree will compile - * if this fails. - */ - mRecorder.traceMonitor->oracle->markGlobalSlotUndemotable(mCx, slot); - } - JS_ASSERT(!(!isPromote && *mTypeMap == JSVAL_TYPE_INT32)); - ++mTypeMap; - } -}; - -class AdjustCallerStackTypesVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - JSContext *mCx; - nanojit::LirBuffer *mLirbuf; - unsigned mSlotnum; - JSValueType *mTypeMap; -public: - AdjustCallerStackTypesVisitor(TraceRecorder &recorder, - JSValueType *typeMap) : - mRecorder(recorder), - mCx(mRecorder.cx), - mLirbuf(mRecorder.lirbuf), - mSlotnum(0), - mTypeMap(typeMap) - {} - - JSValueType* getTypeMap() - { - return mTypeMap; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - /* N.B. vp may actually point to a JSObject*. */ - for (size_t i = 0; i < count; ++i) { - LIns *ins = mRecorder.get(vp); - bool isPromote = IsPromotedInt32(ins); - if (isPromote && *mTypeMap == JSVAL_TYPE_DOUBLE) { - mRecorder.w.st(ins, StackAddress(mLirbuf->sp, mRecorder.nativespOffset(vp))); - /* - * Aggressively undo speculation so the inner tree will compile - * if this fails. - */ - mRecorder.traceMonitor->oracle->markStackSlotUndemotable(mCx, mSlotnum); - } - JS_ASSERT(!(!isPromote && *mTypeMap == JSVAL_TYPE_INT32)); - ++vp; - ++mTypeMap; - ++mSlotnum; - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - JS_ASSERT(*mTypeMap != JSVAL_TYPE_BOXED); - ++mTypeMap; - ++mSlotnum; - return true; - } -}; - -/* - * Promote slots if necessary to match the called tree's type map. This - * function is infallible and must only be called if we are certain that it is - * possible to reconcile the types for each slot in the inner and outer trees. - */ -JS_REQUIRES_STACK void -TraceRecorder::adjustCallerTypes(TreeFragment* f) -{ - AdjustCallerGlobalTypesVisitor globalVisitor(*this, f->globalTypeMap()); - VisitGlobalSlots(globalVisitor, cx, *tree->globalSlots); - - AdjustCallerStackTypesVisitor stackVisitor(*this, f->stackTypeMap()); - VisitStackSlots(stackVisitor, cx, 0); - - JS_ASSERT(f == f->root); -} - -JS_REQUIRES_STACK inline JSValueType -TraceRecorder::determineSlotType(Value* vp) -{ - if (vp->isNumber()) { - LIns *i = getFromTracker(vp); - JSValueType t; - if (i) { - t = IsPromotedInt32(i) ? JSVAL_TYPE_INT32 : JSVAL_TYPE_DOUBLE; - } else if (isGlobal(vp)) { - int offset = tree->globalSlots->offsetOf(uint16(nativeGlobalSlot(vp))); - JS_ASSERT(offset != -1); - t = importTypeMap[importStackSlots + offset]; - } else { - t = importTypeMap[nativeStackSlot(vp)]; - } - JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp)); - return t; - } - - if (vp->isObject()) - return vp->toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ; - return vp->extractNonDoubleObjectTraceType(); -} - -class DetermineTypesVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - JSValueType *mTypeMap; -public: - DetermineTypesVisitor(TraceRecorder &recorder, - JSValueType *typeMap) : - mRecorder(recorder), - mTypeMap(typeMap) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - *mTypeMap++ = mRecorder.determineSlotType(vp); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - for (size_t i = 0; i < count; ++i) - *mTypeMap++ = mRecorder.determineSlotType(vp++); - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - *mTypeMap++ = getFrameObjPtrTraceType(p, fp); - return true; - } - - JSValueType* getTypeMap() - { - return mTypeMap; - } -}; - -#if defined JS_JIT_SPEW -JS_REQUIRES_STACK static void -TreevisLogExit(JSContext* cx, VMSideExit* exit) -{ - debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\"" - " LINE=%d OFFS=%d", (void*)exit, getExitName(exit->exitType), - (void*)exit->from, (void*)cx->regs().pc, cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), FramePCOffset(cx, cx->fp())); - debug_only_print0(LC_TMTreeVis, " STACK=\""); - for (unsigned i = 0; i < exit->numStackSlots; i++) - debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->stackTypeMap()[i])); - debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); - for (unsigned i = 0; i < exit->numGlobalSlots; i++) - debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->globalTypeMap()[i])); - debug_only_print0(LC_TMTreeVis, "\"\n"); -} -#endif - -JS_REQUIRES_STACK VMSideExit* -TraceRecorder::snapshot(ExitType exitType) -{ - StackFrame* const fp = cx->fp(); - FrameRegs& regs = cx->regs(); - jsbytecode* pc = regs.pc; - - /* - * Check for a return-value opcode that needs to restart at the next - * instruction. - */ - const JSCodeSpec& cs = js_CodeSpec[*pc]; - - /* - * When calling a _FAIL native, make the snapshot's pc point to the next - * instruction after the CALL or APPLY. Even on failure, a _FAIL native - * must not be called again from the interpreter. - */ - bool resumeAfter = (pendingSpecializedNative && - JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_STATUS); - if (resumeAfter) { - JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_FUNAPPLY || *pc == JSOP_FUNCALL || - *pc == JSOP_NEW || *pc == JSOP_SETPROP || *pc == JSOP_SETNAME); - pc += cs.length; - regs.pc = pc; - MUST_FLOW_THROUGH("restore_pc"); - } - - /* - * Generate the entry map for the (possibly advanced) pc and stash it in - * the trace. - */ - unsigned stackSlots = NativeStackSlots(cx, callDepth); - - /* - * It's sufficient to track the native stack use here since all stores - * above the stack watermark defined by guards are killed. - */ - trackNativeStackUse(stackSlots + 1); - - /* Capture the type map into a temporary location. */ - unsigned ngslots = tree->globalSlots->length(); - unsigned typemap_size = (stackSlots + ngslots) * sizeof(JSValueType); - - /* Use the recorder-local temporary type map. */ - JSValueType* typemap = NULL; - if (tempTypeMap.resize(typemap_size)) - typemap = tempTypeMap.begin(); /* crash if resize() fails. */ - - /* - * Determine the type of a store by looking at the current type of the - * actual value the interpreter is using. For numbers we have to check what - * kind of store we used last (integer or double) to figure out what the - * side exit show reflect in its typemap. - */ - DetermineTypesVisitor detVisitor(*this, typemap); - VisitSlots(detVisitor, cx, callDepth, ngslots, - tree->globalSlots->data()); - JS_ASSERT(unsigned(detVisitor.getTypeMap() - typemap) == - ngslots + stackSlots); - - /* - * If this snapshot is for a side exit that leaves a boxed Value result on - * the stack, make a note of this in the typemap. Examples include the - * builtinStatus guard after calling a _FAIL builtin, a JSFastNative, or - * GetPropertyByName; and the type guard in unbox_value after such a call - * (also at the beginning of a trace branched from such a type guard). - */ - if (pendingUnboxSlot || - (pendingSpecializedNative && (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER))) { - unsigned pos = stackSlots - 1; - if (pendingUnboxSlot == regs.sp - 2) - pos = stackSlots - 2; - typemap[pos] = JSVAL_TYPE_BOXED; - } else if (pendingSpecializedNative && - (pendingSpecializedNative->flags & JSTN_RETURN_NULLABLE_STR)) { - typemap[stackSlots - 1] = JSVAL_TYPE_STRORNULL; - } else if (pendingSpecializedNative && - (pendingSpecializedNative->flags & JSTN_RETURN_NULLABLE_OBJ)) { - typemap[stackSlots - 1] = JSVAL_TYPE_OBJORNULL; - } - - /* Now restore the the original pc (after which early returns are ok). */ - if (resumeAfter) { - MUST_FLOW_LABEL(restore_pc); - regs.pc = pc - cs.length; - } else { - /* - * If we take a snapshot on a goto, advance to the target address. This - * avoids inner trees returning on a break goto, which the outer - * recorder then would confuse with a break in the outer tree. - */ - if (*pc == JSOP_GOTO) - pc += GET_JUMP_OFFSET(pc); - else if (*pc == JSOP_GOTOX) - pc += GET_JUMPX_OFFSET(pc); - } - - /* - * Check if we already have a matching side exit; if so we can return that - * side exit instead of creating a new one. - */ - VMSideExit** exits = tree->sideExits.data(); - unsigned nexits = tree->sideExits.length(); - if (exitType == LOOP_EXIT) { - for (unsigned n = 0; n < nexits; ++n) { - VMSideExit* e = exits[n]; - if (e->pc == pc && (e->imacpc == fp->maybeImacropc()) && - ngslots == e->numGlobalSlots && - !memcmp(exits[n]->fullTypeMap(), typemap, typemap_size)) { - AUDIT(mergedLoopExits); -#if defined JS_JIT_SPEW - TreevisLogExit(cx, e); -#endif - return e; - } - } - } - - /* We couldn't find a matching side exit, so create a new one. */ - VMSideExit* exit = (VMSideExit*) - traceAlloc().alloc(sizeof(VMSideExit) + (stackSlots + ngslots) * sizeof(JSValueType)); - - /* Setup side exit structure. */ - exit->from = fragment; - exit->calldepth = callDepth; - exit->numGlobalSlots = ngslots; - exit->numStackSlots = stackSlots; - exit->numStackSlotsBelowCurrentFrame = cx->fp()->isFunctionFrame() ? - nativeStackOffset(&cx->fp()->calleev()) / sizeof(double) : - 0; - exit->exitType = exitType; - exit->pc = pc; - exit->imacpc = fp->maybeImacropc(); - exit->sp_adj = (stackSlots * sizeof(double)) - tree->nativeStackBase; - exit->rp_adj = exit->calldepth * sizeof(FrameInfo*); - exit->lookupFlags = js_InferFlags(cx, 0); - memcpy(exit->fullTypeMap(), typemap, typemap_size); - -#if defined JS_JIT_SPEW - TreevisLogExit(cx, exit); -#endif - return exit; -} - -JS_REQUIRES_STACK GuardRecord* -TraceRecorder::createGuardRecord(VMSideExit* exit) -{ -#ifdef JS_JIT_SPEW - // For debug builds, place the guard records in a longer lasting - // pool. This is because the fragment profiler will look at them - // relatively late in the day, after they would have been freed, - // in some cases, had they been allocated in traceAlloc(). - GuardRecord* gr = new (dataAlloc()) GuardRecord(); -#else - // The standard place (for production builds). - GuardRecord* gr = new (traceAlloc()) GuardRecord(); -#endif - - gr->exit = exit; - exit->addGuard(gr); - - // gr->profCount is calloc'd to zero - verbose_only( - gr->profGuardID = fragment->guardNumberer++; - gr->nextInFrag = fragment->guardsForFrag; - fragment->guardsForFrag = gr; - ) - - return gr; -} - -/* Test if 'ins' is in a form that can be used as a guard/branch condition. */ -static bool -isCond(LIns* ins) -{ - return ins->isCmp() || ins->isImmI(0) || ins->isImmI(1); -} - -/* Ensure 'ins' is in a form suitable for a guard/branch condition. */ -void -TraceRecorder::ensureCond(LIns** ins, bool* cond) -{ - if (!isCond(*ins)) { - *cond = !*cond; - *ins = (*ins)->isI() ? w.eqi0(*ins) : w.eqp0(*ins); - } -} - -/* - * Emit a guard for condition (cond), expecting to evaluate to boolean result - * (expected) and using the supplied side exit if the condition doesn't hold. - * - * Callers shouldn't generate guards that always exit (which can occur due to - * optimization of the guard condition) because it's bad for both compile-time - * speed (all the code generated after the guard is dead) and run-time speed - * (fragment that always exit are slow). This function has two modes for - * handling an always-exit guard; which mode is used depends on the value of - * abortIfAlwaysExits: - * - * - abortIfAlwaysExits == false: This is the default mode. If the guard - * will always exit, we assert (in debug builds) as a signal that we are - * generating bad traces. (In optimized builds that lack assertions the - * guard will be generated correctly, so the code will be slow but safe.) In - * this mode, the caller is responsible for not generating an always-exit - * guard. The return value will always be RECORD_CONTINUE, so the caller - * need not check it. - * - * - abortIfAlwaysExits == true: If the guard will always exit, we abort - * recording and return RECORD_STOP; otherwise we generate the guard - * normally and return RECORD_CONTINUE. This mode can be used when the - * caller doesn't know ahead of time whether the guard will always exit. In - * this mode, the caller must check the return value. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit, - bool abortIfAlwaysExits/* = false */) -{ - if (exit->exitType == LOOP_EXIT) - tree->sideExits.add(exit); - - JS_ASSERT(isCond(cond)); - - if ((cond->isImmI(0) && expected) || (cond->isImmI(1) && !expected)) { - if (abortIfAlwaysExits) { - /* The guard always exits, the caller must check for an abort. */ - RETURN_STOP("Constantly false guard detected"); - } - /* - * If you hit this assertion, first decide if you want recording to - * abort in the case where the guard always exits. If not, find a way - * to detect that case and avoid calling guard(). Otherwise, change - * the invocation of guard() so it passes in abortIfAlwaysExits=true, - * and have the caller check the return value, eg. using - * CHECK_STATUS(). (In optimized builds, we'll fall through to the - * insGuard() below and an always-exits guard will be inserted, which - * is correct but sub-optimal.) - */ - JS_NOT_REACHED("unexpected constantly false guard detected"); - } - - /* - * Nb: if the guard is never taken, no instruction will be created and - * insGuard() will return NULL. This is a good thing. - */ - GuardRecord* guardRec = createGuardRecord(exit); - expected ? w.xf(cond, guardRec) : w.xt(cond, guardRec); - return RECORD_CONTINUE; -} - -/* - * Emit a guard for condition (cond), expecting to evaluate to boolean result - * (expected) and generate a side exit with type exitType to jump to if the - * condition does not hold. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType, - bool abortIfAlwaysExits/* = false */) -{ - return guard(expected, cond, snapshot(exitType), abortIfAlwaysExits); -} - -JS_REQUIRES_STACK VMSideExit* -TraceRecorder::copy(VMSideExit* copy) -{ - size_t typemap_size = copy->numGlobalSlots + copy->numStackSlots; - VMSideExit* exit = (VMSideExit*) - traceAlloc().alloc(sizeof(VMSideExit) + typemap_size * sizeof(JSValueType)); - - /* Copy side exit structure. */ - memcpy(exit, copy, sizeof(VMSideExit) + typemap_size * sizeof(JSValueType)); - exit->guards = NULL; - exit->from = fragment; - exit->target = NULL; - - if (exit->exitType == LOOP_EXIT) - tree->sideExits.add(exit); -#if defined JS_JIT_SPEW - TreevisLogExit(cx, exit); -#endif - return exit; -} - -/* - * Determine whether any context associated with the same thread as cx is - * executing native code. - */ -static inline bool -ProhibitFlush(TraceMonitor *tm) -{ - return !!tm->tracerState; // don't flush if we're running a trace -} - -static void -ResetJITImpl(JSContext* cx, TraceMonitor* tm) -{ - if (!cx->traceJitEnabled) - return; - debug_only_print0(LC_TMTracer, "Flushing cache.\n"); - if (tm->recorder) { - JS_ASSERT_NOT_ON_TRACE(cx); - AbortRecording(cx, "flush cache"); - } -#if JS_METHODJIT - if (tm->profile) - AbortProfiling(cx); -#endif - if (ProhibitFlush(tm)) { - debug_only_print0(LC_TMTracer, "Deferring JIT flush due to deep bail.\n"); - tm->needFlush = JS_TRUE; - return; - } - tm->flush(); -} - -/* Compile the current fragment. */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::compile() -{ -#ifdef MOZ_TRACEVIS - TraceVisStateObj tvso(cx, S_COMPILE); -#endif - - if (traceMonitor->needFlush) { - ResetJIT(cx, traceMonitor, FR_DEEP_BAIL); - return ARECORD_ABORTED; - } - if (tree->maxNativeStackSlots >= TraceNativeStorage::MAX_NATIVE_STACK_SLOTS) { - debug_only_print0(LC_TMTracer, "Blacklist: excessive stack use.\n"); - Blacklist((jsbytecode*)tree->ip); - return ARECORD_STOP; - } - if (anchor) - ++tree->branchCount; - if (outOfMemory()) - return ARECORD_STOP; - - /* :TODO: windows support */ -#if defined DEBUG && !defined WIN32 - /* Associate a filename and line number with the fragment. */ - const char* filename = cx->fp()->script()->filename; - char* label = (char*) cx->malloc_((filename ? strlen(filename) : 7) + 16); - if (label) { - sprintf(label, "%s:%u", filename ? filename : "", - js_FramePCToLineNumber(cx, cx->fp())); - lirbuf->printer->addrNameMap->addAddrRange(fragment, sizeof(Fragment), 0, label); - cx->free_(label); - } -#endif - - Assembler *assm = traceMonitor->assembler; - JS_ASSERT(!assm->error()); - assm->compile(fragment, tempAlloc(), /*optimize*/true verbose_only(, lirbuf->printer)); - - if (assm->error()) { - assm->setError(nanojit::None); - debug_only_print0(LC_TMTracer, "Blacklisted: error during compilation\n"); - Blacklist((jsbytecode*)tree->ip); - return ARECORD_STOP; - } - - if (outOfMemory()) - return ARECORD_STOP; - ResetRecordingAttempts(traceMonitor, (jsbytecode*)fragment->ip); - ResetRecordingAttempts(traceMonitor, (jsbytecode*)tree->ip); - JS_ASSERT(!assm->error()); - if (anchor) - assm->patch(anchor); - if (assm->error()) - return ARECORD_STOP; - JS_ASSERT(fragment->code()); - JS_ASSERT_IF(fragment == fragment->root, fragment->root == tree); - - return ARECORD_CONTINUE; -} - -static bool -JoinPeers(Assembler* assm, VMSideExit* exit, TreeFragment* target) -{ - exit->target = target; - JS_ASSERT(!assm->error()); - assm->patch(exit); - if (assm->error()) - return false; - - debug_only_printf(LC_TMTreeVis, "TREEVIS JOIN ANCHOR=%p FRAG=%p\n", (void*)exit, (void*)target); - - if (exit->root() == target) - return true; - - target->dependentTrees.addUnique(exit->root()); - exit->root()->linkedTrees.addUnique(target); - return true; -} - -/* Results of trying to connect an arbitrary type A with arbitrary type B */ -enum TypeCheckResult -{ - TypeCheck_Okay, /* Okay: same type */ - TypeCheck_Promote, /* Okay: Type A needs d2i() */ - TypeCheck_Demote, /* Okay: Type A needs i2d() */ - TypeCheck_Undemote, /* Bad: Slot is undemotable */ - TypeCheck_Bad /* Bad: incompatible types */ -}; - -class SlotMap : public SlotVisitorBase -{ - public: - struct SlotInfo - { - SlotInfo() - : vp(NULL), isPromotedInt32(false), lastCheck(TypeCheck_Bad) - {} - SlotInfo(Value* vp, bool isPromotedInt32) - : vp(vp), isPromotedInt32(isPromotedInt32), lastCheck(TypeCheck_Bad), - type(getCoercedType(*vp)) - {} - SlotInfo(JSValueType t) - : vp(NULL), isPromotedInt32(false), lastCheck(TypeCheck_Bad), type(t) - {} - SlotInfo(Value* vp, JSValueType t) - : vp(vp), isPromotedInt32(t == JSVAL_TYPE_INT32), lastCheck(TypeCheck_Bad), type(t) - {} - void *vp; - bool isPromotedInt32; - TypeCheckResult lastCheck; - JSValueType type; - }; - - SlotMap(TraceRecorder& rec) - : mRecorder(rec), - mCx(rec.cx), - slots(NULL) - { - } - - virtual ~SlotMap() - { - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) - { - addSlot(vp); - } - - JS_ALWAYS_INLINE SlotMap::SlotInfo& - operator [](unsigned i) - { - return slots[i]; - } - - JS_ALWAYS_INLINE SlotMap::SlotInfo& - get(unsigned i) - { - return slots[i]; - } - - JS_ALWAYS_INLINE unsigned - length() - { - return slots.length(); - } - - /** - * Possible return states: - * - * TypeConsensus_Okay: All types are compatible. Caller must go through slot list and handle - * promote/demotes. - * TypeConsensus_Bad: Types are not compatible. Individual type check results are undefined. - * TypeConsensus_Undemotes: Types would be compatible if slots were marked as undemotable - * before recording began. Caller can go through slot list and mark - * such slots as undemotable. - */ - JS_REQUIRES_STACK TypeConsensus - checkTypes(LinkableFragment* f) - { - if (length() != f->typeMap.length()) - return TypeConsensus_Bad; - - bool has_undemotes = false; - for (unsigned i = 0; i < length(); i++) { - TypeCheckResult result = checkType(i, f->typeMap[i]); - if (result == TypeCheck_Bad) - return TypeConsensus_Bad; - if (result == TypeCheck_Undemote) - has_undemotes = true; - slots[i].lastCheck = result; - } - if (has_undemotes) - return TypeConsensus_Undemotes; - return TypeConsensus_Okay; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - addSlot(Value* vp) - { - bool isPromotedInt32 = false; - if (vp->isNumber()) { - if (LIns* i = mRecorder.getFromTracker(vp)) { - isPromotedInt32 = IsPromotedInt32(i); - } else if (mRecorder.isGlobal(vp)) { - int offset = mRecorder.tree->globalSlots->offsetOf(uint16(mRecorder.nativeGlobalSlot(vp))); - JS_ASSERT(offset != -1); - isPromotedInt32 = mRecorder.importTypeMap[mRecorder.importStackSlots + offset] == - JSVAL_TYPE_INT32; - } else { - isPromotedInt32 = mRecorder.importTypeMap[mRecorder.nativeStackSlot(vp)] == - JSVAL_TYPE_INT32; - } - } - slots.add(SlotInfo(vp, isPromotedInt32)); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - addSlot(JSValueType t) - { - slots.add(SlotInfo(NULL, t)); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - addSlot(Value *vp, JSValueType t) - { - slots.add(SlotInfo(vp, t)); - } - - JS_REQUIRES_STACK void - markUndemotes() - { - for (unsigned i = 0; i < length(); i++) { - if (get(i).lastCheck == TypeCheck_Undemote) - mRecorder.markSlotUndemotable(mRecorder.tree, i); - } - } - - JS_REQUIRES_STACK virtual void - adjustTypes() - { - for (unsigned i = 0; i < length(); i++) - adjustType(get(i)); - } - - protected: - JS_REQUIRES_STACK virtual void - adjustType(SlotInfo& info) { - JS_ASSERT(info.lastCheck != TypeCheck_Undemote && info.lastCheck != TypeCheck_Bad); -#ifdef DEBUG - if (info.lastCheck == TypeCheck_Promote) { - JS_ASSERT(info.type == JSVAL_TYPE_INT32 || info.type == JSVAL_TYPE_DOUBLE); - /* - * This should only happen if the slot has a trivial conversion, i.e. - * IsPromotedInt32() is true. We check this. - * - * Note that getFromTracker() will return NULL if the slot was - * never used, in which case we don't do the check. We could - * instead called mRecorder.get(info.vp) and always check, but - * get() has side-effects, which is not good in an assertion. - * Not checking unused slots isn't so bad. - */ - LIns* ins = mRecorder.getFromTrackerImpl(info.vp); - JS_ASSERT_IF(ins, IsPromotedInt32(ins)); - } else -#endif - if (info.lastCheck == TypeCheck_Demote) { - JS_ASSERT(info.type == JSVAL_TYPE_INT32 || info.type == JSVAL_TYPE_DOUBLE); - JS_ASSERT(mRecorder.getImpl(info.vp)->isD()); - - /* Never demote this final i2d. */ - mRecorder.setImpl(info.vp, mRecorder.getImpl(info.vp), false); - } - } - - private: - TypeCheckResult - checkType(unsigned i, JSValueType t) - { - debug_only_printf(LC_TMTracer, - "checkType slot %d: interp=%c typemap=%c isNum=%d isPromotedInt32=%d\n", - i, - TypeToChar(slots[i].type), - TypeToChar(t), - slots[i].type == JSVAL_TYPE_INT32 || slots[i].type == JSVAL_TYPE_DOUBLE, - slots[i].isPromotedInt32); - switch (t) { - case JSVAL_TYPE_INT32: - if (slots[i].type != JSVAL_TYPE_INT32 && slots[i].type != JSVAL_TYPE_DOUBLE) - return TypeCheck_Bad; /* Not a number? Type mismatch. */ - /* This is always a type mismatch, we can't close a double to an int. */ - if (!slots[i].isPromotedInt32) - return TypeCheck_Undemote; - /* Looks good, slot is an int32, the last instruction should be promotable. */ - JS_ASSERT_IF(slots[i].vp, - hasInt32Repr(*(const Value *)slots[i].vp) && slots[i].isPromotedInt32); - return slots[i].vp ? TypeCheck_Promote : TypeCheck_Okay; - case JSVAL_TYPE_DOUBLE: - if (slots[i].type != JSVAL_TYPE_INT32 && slots[i].type != JSVAL_TYPE_DOUBLE) - return TypeCheck_Bad; /* Not a number? Type mismatch. */ - if (slots[i].isPromotedInt32) - return slots[i].vp ? TypeCheck_Demote : TypeCheck_Bad; - return TypeCheck_Okay; - default: - return slots[i].type == t ? TypeCheck_Okay : TypeCheck_Bad; - } - JS_NOT_REACHED("shouldn't fall through type check switch"); - } - protected: - TraceRecorder& mRecorder; - JSContext* mCx; - Queue slots; -}; - -class DefaultSlotMap : public SlotMap -{ - public: - DefaultSlotMap(TraceRecorder& tr) : SlotMap(tr) - { - } - - virtual ~DefaultSlotMap() - { - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) - { - for (size_t i = 0; i < count; i++) - addSlot(&vp[i]); - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) - { - addSlot(getFrameObjPtrTraceType(p, fp)); - return true; - } -}; - -JS_REQUIRES_STACK TypeConsensus -TraceRecorder::selfTypeStability(SlotMap& slotMap) -{ - debug_only_printf(LC_TMTracer, "Checking type stability against self=%p\n", (void*)fragment); - TypeConsensus consensus = slotMap.checkTypes(tree); - - /* Best case: loop jumps back to its own header */ - if (consensus == TypeConsensus_Okay) - return TypeConsensus_Okay; - - /* - * If the only thing keeping this loop from being stable is undemotions, then mark relevant - * slots as undemotable. - */ - if (consensus == TypeConsensus_Undemotes) - slotMap.markUndemotes(); - - return consensus; -} - -JS_REQUIRES_STACK TypeConsensus -TraceRecorder::peerTypeStability(SlotMap& slotMap, const void* ip, TreeFragment** pPeer) -{ - JS_ASSERT(tree->first == LookupLoop(traceMonitor, ip, tree->globalObj, tree->globalShape, tree->argc)); - - /* See if there are any peers that would make this stable */ - bool onlyUndemotes = false; - for (TreeFragment *peer = tree->first; peer != NULL; peer = peer->peer) { - if (!peer->code() || peer == fragment) - continue; - debug_only_printf(LC_TMTracer, "Checking type stability against peer=%p\n", (void*)peer); - TypeConsensus consensus = slotMap.checkTypes(peer); - if (consensus == TypeConsensus_Okay) { - *pPeer = peer; - /* - * Return this even though there will be linkage; the trace itself is not stable. - * Caller should inspect ppeer to check for a compatible peer. - */ - return TypeConsensus_Okay; - } - if (consensus == TypeConsensus_Undemotes) - onlyUndemotes = true; - } - - return onlyUndemotes ? TypeConsensus_Undemotes : TypeConsensus_Bad; -} - -/* - * Complete and compile a trace and link it to the existing tree if - * appropriate. Returns ARECORD_ABORTED or ARECORD_STOP, depending on whether - * the recorder was deleted. Outparam is always set. - */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::closeLoop() -{ - VMSideExit *exit = snapshot(UNSTABLE_LOOP_EXIT); - - DefaultSlotMap slotMap(*this); - VisitSlots(slotMap, cx, 0, *tree->globalSlots); - - /* - * We should have arrived back at the loop header, and hence we don't want - * to be in an imacro here and the opcode should be either JSOP_TRACE or, in - * case this loop was blacklisted in the meantime, JSOP_NOTRACE. - */ - JS_ASSERT(*cx->regs().pc == JSOP_TRACE || *cx->regs().pc == JSOP_NOTRACE); - JS_ASSERT(!cx->fp()->hasImacropc()); - - if (callDepth != 0) { - debug_only_print0(LC_TMTracer, - "Blacklisted: stack depth mismatch, possible recursion.\n"); - Blacklist((jsbytecode*)tree->ip); - trashSelf = true; - return ARECORD_STOP; - } - - JS_ASSERT(exit->numStackSlots == tree->nStackTypes); - JS_ASSERT(fragment->root == tree); - JS_ASSERT(!trashSelf); - - TreeFragment* peer = NULL; - - TypeConsensus consensus = selfTypeStability(slotMap); - if (consensus != TypeConsensus_Okay) { - TypeConsensus peerConsensus = peerTypeStability(slotMap, tree->ip, &peer); - /* If there was a semblance of a stable peer (even if not linkable), keep the result. */ - if (peerConsensus != TypeConsensus_Bad) - consensus = peerConsensus; - } - -#if DEBUG - if (consensus != TypeConsensus_Okay || peer) - AUDIT(unstableLoopVariable); -#endif - - /* - * This exit is indeed linkable to something now. Process any promote or - * demotes that are pending in the slot map. - */ - if (consensus == TypeConsensus_Okay) - slotMap.adjustTypes(); - - if (consensus != TypeConsensus_Okay || peer) { - fragment->lastIns = w.x(createGuardRecord(exit)); - - /* If there is a peer, there must have been an "Okay" consensus. */ - JS_ASSERT_IF(peer, consensus == TypeConsensus_Okay); - - /* Compile as a type-unstable loop, and hope for a connection later. */ - if (!peer) { - /* - * If such a fragment does not exist, let's compile the loop ahead - * of time anyway. Later, if the loop becomes type stable, we will - * connect these two fragments together. - */ - debug_only_print0(LC_TMTracer, - "Trace has unstable loop variable with no stable peer, " - "compiling anyway.\n"); - UnstableExit* uexit = new (traceAlloc()) UnstableExit; - uexit->fragment = fragment; - uexit->exit = exit; - uexit->next = tree->unstableExits; - tree->unstableExits = uexit; - } else { - JS_ASSERT(peer->code()); - exit->target = peer; - debug_only_printf(LC_TMTracer, - "Joining type-unstable trace to target fragment %p.\n", - (void*)peer); - peer->dependentTrees.addUnique(tree); - tree->linkedTrees.addUnique(peer); - } - } else { - exit->exitType = LOOP_EXIT; - debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit, - getExitName(LOOP_EXIT)); - - JS_ASSERT((fragment == fragment->root) == !!loopLabel); - if (loopLabel) { - w.j(loopLabel); - w.comment("end-loop"); - w.livep(lirbuf->state); - } - - exit->target = tree; - /* - * This guard is dead code. However, it must be present because it - * can keep alive values on the stack. Without it, StackFilter can - * remove some stack stores that it shouldn't. See bug 582766 comment - * 19. - */ - fragment->lastIns = w.x(createGuardRecord(exit)); - } - - CHECK_STATUS_A(compile()); - - debug_only_printf(LC_TMTreeVis, "TREEVIS CLOSELOOP EXIT=%p PEER=%p\n", (void*)exit, (void*)peer); - - JS_ASSERT(LookupLoop(traceMonitor, tree->ip, tree->globalObj, tree->globalShape, tree->argc) == - tree->first); - JS_ASSERT(tree->first); - - peer = tree->first; - if (!joinEdgesToEntry(peer)) - return ARECORD_STOP; - - debug_only_stmt(DumpPeerStability(traceMonitor, peer->ip, peer->globalObj, - peer->globalShape, peer->argc);) - - debug_only_print0(LC_TMTracer, - "updating specializations on dependent and linked trees\n"); - if (tree->code()) - SpecializeTreesToMissingGlobals(cx, globalObj, tree); - - /* - * If this is a newly formed tree, and the outer tree has not been compiled yet, we - * should try to compile the outer tree again. - */ - if (outerPC) - AttemptCompilation(traceMonitor, globalObj, outerScript, outerPC, outerArgc); -#ifdef JS_JIT_SPEW - debug_only_printf(LC_TMMinimal, - "Recording completed at %s:%u@%u via closeLoop (FragID=%06u)\n", - cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), - fragment->profFragID); - debug_only_print0(LC_TMMinimal, "\n"); -#endif - - return finishSuccessfully(); -} - -static void -FullMapFromExit(TypeMap& typeMap, VMSideExit* exit) -{ - typeMap.setLength(0); - typeMap.fromRaw(exit->stackTypeMap(), exit->numStackSlots); - typeMap.fromRaw(exit->globalTypeMap(), exit->numGlobalSlots); - /* Include globals that were later specialized at the root of the tree. */ - if (exit->numGlobalSlots < exit->root()->nGlobalTypes()) { - typeMap.fromRaw(exit->root()->globalTypeMap() + exit->numGlobalSlots, - exit->root()->nGlobalTypes() - exit->numGlobalSlots); - } -} - -static JS_REQUIRES_STACK TypeConsensus -TypeMapLinkability(JSContext* cx, TraceMonitor *tm, const TypeMap& typeMap, TreeFragment* peer) -{ - const TypeMap& peerMap = peer->typeMap; - unsigned minSlots = JS_MIN(typeMap.length(), peerMap.length()); - TypeConsensus consensus = TypeConsensus_Okay; - for (unsigned i = 0; i < minSlots; i++) { - if (typeMap[i] == peerMap[i]) - continue; - if (typeMap[i] == JSVAL_TYPE_INT32 && peerMap[i] == JSVAL_TYPE_DOUBLE && - IsSlotUndemotable(tm->oracle, cx, peer, i, peer->ip)) { - consensus = TypeConsensus_Undemotes; - } else { - return TypeConsensus_Bad; - } - } - return consensus; -} - -JS_REQUIRES_STACK unsigned -TraceRecorder::findUndemotesInTypemaps(const TypeMap& typeMap, LinkableFragment* f, - Queue& undemotes) -{ - undemotes.setLength(0); - unsigned minSlots = JS_MIN(typeMap.length(), f->typeMap.length()); - for (unsigned i = 0; i < minSlots; i++) { - if (typeMap[i] == JSVAL_TYPE_INT32 && f->typeMap[i] == JSVAL_TYPE_DOUBLE) { - undemotes.add(i); - } else if (typeMap[i] != f->typeMap[i]) { - return 0; - } - } - for (unsigned i = 0; i < undemotes.length(); i++) - markSlotUndemotable(f, undemotes[i]); - return undemotes.length(); -} - -JS_REQUIRES_STACK bool -TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root) -{ - if (fragment->root != fragment) - return true; - - TypeMap typeMap(NULL, traceMonitor->oracle); - Queue undemotes(NULL); - - for (TreeFragment* peer = peer_root; peer; peer = peer->peer) { - if (!peer->code()) - continue; - UnstableExit* uexit = peer->unstableExits; - while (uexit != NULL) { - /* Build the full typemap for this unstable exit */ - FullMapFromExit(typeMap, uexit->exit); - /* Check its compatibility against this tree */ - TypeConsensus consensus = TypeMapLinkability(cx, traceMonitor, typeMap, tree); - JS_ASSERT_IF(consensus == TypeConsensus_Okay, peer != fragment); - if (consensus == TypeConsensus_Okay) { - debug_only_printf(LC_TMTracer, - "Joining type-stable trace to target exit %p->%p.\n", - (void*)uexit->fragment, (void*)uexit->exit); - - /* - * See bug 531513. Before linking these trees, make sure the - * peer's dependency graph is up to date. - */ - TreeFragment* from = uexit->exit->root(); - if (from->nGlobalTypes() < tree->nGlobalTypes()) { - SpecializeTreesToLateGlobals(cx, from, tree->globalTypeMap(), - tree->nGlobalTypes()); - } - - /* It's okay! Link together and remove the unstable exit. */ - JS_ASSERT(tree == fragment); - if (!JoinPeers(traceMonitor->assembler, uexit->exit, tree)) - return false; - uexit = peer->removeUnstableExit(uexit->exit); - } else { - /* Check for int32->double slots that suggest trashing. */ - if (findUndemotesInTypemaps(typeMap, tree, undemotes)) { - JS_ASSERT(peer == uexit->fragment->root); - if (fragment == peer) - trashSelf = true; - else - whichTreesToTrash.addUnique(uexit->fragment->root); - break; - } - uexit = uexit->next; - } - } - } - return true; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::endLoop() -{ - return endLoop(snapshot(LOOP_EXIT)); -} - -/* Emit an always-exit guard and compile the tree (used for break statements. */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::endLoop(VMSideExit* exit) -{ - JS_ASSERT(fragment->root == tree); - - if (callDepth != 0) { - debug_only_print0(LC_TMTracer, "Blacklisted: stack depth mismatch, possible recursion.\n"); - Blacklist((jsbytecode*)tree->ip); - trashSelf = true; - return ARECORD_STOP; - } - - fragment->lastIns = w.x(createGuardRecord(exit)); - - CHECK_STATUS_A(compile()); - - debug_only_printf(LC_TMTreeVis, "TREEVIS ENDLOOP EXIT=%p\n", (void*)exit); - - JS_ASSERT(LookupLoop(traceMonitor, tree->ip, tree->globalObj, tree->globalShape, tree->argc) == - tree->first); - - if (!joinEdgesToEntry(tree->first)) - return ARECORD_STOP; - - debug_only_stmt(DumpPeerStability(traceMonitor, tree->ip, tree->globalObj, - tree->globalShape, tree->argc);) - - /* - * Note: this must always be done, in case we added new globals on trace - * and haven't yet propagated those to linked and dependent trees. - */ - debug_only_print0(LC_TMTracer, - "updating specializations on dependent and linked trees\n"); - if (tree->code()) - SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root); - - /* - * If this is a newly formed tree, and the outer tree has not been compiled - * yet, we should try to compile the outer tree again. - */ - if (outerPC) - AttemptCompilation(traceMonitor, globalObj, outerScript, outerPC, outerArgc); -#ifdef JS_JIT_SPEW - debug_only_printf(LC_TMMinimal, - "Recording completed at %s:%u@%u via endLoop (FragID=%06u)\n", - cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), - fragment->profFragID); - debug_only_print0(LC_TMTracer, "\n"); -#endif - - return finishSuccessfully(); -} - -/* Emit code to adjust the stack to match the inner tree's stack expectations. */ -JS_REQUIRES_STACK void -TraceRecorder::prepareTreeCall(TreeFragment* inner) -{ - VMSideExit* exit = snapshot(OOM_EXIT); - - /* - * The inner tree expects to be called from the current frame. If the outer - * tree (this trace) is currently inside a function inlining code - * (calldepth > 0), we have to advance the native stack pointer such that - * we match what the inner trace expects to see. We move it back when we - * come out of the inner tree call. - */ - if (callDepth > 0) { - /* - * Calculate the amount we have to lift the native stack pointer by to - * compensate for any outer frames that the inner tree doesn't expect - * but the outer tree has. - */ - ptrdiff_t sp_adj = nativeStackOffset(&cx->fp()->calleev()); - - /* Calculate the amount we have to lift the call stack by. */ - ptrdiff_t rp_adj = callDepth * sizeof(FrameInfo*); - - /* - * Guard that we have enough stack space for the tree we are trying to - * call on top of the new value for sp. - */ - debug_only_printf(LC_TMTracer, - "sp_adj=%lld outer=%lld inner=%lld\n", - (long long int)sp_adj, - (long long int)tree->nativeStackBase, - (long long int)inner->nativeStackBase); - ptrdiff_t sp_offset = - - tree->nativeStackBase /* rebase sp to beginning of outer tree's stack */ - + sp_adj /* adjust for stack in outer frame inner tree can't see */ - + inner->maxNativeStackSlots * sizeof(double); /* plus the inner tree's stack */ - LIns* sp_top = w.addp(lirbuf->sp, w.nameImmw(sp_offset)); - guard(true, w.ltp(sp_top, eos_ins), exit); - - /* Guard that we have enough call stack space. */ - ptrdiff_t rp_offset = rp_adj + inner->maxCallDepth * sizeof(FrameInfo*); - LIns* rp_top = w.addp(lirbuf->rp, w.nameImmw(rp_offset)); - guard(true, w.ltp(rp_top, eor_ins), exit); - - sp_offset = - - tree->nativeStackBase /* rebase sp to beginning of outer tree's stack */ - + sp_adj /* adjust for stack in outer frame inner tree can't see */ - + inner->nativeStackBase; /* plus the inner tree's stack base */ - /* We have enough space, so adjust sp and rp to their new level. */ - w.stStateField(w.addp(lirbuf->sp, w.nameImmw(sp_offset)), sp); - w.stStateField(w.addp(lirbuf->rp, w.nameImmw(rp_adj)), rp); - } - - /* - * The inner tree will probably access stack slots. So tell nanojit not to - * discard or defer stack writes before emitting the call tree code. - * - * (The ExitType of this snapshot is nugatory. The exit can't be taken.) - */ - w.xbarrier(createGuardRecord(exit)); -} - -class ClearSlotsVisitor : public SlotVisitorBase -{ - Tracker &tracker; - public: - ClearSlotsVisitor(Tracker &tracker) - : tracker(tracker) - {} - - JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame *) { - for (Value *vpend = vp + count; vp != vpend; ++vp) - tracker.set(vp, NULL); - return true; - } - - JS_ALWAYS_INLINE bool - visitFrameObjPtr(void *p, StackFrame *) { - tracker.set(p, NULL); - return true; - } -}; - -static unsigned -BuildGlobalTypeMapFromInnerTree(Queue& typeMap, VMSideExit* inner) -{ -#if defined DEBUG - unsigned initialSlots = typeMap.length(); -#endif - /* First, use the innermost exit's global typemap. */ - typeMap.add(inner->globalTypeMap(), inner->numGlobalSlots); - - /* Add missing global types from the innermost exit's tree. */ - TreeFragment* innerFrag = inner->root(); - unsigned slots = inner->numGlobalSlots; - if (slots < innerFrag->nGlobalTypes()) { - typeMap.add(innerFrag->globalTypeMap() + slots, innerFrag->nGlobalTypes() - slots); - slots = innerFrag->nGlobalTypes(); - } - JS_ASSERT(typeMap.length() - initialSlots == slots); - return slots; -} - -/* Record a call to an inner tree. */ -JS_REQUIRES_STACK void -TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) -{ - /* Invoke the inner tree. */ - LIns* args[] = { lirbuf->state }; /* reverse order */ - /* Construct a call info structure for the target tree. */ - CallInfo* ci = new (traceAlloc()) CallInfo(); - ci->_address = uintptr_t(inner->code()); - JS_ASSERT(ci->_address); - ci->_typesig = CallInfo::typeSig1(ARGTYPE_P, ARGTYPE_P); - ci->_isPure = 0; - ci->_storeAccSet = ACCSET_STORE_ANY; - ci->_abi = ABI_FASTCALL; -#ifdef DEBUG - ci->_name = "fragment"; -#endif - LIns* rec = w.call(ci, args); - LIns* lr = w.ldpGuardRecordExit(rec); - LIns* nested = w.jtUnoptimizable(w.eqiN(w.ldiVMSideExitField(lr, exitType), NESTED_EXIT)); - - /* - * If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard - * with that guard. If we mismatch on a tree call guard, this will contain the last - * non-nested guard we encountered, which is the innermost loop or branch guard. - */ - w.stStateField(lr, lastTreeExitGuard); - LIns* done1 = w.j(NULL); - - /* - * The tree exited on a nested guard. This only occurs once a tree call guard mismatches - * and we unwind the tree call stack. We store the first (innermost) tree call guard in state - * and we will try to grow the outer tree the failing call was in starting at that guard. - */ - w.label(nested); - LIns* done2 = w.jfUnoptimizable(w.eqp0(w.ldpStateField(lastTreeCallGuard))); - w.stStateField(lr, lastTreeCallGuard); - w.stStateField(w.addp(w.ldpStateField(rp), - w.i2p(w.lshiN(w.ldiVMSideExitField(lr, calldepth), - sizeof(void*) == 4 ? 2 : 3))), - rpAtLastTreeCall); - w.label(done1, done2); - - /* - * Keep updating outermostTreeExit so that TracerState always contains the most recent - * side exit. - */ - w.stStateField(lr, outermostTreeExitGuard); - - /* Read back all registers, in case the called tree changed any of them. */ -#ifdef DEBUG - JSValueType* map; - size_t i; - map = exit->globalTypeMap(); - for (i = 0; i < exit->numGlobalSlots; i++) - JS_ASSERT(map[i] != JSVAL_TYPE_BOXED); - map = exit->stackTypeMap(); - for (i = 0; i < exit->numStackSlots; i++) - JS_ASSERT(map[i] != JSVAL_TYPE_BOXED); -#endif - - /* The inner tree may modify currently-tracked upvars, so flush everything. */ - ClearSlotsVisitor visitor(tracker); - VisitStackSlots(visitor, cx, callDepth); - SlotList& gslots = *tree->globalSlots; - for (unsigned i = 0; i < gslots.length(); i++) { - unsigned slot = gslots[i]; - Value* vp = &globalObj->getSlotRef(slot); - tracker.set(vp, NULL); - } - - /* Set stack slots from the innermost frame. */ - importTypeMap.setLength(NativeStackSlots(cx, callDepth)); - unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots; - for (unsigned i = 0; i < exit->numStackSlots; i++) - importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i]; - importStackSlots = importTypeMap.length(); - JS_ASSERT(importStackSlots == NativeStackSlots(cx, callDepth)); - - /* - * Bug 502604 - It is illegal to extend from the outer typemap without - * first extending from the inner. Make a new typemap here. - */ - BuildGlobalTypeMapFromInnerTree(importTypeMap, exit); - - importGlobalSlots = importTypeMap.length() - importStackSlots; - JS_ASSERT(importGlobalSlots == tree->globalSlots->length()); - - /* Restore sp and rp to their original values (we still have them in a register). */ - if (callDepth > 0) { - w.stStateField(lirbuf->sp, sp); - w.stStateField(lirbuf->rp, rp); - } - - /* - * Guard that we come out of the inner tree along the same side exit we came out when - * we called the inner tree at recording time. - */ - VMSideExit* nestedExit = snapshot(NESTED_EXIT); - JS_ASSERT(exit->exitType == LOOP_EXIT); - guard(true, w.eqp(lr, w.nameImmpNonGC(exit)), nestedExit); - debug_only_printf(LC_TMTreeVis, "TREEVIS TREECALL INNER=%p EXIT=%p GUARD=%p\n", (void*)inner, - (void*)nestedExit, (void*)exit); - - /* Register us as a dependent tree of the inner tree. */ - inner->dependentTrees.addUnique(fragment->root); - tree->linkedTrees.addUnique(inner); -} - -/* Add a if/if-else control-flow merge point to the list of known merge points. */ -JS_REQUIRES_STACK void -TraceRecorder::trackCfgMerges(jsbytecode* pc) -{ - /* If we hit the beginning of an if/if-else, then keep track of the merge point after it. */ - JS_ASSERT((*pc == JSOP_IFEQ) || (*pc == JSOP_IFEQX)); - jssrcnote* sn = js_GetSrcNote(cx->fp()->script(), pc); - if (sn != NULL) { - if (SN_TYPE(sn) == SRC_IF) { - cfgMerges.add((*pc == JSOP_IFEQ) - ? pc + GET_JUMP_OFFSET(pc) - : pc + GET_JUMPX_OFFSET(pc)); - } else if (SN_TYPE(sn) == SRC_IF_ELSE) - cfgMerges.add(pc + js_GetSrcNoteOffset(sn, 0)); - } -} - -/* - * Invert the direction of the guard if this is a loop edge that is not - * taken (thin loop). - */ -JS_REQUIRES_STACK void -TraceRecorder::emitIf(jsbytecode* pc, bool cond, LIns* x) -{ - ExitType exitType; - JS_ASSERT(isCond(x)); - if (IsLoopEdge(pc, (jsbytecode*)tree->ip)) { - exitType = LOOP_EXIT; - - /* - * If we are about to walk out of the loop, generate code for the - * inverse loop condition, pretending we recorded the case that stays - * on trace. - */ - if ((*pc == JSOP_IFEQ || *pc == JSOP_IFEQX) == cond) { - JS_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX || *pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - debug_only_print0(LC_TMTracer, - "Walking out of the loop, terminating it anyway.\n"); - cond = !cond; - } - - /* - * Conditional guards do not have to be emitted if the condition is - * constant. We make a note whether the loop condition is true or false - * here, so we later know whether to emit a loop edge or a loop end. - */ - if (x->isImmI()) { - pendingLoop = (x->immI() == int32(cond)); - return; - } - } else { - exitType = BRANCH_EXIT; - } - if (!x->isImmI()) - guard(cond, x, exitType); -} - -/* Emit code for a fused IFEQ/IFNE. */ -JS_REQUIRES_STACK void -TraceRecorder::fuseIf(jsbytecode* pc, bool cond, LIns* x) -{ - if (*pc == JSOP_IFEQ || *pc == JSOP_IFNE) { - emitIf(pc, cond, x); - if (*pc == JSOP_IFEQ) - trackCfgMerges(pc); - } -} - -/* Check whether we have reached the end of the trace. */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::checkTraceEnd(jsbytecode *pc) -{ - if (IsLoopEdge(pc, (jsbytecode*)tree->ip)) { - /* - * If we compile a loop, the trace should have a zero stack balance at - * the loop edge. Currently we are parked on a comparison op or - * IFNE/IFEQ, so advance pc to the loop header and adjust the stack - * pointer and pretend we have reached the loop header. - */ - if (pendingLoop) { - JS_ASSERT(!cx->fp()->hasImacropc() && (pc == cx->regs().pc || pc == cx->regs().pc + 1)); - FrameRegs orig = cx->regs(); - - cx->regs().pc = (jsbytecode*)tree->ip; - cx->regs().sp = cx->fp()->base() + tree->spOffsetAtEntry; - - JSContext* localcx = cx; - AbortableRecordingStatus ars = closeLoop(); - localcx->regs() = orig; - return ars; - } - - return endLoop(); - } - return ARECORD_CONTINUE; -} - -/* - * Check whether the shape of the global object has changed. The return value - * indicates whether the recorder is still active. If 'false', any active - * recording has been aborted and the JIT may have been reset. - */ -static JS_REQUIRES_STACK bool -CheckGlobalObjectShape(JSContext* cx, TraceMonitor* tm, JSObject* globalObj, - uint32 *shape = NULL, SlotList** slots = NULL) -{ - if (tm->needFlush) { - ResetJIT(cx, tm, FR_DEEP_BAIL); - return false; - } - - if (globalObj->numSlots() > MAX_GLOBAL_SLOTS) { - if (tm->recorder) - AbortRecording(cx, "too many slots in global object"); - return false; - } - - /* - * The global object must have a unique shape. That way, if an operand - * isn't the global at record time, a shape guard suffices to ensure - * that it isn't the global at run time. - */ - if (!globalObj->hasOwnShape()) { - if (!globalObj->globalObjectOwnShapeChange(cx)) { - debug_only_print0(LC_TMTracer, - "Can't record: failed to give globalObj a unique shape.\n"); - return false; - } - } - - uint32 globalShape = globalObj->shape(); - - if (tm->recorder) { - TreeFragment* root = tm->recorder->getFragment()->root; - - /* Check the global shape matches the recorder's treeinfo's shape. */ - if (globalObj != root->globalObj || globalShape != root->globalShape) { - AUDIT(globalShapeMismatchAtEntry); - debug_only_printf(LC_TMTracer, - "Global object/shape mismatch (%p/%u vs. %p/%u), flushing cache.\n", - (void*)globalObj, globalShape, (void*)root->globalObj, - root->globalShape); - Backoff(tm, (jsbytecode*) root->ip); - ResetJIT(cx, tm, FR_GLOBAL_SHAPE_MISMATCH); - return false; - } - if (shape) - *shape = globalShape; - if (slots) - *slots = root->globalSlots; - return true; - } - - /* No recorder, search for a tracked global-state (or allocate one). */ - for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) { - GlobalState &state = tm->globalStates[i]; - - if (state.globalShape == uint32(-1)) { - state.globalObj = globalObj; - state.globalShape = globalShape; - JS_ASSERT(state.globalSlots); - JS_ASSERT(state.globalSlots->length() == 0); - } - - if (state.globalObj == globalObj && state.globalShape == globalShape) { - if (shape) - *shape = globalShape; - if (slots) - *slots = state.globalSlots; - return true; - } - } - - /* No currently-tracked-global found and no room to allocate, abort. */ - AUDIT(globalShapeMismatchAtEntry); - debug_only_printf(LC_TMTracer, - "No global slotlist for global shape %u, flushing cache.\n", - globalShape); - ResetJIT(cx, tm, FR_GLOBALS_FULL); - return false; -} - -/* - * Return whether or not the recorder could be started. If 'false', the JIT has - * been reset in response to an OOM. - */ -bool JS_REQUIRES_STACK -TraceRecorder::startRecorder(JSContext* cx, TraceMonitor *tm, VMSideExit* anchor, VMFragment* f, - unsigned stackSlots, unsigned ngslots, - JSValueType* typeMap, VMSideExit* expectedInnerExit, - JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc, - bool speculate) -{ - JS_ASSERT(!tm->needFlush); - JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f); - - tm->recorder = cx->new_(cx, tm, anchor, f, stackSlots, ngslots, typeMap, - expectedInnerExit, outerScript, outerPC, outerArgc, - speculate); - - if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(cx, tm)) { - ResetJIT(cx, tm, FR_OOM); - return false; - } - - return true; -} - -static void -TrashTree(TreeFragment* f) -{ - JS_ASSERT(f == f->root); - debug_only_printf(LC_TMTreeVis, "TREEVIS TRASH FRAG=%p\n", (void*)f); - - if (!f->code()) - return; - AUDIT(treesTrashed); - debug_only_print0(LC_TMTracer, "Trashing tree info.\n"); - f->setCode(NULL); - TreeFragment** data = f->dependentTrees.data(); - unsigned length = f->dependentTrees.length(); - for (unsigned n = 0; n < length; ++n) - TrashTree(data[n]); - data = f->linkedTrees.data(); - length = f->linkedTrees.length(); - for (unsigned n = 0; n < length; ++n) - TrashTree(data[n]); -} - -static void -SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee) -{ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - /* Assert that we have a correct sp distance from cx->fp()->slots in fi. */ - StackFrame* const fp = cx->fp(); - JS_ASSERT_IF(!fi.imacpc, - js_ReconstructStackDepth(cx, fp->script(), fi.pc) == - uintN(fi.spdist - fp->numFixed())); - - /* Use the just-flushed prev-frame to get the callee function. */ - JSFunction* newfun = callee->getFunctionPrivate(); - JSScript* newscript = newfun->script(); - - /* Fill in the prev-frame's sp. */ - FrameRegs ®s = cx->regs(); - regs.sp = fp->slots() + fi.spdist; - regs.pc = fi.pc; - if (fi.imacpc) - fp->setImacropc(fi.imacpc); - - /* Set argc/flags then mimic JSOP_CALL. */ - uintN argc = fi.get_argc(); - uint32 flags = fi.is_constructing() ? StackFrame::CONSTRUCTING : 0; - - /* Get pointer to new/frame/slots, prepare arguments. */ - StackFrame *newfp = cx->stack.getInlineFrame(cx, regs.sp, argc, newfun, - newscript, &flags); - - /* Initialize frame; do not need to initialize locals. */ - newfp->initCallFrame(cx, *callee, newfun, argc, flags); - -#ifdef DEBUG - /* The stack is conservatively marked, so we can leave non-canonical args uninitialized. */ - if (newfp->hasOverflowArgs()) { - Value *beg = newfp->actualArgs() - 2; - Value *end = newfp->actualArgs() + newfp->numFormalArgs(); - for (Value *p = beg; p != end; ++p) - p->setMagic(JS_ARG_POISON); - } - - /* These should be initialized by FlushNativeStackFrame. */ - newfp->thisValue().setMagic(JS_THIS_POISON); - newfp->setScopeChainNoCallObj(*StackFrame::sInvalidScopeChain); -#endif - - /* Officially push the frame. */ - cx->stack.pushInlineFrame(newscript, newfp, cx->regs()); - - /* Call object will be set by FlushNativeStackFrame. */ - - /* Call the debugger hook if present. */ - JSInterpreterHook hook = cx->debugHooks->callHook; - if (hook) { - newfp->setHookData(hook(cx, Jsvalify(newfp), JS_TRUE, 0, - cx->debugHooks->callHookData)); - } -} - -static JS_REQUIRES_STACK bool -RecordTree(JSContext* cx, TraceMonitor* tm, TreeFragment* first, - JSScript* outerScript, jsbytecode* outerPC, - uint32 outerArgc, SlotList* globalSlots) -{ - /* Try to find an unused peer fragment, or allocate a new one. */ - JS_ASSERT(first->first == first); - TreeFragment* f = NULL; - size_t count = 0; - for (TreeFragment* peer = first; peer; peer = peer->peer, ++count) { - if (!peer->code()) - f = peer; - } - if (!f) - f = AddNewPeerToPeerList(tm, first); - JS_ASSERT(f->root == f); - - /* Disable speculation if we are starting to accumulate a lot of trees. */ - bool speculate = count < MAXPEERS-1; - - /* save a local copy for use after JIT flush */ - const void* localRootIP = f->root->ip; - - /* Make sure the global type map didn't change on us. */ - if (!CheckGlobalObjectShape(cx, tm, f->globalObj)) { - Backoff(tm, (jsbytecode*) localRootIP); - return false; - } - - AUDIT(recorderStarted); - - if (tm->outOfMemory() || - OverfullJITCache(cx, tm) || - !tm->tracedScripts.put(cx->fp()->script())) - { - if (!OverfullJITCache(cx, tm)) - js_ReportOutOfMemory(cx); - Backoff(tm, (jsbytecode*) f->root->ip); - ResetJIT(cx, tm, FR_OOM); - debug_only_print0(LC_TMTracer, - "Out of memory recording new tree, flushing cache.\n"); - return false; - } - - JS_ASSERT(!f->code()); - - f->initialize(cx, globalSlots, speculate); - -#ifdef DEBUG - AssertTreeIsUnique(tm, f); -#endif -#ifdef JS_JIT_SPEW - debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d", - (void*)f, f->ip, f->treeFileName, f->treeLineNumber, - FramePCOffset(cx, cx->fp())); - debug_only_print0(LC_TMTreeVis, " STACK=\""); - for (unsigned i = 0; i < f->nStackTypes; i++) - debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[i])); - debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); - for (unsigned i = 0; i < f->nGlobalTypes(); i++) - debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[f->nStackTypes + i])); - debug_only_print0(LC_TMTreeVis, "\"\n"); -#endif - - /* Recording primary trace. */ - return TraceRecorder::startRecorder(cx, tm, NULL, f, f->nStackTypes, - f->globalSlots->length(), - f->typeMap.data(), NULL, - outerScript, outerPC, outerArgc, speculate); -} - -static JS_REQUIRES_STACK TypeConsensus -FindLoopEdgeTarget(JSContext* cx, TraceMonitor* tm, VMSideExit* exit, TreeFragment** peerp) -{ - TreeFragment* from = exit->root(); - - JS_ASSERT(from->code()); - Oracle* oracle = tm->oracle; - - TypeMap typeMap(NULL, oracle); - FullMapFromExit(typeMap, exit); - JS_ASSERT(typeMap.length() - exit->numStackSlots == from->nGlobalTypes()); - - /* Mark all double slots as undemotable */ - uint16* gslots = from->globalSlots->data(); - for (unsigned i = 0; i < typeMap.length(); i++) { - if (typeMap[i] == JSVAL_TYPE_DOUBLE) { - if (i < from->nStackTypes) - oracle->markStackSlotUndemotable(cx, i, from->ip); - else if (i >= exit->numStackSlots) - oracle->markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]); - } - } - - JS_ASSERT(exit->exitType == UNSTABLE_LOOP_EXIT); - - TreeFragment* firstPeer = from->first; - - for (TreeFragment* peer = firstPeer; peer; peer = peer->peer) { - if (!peer->code()) - continue; - JS_ASSERT(peer->argc == from->argc); - JS_ASSERT(exit->numStackSlots == peer->nStackTypes); - TypeConsensus consensus = TypeMapLinkability(cx, tm, typeMap, peer); - if (consensus == TypeConsensus_Okay || consensus == TypeConsensus_Undemotes) { - *peerp = peer; - return consensus; - } - } - - return TypeConsensus_Bad; -} - -static JS_REQUIRES_STACK bool -AttemptToStabilizeTree(JSContext* cx, TraceMonitor* tm, JSObject* globalObj, VMSideExit* exit, - JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc) -{ - if (tm->needFlush) { - ResetJIT(cx, tm, FR_DEEP_BAIL); - return false; - } - - TreeFragment* from = exit->root(); - - TreeFragment* peer = NULL; - TypeConsensus consensus = FindLoopEdgeTarget(cx, tm, exit, &peer); - if (consensus == TypeConsensus_Okay) { - JS_ASSERT(from->globalSlots == peer->globalSlots); - JS_ASSERT_IF(exit->exitType == UNSTABLE_LOOP_EXIT, - from->nStackTypes == peer->nStackTypes); - JS_ASSERT(exit->numStackSlots == peer->nStackTypes); - /* Patch this exit to its peer */ - if (!JoinPeers(tm->assembler, exit, peer)) - return false; - /* - * Update peer global types. The |from| fragment should already be updated because it on - * the execution path, and somehow connected to the entry trace. - */ - if (peer->nGlobalTypes() < peer->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, peer); - JS_ASSERT(from->nGlobalTypes() == from->globalSlots->length()); - /* This exit is no longer unstable, so remove it. */ - if (exit->exitType == UNSTABLE_LOOP_EXIT) - from->removeUnstableExit(exit); - debug_only_stmt(DumpPeerStability(tm, peer->ip, globalObj, from->globalShape, from->argc);) - return false; - } else if (consensus == TypeConsensus_Undemotes) { - /* The original tree is unconnectable, so trash it. */ - TrashTree(peer); - return false; - } - - SlotList *globalSlots = from->globalSlots; - - JS_ASSERT(from == from->root); - - /* If this tree has been blacklisted, don't try to record a new one. */ - if (*(jsbytecode*)from->ip == JSOP_NOTRACE) - return false; - - return RecordTree(cx, tm, from->first, outerScript, outerPC, outerArgc, globalSlots); -} - -static JS_REQUIRES_STACK VMFragment* -CreateBranchFragment(JSContext* cx, TraceMonitor* tm, TreeFragment* root, VMSideExit* anchor) -{ - verbose_only( - uint32_t profFragID = (LogController.lcbits & LC_FragProfile) - ? (++(tm->lastFragID)) : 0; - ) - - VMFragment* f = new (*tm->dataAlloc) VMFragment(cx->regs().pc verbose_only(, profFragID)); - - debug_only_printf(LC_TMTreeVis, "TREEVIS CREATEBRANCH ROOT=%p FRAG=%p PC=%p FILE=\"%s\"" - " LINE=%d ANCHOR=%p OFFS=%d\n", - (void*)root, (void*)f, (void*)cx->regs().pc, cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), (void*)anchor, - FramePCOffset(cx, cx->fp())); - verbose_only( tm->branches = new (*tm->dataAlloc) Seq(f, tm->branches); ) - - f->root = root; - if (anchor) - anchor->target = f; - return f; -} - -static JS_REQUIRES_STACK bool -AttemptToExtendTree(JSContext* cx, TraceMonitor* tm, VMSideExit* anchor, VMSideExit* exitedFrom, - JSScript *outerScript, jsbytecode* outerPC -#ifdef MOZ_TRACEVIS - , TraceVisStateObj* tvso = NULL -#endif - ) -{ - JS_ASSERT(!tm->recorder); - - if (tm->needFlush) { - ResetJIT(cx, tm, FR_DEEP_BAIL); -#ifdef MOZ_TRACEVIS - if (tvso) tvso->r = R_FAIL_EXTEND_FLUSH; -#endif - return false; - } - - TreeFragment* f = anchor->root(); - JS_ASSERT(f->code()); - - /* - * Don't grow trees above a certain size to avoid code explosion due to - * tail duplication. - */ - if (f->branchCount >= MAX_BRANCHES) { -#ifdef JS_METHODJIT - if (cx->methodJitEnabled && cx->profilingEnabled) - Blacklist((jsbytecode *)f->ip); -#endif -#ifdef MOZ_TRACEVIS - if (tvso) tvso->r = R_FAIL_EXTEND_MAX_BRANCHES; -#endif - return false; - } - - VMFragment* c = (VMFragment*)anchor->target; - if (!c) { - c = CreateBranchFragment(cx, tm, f, anchor); - } else { - /* - * If we are recycling a fragment, it might have a different ip so reset it - * here. This can happen when attaching a branch to a NESTED_EXIT, which - * might extend along separate paths (i.e. after the loop edge, and after a - * return statement). - */ - c->ip = cx->regs().pc; - JS_ASSERT(c->root == f); - } - - debug_only_printf(LC_TMTracer, - "trying to attach another branch to the tree (hits = %d)\n", c->hits()); - - int32_t& hits = c->hits(); - int32_t maxHits = HOTEXIT + MAXEXIT; - if (outerPC || (hits++ >= HOTEXIT && hits <= maxHits)) { - /* start tracing secondary trace from this point */ - unsigned stackSlots; - unsigned ngslots; - JSValueType* typeMap; - TypeMap fullMap(NULL, tm->oracle); - if (!exitedFrom) { - /* - * If we are coming straight from a simple side exit, just use that - * exit's type map as starting point. - */ - ngslots = anchor->numGlobalSlots; - stackSlots = anchor->numStackSlots; - typeMap = anchor->fullTypeMap(); - } else { - /* - * If we side-exited on a loop exit and continue on a nesting - * guard, the nesting guard (anchor) has the type information for - * everything below the current scope, and the actual guard we - * exited from has the types for everything in the current scope - * (and whatever it inlined). We have to merge those maps here. - */ - VMSideExit* e1 = anchor; - VMSideExit* e2 = exitedFrom; - fullMap.add(e1->stackTypeMap(), e1->numStackSlotsBelowCurrentFrame); - fullMap.add(e2->stackTypeMap(), e2->numStackSlots); - stackSlots = fullMap.length(); - ngslots = BuildGlobalTypeMapFromInnerTree(fullMap, e2); - JS_ASSERT(ngslots >= e1->numGlobalSlots); // inner tree must have all globals - JS_ASSERT(ngslots == fullMap.length() - stackSlots); - typeMap = fullMap.data(); - } - JS_ASSERT(ngslots >= anchor->numGlobalSlots); - bool rv = TraceRecorder::startRecorder(cx, tm, anchor, c, stackSlots, ngslots, typeMap, - exitedFrom, outerScript, outerPC, f->argc, - hits < maxHits); -#ifdef MOZ_TRACEVIS - if (!rv && tvso) - tvso->r = R_FAIL_EXTEND_START; -#endif - return rv; - } -#ifdef MOZ_TRACEVIS - if (tvso) tvso->r = R_FAIL_EXTEND_COLD; -#endif - return false; -} - -static JS_REQUIRES_STACK bool -ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount, - VMSideExit** innermostNestedGuardp, VMSideExit** lrp); - -static inline MonitorResult -RecordingIfTrue(bool b) -{ - return b ? MONITOR_RECORDING : MONITOR_NOT_RECORDING; -} - -/* - * A postcondition of recordLoopEdge is that if recordLoopEdge does not return - * MONITOR_RECORDING, the recording has been aborted. - */ -JS_REQUIRES_STACK MonitorResult -TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount) -{ - TraceMonitor* tm = r->traceMonitor; - - /* Process needFlush and deep abort requests. */ - if (tm->needFlush) { - ResetJIT(cx, tm, FR_DEEP_BAIL); - return MONITOR_NOT_RECORDING; - } - - JS_ASSERT(r->fragment && !r->fragment->lastIns); - TreeFragment* root = r->fragment->root; - TreeFragment* first = LookupOrAddLoop(tm, cx->regs().pc, root->globalObj, - root->globalShape, entryFrameArgc(cx)); - - /* - * Make sure the shape of the global object still matches (this might flush - * the JIT cache). - */ - JSObject* globalObj = cx->fp()->scopeChain().getGlobal(); - uint32 globalShape = -1; - SlotList* globalSlots = NULL; - if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) { - JS_ASSERT(!tm->recorder); - return MONITOR_NOT_RECORDING; - } - - debug_only_printf(LC_TMTracer, - "Looking for type-compatible peer (%s:%d@%d)\n", - cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp())); - - // Find a matching inner tree. If none can be found, compile one. - TreeFragment* f = r->findNestedCompatiblePeer(first); - if (!f || !f->code()) { - AUDIT(noCompatInnerTrees); - - TreeFragment* outerFragment = root; - JSScript* outerScript = outerFragment->script; - jsbytecode* outerPC = (jsbytecode*) outerFragment->ip; - uint32 outerArgc = outerFragment->argc; - JS_ASSERT(entryFrameArgc(cx) == first->argc); - - if (AbortRecording(cx, "No compatible inner tree") == JIT_RESET) - return MONITOR_NOT_RECORDING; - - return RecordingIfTrue(RecordTree(cx, tm, first, - outerScript, outerPC, outerArgc, globalSlots)); - } - - AbortableRecordingStatus status = r->attemptTreeCall(f, inlineCallCount); - if (status == ARECORD_CONTINUE) - return MONITOR_RECORDING; - if (status == ARECORD_ERROR) { - if (tm->recorder) - AbortRecording(cx, "Error returned while recording loop edge"); - return MONITOR_ERROR; - } - JS_ASSERT(status == ARECORD_ABORTED && !tm->recorder); - return MONITOR_NOT_RECORDING; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount) -{ - adjustCallerTypes(f); - prepareTreeCall(f); - -#ifdef DEBUG - uintN oldInlineCallCount = inlineCallCount; -#endif - - JSContext *localCx = cx; - TraceMonitor *localtm = traceMonitor; - - // Refresh the import type map so the tracker can reimport values after the - // call with their correct types. The inner tree must not change the type of - // any variable in a frame above the current one (i.e., upvars). - // - // Note that DetermineTypesVisitor may call determineSlotType, which may - // read from the (current, stale) import type map, but this is safe here. - // The reason is that determineSlotType will read the import type map only - // if there is not a tracker instruction for that value, which means that - // value has not been written yet, so that type map entry is up to date. - importTypeMap.setLength(NativeStackSlots(cx, callDepth)); - DetermineTypesVisitor visitor(*this, importTypeMap.data()); - VisitStackSlots(visitor, cx, callDepth); - - VMSideExit* innermostNestedGuard = NULL; - VMSideExit* lr; - bool ok = ExecuteTree(cx, traceMonitor, f, inlineCallCount, &innermostNestedGuard, &lr); - - /* - * If ExecuteTree reentered the interpreter, it may have killed |this| - * and/or caused an error, which must be propagated. - */ - JS_ASSERT_IF(localtm->recorder, localtm->recorder == this); - if (!ok) - return ARECORD_ERROR; - if (!localtm->recorder) - return ARECORD_ABORTED; - - if (!lr) { - AbortRecording(cx, "Couldn't call inner tree"); - return ARECORD_ABORTED; - } - - TreeFragment* outerFragment = tree; - JSScript* outerScript = outerFragment->script; - jsbytecode* outerPC = (jsbytecode*) outerFragment->ip; - switch (lr->exitType) { - case LOOP_EXIT: - /* If the inner tree exited on an unknown loop exit, grow the tree around it. */ - if (innermostNestedGuard) { - if (AbortRecording(cx, "Inner tree took different side exit, abort current " - "recording and grow nesting tree") == JIT_RESET) { - return ARECORD_ABORTED; - } - return AttemptToExtendTree(localCx, localtm, - innermostNestedGuard, lr, outerScript, outerPC) - ? ARECORD_CONTINUE - : ARECORD_ABORTED; - } - - JS_ASSERT(oldInlineCallCount == inlineCallCount); - - /* Emit a call to the inner tree and continue recording the outer tree trace. */ - emitTreeCall(f, lr); - return ARECORD_CONTINUE; - - case UNSTABLE_LOOP_EXIT: - { - /* Abort recording so the inner loop can become type stable. */ - JSObject* _globalObj = globalObj; - if (AbortRecording(cx, "Inner tree is trying to stabilize, " - "abort outer recording") == JIT_RESET) { - return ARECORD_ABORTED; - } - return AttemptToStabilizeTree(localCx, localtm, _globalObj, lr, outerScript, outerPC, - outerFragment->argc) - ? ARECORD_CONTINUE - : ARECORD_ABORTED; - } - - case MUL_ZERO_EXIT: - case OVERFLOW_EXIT: - if (lr->exitType == MUL_ZERO_EXIT) - traceMonitor->oracle->markInstructionSlowZeroTest(cx->regs().pc); - else - traceMonitor->oracle->markInstructionUndemotable(cx->regs().pc); - /* FALL THROUGH */ - case BRANCH_EXIT: - /* Abort recording the outer tree, extend the inner tree. */ - if (AbortRecording(cx, "Inner tree is trying to grow, " - "abort outer recording") == JIT_RESET) { - return ARECORD_ABORTED; - } - return AttemptToExtendTree(localCx, localtm, lr, NULL, outerScript, outerPC) - ? ARECORD_CONTINUE - : ARECORD_ABORTED; - - case NESTED_EXIT: - JS_NOT_REACHED("NESTED_EXIT should be replaced by innermost side exit"); - default: - debug_only_printf(LC_TMTracer, "exit_type=%s\n", getExitName(lr->exitType)); - AbortRecording(cx, "Inner tree not suitable for calling"); - return ARECORD_ABORTED; - } -} - -static inline bool -IsEntryTypeCompatible(const Value &v, JSValueType type) -{ - bool ok; - - JS_ASSERT(type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET); - JS_ASSERT(type != JSVAL_TYPE_OBJECT); /* JSVAL_TYPE_OBJECT does not belong in a type map */ - - if (v.isInt32()) { - ok = (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_DOUBLE); - - } else if (v.isDouble()) { - int32_t _; - ok = (type == JSVAL_TYPE_DOUBLE) || - (type == JSVAL_TYPE_INT32 && JSDOUBLE_IS_INT32(v.toDouble(), &_)); - - } else if (v.isObject()) { - ok = v.toObject().isFunction() - ? type == JSVAL_TYPE_FUNOBJ - : type == JSVAL_TYPE_NONFUNOBJ; - - } else { - ok = v.extractNonDoubleObjectTraceType() == type; - } -#ifdef DEBUG - char ttag = TypeToChar(type); - char vtag = ValueToTypeChar(v); - debug_only_printf(LC_TMTracer, "%c/%c ", vtag, ttag); - if (!ok) - debug_only_printf(LC_TMTracer, "%s", "(incompatible types)"); -#endif - return ok; -} - -static inline bool -IsFrameObjPtrTypeCompatible(void *p, StackFrame *fp, JSValueType type) -{ - debug_only_printf(LC_TMTracer, "%c/%c ", TypeToChar(type), - (p == fp->addressOfScopeChain() || fp->hasArgsObj()) - ? TypeToChar(JSVAL_TYPE_NONFUNOBJ) - : TypeToChar(JSVAL_TYPE_NULL)); - if (p == fp->addressOfScopeChain()) - return type == JSVAL_TYPE_NONFUNOBJ; - JS_ASSERT(p == fp->addressOfArgs()); - JS_ASSERT(type == JSVAL_TYPE_NONFUNOBJ || type == JSVAL_TYPE_NULL); - return fp->hasArgsObj() == (type == JSVAL_TYPE_NONFUNOBJ); -} - -class TypeCompatibilityVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - JSContext *mCx; - Oracle *mOracle; - JSValueType *mTypeMap; - unsigned mStackSlotNum; - bool mOk; -public: - TypeCompatibilityVisitor (TraceRecorder &recorder, - JSValueType *typeMap) : - mRecorder(recorder), - mCx(mRecorder.cx), - mOracle(recorder.traceMonitor->oracle), - mTypeMap(typeMap), - mStackSlotNum(0), - mOk(true) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - debug_only_printf(LC_TMTracer, "global%d=", n); - if (!IsEntryTypeCompatible(*vp, *mTypeMap)) { - mOk = false; - } else if (!IsPromotedInt32(mRecorder.get(vp)) && *mTypeMap == JSVAL_TYPE_INT32) { - mOracle->markGlobalSlotUndemotable(mCx, slot); - mOk = false; - } else if (vp->isInt32() && *mTypeMap == JSVAL_TYPE_DOUBLE) { - mOracle->markGlobalSlotUndemotable(mCx, slot); - } - mTypeMap++; - } - - /* - * For the below two methods, one may be inclined to 'return false' early - * when mOk is set to 'false'. Don't do that. It is very important to run - * through the whole list to let all mis-matching slots get marked - * undemotable in the oracle. - */ - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - for (size_t i = 0; i < count; ++i) { - debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i)); - if (!IsEntryTypeCompatible(*vp, *mTypeMap)) { - mOk = false; - } else if (!IsPromotedInt32(mRecorder.get(vp)) && *mTypeMap == JSVAL_TYPE_INT32) { - mOracle->markStackSlotUndemotable(mCx, mStackSlotNum); - mOk = false; - } else if (vp->isInt32() && *mTypeMap == JSVAL_TYPE_DOUBLE) { - mOracle->markStackSlotUndemotable(mCx, mStackSlotNum); - } - vp++; - mTypeMap++; - mStackSlotNum++; - } - return true; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame* fp) { - debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0); - if (!IsFrameObjPtrTypeCompatible(p, fp, *mTypeMap)) - mOk = false; - mTypeMap++; - mStackSlotNum++; - return true; - } - - bool isOk() { - return mOk; - } -}; - -JS_REQUIRES_STACK TreeFragment* -TraceRecorder::findNestedCompatiblePeer(TreeFragment* f) -{ - unsigned int ngslots = tree->globalSlots->length(); - - for (; f != NULL; f = f->peer) { - if (!f->code()) - continue; - - debug_only_printf(LC_TMTracer, "checking nested types %p: ", (void*)f); - - if (ngslots > f->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, f); - - /* - * Determine whether the typemap of the inner tree matches the outer - * tree's current state. If the inner tree expects an integer, but the - * outer tree doesn't guarantee an integer for that slot, we mark the - * slot undemotable and mismatch here. This will force a new tree to be - * compiled that accepts a double for the slot. If the inner tree - * expects a double, but the outer tree has an integer, we can proceed, - * but we mark the location undemotable. - */ - TypeCompatibilityVisitor visitor(*this, f->typeMap.data()); - VisitSlots(visitor, cx, 0, *tree->globalSlots); - - debug_only_printf(LC_TMTracer, " %s\n", visitor.isOk() ? "match" : ""); - if (visitor.isOk()) - return f; - } - - return NULL; -} - -class CheckEntryTypeVisitor : public SlotVisitorBase -{ - bool mOk; - JSValueType *mTypeMap; -public: - CheckEntryTypeVisitor(JSValueType *typeMap) : - mOk(true), - mTypeMap(typeMap) - {} - - JS_ALWAYS_INLINE void checkSlot(const Value &v, char const *name, int i) { - debug_only_printf(LC_TMTracer, "%s%d=", name, i); - JS_ASSERT(*(uint8_t*)mTypeMap != 0xCD); - mOk = IsEntryTypeCompatible(v, *mTypeMap++); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE void - visitGlobalSlot(Value *vp, unsigned n, unsigned slot) { - if (mOk) - checkSlot(*vp, "global", n); - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(Value *vp, size_t count, StackFrame* fp) { - for (size_t i = 0; i < count; ++i) { - if (!mOk) - break; - checkSlot(*vp++, stackSlotKind(), i); - } - return mOk; - } - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitFrameObjPtr(void* p, StackFrame *fp) { - debug_only_printf(LC_TMTracer, "%s%d=", stackSlotKind(), 0); - JS_ASSERT(*(uint8_t*)mTypeMap != 0xCD); - return mOk = IsFrameObjPtrTypeCompatible(p, fp, *mTypeMap++); - } - - bool isOk() { - return mOk; - } -}; - -/** - * Check if types are usable for trace execution. - * - * @param cx Context. - * @param f Tree of peer we're testing. - * @return True if compatible (with or without demotions), false otherwise. - */ -static JS_REQUIRES_STACK bool -CheckEntryTypes(JSContext* cx, JSObject* globalObj, TreeFragment* f) -{ - unsigned int ngslots = f->globalSlots->length(); - - JS_ASSERT(f->nStackTypes == NativeStackSlots(cx, 0)); - - if (ngslots > f->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, f); - - JS_ASSERT(f->typeMap.length() == NativeStackSlots(cx, 0) + ngslots); - JS_ASSERT(f->typeMap.length() == f->nStackTypes + ngslots); - JS_ASSERT(f->nGlobalTypes() == ngslots); - - CheckEntryTypeVisitor visitor(f->typeMap.data()); - VisitSlots(visitor, cx, 0, *f->globalSlots); - - debug_only_print0(LC_TMTracer, "\n"); - return visitor.isOk(); -} - -/** - * Find an acceptable entry tree given a PC. - * - * @param cx Context. - * @param globalObj Global object. - * @param f First peer fragment. - * @param nodemote If true, will try to find a peer that does not require demotion. - * @out count Number of fragments consulted. - */ -static JS_REQUIRES_STACK TreeFragment* -FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, TreeFragment* f, uintN& count) -{ - count = 0; - for (; f != NULL; f = f->peer) { - if (!f->code()) - continue; - debug_only_printf(LC_TMTracer, - "checking vm types %p (ip: %p): ", (void*)f, f->ip); - if (CheckEntryTypes(cx, globalObj, f)) - return f; - ++count; - } - return NULL; -} - -/* - * For the native stacks and global frame, reuse the storage in |tm->storage|. - * This reuse depends on the invariant that only one trace uses |tm->storage| - * at a time. This is subtly correct in case of deep bail; see the comment - * about "clobbering deep bails" in DeepBail. - */ -JS_ALWAYS_INLINE -TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f, - uintN& inlineCallCount, VMSideExit** innermostNestedGuardp) - : cx(cx), - traceMonitor(tm), - stackBase(tm->storage->stack()), - sp(stackBase + f->nativeStackBase / sizeof(double)), - eos(tm->storage->global()), - callstackBase(tm->storage->callstack()), - sor(callstackBase), - rp(callstackBase), - eor(callstackBase + JS_MIN(TraceNativeStorage::MAX_CALL_STACK_ENTRIES, - StackSpace::MAX_INLINE_CALLS - inlineCallCount)), - lastTreeExitGuard(NULL), - lastTreeCallGuard(NULL), - rpAtLastTreeCall(NULL), - outermostTree(f), - inlineCallCountp(&inlineCallCount), - innermostNestedGuardp(innermostNestedGuardp), -#ifdef EXECUTE_TREE_TIMER - startTime(rdtsc()), -#endif - builtinStatus(0), - nativeVp(NULL) -{ - JS_ASSERT(!tm->tracecx); - tm->tracecx = cx; - prev = tm->tracerState; - tm->tracerState = this; - -#ifdef JS_METHODJIT - if (TRACE_PROFILER(cx)) - AbortProfiling(cx); -#endif - - JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL); - JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL || - JS_THREAD_DATA(cx)->recordingCompartment == cx->compartment); - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_THREAD_DATA(cx)->onTraceCompartment = cx->compartment; - - JS_ASSERT(eos == stackBase + TraceNativeStorage::MAX_NATIVE_STACK_SLOTS); - JS_ASSERT(sp < eos); - - /* - * inlineCallCount has already been incremented, if being invoked from - * EnterFrame. It is okay to have a 0-frame restriction since the JIT - * might not need any frames. - */ - JS_ASSERT(inlineCallCount <= StackSpace::MAX_INLINE_CALLS); - -#ifdef DEBUG - /* - * Cannot 0xCD-fill global frame since it may overwrite a bailed outer - * ExecuteTree's 0xdeadbeefdeadbeef marker. - */ - memset(tm->storage->stack(), 0xCD, TraceNativeStorage::MAX_NATIVE_STACK_SLOTS * sizeof(double)); - memset(tm->storage->callstack(), 0xCD, TraceNativeStorage::MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo*)); -#endif -} - -JS_ALWAYS_INLINE -TracerState::~TracerState() -{ - JS_ASSERT(!nativeVp); - - if (traceMonitor->tracecx) { - /* If we didn't already deep-bail... */ - JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL || - JS_THREAD_DATA(cx)->recordingCompartment == cx->compartment); - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == cx->compartment); - JS_THREAD_DATA(cx)->onTraceCompartment = NULL; - } - - traceMonitor->tracerState = prev; - traceMonitor->tracecx = NULL; -} - -/* Call |f|, return the exit taken. */ -static JS_ALWAYS_INLINE VMSideExit* -ExecuteTrace(JSContext* cx, TraceMonitor* tm, Fragment* f, TracerState& state) -{ - JS_ASSERT(!tm->bailExit); -#ifdef JS_METHODJIT - JS_ASSERT(!TRACE_PROFILER(cx)); -#endif - union { NIns *code; GuardRecord* (FASTCALL *func)(TracerState*); } u; - u.code = f->code(); - GuardRecord* rec; -#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) - SIMULATE_FASTCALL(rec, state, NULL, u.func); -#else - rec = u.func(&state); -#endif - JS_ASSERT(!tm->bailExit); - return (VMSideExit*)rec->exit; -} - -/* Check whether our assumptions about the incoming scope-chain are upheld. */ -static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool -ScopeChainCheck(JSContext* cx, TreeFragment* f) -{ - JS_ASSERT(f->globalObj == cx->fp()->scopeChain().getGlobal()); - - /* - * The JIT records and expects to execute with two scope-chain - * assumptions baked-in: - * - * 1. That the bottom of the scope chain is global, in the sense of - * JSCLASS_IS_GLOBAL. - * - * 2. That the scope chain between fp and the global is free of - * "unusual" native objects such as HTML forms or other funny - * things. - * - * #2 is checked here while following the scope-chain links, via - * js_IsCacheableNonGlobalScope, which consults a whitelist of known - * class types; once a global is found, it's checked for #1. Failing - * either check causes an early return from execution. - */ - JSObject* child = &cx->fp()->scopeChain(); - while (JSObject* parent = child->getParent()) { - if (!IsCacheableNonGlobalScope(child)) { - debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); - Blacklist((jsbytecode*) f->root->ip); - return false; - } - child = parent; - } - JS_ASSERT(child == f->globalObj); - - if (!f->globalObj->isGlobal()) { - debug_only_print0(LC_TMTracer, "Blacklist: non-global at root of scope chain.\n"); - Blacklist((jsbytecode*) f->root->ip); - return false; - } - - return true; -} - -enum LEAVE_TREE_STATUS { - NO_DEEP_BAIL = 0, - DEEP_BAILED = 1 -}; - -static LEAVE_TREE_STATUS -LeaveTree(TraceMonitor *tm, TracerState&, VMSideExit *lr); - -/* Return false if the interpreter should goto error. */ -static JS_REQUIRES_STACK bool -ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount, - VMSideExit** innermostNestedGuardp, VMSideExit **lrp) -{ -#ifdef MOZ_TRACEVIS - TraceVisStateObj tvso(cx, S_EXECUTE); -#endif - JS_ASSERT(f->root == f && f->code()); - - if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace() || - inlineCallCount + f->maxCallDepth > StackSpace::MAX_INLINE_CALLS) { - *lrp = NULL; - return true; - } - - /* Make sure the global object is sane. */ - JS_ASSERT(f->globalObj->numSlots() <= MAX_GLOBAL_SLOTS); - JS_ASSERT(f->nGlobalTypes() == f->globalSlots->length()); - JS_ASSERT_IF(f->globalSlots->length() != 0, - f->globalObj->shape() == f->globalShape); - - /* Initialize trace state. */ - TracerState state(cx, tm, f, inlineCallCount, innermostNestedGuardp); - double* stack = tm->storage->stack(); - double* global = tm->storage->global(); - JSObject* globalObj = f->globalObj; - unsigned ngslots = f->globalSlots->length(); - uint16* gslots = f->globalSlots->data(); - - BuildNativeFrame(cx, globalObj, 0 /* callDepth */, ngslots, gslots, - f->typeMap.data(), global, stack); - - AUDIT(traceTriggered); - debug_only_printf(LC_TMTracer, "entering trace at %s:%u@%u, execs: %u code: %p\n", - cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), - f->execs, - (void *) f->code()); - - debug_only_stmt(uint32 globalSlots = globalObj->numSlots();) - debug_only_stmt(*(uint64*)&tm->storage->global()[globalSlots] = 0xdeadbeefdeadbeefLL;) - - /* Execute trace. */ - tm->iterationCounter = 0; - debug_only(int64 t0 = PRMJ_Now();) -#ifdef MOZ_TRACEVIS - VMSideExit* lr = (TraceVisStateObj(cx, S_NATIVE), ExecuteTrace(cx, tm, f, state)); -#else - VMSideExit* lr = ExecuteTrace(cx, tm, f, state); -#endif - debug_only(int64 t1 = PRMJ_Now();) - - JS_ASSERT_IF(lr->exitType == LOOP_EXIT, !lr->calldepth); - - /* Restore interpreter state. */ - DebugOnly lts = LeaveTree(tm, state, lr); - JS_ASSERT_IF(lts == NO_DEEP_BAIL, - *(uint64*)&tm->storage->global()[globalSlots] == 0xdeadbeefdeadbeefLL); - - *lrp = state.innermost; - bool ok = !(state.builtinStatus & BUILTIN_ERROR); - JS_ASSERT_IF(cx->isExceptionPending(), !ok); - - size_t iters = tm->iterationCounter; - - f->execs++; - f->iters += iters; - -#ifdef DEBUG - StackFrame *fp = cx->fp(); - const char *prefix = ""; - if (iters == LOOP_COUNT_MAX) - prefix = ">"; - debug_only_printf(LC_TMMinimal, " [%.3f ms] Tree at line %u executed for %s%u iterations;" - " executed %u times; leave for %s at %s:%u (%s)\n", - double(t1-t0) / PRMJ_USEC_PER_MSEC, - f->treeLineNumber, prefix, (uintN)iters, f->execs, - getExitName(lr->exitType), - fp->script()->filename, - js_FramePCToLineNumber(cx, fp), - js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc]); -#endif - -#ifdef JS_METHODJIT - if (cx->methodJitEnabled) { - if (lr->exitType == LOOP_EXIT && f->iters < MIN_LOOP_ITERS - && f->execs >= LOOP_CHECK_ITERS) - { - debug_only_printf(LC_TMMinimal, " Blacklisting at line %u (executed only %d iters)\n", - f->treeLineNumber, f->iters); - Blacklist((jsbytecode *)f->ip); - } - } -#endif - return ok; -} - -class Guardian { - bool *flagp; -public: - Guardian(bool *flagp) { - this->flagp = flagp; - JS_ASSERT(!*flagp); - *flagp = true; - } - - ~Guardian() { - JS_ASSERT(*flagp); - *flagp = false; - } -}; - -static JS_FORCES_STACK LEAVE_TREE_STATUS -LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr) -{ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - JSContext* cx = state.cx; - - /* Temporary waive the soft GC quota to make sure LeaveTree() doesn't fail. */ - Guardian waiver(&JS_THREAD_DATA(cx)->waiveGCQuota); - - FrameInfo** callstack = state.callstackBase; - double* stack = state.stackBase; - - /* - * Except if we find that this is a nested bailout, the guard the call - * returned is the one we have to use to adjust pc and sp. - */ - VMSideExit* innermost = lr; - - /* - * While executing a tree we do not update state.sp and state.rp even if - * they grow. Instead, guards tell us by how much sp and rp should be - * incremented in case of a side exit. When calling a nested tree, however, - * we actively adjust sp and rp. If we have such frames from outer trees on - * the stack, then rp will have been adjusted. Before we can process the - * stack of the frames of the tree we directly exited from, we have to - * first work our way through the outer frames and generate interpreter - * frames for them. Once the call stack (rp) is empty, we can process the - * final frames (which again are not directly visible and only the guard we - * exited on will tells us about). - */ - FrameInfo** rp = (FrameInfo**)state.rp; - if (lr->exitType == NESTED_EXIT) { - VMSideExit* nested = state.lastTreeCallGuard; - if (!nested) { - /* - * If lastTreeCallGuard is not set in state, we only have a single - * level of nesting in this exit, so lr itself is the innermost and - * outermost nested guard, and hence we set nested to lr. The - * calldepth of the innermost guard is not added to state.rp, so we - * do it here manually. For a nesting depth greater than 1 the - * call tree code already added the innermost guard's calldepth - * to state.rpAtLastTreeCall. - */ - nested = lr; - rp += lr->calldepth; - } else { - /* - * During unwinding state.rp gets overwritten at every step and we - * restore it here to its state at the innermost nested guard. The - * builtin already added the calldepth of that innermost guard to - * rpAtLastTreeCall. - */ - rp = (FrameInfo**)state.rpAtLastTreeCall; - } - innermost = state.lastTreeExitGuard; - if (state.innermostNestedGuardp) - *state.innermostNestedGuardp = nested; - JS_ASSERT(nested); - JS_ASSERT(nested->exitType == NESTED_EXIT); - JS_ASSERT(state.lastTreeExitGuard); - JS_ASSERT(state.lastTreeExitGuard->exitType != NESTED_EXIT); - } - - int32_t bs = state.builtinStatus; - bool bailed = innermost->exitType == STATUS_EXIT && (bs & BUILTIN_BAILED); - if (bailed) { - /* - * Deep-bail case. - * - * A _FAIL native already called LeaveTree once. At that time we - * reconstructed the interpreter stack, in pre-call state, with pc - * pointing to the op that triggered the call. Then we continued in - * native code. - */ - if (!(bs & BUILTIN_ERROR)) { - /* - * The builtin or native deep-bailed but finished successfully - * (no exception or error). - * - * After it returned, the JIT code stored the results of the - * builtin or native at the top of the native stack and then - * immediately flunked the guard on state->builtinStatus. - * - * Now LeaveTree has been called again from the tail of - * ExecuteTree. We are about to return to the interpreter. Adjust - * the top stack frame to resume on the next op. - */ - FrameRegs* regs = &cx->regs(); - JSOp op = (JSOp) *regs->pc; - - /* - * JSOP_SETELEM can be coalesced with a JSOP_POP in the interpeter. - * Since this doesn't re-enter the recorder, the post-state snapshot - * is invalid. Fix it up here. - */ - if (op == JSOP_SETELEM && JSOp(regs->pc[JSOP_SETELEM_LENGTH]) == JSOP_POP) { - regs->sp -= js_CodeSpec[JSOP_SETELEM].nuses; - regs->sp += js_CodeSpec[JSOP_SETELEM].ndefs; - regs->pc += JSOP_SETELEM_LENGTH; - op = JSOP_POP; - } - - const JSCodeSpec& cs = js_CodeSpec[op]; - regs->sp -= (cs.format & JOF_INVOKE) ? GET_ARGC(regs->pc) + 2 : cs.nuses; - regs->sp += cs.ndefs; - regs->pc += cs.length; - JS_ASSERT_IF(!cx->fp()->hasImacropc(), - cx->fp()->slots() + cx->fp()->numFixed() + - js_ReconstructStackDepth(cx, cx->fp()->script(), regs->pc) == - regs->sp); - - /* - * If there's a tree call around the point that we deep exited at, - * then state.sp and state.rp were restored to their original - * values before the tree call and sp might be less than deepBailSp, - * which we sampled when we were told to deep bail. - */ - JS_ASSERT(state.deepBailSp >= state.stackBase && state.sp <= state.deepBailSp); - - /* - * As explained above, the JIT code stored a result value or values - * on the native stack. Transfer them to the interpreter stack now. - * (Some opcodes, like JSOP_CALLELEM, produce two values, hence the - * loop.) - */ - JSValueType* typeMap = innermost->stackTypeMap(); - for (int i = 1; i <= cs.ndefs; i++) { - NativeToValue(cx, - regs->sp[-i], - typeMap[innermost->numStackSlots - i], - (jsdouble *) state.deepBailSp - + innermost->sp_adj / sizeof(jsdouble) - i); - } - } - return DEEP_BAILED; - } - - while (callstack < rp) { - FrameInfo* fi = *callstack; - /* Peek at the callee native slot in the not-yet-synthesized prev frame. */ - JSObject* callee = *(JSObject**)&stack[fi->callerHeight]; - - /* - * Flush the slots for cx->fp() (which will become cx->fp()->prev after - * SynthesizeFrame). Since a frame's arguments (including callee - * and thisv) are part of the frame, we only want to flush up to the - * next frame's arguments, so set cx->regs().sp to to not include said - * arguments. The upcoming call to SynthesizeFrame will reset regs->sp - * to its correct value. - */ - cx->regs().sp = cx->fp()->slots() + (fi->spdist - (2 + fi->get_argc())); - int slots = FlushNativeStackFrame(cx, 0 /* callDepth */, fi->get_typemap(), stack); - - /* Finish initializing cx->fp() and push a new cx->fp(). */ - SynthesizeFrame(cx, *fi, callee); -#ifdef DEBUG - StackFrame* fp = cx->fp(); - debug_only_printf(LC_TMTracer, - "synthesized deep frame for %s:%u@%u, slots=%d, fi=%p\n", - fp->script()->filename, - js_FramePCToLineNumber(cx, fp), - FramePCOffset(cx, fp), - slots, - (void*)*callstack); -#endif - /* - * Keep track of the additional frames we put on the interpreter stack - * and the native stack slots we consumed. - */ - ++*state.inlineCallCountp; - ++callstack; - stack += slots; - } - - /* - * We already synthesized the frames around the innermost guard. Here we - * just deal with additional frames inside the tree we are bailing out - * from. - */ - JS_ASSERT(rp == callstack); - unsigned calldepth = innermost->calldepth; - unsigned calleeOffset = 0; - for (unsigned n = 0; n < calldepth; ++n) { - /* Peek at the callee native slot in the not-yet-synthesized prev frame. */ - calleeOffset += callstack[n]->callerHeight; - JSObject* callee = *(JSObject**)&stack[calleeOffset]; - - /* Reconstruct the frame. */ - SynthesizeFrame(cx, *callstack[n], callee); - ++*state.inlineCallCountp; -#ifdef DEBUG - StackFrame* fp = cx->fp(); - debug_only_printf(LC_TMTracer, - "synthesized shallow frame for %s:%u@%u\n", - fp->script()->filename, js_FramePCToLineNumber(cx, fp), - FramePCOffset(cx, fp)); -#endif - } - - /* - * Adjust sp and pc relative to the tree we exited from (not the tree we - * entered into). These are our final values for sp and pc since - * SynthesizeFrame has already taken care of all frames in between. - */ - StackFrame* const fp = cx->fp(); - - /* - * If we are not exiting from an inlined frame, the state->sp is spbase. - * Otherwise spbase is whatever slots frames around us consume. - */ - cx->regs().pc = innermost->pc; - if (innermost->imacpc) - fp->setImacropc(innermost->imacpc); - else - fp->clearImacropc(); - - /* - * Set cx->regs().regs for the top frame. Since the top frame does not have a - * FrameInfo (a FrameInfo is only pushed for calls), we basically need to - * compute the offset from fp->slots() to the top of the stack based on the - * number of native slots allocated for this function. - * - * Duplicate native stack layout computation: see VisitFrameSlots header comment. - */ - uintN slotOffset = innermost->numStackSlots - innermost->numStackSlotsBelowCurrentFrame; - if (fp->isGlobalFrame()) { - /* Global nfixed slots are not kept on the native stack, so add them back. */ - slotOffset += fp->globalScript()->nfixed; - } else { - /* A frame's native slots includes args and frame ptrs, so strip them off. */ - slotOffset -= NumSlotsBeforeFixed(fp); - } - cx->regs().sp = fp->slots() + slotOffset; - - /* Assert that we computed sp correctly. */ - JS_ASSERT_IF(!fp->hasImacropc(), - fp->slots() + fp->numFixed() + - js_ReconstructStackDepth(cx, fp->script(), cx->regs().pc) == cx->regs().sp); - -#ifdef EXECUTE_TREE_TIMER - uint64 cycles = rdtsc() - state.startTime; -#elif defined(JS_JIT_SPEW) - uint64 cycles = 0; -#endif - debug_only_printf(LC_TMTracer, - "leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%s, sp=%lld, " - "calldepth=%d, cycles=%llu\n", - fp->script()->filename, - js_FramePCToLineNumber(cx, fp), - FramePCOffset(cx, fp), - js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc], - (void*)lr, - getExitName(lr->exitType), - (long long int)(cx->regs().sp - fp->base()), - calldepth, - (unsigned long long int)cycles); - - DebugOnly slots = FlushNativeStackFrame(cx, innermost->calldepth, innermost->stackTypeMap(), stack); - JS_ASSERT(unsigned(slots) == innermost->numStackSlots); - - /* - * If this trace is part of a tree, later branches might have added - * additional globals for which we don't have any type information - * available in the side exit. We merge in this information from the entry - * type-map. See also the comment in the constructor of TraceRecorder - * regarding why this is always safe to do. - */ - TreeFragment* outermostTree = state.outermostTree; - uint16* gslots = outermostTree->globalSlots->data(); - unsigned ngslots = outermostTree->globalSlots->length(); - JS_ASSERT(ngslots == outermostTree->nGlobalTypes()); - JSValueType* globalTypeMap; - - /* Are there enough globals? */ - TypeMap& typeMap = *tm->cachedTempTypeMap; - typeMap.clear(); - if (innermost->numGlobalSlots == ngslots) { - /* Yes. This is the ideal fast path. */ - globalTypeMap = innermost->globalTypeMap(); - } else { - /* - * No. Merge the typemap of the innermost entry and exit together. This - * should always work because it is invalid for nested trees or linked - * trees to have incompatible types. Thus, whenever a new global type - * is lazily added into a tree, all dependent and linked trees are - * immediately specialized (see bug 476653). - */ - JS_ASSERT(innermost->root()->nGlobalTypes() == ngslots); - JS_ASSERT(innermost->root()->nGlobalTypes() > innermost->numGlobalSlots); - typeMap.ensure(ngslots); - DebugOnly check_ngslots = BuildGlobalTypeMapFromInnerTree(typeMap, innermost); - JS_ASSERT(check_ngslots == ngslots); - globalTypeMap = typeMap.data(); - } - - /* Write back interned globals. */ - JS_ASSERT(state.eos == state.stackBase + TraceNativeStorage::MAX_NATIVE_STACK_SLOTS); - JSObject* globalObj = outermostTree->globalObj; - FlushNativeGlobalFrame(cx, globalObj, state.eos, ngslots, gslots, globalTypeMap); - -#ifdef JS_JIT_SPEW - if (innermost->exitType != TIMEOUT_EXIT) - AUDIT(sideExitIntoInterpreter); - else - AUDIT(timeoutIntoInterpreter); -#endif - - state.innermost = innermost; - return NO_DEEP_BAIL; -} - -#if defined(DEBUG) || defined(JS_METHODJIT) -static jsbytecode * -GetLoopBottom(JSContext *cx, jsbytecode *pc) -{ - JS_ASSERT(*pc == JSOP_TRACE || *pc == JSOP_NOTRACE); - JSScript *script = cx->fp()->script(); - jssrcnote *sn = js_GetSrcNote(script, pc); - if (!sn) - return NULL; - return pc + js_GetSrcNoteOffset(sn, 0); -} -#endif - -JS_ALWAYS_INLINE void -TraceRecorder::assertInsideLoop() -{ -#ifdef DEBUG - /* Asserts at callDepth == 0 will catch problems at the call op. */ - if (callDepth > 0) - return; - - jsbytecode *pc = cx->fp()->hasImacropc() ? cx->fp()->imacropc() : cx->regs().pc; - jsbytecode *beg = (jsbytecode *)tree->ip; - jsbytecode *end = GetLoopBottom(cx, beg); - - /* - * In some cases (continue in a while loop), we jump to the goto - * immediately preceeding a loop (the one that jumps to the loop - * condition). - */ - JS_ASSERT(pc >= beg - JSOP_GOTO_LENGTH && pc <= end); -#endif -} - -JS_REQUIRES_STACK MonitorResult -RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount) -{ -#ifdef MOZ_TRACEVIS - TraceVisStateObj tvso(cx, S_MONITOR); -#endif - - /* Is the recorder currently active? */ - if (tm->recorder) { - tm->recorder->assertInsideLoop(); - jsbytecode* pc = cx->regs().pc; - if (pc == tm->recorder->tree->ip) { - AbortableRecordingStatus status = tm->recorder->closeLoop(); - if (status != ARECORD_COMPLETED) { - if (tm->recorder) - AbortRecording(cx, "closeLoop failed"); - return MONITOR_NOT_RECORDING; - } - } else { - MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount); - JS_ASSERT((r == MONITOR_RECORDING) == (tm->recorder != NULL)); - if (r == MONITOR_RECORDING || r == MONITOR_ERROR) - return r; - - /* - * recordLoopEdge will invoke an inner tree if we have a matching - * one. If we arrive here, that tree didn't run to completion and - * instead we mis-matched or the inner tree took a side exit other than - * the loop exit. We are thus no longer guaranteed to be parked on the - * same loop header RecordLoopEdge was called for. In fact, this - * might not even be a loop header at all. Hence if the program counter - * no longer hovers over the inner loop header, return to the - * interpreter and do not attempt to trigger or record a new tree at - * this location. - */ - if (pc != cx->regs().pc) { -#ifdef MOZ_TRACEVIS - tvso.r = R_INNER_SIDE_EXIT; -#endif - return MONITOR_NOT_RECORDING; - } - } - } - JS_ASSERT(!tm->recorder); - - /* - * Make sure the shape of the global object still matches (this might flush - * the JIT cache). - */ - JSObject* globalObj = cx->fp()->scopeChain().getGlobal(); - uint32 globalShape = -1; - SlotList* globalSlots = NULL; - - if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) { - Backoff(tm, cx->regs().pc); - return MONITOR_NOT_RECORDING; - } - - /* Do not enter the JIT code with a pending operation callback. */ - if (JS_THREAD_DATA(cx)->interruptFlags) { -#ifdef MOZ_TRACEVIS - tvso.r = R_CALLBACK_PENDING; -#endif - return MONITOR_NOT_RECORDING; - } - - jsbytecode* pc = cx->regs().pc; - uint32 argc = entryFrameArgc(cx); - - TreeFragment* f = LookupOrAddLoop(tm, pc, globalObj, globalShape, argc); - - /* - * If we have no code in the anchor and no peers, we definitively won't be - * able to activate any trees, so start compiling. - */ - if (!f->code() && !f->peer) { - record: - if (++f->hits() < HOTLOOP) { -#ifdef MOZ_TRACEVIS - tvso.r = f->hits() < 1 ? R_BACKED_OFF : R_COLD; -#endif - return MONITOR_NOT_RECORDING; - } - - if (!ScopeChainCheck(cx, f)) { -#ifdef MOZ_TRACEVIS - tvso.r = R_FAIL_SCOPE_CHAIN_CHECK; -#endif - return MONITOR_NOT_RECORDING; - } - - /* - * We can give RecordTree the root peer. If that peer is already taken, - * it will walk the peer list and find us a free slot or allocate a new - * tree if needed. - */ - bool rv = RecordTree(cx, tm, f->first, NULL, NULL, 0, globalSlots); -#ifdef MOZ_TRACEVIS - if (!rv) - tvso.r = R_FAIL_RECORD_TREE; -#endif - return RecordingIfTrue(rv); - } - - debug_only_printf(LC_TMTracer, - "Looking for compat peer %d@%d, from %p (ip: %p)\n", - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), (void*)f, f->ip); - - uintN count; - TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, f, count); - if (!match) { - if (count < MAXPEERS) - goto record; - - /* - * If we hit the max peers ceiling, don't try to lookup fragments all - * the time. That's expensive. This must be a rather type-unstable loop. - */ - debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n"); - Blacklist((jsbytecode*) f->root->ip); -#ifdef MOZ_TRACEVIS - tvso.r = R_MAX_PEERS; -#endif - return MONITOR_NOT_RECORDING; - } - - VMSideExit* lr = NULL; - VMSideExit* innermostNestedGuard = NULL; - - if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr)) - return MONITOR_ERROR; - - if (!lr) { -#ifdef MOZ_TRACEVIS - tvso.r = R_FAIL_EXECUTE_TREE; -#endif - return MONITOR_NOT_RECORDING; - } - - /* - * If we exit on a branch, or on a tree call guard, try to grow the inner - * tree (in case of a branch exit), or the tree nested around the tree we - * exited from (in case of the tree call guard). - */ - bool rv; - switch (lr->exitType) { - case UNSTABLE_LOOP_EXIT: - rv = AttemptToStabilizeTree(cx, tm, globalObj, lr, NULL, NULL, 0); -#ifdef MOZ_TRACEVIS - if (!rv) - tvso.r = R_FAIL_STABILIZE; -#endif - return RecordingIfTrue(rv); - - case MUL_ZERO_EXIT: - case OVERFLOW_EXIT: - if (lr->exitType == MUL_ZERO_EXIT) - tm->oracle->markInstructionSlowZeroTest(cx->regs().pc); - else - tm->oracle->markInstructionUndemotable(cx->regs().pc); - /* FALL THROUGH */ - case BRANCH_EXIT: - rv = AttemptToExtendTree(cx, tm, lr, NULL, NULL, NULL -#ifdef MOZ_TRACEVIS - , &tvso -#endif - ); - return RecordingIfTrue(rv); - - case LOOP_EXIT: - if (innermostNestedGuard) { - rv = AttemptToExtendTree(cx, tm, innermostNestedGuard, lr, NULL, NULL -#ifdef MOZ_TRACEVIS - , &tvso -#endif - ); - return RecordingIfTrue(rv); - } -#ifdef MOZ_TRACEVIS - tvso.r = R_NO_EXTEND_OUTER; -#endif - return MONITOR_NOT_RECORDING; - -#ifdef MOZ_TRACEVIS - case MISMATCH_EXIT: - tvso.r = R_MISMATCH_EXIT; - return MONITOR_NOT_RECORDING; - case OOM_EXIT: - tvso.r = R_OOM_EXIT; - return MONITOR_NOT_RECORDING; - case TIMEOUT_EXIT: - tvso.r = R_TIMEOUT_EXIT; - return MONITOR_NOT_RECORDING; - case DEEP_BAIL_EXIT: - tvso.r = R_DEEP_BAIL_EXIT; - return MONITOR_NOT_RECORDING; - case STATUS_EXIT: - tvso.r = R_STATUS_EXIT; - return MONITOR_NOT_RECORDING; -#endif - - default: - /* - * No, this was an unusual exit (i.e. out of memory/GC), so just resume - * interpretation. - */ -#ifdef MOZ_TRACEVIS - tvso.r = R_OTHER_EXIT; -#endif - return MONITOR_NOT_RECORDING; - } -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::monitorRecording(JSOp op) -{ - JS_ASSERT(!addPropShapeBefore); - - JS_ASSERT(traceMonitor == &cx->compartment->traceMonitor); - - TraceMonitor &localtm = *traceMonitor; - debug_only_stmt( JSContext *localcx = cx; ) - assertInsideLoop(); - JS_ASSERT(!localtm.profile); - - /* Process needFlush requests now. */ - if (localtm.needFlush) { - ResetJIT(cx, &localtm, FR_DEEP_BAIL); - return ARECORD_ABORTED; - } - JS_ASSERT(!fragment->lastIns); - - /* - * Clear one-shot state used to communicate between record_JSOP_CALL and post- - * opcode-case-guts record hook (record_NativeCallComplete). - */ - pendingSpecializedNative = NULL; - newobj_ins = NULL; - pendingGlobalSlotsToSet.clear(); - - /* Handle one-shot request from finishGetProp or INSTANCEOF to snapshot post-op state and guard. */ - if (pendingGuardCondition) { - LIns* cond = pendingGuardCondition; - bool expected = true; - - /* Put 'cond' in a form suitable for a guard/branch condition if it's not already. */ - ensureCond(&cond, &expected); - guard(expected, cond, STATUS_EXIT); - pendingGuardCondition = NULL; - } - - /* Handle one-shot request to unbox the result of a property get or ObjectToIterator. */ - if (pendingUnboxSlot) { - LIns* val_ins = get(pendingUnboxSlot); - /* - * We need to know from where to unbox the value. Since pendingUnboxSlot - * is only set in finishGetProp, we can depend on LIns* tracked for - * pendingUnboxSlot to have this information. - */ - LIns* unboxed_ins = unbox_value(*pendingUnboxSlot, - AnyAddress(val_ins->oprnd1(), val_ins->disp()), - snapshot(BRANCH_EXIT)); - set(pendingUnboxSlot, unboxed_ins); - pendingUnboxSlot = 0; - } - - debug_only_stmt( - if (LogController.lcbits & LC_TMRecorder) { - void *mark = JS_ARENA_MARK(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - - debug_only_print0(LC_TMRecorder, "\n"); - js_Disassemble1(cx, cx->fp()->script(), cx->regs().pc, - cx->fp()->hasImacropc() - ? 0 : cx->regs().pc - cx->fp()->script()->code, - !cx->fp()->hasImacropc(), &sprinter); - - fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); - } - ) - - /* - * If op is not a break or a return from a loop, continue recording and - * follow the trace. We check for imacro-calling bytecodes inside each - * switch case to resolve the if (JSOP_IS_IMACOP(x)) conditions at compile - * time. - */ - - AbortableRecordingStatus status; -#ifdef DEBUG - bool wasInImacro = (cx->fp()->hasImacropc()); -#endif - switch (op) { - default: - AbortRecording(cx, "unsupported opcode"); - status = ARECORD_ERROR; - break; -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - case op: \ - w.comment(#op); \ - status = this->record_##op(); \ - break; -# include "jsopcode.tbl" -# undef OPDEF - } - - /* N.B. |this| may have been deleted. */ - - if (!JSOP_IS_IMACOP(op)) { - JS_ASSERT(status != ARECORD_IMACRO); - JS_ASSERT_IF(!wasInImacro, !localcx->fp()->hasImacropc()); - } - - if (localtm.recorder) { - JS_ASSERT(status != ARECORD_ABORTED); - JS_ASSERT(localtm.recorder == this); - - /* |this| recorder completed, but a new one started; keep recording. */ - if (status == ARECORD_COMPLETED) - return ARECORD_CONTINUE; - - /* Handle lazy aborts; propagate the 'error' status. */ - if (StatusAbortsRecorderIfActive(status)) { - AbortRecording(cx, js_CodeName[op]); - return status == ARECORD_ERROR ? ARECORD_ERROR : ARECORD_ABORTED; - } - - if (outOfMemory() || OverfullJITCache(cx, &localtm)) { - ResetJIT(cx, &localtm, FR_OOM); - - /* - * If the status returned was ARECORD_IMACRO, then we just - * changed cx->regs, we need to tell the interpreter to sync - * its local variables. - */ - return status == ARECORD_IMACRO ? ARECORD_IMACRO_ABORTED : ARECORD_ABORTED; - } - } else { - JS_ASSERT(status == ARECORD_COMPLETED || - status == ARECORD_ABORTED || - status == ARECORD_ERROR); - } - return status; -} - -JS_REQUIRES_STACK TraceRecorder::AbortResult -AbortRecording(JSContext* cx, const char* reason) -{ -#ifdef DEBUG - JS_ASSERT(TRACE_RECORDER(cx)); - return TRACE_RECORDER(cx)->finishAbort(reason); -#else - return TRACE_RECORDER(cx)->finishAbort("[no reason]"); -#endif -} - -#if defined NANOJIT_IA32 -static bool -CheckForSSE2() -{ - char *c = getenv("X86_FORCE_SSE2"); - if (c) - return (!strcmp(c, "true") || - !strcmp(c, "1") || - !strcmp(c, "yes")); - - int features = 0; -#if defined _MSC_VER - __asm - { - pushad - mov eax, 1 - cpuid - mov features, edx - popad - } -#elif defined __GNUC__ - asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ - "mov $0x01, %%eax\n" - "cpuid\n" - "mov %%edx, %0\n" - "xchg %%esi, %%ebx\n" - : "=m" (features) - : /* We have no inputs */ - : "%eax", "%esi", "%ecx", "%edx" - ); -#elif defined __SUNPRO_C || defined __SUNPRO_CC - asm("push %%ebx\n" - "mov $0x01, %%eax\n" - "cpuid\n" - "pop %%ebx\n" - : "=d" (features) - : /* We have no inputs */ - : "%eax", "%ecx" - ); -#endif - return (features & (1<<26)) != 0; -} -#endif - -#if defined(NANOJIT_ARM) - -#if defined(__GNUC__) && defined(AVMPLUS_LINUX) - -// Assume ARMv4 by default. -static unsigned int arm_arch = 4; -static bool arm_has_vfp = false; -static bool arm_has_neon = false; -static bool arm_has_iwmmxt = false; -static bool arm_tests_initialized = false; - -#ifdef ANDROID -// we're actually reading /proc/cpuinfo, but oh well -static void -arm_read_auxv() -{ - char buf[1024]; - char* pos; - const char* ver_token = "CPU architecture: "; - FILE* f = fopen("/proc/cpuinfo", "r"); - fread(buf, sizeof(char), 1024, f); - fclose(f); - pos = strstr(buf, ver_token); - if (pos) { - int ver = *(pos + strlen(ver_token)) - '0'; - arm_arch = ver; - } - arm_has_neon = strstr(buf, "neon") != NULL; - arm_has_vfp = strstr(buf, "vfp") != NULL; - arm_has_iwmmxt = strstr(buf, "iwmmxt") != NULL; - arm_tests_initialized = true; -} - -#else - -static void -arm_read_auxv() -{ - int fd; - Elf32_auxv_t aux; - - fd = open("/proc/self/auxv", O_RDONLY); - if (fd > 0) { - while (read(fd, &aux, sizeof(Elf32_auxv_t))) { - if (aux.a_type == AT_HWCAP) { - uint32_t hwcap = aux.a_un.a_val; - if (getenv("ARM_FORCE_HWCAP")) - hwcap = strtoul(getenv("ARM_FORCE_HWCAP"), NULL, 0); - else if (getenv("_SBOX_DIR")) - continue; // Ignore the rest, if we're running in scratchbox - // hardcode these values to avoid depending on specific versions - // of the hwcap header, e.g. HWCAP_NEON - arm_has_vfp = (hwcap & 64) != 0; - arm_has_iwmmxt = (hwcap & 512) != 0; - // this flag is only present on kernel 2.6.29 - arm_has_neon = (hwcap & 4096) != 0; - } else if (aux.a_type == AT_PLATFORM) { - const char *plat = (const char*) aux.a_un.a_val; - if (getenv("ARM_FORCE_PLATFORM")) - plat = getenv("ARM_FORCE_PLATFORM"); - else if (getenv("_SBOX_DIR")) - continue; // Ignore the rest, if we're running in scratchbox - // The platform string has the form "v[0-9][lb]". The "l" or "b" indicate little- - // or big-endian variants and the digit indicates the version of the platform. - // We can only accept ARMv4 and above, but allow anything up to ARMv9 for future - // processors. Architectures newer than ARMv7 are assumed to be - // backwards-compatible with ARMv7. - if ((plat[0] == 'v') && - (plat[1] >= '4') && (plat[1] <= '9') && - ((plat[2] == 'l') || (plat[2] == 'b'))) - { - arm_arch = plat[1] - '0'; - } - } - } - close (fd); - - // if we don't have 2.6.29, we have to do this hack; set - // the env var to trust HWCAP. - if (!getenv("ARM_TRUST_HWCAP") && (arm_arch >= 7)) - arm_has_neon = true; - } - - arm_tests_initialized = true; -} - -#endif - -static unsigned int -arm_check_arch() -{ - if (!arm_tests_initialized) - arm_read_auxv(); - - return arm_arch; -} - -static bool -arm_check_vfp() -{ - if (!arm_tests_initialized) - arm_read_auxv(); - - return arm_has_vfp; -} - -#else -#warning Not sure how to check for architecture variant on your platform. Assuming ARMv4. -static unsigned int -arm_check_arch() { return 4; } -static bool -arm_check_vfp() { return false; } -#endif - -#ifndef HAVE_ENABLE_DISABLE_DEBUGGER_EXCEPTIONS -static void -enable_debugger_exceptions() { } -static void -disable_debugger_exceptions() { } -#endif - -#endif /* NANOJIT_ARM */ - -#define K *1024 -#define M K K -#define G K M - -void -SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes) -{ - if (bytes > 1 G) - bytes = 1 G; - if (bytes < 128 K) - bytes = 128 K; - JS_THREAD_DATA(cx)->maxCodeCacheBytes = bytes; -} - -bool -InitJIT(TraceMonitor *tm, JSRuntime* rt) -{ -#if defined JS_JIT_SPEW - tm->profAlloc = NULL; - /* Set up debug logging. */ - if (!did_we_set_up_debug_logging) { - InitJITLogController(); - did_we_set_up_debug_logging = true; - } - /* Set up fragprofiling, if required. */ - if (LogController.lcbits & LC_FragProfile) { - tm->profAlloc = rt->new_(rt, (char*)NULL, 0); /* no reserve needed in debug builds */ - if (!tm->profAlloc) - goto error; - tm->profTab = new (*tm->profAlloc) FragStatsMap(*tm->profAlloc); - } - tm->lastFragID = 0; -#else - PodZero(&LogController); -#endif - - if (!did_we_check_processor_features) { -#if defined NANOJIT_IA32 - NJConfig.i386_use_cmov = NJConfig.i386_sse2 = CheckForSSE2(); - NJConfig.i386_fixed_esp = true; -#endif -#if defined NANOJIT_ARM - - disable_debugger_exceptions(); - - bool arm_vfp = arm_check_vfp(); - unsigned int arm_arch = arm_check_arch(); - - enable_debugger_exceptions(); - - NJConfig.arm_vfp = arm_vfp; - NJConfig.soft_float = !arm_vfp; - NJConfig.arm_arch = arm_arch; - - // Sanity-check the configuration detection. - // * We don't understand architectures prior to ARMv4. - JS_ASSERT(arm_arch >= 4); -#endif - did_we_check_processor_features = true; - } - - #define CHECK_NEW(lhs, type, args) \ - do { lhs = rt->new_ args; if (!lhs) goto error; } while (0) - #define CHECK_MALLOC(lhs, conversion, size) \ - do { lhs = (conversion)(rt->malloc_(size)); if (!lhs) goto error; } while (0) - - CHECK_NEW(tm->oracle, Oracle, ()); - - tm->profile = NULL; - - CHECK_NEW(tm->recordAttempts, RecordAttemptMap, ()); - if (!tm->recordAttempts->init(PC_HASH_COUNT)) - goto error; - - CHECK_NEW(tm->loopProfiles, LoopProfileMap, ()); - if (!tm->loopProfiles->init(PC_HASH_COUNT)) - goto error; - - tm->flushEpoch = 0; - - char *dataReserve, *traceReserve, *tempReserve; - CHECK_MALLOC(dataReserve, char*, DataReserveSize); - CHECK_MALLOC(traceReserve, char*, TraceReserveSize); - CHECK_MALLOC(tempReserve, char*, TempReserveSize); - CHECK_NEW(tm->dataAlloc, VMAllocator, (rt, dataReserve, DataReserveSize)); - CHECK_NEW(tm->traceAlloc, VMAllocator, (rt, traceReserve, TraceReserveSize)); - CHECK_NEW(tm->tempAlloc, VMAllocator, (rt, tempReserve, TempReserveSize)); - CHECK_NEW(tm->codeAlloc, CodeAlloc, ()); - CHECK_NEW(tm->frameCache, FrameInfoCache, (tm->dataAlloc)); - CHECK_NEW(tm->storage, TraceNativeStorage, ()); - CHECK_NEW(tm->cachedTempTypeMap, TypeMap, ((Allocator*)NULL, tm->oracle)); - tm->flush(); - verbose_only( tm->branches = NULL; ) - -#if !defined XP_WIN - debug_only(PodZero(&jitstats)); -#endif - -#ifdef JS_JIT_SPEW - /* Architecture properties used by test cases. */ - jitstats.archIsIA32 = 0; - jitstats.archIs64BIT = 0; - jitstats.archIsARM = 0; - jitstats.archIsSPARC = 0; - jitstats.archIsPPC = 0; -#if defined NANOJIT_IA32 - jitstats.archIsIA32 = 1; -#endif -#if defined NANOJIT_64BIT - jitstats.archIs64BIT = 1; -#endif -#if defined NANOJIT_ARM - jitstats.archIsARM = 1; -#endif -#if defined NANOJIT_SPARC - jitstats.archIsSPARC = 1; -#endif -#if defined NANOJIT_PPC - jitstats.archIsPPC = 1; -#endif -#if defined NANOJIT_X64 - jitstats.archIsAMD64 = 1; -#endif -#endif - - if (!tm->tracedScripts.init()) - goto error; - return true; - -error: - /* On error, don't rely on the compartment destructor being called. */ - FinishJIT(tm); - return false; -} - -/* - * NB: FinishJIT needs to work even when InitJIT fails. Each pointer must be - * checked before it's dereferenced, as it may not have been allocated. - */ -void -FinishJIT(TraceMonitor *tm) -{ - JS_ASSERT(!tm->recorder); - JS_ASSERT(!tm->profile); - -#ifdef JS_JIT_SPEW - if (jitstats.recorderStarted) { - char sep = ':'; - debug_only_print0(LC_TMStats, "recorder"); -#define RECORDER_JITSTAT(_ident, _name) \ - debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep, \ - (unsigned long long int)jitstats._ident); \ - sep = ','; -#define JITSTAT(x) /* nothing */ -#include "jitstats.tbl" -#undef JITSTAT -#undef RECORDER_JITSTAT - debug_only_print0(LC_TMStats, "\n"); - - sep = ':'; - debug_only_print0(LC_TMStats, "monitor"); -#define MONITOR_JITSTAT(_ident, _name) \ - debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep, \ - (unsigned long long int)jitstats._ident); \ - sep = ','; -#define JITSTAT(x) /* nothing */ -#include "jitstats.tbl" -#undef JITSTAT -#undef MONITOR_JITSTAT - debug_only_print0(LC_TMStats, "\n"); - } -#endif - - Foreground::delete_(tm->recordAttempts); - Foreground::delete_(tm->loopProfiles); - Foreground::delete_(tm->oracle); - -#ifdef DEBUG - // Recover profiling data from expiring Fragments, and display - // final results. - if (LogController.lcbits & LC_FragProfile) { - - for (Seq* f = tm->branches; f; f = f->tail) - FragProfiling_FragFinalizer(f->head, tm); - - for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { - for (TreeFragment *f = tm->vmfragments[i]; f; f = f->next) { - JS_ASSERT(f->root == f); - for (TreeFragment *p = f; p; p = p->peer) - FragProfiling_FragFinalizer(p, tm); - } - } - - if (tm->profTab) - FragProfiling_showResults(tm); - Foreground::delete_(tm->profAlloc); - - } else { - NanoAssert(!tm->profTab); - NanoAssert(!tm->profAlloc); - } -#endif - - PodArrayZero(tm->vmfragments); - - Foreground::delete_(tm->frameCache); - tm->frameCache = NULL; - - Foreground::delete_(tm->codeAlloc); - tm->codeAlloc = NULL; - - Foreground::delete_(tm->dataAlloc); - tm->dataAlloc = NULL; - - Foreground::delete_(tm->traceAlloc); - tm->traceAlloc = NULL; - - Foreground::delete_(tm->tempAlloc); - tm->tempAlloc = NULL; - - Foreground::delete_(tm->storage); - tm->storage = NULL; - - Foreground::delete_(tm->cachedTempTypeMap); - tm->cachedTempTypeMap = NULL; -} - -JS_REQUIRES_STACK void -PurgeScriptFragments(TraceMonitor* tm, JSScript* script) -{ - debug_only_printf(LC_TMTracer, - "Purging fragments for JSScript %p.\n", (void*)script); - - /* A recorder script is being evaluated and can not be destroyed or GC-ed. */ - JS_ASSERT_IF(tm->recorder, - JS_UPTRDIFF(tm->recorder->getTree()->ip, script->code) >= script->length); - - for (LoopProfileMap::Enum e(*tm->loopProfiles); !e.empty(); e.popFront()) { - if (JS_UPTRDIFF(e.front().key, script->code) < script->length) - e.removeFront(); - } - - TracedScriptSet::Ptr found = tm->tracedScripts.lookup(script); - if (!found) - return; - tm->tracedScripts.remove(found); - - for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { - TreeFragment** fragp = &tm->vmfragments[i]; - while (TreeFragment* frag = *fragp) { - if (JS_UPTRDIFF(frag->ip, script->code) < script->length) { - /* This fragment is associated with the script. */ - debug_only_printf(LC_TMTracer, - "Disconnecting TreeFragment %p " - "with ip %p, in range [%p,%p).\n", - (void*)frag, frag->ip, script->code, - script->code + script->length); - - JS_ASSERT(frag->root == frag); - *fragp = frag->next; - do { - verbose_only( FragProfiling_FragFinalizer(frag, tm); ) - TrashTree(frag); - } while ((frag = frag->peer) != NULL); - continue; - } - fragp = &frag->next; - } - } - - RecordAttemptMap &table = *tm->recordAttempts; - for (RecordAttemptMap::Enum e(table); !e.empty(); e.popFront()) { - if (JS_UPTRDIFF(e.front().key, script->code) < script->length) - e.removeFront(); - } -} - -bool -OverfullJITCache(JSContext *cx, TraceMonitor* tm) -{ - /* - * You might imagine the outOfMemory flag on the allocator is sufficient - * to model the notion of "running out of memory", but there are actually - * two separate issues involved: - * - * 1. The process truly running out of memory: malloc() or mmap() - * failed. - * - * 2. The limit we put on the "intended size" of the tracemonkey code - * cache, in pages, has been exceeded. - * - * Condition 1 doesn't happen very often, but we're obliged to try to - * safely shut down and signal the rest of spidermonkey when it - * does. Condition 2 happens quite regularly. - * - * Presently, the code in this file doesn't check the outOfMemory condition - * often enough, and frequently misuses the unchecked results of - * lirbuffer insertions on the assumption that it will notice the - * outOfMemory flag "soon enough" when it returns to the monitorRecording - * function. This turns out to be a false assumption if we use outOfMemory - * to signal condition 2: we regularly provoke "passing our intended - * size" and regularly fail to notice it in time to prevent writing - * over the end of an artificially self-limited LIR buffer. - * - * To mitigate, though not completely solve, this problem, we're - * modeling the two forms of memory exhaustion *separately* for the - * time being: condition 1 is handled by the outOfMemory flag inside - * nanojit, and condition 2 is being handled independently *here*. So - * we construct our allocators to use all available memory they like, - * and only report outOfMemory to us when there is literally no OS memory - * left. Merely purging our cache when we hit our highwater mark is - * handled by the (few) callers of this function. - * - */ - jsuint maxsz = JS_THREAD_DATA(cx)->maxCodeCacheBytes; - return (tm->codeAlloc->size() + tm->dataAlloc->size() + tm->traceAlloc->size() > maxsz); -} - -JS_FORCES_STACK JS_FRIEND_API(void) -DeepBail(JSContext *cx) -{ - JS_ASSERT(JS_ON_TRACE(cx)); - - /* - * Exactly one context on the current thread is on trace. Find out which - * one. (Most callers cannot guarantee that it's cx.) - */ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_THREAD_DATA(cx)->onTraceCompartment = NULL; - - /* It's a bug if a non-FAIL_STATUS builtin gets here. */ - JS_ASSERT(tm->bailExit); - - tm->tracecx = NULL; - debug_only_print0(LC_TMTracer, "Deep bail.\n"); - LeaveTree(tm, *tm->tracerState, tm->bailExit); - tm->bailExit = NULL; - - TracerState* state = tm->tracerState; - state->builtinStatus |= BUILTIN_BAILED; - - /* - * Between now and the LeaveTree in ExecuteTree, |tm->storage| may be - * reused if another trace executes before the currently executing native - * returns. If this happens, at least some of the native stack will be - * clobbered, potentially all of it. This is called a clobbering deep bail. - * - * The nested trace will complete before we return to the deep-bailed one, - * hence the invariant is maintained that only one trace uses |tm->storage| - * at a time. - * - * When we return to the deep-bailed trace, it will very soon reach a - * STATUS_EXIT guard and bail out. Most of the native stack will just be - * thrown away. However, LeaveTree will copy a few slots from the top of - * the native stack to the interpreter stack--only those slots written by - * the current bytecode instruction. To make sure LeaveTree has correct - * data to copy from the native stack to the operand stack, we have this - * rule: every caller of enterDeepBailCall must ensure that between the - * deep bail call and the STATUS_EXIT guard, all those slots are written. - * - * The rule is a bit subtle. For example, JSOP_MOREITER uses a slot which - * it never writes to; in order to satisfy the above rule, - * record_JSOP_MOREITER emits code to write the value back to the slot - * anyway. - */ - state->deepBailSp = state->sp; -} - -JS_REQUIRES_STACK Value& -TraceRecorder::argval(unsigned n) const -{ - JS_ASSERT(n < cx->fp()->numFormalArgs()); - return cx->fp()->formalArg(n); -} - -JS_REQUIRES_STACK Value& -TraceRecorder::varval(unsigned n) const -{ - JS_ASSERT(n < cx->fp()->numSlots()); - return cx->fp()->slots()[n]; -} - -JS_REQUIRES_STACK Value& -TraceRecorder::stackval(int n) const -{ - return cx->regs().sp[n]; -} - -JS_REQUIRES_STACK void -TraceRecorder::updateAtoms() -{ - JSScript *script = cx->fp()->script(); - atoms = FrameAtomBase(cx, cx->fp()); - consts = (cx->fp()->hasImacropc() || !JSScript::isValidOffset(script->constOffset)) - ? 0 - : script->consts()->vector; - strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict"); -} - -JS_REQUIRES_STACK void -TraceRecorder::updateAtoms(JSScript *script) -{ - atoms = script->atomMap.vector; - consts = JSScript::isValidOffset(script->constOffset) ? script->consts()->vector : 0; - strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict"); -} - -/* - * Generate LIR to compute the scope chain. - */ -JS_REQUIRES_STACK LIns* -TraceRecorder::scopeChain() -{ - return cx->fp()->isFunctionFrame() - ? getFrameObjPtr(cx->fp()->addressOfScopeChain()) - : entryScopeChain(); -} - -/* - * Generate LIR to compute the scope chain on entry to the trace. This is - * generally useful only for getting to the global object, because only - * the global object is guaranteed to be present. - */ -JS_REQUIRES_STACK LIns* -TraceRecorder::entryScopeChain() const -{ - return w.ldpStackFrameScopeChain(entryFrameIns()); -} - -/* - * Generate LIR to compute the stack frame on entry to the trace. - */ -JS_REQUIRES_STACK LIns* -TraceRecorder::entryFrameIns() const -{ - return w.ldpFrameFp(w.ldpContextRegs(cx_ins)); -} - -/* - * Return the frame of a call object if that frame is part of the current - * trace. |depthp| is an optional outparam: if it is non-null, it will be - * filled in with the depth of the call object's frame relevant to cx->fp(). - */ -JS_REQUIRES_STACK StackFrame* -TraceRecorder::frameIfInRange(JSObject* obj, unsigned* depthp) const -{ - StackFrame* ofp = (StackFrame*) obj->getPrivate(); - StackFrame* fp = cx->fp(); - for (unsigned depth = 0; depth <= callDepth; ++depth) { - if (fp == ofp) { - if (depthp) - *depthp = depth; - return ofp; - } - if (!(fp = fp->prev())) - break; - } - return NULL; -} - -JS_DEFINE_CALLINFO_4(extern, UINT32, GetClosureVar, CONTEXT, OBJECT, CVIPTR, DOUBLEPTR, - 0, ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_4(extern, UINT32, GetClosureArg, CONTEXT, OBJECT, CVIPTR, DOUBLEPTR, - 0, ACCSET_STORE_ANY) - -/* - * Search the scope chain for a property lookup operation at the current PC and - * generate LIR to access the given property. Return RECORD_CONTINUE on success, - * otherwise abort and return RECORD_STOP. There are 3 outparams: - * - * vp the address of the current property value - * ins LIR instruction representing the property value on trace - * NameResult describes how to look up name; see comment for NameResult in jstracer.h - */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::scopeChainProp(JSObject* chainHead, Value*& vp, LIns*& ins, NameResult& nr, - JSObject** scopeObjp) -{ - JS_ASSERT(chainHead == &cx->fp()->scopeChain()); - JS_ASSERT(chainHead != globalObj); - - TraceMonitor &localtm = *traceMonitor; - - JSAtom* atom = atoms[GET_INDEX(cx->regs().pc)]; - JSObject* obj2; - JSProperty* prop; - JSObject *obj = chainHead; - if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop)) - RETURN_ERROR_A("error in js_FindProperty"); - - /* js_FindProperty can reenter the interpreter and kill |this|. */ - if (!localtm.recorder) - return ARECORD_ABORTED; - - if (!prop) - RETURN_STOP_A("failed to find name in non-global scope chain"); - - if (scopeObjp) - *scopeObjp = obj; - - if (obj == globalObj) { - // Even if the property is on the global object, we must guard against - // the creation of properties that shadow the property in the middle - // of the scope chain. - LIns *head_ins; - if (cx->fp()->isFunctionFrame()) { - // Skip any Call object when inside a function. Any reference to a - // Call name the compiler resolves statically and we do not need - // to match shapes of the Call objects. - chainHead = cx->fp()->callee().getParent(); - head_ins = w.ldpObjParent(get(&cx->fp()->calleev())); - } else { - head_ins = scopeChain(); - } - LIns *obj_ins; - CHECK_STATUS_A(traverseScopeChain(chainHead, head_ins, obj, obj_ins)); - - if (obj2 != obj) - RETURN_STOP_A("prototype property"); - - Shape* shape = (Shape*) prop; - if (!isValidSlot(obj, shape)) - return ARECORD_STOP; - if (!lazilyImportGlobalSlot(shape->slot)) - RETURN_STOP_A("lazy import of global slot failed"); - vp = &obj->getSlotRef(shape->slot); - ins = get(vp); - nr.tracked = true; - return ARECORD_CONTINUE; - } - - if (obj == obj2 && obj->isCall()) { - AbortableRecordingStatus status = - InjectStatus(callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr)); - return status; - } - - RETURN_STOP_A("fp->scopeChain is not global or active call object"); -} - -/* - * Generate LIR to access a property of a Call object. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, Value*& vp, - LIns*& ins, NameResult& nr) -{ - Shape *shape = (Shape*) prop; - - JSOp op = JSOp(*cx->regs().pc); - uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR)); - if (setflags && !shape->writable()) - RETURN_STOP("writing to a read-only property"); - - uintN slot = uint16(shape->shortid); - - vp = NULL; - StackFrame* cfp = (StackFrame*) obj->getPrivate(); - if (cfp) { - if (shape->getterOp() == GetCallArg) { - JS_ASSERT(slot < cfp->numFormalArgs()); - vp = &cfp->formalArg(slot); - nr.v = *vp; - } else if (shape->getterOp() == GetCallVar || - shape->getterOp() == GetCallVarChecked) { - JS_ASSERT(slot < cfp->numSlots()); - vp = &cfp->slots()[slot]; - nr.v = *vp; - } else { - RETURN_STOP("dynamic property of Call object"); - } - - // Now assert that our use of shape->shortid was in fact kosher. - JS_ASSERT(shape->hasShortID()); - - if (frameIfInRange(obj)) { - // At this point we are guaranteed to be looking at an active call oject - // whose properties are stored in the corresponding StackFrame. - ins = get(vp); - nr.tracked = true; - return RECORD_CONTINUE; - } - } else { - // Call objects do not yet have shape->isMethod() properties, but they - // should. See bug 514046, for which this code is future-proof. Remove - // this comment when that bug is fixed (so, FIXME: 514046). - DebugOnly rv = - js_GetPropertyHelper(cx, obj, shape->id, - (op == JSOP_CALLNAME) - ? JSGET_NO_METHOD_BARRIER - : JSGET_METHOD_BARRIER, - &nr.v); - JS_ASSERT(rv); - } - - LIns* obj_ins; - JSObject* parent = cx->fp()->callee().getParent(); - LIns* parent_ins = w.ldpObjParent(get(&cx->fp()->calleev())); - CHECK_STATUS(traverseScopeChain(parent, parent_ins, obj, obj_ins)); - - if (!cfp) { - // Because the parent guard in guardCallee ensures this Call object - // will be the same object now and on trace, and because once a Call - // object loses its frame it never regains one, on trace we will also - // have a null private in the Call object. So all we need to do is - // write the value to the Call object's slot. - if (shape->getterOp() == GetCallArg) { - JS_ASSERT(slot < ArgClosureTraits::slot_count(obj)); - slot += ArgClosureTraits::slot_offset(obj); - } else if (shape->getterOp() == GetCallVar || - shape->getterOp() == GetCallVarChecked) { - JS_ASSERT(slot < VarClosureTraits::slot_count(obj)); - slot += VarClosureTraits::slot_offset(obj); - } else { - RETURN_STOP("dynamic property of Call object"); - } - - // Now assert that our use of shape->shortid was in fact kosher. - JS_ASSERT(shape->hasShortID()); - - ins = unbox_slot(obj, obj_ins, slot, snapshot(BRANCH_EXIT)); - } else { - ClosureVarInfo* cv = new (traceAlloc()) ClosureVarInfo(); - cv->slot = slot; -#ifdef DEBUG - cv->callDepth = callDepth; -#endif - - // Even though the frame is out of range, later we might be called as an - // inner trace such that the target variable is defined in the outer trace - // entry frame. For simplicity, we just fall off trace. - guard(false, - w.eqp(entryFrameIns(), w.ldpObjPrivate(obj_ins)), - MISMATCH_EXIT); - - LIns* outp = w.allocp(sizeof(double)); - LIns* args[] = { - outp, - w.nameImmpNonGC(cv), - obj_ins, - cx_ins - }; - const CallInfo* ci; - if (shape->getterOp() == GetCallArg) { - ci = &GetClosureArg_ci; - } else if (shape->getterOp() == GetCallVar || - shape->getterOp() == GetCallVarChecked) { - ci = &GetClosureVar_ci; - } else { - RETURN_STOP("dynamic property of Call object"); - } - - // Now assert that our use of shape->shortid was in fact kosher. - JS_ASSERT(shape->hasShortID()); - - LIns* call_ins = w.call(ci, args); - - JSValueType type = getCoercedType(nr.v); - guard(true, - w.name(w.eqi(call_ins, w.immi(type)), "guard(type-stable name access)"), - BRANCH_EXIT); - ins = stackLoad(AllocSlotsAddress(outp), type); - } - nr.tracked = false; - nr.obj = obj; - nr.obj_ins = obj_ins; - nr.shape = shape; - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::arg(unsigned n) -{ - return get(&argval(n)); -} - -JS_REQUIRES_STACK void -TraceRecorder::arg(unsigned n, LIns* i) -{ - set(&argval(n), i); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::var(unsigned n) -{ - return get(&varval(n)); -} - -JS_REQUIRES_STACK void -TraceRecorder::var(unsigned n, LIns* i) -{ - set(&varval(n), i); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::stack(int n) -{ - return get(&stackval(n)); -} - -JS_REQUIRES_STACK void -TraceRecorder::stack(int n, LIns* i) -{ - set(&stackval(n), i); -} - -/* Leave trace iff one operand is negative and the other is non-negative. */ -JS_REQUIRES_STACK void -TraceRecorder::guardNonNeg(LIns* d0, LIns* d1, VMSideExit* exit) -{ - if (d0->isImmI()) - JS_ASSERT(d0->immI() >= 0); - else - guard(false, w.ltiN(d0, 0), exit); - - if (d1->isImmI()) - JS_ASSERT(d1->immI() >= 0); - else - guard(false, w.ltiN(d1, 0), exit); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::tryToDemote(LOpcode op, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) -{ - /* - * If the operands and result of an arithmetic operation are all integers - * at record-time, and the oracle doesn't direct us otherwise, we - * speculatively emit a demoted (integer) operation, betting that at - * runtime we will get integer results again. - * - * We also have to protect against various edge cases. For example, - * to protect against overflow we emit a guard that will inform the oracle - * on overflow and cause a non-demoted trace to be attached that uses - * floating-point math for this operation; the exception to this case is - * if the operands guarantee that the result will be an integer (e.g. - * z = d0 * d1 with 0 <= (d0|d1) <= 0xffff guarantees z <= fffe0001). - */ - - if (!oracle || oracle->isInstructionUndemotable(cx->regs().pc) || - !IsPromotedInt32(s0) || !IsPromotedInt32(s1)) - { - undemotable: - if (op == LIR_modd) { - /* - * LIR_modd is a placeholder that Nanojit doesn't actually support! - * Convert it to a call. - */ - LIns* args[] = { s1, s0 }; - return w.call(&js_dmod_ci, args); - } - LIns* result = w.ins2(op, s0, s1); - JS_ASSERT_IF(s0->isImmD() && s1->isImmD(), result->isImmD()); - return result; - } - - LIns* d0 = w.demoteToInt32(s0); - LIns* d1 = w.demoteToInt32(s1); - jsdouble r = 0; /* init to shut GCC up */ - VMSideExit* exit = NULL; - LIns* result; - - switch (op) { - case LIR_addd: { - r = v0 + v1; - if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) - goto undemotable; - - Interval i0 = Interval::of(d0, 3); - Interval i1 = Interval::of(d1, 3); - result = Interval::add(i0, i1).hasOverflowed - ? w.addxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT))) - : w.addi(d0, d1); - break; - } - - case LIR_subd: { - r = v0 - v1; - if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) - goto undemotable; - - Interval i0 = Interval::of(d0, 3); - Interval i1 = Interval::of(d1, 3); - result = Interval::sub(i0, i1).hasOverflowed - ? w.subxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT))) - : w.subi(d0, d1); - break; - } - - case LIR_muld: { - r = v0 * v1; - if (r == 0.0 && (v0 < 0.0 || v1 < 0.0)) - goto undemotable; - - if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) - goto undemotable; - - Interval i0 = Interval::of(d0, 3); - Interval i1 = Interval::of(d1, 3); - if (Interval::mul(i0, i1).hasOverflowed) { - exit = snapshot(OVERFLOW_EXIT); - result = w.mulxovi(d0, d1, createGuardRecord(exit)); - } else { - result = w.muli(d0, d1); - } - - /* - * A would-be negative zero result can only occur if we have - * mul(0, -n) or mul(-n, 0), where n != 0. In particular, a multiply - * where one operand is a positive immediate cannot result in negative - * zero. - * - * This assumes that -0 cannot be an operand; if one had occurred we - * would have already exited the trace in order to promote the - * computation back to doubles. - */ - bool needsNegZeroCheck = (i0.canBeZero() && i1.canBeNegative()) || - (i1.canBeZero() && i0.canBeNegative()); - if (needsNegZeroCheck) { - /* - * Make sure we don't lose a -0. We exit if the result is zero and if - * either operand is negative. We start out using a weaker guard, checking - * if either argument is negative. If this ever fails, we recompile with - * a stronger, but slower, guard. - */ - if (v0 < 0.0 || v1 < 0.0 || oracle->isInstructionSlowZeroTest(cx->regs().pc)) { - if (!exit) - exit = snapshot(OVERFLOW_EXIT); - - guard(true, - w.eqi0(w.andi(w.eqi0(result), - w.ori(w.ltiN(d0, 0), - w.ltiN(d1, 0)))), - exit); - } else { - guardNonNeg(d0, d1, snapshot(MUL_ZERO_EXIT)); - } - } - break; - } - - case LIR_divd: { -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - if (v1 == 0) - goto undemotable; - r = v0 / v1; - if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) - goto undemotable; - - /* Check for this case ourselves; Nanojit won't do it for us. */ - if (d0->isImmI() && d1->isImmI()) - return w.i2d(w.immi(jsint(r))); - - exit = snapshot(OVERFLOW_EXIT); - - /* - * If the divisor is greater than zero its always safe to execute - * the division. If not, we have to make sure we are not running - * into -2147483648 / -1, because it can raise an overflow exception. - */ - if (!d1->isImmI()) { - if (MaybeBranch mbr = w.jt(w.gtiN(d1, 0))) { - guard(false, w.eqi0(d1), exit); - guard(true, w.eqi0(w.andi(w.eqiN(d0, 0x80000000), - w.eqiN(d1, -1))), exit); - w.label(mbr); - } - } else if (d1->immI() == -1) { - guard(false, w.eqiN(d0, 0x80000000), exit); - } - result = w.divi(d0, d1); - - /* As long as the modulus is zero, the result is an integer. */ - guard(true, w.eqi0(w.modi(result)), exit); - - /* Don't lose a -0. */ - guard(false, w.eqi0(result), exit); - - break; -#else - goto undemotable; -#endif - } - - case LIR_modd: { -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - if (v0 < 0 || v1 == 0 || (s1->isImmD() && v1 < 0)) - goto undemotable; - r = js_dmod(v0, v1); - if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) - goto undemotable; - - /* Check for this case ourselves; Nanojit won't do it for us. */ - if (d0->isImmI() && d1->isImmI()) - return w.i2d(w.immi(jsint(r))); - - exit = snapshot(OVERFLOW_EXIT); - - /* Make sure we don't trigger division by zero at runtime. */ - if (!d1->isImmI()) - guard(false, w.eqi0(d1), exit); - result = w.modi(w.divi(d0, d1)); - - /* - * If the result is not 0, it is always within the integer domain. - * Otherwise, we must exit if the lhs is negative since the result is - * -0 in this case, which is not in the integer domain. - */ - if (MaybeBranch mbr = w.jf(w.eqi0(result))) { - guard(false, w.ltiN(d0, 0), exit); - w.label(mbr); - } - break; -#else - goto undemotable; -#endif - } - - default: - JS_NOT_REACHED("tryToDemote"); - result = NULL; - break; - } - - /* - * Successful demotion! Convert result to a double. This i2d will be - * removed if the result feeds into another integer or demoted operation. - */ - JS_ASSERT_IF(d0->isImmI() && d1->isImmI(), result->isImmI(jsint(r))); - return w.i2d(result); -} - -LIns* -TraceRecorder::d2i(LIns* d, bool resultCanBeImpreciseIfFractional) -{ - if (d->isImmD()) - return w.immi(js_DoubleToECMAInt32(d->immD())); - if (d->isop(LIR_i2d) || d->isop(LIR_ui2d)) { - // The d2i(i2d(i)) case is obviously a no-op. (Unlike i2d(d2i(d))!) - // The d2i(ui2d(ui)) case is less obvious, but it is also a no-op. - // For example, 4294967295U has the bit pattern 0xffffffff, and - // d2i(ui2d(4294967295U)) is -1, which also has the bit pattern - // 0xffffffff. Another way to think about it: d2i(ui2d(ui)) is - // equivalent to ui2i(ui); ui2i doesn't exist, but it would be a - // no-op if it did. - // (Note that the above reasoning depends on the fact that d2i() - // always succeeds, ie. it never aborts). - return d->oprnd1(); - } - if (d->isop(LIR_addd) || d->isop(LIR_subd)) { - // If 'i32ad' and 'i32bd' are integral doubles that fit in int32s, and - // 'i32ai' and 'i32bi' are int32s with the equivalent values, then - // this is true: - // - // d2i(addd(i32ad, i32bd)) == addi(i32ai, i32bi) - // - // If the RHS doesn't overflow, this is obvious. If it does overflow, - // the result will truncate. And the LHS will truncate in exactly the - // same way. So they're always equal. - LIns* lhs = d->oprnd1(); - LIns* rhs = d->oprnd2(); - if (IsPromotedInt32(lhs) && IsPromotedInt32(rhs)) - return w.ins2(arithOpcodeD2I(d->opcode()), w.demoteToInt32(lhs), w.demoteToInt32(rhs)); - } - if (d->isCall()) { - const CallInfo* ci = d->callInfo(); - if (ci == &js_UnboxDouble_ci) { -#if JS_BITS_PER_WORD == 32 - LIns *tag_ins = d->callArgN(0); - LIns *payload_ins = d->callArgN(1); - LIns* args[] = { payload_ins, tag_ins }; - return w.call(&js_UnboxInt32_ci, args); -#else - LIns* val_ins = d->callArgN(0); - LIns* args[] = { val_ins }; - return w.call(&js_UnboxInt32_ci, args); -#endif - } - if (ci == &js_StringToNumber_ci) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, d->callArgN(1), d->callArgN(0) }; - LIns* ret_ins = w.call(&js_StringToInt32_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - return ret_ins; - } - } - return resultCanBeImpreciseIfFractional - ? w.rawD2i(d) - : w.call(&js_DoubleToInt32_ci, &d); -} - -LIns* -TraceRecorder::d2u(LIns* d) -{ - if (d->isImmD()) - return w.immi(js_DoubleToECMAUint32(d->immD())); - if (d->isop(LIR_i2d) || d->isop(LIR_ui2d)) - return d->oprnd1(); - return w.call(&js_DoubleToUint32_ci, &d); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::makeNumberInt32(LIns* d, LIns** out) -{ - JS_ASSERT(d->isD()); - if (IsPromotedInt32(d)) { - *out = w.demoteToInt32(d); - return RECORD_CONTINUE; - } - - // This means "convert double to int if it's integral, otherwise - // exit". We first convert the double to an int, then convert it back - // and exit if the two doubles don't match. If 'f' is a non-integral - // immediate we'll end up aborting. - *out = d2i(d, /* resultCanBeImpreciseIfFractional = */true); - return guard(true, w.eqd(d, w.i2d(*out)), MISMATCH_EXIT, /* abortIfAlwaysExits = */true); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::makeNumberUint32(LIns* d, LIns** out) -{ - JS_ASSERT(d->isD()); - if (IsPromotedUint32(d)) { - *out = w.demoteToUint32(d); - return RECORD_CONTINUE; - } - - // This means "convert double to uint if it's integral, otherwise - // exit". We first convert the double to an unsigned int, then - // convert it back and exit if the two doubles don't match. If - // 'f' is a non-integral immediate we'll end up aborting. - *out = d2u(d); - return guard(true, w.eqd(d, w.ui2d(*out)), MISMATCH_EXIT, /* abortIfAlwaysExits = */true); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::stringify(const Value& v) -{ - LIns* v_ins = get(&v); - if (v.isString()) - return v_ins; - - LIns* args[] = { v_ins, cx_ins }; - const CallInfo* ci; - if (v.isNumber()) { - ci = &js_NumberToString_ci; - } else if (v.isUndefined()) { - return w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); - } else if (v.isBoolean()) { - ci = &js_BooleanIntToString_ci; - } else { - /* - * Callers must deal with non-primitive (non-null object) values by - * calling an imacro. We don't try to guess about which imacro, with - * what valueOf hint, here. - */ - JS_ASSERT(v.isNull()); - return w.immpAtomGC(cx->runtime->atomState.nullAtom); - } - - v_ins = w.call(ci, args); - guard(false, w.eqp0(v_ins), OOM_EXIT); - return v_ins; -} - -JS_REQUIRES_STACK bool -TraceRecorder::canCallImacro() const -{ - /* We cannot nest imacros. */ - return !cx->fp()->hasImacropc(); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callImacro(jsbytecode* imacro) -{ - return canCallImacro() ? callImacroInfallibly(imacro) : RECORD_STOP; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callImacroInfallibly(jsbytecode* imacro) -{ - StackFrame* fp = cx->fp(); - JS_ASSERT(!fp->hasImacropc()); - FrameRegs& regs = cx->regs(); - fp->setImacropc(regs.pc); - regs.pc = imacro; - updateAtoms(); - return RECORD_IMACRO; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::ifop() -{ - Value& v = stackval(-1); - LIns* v_ins = get(&v); - bool cond; - LIns* x; - - if (v.isNull() || v.isUndefined()) { - cond = false; - x = w.immi(0); - } else if (!v.isPrimitive()) { - cond = true; - x = w.immi(1); - } else if (v.isBoolean()) { - /* Test for boolean is true, negate later if we are testing for false. */ - cond = v.isTrue(); - x = w.eqiN(v_ins, 1); - } else if (v.isNumber()) { - jsdouble d = v.toNumber(); - cond = !JSDOUBLE_IS_NaN(d) && d; - x = w.eqi0(w.eqi0(w.andi(w.eqd(v_ins, v_ins), w.eqi0(w.eqd0(v_ins))))); - } else if (v.isString()) { - cond = v.toString()->length() != 0; - x = w.eqi0(w.eqp0(w.getStringLength(v_ins))); - } else { - JS_NOT_REACHED("ifop"); - return ARECORD_STOP; - } - - jsbytecode* pc = cx->regs().pc; - emitIf(pc, cond, x); - return checkTraceEnd(pc); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::switchop() -{ - Value& v = stackval(-1); - LIns* v_ins = get(&v); - - /* No need to guard if the condition is constant. */ - if (v_ins->isImmAny()) - return RECORD_CONTINUE; - if (v.isNumber()) { - jsdouble d = v.toNumber(); - CHECK_STATUS(guard(true, - w.name(w.eqd(v_ins, w.immd(d)), "guard(switch on numeric)"), - BRANCH_EXIT, - /* abortIfAlwaysExits = */true)); - } else if (v.isString()) { - LIns* args[] = { w.immpStrGC(v.toString()), v_ins, cx_ins }; - LIns* equal_rval = w.call(&js_EqualStringsOnTrace_ci, args); - guard(false, - w.name(w.eqiN(equal_rval, JS_NEITHER), "guard(oom)"), - OOM_EXIT); - guard(false, - w.name(w.eqi0(equal_rval), "guard(switch on string)"), - BRANCH_EXIT); - } else if (v.isBoolean()) { - guard(true, - w.name(w.eqi(v_ins, w.immi(v.isTrue())), "guard(switch on boolean)"), - BRANCH_EXIT); - } else if (v.isUndefined()) { - // This is a unit type, so no guard is needed. - } else { - RETURN_STOP("switch on object or null"); - } - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::inc(Value& v, jsint incr, bool pre) -{ - LIns* v_ins = get(&v); - Value dummy; - CHECK_STATUS(inc(v, v_ins, dummy, incr, pre)); - set(&v, v_ins); - return RECORD_CONTINUE; -} - -/* - * On exit, v_ins is the incremented unboxed value, and the appropriate value - * (pre- or post-increment as described by pre) is stacked. v_out is set to - * the value corresponding to v_ins. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::inc(const Value &v, LIns*& v_ins, Value &v_out, jsint incr, bool pre) -{ - LIns* v_after; - CHECK_STATUS(incHelper(v, v_ins, v_out, v_after, incr)); - - const JSCodeSpec& cs = js_CodeSpec[*cx->regs().pc]; - JS_ASSERT(cs.ndefs == 1); - stack(-cs.nuses, pre ? v_after : v_ins); - v_ins = v_after; - return RECORD_CONTINUE; -} - -/* - * Do an increment operation without storing anything to the stack. - * - * v_after is an out param whose value corresponds to the instruction the - * v_ins_after out param gets set to. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::incHelper(const Value &v, LIns*& v_ins, Value &v_after, - LIns*& v_ins_after, jsint incr) -{ - // FIXME: Bug 606071 on making this work for objects. - if (!v.isPrimitive()) - RETURN_STOP("can inc primitives only"); - - // We need to modify |v_ins| the same way relational() modifies - // its RHS and LHS. - if (v.isUndefined()) { - v_ins_after = w.immd(js_NaN); - v_after.setDouble(js_NaN); - v_ins = w.immd(js_NaN); - } else if (v.isNull()) { - v_ins_after = w.immd(incr); - v_after.setDouble(incr); - v_ins = w.immd(0.0); - } else { - if (v.isBoolean()) { - v_ins = w.i2d(v_ins); - } else if (v.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, v_ins, cx_ins }; - v_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - } else { - JS_ASSERT(v.isNumber()); - } - - jsdouble num; - AutoValueRooter tvr(cx); - *tvr.addr() = v; - ValueToNumber(cx, tvr.value(), &num); - v_ins_after = tryToDemote(LIR_addd, num, incr, v_ins, w.immd(incr)); - v_after.setDouble(num + incr); - } - - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::incProp(jsint incr, bool pre) -{ - Value& l = stackval(-1); - if (l.isPrimitive()) - RETURN_STOP_A("incProp on primitive"); - - JSObject* obj = &l.toObject(); - LIns* obj_ins = get(&l); - - uint32 slot; - LIns* v_ins; - CHECK_STATUS_A(prop(obj, obj_ins, &slot, &v_ins, NULL)); - - if (slot == SHAPE_INVALID_SLOT) - RETURN_STOP_A("incProp on invalid slot"); - - Value& v = obj->getSlotRef(slot); - Value v_after; - CHECK_STATUS_A(inc(v, v_ins, v_after, incr, pre)); - - LIns* slots_ins = NULL; - stobj_set_slot(obj, obj_ins, slot, slots_ins, v_after, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::incElem(jsint incr, bool pre) -{ - Value& r = stackval(-1); - Value& l = stackval(-2); - Value* vp; - LIns* v_ins; - LIns* addr_ins; - - if (!l.isPrimitive() && l.toObject().isDenseArray() && r.isInt32()) { - guardDenseArray(get(&l), MISMATCH_EXIT); - CHECK_STATUS(denseArrayElement(l, r, vp, v_ins, addr_ins, snapshot(BRANCH_EXIT))); - if (!addr_ins) // if we read a hole, abort - return RECORD_STOP; - Value v_after; - CHECK_STATUS(inc(*vp, v_ins, v_after, incr, pre)); - box_value_into(v_after, v_ins, DSlotsAddress(addr_ins)); - return RECORD_CONTINUE; - } - - return callImacro((incr == 1) - ? pre ? incelem_imacros.incelem : incelem_imacros.eleminc - : pre ? decelem_imacros.decelem : decelem_imacros.elemdec); -} - -static bool -EvalCmp(LOpcode op, double l, double r) -{ - bool cond; - switch (op) { - case LIR_eqd: - cond = (l == r); - break; - case LIR_ltd: - cond = l < r; - break; - case LIR_gtd: - cond = l > r; - break; - case LIR_led: - cond = l <= r; - break; - case LIR_ged: - cond = l >= r; - break; - default: - JS_NOT_REACHED("unexpected comparison op"); - return false; - } - return cond; -} - -static bool -EvalCmp(JSContext *cx, LOpcode op, JSString* l, JSString* r, JSBool *ret) -{ - if (op == LIR_eqd) - return EqualStrings(cx, l, r, ret); - JSBool cmp; - if (!CompareStrings(cx, l, r, &cmp)) - return false; - *ret = EvalCmp(op, cmp, 0); - return true; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::strictEquality(bool equal, bool cmpCase) -{ - Value& r = stackval(-1); - Value& l = stackval(-2); - LIns* l_ins = get(&l); - LIns* r_ins = get(&r); - LIns* x; - JSBool cond; - - JSValueType ltag = getPromotedType(l); - if (ltag != getPromotedType(r)) { - cond = !equal; - x = w.immi(cond); - } else if (ltag == JSVAL_TYPE_STRING) { - LIns* args[] = { r_ins, l_ins, cx_ins }; - LIns* equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); - guard(false, - w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), - OOM_EXIT); - x = w.eqiN(equal_ins, equal); - if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) - RETURN_ERROR("oom"); - } else { - if (ltag == JSVAL_TYPE_DOUBLE) - x = w.eqd(l_ins, r_ins); - else if (ltag == JSVAL_TYPE_NULL || ltag == JSVAL_TYPE_NONFUNOBJ || ltag == JSVAL_TYPE_FUNOBJ) - x = w.eqp(l_ins, r_ins); - else - x = w.eqi(l_ins, r_ins); - if (!equal) - x = w.eqi0(x); - cond = (ltag == JSVAL_TYPE_DOUBLE) - ? l.toNumber() == r.toNumber() - : l == r; - } - cond = (!!cond == equal); - - if (cmpCase) { - /* Only guard if the same path may not always be taken. */ - if (!x->isImmI()) - guard(cond, x, BRANCH_EXIT); - return RECORD_CONTINUE; - } - - set(&l, x); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::equality(bool negate, bool tryBranchAfterCond) -{ - Value& rval = stackval(-1); - Value& lval = stackval(-2); - LIns* l_ins = get(&lval); - LIns* r_ins = get(&rval); - - return equalityHelper(lval, rval, l_ins, r_ins, negate, tryBranchAfterCond, lval); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, - bool negate, bool tryBranchAfterCond, - Value& rval) -{ - LOpcode op = LIR_eqi; - JSBool cond; - LIns* args[] = { NULL, NULL, NULL }; - - /* - * The if chain below closely mirrors that found in 11.9.3, in general - * deviating from that ordering of ifs only to account for SpiderMonkey's - * conflation of booleans and undefined and for the possibility of - * confusing objects and null. Note carefully the spec-mandated recursion - * in the final else clause, which terminates because Number == T recurs - * only if T is Object, but that must recur again to convert Object to - * primitive, and ToPrimitive throws if the object cannot be converted to - * a primitive value (which would terminate recursion). - */ - - if (getPromotedType(l) == getPromotedType(r)) { - if (l.isUndefined() || l.isNull()) { - cond = true; - if (l.isNull()) - op = LIR_eqp; - } else if (l.isObject()) { - if (l.toObject().getClass()->ext.equality) - RETURN_STOP_A("Can't trace extended class equality operator"); - LIns* flags_ins = w.ldiObjFlags(l_ins); - LIns* flag_ins = w.andi(flags_ins, w.nameImmui(JSObject::HAS_EQUALITY)); - guard(true, w.eqi0(flag_ins), BRANCH_EXIT); - - op = LIR_eqp; - cond = (l == r); - } else if (l.isBoolean()) { - JS_ASSERT(r.isBoolean()); - cond = (l == r); - } else if (l.isString()) { - JSString *l_str = l.toString(); - JSString *r_str = r.toString(); - if (!l_str->isRope() && !r_str->isRope() && l_str->length() == 1 && r_str->length() == 1) { - VMSideExit *exit = snapshot(BRANCH_EXIT); - LIns *c = w.immw(1); - guard(true, w.eqp(w.getStringLength(l_ins), c), exit); - guard(true, w.eqp(w.getStringLength(r_ins), c), exit); - l_ins = w.getStringChar(l_ins, w.immpNonGC(0)); - r_ins = w.getStringChar(r_ins, w.immpNonGC(0)); - } else { - args[0] = r_ins, args[1] = l_ins, args[2] = cx_ins; - LIns *equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); - guard(false, - w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), - OOM_EXIT); - l_ins = equal_ins; - r_ins = w.immi(1); - } - if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) - RETURN_ERROR_A("oom"); - } else { - JS_ASSERT(l.isNumber() && r.isNumber()); - cond = (l.toNumber() == r.toNumber()); - op = LIR_eqd; - } - } else if (l.isNull() && r.isUndefined()) { - l_ins = w.immiUndefined(); - cond = true; - } else if (l.isUndefined() && r.isNull()) { - r_ins = w.immiUndefined(); - cond = true; - } else if (l.isNumber() && r.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - args[0] = ok_ins, args[1] = r_ins, args[2] = cx_ins; - r_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - JSBool ok; - double d = js_StringToNumber(cx, r.toString(), &ok); - if (!ok) - RETURN_ERROR_A("oom"); - cond = (l.toNumber() == d); - op = LIR_eqd; - } else if (l.isString() && r.isNumber()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - args[0] = ok_ins, args[1] = l_ins, args[2] = cx_ins; - l_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - JSBool ok; - double d = js_StringToNumber(cx, l.toString(), &ok); - if (!ok) - RETURN_ERROR_A("oom"); - cond = (d == r.toNumber()); - op = LIR_eqd; - } else { - // Below we may assign to l or r, which modifies the interpreter state. - // This is fine as long as we also update the tracker. - if (l.isBoolean()) { - l_ins = w.i2d(l_ins); - set(&l, l_ins); - l.setInt32(l.isTrue()); - return equalityHelper(l, r, l_ins, r_ins, negate, - tryBranchAfterCond, rval); - } - if (r.isBoolean()) { - r_ins = w.i2d(r_ins); - set(&r, r_ins); - r.setInt32(r.isTrue()); - return equalityHelper(l, r, l_ins, r_ins, negate, - tryBranchAfterCond, rval); - } - if ((l.isString() || l.isNumber()) && !r.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(r)); - return InjectStatus(callImacro(equality_imacros.any_obj)); - } - if (!l.isPrimitive() && (r.isString() || r.isNumber())) { - CHECK_STATUS_A(guardNativeConversion(l)); - return InjectStatus(callImacro(equality_imacros.obj_any)); - } - - l_ins = w.immi(0); - r_ins = w.immi(1); - cond = false; - } - - /* If the operands aren't numbers, compare them as integers. */ - LIns* x = w.ins2(op, l_ins, r_ins); - if (negate) { - x = w.eqi0(x); - cond = !cond; - } - - jsbytecode* pc = cx->regs().pc; - - /* - * Don't guard if the same path is always taken. If it isn't, we have to - * fuse comparisons and the following branch, because the interpreter does - * that. - */ - if (tryBranchAfterCond) - fuseIf(pc + 1, cond, x); - - /* - * There is no need to write out the result of this comparison if the trace - * ends on this operation. - */ - if (pc[1] == JSOP_IFNE || pc[1] == JSOP_IFEQ) - CHECK_STATUS_A(checkTraceEnd(pc + 1)); - - /* - * We update the stack after the guard. This is safe since the guard bails - * out at the comparison and the interpreter will therefore re-execute the - * comparison. This way the value of the condition doesn't have to be - * calculated and saved on the stack in most cases. - */ - set(&rval, x); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) -{ - Value& r = stackval(-1); - Value& l = stackval(-2); - LIns* x = NULL; - JSBool cond; - LIns* l_ins = get(&l); - LIns* r_ins = get(&r); - bool fp = false; - jsdouble lnum, rnum; - - /* - * 11.8.5 if either argument is an object with a function-valued valueOf - * property; if both arguments are objects with non-function-valued valueOf - * properties, abort. - */ - if (!l.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(l)); - if (!r.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(r)); - return InjectStatus(callImacro(binary_imacros.obj_obj)); - } - return InjectStatus(callImacro(binary_imacros.obj_any)); - } - if (!r.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(r)); - return InjectStatus(callImacro(binary_imacros.any_obj)); - } - - /* 11.8.5 steps 3, 16-21. */ - if (l.isString() && r.isString()) { - LIns* args[] = { r_ins, l_ins, cx_ins }; - LIns* result_ins = w.call(&js_CompareStringsOnTrace_ci, args); - guard(false, - w.name(w.eqiN(result_ins, INT32_MIN), "guard(oom)"), - OOM_EXIT); - l_ins = result_ins; - r_ins = w.immi(0); - if (!EvalCmp(cx, op, l.toString(), r.toString(), &cond)) - RETURN_ERROR_A("oom"); - goto do_comparison; - } - - /* 11.8.5 steps 4-5. */ - if (!l.isNumber()) { - if (l.isBoolean()) { - l_ins = w.i2d(l_ins); - } else if (l.isUndefined()) { - l_ins = w.immd(js_NaN); - } else if (l.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, l_ins, cx_ins }; - l_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - } else if (l.isNull()) { - l_ins = w.immd(0.0); - } else { - JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should " - "have been handled at start of method"); - RETURN_STOP_A("safety belt"); - } - } - if (!r.isNumber()) { - if (r.isBoolean()) { - r_ins = w.i2d(r_ins); - } else if (r.isUndefined()) { - r_ins = w.immd(js_NaN); - } else if (r.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, r_ins, cx_ins }; - r_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - } else if (r.isNull()) { - r_ins = w.immd(0.0); - } else { - JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should " - "have been handled at start of method"); - RETURN_STOP_A("safety belt"); - } - } - { - AutoValueRooter tvr(cx); - *tvr.addr() = l; - ValueToNumber(cx, tvr.value(), &lnum); - *tvr.addr() = r; - ValueToNumber(cx, tvr.value(), &rnum); - } - cond = EvalCmp(op, lnum, rnum); - fp = true; - - /* 11.8.5 steps 6-15. */ - do_comparison: - /* - * If the result is not a number or it's not a quad, we must use an integer - * compare. - */ - if (!fp) { - JS_ASSERT(isCmpDOpcode(op)); - op = cmpOpcodeD2I(op); - } - x = w.ins2(op, l_ins, r_ins); - - jsbytecode* pc = cx->regs().pc; - - /* - * Don't guard if the same path is always taken. If it isn't, we have to - * fuse comparisons and the following branch, because the interpreter does - * that. - */ - if (tryBranchAfterCond) - fuseIf(pc + 1, cond, x); - - /* - * There is no need to write out the result of this comparison if the trace - * ends on this operation. - */ - if (pc[1] == JSOP_IFNE || pc[1] == JSOP_IFEQ) - CHECK_STATUS_A(checkTraceEnd(pc + 1)); - - /* - * We update the stack after the guard. This is safe since the guard bails - * out at the comparison and the interpreter will therefore re-execute the - * comparison. This way the value of the condition doesn't have to be - * calculated and saved on the stack in most cases. - */ - set(&l, x); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::unaryIntOp(LOpcode op) -{ - Value& v = stackval(-1); - JS_ASSERT(retTypes[op] == LTy_I); - if (v.isNumber()) { - LIns* a = get(&v); - a = w.i2d(w.ins1(op, d2i(a))); - set(&v, a); - return RECORD_CONTINUE; - } - return RECORD_STOP; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::binary(LOpcode op) -{ - Value& r = stackval(-1); - Value& l = stackval(-2); - - if (!l.isPrimitive()) { - CHECK_STATUS(guardNativeConversion(l)); - if (!r.isPrimitive()) { - CHECK_STATUS(guardNativeConversion(r)); - return callImacro(binary_imacros.obj_obj); - } - return callImacro(binary_imacros.obj_any); - } - if (!r.isPrimitive()) { - CHECK_STATUS(guardNativeConversion(r)); - return callImacro(binary_imacros.any_obj); - } - - bool intop = retTypes[op] == LTy_I; - LIns* a = get(&l); - LIns* b = get(&r); - - bool leftIsNumber = l.isNumber(); - jsdouble lnum = leftIsNumber ? l.toNumber() : 0; - - bool rightIsNumber = r.isNumber(); - jsdouble rnum = rightIsNumber ? r.toNumber() : 0; - - if (l.isString()) { - NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, a, cx_ins }; - a = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - JSBool ok; - lnum = js_StringToNumber(cx, l.toString(), &ok); - if (!ok) - RETURN_ERROR("oom"); - leftIsNumber = true; - } - if (r.isString()) { - NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, b, cx_ins }; - b = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - JSBool ok; - rnum = js_StringToNumber(cx, r.toString(), &ok); - if (!ok) - RETURN_ERROR("oom"); - rightIsNumber = true; - } - if (l.isBoolean()) { - a = w.i2d(a); - lnum = l.toBoolean(); - leftIsNumber = true; - } else if (l.isUndefined()) { - a = w.immd(js_NaN); - lnum = js_NaN; - leftIsNumber = true; - } - if (r.isBoolean()) { - b = w.i2d(b); - rnum = r.toBoolean(); - rightIsNumber = true; - } else if (r.isUndefined()) { - b = w.immd(js_NaN); - rnum = js_NaN; - rightIsNumber = true; - } - if (leftIsNumber && rightIsNumber) { - if (intop) { - a = (op == LIR_rshui) - ? w.ui2d(w.ins2(op, d2u(a), d2i(b))) - : w.i2d(w.ins2(op, d2i(a), d2i(b))); - } else { - a = tryToDemote(op, lnum, rnum, a, b); - } - set(&l, a); - return RECORD_CONTINUE; - } - return RECORD_STOP; -} - -#if defined DEBUG_notme && defined XP_UNIX -#include - -static FILE* shapefp = NULL; - -static void -DumpShape(JSObject* obj, const char* prefix) -{ - if (!shapefp) { - shapefp = fopen("/tmp/shapes.dump", "w"); - if (!shapefp) - return; - } - - fprintf(shapefp, "\n%s: shape %u flags %x\n", prefix, obj->shape(), obj->flags); - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - const Shape &shape = r.front(); - - if (JSID_IS_ATOM(shape.id)) { - putc(' ', shapefp); - JS_PutString(JSID_TO_STRING(shape.id), shapefp); - } else { - JS_ASSERT(!JSID_IS_OBJECT(shape.id)); - fprintf(shapefp, " %d", JSID_TO_INT(shape.id)); - } - fprintf(shapefp, " %u %p %p %x %x %d\n", - shape.slot, shape.getter, shape.setter, shape.attrs, shape.flags, shape.shortid); - } - fflush(shapefp); -} - -void -TraceRecorder::dumpGuardedShapes(const char* prefix) -{ - for (GuardedShapeTable::Range r = guardedShapeTable.all(); !r.empty(); r.popFront()) - DumpShape(r.front().value, prefix); -} -#endif /* DEBUG_notme && XP_UNIX */ - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guardShape(LIns* obj_ins, JSObject* obj, uint32 shape, const char* guardName, - VMSideExit* exit) -{ - // Test (with add if missing) for a remembered guard for (obj_ins, obj). - GuardedShapeTable::AddPtr p = guardedShapeTable.lookupForAdd(obj_ins); - if (p) { - JS_ASSERT(p->value == obj); - return RECORD_CONTINUE; - } - if (!guardedShapeTable.add(p, obj_ins, obj)) - return RECORD_ERROR; - - if (obj == globalObj) { - // In this case checking object identity is equivalent and faster. - guard(true, - w.name(w.eqp(obj_ins, w.immpObjGC(globalObj)), "guard_global"), - exit); - return RECORD_CONTINUE; - } - -#if defined DEBUG_notme && defined XP_UNIX - DumpShape(obj, "guard"); - fprintf(shapefp, "for obj_ins %p\n", obj_ins); -#endif - - // Finally, emit the shape guard. - guard(true, w.name(w.eqiN(w.ldiObjShape(obj_ins), shape), guardName), exit); - return RECORD_CONTINUE; -} - -void -TraceRecorder::forgetGuardedShapesForObject(JSObject* obj) -{ - for (GuardedShapeTable::Enum e(guardedShapeTable); !e.empty(); e.popFront()) { - if (e.front().value == obj) { -#if defined DEBUG_notme && defined XP_UNIX - DumpShape(entry->obj, "forget"); -#endif - e.removeFront(); - } - } -} - -void -TraceRecorder::forgetGuardedShapes() -{ -#if defined DEBUG_notme && defined XP_UNIX - dumpGuardedShapes("forget-all"); -#endif - guardedShapeTable.clear(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, PCVal& pcval) -{ - jsbytecode* pc = cx->regs().pc; - JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_INITMETHOD && - *pc != JSOP_SETNAME && *pc != JSOP_SETPROP && *pc != JSOP_SETMETHOD); - - // Mimic the interpreter's special case for dense arrays by skipping up one - // hop along the proto chain when accessing a named (not indexed) property, - // typically to find Array.prototype methods. - JSObject* aobj = obj; - if (obj->isDenseArray()) { - guardDenseArray(obj_ins, BRANCH_EXIT); - aobj = obj->getProto(); - obj_ins = w.ldpObjProto(obj_ins); - } - - if (!aobj->isNative()) - RETURN_STOP_A("non-native object"); - - JSAtom* atom; - PropertyCacheEntry* entry; - JS_PROPERTY_CACHE(cx).test(cx, pc, aobj, obj2, entry, atom); - if (atom) { - // Miss: pre-fill the cache for the interpreter, as well as for our needs. - // FIXME: bug 458271. - jsid id = ATOM_TO_JSID(atom); - - // The lookup below may change object shapes. - forgetGuardedShapes(); - - JSProperty* prop; - if (JOF_OPMODE(*pc) == JOF_NAME) { - JS_ASSERT(aobj == obj); - - TraceMonitor &localtm = *traceMonitor; - entry = js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop); - if (!entry) - RETURN_ERROR_A("error in js_FindPropertyHelper"); - - /* js_FindPropertyHelper can reenter the interpreter and kill |this|. */ - if (!localtm.recorder) - return ARECORD_ABORTED; - - if (entry == JS_NO_PROP_CACHE_FILL) - RETURN_STOP_A("cannot cache name"); - } else { - TraceMonitor &localtm = *traceMonitor; - int protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, - cx->resolveFlags, - &obj2, &prop); - - if (protoIndex < 0) - RETURN_ERROR_A("error in js_LookupPropertyWithFlags"); - - /* js_LookupPropertyWithFlags can reenter the interpreter and kill |this|. */ - if (!localtm.recorder) - return ARECORD_ABORTED; - - if (prop) { - if (!obj2->isNative()) - RETURN_STOP_A("property found on non-native object"); - entry = JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, - (Shape*) prop); - JS_ASSERT(entry); - if (entry == JS_NO_PROP_CACHE_FILL) - entry = NULL; - } - - } - - if (!prop) { - // Propagate obj from js_FindPropertyHelper to record_JSOP_BINDNAME - // via our obj2 out-parameter. If we are recording JSOP_SETNAME and - // the global it's assigning does not yet exist, create it. - obj2 = obj; - - // Use a null pcval to return "no such property" to our caller. - pcval.setNull(); - return ARECORD_CONTINUE; - } - - if (!entry) - RETURN_STOP_A("failed to fill property cache"); - } - -#ifdef JS_THREADSAFE - // There's a potential race in any JS_THREADSAFE embedding that's nuts - // enough to share mutable objects on the scope or proto chain, but we - // don't care about such insane embeddings. Anyway, the (scope, proto) - // entry->vcap coordinates must reach obj2 from aobj at this point. - JS_ASSERT(cx->thread()->data.requestDepth); -#endif - - return InjectStatus(guardPropertyCacheHit(obj_ins, aobj, obj2, entry, pcval)); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guardPropertyCacheHit(LIns* obj_ins, - JSObject* aobj, - JSObject* obj2, - PropertyCacheEntry* entry, - PCVal& pcval) -{ - VMSideExit* exit = snapshot(BRANCH_EXIT); - - uint32 vshape = entry->vshape(); - - // Special case for the global object, which may be aliased to get a property value. - // To catch cross-global property accesses we must check against globalObj identity. - // But a JOF_NAME mode opcode needs no guard, as we ensure the global object's shape - // never changes, and name ops can't reach across a global object ('with' aborts). - if (aobj == globalObj) { - if (entry->adding()) - RETURN_STOP("adding a property to the global object"); - - JSOp op = js_GetOpcode(cx, cx->fp()->script(), cx->regs().pc); - if (JOF_OPMODE(op) != JOF_NAME) { - guard(true, - w.name(w.eqp(obj_ins, w.immpObjGC(globalObj)), "guard_global"), - exit); - } - } else { - CHECK_STATUS(guardShape(obj_ins, aobj, entry->kshape, "guard_kshape", exit)); - } - - if (entry->adding()) { - LIns *vshape_ins = - w.ldiRuntimeProtoHazardShape(w.ldpConstContextField(runtime)); - - guard(true, - w.name(w.eqiN(vshape_ins, vshape), "guard_protoHazardShape"), - BRANCH_EXIT); - } - - // For any hit that goes up the scope and/or proto chains, we will need to - // guard on the shape of the object containing the property. - if (entry->vcapTag() >= 1) { - JS_ASSERT(obj2->shape() == vshape); - if (obj2 == globalObj) - RETURN_STOP("hitting the global object via a prototype chain"); - - LIns* obj2_ins; - if (entry->vcapTag() == 1) { - // Duplicate the special case in PropertyCache::test. - obj2_ins = w.ldpObjProto(obj_ins); - guard(false, w.eqp0(obj2_ins), exit); - } else { - obj2_ins = w.immpObjGC(obj2); - } - CHECK_STATUS(guardShape(obj2_ins, obj2, vshape, "guard_vshape", exit)); - } - - pcval = entry->vword; - return RECORD_CONTINUE; -} - -void -TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, const Value &v, LIns* v_ins) -{ - box_value_into(v, v_ins, FSlotsAddress(obj_ins, slot)); -} - -void -TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& slots_ins, - const Value &v, LIns* v_ins) -{ - if (!slots_ins) - slots_ins = w.ldpObjSlots(obj_ins); - box_value_into(v, v_ins, DSlotsAddress(slots_ins, slot)); -} - -void -TraceRecorder::stobj_set_slot(JSObject *obj, LIns* obj_ins, unsigned slot, LIns*& slots_ins, - const Value &v, LIns* v_ins) -{ - /* - * A shape guard must have already been generated for obj, which will - * ensure that future objects have the same number of fixed slots. - */ - if (!obj->hasSlotsArray()) { - JS_ASSERT(slot < obj->numSlots()); - stobj_set_fslot(obj_ins, slot, v, v_ins); - } else { - stobj_set_dslot(obj_ins, slot, slots_ins, v, v_ins); - } -} - -LIns* -TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit *exit) -{ - /* Same guarantee about fixed slots as stobj_set_slot. */ - Address addr = (!obj->hasSlotsArray()) - ? (Address)FSlotsAddress(obj_ins, slot) - : (Address)DSlotsAddress(w.ldpObjSlots(obj_ins), slot); - - return unbox_value(obj->getSlot(slot), addr, exit); -} - -#if JS_BITS_PER_WORD == 32 - -void -TraceRecorder::box_undefined_into(Address addr) -{ - w.stiValueTag(w.nameImmui(JSVAL_TAG_UNDEFINED), addr); - w.stiValuePayload(w.immi(0), addr); -} - -void -TraceRecorder::box_null_into(Address addr) -{ - w.stiValueTag(w.nameImmui(JSVAL_TAG_NULL), addr); - w.stiValuePayload(w.immi(0), addr); -} - -inline LIns* -TraceRecorder::unbox_number_as_double(Address addr, LIns *tag_ins, VMSideExit *exit) -{ - guard(true, w.leui(tag_ins, w.nameImmui(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET)), exit); - LIns *val_ins = w.ldiValuePayload(addr); - LIns* args[] = { val_ins, tag_ins }; - return w.call(&js_UnboxDouble_ci, args); -} - -inline LIns* -TraceRecorder::unbox_non_double_object(Address addr, LIns* tag_ins, - JSValueType type, VMSideExit* exit) -{ - LIns *val_ins; - if (type == JSVAL_TYPE_UNDEFINED) { - val_ins = w.immiUndefined(); - } else if (type == JSVAL_TYPE_NULL) { - val_ins = w.immpNull(); - } else { - JS_ASSERT(type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_OBJECT || - type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_BOOLEAN || - type == JSVAL_TYPE_MAGIC); - val_ins = w.ldiValuePayload(addr); - } - - guard(true, w.eqi(tag_ins, w.nameImmui(JSVAL_TYPE_TO_TAG(type))), exit); - return val_ins; -} - -LIns* -TraceRecorder::unbox_object(Address addr, LIns* tag_ins, JSValueType type, VMSideExit* exit) -{ - JS_ASSERT(type == JSVAL_TYPE_FUNOBJ || type == JSVAL_TYPE_NONFUNOBJ); - guard(true, w.name(w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_OBJECT)), "isObj"), exit); - LIns *payload_ins = w.ldiValuePayload(addr); - if (type == JSVAL_TYPE_FUNOBJ) - guardClass(payload_ins, &js_FunctionClass, exit, LOAD_NORMAL); - else - guardNotClass(payload_ins, &js_FunctionClass, exit, LOAD_NORMAL); - return payload_ins; -} - -LIns* -TraceRecorder::unbox_value(const Value &v, Address addr, VMSideExit *exit, bool force_double) -{ - LIns *tag_ins = w.ldiValueTag(addr); - - if (v.isNumber() && force_double) - return unbox_number_as_double(addr, tag_ins, exit); - - if (v.isInt32()) { - guard(true, w.name(w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_INT32)), "isInt"), exit); - return w.i2d(w.ldiValuePayload(addr)); - } - - if (v.isDouble()) { - guard(true, w.name(w.ltui(tag_ins, w.nameImmui(JSVAL_TAG_CLEAR)), "isDouble"), exit); - return w.ldd(addr); - } - - if (v.isObject()) { - JSValueType type = v.toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ; - return unbox_object(addr, tag_ins, type, exit); - } - - JSValueType type = v.extractNonDoubleObjectTraceType(); - return unbox_non_double_object(addr, tag_ins, type, exit); -} - -void -TraceRecorder::unbox_any_object(Address addr, LIns **obj_ins, LIns **is_obj_ins) -{ - LIns *tag_ins = w.ldiValueTag(addr); - *is_obj_ins = w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_OBJECT)); - *obj_ins = w.ldiValuePayload(addr); -} - -LIns* -TraceRecorder::is_boxed_true(Address addr) -{ - LIns *tag_ins = w.ldiValueTag(addr); - LIns *bool_ins = w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_BOOLEAN)); - LIns *payload_ins = w.ldiValuePayload(addr); - return w.gtiN(w.andi(bool_ins, payload_ins), 0); -} - -LIns* -TraceRecorder::is_boxed_magic(Address addr, JSWhyMagic why) -{ - LIns *tag_ins = w.ldiValueTag(addr); - return w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_MAGIC)); -} - -void -TraceRecorder::box_value_into(const Value &v, LIns *v_ins, Address addr) -{ - if (v.isNumber()) { - JS_ASSERT(v_ins->isD()); - if (fcallinfo(v_ins) == &js_UnboxDouble_ci) { - w.stiValueTag(v_ins->callArgN(0), addr); - w.stiValuePayload(v_ins->callArgN(1), addr); - } else if (IsPromotedInt32(v_ins)) { - LIns *int_ins = w.demoteToInt32(v_ins); - w.stiValueTag(w.nameImmui(JSVAL_TAG_INT32), addr); - w.stiValuePayload(int_ins, addr); - } else { - w.std(v_ins, addr); - } - return; - } - - if (v.isUndefined()) { - box_undefined_into(addr); - } else if (v.isNull()) { - box_null_into(addr); - } else { - JSValueTag tag = v.isObject() ? JSVAL_TAG_OBJECT : v.extractNonDoubleObjectTraceTag(); - w.stiValueTag(w.nameImmui(tag), addr); - w.stiValuePayload(v_ins, addr); - } -} - -LIns* -TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins) -{ - return box_value_into_alloc(v, v_ins); -} - -#elif JS_BITS_PER_WORD == 64 - -void -TraceRecorder::box_undefined_into(Address addr) -{ - w.stq(w.nameImmq(JSVAL_BITS(JSVAL_VOID)), addr); -} - -inline LIns * -TraceRecorder::non_double_object_value_has_type(LIns *v_ins, JSValueType type) -{ - return w.eqi(w.q2i(w.rshuqN(v_ins, JSVAL_TAG_SHIFT)), - w.nameImmui(JSVAL_TYPE_TO_TAG(type))); -} - -inline LIns * -TraceRecorder::unpack_ptr(LIns *v_ins) -{ - return w.andq(v_ins, w.nameImmq(JSVAL_PAYLOAD_MASK)); -} - -inline LIns * -TraceRecorder::unbox_number_as_double(LIns *v_ins, VMSideExit *exit) -{ - guard(true, - w.ltuq(v_ins, w.nameImmq(JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET)), - exit); - LIns* args[] = { v_ins }; - return w.call(&js_UnboxDouble_ci, args); -} - -inline nanojit::LIns* -TraceRecorder::unbox_non_double_object(LIns* v_ins, JSValueType type, VMSideExit* exit) -{ - JS_ASSERT(type <= JSVAL_UPPER_INCL_TYPE_OF_VALUE_SET); - LIns *unboxed_ins; - if (type == JSVAL_TYPE_UNDEFINED) { - unboxed_ins = w.immiUndefined(); - } else if (type == JSVAL_TYPE_NULL) { - unboxed_ins = w.immpNull(); - } else if (type >= JSVAL_LOWER_INCL_TYPE_OF_PTR_PAYLOAD_SET) { - unboxed_ins = unpack_ptr(v_ins); - } else { - JS_ASSERT(type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN || type == JSVAL_TYPE_MAGIC); - unboxed_ins = w.q2i(v_ins); - } - - guard(true, non_double_object_value_has_type(v_ins, type), exit); - return unboxed_ins; -} - -LIns* -TraceRecorder::unbox_object(LIns* v_ins, JSValueType type, VMSideExit* exit) -{ - JS_STATIC_ASSERT(JSVAL_TYPE_OBJECT == JSVAL_UPPER_INCL_TYPE_OF_VALUE_SET); - JS_ASSERT(type == JSVAL_TYPE_FUNOBJ || type == JSVAL_TYPE_NONFUNOBJ); - guard(true, - w.geuq(v_ins, w.nameImmq(JSVAL_SHIFTED_TAG_OBJECT)), - exit); - v_ins = unpack_ptr(v_ins); - if (type == JSVAL_TYPE_FUNOBJ) - guardClass(v_ins, &js_FunctionClass, exit, LOAD_NORMAL); - else - guardNotClass(v_ins, &js_FunctionClass, exit, LOAD_NORMAL); - return v_ins; -} - -LIns* -TraceRecorder::unbox_value(const Value &v, Address addr, VMSideExit *exit, bool force_double) -{ - LIns *v_ins = w.ldq(addr); - - if (v.isNumber() && force_double) - return unbox_number_as_double(v_ins, exit); - - if (v.isInt32()) { - guard(true, non_double_object_value_has_type(v_ins, JSVAL_TYPE_INT32), exit); - return w.i2d(w.q2i(v_ins)); - } - - if (v.isDouble()) { - guard(true, w.leuq(v_ins, w.nameImmq(JSVAL_SHIFTED_TAG_MAX_DOUBLE)), exit); - return w.qasd(v_ins); - } - - if (v.isObject()) { - JSValueType type = v.toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ; - return unbox_object(v_ins, type, exit); - } - - JSValueType type = v.extractNonDoubleObjectTraceType(); - return unbox_non_double_object(v_ins, type, exit); -} - -void -TraceRecorder::unbox_any_object(Address addr, LIns **obj_ins, LIns **is_obj_ins) -{ - JS_STATIC_ASSERT(JSVAL_TYPE_OBJECT == JSVAL_UPPER_INCL_TYPE_OF_VALUE_SET); - LIns *v_ins = w.ldq(addr); - *is_obj_ins = w.geuq(v_ins, w.nameImmq(JSVAL_TYPE_OBJECT)); - *obj_ins = unpack_ptr(v_ins); -} - -LIns* -TraceRecorder::is_boxed_true(Address addr) -{ - LIns *v_ins = w.ldq(addr); - return w.eqq(v_ins, w.immq(JSVAL_BITS(JSVAL_TRUE))); -} - -LIns* -TraceRecorder::is_boxed_magic(Address addr, JSWhyMagic why) -{ - LIns *v_ins = w.ldq(addr); - return w.eqq(v_ins, w.nameImmq(BUILD_JSVAL(JSVAL_TAG_MAGIC, why))); -} - -LIns* -TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins) -{ - if (v.isNumber()) { - JS_ASSERT(v_ins->isD()); - if (fcallinfo(v_ins) == &js_UnboxDouble_ci) - return v_ins->callArgN(0); - if (IsPromotedInt32(v_ins)) { - return w.orq(w.ui2uq(w.demoteToInt32(v_ins)), - w.nameImmq(JSVAL_SHIFTED_TAG_INT32)); - } - return w.dasq(v_ins); - } - - if (v.isNull()) - return w.nameImmq(JSVAL_BITS(JSVAL_NULL)); - if (v.isUndefined()) - return w.nameImmq(JSVAL_BITS(JSVAL_VOID)); - - JSValueTag tag = v.isObject() ? JSVAL_TAG_OBJECT : v.extractNonDoubleObjectTraceTag(); - uint64 shiftedTag = ((uint64)tag) << JSVAL_TAG_SHIFT; - LIns *shiftedTag_ins = w.nameImmq(shiftedTag); - - if (v.hasPtrPayload()) - return w.orq(v_ins, shiftedTag_ins); - return w.orq(w.ui2uq(v_ins), shiftedTag_ins); -} - -void -TraceRecorder::box_value_into(const Value &v, LIns *v_ins, Address addr) -{ - LIns *boxed_ins = box_value_for_native_call(v, v_ins); - w.st(boxed_ins, addr); -} - -#endif /* JS_BITS_PER_WORD */ - -LIns* -TraceRecorder::box_value_into_alloc(const Value &v, LIns *v_ins) -{ - LIns *alloc_ins = w.allocp(sizeof(Value)); - box_value_into(v, v_ins, AllocSlotsAddress(alloc_ins)); - return alloc_ins; -} - -LIns* -TraceRecorder::is_string_id(LIns *id_ins) -{ - return w.eqp0(w.andp(id_ins, w.nameImmw(JSID_TYPE_MASK))); -} - -LIns * -TraceRecorder::unbox_string_id(LIns *id_ins) -{ - JS_STATIC_ASSERT(JSID_TYPE_STRING == 0); - return id_ins; -} - -LIns * -TraceRecorder::unbox_int_id(LIns *id_ins) -{ - return w.rshiN(w.p2i(id_ins), 1); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getThis(LIns*& this_ins) -{ - StackFrame *fp = cx->fp(); - - if (fp->isGlobalFrame()) { - // Top-level code. It is an invariant of the interpreter that fp->thisv - // is non-null. Furthermore, we would not be recording if globalObj - // were not at the end of the scope chain, so `this` can only be one - // object, which we can burn into the trace. - JS_ASSERT(!fp->thisValue().isPrimitive()); - -#ifdef DEBUG - JSObject *obj = globalObj->thisObject(cx); - if (!obj) - RETURN_ERROR("thisObject hook failed"); - JS_ASSERT(&fp->thisValue().toObject() == obj); -#endif - - this_ins = w.immpObjGC(&fp->thisValue().toObject()); - return RECORD_CONTINUE; - } - - JS_ASSERT(fp->callee().getGlobal() == globalObj); - Value& thisv = fp->thisValue(); - - if (thisv.isObject() || fp->fun()->inStrictMode()) { - /* - * fp->thisValue() has already been computed. Since the - * type-specialization of traces distinguishes between computed and - * uncomputed |this|, the same will be true at run time (or we - * won't get this far). - */ - this_ins = get(&fp->thisValue()); - return RECORD_CONTINUE; - } - - /* Don't bother tracing calls on wrapped primitive |this| values. */ - if (!thisv.isNullOrUndefined()) - RETURN_STOP("wrapping primitive |this|"); - - /* - * Compute 'this' now. The result is globalObj->thisObject(), which is - * trace-constant. getThisObject writes back to fp->thisValue(), so do - * the same on trace. - */ - if (!ComputeThis(cx, fp)) - RETURN_ERROR("computeThis failed"); - - /* thisv is a reference, so it'll see the newly computed |this|. */ - this_ins = w.immpObjGC(globalObj); - set(&thisv, this_ins); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK void -TraceRecorder::guardClassHelper(bool cond, LIns* obj_ins, Class* clasp, VMSideExit* exit, - LoadQual loadQual) -{ - LIns* class_ins = w.ldpObjClasp(obj_ins, loadQual); - -#ifdef JS_JIT_SPEW - char namebuf[32]; - JS_snprintf(namebuf, sizeof namebuf, "%s_clasp", clasp->name); - LIns* clasp_ins = w.name(w.immpNonGC(clasp), namebuf); - JS_snprintf(namebuf, sizeof namebuf, "guard(class is %s)", clasp->name); - LIns* cmp_ins = w.name(w.eqp(class_ins, clasp_ins), namebuf); -#else - LIns* clasp_ins = w.immpNonGC(clasp); - LIns* cmp_ins = w.eqp(class_ins, clasp_ins); -#endif - guard(cond, cmp_ins, exit); -} - -JS_REQUIRES_STACK void -TraceRecorder::guardClass(LIns* obj_ins, Class* clasp, VMSideExit* exit, LoadQual loadQual) -{ - guardClassHelper(true, obj_ins, clasp, exit, loadQual); -} - -JS_REQUIRES_STACK void -TraceRecorder::guardNotClass(LIns* obj_ins, Class* clasp, VMSideExit* exit, LoadQual loadQual) -{ - guardClassHelper(false, obj_ins, clasp, exit, loadQual); -} - -JS_REQUIRES_STACK void -TraceRecorder::guardDenseArray(LIns* obj_ins, ExitType exitType) -{ - guardClass(obj_ins, &js_ArrayClass, snapshot(exitType), LOAD_NORMAL); -} - -JS_REQUIRES_STACK void -TraceRecorder::guardDenseArray(LIns* obj_ins, VMSideExit* exit) -{ - guardClass(obj_ins, &js_ArrayClass, exit, LOAD_NORMAL); -} - -JS_REQUIRES_STACK bool -TraceRecorder::guardHasPrototype(JSObject* obj, LIns* obj_ins, - JSObject** pobj, LIns** pobj_ins, - VMSideExit* exit) -{ - *pobj = obj->getProto(); - *pobj_ins = w.ldpObjProto(obj_ins); - - bool cond = *pobj == NULL; - guard(cond, w.name(w.eqp0(*pobj_ins), "guard(proto-not-null)"), exit); - return !cond; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guardPrototypeHasNoIndexedProperties(JSObject* obj, LIns* obj_ins, VMSideExit *exit) -{ - /* - * Guard that no object along the prototype chain has any indexed - * properties which might become visible through holes in the array. - */ - if (js_PrototypeHasIndexedProperties(cx, obj)) - return RECORD_STOP; - - JS_ASSERT(obj->isDenseArray()); - - /* - * Changing __proto__ on a dense array makes it slow, so we can just bake in - * the current prototype as the first prototype to test. This avoids an - * extra load when running the trace. - */ - obj = obj->getProto(); - JS_ASSERT(obj); - - obj_ins = w.immpObjGC(obj); - - /* - * Changing __proto__ on a native object changes its shape, and adding - * indexed properties changes shapes too. And non-native objects never pass - * shape guards. So it's enough to just guard on shapes up the proto chain; - * any change to the proto chain length will make us fail a guard before we - * run off the end of the proto chain. - */ - do { - CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard(shape)", exit)); - obj = obj->getProto(); - obj_ins = w.ldpObjProto(obj_ins); - } while (obj); - - return RECORD_CONTINUE; -} - -/* - * Guard that the object stored in v has the ECMA standard [[DefaultValue]] - * method. Several imacros require this. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guardNativeConversion(Value& v) -{ - JSObject* obj = &v.toObject(); - LIns* obj_ins = get(&v); - - ConvertOp convert = obj->getClass()->convert; - if (convert != Valueify(JS_ConvertStub) && convert != js_TryValueOf) - RETURN_STOP("operand has convert hook"); - - VMSideExit* exit = snapshot(BRANCH_EXIT); - if (obj->isNative()) { - // The common case. Guard on shape rather than class because it'll - // often be free: we're about to do a shape guard anyway to get the - // .valueOf property of this object, and this guard will be cached. - CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), - "guardNativeConversion", exit)); - } else { - // We could specialize to guard on just JSClass.convert, but a mere - // class guard is simpler and slightly faster. - guardClass(obj_ins, obj->getClass(), snapshot(MISMATCH_EXIT), LOAD_NORMAL); - } - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK void -TraceRecorder::clearReturningFrameFromNativeTracker() -{ - /* - * Clear all tracker entries associated with the frame for the same reason - * described in record_EnterFrame. Reuse the generic visitor to avoid - * duplicating logic. The generic visitor stops at 'sp', whereas we need to - * clear up to script->nslots, so finish the job manually. - */ - ClearSlotsVisitor visitor(nativeFrameTracker); - VisitStackSlots(visitor, cx, 0); - Value *vp = cx->regs().sp; - Value *vpend = cx->fp()->slots() + cx->fp()->script()->nslots; - for (; vp < vpend; ++vp) - nativeFrameTracker.set(vp, NULL); -} - -class BoxArg -{ - public: - BoxArg(TraceRecorder *tr, Address addr) - : tr(tr), addr(addr) {} - TraceRecorder *tr; - Address addr; - bool operator()(uintN argi, Value *src) { - tr->box_value_into(*src, tr->get(src), OffsetAddress(addr, argi * sizeof(Value))); - return true; - } -}; - -/* - * If we have created an |arguments| object for the frame, we must copy the - * argument values into the object as properties in case it is used after - * this frame returns. - */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::putActivationObjects() -{ - StackFrame *const fp = cx->fp(); - bool have_args = fp->hasArgsObj() && !fp->argsObj().isStrictArguments(); - bool have_call = fp->isFunctionFrame() && fp->fun()->isHeavyweight(); - - if (!have_args && !have_call) - return ARECORD_CONTINUE; - - if (have_args && !fp->script()->usesArguments) { - /* - * have_args is true, so |arguments| has been accessed, but - * usesArguments is false, so there's no statically visible access. - * It must have been a dodgy access like |f["arguments"]|; just - * abort. (In the case where the record-time property name is not - * "arguments" but a later run-time property name is, we wouldn't have - * emitted the call to js_PutArgumentsOnTrace(), and js_GetArgsValue() - * will deep bail asking for the top StackFrame.) - */ - RETURN_STOP_A("dodgy arguments access"); - } - - uintN nformal = fp->numFormalArgs(); - uintN nactual = fp->numActualArgs(); - uintN nargs = have_args && have_call ? Max(nformal, nactual) - : have_args ? nactual : nformal; - - LIns *args_ins; - if (nargs > 0) { - args_ins = w.allocp(nargs * sizeof(Value)); - /* Don't copy all the actuals if we are only boxing for the callobj. */ - Address addr = AllocSlotsAddress(args_ins); - if (nargs == nactual) - fp->forEachCanonicalActualArg(BoxArg(this, addr)); - else - fp->forEachFormalArg(BoxArg(this, addr)); - } else { - args_ins = w.immpNonGC(0); - } - - if (have_args) { - LIns* argsobj_ins = getFrameObjPtr(fp->addressOfArgs()); - LIns* args[] = { args_ins, argsobj_ins, cx_ins }; - w.call(&js_PutArgumentsOnTrace_ci, args); - } - - if (have_call) { - int nslots = fp->fun()->script()->bindings.countVars(); - LIns* slots_ins; - if (nslots) { - slots_ins = w.allocp(sizeof(Value) * nslots); - for (int i = 0; i < nslots; ++i) { - box_value_into(fp->slots()[i], get(&fp->slots()[i]), - AllocSlotsAddress(slots_ins, i)); - } - } else { - slots_ins = w.immpNonGC(0); - } - - LIns* scopeChain_ins = getFrameObjPtr(fp->addressOfScopeChain()); - LIns* args[] = { slots_ins, w.nameImmi(nslots), args_ins, - w.nameImmi(fp->numFormalArgs()), scopeChain_ins }; - w.call(&js_PutCallObjectOnTrace_ci, args); - } - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_EnterFrame() -{ - StackFrame* const fp = cx->fp(); - - if (++callDepth >= MAX_CALLDEPTH) - RETURN_STOP_A("exceeded maximum call depth"); - - debug_only_stmt(JSAutoByteString funBytes); - debug_only_printf(LC_TMTracer, "EnterFrame %s, callDepth=%d\n", - cx->fp()->fun()->atom ? - js_AtomToPrintableString(cx, cx->fp()->fun()->atom, &funBytes) : - "", - callDepth); - debug_only_stmt( - if (LogController.lcbits & LC_TMRecorder) { - void *mark = JS_ARENA_MARK(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - - js_Disassemble(cx, cx->fp()->script(), JS_TRUE, &sprinter); - - debug_only_printf(LC_TMTracer, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); - debug_only_print0(LC_TMTracer, "----\n"); - } - ) - LIns* void_ins = w.immiUndefined(); - - // Before we enter this frame, we need to clear out any dangling insns left - // in the tracer. While we also clear when returning from a function, it is - // possible to have the following sequence of stack usage: - // - // [fp1]***************** push - // [fp1]***** pop - // [fp1]*****[fp2] call - // [fp1]*****[fp2]*** push - // - // Duplicate native stack layout computation: see VisitFrameSlots header comment. - - // args: carefully copy stack layout - uintN nactual = fp->numActualArgs(); - uintN nformal = fp->numFormalArgs(); - if (nactual < nformal) { - // Fill in missing with void. - JS_ASSERT(fp->actualArgs() == fp->formalArgs()); - Value *beg = fp->formalArgs() + nactual; - Value *end = fp->formalArgsEnd(); - for (Value *vp = beg; vp != end; ++vp) { - nativeFrameTracker.set(vp, NULL); - set(vp, void_ins); - } - } else if (nactual > nformal) { - // Although the VM clones the formal args to the top of the stack, due - // to the fact that we only track the canonical arguments (in argument - // order), the native stack offset of the arguments doesn't change. The - // only thing that changes is which js::Value* in the tracker maps to - // that slot. Thus, all we need to do here is fixup the trackers, not - // emit any actual copying on trace. - JS_ASSERT(fp->actualArgs() != fp->formalArgs()); - JS_ASSERT(fp->hasOverflowArgs()); - Value *srcbeg = fp->actualArgs() - 2; - Value *srcend = fp->actualArgs() + nformal; - Value *dstbeg = fp->formalArgs() - 2; - for (Value *src = srcbeg, *dst = dstbeg; src != srcend; ++src, ++dst) { - nativeFrameTracker.set(dst, NULL); - tracker.set(dst, tracker.get(src)); - nativeFrameTracker.set(src, NULL); - tracker.set(src, NULL); - } - } - - // argsObj: clear and set to null - nativeFrameTracker.set(fp->addressOfArgs(), NULL); - setFrameObjPtr(fp->addressOfArgs(), w.immpNull()); - - // scopeChain: clear, initialize before snapshot, set below - nativeFrameTracker.set(fp->addressOfScopeChain(), NULL); - setFrameObjPtr(fp->addressOfScopeChain(), w.immpNull()); - - // nfixed: clear and set to undefined - Value *vp = fp->slots(); - Value *vpstop = vp + fp->numFixed(); - for (; vp < vpstop; ++vp) { - nativeFrameTracker.set(vp, NULL); - set(vp, void_ins); - } - - // nfixed to nslots: clear - vp = fp->base(); - vpstop = fp->slots() + fp->numSlots(); - for (; vp < vpstop; ++vp) - nativeFrameTracker.set(vp, NULL); - - LIns* callee_ins = get(&cx->fp()->calleev()); - LIns* scopeChain_ins = w.ldpObjParent(callee_ins); - - // set scopeChain for real - if (cx->fp()->fun()->isHeavyweight()) { - if (js_IsNamedLambda(cx->fp()->fun())) - RETURN_STOP_A("can't call named lambda heavyweight on trace"); - - LIns* fun_ins = w.nameImmpNonGC(cx->fp()->fun()); - - LIns* args[] = { scopeChain_ins, callee_ins, fun_ins, cx_ins }; - LIns* call_ins = w.call(&js_CreateCallObjectOnTrace_ci, args); - guard(false, w.eqp0(call_ins), OOM_EXIT); - - setFrameObjPtr(fp->addressOfScopeChain(), call_ins); - } else { - setFrameObjPtr(fp->addressOfScopeChain(), scopeChain_ins); - } - - /* Try inlining one level in case this recursion doesn't go too deep. */ - if (fp->script() == fp->prev()->script() && - fp->prev()->prev() && fp->prev()->prev()->script() == fp->script()) { - RETURN_STOP_A("recursion started inlining"); - } - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_LeaveFrame() -{ - debug_only_stmt(StackFrame *fp = cx->fp();) - - JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, fp->script(), - cx->regs().pc)].length == JSOP_CALL_LENGTH); - - if (callDepth-- <= 0) - RETURN_STOP_A("returned out of a loop we started tracing"); - - // LeaveFrame gets called after the interpreter popped the frame and - // stored rval, so cx->fp() not cx->fp()->prev, and -1 not 0. - updateAtoms(); - set(&stackval(-1), rval_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_PUSH() -{ - stack(0, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_POPV() -{ - Value& rval = stackval(-1); - - // Store it in cx->fp()->rval. NB: Tricky dependencies. cx->fp() is the right - // frame because POPV appears only in global and eval code and we don't - // trace JSOP_EVAL or leaving the frame where tracing started. - LIns *fp_ins = entryFrameIns(); - box_value_into(rval, get(&rval), StackFrameAddress(fp_ins, - StackFrame::offsetOfReturnValue())); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENTERWITH() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LEAVEWITH() -{ - return ARECORD_STOP; -} - -static JSBool JS_FASTCALL -functionProbe(JSContext *cx, JSFunction *fun, int enter) -{ -#ifdef MOZ_TRACE_JSCALLS - JSScript *script = fun ? FUN_SCRIPT(fun) : NULL; - if (enter > 0) - Probes::enterJSFun(cx, fun, script, enter); - else - Probes::exitJSFun(cx, fun, script, enter); -#endif - return true; -} - -JS_DEFINE_CALLINFO_3(static, BOOL, functionProbe, CONTEXT, FUNCTION, INT32, 0, ACCSET_ALL) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RETURN() -{ - /* A return from callDepth 0 terminates the current loop, except for recursion. */ - if (callDepth == 0) { - AUDIT(returnLoopExits); - return endLoop(); - } - - CHECK_STATUS_A(putActivationObjects()); - - if (Probes::callTrackingActive(cx)) { - LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins }; - LIns* call_ins = w.call(&functionProbe_ci, args); - guard(false, w.eqi0(call_ins), MISMATCH_EXIT); - } - - /* If we inlined this function call, make the return value available to the caller code. */ - Value& rval = stackval(-1); - StackFrame *fp = cx->fp(); - if (fp->isConstructing() && rval.isPrimitive()) { - rval_ins = get(&fp->thisValue()); - } else { - rval_ins = get(&rval); - } - debug_only_stmt(JSAutoByteString funBytes); - debug_only_printf(LC_TMTracer, - "returning from %s\n", - fp->fun()->atom ? - js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) : - ""); - clearReturningFrameFromNativeTracker(); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GOTO() -{ - /* - * If we hit a break or a continue to an outer loop, end the loop and - * generate an always-taken loop exit guard. For other downward gotos - * (like if/else) continue recording. - */ - jssrcnote* sn = js_GetSrcNote(cx->fp()->script(), cx->regs().pc); - - if (sn) { - if (SN_TYPE(sn) == SRC_BREAK) { - AUDIT(breakLoopExits); - return endLoop(); - } - - /* - * Tracing labeled break isn't impossible, but does require potentially - * fixing up the block chain. See bug 616119. - */ - if (SN_TYPE(sn) == SRC_BREAK2LABEL || SN_TYPE(sn) == SRC_CONT2LABEL) - RETURN_STOP_A("labeled break"); - } - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IFEQ() -{ - trackCfgMerges(cx->regs().pc); - return ifop(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IFNE() -{ - return ifop(); -} - -LIns* -TraceRecorder::newArguments(LIns* callee_ins) -{ - LIns* global_ins = w.immpObjGC(globalObj); - LIns* argc_ins = w.nameImmi(cx->fp()->numActualArgs()); - - LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins }; - LIns* argsobj_ins = w.call(&js_NewArgumentsOnTrace_ci, args); - guard(false, w.eqp0(argsobj_ins), OOM_EXIT); - - return argsobj_ins; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARGUMENTS() -{ - StackFrame* const fp = cx->fp(); - - /* In an eval, 'arguments' will be a BINDNAME, which we don't trace. */ - JS_ASSERT(!fp->isEvalFrame()); - - if (fp->hasOverriddenArgs()) - RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to"); - if (fp->fun()->inStrictMode()) - RETURN_STOP_A("Can't trace strict-mode arguments"); - - LIns* a_ins = getFrameObjPtr(fp->addressOfArgs()); - LIns* args_ins; - LIns* callee_ins = get(&fp->calleev()); - if (a_ins->isImmP()) { - // |arguments| is set to 0 by EnterFrame on this trace, so call to create it. - args_ins = newArguments(callee_ins); - } else { - // Generate LIR to create arguments only if it has not already been created. - - LIns* mem_ins = w.allocp(sizeof(JSObject *)); - - LIns* isZero_ins = w.eqp0(a_ins); - if (isZero_ins->isImmI(0)) { - w.stAlloc(a_ins, mem_ins); - } else if (isZero_ins->isImmI(1)) { - LIns* call_ins = newArguments(callee_ins); - w.stAlloc(call_ins, mem_ins); - } else { - LIns* br1 = w.jtUnoptimizable(isZero_ins); - w.stAlloc(a_ins, mem_ins); - LIns* br2 = w.j(NULL); - w.label(br1); - - LIns* call_ins = newArguments(callee_ins); - w.stAlloc(call_ins, mem_ins); - w.label(br2); - } - args_ins = w.ldpAlloc(mem_ins); - } - - stack(0, args_ins); - setFrameObjPtr(fp->addressOfArgs(), args_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DUP() -{ - stack(0, get(&stackval(-1))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DUP2() -{ - stack(0, get(&stackval(-2))); - stack(1, get(&stackval(-1))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SWAP() -{ - Value& l = stackval(-2); - Value& r = stackval(-1); - LIns* l_ins = get(&l); - LIns* r_ins = get(&r); - set(&r, l_ins); - set(&l, r_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_PICK() -{ - Value* sp = cx->regs().sp; - jsint n = cx->regs().pc[1]; - JS_ASSERT(sp - (n+1) >= cx->fp()->base()); - LIns* top = get(sp - (n+1)); - for (jsint i = 0; i < n; ++i) - set(sp - (n+1) + i, get(sp - n + i)); - set(&sp[-1], top); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETCONST() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BITOR() -{ - return InjectStatus(binary(LIR_ori)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BITXOR() -{ - return InjectStatus(binary(LIR_xori)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BITAND() -{ - return InjectStatus(binary(LIR_andi)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_EQ() -{ - return equality(false, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NE() -{ - return equality(true, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LT() -{ - return relational(LIR_ltd, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LE() -{ - return relational(LIR_led, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GT() -{ - return relational(LIR_gtd, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GE() -{ - return relational(LIR_ged, true); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LSH() -{ - return InjectStatus(binary(LIR_lshi)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RSH() -{ - return InjectStatus(binary(LIR_rshi)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_URSH() -{ - return InjectStatus(binary(LIR_rshui)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ADD() -{ - Value& r = stackval(-1); - Value& l = stackval(-2); - - if (!l.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(l)); - if (!r.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(r)); - return InjectStatus(callImacro(add_imacros.obj_obj)); - } - return InjectStatus(callImacro(add_imacros.obj_any)); - } - if (!r.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(r)); - return InjectStatus(callImacro(add_imacros.any_obj)); - } - - if (l.isString() || r.isString()) { - LIns* args[] = { stringify(r), stringify(l), cx_ins }; - LIns* concat = w.call(&js_ConcatStrings_ci, args); - guard(false, w.eqp0(concat), OOM_EXIT); - set(&l, concat); - return ARECORD_CONTINUE; - } - - return InjectStatus(binary(LIR_addd)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SUB() -{ - return InjectStatus(binary(LIR_subd)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_MUL() -{ - return InjectStatus(binary(LIR_muld)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DIV() -{ - return InjectStatus(binary(LIR_divd)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_MOD() -{ - return InjectStatus(binary(LIR_modd)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NOT() -{ - Value& v = stackval(-1); - if (v.isBoolean() || v.isUndefined()) { - set(&v, w.eqi0(w.eqiN(get(&v), 1))); - return ARECORD_CONTINUE; - } - if (v.isNumber()) { - LIns* v_ins = get(&v); - set(&v, w.ori(w.eqd0(v_ins), w.eqi0(w.eqd(v_ins, v_ins)))); - return ARECORD_CONTINUE; - } - if (v.isObjectOrNull()) { - set(&v, w.eqp0(get(&v))); - return ARECORD_CONTINUE; - } - JS_ASSERT(v.isString()); - set(&v, w.eqp0(w.getStringLength(get(&v)))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BITNOT() -{ - return InjectStatus(unaryIntOp(LIR_noti)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NEG() -{ - Value& v = stackval(-1); - - if (!v.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(v)); - return InjectStatus(callImacro(unary_imacros.sign)); - } - - if (v.isNumber()) { - LIns* a = get(&v); - - /* - * If we're a promoted integer, we have to watch out for 0s since -0 is - * a double. Only follow this path if we're not an integer that's 0 and - * we're not a double that's zero. - */ - if (oracle && - !oracle->isInstructionUndemotable(cx->regs().pc) && - IsPromotedInt32(a) && - (!v.isInt32() || v.toInt32() != 0) && - (!v.isDouble() || v.toDouble() != 0) && - -v.toNumber() == (int)-v.toNumber()) - { - VMSideExit* exit = snapshot(OVERFLOW_EXIT); - a = w.subxovi(w.immi(0), w.demoteToInt32(a), createGuardRecord(exit)); - if (!a->isImmI() && a->isop(LIR_subxovi)) { - guard(false, w.eqiN(a, 0), exit); // make sure we don't lose a -0 - } - a = w.i2d(a); - } else { - a = w.negd(a); - } - - set(&v, a); - return ARECORD_CONTINUE; - } - - if (v.isNull()) { - set(&v, w.immd(-0.0)); - return ARECORD_CONTINUE; - } - - if (v.isUndefined()) { - set(&v, w.immd(js_NaN)); - return ARECORD_CONTINUE; - } - - if (v.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, get(&v), cx_ins }; - LIns* num_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - set(&v, w.negd(num_ins)); - return ARECORD_CONTINUE; - } - - JS_ASSERT(v.isBoolean()); - set(&v, w.negd(w.i2d(get(&v)))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_POS() -{ - Value& v = stackval(-1); - - if (!v.isPrimitive()) { - CHECK_STATUS_A(guardNativeConversion(v)); - return InjectStatus(callImacro(unary_imacros.sign)); - } - - if (v.isNumber()) - return ARECORD_CONTINUE; - - if (v.isNull()) { - set(&v, w.immd(0)); - return ARECORD_CONTINUE; - } - if (v.isUndefined()) { - set(&v, w.immd(js_NaN)); - return ARECORD_CONTINUE; - } - - if (v.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, get(&v), cx_ins }; - LIns* num_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - set(&v, num_ins); - return ARECORD_CONTINUE; - } - - JS_ASSERT(v.isBoolean()); - set(&v, w.i2d(get(&v))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_PRIMTOP() -{ - // Either this opcode does nothing or we couldn't have traced here, because - // we'd have thrown an exception -- so do nothing if we actually hit this. - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_OBJTOP() -{ - Value& v = stackval(-1); - RETURN_IF_XML_A(v); - return ARECORD_CONTINUE; -} - -RecordingStatus -TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins) -{ - // ctor must be a function created via js_InitClass. -#ifdef DEBUG - Class *clasp = FUN_CLASP(GET_FUNCTION_PRIVATE(cx, ctor)); - JS_ASSERT(clasp); - - TraceMonitor &localtm = *traceMonitor; -#endif - - Value pval; - if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &pval)) - RETURN_ERROR("error getting prototype from constructor"); - - // ctor.prototype is a permanent data property, so this lookup cannot have - // deep-aborted. - JS_ASSERT(localtm.recorder); - -#ifdef DEBUG - JSBool ok, found; - uintN attrs; - ok = JS_GetPropertyAttributes(cx, ctor, js_class_prototype_str, &attrs, &found); - JS_ASSERT(ok); - JS_ASSERT(found); - JS_ASSERT((~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)) == 0); -#endif - - // Since ctor was built by js_InitClass, we can assert (rather than check) - // that pval is usable. - JS_ASSERT(!pval.isPrimitive()); - JSObject *proto = &pval.toObject(); - JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShapes[0]->getClass() == clasp); - - proto_ins = w.immpObjGC(proto); - return RECORD_CONTINUE; -} - -RecordingStatus -TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins) -{ -#ifdef DEBUG - TraceMonitor &localtm = *traceMonitor; -#endif - - JSObject* proto; - if (!js_GetClassPrototype(cx, globalObj, key, &proto)) - RETURN_ERROR("error in js_GetClassPrototype"); - - // This should not have reentered. - JS_ASSERT(localtm.recorder); - -#ifdef DEBUG - /* Double-check that a native proto has a matching emptyShape. */ - if (key != JSProto_Array) { - JS_ASSERT(proto->isNative()); - JS_ASSERT(proto->emptyShapes); - EmptyShape *empty = proto->emptyShapes[0]; - JS_ASSERT(empty); - JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(empty->getClass()) == key); - } -#endif - - proto_ins = w.immpObjGC(proto); - return RECORD_CONTINUE; -} - -#define IGNORE_NATIVE_CALL_COMPLETE_CALLBACK ((JSSpecializedNative*)1) - -RecordingStatus -TraceRecorder::newString(JSObject* ctor, uint32 argc, Value* argv, Value* rval) -{ - JS_ASSERT(argc == 1); - - if (!argv[0].isPrimitive()) { - CHECK_STATUS(guardNativeConversion(argv[0])); - return callImacro(new_imacros.String); - } - - LIns* proto_ins; - CHECK_STATUS(getClassPrototype(ctor, proto_ins)); - - LIns* args[] = { stringify(argv[0]), proto_ins, cx_ins }; - LIns* obj_ins = w.call(&js_String_tn_ci, args); - guard(false, w.eqp0(obj_ins), OOM_EXIT); - - set(rval, obj_ins); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; -} - -RecordingStatus -TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval) -{ - LIns *proto_ins; - CHECK_STATUS(getClassPrototype(ctor, proto_ins)); - - LIns *arr_ins; - if (argc == 0) { - LIns *args[] = { proto_ins, cx_ins }; - arr_ins = w.call(&js::NewDenseEmptyArray_ci, args); - guard(false, w.eqp0(arr_ins), OOM_EXIT); - - } else if (argc == 1 && argv[0].isNumber()) { - /* Abort on RangeError if the double doesn't fit in a uint. */ - LIns *len_ins; - CHECK_STATUS(makeNumberUint32(get(argv), &len_ins)); - LIns *args[] = { proto_ins, len_ins, cx_ins }; - arr_ins = w.call(&js::NewDenseUnallocatedArray_ci, args); - guard(false, w.eqp0(arr_ins), OOM_EXIT); - - } else { - LIns *args[] = { proto_ins, w.nameImmi(argc), cx_ins }; - arr_ins = w.call(&js::NewDenseAllocatedArray_ci, args); - guard(false, w.eqp0(arr_ins), OOM_EXIT); - - // arr->slots[i] = box_jsval(vp[i]); for i in 0..argc - LIns *slots_ins = NULL; - for (uint32 i = 0; i < argc && !outOfMemory(); i++) { - stobj_set_dslot(arr_ins, i, slots_ins, argv[i], get(&argv[i])); - } - } - - set(rval, arr_ins); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK void -TraceRecorder::propagateFailureToBuiltinStatus(LIns* ok_ins, LIns*& status_ins) -{ - /* - * Check the boolean return value (ok_ins) of a native JSNative, - * JSFastNative, or JSPropertyOp hook for failure. On failure, set the - * BUILTIN_ERROR bit of cx->builtinStatus. - * - * If the return value (ok_ins) is true, status' == status. Otherwise - * status' = status | BUILTIN_ERROR. We calculate (rval&1)^1, which is 1 - * if rval is JS_FALSE (error), and then shift that by 1, which is the log2 - * of BUILTIN_ERROR. - */ - JS_STATIC_ASSERT(((JS_TRUE & 1) ^ 1) << 1 == 0); - JS_STATIC_ASSERT(((JS_FALSE & 1) ^ 1) << 1 == BUILTIN_ERROR); - status_ins = w.ori(status_ins, w.lshiN(w.xoriN(w.andiN(ok_ins, 1), 1), 1)); - w.stStateField(status_ins, builtinStatus); -} - -JS_REQUIRES_STACK void -TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, - bool setflag, LIns* addr_boxed_val_ins) -{ - JS_ASSERT(addr_boxed_val_ins->isop(LIR_allocp)); - JS_ASSERT(setflag ? !shape->hasSetterValue() : !shape->hasGetterValue()); - JS_ASSERT(setflag ? !shape->hasDefaultSetter() : !shape->hasDefaultGetterOrIsMethod()); - - enterDeepBailCall(); - - w.stStateField(addr_boxed_val_ins, nativeVp); - w.stStateField(w.immi(1), nativeVpLen); - - CallInfo* ci = new (traceAlloc()) CallInfo(); - /* Setters and getters have their initial arguments in common. */ - LIns* possibleArgs[] = { NULL, NULL, w.immpIdGC(SHAPE_USERID(shape)), obj_ins, cx_ins }; - LIns** args; - if (setflag) { - ci->_address = uintptr_t(shape->setterOp()); - ci->_typesig = CallInfo::typeSig5(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_B, - ARGTYPE_P); - possibleArgs[0] = addr_boxed_val_ins; - possibleArgs[1] = strictModeCode_ins; - args = possibleArgs; - } else { - ci->_address = uintptr_t(shape->getterOp()); - ci->_typesig = CallInfo::typeSig4(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P); - possibleArgs[1] = addr_boxed_val_ins; - args = possibleArgs + 1; - } - ci->_isPure = 0; - ci->_storeAccSet = ACCSET_STORE_ANY; - ci->_abi = ABI_CDECL; -#ifdef DEBUG - ci->_name = "JSPropertyOp"; -#endif - LIns* ok_ins = w.call(ci, args); - - // Cleanup. Immediately clear nativeVp before we might deep bail. - w.stStateField(w.immpNull(), nativeVp); - leaveDeepBailCall(); - - // Guard that the call succeeded and builtinStatus is still 0. - // If the native op succeeds but we deep-bail here, the result value is - // lost! Therefore this can only be used for setters of shared properties. - // In that case we ignore the result value anyway. - LIns* status_ins = w.ldiStateField(builtinStatus); - propagateFailureToBuiltinStatus(ok_ins, status_ins); - guard(true, w.eqi0(status_ins), STATUS_EXIT); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) -{ - if (JSTN_ERRTYPE(sn) == FAIL_STATUS) { - // This needs to capture the pre-call state of the stack. So do not set - // pendingSpecializedNative before taking this snapshot. - JS_ASSERT(!pendingSpecializedNative); - - // Take snapshot for DeepBail and store it in tm->bailExit. - enterDeepBailCall(); - } - - LIns* res_ins = w.call(sn->builtin, args); - - // Immediately unroot the vp as soon we return since we might deep bail next. - if (rooted) - w.stStateField(w.immpNull(), nativeVp); - - rval_ins = res_ins; - switch (JSTN_ERRTYPE(sn)) { - case FAIL_NULL: - guard(false, w.eqp0(res_ins), OOM_EXIT); - break; - case FAIL_NEG: - res_ins = w.i2d(res_ins); - guard(false, w.ltdN(res_ins, 0), OOM_EXIT); - break; - case FAIL_NEITHER: - guard(false, w.eqiN(res_ins, JS_NEITHER), OOM_EXIT); - break; - default:; - } - - set(&stackval(0 - (2 + argc)), res_ins); - - /* - * The return value will be processed by NativeCallComplete since - * we have to know the actual return value type for calls that return - * jsval. - */ - pendingSpecializedNative = sn; - - return RECORD_CONTINUE; -} - -/* - * Check whether we have a specialized implementation for this native - * invocation. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callSpecializedNative(JSNativeTraceInfo *trcinfo, uintN argc, - bool constructing) -{ - StackFrame* const fp = cx->fp(); - jsbytecode *pc = cx->regs().pc; - - Value& fval = stackval(0 - (2 + argc)); - Value& tval = stackval(0 - (1 + argc)); - - LIns* this_ins = get(&tval); - - LIns* args[nanojit::MAXARGS]; - JSSpecializedNative *sn = trcinfo->specializations; - JS_ASSERT(sn); - do { - if (((sn->flags & JSTN_CONSTRUCTOR) != 0) != constructing) - continue; - - uintN knownargc = strlen(sn->argtypes); - if (argc != knownargc) - continue; - - intN prefixc = strlen(sn->prefix); - JS_ASSERT(prefixc <= 3); - LIns** argp = &args[argc + prefixc - 1]; - char argtype; - -#if defined DEBUG - memset(args, 0xCD, sizeof(args)); -#endif - - uintN i; - for (i = prefixc; i--; ) { - argtype = sn->prefix[i]; - if (argtype == 'C') { - *argp = cx_ins; - } else if (argtype == 'T') { /* this, as an object */ - if (tval.isPrimitive()) - goto next_specialization; - *argp = this_ins; - } else if (argtype == 'S') { /* this, as a string */ - if (!tval.isString()) - goto next_specialization; - *argp = this_ins; - } else if (argtype == 'f') { - *argp = w.immpObjGC(&fval.toObject()); - } else if (argtype == 'p') { - CHECK_STATUS(getClassPrototype(&fval.toObject(), *argp)); - } else if (argtype == 'R') { - *argp = w.nameImmpNonGC(cx->runtime); - } else if (argtype == 'P') { - // FIXME: Set pc to imacpc when recording JSOP_CALL inside the - // JSOP_GETELEM imacro (bug 476559). - if ((*pc == JSOP_CALL) && - fp->hasImacropc() && *fp->imacropc() == JSOP_GETELEM) - *argp = w.nameImmpNonGC(fp->imacropc()); - else - *argp = w.nameImmpNonGC(pc); - } else if (argtype == 'D') { /* this, as a number */ - if (!tval.isNumber()) - goto next_specialization; - *argp = this_ins; - } else if (argtype == 'M') { - MathCache *mathCache = GetMathCache(cx); - if (!mathCache) - return RECORD_ERROR; - *argp = w.nameImmpNonGC(mathCache); - } else { - JS_NOT_REACHED("unknown prefix arg type"); - } - argp--; - } - - for (i = knownargc; i--; ) { - Value& arg = stackval(0 - (i + 1)); - *argp = get(&arg); - - argtype = sn->argtypes[i]; - if (argtype == 'd' || argtype == 'i') { - if (!arg.isNumber()) - goto next_specialization; - if (argtype == 'i') - *argp = d2i(*argp); - } else if (argtype == 'o') { - if (arg.isPrimitive()) - goto next_specialization; - } else if (argtype == 's') { - if (!arg.isString()) - goto next_specialization; - } else if (argtype == 'r') { - if (!VALUE_IS_REGEXP(cx, arg)) - goto next_specialization; - } else if (argtype == 'f') { - if (!IsFunctionObject(arg)) - goto next_specialization; - } else if (argtype == 'v') { - *argp = box_value_for_native_call(arg, *argp); - } else { - goto next_specialization; - } - argp--; - } -#if defined DEBUG - JS_ASSERT(args[0] != (LIns *)0xcdcdcdcd); -#endif - return emitNativeCall(sn, argc, args, false); - -next_specialization:; - } while ((sn++)->flags & JSTN_MORE); - - return RECORD_STOP; -} - -static JSBool FASTCALL -ceilReturningInt(jsdouble x, int32 *out) -{ - jsdouble r = js_math_ceil_impl(x); - return JSDOUBLE_IS_INT32(r, out); -} - -static JSBool FASTCALL -floorReturningInt(jsdouble x, int32 *out) -{ - jsdouble r = js_math_floor_impl(x); - return JSDOUBLE_IS_INT32(r, out); -} - -static JSBool FASTCALL -roundReturningInt(jsdouble x, int32 *out) -{ - jsdouble r = js_math_round_impl(x); - return JSDOUBLE_IS_INT32(r, out); -} - -/* - * These functions store into their second argument, so they need to - * be annotated accordingly. To be future-proof, we use ACCSET_STORE_ANY - * so that new callers don't have to remember to update the annotation. - */ -JS_DEFINE_CALLINFO_2(static, BOOL, ceilReturningInt, DOUBLE, INT32PTR, 0, ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_2(static, BOOL, floorReturningInt, DOUBLE, INT32PTR, 0, ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_2(static, BOOL, roundReturningInt, DOUBLE, INT32PTR, 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callFloatReturningInt(uintN argc, const nanojit::CallInfo *ci) -{ - Value& arg = stackval(-1); - LIns* resptr_ins = w.allocp(sizeof(int32)); - LIns* args[] = { resptr_ins, get(&arg) }; - LIns* fits_ins = w.call(ci, args); - - guard(false, w.eqi0(fits_ins), OVERFLOW_EXIT); - - LIns* res_ins = w.ldiAlloc(resptr_ins); - - set(&stackval(0 - (2 + argc)), w.i2d(res_ins)); - - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::callNative(uintN argc, JSOp mode) -{ - LIns* args[5]; - - JS_ASSERT(mode == JSOP_CALL || mode == JSOP_NEW || mode == JSOP_FUNAPPLY || - mode == JSOP_FUNCALL); - - Value* vp = &stackval(0 - (2 + argc)); - JSObject* funobj = &vp[0].toObject(); - JSFunction* fun = funobj->getFunctionPrivate(); - JS_ASSERT(fun->isNative()); - Native native = fun->u.n.native; - - switch (argc) { - case 1: - if (vp[2].isNumber() && mode == JSOP_CALL) { - if (native == js_math_ceil || native == js_math_floor || native == js_math_round) { - LIns* a = get(&vp[2]); - int32 result; - if (IsPromotedInt32OrUint32(a)) { - set(&vp[0], a); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - if (native == js_math_floor) { - if (floorReturningInt(vp[2].toNumber(), &result)) - return callFloatReturningInt(argc, &floorReturningInt_ci); - } else if (native == js_math_ceil) { - if (ceilReturningInt(vp[2].toNumber(), &result)) - return callFloatReturningInt(argc, &ceilReturningInt_ci); - } else if (native == js_math_round) { - if (roundReturningInt(vp[2].toNumber(), &result)) - return callFloatReturningInt(argc, &roundReturningInt_ci); - } - } else if (native == js_math_abs) { - LIns* a = get(&vp[2]); - if (IsPromotedInt32(a) && vp[2].toNumber() != INT_MIN) { - a = w.demoteToInt32(a); - /* abs(INT_MIN) can't be done using integers; exit if we see it. */ - LIns* intMin_ins = w.name(w.immi(0x80000000), "INT_MIN"); - LIns* isIntMin_ins = w.name(w.eqi(a, intMin_ins), "isIntMin"); - guard(false, isIntMin_ins, MISMATCH_EXIT); - LIns* neg_ins = w.negi(a); - LIns* isNeg_ins = w.name(w.ltiN(a, 0), "isNeg"); - LIns* abs_ins = w.name(w.cmovi(isNeg_ins, neg_ins, a), "abs"); - set(&vp[0], w.i2d(abs_ins)); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - } - if (vp[1].isString()) { - JSString *str = vp[1].toString(); - if (native == js_str_charAt) { - jsdouble i = vp[2].toNumber(); - if (JSDOUBLE_IS_NaN(i)) - i = 0; - if (i < 0 || i >= str->length()) - RETURN_STOP("charAt out of bounds"); - LIns* str_ins = get(&vp[1]); - LIns* idx_ins = get(&vp[2]); - LIns* char_ins; - CHECK_STATUS(getCharAt(str, str_ins, idx_ins, mode, &char_ins)); - set(&vp[0], char_ins); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } else if (native == js_str_charCodeAt) { - jsdouble i = vp[2].toNumber(); - if (JSDOUBLE_IS_NaN(i)) - i = 0; - if (i < 0 || i >= str->length()) - RETURN_STOP("charCodeAt out of bounds"); - LIns* str_ins = get(&vp[1]); - LIns* idx_ins = get(&vp[2]); - LIns* charCode_ins; - CHECK_STATUS(getCharCodeAt(str, str_ins, idx_ins, &charCode_ins)); - set(&vp[0], charCode_ins); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - } - } else if (vp[2].isString() && mode == JSOP_CALL) { - if (native == js_regexp_exec) { - /* - * If the result of the call will be unused or only tested against - * nullness, we replace the call to RegExp.exec() on the - * stack with a call to RegExp.test() because "r.exec(s) != - * null" is equivalent to "r.test(s)". This avoids building - * the result array, which can be expensive. This requires - * that RegExp.prototype.test() hasn't been changed; we check this. - */ - if (!CallResultEscapes(cx->regs().pc)) { - JSObject* proto; - jsid id = ATOM_TO_JSID(cx->runtime->atomState.testAtom); - /* Get RegExp.prototype.test() and check it hasn't been changed. */ - if (js_GetClassPrototype(cx, NULL, JSProto_RegExp, &proto)) { - if (JSObject *tmp = HasNativeMethod(proto, id, js_regexp_test)) { - vp[0] = ObjectValue(*tmp); - funobj = tmp; - fun = tmp->getFunctionPrivate(); - native = js_regexp_test; - } - } - } - } - } - break; - - case 2: - if (vp[2].isNumber() && vp[3].isNumber() && mode == JSOP_CALL && - (native == js_math_min || native == js_math_max)) { - LIns* a = get(&vp[2]); - LIns* b = get(&vp[3]); - if (IsPromotedInt32(a) && IsPromotedInt32(b)) { - a = w.demoteToInt32(a); - b = w.demoteToInt32(b); - LIns* cmp = (native == js_math_min) ? w.lti(a, b) : w.gti(a, b); - set(&vp[0], w.i2d(w.cmovi(cmp, a, b))); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - if (IsPromotedUint32(a) && IsPromotedUint32(b)) { - a = w.demoteToUint32(a); - b = w.demoteToUint32(b); - LIns* cmp = (native == js_math_min) ? w.ltui(a, b) : w.gtui(a, b); - set(&vp[0], w.ui2d(w.cmovi(cmp, a, b))); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - } - break; - } - - if (fun->flags & JSFUN_TRCINFO) { - JSNativeTraceInfo *trcinfo = FUN_TRCINFO(fun); - JS_ASSERT(trcinfo && fun->u.n.native == trcinfo->native); - - /* Try to call a type specialized version of the native. */ - if (trcinfo->specializations) { - RecordingStatus status = callSpecializedNative(trcinfo, argc, mode == JSOP_NEW); - if (status != RECORD_STOP) - return status; - } - } - - if (native == js_fun_apply || native == js_fun_call) - RETURN_STOP("trying to call native apply or call"); - - // Allocate the vp vector and emit code to root it. - uintN vplen = 2 + argc; - LIns* invokevp_ins = w.allocp(vplen * sizeof(Value)); - - // vp[0] is the callee. - box_value_into(vp[0], w.immpObjGC(funobj), AllocSlotsAddress(invokevp_ins)); - - // Calculate |this|. - LIns* this_ins; - if (mode == JSOP_NEW) { - Class* clasp = fun->u.n.clasp; - JS_ASSERT(clasp != &js_SlowArrayClass); - if (!clasp) - clasp = &js_ObjectClass; - JS_ASSERT(((jsuword) clasp & 3) == 0); - - // Abort on |new Function|. (FIXME: This restriction might not - // unnecessary now that the constructor creates the new function object - // itself.) - if (clasp == &js_FunctionClass) - RETURN_STOP("new Function"); - - if (!clasp->isNative()) - RETURN_STOP("new with non-native ops"); - - // Don't trace |new Math.sin(0)|. - if (!fun->isConstructor()) - RETURN_STOP("new with non-constructor native function"); - - vp[1].setMagicWithObjectOrNullPayload(NULL); - newobj_ins = w.immpMagicNull(); - - /* Treat this as a regular call, the constructor will behave correctly. */ - mode = JSOP_CALL; - this_ins = newobj_ins; - } else { - this_ins = get(&vp[1]); - } - set(&vp[1], this_ins); - box_value_into(vp[1], this_ins, AllocSlotsAddress(invokevp_ins, 1)); - - // Populate argv. - for (uintN n = 2; n < 2 + argc; n++) { - box_value_into(vp[n], get(&vp[n]), AllocSlotsAddress(invokevp_ins, n)); - // For a very long argument list we might run out of LIR space, so - // check inside the loop. - if (outOfMemory()) - RETURN_STOP("out of memory in argument list"); - } - - // Populate extra slots, including the return value slot for a slow native. - if (2 + argc < vplen) { - for (uintN n = 2 + argc; n < vplen; n++) { - box_undefined_into(AllocSlotsAddress(invokevp_ins, n)); - if (outOfMemory()) - RETURN_STOP("out of memory in extra slots"); - } - } - - // Set up arguments for the JSNative or JSFastNative. - if (mode == JSOP_NEW) - RETURN_STOP("untraceable fast native constructor"); - native_rval_ins = invokevp_ins; - args[0] = invokevp_ins; - args[1] = w.immi(argc); - args[2] = cx_ins; - uint32 typesig = CallInfo::typeSig3(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P); - - // Generate CallInfo and a JSSpecializedNative structure on the fly. - // Do not use JSTN_UNBOX_AFTER for mode JSOP_NEW because - // record_NativeCallComplete unboxes the result specially. - - CallInfo* ci = new (traceAlloc()) CallInfo(); - ci->_address = uintptr_t(fun->u.n.native); - ci->_isPure = 0; - ci->_storeAccSet = ACCSET_STORE_ANY; - ci->_abi = ABI_CDECL; - ci->_typesig = typesig; -#ifdef DEBUG - ci->_name = js_anonymous_str; - if (fun->atom) { - JSAutoByteString bytes(cx, fun->atom); - if (!!bytes) { - size_t n = strlen(bytes.ptr()) + 1; - char *buffer = new (traceAlloc()) char[n]; - memcpy(buffer, bytes.ptr(), n); - ci->_name = buffer; - } - } - #endif - - // Generate a JSSpecializedNative structure on the fly. - generatedSpecializedNative.builtin = ci; - generatedSpecializedNative.flags = FAIL_STATUS | ((mode == JSOP_NEW) - ? JSTN_CONSTRUCTOR - : JSTN_UNBOX_AFTER); - generatedSpecializedNative.prefix = NULL; - generatedSpecializedNative.argtypes = NULL; - - // We only have to ensure that the values we wrote into the stack buffer - // are rooted if we actually make it to the call, so only set nativeVp and - // nativeVpLen immediately before emitting the call code. This way we avoid - // leaving trace with a bogus nativeVp because we fall off trace while unboxing - // values into the stack buffer. - w.stStateField(w.nameImmi(vplen), nativeVpLen); - w.stStateField(invokevp_ins, nativeVp); - - // argc is the original argc here. It is used to calculate where to place - // the return value. - return emitNativeCall(&generatedSpecializedNative, argc, args, true); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::functionCall(uintN argc, JSOp mode) -{ - Value& fval = stackval(0 - (2 + argc)); - JS_ASSERT(&fval >= cx->fp()->base()); - - if (!IsFunctionObject(fval)) - RETURN_STOP("callee is not a function"); - - Value& tval = stackval(0 - (1 + argc)); - - /* - * If callee is not constant, it's a shapeless call and we have to guard - * explicitly that we will get this callee again at runtime. - */ - if (!get(&fval)->isImmP()) - CHECK_STATUS(guardCallee(fval)); - - /* - * Require that the callee be a function object, to avoid guarding on its - * class here. We know if the callee and this were pushed by JSOP_CALLNAME - * or JSOP_CALLPROP that callee is a *particular* function, since these hit - * the property cache and guard on the object (this) in which the callee - * was found. So it's sufficient to test here that the particular function - * is interpreted, not guard on that condition. - * - * Bytecode sequences that push shapeless callees must guard on the callee - * class being Function and the function being interpreted. - */ - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &fval.toObject()); - - if (Probes::callTrackingActive(cx)) { - JSScript *script = FUN_SCRIPT(fun); - if (!script || !script->isEmpty()) { - LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins }; - LIns* call_ins = w.call(&functionProbe_ci, args); - guard(false, w.eqi0(call_ins), MISMATCH_EXIT); - } - } - - if (FUN_INTERPRETED(fun)) - return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); - - Native native = fun->maybeNative(); - Value* argv = &tval + 1; - if (native == js_Array) - return newArray(&fval.toObject(), argc, argv, &fval); - if (native == js_String && argc == 1) { - if (mode == JSOP_NEW) - return newString(&fval.toObject(), 1, argv, &fval); - if (!argv[0].isPrimitive()) { - CHECK_STATUS(guardNativeConversion(argv[0])); - return callImacro(call_imacros.String); - } - set(&fval, stringify(argv[0])); - pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; - return RECORD_CONTINUE; - } - - RecordingStatus rs = callNative(argc, mode); - if (Probes::callTrackingActive(cx)) { - LIns* args[] = { w.immi(0), w.nameImmpNonGC(fun), cx_ins }; - LIns* call_ins = w.call(&functionProbe_ci, args); - guard(false, w.eqi0(call_ins), MISMATCH_EXIT); - } - return rs; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NEW() -{ - uintN argc = GET_ARGC(cx->regs().pc); - cx->assertValidStackDepth(argc + 2); - return InjectStatus(functionCall(argc, JSOP_NEW)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DELNAME() -{ - return ARECORD_STOP; -} - -static JSBool JS_FASTCALL -DeleteIntKey(JSContext* cx, JSObject* obj, int32 i, JSBool strict) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - LeaveTraceIfArgumentsObject(cx, obj); - Value v = BooleanValue(false); - jsid id; - if (INT_FITS_IN_JSID(i)) { - id = INT_TO_JSID(i); - } else { - if (!js_ValueToStringId(cx, Int32Value(i), &id)) { - SetBuiltinError(tm); - return false; - } - } - - if (!obj->deleteProperty(cx, id, &v, strict)) - SetBuiltinError(tm); - return v.toBoolean(); -} -JS_DEFINE_CALLINFO_4(extern, BOOL_FAIL, DeleteIntKey, CONTEXT, OBJECT, INT32, BOOL, - 0, ACCSET_STORE_ANY) - -static JSBool JS_FASTCALL -DeleteStrKey(JSContext* cx, JSObject* obj, JSString* str, JSBool strict) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - LeaveTraceIfArgumentsObject(cx, obj); - Value v = BooleanValue(false); - jsid id; - - /* - * NB: JSOP_DELPROP does not need js_ValueToStringId to atomize, but (see - * jsatominlines.h) that helper early-returns if the computed property name - * string is already atomized, and we are *not* on a perf-critical path! - */ - if (!js_ValueToStringId(cx, StringValue(str), &id) || !obj->deleteProperty(cx, id, &v, strict)) - SetBuiltinError(tm); - return v.toBoolean(); -} -JS_DEFINE_CALLINFO_4(extern, BOOL_FAIL, DeleteStrKey, CONTEXT, OBJECT, STRING, BOOL, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DELPROP() -{ - Value& lval = stackval(-1); - if (lval.isPrimitive()) - RETURN_STOP_A("JSOP_DELPROP on primitive base expression"); - if (&lval.toObject() == globalObj) - RETURN_STOP_A("JSOP_DELPROP on global property"); - - JSAtom* atom = atoms[GET_INDEX(cx->regs().pc)]; - - enterDeepBailCall(); - LIns* args[] = { strictModeCode_ins, w.immpAtomGC(atom), get(&lval), cx_ins }; - LIns* rval_ins = w.call(&DeleteStrKey_ci, args); - - LIns* status_ins = w.ldiStateField(builtinStatus); - pendingGuardCondition = w.eqi0(status_ins); - leaveDeepBailCall(); - - set(&lval, rval_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DELELEM() -{ - Value& lval = stackval(-2); - if (lval.isPrimitive()) - RETURN_STOP_A("JSOP_DELELEM on primitive base expression"); - if (&lval.toObject() == globalObj) - RETURN_STOP_A("JSOP_DELELEM on global property"); - if (lval.toObject().isArguments()) - RETURN_STOP_A("JSOP_DELELEM on the |arguments| object"); - - Value& idx = stackval(-1); - LIns* rval_ins; - - enterDeepBailCall(); - if (hasInt32Repr(idx)) { - LIns* num_ins; - CHECK_STATUS_A(makeNumberInt32(get(&idx), &num_ins)); - LIns* args[] = { strictModeCode_ins, num_ins, get(&lval), cx_ins }; - rval_ins = w.call(&DeleteIntKey_ci, args); - } else if (idx.isString()) { - LIns* args[] = { strictModeCode_ins, get(&idx), get(&lval), cx_ins }; - rval_ins = w.call(&DeleteStrKey_ci, args); - } else { - RETURN_STOP_A("JSOP_DELELEM on non-int, non-string index"); - } - - LIns* status_ins = w.ldiStateField(builtinStatus); - pendingGuardCondition = w.eqi0(status_ins); - leaveDeepBailCall(); - - set(&lval, rval_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TYPEOF() -{ - Value& r = stackval(-1); - LIns* type; - if (r.isString()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_STRING]); - } else if (r.isNumber()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - } else if (r.isUndefined()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); - } else if (r.isBoolean()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_BOOLEAN]); - } else if (r.isNull()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_OBJECT]); - } else { - if (r.toObject().isFunction()) { - type = w.immpAtomGC(cx->runtime->atomState.typeAtoms[JSTYPE_FUNCTION]); - } else { - LIns* args[] = { get(&r), cx_ins }; - type = w.call(&js_TypeOfObject_ci, args); - } - } - set(&r, type); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_VOID() -{ - stack(-1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCNAME() -{ - return incName(1); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCPROP() -{ - return incProp(1); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCELEM() -{ - return InjectStatus(incElem(1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECNAME() -{ - return incName(-1); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECPROP() -{ - return incProp(-1); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECELEM() -{ - return InjectStatus(incElem(-1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::incName(jsint incr, bool pre) -{ - Value* vp; - LIns* v_ins; - LIns* v_ins_after; - NameResult nr; - - CHECK_STATUS_A(name(vp, v_ins, nr)); - Value v = nr.tracked ? *vp : nr.v; - Value v_after; - CHECK_STATUS_A(incHelper(v, v_ins, v_after, v_ins_after, incr)); - LIns* v_ins_result = pre ? v_ins_after : v_ins; - if (nr.tracked) { - set(vp, v_ins_after); - stack(0, v_ins_result); - return ARECORD_CONTINUE; - } - - if (!nr.obj->isCall()) - RETURN_STOP_A("incName on unsupported object class"); - - CHECK_STATUS_A(setCallProp(nr.obj, nr.obj_ins, nr.shape, v_ins_after, v_after)); - stack(0, v_ins_result); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NAMEINC() -{ - return incName(1, false); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_PROPINC() -{ - return incProp(1, false); -} - -// XXX consolidate with record_JSOP_GETELEM code... -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ELEMINC() -{ - return InjectStatus(incElem(1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NAMEDEC() -{ - return incName(-1, false); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_PROPDEC() -{ - return incProp(-1, false); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ELEMDEC() -{ - return InjectStatus(incElem(-1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETPROP() -{ - return getProp(stackval(-1)); -} - -/* - * If possible, lookup obj[id] without calling any resolve hooks or touching - * any non-native objects, store the results in *pobjp and *shapep (NULL if no - * such property exists), and return true. - * - * If a safe lookup is not possible, return false; *pobjp and *shapep are - * undefined. - */ -static bool -SafeLookup(JSContext *cx, JSObject* obj, jsid id, JSObject** pobjp, const Shape** shapep) -{ - do { - // Avoid non-native lookupProperty hooks. - if (obj->getOps()->lookupProperty) - return false; - - if (const Shape *shape = obj->nativeLookup(id)) { - *pobjp = obj; - *shapep = shape; - return true; - } - - // Avoid resolve hooks. - if (obj->getClass()->resolve != JS_ResolveStub) - return false; - } while ((obj = obj->getProto()) != NULL); - *pobjp = NULL; - *shapep = NULL; - return true; -} - -/* - * Lookup the property for the SETPROP/SETNAME/SETMETHOD instruction at pc. - * Emit guards to ensure that the result at run time is the same. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::lookupForSetPropertyOp(JSObject* obj, LIns* obj_ins, jsid id, - bool* safep, JSObject** pobjp, const Shape** shapep) -{ - // We could consult the property cache here, but the contract for - // PropertyCache::testForSet is intricate enough that it's a lot less code - // to do a SafeLookup. - *safep = SafeLookup(cx, obj, id, pobjp, shapep); - if (!*safep) - return RECORD_CONTINUE; - - VMSideExit *exit = snapshot(BRANCH_EXIT); - if (*shapep) { - CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_kshape", exit)); - if (obj != *pobjp && *pobjp != globalObj) { - CHECK_STATUS(guardShape(w.immpObjGC(*pobjp), *pobjp, (*pobjp)->shape(), - "guard_vshape", exit)); - } - } else { - for (;;) { - if (obj != globalObj) - CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_proto_chain", exit)); - obj = obj->getProto(); - if (!obj) - break; - obj_ins = w.immpObjGC(obj); - } - } - return RECORD_CONTINUE; -} - -static JSBool FASTCALL -MethodWriteBarrier(JSContext* cx, JSObject* obj, uint32 slot, const Value* v) -{ -#ifdef DEBUG - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); -#endif - - bool ok = obj->methodWriteBarrier(cx, slot, *v); - JS_ASSERT(WasBuiltinSuccessful(tm)); - return ok; -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, UINT32, CVALUEPTR, - 0, ACCSET_STORE_ANY) - -/* Emit a specialized, inlined copy of js_NativeSet. */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, const Shape* shape, - const Value &v, LIns* v_ins) -{ - uint32 slot = shape->slot; - JS_ASSERT((slot != SHAPE_INVALID_SLOT) == shape->hasSlot()); - JS_ASSERT_IF(shape->hasSlot(), obj->nativeContains(*shape)); - - /* - * We do not trace assignment to properties that have both a non-default - * setter and a slot, for several reasons. - * - * First, that would require sampling rt->propertyRemovals before and after - * (see js_NativeSet), and even more code to handle the case where the two - * samples differ. A mere guard is not enough, because you can't just bail - * off trace in the middle of a property assignment without storing the - * value and making the stack right. - * - * If obj is the global object, there are two additional problems. We would - * have to emit still more code to store the result in the object (not the - * native global frame) if the setter returned successfully after - * deep-bailing. And we would have to cope if the run-time type of the - * setter's return value differed from the record-time type of v, in which - * case unboxing would fail and, having called a native setter, we could - * not just retry the instruction in the interpreter. - * - * If obj is branded, we would have a similar problem recovering from a - * failed call to MethodWriteBarrier. - */ - if (!shape->hasDefaultSetter() && slot != SHAPE_INVALID_SLOT) - RETURN_STOP("can't trace set of property with setter and slot"); - - // These two cases are strict-mode errors and can't be traced. - if (shape->hasGetterValue() && shape->hasDefaultSetter()) - RETURN_STOP("can't set a property that has only a getter"); - if (shape->isDataDescriptor() && !shape->writable()) - RETURN_STOP("can't assign to readonly property"); - - // Call the setter, if any. - if (!shape->hasDefaultSetter()) { - if (shape->hasSetterValue()) - RETURN_STOP("can't trace JavaScript function setter yet"); - emitNativePropertyOp(shape, obj_ins, true, box_value_into_alloc(v, v_ins)); - } - - if (slot != SHAPE_INVALID_SLOT) { - if (obj->brandedOrHasMethodBarrier()) { - if (obj == globalObj) { - // Because the trace is type-specialized to the global object's - // slots, no run-time check is needed. Avoid recording a global - // shape change, though. - JS_ASSERT(obj->nativeContains(*shape)); - if (IsFunctionObject(obj->getSlot(slot))) - RETURN_STOP("can't trace set of function-valued global property"); - } else { - // Setting a function-valued property might need to rebrand the - // object. Call the method write barrier. Note that even if the - // property is not function-valued now, it might be on trace. - enterDeepBailCall(); - LIns* args[] = {box_value_into_alloc(v, v_ins), w.immi(slot), obj_ins, cx_ins}; - LIns* ok_ins = w.call(&MethodWriteBarrier_ci, args); - guard(false, w.eqi0(ok_ins), OOM_EXIT); - leaveDeepBailCall(); - } - } - - // Store the value. - if (obj == globalObj) { - if (!lazilyImportGlobalSlot(slot)) - RETURN_STOP("lazy import of global slot failed"); - set(&obj->getSlotRef(slot), v_ins); - } else { - LIns* slots_ins = NULL; - stobj_set_slot(obj, obj_ins, slot, slots_ins, v, v_ins); - } - } - - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::addDataProperty(JSObject* obj) -{ - if (!obj->isExtensible()) - RETURN_STOP("assignment adds property to non-extensible object"); - - // If obj is the global, the global shape is about to change. Note also - // that since we do not record this case, SETNAME and SETPROP are identical - // as far as the tracer is concerned. (js_CheckUndeclaredVarAssignment - // distinguishes the two, in the interpreter.) - if (obj == globalObj) - RETURN_STOP("set new property of global object"); // global shape change - - // js_AddProperty does not call the addProperty hook. - Class* clasp = obj->getClass(); - if (clasp->addProperty != Valueify(JS_PropertyStub)) - RETURN_STOP("set new property of object with addProperty hook"); - - // See comment in TR::nativeSet about why we do not support setting a - // property that has both a setter and a slot. - if (clasp->setProperty != Valueify(JS_StrictPropertyStub)) - RETURN_STOP("set new property with setter and slot"); - -#ifdef DEBUG - addPropShapeBefore = obj->lastProperty(); -#endif - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_AddProperty(JSObject *obj) -{ - Value& objv = stackval(-2); - JS_ASSERT(&objv.toObject() == obj); - LIns* obj_ins = get(&objv); - Value& v = stackval(-1); - LIns* v_ins = get(&v); - const Shape* shape = obj->lastProperty(); - - if (!shape->hasDefaultSetter()) { - JS_ASSERT(IsWatchedProperty(cx, shape)); - RETURN_STOP_A("assignment adds property with watchpoint"); - } - -#ifdef DEBUG - JS_ASSERT(addPropShapeBefore); - if (obj->inDictionaryMode()) - JS_ASSERT(shape->previous()->matches(addPropShapeBefore)); - else - JS_ASSERT(shape->previous() == addPropShapeBefore); - JS_ASSERT(shape->isDataDescriptor()); - JS_ASSERT(shape->hasDefaultSetter()); - addPropShapeBefore = NULL; -#endif - - if (obj->inDictionaryMode()) - RETURN_STOP_A("assignment adds property to dictionary"); // FIXME: bug 625900 - - // On trace, call js_Add{,Atom}Property to do the dirty work. - LIns* args[] = { w.immpShapeGC(shape), obj_ins, cx_ins }; - jsbytecode op = *cx->regs().pc; - bool isDefinitelyAtom = (op == JSOP_SETPROP); - const CallInfo *ci = isDefinitelyAtom ? &js_AddAtomProperty_ci : &js_AddProperty_ci; - LIns* ok_ins = w.call(ci, args); - guard(false, w.eqi0(ok_ins), OOM_EXIT); - - // Box the value and store it in the new slot. - CHECK_STATUS_A(InjectStatus(nativeSet(obj, obj_ins, shape, v, v_ins))); - - // Finish off a SET instruction by moving sp[-1] to sp[-2]. - if (op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETMETHOD) - set(&objv, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::setUpwardTrackedVar(Value* stackVp, const Value &v, LIns* v_ins) -{ - JSValueType stackT = determineSlotType(stackVp); - JSValueType otherT = getCoercedType(v); - - bool promote = true; - - if (stackT != otherT) { - if (stackT == JSVAL_TYPE_DOUBLE && otherT == JSVAL_TYPE_INT32 && IsPromotedInt32(v_ins)) - promote = false; - else - RETURN_STOP("can't trace this upvar mutation"); - } - - set(stackVp, v_ins, promote); - - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *shape, - LIns *v_ins, const Value &v) -{ - // Set variables in on-trace-stack call objects by updating the tracker. - StackFrame *fp = frameIfInRange(callobj); - if (fp) { - if (shape->setterOp() == SetCallArg) { - JS_ASSERT(shape->hasShortID()); - uintN slot = uint16(shape->shortid); - Value *vp2 = &fp->formalArg(slot); - CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins)); - return RECORD_CONTINUE; - } - if (shape->setterOp() == SetCallVar) { - JS_ASSERT(shape->hasShortID()); - uintN slot = uint16(shape->shortid); - Value *vp2 = &fp->slots()[slot]; - CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins)); - return RECORD_CONTINUE; - } - RETURN_STOP("can't trace special CallClass setter"); - } - - if (!callobj->getPrivate()) { - // Because the parent guard in guardCallee ensures this Call object - // will be the same object now and on trace, and because once a Call - // object loses its frame it never regains one, on trace we will also - // have a null private in the Call object. So all we need to do is - // write the value to the Call object's slot. - intN slot = uint16(shape->shortid); - if (shape->setterOp() == SetCallArg) { - JS_ASSERT(slot < ArgClosureTraits::slot_count(callobj)); - slot += ArgClosureTraits::slot_offset(callobj); - } else if (shape->setterOp() == SetCallVar) { - JS_ASSERT(slot < VarClosureTraits::slot_count(callobj)); - slot += VarClosureTraits::slot_offset(callobj); - } else { - RETURN_STOP("can't trace special CallClass setter"); - } - - // Now assert that the shortid get we did above was ok. Have to do it - // after the RETURN_STOP above, since in that case we may in fact not - // have a valid shortid; but we don't use it in that case anyway. - JS_ASSERT(shape->hasShortID()); - - LIns* slots_ins = NULL; - stobj_set_dslot(callobj_ins, slot, slots_ins, v, v_ins); - return RECORD_CONTINUE; - } - - // This is the hard case: we have a StackFrame private, but it's not in - // range. During trace execution we may or may not have a StackFrame - // anymore. Call the standard builtins, which handle that situation. - - // Set variables in off-trace-stack call objects by calling standard builtins. - const CallInfo* ci = NULL; - if (shape->setterOp() == SetCallArg) - ci = &js_SetCallArg_ci; - else if (shape->setterOp() == SetCallVar) - ci = &js_SetCallVar_ci; - else - RETURN_STOP("can't trace special CallClass setter"); - - // Even though the frame is out of range, later we might be called as an - // inner trace such that the target variable is defined in the outer trace - // entry frame. For simplicity, we just fall off trace. - guard(false, - w.eqp(entryFrameIns(), w.ldpObjPrivate(callobj_ins)), - MISMATCH_EXIT); - - LIns* args[] = { - box_value_for_native_call(v, v_ins), - w.nameImmw(JSID_BITS(SHAPE_USERID(shape))), - callobj_ins, - cx_ins - }; - LIns* call_ins = w.call(ci, args); - guard(false, w.name(w.eqi0(call_ins), "guard(set upvar)"), STATUS_EXIT); - - return RECORD_CONTINUE; -} - -/* - * Emit a specialized, inlined copy of js_SetPropertyHelper for the current - * instruction. On success, *deferredp is true if a call to record_AddProperty - * is expected. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::setProperty(JSObject* obj, LIns* obj_ins, const Value &v, LIns* v_ins, - bool* deferredp) -{ - *deferredp = false; - - JSAtom *atom = atoms[GET_INDEX(cx->regs().pc)]; - jsid id = ATOM_TO_JSID(atom); - - if (obj->getOps()->setProperty) - RETURN_STOP("non-native object"); // FIXME: bug 625900 - - bool safe; - JSObject* pobj; - const Shape* shape; - CHECK_STATUS(lookupForSetPropertyOp(obj, obj_ins, id, &safe, &pobj, &shape)); - if (!safe) - RETURN_STOP("setprop: lookup fail"); // FIXME: bug 625900 - - // Handle Call objects specially. The Call objects we create on trace are - // not fully populated until we leave trace. Calling the setter on such an - // object wouldn't work. - if (obj->isCall()) - return setCallProp(obj, obj_ins, shape, v_ins, v); - - // Handle setting a property that is not found on obj or anywhere on its - // the prototype chain. - if (!shape) { - *deferredp = true; - return addDataProperty(obj); - } - - // Check whether we can assign to/over the existing property. - if (shape->isAccessorDescriptor()) { - if (shape->hasDefaultSetter()) - RETURN_STOP("setting accessor property with no setter"); - } else if (!shape->writable()) { - RETURN_STOP("setting readonly data property"); - } - - // Handle setting an existing own property. - if (pobj == obj) { - if (*cx->regs().pc == JSOP_SETMETHOD) { - if (shape->isMethod() && shape->methodObject() == v.toObject()) - return RECORD_CONTINUE; - RETURN_STOP("setmethod: property exists"); - } - return nativeSet(obj, obj_ins, shape, v, v_ins); - } - - // If shape is an inherited non-SHARED property, we will add a new, - // shadowing data property. - if (shape->hasSlot()) { - // Avoid being tripped up by legacy special case for shortids, where - // the new shadowing data property inherits the setter. - if (shape->hasShortID() && !shape->hasDefaultSetter()) - RETURN_STOP("shadowing assignment with shortid"); - *deferredp = true; - return addDataProperty(obj); - } - - // Handle setting an inherited SHARED property. - // If it has the default setter, the assignment is a no-op. - if (shape->hasDefaultSetter() && !shape->hasGetterValue()) - return RECORD_CONTINUE; - return nativeSet(obj, obj_ins, shape, v, v_ins); -} - -/* Record a JSOP_SET{PROP,NAME,METHOD} instruction. */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::recordSetPropertyOp() -{ - Value& l = stackval(-2); - if (!l.isObject()) - RETURN_STOP("set property of primitive"); - JSObject* obj = &l.toObject(); - LIns* obj_ins = get(&l); - - Value& r = stackval(-1); - LIns* r_ins = get(&r); - - bool deferred; - CHECK_STATUS(setProperty(obj, obj_ins, r, r_ins, &deferred)); - - // Finish off a SET instruction by moving sp[-1] to sp[-2]. But if - // record_AddProperty is going be called, we're not done with sp[-2] yet, - // so delay this move until the end of record_AddProperty. - if (!deferred) - set(&l, r_ins); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETPROP() -{ - return InjectStatus(recordSetPropertyOp()); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETMETHOD() -{ - return InjectStatus(recordSetPropertyOp()); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETNAME() -{ - return InjectStatus(recordSetPropertyOp()); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::recordInitPropertyOp(jsbytecode op) -{ - Value& l = stackval(-2); - JSObject* obj = &l.toObject(); - LIns* obj_ins = get(&l); - JS_ASSERT(obj->getClass() == &js_ObjectClass); - - Value& v = stackval(-1); - LIns* v_ins = get(&v); - - JSAtom* atom = atoms[GET_INDEX(cx->regs().pc)]; - jsid id = js_CheckForStringIndex(ATOM_TO_JSID(atom)); - - // If obj already has this property (because JSOP_NEWOBJECT already set its - // shape or because the id appears more than once in the initializer), just - // set it. The existing property can't be an accessor property: we wouldn't - // get here, as JSOP_SETTER can't be recorded. - if (const Shape* shape = obj->nativeLookup(id)) { - // Don't assign a bare (non-cloned) function to an ordinary or method - // property. The opposite case, assigning some other value to a method, - // is OK. nativeSet emits code that trips the write barrier. - if (op == JSOP_INITMETHOD) - RETURN_STOP("initmethod: property exists"); - JS_ASSERT(shape->isDataDescriptor()); - JS_ASSERT(shape->hasSlot()); - JS_ASSERT(shape->hasDefaultSetter()); - return nativeSet(obj, obj_ins, shape, v, v_ins); - } - - // Duplicate the interpreter's special treatment of __proto__. Unlike the - // SET opcodes, JSOP_INIT{PROP,METHOD} do not write to the stack. - if (atom == cx->runtime->atomState.protoAtom) { - bool deferred; - return setProperty(obj, obj_ins, v, v_ins, &deferred); - } - - // Define a new property. - return addDataProperty(obj); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INITPROP() -{ - return InjectStatus(recordInitPropertyOp(JSOP_INITPROP)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INITMETHOD() -{ - return InjectStatus(recordInitPropertyOp(JSOP_INITMETHOD)); -} - -JS_REQUIRES_STACK VMSideExit* -TraceRecorder::enterDeepBailCall() -{ - // Take snapshot for DeepBail and store it in tm->bailExit. - VMSideExit* exit = snapshot(DEEP_BAIL_EXIT); - w.stTraceMonitorField(w.nameImmpNonGC(exit), bailExit); - - // Tell nanojit not to discard or defer stack writes before this call. - w.xbarrier(createGuardRecord(exit)); - - // Forget about guarded shapes, since deep bailers can reshape the world. - forgetGuardedShapes(); - return exit; -} - -JS_REQUIRES_STACK void -TraceRecorder::leaveDeepBailCall() -{ - // Keep tm->bailExit null when it's invalid. - w.stTraceMonitorField(w.immpNull(), bailExit); -} - -JS_REQUIRES_STACK void -TraceRecorder::finishGetProp(LIns* obj_ins, LIns* vp_ins, LIns* ok_ins, Value* outp) -{ - // Store the boxed result (and this-object, if JOF_CALLOP) before the - // guard. The deep-bail case requires this. If the property get fails, - // these slots will be ignored anyway. - // N.B. monitorRecording expects get(outp)->isLoad() - JS_ASSERT(vp_ins->isop(LIR_allocp)); - LIns* result_ins = w.lddAlloc(vp_ins); - set(outp, result_ins); - if (js_CodeSpec[*cx->regs().pc].format & JOF_CALLOP) - set(outp + 1, obj_ins); - - // We need to guard on ok_ins, but this requires a snapshot of the state - // after this op. monitorRecording will do it for us. - pendingGuardCondition = ok_ins; - - // Note there is a boxed result sitting on the stack. The caller must leave - // it there for the time being, since the return type is not yet - // known. monitorRecording will emit the code to unbox it. - pendingUnboxSlot = outp; -} - -static inline bool -RootedStringToId(JSContext* cx, JSString** namep, jsid* idp) -{ - JSString* name = *namep; - if (name->isAtom()) { - *idp = INTERNED_STRING_TO_JSID(name); - return true; - } - - JSAtom* atom = js_AtomizeString(cx, name, 0); - if (!atom) - return false; - *namep = atom; /* write back to GC root */ - *idp = ATOM_TO_JSID(atom); - return true; -} - -static const size_t PIC_TABLE_ENTRY_COUNT = 32; - -struct PICTableEntry -{ - jsid id; - uint32 shape; - uint32 slot; -}; - -struct PICTable -{ - PICTable() : entryCount(0) {} - - PICTableEntry entries[PIC_TABLE_ENTRY_COUNT]; - uint32 entryCount; - - bool scan(uint32 shape, jsid id, uint32 *slotOut) { - for (size_t i = 0; i < entryCount; ++i) { - PICTableEntry &entry = entries[i]; - if (entry.shape == shape && entry.id == id) { - *slotOut = entry.slot; - return true; - } - } - return false; - } - - void update(uint32 shape, jsid id, uint32 slot) { - if (entryCount >= PIC_TABLE_ENTRY_COUNT) - return; - PICTableEntry &newEntry = entries[entryCount++]; - newEntry.shape = shape; - newEntry.id = id; - newEntry.slot = slot; - } -}; - -static JSBool FASTCALL -GetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, Value* vp, PICTable *picTable) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - jsid id; - if (!RootedStringToId(cx, namep, &id)) { - SetBuiltinError(tm); - return false; - } - - /* Delegate to the op, if present. */ - PropertyIdOp op = obj->getOps()->getProperty; - if (op) { - bool result = op(cx, obj, obj, id, vp); - if (!result) - SetBuiltinError(tm); - return WasBuiltinSuccessful(tm); - } - - /* Try to hit in the cache. */ - uint32 slot; - if (picTable->scan(obj->shape(), id, &slot)) { - *vp = obj->getSlot(slot); - return WasBuiltinSuccessful(tm); - } - - const Shape *shape; - JSObject *holder; - if (!js_GetPropertyHelperWithShape(cx, obj, obj, id, JSGET_METHOD_BARRIER, vp, &shape, - &holder)) { - SetBuiltinError(tm); - return false; - } - - /* Only update the table when the object is the holder of the property. */ - if (obj == holder && shape->hasSlot() && shape->hasDefaultGetter()) { - /* - * Note: we insert the non-normalized id into the table so you don't need to - * normalize it before hitting in the table (faster lookup). - */ - picTable->update(obj->shape(), id, shape->slot); - } - - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_5(static, BOOL_FAIL, GetPropertyByName, CONTEXT, OBJECT, STRINGPTR, VALUEPTR, - PICTABLE, - 0, ACCSET_STORE_ANY) - -// Convert the value in a slot to a string and store the resulting string back -// in the slot (typically in order to root it). -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::primitiveToStringInPlace(Value* vp) -{ - Value v = *vp; - JS_ASSERT(v.isPrimitive()); - - if (!v.isString()) { - // v is not a string. Turn it into one. js_ValueToString is safe - // because v is not an object. -#ifdef DEBUG - TraceMonitor *localtm = traceMonitor; -#endif - JSString *str = js_ValueToString(cx, v); - JS_ASSERT(localtm->recorder == this); - if (!str) - RETURN_ERROR("failed to stringify element id"); - v.setString(str); - set(vp, stringify(*vp)); - - // Write the string back to the stack to save the interpreter some work - // and to ensure snapshots get the correct type for this slot. - *vp = v; - } - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getPropertyByName(LIns* obj_ins, Value* idvalp, Value* outp) -{ - CHECK_STATUS(primitiveToStringInPlace(idvalp)); - enterDeepBailCall(); - - // Call GetPropertyByName. The vp parameter points to stack because this is - // what the interpreter currently does. obj and id are rooted on the - // interpreter stack, but the slot at vp is not a root. - LIns* vp_ins = w.name(w.allocp(sizeof(Value)), "vp"); - LIns* idvalp_ins = w.name(addr(idvalp), "idvalp"); - PICTable *picTable = new (traceAlloc()) PICTable(); - LIns* pic_ins = w.nameImmpNonGC(picTable); - LIns* args[] = {pic_ins, vp_ins, idvalp_ins, obj_ins, cx_ins}; - LIns* ok_ins = w.call(&GetPropertyByName_ci, args); - - // GetPropertyByName can assign to *idvalp, so the tracker has an incorrect - // entry for that address. Correct it. (If the value in the address is - // never used again, the usual case, Nanojit will kill this load.) - // The Address could be made more precise with some effort (idvalp_ins may - // be a stack location), but it's not worth it because this case is rare. - tracker.set(idvalp, w.ldp(AnyAddress(idvalp_ins))); - - finishGetProp(obj_ins, vp_ins, ok_ins, outp); - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -static JSBool FASTCALL -GetPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, Value* vp) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - AutoIdRooter idr(cx); - if (!js_Int32ToId(cx, index, idr.addr()) || !obj->getProperty(cx, idr.id(), vp)) { - SetBuiltinError(tm); - return JS_FALSE; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyByIndex, CONTEXT, OBJECT, INT32, VALUEPTR, 0, - ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getPropertyByIndex(LIns* obj_ins, LIns* index_ins, Value* outp) -{ - CHECK_STATUS(makeNumberInt32(index_ins, &index_ins)); - - // See note in getPropertyByName about vp. - enterDeepBailCall(); - LIns* vp_ins = w.name(w.allocp(sizeof(Value)), "vp"); - LIns* args[] = {vp_ins, index_ins, obj_ins, cx_ins}; - LIns* ok_ins = w.call(&GetPropertyByIndex_ci, args); - finishGetProp(obj_ins, vp_ins, ok_ins, outp); - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -static JSBool FASTCALL -GetPropertyById(JSContext* cx, JSObject* obj, jsid id, Value* vp) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - if (!obj->getProperty(cx, id, vp)) { - SetBuiltinError(tm); - return JS_FALSE; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyById, CONTEXT, OBJECT, JSID, VALUEPTR, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getPropertyById(LIns* obj_ins, Value* outp) -{ - // Find the atom. - JSAtom* atom; - jsbytecode* pc = cx->regs().pc; - const JSCodeSpec& cs = js_CodeSpec[*pc]; - if (*pc == JSOP_LENGTH) { - atom = cx->runtime->atomState.lengthAtom; - } else if (JOF_TYPE(cs.format) == JOF_ATOM) { - atom = atoms[GET_INDEX(pc)]; - } else { - JS_ASSERT(JOF_TYPE(cs.format) == JOF_SLOTATOM); - atom = atoms[GET_INDEX(pc + SLOTNO_LEN)]; - } - - JS_STATIC_ASSERT(sizeof(jsid) == sizeof(void *)); - jsid id = ATOM_TO_JSID(atom); - - // Call GetPropertyById. See note in getPropertyByName about vp. - enterDeepBailCall(); - LIns* vp_ins = w.name(w.allocp(sizeof(Value)), "vp"); - LIns* args[] = {vp_ins, w.nameImmw(JSID_BITS(id)), obj_ins, cx_ins}; - LIns* ok_ins = w.call(&GetPropertyById_ci, args); - finishGetProp(obj_ins, vp_ins, ok_ins, outp); - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -/* Manually inlined, specialized copy of js_NativeGet. */ -static JSBool FASTCALL -GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, Shape* shape, Value* vp) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - -#ifdef DEBUG - JSProperty* prop; - JSObject* pobj; - JS_ASSERT(obj->lookupProperty(cx, shape->id, &pobj, &prop)); - JS_ASSERT(prop == (JSProperty*) shape); -#endif - - // Shape::get contains a special case for With objects. We can elide it - // here because With objects are, we claim, never on the operand stack - // while recording. - JS_ASSERT(obj->getClass() != &js_WithClass); - - vp->setUndefined(); - if (!shape->getterOp()(cx, obj, SHAPE_USERID(shape), vp)) { - SetBuiltinError(tm); - return JS_FALSE; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyWithNativeGetter, - CONTEXT, OBJECT, SHAPE, VALUEPTR, 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, const Shape* shape, Value* outp) -{ - JS_ASSERT(!shape->hasGetterValue()); - JS_ASSERT(shape->slot == SHAPE_INVALID_SLOT); - JS_ASSERT(!shape->hasDefaultGetterOrIsMethod()); - - // Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp. - // FIXME - We should call the getter directly. Using a builtin function for - // now because it buys some extra asserts. See bug 508310. - enterDeepBailCall(); - LIns* vp_ins = w.name(w.allocp(sizeof(Value)), "vp"); - LIns* args[] = {vp_ins, w.nameImmpNonGC(shape), obj_ins, cx_ins}; - LIns* ok_ins = w.call(&GetPropertyWithNativeGetter_ci, args); - finishGetProp(obj_ins, vp_ins, ok_ins, outp); - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getPropertyWithScriptGetter(JSObject *obj, LIns* obj_ins, const Shape* shape) -{ - if (!canCallImacro()) - RETURN_STOP("cannot trace script getter, already in imacro"); - - // Rearrange the stack in preparation for the imacro, taking care to adjust - // the interpreter state and the tracker in the same way. This adjustment - // is noted in imacros.jsasm with .fixup tags. - Value getter = shape->getterValue(); - Value*& sp = cx->regs().sp; - switch (*cx->regs().pc) { - case JSOP_GETPROP: - sp++; - sp[-1] = sp[-2]; - set(&sp[-1], get(&sp[-2])); - sp[-2] = getter; - set(&sp[-2], w.immpObjGC(&getter.toObject())); - return callImacroInfallibly(getprop_imacros.scriptgetter); - - case JSOP_CALLPROP: - sp += 2; - sp[-2] = getter; - set(&sp[-2], w.immpObjGC(&getter.toObject())); - sp[-1] = sp[-3]; - set(&sp[-1], get(&sp[-3])); - return callImacroInfallibly(callprop_imacros.scriptgetter); - - case JSOP_GETTHISPROP: - case JSOP_GETARGPROP: - case JSOP_GETLOCALPROP: - sp += 2; - sp[-2] = getter; - set(&sp[-2], w.immpObjGC(&getter.toObject())); - sp[-1] = ObjectValue(*obj); - set(&sp[-1], obj_ins); - return callImacroInfallibly(getthisprop_imacros.scriptgetter); - - default: - RETURN_STOP("cannot trace script getter for this opcode"); - } -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** out) -{ - CHECK_STATUS(makeNumberInt32(idx_ins, &idx_ins)); - idx_ins = w.ui2p(idx_ins); - LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins); - if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) - { - LIns *args[] = { str_ins, cx_ins }; - LIns *ok_ins = w.call(&js_FlattenOnTrace_ci, args); - guard(false, w.eqi0(ok_ins), OOM_EXIT); - w.label(mbr); - } - - guard(true, - w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::LENGTH_SHIFT)), - snapshot(MISMATCH_EXIT)); - *out = w.i2d(w.getStringChar(str_ins, idx_ins)); - return RECORD_CONTINUE; -} - -JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32); - - -JS_REQUIRES_STACK LIns* -TraceRecorder::getUnitString(LIns* str_ins, LIns* idx_ins) -{ - LIns *ch_ins = w.getStringChar(str_ins, idx_ins); - guard(true, w.ltuiN(ch_ins, JSAtom::UNIT_STATIC_LIMIT), MISMATCH_EXIT); - JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32); - return w.addp(w.nameImmpNonGC(JSAtom::unitStaticTable), - w.lshpN(w.ui2p(ch_ins), (sizeof(JSString) == 16) ? 4 : 5)); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns** out) -{ - CHECK_STATUS(makeNumberInt32(idx_ins, &idx_ins)); - idx_ins = w.ui2p(idx_ins); - LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins); - if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, - w.nameImmw(JSString::ROPE_BIT))))) - { - LIns *args[] = { str_ins, cx_ins }; - LIns *ok_ins = w.call(&js_FlattenOnTrace_ci, args); - guard(false, w.eqi0(ok_ins), OOM_EXIT); - w.label(mbr); - } - - LIns* inRange = w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::LENGTH_SHIFT)); - - if (mode == JSOP_GETELEM) { - guard(true, inRange, MISMATCH_EXIT); - - *out = getUnitString(str_ins, idx_ins); - } else { - LIns *phi_ins = w.allocp(sizeof(JSString *)); - w.stAlloc(w.nameImmpNonGC(cx->runtime->emptyString), phi_ins); - - if (MaybeBranch mbr = w.jf(inRange)) { - LIns *unitstr_ins = getUnitString(str_ins, idx_ins); - w.stAlloc(unitstr_ins, phi_ins); - w.label(mbr); - } - *out = w.ldpAlloc(phi_ins); - } - return RECORD_CONTINUE; -} - -// Typed array tracing depends on EXPANDED_LOADSTORE and F2I -#if NJ_EXPANDED_LOADSTORE_SUPPORTED && NJ_F2I_SUPPORTED -static bool OkToTraceTypedArrays = true; -#else -static bool OkToTraceTypedArrays = false; -#endif - -JS_REQUIRES_STACK void -TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins) -{ - // vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx] - LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); - LIns* slotOffset_ins = w.addp(w.nameImmw(offsetof(ArgumentsData, slots)), - w.ui2p(w.muliN(idx_ins, sizeof(Value)))); - LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins); - - guard(false, - w.name(is_boxed_magic(ArgsSlotOffsetAddress(vp_ins), JS_ARGS_HOLE), - "guard(not deleted arg)"), - MISMATCH_EXIT); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETELEM() -{ - bool call = *cx->regs().pc == JSOP_CALLELEM; - - Value& idx = stackval(-1); - Value& lval = stackval(-2); - - LIns* obj_ins = get(&lval); - LIns* idx_ins = get(&idx); - - // Special case for array-like access of strings. - if (lval.isString() && hasInt32Repr(idx)) { - if (call) - RETURN_STOP_A("JSOP_CALLELEM on a string"); - int i = asInt32(idx); - if (size_t(i) >= lval.toString()->length()) - RETURN_STOP_A("Invalid string index in JSOP_GETELEM"); - LIns* char_ins; - CHECK_STATUS_A(getCharAt(lval.toString(), obj_ins, idx_ins, JSOP_GETELEM, &char_ins)); - set(&lval, char_ins); - return ARECORD_CONTINUE; - } - - if (lval.isPrimitive()) - RETURN_STOP_A("JSOP_GETLEM on a primitive"); - RETURN_IF_XML_A(lval); - - JSObject* obj = &lval.toObject(); - if (obj == globalObj) - RETURN_STOP_A("JSOP_GETELEM on global"); - LIns* v_ins; - - /* Property access using a string name or something we have to stringify. */ - if (!idx.isInt32()) { - if (!idx.isPrimitive()) - RETURN_STOP_A("object used as index"); - - return InjectStatus(getPropertyByName(obj_ins, &idx, &lval)); - } - - if (obj->isArguments()) { - // Don't even try to record if out of range or reading a deleted arg - int32 int_idx = idx.toInt32(); - if (int_idx < 0 || int_idx >= (int32)obj->getArgsInitialLength()) - RETURN_STOP_A("cannot trace arguments with out of range index"); - if (obj->getArgsElement(int_idx).isMagic(JS_ARGS_HOLE)) - RETURN_STOP_A("reading deleted args element"); - - // Only trace reading arguments out of active, tracked frame - unsigned depth; - StackFrame *afp = guardArguments(obj, obj_ins, &depth); - if (afp) { - Value* vp = &afp->canonicalActualArg(int_idx); - if (idx_ins->isImmD()) { - JS_ASSERT(int_idx == (int32)idx_ins->immD()); - guardNotHole(obj_ins, w.nameImmi(int_idx)); - v_ins = get(vp); - } else { - // If the index is not a constant expression, we generate LIR to load the value from - // the native stack area. The guard on js_ArgumentClass above ensures the up-to-date - // value has been written back to the native stack area. - CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); - - /* - * For small nactual, - * 0 <= int_idx < nactual iff unsigned(int_idx) < unsigned(nactual). - */ - guard(true, - w.name(w.ltui(idx_ins, w.nameImmui(afp->numActualArgs())), - "guard(upvar index in range)"), - MISMATCH_EXIT); - - guardNotHole(obj_ins, idx_ins); - - JSValueType type = getCoercedType(*vp); - - // Guard that the argument has the same type on trace as during recording. - LIns* typemap_ins; - if (depth == 0) { - // In this case, we are in the same frame where the arguments object was created. - // The entry type map is not necessarily up-to-date, so we capture a new type map - // for this point in the code. - unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */); - JSValueType* typemap = new (traceAlloc()) JSValueType[stackSlots]; - DetermineTypesVisitor detVisitor(*this, typemap); - VisitStackSlots(detVisitor, cx, 0); - typemap_ins = w.nameImmpNonGC(typemap + 2 /* callee, this */); - } else { - // In this case, we are in a deeper frame from where the arguments object was - // created. The type map at the point of the call out from the creation frame - // is accurate. - // Note: this relies on the assumption that we abort on setting an element of - // an arguments object in any deeper frame. - LIns* fip_ins = w.ldpRstack(lirbuf->rp, (callDepth-depth)*sizeof(FrameInfo*)); - typemap_ins = w.addp(fip_ins, w.nameImmw(sizeof(FrameInfo) + 2/*callee,this*/ * sizeof(JSValueType))); - } - - LIns* type_ins = w.lduc2uiConstTypeMapEntry(typemap_ins, idx_ins); - guard(true, - w.name(w.eqi(type_ins, w.immi(type)), "guard(type-stable upvar)"), - BRANCH_EXIT); - - // Read the value out of the native stack area. - size_t stackOffset = nativespOffset(&afp->canonicalActualArg(0)); - LIns* args_addr_ins = w.addp(lirbuf->sp, w.nameImmw(stackOffset)); - LIns* argi_addr_ins = w.addp(args_addr_ins, - w.ui2p(w.muli(idx_ins, w.nameImmi(sizeof(double))))); - - // The Address could be more precise, but ValidateWriter - // doesn't recognise the complex expression involving 'sp' as - // an stack access, and it's not worth the effort to be - // more precise because this case is rare. - v_ins = stackLoad(AnyAddress(argi_addr_ins), type); - } - JS_ASSERT(v_ins); - set(&lval, v_ins); - if (call) - set(&idx, obj_ins); - return ARECORD_CONTINUE; - } - RETURN_STOP_A("can't reach arguments object's frame"); - } - - if (obj->isDenseArray()) { - // Fast path for dense arrays accessed with a integer index. - Value* vp; - LIns* addr_ins; - - VMSideExit* branchExit = snapshot(BRANCH_EXIT); - guardDenseArray(obj_ins, branchExit); - CHECK_STATUS_A(denseArrayElement(lval, idx, vp, v_ins, addr_ins, branchExit)); - set(&lval, v_ins); - if (call) - set(&idx, obj_ins); - return ARECORD_CONTINUE; - } - - if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { - // Fast path for typed arrays accessed with a integer index. - Value* vp; - guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_CONST); - CHECK_STATUS_A(typedArrayElement(lval, idx, vp, v_ins)); - set(&lval, v_ins); - if (call) - set(&idx, obj_ins); - return ARECORD_CONTINUE; - } - - return InjectStatus(getPropertyByIndex(obj_ins, idx_ins, &lval)); -} - -/* Functions used by JSOP_SETELEM */ - -static JSBool FASTCALL -SetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, Value* vp, JSBool strict) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - jsid id; - if (!RootedStringToId(cx, namep, &id) || !obj->setProperty(cx, id, vp, strict)) { - SetBuiltinError(tm); - return false; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_5(static, BOOL_FAIL, SetPropertyByName, - CONTEXT, OBJECT, STRINGPTR, VALUEPTR, BOOL, - 0, ACCSET_STORE_ANY) - -static JSBool FASTCALL -InitPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, ValueArgType arg) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - jsid id; - if (!RootedStringToId(cx, namep, &id) || - !obj->defineProperty(cx, id, ValueArgToConstRef(arg), NULL, NULL, JSPROP_ENUMERATE)) { - SetBuiltinError(tm); - return JS_FALSE; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, InitPropertyByName, CONTEXT, OBJECT, STRINGPTR, VALUE, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::initOrSetPropertyByName(LIns* obj_ins, Value* idvalp, Value* rvalp, bool init) -{ - CHECK_STATUS(primitiveToStringInPlace(idvalp)); - - if (init) { - LIns* v_ins = box_value_for_native_call(*rvalp, get(rvalp)); - enterDeepBailCall(); - LIns* idvalp_ins = w.name(addr(idvalp), "idvalp"); - LIns* args[] = {v_ins, idvalp_ins, obj_ins, cx_ins}; - pendingGuardCondition = w.call(&InitPropertyByName_ci, args); - } else { - // See note in getPropertyByName about vp. - LIns* vp_ins = box_value_into_alloc(*rvalp, get(rvalp)); - enterDeepBailCall(); - LIns* idvalp_ins = w.name(addr(idvalp), "idvalp"); - LIns* args[] = { strictModeCode_ins, vp_ins, idvalp_ins, obj_ins, cx_ins }; - pendingGuardCondition = w.call(&SetPropertyByName_ci, args); - } - - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -static JSBool FASTCALL -SetPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, Value* vp, JSBool strict) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - AutoIdRooter idr(cx); - if (!js_Int32ToId(cx, index, idr.addr()) || !obj->setProperty(cx, idr.id(), vp, strict)) { - SetBuiltinError(tm); - return false; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_5(static, BOOL_FAIL, SetPropertyByIndex, CONTEXT, OBJECT, INT32, VALUEPTR, BOOL, - 0, ACCSET_STORE_ANY) - -static JSBool FASTCALL -InitPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, ValueArgType arg) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - LeaveTraceIfGlobalObject(cx, obj); - - AutoIdRooter idr(cx); - if (!js_Int32ToId(cx, index, idr.addr()) || - !obj->defineProperty(cx, idr.id(), ValueArgToConstRef(arg), NULL, NULL, JSPROP_ENUMERATE)) { - SetBuiltinError(tm); - return JS_FALSE; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, InitPropertyByIndex, CONTEXT, OBJECT, INT32, VALUE, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::initOrSetPropertyByIndex(LIns* obj_ins, LIns* index_ins, Value* rvalp, bool init) -{ - CHECK_STATUS(makeNumberInt32(index_ins, &index_ins)); - - if (init) { - LIns* rval_ins = box_value_for_native_call(*rvalp, get(rvalp)); - enterDeepBailCall(); - LIns* args[] = {rval_ins, index_ins, obj_ins, cx_ins}; - pendingGuardCondition = w.call(&InitPropertyByIndex_ci, args); - } else { - // See note in getPropertyByName about vp. - LIns* vp_ins = box_value_into_alloc(*rvalp, get(rvalp)); - enterDeepBailCall(); - LIns* args[] = {strictModeCode_ins, vp_ins, index_ins, obj_ins, cx_ins}; - pendingGuardCondition = w.call(&SetPropertyByIndex_ci, args); - } - - leaveDeepBailCall(); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) -{ - Value& v = stackval(v_spindex); - Value& idx = stackval(idx_spindex); - Value& lval = stackval(lval_spindex); - - if (lval.isPrimitive()) - RETURN_STOP_A("left JSOP_SETELEM operand is not an object"); - RETURN_IF_XML_A(lval); - - JSObject* obj = &lval.toObject(); - LIns* obj_ins = get(&lval); - LIns* idx_ins = get(&idx); - LIns* v_ins = get(&v); - - if (obj->isArguments()) - RETURN_STOP_A("can't trace setting elements of the |arguments| object"); - - if (obj == globalObj) - RETURN_STOP_A("can't trace setting elements on the global object"); - - if (!idx.isInt32()) { - if (!idx.isPrimitive()) - RETURN_STOP_A("non-primitive index"); - CHECK_STATUS_A(initOrSetPropertyByName(obj_ins, &idx, &v, - *cx->regs().pc == JSOP_INITELEM)); - } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { - // Fast path: assigning to element of typed array. - VMSideExit* branchExit = snapshot(BRANCH_EXIT); - - // Ensure array is a typed array and is the same type as what was written - guardClass(obj_ins, obj->getClass(), branchExit, LOAD_CONST); - - js::TypedArray* tarray = js::TypedArray::fromJSObject(obj); - - LIns* priv_ins = w.ldpObjPrivate(obj_ins); - - // The index was on the stack and is therefore a LIR float; force it to - // be an integer. - CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); - - // Ensure idx >= 0 && idx < length (by using uint32) - CHECK_STATUS_A(guard(true, - w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), - "inRange"), - OVERFLOW_EXIT, /* abortIfAlwaysExits = */true)); - - // We're now ready to store - LIns* data_ins = w.ldpConstTypedArrayData(priv_ins); - LIns* pidx_ins = w.ui2p(idx_ins); - LIns* typed_v_ins = v_ins; - - // If it's not a number, convert objects to NaN, - // null to 0, and call StringToNumber or BooleanOrUndefinedToNumber - // for those. - if (!v.isNumber()) { - if (v.isNull()) { - typed_v_ins = w.immd(0); - } else if (v.isUndefined()) { - typed_v_ins = w.immd(js_NaN); - } else if (v.isString()) { - LIns* ok_ins = w.allocp(sizeof(JSBool)); - LIns* args[] = { ok_ins, typed_v_ins, cx_ins }; - typed_v_ins = w.call(&js_StringToNumber_ci, args); - guard(false, - w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), - OOM_EXIT); - } else if (v.isBoolean()) { - JS_ASSERT(v.isBoolean()); - typed_v_ins = w.i2d(typed_v_ins); - } else { - typed_v_ins = w.immd(js_NaN); - } - } - - switch (tarray->type) { - case js::TypedArray::TYPE_INT8: - case js::TypedArray::TYPE_INT16: - case js::TypedArray::TYPE_INT32: - typed_v_ins = d2i(typed_v_ins); - break; - case js::TypedArray::TYPE_UINT8: - case js::TypedArray::TYPE_UINT16: - case js::TypedArray::TYPE_UINT32: - typed_v_ins = d2u(typed_v_ins); - break; - case js::TypedArray::TYPE_UINT8_CLAMPED: - if (IsPromotedInt32(typed_v_ins)) { - typed_v_ins = w.demoteToInt32(typed_v_ins); - typed_v_ins = w.cmovi(w.ltiN(typed_v_ins, 0), - w.immi(0), - w.cmovi(w.gtiN(typed_v_ins, 0xff), - w.immi(0xff), - typed_v_ins)); - } else { - typed_v_ins = w.call(&js_TypedArray_uint8_clamp_double_ci, &typed_v_ins); - } - break; - case js::TypedArray::TYPE_FLOAT32: - case js::TypedArray::TYPE_FLOAT64: - // Do nothing, this is already a float - break; - default: - JS_NOT_REACHED("Unknown typed array type in tracer"); - } - - switch (tarray->type) { - case js::TypedArray::TYPE_INT8: - case js::TypedArray::TYPE_UINT8_CLAMPED: - case js::TypedArray::TYPE_UINT8: - w.sti2cTypedArrayElement(typed_v_ins, data_ins, pidx_ins); - break; - case js::TypedArray::TYPE_INT16: - case js::TypedArray::TYPE_UINT16: - w.sti2sTypedArrayElement(typed_v_ins, data_ins, pidx_ins); - break; - case js::TypedArray::TYPE_INT32: - case js::TypedArray::TYPE_UINT32: - w.stiTypedArrayElement(typed_v_ins, data_ins, pidx_ins); - break; - case js::TypedArray::TYPE_FLOAT32: - w.std2fTypedArrayElement(typed_v_ins, data_ins, pidx_ins); - break; - case js::TypedArray::TYPE_FLOAT64: - w.stdTypedArrayElement(typed_v_ins, data_ins, pidx_ins); - break; - default: - JS_NOT_REACHED("Unknown typed array type in tracer"); - } - } else if (idx.toInt32() < 0 || !obj->isDenseArray()) { - CHECK_STATUS_A(initOrSetPropertyByIndex(obj_ins, idx_ins, &v, - *cx->regs().pc == JSOP_INITELEM)); - } else { - // Fast path: assigning to element of dense array. - VMSideExit* branchExit = snapshot(BRANCH_EXIT); - VMSideExit* mismatchExit = snapshot(MISMATCH_EXIT); - - // Make sure the array is actually dense. - if (!obj->isDenseArray()) - return ARECORD_STOP; - guardDenseArray(obj_ins, branchExit); - - // The index was on the stack and is therefore a LIR float. Force it to - // be an integer. - CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); - - if (!js_EnsureDenseArrayCapacity(cx, obj, idx.toInt32())) - RETURN_STOP_A("couldn't ensure dense array capacity for setelem"); - - // Grow the array if the index exceeds the capacity. This happens - // rarely, eg. less than 1% of the time in SunSpider. - LIns* capacity_ins = w.ldiDenseArrayCapacity(obj_ins); - /* - * It's important that CSE works across this control-flow diamond - * because it really helps series of interleaved GETELEM and SETELEM - * operations. Likewise with the diamond below. - */ - w.pauseAddingCSEValues(); - if (MaybeBranch mbr = w.jt(w.name(w.ltui(idx_ins, capacity_ins), "inRange"))) { - LIns* args[] = { idx_ins, obj_ins, cx_ins }; - LIns* res_ins = w.call(&js_EnsureDenseArrayCapacity_ci, args); - guard(false, w.eqi0(res_ins), mismatchExit); - w.label(mbr); - } - w.resumeAddingCSEValues(); - - // Get the address of the element. - LIns *elemp_ins = w.name(w.getDslotAddress(obj_ins, idx_ins), "elemp"); - - // If we are overwriting a hole: - // - Guard that we don't have any indexed properties along the prototype chain. - // - Check if the length has changed; if so, update it to index+1. - // This happens moderately often, eg. close to 10% of the time in - // SunSpider, and for some benchmarks it's close to 100%. - Address dslotAddr = DSlotsAddress(elemp_ins); - LIns* isHole_ins = w.name(is_boxed_magic(dslotAddr, JS_ARRAY_HOLE), - "isHole"); - w.pauseAddingCSEValues(); - if (MaybeBranch mbr1 = w.jf(isHole_ins)) { - /* - * It's important that this use branchExit, not mismatchExit, since - * changes to shapes should just mean we compile a new branch, not - * throw the whole trace away. - */ - CHECK_STATUS_A(guardPrototypeHasNoIndexedProperties(obj, obj_ins, branchExit)); - LIns* length_ins = w.lduiObjPrivate(obj_ins); - if (MaybeBranch mbr2 = w.jt(w.ltui(idx_ins, length_ins))) { - LIns* newLength_ins = w.name(w.addiN(idx_ins, 1), "newLength"); - w.stuiObjPrivate(obj_ins, newLength_ins); - w.label(mbr2); - } - w.label(mbr1); - } - w.resumeAddingCSEValues(); - - // Right, actually set the element. - box_value_into(v, v_ins, dslotAddr); - } - - jsbytecode* pc = cx->regs().pc; - if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP) - set(&lval, v_ins); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETELEM() -{ - return setElem(-3, -2, -1); -} - -static JSBool FASTCALL -CheckSameGlobal(JSObject *obj, JSObject *globalObj) -{ - return obj->getGlobal() == globalObj; -} -JS_DEFINE_CALLINFO_2(static, BOOL, CheckSameGlobal, OBJECT, OBJECT, 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLNAME() -{ - JSObject* scopeObj = &cx->fp()->scopeChain(); - LIns *funobj_ins; - JSObject *funobj; - if (scopeObj != globalObj) { - Value* vp; - NameResult nr; - CHECK_STATUS_A(scopeChainProp(scopeObj, vp, funobj_ins, nr, &scopeObj)); - if (!nr.tracked) - vp = &nr.v; - if (!vp->isObject()) - RETURN_STOP_A("callee is not an object"); - funobj = &vp->toObject(); - if (!funobj->isFunction()) - RETURN_STOP_A("callee is not a function"); - } else { - LIns* obj_ins = w.immpObjGC(globalObj); - JSObject* obj2; - PCVal pcval; - - CHECK_STATUS_A(test_property_cache(scopeObj, obj_ins, obj2, pcval)); - - if (pcval.isNull() || !pcval.isFunObj()) - RETURN_STOP_A("callee is not a function"); - - funobj = &pcval.toFunObj(); - funobj_ins = w.immpObjGC(funobj); - } - - // Detect crossed globals early. The interpreter could decide to compute - // a non-Undefined |this| value, and we want to make sure that we'll (1) - // abort in this case, and (2) bail out early if a callee will need special - // |this| computation. Note that if (scopeObj != globalObj), - // scopeChainProp() guarantees that scopeObj is a cacheable scope. - if (scopeObj == globalObj) { - JSFunction *fun = funobj->getFunctionPrivate(); - if (!fun->isInterpreted() || !fun->inStrictMode()) { - if (funobj->getGlobal() != globalObj) - RETURN_STOP_A("callee crosses globals"); - - // If the funobj is not constant, we need may a guard that the - // callee will not cross globals. This is only the case for non- - // compile-and-go trees. - if (!funobj_ins->isImmP() && !tree->script->compileAndGo) { - LIns* args[] = { w.immpObjGC(globalObj), funobj_ins }; - guard(false, w.eqi0(w.call(&CheckSameGlobal_ci, args)), MISMATCH_EXIT); - } - } - } - - stack(0, funobj_ins); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarArgOnTrace, CONTEXT, UINT32, INT32, UINT32, - DOUBLEPTR, 0, ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarVarOnTrace, CONTEXT, UINT32, INT32, UINT32, - DOUBLEPTR, 0, ACCSET_STORE_ANY) -JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarStackOnTrace, CONTEXT, UINT32, INT32, UINT32, - DOUBLEPTR, 0, ACCSET_STORE_ANY) - -/* - * Record LIR to get the given upvar. Return the LIR instruction for the upvar - * value. NULL is returned only on a can't-happen condition with an invalid - * typemap. The value of the upvar is returned as v. - */ -JS_REQUIRES_STACK LIns* -TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v) -{ - /* - * Try to find the upvar in the current trace's tracker. For &vr to be - * the address of the jsval found in js::GetUpvar, we must initialize - * vr directly with the result, so it is a reference to the same location. - * It does not work to assign the result to v, because v is an already - * existing reference that points to something else. - */ - UpvarCookie cookie = uva->vector[index]; - const Value& vr = GetUpvar(cx, script->staticLevel, cookie); - v = vr; - - if (LIns* ins = attemptImport(&vr)) - return ins; - - /* - * The upvar is not in the current trace, so get the upvar value exactly as - * the interpreter does and unbox. - */ - uint32 level = script->staticLevel - cookie.level(); - uint32 cookieSlot = cookie.slot(); - StackFrame* fp = cx->stack.findFrameAtLevel(level); - const CallInfo* ci; - int32 slot; - if (!fp->isFunctionFrame() || fp->isEvalFrame()) { - ci = &GetUpvarStackOnTrace_ci; - slot = cookieSlot; - } else if (cookieSlot < fp->numFormalArgs()) { - ci = &GetUpvarArgOnTrace_ci; - slot = cookieSlot; - } else if (cookieSlot == UpvarCookie::CALLEE_SLOT) { - ci = &GetUpvarArgOnTrace_ci; - slot = -2; - } else { - ci = &GetUpvarVarOnTrace_ci; - slot = cookieSlot - fp->numFormalArgs(); - } - - LIns* outp = w.allocp(sizeof(double)); - LIns* args[] = { - outp, - w.nameImmi(callDepth), - w.nameImmi(slot), - w.nameImmi(level), - cx_ins - }; - LIns* call_ins = w.call(ci, args); - JSValueType type = getCoercedType(v); - guard(true, - w.name(w.eqi(call_ins, w.immi(type)), "guard(type-stable upvar)"), - BRANCH_EXIT); - return stackLoad(AllocSlotsAddress(outp), type); -} - -/* - * Generate LIR to load a value from the native stack. This method ensures that - * the correct LIR load operator is used. - */ -LIns* -TraceRecorder::stackLoad(Address addr, uint8 type) -{ - switch (type) { - case JSVAL_TYPE_DOUBLE: - return w.ldd(addr); - case JSVAL_TYPE_NONFUNOBJ: - case JSVAL_TYPE_STRING: - case JSVAL_TYPE_FUNOBJ: - case JSVAL_TYPE_NULL: - return w.ldp(addr); - case JSVAL_TYPE_INT32: - return w.i2d(w.ldi(addr)); - case JSVAL_TYPE_BOOLEAN: - case JSVAL_TYPE_UNDEFINED: - case JSVAL_TYPE_MAGIC: - return w.ldi(addr); - case JSVAL_TYPE_BOXED: - default: - JS_NOT_REACHED("found jsval type in an upvar type map entry"); - return NULL; - } -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETFCSLOT() -{ - JSObject& callee = cx->fp()->callee(); - LIns* callee_ins = get(&cx->fp()->calleev()); - - LIns* upvars_ins = w.getObjPrivatizedSlot(callee_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); - - unsigned index = GET_UINT16(cx->regs().pc); - LIns *v_ins = unbox_value(callee.getFlatClosureUpvar(index), - FCSlotsAddress(upvars_ins, index), - snapshot(BRANCH_EXIT)); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLFCSLOT() -{ - CHECK_STATUS_A(record_JSOP_GETFCSLOT()); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::guardCallee(Value& callee) -{ - JSObject& callee_obj = callee.toObject(); - JS_ASSERT(callee_obj.isFunction()); - JSFunction* callee_fun = (JSFunction*) callee_obj.getPrivate(); - - /* - * First, guard on the callee's function (JSFunction*) identity. This is - * necessary since tracing always inlines function calls. But note that - * TR::functionCall avoids calling TR::guardCallee for constant methods - * (those hit in the property cache from JSOP_CALLPROP). - */ - VMSideExit* branchExit = snapshot(BRANCH_EXIT); - LIns* callee_ins = get(&callee); - tree->gcthings.addUnique(callee); - - guard(true, - w.eqp(w.ldpObjPrivate(callee_ins), w.nameImmpNonGC(callee_fun)), - branchExit); - - /* - * Second, consider guarding on the parent scope of the callee. - * - * As long as we guard on parent scope, we are guaranteed when recording - * variable accesses for a Call object having no private data that we can - * emit code that avoids checking for an active StackFrame for the Call - * object (which would hold fresh variable values -- the Call object's - * slots would be stale until the stack frame is popped). This is because - * Call objects can't pick up a new stack frame in their private slot once - * they have none. TR::callProp and TR::setCallProp depend on this fact and - * document where; if this guard is removed make sure to fix those methods. - * Search for the "parent guard" comments in them. - * - * In general, a loop in an escaping function scoped by Call objects could - * be traced before the function has returned, and the trace then triggered - * after, or vice versa. The function must escape, i.e., be a "funarg", or - * else there's no need to guard callee parent at all. So once we know (by - * static analysis) that a function may escape, we cannot avoid guarding on - * either the private data of the Call object or the Call object itself, if - * we wish to optimize for the particular deactivated stack frame (null - * private data) case as noted above. - */ - if (callee_fun->isInterpreted() && - (!FUN_NULL_CLOSURE(callee_fun) || callee_fun->script()->bindings.hasUpvars())) { - JSObject* parent = callee_obj.getParent(); - - if (parent != globalObj) { - if (!parent->isCall()) - RETURN_STOP("closure scoped by neither the global object nor a Call object"); - - guard(true, - w.eqp(w.ldpObjParent(callee_ins), w.immpObjGC(parent)), - branchExit); - } - } - return RECORD_CONTINUE; -} - -/* - * Prepare the given |arguments| object to be accessed on trace. If the return - * value is non-NULL, then the given |arguments| object refers to a frame on - * the current trace and is guaranteed to refer to the same frame on trace for - * all later executions. - */ -JS_REQUIRES_STACK StackFrame * -TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp) -{ - JS_ASSERT(obj->isArguments()); - - StackFrame *afp = frameIfInRange(obj, depthp); - if (!afp) - return NULL; - - VMSideExit *exit = snapshot(MISMATCH_EXIT); - guardClass(obj_ins, obj->getClass(), exit, LOAD_CONST); - - LIns* args_ins = getFrameObjPtr(afp->addressOfArgs()); - LIns* cmp = w.eqp(args_ins, obj_ins); - guard(true, cmp, exit); - return afp; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::createThis(JSObject& ctor, LIns* ctor_ins, LIns** thisobj_insp) -{ - JS_ASSERT(ctor.getFunctionPrivate()->isInterpreted()); - if (ctor.getFunctionPrivate()->isFunctionPrototype()) - RETURN_STOP("new Function.prototype"); - if (ctor.isBoundFunction()) - RETURN_STOP("new applied to bound function"); - - // Given the above conditions, ctor.prototype is a non-configurable data - // property with a slot. - const Shape *shape = LookupInterpretedFunctionPrototype(cx, &ctor); - if (!shape) - RETURN_ERROR("new f: error resolving f.prototype"); - - // At run time ctor might be a different instance of the same function. Its - // .prototype property might not be resolved yet. Guard on the function - // object's shape to make sure .prototype is there. - // - // However, if ctor_ins is constant, which is usual, we don't need to - // guard: .prototype is non-configurable, and an object's non-configurable - // data properties always stay in the same slot for the life of the object. - if (!ctor_ins->isImmP()) - guardShape(ctor_ins, &ctor, ctor.shape(), "ctor_shape", snapshot(MISMATCH_EXIT)); - - // Pass the slot of ctor.prototype to js_CreateThisFromTrace. We can only - // bake the slot into the trace, not the value, since .prototype is - // writable. - uintN protoSlot = shape->slot; - LIns* args[] = { w.nameImmw(protoSlot), ctor_ins, cx_ins }; - *thisobj_insp = w.call(&js_CreateThisFromTrace_ci, args); - guard(false, w.eqp0(*thisobj_insp), OOM_EXIT); - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing) -{ - /* - * The function's identity (JSFunction and therefore JSScript) is guarded, - * so we can optimize away the function call if the corresponding script is - * empty. No need to worry about crossing globals or relocating argv, even, - * in this case! - */ - if (fun->script()->isEmpty()) { - LIns* rval_ins; - if (constructing) - CHECK_STATUS(createThis(fval.toObject(), get(&fval), &rval_ins)); - else - rval_ins = w.immiUndefined(); - stack(-2 - argc, rval_ins); - return RECORD_CONTINUE; - } - - if (fval.toObject().getGlobal() != globalObj) - RETURN_STOP("JSOP_CALL or JSOP_NEW crosses global scopes"); - - StackFrame* const fp = cx->fp(); - - if (constructing) { - LIns* thisobj_ins; - CHECK_STATUS(createThis(fval.toObject(), get(&fval), &thisobj_ins)); - stack(-int(argc) - 1, thisobj_ins); - } - - // Generate a type map for the outgoing frame and stash it in the LIR - unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */); - FrameInfo* fi = (FrameInfo*) - tempAlloc().alloc(sizeof(FrameInfo) + stackSlots * sizeof(JSValueType)); - JSValueType* typemap = (JSValueType*)(fi + 1); - - DetermineTypesVisitor detVisitor(*this, typemap); - VisitStackSlots(detVisitor, cx, 0); - - JS_ASSERT(argc < FrameInfo::CONSTRUCTING_FLAG); - - tree->gcthings.addUnique(fval); - fi->pc = cx->regs().pc; - fi->imacpc = fp->maybeImacropc(); - fi->spdist = cx->regs().sp - fp->slots(); - fi->set_argc(uint16(argc), constructing); - fi->callerHeight = stackSlots - (2 + argc); - fi->callerArgc = fp->hasArgs() ? fp->numActualArgs() : 0; - - if (callDepth >= tree->maxCallDepth) - tree->maxCallDepth = callDepth + 1; - - fi = traceMonitor->frameCache->memoize(fi); - if (!fi) - RETURN_STOP("out of memory"); - w.stRstack(w.nameImmpNonGC(fi), lirbuf->rp, callDepth * sizeof(FrameInfo*)); - -#if defined JS_JIT_SPEW - debug_only_printf(LC_TMTracer, "iFC frameinfo=%p, stack=%d, map=", (void*)fi, - fi->callerHeight); - for (unsigned i = 0; i < fi->callerHeight; i++) - debug_only_printf(LC_TMTracer, "%c", TypeToChar(fi->get_typemap()[i])); - debug_only_print0(LC_TMTracer, "\n"); -#endif - - updateAtoms(fun->u.i.script); - return RECORD_CONTINUE; -} - -/* - * We implement JSOP_FUNAPPLY/JSOP_FUNCALL using imacros - */ -static inline JSOp -GetCallMode(StackFrame *fp) -{ - if (fp->hasImacropc()) { - JSOp op = (JSOp) *fp->imacropc(); - if (op == JSOP_FUNAPPLY || op == JSOP_FUNCALL) - return op; - } - return JSOP_CALL; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALL() -{ - uintN argc = GET_ARGC(cx->regs().pc); - cx->assertValidStackDepth(argc + 2); - return InjectStatus(functionCall(argc, GetCallMode(cx->fp()))); -} - -static jsbytecode* funapply_imacro_table[] = { - funapply_imacros.apply0, - funapply_imacros.apply1, - funapply_imacros.apply2, - funapply_imacros.apply3, - funapply_imacros.apply4, - funapply_imacros.apply5, - funapply_imacros.apply6, - funapply_imacros.apply7, - funapply_imacros.apply8 -}; - -static jsbytecode* funcall_imacro_table[] = { - funcall_imacros.call0, - funcall_imacros.call1, - funcall_imacros.call2, - funcall_imacros.call3, - funcall_imacros.call4, - funcall_imacros.call5, - funcall_imacros.call6, - funcall_imacros.call7, - funcall_imacros.call8 -}; - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FUNCALL() -{ - return record_JSOP_FUNAPPLY(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FUNAPPLY() -{ - jsbytecode *pc = cx->regs().pc; - uintN argc = GET_ARGC(pc); - cx->assertValidStackDepth(argc + 2); - - Value* vp = cx->regs().sp - (argc + 2); - jsuint length = 0; - JSObject* aobj = NULL; - LIns* aobj_ins = NULL; - - JS_ASSERT(!cx->fp()->hasImacropc()); - - if (!IsFunctionObject(vp[0])) - return record_JSOP_CALL(); - RETURN_IF_XML_A(vp[0]); - - JSObject* obj = &vp[0].toObject(); - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); - if (FUN_INTERPRETED(fun)) - return record_JSOP_CALL(); - - bool apply = fun->u.n.native == js_fun_apply; - if (!apply && fun->u.n.native != js_fun_call) - return record_JSOP_CALL(); - - /* - * We don't trace apply and call with a primitive 'this', which is the - * first positional parameter, unless 'this' is null. That's ok. - */ - if (argc > 0 && !vp[2].isObjectOrNull()) - return record_JSOP_CALL(); - - /* - * Guard on the identity of this, which is the function we are applying. - */ - if (!IsFunctionObject(vp[1])) - RETURN_STOP_A("callee is not a function"); - CHECK_STATUS_A(guardCallee(vp[1])); - - if (apply && argc >= 2) { - if (argc != 2) - RETURN_STOP_A("apply with excess arguments"); - if (vp[3].isPrimitive()) - RETURN_STOP_A("arguments parameter of apply is primitive"); - aobj = &vp[3].toObject(); - aobj_ins = get(&vp[3]); - - /* - * We trace dense arrays and arguments objects. The code we generate - * for apply uses imacros to handle a specific number of arguments. - */ - if (aobj->isDenseArray()) { - guardDenseArray(aobj_ins, MISMATCH_EXIT); - length = aobj->getArrayLength(); - guard(true, - w.eqiN(w.lduiObjPrivate(aobj_ins), length), - BRANCH_EXIT); - } else if (aobj->isArguments()) { - unsigned depth; - StackFrame *afp = guardArguments(aobj, aobj_ins, &depth); - if (!afp) - RETURN_STOP_A("can't reach arguments object's frame"); - if (aobj->isArgsLengthOverridden()) - RETURN_STOP_A("can't trace arguments with overridden length"); - guardArgsLengthNotAssigned(aobj_ins); - length = afp->numActualArgs(); - } else { - RETURN_STOP_A("arguments parameter of apply is not a dense array or argments object"); - } - - if (length >= JS_ARRAY_LENGTH(funapply_imacro_table)) - RETURN_STOP_A("too many arguments to apply"); - - return InjectStatus(callImacro(funapply_imacro_table[length])); - } - - if (argc >= JS_ARRAY_LENGTH(funcall_imacro_table)) - RETURN_STOP_A("too many arguments to call"); - - return InjectStatus(callImacro(funcall_imacro_table[argc])); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_NativeCallComplete() -{ - if (pendingSpecializedNative == IGNORE_NATIVE_CALL_COMPLETE_CALLBACK) - return ARECORD_CONTINUE; - -#ifdef DEBUG - JS_ASSERT(pendingSpecializedNative); - jsbytecode* pc = cx->regs().pc; - JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_FUNCALL || *pc == JSOP_FUNAPPLY || - *pc == JSOP_NEW || *pc == JSOP_SETPROP); -#endif - - Value& v = stackval(-1); - LIns* v_ins = get(&v); - - /* - * At this point the generated code has already called the native function - * and we can no longer fail back to the original pc location (JSOP_CALL) - * because that would cause the interpreter to re-execute the native - * function, which might have side effects. - * - * Instead, the snapshot() call below sees that we are currently parked on - * a traceable native's JSOP_CALL instruction, and it will advance the pc - * to restore by the length of the current opcode. If the native's return - * type is jsval, snapshot() will also indicate in the type map that the - * element on top of the stack is a boxed value which doesn't need to be - * boxed if the type guard generated by unbox_value() fails. - */ - - if (JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_STATUS) { - leaveDeepBailCall(); - - LIns* status = w.ldiStateField(builtinStatus); - if (pendingSpecializedNative == &generatedSpecializedNative) { - LIns* ok_ins = v_ins; - - /* - * If we run a generic traceable native, the return value is in the argument - * vector for native function calls. The actual return value of the native is a JSBool - * indicating the error status. - */ - - Address nativeRvalAddr = AllocSlotsAddress(native_rval_ins); - if (pendingSpecializedNative->flags & JSTN_CONSTRUCTOR) { - LIns *cond_ins; - LIns *x; - - // v_ins := the object payload from native_rval_ins - // cond_ins := true if native_rval_ins contains a JSObject* - unbox_any_object(nativeRvalAddr, &v_ins, &cond_ins); - // x := v_ins if native_rval_ins contains a JSObject*, NULL otherwise - x = w.cmovp(cond_ins, v_ins, w.immw(0)); - // v_ins := newobj_ins if native_rval_ins doesn't contain a JSObject*, - // the object payload from native_rval_ins otherwise - v_ins = w.cmovp(w.eqp0(x), newobj_ins, x); - } else { - v_ins = w.ldd(nativeRvalAddr); - } - set(&v, v_ins); - - propagateFailureToBuiltinStatus(ok_ins, status); - } - guard(true, w.eqi0(status), STATUS_EXIT); - } - - if (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER) { - /* - * If we side exit on the unboxing code due to a type change, make sure that the boxed - * value is actually currently associated with that location, and that we are talking - * about the top of the stack here, which is where we expected boxed values. - */ - JS_ASSERT(&v == &cx->regs().sp[-1] && get(&v) == v_ins); - set(&v, unbox_value(v, AllocSlotsAddress(native_rval_ins), snapshot(BRANCH_EXIT))); - } else if (pendingSpecializedNative->flags & - (JSTN_RETURN_NULLABLE_STR | JSTN_RETURN_NULLABLE_OBJ)) { - guard(v.isNull(), - w.name(w.eqp0(v_ins), "guard(nullness)"), - BRANCH_EXIT); - } else if (JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_NEG) { - /* Already added i2d in functionCall. */ - JS_ASSERT(v.isNumber()); - } else { - /* Convert the result to double if the builtin returns int32. */ - if (v.isNumber() && - pendingSpecializedNative->builtin->returnType() == ARGTYPE_I) { - set(&v, w.i2d(v_ins)); - } - } - - // We'll null pendingSpecializedNative in monitorRecording, on the next op - // cycle. There must be a next op since the stack is non-empty. - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::name(Value*& vp, LIns*& ins, NameResult& nr) -{ - JSObject* obj = &cx->fp()->scopeChain(); - JSOp op = JSOp(*cx->regs().pc); - if (js_CodeSpec[op].format & JOF_GNAME) - obj = obj->getGlobal(); - if (obj != globalObj) - return scopeChainProp(obj, vp, ins, nr); - - /* Can't use prop here, because we don't want unboxing from global slots. */ - LIns* obj_ins = w.immpObjGC(globalObj); - uint32 slot; - - JSObject* obj2; - PCVal pcval; - - /* - * Property cache ensures that we are dealing with an existing property, - * and guards the shape for us. - */ - CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); - - /* Abort if property doesn't exist (interpreter will report an error.) */ - if (pcval.isNull()) - RETURN_STOP_A("named property not found"); - - /* Insist on obj being the directly addressed object. */ - if (obj2 != obj) - RETURN_STOP_A("name() hit prototype chain"); - - /* Don't trace getter or setter calls, our caller wants a direct slot. */ - if (pcval.isShape()) { - const Shape* shape = pcval.toShape(); - if (!isValidSlot(obj, shape)) - RETURN_STOP_A("name() not accessing a valid slot"); - slot = shape->slot; - } else { - if (!pcval.isSlot()) - RETURN_STOP_A("PCE is not a slot"); - slot = pcval.toSlot(); - } - - if (!lazilyImportGlobalSlot(slot)) - RETURN_STOP_A("lazy import of global slot failed"); - - vp = &obj->getSlotRef(slot); - ins = get(vp); - nr.tracked = true; - return ARECORD_CONTINUE; -} - -static JSObject* FASTCALL -MethodReadBarrier(JSContext* cx, JSObject* obj, Shape* shape, JSObject* funobj) -{ - Value v = ObjectValue(*funobj); - AutoValueRooter tvr(cx, v); - - if (!obj->methodReadBarrier(cx, *shape, tvr.addr())) - return NULL; - return &tvr.value().toObject(); -} -JS_DEFINE_CALLINFO_4(static, OBJECT_FAIL, MethodReadBarrier, CONTEXT, OBJECT, SHAPE, OBJECT, - 0, ACCSET_STORE_ANY) - -/* - * Get a property. The current opcode has JOF_ATOM. - * - * There are two modes. The caller must pass nonnull pointers for either outp - * or both slotp and v_insp. In the latter case, we require a plain old - * property with a slot; if the property turns out to be anything else, abort - * tracing (rather than emit a call to a native getter or GetAnyProperty). - */ -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp, Value *outp) -{ - /* - * Insist that obj have js_SetProperty as its set object-op. This suffices - * to prevent a rogue obj from being used on-trace (loaded via obj_ins), - * because we will guard on shape (or else global object identity) and any - * object not having the same op must have a different class, and therefore - * must differ in its shape (or not be the global object). - */ - if (!obj->isDenseArray() && obj->getOps()->getProperty) - RETURN_STOP_A("non-dense-array, non-native js::ObjectOps::getProperty"); - - JS_ASSERT((slotp && v_insp && !outp) || (!slotp && !v_insp && outp)); - - /* - * Property cache ensures that we are dealing with an existing property, - * and guards the shape for us. - */ - JSObject* obj2; - PCVal pcval; - CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); - - /* Check for nonexistent property reference, which results in undefined. */ - if (pcval.isNull()) { - if (slotp) - RETURN_STOP_A("property not found"); - - /* - * We could specialize to guard on just JSClass.getProperty, but a mere - * class guard is simpler and slightly faster. - */ - if (obj->getClass()->getProperty != Valueify(JS_PropertyStub)) { - RETURN_STOP_A("can't trace through access to undefined property if " - "JSClass.getProperty hook isn't stubbed"); - } - guardClass(obj_ins, obj->getClass(), snapshot(MISMATCH_EXIT), LOAD_NORMAL); - - /* - * This trace will be valid as long as neither the object nor any object - * on its prototype chain changes shape. - * - * FIXME: This loop can become a single shape guard once bug 497789 has - * been fixed. - */ - VMSideExit* exit = snapshot(BRANCH_EXIT); - do { - if (obj->isNative()) { - CHECK_STATUS_A(guardShape(obj_ins, obj, obj->shape(), "guard(shape)", exit)); - } else if (obj->isDenseArray()) { - guardDenseArray(obj_ins, exit); - } else { - RETURN_STOP_A("non-native object involved in undefined property access"); - } - } while (guardHasPrototype(obj, obj_ins, &obj, &obj_ins, exit)); - - set(outp, w.immiUndefined()); - return ARECORD_CONTINUE; - } - - return InjectStatus(propTail(obj, obj_ins, obj2, pcval, slotp, v_insp, outp)); -} - -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::propTail(JSObject* obj, LIns* obj_ins, JSObject* obj2, PCVal pcval, - uint32 *slotp, LIns** v_insp, Value *outp) -{ - const JSCodeSpec& cs = js_CodeSpec[*cx->regs().pc]; - uint32 setflags = (cs.format & (JOF_INCDEC | JOF_FOR)); - JS_ASSERT(!(cs.format & JOF_SET)); - - const Shape* shape; - uint32 slot; - bool isMethod; - - if (pcval.isShape()) { - shape = pcval.toShape(); - JS_ASSERT(obj2->nativeContains(*shape)); - - if (setflags && !shape->hasDefaultSetter()) - RETURN_STOP("non-stub setter"); - if (setflags && !shape->writable()) - RETURN_STOP("writing to a readonly property"); - if (!shape->hasDefaultGetterOrIsMethod()) { - if (slotp) - RETURN_STOP("can't trace non-stub getter for this opcode"); - if (shape->hasGetterValue()) - return getPropertyWithScriptGetter(obj, obj_ins, shape); - if (shape->slot == SHAPE_INVALID_SLOT) - return getPropertyWithNativeGetter(obj_ins, shape, outp); - return getPropertyById(obj_ins, outp); - } - if (!obj2->containsSlot(shape->slot)) - RETURN_STOP("no valid slot"); - slot = shape->slot; - isMethod = shape->isMethod(); - JS_ASSERT_IF(isMethod, obj2->hasMethodBarrier()); - } else { - if (!pcval.isSlot()) - RETURN_STOP("PCE is not a slot"); - slot = pcval.toSlot(); - shape = NULL; - isMethod = false; - } - - /* We have a slot. Check whether it is direct or in a prototype. */ - if (obj2 != obj) { - if (setflags) - RETURN_STOP("JOF_INCDEC|JOF_FOR opcode hit prototype chain"); - - /* - * We're getting a prototype property. Two cases: - * - * 1. If obj2 is obj's immediate prototype we must walk up from obj, - * since direct and immediate-prototype cache hits key on obj's shape, - * not its identity. - * - * 2. Otherwise obj2 is higher up the prototype chain and we've keyed - * on obj's identity, and since setting __proto__ reshapes all objects - * along the old prototype chain, then provided we shape-guard obj2, - * we can "teleport" directly to obj2 by embedding it as a constant - * (this constant object instruction will be CSE'ed with the constant - * emitted by test_property_cache, whose shape is guarded). - */ - obj_ins = (obj2 == obj->getProto()) ? w.ldpObjProto(obj_ins) : w.immpObjGC(obj2); - obj = obj2; - } - - LIns* v_ins; - if (obj2 == globalObj) { - if (isMethod) - RETURN_STOP("get global method"); - if (!lazilyImportGlobalSlot(slot)) - RETURN_STOP("lazy import of global slot failed"); - v_ins = get(&globalObj->getSlotRef(slot)); - } else { - v_ins = unbox_slot(obj, obj_ins, slot, snapshot(BRANCH_EXIT)); - } - - /* - * Joined function object stored as a method must be cloned when extracted - * as a property value other than a callee. Note that shapes cover method - * value as well as other property attributes and order, so this condition - * is trace-invariant. - * - * We do not impose the method read barrier if in an imacro, assuming any - * property gets it does (e.g., for 'toString' from JSOP_NEW) will not be - * leaked to the calling script. - */ - if (isMethod && !cx->fp()->hasImacropc()) { - enterDeepBailCall(); - LIns* args[] = { v_ins, w.immpShapeGC(shape), obj_ins, cx_ins }; - v_ins = w.call(&MethodReadBarrier_ci, args); - leaveDeepBailCall(); - } - - if (slotp) { - *slotp = slot; - *v_insp = v_ins; - } - if (outp) - set(outp, v_ins); - return RECORD_CONTINUE; -} - -/* - * When we end up with a hole, read it as undefined, and make sure to set - * addr_ins to null. - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ins, - LIns*& addr_ins, VMSideExit* branchExit) -{ - JS_ASSERT(oval.isObject() && ival.isInt32()); - - JSObject* obj = &oval.toObject(); - LIns* obj_ins = get(&oval); - jsint idx = ival.toInt32(); - LIns* idx_ins; - CHECK_STATUS(makeNumberInt32(get(&ival), &idx_ins)); - - /* - * Arrays have both a length and a capacity, but we only need to check - * |index < capacity|; in the case where |length < index < capacity| - * the entries [length..capacity-1] will have already been marked as - * holes by resizeDenseArrayElements() so we can read them and get - * the correct value. - */ - LIns* capacity_ins = w.ldiDenseArrayCapacity(obj_ins); - jsuint capacity = obj->getDenseArrayCapacity(); - bool within = (jsuint(idx) < capacity); - if (!within) { - /* If not idx < capacity, stay on trace (and read value as undefined). */ - guard(true, w.geui(idx_ins, capacity_ins), branchExit); - - CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, snapshot(MISMATCH_EXIT))); - - v_ins = w.immiUndefined(); - addr_ins = NULL; - return RECORD_CONTINUE; - } - - /* Guard that index is within capacity. */ - guard(true, w.name(w.ltui(idx_ins, capacity_ins), "inRange"), branchExit); - - /* Load the value and guard on its type to unbox it. */ - vp = &obj->slots[jsuint(idx)]; - JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. - addr_ins = w.name(w.getDslotAddress(obj_ins, idx_ins), "elemp"); - v_ins = unbox_value(*vp, DSlotsAddress(addr_ins), branchExit); - - /* Don't let the hole value escape. Turn it into an undefined. */ - if (vp->isMagic()) { - CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, snapshot(MISMATCH_EXIT))); - v_ins = w.immiUndefined(); - addr_ins = NULL; - } - return RECORD_CONTINUE; -} - -/* See comments in TypedArrayTemplate::copyIndexToValue. */ -LIns * -TraceRecorder::canonicalizeNaNs(LIns *dval_ins) -{ - /* NaNs are the only floating point values that do not == themselves. */ - LIns *isnonnan_ins = w.eqd(dval_ins, dval_ins); - return w.cmovd(isnonnan_ins, dval_ins, w.immd(js_NaN)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::typedArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ins) -{ - JS_ASSERT(oval.isObject() && ival.isInt32()); - - JSObject* obj = &oval.toObject(); - LIns* obj_ins = get(&oval); - jsint idx = ival.toInt32(); - LIns* idx_ins; - CHECK_STATUS_A(makeNumberInt32(get(&ival), &idx_ins)); - LIns* pidx_ins = w.ui2p(idx_ins); - - js::TypedArray* tarray = js::TypedArray::fromJSObject(obj); - JS_ASSERT(tarray); - - /* priv_ins will load the TypedArray* */ - LIns* priv_ins = w.ldpObjPrivate(obj_ins); - - /* Abort if out-of-range. */ - if ((jsuint) idx >= tarray->length) - RETURN_STOP_A("out-of-range index on typed array"); - - /* - * Ensure idx < length - * - * NOTE! mLength is uint32, but it's guaranteed to fit in a Value - * int, so we can treat it as either signed or unsigned. - * If the index happens to be negative, when it's treated as - * unsigned it'll be a very large int, and thus won't be less than - * length. - */ - guard(true, - w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), "inRange"), - BRANCH_EXIT); - - /* We are now ready to load. Do a different type of load - * depending on what type of thing we're loading. */ - LIns* data_ins = w.ldpConstTypedArrayData(priv_ins); - - switch (tarray->type) { - case js::TypedArray::TYPE_INT8: - v_ins = w.i2d(w.ldc2iTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_UINT8: - case js::TypedArray::TYPE_UINT8_CLAMPED: - // i2d on purpose here: it's safe, because an 8-bit uint is guaranteed - // to fit in a 32-bit int, and i2d gets more optimization than ui2d. - v_ins = w.i2d(w.lduc2uiTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_INT16: - v_ins = w.i2d(w.lds2iTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_UINT16: - // i2d on purpose here: it's safe, because a 16-bit uint is guaranteed - // to fit in a 32-bit int, and i2d gets more optimization than ui2d. - v_ins = w.i2d(w.ldus2uiTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_INT32: - v_ins = w.i2d(w.ldiTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_UINT32: - v_ins = w.ui2d(w.ldiTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_FLOAT32: - v_ins = canonicalizeNaNs(w.ldf2dTypedArrayElement(data_ins, pidx_ins)); - break; - case js::TypedArray::TYPE_FLOAT64: - v_ins = canonicalizeNaNs(w.lddTypedArrayElement(data_ins, pidx_ins)); - break; - default: - JS_NOT_REACHED("Unknown typed array type in tracer"); - } - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::getProp(JSObject* obj, LIns* obj_ins) -{ - JSOp op = JSOp(*cx->regs().pc); - const JSCodeSpec& cs = js_CodeSpec[op]; - - JS_ASSERT(cs.ndefs == 1); - return prop(obj, obj_ins, NULL, NULL, &stackval(-cs.nuses)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::getProp(Value& v) -{ - if (v.isPrimitive()) - RETURN_STOP_A("primitive lhs"); - - return getProp(&v.toObject(), get(&v)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NAME() -{ - Value* vp; - LIns* v_ins; - NameResult nr; - CHECK_STATUS_A(name(vp, v_ins, nr)); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DOUBLE() -{ - double d = consts[GET_INDEX(cx->regs().pc)].toDouble(); - stack(0, w.immd(d)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STRING() -{ - JSAtom* atom = atoms[GET_INDEX(cx->regs().pc)]; - stack(0, w.immpAtomGC(atom)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ZERO() -{ - stack(0, w.immd(0)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ONE() -{ - stack(0, w.immd(1)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NULL() -{ - stack(0, w.immpNull()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_THIS() -{ - LIns* this_ins; - CHECK_STATUS_A(getThis(this_ins)); - stack(0, this_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FALSE() -{ - stack(0, w.immi(0)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TRUE() -{ - stack(0, w.immi(1)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_OR() -{ - return ifop(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_AND() -{ - return ifop(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TABLESWITCH() -{ - return InjectStatus(switchop()); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LOOKUPSWITCH() -{ - return InjectStatus(switchop()); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STRICTEQ() -{ - CHECK_STATUS_A(strictEquality(true, false)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STRICTNE() -{ - CHECK_STATUS_A(strictEquality(false, false)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_OBJECT() -{ - StackFrame* const fp = cx->fp(); - JSScript* script = fp->script(); - unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc); - - JSObject* obj; - obj = script->getObject(index); - stack(0, w.immpObjGC(obj)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_POP() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TRAP() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETARG() -{ - stack(0, arg(GET_ARGNO(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETARG() -{ - arg(GET_ARGNO(cx->regs().pc), stack(-1)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETLOCAL() -{ - stack(0, var(GET_SLOTNO(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETLOCAL() -{ - var(GET_SLOTNO(cx->regs().pc), stack(-1)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_UINT16() -{ - stack(0, w.immd(GET_UINT16(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NEWINIT() -{ - initDepth++; - hadNewInit = true; - - JSProtoKey key = JSProtoKey(cx->regs().pc[1]); - - LIns* proto_ins; - CHECK_STATUS_A(getClassPrototype(key, proto_ins)); - - LIns *v_ins; - if (key == JSProto_Array) { - LIns *args[] = { proto_ins, cx_ins }; - v_ins = w.call(&NewDenseEmptyArray_ci, args); - } else { - LIns *args[] = { w.immpNull(), proto_ins, cx_ins }; - v_ins = w.call(&js_InitializerObject_ci, args); - } - guard(false, w.eqp0(v_ins), OOM_EXIT); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NEWARRAY() -{ - initDepth++; - - LIns* proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins)); - - unsigned count = GET_UINT24(cx->regs().pc); - LIns *args[] = { proto_ins, w.immi(count), cx_ins }; - LIns *v_ins = w.call(&NewDenseAllocatedArray_ci, args); - - guard(false, w.eqp0(v_ins), OOM_EXIT); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NEWOBJECT() -{ - initDepth++; - - LIns* proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_Object, proto_ins)); - - JSObject* baseobj = cx->fp()->script()->getObject(getFullIndex(0)); - - LIns *args[] = { w.immpObjGC(baseobj), proto_ins, cx_ins }; - LIns *v_ins = w.call(&js_InitializerObject_ci, args); - - guard(false, w.eqp0(v_ins), OOM_EXIT); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENDINIT() -{ - initDepth--; - if (initDepth == 0) - hadNewInit = false; - -#ifdef DEBUG - Value& v = stackval(-1); - JS_ASSERT(!v.isPrimitive()); -#endif - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INITELEM() -{ - Value& v = stackval(-1); - Value& idx = stackval(-2); - Value& lval = stackval(-3); - - // The object is either a dense Array or an Object. Only handle the dense case here. - // Also skip array initializers which might be unoptimized NEWINIT initializers. - if (!lval.toObject().isDenseArray() || hadNewInit) - return setElem(-3, -2, -1); - - // The index is always the same constant integer. - JS_ASSERT(idx.isInt32()); - - // Nothing to do for holes, the array's length has already been set. - if (v.isMagic(JS_ARRAY_HOLE)) - return ARECORD_CONTINUE; - - LIns* obj_ins = get(&lval); - LIns* v_ins = get(&v); - - // Set the element. - LIns *slots_ins = w.ldpObjSlots(obj_ins); - box_value_into(v, v_ins, DSlotsAddress(slots_ins, idx.toInt32())); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFSHARP() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_USESHARP() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCARG() -{ - return InjectStatus(inc(argval(GET_ARGNO(cx->regs().pc)), 1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCLOCAL() -{ - return InjectStatus(inc(varval(GET_SLOTNO(cx->regs().pc)), 1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECARG() -{ - return InjectStatus(inc(argval(GET_ARGNO(cx->regs().pc)), -1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECLOCAL() -{ - return InjectStatus(inc(varval(GET_SLOTNO(cx->regs().pc)), -1)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARGINC() -{ - return InjectStatus(inc(argval(GET_ARGNO(cx->regs().pc)), 1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LOCALINC() -{ - return InjectStatus(inc(varval(GET_SLOTNO(cx->regs().pc)), 1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARGDEC() -{ - return InjectStatus(inc(argval(GET_ARGNO(cx->regs().pc)), -1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LOCALDEC() -{ - return InjectStatus(inc(varval(GET_SLOTNO(cx->regs().pc)), -1, false)); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IMACOP() -{ - JS_ASSERT(cx->fp()->hasImacropc()); - return ARECORD_CONTINUE; -} - -static JSBool FASTCALL -ObjectToIterator(JSContext* cx, JSObject *obj, int32 flags, Value* vp) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - vp->setObject(*obj); - bool ok = js_ValueToIterator(cx, flags, vp); - if (!ok) { - SetBuiltinError(tm); - return false; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, ObjectToIterator, CONTEXT, OBJECT, INT32, VALUEPTR, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ITER() -{ - Value& v = stackval(-1); - if (v.isPrimitive()) - RETURN_STOP_A("for-in on a primitive value"); - - RETURN_IF_XML_A(v); - - LIns *obj_ins = get(&v); - jsuint flags = cx->regs().pc[1]; - - enterDeepBailCall(); - - LIns* vp_ins = w.allocp(sizeof(Value)); - LIns* args[] = { vp_ins, w.immi(flags), obj_ins, cx_ins }; - LIns* ok_ins = w.call(&ObjectToIterator_ci, args); - - // We need to guard on ok_ins, but this requires a snapshot of the state - // after this op. monitorRecording will do it for us. - pendingGuardCondition = ok_ins; - - // ObjectToIterator can deep-bail without throwing, leaving a value of - // unknown type in *vp (it can be either a function or a non-function - // object). Use the same mechanism as finishGetProp to arrange for - // LeaveTree to deal with this value. - pendingUnboxSlot = cx->regs().sp - 1; - set(pendingUnboxSlot, w.name(w.lddAlloc(vp_ins), "iterval")); - - leaveDeepBailCall(); - - return ARECORD_CONTINUE; -} - -static JSBool FASTCALL -IteratorMore(JSContext *cx, JSObject *iterobj, Value *vp) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - if (!js_IteratorMore(cx, iterobj, vp)) { - SetBuiltinError(tm); - return false; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, IteratorMore, CONTEXT, OBJECT, VALUEPTR, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_MOREITER() -{ - Value& iterobj_val = stackval(-1); - if (iterobj_val.isPrimitive()) - RETURN_STOP_A("for-in on a primitive value"); - - RETURN_IF_XML_A(iterobj_val); - - JSObject* iterobj = &iterobj_val.toObject(); - LIns* iterobj_ins = get(&iterobj_val); - LIns* cond_ins; - - /* - * JSOP_FOR* already guards on this, but in certain rare cases we might - * record misformed loop traces. Note that it's not necessary to guard on - * ni->flags (nor do we in unboxNextValue), because the different - * iteration type will guarantee a different entry typemap. - */ - if (iterobj->hasClass(&js_IteratorClass)) { - guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - - NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); - if (ni->isKeyIter()) { - LIns *ni_ins = w.ldpObjPrivate(iterobj_ins); - LIns *cursor_ins = w.ldpIterCursor(ni_ins); - LIns *end_ins = w.ldpIterEnd(ni_ins); - - cond_ins = w.ltp(cursor_ins, end_ins); - stack(0, cond_ins); - return ARECORD_CONTINUE; - } - } else { - guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - } - - enterDeepBailCall(); - - LIns* vp_ins = w.allocp(sizeof(Value)); - LIns* args[] = { vp_ins, iterobj_ins, cx_ins }; - pendingGuardCondition = w.call(&IteratorMore_ci, args); - - leaveDeepBailCall(); - - cond_ins = is_boxed_true(AllocSlotsAddress(vp_ins)); - stack(0, cond_ins); - - // Write this value back even though we haven't changed it. - // See the comment in DeepBail about "clobbering deep bails". - stack(-1, iterobj_ins); - - return ARECORD_CONTINUE; -} - -static JSBool FASTCALL -CloseIterator(JSContext *cx, JSObject *iterobj) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - if (!js_CloseIterator(cx, iterobj)) { - SetBuiltinError(tm); - return false; - } - return WasBuiltinSuccessful(tm); -} -JS_DEFINE_CALLINFO_2(extern, BOOL_FAIL, CloseIterator, CONTEXT, OBJECT, 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENDITER() -{ - JS_ASSERT(!stackval(-1).isPrimitive()); - - enterDeepBailCall(); - - LIns* args[] = { stack(-1), cx_ins }; - LIns* ok_ins = w.call(&CloseIterator_ci, args); - - // We need to guard on ok_ins, but this requires a snapshot of the state - // after this op. monitorRecording will do it for us. - pendingGuardCondition = ok_ins; - - leaveDeepBailCall(); - - return ARECORD_CONTINUE; -} - -#if JS_BITS_PER_WORD == 32 -JS_REQUIRES_STACK void -TraceRecorder::storeMagic(JSWhyMagic why, Address addr) -{ - w.stiValuePayload(w.immpMagicWhy(why), addr); - w.stiValueTag(w.immpMagicWhy(JSVAL_TAG_MAGIC), addr); -} -#elif JS_BITS_PER_WORD == 64 -JS_REQUIRES_STACK void -TraceRecorder::storeMagic(JSWhyMagic why, Address addr) -{ - LIns *magic = w.nameImmq(BUILD_JSVAL(JSVAL_TAG_MAGIC, why)); - w.stq(magic, addr); -} -#endif - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::unboxNextValue(LIns* &v_ins) -{ - Value &iterobj_val = stackval(-1); - JSObject *iterobj = &iterobj_val.toObject(); - LIns* iterobj_ins = get(&iterobj_val); - - if (iterobj->hasClass(&js_IteratorClass)) { - guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); - - LIns *ni_ins = w.ldpObjPrivate(iterobj_ins); - LIns *cursor_ins = w.ldpIterCursor(ni_ins); - - /* Emit code to stringify the id if necessary. */ - Address cursorAddr = IterPropsAddress(cursor_ins); - if (ni->isKeyIter()) { - /* Read the next id from the iterator. */ - jsid id = *ni->current(); - LIns *id_ins = w.name(w.ldp(cursorAddr), "id"); - - /* - * Most iterations over object properties never have to actually deal with - * any numeric properties, so we guard here instead of branching. - */ - guard(JSID_IS_STRING(id), is_string_id(id_ins), BRANCH_EXIT); - - if (JSID_IS_STRING(id)) { - v_ins = unbox_string_id(id_ins); - } else if (JSID_IS_INT(id)) { - /* id is an integer, convert to a string. */ - LIns *id_to_int_ins = unbox_int_id(id_ins); - LIns* args[] = { id_to_int_ins, cx_ins }; - v_ins = w.call(&js_IntToString_ci, args); - guard(false, w.eqp0(v_ins), OOM_EXIT); - } else { -#if JS_HAS_XML_SUPPORT - JS_ASSERT(JSID_IS_OBJECT(id)); - JS_ASSERT(JSID_TO_OBJECT(id)->isXMLId()); - RETURN_STOP_A("iterated over a property with an XML id"); -#else - JS_NEVER_REACHED("unboxNextValue"); -#endif - } - - /* Increment the cursor by one jsid and store it back. */ - cursor_ins = w.addp(cursor_ins, w.nameImmw(sizeof(jsid))); - w.stpIterCursor(cursor_ins, ni_ins); - return ARECORD_CONTINUE; - } - } else { - guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - } - - - Address iterValueAddr = CxAddress(iterValue); - v_ins = unbox_value(cx->iterValue, iterValueAddr, snapshot(BRANCH_EXIT)); - storeMagic(JS_NO_ITER_VALUE, iterValueAddr); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORNAME() -{ - Value* vp; - LIns* x_ins; - NameResult nr; - CHECK_STATUS_A(name(vp, x_ins, nr)); - if (!nr.tracked) - RETURN_STOP_A("forname on non-tracked value not supported"); - LIns* v_ins; - CHECK_STATUS_A(unboxNextValue(v_ins)); - set(vp, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORGNAME() -{ - return record_JSOP_FORNAME(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORPROP() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORELEM() -{ - LIns* v_ins; - CHECK_STATUS_A(unboxNextValue(v_ins)); - stack(0, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORARG() -{ - LIns* v_ins; - CHECK_STATUS_A(unboxNextValue(v_ins)); - arg(GET_ARGNO(cx->regs().pc), v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FORLOCAL() -{ - LIns* v_ins; - CHECK_STATUS_A(unboxNextValue(v_ins)); - var(GET_SLOTNO(cx->regs().pc), v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_POPN() -{ - return ARECORD_CONTINUE; -} - -static inline bool -IsFindableCallObj(JSObject *obj) -{ - return obj->isCall() && - (obj->callIsForEval() || obj->getCallObjCalleeFunction()->isHeavyweight()); -} - -/* - * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The - * generated code also ensures that any call objects found have not changed shape. - * - * obj starting object - * obj_ins LIR instruction representing obj - * targetObj end object for traversal - * targetIns [out] LIR instruction representing obj2 - */ -JS_REQUIRES_STACK RecordingStatus -TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *targetObj, - LIns *&targetIns) -{ - VMSideExit* exit = NULL; - - /* - * Scope chains are often left "incomplete", and reified lazily when - * necessary, since doing so is expensive. When creating null and flat - * closures on trace (the only kinds supported), the global object is - * hardcoded as the parent, since reifying the scope chain on trace - * would be extremely difficult. This is because block objects need frame - * pointers, which do not exist on trace, and thus would require magic - * similar to arguments objects or reification of stack frames. Luckily, - * for null and flat closures, these blocks are unnecessary. - * - * The problem, as exposed by bug 523793, is that this means creating a - * fixed traversal on trace can be inconsistent with the shorter scope - * chain used when executing a trace. To address this, perform an initial - * sweep of the scope chain to make sure that if there is a heavyweight - * function with a call object, and there is also a block object, the - * trace is safely aborted. - * - * If there is no call object, we must have arrived at the global object, - * and can bypass the scope chain traversal completely. - */ - bool foundCallObj = false; - bool foundBlockObj = false; - JSObject* searchObj = obj; - - for (;;) { - if (searchObj != globalObj) { - if (searchObj->isBlock()) - foundBlockObj = true; - else if (IsFindableCallObj(searchObj)) - foundCallObj = true; - } - - if (searchObj == targetObj) - break; - - searchObj = searchObj->getParent(); - if (!searchObj) - RETURN_STOP("cannot traverse this scope chain on trace"); - } - - if (!foundCallObj) { - JS_ASSERT(targetObj == globalObj); - targetIns = w.nameImmpNonGC(globalObj); - return RECORD_CONTINUE; - } - - if (foundBlockObj) - RETURN_STOP("cannot traverse this scope chain on trace"); - - /* There was a call object, or should be a call object now. */ - for (;;) { - if (obj != globalObj) { - if (!IsCacheableNonGlobalScope(obj)) - RETURN_STOP("scope chain lookup crosses non-cacheable object"); - - // We must guard on the shape of all call objects for heavyweight functions - // that we traverse on the scope chain: if the shape changes, a variable with - // the same name may have been inserted in the scope chain. - if (IsFindableCallObj(obj)) { - if (!exit) - exit = snapshot(BRANCH_EXIT); - guard(true, - w.name(w.eqiN(w.ldiObjShape(obj_ins), obj->shape()), "guard_shape"), - exit); - } - } - - JS_ASSERT(!obj->isBlock()); - - if (obj == targetObj) - break; - - obj = obj->getParent(); - obj_ins = w.ldpObjParent(obj_ins); - } - - targetIns = obj_ins; - return RECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BINDNAME() -{ - TraceMonitor *localtm = traceMonitor; - StackFrame* const fp = cx->fp(); - JSObject *obj; - - if (!fp->isFunctionFrame()) { - obj = &fp->scopeChain(); - -#ifdef DEBUG - StackFrame *fp2 = fp; -#endif - - /* - * In global code, fp->scopeChain can only contain blocks whose values - * are still on the stack. We never use BINDNAME to refer to these. - */ - while (obj->isBlock()) { - // The block's values are still on the stack. -#ifdef DEBUG - // NB: fp2 can't be a generator frame, because !fp->hasFunction. - while (obj->getPrivate() != fp2) { - JS_ASSERT(fp2->isEvalOrDebuggerFrame()); - fp2 = fp2->prev(); - if (!fp2) - JS_NOT_REACHED("bad stack frame"); - } -#endif - obj = obj->getParent(); - // Blocks always have parents. - JS_ASSERT(obj); - } - - /* - * If this is a strict mode eval frame, we will have a Call object for - * it. For now just don't trace this case. - */ - if (obj != globalObj) { - JS_ASSERT(obj->isCall()); - JS_ASSERT(obj->callIsForEval()); - RETURN_STOP_A("BINDNAME within strict eval code"); - } - - /* - * The trace is specialized to this global object. Furthermore, we know - * it is the sole 'global' object on the scope chain: we set globalObj - * to the scope chain element with no parent, and we reached it - * starting from the function closure or the current scopeChain, so - * there is nothing inner to it. Therefore this must be the right base - * object. - */ - stack(0, w.immpObjGC(obj)); - return ARECORD_CONTINUE; - } - - // We can't trace BINDNAME in functions that contain direct calls to eval, - // as they might add bindings which previously-traced references would have - // to see. - if (JSFUN_HEAVYWEIGHT_TEST(fp->fun()->flags)) - RETURN_STOP_A("BINDNAME in heavyweight function."); - - // We don't have the scope chain on trace, so instead we get a start object - // that is on the scope chain and doesn't skip the target object (the one - // that contains the property). - Value *callee = &cx->fp()->calleev(); - obj = callee->toObject().getParent(); - if (obj == globalObj) { - stack(0, w.immpObjGC(obj)); - return ARECORD_CONTINUE; - } - LIns *obj_ins = w.ldpObjParent(get(callee)); - - // Find the target object. - JSAtom *atom = atoms[GET_INDEX(cx->regs().pc)]; - jsid id = ATOM_TO_JSID(atom); - JSObject *obj2 = js_FindIdentifierBase(cx, &fp->scopeChain(), id); - if (!obj2) - RETURN_ERROR_A("error in js_FindIdentifierBase"); - if (!localtm->recorder) - return ARECORD_ABORTED; - if (obj2 != globalObj && !obj2->isCall()) - RETURN_STOP_A("BINDNAME on non-global, non-call object"); - - // Generate LIR to get to the target object from the start object. - LIns *obj2_ins; - CHECK_STATUS_A(traverseScopeChain(obj, obj_ins, obj2, obj2_ins)); - - // If |obj2| is the global object, we can refer to it directly instead of walking up - // the scope chain. There may still be guards on intervening call objects. - stack(0, obj2 == globalObj ? w.immpObjGC(obj2) : obj2_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_THROW() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IN() -{ - Value& rval = stackval(-1); - Value& lval = stackval(-2); - - if (rval.isPrimitive()) - RETURN_STOP_A("JSOP_IN on non-object right operand"); - JSObject* obj = &rval.toObject(); - LIns* obj_ins = get(&rval); - - jsid id; - LIns* x; - if (lval.isInt32()) { - if (!js_Int32ToId(cx, lval.toInt32(), &id)) - RETURN_ERROR_A("OOM converting left operand of JSOP_IN to string"); - - if (obj->isDenseArray()) { - // Fast path for dense arrays - VMSideExit* branchExit = snapshot(BRANCH_EXIT); - guardDenseArray(obj_ins, branchExit); - - // If our proto has indexed props, all bets are off on our - // "false" values and out-of-bounds access. Just guard on - // that. - CHECK_STATUS_A(guardPrototypeHasNoIndexedProperties(obj, obj_ins, - snapshot(MISMATCH_EXIT))); - - LIns* idx_ins; - CHECK_STATUS_A(makeNumberInt32(get(&lval), &idx_ins)); - idx_ins = w.name(idx_ins, "index"); - LIns* capacity_ins = w.ldiDenseArrayCapacity(obj_ins); - LIns* inRange = w.ltui(idx_ins, capacity_ins); - - if (jsuint(lval.toInt32()) < obj->getDenseArrayCapacity()) { - guard(true, inRange, branchExit); - - LIns *elem_ins = w.getDslotAddress(obj_ins, idx_ins); - // Need to make sure we don't have a hole - LIns *is_hole_ins = - is_boxed_magic(DSlotsAddress(elem_ins), JS_ARRAY_HOLE); - - // Set x to true (index in our array) if is_hole_ins == 0 - x = w.eqi0(is_hole_ins); - } else { - guard(false, inRange, branchExit); - x = w.nameImmi(0); - } - } else { - LIns* num_ins; - CHECK_STATUS_A(makeNumberInt32(get(&lval), &num_ins)); - LIns* args[] = { num_ins, obj_ins, cx_ins }; - x = w.call(&js_HasNamedPropertyInt32_ci, args); - } - } else if (lval.isString()) { - if (!js_ValueToStringId(cx, lval, &id)) - RETURN_ERROR_A("left operand of JSOP_IN didn't convert to a string-id"); - LIns* args[] = { get(&lval), obj_ins, cx_ins }; - x = w.call(&js_HasNamedProperty_ci, args); - } else { - RETURN_STOP_A("string or integer expected"); - } - - guard(false, w.eqiN(x, JS_NEITHER), OOM_EXIT); - x = w.eqiN(x, 1); - - TraceMonitor &localtm = *traceMonitor; - - JSObject* obj2; - JSProperty* prop; - JSBool ok = obj->lookupProperty(cx, id, &obj2, &prop); - - if (!ok) - RETURN_ERROR_A("obj->lookupProperty failed in JSOP_IN"); - - /* lookupProperty can reenter the interpreter and kill |this|. */ - if (!localtm.recorder) - return ARECORD_ABORTED; - - bool cond = prop != NULL; - - /* - * The interpreter fuses comparisons and the following branch, so we have - * to do that here as well. - */ - jsbytecode *pc = cx->regs().pc; - fuseIf(pc + 1, cond, x); - - /* If the branch was to a loop header, we may need to close it. */ - if (pc[1] == JSOP_IFNE || pc[1] == JSOP_IFEQ) - CHECK_STATUS_A(checkTraceEnd(pc + 1)); - - /* - * We update the stack after the guard. This is safe since the guard bails - * out at the comparison and the interpreter will therefore re-execute the - * comparison. This way the value of the condition doesn't have to be - * calculated and saved on the stack in most cases. - */ - set(&lval, x); - return ARECORD_CONTINUE; -} - -static JSBool FASTCALL -HasInstanceOnTrace(JSContext* cx, JSObject* ctor, ValueArgType arg) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx); - - const Value &argref = ValueArgToConstRef(arg); - JSBool result = JS_FALSE; - if (!HasInstance(cx, ctor, &argref, &result)) - SetBuiltinError(tm); - return result; -} -JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstanceOnTrace, CONTEXT, OBJECT, VALUE, - 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INSTANCEOF() -{ - // If the rhs isn't an object, we are headed for a TypeError. - Value& ctor = stackval(-1); - if (ctor.isPrimitive()) - RETURN_STOP_A("non-object on rhs of instanceof"); - - Value& val = stackval(-2); - LIns* val_ins = box_value_for_native_call(val, get(&val)); - - enterDeepBailCall(); - LIns* args[] = {val_ins, get(&ctor), cx_ins}; - stack(-2, w.call(&HasInstanceOnTrace_ci, args)); - LIns* status_ins = w.ldiStateField(builtinStatus); - pendingGuardCondition = w.eqi0(status_ins); - leaveDeepBailCall(); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEBUGGER() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GOSUB() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RETSUB() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_EXCEPTION() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LINENO() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BLOCKCHAIN() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NULLBLOCKCHAIN() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CONDSWITCH() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CASE() -{ - CHECK_STATUS_A(strictEquality(true, true)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFAULT() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_EVAL() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENUMELEM() -{ - /* - * To quote from jsinterp.cpp's JSOP_ENUMELEM case: - * Funky: the value to set is under the [obj, id] pair. - */ - return setElem(-2, -1, -3); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETTER() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETTER() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFFUN() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFFUN_FC() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFCONST() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFVAR() -{ - return ARECORD_STOP; -} - -jsatomid -TraceRecorder::getFullIndex(ptrdiff_t pcoff) -{ - jsatomid index = GET_INDEX(cx->regs().pc + pcoff); - index += atoms - cx->fp()->script()->atomMap.vector; - return index; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LAMBDA() -{ - JSFunction* fun; - fun = cx->fp()->script()->getFunction(getFullIndex()); - - if (FUN_NULL_CLOSURE(fun) && FUN_OBJECT(fun)->getParent() != globalObj) - RETURN_STOP_A("Null closure function object parent must be global object"); - - /* - * Emit code to clone a null closure parented by this recorder's global - * object, in order to preserve function object evaluation rules observable - * via identity and mutation. But don't clone if our result is consumed by - * JSOP_SETMETHOD or JSOP_INITMETHOD, since we optimize away the clone for - * these combinations and clone only if the "method value" escapes. - * - * See jsinterp.cpp, the JSOP_LAMBDA null closure case. The JSOP_SETMETHOD and - * JSOP_INITMETHOD logic governing the early ARECORD_CONTINUE returns below - * must agree with the corresponding break-from-do-while(0) logic there. - */ - if (FUN_NULL_CLOSURE(fun) && FUN_OBJECT(fun)->getParent() == &cx->fp()->scopeChain()) { - jsbytecode *pc2 = AdvanceOverBlockchainOp(cx->regs().pc + JSOP_LAMBDA_LENGTH); - JSOp op2 = JSOp(*pc2); - - if (op2 == JSOP_INITMETHOD) { - stack(0, w.immpObjGC(FUN_OBJECT(fun))); - return ARECORD_CONTINUE; - } - - if (op2 == JSOP_SETMETHOD) { - Value lval = stackval(-1); - - if (!lval.isPrimitive() && lval.toObject().canHaveMethodBarrier()) { - stack(0, w.immpObjGC(FUN_OBJECT(fun))); - return ARECORD_CONTINUE; - } - } else if (fun->joinable()) { - if (op2 == JSOP_CALL) { - /* - * Array.prototype.sort and String.prototype.replace are - * optimized as if they are special form. We know that they - * won't leak the joined function object in obj, therefore - * we don't need to clone that compiler- created function - * object for identity/mutation reasons. - */ - int iargc = GET_ARGC(pc2); - - /* - * Note that we have not yet pushed obj as the final argument, - * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], - * is the callee for this JSOP_CALL. - */ - const Value &cref = cx->regs().sp[1 - (iargc + 2)]; - JSObject *callee; - - if (IsFunctionObject(cref, &callee)) { - JSFunction *calleeFun = callee->getFunctionPrivate(); - Native native = calleeFun->maybeNative(); - - if ((iargc == 1 && native == array_sort) || - (iargc == 2 && native == str_replace)) { - stack(0, w.immpObjGC(FUN_OBJECT(fun))); - return ARECORD_CONTINUE; - } - } - } else if (op2 == JSOP_NULL) { - pc2 += JSOP_NULL_LENGTH; - op2 = JSOp(*pc2); - - if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) { - stack(0, w.immpObjGC(FUN_OBJECT(fun))); - return ARECORD_CONTINUE; - } - } - } - - LIns *proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins)); - - LIns* args[] = { w.immpObjGC(globalObj), proto_ins, w.immpFunGC(fun), cx_ins }; - LIns* x = w.call(&js_NewNullClosure_ci, args); - stack(0, x); - return ARECORD_CONTINUE; - } - - if (GetBlockChainFast(cx, cx->fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH)) - RETURN_STOP_A("Unable to trace creating lambda in let"); - - LIns *proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins)); - LIns* scopeChain_ins = scopeChain(); - JS_ASSERT(scopeChain_ins); - LIns* args[] = { proto_ins, scopeChain_ins, w.nameImmpNonGC(fun), cx_ins }; - LIns* call_ins = w.call(&js_CloneFunctionObject_ci, args); - guard(false, - w.name(w.eqp0(call_ins), "guard(js_CloneFunctionObject)"), - OOM_EXIT); - stack(0, call_ins); - - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LAMBDA_FC() -{ - JSFunction* fun; - fun = cx->fp()->script()->getFunction(getFullIndex()); - - if (FUN_OBJECT(fun)->getParent() != globalObj) - return ARECORD_STOP; - - if (GetBlockChainFast(cx, cx->fp(), JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH)) - RETURN_STOP_A("Unable to trace creating lambda in let"); - - LIns* args[] = { scopeChain(), w.immpFunGC(fun), cx_ins }; - LIns* closure_ins = w.call(&js_AllocFlatClosure_ci, args); - guard(false, - w.name(w.eqp(closure_ins, w.immpNull()), "guard(js_AllocFlatClosure)"), - OOM_EXIT); - - JSScript *script = fun->script(); - if (script->bindings.hasUpvars()) { - JSUpvarArray *uva = script->upvars(); - LIns* upvars_ins = w.getObjPrivatizedSlot(closure_ins, - JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); - - for (uint32 i = 0, n = uva->length; i < n; i++) { - Value v; - LIns* v_ins = upvar(script, uva, i, v); - if (!v_ins) - return ARECORD_STOP; - - box_value_into(v, v_ins, FCSlotsAddress(upvars_ins, i)); - } - } - - stack(0, closure_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLEE() -{ - stack(0, get(&cx->fp()->calleev())); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETLOCALPOP() -{ - var(GET_SLOTNO(cx->regs().pc), stack(-1)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IFPRIMTOP() -{ - // Traces are type-specialized, including null vs. object, so we need do - // nothing here. The upstream unbox_value called after valueOf or toString - // from an imacro (e.g.) will fork the trace for us, allowing us to just - // follow along mindlessly :-). - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETCALL() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TRY() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FINALLY() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_NOP() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARGSUB() -{ - StackFrame* const fp = cx->fp(); - - /* - * The arguments object or its absence in the frame is part of the typemap, - * so a record-time check suffices here. We don't bother tracing ARGSUB in - * the case of an arguments object exising, because ARGSUB and to a lesser - * extent ARGCNT are emitted to avoid arguments object creation. - */ - if (!fp->hasArgsObj() && !fp->fun()->isHeavyweight()) { - uintN slot = GET_ARGNO(cx->regs().pc); - if (slot >= fp->numActualArgs()) - RETURN_STOP_A("can't trace out-of-range arguments"); - - stack(0, get(&cx->fp()->canonicalActualArg(slot))); - return ARECORD_CONTINUE; - } - RETURN_STOP_A("can't trace JSOP_ARGSUB hard case"); -} - -JS_REQUIRES_STACK LIns* -TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) -{ - // The following implements JSObject::isArgsLengthOverridden on trace. - // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden. - LIns *len_ins = w.getArgsLength(argsobj_ins); - LIns *ovr_ins = w.andi(len_ins, w.nameImmi(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); - guard(true, w.eqi0(ovr_ins), MISMATCH_EXIT); - return len_ins; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARGCNT() -{ - StackFrame * const fp = cx->fp(); - - if (fp->fun()->flags & JSFUN_HEAVYWEIGHT) - RETURN_STOP_A("can't trace heavyweight JSOP_ARGCNT"); - - // argc is fixed on trace, so ideally we would simply generate LIR for - // constant argc. But the user can mutate arguments.length in the - // interpreter, so we have to check for that in the trace entry frame. - // We also have to check that arguments.length has not been mutated - // at record time, because if so we will generate incorrect constant - // LIR, which will assert in tryToDemote(). - if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden()) - RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); - LIns *a_ins = getFrameObjPtr(fp->addressOfArgs()); - if (callDepth == 0) { - if (MaybeBranch mbr = w.jt(w.eqp0(a_ins))) { - guardArgsLengthNotAssigned(a_ins); - w.label(mbr); - } - } - stack(0, w.immd(fp->numActualArgs())); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj) -{ - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); - - if (FUN_NULL_CLOSURE(fun) && FUN_OBJECT(fun)->getParent() == globalObj) { - LIns *proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins)); - - LIns* args[] = { w.immpObjGC(globalObj), proto_ins, w.immpFunGC(fun), cx_ins }; - LIns* x = w.call(&js_NewNullClosure_ci, args); - var(slot, x); - return ARECORD_CONTINUE; - } - - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFLOCALFUN() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFLOCALFUN_FC() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GOTOX() -{ - return record_JSOP_GOTO(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IFEQX() -{ - return record_JSOP_IFEQ(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_IFNEX() -{ - return record_JSOP_IFNE(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ORX() -{ - return record_JSOP_OR(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ANDX() -{ - return record_JSOP_AND(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GOSUBX() -{ - return record_JSOP_GOSUB(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CASEX() -{ - CHECK_STATUS_A(strictEquality(true, true)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFAULTX() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TABLESWITCHX() -{ - return record_JSOP_TABLESWITCH(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LOOKUPSWITCHX() -{ - return InjectStatus(switchop()); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BACKPATCH() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BACKPATCH_POP() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_THROWING() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETRVAL() -{ - // If we implement this, we need to update JSOP_STOP. - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RETRVAL() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_REGEXP() -{ - StackFrame* const fp = cx->fp(); - JSScript* script = fp->script(); - unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc); - - LIns* proto_ins; - CHECK_STATUS_A(getClassPrototype(JSProto_RegExp, proto_ins)); - - LIns* args[] = { - proto_ins, - w.immpObjGC(script->getRegExp(index)), - cx_ins - }; - LIns* regex_ins = w.call(&js_CloneRegExpObject_ci, args); - guard(false, w.eqp0(regex_ins), OOM_EXIT); - - stack(0, regex_ins); - return ARECORD_CONTINUE; -} - -// begin JS_HAS_XML_SUPPORT - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DEFXMLNS() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ANYNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_QNAMEPART() -{ - return record_JSOP_STRING(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_QNAMECONST() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_QNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TOATTRNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TOATTRVAL() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ADDATTRNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ADDATTRVAL() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BINDXMLNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETXMLNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DESCENDANTS() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_FILTER() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENDFILTER() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TOXML() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TOXMLLIST() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLTAGEXPR() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLELTEXPR() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLCDATA() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLCOMMENT() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_XMLPI() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETFUNNS() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STARTXML() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STARTXMLEXPR() -{ - return ARECORD_STOP; -} - -// end JS_HAS_XML_SUPPORT - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLPROP() -{ - Value& l = stackval(-1); - JSObject* obj; - LIns* obj_ins; - LIns* this_ins; - if (!l.isPrimitive()) { - obj = &l.toObject(); - obj_ins = get(&l); - this_ins = obj_ins; // |this| for subsequent call - } else { - JSProtoKey protoKey; - debug_only_stmt(const char* protoname = NULL;) - if (l.isString()) { - protoKey = JSProto_String; - debug_only_stmt(protoname = "String.prototype";) - } else if (l.isNumber()) { - protoKey = JSProto_Number; - debug_only_stmt(protoname = "Number.prototype";) - } else if (l.isBoolean()) { - protoKey = JSProto_Boolean; - debug_only_stmt(protoname = "Boolean.prototype";) - } else { - JS_ASSERT(l.isNull() || l.isUndefined()); - RETURN_STOP_A("callprop on null or void"); - } - - if (!js_GetClassPrototype(cx, NULL, protoKey, &obj)) - RETURN_ERROR_A("GetClassPrototype failed!"); - - obj_ins = w.immpObjGC(obj); - debug_only_stmt(obj_ins = w.name(obj_ins, protoname);) - this_ins = get(&l); // use primitive as |this| - } - - JSObject* obj2; - PCVal pcval; - CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); - - if (pcval.isNull()) - RETURN_STOP_A("callprop of missing method"); - - if (pcval.isFunObj()) { - if (l.isPrimitive()) { - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &pcval.toFunObj()); - if (fun->isInterpreted() && !fun->inStrictMode()) - RETURN_STOP_A("callee does not accept primitive |this|"); - } - set(&l, w.immpObjGC(&pcval.toFunObj())); - } else { - if (l.isPrimitive()) - RETURN_STOP_A("callprop of primitive method"); - JS_ASSERT_IF(pcval.isShape(), !pcval.toShape()->isMethod()); - CHECK_STATUS_A(propTail(obj, obj_ins, obj2, pcval, NULL, NULL, &l)); - } - stack(0, this_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DELDESC() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_UINT24() -{ - stack(0, w.immd(GET_UINT24(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INDEXBASE() -{ - atoms += GET_INDEXBASE(cx->regs().pc); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RESETBASE() -{ - updateAtoms(); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_RESETBASE0() -{ - updateAtoms(); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLELEM() -{ - return record_JSOP_GETELEM(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_STOP() -{ - StackFrame *fp = cx->fp(); - - /* A return from callDepth 0 terminates the current loop, except for recursion. */ - if (callDepth == 0 && !fp->hasImacropc()) { - AUDIT(returnLoopExits); - return endLoop(); - } - - if (fp->hasImacropc()) { - /* - * End of imacro, so return true to the interpreter immediately. The - * interpreter's JSOP_STOP case will return from the imacro, back to - * the pc after the calling op, still in the same StackFrame. - */ - updateAtoms(fp->script()); - return ARECORD_CONTINUE; - } - - CHECK_STATUS_A(putActivationObjects()); - - if (Probes::callTrackingActive(cx)) { - LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins }; - LIns* call_ins = w.call(&functionProbe_ci, args); - guard(false, w.eqi0(call_ins), MISMATCH_EXIT); - } - - /* - * We know falling off the end of a constructor returns the new object that - * was passed in via fp->argv[-1], while falling off the end of a function - * returns undefined. - * - * NB: we do not support script rval (eval, API users who want the result - * of the last expression-statement, debugger API calls). - */ - if (fp->isConstructing()) { - rval_ins = get(&fp->thisValue()); - } else { - rval_ins = w.immiUndefined(); - } - clearReturningFrameFromNativeTracker(); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETXPROP() -{ - Value& l = stackval(-1); - if (l.isPrimitive()) - RETURN_STOP_A("primitive-this for GETXPROP?"); - - Value* vp; - LIns* v_ins; - NameResult nr; - CHECK_STATUS_A(name(vp, v_ins, nr)); - stack(-1, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLXMLNAME() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_TYPEOFEXPR() -{ - return record_JSOP_TYPEOF(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENTERBLOCK() -{ - JSObject* obj; - obj = cx->fp()->script()->getObject(getFullIndex(0)); - - LIns* void_ins = w.immiUndefined(); - for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++) - stack(i, void_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LEAVEBLOCK() -{ - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GENERATOR() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_YIELD() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ARRAYPUSH() -{ - uint32_t slot = GET_UINT16(cx->regs().pc); - JS_ASSERT(cx->fp()->numFixed() <= slot); - JS_ASSERT(cx->fp()->slots() + slot < cx->regs().sp - 1); - Value &arrayval = cx->fp()->slots()[slot]; - JS_ASSERT(arrayval.isObject()); - LIns *array_ins = get(&arrayval); - Value &elt = stackval(-1); - LIns *elt_ins = box_value_for_native_call(elt, get(&elt)); - - enterDeepBailCall(); - - LIns *args[] = { elt_ins, array_ins, cx_ins }; - pendingGuardCondition = w.call(&js_ArrayCompPush_tn_ci, args); - - leaveDeepBailCall(); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_ENUMCONSTELEM() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LEAVEBLOCKEXPR() -{ - LIns* v_ins = stack(-1); - int n = -1 - GET_UINT16(cx->regs().pc); - stack(n, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETTHISPROP() -{ - LIns* this_ins; - - CHECK_STATUS_A(getThis(this_ins)); - - /* - * It's safe to just use cx->fp->thisValue() here because getThis() returns - * ARECORD_STOP or ARECORD_ERROR if thisv is not available. - */ - const Value &thisv = cx->fp()->thisValue(); - if (!thisv.isObject()) - RETURN_STOP_A("primitive this for GETTHISPROP"); - - CHECK_STATUS_A(getProp(&thisv.toObject(), this_ins)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETARGPROP() -{ - return getProp(argval(GET_ARGNO(cx->regs().pc))); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETLOCALPROP() -{ - return getProp(varval(GET_SLOTNO(cx->regs().pc))); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INDEXBASE1() -{ - atoms += 1 << 16; - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INDEXBASE2() -{ - atoms += 2 << 16; - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INDEXBASE3() -{ - atoms += 3 << 16; - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLLOCAL() -{ - uintN slot = GET_SLOTNO(cx->regs().pc); - stack(0, var(slot)); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLARG() -{ - uintN slot = GET_ARGNO(cx->regs().pc); - stack(0, arg(slot)); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BINDGNAME() -{ - stack(0, w.immpObjGC(globalObj)); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INT8() -{ - stack(0, w.immd(GET_INT8(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INT32() -{ - stack(0, w.immd(GET_INT32(cx->regs().pc))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_LENGTH() -{ - Value& l = stackval(-1); - if (l.isPrimitive()) { - if (!l.isString()) - RETURN_STOP_A("non-string primitive JSOP_LENGTH unsupported"); - set(&l, w.i2d(w.p2i(w.getStringLength(get(&l))))); - return ARECORD_CONTINUE; - } - - JSObject* obj = &l.toObject(); - LIns* obj_ins = get(&l); - - if (obj->isArguments()) { - unsigned depth; - StackFrame *afp = guardArguments(obj, obj_ins, &depth); - if (!afp) - RETURN_STOP_A("can't reach arguments object's frame"); - - // We must both check at record time and guard at run time that - // arguments.length has not been reassigned, redefined or deleted. - if (obj->isArgsLengthOverridden()) - RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); - LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins); - - // slot_ins is the value from the slot; right-shift to get the length - // (see JSObject::getArgsInitialLength in jsfun.cpp). - LIns* v_ins = w.i2d(w.rshiN(slot_ins, JSObject::ARGS_PACKED_BITS_COUNT)); - set(&l, v_ins); - return ARECORD_CONTINUE; - } - - LIns* v_ins; - if (obj->isArray()) { - if (obj->isDenseArray()) { - guardDenseArray(obj_ins, BRANCH_EXIT); - } else { - JS_ASSERT(obj->isSlowArray()); - guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); - } - v_ins = w.lduiObjPrivate(obj_ins); - if (obj->getArrayLength() <= JSVAL_INT_MAX) { - guard(true, w.leui(v_ins, w.immi(JSVAL_INT_MAX)), BRANCH_EXIT); - v_ins = w.i2d(v_ins); - } else { - v_ins = w.ui2d(v_ins); - } - } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { - // Ensure array is a typed array and is the same type as what was written - guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_NORMAL); - v_ins = w.i2d(w.ldiConstTypedArrayLength(w.ldpObjPrivate(obj_ins))); - } else { - if (!obj->isNative()) - RETURN_STOP_A("can't trace length property access on non-array, non-native object"); - return getProp(obj, obj_ins); - } - set(&l, v_ins); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_HOLE() -{ - stack(0, w.immpMagicWhy(JS_ARRAY_HOLE)); - return ARECORD_CONTINUE; -} - -AbortableRecordingStatus -TraceRecorder::record_JSOP_TRACE() -{ - return ARECORD_CONTINUE; -} - -AbortableRecordingStatus -TraceRecorder::record_JSOP_NOTRACE() -{ - return ARECORD_CONTINUE; -} - -JSBool FASTCALL -js_Unbrand(JSContext *cx, JSObject *obj) -{ - return obj->unbrand(cx); -} - -JS_DEFINE_CALLINFO_2(extern, BOOL, js_Unbrand, CONTEXT, OBJECT, 0, ACCSET_STORE_ANY) - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_UNBRAND() -{ - LIns* args_ins[] = { stack(-1), cx_ins }; - LIns* call_ins = w.call(&js_Unbrand_ci, args_ins); - guard(false, w.eqi0(call_ins), OOM_EXIT); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_UNBRANDTHIS() -{ - /* In case of primitive this, do nothing. */ - StackFrame *fp = cx->fp(); - if (fp->fun()->inStrictMode() && !fp->thisValue().isObject()) - return ARECORD_CONTINUE; - - LIns* this_ins; - RecordingStatus status = getThis(this_ins); - if (status != RECORD_CONTINUE) - return InjectStatus(status); - - LIns* args_ins[] = { this_ins, cx_ins }; - LIns* call_ins = w.call(&js_Unbrand_ci, args_ins); - guard(false, w.eqi0(call_ins), OOM_EXIT); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SHARPINIT() -{ - return ARECORD_STOP; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETGLOBAL() -{ - uint32 slot = cx->fp()->script()->getGlobalSlot(GET_SLOTNO(cx->regs().pc)); - if (!lazilyImportGlobalSlot(slot)) - RETURN_STOP_A("lazy import of global slot failed"); - - stack(0, get(&globalObj->getSlotRef(slot))); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLGLOBAL() -{ - uint32 slot = cx->fp()->script()->getGlobalSlot(GET_SLOTNO(cx->regs().pc)); - if (!lazilyImportGlobalSlot(slot)) - RETURN_STOP_A("lazy import of global slot failed"); - - Value &v = globalObj->getSlotRef(slot); - stack(0, get(&v)); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GETGNAME() -{ - return record_JSOP_NAME(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_SETGNAME() -{ - return record_JSOP_SETNAME(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GNAMEDEC() -{ - return record_JSOP_NAMEDEC(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_GNAMEINC() -{ - return record_JSOP_NAMEINC(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_DECGNAME() -{ - return record_JSOP_DECNAME(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_INCGNAME() -{ - return record_JSOP_INCNAME(); -} - -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_CALLGNAME() -{ - return record_JSOP_CALLNAME(); -} - -#define DBG_STUB(OP) \ - JS_REQUIRES_STACK AbortableRecordingStatus \ - TraceRecorder::record_##OP() \ - { \ - RETURN_STOP_A("can't trace " #OP); \ - } - -DBG_STUB(JSOP_GETUPVAR_DBG) -DBG_STUB(JSOP_CALLUPVAR_DBG) -DBG_STUB(JSOP_DEFFUN_DBGFC) -DBG_STUB(JSOP_DEFLOCALFUN_DBGFC) -DBG_STUB(JSOP_LAMBDA_DBGFC) - -#ifdef JS_JIT_SPEW -/* - * Print information about entry typemaps and unstable exits for all peers - * at a PC. - */ -void -DumpPeerStability(TraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, - uint32 argc) -{ - TreeFragment* f; - bool looped = false; - unsigned length = 0; - - for (f = LookupLoop(tm, ip, globalObj, globalShape, argc); f != NULL; f = f->peer) { - if (!f->code()) - continue; - debug_only_printf(LC_TMRecorder, "Stability of fragment %p:\nENTRY STACK=", (void*)f); - if (looped) - JS_ASSERT(f->nStackTypes == length); - for (unsigned i = 0; i < f->nStackTypes; i++) - debug_only_printf(LC_TMRecorder, "%c", TypeToChar(f->stackTypeMap()[i])); - debug_only_print0(LC_TMRecorder, " GLOBALS="); - for (unsigned i = 0; i < f->nGlobalTypes(); i++) - debug_only_printf(LC_TMRecorder, "%c", TypeToChar(f->globalTypeMap()[i])); - debug_only_print0(LC_TMRecorder, "\n"); - UnstableExit* uexit = f->unstableExits; - while (uexit != NULL) { - debug_only_print0(LC_TMRecorder, "EXIT "); - JSValueType* m = uexit->exit->fullTypeMap(); - debug_only_print0(LC_TMRecorder, "STACK="); - for (unsigned i = 0; i < uexit->exit->numStackSlots; i++) - debug_only_printf(LC_TMRecorder, "%c", TypeToChar(m[i])); - debug_only_print0(LC_TMRecorder, " GLOBALS="); - for (unsigned i = 0; i < uexit->exit->numGlobalSlots; i++) { - debug_only_printf(LC_TMRecorder, "%c", - TypeToChar(m[uexit->exit->numStackSlots + i])); - } - debug_only_print0(LC_TMRecorder, "\n"); - uexit = uexit->next; - } - length = f->nStackTypes; - looped = true; - } -} -#endif - -#ifdef MOZ_TRACEVIS - -FILE* traceVisLogFile = NULL; -JSHashTable *traceVisScriptTable = NULL; - -JS_FRIEND_API(bool) -StartTraceVis(const char* filename = "tracevis.dat") -{ - if (traceVisLogFile) { - // If we're currently recording, first we must stop. - StopTraceVis(); - } - - traceVisLogFile = fopen(filename, "wb"); - if (!traceVisLogFile) - return false; - - return true; -} - -JS_FRIEND_API(JSBool) -StartTraceVisNative(JSContext *cx, uintN argc, jsval *vp) -{ - JSBool ok; - - if (argc > 0 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) { - JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]); - char *filename = js_DeflateString(cx, str->chars(), str->length()); - if (!filename) - goto error; - ok = StartTraceVis(filename); - cx->free_(filename); - } else { - ok = StartTraceVis(); - } - - if (ok) { - fprintf(stderr, "started TraceVis recording\n"); - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; - } - - error: - JS_ReportError(cx, "failed to start TraceVis recording"); - return false; -} - -JS_FRIEND_API(bool) -StopTraceVis() -{ - if (!traceVisLogFile) - return false; - - fclose(traceVisLogFile); // not worth checking the result - traceVisLogFile = NULL; - - return true; -} - -JS_FRIEND_API(JSBool) -StopTraceVisNative(JSContext *cx, uintN argc, jsval *vp) -{ - JSBool ok = StopTraceVis(); - - if (ok) { - fprintf(stderr, "stopped TraceVis recording\n"); - JS_SET_RVAL(cx, vp, JSVAL_VOID); - } else { - JS_ReportError(cx, "TraceVis isn't running"); - } - - return ok; -} - -#endif /* MOZ_TRACEVIS */ - -JS_REQUIRES_STACK void -TraceRecorder::captureStackTypes(unsigned callDepth, JSValueType* typeMap) -{ - CaptureTypesVisitor capVisitor(cx, traceMonitor->oracle, typeMap, !!oracle); - VisitStackSlots(capVisitor, cx, callDepth); -} - -JS_REQUIRES_STACK void -TraceRecorder::determineGlobalTypes(JSValueType* typeMap) -{ - DetermineTypesVisitor detVisitor(*this, typeMap); - VisitGlobalSlots(detVisitor, cx, *tree->globalSlots); -} - -#ifdef JS_METHODJIT - -class AutoRetBlacklist -{ - jsbytecode* pc; - bool* blacklist; - - public: - AutoRetBlacklist(jsbytecode* pc, bool* blacklist) - : pc(pc), blacklist(blacklist) - { } - - ~AutoRetBlacklist() - { - *blacklist = IsBlacklisted(pc); - } -}; - -JS_REQUIRES_STACK TracePointAction -RecordTracePoint(JSContext* cx, TraceMonitor* tm, - uintN& inlineCallCount, bool* blacklist, bool execAllowed) -{ - StackFrame* fp = cx->fp(); - jsbytecode* pc = cx->regs().pc; - - JS_ASSERT(!tm->recorder); - JS_ASSERT(!tm->profile); - - JSObject* globalObj = cx->fp()->scopeChain().getGlobal(); - uint32 globalShape = -1; - SlotList* globalSlots = NULL; - - AutoRetBlacklist autoRetBlacklist(pc, blacklist); - - if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) { - Backoff(tm, pc); - return TPA_Nothing; - } - - uint32 argc = entryFrameArgc(cx); - TreeFragment* tree = LookupOrAddLoop(tm, pc, globalObj, globalShape, argc); - - debug_only_printf(LC_TMTracer, - "Looking for compat peer %d@%d, from %p (ip: %p)\n", - js_FramePCToLineNumber(cx, cx->fp()), - FramePCOffset(cx, cx->fp()), (void*)tree, tree->ip); - - if (tree->code() || tree->peer) { - uintN count; - TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, tree, count); - if (match) { - VMSideExit* lr = NULL; - VMSideExit* innermostNestedGuard = NULL; - - if (!execAllowed) { - /* We've already compiled a trace for it, but we don't want to use that trace. */ - Blacklist((jsbytecode*)tree->root->ip); - return TPA_Nothing; - } - - /* Best case - just go and execute. */ - if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr)) - return TPA_Error; - - if (!lr) - return TPA_Nothing; - - switch (lr->exitType) { - case UNSTABLE_LOOP_EXIT: - if (!AttemptToStabilizeTree(cx, tm, globalObj, lr, NULL, NULL, 0)) - return TPA_RanStuff; - break; - - case MUL_ZERO_EXIT: - case OVERFLOW_EXIT: - if (lr->exitType == MUL_ZERO_EXIT) - tm->oracle->markInstructionSlowZeroTest(cx->regs().pc); - else - tm->oracle->markInstructionUndemotable(cx->regs().pc); - /* FALL THROUGH */ - case BRANCH_EXIT: - if (!AttemptToExtendTree(cx, tm, lr, NULL, NULL, NULL)) - return TPA_RanStuff; - break; - - case LOOP_EXIT: - if (!innermostNestedGuard) - return TPA_RanStuff; - if (!AttemptToExtendTree(cx, tm, innermostNestedGuard, lr, NULL, NULL)) - return TPA_RanStuff; - break; - - default: - return TPA_RanStuff; - } - - JS_ASSERT(tm->recorder); - - goto interpret; - } - - if (count >= MAXPEERS) { - debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n"); - Blacklist((jsbytecode*)tree->root->ip); - return TPA_Nothing; - } - } - - if (++tree->hits() < HOTLOOP) - return TPA_Nothing; - if (!ScopeChainCheck(cx, tree)) - return TPA_Nothing; - if (!RecordTree(cx, tm, tree->first, NULL, NULL, 0, globalSlots)) - return TPA_Nothing; - - interpret: - JS_ASSERT(tm->recorder); - - /* Locked and loaded with a recorder. Ask the interperter to go run some code. */ - if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD)) - return TPA_Error; - - JS_ASSERT(!cx->isExceptionPending()); - - return TPA_RanStuff; -} - -LoopProfile::LoopProfile(TraceMonitor *tm, StackFrame *entryfp, - jsbytecode *top, jsbytecode *bottom) - : traceMonitor(tm), - entryScript(entryfp->script()), - entryfp(entryfp), - top(top), - bottom(bottom), - hits(0), - undecided(false), - unprofitable(false) -{ - reset(); -} - -void -LoopProfile::reset() -{ - profiled = false; - traceOK = false; - numAllOps = 0; - numSelfOps = 0; - numSelfOpsMult = 0; - branchMultiplier = 1; - shortLoop = false; - maybeShortLoop = false; - numInnerLoops = 0; - loopStackDepth = 0; - sp = 0; - - PodArrayZero(allOps); - PodArrayZero(selfOps); -} - -MonitorResult -LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount) -{ - if (cx->regs().pc == top) { - debug_only_print0(LC_TMProfiler, "Profiling complete (edge)\n"); - decide(cx); - } else { - /* Record an inner loop invocation. */ - StackFrame *fp = cx->fp(); - jsbytecode *pc = cx->regs().pc; - bool found = false; - - /* We started with the most deeply nested one first, since it gets hit most often.*/ - for (int i = int(numInnerLoops)-1; i >= 0; i--) { - if (innerLoops[i].entryfp == fp && innerLoops[i].top == pc) { - innerLoops[i].iters++; - found = true; - break; - } - } - - if (!found && numInnerLoops < PROFILE_MAX_INNER_LOOPS) - innerLoops[numInnerLoops++] = InnerLoop(fp, pc, NULL); - } - - return MONITOR_NOT_RECORDING; -} - - -static const uintN PROFILE_HOTLOOP = 61; -static const uintN MAX_PROFILE_OPS = 4096; - -static jsbytecode * -GetLoopBottom(JSContext *cx) -{ - return GetLoopBottom(cx, cx->regs().pc); -} - -static LoopProfile * -LookupOrAddProfile(JSContext *cx, TraceMonitor *tm, void** traceData, uintN *traceEpoch) -{ - LoopProfile *prof; - - /* - * We try to keep a pointer to the loop profile inside the TRACE IC. - * We also keep a pointer inside a hashtable for when we need to - * look up nested loops (or when ICs are disabled). - * - * Memory for the profile is allocated in the dataAlloc for the - * trace monitor. Since this thing can get flushed periodically, - * we use epochs to decide if the profile in the MIC is valid, as - * follows. Every time the trace monitor is flushed, - * |tm->flushEpoch| is incremented. When storing the profile in - * the IC, we store the current |tm->flushEpoch| along with it. - * Before pulling a profile out of the IC, we check that its - * stored epoch is still up-to-date with |tm->flushEpoch|. - * This ensures that no flush has happened in between. - */ - -#if JS_MONOIC - if (*traceData && *traceEpoch == tm->flushEpoch) { - prof = (LoopProfile *)*traceData; - } else { - jsbytecode* pc = cx->regs().pc; - jsbytecode* bottom = GetLoopBottom(cx); - if (!bottom) - return NULL; - prof = new (*tm->dataAlloc) LoopProfile(tm, cx->fp(), pc, bottom); - *traceData = prof; - *traceEpoch = tm->flushEpoch; - tm->loopProfiles->put(pc, prof); - } -#else - LoopProfileMap &table = *tm->loopProfiles; - jsbytecode* pc = cx->regs().pc; - if (LoopProfileMap::AddPtr p = table.lookupForAdd(pc)) { - prof = p->value; - } else { - jsbytecode* bottom = GetLoopBottom(cx); - if (!bottom) - return NULL; - prof = new (*tm->dataAlloc) LoopProfile(tm, cx->fp(), pc, bottom); - table.add(p, pc, prof); - } -#endif - - return prof; -} - -static LoopProfile * -LookupLoopProfile(TraceMonitor *tm, jsbytecode *pc) -{ - LoopProfileMap &table = *tm->loopProfiles; - if (LoopProfileMap::Ptr p = table.lookup(pc)) { - JS_ASSERT(p->value->top == pc); - return p->value; - } else - return NULL; -} - -void -LoopProfile::stopProfiling(JSContext *cx) -{ - JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL); - JS_THREAD_DATA(cx)->profilingCompartment = NULL; - - traceMonitor->profile = NULL; -} - -JS_REQUIRES_STACK TracePointAction -MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist, - void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx); - - if (!cx->profilingEnabled) - return RecordTracePoint(cx, tm, inlineCallCount, blacklist, true); - - *blacklist = false; - - /* - * This is the only place where we check for re-entering the profiler. - * The assumption is that MonitorTracePoint is the only place where we - * start profiling. When we do so, we enter an interpreter frame with - * JSINTERP_PROFILE mode. All other entry points to the profiler check - * that the interpreter mode is JSINTERP_PROFILE. If it isn't, they - * don't profile. - */ - if (TRACE_PROFILER(cx)) - return TPA_Nothing; - - jsbytecode* pc = cx->regs().pc; - LoopProfile *prof = LookupOrAddProfile(cx, tm, traceData, traceEpoch); - if (!prof) { - *blacklist = true; - return TPA_Nothing; - } - - prof->hits += hits; - if (prof->hits < PROFILE_HOTLOOP) - return TPA_Nothing; - - AutoRetBlacklist autoRetBlacklist(cx->regs().pc, blacklist); - - if (prof->profiled) { - if (prof->traceOK) { - return RecordTracePoint(cx, tm, inlineCallCount, blacklist, prof->execOK); - } else { - return TPA_Nothing; - } - } - - debug_only_printf(LC_TMProfiler, "Profiling at line %d\n", - js_FramePCToLineNumber(cx, cx->fp())); - - tm->profile = prof; - - JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL); - JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL); - JS_THREAD_DATA(cx)->profilingCompartment = cx->compartment; - - if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE)) - return TPA_Error; - - JS_ASSERT(!cx->isExceptionPending()); - - /* Look it up again since a reset may have happened during Interpret. */ - prof = LookupLoopProfile(tm, pc); - if (prof && prof->undecided) { - *loopCounter = 3000; - prof->reset(); - } - - return TPA_RanStuff; -} - -/* - * Returns true if pc is within the given loop. - * If we're in a different script, then we must have come from - * a call instruction within the loop (since we check if we're within - * the loop before each instruction) so we're still in the loop. - */ -template -static inline bool -PCWithinLoop(StackFrame *fp, jsbytecode *pc, T& loop) -{ - return fp > loop.entryfp || (fp == loop.entryfp && pc >= loop.top && pc <= loop.bottom); -} - -LoopProfile::ProfileAction -LoopProfile::profileOperation(JSContext* cx, JSOp op) -{ - TraceMonitor* tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx); - - JS_ASSERT(tm == traceMonitor); - JS_ASSERT(&entryScript->compartment->traceMonitor == tm); - - if (profiled) { - stopProfiling(cx); - return ProfComplete; - } - - jsbytecode *pc = cx->regs().pc; - StackFrame *fp = cx->fp(); - JSScript *script = fp->script(); - - if (!PCWithinLoop(fp, pc, *this)) { - debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at line %u\n", - js_FramePCToLineNumber(cx, cx->fp())); - tm->profile->decide(cx); - stopProfiling(cx); - return ProfComplete; - } - - while (loopStackDepth > 0 && !PCWithinLoop(fp, pc, loopStack[loopStackDepth-1])) { - debug_only_print0(LC_TMProfiler, "Profiler: Exiting inner loop\n"); - loopStackDepth--; - } - - if (op == JSOP_TRACE || op == JSOP_NOTRACE) { - if (pc != top && (loopStackDepth == 0 || pc != loopStack[loopStackDepth-1].top)) { - if (loopStackDepth == PROFILE_MAX_INNER_LOOPS) { - debug_only_print0(LC_TMProfiler, "Profiling complete (maxnest)\n"); - tm->profile->decide(cx); - stopProfiling(cx); - return ProfComplete; - } - - debug_only_printf(LC_TMProfiler, "Profiler: Entering inner loop at line %d\n", - js_FramePCToLineNumber(cx, cx->fp())); - loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx)); - } - } - - numAllOps++; - if (loopStackDepth == 0) { - numSelfOps++; - numSelfOpsMult += branchMultiplier; - } - - if (op == JSOP_ADD || op == JSOP_SUB || op == JSOP_MUL || op == JSOP_DIV) { - Value& v1 = cx->regs().sp[-1]; - Value& v2 = cx->regs().sp[-2]; - - /* If either operand is a double, treat it as a floating-point op. */ - if (v1.isDouble() || v2.isDouble()) - increment(OP_FLOAT); - else if (v1.isInt32() || v2.isInt32()) - increment(OP_INT); - } - - if (op == JSOP_EQ || op == JSOP_NE) - increment(OP_EQ); - - if (op == JSOP_BITOR || op == JSOP_BITXOR || op == JSOP_BITAND - || op == JSOP_LSH || op == JSOP_RSH || op == JSOP_URSH || op == JSOP_BITNOT) - { - increment(OP_BIT); - } - - if (op == JSOP_EVAL) - increment(OP_EVAL); - - if (op == JSOP_NEW) - increment(OP_NEW); - - if (op == JSOP_GETELEM || op == JSOP_SETELEM) { - Value& lval = cx->regs().sp[op == JSOP_GETELEM ? -2 : -3]; - if (lval.isObject() && js_IsTypedArray(&lval.toObject())) - increment(OP_TYPED_ARRAY); - else if (lval.isObject() && lval.toObject().isDenseArray() && op == JSOP_GETELEM) - increment(OP_ARRAY_READ); - } - - if (op == JSOP_GETPROP || op == JSOP_CALLPROP || - op == JSOP_GETARGPROP || op == JSOP_GETLOCALPROP) - { - /* Try to see if it's a scripted getter, which is faster in the tracer. */ - Value v = UndefinedValue(); - if (op == JSOP_GETPROP || op == JSOP_CALLPROP) { - v = cx->regs().sp[-1]; - } else if (op == JSOP_GETARGPROP) { - uint32 slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->numFormalArgs()); - v = fp->formalArg(slot); - } else if (op == JSOP_GETLOCALPROP) { - uint32 slot = GET_SLOTNO(pc); - JS_ASSERT(slot < script->nslots); - v = fp->slots()[slot]; - } else { - JS_NOT_REACHED("no else"); - } - - if (v.isObject()) { - JSObject *aobj = js_GetProtoIfDenseArray(&v.toObject()); - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, pc, aobj, obj2, entry, atom); - if (!atom && entry->vword.isShape()) { - const Shape *shape = entry->vword.toShape(); - if (shape->hasGetterValue()) - increment(OP_SCRIPTED_GETTER); - } - } - } - - if (op == JSOP_CALL) { - increment(OP_CALL); - - uintN argc = GET_ARGC(cx->regs().pc); - Value &v = cx->regs().sp[-((int)argc + 2)]; - JSObject *callee; - if (IsFunctionObject(v, &callee)) { - JSFunction *fun = callee->getFunctionPrivate(); - if (fun->isInterpreted()) { - if (cx->fp()->isFunctionFrame() && fun == cx->fp()->fun()) - increment(OP_RECURSIVE); - } else { - js::Native native = fun->u.n.native; - if (js_IsMathFunction(JS_JSVALIFY_NATIVE(native))) - increment(OP_FLOAT); - } - } - } - - if (op == JSOP_CALLPROP && loopStackDepth == 0) - branchMultiplier *= mjit::GetCallTargetCount(script, pc); - - if (op == JSOP_TABLESWITCH) { - jsint low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN); - jsint high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN*2); - branchMultiplier *= high - low + 1; - } - - if (op == JSOP_LOOKUPSWITCH) - branchMultiplier *= GET_UINT16(pc + JUMP_OFFSET_LEN); - - if (numAllOps >= MAX_PROFILE_OPS) { - debug_only_print0(LC_TMProfiler, "Profiling complete (maxops)\n"); - tm->profile->decide(cx); - stopProfiling(cx); - return ProfComplete; - } - - /* These are the places where the interpreter skips over branches. */ - jsbytecode *testPC = cx->regs().pc; - if (op == JSOP_EQ || op == JSOP_NE || op == JSOP_LT || op == JSOP_GT - || op == JSOP_LE || op == JSOP_GE || op == JSOP_IN || op == JSOP_MOREITER) - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - ptrdiff_t oplen = cs->length; - JS_ASSERT(oplen != -1); - - if (cx->regs().pc - script->code + oplen < ptrdiff_t(script->length)) - if (cx->regs().pc[oplen] == JSOP_IFEQ || cx->regs().pc[oplen] == JSOP_IFNE) - testPC = cx->regs().pc + oplen; - } - - /* Check if we're exiting the loop being profiled. */ - JSOp testOp = js_GetOpcode(cx, script, testPC); - if (testOp == JSOP_IFEQ || testOp == JSOP_IFNE || testOp == JSOP_GOTO - || testOp == JSOP_AND || testOp == JSOP_OR) - { - ptrdiff_t len = GET_JUMP_OFFSET(testPC); - if (testPC + len == top && (op == JSOP_LT || op == JSOP_LE)) { - StackValue v = stackAt(-1); - if (v.hasValue && v.value < 8) - shortLoop = true; - } - - if (testPC + len == top && (op == JSOP_LT || op == JSOP_LE) - && cx->regs().sp[-2].isInt32() && cx->regs().sp[-2].toInt32() < 16) - { - maybeShortLoop = true; - } - - if (testOp != JSOP_GOTO && len > 0) { - bool isConst; - if (testOp == JSOP_IFEQ || testOp == JSOP_IFNE) - isConst = stackAt(-1).isConst && stackAt(-2).isConst; - else - isConst = stackAt(-1).isConst; - - increment(OP_FWDJUMP); - if (loopStackDepth == 0 && !isConst) - branchMultiplier *= 2; - } - } - - if (op == JSOP_INT8) { - stackPush(StackValue(true, GET_INT8(cx->regs().pc))); - } else if (op == JSOP_STRING) { - stackPush(StackValue(true)); - } else if (op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR) { - stackPush(StackValue(true)); - } else if (op == JSOP_EQ || op == JSOP_NE) { - StackValue v1 = stackAt(-1); - StackValue v2 = stackAt(-2); - stackPush(StackValue(v1.isConst && v2.isConst)); - } else if (op == JSOP_AND) { - bool b = !!js_ValueToBoolean(cx->regs().sp[-1]); - StackValue v = stackAt(-1); - if (b) - stackPop(); - } else { - stackClear(); - } - - return ProfContinue; -} - -/* - * Returns true if the loop would probably take a long time to - * compile. - */ -bool -LoopProfile::isCompilationExpensive(JSContext *cx, uintN depth) -{ - if (depth == 0) - return true; - - if (!profiled) - return false; - - /* Too many ops to compile? */ - if (numSelfOps == MAX_PROFILE_OPS) - return true; - - /* Is the code too branchy? */ - if (numSelfOpsMult > numSelfOps*100000) - return true; - - /* Ensure that inner loops aren't too expensive. */ - for (uintN i=0; iisCompilationExpensive(cx, depth-1)) - return true; - } - - return false; -} - -/* - * This function recognizes loops that are short and that contain - * jumps. The tracer does badly with these loops because it - * needs to do a lot of side exits, which are somewhat - * expensive. - */ -bool -LoopProfile::isCompilationUnprofitable(JSContext *cx, uintN goodOps) -{ - if (!profiled) - return false; - - if (goodOps <= 22 && allOps[OP_FWDJUMP]) - return true; - - /* Ensure that inner loops aren't fleeting. */ - for (uintN i=0; iunprofitable) - return true; - } - - return false; -} - -/* After profiling is done, this method decides whether to trace the loop. */ -void -LoopProfile::decide(JSContext *cx) -{ - bool wasUndecided = undecided; - bool wasTraceOK = traceOK; - - profiled = true; - traceOK = false; - undecided = false; - -#ifdef DEBUG - uintN line = js_PCToLineNumber(cx, entryScript, top); - - debug_only_printf(LC_TMProfiler, "LOOP %s:%d\n", entryScript->filename, line); - - for (uintN i=0; ientryScript, prof->top); - debug_only_printf(LC_TMProfiler, "NESTED %s:%d (%d iters)\n", - prof->entryScript->filename, line, loop.iters); - } - } - debug_only_printf(LC_TMProfiler, "FEATURE float %d\n", allOps[OP_FLOAT]); - debug_only_printf(LC_TMProfiler, "FEATURE int %d\n", allOps[OP_INT]); - debug_only_printf(LC_TMProfiler, "FEATURE bit %d\n", allOps[OP_BIT]); - debug_only_printf(LC_TMProfiler, "FEATURE equality %d\n", allOps[OP_EQ]); - debug_only_printf(LC_TMProfiler, "FEATURE eval %d\n", allOps[OP_EVAL]); - debug_only_printf(LC_TMProfiler, "FEATURE new %d\n", allOps[OP_NEW]); - debug_only_printf(LC_TMProfiler, "FEATURE call %d\n", allOps[OP_CALL]); - debug_only_printf(LC_TMProfiler, "FEATURE arrayread %d\n", allOps[OP_ARRAY_READ]); - debug_only_printf(LC_TMProfiler, "FEATURE typedarray %d\n", allOps[OP_TYPED_ARRAY]); - debug_only_printf(LC_TMProfiler, "FEATURE scriptedgetter %d\n", allOps[OP_SCRIPTED_GETTER]); - debug_only_printf(LC_TMProfiler, "FEATURE fwdjump %d\n", allOps[OP_FWDJUMP]); - debug_only_printf(LC_TMProfiler, "FEATURE recursive %d\n", allOps[OP_RECURSIVE]); - debug_only_printf(LC_TMProfiler, "FEATURE shortLoop %d\n", shortLoop); - debug_only_printf(LC_TMProfiler, "FEATURE maybeShortLoop %d\n", maybeShortLoop); - debug_only_printf(LC_TMProfiler, "FEATURE numAllOps %d\n", numAllOps); - debug_only_printf(LC_TMProfiler, "FEATURE selfOps %d\n", numSelfOps); - debug_only_printf(LC_TMProfiler, "FEATURE selfOpsMult %g\n", numSelfOpsMult); -#endif - - if (count(OP_RECURSIVE)) { - debug_only_print0(LC_TMProfiler, "NOTRACE: recursive\n"); - } else if (count(OP_EVAL)) { - debug_only_print0(LC_TMProfiler, "NOTRACE: eval\n"); - } else if (numInnerLoops > 7) { - debug_only_print0(LC_TMProfiler, "NOTRACE: >3 inner loops\n"); - } else if (shortLoop) { - debug_only_print0(LC_TMProfiler, "NOTRACE: short\n"); - } else if (isCompilationExpensive(cx, 4)) { - debug_only_print0(LC_TMProfiler, "NOTRACE: expensive\n"); - } else if (maybeShortLoop && numInnerLoops < 2) { - if (wasUndecided) { - debug_only_print0(LC_TMProfiler, "NOTRACE: maybe short\n"); - } else { - debug_only_print0(LC_TMProfiler, "UNDECIDED: maybe short\n"); - undecided = true; /* Profile the loop again to see if it's still short. */ - } - } else { - uintN goodOps = 0; - - /* The tracer handles these ops well because of type specialization. */ - goodOps += count(OP_FLOAT)*10 + count(OP_BIT)*11 + count(OP_INT)*5 + count(OP_EQ)*15; - - /* The tracer handles these ops well because of inlining. */ - goodOps += (count(OP_CALL) + count(OP_NEW))*20; - - /* The tracer specializes typed array access. */ - goodOps += count(OP_TYPED_ARRAY)*10; - - /* The tracer traces scripted getters. */ - goodOps += count(OP_SCRIPTED_GETTER)*40; - - /* The methodjit is faster at array writes, but the tracer is faster for reads. */ - goodOps += count(OP_ARRAY_READ)*15; - - debug_only_printf(LC_TMProfiler, "FEATURE goodOps %u\n", goodOps); - - unprofitable = isCompilationUnprofitable(cx, goodOps); - if (unprofitable) - debug_only_print0(LC_TMProfiler, "NOTRACE: unprofitable\n"); - else if (goodOps >= numAllOps) - traceOK = true; - } - - debug_only_printf(LC_TMProfiler, "TRACE %s:%d = %d\n", entryScript->filename, line, traceOK); - - if (traceOK) { - /* Unblacklist the inner loops. */ - for (uintN i=0; itraceOK = true; - if (IsBlacklisted(loop.top)) { - debug_only_printf(LC_TMProfiler, "Unblacklisting at %d\n", - js_PCToLineNumber(cx, prof->entryScript, loop.top)); - Unblacklist(prof->entryScript, loop.top); - } - } - } - } - - execOK = traceOK; - traceOK = wasTraceOK || traceOK; - - if (!traceOK && !undecided) { - debug_only_printf(LC_TMProfiler, "Blacklisting at %d\n", line); - Blacklist(top); - } - - debug_only_print0(LC_TMProfiler, "\n"); -} - -JS_REQUIRES_STACK MonitorResult -MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx); - if (interpMode == JSINTERP_PROFILE && tm->profile) - return tm->profile->profileLoopEdge(cx, inlineCallCount); - else - return RecordLoopEdge(cx, tm, inlineCallCount); -} - -void -AbortProfiling(JSContext *cx) -{ - JS_ASSERT(TRACE_PROFILER(cx)); - LoopProfile *prof = TRACE_PROFILER(cx); - - debug_only_print0(LC_TMProfiler, "Profiling complete (aborted)\n"); - prof->profiled = true; - prof->traceOK = false; - prof->execOK = false; - prof->stopProfiling(cx); -} - -#else /* JS_METHODJIT */ - -JS_REQUIRES_STACK MonitorResult -MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode) -{ - TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx); - return RecordLoopEdge(cx, tm, inlineCallCount); -} - -#endif /* JS_METHODJIT */ - -uint32 -GetHotloop(JSContext *cx) -{ -#ifdef JS_METHODJIT - if (cx->profilingEnabled) - return PROFILE_HOTLOOP; - else -#endif - return 1; -} - -} /* namespace js */ - diff --git a/deps/mozjs/js/src/jstracer.h b/deps/mozjs/js/src/jstracer.h deleted file mode 100644 index 8985dd9fd93..00000000000 --- a/deps/mozjs/js/src/jstracer.h +++ /dev/null @@ -1,1888 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * May 28, 2008. - * - * The Initial Developer of the Original Code is - * Brendan Eich - * - * Contributor(s): - * Andreas Gal - * Mike Shaver - * David Anderson - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jstracer_h___ -#define jstracer_h___ - -#ifdef JS_TRACER - -#include "jstypes.h" -#include "jsbuiltins.h" -#include "jscntxt.h" -#include "jsdhash.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsvector.h" -#include "jscompartment.h" -#include "Writer.h" - -namespace js { - -template -class Queue { - T* _data; - unsigned _len; - unsigned _max; - nanojit::Allocator* alloc; - -public: - void ensure(unsigned size) { - if (_max > size) - return; - if (!_max) - _max = 8; - _max = JS_MAX(_max * 2, size); - if (alloc) { - T* tmp = new (*alloc) T[_max]; - memcpy(tmp, _data, _len * sizeof(T)); - _data = tmp; - } else { - _data = (T*) js::OffTheBooks::realloc_(_data, _max * sizeof(T)); - } -#if defined(DEBUG) - memset(&_data[_len], 0xcd, _max - _len); -#endif - } - - Queue(nanojit::Allocator* alloc) - : alloc(alloc) - { - this->_max = - this->_len = 0; - this->_data = NULL; - } - - ~Queue() { - if (!alloc) - js::UnwantedForeground::free_(_data); - } - - bool contains(T a) { - for (unsigned n = 0; n < _len; ++n) { - if (_data[n] == a) - return true; - } - return false; - } - - void add(T a) { - ensure(_len + 1); - JS_ASSERT(_len <= _max); - _data[_len++] = a; - } - - void add(T* chunk, unsigned size) { - ensure(_len + size); - JS_ASSERT(_len <= _max); - memcpy(&_data[_len], chunk, size * sizeof(T)); - _len += size; - } - - void addUnique(T a) { - if (!contains(a)) - add(a); - } - - void setLength(unsigned len) { - ensure(len + 1); - _len = len; - } - - void clear() { - _len = 0; - } - - T & get(unsigned i) { - JS_ASSERT(i < length()); - return _data[i]; - } - - const T & get(unsigned i) const { - JS_ASSERT(i < length()); - return _data[i]; - } - - T & operator [](unsigned i) { - return get(i); - } - - const T & operator [](unsigned i) const { - return get(i); - } - - unsigned length() const { - return _len; - } - - T* data() const { - return _data; - } - - int offsetOf(T slot) { - T* p = _data; - unsigned n = 0; - for (n = 0; n < _len; ++n) - if (*p++ == slot) - return n; - return -1; - } - -}; - -/* - * Tracker is used to keep track of values being manipulated by the interpreter - * during trace recording. It maps opaque, 4-byte aligned address to LIns pointers. - * pointers. To do this efficiently, we observe that the addresses of jsvals - * living in the interpreter tend to be aggregated close to each other - - * usually on the same page (where a tracker page doesn't have to be the same - * size as the OS page size, but it's typically similar). The Tracker - * consists of a linked-list of structures representing a memory page, which - * are created on-demand as memory locations are used. - * - * For every address, first we split it into two parts: upper bits which - * represent the "base", and lower bits which represent an offset against the - * base. For the offset, we then right-shift it by two because the bottom two - * bits of a 4-byte aligned address are always zero. The mapping then - * becomes: - * - * page = page in pagelist such that Base(address) == page->base, - * page->map[Offset(address)] - */ -class Tracker { - #define TRACKER_PAGE_SZB 4096 - #define TRACKER_PAGE_ENTRIES (TRACKER_PAGE_SZB >> 2) // each slot is 4 bytes - #define TRACKER_PAGE_MASK jsuword(TRACKER_PAGE_SZB - 1) - - struct TrackerPage { - struct TrackerPage* next; - jsuword base; - nanojit::LIns* map[TRACKER_PAGE_ENTRIES]; - }; - struct TrackerPage* pagelist; - - /* Keep track of memory allocation. */ - JSContext* cx; - - jsuword getTrackerPageBase(const void* v) const; - jsuword getTrackerPageOffset(const void* v) const; - struct TrackerPage* findTrackerPage(const void* v) const; - struct TrackerPage* addTrackerPage(const void* v); -public: - Tracker(JSContext* cx); - ~Tracker(); - - bool has(const void* v) const; - nanojit::LIns* get(const void* v) const; - void set(const void* v, nanojit::LIns* ins); - void clear(); -}; - -class VMFragment : public nanojit::Fragment { -public: - VMFragment(const void* _ip verbose_only(, uint32_t profFragID)) - : Fragment(_ip verbose_only(, profFragID)) - {} - - /* - * If this is anchored off a TreeFragment, this points to that tree fragment. - * Otherwise, it is |this|. - */ - TreeFragment* root; - - TreeFragment* toTreeFragment(); -}; - -#if defined(JS_JIT_SPEW) - -// Top level Nanojit config object. -extern nanojit::Config NJConfig; - -// Top level logging controller object. -extern nanojit::LogControl LogController; - -// Top level profiling hook, needed to harvest profile info from Fragments -// whose logical lifetime is about to finish -extern void FragProfiling_FragFinalizer(nanojit::Fragment* f, TraceMonitor*); - -#define debug_only_stmt(stmt) \ - stmt - -#define debug_only_printf(mask, fmt, ...) \ - JS_BEGIN_MACRO \ - if ((LogController.lcbits & (mask)) > 0) { \ - LogController.printf(fmt, __VA_ARGS__); \ - fflush(stdout); \ - } \ - JS_END_MACRO - -#define debug_only_print0(mask, str) \ - JS_BEGIN_MACRO \ - if ((LogController.lcbits & (mask)) > 0) { \ - LogController.printf("%s", str); \ - fflush(stdout); \ - } \ - JS_END_MACRO - -#else - -#define debug_only_stmt(action) /* */ -#define debug_only_printf(mask, fmt, ...) JS_BEGIN_MACRO JS_END_MACRO -#define debug_only_print0(mask, str) JS_BEGIN_MACRO JS_END_MACRO - -#endif - -/* - * The oracle keeps track of hit counts for program counter locations, as - * well as slots that should not be demoted to int because we know them to - * overflow or they result in type-unstable traces. We are using simple - * hash tables. Collisions lead to loss of optimization (demotable slots - * are not demoted, etc.) but have no correctness implications. - */ -#define ORACLE_SIZE 4096 - -class Oracle { - avmplus::BitSet _stackDontDemote; - avmplus::BitSet _globalDontDemote; - avmplus::BitSet _pcDontDemote; - avmplus::BitSet _pcSlowZeroTest; -public: - Oracle(); - - JS_REQUIRES_STACK void markGlobalSlotUndemotable(JSContext* cx, unsigned slot); - JS_REQUIRES_STACK bool isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const; - JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot); - JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc); - JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot) const; - JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) const; - void markInstructionUndemotable(jsbytecode* pc); - bool isInstructionUndemotable(jsbytecode* pc) const; - void markInstructionSlowZeroTest(jsbytecode* pc); - bool isInstructionSlowZeroTest(jsbytecode* pc) const; - - void clearDemotability(); - void clear() { - clearDemotability(); - } -}; - -typedef Queue SlotList; - -class TypeMap : public Queue { - Oracle *oracle; -public: - TypeMap(nanojit::Allocator* alloc, Oracle *oracle) - : Queue(alloc), - oracle(oracle) - {} - void set(unsigned stackSlots, unsigned ngslots, - const JSValueType* stackTypeMap, const JSValueType* globalTypeMap); - JS_REQUIRES_STACK void captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth, - bool speculate); - JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, - unsigned stackSlots, bool speculate); - bool matches(TypeMap& other) const; - void fromRaw(JSValueType* other, unsigned numSlots); -}; - -#define JS_TM_EXITCODES(_) \ - /* \ - * An exit at a possible branch-point in the trace at which to attach a \ - * future secondary trace. Therefore the recorder must generate different \ - * code to handle the other outcome of the branch condition from the \ - * primary trace's outcome. \ - */ \ - _(BRANCH) \ - _(LOOP) \ - _(NESTED) \ - /* \ - * An exit from a trace because a condition relied upon at recording time \ - * no longer holds, where the alternate path of execution is so rare or \ - * difficult to address in native code that it is not traced at all, e.g. \ - * negative array index accesses, which differ from positive indexes in \ - * that they require a string-based property lookup rather than a simple \ - * memory access. \ - */ \ - _(MISMATCH) \ - /* \ - * A specialization of MISMATCH_EXIT to handle allocation failures. \ - */ \ - _(OOM) \ - _(OVERFLOW) \ - _(MUL_ZERO) \ - _(UNSTABLE_LOOP) \ - _(TIMEOUT) \ - _(DEEP_BAIL) \ - _(STATUS) - -enum ExitType { - #define MAKE_EXIT_CODE(x) x##_EXIT, - JS_TM_EXITCODES(MAKE_EXIT_CODE) - #undef MAKE_EXIT_CODE - TOTAL_EXIT_TYPES -}; - -struct FrameInfo; - -struct VMSideExit : public nanojit::SideExit -{ - jsbytecode* pc; - jsbytecode* imacpc; - intptr_t sp_adj; - intptr_t rp_adj; - int32_t calldepth; - uint32 numGlobalSlots; - uint32 numStackSlots; - uint32 numStackSlotsBelowCurrentFrame; - ExitType exitType; - uintN lookupFlags; - unsigned hitcount; - - inline JSValueType* stackTypeMap() { - return (JSValueType*)(this + 1); - } - - inline JSValueType& stackType(unsigned i) { - JS_ASSERT(i < numStackSlots); - return stackTypeMap()[i]; - } - - inline JSValueType* globalTypeMap() { - return (JSValueType*)(this + 1) + this->numStackSlots; - } - - inline JSValueType* fullTypeMap() { - return stackTypeMap(); - } - - inline VMFragment* fromFrag() { - return (VMFragment*)from; - } - - inline TreeFragment* root() { - return fromFrag()->root; - } -}; - -class VMAllocator : public nanojit::Allocator -{ -public: - VMAllocator(JSRuntime *rt, char* reserve, size_t reserveSize) - : mOutOfMemory(false) - , mSize(0) - , mReserve(reserve) - , mReserveSize(reserveSize) - , mReserveCurr(uintptr_t(reserve)) - , mReserveLimit(uintptr_t(reserve + reserveSize)) - , mRt(rt) - {} - - ~VMAllocator() { - js::UnwantedForeground::free_(mReserve); - } - - size_t size() { - return mSize; - } - - bool outOfMemory() { - return mOutOfMemory; - } - - struct Mark - { - VMAllocator& vma; - bool committed; - nanojit::Allocator::Chunk* saved_chunk; - char* saved_top; - char* saved_limit; - size_t saved_size; - - Mark(VMAllocator& vma) : - vma(vma), - committed(false), - saved_chunk(vma.current_chunk), - saved_top(vma.current_top), - saved_limit(vma.current_limit), - saved_size(vma.mSize) - {} - - ~Mark() - { - if (!committed) - vma.rewind(*this); - } - - void commit() { committed = true; } - }; - - void rewind(const Mark& m) { - while (current_chunk != m.saved_chunk) { - Chunk *prev = current_chunk->prev; - freeChunk(current_chunk); - current_chunk = prev; - } - current_top = m.saved_top; - current_limit = m.saved_limit; - mSize = m.saved_size; - memset(current_top, 0, current_limit - current_top); - } - - bool mOutOfMemory; - size_t mSize; - - /* See nanojit::Allocator::allocChunk() for details on these. */ - char* mReserve; - size_t mReserveSize; - uintptr_t mReserveCurr; - uintptr_t mReserveLimit; - - /* To keep track of allocation. */ - JSRuntime* mRt; -}; - -struct FrameInfo { - JSObject* block; // caller block chain head - jsbytecode* pc; // caller fp->regs->pc - jsbytecode* imacpc; // caller fp->imacpc - uint32 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL - - /* - * Bit 15 (0x8000) is a flag that is set if constructing (called through new). - * Bits 0-14 are the actual argument count. This may be less than fun->nargs. - * NB: This is argc for the callee, not the caller. - */ - uint32 argc; - - /* - * Number of stack slots in the caller, not counting slots pushed when - * invoking the callee. That is, slots after JSOP_CALL completes but - * without the return value. This is also equal to the number of slots - * between fp->prev->argv[-2] (calleR fp->callee) and fp->argv[-2] - * (calleE fp->callee). - */ - uint32 callerHeight; - - /* argc of the caller */ - uint32 callerArgc; - - // Safer accessors for argc. - enum { CONSTRUCTING_FLAG = 0x10000 }; - void set_argc(uint16 argc, bool constructing) { - this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0); - } - uint16 get_argc() const { return uint16(argc & ~CONSTRUCTING_FLAG); } - bool is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; } - - // The typemap just before the callee is called. - JSValueType* get_typemap() { return (JSValueType*) (this+1); } - const JSValueType* get_typemap() const { return (JSValueType*) (this+1); } -}; - -struct UnstableExit -{ - VMFragment* fragment; - VMSideExit* exit; - UnstableExit* next; -}; - -struct LinkableFragment : public VMFragment -{ - LinkableFragment(const void* _ip, nanojit::Allocator* alloc, Oracle *oracle - verbose_only(, uint32_t profFragID)) - : VMFragment(_ip verbose_only(, profFragID)), typeMap(alloc, oracle), nStackTypes(0) - { } - - uint32 branchCount; - TypeMap typeMap; - unsigned nStackTypes; - unsigned spOffsetAtEntry; - SlotList* globalSlots; -}; - -/* - * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments - * pushed for the innermost JS frame. This is required as part of the fragment - * key because the fragment will write those arguments back to the interpreter - * stack when it exits, using its typemap, which implicitly incorporates a - * given value of argc. Without this feature, a fragment could be called as an - * inner tree with two different values of argc, and entry type checking or - * exit frame synthesis could crash. - */ -struct TreeFragment : public LinkableFragment -{ - TreeFragment(const void* _ip, nanojit::Allocator* alloc, Oracle *oracle, JSObject* _globalObj, - uint32 _globalShape, uint32 _argc verbose_only(, uint32_t profFragID)): - LinkableFragment(_ip, alloc, oracle verbose_only(, profFragID)), - first(NULL), - next(NULL), - peer(NULL), - globalObj(_globalObj), - globalShape(_globalShape), - argc(_argc), - dependentTrees(alloc), - linkedTrees(alloc), - sideExits(alloc), - gcthings(alloc), - shapes(alloc) - { } - - TreeFragment* first; - TreeFragment* next; - TreeFragment* peer; - JSObject* globalObj; - uint32 globalShape; - uint32 argc; - /* Dependent trees must be trashed if this tree dies, and updated on missing global types */ - Queue dependentTrees; - /* Linked trees must be updated on missing global types, but are not dependent */ - Queue linkedTrees; -#ifdef DEBUG - const char* treeFileName; - uintN treeLineNumber; - uintN treePCOffset; -#endif - JSScript* script; - UnstableExit* unstableExits; - Queue sideExits; - ptrdiff_t nativeStackBase; - unsigned maxCallDepth; - /* All embedded GC things are registered here so the GC can scan them. */ - Queue gcthings; - Queue shapes; - unsigned maxNativeStackSlots; - /* Gives the number of times we have entered this trace. */ - uintN execs; - /* Gives the total number of iterations executed by the trace (up to a limit). */ - uintN iters; - - inline unsigned nGlobalTypes() { - return typeMap.length() - nStackTypes; - } - inline JSValueType* globalTypeMap() { - return typeMap.data() + nStackTypes; - } - inline JSValueType* stackTypeMap() { - return typeMap.data(); - } - - JS_REQUIRES_STACK void initialize(JSContext* cx, SlotList *globalSlots, bool speculate); - UnstableExit* removeUnstableExit(VMSideExit* exit); -}; - -inline TreeFragment* -VMFragment::toTreeFragment() -{ - JS_ASSERT(root == this); - return static_cast(this); -} - -enum MonitorResult { - MONITOR_RECORDING, - MONITOR_NOT_RECORDING, - MONITOR_ERROR -}; - -const uintN PROFILE_MAX_INNER_LOOPS = 8; -const uintN PROFILE_MAX_STACK = 6; - -/* - * A loop profile keeps track of the instruction mix of a hot loop. We use this - * information to predict whether tracing would be beneficial for the loop. - */ -class LoopProfile -{ -public: - /* Instructions are divided into a few categories. */ - enum OpKind { - OP_FLOAT, // Floating point arithmetic - OP_INT, // Integer arithmetic - OP_BIT, // Bit operations - OP_EQ, // == and != - OP_EVAL, // Calls to eval() - OP_CALL, // JSOP_CALL instructions - OP_FWDJUMP, // Jumps with positive delta - OP_NEW, // JSOP_NEW instructions - OP_RECURSIVE, // Recursive calls - OP_ARRAY_READ, // Reads from dense arrays - OP_TYPED_ARRAY, // Accesses to typed arrays - OP_SCRIPTED_GETTER, // Getters defined in JS - OP_LIMIT - }; - - /* The TraceMonitor for which we're profiling. */ - TraceMonitor *traceMonitor; - - /* The script in which the loop header lives. */ - JSScript *entryScript; - - /* The stack frame where we started profiling. Only valid while profiling! */ - StackFrame *entryfp; - - /* The bytecode locations of the loop header and the back edge. */ - jsbytecode *top, *bottom; - - /* Number of times we have seen this loop executed; used to decide when to profile. */ - uintN hits; - - /* Whether we have run a complete profile of the loop. */ - bool profiled; - - /* Sometimes we can't decide in one profile run whether to trace, so we set undecided. */ - bool undecided; - - /* If we have profiled the loop, this saves the decision of whether to trace it. */ - bool traceOK; - - /* Memoized value of isCompilationUnprofitable. */ - bool unprofitable; - - /* - * Sometimes loops are not good tracing opportunities, but they are nested inside - * loops that we want to trace. In that case, we set their traceOK flag to true, - * but we set execOK to false. That way, the loop is traced so that it can be - * integrated into the outer trace. But we never execute the trace on its only. - */ - bool execOK; - - /* Instruction mix for the loop and total number of instructions. */ - uintN allOps[OP_LIMIT]; - uintN numAllOps; - - /* Instruction mix and total for the loop, excluding nested inner loops. */ - uintN selfOps[OP_LIMIT]; - uintN numSelfOps; - - /* - * A prediction of the number of instructions we would have to compile - * for the loop. This takes into account the fact that a branch may cause us to - * compile every instruction after it twice. Polymorphic calls are - * treated as n-way branches. - */ - double numSelfOpsMult; - - /* - * This keeps track of the number of times that every succeeding instruction - * in the trace will have to be compiled. Every time we hit a branch, we - * double this number. Polymorphic calls multiply it by n (for n-way - * polymorphism). - */ - double branchMultiplier; - - /* Set to true if the loop is short (i.e., has fewer than 8 iterations). */ - bool shortLoop; - - /* Set to true if the loop may be short (has few iterations at profiling time). */ - bool maybeShortLoop; - - /* - * When we hit a nested loop while profiling, we record where it occurs - * and how many iterations we execute it. - */ - struct InnerLoop { - StackFrame *entryfp; - jsbytecode *top, *bottom; - uintN iters; - - InnerLoop() {} - InnerLoop(StackFrame *entryfp, jsbytecode *top, jsbytecode *bottom) - : entryfp(entryfp), top(top), bottom(bottom), iters(0) {} - }; - - /* These two variables track all the inner loops seen while profiling (up to a limit). */ - InnerLoop innerLoops[PROFILE_MAX_INNER_LOOPS]; - uintN numInnerLoops; - - /* - * These two variables track the loops that we are currently nested - * inside while profiling. Loops get popped off here when they exit. - */ - InnerLoop loopStack[PROFILE_MAX_INNER_LOOPS]; - uintN loopStackDepth; - - /* - * These fields keep track of values on the JS stack. If the stack grows larger - * than PROFILE_MAX_STACK, we continue to track sp, but we return conservative results - * for stackTop(). - */ - struct StackValue { - bool isConst; - bool hasValue; - int value; - - StackValue() : isConst(false), hasValue(false) {} - StackValue(bool isConst) : isConst(isConst), hasValue(false) {} - StackValue(bool isConst, int value) : isConst(isConst), hasValue(true), value(value) {} - }; - StackValue stack[PROFILE_MAX_STACK]; - uintN sp; - - inline void stackClear() { sp = 0; } - - inline void stackPush(const StackValue &v) { - if (sp < PROFILE_MAX_STACK) - stack[sp++] = v; - else - stackClear(); - } - - inline void stackPop() { if (sp > 0) sp--; } - - inline StackValue stackAt(int pos) { - pos += sp; - if (pos >= 0 && uintN(pos) < PROFILE_MAX_STACK) - return stack[pos]; - else - return StackValue(false); - } - - LoopProfile(TraceMonitor *tm, StackFrame *entryfp, jsbytecode *top, jsbytecode *bottom); - - void reset(); - - enum ProfileAction { - ProfContinue, - ProfComplete - }; - - /* These two functions track the instruction mix. */ - inline void increment(OpKind kind) - { - allOps[kind]++; - if (loopStackDepth == 0) - selfOps[kind]++; - } - - inline uintN count(OpKind kind) { return allOps[kind]; } - - /* Called for every back edge being profiled. */ - MonitorResult profileLoopEdge(JSContext* cx, uintN& inlineCallCount); - - /* Called for every instruction being profiled. */ - ProfileAction profileOperation(JSContext *cx, JSOp op); - - /* Once a loop's profile is done, these decide whether it should be traced. */ - bool isCompilationExpensive(JSContext *cx, uintN depth); - bool isCompilationUnprofitable(JSContext *cx, uintN goodOps); - void decide(JSContext *cx); - - void stopProfiling(JSContext *cx); -}; - -/* - * BUILTIN_NO_FIXUP_NEEDED indicates that after the initial LeaveTree of a deep - * bail, the builtin call needs no further fixup when the trace exits and calls - * LeaveTree the second time. - */ -typedef enum BuiltinStatus { - BUILTIN_BAILED = 1, - BUILTIN_ERROR = 2 -} BuiltinStatus; - -static JS_INLINE void -SetBuiltinError(TraceMonitor *tm) -{ - tm->tracerState->builtinStatus |= BUILTIN_ERROR; -} - -static JS_INLINE bool -WasBuiltinSuccessful(TraceMonitor *tm) -{ - return tm->tracerState->builtinStatus == 0; -} - -#ifdef DEBUG_RECORDING_STATUS_NOT_BOOL -/* #define DEBUG_RECORDING_STATUS_NOT_BOOL to detect misuses of RecordingStatus */ -struct RecordingStatus { - int code; - bool operator==(RecordingStatus &s) { return this->code == s.code; }; - bool operator!=(RecordingStatus &s) { return this->code != s.code; }; -}; -enum RecordingStatusCodes { - RECORD_ERROR_code = 0, - RECORD_STOP_code = 1, - - RECORD_CONTINUE_code = 3, - RECORD_IMACRO_code = 4 -}; -RecordingStatus RECORD_CONTINUE = { RECORD_CONTINUE_code }; -RecordingStatus RECORD_STOP = { RECORD_STOP_code }; -RecordingStatus RECORD_IMACRO = { RECORD_IMACRO_code }; -RecordingStatus RECORD_ERROR = { RECORD_ERROR_code }; - -struct AbortableRecordingStatus { - int code; - bool operator==(AbortableRecordingStatus &s) { return this->code == s.code; }; - bool operator!=(AbortableRecordingStatus &s) { return this->code != s.code; }; -}; -enum AbortableRecordingStatusCodes { - ARECORD_ERROR_code = 0, - ARECORD_STOP_code = 1, - ARECORD_ABORTED_code = 2, - ARECORD_CONTINUE_code = 3, - ARECORD_IMACRO_code = 4, - ARECORD_IMACRO_ABORTED_code = 5, - ARECORD_COMPLETED_code = 6 -}; -AbortableRecordingStatus ARECORD_ERROR = { ARECORD_ERROR_code }; -AbortableRecordingStatus ARECORD_STOP = { ARECORD_STOP_code }; -AbortableRecordingStatus ARECORD_CONTINUE = { ARECORD_CONTINUE_code }; -AbortableRecordingStatus ARECORD_IMACRO = { ARECORD_IMACRO_code }; -AbortableRecordingStatus ARECORD_IMACRO_ABORTED = { ARECORD_IMACRO_ABORTED_code }; -AbortableRecordingStatus ARECORD_ABORTED = { ARECORD_ABORTED_code }; -AbortableRecordingStatus ARECORD_COMPLETED = { ARECORD_COMPLETED_code }; - -static inline AbortableRecordingStatus -InjectStatus(RecordingStatus rs) -{ - AbortableRecordingStatus ars = { rs.code }; - return ars; -} -static inline AbortableRecordingStatus -InjectStatus(AbortableRecordingStatus ars) -{ - return ars; -} - -static inline bool -StatusAbortsRecorderIfActive(AbortableRecordingStatus ars) -{ - return ars == ARECORD_ERROR || ars == ARECORD_STOP; -} -#else - -/* - * Normally, during recording, when the recorder cannot continue, it returns - * ARECORD_STOP to indicate that recording should be aborted by the top-level - * recording function. However, if the recorder reenters the interpreter (e.g., - * when executing an inner loop), there will be an immediate abort. This - * condition must be carefully detected and propagated out of all nested - * recorder calls lest the now-invalid TraceRecorder object be accessed - * accidentally. This condition is indicated by the ARECORD_ABORTED value. - * - * The AbortableRecordingStatus enumeration represents the general set of - * possible results of calling a recorder function. Functions that cannot - * possibly return ARECORD_ABORTED may statically guarantee this to the caller - * using the RecordingStatus enumeration. Ideally, C++ would allow subtyping - * of enumerations, but it doesn't. To simulate subtype conversion manually, - * code should call InjectStatus to inject a value of the restricted set into a - * value of the general set. - */ - -enum RecordingStatus { - RECORD_STOP = 0, // Recording should be aborted at the top-level - // call to the recorder. - RECORD_ERROR = 1, // Recording should be aborted at the top-level - // call to the recorder and the interpreter should - // goto error - RECORD_CONTINUE = 2, // Continue recording. - RECORD_IMACRO = 3 // Entered imacro; continue recording. - // Only JSOP_IS_IMACOP opcodes may return this. -}; - -enum AbortableRecordingStatus { - ARECORD_STOP = 0, // see RECORD_STOP - ARECORD_ERROR = 1, // Recording may or may not have been aborted. - // Recording should be aborted at the top-level - // if it has not already been and the interpreter - // should goto error - ARECORD_CONTINUE = 2, // see RECORD_CONTINUE - ARECORD_IMACRO = 3, // see RECORD_IMACRO - ARECORD_IMACRO_ABORTED = 4, // see comment in TR::monitorRecording. - ARECORD_ABORTED = 5, // Recording has already been aborted; the - // interpreter should continue executing - ARECORD_COMPLETED = 6 // Recording completed successfully, the - // trace recorder has been deleted -}; - -static JS_ALWAYS_INLINE AbortableRecordingStatus -InjectStatus(RecordingStatus rs) -{ - return static_cast(rs); -} - -static JS_ALWAYS_INLINE AbortableRecordingStatus -InjectStatus(AbortableRecordingStatus ars) -{ - return ars; -} - -/* - * Return whether the recording status requires the current recording session - * to be deleted. ERROR means the recording session should be deleted if it - * hasn't already. ABORTED and COMPLETED indicate the recording session is - * already deleted, so they return 'false'. - */ -static JS_ALWAYS_INLINE bool -StatusAbortsRecorderIfActive(AbortableRecordingStatus ars) -{ - return ars <= ARECORD_ERROR; -} -#endif - -class SlotMap; -class SlurpInfo; - -/* Results of trying to compare two typemaps together */ -enum TypeConsensus -{ - TypeConsensus_Okay, /* Two typemaps are compatible */ - TypeConsensus_Undemotes, /* Not compatible now, but would be with pending undemotes. */ - TypeConsensus_Bad /* Typemaps are not compatible */ -}; - -enum TracePointAction { - TPA_Nothing, - TPA_RanStuff, - TPA_Recorded, - TPA_Error -}; - -typedef HashMap GuardedShapeTable; - -#ifdef DEBUG -# define AbortRecording(cx, reason) AbortRecordingImpl(cx, reason) -#else -# define AbortRecording(cx, reason) AbortRecordingImpl(cx) -#endif - -void -AbortProfiling(JSContext *cx); - -class TraceRecorder -{ - JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR; - - /*************************************************************** Recording session constants */ - - /* The context in which recording started. */ - JSContext* const cx; - - /* Cached value of JS_TRACE_MONITOR(cx). */ - TraceMonitor* const traceMonitor; - - /* Cached oracle keeps track of hit counts for program counter locations */ - Oracle* oracle; - - /* The Fragment being recorded by this recording session. */ - VMFragment* const fragment; - - /* The root fragment representing the tree. */ - TreeFragment* const tree; - - /* The global object from the start of recording until now. */ - JSObject* const globalObj; - - /* If non-null, the script of outer loop aborted to start recording this loop. */ - JSScript* const outerScript; - - /* If non-null, the pc of the outer loop aborted to start recording this loop. */ - jsbytecode* const outerPC; - - /* If |outerPC|, the argc to use when looking up |outerPC| in the fragments table. */ - uint32 const outerArgc; - - /* If non-null, the side exit from which we are growing. */ - VMSideExit* const anchor; - - /* Instructions yielding the corresponding trace-const members of TracerState. */ - nanojit::LIns* const cx_ins; - nanojit::LIns* const eos_ins; - nanojit::LIns* const eor_ins; - nanojit::LIns* const loopLabel; - - /* Lazy slot import state. */ - unsigned importStackSlots; - unsigned importGlobalSlots; - TypeMap importTypeMap; - - /* - * The LirBuffer used to supply memory to our LirWriter pipeline. Also contains the most recent - * instruction for {sp, rp, state}. Also contains names for debug JIT spew. Should be split. - */ - nanojit::LirBuffer* const lirbuf; - - /* - * Remembers traceAlloc state before recording started; automatically rewinds when mark is - * destroyed on a failed compilation. - */ - VMAllocator::Mark mark; - - /* Remembers the number of sideExits in treeInfo before recording started. */ - const unsigned numSideExitsBefore; - - /*********************************************************** Recording session mutable state */ - - /* Maps interpreter stack values to the instruction generating that value. */ - Tracker tracker; - - /* Maps interpreter stack values to the instruction writing back to the native stack. */ - Tracker nativeFrameTracker; - - /* The start of the global object's slots we assume for the trackers. */ - Value* global_slots; - - /* The number of interpreted calls entered (and not yet left) since recording began. */ - unsigned callDepth; - - /* The current atom table, mirroring the interpreter loop's variable of the same name. */ - JSAtom** atoms; - Value* consts; - - /* An instruction yielding the current script's strict mode code flag. */ - nanojit::LIns* strictModeCode_ins; - - /* FIXME: Dead, but soon to be used for something or other. */ - Queue cfgMerges; - - /* Indicates whether the current tree should be trashed when the recording session ends. */ - bool trashSelf; - - /* A list of trees to trash at the end of the recording session. */ - Queue whichTreesToTrash; - - /* The set of objects whose shapes already have been guarded. */ - GuardedShapeTable guardedShapeTable; - - /* Current initializer depth, and whether any of the initializers are unoptimized NEWINIT. */ - int initDepth; - bool hadNewInit; - -#ifdef DEBUG - /* - * If we are expecting a record_AddProperty callback for this instruction, - * the shape of the object before adding the data property. Else NULL. - */ - const js::Shape* addPropShapeBefore; -#endif - - /***************************************** Temporal state hoisted into the recording session */ - - /* Carry the return value from a STOP/RETURN to the subsequent record_LeaveFrame. */ - nanojit::LIns* rval_ins; - - /* Carry the return value from a native call to the record_NativeCallComplete. */ - nanojit::LIns* native_rval_ins; - - /* Carry the return value of js_CreateThis to record_NativeCallComplete. */ - nanojit::LIns* newobj_ins; - - /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ - JSSpecializedNative* pendingSpecializedNative; - - /* Carry whether this is a jsval on the native stack from finishGetProp to monitorRecording. */ - Value* pendingUnboxSlot; - - /* Carry a guard condition to the beginning of the next monitorRecording. */ - nanojit::LIns* pendingGuardCondition; - - /* See AbortRecordingIfUnexpectedGlobalWrite. */ - js::Vector pendingGlobalSlotsToSet; - - /* Carry whether we have an always-exit from emitIf to checkTraceEnd. */ - bool pendingLoop; - - /* Temporary JSSpecializedNative used to describe non-specialized fast natives. */ - JSSpecializedNative generatedSpecializedNative; - - /* Temporary JSValueType array used to construct temporary typemaps. */ - js::Vector tempTypeMap; - - /* Used to generate LIR. Has a short name because it's used a lot. */ - tjit::Writer w; - - /************************************************************* 10 bajillion member functions */ - - /* - * These would be in Writer if they didn't modify TraceRecorder state. - * They are invoked the via macros below that make them look like they are - * part of Writer (hence the "w_" prefix, which looks like "w."). - */ - nanojit::LIns* w_immpObjGC(JSObject* obj); - nanojit::LIns* w_immpFunGC(JSFunction* fun); - nanojit::LIns* w_immpStrGC(JSString* str); - nanojit::LIns* w_immpShapeGC(const js::Shape* shape); - nanojit::LIns* w_immpIdGC(jsid id); - - #define immpObjGC(obj) name(w_immpObjGC(obj), #obj) - #define immpFunGC(fun) name(w_immpFunGC(fun), #fun) - #define immpStrGC(str) name(w_immpStrGC(str), #str) - #define immpAtomGC(atom) name(w_immpStrGC(atom), "ATOM(" #atom ")") - #define immpShapeGC(shape) name(w_immpShapeGC(shape), #shape) - #define immpIdGC(id) name(w_immpIdGC(id), #id) - - /* - * Examines current interpreter state to record information suitable for returning to the - * interpreter through a side exit of the given type. - */ - JS_REQUIRES_STACK VMSideExit* snapshot(ExitType exitType); - - /* - * Creates a separate but identical copy of the given side exit, allowing the guards associated - * with each to be entirely separate even after subsequent patching. - */ - JS_REQUIRES_STACK VMSideExit* copy(VMSideExit* exit); - - /* - * Creates an instruction whose payload is a GuardRecord for the given exit. The instruction - * is suitable for use as the final argument of a single call to LirBuffer::insGuard; do not - * reuse the returned value. - */ - JS_REQUIRES_STACK nanojit::GuardRecord* createGuardRecord(VMSideExit* exit); - - JS_REQUIRES_STACK JS_INLINE void markSlotUndemotable(LinkableFragment* f, unsigned slot); - - JS_REQUIRES_STACK JS_INLINE void markSlotUndemotable(LinkableFragment* f, unsigned slot, const void* pc); - - JS_REQUIRES_STACK unsigned findUndemotesInTypemaps(const TypeMap& typeMap, LinkableFragment* f, - Queue& undemotes); - - JS_REQUIRES_STACK void assertDownFrameIsConsistent(VMSideExit* anchor, FrameInfo* fi); - - JS_REQUIRES_STACK void captureStackTypes(unsigned callDepth, JSValueType* typeMap); - - bool isVoidPtrGlobal(const void* p) const; - bool isGlobal(const Value* p) const; - ptrdiff_t nativeGlobalSlot(const Value *p) const; - ptrdiff_t nativeGlobalOffset(const Value* p) const; - JS_REQUIRES_STACK ptrdiff_t nativeStackOffsetImpl(const void* p) const; - JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(const Value* p) const; - JS_REQUIRES_STACK ptrdiff_t nativeStackSlotImpl(const void* p) const; - JS_REQUIRES_STACK ptrdiff_t nativeStackSlot(const Value* p) const; - JS_REQUIRES_STACK ptrdiff_t nativespOffsetImpl(const void* p) const; - JS_REQUIRES_STACK ptrdiff_t nativespOffset(const Value* p) const; - JS_REQUIRES_STACK void importImpl(tjit::Address addr, const void* p, JSValueType t, - const char *prefix, uintN index, StackFrame *fp); - JS_REQUIRES_STACK void import(tjit::Address addr, const Value* p, JSValueType t, - const char *prefix, uintN index, StackFrame *fp); - JS_REQUIRES_STACK void import(TreeFragment* tree, nanojit::LIns* sp, unsigned stackSlots, - unsigned callDepth, unsigned ngslots, JSValueType* typeMap); - void trackNativeStackUse(unsigned slots); - - JS_REQUIRES_STACK bool isValidSlot(JSObject *obj, const js::Shape* shape); - JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot); - JS_REQUIRES_STACK void importGlobalSlot(unsigned slot); - - void ensureCond(nanojit::LIns** ins, bool* cond); - - JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, ExitType exitType, - bool abortIfAlwaysExits = false); - JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit, - bool abortIfAlwaysExits = false); - - nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset, - bool shouldDemoteToInt32); - -#ifdef DEBUG - bool isValidFrameObjPtr(void *obj); -#endif - void assertInsideLoop(); - - JS_REQUIRES_STACK void setImpl(void* p, nanojit::LIns* l, bool shouldDemoteToInt32 = true); - JS_REQUIRES_STACK void set(Value* p, nanojit::LIns* l, bool shouldDemoteToInt32 = true); - JS_REQUIRES_STACK void setFrameObjPtr(void* p, nanojit::LIns* l, - bool shouldDemoteToInt32 = true); - nanojit::LIns* getFromTrackerImpl(const void *p); - nanojit::LIns* getFromTracker(const Value* p); - JS_REQUIRES_STACK nanojit::LIns* getImpl(const void* p); - JS_REQUIRES_STACK nanojit::LIns* get(const Value* p); - JS_REQUIRES_STACK nanojit::LIns* getFrameObjPtr(void* p); - JS_REQUIRES_STACK nanojit::LIns* attemptImport(const Value* p); - JS_REQUIRES_STACK nanojit::LIns* addr(Value* p); - - JS_REQUIRES_STACK bool knownImpl(const void* p); - JS_REQUIRES_STACK bool known(const Value* p); - JS_REQUIRES_STACK bool known(JSObject** p); - /* - * The slots of the global object are sometimes reallocated by the - * interpreter. This function checks for that condition and re-maps the - * entries of the tracker accordingly. - */ - JS_REQUIRES_STACK void checkForGlobalObjectReallocation() { - if (global_slots != globalObj->getSlots()) - checkForGlobalObjectReallocationHelper(); - } - JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper(); - - JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap); - JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip, - TreeFragment** peer); - - JS_REQUIRES_STACK Value& argval(unsigned n) const; - JS_REQUIRES_STACK Value& varval(unsigned n) const; - JS_REQUIRES_STACK Value& stackval(int n) const; - - JS_REQUIRES_STACK void updateAtoms(); - JS_REQUIRES_STACK void updateAtoms(JSScript *script); - - struct NameResult { - // |tracked| is true iff the result of the name lookup is a variable that - // is already in the tracker. The rest of the fields are set only if - // |tracked| is false. - bool tracked; - Value v; // current property value - JSObject *obj; // Call object where name was found - nanojit::LIns *obj_ins; // LIR value for obj - js::Shape *shape; // shape name was resolved to - }; - - JS_REQUIRES_STACK nanojit::LIns* scopeChain(); - JS_REQUIRES_STACK nanojit::LIns* entryScopeChain() const; - JS_REQUIRES_STACK nanojit::LIns* entryFrameIns() const; - JS_REQUIRES_STACK StackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const; - JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins); - JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, Value*& vp, nanojit::LIns*& ins, NameResult& nr, JSObject **scopeObjp = NULL); - JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* shape, jsid id, Value*& vp, nanojit::LIns*& ins, NameResult& nr); - - JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n); - JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i); - JS_REQUIRES_STACK nanojit::LIns* var(unsigned n); - JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i); - JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v); - nanojit::LIns* stackLoad(tjit::Address addr, uint8 type); - JS_REQUIRES_STACK nanojit::LIns* stack(int n); - JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i); - - JS_REQUIRES_STACK void guardNonNeg(nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit); - JS_REQUIRES_STACK nanojit::LIns* tryToDemote(nanojit::LOpcode op, jsdouble v0, jsdouble v1, - nanojit::LIns* s0, nanojit::LIns* s1); - - nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); - nanojit::LIns* d2u(nanojit::LIns* d); - JS_REQUIRES_STACK RecordingStatus makeNumberInt32(nanojit::LIns* d, nanojit::LIns** num_ins); - JS_REQUIRES_STACK RecordingStatus makeNumberUint32(nanojit::LIns* d, nanojit::LIns** num_ins); - JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v); - - JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins); - - JS_REQUIRES_STACK bool canCallImacro() const; - JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro); - JS_REQUIRES_STACK RecordingStatus callImacroInfallibly(jsbytecode* imacro); - - JS_REQUIRES_STACK AbortableRecordingStatus ifop(); - JS_REQUIRES_STACK RecordingStatus switchop(); - JS_REQUIRES_STACK RecordingStatus inc(Value& v, jsint incr, bool pre = true); - JS_REQUIRES_STACK RecordingStatus inc(const Value &v, nanojit::LIns*& v_ins, - Value &v_out, jsint incr, - bool pre = true); - JS_REQUIRES_STACK RecordingStatus incHelper(const Value &v, nanojit::LIns*& v_ins, - Value &v_after, - nanojit::LIns*& v_ins_after, - jsint incr); - JS_REQUIRES_STACK AbortableRecordingStatus incProp(jsint incr, bool pre = true); - JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true); - JS_REQUIRES_STACK AbortableRecordingStatus incName(jsint incr, bool pre = true); - - JS_REQUIRES_STACK RecordingStatus strictEquality(bool equal, bool cmpCase); - JS_REQUIRES_STACK AbortableRecordingStatus equality(bool negate, bool tryBranchAfterCond); - JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r, - nanojit::LIns* l_ins, nanojit::LIns* r_ins, - bool negate, bool tryBranchAfterCond, - Value& rval); - JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond); - - JS_REQUIRES_STACK RecordingStatus unaryIntOp(nanojit::LOpcode op); - JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op); - - JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj, - uint32 shape, const char* name, VMSideExit* exit); - -#if defined DEBUG_notme && defined XP_UNIX - void dumpGuardedShapes(const char* prefix); -#endif - - void forgetGuardedShapes(); - - JS_REQUIRES_STACK AbortableRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins, - JSObject*& obj2, PCVal& pcval); - JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins, - JSObject* aobj, - JSObject* obj2, - PropertyCacheEntry* entry, - PCVal& pcval); - - void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot, const Value &v, - nanojit::LIns* v_ins); - void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, - nanojit::LIns*& slots_ins, const Value &v, nanojit::LIns* v_ins); - void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot, - nanojit::LIns*& slots_ins, const Value &v, nanojit::LIns* v_ins); - - nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot, - VMSideExit *exit); - - JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr); - JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, - uint32 *slotp, nanojit::LIns** v_insp, - Value* outp); - JS_REQUIRES_STACK RecordingStatus propTail(JSObject* obj, nanojit::LIns* obj_ins, - JSObject* obj2, PCVal pcval, - uint32 *slotp, nanojit::LIns** v_insp, - Value* outp); - JS_REQUIRES_STACK RecordingStatus denseArrayElement(Value& oval, Value& idx, Value*& vp, - nanojit::LIns*& v_ins, - nanojit::LIns*& addr_ins, - VMSideExit* exit); - JS_REQUIRES_STACK nanojit::LIns *canonicalizeNaNs(nanojit::LIns *dval_ins); - JS_REQUIRES_STACK AbortableRecordingStatus typedArrayElement(Value& oval, Value& idx, Value*& vp, - nanojit::LIns*& v_ins); - JS_REQUIRES_STACK AbortableRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins); - JS_REQUIRES_STACK AbortableRecordingStatus getProp(Value& v); - JS_REQUIRES_STACK RecordingStatus getThis(nanojit::LIns*& this_ins); - - JS_REQUIRES_STACK void storeMagic(JSWhyMagic why, tjit::Address addr); - JS_REQUIRES_STACK AbortableRecordingStatus unboxNextValue(nanojit::LIns* &v_ins); - - JS_REQUIRES_STACK VMSideExit* enterDeepBailCall(); - JS_REQUIRES_STACK void leaveDeepBailCall(); - - JS_REQUIRES_STACK RecordingStatus primitiveToStringInPlace(Value* vp); - JS_REQUIRES_STACK void finishGetProp(nanojit::LIns* obj_ins, nanojit::LIns* vp_ins, - nanojit::LIns* ok_ins, Value* outp); - JS_REQUIRES_STACK RecordingStatus getPropertyByName(nanojit::LIns* obj_ins, Value* idvalp, - Value* outp); - JS_REQUIRES_STACK RecordingStatus getPropertyByIndex(nanojit::LIns* obj_ins, - nanojit::LIns* index_ins, Value* outp); - JS_REQUIRES_STACK RecordingStatus getPropertyById(nanojit::LIns* obj_ins, Value* outp); - JS_REQUIRES_STACK RecordingStatus getPropertyWithNativeGetter(nanojit::LIns* obj_ins, - const js::Shape* shape, - Value* outp); - JS_REQUIRES_STACK RecordingStatus getPropertyWithScriptGetter(JSObject *obj, - nanojit::LIns* obj_ins, - const js::Shape* shape); - - JS_REQUIRES_STACK RecordingStatus getCharCodeAt(JSString *str, - nanojit::LIns* str_ins, nanojit::LIns* idx_ins, - nanojit::LIns** out_ins); - JS_REQUIRES_STACK nanojit::LIns* getUnitString(nanojit::LIns* str_ins, nanojit::LIns* idx_ins); - JS_REQUIRES_STACK RecordingStatus getCharAt(JSString *str, - nanojit::LIns* str_ins, nanojit::LIns* idx_ins, - JSOp mode, nanojit::LIns** out_ins); - - JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins, - Value* idvalp, Value* rvalp, - bool init); - JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins, - nanojit::LIns* index_ins, - Value* rvalp, bool init); - JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex, - int v_spindex); - - JS_REQUIRES_STACK RecordingStatus lookupForSetPropertyOp(JSObject* obj, nanojit::LIns* obj_ins, - jsid id, bool* safep, - JSObject** pobjp, - const js::Shape** shapep); - JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins, - const js::Shape* shape, - const Value& v, nanojit::LIns* v_ins); - JS_REQUIRES_STACK RecordingStatus addDataProperty(JSObject* obj); - JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject* callobj, nanojit::LIns* callobj_ins, - const js::Shape* shape, nanojit::LIns* v_ins, - const Value& v); - JS_REQUIRES_STACK RecordingStatus setProperty(JSObject* obj, nanojit::LIns* obj_ins, - const Value& v, nanojit::LIns* v_ins, - bool* deferredp); - JS_REQUIRES_STACK RecordingStatus recordSetPropertyOp(); - JS_REQUIRES_STACK RecordingStatus recordInitPropertyOp(jsbytecode op); - - void box_undefined_into(tjit::Address addr); -#if JS_BITS_PER_WORD == 32 - void box_null_into(tjit::Address addr); - nanojit::LIns* unbox_number_as_double(tjit::Address addr, nanojit::LIns* tag_ins, - VMSideExit* exit); - nanojit::LIns* unbox_object(tjit::Address addr, nanojit::LIns* tag_ins, JSValueType type, - VMSideExit* exit); - nanojit::LIns* unbox_non_double_object(tjit::Address addr, nanojit::LIns* tag_ins, - JSValueType type, VMSideExit* exit); -#elif JS_BITS_PER_WORD == 64 - nanojit::LIns* non_double_object_value_has_type(nanojit::LIns* v_ins, JSValueType type); - nanojit::LIns* unpack_ptr(nanojit::LIns* v_ins); - nanojit::LIns* unbox_number_as_double(nanojit::LIns* v_ins, VMSideExit* exit); - nanojit::LIns* unbox_object(nanojit::LIns* v_ins, JSValueType type, VMSideExit* exit); - nanojit::LIns* unbox_non_double_object(nanojit::LIns* v_ins, JSValueType type, VMSideExit* exit); -#endif - - nanojit::LIns* unbox_value(const Value& v, tjit::Address addr, VMSideExit* exit, - bool force_double=false); - void unbox_any_object(tjit::Address addr, nanojit::LIns** obj_ins, nanojit::LIns** is_obj_ins); - nanojit::LIns* is_boxed_true(tjit::Address addr); - nanojit::LIns* is_boxed_magic(tjit::Address addr, JSWhyMagic why); - - nanojit::LIns* is_string_id(nanojit::LIns* id_ins); - nanojit::LIns* unbox_string_id(nanojit::LIns* id_ins); - nanojit::LIns* unbox_int_id(nanojit::LIns* id_ins); - - /* Box a slot on trace into the given address at the given offset. */ - void box_value_into(const Value& v, nanojit::LIns* v_ins, tjit::Address addr); - - /* - * Box a slot so that it may be passed with value semantics to a native. On - * 32-bit, this currently means boxing the value into insAlloc'd memory and - * returning the address which is passed as a Value*. On 64-bit, this - * currently means returning the boxed value which is passed as a jsval. - */ - nanojit::LIns* box_value_for_native_call(const Value& v, nanojit::LIns* v_ins); - - /* Box a slot into insAlloc'd memory. */ - nanojit::LIns* box_value_into_alloc(const Value& v, nanojit::LIns* v_ins); - - JS_REQUIRES_STACK void guardClassHelper(bool cond, nanojit::LIns* obj_ins, Class* clasp, - VMSideExit* exit, nanojit::LoadQual loadQual); - JS_REQUIRES_STACK void guardClass(nanojit::LIns* obj_ins, Class* clasp, - VMSideExit* exit, nanojit::LoadQual loadQual); - JS_REQUIRES_STACK void guardNotClass(nanojit::LIns* obj_ins, Class* clasp, - VMSideExit* exit, nanojit::LoadQual loadQual); - JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, ExitType exitType); - JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, VMSideExit* exit); - JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins, - JSObject** pobj, nanojit::LIns** pobj_ins, - VMSideExit* exit); - JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj, - nanojit::LIns* obj_ins, - VMSideExit* exit); - JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v); - JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker(); - JS_REQUIRES_STACK AbortableRecordingStatus putActivationObjects(); - JS_REQUIRES_STACK RecordingStatus createThis(JSObject& ctor, nanojit::LIns* ctor_ins, - nanojit::LIns** thisobj_insp); - JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee); - JS_REQUIRES_STACK StackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, - unsigned *depthp); - JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins); - JS_REQUIRES_STACK void guardNotHole(nanojit::LIns* argsobj_ins, nanojit::LIns* ids_ins); - JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor, - nanojit::LIns*& proto_ins); - JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key, - nanojit::LIns*& proto_ins); - JS_REQUIRES_STACK RecordingStatus newArray(JSObject* ctor, uint32 argc, Value* argv, - Value* rval); - JS_REQUIRES_STACK RecordingStatus newString(JSObject* ctor, uint32 argc, Value* argv, - Value* rval); - JS_REQUIRES_STACK RecordingStatus interpretedFunctionCall(Value& fval, JSFunction* fun, - uintN argc, bool constructing); - JS_REQUIRES_STACK void propagateFailureToBuiltinStatus(nanojit::LIns *ok_ins, - nanojit::LIns *&status_ins); - JS_REQUIRES_STACK RecordingStatus emitNativeCall(JSSpecializedNative* sn, uintN argc, - nanojit::LIns* args[], bool rooted); - JS_REQUIRES_STACK void emitNativePropertyOp(const js::Shape* shape, - nanojit::LIns* obj_ins, - bool setflag, - nanojit::LIns* addr_boxed_val_ins); - JS_REQUIRES_STACK RecordingStatus callSpecializedNative(JSNativeTraceInfo* trcinfo, uintN argc, - bool constructing); - JS_REQUIRES_STACK RecordingStatus callNative(uintN argc, JSOp mode); - JS_REQUIRES_STACK RecordingStatus callFloatReturningInt(uintN argc, - const nanojit::CallInfo *ci); - JS_REQUIRES_STACK RecordingStatus functionCall(uintN argc, JSOp mode); - - JS_REQUIRES_STACK void trackCfgMerges(jsbytecode* pc); - JS_REQUIRES_STACK void emitIf(jsbytecode* pc, bool cond, nanojit::LIns* x); - JS_REQUIRES_STACK void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x); - JS_REQUIRES_STACK AbortableRecordingStatus checkTraceEnd(jsbytecode* pc); - - AbortableRecordingStatus hasMethod(JSObject* obj, jsid id, bool& found); - JS_REQUIRES_STACK AbortableRecordingStatus hasIteratorMethod(JSObject* obj, bool& found); - - JS_REQUIRES_STACK jsatomid getFullIndex(ptrdiff_t pcoff = 0); - - JS_REQUIRES_STACK JSValueType determineSlotType(Value* vp); - - JS_REQUIRES_STACK RecordingStatus setUpwardTrackedVar(Value* stackVp, const Value& v, - nanojit::LIns* v_ins); - - JS_REQUIRES_STACK AbortableRecordingStatus compile(); - JS_REQUIRES_STACK AbortableRecordingStatus closeLoop(); - JS_REQUIRES_STACK AbortableRecordingStatus endLoop(); - JS_REQUIRES_STACK AbortableRecordingStatus endLoop(VMSideExit* exit); - JS_REQUIRES_STACK bool joinEdgesToEntry(TreeFragment* peer_root); - JS_REQUIRES_STACK void adjustCallerTypes(TreeFragment* f); - JS_REQUIRES_STACK void prepareTreeCall(TreeFragment* inner); - JS_REQUIRES_STACK void emitTreeCall(TreeFragment* inner, VMSideExit* exit); - JS_REQUIRES_STACK void determineGlobalTypes(JSValueType* typeMap); - JS_REQUIRES_STACK VMSideExit* downSnapshot(FrameInfo* downFrame); - JS_REQUIRES_STACK TreeFragment* findNestedCompatiblePeer(TreeFragment* f); - JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner, - uintN& inlineCallCount); - - static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r, - uintN& inlineCallCount); - - /* Allocators associated with this recording session. */ - VMAllocator& tempAlloc() const { return *traceMonitor->tempAlloc; } - VMAllocator& traceAlloc() const { return *traceMonitor->traceAlloc; } - VMAllocator& dataAlloc() const { return *traceMonitor->dataAlloc; } - - /* Member declarations for each opcode, to be called before interpreting the opcode. */ -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - JS_REQUIRES_STACK AbortableRecordingStatus record_##op(); -# include "jsopcode.tbl" -#undef OPDEF - - JS_REQUIRES_STACK - TraceRecorder(JSContext* cx, TraceMonitor *tm, VMSideExit*, VMFragment*, - unsigned stackSlots, unsigned ngslots, JSValueType* typeMap, - VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC, - uint32 outerArgc, bool speculate); - - /* The destructor should only be called through finish*, not directly. */ - ~TraceRecorder(); - JS_REQUIRES_STACK AbortableRecordingStatus finishSuccessfully(); - - enum AbortResult { NORMAL_ABORT, JIT_RESET }; - JS_REQUIRES_STACK AbortResult finishAbort(const char* reason); - -#ifdef DEBUG - /* Debug printing functionality to emit printf() on trace. */ - JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]); - JS_REQUIRES_STACK void tprint(const char *format); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, - nanojit::LIns *ins2); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, - nanojit::LIns *ins2, nanojit::LIns *ins3); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, - nanojit::LIns *ins2, nanojit::LIns *ins3, - nanojit::LIns *ins4); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, - nanojit::LIns *ins2, nanojit::LIns *ins3, - nanojit::LIns *ins4, nanojit::LIns *ins5); - JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, - nanojit::LIns *ins2, nanojit::LIns *ins3, - nanojit::LIns *ins4, nanojit::LIns *ins5, - nanojit::LIns *ins6); -#endif - - friend class ImportBoxedStackSlotVisitor; - friend class AdjustCallerGlobalTypesVisitor; - friend class AdjustCallerStackTypesVisitor; - friend class TypeCompatibilityVisitor; - friend class SlotMap; - friend class DefaultSlotMap; - friend class DetermineTypesVisitor; - friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*, uintN&); - friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, uintN &inlineCallCount, - bool *blacklist); - friend AbortResult AbortRecording(JSContext*, const char*); - friend class BoxArg; - friend void TraceMonitor::sweep(JSContext *cx); - - public: - static bool JS_REQUIRES_STACK - startRecorder(JSContext*, TraceMonitor *, VMSideExit*, VMFragment*, - unsigned stackSlots, unsigned ngslots, JSValueType* typeMap, - VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC, - uint32 outerArgc, bool speculate); - - /* Accessors. */ - VMFragment* getFragment() const { return fragment; } - TreeFragment* getTree() const { return tree; } - bool outOfMemory() const { return traceMonitor->outOfMemory(); } - Oracle* getOracle() const { return oracle; } - JSObject* getGlobal() const { return globalObj; } - - /* Entry points / callbacks from the interpreter. */ - JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op); - JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame(); - JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame(); - JS_REQUIRES_STACK AbortableRecordingStatus record_AddProperty(JSObject *obj); - JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot, - JSObject* obj); - JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete(); - void forgetGuardedShapesForObject(JSObject* obj); - - bool globalSetExpected(unsigned slot) { - unsigned *pi = Find(pendingGlobalSlotsToSet, slot); - if (pi == pendingGlobalSlotsToSet.end()) { - /* - * Do slot arithmetic manually to avoid getSlotRef assertions which - * do not need to be satisfied for this purpose. - */ - Value *vp = globalObj->getSlots() + slot; - - /* If this global is definitely being tracked, then the write is unexpected. */ - if (tracker.has(vp)) - return false; - - /* - * Otherwise, only abort if the global is not present in the - * import typemap. Just deep aborting false here is not acceptable, - * because the recorder does not guard on every operation that - * could lazily resolve. Since resolving adds properties to - * reserved slots, the tracer will never have imported them. - */ - return tree->globalSlots->offsetOf((uint16)nativeGlobalSlot(vp)) == -1; - } - pendingGlobalSlotsToSet.erase(pi); - return true; - } -}; - -#define TRACING_ENABLED(cx) ((cx)->traceJitEnabled) -#define REGEX_JIT_ENABLED(cx) ((cx)->traceJitEnabled || (cx)->methodJitEnabled) - -#define JSOP_IN_RANGE(op,lo,hi) (uintN((op) - (lo)) <= uintN((hi) - (lo))) -#define JSOP_IS_BINARY(op) JSOP_IN_RANGE(op, JSOP_BITOR, JSOP_MOD) -#define JSOP_IS_UNARY(op) JSOP_IN_RANGE(op, JSOP_NEG, JSOP_POS) -#define JSOP_IS_EQUALITY(op) JSOP_IN_RANGE(op, JSOP_EQ, JSOP_NE) - -#define TRACE_ARGS_(x,args) \ - JS_BEGIN_MACRO \ - if (TraceRecorder* tr_ = TRACE_RECORDER(cx)) { \ - AbortableRecordingStatus status = tr_->record_##x args; \ - if (StatusAbortsRecorderIfActive(status)) { \ - if (TRACE_RECORDER(cx)) { \ - JS_ASSERT(TRACE_RECORDER(cx) == tr_); \ - AbortRecording(cx, #x); \ - } \ - if (status == ARECORD_ERROR) \ - goto error; \ - } \ - JS_ASSERT(status != ARECORD_IMACRO); \ - } \ - JS_END_MACRO - -#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args) -#define TRACE_0(x) TRACE_ARGS(x, ()) -#define TRACE_1(x,a) TRACE_ARGS(x, (a)) -#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b)) - -extern JS_REQUIRES_STACK MonitorResult -MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode); - -extern JS_REQUIRES_STACK TracePointAction -RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist); - -extern JS_REQUIRES_STACK TracePointAction -MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist, - void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits); - -extern JS_REQUIRES_STACK TraceRecorder::AbortResult -AbortRecording(JSContext* cx, const char* reason); - -extern bool -InitJIT(TraceMonitor *tm, JSRuntime *rt); - -extern void -FinishJIT(TraceMonitor *tm); - -extern void -PurgeScriptFragments(TraceMonitor* tm, JSScript* script); - -extern bool -OverfullJITCache(JSContext *cx, TraceMonitor* tm); - -extern void -FlushJITCache(JSContext* cx, TraceMonitor* tm); - -extern JSObject * -GetBuiltinFunction(JSContext *cx, uintN index); - -extern void -SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes); - -extern void -ExternNativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot); - -#ifdef MOZ_TRACEVIS - -extern JS_FRIEND_API(bool) -StartTraceVis(const char* filename); - -extern JS_FRIEND_API(JSBool) -StartTraceVisNative(JSContext *cx, uintN argc, jsval *vp); - -extern JS_FRIEND_API(bool) -StopTraceVis(); - -extern JS_FRIEND_API(JSBool) -StopTraceVisNative(JSContext *cx, uintN argc, jsval *vp); - -/* Must contain no more than 16 items. */ -enum TraceVisState { - // Special: means we returned from current activity to last - S_EXITLAST, - // Activities - S_INTERP, - S_MONITOR, - S_RECORD, - S_COMPILE, - S_EXECUTE, - S_NATIVE, - // Events: these all have (bit 3) == 1. - S_RESET = 8 -}; - -/* Reason for an exit to the interpreter. */ -enum TraceVisExitReason { - R_NONE, - R_ABORT, - /* Reasons in MonitorLoopEdge */ - R_INNER_SIDE_EXIT, - R_DOUBLES, - R_CALLBACK_PENDING, - R_OOM_GETANCHOR, - R_BACKED_OFF, - R_COLD, - R_FAIL_RECORD_TREE, - R_MAX_PEERS, - R_FAIL_EXECUTE_TREE, - R_FAIL_STABILIZE, - R_FAIL_EXTEND_FLUSH, - R_FAIL_EXTEND_MAX_BRANCHES, - R_FAIL_EXTEND_START, - R_FAIL_EXTEND_COLD, - R_FAIL_SCOPE_CHAIN_CHECK, - R_NO_EXTEND_OUTER, - R_MISMATCH_EXIT, - R_OOM_EXIT, - R_TIMEOUT_EXIT, - R_DEEP_BAIL_EXIT, - R_STATUS_EXIT, - R_OTHER_EXIT -}; - -enum TraceVisFlushReason { - FR_DEEP_BAIL, - FR_OOM, - FR_GLOBAL_SHAPE_MISMATCH, - FR_GLOBALS_FULL -}; - -const unsigned long long MS64_MASK = 0xfull << 60; -const unsigned long long MR64_MASK = 0x1full << 55; -const unsigned long long MT64_MASK = ~(MS64_MASK | MR64_MASK); - -extern FILE* traceVisLogFile; -extern JSHashTable *traceVisScriptTable; - -extern JS_FRIEND_API(void) -StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r); - -static inline void -LogTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) -{ - if (traceVisLogFile) { - unsigned long long sllu = s; - unsigned long long rllu = r; - unsigned long long d = (sllu << 60) | (rllu << 55) | (rdtsc() & MT64_MASK); - fwrite(&d, sizeof(d), 1, traceVisLogFile); - } - if (traceVisScriptTable) { - StoreTraceVisState(cx, s, r); - } -} - -/* - * Although this runs the same code as LogTraceVisState, it is a separate - * function because the meaning of the log entry is different. Also, the entry - * formats may diverge someday. - */ -static inline void -LogTraceVisEvent(JSContext *cx, TraceVisState s, TraceVisFlushReason r) -{ - LogTraceVisState(cx, s, (TraceVisExitReason) r); -} - -static inline void -EnterTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) -{ - LogTraceVisState(cx, s, r); -} - -static inline void -ExitTraceVisState(JSContext *cx, TraceVisExitReason r) -{ - LogTraceVisState(cx, S_EXITLAST, r); -} - -struct TraceVisStateObj { - TraceVisExitReason r; - JSContext *mCx; - - inline TraceVisStateObj(JSContext *cx, TraceVisState s) : r(R_NONE) - { - EnterTraceVisState(cx, s, R_NONE); - mCx = cx; - } - inline ~TraceVisStateObj() - { - ExitTraceVisState(mCx, r); - } -}; - -#endif /* MOZ_TRACEVIS */ - -} /* namespace js */ - -#else /* !JS_TRACER */ - -#define TRACE_0(x) ((void)0) -#define TRACE_1(x,a) ((void)0) -#define TRACE_2(x,a,b) ((void)0) - -#endif /* !JS_TRACER */ - -namespace js { - -/* - * While recording, the slots of the global object may change payload or type. - * This is fine as long as the recorder expects this change (and therefore has - * generated the corresponding LIR, snapshots, etc). The recorder indicates - * that it expects a write to a global slot by setting pendingGlobalSlotsToSet - * in the recorder, before the write is made by the interpreter, and clearing - * pendingGlobalSlotsToSet before recording the next op. Any global slot write - * that has not been whitelisted in this manner is therefore unexpected and, if - * the global slot is actually being tracked, recording must be aborted. - */ -static JS_INLINE void -AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slot) -{ -#ifdef JS_TRACER - if (TraceRecorder *tr = TRACE_RECORDER(cx)) { - if (obj == tr->getGlobal() && !tr->globalSetExpected(slot)) - AbortRecording(cx, "Global slot written outside tracer supervision"); - } -#endif -} - -} /* namespace js */ - -#endif /* jstracer_h___ */ diff --git a/deps/mozjs/js/src/jstypedarray.cpp b/deps/mozjs/js/src/jstypedarray.cpp index 95e6c1909fb..f3ac1049072 100644 --- a/deps/mozjs/js/src/jstypedarray.cpp +++ b/deps/mozjs/js/src/jstypedarray.cpp @@ -39,8 +39,9 @@ #include +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jshash.h" #include "jsprf.h" @@ -48,7 +49,6 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsversion.h" #include "jsgc.h" @@ -57,15 +57,27 @@ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsstaticcheck.h" -#include "jsbit.h" -#include "jsvector.h" #include "jstypedarray.h" +#include "jsutil.h" + +#include "vm/GlobalObject.h" +#include "jsatominlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" +#include "jstypedarrayinlines.h" +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; + +/* + * Allocate array buffers with the maximum number of fixed slots marked as + * reserved, so that the fixed slots may be used for the buffer's contents. + * The last fixed slot is kept for the object's private data. + */ +static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1; static bool ValueIsLength(JSContext *cx, const Value &v, jsuint *len) @@ -94,6 +106,26 @@ ValueIsLength(JSContext *cx, const Value &v, jsuint *len) return false; } +/* + * Convert |v| to an array index for an array of length |length| per + * the Typed Array Specification section 7.0, |subarray|. If successful, + * the output value is in the range [0, length). + */ +static bool +ToClampedIndex(JSContext *cx, const Value &v, int32_t length, int32_t *out) +{ + if (!ToInt32(cx, v, out)) + return false; + if (*out < 0) { + *out += length; + if (*out < 0) + *out = 0; + } else if (*out > length) { + *out = length; + } + return true; +} + /* * ArrayBuffer * @@ -101,32 +133,68 @@ ValueIsLength(JSContext *cx, const Value &v, jsuint *len) * access. It can be created explicitly and passed to a TypedArray, or * can be created implicitly by constructing a TypedArray with a size. */ -ArrayBuffer * -ArrayBuffer::fromJSObject(JSObject *obj) + +/** + * Walks up the prototype chain to find the actual ArrayBuffer data. + * This MAY return NULL. Callers should always use js_IsArrayBuffer() + * first. + */ +JSObject * +ArrayBuffer::getArrayBuffer(JSObject *obj) { - while (!js_IsArrayBuffer(obj)) + while (obj && !js_IsArrayBuffer(obj)) obj = obj->getProto(); - return reinterpret_cast(obj->getPrivate()); + return obj; } JSBool ArrayBuffer::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj); - if (abuf) - vp->setInt32(jsint(abuf->byteLength)); + JSObject *arrayBuffer = getArrayBuffer(obj); + if (!arrayBuffer) { + vp->setInt32(0); + return true; + } + vp->setInt32(jsint(arrayBuffer->arrayBufferByteLength())); return true; } -void -ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj) +JSBool +ArrayBuffer::fun_slice(JSContext *cx, uintN argc, Value *vp) { - ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj); - if (abuf) { - if (!abuf->isExternal) - abuf->freeStorage(cx); - cx->delete_(abuf); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, fun_slice, &ArrayBufferClass, &ok); + if (!obj) + return ok; + + JSObject *arrayBuffer = getArrayBuffer(obj); + if (!arrayBuffer) + return true; + + // these are the default values + int32_t length = int32_t(arrayBuffer->arrayBufferByteLength()); + int32_t begin = 0, end = length; + + if (args.length() > 0) { + if (!ToClampedIndex(cx, args[0], length, &begin)) + return false; + + if (args.length() > 1) { + if (!ToClampedIndex(cx, args[1], length, &end)) + return false; + } } + + if (begin > end) + begin = end; + + JSObject *nobj = createSlice(cx, arrayBuffer, begin, end); + if (!nobj) + return false; + args.rval().setObject(*nobj); + return true; } /* @@ -135,8 +203,8 @@ ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj) JSBool ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp) { - int32 nbytes = 0; - if (argc > 0 && !ValueToECMAInt32(cx, vp[2], &nbytes)) + int32_t nbytes = 0; + if (argc > 0 && !ToInt32(cx, vp[2], &nbytes)) return false; JSObject *bufobj = create(cx, nbytes); @@ -146,12 +214,66 @@ ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp) return true; } +bool +JSObject::allocateArrayBufferSlots(JSContext *cx, uint32_t size, uint8_t *contents) +{ + /* + * ArrayBuffer objects delegate added properties to another JSObject, so + * their internal layout can use the object's fixed slots for storage. + * Set up the object to look like an array with an elements header. + */ + JS_ASSERT(isArrayBuffer() && !hasDynamicSlots() && !hasDynamicElements()); + + size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER; + + if (size > sizeof(Value) * usableSlots) { + ObjectElements *newheader = (ObjectElements *)cx->calloc_(size + sizeof(ObjectElements)); + if (!newheader) + return false; + elements = newheader->elements(); + if (contents) + memcpy(elements, contents, size); + } else { + elements = fixedElements(); + if (contents) + memcpy(elements, contents, size); + else + memset(elements, 0, size); + } + + ObjectElements *header = getElementsHeader(); + + /* + * Note that |bytes| may not be a multiple of |sizeof(Value)|, so + * |capacity * sizeof(Value)| may underestimate the size by up to + * |sizeof(Value) - 1| bytes. + */ + header->capacity = size / sizeof(Value); + header->initializedLength = 0; + header->length = size; + header->unused = 0; + + return true; +} + +static JSObject * +DelegateObject(JSContext *cx, JSObject *obj) +{ + if (!obj->getPrivate()) { + JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL); + obj->setPrivate(delegate); + return delegate; + } + return static_cast(obj->getPrivate()); +} + JSObject * -ArrayBuffer::create(JSContext *cx, int32 nbytes) +ArrayBuffer::create(JSContext *cx, int32_t nbytes, uint8_t *contents) { - JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass); + JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::slowClass); if (!obj) return NULL; + JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT16); if (nbytes < 0) { /* @@ -163,50 +285,429 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes) return NULL; } - ArrayBuffer *abuf = cx->new_(); - if (!abuf) + JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass); + + js::Shape *empty = EmptyShape::getInitialShape(cx, &ArrayBufferClass, + obj->getProto(), obj->getParent(), + gc::FINALIZE_OBJECT16); + if (!empty) return NULL; + obj->setLastPropertyInfallible(empty); - if (!abuf->allocateStorage(cx, nbytes)) { - Foreground::delete_(abuf); + /* + * The first 8 bytes hold the length. + * The rest of it is a flat data store for the array buffer. + */ + if (!obj->allocateArrayBufferSlots(cx, nbytes, contents)) return NULL; - } - obj->setPrivate(abuf); return obj; } -bool -ArrayBuffer::allocateStorage(JSContext *cx, uint32 nbytes) +JSObject * +ArrayBuffer::createSlice(JSContext *cx, JSObject *arrayBuffer, uint32_t begin, uint32_t end) { - JS_ASSERT(data == 0); + JS_ASSERT(arrayBuffer->isArrayBuffer()); + JS_ASSERT(begin <= arrayBuffer->arrayBufferByteLength()); + JS_ASSERT(end <= arrayBuffer->arrayBufferByteLength()); - if (nbytes) { - data = cx->calloc_(nbytes); - if (!data) - return false; + JS_ASSERT(begin <= end); + uint32_t length = end - begin; + + return create(cx, length, arrayBuffer->arrayBufferDataOffset() + begin); +} + +ArrayBuffer::~ArrayBuffer() +{ +} + +void +ArrayBuffer::obj_trace(JSTracer *trc, JSObject *obj) +{ + /* + * If this object changes, it will get marked via the private data barrier, + * so it's safe to leave it Unbarriered. + */ + JSObject *delegate = static_cast(obj->getPrivate()); + if (delegate) + MarkObjectUnbarriered(trc, delegate, "arraybuffer.delegate"); +} + +static JSProperty * const PROPERTY_FOUND = reinterpret_cast(1); + +JSBool +ArrayBuffer::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp) +{ + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) { + *propp = PROPERTY_FOUND; + *objp = getArrayBuffer(obj); + return true; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + + JSBool delegateResult = delegate->lookupGeneric(cx, id, objp, propp); + + /* If false, there was an error, so propagate it. + * Otherwise, if propp is non-null, the property + * was found. Otherwise it was not + * found so look in the prototype chain. + */ + if (!delegateResult) + return false; + + if (*propp != NULL) { + if (*objp == delegate) + *objp = obj; + return true; + } + + JSObject *proto = obj->getProto(); + if (!proto) { + *objp = NULL; + *propp = NULL; + return true; + } + + return proto->lookupGeneric(cx, id, objp, propp); +} + +JSBool +ArrayBuffer::obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, + JSObject **objp, JSProperty **propp) +{ + return obj_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp); +} + +JSBool +ArrayBuffer::obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, + JSObject **objp, JSProperty **propp) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + + /* + * If false, there was an error, so propagate it. + * Otherwise, if propp is non-null, the property + * was found. Otherwise it was not + * found so look in the prototype chain. + */ + if (!delegate->lookupElement(cx, index, objp, propp)) + return false; + + if (*propp != NULL) { + if (*objp == delegate) + *objp = obj; + return true; } - byteLength = nbytes; - isExternal = false; + if (JSObject *proto = obj->getProto()) + return proto->lookupElement(cx, index, objp, propp); + + *objp = NULL; + *propp = NULL; return true; } -void -ArrayBuffer::freeStorage(JSContext *cx) +JSBool +ArrayBuffer::obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, + JSObject **objp, JSProperty **propp) +{ + return obj_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp); +} + +JSBool +ArrayBuffer::obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) + return true; + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_DefineProperty(cx, delegate, id, v, getter, setter, attrs); +} + +JSBool +ArrayBuffer::obj_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return obj_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs); +} + +JSBool +ArrayBuffer::obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_DefineElement(cx, delegate, index, v, getter, setter, attrs); +} + +JSBool +ArrayBuffer::obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return obj_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs); +} + +JSBool +ArrayBuffer::obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { - if (data) { - cx->free_(data); -#ifdef DEBUG - // the destructor asserts that data is 0 in debug builds - data = NULL; -#endif + obj = getArrayBuffer(obj); + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) { + vp->setInt32(obj->arrayBufferByteLength()); + return true; } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_GetProperty(cx, delegate, receiver, id, vp); } -ArrayBuffer::~ArrayBuffer() +JSBool +ArrayBuffer::obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, + Value *vp) +{ + obj = getArrayBuffer(obj); + if (name == cx->runtime->atomState.byteLengthAtom) { + vp->setInt32(obj->arrayBufferByteLength()); + return true; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_GetProperty(cx, delegate, receiver, ATOM_TO_JSID(name), vp); +} + +JSBool +ArrayBuffer::obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) +{ + JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj)); + if (!delegate) + return false; + return js_GetElement(cx, delegate, receiver, index, vp); +} + +JSBool +ArrayBuffer::obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, + uint32_t index, Value *vp, bool *present) +{ + JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj)); + if (!delegate) + return false; + return delegate->getElementIfPresent(cx, receiver, index, vp, present); +} + +JSBool +ArrayBuffer::obj_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp) +{ + return obj_getGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp); +} + +JSBool +ArrayBuffer::obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +{ + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) + return true; + + if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) { + // setting __proto__ = null + // effectively removes the prototype chain. + // any attempt to set __proto__ on native + // objects after setting them to null makes + // __proto__ just a plain property. + // the following code simulates this behaviour on arrays. + // + // we first attempt to set the prototype on + // the delegate which is a native object + // so that existing code handles the case + // of treating it as special or plain. + // if the delegate's prototype has now changed + // then we change our prototype too. + // + // otherwise __proto__ was a plain property + // and we don't modify our prototype chain + // since obj_getProperty will fetch it as a plain + // property from the delegate. + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + + JSObject *oldDelegateProto = delegate->getProto(); + + if (!js_SetPropertyHelper(cx, delegate, id, 0, vp, strict)) + return false; + + if (delegate->getProto() != oldDelegateProto) { + // actual __proto__ was set and not a plain property called + // __proto__ + if (!obj->isExtensible()) { + obj->reportNotExtensible(cx); + return false; + } + if (!SetProto(cx, obj, vp->toObjectOrNull(), true)) { + // this can be caused for example by setting x.__proto__ = x + // restore delegate prototype chain + SetProto(cx, delegate, oldDelegateProto, true); + return false; + } + } + return true; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + + return js_SetPropertyHelper(cx, delegate, id, 0, vp, strict); +} + +JSBool +ArrayBuffer::obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) +{ + return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); +} + +JSBool +ArrayBuffer::obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + + return js_SetElementHelper(cx, delegate, index, 0, vp, strict); +} + +JSBool +ArrayBuffer::obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict) +{ + return obj_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict); +} + +JSBool +ArrayBuffer::obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +{ + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) { + *attrsp = JSPROP_PERMANENT | JSPROP_READONLY; + return true; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_GetAttributes(cx, delegate, id, attrsp); +} + +JSBool +ArrayBuffer::obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) +{ + return obj_getGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp); +} + +JSBool +ArrayBuffer::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_GetElementAttributes(cx, delegate, index, attrsp); +} + +JSBool +ArrayBuffer::obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + return obj_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp); +} + +JSBool +ArrayBuffer::obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +{ + if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_ARRAY_ATTRS); + return false; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_SetAttributes(cx, delegate, id, attrsp); +} + +JSBool +ArrayBuffer::obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) { - JS_ASSERT(data == NULL); + return obj_setGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp); +} + +JSBool +ArrayBuffer::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_SetElementAttributes(cx, delegate, index, attrsp); +} + +JSBool +ArrayBuffer::obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + return obj_setGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp); +} + +JSBool +ArrayBuffer::obj_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict) +{ + if (name == cx->runtime->atomState.byteLengthAtom) { + rval->setBoolean(false); + return true; + } + + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_DeleteProperty(cx, delegate, name, rval, strict); +} + +JSBool +ArrayBuffer::obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_DeleteElement(cx, delegate, index, rval, strict); +} + +JSBool +ArrayBuffer::obj_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) +{ + JSObject *delegate = DelegateObject(cx, obj); + if (!delegate) + return false; + return js_DeleteSpecial(cx, delegate, sid, rval, strict); +} + +JSBool +ArrayBuffer::obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + Value *statep, jsid *idp) +{ + statep->setNull(); + return true; +} + +JSType +ArrayBuffer::obj_typeOf(JSContext *cx, JSObject *obj) +{ + return JSTYPE_OBJECT; } /* @@ -217,19 +718,19 @@ ArrayBuffer::~ArrayBuffer() * the subclasses. */ -TypedArray * -TypedArray::fromJSObject(JSObject *obj) +JSObject * +TypedArray::getTypedArray(JSObject *obj) { while (!js_IsTypedArray(obj)) obj = obj->getProto(); - return reinterpret_cast(obj->getPrivate()); + return obj; } inline bool -TypedArray::isArrayIndex(JSContext *cx, jsid id, jsuint *ip) +TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip) { jsuint index; - if (js_IdIsIndex(id, &index) && index < length) { + if (js_IdIsIndex(id, &index) && index < getLength(obj)) { if (ip) *ip = index; return true; @@ -238,7 +739,7 @@ TypedArray::isArrayIndex(JSContext *cx, jsid id, jsuint *ip) return false; } -typedef Value (* TypedArrayPropertyGetter)(TypedArray *tarray); +typedef Value (* TypedArrayPropertyGetter)(JSObject *tarray); template class TypedArrayGetter { @@ -246,7 +747,7 @@ class TypedArrayGetter { static inline bool get(JSContext *cx, JSObject *obj, jsid id, Value *vp) { do { if (js_IsTypedArray(obj)) { - TypedArray *tarray = TypedArray::fromJSObject(obj); + JSObject *tarray = TypedArray::getTypedArray(obj); if (tarray) *vp = Get(tarray); return true; @@ -256,63 +757,68 @@ class TypedArrayGetter { } }; +/* + * For now (until slots directly hold data) + * slots data element points to the JSObject representing the ArrayBuffer. + */ inline Value -getBuffer(TypedArray *tarray) +getBufferValue(JSObject *tarray) { - return ObjectValue(*tarray->bufferJS); + JSObject *buffer = TypedArray::getBuffer(tarray); + return ObjectValue(*buffer); } JSBool TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - return TypedArrayGetter::get(cx, obj, id, vp); + return TypedArrayGetter::get(cx, obj, id, vp); } inline Value -getByteOffset(TypedArray *tarray) +getByteOffsetValue(JSObject *tarray) { - return Int32Value(tarray->byteOffset); + return Int32Value(TypedArray::getByteOffset(tarray)); } JSBool TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - return TypedArrayGetter::get(cx, obj, id, vp); + return TypedArrayGetter::get(cx, obj, id, vp); } inline Value -getByteLength(TypedArray *tarray) +getByteLengthValue(JSObject *tarray) { - return Int32Value(tarray->byteLength); + return Int32Value(TypedArray::getByteLength(tarray)); } JSBool TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - return TypedArrayGetter::get(cx, obj, id, vp); + return TypedArrayGetter::get(cx, obj, id, vp); } inline Value -getLength(TypedArray *tarray) +getLengthValue(JSObject *tarray) { - return Int32Value(tarray->length); + return Int32Value(TypedArray::getLength(tarray)); } JSBool TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - return TypedArrayGetter::get(cx, obj, id, vp); + return TypedArrayGetter::get(cx, obj, id, vp); } JSBool -TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id, - JSObject **objp, JSProperty **propp) +TypedArray::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp) { - TypedArray *tarray = fromJSObject(obj); + JSObject *tarray = getTypedArray(obj); JS_ASSERT(tarray); - if (tarray->isArrayIndex(cx, id)) { - *propp = (JSProperty *) 1; /* non-null to indicate found */ + if (isArrayIndex(cx, tarray, id)) { + *propp = PROPERTY_FOUND; *objp = obj; return true; } @@ -324,19 +830,46 @@ TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id, return true; } - return proto->lookupProperty(cx, id, objp, propp); + return proto->lookupGeneric(cx, id, objp, propp); } -void -TypedArray::obj_trace(JSTracer *trc, JSObject *obj) +JSBool +TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, + JSObject **objp, JSProperty **propp) +{ + return obj_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp); +} + +JSBool +TypedArray::obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, + JSObject **objp, JSProperty **propp) { - TypedArray *tarray = fromJSObject(obj); + JSObject *tarray = getTypedArray(obj); JS_ASSERT(tarray); - MarkObject(trc, *tarray->bufferJS, "typedarray.buffer"); + + if (index < getLength(tarray)) { + *propp = PROPERTY_FOUND; + *objp = obj; + return true; + } + + if (JSObject *proto = obj->getProto()) + return proto->lookupElement(cx, index, objp, propp); + + *objp = NULL; + *propp = NULL; + return true; +} + +JSBool +TypedArray::obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, + JSObject **objp, JSProperty **propp) +{ + return obj_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp); } JSBool -TypedArray::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +TypedArray::obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { *attrsp = (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) ? JSPROP_PERMANENT | JSPROP_READONLY @@ -345,16 +878,68 @@ TypedArray::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attr } JSBool -TypedArray::obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +TypedArray::obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) +{ + *attrsp = JSPROP_PERMANENT | JSPROP_ENUMERATE; + return true; +} + +JSBool +TypedArray::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + *attrsp = JSPROP_PERMANENT | JSPROP_ENUMERATE; + return true; +} + +JSBool +TypedArray::obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SET_ARRAY_ATTRS); + return obj_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp); +} + +JSBool +TypedArray::obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} + +JSBool +TypedArray::obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); return false; } -/* Helper clamped uint8 type */ +JSBool +TypedArray::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} -int32 JS_FASTCALL +JSBool +TypedArray::obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS); + return false; +} + +/* static */ int +TypedArray::lengthOffset() +{ + return JSObject::getFixedSlotOffset(FIELD_LENGTH); +} + +/* static */ int +TypedArray::dataOffset() +{ + return JSObject::getPrivateDataOffset(NUM_FIXED_SLOTS); +} + +/* Helper clamped uint8_t type */ + +int32_t JS_FASTCALL js_TypedArray_uint8_clamp_double(const double x) { // Not < so that NaN coerces to 0 @@ -365,7 +950,7 @@ js_TypedArray_uint8_clamp_double(const double x) return 255; jsdouble toTruncate = x + 0.5; - JSUint8 y = JSUint8(toTruncate); + uint8_t y = uint8_t(toTruncate); /* * now val is rounded to nearest, ties rounded up. We want @@ -387,23 +972,19 @@ js_TypedArray_uint8_clamp_double(const double x) return y; } -JS_DEFINE_CALLINFO_1(extern, INT32, js_TypedArray_uint8_clamp_double, DOUBLE, - 1, nanojit::ACCSET_NONE) - - struct uint8_clamped { - uint8 val; + uint8_t val; uint8_clamped() { } uint8_clamped(const uint8_clamped& other) : val(other.val) { } // invoke our assignment helpers for constructor conversion - uint8_clamped(uint8 x) { *this = x; } - uint8_clamped(uint16 x) { *this = x; } - uint8_clamped(uint32 x) { *this = x; } - uint8_clamped(int8 x) { *this = x; } - uint8_clamped(int16 x) { *this = x; } - uint8_clamped(int32 x) { *this = x; } + uint8_clamped(uint8_t x) { *this = x; } + uint8_clamped(uint16_t x) { *this = x; } + uint8_clamped(uint32_t x) { *this = x; } + uint8_clamped(int8_t x) { *this = x; } + uint8_clamped(int16_t x) { *this = x; } + uint8_clamped(int32_t x) { *this = x; } uint8_clamped(jsdouble x) { *this = x; } inline uint8_clamped& operator= (const uint8_clamped& x) { @@ -411,50 +992,50 @@ struct uint8_clamped { return *this; } - inline uint8_clamped& operator= (uint8 x) { + inline uint8_clamped& operator= (uint8_t x) { val = x; return *this; } - inline uint8_clamped& operator= (uint16 x) { - val = (x > 255) ? 255 : uint8(x); + inline uint8_clamped& operator= (uint16_t x) { + val = (x > 255) ? 255 : uint8_t(x); return *this; } - inline uint8_clamped& operator= (uint32 x) { - val = (x > 255) ? 255 : uint8(x); + inline uint8_clamped& operator= (uint32_t x) { + val = (x > 255) ? 255 : uint8_t(x); return *this; } - inline uint8_clamped& operator= (int8 x) { - val = (x >= 0) ? uint8(x) : 0; + inline uint8_clamped& operator= (int8_t x) { + val = (x >= 0) ? uint8_t(x) : 0; return *this; } - inline uint8_clamped& operator= (int16 x) { + inline uint8_clamped& operator= (int16_t x) { val = (x >= 0) ? ((x < 255) - ? uint8(x) + ? uint8_t(x) : 255) : 0; return *this; } - inline uint8_clamped& operator= (int32 x) { + inline uint8_clamped& operator= (int32_t x) { val = (x >= 0) ? ((x < 255) - ? uint8(x) + ? uint8_t(x) : 255) : 0; return *this; } inline uint8_clamped& operator= (const jsdouble x) { - val = uint8(js_TypedArray_uint8_clamp_double(x)); + val = uint8_t(js_TypedArray_uint8_clamp_double(x)); return *this; } - inline operator uint8() const { + inline operator uint8_t() const { return val; } }; @@ -463,36 +1044,31 @@ struct uint8_clamped { JS_STATIC_ASSERT(sizeof(uint8_clamped) == 1); template static inline const int TypeIDOfType(); -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT8; } -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT8; } -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT16; } -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT16; } -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT32; } -template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT32; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT8; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT8; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT16; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT16; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_INT32; } +template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT32; } template<> inline const int TypeIDOfType() { return TypedArray::TYPE_FLOAT32; } template<> inline const int TypeIDOfType() { return TypedArray::TYPE_FLOAT64; } template<> inline const int TypeIDOfType() { return TypedArray::TYPE_UINT8_CLAMPED; } template static inline const bool TypeIsUnsigned() { return false; } -template<> inline const bool TypeIsUnsigned() { return true; } -template<> inline const bool TypeIsUnsigned() { return true; } -template<> inline const bool TypeIsUnsigned() { return true; } +template<> inline const bool TypeIsUnsigned() { return true; } +template<> inline const bool TypeIsUnsigned() { return true; } +template<> inline const bool TypeIsUnsigned() { return true; } template static inline const bool TypeIsFloatingPoint() { return false; } template<> inline const bool TypeIsFloatingPoint() { return true; } template<> inline const bool TypeIsFloatingPoint() { return true; } -template class TypedArrayTemplate; +template static inline const bool ElementTypeMayBeDouble() { return false; } +template<> inline const bool ElementTypeMayBeDouble() { return true; } +template<> inline const bool ElementTypeMayBeDouble() { return true; } +template<> inline const bool ElementTypeMayBeDouble() { return true; } -typedef TypedArrayTemplate Int8Array; -typedef TypedArrayTemplate Uint8Array; -typedef TypedArrayTemplate Int16Array; -typedef TypedArrayTemplate Uint16Array; -typedef TypedArrayTemplate Int32Array; -typedef TypedArrayTemplate Uint32Array; -typedef TypedArrayTemplate Float32Array; -typedef TypedArrayTemplate Float64Array; -typedef TypedArrayTemplate Uint8ClampedArray; +template class TypedArrayTemplate; template class TypedArrayTemplate @@ -504,8 +1080,9 @@ class TypedArrayTemplate static const int ArrayTypeID() { return TypeIDOfType(); } static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned(); } static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint(); } + static const bool ArrayElementTypeMayBeDouble() { return ElementTypeMayBeDouble(); } - static JSFunctionSpec jsfuncs[]; + static const size_t BYTES_PER_ELEMENT = sizeof(ThisType); static inline Class *slowClass() { @@ -517,96 +1094,132 @@ class TypedArrayTemplate return &TypedArray::fastClasses[ArrayTypeID()]; } + static void + obj_trace(JSTracer *trc, JSObject *obj) + { + MarkValue(trc, obj->getFixedSlotRef(FIELD_BUFFER), "typedarray.buffer"); + } + static JSBool - obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) + obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, + Value *vp) { - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); - JS_ASSERT(tarray); + JSObject *tarray = getTypedArray(obj); - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - vp->setNumber(tarray->length); + if (name == cx->runtime->atomState.lengthAtom) { + vp->setNumber(getLength(tarray)); return true; } - jsuint index; - if (tarray->isArrayIndex(cx, id, &index)) { - // this inline function is specialized for each type - tarray->copyIndexToValue(cx, index, vp); - } else { - JSObject *obj2; - JSProperty *prop; - const Shape *shape; + JSObject *proto = obj->getProto(); + if (!proto) { + vp->setUndefined(); + return true; + } - JSObject *proto = obj->getProto(); - if (!proto) { - vp->setUndefined(); - return true; - } + return proto->getProperty(cx, receiver, name, vp); + } + + static JSBool + obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) + { + JSObject *tarray = getTypedArray(obj); + if (index < getLength(tarray)) { + copyIndexToValue(cx, tarray, index, vp); + return true; + } + + JSObject *proto = obj->getProto(); + if (!proto) { vp->setUndefined(); - if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, &obj2, &prop) < 0) - return false; + return true; + } - if (prop) { - if (obj2->isNative()) { - shape = (Shape *) prop; - if (!js_NativeGet(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, vp)) - return false; - } - } + return proto->getElement(cx, receiver, index, vp); + } + + static JSBool + obj_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp) + { + JSObject *proto = obj->getProto(); + if (!proto) { + vp->setUndefined(); + return true; } - return true; + return proto->getSpecial(cx, receiver, sid, vp); + } + + static JSBool + obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) + { + Value idval = IdToValue(id); + + uint32_t index; + if (IsDefinitelyIndex(idval, &index)) + return obj_getElement(cx, obj, receiver, index, vp); + + SpecialId sid; + if (ValueIsSpecial(obj, &idval, &sid, cx)) + return obj_getSpecial(cx, obj, receiver, sid, vp); + + JSAtom *atom; + if (!js_ValueToAtom(cx, idval, &atom)) + return false; + + if (atom->isIndex(&index)) + return obj_getElement(cx, obj, receiver, index, vp); + + return obj_getProperty(cx, obj, receiver, atom->asPropertyName(), vp); } static JSBool - obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) + obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp, bool *present) { - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); - JS_ASSERT(tarray); + // Fast-path the common case of index < length + JSObject *tarray = getTypedArray(obj); - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - vp->setNumber(tarray->length); + if (index < getLength(tarray)) { + // this inline function is specialized for each type + copyIndexToValue(cx, tarray, index, vp); + *present = true; return true; } - jsuint index; - // We can't just chain to js_SetProperty, because we're not a normal object. - if (!tarray->isArrayIndex(cx, id, &index)) { -#if 0 - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TYPED_ARRAY_BAD_INDEX); - return false; -#endif - // Silent ignore is better than an exception here, because - // at some point we may want to support other properties on - // these objects. This is especially true when these arrays - // are used to implement HTML Canvas 2D's PixelArray objects, - // which used to be plain old arrays. + JSObject *proto = obj->getProto(); + if (!proto) { vp->setUndefined(); return true; } + return proto->getElementIfPresent(cx, receiver, index, vp, present); + } + + static bool + setElementTail(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp, JSBool strict) + { + JS_ASSERT(tarray); + JS_ASSERT(index < getLength(tarray)); + if (vp->isInt32()) { - tarray->setIndex(index, NativeType(vp->toInt32())); + setIndex(tarray, index, NativeType(vp->toInt32())); return true; } jsdouble d; - if (vp->isDouble()) { d = vp->toDouble(); } else if (vp->isNull()) { - d = 0.0f; + d = 0.0; } else if (vp->isPrimitive()) { JS_ASSERT(vp->isString() || vp->isUndefined() || vp->isBoolean()); if (vp->isString()) { - // note that ValueToNumber will always succeed with a string arg - ValueToNumber(cx, *vp, &d); + JS_ALWAYS_TRUE(ToNumber(cx, *vp, &d)); } else if (vp->isUndefined()) { d = js_NaN; } else { - d = (double) vp->toBoolean(); + d = double(vp->toBoolean()); } } else { // non-primitive assignments become NaN or 0 (for float/int arrays) @@ -619,47 +1232,133 @@ class TypedArrayTemplate // Assign based on characteristics of the destination type if (ArrayTypeIsFloatingPoint()) { - tarray->setIndex(index, NativeType(d)); + setIndex(tarray, index, NativeType(d)); } else if (ArrayTypeIsUnsigned()) { JS_ASSERT(sizeof(NativeType) <= 4); - uint32 n = js_DoubleToECMAUint32(d); - tarray->setIndex(index, NativeType(n)); + uint32_t n = js_DoubleToECMAUint32(d); + setIndex(tarray, index, NativeType(n)); } else if (ArrayTypeID() == TypedArray::TYPE_UINT8_CLAMPED) { // The uint8_clamped type has a special rounding converter // for doubles. - tarray->setIndex(index, NativeType(d)); + setIndex(tarray, index, NativeType(d)); } else { JS_ASSERT(sizeof(NativeType) <= 4); - int32 n = js_DoubleToECMAInt32(d); - tarray->setIndex(index, NativeType(n)); + int32_t n = js_DoubleToECMAInt32(d); + setIndex(tarray, index, NativeType(n)); } return true; } static JSBool - obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v, - PropertyOp getter, StrictPropertyOp setter, uintN attrs) + obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) + { + JSObject *tarray = getTypedArray(obj); + JS_ASSERT(tarray); + + if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + vp->setNumber(getLength(tarray)); + return true; + } + + jsuint index; + // We can't just chain to js_SetPropertyHelper, because we're not a normal object. + if (!isArrayIndex(cx, tarray, id, &index)) { + // Silent ignore is better than an exception here, because + // at some point we may want to support other properties on + // these objects. This is especially true when these arrays + // are used to implement HTML Canvas 2D's PixelArray objects, + // which used to be plain old arrays. + vp->setUndefined(); + return true; + } + + return setElementTail(cx, tarray, index, vp, strict); + } + + static JSBool + obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) + { + return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); + } + + static JSBool + obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) + { + JSObject *tarray = getTypedArray(obj); + JS_ASSERT(tarray); + + if (index >= getLength(tarray)) { + // Silent ignore is better than an exception here, because + // at some point we may want to support other properties on + // these objects. This is especially true when these arrays + // are used to implement HTML Canvas 2D's PixelArray objects, + // which used to be plain old arrays. + vp->setUndefined(); + return true; + } + + return setElementTail(cx, tarray, index, vp, strict); + } + + static JSBool + obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict) + { + return obj_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict); + } + + static JSBool + obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) { if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return true; Value tmp = *v; - return obj_setProperty(cx, obj, id, &tmp, false); + return obj_setGeneric(cx, obj, id, &tmp, false); } static JSBool - obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) + obj_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) { - if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { + return obj_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs); + } + + static JSBool + obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) + { + Value tmp = *v; + return obj_setElement(cx, obj, index, &tmp, false); + } + + static JSBool + obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) + { + return obj_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs); + } + + static JSBool + obj_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict) + { + if (name == cx->runtime->atomState.lengthAtom) { rval->setBoolean(false); return true; } - TypedArray *tarray = TypedArray::fromJSObject(obj); + rval->setBoolean(true); + return true; + } + + static JSBool + obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) + { + JSObject *tarray = TypedArray::getTypedArray(obj); JS_ASSERT(tarray); - if (tarray->isArrayIndex(cx, id)) { + if (index < getLength(tarray)) { rval->setBoolean(false); return true; } @@ -668,11 +1367,18 @@ class TypedArrayTemplate return true; } + static JSBool + obj_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) + { + rval->setBoolean(true); + return true; + } + static JSBool obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp) { - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); + JSObject *tarray = getTypedArray(obj); JS_ASSERT(tarray); /* @@ -684,13 +1390,13 @@ class TypedArrayTemplate case JSENUMERATE_INIT_ALL: statep->setBoolean(true); if (idp) - *idp = ::INT_TO_JSID(tarray->length + 1); + *idp = ::INT_TO_JSID(getLength(tarray) + 1); break; case JSENUMERATE_INIT: statep->setInt32(0); if (idp) - *idp = ::INT_TO_JSID(tarray->length); + *idp = ::INT_TO_JSID(getLength(tarray)); break; case JSENUMERATE_NEXT: @@ -698,12 +1404,12 @@ class TypedArrayTemplate *idp = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); statep->setInt32(0); } else { - uint32 index = statep->toInt32(); - if (index < uint32(tarray->length)) { + uint32_t index = statep->toInt32(); + if (index < getLength(tarray)) { *idp = ::INT_TO_JSID(index); statep->setInt32(index + 1); } else { - JS_ASSERT(index == tarray->length); + JS_ASSERT(index == getLength(tarray)); statep->setNull(); } } @@ -724,23 +1430,55 @@ class TypedArrayTemplate } static JSObject * - createTypedArray(JSContext *cx, JSObject *bufobj, uint32 byteOffset, uint32 len) + createTypedArray(JSContext *cx, JSObject *bufobj, uint32_t byteOffset, uint32_t len) { + JS_ASSERT(bufobj->isArrayBuffer()); JSObject *obj = NewBuiltinClassInstance(cx, slowClass()); if (!obj) return NULL; + JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8); - ThisTypeArray *tarray = cx->new_(bufobj, byteOffset, len); - if (!tarray) + /* + * Specialize the type of the object on the current scripted location, + * and mark the type as definitely a typed array. + */ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(slowClass()); + types::TypeObject *type = types::GetTypeCallerInitObject(cx, key); + if (!type) return NULL; + obj->setType(type); + + obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID())); + obj->setSlot(FIELD_BUFFER, ObjectValue(*bufobj)); + + /* + * N.B. The base of the array's data is stored in the object's + * private data rather than a slot, to avoid alignment restrictions + * on private Values. + */ + obj->setPrivate(bufobj->arrayBufferDataOffset() + byteOffset); + + obj->setSlot(FIELD_LENGTH, Int32Value(len)); + obj->setSlot(FIELD_BYTEOFFSET, Int32Value(byteOffset)); + obj->setSlot(FIELD_BYTELENGTH, Int32Value(len * sizeof(NativeType))); + + DebugOnly bufferByteLength = getBuffer(obj)->arrayBufferByteLength(); + JS_ASSERT(bufferByteLength - getByteOffset(obj) >= getByteLength(obj)); + JS_ASSERT(getByteOffset(obj) <= bufferByteLength); + JS_ASSERT(getBuffer(obj)->arrayBufferDataOffset() <= getDataOffset(obj)); + JS_ASSERT(getDataOffset(obj) <= offsetData(obj, bufferByteLength)); JS_ASSERT(obj->getClass() == slowClass()); - obj->setSharedNonNativeMap(); - obj->clasp = fastClass(); - obj->setPrivate(tarray); - // FIXME Bug 599008: make it ok to call preventExtensions here. - obj->flags |= JSObject::NOT_EXTENSIBLE; + js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(), + obj->getProto(), obj->getParent(), + gc::FINALIZE_OBJECT8, + BaseShape::NOT_EXTENSIBLE); + if (!empty) + return NULL; + obj->setLastPropertyInfallible(empty); + + JS_ASSERT(obj->numFixedSlots() == NUM_FIXED_SLOTS); return obj; } @@ -788,16 +1526,16 @@ class TypedArrayTemplate /* (typedArray) */ if (js_IsTypedArray(dataObj)) { - TypedArray *otherTypedArray = TypedArray::fromJSObject(dataObj); + JSObject *otherTypedArray = getTypedArray(dataObj); JS_ASSERT(otherTypedArray); - uint32 len = otherTypedArray->length; + uint32_t len = getLength(otherTypedArray); JSObject *bufobj = createBufferWithSizeAndCount(cx, len); if (!bufobj) return NULL; JSObject *obj = createTypedArray(cx, bufobj, 0, len); - if (!obj || !copyFrom(cx, obj, otherTypedArray, 0)) + if (!obj || !copyFromTypedArray(cx, obj, otherTypedArray, 0)) return NULL; return obj; } @@ -807,7 +1545,7 @@ class TypedArrayTemplate int32_t length = -1; if (argc > 1) { - if (!ValueToInt32(cx, argv[1], &byteOffset)) + if (!ToInt32(cx, argv[1], &byteOffset)) return NULL; if (byteOffset < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -816,7 +1554,7 @@ class TypedArrayTemplate } if (argc > 2) { - if (!ValueToInt32(cx, argv[2], &length)) + if (!ToInt32(cx, argv[2], &length)) return NULL; if (length < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -830,58 +1568,32 @@ class TypedArrayTemplate return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length); } - static void - class_finalize(JSContext *cx, JSObject *obj) - { - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); - if (tarray) - cx->delete_(tarray); - } - /* subarray(start[, end]) */ static JSBool fun_subarray(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - if (obj->getClass() != fastClass()) { - // someone tried to apply this subarray() to the wrong class - ReportIncompatibleMethod(cx, vp, fastClass()); - return false; - } + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, fun_subarray, fastClass(), &ok); + if (!obj) + return ok; - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); + JSObject *tarray = getTypedArray(obj); if (!tarray) return true; // these are the default values - int32_t begin = 0, end = tarray->length; - int32_t length = int32(tarray->length); + int32_t begin = 0, end = getLength(tarray); + int32_t length = int32_t(getLength(tarray)); - if (argc > 0) { - Value *argv = JS_ARGV(cx, vp); - if (!ValueToInt32(cx, argv[0], &begin)) + if (args.length() > 0) { + if (!ToClampedIndex(cx, args[0], length, &begin)) return false; - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } - if (argc > 1) { - if (!ValueToInt32(cx, argv[1], &end)) + if (args.length() > 1) { + if (!ToClampedIndex(cx, args[1], length, &end)) return false; - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else if (end > length) { - end = length; - } } } @@ -891,7 +1603,7 @@ class TypedArrayTemplate JSObject *nobj = createSubarray(cx, tarray, begin, end); if (!nobj) return false; - vp->setObject(*nobj); + args.rval().setObject(*nobj); return true; } @@ -899,29 +1611,25 @@ class TypedArrayTemplate static JSBool fun_set(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); - if (!obj) - return false; + CallArgs args = CallArgsFromVp(argc, vp); - if (obj->getClass() != fastClass()) { - // someone tried to apply this set() to the wrong class - ReportIncompatibleMethod(cx, vp, fastClass()); - return false; - } + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, fun_set, fastClass(), &ok); + if (!obj) + return ok; - ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj); + JSObject *tarray = getTypedArray(obj); if (!tarray) return true; // these are the default values int32_t off = 0; - Value *argv = JS_ARGV(cx, vp); - if (argc > 1) { - if (!ValueToInt32(cx, argv[1], &off)) + if (args.length() > 1) { + if (!ToInt32(cx, args[1], &off)) return false; - if (off < 0 || uint32_t(off) > tarray->length) { + if (off < 0 || uint32_t(off) > getLength(tarray)) { // the given offset is bogus JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); @@ -929,27 +1637,27 @@ class TypedArrayTemplate } } - uint32 offset(off); + uint32_t offset(off); // first arg must be either a typed array or a JS array - if (argc == 0 || !argv[0].isObject()) { + if (args.length() == 0 || !args[0].isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } - JSObject *arg0 = argv[0].toObjectOrNull(); + JSObject *arg0 = args[0].toObjectOrNull(); if (js_IsTypedArray(arg0)) { - TypedArray *src = TypedArray::fromJSObject(arg0); + JSObject *src = TypedArray::getTypedArray(arg0); if (!src || - src->length > tarray->length - offset) + getLength(src) > getLength(tarray) - offset) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } - if (!copyFrom(cx, obj, src, offset)) + if (!copyFromTypedArray(cx, obj, src, offset)) return false; } else { jsuint len; @@ -957,90 +1665,60 @@ class TypedArrayTemplate return false; // avoid overflow; we know that offset <= length - if (len > tarray->length - offset) { + if (len > getLength(tarray) - offset) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } - if (!copyFrom(cx, obj, arg0, len, offset)) + if (!copyFromArray(cx, obj, arg0, len, offset)) return false; } - vp->setUndefined(); + args.rval().setUndefined(); return true; } - static ThisTypeArray * - fromJSObject(JSObject *obj) - { - JS_ASSERT(obj->getClass() == fastClass()); - return reinterpret_cast(obj->getPrivate()); - } - public: - TypedArrayTemplate(JSObject *bufobj, uint32 byteOffset, uint32 len) - { - JS_ASSERT(bufobj->getClass() == &ArrayBuffer::jsclass); - - type = ArrayTypeID(); - bufferJS = bufobj; - buffer = ArrayBuffer::fromJSObject(bufobj); - - this->byteOffset = byteOffset; - - JS_ASSERT(byteOffset <= buffer->byteLength); - this->data = buffer->offsetData(byteOffset); - JS_ASSERT(buffer->data <= this->data); - JS_ASSERT(this->data <= buffer->offsetData(buffer->byteLength)); - - this->byteLength = len * sizeof(NativeType); - JS_ASSERT(buffer->byteLength - byteOffset >= this->byteLength); - - this->length = len; - } - static JSObject * createTypedArrayWithOffsetLength(JSContext *cx, JSObject *other, - int32 byteOffsetInt, int32 lengthInt) + int32_t byteOffsetInt, int32_t lengthInt) { JS_ASSERT(!js_IsTypedArray(other)); /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */ - ArrayBuffer *abuf; - if (other->getClass() == &ArrayBuffer::jsclass && - ((abuf = ArrayBuffer::fromJSObject(other)) != NULL)) { - uint32 boffset = (byteOffsetInt < 0) ? 0 : uint32(byteOffsetInt); + if (other->isArrayBuffer()) { + uint32_t boffset = (byteOffsetInt < 0) ? 0 : uint32_t(byteOffsetInt); - if (boffset > abuf->byteLength || boffset % sizeof(NativeType) != 0) { + if (boffset > other->arrayBufferByteLength() || boffset % sizeof(NativeType) != 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return NULL; // invalid byteOffset } - uint32 len; + uint32_t len; if (lengthInt < 0) { - len = (abuf->byteLength - boffset) / sizeof(NativeType); - if (len * sizeof(NativeType) != (abuf->byteLength - boffset)) { + len = (other->arrayBufferByteLength() - boffset) / sizeof(NativeType); + if (len * sizeof(NativeType) != (other->arrayBufferByteLength() - boffset)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return NULL; // given byte array doesn't map exactly to sizeof(NativeType)*N } } else { - len = (uint32) lengthInt; + len = (uint32_t) lengthInt; } // Go slowly and check for overflow. - uint32 arrayByteLength = len*sizeof(NativeType); - if (uint32(len) >= INT32_MAX / sizeof(NativeType) || - uint32(boffset) >= INT32_MAX - arrayByteLength) + uint32_t arrayByteLength = len*sizeof(NativeType); + if (uint32_t(len) >= INT32_MAX / sizeof(NativeType) || + uint32_t(boffset) >= INT32_MAX - arrayByteLength) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return NULL; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType) } - if (arrayByteLength + boffset > abuf->byteLength) { + if (arrayByteLength + boffset > other->arrayBufferByteLength()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return NULL; // boffset+len is too big for the arraybuffer @@ -1062,102 +1740,112 @@ class TypedArrayTemplate return NULL; JSObject *obj = createTypedArray(cx, bufobj, 0, len); - if (!obj || !copyFrom(cx, obj, other, len)) + if (!obj || !copyFromArray(cx, obj, other, len)) return NULL; return obj; } - const NativeType - getIndex(uint32 index) const + static const NativeType + getIndex(JSObject *obj, uint32_t index) { - return *(static_cast(data) + index); + return *(static_cast(getDataOffset(obj)) + index); } - void - setIndex(uint32 index, NativeType val) + static void + setIndex(JSObject *obj, uint32_t index, NativeType val) { - *(static_cast(data) + index) = val; + *(static_cast(getDataOffset(obj)) + index) = val; } - inline void copyIndexToValue(JSContext *cx, uint32 index, Value *vp); + static void copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp); static JSObject * - createSubarray(JSContext *cx, ThisTypeArray *tarray, uint32 begin, uint32 end) + createSubarray(JSContext *cx, JSObject *tarray, uint32_t begin, uint32_t end) { JS_ASSERT(tarray); JS_ASSERT(0 <= begin); - JS_ASSERT(begin <= tarray->length); + JS_ASSERT(begin <= getLength(tarray)); JS_ASSERT(0 <= end); - JS_ASSERT(end <= tarray->length); + JS_ASSERT(end <= getLength(tarray)); - JSObject *bufobj = tarray->bufferJS; + JSObject *bufobj = getBuffer(tarray); JS_ASSERT(bufobj); JS_ASSERT(begin <= end); - uint32 length = end - begin; + uint32_t length = end - begin; JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType)); - JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= tarray->byteOffset); - uint32 byteOffset = tarray->byteOffset + begin * sizeof(NativeType); + JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= getByteOffset(tarray)); + uint32_t byteOffset = getByteOffset(tarray) + begin * sizeof(NativeType); return createTypedArray(cx, bufobj, byteOffset, length); } protected: + static NativeType + nativeFromDouble(double d) + { + if (!ArrayTypeIsFloatingPoint() && JS_UNLIKELY(JSDOUBLE_IS_NaN(d))) + return NativeType(int32_t(0)); + if (TypeIsFloatingPoint()) + return NativeType(d); + if (TypeIsUnsigned()) + return NativeType(js_DoubleToECMAUint32(d)); + return NativeType(js_DoubleToECMAInt32(d)); + } + static NativeType nativeFromValue(JSContext *cx, const Value &v) { if (v.isInt32()) return NativeType(v.toInt32()); - if (v.isDouble()) { - double d = v.toDouble(); - if (!ArrayTypeIsFloatingPoint() && JS_UNLIKELY(JSDOUBLE_IS_NaN(d))) - return NativeType(int32(0)); - if (TypeIsFloatingPoint()) - return NativeType(d); - if (TypeIsUnsigned()) - return NativeType(js_DoubleToECMAUint32(d)); - return NativeType(js_DoubleToECMAInt32(d)); - } + if (v.isDouble()) + return nativeFromDouble(v.toDouble()); - if (v.isPrimitive() && !v.isMagic()) { + /* + * The condition guarantees that holes and undefined values + * are treated identically. + */ + if (v.isPrimitive() && !v.isMagic() && !v.isUndefined()) { jsdouble dval; - ValueToNumber(cx, v, &dval); - return NativeType(dval); + JS_ALWAYS_TRUE(ToNumber(cx, v, &dval)); + return nativeFromDouble(dval); } - if (ArrayTypeIsFloatingPoint()) - return NativeType(js_NaN); - - return NativeType(int32(0)); + return ArrayTypeIsFloatingPoint() + ? NativeType(js_NaN) + : NativeType(int32_t(0)); } static bool - copyFrom(JSContext *cx, JSObject *thisTypedArrayObj, + copyFromArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *ar, jsuint len, jsuint offset = 0) { - ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj); - JS_ASSERT(thisTypedArray); + thisTypedArrayObj = getTypedArray(thisTypedArrayObj); + JS_ASSERT(thisTypedArrayObj); - JS_ASSERT(offset <= thisTypedArray->length); - JS_ASSERT(len <= thisTypedArray->length - offset); - NativeType *dest = static_cast(thisTypedArray->data) + offset; + JS_ASSERT(offset <= getLength(thisTypedArrayObj)); + JS_ASSERT(len <= getLength(thisTypedArrayObj) - offset); + NativeType *dest = static_cast(getDataOffset(thisTypedArrayObj)) + offset; - if (ar->isDenseArray() && ar->getDenseArrayCapacity() >= len) { + if (ar->isDenseArray() && ar->getDenseArrayInitializedLength() >= len) { JS_ASSERT(ar->getArrayLength() == len); - Value *src = ar->getDenseArrayElements(); + const Value *src = ar->getDenseArrayElements(); + /* + * It is valid to skip the hole check here because nativeFromValue + * treats a hole as undefined. + */ for (uintN i = 0; i < len; ++i) *dest++ = nativeFromValue(cx, *src++); } else { - // slow path Value v; for (uintN i = 0; i < len; ++i) { - if (!ar->getProperty(cx, ::INT_TO_JSID(i), &v)) + if (!ar->getElement(cx, i, &v)) return false; *dest++ = nativeFromValue(cx, v); } @@ -1167,70 +1855,70 @@ class TypedArrayTemplate } static bool - copyFrom(JSContext *cx, JSObject *thisTypedArrayObj, TypedArray *tarray, jsuint offset) + copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, jsuint offset) { - ThisTypeArray *thisTypedArray = fromJSObject(thisTypedArrayObj); - JS_ASSERT(thisTypedArray); + thisTypedArrayObj = getTypedArray(thisTypedArrayObj); + JS_ASSERT(thisTypedArrayObj); - JS_ASSERT(offset <= thisTypedArray->length); - JS_ASSERT(tarray->length <= thisTypedArray->length - offset); - if (tarray->buffer == thisTypedArray->buffer) - return thisTypedArray->copyFromWithOverlap(cx, tarray, offset); + JS_ASSERT(offset <= getLength(thisTypedArrayObj)); + JS_ASSERT(getLength(tarray) <= getLength(thisTypedArrayObj) - offset); + if (getBuffer(tarray) == getBuffer(thisTypedArrayObj)) + return copyFromWithOverlap(cx, thisTypedArrayObj, tarray, offset); - NativeType *dest = static_cast(thisTypedArray->data) + offset; + NativeType *dest = static_cast((void*)getDataOffset(thisTypedArrayObj)) + offset; - if (tarray->type == thisTypedArray->type) { - memcpy(dest, tarray->data, tarray->byteLength); + if (getType(tarray) == getType(thisTypedArrayObj)) { + js_memcpy(dest, getDataOffset(tarray), getByteLength(tarray)); return true; } - uintN srclen = tarray->length; - switch (tarray->type) { + uintN srclen = getLength(tarray); + switch (getType(tarray)) { case TypedArray::TYPE_INT8: { - int8 *src = static_cast(tarray->data); + int8_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT8: case TypedArray::TYPE_UINT8_CLAMPED: { - uint8 *src = static_cast(tarray->data); + uint8_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT16: { - int16 *src = static_cast(tarray->data); + int16_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT16: { - uint16 *src = static_cast(tarray->data); + uint16_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT32: { - int32 *src = static_cast(tarray->data); + int32_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT32: { - uint32 *src = static_cast(tarray->data); + uint32_t *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT32: { - float *src = static_cast(tarray->data); + float *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT64: { - double *src = static_cast(tarray->data); + double *src = static_cast(getDataOffset(tarray)); for (uintN i = 0; i < srclen; ++i) *dest++ = NativeType(*src++); break; @@ -1243,72 +1931,72 @@ class TypedArrayTemplate return true; } - bool - copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset) + static bool + copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, jsuint offset) { - JS_ASSERT(offset <= length); + JS_ASSERT(offset <= getLength(self)); - NativeType *dest = static_cast(data) + offset; + NativeType *dest = static_cast(getDataOffset(self)) + offset; - if (tarray->type == type) { - memmove(dest, tarray->data, tarray->byteLength); + if (getType(tarray) == getType(self)) { + memmove(dest, getDataOffset(tarray), getByteLength(tarray)); return true; } // We have to make a copy of the source array here, since // there's overlap, and we have to convert types. - void *srcbuf = cx->malloc_(tarray->byteLength); + void *srcbuf = cx->malloc_(getLength(tarray)); if (!srcbuf) return false; - memcpy(srcbuf, tarray->data, tarray->byteLength); + js_memcpy(srcbuf, getDataOffset(tarray), getByteLength(tarray)); - switch (tarray->type) { + switch (getType(tarray)) { case TypedArray::TYPE_INT8: { - int8 *src = (int8*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + int8_t *src = (int8_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT8: case TypedArray::TYPE_UINT8_CLAMPED: { - uint8 *src = (uint8*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + uint8_t *src = (uint8_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT16: { - int16 *src = (int16*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + int16_t *src = (int16_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT16: { - uint16 *src = (uint16*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + uint16_t *src = (uint16_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT32: { - int32 *src = (int32*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + int32_t *src = (int32_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT32: { - uint32 *src = (uint32*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + uint32_t *src = (uint32_t*) srcbuf; + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT32: { float *src = (float*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT64: { double *src = (double*) srcbuf; - for (uintN i = 0; i < tarray->length; ++i) + for (uintN i = 0; i < getLength(tarray); ++i) *dest++ = NativeType(*src++); break; } @@ -1321,8 +2009,13 @@ class TypedArrayTemplate return true; } + static void * + offsetData(JSObject *obj, uint32_t offs) { + return (void*)(((uint8_t*)getDataOffset(obj)) + offs); + } + static JSObject * - createBufferWithSizeAndCount(JSContext *cx, uint32 count) + createBufferWithSizeAndCount(JSContext *cx, uint32_t count) { size_t size = sizeof(NativeType); if (size != 0 && count >= INT32_MAX / size) { @@ -1331,44 +2024,99 @@ class TypedArrayTemplate return NULL; } - int32 bytelen = size * count; + int32_t bytelen = size * count; return ArrayBuffer::create(cx, bytelen); } }; +class Int8Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_INT8 }; + static const JSProtoKey key = JSProto_Int8Array; + static JSFunctionSpec jsfuncs[]; +}; +class Uint8Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_UINT8 }; + static const JSProtoKey key = JSProto_Uint8Array; + static JSFunctionSpec jsfuncs[]; +}; +class Int16Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_INT16 }; + static const JSProtoKey key = JSProto_Int16Array; + static JSFunctionSpec jsfuncs[]; +}; +class Uint16Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_UINT16 }; + static const JSProtoKey key = JSProto_Uint16Array; + static JSFunctionSpec jsfuncs[]; +}; +class Int32Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_INT32 }; + static const JSProtoKey key = JSProto_Int32Array; + static JSFunctionSpec jsfuncs[]; +}; +class Uint32Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_UINT32 }; + static const JSProtoKey key = JSProto_Uint32Array; + static JSFunctionSpec jsfuncs[]; +}; +class Float32Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_FLOAT32 }; + static const JSProtoKey key = JSProto_Float32Array; + static JSFunctionSpec jsfuncs[]; +}; +class Float64Array : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_FLOAT64 }; + static const JSProtoKey key = JSProto_Float64Array; + static JSFunctionSpec jsfuncs[]; +}; +class Uint8ClampedArray : public TypedArrayTemplate { + public: + enum { ACTUAL_TYPE = TYPE_UINT8_CLAMPED }; + static const JSProtoKey key = JSProto_Uint8ClampedArray; + static JSFunctionSpec jsfuncs[]; +}; + // this default implementation is only valid for integer types // less than 32-bits in size. template void -TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value *vp) +TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp) { JS_STATIC_ASSERT(sizeof(NativeType) < 4); - vp->setInt32(getIndex(index)); + vp->setInt32(getIndex(tarray, index)); } // and we need to specialize for 32-bit integers and floats template<> void -TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value *vp) +TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp) { - int32 val = getIndex(index); + int32_t val = getIndex(tarray, index); vp->setInt32(val); } template<> void -TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value *vp) +TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp) { - uint32 val = getIndex(index); + uint32_t val = getIndex(tarray, index); vp->setNumber(val); } template<> void -TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value *vp) +TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp) { - float val = getIndex(index); + float val = getIndex(tarray, index); double dval = val; /* @@ -1389,9 +2137,9 @@ TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value * template<> void -TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value *vp) +TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp) { - double val = getIndex(index); + double val = getIndex(tarray, index); /* * Doubles in typed arrays could be typed-punned arrays of integers. This @@ -1414,28 +2162,91 @@ TypedArrayTemplate::copyIndexToValue(JSContext *cx, uint32 index, Value * ArrayBuffer (base) */ -Class ArrayBuffer::jsclass = { +Class ArrayBuffer::slowClass = { + "ArrayBuffer", + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub +}; + +Class js::ArrayBufferClass = { "ArrayBuffer", JSCLASS_HAS_PRIVATE | - JSCLASS_CONCURRENT_FINALIZER | + Class::NON_NATIVE | + JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - ArrayBuffer::class_finalize, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + ArrayBuffer::obj_trace, + JS_NULL_CLASS_EXT, + { + ArrayBuffer::obj_lookupGeneric, + ArrayBuffer::obj_lookupProperty, + ArrayBuffer::obj_lookupElement, + ArrayBuffer::obj_lookupSpecial, + ArrayBuffer::obj_defineGeneric, + ArrayBuffer::obj_defineProperty, + ArrayBuffer::obj_defineElement, + ArrayBuffer::obj_defineSpecial, + ArrayBuffer::obj_getGeneric, + ArrayBuffer::obj_getProperty, + ArrayBuffer::obj_getElement, + ArrayBuffer::obj_getElementIfPresent, + ArrayBuffer::obj_getSpecial, + ArrayBuffer::obj_setGeneric, + ArrayBuffer::obj_setProperty, + ArrayBuffer::obj_setElement, + ArrayBuffer::obj_setSpecial, + ArrayBuffer::obj_getGenericAttributes, + ArrayBuffer::obj_getPropertyAttributes, + ArrayBuffer::obj_getElementAttributes, + ArrayBuffer::obj_getSpecialAttributes, + ArrayBuffer::obj_setGenericAttributes, + ArrayBuffer::obj_setPropertyAttributes, + ArrayBuffer::obj_setElementAttributes, + ArrayBuffer::obj_setSpecialAttributes, + ArrayBuffer::obj_deleteProperty, + ArrayBuffer::obj_deleteElement, + ArrayBuffer::obj_deleteSpecial, + ArrayBuffer::obj_enumerate, + ArrayBuffer::obj_typeOf, + NULL, /* thisObject */ + NULL, /* clear */ + } }; JSPropertySpec ArrayBuffer::jsprops[] = { { "byteLength", -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY, - Jsvalify(ArrayBuffer::prop_getByteLength), JS_StrictPropertyStub }, + ArrayBuffer::prop_getByteLength, JS_StrictPropertyStub }, {0,0,0,0,0} }; +JSFunctionSpec ArrayBuffer::jsfuncs[] = { + JS_FN("slice", ArrayBuffer::fun_slice, 2, JSFUN_GENERIC_NATIVE), + JS_FS_END +}; + /* * shared TypedArray */ @@ -1443,16 +2254,16 @@ JSPropertySpec ArrayBuffer::jsprops[] = { JSPropertySpec TypedArray::jsprops[] = { { js_length_str, -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY, - Jsvalify(TypedArray::prop_getLength), JS_StrictPropertyStub }, + TypedArray::prop_getLength, JS_StrictPropertyStub }, { "byteLength", -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY, - Jsvalify(TypedArray::prop_getByteLength), JS_StrictPropertyStub }, + TypedArray::prop_getByteLength, JS_StrictPropertyStub }, { "byteOffset", -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY, - Jsvalify(TypedArray::prop_getByteOffset), JS_StrictPropertyStub }, + TypedArray::prop_getByteOffset, JS_StrictPropertyStub }, { "buffer", -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY, - Jsvalify(TypedArray::prop_getBuffer), JS_StrictPropertyStub }, + TypedArray::prop_getBuffer, JS_StrictPropertyStub }, {0,0,0,0,0} }; @@ -1461,86 +2272,131 @@ JSPropertySpec TypedArray::jsprops[] = { */ #define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ -template<> JSFunctionSpec _typedArray::jsfuncs[] = { \ - JS_FN("subarray", _typedArray::fun_subarray, 2, 0), \ - JS_FN("set", _typedArray::fun_set, 2, 0), \ +JSFunctionSpec _typedArray::jsfuncs[] = { \ + JS_FN("subarray", _typedArray::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ + JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE), \ JS_FS_END \ } #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray) \ { \ #_typedArray, \ - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ - PropertyStub, /* addProperty */ \ - PropertyStub, /* delProperty */ \ - PropertyStub, /* getProperty */ \ - StrictPropertyStub, /* setProperty */ \ - EnumerateStub, \ - ResolveStub, \ - ConvertStub, \ - FinalizeStub \ + JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ + JSCLASS_HAS_PRIVATE | \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ + JS_PropertyStub, /* addProperty */ \ + JS_PropertyStub, /* delProperty */ \ + JS_PropertyStub, /* getProperty */ \ + JS_StrictPropertyStub, /* setProperty */ \ + JS_EnumerateStub, \ + JS_ResolveStub, \ + JS_ConvertStub, \ + JS_FinalizeStub \ } #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \ { \ #_typedArray, \ - Class::NON_NATIVE | JSCLASS_HAS_PRIVATE, \ - PropertyStub, /* addProperty */ \ - PropertyStub, /* delProperty */ \ - PropertyStub, /* getProperty */ \ - StrictPropertyStub, /* setProperty */ \ - EnumerateStub, \ - ResolveStub, \ - ConvertStub, \ - _typedArray::class_finalize, \ - NULL, /* reserved0 */ \ - NULL, /* checkAccess */ \ - NULL, /* call */ \ - NULL, /* construct */ \ - NULL, /* xdrObject */ \ - NULL, /* hasInstance */ \ - _typedArray::obj_trace, \ - JS_NULL_CLASS_EXT, \ + JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ + JSCLASS_HAS_PRIVATE | \ + JSCLASS_FOR_OF_ITERATION | \ + Class::NON_NATIVE, \ + JS_PropertyStub, /* addProperty */ \ + JS_PropertyStub, /* delProperty */ \ + JS_PropertyStub, /* getProperty */ \ + JS_StrictPropertyStub, /* setProperty */ \ + JS_EnumerateStub, \ + JS_ResolveStub, \ + JS_ConvertStub, \ + NULL, /* finalize */ \ + NULL, /* reserved0 */ \ + NULL, /* checkAccess */ \ + NULL, /* call */ \ + NULL, /* construct */ \ + NULL, /* xdrObject */ \ + NULL, /* hasInstance */ \ + _typedArray::obj_trace, /* trace */ \ { \ + NULL, /* equality */ \ + NULL, /* outerObject */ \ + NULL, /* innerObject */ \ + JS_ElementIteratorStub, \ + NULL, /* unused */ \ + false, /* isWrappedNative */ \ + }, \ + { \ + _typedArray::obj_lookupGeneric, \ _typedArray::obj_lookupProperty, \ + _typedArray::obj_lookupElement, \ + _typedArray::obj_lookupSpecial, \ + _typedArray::obj_defineGeneric, \ _typedArray::obj_defineProperty, \ + _typedArray::obj_defineElement, \ + _typedArray::obj_defineSpecial, \ + _typedArray::obj_getGeneric, \ _typedArray::obj_getProperty, \ + _typedArray::obj_getElement, \ + _typedArray::obj_getElementIfPresent, \ + _typedArray::obj_getSpecial, \ + _typedArray::obj_setGeneric, \ _typedArray::obj_setProperty, \ - _typedArray::obj_getAttributes, \ - _typedArray::obj_setAttributes, \ + _typedArray::obj_setElement, \ + _typedArray::obj_setSpecial, \ + _typedArray::obj_getGenericAttributes, \ + _typedArray::obj_getPropertyAttributes, \ + _typedArray::obj_getElementAttributes, \ + _typedArray::obj_getSpecialAttributes, \ + _typedArray::obj_setGenericAttributes, \ + _typedArray::obj_setPropertyAttributes, \ + _typedArray::obj_setElementAttributes, \ + _typedArray::obj_setSpecialAttributes, \ _typedArray::obj_deleteProperty, \ + _typedArray::obj_deleteElement, \ + _typedArray::obj_deleteSpecial, \ _typedArray::obj_enumerate, \ _typedArray::obj_typeOf, \ - NULL, /* thisObject */ \ - NULL, /* clear */ \ + NULL, /* thisObject */ \ + NULL, /* clear */ \ } \ } -#define INIT_TYPED_ARRAY_CLASS(_typedArray,_type) \ -do { \ - proto = js_InitClass(cx, obj, NULL, \ - &TypedArray::slowClasses[TypedArray::_type], \ - _typedArray::class_constructor, 3, \ - _typedArray::jsprops, \ - _typedArray::jsfuncs, \ - NULL, NULL); \ - if (!proto) \ - return NULL; \ - JSObject *ctor = JS_GetConstructor(cx, proto); \ - if (!ctor || \ - !JS_DefineProperty(cx, ctor, "BYTES_PER_ELEMENT", \ - INT_TO_JSVAL(sizeof(_typedArray::ThisType)), \ - JS_PropertyStub, JS_StrictPropertyStub, \ - JSPROP_PERMANENT | JSPROP_READONLY) || \ - !JS_DefineProperty(cx, proto, "BYTES_PER_ELEMENT", \ - INT_TO_JSVAL(sizeof(_typedArray::ThisType)), \ - JS_PropertyStub, JS_StrictPropertyStub, \ - JSPROP_PERMANENT | JSPROP_READONLY)) \ - { \ - return NULL; \ - } \ - proto->setPrivate(0); \ -} while (0) +template +static inline JSObject * +InitTypedArrayClass(JSContext *cx, GlobalObject *global) +{ + JSObject *proto = global->createBlankPrototype(cx, ArrayType::slowClass()); + if (!proto) + return NULL; + + JSFunction *ctor = + global->createConstructor(cx, ArrayType::class_constructor, ArrayType::fastClass(), + cx->runtime->atomState.classAtoms[ArrayType::key], 3); + if (!ctor) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, proto)) + return NULL; + + if (!ctor->defineProperty(cx, cx->runtime->atomState.BYTES_PER_ELEMENTAtom, + Int32Value(ArrayType::BYTES_PER_ELEMENT), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY) || + !proto->defineProperty(cx, cx->runtime->atomState.BYTES_PER_ELEMENTAtom, + Int32Value(ArrayType::BYTES_PER_ELEMENT), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY)) + { + return NULL; + } + + if (!DefinePropertiesAndBrand(cx, proto, ArrayType::jsprops, ArrayType::jsfuncs)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, ArrayType::key, ctor, proto)) + return NULL; + + return proto; +} IMPL_TYPED_ARRAY_STATICS(Int8Array); IMPL_TYPED_ARRAY_STATICS(Uint8Array); @@ -1576,43 +2432,95 @@ Class TypedArray::slowClasses[TYPE_MAX] = { IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8ClampedArray) }; +static JSObject * +InitArrayBufferClass(JSContext *cx, GlobalObject *global) +{ + JSObject *arrayBufferProto = global->createBlankPrototype(cx, &ArrayBuffer::slowClass); + if (!arrayBufferProto) + return NULL; + + JSFunction *ctor = + global->createConstructor(cx, ArrayBuffer::class_constructor, &ArrayBufferClass, + CLASS_ATOM(cx, ArrayBuffer), 1); + if (!ctor) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, arrayBufferProto, ArrayBuffer::jsprops, ArrayBuffer::jsfuncs)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_ArrayBuffer, ctor, arrayBufferProto)) + return NULL; + + return arrayBufferProto; +} + JS_FRIEND_API(JSObject *) js_InitTypedArrayClasses(JSContext *cx, JSObject *obj) { + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + /* Idempotency required: we initialize several things, possibly lazily. */ JSObject *stop; - if (!js_GetClassObject(cx, obj, JSProto_ArrayBuffer, &stop)) + if (!js_GetClassObject(cx, global, JSProto_ArrayBuffer, &stop)) return NULL; if (stop) return stop; - JSObject *proto; - - INIT_TYPED_ARRAY_CLASS(Int8Array,TYPE_INT8); - INIT_TYPED_ARRAY_CLASS(Uint8Array,TYPE_UINT8); - INIT_TYPED_ARRAY_CLASS(Int16Array,TYPE_INT16); - INIT_TYPED_ARRAY_CLASS(Uint16Array,TYPE_UINT16); - INIT_TYPED_ARRAY_CLASS(Int32Array,TYPE_INT32); - INIT_TYPED_ARRAY_CLASS(Uint32Array,TYPE_UINT32); - INIT_TYPED_ARRAY_CLASS(Float32Array,TYPE_FLOAT32); - INIT_TYPED_ARRAY_CLASS(Float64Array,TYPE_FLOAT64); - INIT_TYPED_ARRAY_CLASS(Uint8ClampedArray,TYPE_UINT8_CLAMPED); - - proto = js_InitClass(cx, obj, NULL, &ArrayBuffer::jsclass, - ArrayBuffer::class_constructor, 1, - ArrayBuffer::jsprops, NULL, NULL, NULL); - if (!proto) + if (!InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global) || + !InitTypedArrayClass(cx, global)) + { return NULL; + } - proto->setPrivate(NULL); - return proto; + return InitArrayBufferClass(cx, global); } JS_FRIEND_API(JSBool) js_IsArrayBuffer(JSObject *obj) { JS_ASSERT(obj); - return obj->getClass() == &ArrayBuffer::jsclass; + return obj->isArrayBuffer(); +} + +JS_FRIEND_API(JSBool) +JS_IsArrayBufferObject(JSObject *obj) +{ + return js_IsArrayBuffer(obj); +} + +namespace js { + +bool +IsFastTypedArrayClass(const Class *clasp) +{ + return &TypedArray::fastClasses[0] <= clasp && + clasp < &TypedArray::fastClasses[TypedArray::TYPE_MAX]; +} + +} // namespace js + +uint32_t +JS_GetArrayBufferByteLength(JSObject *obj) +{ + return obj->arrayBufferByteLength(); +} + +uint8_t * +JS_GetArrayBufferData(JSObject *obj) +{ + return obj->arrayBufferDataOffset(); } JS_FRIEND_API(JSBool) @@ -1620,8 +2528,7 @@ js_IsTypedArray(JSObject *obj) { JS_ASSERT(obj); Class *clasp = obj->getClass(); - return clasp >= &TypedArray::fastClasses[0] && - clasp < &TypedArray::fastClasses[TypedArray::TYPE_MAX]; + return IsFastTypedArrayClass(clasp); } JS_FRIEND_API(JSObject *) @@ -1630,6 +2537,12 @@ js_CreateArrayBuffer(JSContext *cx, jsuint nbytes) return ArrayBuffer::create(cx, nbytes); } +JS_FRIEND_API(JSObject *) +JS_NewArrayBuffer(JSContext *cx, jsuint nbytes) +{ + return js_CreateArrayBuffer(cx, nbytes); +} + static inline JSObject * TypedArrayConstruct(JSContext *cx, jsint atype, uintN argc, Value *argv) { @@ -1690,7 +2603,7 @@ js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg, jsint byteoffset, jsint length) { JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX); - JS_ASSERT(bufArg && ArrayBuffer::fromJSObject(bufArg)); + JS_ASSERT(bufArg && js_IsArrayBuffer(bufArg)); JS_ASSERT_IF(byteoffset < 0, length < 0); Value vals[4]; @@ -1708,42 +2621,42 @@ js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg, argc++; } - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vals), vals); + AutoArrayRooter tvr(cx, ArrayLength(vals), vals); return TypedArrayConstruct(cx, atype, argc, &vals[0]); } -JS_FRIEND_API(JSBool) -js_ReparentTypedArrayToScope(JSContext *cx, JSObject *obj, JSObject *scope) +uint32_t +JS_GetTypedArrayLength(JSObject *obj) { - JS_ASSERT(obj); - - scope = JS_GetGlobalForObject(cx, scope); - if (!scope) - return JS_FALSE; - - if (!js_IsTypedArray(obj)) - return JS_FALSE; - - TypedArray *typedArray = TypedArray::fromJSObject(obj); - - JSObject *buffer = typedArray->bufferJS; - JS_ASSERT(js_IsArrayBuffer(buffer)); + return obj->getSlot(TypedArray::FIELD_LENGTH).toInt32(); +} - JSObject *proto; - JSProtoKey key = - JSCLASS_CACHED_PROTO_KEY(&TypedArray::slowClasses[typedArray->type]); - if (!js_GetClassPrototype(cx, scope, key, &proto)) - return JS_FALSE; +uint32_t +JS_GetTypedArrayByteOffset(JSObject *obj) +{ + return obj->getSlot(TypedArray::FIELD_BYTEOFFSET).toInt32(); +} - obj->setProto(proto); - obj->setParent(scope); +uint32_t +JS_GetTypedArrayByteLength(JSObject *obj) +{ + return obj->getSlot(TypedArray::FIELD_BYTELENGTH).toInt32(); +} - key = JSCLASS_CACHED_PROTO_KEY(&ArrayBuffer::jsclass); - if (!js_GetClassPrototype(cx, scope, key, &proto)) - return JS_FALSE; +uint32_t +JS_GetTypedArrayType(JSObject *obj) +{ + return obj->getSlot(TypedArray::FIELD_TYPE).toInt32(); +} - buffer->setProto(proto); - buffer->setParent(scope); +JSObject * +JS_GetTypedArrayBuffer(JSObject *obj) +{ + return (JSObject *) obj->getSlot(TypedArray::FIELD_BUFFER).toPrivate(); +} - return JS_TRUE; +void * +JS_GetTypedArrayData(JSObject *obj) +{ + return TypedArray::getDataOffset(obj); } diff --git a/deps/mozjs/js/src/jstypedarray.h b/deps/mozjs/js/src/jstypedarray.h index e79f12d05cb..f8af257ad00 100644 --- a/deps/mozjs/js/src/jstypedarray.h +++ b/deps/mozjs/js/src/jstypedarray.h @@ -41,7 +41,9 @@ #define jstypedarray_h #include "jsapi.h" -#include "jsvalue.h" +#include "jsclass.h" + +#include "gc/Barrier.h" typedef struct JSProperty JSProperty; @@ -56,35 +58,115 @@ namespace js { * TypedArray with a size. */ struct JS_FRIEND_API(ArrayBuffer) { - static Class jsclass; + static Class slowClass; static JSPropertySpec jsprops[]; + static JSFunctionSpec jsfuncs[]; static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp); - static void class_finalize(JSContext *cx, JSObject *obj); + + static JSBool fun_slice(JSContext *cx, uintN argc, Value *vp); static JSBool class_constructor(JSContext *cx, uintN argc, Value *vp); - static JSObject *create(JSContext *cx, int32 nbytes); + static JSObject *create(JSContext *cx, int32_t nbytes, uint8_t *contents = NULL); - static ArrayBuffer *fromJSObject(JSObject *obj); + static JSObject *createSlice(JSContext *cx, JSObject *arrayBuffer, + uint32_t begin, uint32_t end); ArrayBuffer() - : data(0), byteLength() { } ~ArrayBuffer(); - bool allocateStorage(JSContext *cx, uint32 bytes); - void freeStorage(JSContext *cx); - - void *offsetData(uint32 offs) { - return (void*) (((intptr_t)data) + offs); - } - - void *data; - uint32 byteLength; - bool isExternal; + static void + obj_trace(JSTracer *trc, JSObject *obj); + + static JSBool + obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp); + static JSBool + obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, + JSObject **objp, JSProperty **propp); + static JSBool + obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, + JSObject **objp, JSProperty **propp); + static JSBool + obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, + JSProperty **propp); + + static JSBool + obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); + static JSBool + obj_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); + static JSBool + obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); + static JSBool + obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs); + + static JSBool + obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp); + + static JSBool + obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, + Value *vp); + + static JSBool + obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp); + static JSBool + obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, + Value *vp, bool *present); + + static JSBool + obj_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp); + + static JSBool + obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict); + static JSBool + obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict); + static JSBool + obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict); + static JSBool + obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict); + + static JSBool + obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); + static JSBool + obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp); + static JSBool + obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); + static JSBool + obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp); + + static JSBool + obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); + static JSBool + obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp); + static JSBool + obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); + static JSBool + obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp); + + static JSBool + obj_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict); + static JSBool + obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict); + static JSBool + obj_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict); + + static JSBool + obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + Value *statep, jsid *idp); + + static JSType + obj_typeOf(JSContext *cx, JSObject *obj); + + static JSObject * + getArrayBuffer(JSObject *obj); }; /* @@ -115,6 +197,17 @@ struct JS_FRIEND_API(TypedArray) { TYPE_MAX }; + enum { + /* Properties of the typed array stored in reserved slots. */ + FIELD_LENGTH = 0, + FIELD_BYTEOFFSET, + FIELD_BYTELENGTH, + FIELD_TYPE, + FIELD_BUFFER, + FIELD_MAX, + NUM_FIXED_SLOTS = 7 + }; + // and MUST NOT be used to construct new objects. static Class fastClasses[TYPE_MAX]; @@ -124,43 +217,45 @@ struct JS_FRIEND_API(TypedArray) { static JSPropertySpec jsprops[]; - static TypedArray *fromJSObject(JSObject *obj); + static JSObject *getTypedArray(JSObject *obj); static JSBool prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp); - static JSBool obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id, + static JSBool obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp); + static JSBool obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, JSProperty **propp); - - static void obj_trace(JSTracer *trc, JSObject *obj); - - static JSBool obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); - - static JSBool obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); - - static int32 lengthOffset() { return offsetof(TypedArray, length); } - static int32 dataOffset() { return offsetof(TypedArray, data); } - static int32 typeOffset() { return offsetof(TypedArray, type); } + static JSBool obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, + JSObject **objp, JSProperty **propp); + static JSBool obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, + JSObject **objp, JSProperty **propp); + + static JSBool obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); + static JSBool obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp); + static JSBool obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); + static JSBool obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp); + + static JSBool obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); + static JSBool obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp); + static JSBool obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp); + static JSBool obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp); + + static uint32_t getLength(JSObject *obj); + static uint32_t getByteOffset(JSObject *obj); + static uint32_t getByteLength(JSObject *obj); + static uint32_t getType(JSObject *obj); + static JSObject * getBuffer(JSObject *obj); + static void * getDataOffset(JSObject *obj); public: - TypedArray() : buffer(0) { } - - bool isArrayIndex(JSContext *cx, jsid id, jsuint *ip = NULL); - bool valid() { return buffer != 0; } + static bool + isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip = NULL); - ArrayBuffer *buffer; - JSObject *bufferJS; - uint32 byteOffset; - uint32 byteLength; - uint32 length; - uint32 type; - - void *data; - - inline int slotWidth() const { - switch (type) { + static inline uint32_t slotWidth(int atype) { + switch (atype) { case js::TypedArray::TYPE_INT8: case js::TypedArray::TYPE_UINT8: case js::TypedArray::TYPE_UINT8_CLAMPED: @@ -175,12 +270,22 @@ struct JS_FRIEND_API(TypedArray) { case js::TypedArray::TYPE_FLOAT64: return 8; default: - JS_NOT_REACHED("invalid typed array"); + JS_NOT_REACHED("invalid typed array type"); return 0; } } + + static inline int slotWidth(JSObject *obj) { + return slotWidth(getType(obj)); + } + + static int lengthOffset(); + static int dataOffset(); }; +extern bool +IsFastTypedArrayClass(const Class *clasp); + } // namespace js /* Friend API methods */ @@ -195,14 +300,14 @@ JS_FRIEND_API(JSBool) js_IsArrayBuffer(JSObject *obj); JS_FRIEND_API(JSObject *) -js_CreateArrayBuffer(JSContext *cx, jsuint nbytes); +js_CreateArrayBuffer(JSContext *cx, uint32_t nbytes); /* * Create a new typed array of type atype (one of the TypedArray * enumerant values above), with nelements elements. */ JS_FRIEND_API(JSObject *) -js_CreateTypedArray(JSContext *cx, jsint atype, jsuint nelements); +js_CreateTypedArray(JSContext *cx, jsint atype, uint32_t nelements); /* * Create a new typed array of type atype (one of the TypedArray @@ -223,16 +328,37 @@ JS_FRIEND_API(JSObject *) js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg, jsint byteoffset, jsint length); -/* - * Reparent a typed array to a new scope. This should only be used to reparent - * a typed array that does not share its underlying ArrayBuffer with another - * typed array to avoid having a parent mismatch with the other typed array and - * its ArrayBuffer. - */ +extern int32_t JS_FASTCALL +js_TypedArray_uint8_clamp_double(const double x); + JS_FRIEND_API(JSBool) -js_ReparentTypedArrayToScope(JSContext *cx, JSObject *obj, JSObject *scope); +JS_IsArrayBufferObject(JSObject *obj); -extern int32 JS_FASTCALL -js_TypedArray_uint8_clamp_double(const double x); +JS_FRIEND_API(JSObject *) +JS_NewArrayBuffer(JSContext *cx, jsuint nbytes); + +JS_FRIEND_API(uint32_t) +JS_GetArrayBufferByteLength(JSObject *obj); + +JS_FRIEND_API(uint8_t *) +JS_GetArrayBufferData(JSObject *obj); + +JS_FRIEND_API(uint32_t) +JS_GetTypedArrayLength(JSObject *obj); + +JS_FRIEND_API(uint32_t) +JS_GetTypedArrayByteOffset(JSObject *obj); + +JS_FRIEND_API(uint32_t) +JS_GetTypedArrayByteLength(JSObject *obj); + +JS_FRIEND_API(uint32_t) +JS_GetTypedArrayType(JSObject *obj); + +JS_FRIEND_API(JSObject *) +JS_GetTypedArrayBuffer(JSObject *obj); + +JS_FRIEND_API(void *) +JS_GetTypedArrayData(JSObject *obj); #endif /* jstypedarray_h */ diff --git a/deps/mozjs/js/src/jstypedarrayinlines.h b/deps/mozjs/js/src/jstypedarrayinlines.h new file mode 100644 index 00000000000..05ddc00894b --- /dev/null +++ b/deps/mozjs/js/src/jstypedarrayinlines.h @@ -0,0 +1,102 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */ +/* vim: set ts=40 sw=4 et tw=99: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla WebGL impl + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nikhil Marathe + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jstypedarrayinlines_h +#define jstypedarrayinlines_h + +#include "jsapi.h" +#include "jsobj.h" + +inline uint32_t +JSObject::arrayBufferByteLength() +{ + JS_ASSERT(isArrayBuffer()); + return getElementsHeader()->length; +} + +inline uint8_t * +JSObject::arrayBufferDataOffset() +{ + return (uint8_t *) elements; +} + +namespace js { + +static inline int32_t +ClampIntForUint8Array(int32_t x) +{ + if (x < 0) + return 0; + if (x > 255) + return 255; + return x; +} + +inline uint32_t +TypedArray::getLength(JSObject *obj) { + return obj->getFixedSlot(FIELD_LENGTH).toInt32(); +} + +inline uint32_t +TypedArray::getByteOffset(JSObject *obj) { + return obj->getFixedSlot(FIELD_BYTEOFFSET).toInt32(); +} + +inline uint32_t +TypedArray::getByteLength(JSObject *obj) { + return obj->getFixedSlot(FIELD_BYTELENGTH).toInt32(); +} + +inline uint32_t +TypedArray::getType(JSObject *obj) { + return obj->getFixedSlot(FIELD_TYPE).toInt32(); +} + +inline JSObject * +TypedArray::getBuffer(JSObject *obj) { + return &obj->getFixedSlot(FIELD_BUFFER).toObject(); +} + +inline void * +TypedArray::getDataOffset(JSObject *obj) { + return (void *)obj->getPrivate(NUM_FIXED_SLOTS); +} + +} +#endif /* jstypedarrayinlines_h */ diff --git a/deps/mozjs/js/src/jstypes.h b/deps/mozjs/js/src/jstypes.h index 8ff593ed620..68e9c8d1790 100644 --- a/deps/mozjs/js/src/jstypes.h +++ b/deps/mozjs/js/src/jstypes.h @@ -54,7 +54,9 @@ #ifndef jstypes_h___ #define jstypes_h___ -#include +#include "mozilla/Attributes.h" +#include "mozilla/Util.h" + #include "js-config.h" /*********************************************************************** @@ -78,78 +80,11 @@ ** ***********************************************************************/ -#define DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION(Name) class Name - -#if defined(WIN32) || defined(XP_OS2) - -/* These also work for __MWERKS__ */ -# define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -# define JS_EXPORT_API(__type) __declspec(dllexport) __type -# define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -# define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#elif defined(__SYMBIAN32__) - -# define JS_EXTERN_API(__type) extern EXPORT_C __type -# define JS_EXPORT_API(__type) EXPORT_C __type -# define JS_EXTERN_DATA(__type) extern EXPORT_C __type -# define JS_EXPORT_DATA(__type) EXPORT_C __type - -#else /* Unix */ - -# ifdef HAVE_VISIBILITY_ATTRIBUTE -# define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) -# if defined(__GNUC__) && __GNUC__ <= 4 && __GNUC_MINOR__ < 5 - /* - * GCC wrongly produces a warning when a type with hidden visibility - * (e.g. js::Value) is a member of a local class of a static function. - * This is apparently fixed with GCC 4.5 and above. See: - * - * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40145. - */ -# undef DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION -# define DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION(Name) class __attribute__((visibility ("hidden"))) Name -# endif -# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# define JS_EXTERNAL_VIS __global -# else -# define JS_EXTERNAL_VIS -# endif - -# define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type -# define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type -# define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type -# define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type - -#endif - -#ifdef _WIN32 -# if defined(__MWERKS__) || defined(__GNUC__) -# define JS_IMPORT_API(__x) __x -# else -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -# endif -#elif defined(XP_OS2) -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -#elif defined(__SYMBIAN32__) -# define JS_IMPORT_API(__x) IMPORT_C __x -#else -# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) -#endif - -#if defined(_WIN32) && !defined(__MWERKS__) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#elif defined(XP_OS2) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#elif defined(__SYMBIAN32__) -# if defined(__CW32__) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -# else -# define JS_IMPORT_DATA(__x) IMPORT_C __x -# endif -#else -# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) -#endif +#define JS_EXTERN_API(type) extern MOZ_EXPORT_API(type) +#define JS_EXPORT_API(type) MOZ_EXPORT_API(type) +#define JS_EXPORT_DATA(type) MOZ_EXPORT_DATA(type) +#define JS_IMPORT_API(type) MOZ_IMPORT_API(type) +#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA(type) /* * The linkage of JS API functions differs depending on whether the file is @@ -158,20 +93,14 @@ * should not. STATIC_JS_API is used to build JS as a static library. */ #if defined(STATIC_JS_API) - -# define JS_PUBLIC_API(t) t -# define JS_PUBLIC_DATA(t) t - +# define JS_PUBLIC_API(t) t +# define JS_PUBLIC_DATA(t) t #elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) - -# define JS_PUBLIC_API(t) JS_EXPORT_API(t) -# define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) - +# define JS_PUBLIC_API(t) MOZ_EXPORT_API(t) +# define JS_PUBLIC_DATA(t) MOZ_EXPORT_DATA(t) #else - -# define JS_PUBLIC_API(t) JS_IMPORT_API(t) -# define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) - +# define JS_PUBLIC_API(t) MOZ_IMPORT_API(t) +# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA(t) #endif #define JS_FRIEND_API(t) JS_PUBLIC_API(t) @@ -188,37 +117,15 @@ #endif #ifndef JS_INLINE -# if defined __cplusplus -# define JS_INLINE inline -# elif defined _MSC_VER -# define JS_INLINE __inline -# elif defined __GNUC__ -# define JS_INLINE __inline__ -# else -# define JS_INLINE inline -# endif +#define JS_INLINE MOZ_INLINE #endif #ifndef JS_ALWAYS_INLINE -# if defined DEBUG -# define JS_ALWAYS_INLINE JS_INLINE -# elif defined _MSC_VER -# define JS_ALWAYS_INLINE __forceinline -# elif defined __GNUC__ -# define JS_ALWAYS_INLINE __attribute__((always_inline)) JS_INLINE -# else -# define JS_ALWAYS_INLINE JS_INLINE -# endif +#define JS_ALWAYS_INLINE MOZ_ALWAYS_INLINE #endif #ifndef JS_NEVER_INLINE -# if defined _MSC_VER -# define JS_NEVER_INLINE __declspec(noinline) -# elif defined __GNUC__ -# define JS_NEVER_INLINE __attribute__((noinline)) -# else -# define JS_NEVER_INLINE -# endif +#define JS_NEVER_INLINE MOZ_NEVER_INLINE #endif #ifndef JS_WARN_UNUSED_RESULT @@ -229,25 +136,6 @@ # endif #endif -#ifdef NS_STATIC_CHECKING -/* - * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK - * always have a valid cx->fp and can access it freely. Other functions can - * access cx->fp only after calling a function that "forces" the stack - * (i.e. lazily instantiates it as needed). - */ -# define JS_REQUIRES_STACK __attribute__((user("JS_REQUIRES_STACK"))) -# define JS_FORCES_STACK __attribute__((user("JS_FORCES_STACK"))) -/* - * Skip the JS_REQUIRES_STACK analysis within functions with this annotation. - */ -# define JS_IGNORE_STACK __attribute__((user("JS_IGNORE_STACK"))) -#else -# define JS_REQUIRES_STACK -# define JS_FORCES_STACK -# define JS_IGNORE_STACK -#endif - /*********************************************************************** ** MACROS: JS_BEGIN_MACRO ** JS_END_MACRO @@ -271,17 +159,8 @@ ** DESCRIPTION: ** Macro shorthands for conditional C++ extern block delimiters. ***********************************************************************/ -#ifdef __cplusplus - -# define JS_BEGIN_EXTERN_C extern "C" { -# define JS_END_EXTERN_C } - -#else - -# define JS_BEGIN_EXTERN_C -# define JS_END_EXTERN_C - -#endif +#define JS_BEGIN_EXTERN_C MOZ_BEGIN_EXTERN_C +#define JS_END_EXTERN_C MOZ_END_EXTERN_C /*********************************************************************** ** MACROS: JS_BIT @@ -289,7 +168,7 @@ ** DESCRIPTION: ** Bit masking macros. XXX n must be <= 31 to be portable ***********************************************************************/ -#define JS_BIT(n) ((JSUint32)1 << (n)) +#define JS_BIT(n) ((uint32_t)1 << (n)) #define JS_BITMASK(n) (JS_BIT(n) - 1) /*********************************************************************** @@ -305,11 +184,7 @@ #define JS_MIN(x,y) ((x)<(y)?(x):(y)) #define JS_MAX(x,y) ((x)>(y)?(x):(y)) -#ifdef _MSC_VER -# include "jscpucfg.h" /* We can't auto-detect MSVC configuration */ -#else -# include "jsautocfg.h" /* Use auto-detected configuration */ -#endif +#include "jscpucfg.h" /* * Define JS_64BIT iff we are building in an environment with 64-bit @@ -342,8 +217,6 @@ #endif -#include "jsinttypes.h" - JS_BEGIN_EXTERN_C /************************************************************************ @@ -359,36 +232,6 @@ JS_BEGIN_EXTERN_C typedef int JSIntn; typedef unsigned int JSUintn; -/************************************************************************ -** TYPES: JSFloat64 -** DESCRIPTION: -** NSPR's floating point type is always 64 bits. -************************************************************************/ -typedef double JSFloat64; - -/************************************************************************ -** TYPES: JSSize -** DESCRIPTION: -** A type for representing the size of objects. -************************************************************************/ -typedef size_t JSSize; - -/************************************************************************ -** TYPES: JSPtrDiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -typedef ptrdiff_t JSPtrdiff; - -/************************************************************************ -** TYPES: JSUptrdiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -typedef JSUintPtr JSUptrdiff; - /************************************************************************ ** TYPES: JSBool ** DESCRIPTION: @@ -400,11 +243,6 @@ typedef JSUintPtr JSUptrdiff; typedef JSIntn JSBool; #define JS_TRUE (JSIntn)1 #define JS_FALSE (JSIntn)0 -/* -** Special: JS_NEITHER is used by the tracer to have tri-state booleans. -** This should not be used in new code. -*/ -#define JS_NEITHER (JSIntn)2 /************************************************************************ ** TYPES: JSPackedBool @@ -412,15 +250,7 @@ typedef JSIntn JSBool; ** Use JSPackedBool within structs where bitfields are not desireable ** but minimum and consistent overhead matters. ************************************************************************/ -typedef JSUint8 JSPackedBool; - -/* -** A JSWord is an integer that is the same size as a void* -*/ -typedef JSIntPtr JSWord; -typedef JSUintPtr JSUword; - -#include "jsotypes.h" +typedef uint8_t JSPackedBool; /*********************************************************************** ** MACROS: JS_LIKELY diff --git a/deps/mozjs/js/src/jsutil.cpp b/deps/mozjs/js/src/jsutil.cpp index b5b5b6ae135..5968e70e71c 100644 --- a/deps/mozjs/js/src/jsutil.cpp +++ b/deps/mozjs/js/src/jsutil.cpp @@ -38,13 +38,13 @@ * * ***** END LICENSE BLOCK ***** */ -/* - * PR assertion checker. - */ +/* Various JS utility functions. */ + +#include "mozilla/Attributes.h" + #include #include #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #ifdef WIN32 @@ -53,12 +53,14 @@ # include #endif +#include "js/TemplateLib.h" + using namespace js; #ifdef DEBUG /* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */ -JS_PUBLIC_DATA(JSUint32) OOM_maxAllocations = (JSUint32)-1; -JS_PUBLIC_DATA(JSUint32) OOM_counter = 0; +JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX; +JS_PUBLIC_DATA(uint32_t) OOM_counter = 0; #endif /* @@ -67,35 +69,19 @@ JS_PUBLIC_DATA(JSUint32) OOM_counter = 0; */ JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)())); -JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) -{ - fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); - fflush(stderr); -#if defined(WIN32) - /* - * We used to call DebugBreak() on Windows, but amazingly, it causes - * the MSVS 2010 debugger not to be able to recover a call stack. - */ - *((int *) NULL) = 0; - exit(3); -#elif defined(__APPLE__) - /* - * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are - * trapped. - */ - *((int *) NULL) = 0; /* To continue from here in GDB: "return" then "continue". */ - raise(SIGABRT); /* In case above statement gets nixed by the optimizer. */ -#else - raise(SIGABRT); /* To continue from here in GDB: "signal 0". */ -#endif -} +/* + * |JS_Assert| historically took |JSIntn ln| as its last argument. We've + * boiled |JSIntn ln| down to simply |int ln| so that mfbt may declare the + * function without depending on the |JSIntn| typedef, so we must manually + * verify that the |JSIntn| typedef is consistent. + */ +JS_STATIC_ASSERT((tl::IsSameType::result)); #ifdef JS_BASIC_STATS #include #include #include "jscompat.h" -#include "jsbit.h" /* * Histogram bins count occurrences of values <= the bin label, as follows: @@ -106,7 +92,7 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) * * We wish to count occurrences of 0 and 1 values separately, always. */ -static uint32 +static uint32_t BinToVal(uintN logscale, uintN bin) { JS_ASSERT(bin <= 10); @@ -116,11 +102,11 @@ BinToVal(uintN logscale, uintN bin) if (logscale == 2) return JS_BIT(bin); JS_ASSERT(logscale == 10); - return (uint32) pow(10.0, (double) bin); + return uint32_t(pow(10.0, (double) bin)); } static uintN -ValToBin(uintN logscale, uint32 val) +ValToBin(uintN logscale, uint32_t val) { uintN bin; @@ -129,13 +115,13 @@ ValToBin(uintN logscale, uint32 val) bin = (logscale == 10) ? (uintN) ceil(log10((double) val)) : (logscale == 2) - ? (uintN) JS_CeilingLog2(val) + ? (uintN) JS_CEILING_LOG2W(val) : val; return JS_MIN(bin, 10); } void -JS_BasicStatsAccum(JSBasicStats *bs, uint32 val) +JS_BasicStatsAccum(JSBasicStats *bs, uint32_t val) { uintN oldscale, newscale, bin; double mean; @@ -152,14 +138,14 @@ JS_BasicStatsAccum(JSBasicStats *bs, uint32 val) if (bs->max > 16 && mean > 8) { newscale = (bs->max > 1e6 && mean > 1000) ? 10 : 2; if (newscale != oldscale) { - uint32 newhist[11], newbin; + uint32_t newhist[11], newbin; PodArrayZero(newhist); for (bin = 0; bin <= 10; bin++) { newbin = ValToBin(newscale, BinToVal(oldscale, bin)); newhist[newbin] += bs->hist[bin]; } - memcpy(bs->hist, newhist, sizeof bs->hist); + js_memcpy(bs->hist, newhist, sizeof bs->hist); bs->logscale = newscale; } } @@ -170,7 +156,7 @@ JS_BasicStatsAccum(JSBasicStats *bs, uint32 val) } double -JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma) +JS_MeanAndStdDev(uint32_t num, double sum, double sqsum, double *sigma) { double var; @@ -205,7 +191,7 @@ void JS_DumpHistogram(JSBasicStats *bs, FILE *fp) { uintN bin; - uint32 cnt, max; + uint32_t cnt, max; double sum, mean; for (bin = 0, max = 0, sum = 0; bin <= 10; bin++) { @@ -228,9 +214,9 @@ JS_DumpHistogram(JSBasicStats *bs, FILE *fp) fprintf(fp, ": %8u ", cnt); if (cnt != 0) { if (max > 1e6 && mean > 1e3) - cnt = (uint32) ceil(log10((double) cnt)); + cnt = uint32_t(ceil(log10((double) cnt))); else if (max > 16 && mean > 8) - cnt = JS_CeilingLog2(cnt); + cnt = JS_CEILING_LOG2W(cnt); for (uintN i = 0; i < cnt; i++) putc('*', fp); } @@ -239,147 +225,3 @@ JS_DumpHistogram(JSBasicStats *bs, FILE *fp) } #endif /* JS_BASIC_STATS */ - -#if defined(DEBUG_notme) && defined(XP_UNIX) - -#define __USE_GNU 1 -#include -#include -#include "jshash.h" -#include "jsprf.h" - -JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; - -static JSCallsite * -CallTree(void **bp) -{ - void **bpup, **bpdown, *pc; - JSCallsite *parent, *site, **csp; - Dl_info info; - int ok, offset; - const char *symbol; - char *method; - - /* Reverse the stack frame list to avoid recursion. */ - bpup = NULL; - for (;;) { - bpdown = (void**) bp[0]; - bp[0] = (void*) bpup; - if ((void**) bpdown[0] < bpdown) - break; - bpup = bp; - bp = bpdown; - } - - /* Reverse the stack again, finding and building a path in the tree. */ - parent = &js_calltree_root; - do { - bpup = (void**) bp[0]; - bp[0] = (void*) bpdown; - pc = bp[1]; - - csp = &parent->kids; - while ((site = *csp) != NULL) { - if (site->pc == (uint32)pc) { - /* Put the most recently used site at the front of siblings. */ - *csp = site->siblings; - site->siblings = parent->kids; - parent->kids = site; - - /* Site already built -- go up the stack. */ - goto upward; - } - csp = &site->siblings; - } - - /* Check for recursion: see if pc is on our ancestor line. */ - for (site = parent; site; site = site->parent) { - if (site->pc == (uint32)pc) - goto upward; - } - - /* - * Not in tree at all: let's find our symbolic callsite info. - * XXX static syms are masked by nearest lower global - */ - info.dli_fname = info.dli_sname = NULL; - ok = dladdr(pc, &info); - if (ok < 0) { - fprintf(stderr, "dladdr failed!\n"); - return NULL; - } - -/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ - symbol = info.dli_sname; - offset = (char*)pc - (char*)info.dli_fbase; - method = symbol - ? strdup(symbol) - : JS_smprintf("%s+%X", - info.dli_fname ? info.dli_fname : "main", - offset); - if (!method) - return NULL; - - /* Create a new callsite record. */ - site = (JSCallsite *) OffTheBooks::malloc(sizeof(JSCallsite)); - if (!site) - return NULL; - - /* Insert the new site into the tree. */ - site->pc = (uint32)pc; - site->name = method; - site->library = info.dli_fname; - site->offset = offset; - site->parent = parent; - site->siblings = parent->kids; - parent->kids = site; - site->kids = NULL; - - upward: - parent = site; - bpdown = bp; - bp = bpup; - } while (bp); - - return site; -} - -JS_FRIEND_API(JSCallsite *) -JS_Backtrace(int skip) -{ - void **bp, **bpdown; - - /* Stack walking code adapted from Kipp's "leaky". */ -#if defined(__i386) - __asm__( "movl %%ebp, %0" : "=g"(bp)); -#elif defined(__x86_64__) - __asm__( "movq %%rbp, %0" : "=g"(bp)); -#else - /* - * It would be nice if this worked uniformly, but at least on i386 and - * x86_64, it stopped working with gcc 4.1, because it points to the - * end of the saved registers instead of the start. - */ - bp = (void**) __builtin_frame_address(0); -#endif - while (--skip >= 0) { - bpdown = (void**) *bp++; - if (bpdown < bp) - break; - bp = bpdown; - } - - return CallTree(bp); -} - -JS_FRIEND_API(void) -JS_DumpBacktrace(JSCallsite *trace) -{ - while (trace) { - fprintf(stdout, "%s [%s +0x%X]\n", trace->name, trace->library, - trace->offset); - trace = trace->parent; - } -} - -#endif /* defined(DEBUG_notme) && defined(XP_UNIX) */ diff --git a/deps/mozjs/js/src/jsutil.h b/deps/mozjs/js/src/jsutil.h index 68570f25102..c20fbf14333 100644 --- a/deps/mozjs/js/src/jsutil.h +++ b/deps/mozjs/js/src/jsutil.h @@ -44,577 +44,153 @@ #ifndef jsutil_h___ #define jsutil_h___ -#include "jstypes.h" -#include "mozilla/Util.h" -#include -#include - -JS_BEGIN_EXTERN_C - -/* - * JS_Assert is present even in release builds, for the benefit of applications - * that build DEBUG and link against a non-DEBUG SpiderMonkey library. - */ -extern JS_PUBLIC_API(void) -JS_Assert(const char *s, const char *file, JSIntn ln); - -#define JS_CRASH_UNLESS(__cond) \ - JS_BEGIN_MACRO \ - if (!(__cond)) { \ - *(int *)(uintptr_t)0xccadbeef = 0; \ - ((void(*)())0)(); /* More reliable, but doesn't say CCADBEEF */ \ - } \ - JS_END_MACRO - -#ifdef DEBUG +#include "mozilla/Attributes.h" -#define JS_ASSERT(expr) \ - ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) - -#define JS_ASSERT_IF(cond, expr) \ - ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) - -#define JS_NOT_REACHED(reason) \ - JS_Assert(reason, __FILE__, __LINE__) - -#define JS_ALWAYS_TRUE(expr) JS_ASSERT(expr) - -#define JS_ALWAYS_FALSE(expr) JS_ASSERT(!(expr)) - -# ifdef JS_THREADSAFE -# define JS_THREADSAFE_ASSERT(expr) JS_ASSERT(expr) -# else -# define JS_THREADSAFE_ASSERT(expr) ((void) 0) -# endif - -#else +#include "js/Utility.h" -#define JS_ASSERT(expr) ((void) 0) -#define JS_ASSERT_IF(cond,expr) ((void) 0) -#define JS_NOT_REACHED(reason) -#define JS_ALWAYS_TRUE(expr) ((void) (expr)) -#define JS_ALWAYS_FALSE(expr) ((void) (expr)) -#define JS_THREADSAFE_ASSERT(expr) ((void) 0) +/* Forward declarations. */ +struct JSContext; -#endif /* defined(DEBUG) */ - -/* - * Compile-time assert. "cond" must be a constant expression. - * The macro can be used only in places where an "extern" declaration is - * allowed. - */ - -#ifdef __SUNPRO_CC -/* - * Sun Studio C++ compiler has a bug - * "sizeof expression not accepted as size of array parameter" - * It happens when js_static_assert() function is declared inside functions. - * The bug number is 6688515. It is not public yet. - * Therefore, for Sun Studio, declare js_static_assert as an array instead. - */ -#define JS_STATIC_ASSERT(cond) extern char js_static_assert[(cond) ? 1 : -1] -#else -#ifdef __COUNTER__ - #define JS_STATIC_ASSERT_GLUE1(x,y) x##y - #define JS_STATIC_ASSERT_GLUE(x,y) JS_STATIC_ASSERT_GLUE1(x,y) - #define JS_STATIC_ASSERT(cond) \ - typedef int JS_STATIC_ASSERT_GLUE(js_static_assert, __COUNTER__)[(cond) ? 1 : -1] -#else - #define JS_STATIC_ASSERT(cond) extern void js_static_assert(int arg[(cond) ? 1 : -1]) -#endif -#endif - -#define JS_STATIC_ASSERT_IF(cond, expr) JS_STATIC_ASSERT(!(cond) || (expr)) - -/* - * Abort the process in a non-graceful manner. This will cause a core file, - * call to the debugger or other moral equivalent as well as causing the - * entire process to stop. - */ -extern JS_PUBLIC_API(void) JS_Abort(void); - -#ifdef DEBUG -# define JS_BASIC_STATS 1 -#endif - -#ifdef DEBUG_brendan -# define JS_SCOPE_DEPTH_METER 1 -#endif - -#ifdef JS_BASIC_STATS - -#include - -typedef struct JSBasicStats { - uint32 num; - uint32 max; - double sum; - double sqsum; - uint32 logscale; /* logarithmic scale: 0 (linear), 2, 10 */ - uint32 hist[11]; -} JSBasicStats; - -#define JS_INIT_STATIC_BASIC_STATS {0,0,0,0,0,{0,0,0,0,0,0,0,0,0,0,0}} -#define JS_BASIC_STATS_INIT(bs) memset((bs), 0, sizeof(JSBasicStats)) - -#define JS_BASIC_STATS_ACCUM(bs,val) \ - JS_BasicStatsAccum(bs, val) - -#define JS_MeanAndStdDevBS(bs,sigma) \ - JS_MeanAndStdDev((bs)->num, (bs)->sum, (bs)->sqsum, sigma) - -extern void -JS_BasicStatsAccum(JSBasicStats *bs, uint32 val); - -extern double -JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma); - -extern void -JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp); - -extern void -JS_DumpHistogram(JSBasicStats *bs, FILE *fp); - -#else - -#define JS_BASIC_STATS_ACCUM(bs,val) /* nothing */ - -#endif /* JS_BASIC_STATS */ - - -#if defined(DEBUG_notme) && defined(XP_UNIX) - -typedef struct JSCallsite JSCallsite; - -struct JSCallsite { - uint32 pc; - char *name; - const char *library; - int offset; - JSCallsite *parent; - JSCallsite *siblings; - JSCallsite *kids; - void *handy; -}; - -extern JS_FRIEND_API(JSCallsite *) -JS_Backtrace(int skip); - -extern JS_FRIEND_API(void) -JS_DumpBacktrace(JSCallsite *trace); -#endif - -#if defined JS_USE_CUSTOM_ALLOCATOR - -#include "jscustomallocator.h" - -#else - -#ifdef DEBUG -/* - * In order to test OOM conditions, when the shell command-line option - * |-A NUM| is passed, we fail continuously after the NUM'th allocation. - */ -extern JS_PUBLIC_DATA(JSUint32) OOM_maxAllocations; /* set from shell/js.cpp */ -extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ -#define JS_OOM_POSSIBLY_FAIL() \ - do \ - { \ - if (OOM_counter++ >= OOM_maxAllocations) { \ - return NULL; \ - } \ - } while (0) - -#else -#define JS_OOM_POSSIBLY_FAIL() do {} while(0) -#endif - -/* - * SpiderMonkey code should not be calling these allocation functions directly. - * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. - * However, js_free() can be called directly. - */ -static JS_INLINE void* js_malloc(size_t bytes) { - JS_OOM_POSSIBLY_FAIL(); - return malloc(bytes); -} - -static JS_INLINE void* js_calloc(size_t bytes) { - JS_OOM_POSSIBLY_FAIL(); - return calloc(bytes, 1); -} - -static JS_INLINE void* js_realloc(void* p, size_t bytes) { - JS_OOM_POSSIBLY_FAIL(); - return realloc(p, bytes); -} +static JS_ALWAYS_INLINE void * +js_memcpy(void *dst_, const void *src_, size_t len) +{ + char *dst = (char *) dst_; + const char *src = (const char *) src_; + JS_ASSERT_IF(dst >= src, (size_t) (dst - src) >= len); + JS_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len); -static JS_INLINE void js_free(void* p) { - free(p); + return memcpy(dst, src, len); } -#endif/* JS_USE_CUSTOM_ALLOCATOR */ - -JS_END_EXTERN_C - - #ifdef __cplusplus +namespace js { -/* - * User guide to memory management within SpiderMonkey: - * - * Quick tips: - * - * Allocation: - * - Prefer to allocate using JSContext: - * cx->{malloc_,realloc_,calloc_,new_,new_array} - * - * - If no JSContext is available, use a JSRuntime: - * rt->{malloc_,realloc_,calloc_,new_,new_array} - * - * - As a last resort, use unaccounted allocation ("OffTheBooks"): - * js::OffTheBooks::{malloc_,realloc_,calloc_,new_,new_array} - * - * Deallocation: - * - When the deallocation occurs on a slow path, use: - * Foreground::{free_,delete_,array_delete} - * - * - Otherwise deallocate on a background thread using a JSContext: - * cx->{free_,delete_,array_delete} - * - * - If no JSContext is available, use a JSRuntime: - * rt->{free_,delete_,array_delete} - * - * - As a last resort, use UnwantedForeground deallocation: - * js::UnwantedForeground::{free_,delete_,array_delete} - * - * General tips: - * - * - Mixing and matching these allocators is allowed (you may free memory - * allocated by any allocator, with any deallocator). - * - * - Never, ever use normal C/C++ memory management: - * malloc, free, new, new[], delete, operator new, etc. - * - * - Never, ever use low-level SpiderMonkey allocators: - * js_malloc(), js_free(), js_calloc(), js_realloc() - * Their use is reserved for the other memory managers. - * - * - Classes which have private constructors or destructors should have - * JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR added to their - * declaration. - * - * Details: - * - * Using vanilla new/new[] is unsafe in SpiderMonkey because they throw on - * failure instead of returning NULL, which is what SpiderMonkey expects. - * (Even overriding them is unsafe, as the system's C++ runtime library may - * throw, which we do not support. We also can't just use the 'nothrow' - * variant of new/new[], because we want to mediate *all* allocations - * within SpiderMonkey, to satisfy any embedders using - * JS_USE_CUSTOM_ALLOCATOR.) - * - * JSContexts and JSRuntimes keep track of memory allocated, and use this - * accounting to schedule GC. OffTheBooks does not. We'd like to remove - * OffTheBooks allocations as much as possible (bug 636558). - * - * On allocation failure, a JSContext correctly reports an error, which a - * JSRuntime and OffTheBooks does not. - * - * A JSContext deallocates in a background thread. A JSRuntime might - * deallocate in the background in the future, but does not now. Foreground - * deallocation is preferable on slow paths. UnwantedForeground deallocations - * occur where we have no JSContext or JSRuntime, and the deallocation is not - * on a slow path. We want to remove UnwantedForeground deallocations (bug - * 636561). - * - * JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR makes the allocation - * classes friends with your class, giving them access to private - * constructors and destructors. - * - * |make check| does a source level check on the number of uses OffTheBooks, - * UnwantedForeground, js_malloc, js_free etc, to prevent regressions. If you - * really must add one, update Makefile.in, and run |make check|. - * - * |make check| also statically prevents the use of vanilla new/new[]. - */ - -#define JS_NEW_BODY(allocator, t, parms) \ - void *memory = allocator(sizeof(t)); \ - return memory ? new(memory) t parms : NULL; +template +class AlignedPtrAndFlag +{ + uintptr_t bits; -/* - * Given a class which should provide new_() methods, add - * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This - * adds new_()s with up to 12 parameters. Add more versions of new_ below if - * you need more than 12 parameters. - * - * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS, - * or the build will break. - */ -#define JS_DECLARE_NEW_METHODS(ALLOCATOR, QUALIFIERS)\ - template \ - QUALIFIERS T *new_() {\ - JS_NEW_BODY(ALLOCATOR, T, ())\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10, const P11 &p11) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11))\ - }\ -\ - template \ - QUALIFIERS T *new_(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4, const P5 &p5, const P6 &p6, const P7 &p7, const P8 &p8, const P9 &p9, const P10 &p10, const P11 &p11, const P12 &p12) {\ - JS_NEW_BODY(ALLOCATOR, T, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12))\ - }\ - static const int JSMinAlignment = 8;\ - template \ - QUALIFIERS T *array_new(size_t n) {\ - /* The length is stored just before the vector memory. */\ - uint64 numBytes64 = uint64(JSMinAlignment) + uint64(sizeof(T)) * uint64(n);\ - size_t numBytes = size_t(numBytes64);\ - if (numBytes64 != numBytes) {\ - JS_ASSERT(0); /* we want to know if this happens in debug builds */\ - return NULL;\ - }\ - void *memory = ALLOCATOR(numBytes);\ - if (!memory)\ - return NULL;\ - *(size_t *)memory = n;\ - memory = (void*)(uintptr_t(memory) + JSMinAlignment);\ - return new(memory) T[n];\ - }\ - - -#define JS_DECLARE_DELETE_METHODS(DEALLOCATOR, QUALIFIERS)\ - template \ - QUALIFIERS void delete_(T *p) {\ - if (p) {\ - p->~T();\ - DEALLOCATOR(p);\ - }\ - }\ -\ - template \ - QUALIFIERS void array_delete(T *p) {\ - if (p) {\ - void* p0 = (void *)(uintptr_t(p) - js::OffTheBooks::JSMinAlignment);\ - size_t n = *(size_t *)p0;\ - for (size_t i = 0; i < n; i++)\ - (p + i)->~T();\ - DEALLOCATOR(p0);\ - }\ + public: + AlignedPtrAndFlag(T *t, bool flag) { + JS_ASSERT((uintptr_t(t) & 1) == 0); + bits = uintptr_t(t) | uintptr_t(flag); } - -/* - * In general, all allocations should go through a JSContext or JSRuntime, so - * that the garbage collector knows how much memory has been allocated. In - * cases where it is difficult to use a JSContext or JSRuntime, OffTheBooks can - * be used, though this is undesirable. - */ -namespace js { -/* Import common mfbt declarations into "js". */ -using namespace mozilla; - -class OffTheBooks { -public: - JS_DECLARE_NEW_METHODS(::js_malloc, JS_ALWAYS_INLINE static) - - static JS_INLINE void* malloc_(size_t bytes) { - return ::js_malloc(bytes); + T *ptr() const { + return (T *)(bits & ~uintptr_t(1)); } - static JS_INLINE void* calloc_(size_t bytes) { - return ::js_calloc(bytes); + bool flag() const { + return (bits & 1) != 0; } - static JS_INLINE void* realloc_(void* p, size_t bytes) { - return ::js_realloc(p, bytes); + void setPtr(T *t) { + JS_ASSERT((uintptr_t(t) & 1) == 0); + bits = uintptr_t(t) | uintptr_t(flag()); } -}; -/* - * We generally prefer deallocating using JSContext because it can happen in - * the background. On slow paths, we may prefer foreground allocation. - */ -class Foreground { -public: - /* See parentheses comment above. */ - static JS_ALWAYS_INLINE void free_(void* p) { - ::js_free(p); + void setFlag() { + bits |= 1; } - JS_DECLARE_DELETE_METHODS(::js_free, JS_ALWAYS_INLINE static) -}; + void unsetFlag() { + bits &= ~uintptr_t(1); + } -class UnwantedForeground : public Foreground { + void set(T *t, bool flag) { + JS_ASSERT((uintptr_t(t) & 1) == 0); + bits = uintptr_t(t) | flag; + } }; -} /* namespace js */ - -/* - * Note lack of ; in JSRuntime below. This is intentional so "calling" this - * looks "normal". - */ -#define JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR \ - friend class js::OffTheBooks;\ - friend class js::Foreground;\ - friend class js::UnwantedForeground;\ - friend struct ::JSContext;\ - friend struct ::JSRuntime - - -/** - * The following classes are designed to cause assertions to detect - * inadvertent use of guard objects as temporaries. In other words, - * when we have a guard object whose only purpose is its constructor and - * destructor (and is never otherwise referenced), the intended use - * might be: - * JSAutoTempValueRooter tvr(cx, 1, &val); - * but is is easy to accidentally write: - * JSAutoTempValueRooter(cx, 1, &val); - * which compiles just fine, but runs the destructor well before the - * intended time. - * - * They work by adding (#ifdef DEBUG) an additional parameter to the - * guard object's constructor, with a default value, so that users of - * the guard object's API do not need to do anything. The default value - * of this parameter is a temporary object. C++ (ISO/IEC 14882:1998), - * section 12.2 [class.temporary], clauses 4 and 5 seem to assume a - * guarantee that temporaries are destroyed in the reverse of their - * construction order, but I actually can't find a statement that that - * is true in the general case (beyond the two specific cases mentioned - * there). However, it seems to be true. - * - * These classes are intended to be used only via the macros immediately - * below them: - * JS_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member - * variable, and should be put where a declaration of a private - * member variable would be placed. - * JS_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the - * parameters to each constructor of the guard object; it declares - * (ifdef DEBUG) an additional parameter. - * JS_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each - * constructor. It uses the parameter declared by - * JS_GUARD_OBJECT_NOTIFIER_PARAM. - */ -#ifdef DEBUG -class JSGuardObjectNotifier +template +static inline void +Reverse(T *beg, T *end) { -private: - bool* mStatementDone; -public: - JSGuardObjectNotifier() : mStatementDone(NULL) {} - - ~JSGuardObjectNotifier() { - *mStatementDone = true; + while (beg != end) { + if (--end == beg) + return; + T tmp = *beg; + *beg = *end; + *end = tmp; + ++beg; } +} - void setStatementDone(bool *aStatementDone) { - mStatementDone = aStatementDone; +template +static inline T * +Find(T *beg, T *end, const T &v) +{ + for (T *p = beg; p != end; ++p) { + if (*p == v) + return p; } -}; + return end; +} -class JSGuardObjectNotificationReceiver +template +static inline typename Container::ElementType * +Find(Container &c, const typename Container::ElementType &v) { -private: - bool mStatementDone; -public: - JSGuardObjectNotificationReceiver() : mStatementDone(false) {} + return Find(c.begin(), c.end(), v); +} - ~JSGuardObjectNotificationReceiver() { - /* - * Assert that the guard object was not used as a temporary. - * (Note that this assert might also fire if Init is not called - * because the guard object's implementation is not using the - * above macros correctly.) - */ - JS_ASSERT(mStatementDone); - } +template +void +ForEach(InputIterT begin, InputIterT end, CallableT f) +{ + for (; begin != end; ++begin) + f(*begin); +} - void Init(const JSGuardObjectNotifier &aNotifier) { - /* - * aNotifier is passed as a const reference so that we can pass a - * temporary, but we really intend it as non-const - */ - const_cast(aNotifier). - setStatementDone(&mStatementDone); - } -}; +template +static inline T +Min(T t1, T t2) +{ + return t1 < t2 ? t1 : t2; +} -#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER \ - JSGuardObjectNotificationReceiver _mCheckNotUsedAsTemporary; -#define JS_GUARD_OBJECT_NOTIFIER_PARAM \ - , const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier() -#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT \ - , const JSGuardObjectNotifier& _notifier -#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 \ - const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier() -#define JS_GUARD_OBJECT_NOTIFIER_INIT \ - JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO +template +static inline T +Max(T t1, T t2) +{ + return t1 > t2 ? t1 : t2; +} -#else /* defined(DEBUG) */ +/* Allows a const variable to be initialized after its declaration. */ +template +static T& +InitConst(const T &t) +{ + return const_cast(t); +} -#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER -#define JS_GUARD_OBJECT_NOTIFIER_PARAM -#define JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT -#define JS_GUARD_OBJECT_NOTIFIER_PARAM0 -#define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO +template +JS_ALWAYS_INLINE T & +ImplicitCast(U &u) +{ + T &t = u; + return t; +} -#endif /* !defined(DEBUG) */ +template +class AutoScopedAssign +{ + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + T *addr; + T old; + + public: + AutoScopedAssign(T *addr, const T &value JS_GUARD_OBJECT_NOTIFIER_PARAM) + : addr(addr), old(*addr) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + *addr = value; + } -namespace js { + ~AutoScopedAssign() { *addr = old; } +}; template JS_ALWAYS_INLINE static void @@ -627,7 +203,14 @@ template JS_ALWAYS_INLINE static void PodZero(T *t, size_t nelem) { - memset(t, 0, nelem * sizeof(T)); + /* + * This function is often called with 'nelem' small; we use an + * inline loop instead of calling 'memset' with a non-constant + * length. The compiler should inline the memset call with constant + * size, though. + */ + for (T *end = t + nelem; t != end; ++t) + memset(t, 0, sizeof(T)); } /* @@ -647,6 +230,13 @@ PodArrayZero(T (&t)[N]) memset(t, 0, N * sizeof(T)); } +template +JS_ALWAYS_INLINE static void +PodAssign(T *dst, const T *src) +{ + js_memcpy((char *) dst, (const char *) src, sizeof(T)); +} + template JS_ALWAYS_INLINE static void PodCopy(T *dst, const T *src, size_t nelem) @@ -656,8 +246,12 @@ PodCopy(T *dst, const T *src, size_t nelem) JS_ASSERT_IF(src >= dst, size_t(src - dst) >= nelem); if (nelem < 128) { + /* + * Avoid using operator= in this loop, as it may have been + * intentionally deleted by the POD type. + */ for (const T *srcend = src + nelem; src != srcend; ++src, ++dst) - *dst = *src; + PodAssign(dst, src); } else { memcpy(dst, src, nelem * sizeof(T)); } @@ -679,8 +273,136 @@ PodEqual(T *one, T *two, size_t len) return !memcmp(one, two, len * sizeof(T)); } -} /* namespace js */ +JS_ALWAYS_INLINE static size_t +UnsignedPtrDiff(const void *bigger, const void *smaller) +{ + return size_t(bigger) - size_t(smaller); +} + +/* + * Ordinarily, a function taking a JSContext* 'cx' parameter reports errors on + * the context. In some cases, functions optionally report and indicate this by + * taking a nullable 'maybecx' parameter. In some cases, though, a function + * always needs a 'cx', but optionally reports. This option is presented by the + * MaybeReportError. + */ +enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false }; + +} /* namespace js */ +#endif /* __cplusplus */ + +/* + * JS_ROTATE_LEFT32 + * + * There is no rotate operation in the C Language so the construct (a << 4) | + * (a >> 28) is used instead. Most compilers convert this to a rotate + * instruction but some versions of MSVC don't without a little help. To get + * MSVC to generate a rotate instruction, we have to use the _rotl intrinsic + * and use a pragma to make _rotl inline. + * + * MSVC in VS2005 will do an inline rotate instruction on the above construct. + */ +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \ + defined(_M_X64)) +#include +#pragma intrinsic(_rotl) +#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits) +#else +#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) +#endif + +/* Static control-flow checks. */ +#ifdef NS_STATIC_CHECKING +/* Trigger a control flow check to make sure that code flows through label */ +inline __attribute__ ((unused)) void MUST_FLOW_THROUGH(const char *label) {} + +/* Avoid unused goto-label warnings. */ +# define MUST_FLOW_LABEL(label) goto label; label: + +#else +# define MUST_FLOW_THROUGH(label) ((void) 0) +# define MUST_FLOW_LABEL(label) +#endif + +/* Crash diagnostics */ +#ifdef DEBUG +# define JS_CRASH_DIAGNOSTICS 1 +#endif +#ifdef JS_CRASH_DIAGNOSTICS +# define JS_POISON(p, val, size) memset((p), (val), (size)) +# define JS_OPT_ASSERT(expr) \ + ((expr) ? (void)0 : MOZ_Assert(#expr, __FILE__, __LINE__)) +# define JS_OPT_ASSERT_IF(cond, expr) \ + ((!(cond) || (expr)) ? (void)0 : MOZ_Assert(#expr, __FILE__, __LINE__)) +#else +# define JS_POISON(p, val, size) ((void) 0) +# define JS_OPT_ASSERT(expr) ((void) 0) +# define JS_OPT_ASSERT_IF(cond, expr) ((void) 0) +#endif + +/* Basic stats */ +#ifdef DEBUG +# define JS_BASIC_STATS 1 +#endif +#ifdef JS_BASIC_STATS +# include +typedef struct JSBasicStats { + uint32_t num; + uint32_t max; + double sum; + double sqsum; + uint32_t logscale; /* logarithmic scale: 0 (linear), 2, 10 */ + uint32_t hist[11]; +} JSBasicStats; +# define JS_INIT_STATIC_BASIC_STATS {0,0,0,0,0,{0,0,0,0,0,0,0,0,0,0,0}} +# define JS_BASIC_STATS_INIT(bs) memset((bs), 0, sizeof(JSBasicStats)) +# define JS_BASIC_STATS_ACCUM(bs,val) \ + JS_BasicStatsAccum(bs, val) +# define JS_MeanAndStdDevBS(bs,sigma) \ + JS_MeanAndStdDev((bs)->num, (bs)->sum, (bs)->sqsum, sigma) +extern void +JS_BasicStatsAccum(JSBasicStats *bs, uint32_t val); +extern double +JS_MeanAndStdDev(uint32_t num, double sum, double sqsum, double *sigma); +extern void +JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp); +extern void +JS_DumpHistogram(JSBasicStats *bs, FILE *fp); +#else +# define JS_BASIC_STATS_ACCUM(bs,val) +#endif -#endif /* defined(__cplusplus) */ +/* A jsbitmap_t is a long integer that can be used for bitmaps. */ +typedef size_t jsbitmap; +#define JS_TEST_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & \ + ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) +#define JS_SET_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= \ + ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) +#define JS_CLEAR_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= \ + ~((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) + +/* Wrapper for various macros to stop warnings coming from their expansions. */ +#if defined(__clang__) +# define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr) \ + JS_BEGIN_MACRO \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunused-value\"") \ + expr; \ + _Pragma("clang diagnostic pop") \ + JS_END_MACRO +#elif (__GNUC__ >= 5) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr) \ + JS_BEGIN_MACRO \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wunused-but-set-variable\"") \ + expr; \ + _Pragma("GCC diagnostic pop") \ + JS_END_MACRO +#else +# define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr) \ + JS_BEGIN_MACRO \ + expr; \ + JS_END_MACRO +#endif #endif /* jsutil_h___ */ diff --git a/deps/mozjs/js/src/jsval.h b/deps/mozjs/js/src/jsval.h index f76f2970b7a..796548026ed 100644 --- a/deps/mozjs/js/src/jsval.h +++ b/deps/mozjs/js/src/jsval.h @@ -40,15 +40,50 @@ #ifndef jsvalimpl_h__ #define jsvalimpl_h__ /* - * JS value implementation details for operations on jsval and jsid. - * Embeddings should not rely on any of the definitions in this file. For a - * description of the value representation and the engine-internal C++ value - * interface, js::Value, see jsvalue.h. + * Implementation details for js::Value in jsapi.h. */ -#include "jsutil.h" +#include "js/Utility.h" JS_BEGIN_EXTERN_C +/******************************************************************************/ + +/* To avoid a circular dependency, pull in the necessary pieces of jsnum.h. */ + +#define JSDOUBLE_SIGNBIT (((uint64_t) 1) << 63) +#define JSDOUBLE_EXPMASK (((uint64_t) 0x7ff) << 52) +#define JSDOUBLE_MANTMASK ((((uint64_t) 1) << 52) - 1) +#define JSDOUBLE_HI32_SIGNBIT 0x80000000 + +static JS_ALWAYS_INLINE JSBool +JSDOUBLE_IS_NEGZERO(double d) +{ + union { + struct { +#if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA) + uint32_t lo, hi; +#else + uint32_t hi, lo; +#endif + } s; + double d; + } x; + if (d != 0) + return JS_FALSE; + x.d = d; + return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; +} + +static JS_ALWAYS_INLINE JSBool +JSDOUBLE_IS_INT32(double d, int32_t* pi) +{ + if (JSDOUBLE_IS_NEGZERO(d)) + return JS_FALSE; + return d == (*pi = (int32_t)d); +} + +/******************************************************************************/ + /* * Try to get jsvals 64-bit aligned. We could almost assert that all values are * aligned, but MSVC and GCC occasionally break alignment. @@ -91,7 +126,7 @@ JS_BEGIN_EXTERN_C #endif /* Remember to propagate changes to the C defines below. */ -JS_ENUM_HEADER(JSValueType, uint8) +JS_ENUM_HEADER(JSValueType, uint8_t) { JSVAL_TYPE_DOUBLE = 0x00, JSVAL_TYPE_INT32 = 0x01, @@ -102,15 +137,9 @@ JS_ENUM_HEADER(JSValueType, uint8) JSVAL_TYPE_NULL = 0x06, JSVAL_TYPE_OBJECT = 0x07, - /* The below types never appear in a jsval; they are only used in tracing. */ - - JSVAL_TYPE_NONFUNOBJ = 0x57, - JSVAL_TYPE_FUNOBJ = 0x67, - - JSVAL_TYPE_STRORNULL = 0x97, - JSVAL_TYPE_OBJORNULL = 0x98, - - JSVAL_TYPE_BOXED = 0x99 + /* These never appear in a jsval; they are only provided as an out-of-band value. */ + JSVAL_TYPE_UNKNOWN = 0x20, + JSVAL_TYPE_MISSING = 0x21 } JS_ENUM_FOOTER(JSValueType); JS_STATIC_ASSERT(sizeof(JSValueType) == 1); @@ -118,9 +147,9 @@ JS_STATIC_ASSERT(sizeof(JSValueType) == 1); #if JS_BITS_PER_WORD == 32 /* Remember to propagate changes to the C defines below. */ -JS_ENUM_HEADER(JSValueTag, uint32) +JS_ENUM_HEADER(JSValueTag, uint32_t) { - JSVAL_TAG_CLEAR = 0xFFFF0000, + JSVAL_TAG_CLEAR = 0xFFFFFF80, JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, @@ -135,7 +164,7 @@ JS_STATIC_ASSERT(sizeof(JSValueTag) == 4); #elif JS_BITS_PER_WORD == 64 /* Remember to propagate changes to the C defines below. */ -JS_ENUM_HEADER(JSValueTag, uint32) +JS_ENUM_HEADER(JSValueTag, uint32_t) { JSVAL_TAG_MAX_DOUBLE = 0x1FFF0, JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32, @@ -147,75 +176,70 @@ JS_ENUM_HEADER(JSValueTag, uint32) JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT } JS_ENUM_FOOTER(JSValueTag); -JS_STATIC_ASSERT(sizeof(JSValueTag) == sizeof(uint32)); +JS_STATIC_ASSERT(sizeof(JSValueTag) == sizeof(uint32_t)); -JS_ENUM_HEADER(JSValueShiftedTag, uint64) +JS_ENUM_HEADER(JSValueShiftedTag, uint64_t) { - JSVAL_SHIFTED_TAG_MAX_DOUBLE = ((((uint64)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF), - JSVAL_SHIFTED_TAG_INT32 = (((uint64)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_STRING = (((uint64)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_BOOLEAN = (((uint64)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_MAGIC = (((uint64)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_NULL = (((uint64)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT), - JSVAL_SHIFTED_TAG_OBJECT = (((uint64)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) + JSVAL_SHIFTED_TAG_MAX_DOUBLE = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF), + JSVAL_SHIFTED_TAG_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_STRING = (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_BOOLEAN = (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_MAGIC = (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_NULL = (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_OBJECT = (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) } JS_ENUM_FOOTER(JSValueShiftedTag); -JS_STATIC_ASSERT(sizeof(JSValueShiftedTag) == sizeof(uint64)); +JS_STATIC_ASSERT(sizeof(JSValueShiftedTag) == sizeof(uint64_t)); #endif #else /* defined(__cplusplus) */ -typedef uint8 JSValueType; -#define JSVAL_TYPE_DOUBLE ((uint8)0x00) -#define JSVAL_TYPE_INT32 ((uint8)0x01) -#define JSVAL_TYPE_UNDEFINED ((uint8)0x02) -#define JSVAL_TYPE_BOOLEAN ((uint8)0x03) -#define JSVAL_TYPE_MAGIC ((uint8)0x04) -#define JSVAL_TYPE_STRING ((uint8)0x05) -#define JSVAL_TYPE_NULL ((uint8)0x06) -#define JSVAL_TYPE_OBJECT ((uint8)0x07) -#define JSVAL_TYPE_NONFUNOBJ ((uint8)0x57) -#define JSVAL_TYPE_FUNOBJ ((uint8)0x67) -#define JSVAL_TYPE_STRORNULL ((uint8)0x97) -#define JSVAL_TYPE_OBJORNULL ((uint8)0x98) -#define JSVAL_TYPE_BOXED ((uint8)0x99) -#define JSVAL_TYPE_UNINITIALIZED ((uint8)0xcd) +typedef uint8_t JSValueType; +#define JSVAL_TYPE_DOUBLE ((uint8_t)0x00) +#define JSVAL_TYPE_INT32 ((uint8_t)0x01) +#define JSVAL_TYPE_UNDEFINED ((uint8_t)0x02) +#define JSVAL_TYPE_BOOLEAN ((uint8_t)0x03) +#define JSVAL_TYPE_MAGIC ((uint8_t)0x04) +#define JSVAL_TYPE_STRING ((uint8_t)0x05) +#define JSVAL_TYPE_NULL ((uint8_t)0x06) +#define JSVAL_TYPE_OBJECT ((uint8_t)0x07) +#define JSVAL_TYPE_UNKNOWN ((uint8_t)0x20) #if JS_BITS_PER_WORD == 32 -typedef uint32 JSValueTag; -#define JSVAL_TAG_CLEAR ((uint32)(0xFFFF0000)) -#define JSVAL_TAG_INT32 ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32)) -#define JSVAL_TAG_UNDEFINED ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED)) -#define JSVAL_TAG_STRING ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING)) -#define JSVAL_TAG_BOOLEAN ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN)) -#define JSVAL_TAG_MAGIC ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC)) -#define JSVAL_TAG_NULL ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL)) -#define JSVAL_TAG_OBJECT ((uint32)(JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT)) +typedef uint32_t JSValueTag; +#define JSVAL_TAG_CLEAR ((uint32_t)(0xFFFFFF80)) +#define JSVAL_TAG_INT32 ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32)) +#define JSVAL_TAG_UNDEFINED ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED)) +#define JSVAL_TAG_STRING ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING)) +#define JSVAL_TAG_BOOLEAN ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN)) +#define JSVAL_TAG_MAGIC ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC)) +#define JSVAL_TAG_NULL ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL)) +#define JSVAL_TAG_OBJECT ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT)) #elif JS_BITS_PER_WORD == 64 -typedef uint32 JSValueTag; -#define JSVAL_TAG_MAX_DOUBLE ((uint32)(0x1FFF0)) -#define JSVAL_TAG_INT32 (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) -#define JSVAL_TAG_UNDEFINED (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED) -#define JSVAL_TAG_STRING (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING) -#define JSVAL_TAG_BOOLEAN (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) -#define JSVAL_TAG_MAGIC (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC) -#define JSVAL_TAG_NULL (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) -#define JSVAL_TAG_OBJECT (uint32)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT) - -typedef uint64 JSValueShiftedTag; -#define JSVAL_SHIFTED_TAG_MAX_DOUBLE ((((uint64)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF) -#define JSVAL_SHIFTED_TAG_INT32 (((uint64)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_UNDEFINED (((uint64)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_STRING (((uint64)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_BOOLEAN (((uint64)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_MAGIC (((uint64)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_NULL (((uint64)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT) -#define JSVAL_SHIFTED_TAG_OBJECT (((uint64)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) +typedef uint32_t JSValueTag; +#define JSVAL_TAG_MAX_DOUBLE ((uint32_t)(0x1FFF0)) +#define JSVAL_TAG_INT32 (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) +#define JSVAL_TAG_UNDEFINED (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED) +#define JSVAL_TAG_STRING (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING) +#define JSVAL_TAG_BOOLEAN (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) +#define JSVAL_TAG_MAGIC (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC) +#define JSVAL_TAG_NULL (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) +#define JSVAL_TAG_OBJECT (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT) + +typedef uint64_t JSValueShiftedTag; +#define JSVAL_SHIFTED_TAG_MAX_DOUBLE ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF) +#define JSVAL_SHIFTED_TAG_INT32 (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_UNDEFINED (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_STRING (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_BOOLEAN (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_MAGIC (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_NULL (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_OBJECT (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) #endif /* JS_BITS_PER_WORD */ #endif /* defined(__cplusplus) && !defined(__SUNPRO_CC) */ @@ -224,8 +248,6 @@ typedef uint64 JSValueShiftedTag; #define JSVAL_UPPER_EXCL_TYPE_OF_PRIMITIVE_SET JSVAL_TYPE_OBJECT #define JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET JSVAL_TYPE_INT32 #define JSVAL_LOWER_INCL_TYPE_OF_PTR_PAYLOAD_SET JSVAL_TYPE_MAGIC -#define JSVAL_UPPER_INCL_TYPE_OF_VALUE_SET JSVAL_TYPE_OBJECT -#define JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET JSVAL_TYPE_FUNOBJ #if JS_BITS_PER_WORD == 32 @@ -241,12 +263,11 @@ typedef uint64 JSValueShiftedTag; #define JSVAL_PAYLOAD_MASK 0x00007FFFFFFFFFFFLL #define JSVAL_TAG_MASK 0xFFFF800000000000LL #define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type))) -#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT) +#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT) #define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET JSVAL_SHIFTED_TAG_NULL #define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET JSVAL_SHIFTED_TAG_OBJECT #define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_UNDEFINED -#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_PTR_PAYLOAD_SET JSVAL_SHIFTED_TAG_MAGIC #define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET JSVAL_SHIFTED_TAG_STRING #endif /* JS_BITS_PER_WORD */ @@ -256,7 +277,7 @@ typedef enum JSWhyMagic JS_ARRAY_HOLE, /* a hole in a dense array */ JS_ARGS_HOLE, /* a hole in the args object's array */ JS_NATIVE_ENUMERATE, /* indicates that a custom enumerate hook forwarded - * to js_Enumerate, which really means the object can be + * to JS_EnumerateState, which really means the object can be * enumerated like a native object. */ JS_NO_ITER_VALUE, /* there is not a pending iterator value */ JS_GENERATOR_CLOSING, /* exception value thrown when closing a generator */ @@ -264,120 +285,121 @@ typedef enum JSWhyMagic JS_THIS_POISON, /* used in debug builds to catch tracing errors */ JS_ARG_POISON, /* used in debug builds to catch tracing errors */ JS_SERIALIZE_NO_NODE, /* an empty subnode in the AST serializer */ + JS_LAZY_ARGUMENTS, /* lazy arguments value on the stack */ + JS_IS_CONSTRUCTING, /* magic value passed to natives to indicate construction */ JS_GENERIC_MAGIC /* for local use */ } JSWhyMagic; -#ifdef __cplusplus -class JSString; -class JSFlatString; -#else -typedef struct JSString JSString; -typedef struct JSFlatString JSFlatString; -#endif -typedef struct JSObject JSObject; - #if defined(IS_LITTLE_ENDIAN) # if JS_BITS_PER_WORD == 32 typedef union jsval_layout { - uint64 asBits; + uint64_t asBits; struct { union { - int32 i32; - uint32 u32; + int32_t i32; + uint32_t u32; JSBool boo; JSString *str; JSObject *obj; void *ptr; JSWhyMagic why; - jsuword word; + size_t word; } payload; JSValueTag tag; } s; double asDouble; void *asPtr; -} jsval_layout; +} JSVAL_ALIGNMENT jsval_layout; # elif JS_BITS_PER_WORD == 64 typedef union jsval_layout { - uint64 asBits; + uint64_t asBits; #if (!defined(_WIN64) && defined(__cplusplus)) /* MSVC does not pack these correctly :-( */ struct { - uint64 payload47 : 47; + uint64_t payload47 : 47; JSValueTag tag : 17; } debugView; #endif struct { union { - int32 i32; - uint32 u32; + int32_t i32; + uint32_t u32; JSWhyMagic why; - jsuword word; } payload; } s; double asDouble; void *asPtr; -} jsval_layout; + size_t asWord; +} JSVAL_ALIGNMENT jsval_layout; # endif /* JS_BITS_PER_WORD */ #else /* defined(IS_LITTLE_ENDIAN) */ # if JS_BITS_PER_WORD == 32 typedef union jsval_layout { - uint64 asBits; + uint64_t asBits; struct { JSValueTag tag; union { - int32 i32; - uint32 u32; + int32_t i32; + uint32_t u32; JSBool boo; JSString *str; JSObject *obj; void *ptr; JSWhyMagic why; - jsuword word; + size_t word; } payload; } s; double asDouble; void *asPtr; -} jsval_layout; +} JSVAL_ALIGNMENT jsval_layout; # elif JS_BITS_PER_WORD == 64 typedef union jsval_layout { - uint64 asBits; + uint64_t asBits; struct { JSValueTag tag : 17; - uint64 payload47 : 47; + uint64_t payload47 : 47; } debugView; struct { + uint32_t padding; union { - int32 i32; - uint32 u32; + int32_t i32; + uint32_t u32; JSWhyMagic why; - jsuword word; } payload; } s; double asDouble; void *asPtr; -} jsval_layout; + size_t asWord; +} JSVAL_ALIGNMENT jsval_layout; # endif /* JS_BITS_PER_WORD */ #endif /* defined(IS_LITTLE_ENDIAN) */ +JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); + #if JS_BITS_PER_WORD == 32 /* * N.B. GCC, in some but not all cases, chooses to emit signed comparison of - * JSValueTag even though its underlying type has been forced to be uint32. - * Thus, all comparisons should explicitly cast operands to uint32. + * JSValueTag even though its underlying type has been forced to be uint32_t. + * Thus, all comparisons should explicitly cast operands to uint32_t. */ -#define BUILD_JSVAL(tag, payload) \ - ((((uint64)(uint32)(tag)) << 32) | (uint32)(payload)) +static JS_ALWAYS_INLINE jsval_layout +BUILD_JSVAL(JSValueTag tag, uint32_t payload) +{ + jsval_layout l; + l.asBits = (((uint64_t)(uint32_t)tag) << 32) | payload; + return l; +} static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE_IMPL(jsval_layout l) { - return (uint32)l.s.tag <= (uint32)JSVAL_TAG_CLEAR; + return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR; } static JS_ALWAYS_INLINE jsval_layout @@ -395,14 +417,14 @@ JSVAL_IS_INT32_IMPL(jsval_layout l) return l.s.tag == JSVAL_TAG_INT32; } -static JS_ALWAYS_INLINE int32 +static JS_ALWAYS_INLINE int32_t JSVAL_TO_INT32_IMPL(jsval_layout l) { return l.s.payload.i32; } static JS_ALWAYS_INLINE jsval_layout -INT32_TO_JSVAL_IMPL(int32 i) +INT32_TO_JSVAL_IMPL(int32_t i) { jsval_layout l; l.s.tag = JSVAL_TAG_INT32; @@ -415,7 +437,7 @@ JSVAL_IS_NUMBER_IMPL(jsval_layout l) { JSValueTag tag = l.s.tag; JS_ASSERT(tag != JSVAL_TAG_CLEAR); - return (uint32)tag <= (uint32)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET; + return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET; } static JS_ALWAYS_INLINE JSBool @@ -462,6 +484,7 @@ static JS_ALWAYS_INLINE jsval_layout BOOLEAN_TO_JSVAL_IMPL(JSBool b) { jsval_layout l; + JS_ASSERT(b == JS_TRUE || b == JS_FALSE); l.s.tag = JSVAL_TAG_BOOLEAN; l.s.payload.boo = b; return l; @@ -473,13 +496,6 @@ JSVAL_IS_MAGIC_IMPL(jsval_layout l) return l.s.tag == JSVAL_TAG_MAGIC; } -static JS_ALWAYS_INLINE JSObject * -MAGIC_JSVAL_TO_OBJECT_OR_NULL_IMPL(jsval_layout l) -{ - JS_ASSERT(JSVAL_IS_MAGIC_IMPL(l)); - return l.s.payload.obj; -} - static JS_ALWAYS_INLINE JSBool JSVAL_IS_OBJECT_IMPL(jsval_layout l) { @@ -489,14 +505,14 @@ JSVAL_IS_OBJECT_IMPL(jsval_layout l) static JS_ALWAYS_INLINE JSBool JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l) { - return (uint32)l.s.tag < (uint32)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET; + return (uint32_t)l.s.tag < (uint32_t)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET; } static JS_ALWAYS_INLINE JSBool JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l) { - JS_ASSERT((uint32)l.s.tag <= (uint32)JSVAL_TAG_OBJECT); - return (uint32)l.s.tag >= (uint32)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET; + JS_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT); + return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET; } static JS_ALWAYS_INLINE JSObject * @@ -525,7 +541,7 @@ static JS_ALWAYS_INLINE jsval_layout PRIVATE_PTR_TO_JSVAL_IMPL(void *ptr) { jsval_layout l; - JS_ASSERT(((uint32)ptr & 1) == 0); + JS_ASSERT(((uint32_t)ptr & 1) == 0); l.s.tag = (JSValueTag)0; l.s.payload.ptr = ptr; JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); @@ -542,7 +558,7 @@ static JS_ALWAYS_INLINE JSBool JSVAL_IS_GCTHING_IMPL(jsval_layout l) { /* gcc sometimes generates signed < without explicit casts. */ - return (uint32)l.s.tag >= (uint32)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET; + return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET; } static JS_ALWAYS_INLINE void * @@ -557,16 +573,73 @@ JSVAL_IS_TRACEABLE_IMPL(jsval_layout l) return l.s.tag == JSVAL_TAG_STRING || l.s.tag == JSVAL_TAG_OBJECT; } -static JS_ALWAYS_INLINE uint32 +static JS_ALWAYS_INLINE uint32_t JSVAL_TRACE_KIND_IMPL(jsval_layout l) { - return (uint32)(JSBool)JSVAL_IS_STRING_IMPL(l); + return (uint32_t)(JSBool)JSVAL_IS_STRING_IMPL(l); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32) +{ + return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) +{ + return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == b); +} + +static JS_ALWAYS_INLINE jsval_layout +MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) +{ + jsval_layout l; + l.s.tag = JSVAL_TAG_MAGIC; + l.s.payload.why = why; + return l; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag; + return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); +} + +static JS_ALWAYS_INLINE jsval_layout +PRIVATE_UINT32_TO_JSVAL_IMPL(uint32_t ui) +{ + jsval_layout l; + l.s.tag = (JSValueTag)0; + l.s.payload.u32 = ui; + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); + return l; +} + +static JS_ALWAYS_INLINE uint32_t +JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l) +{ + return l.s.payload.u32; +} + +static JS_ALWAYS_INLINE JSValueType +JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l) +{ + uint32_t type = l.s.tag & 0xF; + JS_ASSERT(type > JSVAL_TYPE_DOUBLE); + return (JSValueType)type; } #elif JS_BITS_PER_WORD == 64 -#define BUILD_JSVAL(tag, payload) \ - ((((uint64)(uint32)(tag)) << JSVAL_TAG_SHIFT) | (payload)) +static JS_ALWAYS_INLINE jsval_layout +BUILD_JSVAL(JSValueTag tag, uint64_t payload) +{ + jsval_layout l; + l.asBits = (((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload; + return l; +} static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE_IMPL(jsval_layout l) @@ -586,20 +659,20 @@ DOUBLE_TO_JSVAL_IMPL(double d) static JS_ALWAYS_INLINE JSBool JSVAL_IS_INT32_IMPL(jsval_layout l) { - return (uint32)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32; + return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32; } -static JS_ALWAYS_INLINE int32 +static JS_ALWAYS_INLINE int32_t JSVAL_TO_INT32_IMPL(jsval_layout l) { - return (int32)l.asBits; + return (int32_t)l.asBits; } static JS_ALWAYS_INLINE jsval_layout -INT32_TO_JSVAL_IMPL(int32 i32) +INT32_TO_JSVAL_IMPL(int32_t i32) { jsval_layout l; - l.asBits = ((uint64)(uint32)i32) | JSVAL_SHIFTED_TAG_INT32; + l.asBits = ((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32; return l; } @@ -618,14 +691,14 @@ JSVAL_IS_UNDEFINED_IMPL(jsval_layout l) static JS_ALWAYS_INLINE JSBool JSVAL_IS_STRING_IMPL(jsval_layout l) { - return (uint32)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING; + return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING; } static JS_ALWAYS_INLINE jsval_layout STRING_TO_JSVAL_IMPL(JSString *str) { jsval_layout l; - uint64 strBits = (uint64)str; + uint64_t strBits = (uint64_t)str; JS_ASSERT(str); JS_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0); l.asBits = strBits | JSVAL_SHIFTED_TAG_STRING; @@ -641,7 +714,7 @@ JSVAL_TO_STRING_IMPL(jsval_layout l) static JS_ALWAYS_INLINE JSBool JSVAL_IS_BOOLEAN_IMPL(jsval_layout l) { - return (uint32)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN; + return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN; } static JS_ALWAYS_INLINE JSBool @@ -654,7 +727,8 @@ static JS_ALWAYS_INLINE jsval_layout BOOLEAN_TO_JSVAL_IMPL(JSBool b) { jsval_layout l; - l.asBits = ((uint64)(uint32)b) | JSVAL_SHIFTED_TAG_BOOLEAN; + JS_ASSERT(b == JS_TRUE || b == JS_FALSE); + l.asBits = ((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN; return l; } @@ -664,15 +738,6 @@ JSVAL_IS_MAGIC_IMPL(jsval_layout l) return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_MAGIC; } -static JS_ALWAYS_INLINE JSObject * -MAGIC_JSVAL_TO_OBJECT_OR_NULL_IMPL(jsval_layout l) -{ - uint64 ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; - JS_ASSERT(JSVAL_IS_MAGIC_IMPL(l)); - JS_ASSERT((ptrBits >> JSVAL_TAG_SHIFT) == 0); - return (JSObject *)ptrBits; -} - static JS_ALWAYS_INLINE JSBool JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l) { @@ -696,7 +761,7 @@ JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l) static JS_ALWAYS_INLINE JSObject * JSVAL_TO_OBJECT_IMPL(jsval_layout l) { - uint64 ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; + uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; JS_ASSERT((ptrBits & 0x7) == 0); return (JSObject *)ptrBits; } @@ -705,7 +770,7 @@ static JS_ALWAYS_INLINE jsval_layout OBJECT_TO_JSVAL_IMPL(JSObject *obj) { jsval_layout l; - uint64 objBits = (uint64)obj; + uint64_t objBits = (uint64_t)obj; JS_ASSERT(obj); JS_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0); l.asBits = objBits | JSVAL_SHIFTED_TAG_OBJECT; @@ -727,7 +792,7 @@ JSVAL_IS_GCTHING_IMPL(jsval_layout l) static JS_ALWAYS_INLINE void * JSVAL_TO_GCTHING_IMPL(jsval_layout l) { - uint64 ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; + uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; JS_ASSERT((ptrBits & 0x7) == 0); return (void *)ptrBits; } @@ -738,17 +803,17 @@ JSVAL_IS_TRACEABLE_IMPL(jsval_layout l) return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l); } -static JS_ALWAYS_INLINE uint32 +static JS_ALWAYS_INLINE uint32_t JSVAL_TRACE_KIND_IMPL(jsval_layout l) { - return (uint32)(JSBool)!(JSVAL_IS_OBJECT_IMPL(l)); + return (uint32_t)(JSBool)!(JSVAL_IS_OBJECT_IMPL(l)); } static JS_ALWAYS_INLINE jsval_layout PRIVATE_PTR_TO_JSVAL_IMPL(void *ptr) { jsval_layout l; - uint64 ptrBits = (uint64)ptr; + uint64_t ptrBits = (uint64_t)ptr; JS_ASSERT((ptrBits & 1) == 0); l.asBits = ptrBits >> 1; JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); @@ -762,78 +827,76 @@ JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l) return (void *)(l.asBits << 1); } -#endif +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32) +{ + return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32); +} -static JS_ALWAYS_INLINE double -JS_CANONICALIZE_NAN(double d) +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) { - if (JS_UNLIKELY(d != d)) { - jsval_layout l; - l.asBits = 0x7FF8000000000000LL; - return l.asDouble; - } - return d; + return l.asBits == (((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN); } -/* See JS_USE_JSVAL_JSID_STRUCT_TYPES comment in jsapi.h. */ -#if defined(DEBUG) && !defined(JS_NO_JSVAL_JSID_STRUCT_TYPES) -# define JS_USE_JSVAL_JSID_STRUCT_TYPES -#endif +static JS_ALWAYS_INLINE jsval_layout +MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) +{ + jsval_layout l; + l.asBits = ((uint64_t)(uint32_t)why) | JSVAL_SHIFTED_TAG_MAGIC; + return l; +} -#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES +static JS_ALWAYS_INLINE JSBool +JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + uint64_t lbits = lhs.asBits, rbits = rhs.asBits; + return (lbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE && rbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE) || + (((lbits ^ rbits) & 0xFFFF800000000000LL) == 0); +} -typedef JSVAL_ALIGNMENT jsval_layout jsval; -typedef struct jsid { size_t asBits; } jsid; +static JS_ALWAYS_INLINE jsval_layout +PRIVATE_UINT32_TO_JSVAL_IMPL(uint32_t ui) +{ + jsval_layout l; + l.asBits = (uint64_t)ui; + JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); + return l; +} -#if defined(__cplusplus) -extern "C++" +static JS_ALWAYS_INLINE uint32_t +JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l) { - static JS_ALWAYS_INLINE bool - operator==(jsid lhs, jsid rhs) - { - return lhs.asBits == rhs.asBits; - } + JS_ASSERT((l.asBits >> 32) == 0); + return (uint32_t)l.asBits; +} - static JS_ALWAYS_INLINE bool - operator!=(jsid lhs, jsid rhs) - { - return lhs.asBits != rhs.asBits; - } +static JS_ALWAYS_INLINE JSValueType +JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l) +{ + uint64_t type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF; + JS_ASSERT(type > JSVAL_TYPE_DOUBLE); + return (JSValueType)type; +} - static JS_ALWAYS_INLINE bool - operator==(jsval lhs, jsval rhs) - { - return lhs.asBits == rhs.asBits; - } +#endif /* JS_BITS_PER_WORD */ - static JS_ALWAYS_INLINE bool - operator!=(jsval lhs, jsval rhs) - { - return lhs.asBits != rhs.asBits; +static JS_ALWAYS_INLINE double +JS_CANONICALIZE_NAN(double d) +{ + if (JS_UNLIKELY(d != d)) { + jsval_layout l; + l.asBits = 0x7FF8000000000000LL; + return l.asDouble; } + return d; } -# endif /* defined(__cplusplus) */ - -/* Internal helper macros */ -#define JSVAL_BITS(v) ((v).asBits) -#define JSVAL_FROM_LAYOUT(l) (l) -#define IMPL_TO_JSVAL(v) (v) -#define JSID_BITS(id) ((id).asBits) - -#else /* defined(JS_USE_JSVAL_JSID_STRUCT_TYPES) */ - -/* Use different primitive types so overloading works. */ -typedef JSVAL_ALIGNMENT uint64 jsval; -typedef ptrdiff_t jsid; - -/* Internal helper macros */ -#define JSVAL_BITS(v) (v) -#define JSVAL_FROM_LAYOUT(l) ((l).asBits) -#define IMPL_TO_JSVAL(v) ((v).asBits) -#define JSID_BITS(id) (id) - -#endif /* defined(JS_USE_JSVAL_JSID_STRUCT_TYPES) */ JS_END_EXTERN_C +#ifdef __cplusplus +static jsval_layout JSVAL_TO_IMPL(JS::Value); +static JS::Value IMPL_TO_JSVAL(jsval_layout); +#endif + #endif /* jsvalimpl_h__ */ diff --git a/deps/mozjs/js/src/jsvalue.h b/deps/mozjs/js/src/jsvalue.h deleted file mode 100644 index ebe6cc98777..00000000000 --- a/deps/mozjs/js/src/jsvalue.h +++ /dev/null @@ -1,1240 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * June 30, 2010 - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * - * Contributor(s): - * Luke Wagner - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsvalue_h__ -#define jsvalue_h__ -/* - * Private value interface. - */ -#include "jsprvtd.h" -#include "jsstdint.h" - -/* - * js::Value is a C++-ified version of jsval that provides more information and - * helper functions than the basic jsval interface exposed by jsapi.h. A few - * general notes on js::Value: - * - * - Since js::Value and jsval have the same representation, values of these - * types, function pointer types differing only in these types, and structs - * differing only in these types can be converted back and forth at no cost - * using the Jsvalify() and Valueify(). See Jsvalify comment below. - * - * - js::Value has setX() and isX() members for X in - * - * { Int32, Double, String, Boolean, Undefined, Null, Object, Magic } - * - * js::Value also contains toX() for each of the non-singleton types. - * - * - Magic is a singleton type whose payload contains a JSWhyMagic "reason" for - * the magic value. By providing JSWhyMagic values when creating and checking - * for magic values, it is possible to assert, at runtime, that only magic - * values with the expected reason flow through a particular value. For - * example, if cx->exception has a magic value, the reason must be - * JS_GENERATOR_CLOSING. - * - * - A key difference between jsval and js::Value is that js::Value gives null - * a separate type. Thus - * - * JSVAL_IS_OBJECT(v) === v.isObjectOrNull() - * !JSVAL_IS_PRIMITIVE(v) === v.isObject() - * - * To help prevent mistakenly boxing a nullable JSObject* as an object, - * Value::setObject takes a JSObject&. (Conversely, Value::asObject returns a - * JSObject&. A convenience member Value::setObjectOrNull is provided. - * - * - JSVAL_VOID is the same as the singleton value of the Undefined type. - * - * - Note that js::Value is always 64-bit. Thus, on 32-bit user code should - * avoid copying jsval/js::Value as much as possible, preferring to pass by - * const Value &. - */ - -/******************************************************************************/ - -/* To avoid a circular dependency, pull in the necessary pieces of jsnum.h. */ - -#define JSDOUBLE_SIGNBIT (((uint64) 1) << 63) -#define JSDOUBLE_EXPMASK (((uint64) 0x7ff) << 52) -#define JSDOUBLE_MANTMASK ((((uint64) 1) << 52) - 1) - -static JS_ALWAYS_INLINE JSBool -JSDOUBLE_IS_NEGZERO(jsdouble d) -{ - union { - jsdouble d; - uint64 bits; - } x; - x.d = d; - return x.bits == JSDOUBLE_SIGNBIT; -} - -static inline bool -JSDOUBLE_IS_INT32(jsdouble d, int32_t* pi) -{ - if (JSDOUBLE_IS_NEGZERO(d)) - return false; - return d == (*pi = int32_t(d)); -} - -/******************************************************************************/ - -/* Additional value operations used in js::Value but not in jsapi.h. */ - -#if JS_BITS_PER_WORD == 32 - -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32 i32) -{ - return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32; -} - -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) -{ - return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == b); -} - -static JS_ALWAYS_INLINE jsval_layout -MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) -{ - jsval_layout l; - l.s.tag = JSVAL_TAG_MAGIC; - l.s.payload.why = why; - return l; -} - -static JS_ALWAYS_INLINE jsval_layout -MAGIC_TO_JSVAL_IMPL(JSObject *obj) -{ - jsval_layout l; - l.s.tag = JSVAL_TAG_MAGIC; - l.s.payload.obj = obj; - return l; -} - -static JS_ALWAYS_INLINE JSBool -JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs) -{ - JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag; - return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); -} - -static JS_ALWAYS_INLINE jsval_layout -PRIVATE_UINT32_TO_JSVAL_IMPL(uint32 ui) -{ - jsval_layout l; - l.s.tag = (JSValueTag)0; - l.s.payload.u32 = ui; - JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - -static JS_ALWAYS_INLINE uint32 -JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l) -{ - return l.s.payload.u32; -} - -static JS_ALWAYS_INLINE JSValueType -JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l) -{ - uint32 type = l.s.tag & 0xF; - JS_ASSERT(type > JSVAL_TYPE_DOUBLE); - return (JSValueType)type; -} - -static JS_ALWAYS_INLINE JSValueTag -JSVAL_EXTRACT_NON_DOUBLE_TAG_IMPL(jsval_layout l) -{ - JSValueTag tag = l.s.tag; - JS_ASSERT(tag >= JSVAL_TAG_INT32); - return tag; -} - -#ifdef __cplusplus -JS_STATIC_ASSERT((JSVAL_TYPE_NONFUNOBJ & 0xF) == JSVAL_TYPE_OBJECT); -JS_STATIC_ASSERT((JSVAL_TYPE_FUNOBJ & 0xF) == JSVAL_TYPE_OBJECT); -#endif - -static JS_ALWAYS_INLINE jsval_layout -BOX_NON_DOUBLE_JSVAL(JSValueType type, uint64 *slot) -{ - jsval_layout l; - JS_ASSERT(type > JSVAL_TYPE_DOUBLE && type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET); - JS_ASSERT_IF(type == JSVAL_TYPE_STRING || - type == JSVAL_TYPE_OBJECT || - type == JSVAL_TYPE_NONFUNOBJ || - type == JSVAL_TYPE_FUNOBJ, - *(uint32 *)slot != 0); - l.s.tag = JSVAL_TYPE_TO_TAG(type & 0xF); - /* A 32-bit value in a 64-bit slot always occupies the low-addressed end. */ - l.s.payload.u32 = *(uint32 *)slot; - return l; -} - -static JS_ALWAYS_INLINE void -UNBOX_NON_DOUBLE_JSVAL(jsval_layout l, uint64 *out) -{ - JS_ASSERT(!JSVAL_IS_DOUBLE_IMPL(l)); - *(uint32 *)out = l.s.payload.u32; -} - -#elif JS_BITS_PER_WORD == 64 - -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32 i32) -{ - return l.asBits == (((uint64)(uint32)i32) | JSVAL_SHIFTED_TAG_INT32); -} - -static JS_ALWAYS_INLINE JSBool -JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) -{ - return l.asBits == (((uint64)(uint32)b) | JSVAL_SHIFTED_TAG_BOOLEAN); -} - -static JS_ALWAYS_INLINE jsval_layout -MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) -{ - jsval_layout l; - l.asBits = ((uint64)(uint32)why) | JSVAL_SHIFTED_TAG_MAGIC; - return l; -} - -static JS_ALWAYS_INLINE jsval_layout -MAGIC_TO_JSVAL_IMPL(JSObject *obj) -{ - jsval_layout l; - l.asBits = ((uint64)obj) | JSVAL_SHIFTED_TAG_MAGIC; - return l; -} - -static JS_ALWAYS_INLINE JSBool -JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs) -{ - uint64 lbits = lhs.asBits, rbits = rhs.asBits; - return (lbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE && rbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE) || - (((lbits ^ rbits) & 0xFFFF800000000000LL) == 0); -} - -static JS_ALWAYS_INLINE jsval_layout -PRIVATE_UINT32_TO_JSVAL_IMPL(uint32 ui) -{ - jsval_layout l; - l.asBits = (uint64)ui; - JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - -static JS_ALWAYS_INLINE uint32 -JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l) -{ - JS_ASSERT((l.asBits >> 32) == 0); - return (uint32)l.asBits; -} - -static JS_ALWAYS_INLINE JSValueType -JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l) -{ - uint64 type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF; - JS_ASSERT(type > JSVAL_TYPE_DOUBLE); - return (JSValueType)type; -} - -static JS_ALWAYS_INLINE JSValueTag -JSVAL_EXTRACT_NON_DOUBLE_TAG_IMPL(jsval_layout l) -{ - uint64 tag = l.asBits >> JSVAL_TAG_SHIFT; - JS_ASSERT(tag > JSVAL_TAG_MAX_DOUBLE); - return (JSValueTag)tag; -} - -#ifdef __cplusplus -JS_STATIC_ASSERT(offsetof(jsval_layout, s.payload) == 0); -JS_STATIC_ASSERT((JSVAL_TYPE_NONFUNOBJ & 0xF) == JSVAL_TYPE_OBJECT); -JS_STATIC_ASSERT((JSVAL_TYPE_FUNOBJ & 0xF) == JSVAL_TYPE_OBJECT); -#endif - -static JS_ALWAYS_INLINE jsval_layout -BOX_NON_DOUBLE_JSVAL(JSValueType type, uint64 *slot) -{ - /* N.B. for 32-bit payloads, the high 32 bits of the slot are trash. */ - jsval_layout l; - JS_ASSERT(type > JSVAL_TYPE_DOUBLE && type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET); - uint32 isI32 = (uint32)(type < JSVAL_LOWER_INCL_TYPE_OF_PTR_PAYLOAD_SET); - uint32 shift = isI32 * 32; - uint64 mask = ((uint64)-1) >> shift; - uint64 payload = *slot & mask; - JS_ASSERT_IF(type == JSVAL_TYPE_STRING || - type == JSVAL_TYPE_OBJECT || - type == JSVAL_TYPE_NONFUNOBJ || - type == JSVAL_TYPE_FUNOBJ, - payload != 0); - l.asBits = payload | JSVAL_TYPE_TO_SHIFTED_TAG(type & 0xF); - return l; -} - -static JS_ALWAYS_INLINE void -UNBOX_NON_DOUBLE_JSVAL(jsval_layout l, uint64 *out) -{ - JS_ASSERT(!JSVAL_IS_DOUBLE_IMPL(l)); - *out = (l.asBits & JSVAL_PAYLOAD_MASK); -} - -#endif - -/******************************************************************************/ - -namespace js { - -class Value -{ - public: - /* - * N.B. the default constructor leaves Value unitialized. Adding a default - * constructor prevents Value from being stored in a union. - */ - - /*** Mutatators ***/ - - JS_ALWAYS_INLINE - void setNull() { - data.asBits = JSVAL_BITS(JSVAL_NULL); - } - - JS_ALWAYS_INLINE - void setUndefined() { - data.asBits = JSVAL_BITS(JSVAL_VOID); - } - - JS_ALWAYS_INLINE - void setInt32(int32 i) { - data = INT32_TO_JSVAL_IMPL(i); - } - - JS_ALWAYS_INLINE - int32 &getInt32Ref() { - JS_ASSERT(isInt32()); - return data.s.payload.i32; - } - - JS_ALWAYS_INLINE - void setDouble(double d) { - data = DOUBLE_TO_JSVAL_IMPL(d); - } - - JS_ALWAYS_INLINE - double &getDoubleRef() { - JS_ASSERT(isDouble()); - return data.asDouble; - } - - JS_ALWAYS_INLINE - void setString(JSString *str) { - data = STRING_TO_JSVAL_IMPL(str); - } - - JS_ALWAYS_INLINE - void setString(const JS::Anchor &str) { - setString(str.get()); - } - - JS_ALWAYS_INLINE - void setObject(JSObject &obj) { - data = OBJECT_TO_JSVAL_IMPL(&obj); - } - - JS_ALWAYS_INLINE - void setObject(const JS::Anchor &obj) { - setObject(*obj.get()); - } - - JS_ALWAYS_INLINE - void setBoolean(bool b) { - data = BOOLEAN_TO_JSVAL_IMPL(b); - } - - JS_ALWAYS_INLINE - void setMagic(JSWhyMagic why) { - data = MAGIC_TO_JSVAL_IMPL(why); - } - - JS_ALWAYS_INLINE - void setMagicWithObjectOrNullPayload(JSObject *obj) { - data = MAGIC_TO_JSVAL_IMPL(obj); - } - - JS_ALWAYS_INLINE - JSObject *getMagicObjectOrNullPayload() const { - return MAGIC_JSVAL_TO_OBJECT_OR_NULL_IMPL(data); - } - - JS_ALWAYS_INLINE - void setNumber(uint32 ui) { - if (ui > JSVAL_INT_MAX) - setDouble((double)ui); - else - setInt32((int32)ui); - } - - JS_ALWAYS_INLINE - void setNumber(double d) { - int32_t i; - if (JSDOUBLE_IS_INT32(d, &i)) - setInt32(i); - else - setDouble(d); - } - - JS_ALWAYS_INLINE - void setObjectOrNull(JSObject *arg) { - if (arg) - setObject(*arg); - else - setNull(); - } - - JS_ALWAYS_INLINE - void setObjectOrUndefined(JSObject *arg) { - if (arg) - setObject(*arg); - else - setUndefined(); - } - - JS_ALWAYS_INLINE - void swap(Value &rhs) { - uint64 tmp = rhs.data.asBits; - rhs.data.asBits = data.asBits; - data.asBits = tmp; - } - - /*** Value type queries ***/ - - JS_ALWAYS_INLINE - bool isUndefined() const { - return JSVAL_IS_UNDEFINED_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isNull() const { - return JSVAL_IS_NULL_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isNullOrUndefined() const { - return isNull() || isUndefined(); - } - - JS_ALWAYS_INLINE - bool isInt32() const { - return JSVAL_IS_INT32_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isInt32(int32 i32) const { - return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32); - } - - JS_ALWAYS_INLINE - bool isDouble() const { - return JSVAL_IS_DOUBLE_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isNumber() const { - return JSVAL_IS_NUMBER_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isString() const { - return JSVAL_IS_STRING_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isObject() const { - return JSVAL_IS_OBJECT_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isPrimitive() const { - return JSVAL_IS_PRIMITIVE_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isObjectOrNull() const { - return JSVAL_IS_OBJECT_OR_NULL_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isGCThing() const { - return JSVAL_IS_GCTHING_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isBoolean() const { - return JSVAL_IS_BOOLEAN_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isTrue() const { - return JSVAL_IS_SPECIFIC_BOOLEAN(data, true); - } - - JS_ALWAYS_INLINE - bool isFalse() const { - return JSVAL_IS_SPECIFIC_BOOLEAN(data, false); - } - - JS_ALWAYS_INLINE - bool isMagic() const { - return JSVAL_IS_MAGIC_IMPL(data); - } - - JS_ALWAYS_INLINE - bool isMagic(JSWhyMagic why) const { - JS_ASSERT_IF(isMagic(), data.s.payload.why == why); - return JSVAL_IS_MAGIC_IMPL(data); - } - -#if JS_BITS_PER_WORD == 64 - JS_ALWAYS_INLINE - bool hasPtrPayload() const { - return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_PTR_PAYLOAD_SET; - } -#endif - - JS_ALWAYS_INLINE - bool isMarkable() const { - return JSVAL_IS_TRACEABLE_IMPL(data); - } - - JS_ALWAYS_INLINE - int32 gcKind() const { - JS_ASSERT(isMarkable()); - return JSVAL_TRACE_KIND_IMPL(data); - } - -#ifdef DEBUG - JS_ALWAYS_INLINE - JSWhyMagic whyMagic() const { - JS_ASSERT(isMagic()); - return data.s.payload.why; - } -#endif - - /*** Comparison ***/ - - JS_ALWAYS_INLINE - bool operator==(const Value &rhs) const { - return data.asBits == rhs.data.asBits; - } - - JS_ALWAYS_INLINE - bool operator!=(const Value &rhs) const { - return data.asBits != rhs.data.asBits; - } - - /* This function used to be inlined here, but this triggered a gcc bug - due to SameType being used in a template method. - See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38850 */ - friend bool SameType(const Value &lhs, const Value &rhs); - - /*** Extract the value's typed payload ***/ - - JS_ALWAYS_INLINE - int32 toInt32() const { - JS_ASSERT(isInt32()); - return JSVAL_TO_INT32_IMPL(data); - } - - JS_ALWAYS_INLINE - double toDouble() const { - JS_ASSERT(isDouble()); - return data.asDouble; - } - - JS_ALWAYS_INLINE - double toNumber() const { - JS_ASSERT(isNumber()); - return isDouble() ? toDouble() : double(toInt32()); - } - - JS_ALWAYS_INLINE - JSString *toString() const { - JS_ASSERT(isString()); - return JSVAL_TO_STRING_IMPL(data); - } - - JS_ALWAYS_INLINE - JSObject &toObject() const { - JS_ASSERT(isObject()); - return *JSVAL_TO_OBJECT_IMPL(data); - } - - JS_ALWAYS_INLINE - JSObject *toObjectOrNull() const { - JS_ASSERT(isObjectOrNull()); - return JSVAL_TO_OBJECT_IMPL(data); - } - - JS_ALWAYS_INLINE - void *toGCThing() const { - JS_ASSERT(isGCThing()); - return JSVAL_TO_GCTHING_IMPL(data); - } - - JS_ALWAYS_INLINE - bool toBoolean() const { - JS_ASSERT(isBoolean()); - return JSVAL_TO_BOOLEAN_IMPL(data); - } - - JS_ALWAYS_INLINE - uint32 payloadAsRawUint32() const { - JS_ASSERT(!isDouble()); - return data.s.payload.u32; - } - - JS_ALWAYS_INLINE - uint64 asRawBits() const { - return data.asBits; - } - - /* - * In the extract/box/unbox functions below, "NonDouble" means this - * functions must not be called on a value that is a double. This allows - * these operations to be implemented more efficiently, since doubles - * generally already require special handling by the caller. - */ - JS_ALWAYS_INLINE - JSValueType extractNonDoubleType() const { - return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data); - } - - JS_ALWAYS_INLINE - JSValueTag extractNonDoubleTag() const { - return JSVAL_EXTRACT_NON_DOUBLE_TAG_IMPL(data); - } - - JS_ALWAYS_INLINE - void unboxNonDoubleTo(uint64 *out) const { - UNBOX_NON_DOUBLE_JSVAL(data, out); - } - - JS_ALWAYS_INLINE - void boxNonDoubleFrom(JSValueType type, uint64 *out) { - data = BOX_NON_DOUBLE_JSVAL(type, out); - } - - /* - * The trace-jit specializes JSVAL_TYPE_OBJECT into JSVAL_TYPE_FUNOBJ and - * JSVAL_TYPE_NONFUNOBJ. Since these two operations just return the type of - * a value, the caller must handle JSVAL_TYPE_OBJECT separately. - */ - JS_ALWAYS_INLINE - JSValueType extractNonDoubleObjectTraceType() const { - JS_ASSERT(!isObject()); - return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data); - } - - JS_ALWAYS_INLINE - JSValueTag extractNonDoubleObjectTraceTag() const { - JS_ASSERT(!isObject()); - return JSVAL_EXTRACT_NON_DOUBLE_TAG_IMPL(data); - } - - /* - * Private API - * - * Private setters/getters allow the caller to read/write arbitrary types - * that fit in the 64-bit payload. It is the caller's responsibility, after - * storing to a value with setPrivateX to read only using getPrivateX. - * Privates values are given a type type which ensures they are not marked. - */ - - JS_ALWAYS_INLINE - void setPrivate(void *ptr) { - data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr); - } - - JS_ALWAYS_INLINE - void *toPrivate() const { - JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); - return JSVAL_TO_PRIVATE_PTR_IMPL(data); - } - - JS_ALWAYS_INLINE - void setPrivateUint32(uint32 ui) { - data = PRIVATE_UINT32_TO_JSVAL_IMPL(ui); - } - - JS_ALWAYS_INLINE - uint32 toPrivateUint32() const { - JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); - return JSVAL_TO_PRIVATE_UINT32_IMPL(data); - } - - JS_ALWAYS_INLINE - uint32 &getPrivateUint32Ref() { - JS_ASSERT(isDouble()); - return data.s.payload.u32; - } - - /* - * An unmarked value is just a void* cast as a Value. Thus, the Value is - * not safe for GC and must not be marked. This API avoids raw casts - * and the ensuing strict-aliasing warnings. - */ - - JS_ALWAYS_INLINE - void setUnmarkedPtr(void *ptr) { - data.asPtr = ptr; - } - - JS_ALWAYS_INLINE - void *toUnmarkedPtr() const { - return data.asPtr; - } - - const jsuword *payloadWord() const { - return &data.s.payload.word; - } - - private: - void staticAssertions() { - JS_STATIC_ASSERT(sizeof(JSValueType) == 1); - JS_STATIC_ASSERT(sizeof(JSValueTag) == 4); - JS_STATIC_ASSERT(sizeof(JSBool) == 4); - JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4); - JS_STATIC_ASSERT(sizeof(jsval) == 8); - } - - jsval_layout data; -} JSVAL_ALIGNMENT; - -JS_ALWAYS_INLINE bool -SameType(const Value &lhs, const Value &rhs) -{ - return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data); -} - -static JS_ALWAYS_INLINE Value -NullValue() -{ - Value v; - v.setNull(); - return v; -} - -static JS_ALWAYS_INLINE Value -UndefinedValue() -{ - Value v; - v.setUndefined(); - return v; -} - -static JS_ALWAYS_INLINE Value -Int32Value(int32 i32) -{ - Value v; - v.setInt32(i32); - return v; -} - -static JS_ALWAYS_INLINE Value -DoubleValue(double dbl) -{ - Value v; - v.setDouble(dbl); - return v; -} - -static JS_ALWAYS_INLINE Value -StringValue(JSString *str) -{ - Value v; - v.setString(str); - return v; -} - -static JS_ALWAYS_INLINE Value -BooleanValue(bool boo) -{ - Value v; - v.setBoolean(boo); - return v; -} - -static JS_ALWAYS_INLINE Value -ObjectValue(JSObject &obj) -{ - Value v; - v.setObject(obj); - return v; -} - -static JS_ALWAYS_INLINE Value -MagicValue(JSWhyMagic why) -{ - Value v; - v.setMagic(why); - return v; -} - -static JS_ALWAYS_INLINE Value -NumberValue(double dbl) -{ - Value v; - v.setNumber(dbl); - return v; -} - -static JS_ALWAYS_INLINE Value -ObjectOrNullValue(JSObject *obj) -{ - Value v; - v.setObjectOrNull(obj); - return v; -} - -static JS_ALWAYS_INLINE Value -PrivateValue(void *ptr) -{ - Value v; - v.setPrivate(ptr); - return v; -} - -static JS_ALWAYS_INLINE void -ClearValueRange(Value *vec, uintN len, bool useHoles) -{ - if (useHoles) { - for (uintN i = 0; i < len; i++) - vec[i].setMagic(JS_ARRAY_HOLE); - } else { - for (uintN i = 0; i < len; i++) - vec[i].setUndefined(); - } -} - -/******************************************************************************/ - -/* - * As asserted above, js::Value and jsval are layout equivalent. This means: - * - an instance of jsval may be reinterpreted as a js::Value and vice versa; - * - a pointer to a function taking jsval arguments may be reinterpreted as a - * function taking the same arguments, s/jsval/js::Value/, and vice versa; - * - a struct containing jsval members may be reinterpreted as a struct with - * the same members, s/jsval/js::Value/, and vice versa. - * - * To prevent widespread conversion using casts, which would effectively - * disable the C++ typesystem in places where we want it, a set of safe - * conversions between known-equivalent types is provided below. Given a type - * JsvalT expressedin terms of jsval and an equivalent type ValueT expressed in - * terms of js::Value, instances may be converted back and forth using: - * - * JsvalT *x = ... - * ValueT *y = js::Valueify(x); - * JsvalT *z = js::Jsvalify(y); - * assert(x == z); - * - * Conversions between references is also provided for some types. If it seems - * like a cast is needed to convert between jsval/js::Value, consider adding a - * new safe overload to Jsvalify/Valueify. - */ - -static inline jsval * Jsvalify(Value *v) { return (jsval *)v; } -static inline const jsval * Jsvalify(const Value *v) { return (const jsval *)v; } -static inline jsval & Jsvalify(Value &v) { return (jsval &)v; } -static inline const jsval & Jsvalify(const Value &v) { return (const jsval &)v; } -static inline Value * Valueify(jsval *v) { return (Value *)v; } -static inline const Value * Valueify(const jsval *v) { return (const Value *)v; } -static inline Value ** Valueify(jsval **v) { return (Value **)v; } -static inline Value & Valueify(jsval &v) { return (Value &)v; } -static inline const Value & Valueify(const jsval &v) { return (const Value &)v; } - -struct Class; - -typedef JSBool -(* Native)(JSContext *cx, uintN argc, Value *vp); -typedef JSBool -(* PropertyOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp); -typedef JSBool -(* StrictPropertyOp)(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp); -typedef JSBool -(* ConvertOp)(JSContext *cx, JSObject *obj, JSType type, Value *vp); -typedef JSBool -(* NewEnumerateOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - Value *statep, jsid *idp); -typedef JSBool -(* HasInstanceOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp); -typedef JSBool -(* CheckAccessOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - Value *vp); -typedef JSBool -(* EqualityOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp); -typedef JSBool -(* DefinePropOp)(JSContext *cx, JSObject *obj, jsid id, const Value *value, - PropertyOp getter, StrictPropertyOp setter, uintN attrs); -typedef JSBool -(* PropertyIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp); -typedef JSBool -(* StrictPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict); -typedef JSBool -(* DeleteIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict); -typedef JSBool -(* CallOp)(JSContext *cx, uintN argc, Value *vp); -typedef JSBool -(* LookupPropOp)(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp); -typedef JSBool -(* AttributesOp)(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp); -typedef JSType -(* TypeOfOp)(JSContext *cx, JSObject *obj); -typedef JSObject * -(* ObjectOp)(JSContext *cx, JSObject *obj); -typedef void -(* FinalizeOp)(JSContext *cx, JSObject *obj); - -class AutoIdVector; - -/* - * Prepare to make |obj| non-extensible; in particular, fully resolve its properties. - * On error, return false. - * If |obj| is now ready to become non-extensible, set |*fixed| to true and return true. - * If |obj| refuses to become non-extensible, set |*fixed| to false and return true; the - * caller will throw an appropriate error. - */ -typedef JSBool -(* FixOp)(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props); - -static inline Native Valueify(JSNative f) { return (Native)f; } -static inline JSNative Jsvalify(Native f) { return (JSNative)f; } -static inline PropertyOp Valueify(JSPropertyOp f) { return (PropertyOp)f; } -static inline JSPropertyOp Jsvalify(PropertyOp f) { return (JSPropertyOp)f; } -static inline StrictPropertyOp Valueify(JSStrictPropertyOp f) { return (StrictPropertyOp)f; } -static inline JSStrictPropertyOp Jsvalify(StrictPropertyOp f) { return (JSStrictPropertyOp)f; } -static inline ConvertOp Valueify(JSConvertOp f) { return (ConvertOp)f; } -static inline JSConvertOp Jsvalify(ConvertOp f) { return (JSConvertOp)f; } -static inline NewEnumerateOp Valueify(JSNewEnumerateOp f) { return (NewEnumerateOp)f; } -static inline JSNewEnumerateOp Jsvalify(NewEnumerateOp f) { return (JSNewEnumerateOp)f; } -static inline HasInstanceOp Valueify(JSHasInstanceOp f) { return (HasInstanceOp)f; } -static inline JSHasInstanceOp Jsvalify(HasInstanceOp f) { return (JSHasInstanceOp)f; } -static inline CheckAccessOp Valueify(JSCheckAccessOp f) { return (CheckAccessOp)f; } -static inline JSCheckAccessOp Jsvalify(CheckAccessOp f) { return (JSCheckAccessOp)f; } -static inline EqualityOp Valueify(JSEqualityOp f); /* Same type as JSHasInstanceOp */ -static inline JSEqualityOp Jsvalify(EqualityOp f); /* Same type as HasInstanceOp */ - -static const PropertyOp PropertyStub = (PropertyOp)JS_PropertyStub; -static const StrictPropertyOp StrictPropertyStub = (StrictPropertyOp)JS_StrictPropertyStub; -static const JSEnumerateOp EnumerateStub = JS_EnumerateStub; -static const JSResolveOp ResolveStub = JS_ResolveStub; -static const ConvertOp ConvertStub = (ConvertOp)JS_ConvertStub; -static const JSFinalizeOp FinalizeStub = JS_FinalizeStub; - -#define JS_CLASS_MEMBERS \ - const char *name; \ - uint32 flags; \ - \ - /* Mandatory non-null function pointer members. */ \ - PropertyOp addProperty; \ - PropertyOp delProperty; \ - PropertyOp getProperty; \ - StrictPropertyOp setProperty; \ - JSEnumerateOp enumerate; \ - JSResolveOp resolve; \ - ConvertOp convert; \ - JSFinalizeOp finalize; \ - \ - /* Optionally non-null members start here. */ \ - JSClassInternal reserved0; \ - CheckAccessOp checkAccess; \ - Native call; \ - Native construct; \ - JSXDRObjectOp xdrObject; \ - HasInstanceOp hasInstance; \ - JSTraceOp trace - -/* - * The helper struct to measure the size of JS_CLASS_MEMBERS to know how much - * we have to padd js::Class to match the size of JSClass; - */ -struct ClassSizeMeasurement { - JS_CLASS_MEMBERS; -}; - -struct ClassExtension { - EqualityOp equality; - JSObjectOp outerObject; - JSObjectOp innerObject; - JSIteratorOp iteratorObject; - void *unused; -}; - -#define JS_NULL_CLASS_EXT {NULL,NULL,NULL,NULL,NULL} - -struct ObjectOps { - js::LookupPropOp lookupProperty; - js::DefinePropOp defineProperty; - js::PropertyIdOp getProperty; - js::StrictPropertyIdOp setProperty; - js::AttributesOp getAttributes; - js::AttributesOp setAttributes; - js::DeleteIdOp deleteProperty; - js::NewEnumerateOp enumerate; - js::TypeOfOp typeOf; - js::FixOp fix; - js::ObjectOp thisObject; - js::FinalizeOp clear; -}; - -#define JS_NULL_OBJECT_OPS {NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL} - -struct Class { - JS_CLASS_MEMBERS; - ClassExtension ext; - ObjectOps ops; - uint8 pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) - - sizeof(ClassExtension) - sizeof(ObjectOps)]; - - /* Class is not native and its map is not a scope. */ - static const uint32 NON_NATIVE = JSCLASS_INTERNAL_FLAG2; - - bool isNative() const { - return !(flags & NON_NATIVE); - } -}; - -JS_STATIC_ASSERT(offsetof(JSClass, name) == offsetof(Class, name)); -JS_STATIC_ASSERT(offsetof(JSClass, flags) == offsetof(Class, flags)); -JS_STATIC_ASSERT(offsetof(JSClass, addProperty) == offsetof(Class, addProperty)); -JS_STATIC_ASSERT(offsetof(JSClass, delProperty) == offsetof(Class, delProperty)); -JS_STATIC_ASSERT(offsetof(JSClass, getProperty) == offsetof(Class, getProperty)); -JS_STATIC_ASSERT(offsetof(JSClass, setProperty) == offsetof(Class, setProperty)); -JS_STATIC_ASSERT(offsetof(JSClass, enumerate) == offsetof(Class, enumerate)); -JS_STATIC_ASSERT(offsetof(JSClass, resolve) == offsetof(Class, resolve)); -JS_STATIC_ASSERT(offsetof(JSClass, convert) == offsetof(Class, convert)); -JS_STATIC_ASSERT(offsetof(JSClass, finalize) == offsetof(Class, finalize)); -JS_STATIC_ASSERT(offsetof(JSClass, reserved0) == offsetof(Class, reserved0)); -JS_STATIC_ASSERT(offsetof(JSClass, checkAccess) == offsetof(Class, checkAccess)); -JS_STATIC_ASSERT(offsetof(JSClass, call) == offsetof(Class, call)); -JS_STATIC_ASSERT(offsetof(JSClass, construct) == offsetof(Class, construct)); -JS_STATIC_ASSERT(offsetof(JSClass, xdrObject) == offsetof(Class, xdrObject)); -JS_STATIC_ASSERT(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance)); -JS_STATIC_ASSERT(offsetof(JSClass, trace) == offsetof(Class, trace)); -JS_STATIC_ASSERT(sizeof(JSClass) == sizeof(Class)); - -struct PropertyDescriptor { - JSObject *obj; - uintN attrs; - PropertyOp getter; - StrictPropertyOp setter; - Value value; - uintN shortid; -}; -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, obj) == offsetof(PropertyDescriptor, obj)); -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, attrs) == offsetof(PropertyDescriptor, attrs)); -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, getter) == offsetof(PropertyDescriptor, getter)); -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, setter) == offsetof(PropertyDescriptor, setter)); -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, value) == offsetof(PropertyDescriptor, value)); -JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, shortid) == offsetof(PropertyDescriptor, shortid)); -JS_STATIC_ASSERT(sizeof(JSPropertyDescriptor) == sizeof(PropertyDescriptor)); - -static JS_ALWAYS_INLINE JSClass * Jsvalify(Class *c) { return (JSClass *)c; } -static JS_ALWAYS_INLINE Class * Valueify(JSClass *c) { return (Class *)c; } -static JS_ALWAYS_INLINE JSPropertyDescriptor * Jsvalify(PropertyDescriptor *p) { return (JSPropertyDescriptor *) p; } -static JS_ALWAYS_INLINE PropertyDescriptor * Valueify(JSPropertyDescriptor *p) { return (PropertyDescriptor *) p; } - -/******************************************************************************/ - -/* - * Any cast-via-function-call, inlined or not, will cause initialization to - * happen at startup, rather than statically, so just cast in release builds. - */ -#ifdef DEBUG - -# define JS_VALUEIFY(type, v) js::Valueify(v) -# define JS_JSVALIFY(type, v) js::Jsvalify(v) - -static inline JSNative JsvalifyNative(Native n) { return (JSNative)n; } -static inline JSNative JsvalifyNative(JSNative n) { return n; } -static inline Native ValueifyNative(JSNative n) { return (Native)n; } -static inline Native ValueifyNative(Native n) { return n; } - -# define JS_VALUEIFY_NATIVE(n) js::ValueifyNative(n) -# define JS_JSVALIFY_NATIVE(n) js::JsvalifyNative(n) - -#else - -# define JS_VALUEIFY(type, v) ((type)(v)) -# define JS_JSVALIFY(type, v) ((type)(v)) - -# define JS_VALUEIFY_NATIVE(n) ((js::Native)(n)) -# define JS_JSVALIFY_NATIVE(n) ((JSNative)(n)) - -#endif - -/* - * JSFunctionSpec uses JSAPI jsval in function signatures whereas the engine - * uses js::Value. To avoid widespread (JSNative) casting, have JS_FN perfom a - * type-safe cast. - */ -#undef JS_FN -#define JS_FN(name,call,nargs,flags) \ - {name, JS_JSVALIFY_NATIVE(call), nargs, (flags) | JSFUN_STUB_GSOPS} - -/******************************************************************************/ - -/* - * In some cases (quickstubs) we want to take a value in whatever manner is - * appropriate for the architecture and normalize to a const js::Value &. On - * x64, passing a js::Value may cause the to unnecessarily be passed through - * memory instead of registers, so jsval, which is a builtin uint64 is used. - */ -#if JS_BITS_PER_WORD == 32 -typedef const js::Value *ValueArgType; - -static JS_ALWAYS_INLINE const js::Value & -ValueArgToConstRef(const js::Value *arg) -{ - return *arg; -} - -#elif JS_BITS_PER_WORD == 64 -typedef js::Value ValueArgType; - -static JS_ALWAYS_INLINE const Value & -ValueArgToConstRef(const Value &v) -{ - return v; -} -#endif - -/******************************************************************************/ - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(Value *vec, size_t len) -{ - PodZero(vec, len); -} - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(Value *beg, Value *end) -{ - PodZero(beg, end - beg); -} - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(jsid *beg, jsid *end) -{ - for (jsid *id = beg; id != end; ++id) - *id = INT_TO_JSID(0); -} - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(jsid *vec, size_t len) -{ - MakeRangeGCSafe(vec, vec + len); -} - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(const Shape **beg, const Shape **end) -{ - PodZero(beg, end - beg); -} - -static JS_ALWAYS_INLINE void -MakeRangeGCSafe(const Shape **vec, size_t len) -{ - PodZero(vec, len); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToUndefined(Value *beg, Value *end) -{ - for (Value *v = beg; v != end; ++v) - v->setUndefined(); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToUndefined(Value *vec, size_t len) -{ - SetValueRangeToUndefined(vec, vec + len); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToNull(Value *beg, Value *end) -{ - for (Value *v = beg; v != end; ++v) - v->setNull(); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToNull(Value *vec, size_t len) -{ - SetValueRangeToNull(vec, vec + len); -} - -/* - * To really poison a set of values, using 'magic' or 'undefined' isn't good - * enough since often these will just be ignored by buggy code (see bug 629974) - * in debug builds and crash in release builds. Instead, we use a safe-for-crash - * pointer. - */ -static JS_ALWAYS_INLINE void -Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end) -{ -#ifdef DEBUG - for (Value *v = beg; v != end; ++v) - v->setObject(*reinterpret_cast(0x42)); -#endif -} - -static JS_ALWAYS_INLINE void -Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len) -{ -#ifdef DEBUG - Debug_SetValueRangeToCrashOnTouch(vec, vec + len); -#endif -} - -} /* namespace js */ -#endif /* jsvalue_h__ */ diff --git a/deps/mozjs/js/src/jsvector.h b/deps/mozjs/js/src/jsvector.h deleted file mode 100644 index 3d413c1f64b..00000000000 --- a/deps/mozjs/js/src/jsvector.h +++ /dev/null @@ -1,860 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * June 12, 2009. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * - * Contributor(s): - * Luke Wagner - * Nicholas Nethercote - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsvector_h_ -#define jsvector_h_ - -#include "jsalloc.h" -#include "jstl.h" -#include "jsprvtd.h" - -/* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */ -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4345) -#endif - -namespace js { - -/* - * This template class provides a default implementation for vector operations - * when the element type is not known to be a POD, as judged by IsPodType. - */ -template -struct VectorImpl -{ - /* Destroys constructed objects in the range [begin, end). */ - static inline void destroy(T *begin, T *end) { - for (T *p = begin; p != end; ++p) - p->~T(); - } - - /* Constructs objects in the uninitialized range [begin, end). */ - static inline void initialize(T *begin, T *end) { - for (T *p = begin; p != end; ++p) - new(p) T(); - } - - /* - * Copy-constructs objects in the uninitialized range - * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). - */ - template - static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { - for (const U *p = srcbeg; p != srcend; ++p, ++dst) - new(dst) T(*p); - } - - /* - * Copy-constructs objects in the uninitialized range [dst, dst+n) from the - * same object u. - */ - template - static inline void copyConstructN(T *dst, size_t n, const U &u) { - for (T *end = dst + n; dst != end; ++dst) - new(dst) T(u); - } - - /* - * Grows the given buffer to have capacity newcap, preserving the objects - * constructed in the range [begin, end) and updating v. Assumes that (1) - * newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will - * not overflow. - */ - static inline bool growTo(Vector &v, size_t newcap) { - JS_ASSERT(!v.usingInlineStorage()); - T *newbuf = reinterpret_cast(v.malloc_(newcap * sizeof(T))); - if (!newbuf) - return false; - for (T *dst = newbuf, *src = v.beginNoCheck(); src != v.endNoCheck(); ++dst, ++src) - new(dst) T(*src); - VectorImpl::destroy(v.beginNoCheck(), v.endNoCheck()); - v.free_(v.mBegin); - v.mBegin = newbuf; - /* v.mLength is unchanged. */ - v.mCapacity = newcap; - return true; - } -}; - -/* - * This partial template specialization provides a default implementation for - * vector operations when the element type is known to be a POD, as judged by - * IsPodType. - */ -template -struct VectorImpl -{ - static inline void destroy(T *, T *) {} - - static inline void initialize(T *begin, T *end) { - /* - * You would think that memset would be a big win (or even break even) - * when we know T is a POD. But currently it's not. This is probably - * because |append| tends to be given small ranges and memset requires - * a function call that doesn't get inlined. - * - * memset(begin, 0, sizeof(T) * (end-begin)); - */ - for (T *p = begin; p != end; ++p) - new(p) T(); - } - - template - static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { - /* - * See above memset comment. Also, notice that copyConstruct is - * currently templated (T != U), so memcpy won't work without - * requiring T == U. - * - * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg)); - */ - for (const U *p = srcbeg; p != srcend; ++p, ++dst) - *dst = *p; - } - - static inline void copyConstructN(T *dst, size_t n, const T &t) { - for (T *p = dst, *end = dst + n; p != end; ++p) - *p = t; - } - - static inline bool growTo(Vector &v, size_t newcap) { - JS_ASSERT(!v.usingInlineStorage()); - size_t bytes = sizeof(T) * newcap; - T *newbuf = reinterpret_cast(v.realloc_(v.mBegin, bytes)); - if (!newbuf) - return false; - v.mBegin = newbuf; - /* v.mLength is unchanged. */ - v.mCapacity = newcap; - return true; - } -}; - -/* - * JS-friendly, STL-like container providing a short-lived, dynamic buffer. - * Vector calls the constructors/destructors of all elements stored in - * its internal buffer, so non-PODs may be safely used. Additionally, - * Vector will store the first N elements in-place before resorting to - * dynamic allocation. - * - * T requirements: - * - default and copy constructible, assignable, destructible - * - operations do not throw - * N requirements: - * - any value, however, N is clamped to min/max values - * AllocPolicy: - * - see "Allocation policies" in jstl.h (default ContextAllocPolicy) - * - * N.B: Vector is not reentrant: T member functions called during Vector member - * functions must not call back into the same object. - */ -template -class Vector : private AllocPolicy -{ - /* utilities */ - - static const bool sElemIsPod = tl::IsPodType::result; - typedef VectorImpl Impl; - friend struct VectorImpl; - - bool calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap); - bool growStorageBy(size_t lengthInc); - bool growHeapStorageBy(size_t lengthInc); - bool convertToHeapStorage(size_t lengthInc); - - template inline bool growByImpl(size_t inc); - - /* magic constants */ - - static const int sMaxInlineBytes = 1024; - - /* compute constants */ - - static const size_t sInlineCapacity = - tl::Min::result; - - /* Calculate inline buffer size; avoid 0-sized array. */ - static const size_t sInlineBytes = - tl::Max<1, sInlineCapacity * sizeof(T)>::result; - - /* member data */ - - /* - * Pointer to the buffer, be it inline or heap-allocated. Only [mBegin, - * mBegin + mLength) hold valid constructed T objects. The range [mBegin + - * mLength, mBegin + mCapacity) holds uninitialized memory. The range - * [mBegin + mLength, mBegin + mReserved) also holds uninitialized memory - * previously allocated by a call to reserve(). - */ - T *mBegin; - size_t mLength; /* Number of elements in the Vector. */ - size_t mCapacity; /* Max number of elements storable in the Vector without resizing. */ -#ifdef DEBUG - size_t mReserved; /* Max elements of reserved or used space in this vector. */ -#endif - - AlignedStorage storage; - -#ifdef DEBUG - friend class ReentrancyGuard; - bool entered; -#endif - - Vector(const Vector &); - Vector &operator=(const Vector &); - - /* private accessors */ - - bool usingInlineStorage() const { - return mBegin == (T *)storage.addr(); - } - - T *beginNoCheck() const { - return mBegin; - } - - T *endNoCheck() { - return mBegin + mLength; - } - - const T *endNoCheck() const { - return mBegin + mLength; - } - -#ifdef DEBUG - size_t reserved() const { - JS_ASSERT(mReserved <= mCapacity); - JS_ASSERT(mLength <= mReserved); - return mReserved; - } -#endif - - /* Append operations guaranteed to succeed due to pre-reserved space. */ - void internalAppend(const T &t); - void internalAppendN(const T &t, size_t n); - template void internalAppend(const U *begin, size_t length); - template void internalAppend(const Vector &other); - - public: - static const size_t sMaxInlineStorage = N; - - typedef T ElementType; - - Vector(AllocPolicy = AllocPolicy()); - ~Vector(); - - /* accessors */ - - const AllocPolicy &allocPolicy() const { - return *this; - } - - enum { InlineLength = N }; - - size_t length() const { - return mLength; - } - - bool empty() const { - return mLength == 0; - } - - size_t capacity() const { - return mCapacity; - } - - T *begin() const { - JS_ASSERT(!entered); - return mBegin; - } - - T *end() { - JS_ASSERT(!entered); - return mBegin + mLength; - } - - const T *end() const { - JS_ASSERT(!entered); - return mBegin + mLength; - } - - T &operator[](size_t i) { - JS_ASSERT(!entered && i < mLength); - return begin()[i]; - } - - const T &operator[](size_t i) const { - JS_ASSERT(!entered && i < mLength); - return begin()[i]; - } - - T &back() { - JS_ASSERT(!entered && !empty()); - return *(end() - 1); - } - - const T &back() const { - JS_ASSERT(!entered && !empty()); - return *(end() - 1); - } - - /* mutators */ - - /* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */ - bool reserve(size_t capacity); - - /* - * Destroy elements in the range [end() - incr, end()). Does not deallocate - * or unreserve storage for those elements. - */ - void shrinkBy(size_t incr); - - /* Grow the vector by incr elements. */ - bool growBy(size_t incr); - - /* Call shrinkBy or growBy based on whether newSize > length(). */ - bool resize(size_t newLength); - - /* Leave new elements as uninitialized memory. */ - bool growByUninitialized(size_t incr); - bool resizeUninitialized(size_t newLength); - - /* Shorthand for shrinkBy(length()). */ - void clear(); - - /* Potentially fallible append operations. */ - bool append(const T &t); - bool appendN(const T &t, size_t n); - template bool append(const U *begin, const U *end); - template bool append(const U *begin, size_t length); - template bool append(const Vector &other); - - /* - * Guaranteed-infallible append operations for use upon vectors whose - * memory has been pre-reserved. - */ - void infallibleAppend(const T &t) { - internalAppend(t); - } - void infallibleAppendN(const T &t, size_t n) { - internalAppendN(t, n); - } - template void infallibleAppend(const U *begin, const U *end) { - internalAppend(begin, PointerRangeSize(begin, end)); - } - template void infallibleAppend(const U *begin, size_t length) { - internalAppend(begin, length); - } - template void infallibleAppend(const Vector &other) { - internalAppend(other); - } - - void popBack(); - - T popCopy(); - - /* - * Transfers ownership of the internal buffer used by Vector to the caller. - * After this call, the Vector is empty. Since the returned buffer may need - * to be allocated (if the elements are currently stored in-place), the - * call can fail, returning NULL. - * - * N.B. Although a T*, only the range [0, length()) is constructed. - */ - T *extractRawBuffer(); - - /* - * Transfer ownership of an array of objects into the Vector. - * N.B. This call assumes that there are no uninitialized elements in the - * passed array. - */ - void replaceRawBuffer(T *p, size_t length); - - /* - * Places |val| at position |p|, shifting existing elements - * from |p| onward one position higher. - */ - bool insert(T *p, const T &val); - - /* - * Removes the element |t|, which must fall in the bounds [begin, end), - * shifting existing elements from |t + 1| onward one position lower. - */ - void erase(T *t); -}; - -/* This does the re-entrancy check plus several other sanity checks. */ -#define REENTRANCY_GUARD_ET_AL \ - ReentrancyGuard g(*this); \ - JS_ASSERT_IF(usingInlineStorage(), mCapacity == sInlineCapacity); \ - JS_ASSERT(reserved() <= mCapacity); \ - JS_ASSERT(mLength <= reserved()); \ - JS_ASSERT(mLength <= mCapacity) - -/* Vector Implementation */ - -template -JS_ALWAYS_INLINE -Vector::Vector(AllocPolicy ap) - : AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0), - mCapacity(sInlineCapacity) -#ifdef DEBUG - , mReserved(0), entered(false) -#endif -{} - -template -JS_ALWAYS_INLINE -Vector::~Vector() -{ - REENTRANCY_GUARD_ET_AL; - Impl::destroy(beginNoCheck(), endNoCheck()); - if (!usingInlineStorage()) - this->free_(beginNoCheck()); -} - -/* - * Calculate a new capacity that is at least lengthInc greater than - * curLength and check for overflow. - */ -template -STATIC_POSTCONDITION(!return || newCap >= curLength + lengthInc) -inline bool -Vector::calculateNewCapacity(size_t curLength, size_t lengthInc, - size_t &newCap) -{ - size_t newMinCap = curLength + lengthInc; - - /* - * Check for overflow in the above addition, below CEILING_LOG2, and later - * multiplication by sizeof(T). - */ - if (newMinCap < curLength || - newMinCap & tl::MulOverflowMask<2 * sizeof(T)>::result) { - this->reportAllocOverflow(); - return false; - } - - /* Round up to next power of 2. */ - newCap = RoundUpPow2(newMinCap); - - /* - * Do not allow a buffer large enough that the expression ((char *)end() - - * (char *)begin()) overflows ptrdiff_t. See Bug 510319. - */ - if (newCap & tl::UnsafeRangeSizeMask::result) { - this->reportAllocOverflow(); - return false; - } - return true; -} - -/* - * This function will grow the current heap capacity to have capacity - * (mLength + lengthInc) and fail on OOM or integer overflow. - */ -template -JS_ALWAYS_INLINE bool -Vector::growHeapStorageBy(size_t lengthInc) -{ - JS_ASSERT(!usingInlineStorage()); - size_t newCap; - return calculateNewCapacity(mLength, lengthInc, newCap) && - Impl::growTo(*this, newCap); -} - -/* - * This function will create a new heap buffer with capacity (mLength + - * lengthInc()), move all elements in the inline buffer to this new buffer, - * and fail on OOM or integer overflow. - */ -template -inline bool -Vector::convertToHeapStorage(size_t lengthInc) -{ - JS_ASSERT(usingInlineStorage()); - size_t newCap; - if (!calculateNewCapacity(mLength, lengthInc, newCap)) - return false; - - /* Allocate buffer. */ - T *newBuf = reinterpret_cast(this->malloc_(newCap * sizeof(T))); - if (!newBuf) - return false; - - /* Copy inline elements into heap buffer. */ - Impl::copyConstruct(newBuf, beginNoCheck(), endNoCheck()); - Impl::destroy(beginNoCheck(), endNoCheck()); - - /* Switch in heap buffer. */ - mBegin = newBuf; - /* mLength is unchanged. */ - mCapacity = newCap; - return true; -} - -template -JS_NEVER_INLINE bool -Vector::growStorageBy(size_t incr) -{ - JS_ASSERT(mLength + incr > mCapacity); - return usingInlineStorage() - ? convertToHeapStorage(incr) - : growHeapStorageBy(incr); -} - -template -inline bool -Vector::reserve(size_t request) -{ - REENTRANCY_GUARD_ET_AL; - if (request <= mCapacity || growStorageBy(request - mLength)) { -#ifdef DEBUG - if (request > mReserved) - mReserved = request; - JS_ASSERT(mLength <= mReserved); - JS_ASSERT(mReserved <= mCapacity); -#endif - return true; - } - return false; -} - -template -inline void -Vector::shrinkBy(size_t incr) -{ - REENTRANCY_GUARD_ET_AL; - JS_ASSERT(incr <= mLength); - Impl::destroy(endNoCheck() - incr, endNoCheck()); - mLength -= incr; -} - -template -template -JS_ALWAYS_INLINE bool -Vector::growByImpl(size_t incr) -{ - REENTRANCY_GUARD_ET_AL; - if (incr > mCapacity - mLength && !growStorageBy(incr)) - return false; - - JS_ASSERT(mLength + incr <= mCapacity); - T *newend = endNoCheck() + incr; - if (InitNewElems) - Impl::initialize(endNoCheck(), newend); - mLength += incr; -#ifdef DEBUG - if (mLength > mReserved) - mReserved = mLength; -#endif - return true; -} - -template -JS_ALWAYS_INLINE bool -Vector::growBy(size_t incr) -{ - return growByImpl(incr); -} - -template -JS_ALWAYS_INLINE bool -Vector::growByUninitialized(size_t incr) -{ - return growByImpl(incr); -} - -template -STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength) -inline bool -Vector::resize(size_t newLength) -{ - size_t curLength = mLength; - if (newLength > curLength) - return growBy(newLength - curLength); - shrinkBy(curLength - newLength); - return true; -} - -template -JS_ALWAYS_INLINE bool -Vector::resizeUninitialized(size_t newLength) -{ - size_t curLength = mLength; - if (newLength > curLength) - return growByUninitialized(newLength - curLength); - shrinkBy(curLength - newLength); - return true; -} - -template -inline void -Vector::clear() -{ - REENTRANCY_GUARD_ET_AL; - Impl::destroy(beginNoCheck(), endNoCheck()); - mLength = 0; -} - -template -JS_ALWAYS_INLINE bool -Vector::append(const T &t) -{ - REENTRANCY_GUARD_ET_AL; - if (mLength == mCapacity && !growStorageBy(1)) - return false; - -#ifdef DEBUG - if (mLength + 1 > mReserved) - mReserved = mLength + 1; -#endif - internalAppend(t); - return true; -} - -template -JS_ALWAYS_INLINE void -Vector::internalAppend(const T &t) -{ - JS_ASSERT(mLength + 1 <= mReserved); - JS_ASSERT(mReserved <= mCapacity); - new(endNoCheck()) T(t); - ++mLength; -} - -template -JS_ALWAYS_INLINE bool -Vector::appendN(const T &t, size_t needed) -{ - REENTRANCY_GUARD_ET_AL; - if (mLength + needed > mCapacity && !growStorageBy(needed)) - return false; - -#ifdef DEBUG - if (mLength + needed > mReserved) - mReserved = mLength + needed; -#endif - internalAppendN(t, needed); - return true; -} - -template -JS_ALWAYS_INLINE void -Vector::internalAppendN(const T &t, size_t needed) -{ - JS_ASSERT(mLength + needed <= mReserved); - JS_ASSERT(mReserved <= mCapacity); - Impl::copyConstructN(endNoCheck(), needed, t); - mLength += needed; -} - -template -inline bool -Vector::insert(T *p, const T &val) -{ - JS_ASSERT(begin() <= p && p <= end()); - size_t pos = p - begin(); - JS_ASSERT(pos <= mLength); - size_t oldLength = mLength; - if (pos == oldLength) - return append(val); - { - T oldBack = back(); - if (!append(oldBack)) /* Dup the last element. */ - return false; - } - for (size_t i = oldLength; i > pos; --i) - (*this)[i] = (*this)[i - 1]; - (*this)[pos] = val; - return true; -} - -template -inline void -Vector::erase(T *it) -{ - JS_ASSERT(begin() <= it && it < end()); - while (it + 1 != end()) { - *it = *(it + 1); - ++it; - } - popBack(); -} - -template -template -JS_ALWAYS_INLINE bool -Vector::append(const U *insBegin, const U *insEnd) -{ - REENTRANCY_GUARD_ET_AL; - size_t needed = PointerRangeSize(insBegin, insEnd); - if (mLength + needed > mCapacity && !growStorageBy(needed)) - return false; - -#ifdef DEBUG - if (mLength + needed > mReserved) - mReserved = mLength + needed; -#endif - internalAppend(insBegin, needed); - return true; -} - -template -template -JS_ALWAYS_INLINE void -Vector::internalAppend(const U *insBegin, size_t length) -{ - JS_ASSERT(mLength + length <= mReserved); - JS_ASSERT(mReserved <= mCapacity); - Impl::copyConstruct(endNoCheck(), insBegin, insBegin + length); - mLength += length; -} - -template -template -inline bool -Vector::append(const Vector &other) -{ - return append(other.begin(), other.end()); -} - -template -template -inline void -Vector::internalAppend(const Vector &other) -{ - internalAppend(other.begin(), other.length()); -} - -template -template -JS_ALWAYS_INLINE bool -Vector::append(const U *insBegin, size_t length) -{ - return this->append(insBegin, insBegin + length); -} - -template -JS_ALWAYS_INLINE void -Vector::popBack() -{ - REENTRANCY_GUARD_ET_AL; - JS_ASSERT(!empty()); - --mLength; - endNoCheck()->~T(); -} - -template -JS_ALWAYS_INLINE T -Vector::popCopy() -{ - T ret = back(); - popBack(); - return ret; -} - -template -inline T * -Vector::extractRawBuffer() -{ - T *ret; - if (usingInlineStorage()) { - ret = reinterpret_cast(this->malloc_(mLength * sizeof(T))); - if (!ret) - return NULL; - Impl::copyConstruct(ret, beginNoCheck(), endNoCheck()); - Impl::destroy(beginNoCheck(), endNoCheck()); - /* mBegin, mCapacity are unchanged. */ - mLength = 0; - } else { - ret = mBegin; - mBegin = (T *)storage.addr(); - mLength = 0; - mCapacity = sInlineCapacity; -#ifdef DEBUG - mReserved = 0; -#endif - } - return ret; -} - -template -inline void -Vector::replaceRawBuffer(T *p, size_t length) -{ - REENTRANCY_GUARD_ET_AL; - - /* Destroy what we have. */ - Impl::destroy(beginNoCheck(), endNoCheck()); - if (!usingInlineStorage()) - this->free_(beginNoCheck()); - - /* Take in the new buffer. */ - if (length <= sInlineCapacity) { - /* - * We convert to inline storage if possible, even though p might - * otherwise be acceptable. Maybe this behaviour should be - * specifiable with an argument to this function. - */ - mBegin = (T *)storage.addr(); - mLength = length; - mCapacity = sInlineCapacity; - Impl::copyConstruct(mBegin, p, p + length); - Impl::destroy(p, p + length); - this->free_(p); - } else { - mBegin = p; - mLength = length; - mCapacity = length; - } -#ifdef DEBUG - mReserved = length; -#endif -} - -} /* namespace js */ - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif /* jsvector_h_ */ diff --git a/deps/mozjs/js/src/jsversion.h b/deps/mozjs/js/src/jsversion.h index 5d22028e169..332ead2ad9c 100644 --- a/deps/mozjs/js/src/jsversion.h +++ b/deps/mozjs/js/src/jsversion.h @@ -86,7 +86,6 @@ #define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ #endif #define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ -#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ @@ -96,7 +95,6 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -113,7 +111,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ @@ -123,7 +120,6 @@ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -136,7 +132,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ @@ -146,7 +141,6 @@ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -159,7 +153,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ @@ -169,7 +162,6 @@ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 1 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -182,7 +174,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ @@ -192,7 +183,6 @@ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 1 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 2 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -219,13 +209,3 @@ * support likely to be made opt-in at some future time. */ #define OLD_GETTER_SETTER_METHODS 1 - -/* - * Embedders: don't change this: it's a bake-until-ready hack only! - * - * NB: Changing this value requires adjusting the pass/fail state of a handful - * of tests in ecma_5/JSON/ which the old parser implemented incorrectly. - * Also make sure to rename JSONSourceParser to just JSONParser when the - * old parser is removed completely. - */ -#define USE_OLD_AND_BUSTED_JSON_PARSER 0 diff --git a/deps/mozjs/js/src/jswatchpoint.cpp b/deps/mozjs/js/src/jswatchpoint.cpp new file mode 100644 index 00000000000..21e9f8f7100 --- /dev/null +++ b/deps/mozjs/js/src/jswatchpoint.cpp @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is an implementation of watchpoints for SpiderMonkey. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jswatchpoint.h" +#include "jsatom.h" +#include "jsgcmark.h" +#include "jsobjinlines.h" + +using namespace js; +using namespace js::gc; + +inline HashNumber +DefaultHasher::hash(const Lookup &key) +{ + return DefaultHasher::hash(key.object.get()) ^ HashId(key.id.get()); +} + +class AutoEntryHolder { + typedef WatchpointMap::Map Map; + Map ↦ + Map::Ptr p; + uint32_t gen; + WatchKey key; + + public: + AutoEntryHolder(Map &map, Map::Ptr p) + : map(map), p(p), gen(map.generation()), key(p->key) { + JS_ASSERT(!p->value.held); + p->value.held = true; + } + + ~AutoEntryHolder() { + if (gen != map.generation()) + p = map.lookup(key); + if (p) + p->value.held = false; + } +}; + +bool +WatchpointMap::init() +{ + return map.init(); +} + +bool +WatchpointMap::watch(JSContext *cx, JSObject *obj, jsid id, + JSWatchPointHandler handler, JSObject *closure) +{ + JS_ASSERT(id == js_CheckForStringIndex(id)); + JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); + + if (!obj->setWatched(cx)) + return false; + + Watchpoint w; + w.handler = handler; + w.closure = closure; + w.held = false; + if (!map.put(WatchKey(obj, id), w)) { + js_ReportOutOfMemory(cx); + return false; + } + return true; +} + +void +WatchpointMap::unwatch(JSObject *obj, jsid id, + JSWatchPointHandler *handlerp, JSObject **closurep) +{ + JS_ASSERT(id == js_CheckForStringIndex(id)); + if (Map::Ptr p = map.lookup(WatchKey(obj, id))) { + if (handlerp) + *handlerp = p->value.handler; + if (closurep) + *closurep = p->value.closure; + map.remove(p); + } +} + +void +WatchpointMap::unwatchObject(JSObject *obj) +{ + for (Map::Enum r(map); !r.empty(); r.popFront()) { + Map::Entry &e = r.front(); + if (e.key.object == obj) + r.removeFront(); + } +} + +void +WatchpointMap::clear() +{ + map.clear(); +} + +bool +WatchpointMap::triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp) +{ + JS_ASSERT(id == js_CheckForStringIndex(id)); + Map::Ptr p = map.lookup(WatchKey(obj, id)); + if (!p || p->value.held) + return true; + + AutoEntryHolder holder(map, p); + + /* Copy the entry, since GC would invalidate p. */ + JSWatchPointHandler handler = p->value.handler; + JSObject *closure = p->value.closure; + + /* Determine the property's old value. */ + Value old; + old.setUndefined(); + if (obj->isNative()) { + if (const Shape *shape = obj->nativeLookup(cx, id)) { + if (shape->hasSlot()) { + if (shape->isMethod()) { + /* + * The existing watched property is a method. Trip + * the method read barrier in order to avoid + * passing an uncloned function object to the + * handler. + */ + old = UndefinedValue(); + Value method = ObjectValue(*obj->nativeGetMethod(shape)); + if (!obj->methodReadBarrier(cx, *shape, &method)) + return false; + shape = obj->nativeLookup(cx, id); + JS_ASSERT(shape->isDataDescriptor()); + JS_ASSERT(!shape->isMethod()); + old = method; + } else { + old = obj->nativeGetSlot(shape->slot()); + } + } + } + } + + /* Call the handler. */ + return handler(cx, obj, id, old, vp, closure); +} + +bool +WatchpointMap::markAllIteratively(JSTracer *trc) +{ + JSRuntime *rt = trc->runtime; + if (rt->gcCurrentCompartment) { + WatchpointMap *wpmap = rt->gcCurrentCompartment->watchpointMap; + return wpmap && wpmap->markIteratively(trc); + } + + bool mutated = false; + for (CompartmentsIter c(rt); !c.done(); c.next()) { + if (c->watchpointMap) + mutated |= c->watchpointMap->markIteratively(trc); + } + return mutated; +} + +bool +WatchpointMap::markIteratively(JSTracer *trc) +{ + bool marked = false; + for (Map::Range r = map.all(); !r.empty(); r.popFront()) { + Map::Entry &e = r.front(); + bool objectIsLive = !IsAboutToBeFinalized(e.key.object); + if (objectIsLive || e.value.held) { + if (!objectIsLive) { + MarkObject(trc, e.key.object, "held Watchpoint object"); + marked = true; + } + + const HeapId &id = e.key.id; + JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); + MarkId(trc, id, "WatchKey::id"); + + if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) { + MarkObject(trc, e.value.closure, "Watchpoint::closure"); + marked = true; + } + } + } + return marked; +} + +void +WatchpointMap::markAll(JSTracer *trc) +{ + for (Map::Range r = map.all(); !r.empty(); r.popFront()) { + Map::Entry &e = r.front(); + MarkObject(trc, e.key.object, "held Watchpoint object"); + + const HeapId &id = e.key.id; + JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); + MarkId(trc, id, "WatchKey::id"); + + MarkObject(trc, e.value.closure, "Watchpoint::closure"); + } +} + +void +WatchpointMap::sweepAll(JSRuntime *rt) +{ + if (rt->gcCurrentCompartment) { + if (WatchpointMap *wpmap = rt->gcCurrentCompartment->watchpointMap) + wpmap->sweep(); + } else { + for (CompartmentsIter c(rt); !c.done(); c.next()) { + if (WatchpointMap *wpmap = c->watchpointMap) + wpmap->sweep(); + } + } +} + +void +WatchpointMap::sweep() +{ + for (Map::Enum r(map); !r.empty(); r.popFront()) { + Map::Entry &e = r.front(); + if (IsAboutToBeFinalized(e.key.object)) { + JS_ASSERT(!e.value.held); + r.removeFront(); + } + } +} + +void +WatchpointMap::traceAll(WeakMapTracer *trc) +{ + JSRuntime *rt = trc->runtime; + for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { + if (WatchpointMap *wpmap = (*c)->watchpointMap) + wpmap->trace(trc); + } +} + +void +WatchpointMap::trace(WeakMapTracer *trc) +{ + for (Map::Range r = map.all(); !r.empty(); r.popFront()) { + Map::Entry &e = r.front(); + trc->callback(trc, NULL, + e.key.object.get(), JSTRACE_OBJECT, + e.value.closure.get(), JSTRACE_OBJECT); + } +} diff --git a/deps/mozjs/js/src/jswatchpoint.h b/deps/mozjs/js/src/jswatchpoint.h new file mode 100644 index 00000000000..7e4f7402ae9 --- /dev/null +++ b/deps/mozjs/js/src/jswatchpoint.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is an implementation of watchpoints for SpiderMonkey. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jswatchpoint_h___ +#define jswatchpoint_h___ + +#include "jsalloc.h" +#include "jsprvtd.h" +#include "jsapi.h" +#include "jsfriendapi.h" + +#include "gc/Barrier.h" +#include "js/HashTable.h" + +namespace js { + +struct WatchKey { + WatchKey() {} + WatchKey(JSObject *obj, jsid id) : object(obj), id(id) {} + WatchKey(const WatchKey &key) : object(key.object.get()), id(key.id.get()) {} + HeapPtrObject object; + HeapId id; +}; + +struct Watchpoint { + JSWatchPointHandler handler; + HeapPtrObject closure; + bool held; /* true if currently running handler */ +}; + +template <> +struct DefaultHasher { + typedef WatchKey Lookup; + static inline js::HashNumber hash(const Lookup &key); + + static bool match(const WatchKey &k, const Lookup &l) { + return k.object == l.object && k.id.get() == l.id.get(); + } +}; + +class WatchpointMap { + public: + typedef HashMap, SystemAllocPolicy> Map; + + bool init(); + bool watch(JSContext *cx, JSObject *obj, jsid id, + JSWatchPointHandler handler, JSObject *closure); + void unwatch(JSObject *obj, jsid id, + JSWatchPointHandler *handlerp, JSObject **closurep); + void unwatchObject(JSObject *obj); + void clear(); + + bool triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp); + + static bool markAllIteratively(JSTracer *trc); + bool markIteratively(JSTracer *trc); + void markAll(JSTracer *trc); + static void sweepAll(JSRuntime *rt); + void sweep(); + + static void traceAll(WeakMapTracer *trc); + void trace(WeakMapTracer *trc); + + private: + Map map; +}; + +} + +#endif /* jswatchpoint_h___ */ diff --git a/deps/mozjs/js/src/jsweakmap.cpp b/deps/mozjs/js/src/jsweakmap.cpp index 8219ac52b3c..d757d8e7b87 100644 --- a/deps/mozjs/js/src/jsweakmap.cpp +++ b/deps/mozjs/js/src/jsweakmap.cpp @@ -42,37 +42,73 @@ #include #include "jsapi.h" #include "jscntxt.h" +#include "jsfriendapi.h" #include "jsgc.h" -#include "jshashtable.h" #include "jsobj.h" #include "jsgc.h" #include "jsgcmark.h" #include "jsweakmap.h" +#include "vm/GlobalObject.h" + #include "jsgcinlines.h" #include "jsobjinlines.h" using namespace js; +namespace js { + bool -JSObject::isWeakMap() const +WeakMapBase::markAllIteratively(JSTracer *tracer) { - return getClass() == &WeakMap::jsclass; + bool markedAny = false; + JSRuntime *rt = tracer->context->runtime; + for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) { + if (m->markIteratively(tracer)) + markedAny = true; + } + return markedAny; } -namespace js { +void +WeakMapBase::sweepAll(JSTracer *tracer) +{ + JSRuntime *rt = tracer->context->runtime; + for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) + m->sweep(tracer); +} -WeakMap::WeakMap(JSContext *cx) : - map(cx), - next(NULL) +void +WeakMapBase::traceAllMappings(WeakMapTracer *tracer) { + JSRuntime *rt = tracer->runtime; + for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) + m->traceMappings(tracer); } -WeakMap * -WeakMap::fromJSObject(JSObject *obj) +void +WeakMapBase::resetWeakMapList(JSRuntime *rt) +{ + JS_ASSERT(WeakMapNotInList != NULL); + + WeakMapBase *m = rt->gcWeakMapList; + rt->gcWeakMapList = NULL; + while (m) { + WeakMapBase *n = m->next; + m->next = WeakMapNotInList; + m = n; + } +} + +} /* namespace js */ + +typedef WeakMap, HeapValue> ObjectValueMap; + +static ObjectValueMap * +GetObjectMap(JSObject *obj) { - JS_ASSERT(obj->getClass() == &WeakMap::jsclass); - return (WeakMap *)obj->getPrivate(); + JS_ASSERT(obj->isWeakMap()); + return (ObjectValueMap *)obj->getPrivate(); } static JSObject * @@ -85,274 +121,247 @@ NonNullObject(JSContext *cx, Value *vp) return &vp->toObject(); } -JSBool -WeakMap::has(JSContext *cx, uintN argc, Value *vp) +static JSBool +WeakMap_has(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_has, &WeakMapClass, &ok); if (!obj) - return false; - if (!obj->isWeakMap()) { - ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass); - return false; - } - if (argc < 1) { + return ok; + + if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.has", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &vp[2]); + JSObject *key = NonNullObject(cx, &args[0]); if (!key) return false; - WeakMap *weakmap = fromJSObject(obj); - if (weakmap) { - js::HashMap::Ptr ptr = weakmap->map.lookup(key); + ObjectValueMap *map = GetObjectMap(obj); + if (map) { + ObjectValueMap::Ptr ptr = map->lookup(key); if (ptr) { - *vp = BooleanValue(true); + args.rval() = BooleanValue(true); return true; } } - *vp = BooleanValue(false); + args.rval() = BooleanValue(false); return true; } -JSBool -WeakMap::get(JSContext *cx, uintN argc, Value *vp) +static JSBool +WeakMap_get(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_get, &WeakMapClass, &ok); if (!obj) - return false; - if (!obj->isWeakMap()) { - ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass); - return false; - } - if (argc < 1) { + return ok; + + if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.get", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &vp[2]); + JSObject *key = NonNullObject(cx, &args[0]); if (!key) return false; - WeakMap *weakmap = fromJSObject(obj); - if (weakmap) { - js::HashMap::Ptr ptr = weakmap->map.lookup(key); + ObjectValueMap *map = GetObjectMap(obj); + if (map) { + ObjectValueMap::Ptr ptr = map->lookup(key); if (ptr) { - *vp = ptr->value; + args.rval() = ptr->value; return true; } } - *vp = (argc > 1) ? vp[3] : UndefinedValue(); + args.rval() = (args.length() > 1) ? args[1] : UndefinedValue(); return true; } -JSBool -WeakMap::delete_(JSContext *cx, uintN argc, Value *vp) +static JSBool +WeakMap_delete(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_delete, &WeakMapClass, &ok); if (!obj) - return false; - if (!obj->isWeakMap()) { - ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass); - return false; - } - if (argc < 1) { + return ok; + + if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.delete", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &vp[2]); + JSObject *key = NonNullObject(cx, &args[0]); if (!key) return false; - WeakMap *weakmap = fromJSObject(obj); - if (weakmap) { - js::HashMap::Ptr ptr = weakmap->map.lookup(key); + ObjectValueMap *map = GetObjectMap(obj); + if (map) { + ObjectValueMap::Ptr ptr = map->lookup(key); if (ptr) { - weakmap->map.remove(ptr); - *vp = BooleanValue(true); + map->remove(ptr); + args.rval() = BooleanValue(true); return true; } } - *vp = BooleanValue(false); + args.rval() = BooleanValue(false); return true; } -JSBool -WeakMap::set(JSContext *cx, uintN argc, Value *vp) +static JSBool +WeakMap_set(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + bool ok; + JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_set, &WeakMapClass, &ok); if (!obj) - return false; - if (!obj->isWeakMap()) { - ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass); - return false; - } - if (argc < 1) { + return ok; + + if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.set", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &vp[2]); + JSObject *key = NonNullObject(cx, &args[0]); if (!key) return false; - Value value = (argc > 1) ? vp[3] : UndefinedValue(); + Value value = (args.length() > 1) ? args[1] : UndefinedValue(); - WeakMap *table = (WeakMap *)obj->getPrivate(); - if (!table) { - table = cx->new_(cx); - if (!table->map.init()) { - delete table; + ObjectValueMap *map = GetObjectMap(obj); + if (!map) { + map = cx->new_(cx, obj); + if (!map->init()) { + cx->delete_(map); goto out_of_memory; } - obj->setPrivate(table); + obj->setPrivate(map); } - *vp = UndefinedValue(); - return table->map.put(key, value) != NULL; + if (!map->put(key, value)) + goto out_of_memory; + + // Preserve wrapped native keys to prevent wrapper optimization. + if (key->getClass()->ext.isWrappedNative) { + if (!cx->runtime->preserveWrapperCallback || + !cx->runtime->preserveWrapperCallback(cx, key)) { + JS_ReportWarning(cx, "Failed to preserve wrapper of wrapped native weak map key."); + } + } + + args.rval().setUndefined(); + return true; out_of_memory: JS_ReportOutOfMemory(cx); return false; } -void -WeakMap::mark(JSTracer *trc, JSObject *obj) +JS_FRIEND_API(JSBool) +JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret) { - WeakMap *table = fromJSObject(obj); - if (table) { - if (IS_GC_MARKING_TRACER(trc)) { - if (table->map.empty()) { - delete table; - obj->setPrivate(NULL); - return; - } - JSRuntime *rt = trc->context->runtime; - table->next = rt->gcWeakMapList; - rt->gcWeakMapList = obj; - } else { - for (js::HashMap::Range r = table->map.all(); !r.empty(); r.popFront()) { - JSObject *key = r.front().key; - Value &value = r.front().value; - js::gc::MarkObject(trc, *key, "key"); - js::gc::MarkValue(trc, value, "value"); - } - } + if (!obj || !obj->isWeakMap()) { + *ret = NULL; + return true; } -} - -/* - * Walk through the previously collected list of tables and mark rows - * iteratively. - */ -bool -WeakMap::markIteratively(JSTracer *trc) -{ - JSContext *cx = trc->context; - JSRuntime *rt = cx->runtime; - - bool again = false; - JSObject *obj = rt->gcWeakMapList; - while (obj) { - WeakMap *table = fromJSObject(obj); - for (js::HashMap::Range r = table->map.all(); !r.empty(); r.popFront()) { - JSObject *key = r.front().key; - Value &value = r.front().value; - if (value.isMarkable() && !IsAboutToBeFinalized(cx, key)) { - /* If the key is alive, mark the value if needed. */ - if (IsAboutToBeFinalized(cx, value.toGCThing())) { - js::gc::MarkValue(trc, value, "value"); - /* We revived a value with children, we have to iterate again. */ - if (value.isGCThing()) - again = true; - } - } + JSObject *arr = NewDenseEmptyArray(cx); + if (!arr) + return false; + ObjectValueMap *map = GetObjectMap(obj); + if (map) { + for (ObjectValueMap::Range r = map->nondeterministicAll(); !r.empty(); r.popFront()) { + if (!js_NewbornArrayPush(cx, arr, ObjectValue(*r.front().key))) + return false; } - obj = table->next; } - return again; + *ret = arr; + return true; } -void -WeakMap::sweep(JSContext *cx) +static void +WeakMap_mark(JSTracer *trc, JSObject *obj) { - JSRuntime *rt = cx->runtime; - - JSObject *obj = rt->gcWeakMapList; - while (obj) { - WeakMap *table = fromJSObject(obj); - for (js::HashMap::Enum e(table->map); !e.empty(); e.popFront()) { - if (IsAboutToBeFinalized(cx, e.front().key)) - e.removeFront(); - } - obj = table->next; - } - - rt->gcWeakMapList = NULL; + if (ObjectValueMap *map = GetObjectMap(obj)) + map->trace(trc); } -void -WeakMap::finalize(JSContext *cx, JSObject *obj) +static void +WeakMap_finalize(JSContext *cx, JSObject *obj) { - WeakMap *table = fromJSObject(obj); - if (table) - delete table; + ObjectValueMap *map = GetObjectMap(obj); + cx->delete_(map); } -JSBool -WeakMap::construct(JSContext *cx, uintN argc, Value *vp) +static JSBool +WeakMap_construct(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = NewBuiltinClassInstance(cx, &WeakMap::jsclass); + JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapClass); if (!obj) return false; - obj->setPrivate(NULL); - vp->setObject(*obj); return true; } -Class WeakMap::jsclass = { +Class js::WeakMapClass = { "WeakMap", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - WeakMap::finalize, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - WeakMap::mark + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + WeakMap_finalize, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + WeakMap_mark }; -} - -JSFunctionSpec WeakMap::methods[] = { - JS_FN("has", WeakMap::has, 1, 0), - JS_FN("get", WeakMap::get, 2, 0), - JS_FN("delete", WeakMap::delete_, 1, 0), - JS_FN("set", WeakMap::set, 2, 0), +static JSFunctionSpec weak_map_methods[] = { + JS_FN("has", WeakMap_has, 1, 0), + JS_FN("get", WeakMap_get, 2, 0), + JS_FN("delete", WeakMap_delete, 1, 0), + JS_FN("set", WeakMap_set, 2, 0), JS_FS_END }; JSObject * js_InitWeakMapClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &WeakMap::jsclass, WeakMap::construct, 0, - NULL, WeakMap::methods, NULL, NULL); - if (!proto) + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *weakMapProto = global->createBlankPrototype(cx, &WeakMapClass); + if (!weakMapProto) + return NULL; + + JSFunction *ctor = global->createConstructor(cx, WeakMap_construct, &WeakMapClass, + CLASS_ATOM(cx, WeakMap), 0); + if (!ctor) return NULL; - proto->setPrivate(NULL); + if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, weakMapProto, NULL, weak_map_methods)) + return NULL; - return proto; + if (!DefineConstructorAndPrototype(cx, global, JSProto_WeakMap, ctor, weakMapProto)) + return NULL; + return weakMapProto; } diff --git a/deps/mozjs/js/src/jsweakmap.h b/deps/mozjs/js/src/jsweakmap.h index 055f73f1992..9494686eee4 100644 --- a/deps/mozjs/js/src/jsweakmap.h +++ b/deps/mozjs/js/src/jsweakmap.h @@ -43,39 +43,303 @@ #define jsweakmap_h___ #include "jsapi.h" +#include "jsfriendapi.h" #include "jscntxt.h" #include "jsobj.h" +#include "jsgcmark.h" + +#include "js/HashTable.h" namespace js { -typedef js::HashMap ObjectValueMap; +// A subclass template of js::HashMap whose keys and values may be garbage-collected. When +// a key is collected, the table entry disappears, dropping its reference to the value. +// +// More precisely: +// +// A WeakMap entry is collected if and only if either the WeakMap or the entry's key +// is collected. If an entry is not collected, it remains in the WeakMap and it has a +// strong reference to the value. +// +// You must call this table's 'mark' method when the object of which it is a part is +// reached by the garbage collection tracer. Once a table is known to be live, the +// implementation takes care of the iterative marking needed for weak tables and removing +// table entries when collection is complete. +// +// You may provide your own MarkPolicy class to specify how keys and values are marked; a +// policy template provides default definitions for some common key/value type +// combinations. +// +// Details: +// +// The interface is as for a js::HashMap, with the following additions: +// +// - You must call the WeakMap's 'trace' member function when you discover that the map is +// part of a live object. (You'll typically call this from the containing type's 'trace' +// function.) +// +// - There is no AllocPolicy parameter; these are used with our garbage collector, so +// RuntimeAllocPolicy is hard-wired. +// +// - Optional fourth and fifth parameters are the MarkPolicies for the key and value type. +// A MarkPolicy has the constructor: +// +// MarkPolicy(JSTracer *) +// +// and the following member functions: +// +// bool isMarked(const Type &x) +// Return true if x has been marked as live by the garbage collector. +// +// bool mark(const Type &x) +// Return false if x is already marked. Otherwise, mark x and return true. +// +// If omitted, the MarkPolicy parameter defaults to js::DefaultMarkPolicy, +// a policy template with the obvious definitions for some typical +// SpiderMonkey type combinations. + +// A policy template holding default marking algorithms for common type combinations. This +// provides default types for WeakMap's MarkPolicy template parameter. +template class DefaultMarkPolicy; + +// A policy template holding default tracing algorithms for common type combinations. This +// provides default types for WeakMap's TracePolicy template parameter. +template class DefaultTracePolicy; + +// The value for the next pointer for maps not in the map list. +static WeakMapBase * const WeakMapNotInList = reinterpret_cast(1); + +// Common base class for all WeakMap specializations. The collector uses this to call +// their markIteratively and sweep methods. +class WeakMapBase { + public: + WeakMapBase(JSObject *memOf) : memberOf(memOf), next(WeakMapNotInList) { } + virtual ~WeakMapBase() { } + + void trace(JSTracer *tracer) { + if (IS_GC_MARKING_TRACER(tracer)) { + // We don't do anything with a WeakMap at trace time. Rather, we wait until as + // many keys as possible have been marked, and add ourselves to the list of + // known-live WeakMaps to be scanned in the iterative marking phase, by + // markAllIteratively. + JS_ASSERT(!tracer->eagerlyTraceWeakMaps); + + // Add ourselves to the list if we are not already in the list. We can already + // be in the list if the weak map is marked more than once due delayed marking. + if (next == WeakMapNotInList) { + JSRuntime *rt = tracer->context->runtime; + next = rt->gcWeakMapList; + rt->gcWeakMapList = this; + } + } else { + // If we're not actually doing garbage collection, the keys won't be marked + // nicely as needed by the true ephemeral marking algorithm --- custom tracers + // such as the cycle collector must use their own means for cycle detection. + // So here we do a conservative approximation: pretend all keys are live. + if (tracer->eagerlyTraceWeakMaps) + nonMarkingTrace(tracer); + } + } -class WeakMap { - ObjectValueMap map; - JSObject *next; + // Garbage collector entry points. - static WeakMap *fromJSObject(JSObject *obj); + // Check all weak maps that have been marked as live so far in this garbage + // collection, and mark the values of all entries that have become strong references + // to them. Return true if we marked any new values, indicating that we need to make + // another pass. In other words, mark my marked maps' marked members' mid-collection. + static bool markAllIteratively(JSTracer *tracer); - static JSBool has(JSContext *cx, uintN argc, Value *vp); - static JSBool get(JSContext *cx, uintN argc, Value *vp); - static JSBool delete_(JSContext *cx, uintN argc, Value *vp); - static JSBool set(JSContext *cx, uintN argc, Value *vp); + // Remove entries whose keys are dead from all weak maps marked as live in this + // garbage collection. + static void sweepAll(JSTracer *tracer); + // Trace all delayed weak map bindings. Used by the cycle collector. + static void traceAllMappings(WeakMapTracer *tracer); + + // Remove everything from the live weak map list. + static void resetWeakMapList(JSRuntime *rt); protected: - static void mark(JSTracer *trc, JSObject *obj); - static void finalize(JSContext *cx, JSObject *obj); + // Instance member functions called by the above. Instantiations of WeakMap override + // these with definitions appropriate for their Key and Value types. + virtual void nonMarkingTrace(JSTracer *tracer) = 0; + virtual bool markIteratively(JSTracer *tracer) = 0; + virtual void sweep(JSTracer *tracer) = 0; + virtual void traceMappings(WeakMapTracer *tracer) = 0; + + // Object that this weak map is part of, if any. + JSObject *memberOf; + + private: + // Link in a list of WeakMaps to mark iteratively and sweep in this garbage + // collection, headed by JSRuntime::gcWeakMapList. The last element of the list + // has NULL as its next. Maps not in the list have WeakMapNotInList as their + // next. We must distinguish these cases to avoid creating infinite lists + // when a weak map gets traced twice due to delayed marking. + WeakMapBase *next; +}; + +template , + class KeyMarkPolicy = DefaultMarkPolicy, + class ValueMarkPolicy = DefaultMarkPolicy, + class TracePolicy = DefaultTracePolicy > +class WeakMap : public HashMap, public WeakMapBase { + private: + typedef HashMap Base; + typedef typename Base::Enum Enum; + + public: + typedef typename Base::Range Range; + + explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { } + explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { } + + // Use with caution, as result can be affected by garbage collection. + Range nondeterministicAll() { + return Base::all(); + } + + private: + void nonMarkingTrace(JSTracer *trc) { + ValueMarkPolicy vp(trc); + for (Range r = Base::all(); !r.empty(); r.popFront()) + vp.mark(r.front().value); + } + + bool markIteratively(JSTracer *trc) { + KeyMarkPolicy kp(trc); + ValueMarkPolicy vp(trc); + bool markedAny = false; + for (Range r = Base::all(); !r.empty(); r.popFront()) { + const Key &k = r.front().key; + const Value &v = r.front().value; + /* If the entry is live, ensure its key and value are marked. */ + if (kp.isMarked(k)) { + markedAny |= vp.mark(v); + } + JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v)); + } + return markedAny; + } + void sweep(JSTracer *trc) { + KeyMarkPolicy kp(trc); + + /* Remove all entries whose keys remain unmarked. */ + for (Enum e(*this); !e.empty(); e.popFront()) { + if (!kp.isMarked(e.front().key)) + e.removeFront(); + } + +#if DEBUG + ValueMarkPolicy vp(trc); + /* + * Once we've swept, all remaining edges should stay within the + * known-live part of the graph. + */ + for (Range r = Base::all(); !r.empty(); r.popFront()) { + JS_ASSERT(kp.isMarked(r.front().key)); + JS_ASSERT(vp.isMarked(r.front().value)); + } +#endif + } + + // mapObj can be NULL, which means that the map is not part of a JSObject. + void traceMappings(WeakMapTracer *tracer) { + TracePolicy t(tracer); + for (Range r = Base::all(); !r.empty(); r.popFront()) + t.traceMapping(memberOf, r.front().key, r.front().value); + } +}; + +template <> +class DefaultMarkPolicy { + private: + JSTracer *tracer; public: - WeakMap(JSContext *cx); + DefaultMarkPolicy(JSTracer *t) : tracer(t) { } + bool isMarked(const HeapValue &x) { + if (x.isMarkable()) + return !IsAboutToBeFinalized(x); + return true; + } + bool mark(const HeapValue &x) { + if (isMarked(x)) + return false; + js::gc::MarkValue(tracer, x, "WeakMap entry"); + return true; + } +}; - static JSBool construct(JSContext *cx, uintN argc, Value *vp); +template <> +class DefaultMarkPolicy { + private: + JSTracer *tracer; + public: + DefaultMarkPolicy(JSTracer *t) : tracer(t) { } + bool isMarked(const HeapPtrObject &x) { + return !IsAboutToBeFinalized(x); + } + bool mark(const HeapPtrObject &x) { + if (isMarked(x)) + return false; + js::gc::MarkObject(tracer, x, "WeakMap entry"); + return true; + } +}; - static bool markIteratively(JSTracer *trc); - static void sweep(JSContext *cx); +template <> +class DefaultMarkPolicy { + private: + JSTracer *tracer; + public: + DefaultMarkPolicy(JSTracer *t) : tracer(t) { } + bool isMarked(const HeapPtrScript &x) { + return !IsAboutToBeFinalized(x); + } + bool mark(const HeapPtrScript &x) { + if (isMarked(x)) + return false; + js::gc::MarkScript(tracer, x, "WeakMap entry"); + return true; + } +}; + +// Default trace policies + +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtr &k, HeapValue &v) { + if (v.isMarkable()) + tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind()); + } +}; - static Class jsclass; - static JSFunctionSpec methods[]; +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtrObject &k, const HeapPtrObject &v) { + tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT); + } +}; + +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtrScript &k, const HeapPtrObject &v) { + tracer->callback(tracer, m, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT); + } }; } diff --git a/deps/mozjs/js/src/jswin.h b/deps/mozjs/js/src/jswin.h index b71556a5cc9..d11dcd90ca0 100644 --- a/deps/mozjs/js/src/jswin.h +++ b/deps/mozjs/js/src/jswin.h @@ -1,45 +1,49 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is workarounds for the Win32 headers. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This file is a wrapper around to prevent the mangling of - * various function names throughout the codebase. - */ -#ifdef XP_WIN -# include -# undef GetProp -# undef SetProp -#endif +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is workarounds for the Win32 headers. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This file is a wrapper around to prevent the mangling of + * various function names throughout the codebase. + */ +#ifdef XP_WIN +# include +# undef GetProp +# undef SetProp +# undef CONST +# undef STRICT +# undef LEGACY +# undef THIS +#endif diff --git a/deps/mozjs/js/src/jswin64.asm b/deps/mozjs/js/src/jswin64.asm new file mode 100644 index 00000000000..657fc537a45 --- /dev/null +++ b/deps/mozjs/js/src/jswin64.asm @@ -0,0 +1,49 @@ +; ***** BEGIN LICENSE BLOCK ***** +; Version: MPL 1.1/GPL 2.0/LGPL 2.1 +; +; The contents of this file are subject to the Mozilla Public License Version +; 1.1 (the "License"); you may not use this file except in compliance with +; the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" basis, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +; for the specific language governing rights and limitations under the +; License. +; +; The Original Code is mozilla.org code. +; +; The Initial Developer of the Original Code is The Mozilla Foundation. +; Portions created by the Initial Developer are Copyright (C) 2011 +; the Initial Developer. All Rights Reserved. +; +; Contributor(s): +; Makoto Kato (Original Author) +; +; Alternatively, the contents of this file may be used under the terms of +; either the GNU General Public License Version 2 or later (the "GPL"), or +; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +; in which case the provisions of the GPL or the LGPL are applicable instead +; of those above. If you wish to allow use of your version of this file only +; under the terms of either the GPL or the LGPL, and not to allow others to +; use your version of this file under the terms of the MPL, indicate your +; decision by deleting the provisions above and replace them with the notice +; and other provisions required by the GPL or the LGPL. If you do not delete +; the provisions above, a recipient may use your version of this file under +; the terms of any one of the MPL, the GPL or the LGPL. +; +; ***** END LICENSE BLOCK ***** + + +.CODE + +extern fmod:PROC + +; This is a workaround for KB982107 (http://support.microsoft.com/kb/982107) +js_myfmod PROC FRAME + .ENDPROLOG + fnclex + jmp fmod +js_myfmod ENDP + +END diff --git a/deps/mozjs/js/src/jswince.asm b/deps/mozjs/js/src/jswince.asm deleted file mode 100644 index 6a2e1eb28c1..00000000000 --- a/deps/mozjs/js/src/jswince.asm +++ /dev/null @@ -1,82 +0,0 @@ - INCLUDE kxarm.h - - area js_msvc, code, readonly - - MACRO - FUNC_HEADER $Name -FuncName SETS VBar:CC:"$Name":CC:VBar -PrologName SETS VBar:CC:"$Name":CC:"_Prolog":CC:VBar -FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar - - AREA |.pdata|,ALIGN=2,PDATA - DCD $FuncName - DCD (($PrologName-$FuncName)/4) :OR: ((($FuncEndName-$FuncName)/4):SHL:8) :OR: 0x40000000 - AREA $AreaName,CODE,READONLY - ALIGN 2 - GLOBAL $FuncName - EXPORT $FuncName -$FuncName - ROUT -$PrologName - MEND - - ;; -------- Functions to test processor features. - export js_arm_try_thumb_op - export js_arm_try_armv6t2_op - export js_arm_try_armv7_op - export js_arm_try_armv6_op - export js_arm_try_armv5_op - export js_arm_try_vfp_op - - ;; Test for Thumb support. - FUNC_HEADER js_arm_try_thumb_op - bx lr - mov pc, lr - ENTRY_END - endp - - ;; I'm not smart enough to figure out which flags to pass to armasm to get it - ;; to understand movt and fmdrr/vmov; the disassembler figures them out just fine! - - ;; Test for Thumb2 support. - FUNC_HEADER js_arm_try_armv6t2_op - ;; movt r0,#0xFFFF - DCD 0xE34F0FFF - mov pc,lr - ENTRY_END - endp - - ;; Test for VFP support. - FUNC_HEADER js_arm_try_vfp_op - ;; fmdrr d0, r0, r1 - DCD 0xEC410B10 - mov pc,lr - ENTRY_END - endp - - ;; Tests for each architecture version. - - FUNC_HEADER js_arm_try_armv7_op - ;; pli pc, #0 - DCD 0xF45FF000 - mov pc, lr - ENTRY_END - endp - - FUNC_HEADER js_arm_try_armv6_op - ;; rev ip, ip - DCD 0xE6BFCF3C - mov pc, lr - ENTRY_END - endp - - FUNC_HEADER js_arm_try_armv5_op - ;; clz ip, ip - DCD 0xE16FCF1C - mov pc, lr - ENTRY_END - endp - - ;; -------- - - end diff --git a/deps/mozjs/js/src/jswrapper.cpp b/deps/mozjs/js/src/jswrapper.cpp index 0e0577658d0..1e0f5af650f 100644 --- a/deps/mozjs/js/src/jswrapper.cpp +++ b/deps/mozjs/js/src/jswrapper.cpp @@ -41,11 +41,11 @@ #include "jsapi.h" #include "jscntxt.h" +#include "jsexn.h" #include "jsgc.h" #include "jsgcmark.h" #include "jsiter.h" #include "jsnum.h" -#include "jsregexp.h" #include "jswrapper.h" #include "methodjit/PolyIC.h" #include "methodjit/MonoIC.h" @@ -56,32 +56,33 @@ #include "jsobjinlines.h" +#include "vm/RegExpObject-inl.h" + using namespace js; using namespace js::gc; static int sWrapperFamily; void * -JSWrapper::getWrapperFamily() +Wrapper::getWrapperFamily() { return &sWrapperFamily; } -bool -JSObject::isWrapper() const +JS_FRIEND_API(bool) +js::IsWrapper(const JSObject *wrapper) { - return isProxy() && getProxyHandler()->family() == &sWrapperFamily; + return wrapper->isProxy() && GetProxyHandler(wrapper)->family() == &sWrapperFamily; } -JSObject * -JSObject::unwrap(uintN *flagsp) +JS_FRIEND_API(JSObject *) +js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, uintN *flagsp) { - JSObject *wrapped = this; uintN flags = 0; while (wrapped->isWrapper()) { - flags |= static_cast(wrapped->getProxyHandler())->flags(); - wrapped = wrapped->getProxyPrivate().toObjectOrNull(); - if (wrapped->getClass()->ext.innerObject) + flags |= static_cast(GetProxyHandler(wrapped))->flags(); + wrapped = GetProxyPrivate(wrapped).toObjectOrNull(); + if (stopAtOuter && wrapped->getClass()->ext.innerObject) break; } if (flagsp) @@ -89,11 +90,18 @@ JSObject::unwrap(uintN *flagsp) return wrapped; } -JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags) +bool +js::IsCrossCompartmentWrapper(const JSObject *wrapper) +{ + return wrapper->isWrapper() && + !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT); +} + +Wrapper::Wrapper(uintN flags) : ProxyHandler(&sWrapperFamily), mFlags(flags) { } -JSWrapper::~JSWrapper() +Wrapper::~Wrapper() { } @@ -111,17 +119,22 @@ JSWrapper::~JSWrapper() #define GET(action) CHECKED(action, GET) bool -JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc) +Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, + PropertyDescriptor *desc) { desc->obj = NULL; // default result if we refuse to perform this action - CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, - Jsvalify(desc)), set ? SET : GET); + CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc), + set ? SET : GET); } static bool GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSPropertyDescriptor *desc) { + // If obj is a proxy, we can do better than just guessing. This is + // important for certain types of wrappers that wrap other wrappers. + if (obj->isProxy()) + return Proxy::getOwnPropertyDescriptor(cx, obj, id, flags & JSRESOLVE_ASSIGNING, desc); + if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc)) return false; if (desc->obj != obj) @@ -130,24 +143,23 @@ GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSP } bool -JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, - PropertyDescriptor *desc) +Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, + PropertyDescriptor *desc) { - desc->obj= NULL; // default result if we refuse to perform this action - CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, - Jsvalify(desc)), set ? SET : GET); + desc->obj = NULL; // default result if we refuse to perform this action + CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc), + set ? SET : GET); } bool -JSWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) +Wrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) { - SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, Jsvalify(desc->value), - Jsvalify(desc->getter), Jsvalify(desc->setter), desc->attrs)); + SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, desc->value, + desc->getter, desc->setter, desc->attrs)); } bool -JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { // if we refuse to perform this action, props remains empty jsid id = JSID_VOID; @@ -162,16 +174,16 @@ ValueToBoolean(Value *vp, bool *bp) } bool -JSWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +Wrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { *bp = true; // default result if we refuse to perform this action Value v; - SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, Jsvalify(&v)) && + SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, &v) && ValueToBoolean(&v, bp)); } bool -JSWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +Wrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { // if we refuse to perform this action, props remains empty static jsid id = JSID_VOID; @@ -179,7 +191,7 @@ JSWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) } bool -JSWrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp) +Wrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp) { vp->setUndefined(); return true; @@ -193,7 +205,7 @@ Cond(JSBool b, bool *bp) } bool -JSWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +Wrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { *bp = false; // default result if we refuse to perform this action JSBool found; @@ -202,32 +214,32 @@ JSWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) } bool -JSWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +Wrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { *bp = false; // default result if we refuse to perform this action PropertyDescriptor desc; JSObject *wobj = wrappedObject(wrapper); - GET(JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, Jsvalify(&desc)) && + GET(JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, &desc) && Cond(desc.obj == wobj, bp)); } bool -JSWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) +Wrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action - GET(wrappedObject(wrapper)->getProperty(cx, receiver, id, vp)); + GET(wrappedObject(wrapper)->getGeneric(cx, receiver, id, vp)); } bool -JSWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, +Wrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, Value *vp) { // FIXME (bug 596351): Need deal with strict mode. - SET(wrappedObject(wrapper)->setProperty(cx, id, vp, false)); + SET(wrappedObject(wrapper)->setGeneric(cx, id, vp, false)); } bool -JSWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +Wrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { // if we refuse to perform this action, props remains empty const jsid id = JSID_VOID; @@ -235,7 +247,7 @@ JSWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) } bool -JSWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) +Wrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action const jsid id = JSID_VOID; @@ -243,38 +255,51 @@ JSWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) } bool -JSWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) +Wrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action const jsid id = JSID_VOID; - CHECKED(JSProxyHandler::call(cx, wrapper, argc, vp), CALL); + CHECKED(ProxyHandler::call(cx, wrapper, argc, vp), CALL); } bool -JSWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *vp) +Wrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action const jsid id = JSID_VOID; - GET(JSProxyHandler::construct(cx, wrapper, argc, argv, vp)); + GET(ProxyHandler::construct(cx, wrapper, argc, argv, vp)); } bool -JSWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) +Wrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) { - *bp = true; // default result if we refuse to perform this action const jsid id = JSID_VOID; - JSBool b; - GET(JS_HasInstance(cx, wrappedObject(wrapper), Jsvalify(*vp), &b) && Cond(b, bp)); + CHECKED(CallJSNative(cx, native, args), CALL); +} + +bool +Wrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) +{ + *bp = false; // default result if we refuse to perform this action + const jsid id = JSID_VOID; + JSBool b = JS_FALSE; + GET(JS_HasInstance(cx, wrappedObject(wrapper), *vp, &b) && Cond(b, bp)); } JSType -JSWrapper::typeOf(JSContext *cx, JSObject *wrapper) +Wrapper::typeOf(JSContext *cx, JSObject *wrapper) { return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper))); } +bool +Wrapper::objectClassIs(JSObject *wrapper, ESClassValue classValue, JSContext *cx) +{ + return ObjectClassIs(*wrappedObject(wrapper), classValue, cx); +} + JSString * -JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper) +Wrapper::obj_toString(JSContext *cx, JSObject *wrapper) { bool status; if (!enter(cx, wrapper, JSID_VOID, GET, &status)) { @@ -290,7 +315,7 @@ JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper) } JSString * -JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) +Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) { bool status; if (!enter(cx, wrapper, JSID_VOID, GET, &status)) { @@ -304,34 +329,75 @@ JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) } return NULL; } - JSString *str = JSProxyHandler::fun_toString(cx, wrapper, indent); + JSString *str = ProxyHandler::fun_toString(cx, wrapper, indent); leave(cx, wrapper); return str; } +RegExpShared * +Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper) +{ + return wrappedObject(wrapper)->asRegExp().getShared(cx); +} + +bool +Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) +{ + *vp = ObjectValue(*wrappedObject(wrapper)); + if (hint == JSTYPE_VOID) + return ToPrimitive(cx, vp); + return ToPrimitive(cx, hint, vp); +} + +bool +Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) +{ + if (!js_IteratorMore(cx, wrappedObject(wrapper), vp)) + return false; + + if (vp->toBoolean()) { + *vp = cx->iterValue; + cx->iterValue.setUndefined(); + } else { + vp->setMagic(JS_NO_ITER_VALUE); + } + return true; +} + void -JSWrapper::trace(JSTracer *trc, JSObject *wrapper) +Wrapper::trace(JSTracer *trc, JSObject *wrapper) +{ + MarkValue(trc, wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject"); +} + +JSObject * +Wrapper::wrappedObject(const JSObject *wrapper) { - MarkObject(trc, *wrappedObject(wrapper), "wrappedObject"); + return GetProxyPrivate(wrapper).toObjectOrNull(); +} + +Wrapper * +Wrapper::wrapperHandler(const JSObject *wrapper) +{ + return static_cast(GetProxyHandler(wrapper)); } bool -JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp) +Wrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp) { *bp = true; return true; } void -JSWrapper::leave(JSContext *cx, JSObject *wrapper) +Wrapper::leave(JSContext *cx, JSObject *wrapper) { } -JSWrapper JSWrapper::singleton((uintN)0); +Wrapper Wrapper::singleton((uintN)0); JSObject * -JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, - JSWrapper *handler) +Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler) { JS_ASSERT(parent); if (obj->isXML()) { @@ -352,17 +418,44 @@ TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, J { // Allow wrapping outer window proxies. JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); - return JSWrapper::New(cx, obj, wrappedProto, parent, &JSCrossCompartmentWrapper::singleton); + return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton); +} + } +ForceFrame::ForceFrame(JSContext *cx, JSObject *target) + : context(cx), + target(target), + frame(NULL) +{ +} + +ForceFrame::~ForceFrame() +{ + context->delete_(frame); +} + +bool +ForceFrame::enter() +{ + frame = context->new_(); + if (!frame) + return false; + + JS_ASSERT(context->compartment == target->compartment()); + JSCompartment *destination = context->compartment; + + JSObject &scopeChain = target->global(); + JS_ASSERT(scopeChain.isNative()); + + return context->stack.pushDummyFrame(context, destination, scopeChain, frame); } AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target) : context(cx), origin(cx->compartment), target(target), - destination(target->getCompartment()), - input(cx), + destination(target->compartment()), entered(false) { } @@ -378,17 +471,12 @@ AutoCompartment::enter() { JS_ASSERT(!entered); if (origin != destination) { - LeaveTrace(context); - - context->compartment = destination; - JSObject *scopeChain = target->getGlobal(); - JS_ASSERT(scopeChain->isNative()); + JSObject &scopeChain = target->global(); + JS_ASSERT(scopeChain.isNative()); frame.construct(); - if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref())) { - context->compartment = origin; + if (!context->stack.pushDummyFrame(context, destination, scopeChain, &frame.ref())) return false; - } if (context->isExceptionPending()) context->wrapPendingException(); @@ -408,14 +496,32 @@ AutoCompartment::leave() entered = false; } +ErrorCopier::~ErrorCopier() +{ + JSContext *cx = ac.context; + if (cx->compartment == ac.destination && + ac.origin != ac.destination && + cx->isExceptionPending()) + { + Value exc = cx->getPendingException(); + if (exc.isObject() && exc.toObject().isError() && exc.toObject().getPrivate()) { + cx->clearPendingException(); + ac.leave(); + JSObject *copyobj = js_CopyErrorObject(cx, &exc.toObject(), scope); + if (copyobj) + cx->setPendingException(ObjectValue(*copyobj)); + } + } +} + /* Cross compartment wrappers. */ -JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags) - : JSWrapper(CROSS_COMPARTMENT | flags) +CrossCompartmentWrapper::CrossCompartmentWrapper(uintN flags) + : Wrapper(CROSS_COMPARTMENT | flags) { } -JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper() +CrossCompartmentWrapper::~CrossCompartmentWrapper() { } @@ -432,108 +538,108 @@ JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper() #define NOTHING (true) bool -JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc) +CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { PIERCE(cx, wrapper, set ? SET : GET, call.destination->wrapId(cx, &id), - JSWrapper::getPropertyDescriptor(cx, wrapper, id, set, desc), + Wrapper::getPropertyDescriptor(cx, wrapper, id, set, desc), call.origin->wrap(cx, desc)); } bool -JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc) +CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { PIERCE(cx, wrapper, set ? SET : GET, call.destination->wrapId(cx, &id), - JSWrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc), + Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc), call.origin->wrap(cx, desc)); } bool -JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) +CrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) { AutoPropertyDescriptorRooter desc2(cx, desc); PIERCE(cx, wrapper, SET, call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2), - JSWrapper::defineProperty(cx, wrapper, id, &desc2), + Wrapper::defineProperty(cx, wrapper, id, &desc2), NOTHING); } bool -JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { PIERCE(cx, wrapper, GET, NOTHING, - JSWrapper::getOwnPropertyNames(cx, wrapper, props), + Wrapper::getOwnPropertyNames(cx, wrapper, props), call.origin->wrap(cx, props)); } bool -JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +CrossCompartmentWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { PIERCE(cx, wrapper, SET, call.destination->wrapId(cx, &id), - JSWrapper::delete_(cx, wrapper, id, bp), + Wrapper::delete_(cx, wrapper, id, bp), NOTHING); } bool -JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +CrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { PIERCE(cx, wrapper, GET, NOTHING, - JSWrapper::enumerate(cx, wrapper, props), + Wrapper::enumerate(cx, wrapper, props), call.origin->wrap(cx, props)); } bool -JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +CrossCompartmentWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { PIERCE(cx, wrapper, GET, call.destination->wrapId(cx, &id), - JSWrapper::has(cx, wrapper, id, bp), + Wrapper::has(cx, wrapper, id, bp), NOTHING); } bool -JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +CrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { PIERCE(cx, wrapper, GET, call.destination->wrapId(cx, &id), - JSWrapper::hasOwn(cx, wrapper, id, bp), + Wrapper::hasOwn(cx, wrapper, id, bp), NOTHING); } bool -JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) +CrossCompartmentWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) { PIERCE(cx, wrapper, GET, call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id), - JSWrapper::get(cx, wrapper, receiver, id, vp), + Wrapper::get(cx, wrapper, receiver, id, vp), call.origin->wrap(cx, vp)); } bool -JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - bool strict, Value *vp) +CrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + bool strict, Value *vp) { AutoValueRooter tvr(cx, *vp); PIERCE(cx, wrapper, SET, call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id) && call.destination->wrap(cx, tvr.addr()), - JSWrapper::set(cx, wrapper, receiver, id, strict, tvr.addr()), + Wrapper::set(cx, wrapper, receiver, id, strict, tvr.addr()), NOTHING); } bool -JSCrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) +CrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { PIERCE(cx, wrapper, GET, NOTHING, - JSWrapper::keys(cx, wrapper, props), + Wrapper::keys(cx, wrapper, props), call.origin->wrap(cx, props)); } @@ -546,7 +652,7 @@ CanReify(Value *vp) { JSObject *obj; return vp->isObject() && - (obj = &vp->toObject())->getClass() == &js_IteratorClass && + (obj = &vp->toObject())->getClass() == &IteratorClass && (obj->getNativeIterator()->flags & JSITER_ENUMERATE); } @@ -588,7 +694,11 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp) if (!keys.resize(length)) return false; for (size_t i = 0; i < length; ++i) { - keys[i] = ni->begin()[i]; + jsid id; + if (!ValueToId(cx, StringValue(ni->begin()[i]), &id)) + return false; + id = js_CheckForStringIndex(id); + keys[i] = id; if (!origin->wrapId(cx, &keys[i])) return false; } @@ -604,16 +714,16 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp) } bool -JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) +CrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) { PIERCE(cx, wrapper, GET, NOTHING, - JSWrapper::iterate(cx, wrapper, flags, vp), + Wrapper::iterate(cx, wrapper, flags, vp), CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp)); } bool -JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) +CrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) { AutoCompartment call(cx, wrappedObject(wrapper)); if (!call.enter()) @@ -627,7 +737,7 @@ JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Va if (!call.destination->wrap(cx, &argv[n])) return false; } - if (!JSWrapper::call(cx, wrapper, argc, vp)) + if (!Wrapper::call(cx, wrapper, argc, vp)) return false; call.leave(); @@ -635,8 +745,8 @@ JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Va } bool -JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, - Value *rval) +CrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, + Value *rval) { AutoCompartment call(cx, wrappedObject(wrapper)); if (!call.enter()) @@ -646,15 +756,54 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN arg if (!call.destination->wrap(cx, &argv[n])) return false; } - if (!JSWrapper::construct(cx, wrapper, argc, argv, rval)) + if (!Wrapper::construct(cx, wrapper, argc, argv, rval)) return false; call.leave(); return call.origin->wrap(cx, rval); } +extern JSBool +js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp); + bool -JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) +CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs srcArgs) +{ + JS_ASSERT_IF(!srcArgs.calleev().isUndefined(), + srcArgs.callee().toFunction()->native() == native || + srcArgs.callee().toFunction()->native() == js_generic_native_method_dispatcher); + JS_ASSERT(&srcArgs.thisv().toObject() == wrapper); + JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper()); + + JSObject *wrapped = wrappedObject(wrapper); + AutoCompartment call(cx, wrapped); + if (!call.enter()) + return false; + + InvokeArgsGuard dstArgs; + if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs)) + return false; + + Value *src = srcArgs.base(); + Value *srcend = srcArgs.array() + srcArgs.length(); + Value *dst = dstArgs.base(); + for (; src != srcend; ++src, ++dst) { + *dst = *src; + if (!call.destination->wrap(cx, dst)) + return false; + } + + if (!Wrapper::nativeCall(cx, wrapper, clasp, native, dstArgs)) + return false; + + dstArgs.pop(); + call.leave(); + srcArgs.rval() = dstArgs.rval(); + return call.origin->wrap(cx, &srcArgs.rval()); +} + +bool +CrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) { AutoCompartment call(cx, wrappedObject(wrapper)); if (!call.enter()) @@ -663,17 +812,17 @@ JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const V Value v = *vp; if (!call.destination->wrap(cx, &v)) return false; - return JSWrapper::hasInstance(cx, wrapper, &v, bp); + return Wrapper::hasInstance(cx, wrapper, &v, bp); } JSString * -JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper) +CrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper) { AutoCompartment call(cx, wrappedObject(wrapper)); if (!call.enter()) return NULL; - JSString *str = JSWrapper::obj_toString(cx, wrapper); + JSString *str = Wrapper::obj_toString(cx, wrapper); if (!str) return NULL; @@ -684,13 +833,13 @@ JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper) } JSString * -JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) +CrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) { AutoCompartment call(cx, wrappedObject(wrapper)); if (!call.enter()) return NULL; - JSString *str = JSWrapper::fun_toString(cx, wrapper, indent); + JSString *str = Wrapper::fun_toString(cx, wrapper, indent); if (!str) return NULL; @@ -700,4 +849,75 @@ JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN return str; } -JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u); +bool +CrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) +{ + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!call.enter()) + return false; + + if (!Wrapper::defaultValue(cx, wrapper, hint, vp)) + return false; + + call.leave(); + return call.origin->wrap(cx, vp); +} + +bool +CrossCompartmentWrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) +{ + PIERCE(cx, wrapper, GET, + NOTHING, + Wrapper::iteratorNext(cx, wrapper, vp), + call.origin->wrap(cx, vp)); +} + +void +CrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper) +{ + MarkCrossCompartmentValue(trc, wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), + "wrappedObject"); +} + +CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u); + +/* Security wrappers. */ + +template +SecurityWrapper::SecurityWrapper(uintN flags) + : Base(flags) +{} + +template +bool +SecurityWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, + CallArgs args) +{ + /* + * Let this through until compartment-per-global lets us have stronger + * invariants wrt document.domain (bug 714547). + */ + return Base::nativeCall(cx, wrapper, clasp, native, args); +} + +template +bool +SecurityWrapper::objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) +{ + /* + * Let this through until compartment-per-global lets us have stronger + * invariants wrt document.domain (bug 714547). + */ + return Base::objectClassIs(obj, classValue, cx); +} + +template +RegExpShared * +SecurityWrapper::regexp_toShared(JSContext *cx, JSObject *obj) +{ + return Base::regexp_toShared(cx, obj); +} + + +template class js::SecurityWrapper; +template class js::SecurityWrapper; diff --git a/deps/mozjs/js/src/jswrapper.h b/deps/mozjs/js/src/jswrapper.h index 8c5f73eccba..6549b3369a3 100644 --- a/deps/mozjs/js/src/jswrapper.h +++ b/deps/mozjs/js/src/jswrapper.h @@ -42,71 +42,76 @@ #ifndef jswrapper_h___ #define jswrapper_h___ +#include "mozilla/Attributes.h" + #include "jsapi.h" #include "jsproxy.h" -JS_BEGIN_EXTERN_C +namespace js { + +class DummyFrameGuard; /* No-op wrapper handler base class. */ -class JS_FRIEND_API(JSWrapper) : public js::JSProxyHandler { +class JS_FRIEND_API(Wrapper) : public ProxyHandler +{ uintN mFlags; public: uintN flags() const { return mFlags; } - explicit JSWrapper(uintN flags); + explicit Wrapper(uintN flags); typedef enum { PermitObjectAccess, PermitPropertyAccess, DenyAccess } Permission; - virtual ~JSWrapper(); + virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, - js::PropertyDescriptor *desc); + PropertyDescriptor *desc) MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, - js::PropertyDescriptor *desc); + PropertyDescriptor *desc) MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual bool fix(JSContext *cx, JSObject *wrapper, js::Value *vp); + PropertyDescriptor *desc) MOZ_OVERRIDE; + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; + virtual bool fix(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE; /* ES5 Harmony derived wrapper traps. */ - virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp); + virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) MOZ_OVERRIDE; virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, - js::Value *vp); - virtual bool keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp); + Value *vp) MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) MOZ_OVERRIDE; /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); - virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *argv, - js::Value *rval); - virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp); - virtual JSType typeOf(JSContext *cx, JSObject *proxy); - virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); - virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); - - virtual void trace(JSTracer *trc, JSObject *wrapper); + virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *rval) MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE; + virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE; + virtual JSType typeOf(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE; + virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; + virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; + virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE; + virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE; + virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE; + virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE; + + virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; /* Policy enforcement traps. */ enum Action { GET, SET, CALL }; virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp); virtual void leave(JSContext *cx, JSObject *wrapper); - static JSWrapper singleton; + static Wrapper singleton; static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, - JSWrapper *handler); + Wrapper *handler); - static inline JSObject *wrappedObject(const JSObject *wrapper) { - return wrapper->getProxyPrivate().toObjectOrNull(); - } - static inline JSWrapper *wrapperHandler(const JSObject *wrapper) { - return static_cast(wrapper->getProxyHandler()); - } + static JSObject *wrappedObject(const JSObject *wrapper); + static Wrapper *wrapperHandler(const JSObject *wrapper); enum { CROSS_COMPARTMENT = 1 << 0, @@ -117,77 +122,104 @@ class JS_FRIEND_API(JSWrapper) : public js::JSProxyHandler { }; /* Base class for all cross compartment wrapper handlers. */ -class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { +class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper +{ public: - JSCrossCompartmentWrapper(uintN flags); + CrossCompartmentWrapper(uintN flags); - virtual ~JSCrossCompartmentWrapper(); + virtual ~CrossCompartmentWrapper(); /* ES5 Harmony fundamental wrapper traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, - js::PropertyDescriptor *desc); + PropertyDescriptor *desc) MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, - js::PropertyDescriptor *desc); + PropertyDescriptor *desc) MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + PropertyDescriptor *desc) MOZ_OVERRIDE; + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; /* ES5 Harmony derived wrapper traps. */ - virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp); + virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) MOZ_OVERRIDE; virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, - js::Value *vp); - virtual bool keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp); + Value *vp) MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp) MOZ_OVERRIDE; /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); - virtual bool construct(JSContext *cx, JSObject *wrapper, - uintN argc, js::Value *argv, js::Value *rval); - virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp); - virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); - virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); - - static JSCrossCompartmentWrapper singleton; + virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *rval) MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE; + virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE; + virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; + virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE; + virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE; + virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp); + + virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; + + static CrossCompartmentWrapper singleton; }; -namespace js { +/* + * Base class for security wrappers. A security wrapper is potentially hiding + * all or part of some wrapped object thus SecurityWrapper defaults to denying + * access to the wrappee. This is the opposite of Wrapper which tries to be + * completely transparent. + * + * NB: Currently, only a few ProxyHandler operations are overridden to deny + * access, relying on derived SecurityWrapper to block access when necessary. + */ +template +class JS_FRIEND_API(SecurityWrapper) : public Base +{ + public: + SecurityWrapper(uintN flags); + + virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE; + virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; + virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE; +}; -class AutoCompartment +typedef SecurityWrapper SameCompartmentSecurityWrapper; +typedef SecurityWrapper CrossCompartmentSecurityWrapper; + +/* + * A hacky class that lets a friend force a fake frame. We must already be + * in the compartment of |target| when we enter the forced frame. + */ +class JS_FRIEND_API(ForceFrame) { public: JSContext * const context; - JSCompartment * const origin; JSObject * const target; - JSCompartment * const destination; private: - Maybe frame; - FrameRegs regs; - AutoStringRooter input; - bool entered; + DummyFrameGuard *frame; public: - AutoCompartment(JSContext *cx, JSObject *target); - ~AutoCompartment(); - + ForceFrame(JSContext *cx, JSObject *target); + ~ForceFrame(); bool enter(); - void leave(); - - private: - // Prohibit copying. - AutoCompartment(const AutoCompartment &); - AutoCompartment & operator=(const AutoCompartment &); }; extern JSObject * TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags); -} +JS_FRIEND_API(bool) IsWrapper(const JSObject *obj); + +// Given a JSObject, returns that object stripped of wrappers. If +// stopAtOuter is true, then this returns the outer window if it was +// previously wrapped. Otherwise, this returns the first object for +// which JSObject::isWrapper returns false. +JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true, + uintN *flagsp = NULL); + +bool IsCrossCompartmentWrapper(const JSObject *obj); -JS_END_EXTERN_C +} /* namespace js */ #endif diff --git a/deps/mozjs/js/src/jsxdrapi.cpp b/deps/mozjs/js/src/jsxdrapi.cpp index e4335c15e39..58663515d24 100644 --- a/deps/mozjs/js/src/jsxdrapi.cpp +++ b/deps/mozjs/js/src/jsxdrapi.cpp @@ -37,13 +37,14 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/Util.h" + #include "jsversion.h" #if JS_HAS_XDR #include #include "jstypes.h" -#include "jsstdint.h" #include "jsutil.h" #include "jsdhash.h" #include "jsprf.h" @@ -54,9 +55,11 @@ #include "jsscript.h" /* js_XDRScript */ #include "jsstr.h" #include "jsxdrapi.h" +#include "vm/Debugger.h" #include "jsobjinlines.h" +using namespace mozilla; using namespace js; #ifdef DEBUG @@ -68,8 +71,8 @@ using namespace js; typedef struct JSXDRMemState { JSXDRState state; char *base; - uint32 count; - uint32 limit; + uint32_t count; + uint32_t limit; } JSXDRMemState; #define MEM_BLOCK 8192 @@ -94,7 +97,7 @@ typedef struct JSXDRMemState { if ((xdr)->mode == JSXDR_ENCODE) { \ if (MEM_LIMIT(xdr) && \ MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ + uint32_t limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ void *data_ = (xdr)->cx->realloc_(MEM_BASE(xdr), limit_); \ if (!data_) \ return 0; \ @@ -110,43 +113,43 @@ typedef struct JSXDRMemState { #define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) static JSBool -mem_get32(JSXDRState *xdr, uint32 *lp) +mem_get32(JSXDRState *xdr, uint32_t *lp) { MEM_LEFT(xdr, 4); - *lp = *(uint32 *)MEM_DATA(xdr); + *lp = *(uint32_t *)MEM_DATA(xdr); MEM_INCR(xdr, 4); return JS_TRUE; } static JSBool -mem_set32(JSXDRState *xdr, uint32 *lp) +mem_set32(JSXDRState *xdr, uint32_t *lp) { MEM_NEED(xdr, 4); - *(uint32 *)MEM_DATA(xdr) = *lp; + *(uint32_t *)MEM_DATA(xdr) = *lp; MEM_INCR(xdr, 4); return JS_TRUE; } static JSBool -mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) +mem_getbytes(JSXDRState *xdr, char *bytes, uint32_t len) { MEM_LEFT(xdr, len); - memcpy(bytes, MEM_DATA(xdr), len); + js_memcpy(bytes, MEM_DATA(xdr), len); MEM_INCR(xdr, len); return JS_TRUE; } static JSBool -mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) +mem_setbytes(JSXDRState *xdr, char *bytes, uint32_t len) { MEM_NEED(xdr, len); - memcpy(MEM_DATA(xdr), bytes, len); + js_memcpy(MEM_DATA(xdr), bytes, len); MEM_INCR(xdr, len); return JS_TRUE; } static void * -mem_raw(JSXDRState *xdr, uint32 len) +mem_raw(JSXDRState *xdr, uint32_t len) { void *data; if (xdr->mode == JSXDR_ENCODE) { @@ -160,11 +163,11 @@ mem_raw(JSXDRState *xdr, uint32 len) } static JSBool -mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) +mem_seek(JSXDRState *xdr, int32_t offset, JSXDRWhence whence) { switch (whence) { case JSXDR_SEEK_CUR: - if ((int32)MEM_COUNT(xdr) + offset < 0) { + if ((int32_t)MEM_COUNT(xdr) + offset < 0) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_SEEK_BEYOND_START); return JS_FALSE; @@ -180,11 +183,11 @@ mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) return JS_FALSE; } if (xdr->mode == JSXDR_ENCODE) { - if ((uint32)offset > MEM_COUNT(xdr)) + if ((uint32_t)offset > MEM_COUNT(xdr)) MEM_NEED(xdr, offset - MEM_COUNT(xdr)); MEM_COUNT(xdr) = offset; } else { - if ((uint32)offset > MEM_LIMIT(xdr)) { + if ((uint32_t)offset > MEM_LIMIT(xdr)) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_SEEK_BEYOND_END); return JS_FALSE; @@ -195,7 +198,7 @@ mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) case JSXDR_SEEK_END: if (offset >= 0 || xdr->mode == JSXDR_ENCODE || - (int32)MEM_LIMIT(xdr) + offset < 0) { + (int32_t)MEM_LIMIT(xdr) + offset < 0) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_END_SEEK); return JS_FALSE; @@ -212,7 +215,7 @@ mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) } } -static uint32 +static uint32_t mem_tell(JSXDRState *xdr) { return MEM_COUNT(xdr); @@ -265,7 +268,7 @@ JS_XDRNewMem(JSContext *cx, JSXDRMode mode) } JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) +JS_XDRMemGetData(JSXDRState *xdr, uint32_t *lp) { if (xdr->ops != &xdrmem_ops) return NULL; @@ -274,7 +277,7 @@ JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) } JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32_t len) { if (xdr->ops != &xdrmem_ops) return; @@ -283,7 +286,7 @@ JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) MEM_COUNT(xdr) = 0; } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_XDRMemDataLeft(JSXDRState *xdr) { if (xdr->ops != &xdrmem_ops) @@ -313,31 +316,31 @@ JS_XDRDestroy(JSXDRState *xdr) } JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b) +JS_XDRUint8(JSXDRState *xdr, uint8_t *b) { - uint32 l = *b; + uint32_t l = *b; if (!JS_XDRUint32(xdr, &l)) return JS_FALSE; - *b = (uint8) l; + *b = (uint8_t) l; return JS_TRUE; } JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s) +JS_XDRUint16(JSXDRState *xdr, uint16_t *s) { - uint32 l = *s; + uint32_t l = *s; if (!JS_XDRUint32(xdr, &l)) return JS_FALSE; - *s = (uint16) l; + *s = (uint16_t) l; return JS_TRUE; } JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp) +JS_XDRUint32(JSXDRState *xdr, uint32_t *lp) { JSBool ok = JS_TRUE; if (xdr->mode == JSXDR_ENCODE) { - uint32 xl = JSXDR_SWAB32(*lp); + uint32_t xl = JSXDR_SWAB32(*lp); ok = xdr->ops->set32(xdr, &xl); } else if (xdr->mode == JSXDR_DECODE) { ok = xdr->ops->get32(xdr, lp); @@ -347,9 +350,9 @@ JS_XDRUint32(JSXDRState *xdr, uint32 *lp) } JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len) { - uint32 padlen; + uint32_t padlen; static char padbuf[JSXDR_ALIGN-1]; if (xdr->mode == JSXDR_ENCODE) { @@ -381,7 +384,7 @@ JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) JS_PUBLIC_API(JSBool) JS_XDRCString(JSXDRState *xdr, char **sp) { - uint32 len; + uint32_t len; if (xdr->mode == JSXDR_ENCODE) len = strlen(*sp); @@ -404,7 +407,7 @@ JS_XDRCString(JSXDRState *xdr, char **sp) JS_PUBLIC_API(JSBool) JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) { - uint32 null = (*sp == NULL); + uint32_t null = (*sp == NULL); if (!JS_XDRUint32(xdr, &null)) return JS_FALSE; if (null) { @@ -414,61 +417,73 @@ JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) return JS_XDRCString(xdr, sp); } +static JSBool +XDRChars(JSXDRState *xdr, jschar *chars, uint32_t nchars) +{ + uint32_t i, padlen, nbytes; + jschar *raw; + + nbytes = nchars * sizeof(jschar); + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) + return JS_FALSE; + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i != nchars; i++) + raw[i] = JSXDR_SWAB16(chars[i]); + if (padlen) + memset((char *)raw + nbytes - padlen, 0, padlen); + } else if (xdr->mode == JSXDR_DECODE) { + for (i = 0; i != nchars; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + } + return JS_TRUE; +} + /* * Convert between a JS (Unicode) string and the XDR representation. */ JS_PUBLIC_API(JSBool) JS_XDRString(JSXDRState *xdr, JSString **strp) { - uint32 nchars = 0; - size_t len; + uint32_t nchars; + jschar *chars; - if (xdr->mode == JSXDR_ENCODE) { - len = js_GetDeflatedUTF8StringLength(xdr->cx, - (*strp)->getChars(xdr->cx), - (*strp)->length(), - true); - /* js_GetDeflatedUTF8StringLength never fails in CESU8 mode */ - JS_ASSERT(len != (size_t) -1); - JS_ASSERT(size_t(uint32(len)) == len); - /* ensure MAX_LENGTH strings can always fit when CESU8 encoded */ - JS_STATIC_ASSERT(JSString::MAX_LENGTH < (uint32(-1) / 3)); - nchars = (uint32)len; - } + if (xdr->mode == JSXDR_ENCODE) + nchars = (*strp)->length(); if (!JS_XDRUint32(xdr, &nchars)) - return false; - - JS_ASSERT(xdr->ops == &xdrmem_ops); - uint32 paddedLen = (nchars + JSXDR_MASK) & ~JSXDR_MASK; - char *buf = (char *)mem_raw(xdr, paddedLen); - if (!buf) - return false; + return JS_FALSE; - len = (uint32)nchars; - if (xdr->mode == JSXDR_ENCODE) { - JS_ALWAYS_TRUE(js_DeflateStringToUTF8Buffer(xdr->cx, - (*strp)->getChars(xdr->cx), - (*strp)->length(), buf, - &len, true)); - } else { - jschar *chars = js_InflateString(xdr->cx, buf, &len, true); - if (!chars) - return false; + if (xdr->mode == JSXDR_DECODE) + chars = (jschar *) xdr->cx->malloc_((nchars + 1) * sizeof(jschar)); + else + chars = const_cast((*strp)->getChars(xdr->cx)); + if (!chars) + return JS_FALSE; - *strp = js_NewString(xdr->cx, chars, len); - if (!*strp) { - xdr->cx->free_(chars); - return false; - } + if (!XDRChars(xdr, chars, nchars)) + goto bad; + if (xdr->mode == JSXDR_DECODE) { + chars[nchars] = 0; + *strp = JS_NewUCString(xdr->cx, chars, nchars); + if (!*strp) + goto bad; } + return JS_TRUE; - return true; +bad: + if (xdr->mode == JSXDR_DECODE) + xdr->cx->free_(chars); + return JS_FALSE; } JS_PUBLIC_API(JSBool) JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) { - uint32 null = (*strp == NULL); + uint32_t null = (*strp == NULL); if (!JS_XDRUint32(xdr, &null)) return JS_FALSE; if (null) { @@ -532,7 +547,7 @@ GetXDRTag(jsval v) } static JSBool -XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) +XDRValueBody(JSXDRState *xdr, uint32_t type, jsval *vp) { switch (type) { case XDRTAG_XDRNULL: @@ -570,9 +585,9 @@ XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) break; } case XDRTAG_SPECIAL: { - uint32 b; + uint32_t b; if (xdr->mode == JSXDR_ENCODE) - b = (uint32) JSVAL_TO_BOOLEAN(*vp); + b = (uint32_t) JSVAL_TO_BOOLEAN(*vp); if (!JS_XDRUint32(xdr, &b)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) @@ -580,15 +595,15 @@ XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) break; } default: { - uint32 i; + uint32_t i; JS_ASSERT(type == XDRTAG_INT); if (xdr->mode == JSXDR_ENCODE) - i = (uint32) JSVAL_TO_INT(*vp); + i = (uint32_t) JSVAL_TO_INT(*vp); if (!JS_XDRUint32(xdr, &i)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) - *vp = INT_TO_JSVAL((int32) i); + *vp = INT_TO_JSVAL((int32_t) i); break; } } @@ -598,83 +613,58 @@ XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp) { - uint32 type; + uint32_t type; if (xdr->mode == JSXDR_ENCODE) type = GetXDRTag(*vp); return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); } -static uint32 -XDRGetAtomIndex(JSXDRState *xdr, JSAtom *atom) -{ - if (XDRAtomsHashMap::Ptr p = xdr->state->atomsMap.lookup(atom)) - return p->value; - return uint32(-1); -} - -static bool -XDRPutAtomIndex(JSXDRState *xdr, JSAtom *atom) -{ - XDRScriptState *state = xdr->state; - if (state->atoms.length() >= size_t(uint32(-1))) - return true; - - if ((xdr->mode == JSXDR_DECODE || - state->atomsMap.put(atom, uint32(state->atoms.length()))) && - state->atoms.append(atom)) - return true; - - js_ReportOutOfMemory(xdr->cx); - return false; -} - extern JSBool js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) { - uint32 idx; + JSString *str; + uint32_t nchars; + JSAtom *atom; + JSContext *cx; + jschar *chars; + jschar stackChars[256]; + if (xdr->mode == JSXDR_ENCODE) { - idx = XDRGetAtomIndex(xdr, *atomp); - if (idx == uint32(-1) && - !XDRPutAtomIndex(xdr, *atomp)) - return false; + str = *atomp; + return JS_XDRString(xdr, &str); } - if (!JS_XDRUint32(xdr, &idx)) - return false; - - if (xdr->mode == JSXDR_DECODE) { - if (idx != uint32(-1)) { - JS_ASSERT(size_t(idx) < xdr->state->atoms.length()); - *atomp = xdr->state->atoms[idx]; - } else { - uint32 len; - if (!JS_XDRUint32(xdr, &len)) - return false; - - JS_ASSERT(xdr->ops == &xdrmem_ops); - uint32 paddedLen = (len + JSXDR_MASK) & ~JSXDR_MASK; - char *buf = (char *)mem_raw(xdr, paddedLen); - if (!buf) - return false; - - JSAtom *atom = js_Atomize(xdr->cx, buf, len, 0, true); - if (!atom) - return false; - - if (!XDRPutAtomIndex(xdr, atom)) - return false; - - *atomp = atom; - } + /* + * Inline JS_XDRString when decoding to avoid JSString allocation + * for already existing atoms. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + if (nchars <= ArrayLength(stackChars)) { + chars = stackChars; } else { - if (idx == uint32(-1)) { - JSString *str = *atomp; - return JS_XDRString(xdr, &str); - } + /* + * This is very uncommon. Don't use the tempLifoAlloc arena for this as + * most allocations here will be bigger than tempLifoAlloc's default + * chunk size. + */ + chars = (jschar *) cx->malloc_(nchars * sizeof(jschar)); + if (!chars) + return JS_FALSE; } - return true; + if (XDRChars(xdr, chars, nchars)) + atom = js_AtomizeChars(cx, chars, nchars); + if (chars != stackChars) + cx->free_(chars); + + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; } XDRScriptState::XDRScriptState(JSXDRState *x) @@ -684,11 +674,6 @@ XDRScriptState::XDRScriptState(JSXDRState *x) { JS_ASSERT(!xdr->state); - if (xdr->mode == JSXDR_ENCODE && !atomsMap.init()) { - js_ReportOutOfMemory(xdr->cx); - return; - } - xdr->state = this; } @@ -700,24 +685,45 @@ XDRScriptState::~XDRScriptState() } JS_PUBLIC_API(JSBool) -JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp) +JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) +{ + XDRScriptState fstate(xdr); + + if (xdr->mode == JSXDR_ENCODE) { + JSFunction* fun = (*objp)->toFunction(); + fstate.filename = fun->script()->filename; + } + + if (!JS_XDRCStringOrNull(xdr, (char **) &fstate.filename)) + return false; + + return js_XDRFunctionObject(xdr, objp); +} + +JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) { JS_ASSERT(!xdr->state); JSScript *script; - uint32 magic; + uint32_t magic; + uint32_t bytecodeVer; if (xdr->mode == JSXDR_DECODE) { script = NULL; - *scriptObjp = NULL; + *scriptp = NULL; } else { - script = (*scriptObjp)->getScript(); + script = *scriptp; magic = JSXDR_MAGIC_SCRIPT_CURRENT; + bytecodeVer = JSXDR_BYTECODE_VERSION; } if (!JS_XDRUint32(xdr, &magic)) return false; + if (!JS_XDRUint32(xdr, &bytecodeVer)) + return false; - if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) { + if (magic != JSXDR_MAGIC_SCRIPT_CURRENT || + bytecodeVer != JSXDR_BYTECODE_VERSION) { /* We do not provide binary compatibility with older scripts. */ JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); return false; @@ -736,12 +742,11 @@ JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp) return false; if (xdr->mode == JSXDR_DECODE) { + JS_ASSERT(!script->compileAndGo); + script->globalObject = GetCurrentGlobal(xdr->cx); js_CallNewScriptHook(xdr->cx, script, NULL); - *scriptObjp = js_NewScriptObject(xdr->cx, script); - if (!*scriptObjp) { - js_DestroyScript(xdr->cx, script); - return false; - } + Debugger::onNewScript(xdr->cx, script, NULL); + *scriptp = script; } return true; @@ -754,11 +759,11 @@ JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp) typedef struct JSRegHashEntry { JSDHashEntryHdr hdr; const char *name; - uint32 index; + uint32_t index; } JSRegHashEntry; JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32_t *idp) { uintN numclasses, maxclasses; JSClass **registry; @@ -795,7 +800,7 @@ JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) return JS_TRUE; } -JS_PUBLIC_API(uint32) +JS_PUBLIC_API(uint32_t) JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) { uintN i, numclasses; @@ -841,7 +846,7 @@ JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) } JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id) +JS_XDRFindClassById(JSXDRState *xdr, uint32_t id) { uintN i = CLASS_ID_TO_INDEX(id); diff --git a/deps/mozjs/js/src/jsxdrapi.h b/deps/mozjs/js/src/jsxdrapi.h index 5b55586173c..ca10000671b 100644 --- a/deps/mozjs/js/src/jsxdrapi.h +++ b/deps/mozjs/js/src/jsxdrapi.h @@ -61,11 +61,8 @@ * Spiritually guided by Sun's XDR, where appropriate. */ -#include "jsatom.h" #include "jspubtd.h" #include "jsprvtd.h" -#include "jsvector.h" -#include "jshashtable.h" JS_BEGIN_EXTERN_C @@ -75,17 +72,16 @@ JS_BEGIN_EXTERN_C #define JSXDR_SWAB32(x) x #define JSXDR_SWAB16(x) x #elif defined IS_BIG_ENDIAN -#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ - (((uint32)(x) >> 8) & 0xff00) | \ - (((uint32)(x) << 8) & 0xff0000) | \ - ((uint32)(x) << 24)) -#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) +#define JSXDR_SWAB32(x) (((uint32_t)(x) >> 24) | \ + (((uint32_t)(x) >> 8) & 0xff00) | \ + (((uint32_t)(x) << 8) & 0xff0000) | \ + ((uint32_t)(x) << 24)) +#define JSXDR_SWAB16(x) (((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8)) #else #error "unknown byte order" #endif #define JSXDR_ALIGN 4 -#define JSXDR_MASK (JSXDR_ALIGN - 1) typedef enum JSXDRMode { JSXDR_ENCODE, @@ -99,19 +95,16 @@ typedef enum JSXDRWhence { } JSXDRWhence; typedef struct JSXDROps { - JSBool (*get32)(JSXDRState *, uint32 *); - JSBool (*set32)(JSXDRState *, uint32 *); - JSBool (*getbytes)(JSXDRState *, char *, uint32); - JSBool (*setbytes)(JSXDRState *, char *, uint32); - void * (*raw)(JSXDRState *, uint32); - JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); - uint32 (*tell)(JSXDRState *); + JSBool (*get32)(JSXDRState *, uint32_t *); + JSBool (*set32)(JSXDRState *, uint32_t *); + JSBool (*getbytes)(JSXDRState *, char *, uint32_t); + JSBool (*setbytes)(JSXDRState *, char *, uint32_t); + void * (*raw)(JSXDRState *, uint32_t); + JSBool (*seek)(JSXDRState *, int32_t, JSXDRWhence); + uint32_t (*tell)(JSXDRState *); void (*finalize)(JSXDRState *); } JSXDROps; -typedef js::Vector XDRAtoms; -typedef js::HashMap, js::SystemAllocPolicy> XDRAtomsHashMap; - struct JSXDRState; namespace js { @@ -124,8 +117,6 @@ class XDRScriptState { JSXDRState *xdr; const char *filename; bool filenameSaved; - XDRAtoms atoms; - XDRAtomsHashMap atomsMap; }; } /* namespace JS */ @@ -150,12 +141,12 @@ extern JS_PUBLIC_API(JSXDRState *) JS_XDRNewMem(JSContext *cx, JSXDRMode mode); extern JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); +JS_XDRMemGetData(JSXDRState *xdr, uint32_t *lp); extern JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32_t len); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_XDRMemDataLeft(JSXDRState *xdr); extern JS_PUBLIC_API(void) @@ -165,16 +156,16 @@ extern JS_PUBLIC_API(void) JS_XDRDestroy(JSXDRState *xdr); extern JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b); +JS_XDRUint8(JSXDRState *xdr, uint8_t *b); extern JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s); +JS_XDRUint16(JSXDRState *xdr, uint16_t *s); extern JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp); +JS_XDRUint32(JSXDRState *xdr, uint32_t *lp); extern JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len); extern JS_PUBLIC_API(JSBool) JS_XDRCString(JSXDRState *xdr, char **sp); @@ -195,16 +186,19 @@ extern JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp); extern JS_PUBLIC_API(JSBool) -JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp); +JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); extern JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32_t *lp); -extern JS_PUBLIC_API(uint32) +extern JS_PUBLIC_API(uint32_t) JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); extern JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id); +JS_XDRFindClassById(JSXDRState *xdr, uint32_t id); /* * Magic numbers. @@ -220,18 +214,19 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); #define JSXDR_MAGIC_SCRIPT_9 0xdead0009 #define JSXDR_MAGIC_SCRIPT_10 0xdead000a #define JSXDR_MAGIC_SCRIPT_11 0xdead000b -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_11 +#define JSXDR_MAGIC_SCRIPT_12 0xdead000c +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_12 /* * Bytecode version number. Increment the subtrahend whenever JS bytecode * changes incompatibly. * - * This version number should be XDR'ed once near the front of any file or - * larger storage unit containing XDR'ed bytecode and other data, and checked - * before deserialization of bytecode. If the saved version does not match - * the current version, abort deserialization and invalidate the file. + * This version number is XDR'd near the front of xdr bytecode and + * aborts deserialization if there is a mismatch between the current + * and saved versions. If deserialization fails, the data should be + * invalidated if possible. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 85) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 107) /* * Library-private functions. diff --git a/deps/mozjs/js/src/jsxml.cpp b/deps/mozjs/js/src/jsxml.cpp index d2a2eb67499..b3a98d6487d 100644 --- a/deps/mozjs/js/src/jsxml.cpp +++ b/deps/mozjs/js/src/jsxml.cpp @@ -44,9 +44,10 @@ #include #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" #include "jsprf.h" #include "jsutil.h" #include "jsapi.h" @@ -61,27 +62,44 @@ #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsxml.h" -#include "jsstaticcheck.h" -#include "jsvector.h" + +#include "frontend/Parser.h" +#include "frontend/TokenStream.h" +#include "vm/GlobalObject.h" #include "jsatominlines.h" +#include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" #ifdef DEBUG #include /* for #ifdef DEBUG memset calls */ #endif +using namespace mozilla; using namespace js; using namespace js::gc; +using namespace js::types; + +template +struct IdentityOp +{ + typedef JSBool (* compare)(const T *a, const U *b); +}; + +template +static JSBool +pointer_match(const T *a, const T *b) +{ + return a == b; +} /* * NOTES @@ -106,11 +124,6 @@ js_LeaveLocalRootScope(JSContext *cx) { } -static inline void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ -} - static inline void js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval) { @@ -121,21 +134,6 @@ js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval) { } -#ifdef XML_METERING -static struct { - jsrefcount qname; - jsrefcount xmlnamespace; - jsrefcount xml; - jsrefcount xmlobj; -} xml_stats; - -#define METER(x) JS_ATOMIC_INCREMENT(&(x)) -#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) -#else -#define METER(x) /* nothing */ -#define UNMETER(x) /* nothing */ -#endif - /* * Random utilities and global functions. */ @@ -164,7 +162,7 @@ IsDeclared(const JSObject *obj) { jsval v; - JS_ASSERT(obj->getClass() == &js_NamespaceClass); + JS_ASSERT(obj->getClass() == &NamespaceClass); v = obj->getNamespaceDeclared(); JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE); return v == JSVAL_TRUE; @@ -177,6 +175,8 @@ xml_isXMLName(JSContext *cx, uintN argc, jsval *vp) return JS_TRUE; } +size_t sE4XObjectsCreated = 0; + /* * This wrapper is needed because NewBuiltinClassInstance doesn't * call the constructor, and we need a place to set the @@ -185,10 +185,10 @@ xml_isXMLName(JSContext *cx, uintN argc, jsval *vp) static inline JSObject * NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp) { - JSObject *obj = NewBuiltinClassInstance(cx, clasp); - if (obj) - obj->syncSpecialEquality(); - return obj; + if (!cx->runningWithTrustedPrincipals()) + ++sE4XObjectsCreated; + + return NewBuiltinClassInstance(cx, clasp); } #define DEFINE_GETTER(name,code) \ @@ -203,9 +203,9 @@ NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp) * Namespace class and library functions. */ DEFINE_GETTER(NamePrefix_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefixVal()) + if (obj->getClass() == &NamespaceClass) *vp = obj->getNamePrefixVal()) DEFINE_GETTER(NameURI_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURIVal()) + if (obj->getClass() == &NamespaceClass) *vp = obj->getNameURIVal()) static JSBool namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) @@ -214,38 +214,37 @@ namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) JS_ASSERT(v->isObjectOrNull()); obj2 = v->toObjectOrNull(); - *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass) + *bp = (!obj2 || obj2->getClass() != &NamespaceClass) ? JS_FALSE : EqualStrings(obj->getNameURI(), obj2->getNameURI()); return JS_TRUE; } -JS_FRIEND_DATA(Class) js_NamespaceClass = { +JS_FRIEND_DATA(Class) js::NamespaceClass = { "Namespace", - JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - FinalizeStub, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - NULL, /* mark */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* mark */ { namespace_equality, - NULL, /* outerObject */ - NULL, /* innerObject */ - NULL, /* iteratorObject */ - NULL, /* wrappedObject */ + NULL, /* outerObject */ + NULL, /* innerObject */ + NULL, /* iteratorObject */ + NULL, /* wrappedObject */ } }; @@ -265,10 +264,10 @@ namespace_toString(JSContext *cx, uintN argc, Value *vp) if (!obj) return JS_FALSE; if (!obj->isNamespace()) { - ReportIncompatibleMethod(cx, vp, &js_NamespaceClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &NamespaceClass); return JS_FALSE; } - *vp = Valueify(obj->getNameURIVal()); + *vp = obj->getNameURIVal(); return JS_TRUE; } @@ -282,19 +281,23 @@ NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBo { JSObject *obj; - obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); + obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass); if (!obj) - return JS_FALSE; + return NULL; JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared())); + + /* Per ECMA-357, 13.2.5, these properties must be "own". */ + if (!JS_DefineProperties(cx, obj, namespace_props)) + return NULL; + if (prefix) obj->setNamePrefix(prefix); if (uri) obj->setNameURI(uri); if (declared) obj->setNamespaceDeclared(JSVAL_TRUE); - METER(xml_stats.xmlnamespace); return obj; } @@ -302,14 +305,14 @@ NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBo * QName class and library functions. */ DEFINE_GETTER(QNameNameURI_getter, - if (obj->getClass() == &js_QNameClass) + if (obj->getClass() == &QNameClass) *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal()) DEFINE_GETTER(QNameLocalName_getter, - if (obj->getClass() == &js_QNameClass) + if (obj->getClass() == &QNameClass) *vp = obj->getQNameLocalNameVal()) static JSBool -qname_identity(JSObject *qna, JSObject *qnb) +qname_identity(JSObject *qna, const JSObject *qnb) { JSLinearString *uri1 = qna->getNameURI(); JSLinearString *uri2 = qnb->getNameURI(); @@ -327,38 +330,37 @@ qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp) JSObject *obj2; obj2 = v->toObjectOrNull(); - *bp = (!obj2 || obj2->getClass() != &js_QNameClass) + *bp = (!obj2 || obj2->getClass() != &QNameClass) ? JS_FALSE : qname_identity(qn, obj2); return JS_TRUE; } -JS_FRIEND_DATA(Class) js_QNameClass = { +JS_FRIEND_DATA(Class) js::QNameClass = { "QName", - JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_QName), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - FinalizeStub, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - NULL, /* mark */ + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* mark */ { qname_equality, - NULL, /* outerObject */ - NULL, /* innerObject */ - NULL, /* iteratorObject */ - NULL, /* wrappedObject */ + NULL, /* outerObject */ + NULL, /* innerObject */ + NULL, /* iteratorObject */ + NULL, /* wrappedObject */ } }; @@ -368,34 +370,32 @@ JS_FRIEND_DATA(Class) js_QNameClass = { * qname_toString method, and therefore are exposed as constructable objects * in this implementation. */ -JS_FRIEND_DATA(Class) js_AttributeNameClass = { +JS_FRIEND_DATA(Class) js::AttributeNameClass = { js_AttributeName_str, - JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - FinalizeStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub }; -JS_FRIEND_DATA(Class) js_AnyNameClass = { +JS_FRIEND_DATA(Class) js::AnyNameClass = { js_AnyName_str, - JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, - FinalizeStub + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub }; #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) @@ -428,7 +428,7 @@ ConvertQNameToString(JSContext *cx, JSObject *obj) if (!str) return NULL; - if (obj->getClass() == &js_AttributeNameClass) { + if (obj->getClass() == &AttributeNameClass) { JS::Anchor anchor(str); size_t length = str->length(); jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar)); @@ -459,7 +459,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) return false; if (!obj->isQName()) { - ReportIncompatibleMethod(cx, vp, &js_QNameClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &QNameClass); return false; } @@ -477,48 +477,55 @@ static JSFunctionSpec qname_methods[] = { }; -static void -InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) +static bool +InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix, + JSAtom *localName) { JS_ASSERT(obj->isQName()); JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal())); + + /* Per ECMA-357, 13.3.5, these properties must be "own". */ + if (!JS_DefineProperties(cx, obj, qname_props)) + return false; + if (uri) obj->setNameURI(uri); if (prefix) obj->setNamePrefix(prefix); if (localName) obj->setQNameLocalName(localName); + return true; } static JSObject * NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) + JSAtom *localName) { - JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass); + JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass); if (!obj) return NULL; - InitXMLQName(obj, uri, prefix, localName); - METER(xml_stats.qname); + if (!InitXMLQName(cx, obj, uri, prefix, localName)) + return NULL; return obj; } static JSObject * NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) + JSAtom *localName) { /* * AttributeName is an internal anonymous class which instances are not * exposed to scripts. */ - JSObject *obj = NewNonFunction(cx, &js_AttributeNameClass, NULL, NULL); + JSObject *parent = GetGlobalForScopeChain(cx); + JSObject *obj = NewObjectWithGivenProto(cx, &AttributeNameClass, NULL, parent); if (!obj) return NULL; JS_ASSERT(obj->isQName()); - InitXMLQName(obj, uri, prefix, localName); - METER(xml_stats.qname); + if (!InitXMLQName(cx, obj, uri, prefix, localName)) + return NULL; return obj; } @@ -533,13 +540,13 @@ js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval * production, step 2. */ if (nsval.isObject() && - nsval.toObject().getClass() == &js_AnyNameClass) { + nsval.toObject().getClass() == &AnyNameClass) { argv[0].setNull(); } else { argv[0] = nsval; } argv[1] = lnval; - return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv); + return JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, argv); } static JSBool @@ -549,10 +556,10 @@ IsXMLName(const jschar *cp, size_t n) jschar c; rv = JS_FALSE; - if (n != 0 && JS_ISXMLNSSTART(*cp)) { + if (n != 0 && unicode::IsXMLNamespaceStart(*cp)) { while (--n != 0) { c = *++cp; - if (!JS_ISXMLNS(c)) + if (!unicode::IsXMLNamespacePart(c)) return rv; } rv = JS_TRUE; @@ -577,7 +584,7 @@ js_IsXMLName(JSContext *cx, jsval v) name = JSVAL_TO_OBJECT(v)->getQNameLocalName(); } else { older = JS_SetErrorReporter(cx, NULL); - JSString *str = js_ValueToString(cx, Valueify(v)); + JSString *str = ToString(cx, v); if (str) name = str->ensureLinear(cx); JS_SetErrorReporter(cx, older); @@ -595,8 +602,7 @@ js_IsXMLName(JSContext *cx, jsval v) * if argc is 1 and argv[0] is JSVAL_VOID. */ static JSBool -NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, - jsval *rval) +NamespaceHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval) { jsval urival, prefixval; JSObject *uriobj; @@ -615,25 +621,27 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, if (!JSVAL_IS_PRIMITIVE(urival)) { uriobj = JSVAL_TO_OBJECT(urival); clasp = uriobj->getClass(); - isNamespace = (clasp == &js_NamespaceClass); - isQName = (clasp == &js_QNameClass); + isNamespace = (clasp == &NamespaceClass); + isQName = (clasp == &QNameClass); } } - if (!obj) { - /* Namespace called as function. */ - if (argc == 1 && isNamespace) { - /* Namespace called with one Namespace argument is identity. */ - *rval = urival; - return JS_TRUE; - } - - obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); - if (!obj) - return JS_FALSE; + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; } + + JSObject *obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass); + if (!obj) + return JS_FALSE; + + /* Per ECMA-357, 13.2.5, these properties must be "own". */ + if (!JS_DefineProperties(cx, obj, namespace_props)) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); - METER(xml_stats.xmlnamespace); empty = cx->runtime->emptyString; obj->setNamePrefix(empty); @@ -647,7 +655,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, obj->setNameURI(uri); obj->setNamePrefix(uriobj->getNamePrefix()); } else { - JSString *str = js_ValueToString(cx, Valueify(urival)); + JSString *str = ToString(cx, urival); if (!str) return JS_FALSE; uri = str->ensureLinear(cx); @@ -659,7 +667,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, } } else if (argc == 2) { if (!isQName || !(uri = uriobj->getNameURI())) { - JSString *str = js_ValueToString(cx, Valueify(urival)); + JSString *str = ToString(cx, urival); if (!str) return JS_FALSE; uri = str->ensureLinear(cx); @@ -671,7 +679,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, prefixval = argv[0]; if (uri->empty()) { if (!JSVAL_IS_VOID(prefixval)) { - JSString *str = js_ValueToString(cx, Valueify(prefixval)); + JSString *str = ToString(cx, prefixval); if (!str) return JS_FALSE; if (!str->empty()) { @@ -686,7 +694,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { obj->clearNamePrefix(); } else { - JSString *str = js_ValueToString(cx, Valueify(prefixval)); + JSString *str = ToString(cx, prefixval); if (!str) return JS_FALSE; prefix = str->ensureLinear(cx); @@ -701,9 +709,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, static JSBool Namespace(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = NULL; - (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj); - return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp)); + return NamespaceHelper(cx, argc, vp + 2, vp); } /* @@ -711,14 +717,15 @@ Namespace(JSContext *cx, uintN argc, Value *vp) * if argc is 1 and argv[0] is JSVAL_VOID. */ static JSBool -QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) +QNameHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval) { jsval nameval, nsval; JSBool isQName, isNamespace; JSObject *qn; - JSLinearString *uri, *prefix, *name; + JSLinearString *uri, *prefix; JSObject *obj2; + JSAtom *name; if (argc <= 0) { nameval = JSVAL_VOID; isQName = JS_FALSE; @@ -726,24 +733,21 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) nameval = argv[argc > 1]; isQName = !JSVAL_IS_PRIMITIVE(nameval) && - JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass; + JSVAL_TO_OBJECT(nameval)->getClass() == &QNameClass; } - if (!obj) { - /* QName called as function. */ - if (argc == 1 && isQName) { - /* QName called with one QName argument is identity. */ - *rval = nameval; - return JS_TRUE; - } + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } /* Create and return a new QName object exactly as if constructed. */ - obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass); - if (!obj) - return JS_FALSE; - } + JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass); + if (!obj) + return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); - METER(xml_stats.qname); if (isQName) { /* If namespace is not specified and name is a QName, clone it. */ @@ -764,13 +768,8 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) } else if (argc < 0) { name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } else { - JSString *str = js_ValueToString(cx, Valueify(nameval)); - if (!str) - return JS_FALSE; - name = str->ensureLinear(cx); - if (!name) - return JS_FALSE; - argv[argc > 1] = STRING_TO_JSVAL(name); + if (!js_ValueToAtom(cx, nameval, &name)) + return false; } if (argc > 1 && !JSVAL_IS_VOID(argv[0])) { @@ -782,7 +781,7 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() == - &js_NamespaceClass); + &NamespaceClass); } if (JSVAL_IS_NULL(nsval)) { @@ -799,8 +798,8 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) isNamespace = isQName = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(nsval)) { obj2 = JSVAL_TO_OBJECT(nsval); - isNamespace = (obj2->getClass() == &js_NamespaceClass); - isQName = (obj2->getClass() == &js_QNameClass); + isNamespace = (obj2->getClass() == &NamespaceClass); + isQName = (obj2->getClass() == &QNameClass); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else obj2 = NULL; @@ -814,7 +813,7 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) prefix = obj2->getNamePrefix(); } else { JS_ASSERT(argc > 1); - JSString *str = js_ValueToString(cx, Valueify(nsval)); + JSString *str = ToString(cx, nsval); if (!str) return JS_FALSE; uri = str->ensureLinear(cx); @@ -828,26 +827,21 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) } out: - InitXMLQName(obj, uri, prefix, name); - return JS_TRUE; + return InitXMLQName(cx, obj, uri, prefix, name); } static JSBool QName(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = NULL; - (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj); - return QNameHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp)); + return QNameHelper(cx, argc, vp + 2, vp); } /* * XMLArray library functions. */ static JSBool -namespace_identity(const void *a, const void *b) +namespace_identity(const JSObject *nsa, const JSObject *nsb) { - const JSObject *nsa = (const JSObject *) a; - const JSObject *nsb = (const JSObject *) b; JSLinearString *prefixa = nsa->getNamePrefix(); JSLinearString *prefixb = nsb->getNamePrefix(); @@ -862,32 +856,46 @@ namespace_identity(const void *a, const void *b) } static JSBool -attr_identity(const void *a, const void *b) +attr_identity(const JSXML *xmla, const JSXML *xmlb) { - const JSXML *xmla = (const JSXML *) a; - const JSXML *xmlb = (const JSXML *) b; - return qname_identity(xmla->name, xmlb->name); } void -JSXMLArrayCursor::trace(JSTracer *trc) { -#ifdef DEBUG - size_t index = 0; -#endif - for (JSXMLArrayCursor *cursor = this; cursor; cursor = cursor->next) - js::gc::MarkGCThing(trc, cursor->root, "cursor_root", index++); +js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) +{ + for (; cursor; cursor = cursor->next) { + if (cursor->root) + MarkXML(trc, (const HeapPtr &)cursor->root, "cursor_root"); + } } -static void -XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) +void +js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) { - cursor->trace(trc); + for (; cursor; cursor = cursor->next) { + if (cursor->root) + MarkObject(trc, (const HeapPtr &)cursor->root, "cursor_root"); + } +} + +template +static HeapPtr * +ReallocateVector(HeapPtr *vector, size_t count) +{ +#if JS_BITS_PER_WORD == 32 + if (count > ~(size_t)0 / sizeof(HeapPtr)) + return NULL; +#endif + + size_t size = count * sizeof(HeapPtr); + return (HeapPtr *) OffTheBooks::realloc_(vector, size); } /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */ +template bool -JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity) +JSXMLArray::setCapacity(JSContext *cx, uint32_t newCapacity) { if (newCapacity == 0) { /* We could let realloc(p, 0) free this, but purify gets confused. */ @@ -899,13 +907,8 @@ JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity) } vector = NULL; } else { - void **tmp; - - if ( -#if JS_BITS_PER_WORD == 32 - (size_t)newCapacity > ~(size_t)0 / sizeof(void *) || -#endif - !(tmp = (void **) OffTheBooks::realloc_(vector, newCapacity * sizeof(void *)))) { + HeapPtr *tmp = ReallocateVector(vector, newCapacity); + if (!tmp) { if (cx) JS_ReportOutOfMemory(cx); return false; @@ -916,8 +919,9 @@ JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity) return true; } +template void -JSXMLArray::trim() +JSXMLArray::trim() { if (capacity & JSXML_PRESET_CAPACITY) return; @@ -925,12 +929,19 @@ JSXMLArray::trim() setCapacity(NULL, length); } +template void -JSXMLArray::finish(JSContext *cx) +JSXMLArray::finish(JSContext *cx) { + if (!cx->runtime->gcRunning) { + /* We need to clear these to trigger a write barrier. */ + for (uint32_t i = 0; i < length; i++) + vector[i].~HeapPtr(); + } + cx->free_(vector); - while (JSXMLArrayCursor *cursor = cursors) + while (JSXMLArrayCursor *cursor = cursors) cursor->disconnect(); #ifdef DEBUG @@ -938,26 +949,20 @@ JSXMLArray::finish(JSContext *cx) #endif } -#define XML_NOT_FOUND ((uint32) -1) +#define XML_NOT_FOUND UINT32_MAX -static uint32 -XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) +template +static uint32_t +XMLArrayFindMember(const JSXMLArray *array, U *elt, typename IdentityOp::compare identity) { - void **vector; - uint32 i, n; + HeapPtr *vector; + uint32_t i, n; /* The identity op must not reallocate array->vector. */ vector = array->vector; - if (identity) { - for (i = 0, n = array->length; i < n; i++) { - if (identity(vector[i], elt)) - return i; - } - } else { - for (i = 0, n = array->length; i < n; i++) { - if (vector[i] == elt) - return i; - } + for (i = 0, n = array->length; i < n; i++) { + if (identity(vector[i].get(), elt)) + return i; } return XML_NOT_FOUND; } @@ -970,12 +975,13 @@ XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) #define LINEAR_THRESHOLD 256 #define LINEAR_INCREMENT 32 +template static JSBool -XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) +XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32_t index, T *elt) { - uint32 capacity, i; + uint32_t capacity, i; int log2; - void **vector; + HeapPtr *vector; if (index >= array->length) { if (index >= JSXML_CAPACITY(array)) { @@ -987,20 +993,16 @@ XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) JS_CEILING_LOG2(log2, capacity); capacity = JS_BIT(log2); } - if ( -#if JS_BITS_PER_WORD == 32 - (size_t)capacity > ~(size_t)0 / sizeof(void *) || -#endif - !(vector = (void **) - cx->realloc_(array->vector, capacity * sizeof(void *)))) { + if (!(vector = ReallocateVector(array->vector, capacity))) { JS_ReportOutOfMemory(cx); return JS_FALSE; } array->capacity = capacity; array->vector = vector; for (i = array->length; i < index; i++) - vector[i] = NULL; + vector[i].init(NULL); } + array->vector[index].init(NULL); array->length = index + 1; } @@ -1008,19 +1010,26 @@ XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) return JS_TRUE; } +template static JSBool -XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) +XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32_t i, uint32_t n) { - uint32 j; - JSXMLArrayCursor *cursor; + uint32_t j, k; + JSXMLArrayCursor *cursor; j = array->length; JS_ASSERT(i <= j); if (!array->setCapacity(cx, j + n)) return JS_FALSE; + k = j; + while (k != j + n) { + array->vector[k].init(NULL); + k++; + } + array->length = j + n; - JS_ASSERT(n != (uint32)-1); + JS_ASSERT(n != (uint32_t)-1); while (j != i) { --j; array->vector[j + n] = array->vector[j]; @@ -1033,12 +1042,14 @@ XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) return JS_TRUE; } -static void * -XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) +template +static T * +XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32_t index, JSBool compress) { - uint32 length; - void **vector, *elt; - JSXMLArrayCursor *cursor; + uint32_t length; + HeapPtr *vector; + T *elt; + JSXMLArrayCursor *cursor; length = array->length; if (index >= length) @@ -1047,6 +1058,7 @@ XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) vector = array->vector; elt = vector[index]; if (compress) { + vector[length - 1].~HeapPtr(); while (++index < length) vector[index-1] = vector[index]; array->length = length - 1; @@ -1062,21 +1074,25 @@ XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) return elt; } +template static void -XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) +XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32_t length) { - void **vector; + HeapPtr *vector; JS_ASSERT(!array->cursors); if (length >= array->length) return; + for (uint32_t i = length; i < array->length; i++) + array->vector[i].~HeapPtr(); + if (length == 0) { if (array->vector) cx->free_(array->vector); vector = NULL; } else { - vector = (void **) cx->realloc_(array->vector, length * sizeof(void *)); + vector = ReallocateVector(array->vector, length); if (!vector) return; } @@ -1087,21 +1103,24 @@ XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) array->vector = vector; } -#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) -#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ +#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, e, f) +#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, e, f) != \ XML_NOT_FOUND) #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ - ? (t *) (a)->vector[i] \ + ? (a)->vector[i].get() \ : NULL) #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ - if ((a)->length <= (i)) \ + if ((a)->length <= (i)) { \ (a)->length = (i) + 1; \ - ((a)->vector[i] = (void *)(e)); \ + ((a)->vector[i].init(e)); \ + } else { \ + ((a)->vector[i] = e); \ + } \ JS_END_MACRO -#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) +#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, e) #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) -#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) +#define XMLARRAY_DELETE(x,a,i,c,t) (XMLArrayDelete(x, a, i, c)) #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) /* @@ -1136,14 +1155,20 @@ static JSPropertySpec xml_static_props[] = { #define IS_XMLNS(str) \ (str->length() == 5 && IS_XMLNS_CHARS(str->chars())) -#define IS_XML_CHARS(chars) \ - (JS_TOLOWER((chars)[0]) == 'x' && \ - JS_TOLOWER((chars)[1]) == 'm' && \ - JS_TOLOWER((chars)[2]) == 'l') +static inline bool +IS_XML_CHARS(const jschar *chars) +{ + return (chars[0] == 'x' || chars[0] == 'X') && + (chars[1] == 'm' || chars[1] == 'M') && + (chars[2] == 'l' || chars[2] == 'L'); +} -#define HAS_NS_AFTER_XML(chars) \ - (JS_TOLOWER((chars)[3]) == 'n' && \ - JS_TOLOWER((chars)[4]) == 's') +static inline bool +HAS_NS_AFTER_XML(const jschar *chars) +{ + return (chars[3] == 'n' || chars[3] == 'N') && + (chars[4] == 's' || chars[4] == 'S'); +} #define IS_XMLNS_CHARS(chars) \ (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) @@ -1154,25 +1179,42 @@ static JSPropertySpec xml_static_props[] = { static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; +void +JSXML::finalize(JSContext *cx, bool builtin) +{ + if (JSXML_HAS_KIDS(this)) { + xml_kids.finish(cx); + if (xml_class == JSXML_CLASS_ELEMENT) { + xml_namespaces.finish(cx); + xml_attrs.finish(cx); + } + } +#ifdef DEBUG_notme + JS_REMOVE_LINK(&links); +#endif +} + static JSObject * -ParseNodeToQName(Parser *parser, JSParseNode *pn, - JSXMLArray *inScopeNSes, JSBool isAttributeName) +ParseNodeToQName(Parser *parser, ParseNode *pn, + JSXMLArray *inScopeNSes, JSBool isAttributeName) { JSContext *cx = parser->context; - JSLinearString *str, *uri, *prefix, *localName; + JSLinearString *uri, *prefix; size_t length, offset; const jschar *start, *limit, *colon; - uint32 n; + uint32_t n; JSObject *ns; JSLinearString *nsprefix; - JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = pn->pn_atom; + JS_ASSERT(pn->isArity(PN_NULLARY)); + JSAtom *str = pn->pn_atom; start = str->chars(); length = str->length(); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); + JSAtom *localName; + uri = cx->runtime->emptyString; limit = start + length; colon = js_strchr_limit(start, ':', limit); @@ -1218,7 +1260,7 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, return NULL; } - localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1)); + localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1)); if (!localName) return NULL; } else { @@ -1265,12 +1307,12 @@ ChompXMLWhitespace(JSContext *cx, JSString *str) for (cp = start, end = cp + length; cp < end; cp++) { c = *cp; - if (!JS_ISXMLSPACE(c)) + if (!unicode::IsXMLSpace(c)) break; } while (end > cp) { c = end[-1]; - if (!JS_ISXMLSPACE(c)) + if (!unicode::IsXMLSpace(c)) break; --end; } @@ -1282,20 +1324,20 @@ ChompXMLWhitespace(JSContext *cx, JSString *str) } static JSXML * -ParseNodeToXML(Parser *parser, JSParseNode *pn, - JSXMLArray *inScopeNSes, uintN flags) +ParseNodeToXML(Parser *parser, ParseNode *pn, + JSXMLArray *inScopeNSes, uintN flags) { JSContext *cx = parser->context; JSXML *xml, *kid, *attr, *attrj; JSLinearString *str; - uint32 length, n, i, j; - JSParseNode *pn2, *pn3, *head, **pnp; + uint32_t length, n, i, j; + ParseNode *pn2, *pn3, *head, **pnp; JSObject *ns; JSObject *qn, *attrjqn; JSXMLClass xml_class; int stackDummy; - if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)) { + if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) { ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_OVER_RECURSED); return NULL; @@ -1311,8 +1353,8 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, xml = NULL; if (!js_EnterLocalRootScope(cx)) return NULL; - switch (pn->pn_type) { - case TOK_XMLELEM: + switch (pn->getKind()) { + case PNK_XMLELEM: length = inScopeNSes->length; pn2 = pn->pn_head; xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags); @@ -1329,12 +1371,12 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, while ((pn2 = pn2->pn_next) != NULL) { if (!pn2->pn_next) { /* Don't append the end tag! */ - JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + JS_ASSERT(pn2->isKind(PNK_XMLETAGO)); break; } if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && pn2->pn_type == TOK_XMLSPACE) { + n > 1 && pn2->isKind(PNK_XMLSPACE)) { --n; continue; } @@ -1369,7 +1411,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, XMLARRAY_TRUNCATE(cx, inScopeNSes, length); break; - case TOK_XMLLIST: + case PNK_XMLLIST: xml = js_NewXML(cx, JSXML_CLASS_LIST); if (!xml) goto fail; @@ -1385,7 +1427,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, * condition this on an XML.ignoreWhitespace setting when the list * constructor is XMLList (note XML/XMLList unification hazard). */ - if (pn2->pn_type == TOK_XMLSPACE) { + if (pn2->isKind(PNK_XMLSPACE)) { --n; continue; } @@ -1407,12 +1449,12 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, xml->xml_kids.trim(); break; - case TOK_XMLSTAGO: - case TOK_XMLPTAGC: + case PNK_XMLSTAGO: + case PNK_XMLPTAGC: length = inScopeNSes->length; pn2 = pn->pn_head; - JS_ASSERT(pn2->pn_type == TOK_XMLNAME); - if (pn2->pn_arity == PN_LIST) + JS_ASSERT(pn2->isKind(PNK_XMLNAME)); + if (pn2->isArity(PN_LIST)) goto syntax; xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); @@ -1428,7 +1470,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, size_t length; const jschar *chars; - if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + if (!pn2->isKind(PNK_XMLNAME) || !pn2->isArity(PN_NULLARY)) goto syntax; /* Enforce "Well-formedness constraint: Unique Att Spec". */ @@ -1448,7 +1490,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, JSAtom *atom = pn2->pn_atom; pn2 = pn2->pn_next; JS_ASSERT(pn2); - if (pn2->pn_type != TOK_XMLATTR) + if (!pn2->isKind(PNK_XMLATTR)) goto syntax; chars = atom->chars(); @@ -1545,7 +1587,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, pn2 = pn2->pn_next; JS_ASSERT(pn2); - JS_ASSERT(pn2->pn_type == TOK_XMLATTR); + JS_ASSERT(pn2->isKind(PNK_XMLATTR)); attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); if (!attr) @@ -1558,27 +1600,28 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, } /* Point tag closes its own namespace scope. */ - if (pn->pn_type == TOK_XMLPTAGC) + if (pn->isKind(PNK_XMLPTAGC)) XMLARRAY_TRUNCATE(cx, inScopeNSes, length); break; - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: + case PNK_XMLSPACE: + case PNK_XMLTEXT: + case PNK_XMLCDATA: + case PNK_XMLCOMMENT: + case PNK_XMLPI: str = pn->pn_atom; qn = NULL; - if (pn->pn_type == TOK_XMLCOMMENT) { + if (pn->isKind(PNK_XMLCOMMENT)) { if (flags & XSF_IGNORE_COMMENTS) goto skip_child; xml_class = JSXML_CLASS_COMMENT; - } else if (pn->pn_type == TOK_XMLPI) { + } else if (pn->isKind(PNK_XMLPI)) { + XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction(); if (IS_XML(str)) { Value v = StringValue(str); JSAutoByteString bytes; if (js_ValueToPrintable(cx, v, &bytes)) { - ReportCompileErrorNumber(cx, &parser->tokenStream, pn, + ReportCompileErrorNumber(cx, &parser->tokenStream, &pi, JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr()); } goto fail; @@ -1587,11 +1630,11 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) goto skip_child; - qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE); + qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE); if (!qn) goto fail; - str = pn->pn_atom2 ? pn->pn_atom2 : cx->runtime->emptyString; + str = pi.data(); xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; } else { /* CDATA section content, or element text. */ @@ -1602,7 +1645,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, if (!xml) goto fail; xml->name = qn; - if (pn->pn_type == TOK_XMLSPACE) + if (pn->isKind(PNK_XMLSPACE)) xml->xml_flags |= XMLF_WHITESPACE_TEXT; xml->xml_value = str; break; @@ -1636,9 +1679,9 @@ GetXMLSetting(JSContext *cx, const char *name, jsval *vp) { jsval v; - if (!js_FindClassObject(cx, NULL, JSProto_XML, Valueify(&v))) + if (!js_FindClassObject(cx, NULL, JSProto_XML, &v)) return JS_FALSE; - if (!VALUE_IS_FUNCTION(cx, v)) { + if (JSVAL_IS_PRIMITIVE(v) || !JSVAL_TO_OBJECT(v)->isFunction()) { *vp = JSVAL_VOID; return JS_TRUE; } @@ -1654,7 +1697,7 @@ GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) } static JSBool -GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) +GetUint32XMLSetting(JSContext *cx, const char *name, uint32_t *uip) { jsval v; @@ -1680,6 +1723,14 @@ GetXMLSettingFlags(JSContext *cx, uintN *flagsp) return true; } +static JSObject * +GetCurrentScopeChain(JSContext *cx) +{ + if (cx->hasfp()) + return &cx->fp()->scopeChain(); + return JS_ObjectToInnerObject(cx, cx->globalObject); +} + static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { @@ -1716,13 +1767,12 @@ ParseXMLSource(JSContext *cx, JSString *src) return NULL; dstlen = length; - js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); + InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); offset = dstlen; js_strncpy(chars + offset, uri->chars(), urilen); offset += urilen; dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, - &dstlen); + InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); offset += dstlen; srcp = src->getChars(cx); if (!srcp) { @@ -1732,23 +1782,18 @@ ParseXMLSource(JSContext *cx, JSString *src) js_strncpy(chars + offset, srcp, srclen); offset += srclen; dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, - &dstlen); + InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen); chars [offset + dstlen] = 0; - LeaveTrace(cx); xml = NULL; - FrameRegsIter i(cx); - for (; !i.done() && !i.pc(); ++i) - JS_ASSERT(!i.fp()->isScriptFrame()); filename = NULL; lineno = 1; + FrameRegsIter i(cx); if (!i.done()) { - StackFrame *fp = i.fp(); op = (JSOp) *i.pc(); if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { - filename = fp->script()->filename; - lineno = js_FramePCToLineNumber(cx, fp); + filename = i.fp()->script()->filename; + lineno = js_PCToLineNumber(cx, i.fp()->script(), i.pc()); for (endp = srcp + srclen; srcp < endp; srcp++) { if (*srcp == '\n') --lineno; @@ -1759,12 +1804,13 @@ ParseXMLSource(JSContext *cx, JSString *src) { Parser parser(cx); if (parser.init(chars, length, filename, lineno, cx->findVersion())) { - JSObject *scopeChain = GetScopeChain(cx); + JSObject *scopeChain = GetCurrentScopeChain(cx); if (!scopeChain) { cx->free_(chars); return NULL; } - JSParseNode *pn = parser.parseXMLText(scopeChain, false); + + ParseNode *pn = parser.parseXMLText(scopeChain, false); uintN flags; if (pn && GetXMLSettingFlags(cx, &flags)) { AutoNamespaceArray namespaces(cx); @@ -1800,7 +1846,7 @@ ParseXMLSource(JSContext *cx, JSString *src) * undeclared namespaces associated with x not belonging to ancestorNS. */ static JSXML * -OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) +OrphanXMLChild(JSContext *cx, JSXML *xml, uint32_t i) { JSObject *ns; @@ -1824,7 +1870,7 @@ ToXML(JSContext *cx, jsval v) JSXML *xml; Class *clasp; JSString *str; - uint32 length; + uint32_t length; if (JSVAL_IS_PRIMITIVE(v)) { if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) @@ -1850,14 +1896,14 @@ ToXML(JSContext *cx, jsval v) JS_ASSERT(0); } - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { + if (clasp != &StringClass && + clasp != &NumberClass && + clasp != &BooleanClass) { goto bad; } } - str = js_ValueToString(cx, Valueify(v)); + str = ToString(cx, v); if (!str) return NULL; if (str->empty()) { @@ -1891,7 +1937,7 @@ ToXML(JSContext *cx, jsval v) bad: js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION, - JSDVG_IGNORE_STACK, Valueify(v), NULL); + JSDVG_IGNORE_STACK, v, NULL); return NULL; } @@ -1905,7 +1951,7 @@ ToXMLList(JSContext *cx, jsval v) JSXML *xml, *list, *kid; Class *clasp; JSString *str; - uint32 i, length; + uint32_t i, length; if (JSVAL_IS_PRIMITIVE(v)) { if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) @@ -1931,14 +1977,14 @@ ToXMLList(JSContext *cx, jsval v) JS_ASSERT(0); } - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { + if (clasp != &StringClass && + clasp != &NumberClass && + clasp != &BooleanClass) { goto bad; } } - str = js_ValueToString(cx, Valueify(v)); + str = ToString(cx, v); if (!str) return NULL; if (str->empty()) { @@ -1973,7 +2019,7 @@ ToXMLList(JSContext *cx, jsval v) bad: js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION, - JSDVG_IGNORE_STACK, Valueify(v), NULL); + JSDVG_IGNORE_STACK, v, NULL); return NULL; } @@ -2039,6 +2085,74 @@ MakeXMLPIString(JSContext *cx, StringBuffer &sb, JSString *name, pi_suffix_ucNstr, 2); } +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * + * This function appends the output into the supplied string buffer. + */ +static bool +EscapeAttributeValueBuffer(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote) +{ + size_t length = str->length(); + const jschar *start = str->getChars(cx); + if (!start) + return false; + + if (quote && !sb.append('"')) + return false; + + for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { + jschar c = *cp; + switch (c) { + case '"': + if (!sb.append(js_quot_entity_str)) + return false; + break; + case '<': + if (!sb.append(js_lt_entity_str)) + return false; + break; + case '&': + if (!sb.append(js_amp_entity_str)) + return false; + break; + case '\n': + if (!sb.append(" ")) + return false; + break; + case '\r': + if (!sb.append(" ")) + return false; + break; + case '\t': + if (!sb.append(" ")) + return false; + break; + default: + if (!sb.append(c)) + return false; + } + } + + if (quote && !sb.append('"')) + return false; + + return true; +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * + * This function mutates sb, leaving it empty. + */ +static JSFlatString * +EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote) +{ + if (!EscapeAttributeValueBuffer(cx, sb, str, quote)) + return NULL; + return sb.finishString(); +} + /* * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends * equals, a double quote, an attribute value, and a closing double quote. @@ -2048,8 +2162,7 @@ AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr) { if (!sb.append('=')) return false; - valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE); - return valstr && sb.append(valstr); + return EscapeAttributeValueBuffer(cx, sb, valstr, JS_TRUE); } /* @@ -2058,7 +2171,7 @@ AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr) * These functions mutate sb, leaving it empty. */ static JSFlatString * -EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32 toSourceFlag) +EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32_t toSourceFlag) { size_t length = str->length(); const jschar *start = str->getChars(cx); @@ -2099,68 +2212,13 @@ EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32 toSour return sb.finishString(); } -/* - * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. - * - * These functions mutate sb, leaving it empty. - */ -static JSFlatString * -EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote) -{ - size_t length = str->length(); - const jschar *start = str->getChars(cx); - if (!start) - return NULL; - - if (quote && !sb.append('"')) - return NULL; - - for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { - jschar c = *cp; - switch (c) { - case '"': - if (!sb.append(js_quot_entity_str)) - return NULL; - break; - case '<': - if (!sb.append(js_lt_entity_str)) - return NULL; - break; - case '&': - if (!sb.append(js_amp_entity_str)) - return NULL; - break; - case '\n': - if (!sb.append(" ")) - return NULL; - break; - case '\r': - if (!sb.append(" ")) - return NULL; - break; - case '\t': - if (!sb.append(" ")) - return NULL; - break; - default: - if (!sb.append(c)) - return NULL; - } - } - - if (quote && !sb.append('"')) - return NULL; - - return sb.finishString(); -} - /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ static JSObject * -GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) +GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) { JSLinearString *uri, *prefix, *nsprefix; JSObject *match, *ns; - uint32 i, n; + uint32_t i, n; jsval argv[2]; uri = qn->getNameURI(); @@ -2228,8 +2286,7 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) if (!match) { argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID; argv[1] = STRING_TO_JSVAL(uri); - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, - 2, Valueify(argv)); + ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 2, argv); if (!ns) return NULL; match = ns; @@ -2238,11 +2295,11 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) } static JSLinearString * -GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls) +GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls) { const jschar *cp, *start, *end; size_t length, newlength, offset; - uint32 i, n, m, serial; + uint32_t i, n, m, serial; jschar *bp, *dp; JSBool done; JSObject *ns; @@ -2354,10 +2411,8 @@ GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls) } static JSBool -namespace_match(const void *a, const void *b) +namespace_match(const JSObject *nsa, const JSObject *nsb) { - const JSObject *nsa = (const JSObject *) a; - const JSObject *nsb = (const JSObject *) b; JSLinearString *prefixa, *prefixb = nsb->getNamePrefix(); if (prefixb) { @@ -2371,20 +2426,17 @@ namespace_match(const void *a, const void *b) #define TO_SOURCE_FLAG 0x80000000 static JSString * -XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, - uint32 indentLevel) +XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, + uint32_t indentLevel, JSBool pretty) { - JSBool pretty, indentKids; + JSBool indentKids; StringBuffer sb(cx); JSString *str; JSLinearString *prefix, *nsuri; - uint32 i, n, nextIndentLevel; + uint32_t i, n, nextIndentLevel; JSObject *ns, *ns2; AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx); - if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) - return NULL; - if (pretty) { if (!sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG)) return NULL; @@ -2421,15 +2473,15 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, case JSXML_CLASS_LIST: /* ECMA-357 10.2.2. */ { - JSXMLArrayCursor cursor(&xml->xml_kids); + JSXMLArrayCursor cursor(&xml->xml_kids); i = 0; - while (JSXML *kid = (JSXML *) cursor.getNext()) { + while (JSXML *kid = cursor.getNext()) { if (pretty && i != 0) { if (!sb.append('\n')) return NULL; } - JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel, pretty); if (!kidstr || !sb.append(kidstr)) return NULL; ++i; @@ -2448,13 +2500,21 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, return NULL; /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ - if (!ancestorNSes) + if (!ancestorNSes) { + // Ensure a namespace with empty strings exists in the initial array, + // otherwise every call to GetNamespace() when running toString() on + // an XML object with no namespace defined will create a new Namespace + // object on every call. + JSObject *emptyns = NewXMLNamespace(cx, cx->runtime->emptyString, cx->runtime->emptyString, JS_FALSE); + if (!emptyns || !XMLARRAY_APPEND(cx, &empty.array, emptyns)) + goto out; ancestorNSes = &empty.array; + } /* Clone in-scope namespaces not in ancestorNSes into decls. */ { - JSXMLArrayCursor cursor(&xml->xml_namespaces); - while ((ns = (JSObject *) cursor.getNext()) != NULL) { + JSXMLArrayCursor cursor(&xml->xml_namespaces); + while ((ns = cursor.getNext()) != NULL) { if (!IsDeclared(ns)) continue; if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { @@ -2576,8 +2636,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, /* Step 17(b): append attributes. */ { - JSXMLArrayCursor cursor(&xml->xml_attrs); - while (JSXML *attr = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_attrs); + while (JSXML *attr = cursor.getNext()) { if (!sb.append(' ')) goto out; ns2 = GetNamespace(cx, attr->name, &ancdecls.array); @@ -2627,8 +2687,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, /* Step 17(c): append XML namespace declarations. */ { - JSXMLArrayCursor cursor(&decls.array); - while (JSObject *ns3 = (JSObject *) cursor.getNext()) { + JSXMLArrayCursor cursor(&decls.array); + while (JSObject *ns3 = cursor.getNext()) { JS_ASSERT(IsDeclared(ns3)); if (!sb.append(" xmlns")) @@ -2681,14 +2741,14 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } { - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { if (pretty && indentKids) { if (!sb.append('\n')) goto out; } - JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel); + JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel, pretty); if (!kidstr) goto out; @@ -2725,12 +2785,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, /* ECMA-357 10.2 */ static JSString * -ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag) +ToXMLString(JSContext *cx, jsval v, uint32_t toSourceFlag) { - JSObject *obj; - JSString *str; - JSXML *xml; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_CONVERSION, @@ -2739,68 +2795,69 @@ ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag) } if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) - return js_ValueToString(cx, Valueify(v)); + return ToString(cx, v); if (JSVAL_IS_STRING(v)) { StringBuffer sb(cx); return EscapeElementValue(cx, sb, JSVAL_TO_STRING(v), toSourceFlag); } - obj = JSVAL_TO_OBJECT(v); + JSObject *obj = JSVAL_TO_OBJECT(v); if (!obj->isXML()) { - if (!DefaultValue(cx, obj, JSTYPE_STRING, Valueify(&v))) + if (!ToPrimitive(cx, JSTYPE_STRING, &v)) return NULL; - str = js_ValueToString(cx, Valueify(v)); + JSString *str = ToString(cx, v); if (!str) return NULL; StringBuffer sb(cx); return EscapeElementValue(cx, sb, str, toSourceFlag); } + JSBool pretty; + if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) + return NULL; + /* Handle non-element cases in this switch, returning from each case. */ - xml = (JSXML *) obj->getPrivate(); - return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0); + JS::Anchor anch(obj); + JSXML *xml = reinterpret_cast(obj->getPrivate()); + return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0, pretty); } static JSObject * ToAttributeName(JSContext *cx, jsval v) { - JSLinearString *name, *uri, *prefix; + JSLinearString *uri, *prefix; JSObject *obj; Class *clasp; JSObject *qn; + JSAtom *name; if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v)->ensureLinear(cx); - if (!name) + if (!js_ValueToAtom(cx, v, &name)) return NULL; uri = prefix = cx->runtime->emptyString; } else { if (JSVAL_IS_PRIMITIVE(v)) { js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME, - JSDVG_IGNORE_STACK, Valueify(v), NULL); + JSDVG_IGNORE_STACK, v, NULL); return NULL; } obj = JSVAL_TO_OBJECT(v); clasp = obj->getClass(); - if (clasp == &js_AttributeNameClass) + if (clasp == &AttributeNameClass) return obj; - if (clasp == &js_QNameClass) { + if (clasp == &QNameClass) { qn = obj; uri = qn->getNameURI(); prefix = qn->getNamePrefix(); name = qn->getQNameLocalName(); } else { - if (clasp == &js_AnyNameClass) { + if (clasp == &AnyNameClass) { name = cx->runtime->atomState.starAtom; } else { - JSString *str = js_ValueToString(cx, Valueify(v)); - if (!str) - return NULL; - name = str->ensureLinear(cx); - if (!name) + if (!js_ValueToAtom(cx, v, &name)) return NULL; } uri = prefix = cx->runtime->emptyString; @@ -2819,27 +2876,33 @@ ReportBadXMLName(JSContext *cx, const Value &idval) js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL); } -static JSBool -IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp) -{ - JSAtom *atom; - JSLinearString *uri; +namespace js { - atom = cx->runtime->atomState.functionNamespaceURIAtom; - uri = qn->getNameURI(); - if (uri && (uri == atom || EqualStrings(uri, atom))) - return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp); - *funidp = JSID_VOID; - return JS_TRUE; +bool +GetLocalNameFromFunctionQName(JSObject *qn, JSAtom **namep, JSContext *cx) +{ + JSAtom *atom = cx->runtime->atomState.functionNamespaceURIAtom; + JSLinearString *uri = qn->getNameURI(); + if (uri && (uri == atom || EqualStrings(uri, atom))) { + *namep = qn->getQNameLocalName(); + return true; + } + return false; } -JSBool -js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp) +} /* namespace js */ + +bool +js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx) { - if (obj->getClass() == &js_QNameClass) - return IsFunctionQName(cx, obj, funidp); - *funidp = JSID_VOID; - return JS_TRUE; + if (!obj->isQName()) + return false; + JSAtom *name; + if (GetLocalNameFromFunctionQName(obj, &name, cx)) { + *funidp = ATOM_TO_JSID(name); + return true; + } + return false; } static JSObject * @@ -2849,30 +2912,30 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) JSString *name; JSObject *obj; Class *clasp; - uint32 index; + uint32_t index; if (JSVAL_IS_STRING(v)) { name = JSVAL_TO_STRING(v); } else { if (JSVAL_IS_PRIMITIVE(v)) { - ReportBadXMLName(cx, Valueify(v)); + ReportBadXMLName(cx, v); return NULL; } obj = JSVAL_TO_OBJECT(v); clasp = obj->getClass(); - if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass) + if (clasp == &AttributeNameClass || clasp == &QNameClass) goto out; - if (clasp == &js_AnyNameClass) { + if (clasp == &AnyNameClass) { name = cx->runtime->atomState.starAtom; goto construct; } - name = js_ValueToString(cx, Valueify(v)); + name = ToStringSlow(cx, v); if (!name) return NULL; } - atomizedName = js_AtomizeString(cx, name, 0); + atomizedName = js_AtomizeString(cx, name); if (!atomizedName) return NULL; @@ -2886,7 +2949,7 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) * Second, why does ToXMLName applied to the string type throw TypeError * only for numeric literals without any leading or trailing whitespace? * - * If the idea is to reject uint32 property names, then the check needs to + * If the idea is to reject uint32_t property names, then the check needs to * be stricter, to exclude hexadecimal and floating point literals. */ if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index)) @@ -2902,13 +2965,15 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) construct: v = STRING_TO_JSVAL(name); - obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v)); + obj = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &v); if (!obj) return NULL; out: - if (!IsFunctionQName(cx, obj, funidp)) - return NULL; + JSAtom *localName; + *funidp = GetLocalNameFromFunctionQName(obj, &localName, cx) + ? ATOM_TO_JSID(localName) + : JSID_VOID; return obj; bad: @@ -2924,7 +2989,7 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) { JSLinearString *prefix, *prefix2; JSObject *match, *ns2; - uint32 i, n, m; + uint32_t i, n, m; if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; @@ -2978,22 +3043,17 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) static JSBool Append(JSContext *cx, JSXML *list, JSXML *xml) { - uint32 i, j, k, n; - JSXML *kid; - JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); - i = list->xml_kids.length; - n = 1; + + uint32_t i = list->xml_kids.length; if (xml->xml_class == JSXML_CLASS_LIST) { list->xml_target = xml->xml_target; list->xml_targetprop = xml->xml_targetprop; - n = JSXML_LENGTH(xml); - k = i + n; - if (!list->xml_kids.setCapacity(cx, k)) + uint32_t n = JSXML_LENGTH(xml); + if (!list->xml_kids.setCapacity(cx, i + n)) return JS_FALSE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); - if (kid) + for (uint32_t j = 0; j < n; j++) { + if (JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML)) XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); } return JS_TRUE; @@ -3041,10 +3101,10 @@ DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) * (iii) from's owning object must be locked if not thread-local. */ static JSBool -DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, uintN flags) { - uint32 j, n; + uint32_t j, n; JSXML *kid2; JSString *str; @@ -3052,9 +3112,9 @@ DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, if (!to->setCapacity(cx, n)) return JS_FALSE; - JSXMLArrayCursor cursor(from); + JSXMLArrayCursor cursor(from); j = 0; - while (JSXML *kid = (JSXML *) cursor.getNext()) { + while (JSXML *kid = cursor.getNext()) { if ((flags & XSF_IGNORE_COMMENTS) && kid->xml_class == JSXML_CLASS_COMMENT) { continue; @@ -3100,7 +3160,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) JSXML *copy; JSObject *qn; JSBool ok; - uint32 i, n; + uint32_t i, n; JSObject *ns, *ns2; JS_CHECK_RECURSION(cx, return NULL); @@ -3164,7 +3224,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ static void -DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index) +DeleteByIndex(JSContext *cx, JSXML *xml, uint32_t index) { JSXML *kid; @@ -3209,13 +3269,13 @@ MatchElemName(JSObject *nameqn, JSXML *elem) static JSBool DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list) { - uint32 i, n; + uint32_t i, n; JSXML *attr, *kid; JS_CHECK_RECURSION(cx, return JS_FALSE); if (xml->xml_class == JSXML_CLASS_ELEMENT && - nameqn->getClass() == &js_AttributeNameClass) { + nameqn->getClass() == &AttributeNameClass) { for (i = 0, n = xml->xml_attrs.length; i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (attr && MatchAttrName(nameqn, attr)) { @@ -3229,7 +3289,7 @@ DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list) kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!kid) continue; - if (nameqn->getClass() != &js_AttributeNameClass && + if (nameqn->getClass() != &AttributeNameClass && MatchElemName(nameqn, kid)) { if (!Append(cx, list, kid)) return JS_FALSE; @@ -3247,7 +3307,7 @@ Descendants(JSContext *cx, JSXML *xml, jsval id) JSObject *nameqn; JSObject *listobj; JSXML *list, *kid; - uint32 i, n; + uint32_t i, n; JSBool ok; nameqn = ToXMLName(cx, id, &funid); @@ -3295,7 +3355,7 @@ static JSBool XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) { JSObject *qn, *vqn; - uint32 i, j, n; + uint32_t i, j, n; JSXML *kid, *vkid, *attr, *vattr; JSObject *xobj, *vobj; @@ -3328,17 +3388,19 @@ XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) return JS_TRUE; if (JSXML_HAS_VALUE(xml)) { - if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp)) + bool equal; + if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, &equal)) return JS_FALSE; + *bp = equal; } else if (xml->xml_kids.length != vxml->xml_kids.length) { *bp = JS_FALSE; } else { { - JSXMLArrayCursor cursor(&xml->xml_kids); - JSXMLArrayCursor vcursor(&vxml->xml_kids); + JSXMLArrayCursor cursor(&xml->xml_kids); + JSXMLArrayCursor vcursor(&vxml->xml_kids); for (;;) { - kid = (JSXML *) cursor.getNext(); - vkid = (JSXML *) vcursor.getNext(); + kid = cursor.getNext(); + vkid = vcursor.getNext(); if (!kid || !vkid) { *bp = !kid && !vkid; break; @@ -3369,8 +3431,10 @@ XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); if (!vattr) continue; - if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp)) + bool equal; + if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, &equal)) return JS_FALSE; + *bp = equal; } } } @@ -3395,7 +3459,7 @@ Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) vobj = js_GetXMLObject(cx, vxml); if (!vobj) return JS_FALSE; - return js_TestXMLEquality(cx, ObjectValue(*vobj), Valueify(v), bp); + return js_TestXMLEquality(cx, ObjectValue(*vobj), v, bp); } if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) *bp = JS_TRUE; @@ -3431,9 +3495,9 @@ CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) /* ECMA-357 9.1.1.11 XML [[Insert]]. */ static JSBool -Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) +Insert(JSContext *cx, JSXML *xml, uint32_t i, jsval v) { - uint32 j, n; + uint32_t j, n; JSXML *vxml, *kid; JSObject *vobj; JSString *str; @@ -3466,7 +3530,7 @@ Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) } } if (!vxml) { - str = js_ValueToString(cx, Valueify(v)); + str = ToString(cx, v); if (!str) return JS_FALSE; @@ -3499,31 +3563,11 @@ Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) return JS_TRUE; } -static JSBool -IndexToId(JSContext *cx, uint32 index, jsid *idp) -{ - JSAtom *atom; - JSString *str; - - if (index <= JSID_INT_MAX) { - *idp = INT_TO_JSID(index); - } else { - str = js_NumberToString(cx, (jsdouble) index); - if (!str) - return JS_FALSE; - atom = js_AtomizeString(cx, str, 0); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - /* ECMA-357 9.1.1.12 XML [[Replace]]. */ static JSBool -Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v) +Replace(JSContext *cx, JSXML *xml, uint32_t i, jsval v) { - uint32 n; + uint32_t n; JSXML *vxml, *kid; JSObject *vobj; JSString *str; @@ -3565,7 +3609,7 @@ Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v) break; default: - str = js_ValueToString(cx, Valueify(v)); + str = ToString(cx, v); if (!str) return JS_FALSE; @@ -3594,8 +3638,8 @@ static void DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn, JSBool attributes) { - JSXMLArray *array; - uint32 index, deleteCount; + JSXMLArray *array; + uint32_t index, deleteCount; JSXML *kid; JSXMLNameMatcher matcher; @@ -3633,10 +3677,10 @@ DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn, /* ECMA-357 9.2.1.3 index case. */ static void -DeleteListElement(JSContext *cx, JSXML *xml, uint32 index) +DeleteListElement(JSContext *cx, JSXML *xml, uint32_t index) { JSXML *kid, *parent; - uint32 kidIndex; + uint32_t kidIndex; JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST); @@ -3652,7 +3696,7 @@ DeleteListElement(JSContext *cx, JSXML *xml, uint32 index) DeleteNamedProperty(cx, parent, kid->name, JS_TRUE); } else { kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, - NULL); + pointer_match); JS_ASSERT(kidIndex != XML_NOT_FOUND); DeleteByIndex(cx, parent, kidIndex); } @@ -3665,8 +3709,8 @@ DeleteListElement(JSContext *cx, JSXML *xml, uint32 index) static JSBool SyncInScopeNamespaces(JSContext *cx, JSXML *xml) { - JSXMLArray *nsarray; - uint32 i, n; + JSXMLArray *nsarray; + uint32_t i, n; JSObject *ns; nsarray = &xml->xml_namespaces; @@ -3685,20 +3729,20 @@ SyncInScopeNamespaces(JSContext *cx, JSXML *xml) static JSBool GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list) { - JSXMLArray *array; + JSXMLArray *array; JSXMLNameMatcher matcher; JSBool attrs; if (xml->xml_class == JSXML_CLASS_LIST) { - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { if (kid->xml_class == JSXML_CLASS_ELEMENT && !GetNamedProperty(cx, kid, nameqn, list)) { return JS_FALSE; } } } else if (xml->xml_class == JSXML_CLASS_ELEMENT) { - attrs = (nameqn->getClass() == &js_AttributeNameClass); + attrs = (nameqn->getClass() == &AttributeNameClass); if (attrs) { array = &xml->xml_attrs; matcher = MatchAttrName; @@ -3707,8 +3751,8 @@ GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list) matcher = MatchElemName; } - JSXMLArrayCursor cursor(array); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(array); + while (JSXML *kid = cursor.getNext()) { if (matcher(nameqn, kid)) { if (!attrs && kid->xml_class == JSXML_CLASS_ELEMENT && @@ -3729,7 +3773,7 @@ static JSBool GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSXML *xml, *list, *kid; - uint32 index; + uint32_t index; JSObject *kidobj, *listobj; JSObject *nameqn; jsid funid; @@ -3779,7 +3823,7 @@ GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return GetXMLFunction(cx, obj, funid, vp); jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL }; - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots)); + AutoArrayRooter tvr(cx, ArrayLength(roots), roots); listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) @@ -3821,7 +3865,7 @@ CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) static JSString * -KidToString(JSContext *cx, JSXML *xml, uint32 index) +KidToString(JSContext *cx, JSXML *xml, uint32_t index) { JSXML *kid; JSObject *kidobj; @@ -3832,7 +3876,7 @@ KidToString(JSContext *cx, JSXML *xml, uint32 index) kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return NULL; - return js_ValueToString(cx, ObjectValue(*kidobj)); + return ToString(cx, ObjectValue(*kidobj)); } /* Forward declared -- its implementation uses other statics that call it. */ @@ -3848,7 +3892,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; JSObject *targetprop, *nameqn, *attrqn; - uint32 index, i, j, k, n, q, matchIndex; + uint32_t index, i, j, k, n, q, matchIndex; jsval attrval, nsval; jsid funid; JSObject *ns; @@ -3880,7 +3924,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); roots[ID_ROOT] = IdToJsval(id); roots[VAL_ROOT] = *vp; - AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots)); + AutoArrayRooter tvr(cx, ArrayLength(roots), roots); if (js_IdIsIndex(id, &index)) { if (xml->xml_class != JSXML_CLASS_LIST) { @@ -3949,7 +3993,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) goto bad; } else { nameobj = targetprop; - if (nameobj->getClass() == &js_AttributeNameClass) { + if (nameobj->getClass() == &AttributeNameClass) { /* * 2(c)(iii)(1-3). * Note that rxml can't be null here, because target @@ -4045,7 +4089,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) parent = kid->parent; if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameobj = kid->name; - if (nameobj->getClass() != &js_AttributeNameClass) { + if (nameobj->getClass() != &AttributeNameClass) { nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(), nameobj->getQNameLocalName()); if (!nameobj) @@ -4094,7 +4138,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) JS_ASSERT(parent != xml); if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, pointer_match); JS_ASSERT(q != XML_NOT_FOUND); ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj)); if (!ok) @@ -4131,7 +4175,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) /* 2(g). */ else if (vxml || JSXML_HAS_VALUE(kid)) { if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, pointer_match); JS_ASSERT(q != XML_NOT_FOUND); ok = Replace(cx, parent, q, *vp); if (!ok) @@ -4182,7 +4226,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) if (!nameqn) goto bad; if (!JSID_IS_VOID(funid)) { - ok = js_SetProperty(cx, obj, funid, Valueify(vp), false); + ok = js_SetPropertyHelper(cx, obj, funid, 0, vp, false); goto out; } nameobj = nameqn; @@ -4252,7 +4296,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) if (!ok) goto out; - if (nameobj->getClass() == &js_AttributeNameClass) { + if (nameobj->getClass() == &AttributeNameClass) { /* 7(a). */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) goto out; @@ -4442,9 +4486,9 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) /* 14. */ if (primitiveAssign) { - JSXMLArrayCursor cursor(&xml->xml_kids); + JSXMLArrayCursor cursor(&xml->xml_kids); cursor.index = matchIndex; - kid = (JSXML *) cursor.getCurrent(); + kid = cursor.getCurrent(); if (JSXML_HAS_KIDS(kid)) { kid->xml_kids.finish(cx); kid->xml_kids.init(); @@ -4457,7 +4501,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); if (ok && !JSVAL_TO_STRING(*vp)->empty()) { roots[VAL_ROOT] = *vp; - if ((JSXML *) cursor.getCurrent() == kid) + if (cursor.getCurrent() == kid) ok = Replace(cx, kid, 0, *vp); } } @@ -4505,7 +4549,7 @@ ResolveValue(JSContext *cx, JSXML *list, JSXML **result) return JS_TRUE; } - if (targetprop->getClass() == &js_AttributeNameClass) { + if (targetprop->getClass() == &AttributeNameClass) { *result = NULL; return JS_TRUE; } @@ -4545,14 +4589,14 @@ static JSBool HasNamedProperty(JSXML *xml, JSObject *nameqn) { JSBool found; - JSXMLArray *array; + JSXMLArray *array; JSXMLNameMatcher matcher; - uint32 i, n; + uint32_t i, n; if (xml->xml_class == JSXML_CLASS_LIST) { found = JS_FALSE; - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { found = HasNamedProperty(kid, nameqn); if (found) break; @@ -4561,7 +4605,7 @@ HasNamedProperty(JSXML *xml, JSObject *nameqn) } if (xml->xml_class == JSXML_CLASS_ELEMENT) { - if (nameqn->getClass() == &js_AttributeNameClass) { + if (nameqn->getClass() == &AttributeNameClass) { array = &xml->xml_attrs; matcher = MatchAttrName; } else { @@ -4579,7 +4623,7 @@ HasNamedProperty(JSXML *xml, JSObject *nameqn) } static JSBool -HasIndexedProperty(JSXML *xml, uint32 i) +HasIndexedProperty(JSXML *xml, uint32_t i) { if (xml->xml_class == JSXML_CLASS_LIST) return i < JSXML_LENGTH(xml); @@ -4600,24 +4644,22 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found) JSProperty *prop; JSXML *xml; - JS_ASSERT(obj->getClass() == &js_XMLClass); + JS_ASSERT(obj->getClass() == &XMLClass); if (!js_LookupProperty(cx, obj, funid, &pobj, &prop)) return false; if (!prop) { xml = (JSXML *) obj->getPrivate(); if (HasSimpleContent(xml)) { - AutoObjectRooter tvr(cx); - /* * Search in String.prototype to set found whenever * GetXMLFunction returns existing function. */ - if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr())) + JSObject *proto = obj->global().getOrCreateStringPrototype(cx); + if (!proto) return false; - JS_ASSERT(tvr.object()); - if (!js_LookupProperty(cx, tvr.object(), funid, &pobj, &prop)) + if (!js_LookupProperty(cx, proto, funid, &pobj, &prop)) return false; } } @@ -4649,7 +4691,7 @@ IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex) if (!str) return false; - *isIndex = js_StringIsIndex(str, indexp); + *isIndex = StringIsArrayIndex(str, indexp); return true; } @@ -4659,7 +4701,7 @@ HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found) { JSXML *xml; bool isIndex; - uint32 i; + uint32_t i; JSObject *qn; jsid funid; @@ -4686,23 +4728,12 @@ HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found) static void xml_finalize(JSContext *cx, JSObject *obj) { - JSXML *xml = (JSXML *) obj->getPrivate(); - if (!xml) - return; - if (xml->object == obj) - xml->object = NULL; -} - -static void -xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) -{ - MarkXMLRange(trc, len, vec, "xml_vector"); } /* - * XML objects are native. Thus xml_lookupProperty must return a valid + * XML objects are native. Thus xml_lookupGeneric must return a valid * Shape pointer parameter via *propp to signify "property found". Since the - * only call to xml_lookupProperty is via JSObject::lookupProperty, and then + * only call to xml_lookupGeneric is via JSObject::lookupGeneric, and then * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from * JSOP_IN case in the interpreter, the only time we add a Shape here is when * an unqualified name is being accessed or when "name in xml" is called. @@ -4713,7 +4744,7 @@ xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) * NB: xml_deleteProperty must take care to remove any property added here. * * FIXME This clashes with the function namespace implementation which also - * uses native properties. Effectively after xml_lookupProperty any property + * uses native properties. Effectively after xml_lookupGeneric any property * stored previously using assignments to xml.function::name will be removed. * We partially workaround the problem in GetXMLFunction. There we take * advantage of the fact that typically function:: is used to access the @@ -4723,12 +4754,11 @@ xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) * For a proper solution see bug 355257. */ static JSBool -xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) +xml_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { JSBool found; JSXML *xml; - uint32 i; + uint32_t i; JSObject *qn; jsid funid; @@ -4746,24 +4776,63 @@ xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, if (!found) { *objp = NULL; *propp = NULL; - } else { - const Shape *shape = - js_AddNativeProperty(cx, obj, id, - Valueify(GetProperty), Valueify(PutProperty), - SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, - 0, 0); - if (!shape) - return JS_FALSE; - - *objp = obj; - *propp = (JSProperty *) shape; + } else { + const Shape *shape = + js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!shape) + return JS_FALSE; + + *objp = obj; + *propp = (JSProperty *) shape; + } + return JS_TRUE; +} + +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, + JSProperty **propp) +{ + return xml_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp); +} + +static JSBool +xml_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, + JSProperty **propp) +{ + JSXML *xml = reinterpret_cast(obj->getPrivate()); + if (!HasIndexedProperty(xml, index)) { + *objp = NULL; + *propp = NULL; + return true; } - return JS_TRUE; + + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + + const Shape *shape = + js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!shape) + return false; + + *objp = obj; + *propp = (JSProperty *) shape; + return true; } static JSBool -xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v, - PropertyOp getter, StrictPropertyOp setter, uintN attrs) +xml_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp) +{ + return xml_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp); +} + +static JSBool +xml_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) { if (IsFunctionObject(*v) || getter || setter || (attrs & JSPROP_ENUMERATE) == 0 || @@ -4771,29 +4840,95 @@ xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v, return js_DefineProperty(cx, obj, id, v, getter, setter, attrs); } - jsval tmp = Jsvalify(*v); + jsval tmp = *v; return PutProperty(cx, obj, id, false, &tmp); } static JSBool -xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) +xml_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return xml_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs); +} + +static JSBool +xml_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return xml_defineGeneric(cx, obj, id, v, getter, setter, attrs); +} + +static JSBool +xml_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v, + PropertyOp getter, StrictPropertyOp setter, uintN attrs) +{ + return xml_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs); +} + +static JSBool +xml_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) { vp->setUndefined(); return JS_TRUE; } - return GetProperty(cx, obj, id, Jsvalify(vp)); + return GetProperty(cx, obj, id, vp); +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp) +{ + return xml_getGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp); +} + +static JSBool +xml_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return xml_getGeneric(cx, obj, receiver, id, vp); +} + +static JSBool +xml_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp) +{ + return xml_getGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp); +} + +static JSBool +xml_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +{ + return PutProperty(cx, obj, id, strict, vp); +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) +{ + return xml_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); +} + +static JSBool +xml_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return xml_setGeneric(cx, obj, id, vp, strict); } static JSBool -xml_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +xml_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict) { - return PutProperty(cx, obj, id, strict, Jsvalify(vp)); + return xml_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict); } static JSBool -xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +xml_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { JSBool found; if (!HasProperty(cx, obj, IdToJsval(id), &found)) @@ -4804,7 +4939,28 @@ xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) } static JSBool -xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) +xml_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) +{ + return xml_getGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp); +} + +static JSBool +xml_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return xml_getGenericAttributes(cx, obj, id, attrsp); +} + +static JSBool +xml_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + return xml_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp); +} + +static JSBool +xml_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { JSBool found; if (!HasProperty(cx, obj, IdToJsval(id), &found)) @@ -4819,16 +4975,35 @@ xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) } static JSBool -xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) +xml_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp) { - JSXML *xml; - jsval idval; - uint32 index; + return xml_setGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp); +} + +static JSBool +xml_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp) +{ + jsid id; + if (!IndexToId(cx, index, &id)) + return false; + return xml_setGenericAttributes(cx, obj, id, attrsp); +} + +static JSBool +xml_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp) +{ + return xml_setGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp); +} + +static JSBool +xml_deleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict) +{ + uint32_t index; JSObject *nameqn; jsid funid; - idval = IdToJsval(id); - xml = (JSXML *) obj->getPrivate(); + Value idval = IdToValue(id); + JSXML *xml = (JSXML *) obj->getPrivate(); if (js_IdIsIndex(id, &index)) { if (xml->xml_class != JSXML_CLASS_LIST) { /* See NOTE in spec: this variation is reserved for future use. */ @@ -4843,30 +5018,76 @@ xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool st if (!nameqn) return false; if (!JSID_IS_VOID(funid)) - return js_DeleteProperty(cx, obj, funid, rval, false); + return js_DeleteGeneric(cx, obj, funid, rval, false); DeleteNamedProperty(cx, xml, nameqn, - nameqn->getClass() == &js_AttributeNameClass); + nameqn->getClass() == &AttributeNameClass); + } + + /* + * If this object has its own (mutable) scope, then we may have added a + * property to the scope in xml_lookupGeneric for it to return to mean + * "found" and to provide a handle for access operations to call the + * property's getter or setter. But now it's time to remove any such + * property, to purge the property cache and remove the scope entry. + */ + if (!obj->nativeEmpty() && !js_DeleteGeneric(cx, obj, id, rval, false)) + return false; + + rval->setBoolean(true); + return true; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict) +{ + return xml_deleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict); +} + +static JSBool +xml_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) +{ + JSXML *xml = reinterpret_cast(obj->getPrivate()); + if (xml->xml_class != JSXML_CLASS_LIST) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, DoubleValue(index)); + return false; } + /* ECMA-357 9.2.1.3. */ + DeleteListElement(cx, xml, index); + /* * If this object has its own (mutable) scope, then we may have added a - * property to the scope in xml_lookupProperty for it to return to mean + * property to the scope in xml_lookupGeneric for it to return to mean * "found" and to provide a handle for access operations to call the * property's getter or setter. But now it's time to remove any such * property, to purge the property cache and remove the scope entry. */ - if (!obj->nativeEmpty() && !js_DeleteProperty(cx, obj, id, rval, false)) + if (!obj->nativeEmpty() && !js_DeleteElement(cx, obj, index, rval, false)) return false; rval->setBoolean(true); return true; } +static JSBool +xml_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) +{ + return xml_deleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict); +} + +static JSString * +xml_toString_helper(JSContext *cx, JSXML *xml); + JSBool -xml_convert(JSContext *cx, JSObject *obj, JSType type, Value *rval) +xml_convert(JSContext *cx, JSObject *obj, JSType hint, Value *rval) { - JSString *str = js_ValueToString(cx, ObjectValue(*obj)); + JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); + JS_ASSERT(obj->isXML()); + + JS::Anchor anch(obj); + JSString *str = xml_toString_helper(cx, reinterpret_cast(obj->getPrivate())); if (!str) return false; *rval = StringValue(str); @@ -4877,8 +5098,8 @@ static JSBool xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp) { JSXML *xml; - uint32 length, index; - JSXMLArrayCursor *cursor; + uint32_t length, index; + JSXMLArrayCursor *cursor; xml = (JSXML *)obj->getPrivate(); length = JSXML_LENGTH(xml); @@ -4889,7 +5110,7 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, if (length == 0) { statep->setInt32(0); } else { - cursor = cx->new_(&xml->xml_kids); + cursor = cx->new_< JSXMLArrayCursor >(&xml->xml_kids); if (!cursor) return JS_FALSE; statep->setPrivate(cursor); @@ -4903,7 +5124,7 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, statep->setNull(); break; } - cursor = (JSXMLArrayCursor *) statep->toPrivate(); + cursor = (JSXMLArrayCursor *) statep->toPrivate(); if (cursor && cursor->array && (index = cursor->index) < length) { *idp = INT_TO_JSID(index); cursor->index = index + 1; @@ -4913,7 +5134,7 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, case JSENUMERATE_DESTROY: if (!statep->isInt32(0)) { - cursor = (JSXMLArrayCursor *) statep->toPrivate(); + cursor = (JSXMLArrayCursor *) statep->toPrivate(); if (cursor) cx->delete_(cursor); } @@ -4939,8 +5160,12 @@ static void xml_trace(JSTracer *trc, JSObject *obj) { JSXML *xml = (JSXML *) obj->getPrivate(); + /* + * This is safe to leave Unbarriered for incremental GC, but we'll need + * to fix somehow for generational. + */ if (xml) - JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private"); + MarkXMLUnbarriered(trc, xml, "private"); } static JSBool @@ -4961,7 +5186,7 @@ HasSimpleContent(JSXML *xml) { JSXML *kid; JSBool simple; - uint32 i, n; + uint32_t i, n; again: switch (xml->xml_class) { @@ -5000,21 +5225,15 @@ js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp) { JS_ASSERT(obj->isXML()); - if (JSID_IS_OBJECT(id)) { - jsid funid; - - if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid)) - return JS_FALSE; - if (!JSID_IS_VOID(funid)) - id = funid; - } + if (JSID_IS_OBJECT(id)) + js_GetLocalNameFromFunctionQName(JSID_TO_OBJECT(id), &id, cx); /* * As our callers have a bad habit of passing a pointer to an unrooted * local value as vp, we use a proper root here. */ AutoValueRooter tvr(cx); - JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr())); + JSBool ok = GetXMLFunction(cx, obj, id, tvr.addr()); *vp = tvr.value(); return ok; } @@ -5032,9 +5251,9 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) jsval v; if (v1.isObject() && v1.toObject().isXML()) { obj = &v1.toObject(); - v = Jsvalify(v2); + v = v2; } else { - v = Jsvalify(v1); + v = v1; obj = &v2.toObject(); } @@ -5062,10 +5281,13 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) HasSimpleContent(xml))) { ok = js_EnterLocalRootScope(cx); if (ok) { - ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && - (vstr = js_ValueToString(cx, Valueify(v))); - if (ok) - ok = EqualStrings(cx, str, vstr, bp); + ok = (str = ToStringSlow(cx, ObjectValue(*obj))) && + (vstr = ToString(cx, v)); + if (ok) { + bool equal; + ok = EqualStrings(cx, str, vstr, &equal); + *bp = equal; + } js_LeaveLocalRootScope(cx); } } else { @@ -5076,22 +5298,28 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) ok = js_EnterLocalRootScope(cx); if (ok) { if (HasSimpleContent(xml)) { - ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && - (vstr = js_ValueToString(cx, Valueify(v))); - if (ok) - ok = EqualStrings(cx, str, vstr, bp); + ok = (str = ToString(cx, ObjectValue(*obj))) && + (vstr = ToString(cx, v)); + if (ok) { + bool equal; + ok = EqualStrings(cx, str, vstr, &equal); + *bp = equal; + } } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { - str = js_ValueToString(cx, ObjectValue(*obj)); + str = ToString(cx, ObjectValue(*obj)); if (!str) { ok = JS_FALSE; } else if (JSVAL_IS_STRING(v)) { - ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp); + bool equal; + ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), &equal); + if (ok) + *bp = equal; } else { ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); if (ok) { d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) : JSVAL_TO_DOUBLE(v); - *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); + *bp = (d == d2); } } } else { @@ -5139,16 +5367,16 @@ js_ConcatenateXML(JSContext *cx, JSObject *obj, JSObject *robj, Value *vp) return ok; } -JS_FRIEND_DATA(Class) js_XMLClass = { +JS_FRIEND_DATA(Class) js::XMLClass = { js_XML_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, xml_convert, xml_finalize, NULL, /* reserved0 */ @@ -5160,13 +5388,34 @@ JS_FRIEND_DATA(Class) js_XMLClass = { xml_trace, JS_NULL_CLASS_EXT, { + xml_lookupGeneric, xml_lookupProperty, + xml_lookupElement, + xml_lookupSpecial, + xml_defineGeneric, xml_defineProperty, + xml_defineElement, + xml_defineSpecial, + xml_getGeneric, xml_getProperty, + xml_getElement, + NULL, /* getElementIfPresent */ + xml_getSpecial, + xml_setGeneric, xml_setProperty, - xml_getAttributes, - xml_setAttributes, + xml_setElement, + xml_setSpecial, + xml_getGenericAttributes, + xml_getPropertyAttributes, + xml_getElementAttributes, + xml_getSpecialAttributes, + xml_setGenericAttributes, + xml_setPropertyAttributes, + xml_setElementAttributes, + xml_setSpecialAttributes, xml_deleteProperty, + xml_deleteElement, + xml_deleteSpecial, xml_enumerate, xml_typeOf, xml_fix, @@ -5182,13 +5431,14 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) JSFunction *fun; char numBuf[12]; - JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp)); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); + JS_ASSERT(JSVAL_TO_OBJECT(*vp)->isFunction()); - *objp = ToObject(cx, Valueify(&vp[1])); + *objp = ToObject(cx, &vp[1]); if (!*objp) return NULL; if (!(*objp)->isXML()) { - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass); return NULL; } xml = (JSXML *) (*objp)->getPrivate(); @@ -5206,7 +5456,7 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) } } - fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp)); + fun = JSVAL_TO_OBJECT(*vp)->toFunction(); JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); JSAutoByteString funNameBytes; if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) { @@ -5218,11 +5468,11 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */ #define XML_METHOD_PROLOG \ - JSObject *obj = ToObject(cx, Valueify(&vp[1])); \ + JSObject *obj = ToObject(cx, &vp[1]); \ if (!obj) \ return JS_FALSE; \ if (!obj->isXML()) { \ - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); \ + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass); \ return JS_FALSE; \ } \ JSXML *xml = (JSXML *)obj->getPrivate(); \ @@ -5248,7 +5498,7 @@ xml_addNamespace(JSContext *cx, uintN argc, jsval *vp) if (!xml) return JS_FALSE; - if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp)) + if (!NamespaceHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp)) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); @@ -5305,7 +5555,7 @@ xml_attribute(JSContext *cx, uintN argc, jsval *vp) JSObject *qn; if (argc == 0) { - js_ReportMissingArg(cx, Valueify(*vp), 0); + js_ReportMissingArg(cx, *vp, 0); return JS_FALSE; } @@ -5315,7 +5565,7 @@ xml_attribute(JSContext *cx, uintN argc, jsval *vp) vp[2] = OBJECT_TO_JSVAL(qn); /* local root */ jsid id = OBJECT_TO_JSID(qn); - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; return GetProperty(cx, obj, id, vp); @@ -5330,9 +5580,8 @@ xml_attributes(JSContext *cx, uintN argc, jsval *vp) if (!qn) return JS_FALSE; - AutoObjectRooter tvr(cx, qn); jsid id = OBJECT_TO_JSID(qn); - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; return GetProperty(cx, obj, id, vp); @@ -5361,17 +5610,17 @@ ValueToId(JSContext *cx, jsval v, AutoIdRooter *idr) jsint i = JSVAL_TO_INT(v); if (INT_FITS_IN_JSID(i)) *idr->addr() = INT_TO_JSID(i); - else if (!js_ValueToStringId(cx, Valueify(v), idr->addr())) + else if (!js_ValueToStringId(cx, v, idr->addr())) return JS_FALSE; } else if (JSVAL_IS_STRING(v)) { - JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0); + JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v)); if (!atom) return JS_FALSE; *idr->addr() = ATOM_TO_JSID(atom); } else if (!JSVAL_IS_PRIMITIVE(v)) { *idr->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)); } else { - ReportBadXMLName(cx, Valueify(v)); + ReportBadXMLName(cx, v); return JS_FALSE; } return JS_TRUE; @@ -5382,7 +5631,7 @@ xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, jsval *rval) { bool isIndex; - uint32 index; + uint32_t index; JSXML *kid; JSObject *kidobj; @@ -5432,8 +5681,8 @@ xml_child(JSContext *cx, uintN argc, jsval *vp) if (!list) return JS_FALSE; - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return JS_FALSE; @@ -5466,7 +5715,7 @@ static JSBool xml_childIndex(JSContext *cx, uintN argc, jsval *vp) { JSXML *parent; - uint32 i, n; + uint32_t i, n; NON_LIST_XML_METHOD_PROLOG; parent = xml->parent; @@ -5490,7 +5739,7 @@ xml_childIndex(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_children(JSContext *cx, uintN argc, jsval *vp) { - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; jsid name = ATOM_TO_JSID(cx->runtime->atomState.starAtom); @@ -5503,7 +5752,7 @@ xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) { JSXML *list, *kid, *vxml; JSBool ok; - uint32 i, n; + uint32_t i, n; JSObject *kidobj; jsval v; @@ -5528,7 +5777,7 @@ xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) ok = JS_FALSE; v = JSVAL_NULL; } - js_LeaveLocalRootScopeWithResult(cx, Valueify(v)); + js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); @@ -5573,16 +5822,16 @@ xml_contains(JSContext *cx, uintN argc, jsval *vp) value = argc != 0 ? vp[2] : JSVAL_VOID; if (xml->xml_class == JSXML_CLASS_LIST) { eq = JS_FALSE; - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), Valueify(value), &eq)) + if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), value, &eq)) return JS_FALSE; if (eq) break; } } else { - if (!js_TestXMLEquality(cx, ObjectValue(*obj), Valueify(value), &eq)) + if (!js_TestXMLEquality(cx, ObjectValue(*obj), value, &eq)) return JS_FALSE; } *vp = BOOLEAN_TO_JSVAL(eq); @@ -5628,7 +5877,7 @@ xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval v; JSBool ok; JSObject *kidobj; - uint32 i, n; + uint32_t i, n; list = xml_list_helper(cx, xml, vp); if (!list) @@ -5639,8 +5888,8 @@ xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml, if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.6 */ - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) @@ -5652,7 +5901,7 @@ xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml, ok = JS_FALSE; v = JSVAL_NULL; } - js_LeaveLocalRootScopeWithResult(cx, Valueify(v)); + js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); @@ -5705,11 +5954,11 @@ xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) jsval name; JSBool found; - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; if (!obj->isXML()) { - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); + ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass); return JS_FALSE; } @@ -5720,7 +5969,7 @@ xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) *vp = JSVAL_TRUE; return JS_TRUE; } - return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, Valueify(vp)); + return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, vp); } /* XML and XMLList */ @@ -5729,7 +5978,7 @@ xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp) { JSXML *kid; JSObject *kidobj; - uint32 i, n; + uint32_t i, n; XML_METHOD_PROLOG; again: @@ -5779,9 +6028,9 @@ xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp) } static JSBool -FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) +FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) { - uint32 length, i, j, n; + uint32_t length, i, j, n; JSObject *ns, *ns2; JSLinearString *prefix, *prefix2; @@ -5824,7 +6073,7 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) * rval. rval must point to a rooted location. */ static bool -NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval) +NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval) { JSObject *arrayobj = NewDenseEmptyArray(cx); if (!arrayobj) @@ -5832,12 +6081,12 @@ NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval) *rval = OBJECT_TO_JSVAL(arrayobj); AutoValueRooter tvr(cx); - for (uint32 i = 0, n = array->length; i < n; i++) { + for (uint32_t i = 0, n = array->length; i < n; i++) { JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject); if (!ns) continue; tvr.set(ObjectValue(*ns)); - if (!arrayobj->setProperty(cx, INT_TO_JSID(i), tvr.addr(), false)) + if (!arrayobj->setElement(cx, i, tvr.addr(), false)) return false; } return true; @@ -5858,7 +6107,7 @@ xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp) { jsval arg; JSXML *kid; - uint32 i; + uint32_t i; NON_LIST_XML_METHOD_PROLOG; *vp = OBJECT_TO_JSVAL(obj); @@ -5873,7 +6122,7 @@ xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp) if (!VALUE_IS_XML(arg)) return JS_TRUE; kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate(); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, pointer_match); if (i == XML_NOT_FOUND) return JS_TRUE; ++i; @@ -5890,7 +6139,7 @@ xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp) { jsval arg; JSXML *kid; - uint32 i; + uint32_t i; NON_LIST_XML_METHOD_PROLOG; *vp = OBJECT_TO_JSVAL(obj); @@ -5905,7 +6154,7 @@ xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp) if (!VALUE_IS_XML(arg)) return JS_TRUE; kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate(); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, pointer_match); if (i == XML_NOT_FOUND) return JS_TRUE; } @@ -5924,7 +6173,7 @@ xml_length(JSContext *cx, uintN argc, jsval *vp) if (xml->xml_class != JSXML_CLASS_LIST) { *vp = JSVAL_ONE; } else { - uint32 l = xml->xml_kids.length; + uint32_t l = xml->xml_kids.length; if (l <= JSVAL_INT_MAX) *vp = INT_TO_JSVAL(l); else @@ -5965,7 +6214,7 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp) if (argc == 0) { prefix = NULL; } else { - JSString *str = js_ValueToString(cx, Valueify(vp[2])); + JSString *str = ToString(cx, vp[2]); if (!str) return false; prefix = str->ensureLinear(cx); @@ -6012,7 +6261,7 @@ xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp) JSXML *yml = xml; while ((yml = yml->parent) != NULL) { JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); - for (uint32 i = 0, n = yml->xml_namespaces.length; i < n; i++) { + for (uint32_t i = 0, n = yml->xml_namespaces.length; i < n; i++) { JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject); if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { if (!XMLARRAY_APPEND(cx, &ancestors.array, ns)) @@ -6021,7 +6270,7 @@ xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp) } } - for (uint32 i = 0, n = xml->xml_namespaces.length; i < n; i++) { + for (uint32_t i = 0, n = xml->xml_namespaces.length; i < n; i++) { JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); if (!ns) continue; @@ -6063,7 +6312,7 @@ xml_nodeKind(JSContext *cx, uintN argc, jsval *vp) } static void -NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index) +NormalizingDelete(JSContext *cx, JSXML *xml, uint32_t index) { if (xml->xml_class == JSXML_CLASS_LIST) DeleteListElement(cx, xml, index); @@ -6076,7 +6325,7 @@ static JSBool xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml) { JSXML *kid, *kid2; - uint32 i, n; + uint32_t i, n; JSObject *kidobj; JSString *str; @@ -6130,7 +6379,7 @@ static JSBool xml_parent(JSContext *cx, uintN argc, jsval *vp) { JSXML *parent, *kid; - uint32 i, n; + uint32_t i, n; JSObject *parentobj; XML_METHOD_PROLOG; @@ -6173,7 +6422,7 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, JSBool ok; JSObject *kidobj; jsval v; - uint32 i, n; + uint32_t i, n; list = xml_list_helper(cx, xml, vp); if (!list) @@ -6184,8 +6433,8 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) @@ -6198,7 +6447,7 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, ok = JS_FALSE; v = JSVAL_NULL; } - js_LeaveLocalRootScopeWithResult(cx, Valueify(v)); + js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); @@ -6265,7 +6514,7 @@ static JSBool xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) { bool isIndex; - uint32 index; + uint32_t index; XML_METHOD_PROLOG; *vp = JSVAL_FALSE; @@ -6287,10 +6536,8 @@ xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) } static JSBool -namespace_full_match(const void *a, const void *b) +namespace_full_match(const JSObject *nsa, const JSObject *nsb) { - const JSObject *nsa = (const JSObject *) a; - const JSObject *nsb = (const JSObject *) b; JSLinearString *prefixa = nsa->getNamePrefix(); JSLinearString *prefixb; @@ -6306,7 +6553,7 @@ static JSBool xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns) { JSObject *thisns, *attrns; - uint32 i, n; + uint32_t i, n; JSXML *attr, *kid; thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); @@ -6350,7 +6597,7 @@ xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp) if (!xml) return JS_FALSE; - if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp)) + if (!NamespaceHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp)) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); ns = JSVAL_TO_OBJECT(*vp); @@ -6368,7 +6615,7 @@ xml_replace(JSContext *cx, uintN argc, jsval *vp) { jsval value; JSXML *vxml, *kid; - uint32 index, i; + uint32_t index, i; JSObject *nameqn; NON_LIST_XML_METHOD_PROLOG; @@ -6411,7 +6658,7 @@ xml_replace(JSContext *cx, uintN argc, jsval *vp) * Call function QName per spec, not ToXMLName, to avoid attribute * names. */ - if (!QNameHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp)) + if (!QNameHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp)) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); nameqn = JSVAL_TO_OBJECT(*vp); @@ -6459,29 +6706,22 @@ xml_setChildren(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) { - jsval name; - JSObject *nameqn; - JSLinearString *namestr; - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) + if (!JSXML_HAS_NAME(xml)) { + vp[0] = JSVAL_VOID; return JS_TRUE; + } + JSAtom *namestr; if (argc == 0) { namestr = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } else { - name = vp[2]; - if (!JSVAL_IS_PRIMITIVE(name) && - JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) { - nameqn = JSVAL_TO_OBJECT(name); - namestr = nameqn->getQNameLocalName(); + jsval name = vp[2]; + if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->isQName()) { + namestr = JSVAL_TO_OBJECT(name)->getQNameLocalName(); } else { - if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2])) - return JS_FALSE; - name = vp[2]; - namestr = JSVAL_TO_STRING(name)->ensureLinear(cx); - if (!namestr) - return JS_FALSE; + if (!js_ValueToAtom(cx, name, &namestr)) + return false; } } @@ -6490,6 +6730,7 @@ xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; if (namestr) xml->name->setQNameLocalName(namestr); + vp[0] = JSVAL_VOID; return JS_TRUE; } @@ -6499,8 +6740,8 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) jsval name; JSObject *nameqn; JSXML *nsowner; - JSXMLArray *nsarray; - uint32 i, n; + JSXMLArray *nsarray; + uint32_t i, n; JSObject *ns; NON_LIST_XML_METHOD_PROLOG; @@ -6512,13 +6753,13 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) } else { name = vp[2]; if (!JSVAL_IS_PRIMITIVE(name) && - JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass && + JSVAL_TO_OBJECT(name)->getClass() == &QNameClass && !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) { name = vp[2] = nameqn->getQNameLocalNameVal(); } } - nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name)); + nameqn = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &name); if (!nameqn) return JS_FALSE; @@ -6562,8 +6803,10 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; /* XXXbe have to test membership to see whether GetNamespace added */ - if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) + if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, pointer_match)) { + vp[0] = JSVAL_VOID; return JS_TRUE; + } } else { /* * At this point, we know prefix of nameqn is null, so its uri can't @@ -6586,6 +6829,7 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) ns = XMLARRAY_MEMBER(nsarray, i, JSObject); if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) { nameqn->setNamePrefix(ns->getNamePrefix()); + vp[0] = JSVAL_VOID; return JS_TRUE; } } @@ -6601,6 +6845,13 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) return JS_TRUE; } +/* Utility function used within xml_setNamespace */ +static JSBool qn_match(const JSXML *xml, const JSObject *qn) +{ + return qname_identity(xml->name, qn); +} + +/* ECMA-357 13.4.4.36 */ static JSBool xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) { @@ -6613,12 +6864,8 @@ xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) if (!JSXML_HAS_NAME(xml)) return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, - argc == 0 ? 0 : 1, Valueify(vp + 2)); + ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, + argc == 0 ? 0 : 1, vp + 2); if (!ns) return JS_FALSE; vp[0] = OBJECT_TO_JSVAL(ns); @@ -6626,10 +6873,26 @@ xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) qnargv[0] = OBJECT_TO_JSVAL(ns); qnargv[1] = OBJECT_TO_JSVAL(xml->name); - qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv)); + qn = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, qnargv); if (!qn) return JS_FALSE; + /* + * Erratum: setting the namespace of an attribute may cause it to duplicate + * an already-existing attribute. To preserve the invariant that there are + * not multiple attributes with the same name, we delete the existing + * attribute so that the mutated attribute will not be a duplicate. + */ + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE && + xml->parent && xml->parent->xml_class == JSXML_CLASS_ELEMENT && + !qn_match(xml, qn)) + { + JSXMLArray *array = &xml->parent->xml_attrs; + uint32_t i = XMLArrayFindMember(array, qn, qn_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, array, i, JS_TRUE); + } + xml->name = qn; /* @@ -6654,8 +6917,7 @@ static JSBool xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) { JSXML *list, *kid, *vxml; - uint32 i, n; - JSBool ok; + uint32_t i, n; JSObject *kidobj; jsval v; @@ -6664,11 +6926,10 @@ xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) return JS_FALSE; if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); + JSBool ok = js_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); @@ -6678,7 +6939,7 @@ xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) ok = JS_FALSE; v = JSVAL_NULL; } - js_LeaveLocalRootScopeWithResult(cx, Valueify(v)); + js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) return JS_FALSE; vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); @@ -6722,8 +6983,8 @@ xml_toString_helper(JSContext *cx, JSXML *xml) str = cx->runtime->emptyString; if (!js_EnterLocalRootScope(cx)) return NULL; - JSXMLArrayCursor cursor(&xml->xml_kids); - while (JSXML *kid = (JSXML *) cursor.getNext()) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = cursor.getNext()) { if (kid->xml_class != JSXML_CLASS_COMMENT && kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { kidstr = xml_toString_helper(cx, kid); @@ -6743,7 +7004,7 @@ xml_toString_helper(JSContext *cx, JSXML *xml) static JSBool xml_toSource(JSContext *cx, uintN argc, jsval *vp) { - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), TO_SOURCE_FLAG); @@ -6770,7 +7031,7 @@ xml_toString(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_toXMLString(JSContext *cx, uintN argc, jsval *vp) { - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), 0); @@ -6784,7 +7045,7 @@ xml_toXMLString(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_valueOf(JSContext *cx, uintN argc, jsval *vp) { - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); @@ -6884,7 +7145,7 @@ xml_settings(JSContext *cx, uintN argc, jsval *vp) if (!settings) return false; *vp = OBJECT_TO_JSVAL(settings); - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; return CopyXMLSettings(cx, obj, settings); @@ -6897,18 +7158,21 @@ xml_setSettings(JSContext *cx, uintN argc, jsval *vp) jsval v; JSBool ok; - JSObject *obj = ToObject(cx, Valueify(&vp[1])); + JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; v = (argc == 0) ? JSVAL_VOID : vp[2]; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { ok = SetDefaultXMLSettings(cx, obj); } else { - if (JSVAL_IS_PRIMITIVE(v)) + if (JSVAL_IS_PRIMITIVE(v)) { + vp[0] = JSVAL_VOID; return JS_TRUE; + } settings = JSVAL_TO_OBJECT(v); ok = CopyXMLSettings(cx, settings, obj); } + vp[0] = JSVAL_VOID; return ok; } @@ -6938,7 +7202,7 @@ XML(JSContext *cx, uintN argc, Value *vp) JSObject *xobj, *vobj; Class *clasp; - jsval v = argc ? Jsvalify(vp[2]) : JSVAL_VOID; + jsval v = argc ? vp[2] : JSVAL_VOID; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) v = STRING_TO_JSVAL(cx->runtime->emptyString); @@ -6951,7 +7215,7 @@ XML(JSContext *cx, uintN argc, Value *vp) if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); clasp = vobj->getClass(); - if (clasp == &js_XMLClass || + if (clasp == &XMLClass || (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { copy = DeepCopy(cx, xml, NULL, 0); if (!copy) @@ -6976,7 +7240,7 @@ XMLList(JSContext *cx, uintN argc, jsval *vp) if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) v = STRING_TO_JSVAL(cx->runtime->emptyString); - if (IsConstructing(Valueify(vp)) && !JSVAL_IS_PRIMITIVE(v)) { + if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); if (vobj->isXML()) { xml = (JSXML *) vobj->getPrivate(); @@ -7005,7 +7269,7 @@ XMLList(JSContext *cx, uintN argc, jsval *vp) #ifdef DEBUG_notme JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); -uint32 xml_serial; +uint32_t xml_serial; #endif JSXML * @@ -7015,19 +7279,20 @@ js_NewXML(JSContext *cx, JSXMLClass xml_class) if (!xml) return NULL; - xml->object = NULL; + xml->object.init(NULL); xml->domnode = NULL; - xml->parent = NULL; - xml->name = NULL; + xml->parent.init(NULL); + xml->name.init(NULL); xml->xml_class = xml_class; xml->xml_flags = 0; if (JSXML_CLASS_HAS_VALUE(xml_class)) { - xml->xml_value = cx->runtime->emptyString; + xml->xml_value.init(cx->runtime->emptyString); } else { + xml->xml_value.init(NULL); xml->xml_kids.init(); if (xml_class == JSXML_CLASS_LIST) { - xml->xml_target = NULL; - xml->xml_targetprop = NULL; + xml->xml_target.init(NULL); + xml->xml_targetprop.init(NULL); } else { xml->xml_namespaces.init(); xml->xml_attrs.init(); @@ -7038,19 +7303,36 @@ js_NewXML(JSContext *cx, JSXMLClass xml_class) JS_APPEND_LINK(&xml->links, &xml_leaks); xml->serial = xml_serial++; #endif - METER(xml_stats.xml); return xml; } +void +JSXML::writeBarrierPre(JSXML *xml) +{ +#ifdef JSGC_INCREMENTAL + if (!xml) + return; + + JSCompartment *comp = xml->compartment(); + if (comp->needsBarrier()) + MarkXMLUnbarriered(comp->barrierTracer(), xml, "write barrier"); +#endif +} + +void +JSXML::writeBarrierPost(JSXML *xml, void *addr) +{ +} + void js_TraceXML(JSTracer *trc, JSXML *xml) { if (xml->object) - MarkObject(trc, *xml->object, "object"); + MarkObject(trc, xml->object, "object"); if (xml->name) - MarkObject(trc, *xml->name, "name"); + MarkObject(trc, xml->name, "name"); if (xml->parent) - JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent"); + MarkXML(trc, xml->parent, "xml_parent"); if (JSXML_HAS_VALUE(xml)) { if (xml->xml_value) @@ -7058,32 +7340,22 @@ js_TraceXML(JSTracer *trc, JSXML *xml) return; } - xml_trace_vector(trc, - (JSXML **) xml->xml_kids.vector, - xml->xml_kids.length); - XMLArrayCursorTrace(trc, xml->xml_kids.cursors); - if (IS_GC_MARKING_TRACER(trc)) - xml->xml_kids.trim(); + MarkXMLRange(trc, xml->xml_kids.length, xml->xml_kids.vector, "xml_kids"); + js_XMLArrayCursorTrace(trc, xml->xml_kids.cursors); if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_target) - JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target"); + MarkXML(trc, xml->xml_target, "target"); if (xml->xml_targetprop) - MarkObject(trc, *xml->xml_targetprop, "targetprop"); + MarkObject(trc, xml->xml_targetprop, "targetprop"); } else { MarkObjectRange(trc, xml->xml_namespaces.length, - (JSObject **) xml->xml_namespaces.vector, + xml->xml_namespaces.vector, "xml_namespaces"); - XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors); - if (IS_GC_MARKING_TRACER(trc)) - xml->xml_namespaces.trim(); + js_XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors); - xml_trace_vector(trc, - (JSXML **) xml->xml_attrs.vector, - xml->xml_attrs.length); - XMLArrayCursorTrace(trc, xml->xml_attrs.cursors); - if (IS_GC_MARKING_TRACER(trc)) - xml->xml_attrs.trim(); + MarkXMLRange(trc, xml->xml_attrs.length, xml->xml_attrs.vector, "xml_attrs"); + js_XMLArrayCursorTrace(trc, xml->xml_attrs.cursors); } } @@ -7103,11 +7375,11 @@ NewXMLObject(JSContext *cx, JSXML *xml) { JSObject *obj; - obj = NewNonFunction(cx, &js_XMLClass, NULL, NULL); + JSObject *parent = GetGlobalForScopeChain(cx); + obj = NewObjectWithClassProto(cx, &XMLClass, NULL, parent); if (!obj) return NULL; obj->setPrivate(xml); - METER(xml_stats.xmlobj); return obj; } @@ -7132,77 +7404,128 @@ js_GetXMLObject(JSContext *cx, JSXML *xml) JSObject * js_InitNamespaceClass(JSContext *cx, JSObject *obj) { - return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2, - namespace_props, namespace_methods, NULL, NULL); + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *namespaceProto = global->createBlankPrototype(cx, &NamespaceClass); + if (!namespaceProto) + return NULL; + JSFlatString *empty = cx->runtime->emptyString; + namespaceProto->setNamePrefix(empty); + namespaceProto->setNameURI(empty); + + const uintN NAMESPACE_CTOR_LENGTH = 2; + JSFunction *ctor = global->createConstructor(cx, Namespace, &NamespaceClass, + CLASS_ATOM(cx, Namespace), + NAMESPACE_CTOR_LENGTH); + if (!ctor) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, namespaceProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, namespaceProto, namespace_props, namespace_methods)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_Namespace, ctor, namespaceProto)) + return NULL; + + return namespaceProto; } JSObject * js_InitQNameClass(JSContext *cx, JSObject *obj) { - return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, - qname_props, qname_methods, NULL, NULL); + JS_ASSERT(obj->isNative()); + + GlobalObject *global = &obj->asGlobal(); + + JSObject *qnameProto = global->createBlankPrototype(cx, &QNameClass); + if (!qnameProto) + return NULL; + JSAtom *empty = cx->runtime->emptyString; + if (!InitXMLQName(cx, qnameProto, empty, empty, empty)) + return NULL; + + const uintN QNAME_CTOR_LENGTH = 2; + JSFunction *ctor = global->createConstructor(cx, QName, &QNameClass, + CLASS_ATOM(cx, QName), QNAME_CTOR_LENGTH); + if (!ctor) + return NULL; + + if (!LinkConstructorAndPrototype(cx, ctor, qnameProto)) + return NULL; + + if (!DefinePropertiesAndBrand(cx, qnameProto, NULL, qname_methods)) + return NULL; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_QName, ctor, qnameProto)) + return NULL; + + return qnameProto; } JSObject * js_InitXMLClass(JSContext *cx, JSObject *obj) { - JSObject *proto, *pobj; - JSFunction *fun; - JSXML *xml; - JSProperty *prop; - Shape *shape; - jsval cval, vp[3]; + JS_ASSERT(obj->isNative()); - /* Define the isXMLName function. */ - if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + GlobalObject *global = &obj->asGlobal(); + + JSObject *xmlProto = global->createBlankPrototype(cx, &XMLClass); + if (!xmlProto) + return NULL; + JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml) return NULL; + xmlProto->setPrivate(xml); + xml->object = xmlProto; - /* Define the XML class constructor and prototype. */ - proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, - NULL, xml_methods, - xml_static_props, xml_static_methods); - if (!proto) + /* Don't count this as a real content-created XML object. */ + if (!cx->runningWithTrustedPrincipals()) { + JS_ASSERT(sE4XObjectsCreated > 0); + --sE4XObjectsCreated; + } + + const uintN XML_CTOR_LENGTH = 1; + JSFunction *ctor = global->createConstructor(cx, XML, &XMLClass, CLASS_ATOM(cx, XML), + XML_CTOR_LENGTH); + if (!ctor) return NULL; - xml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!xml) + if (!LinkConstructorAndPrototype(cx, ctor, xmlProto)) return NULL; - proto->setPrivate(xml); - xml->object = proto; - METER(xml_stats.xmlobj); - /* - * Prepare to set default settings on the XML constructor we just made. - * NB: We can't use JS_GetConstructor, because it calls - * JSObject::getProperty, which is xml_getProperty, which creates a new - * XMLList every time! We must instead call js_LookupProperty directly. - */ - if (!js_LookupProperty(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &pobj, &prop)) { + if (!DefinePropertiesAndBrand(cx, xmlProto, NULL, xml_methods) || + !DefinePropertiesAndBrand(cx, ctor, xml_static_props, xml_static_methods)) + { return NULL; } - JS_ASSERT(prop); - shape = (Shape *) prop; - cval = Jsvalify(pobj->nativeGetSlot(shape->slot)); - JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); - /* Set default settings. */ - vp[0] = JSVAL_NULL; - vp[1] = cval; - vp[2] = JSVAL_VOID; - if (!xml_setSettings(cx, 1, vp)) + if (!SetDefaultXMLSettings(cx, ctor)) return NULL; - /* Define the XMLList function and give it the same prototype as XML. */ - fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR); - if (!fun) + /* Define the XMLList function, and give it the same .prototype as XML. */ + JSFunction *xmllist = + JS_DefineFunction(cx, global, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR); + if (!xmllist) return NULL; - if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { + if (!xmllist->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom, + ObjectValue(*xmlProto), JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_PERMANENT | JSPROP_READONLY)) + { return NULL; } - return proto; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_XML, ctor, xmlProto)) + return NULL; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + return xmlProto; } JSObject * @@ -7220,7 +7543,7 @@ namespace js { bool GlobalObject::getFunctionNamespace(JSContext *cx, Value *vp) { - Value &v = getSlotRef(FUNCTION_NS); + HeapValue &v = getSlotRef(FUNCTION_NS); if (v.isUndefined()) { JSRuntime *rt = cx->runtime; JSLinearString *prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION]; @@ -7236,9 +7559,10 @@ GlobalObject::getFunctionNamespace(JSContext *cx, Value *vp) * names, its prefix and uri references are copied to the QName. * The parent remains set and links back to global. */ - obj->clearProto(); + if (!obj->clearType(cx)) + return false; - v.setObject(*obj); + v.set(compartment(), ObjectValue(*obj)); } *vp = v; @@ -7268,14 +7592,15 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) JSObject *ns, *obj, *tmp; jsval v; - JSObject *scopeChain = GetScopeChain(cx); + JSObject *scopeChain = GetCurrentScopeChain(cx); + if (!scopeChain) + return false; obj = NULL; - for (tmp = scopeChain; tmp; tmp = tmp->getParent()) { - Class *clasp = tmp->getClass(); - if (clasp == &js_BlockClass || clasp == &js_WithClass) + for (tmp = scopeChain; tmp; tmp = tmp->enclosingScope()) { + if (tmp->isBlock() || tmp->isWith()) continue; - if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(&v))) + if (!tmp->getSpecial(cx, tmp, SpecialId::defaultXMLNamespace(), &v)) return JS_FALSE; if (!JSVAL_IS_PRIMITIVE(v)) { *vp = v; @@ -7284,12 +7609,12 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) obj = tmp; } - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL); + ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 0, NULL); if (!ns) return JS_FALSE; v = OBJECT_TO_JSVAL(ns); - if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v), - PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) { + if (!obj->defineSpecial(cx, SpecialId::defaultXMLNamespace(), v, + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT)) { return JS_FALSE; } *vp = v; @@ -7302,13 +7627,13 @@ js_SetDefaultXMLNamespace(JSContext *cx, const Value &v) Value argv[2]; argv[0].setString(cx->runtime->emptyString); argv[1] = v; - JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv); + JSObject *ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 2, argv); if (!ns) return JS_FALSE; - JSObject &varobj = cx->stack.currentVarObj(); - if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns), - PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) { + JSObject &varobj = cx->fp()->varObj(); + if (!varobj.defineSpecial(cx, SpecialId::defaultXMLNamespace(), ObjectValue(*ns), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT)) { return JS_FALSE; } return JS_TRUE; @@ -7319,7 +7644,7 @@ js_ToAttributeName(JSContext *cx, Value *vp) { JSObject *qn; - qn = ToAttributeName(cx, Jsvalify(*vp)); + qn = ToAttributeName(cx, *vp); if (!qn) return JS_FALSE; vp->setObject(*qn); @@ -7378,28 +7703,27 @@ js_EscapeElementValue(JSContext *cx, JSString *str) JSString * js_ValueToXMLString(JSContext *cx, const Value &v) { - return ToXMLString(cx, Jsvalify(v), 0); + return ToXMLString(cx, v, 0); } JSBool js_GetAnyName(JSContext *cx, jsid *idp) { - JSObject *global = cx->running() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject; + JSObject *global = cx->hasfp() ? &cx->fp()->scopeChain().global() : cx->globalObject; Value v = global->getReservedSlot(JSProto_AnyName); if (v.isUndefined()) { - JSObject *obj = NewNonFunction(cx, &js_AnyNameClass, NULL, global); + JSObject *obj = NewObjectWithGivenProto(cx, &AnyNameClass, NULL, global); if (!obj) return false; JS_ASSERT(!obj->getProto()); JSRuntime *rt = cx->runtime; - InitXMLQName(obj, rt->emptyString, rt->emptyString, rt->atomState.starAtom); - METER(xml_stats.qname); + if (!InitXMLQName(cx, obj, rt->emptyString, rt->emptyString, rt->atomState.starAtom)) + return false; v.setObject(*obj); - if (!js_SetReservedSlot(cx, global, JSProto_AnyName, v)) - return false; + SetReservedSlot(global, JSProto_AnyName, v); } *idp = OBJECT_TO_JSID(&v.toObject()); return true; @@ -7419,26 +7743,28 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i JS_ASSERT(nameval.isObject()); nameobj = &nameval.toObject(); - if (nameobj->getClass() == &js_AnyNameClass) { + if (nameobj->getClass() == &AnyNameClass) { v = STRING_TO_JSVAL(cx->runtime->atomState.starAtom); - nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, - Valueify(&v)); + nameobj = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &v); if (!nameobj) return JS_FALSE; } else { - JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass || - nameobj->getClass() == &js_QNameClass); + JS_ASSERT(nameobj->getClass() == &AttributeNameClass || + nameobj->getClass() == &QNameClass); } qn = nameobj; - if (!IsFunctionQName(cx, qn, &funid)) - return JS_FALSE; - obj = &js_GetTopStackFrame(cx)->scopeChain(); + JSAtom *name; + funid = GetLocalNameFromFunctionQName(qn, &name, cx) + ? ATOM_TO_JSID(name) + : JSID_VOID; + + obj = cx->stack.currentScriptedScopeChain(); do { /* Skip any With object that can wrap XML. */ target = obj; - while (target->getClass() == &js_WithClass) { + while (target->getClass() == &WithClass) { proto = target->getProto(); if (!proto) break; @@ -7459,7 +7785,7 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i return JS_TRUE; } } else if (!JSID_IS_VOID(funid)) { - if (!target->lookupProperty(cx, funid, &pobj, &prop)) + if (!target->lookupGeneric(cx, funid, &pobj, &prop)) return JS_FALSE; if (prop) { *idp = funid; @@ -7467,7 +7793,7 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i return JS_TRUE; } } - } while ((obj = obj->getParent()) != NULL); + } while ((obj = obj->enclosingScope()) != NULL); JSAutoByteString printable; JSString *str = ConvertQNameToString(cx, nameobj); @@ -7484,20 +7810,18 @@ GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JS_ASSERT(obj->isXML()); /* - * See comments before xml_lookupProperty about the need for the proto + * See comments before xml_lookupGeneric about the need for the proto * chain lookup. */ JSObject *target = obj; - AutoObjectRooter tvr(cx); for (;;) { - if (!js_GetProperty(cx, target, id, Valueify(vp))) + if (!js_GetProperty(cx, target, id, vp)) return false; - if (VALUE_IS_FUNCTION(cx, *vp)) + if (!JSVAL_IS_PRIMITIVE(*vp) && JSVAL_TO_OBJECT(*vp)->isFunction()) return true; target = target->getProto(); if (target == NULL || !target->isNative()) break; - tvr.setObject(target); } JSXML *xml = (JSXML *) obj->getPrivate(); @@ -7505,11 +7829,11 @@ GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return true; /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ - if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr())) + JSObject *proto = obj->global().getOrCreateStringPrototype(cx); + if (!proto) return false; - JS_ASSERT(tvr.object()); - return tvr.object()->getProperty(cx, id, Valueify(vp)); + return proto->getGeneric(cx, id, vp); } static JSXML * @@ -7544,7 +7868,7 @@ JSBool js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) { JSXML *list; - uint32 n; + uint32_t n; list = (JSXML *) listobj->getPrivate(); for (n = list->xml_kids.length; n != 0; --n) @@ -7555,12 +7879,12 @@ js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) struct JSXMLFilter { - JSXML *list; - JSXML *result; - JSXML *kid; - JSXMLArrayCursor cursor; + HeapPtr list; + HeapPtr result; + HeapPtr kid; + JSXMLArrayCursor cursor; - JSXMLFilter(JSXML *list, JSXMLArray *array) + JSXMLFilter(JSXML *list, JSXMLArray *array) : list(list), result(NULL), kid(NULL), cursor(array) {} ~JSXMLFilter() {} @@ -7574,11 +7898,11 @@ xmlfilter_trace(JSTracer *trc, JSObject *obj) return; JS_ASSERT(filter->list); - JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list"); + MarkXML(trc, filter->list, "list"); if (filter->result) - JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result"); + MarkXML(trc, filter->result, "result"); if (filter->kid) - JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid"); + MarkXML(trc, filter->kid, "kid"); /* * We do not need to trace the cursor as that would be done when @@ -7599,20 +7923,20 @@ xmlfilter_finalize(JSContext *cx, JSObject *obj) Class js_XMLFilterClass = { "XMLFilter", JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS, - PropertyStub, /* addProperty */ - PropertyStub, /* delProperty */ - PropertyStub, /* getProperty */ - StrictPropertyStub, /* setProperty */ - EnumerateStub, - ResolveStub, - ConvertStub, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, xmlfilter_finalize, - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ xmlfilter_trace }; @@ -7624,15 +7948,14 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized) JSXML *xml, *list; JSXMLFilter *filter; - LeaveTrace(cx); - sp = Jsvalify(cx->regs().sp); + sp = cx->regs().sp; if (!initialized) { /* * We haven't iterated yet, so initialize the filter based on the * value stored in sp[-2]. */ if (!VALUE_IS_XML(sp[-2])) { - js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, Valueify(sp[-2]), NULL); + js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL); return JS_FALSE; } obj = JSVAL_TO_OBJECT(sp[-2]); @@ -7655,7 +7978,8 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized) return JS_FALSE; } - filterobj = NewNonFunction(cx, &js_XMLFilterClass, NULL, NULL); + JSObject *parent = GetGlobalForScopeChain(cx); + filterobj = NewObjectWithGivenProto(cx, &js_XMLFilterClass, NULL, parent); if (!filterobj) return JS_FALSE; @@ -7685,14 +8009,14 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized) JS_ASSERT(filter->kid); /* Check if the filter expression wants to append the element. */ - if (js_ValueToBoolean(Valueify(sp[-1])) && + if (js_ValueToBoolean(sp[-1]) && !Append(cx, filter->result, filter->kid)) { return JS_FALSE; } } /* Do the iteration. */ - filter->kid = (JSXML *) filter->cursor.getNext(); + filter->kid = filter->cursor.getNext(); if (!filter->kid) { /* * Do not defer finishing the cursor until the next GC cycle to avoid @@ -7716,13 +8040,13 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized) JSObject * js_ValueToXMLObject(JSContext *cx, const Value &v) { - return ToXML(cx, Jsvalify(v)); + return ToXML(cx, v); } JSObject * js_ValueToXMLListObject(JSContext *cx, const Value &v) { - return ToXMLList(cx, Jsvalify(v)); + return ToXMLList(cx, v); } JSObject * @@ -7749,10 +8073,10 @@ js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, return NULL; xml = (JSXML *) obj->getPrivate(); if (name) { - JSLinearString *linearName = name->ensureLinear(cx); - if (!linearName) + JSAtom *atomName = js_AtomizeString(cx, name); + if (!atomName) return NULL; - qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName); + qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, atomName); if (!qn) return NULL; xml->name = qn; diff --git a/deps/mozjs/js/src/jsxml.h b/deps/mozjs/js/src/jsxml.h index 5fef407bfe4..6092b43e261 100644 --- a/deps/mozjs/js/src/jsxml.h +++ b/deps/mozjs/js/src/jsxml.h @@ -43,6 +43,8 @@ #include "jsobj.h" #include "jscell.h" +#include "gc/Barrier.h" + extern const char js_AnyName_str[]; extern const char js_AttributeName_str[]; extern const char js_isXMLName_str[]; @@ -53,14 +55,16 @@ extern const char js_gt_entity_str[]; extern const char js_lt_entity_str[]; extern const char js_quot_entity_str[]; -typedef JSBool -(* JSIdentityOp)(const void *a, const void *b); +template +struct JSXMLArrayCursor; -struct JSXMLArray { - uint32 length; - uint32 capacity; - void **vector; - JSXMLArrayCursor *cursors; +template +struct JSXMLArray +{ + uint32_t length; + uint32_t capacity; + js::HeapPtr *vector; + JSXMLArrayCursor *cursors; void init() { length = capacity = 0; @@ -70,19 +74,22 @@ struct JSXMLArray { void finish(JSContext *cx); - bool setCapacity(JSContext *cx, uint32 capacity); + bool setCapacity(JSContext *cx, uint32_t capacity); void trim(); }; +template struct JSXMLArrayCursor { - JSXMLArray *array; - uint32 index; - JSXMLArrayCursor *next; - JSXMLArrayCursor **prevp; - void *root; + typedef js::HeapPtr HeapPtrT; - JSXMLArrayCursor(JSXMLArray *array) + JSXMLArray *array; + uint32_t index; + JSXMLArrayCursor *next; + JSXMLArrayCursor **prevp; + HeapPtrT root; + + JSXMLArrayCursor(JSXMLArray *array) : array(array), index(0), next(array->cursors), prevp(&array->cursors), root(NULL) { @@ -100,23 +107,25 @@ struct JSXMLArrayCursor next->prevp = prevp; *prevp = next; array = NULL; + root.~HeapPtrT(); } - void *getNext() { + T *getNext() { if (!array || index >= array->length) return NULL; return root = array->vector[index++]; } - void *getCurrent() { + T *getCurrent() { if (!array || index >= array->length) return NULL; return root = array->vector[index]; } - - void trace(JSTracer *trc); }; +void js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor); +void js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor); + #define JSXML_PRESET_CAPACITY JS_BIT(31) #define JSXML_CAPACITY_MASK JS_BITMASK(31) #define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) @@ -146,24 +155,24 @@ typedef enum JSXMLClass { #endif typedef struct JSXMLListVar { - JSXMLArray kids; /* NB: must come first */ - JSXML *target; - JSObject *targetprop; + JSXMLArray kids; /* NB: must come first */ + js::HeapPtrXML target; + js::HeapPtrObject targetprop; } JSXMLListVar; typedef struct JSXMLElemVar { - JSXMLArray kids; /* NB: must come first */ - JSXMLArray namespaces; - JSXMLArray attrs; + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; } JSXMLElemVar; /* union member shorthands */ -#define xml_kids u.list.kids -#define xml_target u.list.target -#define xml_targetprop u.list.targetprop -#define xml_namespaces u.elem.namespaces -#define xml_attrs u.elem.attrs -#define xml_value u.value +#define xml_kids list.kids +#define xml_target list.target +#define xml_targetprop list.targetprop +#define xml_namespaces elem.namespaces +#define xml_attrs elem.attrs +#define xml_value value /* xml_class-testing macros */ #define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) @@ -176,32 +185,28 @@ typedef struct JSXMLElemVar { struct JSXML : js::gc::Cell { #ifdef DEBUG_notme JSCList links; - uint32 serial; + uint32_t serial; #endif - JSObject *object; + js::HeapPtrObject object; void *domnode; /* DOM node if mapped info item */ - JSXML *parent; - JSObject *name; - uint32 xml_class; /* discriminates u, below */ - uint32 xml_flags; /* flags, see below */ - union { - JSXMLListVar list; - JSXMLElemVar elem; - JSString *value; - } u; - - void finalize(JSContext *cx) { - if (JSXML_HAS_KIDS(this)) { - xml_kids.finish(cx); - if (xml_class == JSXML_CLASS_ELEMENT) { - xml_namespaces.finish(cx); - xml_attrs.finish(cx); - } - } -#ifdef DEBUG_notme - JS_REMOVE_LINK(&links); + js::HeapPtrXML parent; + js::HeapPtrObject name; + uint32_t xml_class; /* discriminates u, below */ + uint32_t xml_flags; /* flags, see below */ + + JSXMLListVar list; + JSXMLElemVar elem; + js::HeapPtrString value; + +#if JS_BITS_PER_WORD == 32 + /* The size of every GC thing must be divisible by the FreeCell size. */ + void *pad; #endif - } + + void finalize(JSContext *cx, bool background); + + static void writeBarrierPre(JSXML *xml); + static void writeBarrierPost(JSXML *xml, void *addr); }; /* xml_flags values */ @@ -219,54 +224,6 @@ js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); extern JSObject * js_GetXMLObject(JSContext *cx, JSXML *xml); -extern JS_FRIEND_DATA(js::Class) js_XMLClass; -extern JS_FRIEND_DATA(js::Class) js_NamespaceClass; -extern JS_FRIEND_DATA(js::Class) js_QNameClass; -extern JS_FRIEND_DATA(js::Class) js_AttributeNameClass; -extern JS_FRIEND_DATA(js::Class) js_AnyNameClass; -extern js::Class js_XMLFilterClass; - -/* - * Methods to test whether an object or a value is of type "xml" (per typeof). - */ -inline bool -JSObject::isXML() const -{ - return getClass() == &js_XMLClass; -} - -inline bool -JSObject::isXMLId() const -{ - js::Class *clasp = getClass(); - return clasp == &js_QNameClass || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass; -} - -#define VALUE_IS_XML(v) (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isXML()) - -inline bool -JSObject::isNamespace() const -{ - return getClass() == &js_NamespaceClass; -} - -inline bool -JSObject::isQName() const -{ - js::Class* clasp = getClass(); - return clasp == &js_QNameClass || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass; -} - -static inline bool -IsXML(const js::Value &v) -{ - return v.isObject() && v.toObject().isXML(); -} - extern JSObject * js_InitNamespaceClass(JSContext *cx, JSObject *obj); @@ -280,11 +237,11 @@ extern JSObject * js_InitXMLClasses(JSContext *cx, JSObject *obj); /* - * If obj is QName corresponding to function::name, set *funidp to name's id, - * otherwise set *funidp to void. + * If obj is a QName corresponding to function::name, set *funidp to name's id + * and return true, else return false. */ -JSBool -js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp); +extern bool +js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx); extern JSBool js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); @@ -368,4 +325,11 @@ js_TestXMLEquality(JSContext *cx, const js::Value &v1, const js::Value &v2, extern JSBool js_ConcatenateXML(JSContext *cx, JSObject *obj1, JSObject *obj2, js::Value *vp); +namespace js { + +extern bool +GetLocalNameFromFunctionQName(JSObject *qn, JSAtom **namep, JSContext *cx); + +} /* namespace js */ + #endif /* jsxml_h___ */ diff --git a/deps/mozjs/js/src/lirasm/LInsClasses.tbl b/deps/mozjs/js/src/lirasm/LInsClasses.tbl deleted file mode 100644 index a198788c0a7..00000000000 --- a/deps/mozjs/js/src/lirasm/LInsClasses.tbl +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=0 ft=c: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey nanojit. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Nicholas Nethercote - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* LIns classes, as required for --random mode. Includers must define a CL___ - * macro of the following form: - * - * #define CL___(name, only64bit, relFreq) ... - * - * Selected arguments can be used within the macro expansions. - * - * Field Description - * ----- ----------- - * name Name of the instruction class. The types are B (boolean), I - * (32-bit integer), Q (64-bit integer), F (64-bit float), N - * (null). A name of the form LOP_Z_XY means that it takes - * arguments of type X and Y and produces a result of type Z. - * - * relFreq We weight each class differently, so that some classes are more - * common than others. This field gives the relative frequency of - * the instruction class. All the relFreqs together can sum up to - * any number, but it's easier to think about if the sum is a - * round number. (That's why the relFreqs add up to 100%; the - * running total is shown in comments.) The sum also shouldn't be - * too big, as we generate a table with that many elements in it. - * - * Note that we want a decent number of value sinks (eg. - * stores, calls, guards) and not too many value sources (eg. - * immediates, loads) so that the amount of dead code generated is - * reasonable. - * - * Because certain opcode classes aren't supported on all platforms, CLxyz must be one - * of the following: - * - * CL___: for opcode classes supported on all platforms. - * CL_64: for opcode classes supported only on 64-bit platforms. - */ - -#ifdef NANOJIT_64BIT -# define CL_64(a, b) CL___(a, b) -#else -# define CL_64(a, b) -#endif - - -CL___( LFENCE, 1) // 1% LIR_regfence, LIR_xbarrier - -CL___( LALLOC, 1) // 2% LIR_alloc - -CL___( LIMM_I, 4) // 6% LIR_imm -CL_64( LIMM_Q, 3) // 9% LIR_quad -CL___( LIMM_D, 3) // 12% LIR_float - -CL___( LOP_I_I, 2) // 14% LIR_neg, LIR_not -CL_64( LOP_Q_Q, 0) // 14% (none) -CL___( LOP_D_D, 2) // 16% LIR_fneg - -CL___( LOP_I_II, 6) // 32% LIR_add, LIR_and, LIR_eq, etc. -CL_64( LOP_Q_QQ, 7) // 39% LIR_qiadd, LIR_qiand, LIR_qeq, etc. -CL_64( LOP_Q_QI, 2) // 41% LIR_qilsh, LIR_qirsh, LIR_qursh -CL___( LOP_D_DD, 0) // 51% LIR_fadd, etc. - -// cmov has a low weight because is also used with LIR_div/LIR_mod. -CL___( LOP_I_BII, 1) // 52% LIR_cmovi -CL_64( LOP_Q_BQQ, 1) // 53% LIR_cmovq -CL___( LOP_D_BDD, 1) // 54% LIR_cmovd - -CL___( LOP_B_II, 3) // 57% LIR_eq, LIR_lt, etc -CL_64( LOP_B_QQ, 3) // 60% LIR_qeq, LIR_qlt, etc -CL___( LOP_B_DD, 3) // 63% LIR_feq, LIR_flt, etc - -CL_64( LOP_Q_I, 1) // 64% LIR_i2q, LIR_u2q -CL___( LOP_D_I, 1) // 65% LIR_i2f, LIR_u2f -CL_64( LOP_I_Q, 1) // 66% LIR_q2i -CL___( LOP_I_D, 1) // 67% LIR_qlo, LIR_qhi, LIR_f2i -CL_64( LOP_Q_D, 1) // 68% LIR_dasq -CL_64( LOP_D_Q, 1) // 69% LIR_qasd -CL___( LOP_D_II, 1) // 70% LIR_qjoin - -CL___( LLD_I, 3) // 73% LIR_ld, LIR_ldc, LIR_ld*b, LIR_ld*s -CL_64( LLD_Q, 2) // 75% LIR_ldq, LIR_ldqc -CL___( LLD_D, 3) // 78% LIR_ldf, LIR_ldfc - -CL___( LST_I, 5) // 83% LIR_sti -CL_64( LST_Q, 4) // 87% LIR_stqi -CL___( LST_D, 5) // 92% LIR_stfi - -CL___( LCALL_I_I1, 1) // 93% LIR_icall -CL___( LCALL_I_I6, 1) // 94% LIR_icall -CL_64( LCALL_Q_Q2, 1) // 95% LIR_qcall -CL_64( LCALL_Q_Q7, 1) // 96% LIR_qcall -CL___( LCALL_D_D3, 1) // 97% LIR_fcall -CL___( LCALL_D_D8, 1) // 98% LIR_fcall -CL_64( LCALL_V_IQD, 1) // 99% LIR_icall or LIR_qcall - -CL___( LLABEL, 1) //100% LIR_label - - -#undef CL_64 diff --git a/deps/mozjs/js/src/lirasm/Makefile.in b/deps/mozjs/js/src/lirasm/Makefile.in deleted file mode 100644 index edfe51dda6e..00000000000 --- a/deps/mozjs/js/src/lirasm/Makefile.in +++ /dev/null @@ -1,76 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Spidermonkey build system. -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Ted Mielczarek -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = .. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ - -VPATH = $(srcdir) -VPATH += $(srcdir)/../nanojit - -include $(DEPTH)/config/autoconf.mk - -PROGRAM = lirasm$(BIN_SUFFIX) -CPPSRCS = lirasm.cpp \ - Assembler.cpp \ - Allocator.cpp \ - CodeAlloc.cpp \ - Containers.cpp \ - Fragmento.cpp \ - LIR.cpp \ - RegAlloc.cpp \ - avmplus.cpp \ - Native$(NANOJIT_ARCH).cpp \ - njconfig.cpp \ - VMPI.cpp \ - $(NULL) - - -LOCAL_INCLUDES += -I$(topsrcdir) -I.. - -include $(topsrcdir)/config/rules.mk - -ifdef _MSC_VER -CFLAGS += -EHsc -CXXFLAGS += -EHsc -endif - -check:: $(PROGRAM) - $(srcdir)/testlirc.sh $(PROGRAM) - diff --git a/deps/mozjs/js/src/lirasm/lirasm.cpp b/deps/mozjs/js/src/lirasm/lirasm.cpp deleted file mode 100644 index 37e2e377be0..00000000000 --- a/deps/mozjs/js/src/lirasm/lirasm.cpp +++ /dev/null @@ -1,2493 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99: - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is LIR Assembler code, released 2009. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Graydon Hoare - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef AVMPLUS_UNIX -#include -#include -#include -#endif - -#include -#include -#include -#include - -#include "nanojit/nanojit.h" - -using namespace nanojit; -using namespace std; - -/* Allocator SPI implementation. */ - -void* -nanojit::Allocator::allocChunk(size_t nbytes, bool /*fallible*/) -{ - void *p = malloc(nbytes); - if (!p) - exit(1); - return p; -} - -void -nanojit::Allocator::freeChunk(void *p) { - free(p); -} - -void -nanojit::Allocator::postReset() { -} - - -struct LasmSideExit : public SideExit { - size_t line; -}; - - -/* LIR SPI implementation */ - -int -nanojit::StackFilter::getTop(LIns*) -{ - return 0; -} - -// We lump everything into a single access region for lirasm. -static const AccSet ACCSET_OTHER = (1 << 0); -static const uint8_t LIRASM_NUM_USED_ACCS = 1; - -#if defined NJ_VERBOSE -void -nanojit::LInsPrinter::formatGuard(InsBuf *buf, LIns *ins) -{ - RefBuf b1, b2; - LasmSideExit *x = (LasmSideExit *)ins->record()->exit; - VMPI_snprintf(buf->buf, buf->len, - "%s: %s %s -> line=%ld (GuardID=%03d)", - formatRef(&b1, ins), - lirNames[ins->opcode()], - ins->oprnd1() ? formatRef(&b2, ins->oprnd1()) : "", - (long)x->line, - ins->record()->profGuardID); -} - -void -nanojit::LInsPrinter::formatGuardXov(InsBuf *buf, LIns *ins) -{ - RefBuf b1, b2, b3; - LasmSideExit *x = (LasmSideExit *)ins->record()->exit; - VMPI_snprintf(buf->buf, buf->len, - "%s = %s %s, %s -> line=%ld (GuardID=%03d)", - formatRef(&b1, ins), - lirNames[ins->opcode()], - formatRef(&b2, ins->oprnd1()), - formatRef(&b3, ins->oprnd2()), - (long)x->line, - ins->record()->profGuardID); -} - -const char* -nanojit::LInsPrinter::accNames[] = { - "o", // (1 << 0) == ACCSET_OTHER - "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 1..10 (unused) - "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 11..20 (unused) - "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 21..30 (unused) - "?" // 31 (unused) -}; -#endif - -#ifdef DEBUG -void ValidateWriter::checkAccSet(LOpcode op, LIns* base, int32_t disp, AccSet accSet) -{ - (void)op; - (void)base; - (void)disp; - NanoAssert(accSet == ACCSET_OTHER); -} -#endif - -typedef int32_t (FASTCALL *RetInt)(); -typedef int64_t (FASTCALL *RetQuad)(); -typedef double (FASTCALL *RetDouble)(); -typedef GuardRecord* (FASTCALL *RetGuard)(); - -struct Function { - const char *name; - struct nanojit::CallInfo callInfo; -}; - -enum ReturnType { - RT_INT = 1, -#ifdef NANOJIT_64BIT - RT_QUAD = 2, -#endif - RT_DOUBLE = 4, - RT_GUARD = 8 -}; - -#ifdef DEBUG -#define DEBUG_ONLY_NAME(name) ,#name -#else -#define DEBUG_ONLY_NAME(name) -#endif - -#define CI(name, args) \ - {(uintptr_t) (&name), args, nanojit::ABI_CDECL, /*isPure*/0, ACCSET_STORE_ANY \ - DEBUG_ONLY_NAME(name)} - -#define FN(name, args) \ - {#name, CI(name, args)} - -enum LirTokenType { - NAME, NUMBER, PUNCT, NEWLINE -}; - -struct LirToken { - LirTokenType type; - string data; - int lineno; -}; - -inline bool -startsWith(const string &s, const string &prefix) -{ - return s.size() >= prefix.size() && s.compare(0, prefix.length(), prefix) == 0; -} - -// LIR files must be ASCII, for simplicity. -class LirTokenStream { -public: - LirTokenStream(istream &in) : mIn(in), mLineno(0) {} - - bool get(LirToken &token) { - if (mLine.empty()) { - if (!getline(mIn, mLine)) - return false; - mLine += '\n'; - mLineno++; - } - mLine.erase(0, mLine.find_first_not_of(" \t\v\r")); - char c = mLine[0]; - size_t e = mLine.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$.+-"); - if (startsWith(mLine, "->")) { - mLine.erase(0, 2); - token.type = PUNCT; - token.data = "->"; - } else if (e > 0) { - string s = mLine.substr(0, e); - mLine.erase(0, e); - if (e > 1 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) - token.type = NUMBER; - else if (isdigit(s[0]) || (e > 1 && s[0] == '.' && isdigit(s[1]))) - token.type = NUMBER; - else - token.type = NAME; - token.data = s; - } else if (strchr(":,=[]()", c)) { - token.type = PUNCT; - token.data = c; - mLine.erase(0, 1); - } else if (c == ';' || c == '\n') { - token.type = NEWLINE; - token.data.clear(); - mLine.clear(); - } else { - cerr << "line " << mLineno << ": error: Unrecognized character in file." << endl; - return false; - } - - token.lineno = mLineno; - return true; - } - - bool eat(LirTokenType type, const char *exact = NULL) { - LirToken token; - return (get(token) && token.type == type && (exact == NULL || token.data == exact)); - } - - bool getName(string &name) { - LirToken t; - if (get(t) && t.type == NAME) { - name = t.data; - return true; - } - return false; - } - -private: - istream &mIn; - string mLine; - int mLineno; -}; - -class LirasmFragment { -public: - union { - RetInt rint; -#ifdef NANOJIT_64BIT - RetQuad rquad; -#endif - RetDouble rdouble; - RetGuard rguard; - }; - ReturnType mReturnType; - Fragment *fragptr; - map mLabels; -}; - -typedef map Fragments; - -class Lirasm { -public: - Lirasm(bool verbose, Config& config); - ~Lirasm(); - - void assemble(istream &in, bool optimize); - void assembleRandom(int nIns, bool optimize); - bool lookupFunction(const string &name, CallInfo *&ci); - - LirBuffer *mLirbuf; - LogControl mLogc; - Config mConfig; - Allocator mAlloc; - CodeAlloc mCodeAlloc; - bool mVerbose; - Fragments mFragments; - Assembler mAssm; - map mOpMap; - - void bad(const string &msg) { - cerr << "error: " << msg << endl; - exit(1); - } - -private: - void handlePatch(LirTokenStream &in); -}; - -class FragmentAssembler { -public: - FragmentAssembler(Lirasm &parent, const string &fragmentName, bool optimize); - ~FragmentAssembler(); - - void assembleFragment(LirTokenStream &in, - bool implicitBegin, - const LirToken *firstToken); - - void assembleRandomFragment(int nIns); - -private: - static uint32_t sProfId; - // Prohibit copying. - FragmentAssembler(const FragmentAssembler &); - FragmentAssembler & operator=(const FragmentAssembler &); - LasmSideExit *createSideExit(); - GuardRecord *createGuardRecord(LasmSideExit *exit); - - Lirasm &mParent; - const string mFragName; - Fragment *mFragment; - bool optimize; - vector mCallInfos; - map mLabels; - LirWriter *mLir; - LirBufWriter *mBufWriter; - LirWriter *mCseFilter; - LirWriter *mExprFilter; - LirWriter *mSoftFloatFilter; - LirWriter *mVerboseWriter; - LirWriter *mValidateWriter1; - LirWriter *mValidateWriter2; - vector< pair > mJumps; - map mJumpLabels; - - size_t mLineno; - LOpcode mOpcode; - size_t mOpcount; - - char mReturnTypeBits; - vector mTokens; - - void tokenizeLine(LirTokenStream &in, LirToken &token); - void need(size_t); - LIns *ref(const string &); - LIns *assemble_jump(bool isCond); - LIns *assemble_load(); - LIns *assemble_call(const string &); - LIns *assemble_ret(ReturnType rt); - LIns *assemble_guard(bool isCond); - LIns *assemble_guard_xov(); - LIns *assemble_jump_jov(); - void bad(const string &msg); - void nyi(const string &opname); - void extract_any_label(string &lab, char lab_delim); - void resolve_jumps(); - void add_jump_label(const string& lab, LIns* ins); - void endFragment(); -}; - -// 'sin' is overloaded on some platforms, so taking its address -// doesn't quite work. Provide a do-nothing function here -// that's not overloaded. -double sinFn(double d) { - return sin(d); -} -#define sin sinFn - -double calld1(double x, double i, double y, double l, double x1, double i1, double y1, double l1) { - return x + i * y - l + x1 / i1 - y1 * l1; -} - -// The calling tests with mixed argument types are sensible for all platforms, but they highlight -// the differences between the supported ABIs on ARM. - -double callid1(int i, double x, double y, int j, int k, double z) { - return (x + y + z) / (double)(i + j + k); -} - -double callid2(int i, int j, int k, double x) { - return x / (double)(i + j + k); -} - -double callid3(int i, int j, double x, int k, double y, double z) { - return (x + y + z) / (double)(i + j + k); -} - -// Simple print function for testing void calls. -void printi(int x) { - cout << x << endl; -} - -Function functions[] = { - FN(puts, CallInfo::typeSig1(ARGTYPE_I, ARGTYPE_P)), - FN(sin, CallInfo::typeSig1(ARGTYPE_D, ARGTYPE_D)), - FN(malloc, CallInfo::typeSig1(ARGTYPE_P, ARGTYPE_P)), - FN(free, CallInfo::typeSig1(ARGTYPE_V, ARGTYPE_P)), - FN(calld1, CallInfo::typeSig8(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, - ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D)), - FN(callid1, CallInfo::typeSig6(ARGTYPE_D, ARGTYPE_I, ARGTYPE_D, ARGTYPE_D, - ARGTYPE_I, ARGTYPE_I, ARGTYPE_D)), - FN(callid2, CallInfo::typeSig4(ARGTYPE_D, ARGTYPE_I, ARGTYPE_I, ARGTYPE_I, ARGTYPE_D)), - FN(callid3, CallInfo::typeSig6(ARGTYPE_D, ARGTYPE_I, ARGTYPE_I, ARGTYPE_D, - ARGTYPE_I, ARGTYPE_D, ARGTYPE_D)), - FN(printi, CallInfo::typeSig1(ARGTYPE_V, ARGTYPE_I)), -}; - -template out -lexical_cast(in arg) -{ - stringstream tmp; - out ret; - if ((tmp << arg && tmp >> ret && tmp.eof())) - return ret; - cerr << "bad lexical cast from " << arg << endl; - exit(1); -} - -int32_t -immI(const string &s) -{ - stringstream tmp(s); - int32_t ret; - if ((s.find("0x") == 0 || s.find("0X") == 0) && - (tmp >> hex >> ret && tmp.eof())) { - return ret; - } - return lexical_cast(s); -} - -uint64_t -immQ(const string &s) -{ - stringstream tmp(s); - uint64_t ret; - if ((s.find("0x") == 0 || s.find("0X") == 0) && - (tmp >> hex >> ret && tmp.eof())) { - return ret; - } - return lexical_cast(s); -} - -double -immD(const string &s) -{ - return lexical_cast(s); -} - -template t -pop_front(vector &vec) -{ - if (vec.empty()) { - cerr << "pop_front of empty vector" << endl; - exit(1); - } - t tmp = vec[0]; - vec.erase(vec.begin()); - return tmp; -} - -void -dep_u8(char *&buf, uint8_t byte, uint32_t &cksum) -{ - sprintf(buf, "%2.2X", byte); - cksum += byte; - buf += 2; -} - -void -dep_u32(char *&buf, uint32_t word, uint32_t &cksum) -{ - dep_u8(buf, (uint8_t)((word >> 24) & 0xff), cksum); - dep_u8(buf, (uint8_t)((word >> 16) & 0xff), cksum); - dep_u8(buf, (uint8_t)((word >> 8) & 0xff), cksum); - dep_u8(buf, (uint8_t)((word) & 0xff), cksum); -} - -void -dump_srecords(ostream &, Fragment *) -{ - // FIXME: Disabled until we work out a sane way to walk through - // code chunks under the new CodeAlloc regime. -/* - // Write S-records. Can only do 4-byte addresses at the moment. - - // FIXME: this presently dumps out the entire set of code pages - // written-to, which means it often dumps *some* bytes on the last - // page that are not necessarily initialized at all; they're - // beyond the last instruction written. Fix this to terminate - // s-record writing early. - - assert(sizeof(uintptr_t) == 4); - for (Page *page = frag->pages(); page; page = page->next) { - size_t step = 32; - uintptr_t p0 = (uintptr_t) &(page->code); - for (uintptr_t p = p0; p < p0 + sizeof(page->code); p += step) { - char buf[1024]; - - // S-record type S3: 8-char / 4-byte address. - // - // +2 char code 'S3'. - // +2 char / 1 byte count of remaining bytes (37 = addr, payload, cksum). - // +8 char / 4 byte addr. - // --- - // +64 char / 32 byte payload. - // --- - // +2 char / 1 byte checksum. - - uint32_t cksum = 0; - size_t count = sizeof(p) + step + 1; - - sprintf(buf, "S3"); - - char *b = buf + 2; // 2 chars for the "S3" code. - - dep_u8(b, (uint8_t) count, cksum); // Count of data bytes - dep_u32(b, p, cksum); // Address of the data byte being emitted - uint8_t *c = (uint8_t*) p; - for (size_t i = 0; i < step; ++i) { // Actual object code being emitted - dep_u8(b, c[i], cksum); - } - dep_u8(b, (uint8_t)((~cksum) & 0xff), cksum); - out << string(buf) << endl; - } - } -*/ -} - - - -uint32_t -FragmentAssembler::sProfId = 0; - -FragmentAssembler::FragmentAssembler(Lirasm &parent, const string &fragmentName, bool optimize) - : mParent(parent), mFragName(fragmentName), optimize(optimize), - mBufWriter(NULL), mCseFilter(NULL), mExprFilter(NULL), mSoftFloatFilter(NULL), mVerboseWriter(NULL), - mValidateWriter1(NULL), mValidateWriter2(NULL) -{ - mFragment = new Fragment(NULL verbose_only(, (mParent.mLogc.lcbits & - nanojit::LC_FragProfile) ? - sProfId++ : 0)); - mFragment->lirbuf = mParent.mLirbuf; - mParent.mFragments[mFragName].fragptr = mFragment; - - mLir = mBufWriter = new LirBufWriter(mParent.mLirbuf, mParent.mConfig); -#ifdef DEBUG - if (optimize) { // don't re-validate if no optimization has taken place - mLir = mValidateWriter2 = - new ValidateWriter(mLir, mFragment->lirbuf->printer, "end of writer pipeline"); - } -#endif -#ifdef DEBUG - if (mParent.mVerbose) { - mLir = mVerboseWriter = new VerboseWriter(mParent.mAlloc, mLir, - mParent.mLirbuf->printer, - &mParent.mLogc); - } -#endif - if (optimize) { - mLir = mCseFilter = new CseFilter(mLir, LIRASM_NUM_USED_ACCS, mParent.mAlloc); - } -#if NJ_SOFTFLOAT_SUPPORTED - if (mParent.mConfig.soft_float) { - mLir = new SoftFloatFilter(mLir); - } -#endif - if (optimize) { - mLir = mExprFilter = new ExprFilter(mLir); - } -#ifdef DEBUG - mLir = mValidateWriter1 = - new ValidateWriter(mLir, mFragment->lirbuf->printer, "start of writer pipeline"); -#endif - - mReturnTypeBits = 0; - mLir->ins0(LIR_start); - for (int i = 0; i < nanojit::NumSavedRegs; ++i) - mLir->insParam(i, 1); - - mLineno = 0; -} - -FragmentAssembler::~FragmentAssembler() -{ - delete mValidateWriter1; - delete mValidateWriter2; - delete mVerboseWriter; - delete mExprFilter; - delete mSoftFloatFilter; - delete mCseFilter; - delete mBufWriter; -} - - -void -FragmentAssembler::bad(const string &msg) -{ - cerr << "line " << mLineno << ": " << msg << endl; - exit(1); -} - -void -FragmentAssembler::nyi(const string &opname) -{ - cerr << "line " << mLineno << ": '" << opname << "' not yet implemented, sorry" << endl; - exit(1); -} - -void -FragmentAssembler::need(size_t n) -{ - if (mTokens.size() != n) { - bad("need " + lexical_cast(n) - + " tokens, have " + lexical_cast(mTokens.size())); - } -} - -LIns * -FragmentAssembler::ref(const string &lab) -{ - if (mLabels.find(lab) == mLabels.end()) - bad("unknown label '" + lab + "'"); - return mLabels.find(lab)->second; -} - -LIns * -FragmentAssembler::assemble_jump(bool isCond) -{ - LIns *condition; - - if (isCond) { - need(2); - string cond = pop_front(mTokens); - condition = ref(cond); - } else { - need(1); - condition = NULL; - } - string name = pop_front(mTokens); - LIns *ins = mLir->insBranch(mOpcode, condition, NULL); - mJumps.push_back(make_pair(name, ins)); - return ins; -} - -LIns * -FragmentAssembler::assemble_load() -{ - // Support implicit immediate-as-second-operand modes - // since, unlike sti/stqi, no immediate-displacement - // load opcodes were defined in LIR. - need(2); - if (mTokens[1].find("0x") == 0 || - mTokens[1].find("0x") == 0 || - mTokens[1].find_first_of("0123456789") == 0) { - return mLir->insLoad(mOpcode, - ref(mTokens[0]), - immI(mTokens[1]), ACCSET_OTHER); - } - bad("immediate offset required for load"); - return NULL; // not reached -} - -LIns * -FragmentAssembler::assemble_call(const string &op) -{ - CallInfo *ci = new (mParent.mAlloc) CallInfo; - mCallInfos.push_back(ci); - LIns *args[MAXARGS]; - memset(&args[0], 0, sizeof(args)); - - // Assembler syntax for a call: - // - // call 0x1234 fastcall a b c - // - // requires at least 2 args, - // fn address immediate and ABI token. - - if (mTokens.size() < 2) - bad("need at least address and ABI code for " + op); - - string func = pop_front(mTokens); - string abi = pop_front(mTokens); - - AbiKind _abi = ABI_CDECL; - if (abi == "fastcall") - _abi = ABI_FASTCALL; - else if (abi == "stdcall") - _abi = ABI_STDCALL; - else if (abi == "thiscall") - _abi = ABI_THISCALL; - else if (abi == "cdecl") - _abi = ABI_CDECL; - else - bad("call abi name '" + abi + "'"); - - if (mTokens.size() > MAXARGS) - bad("too many args to " + op); - - bool isBuiltin = mParent.lookupFunction(func, ci); - if (isBuiltin) { - // Built-in: use its CallInfo. Also check (some) CallInfo details - // against those from the call site. - if (_abi != ci->_abi) - bad("invalid calling convention for " + func); - - size_t i; - for (i = 0; i < mTokens.size(); ++i) { - args[i] = ref(mTokens[mTokens.size() - (i+1)]); - } - if (i != ci->count_args()) - bad("wrong number of arguments for " + func); - - } else { - // User-defined function: infer CallInfo details (ABI, arg types, ret - // type) from the call site. - ci->_abi = _abi; - size_t argc = mTokens.size(); - ArgType argTypes[MAXARGS]; - for (size_t i = 0; i < argc; ++i) { - NanoAssert(i < MAXARGS); // should give a useful error msg if this fails - args[i] = ref(mTokens[mTokens.size() - (i+1)]); - if (args[i]->isD()) argTypes[i] = ARGTYPE_D; -#ifdef NANOJIT_64BIT - else if (args[i]->isQ()) argTypes[i] = ARGTYPE_Q; -#endif - else argTypes[i] = ARGTYPE_I; - } - - // Select return type from opcode. - ArgType retType = ARGTYPE_P; - if (mOpcode == LIR_callv) retType = ARGTYPE_V; - else if (mOpcode == LIR_calli) retType = ARGTYPE_I; -#ifdef NANOJIT_64BIT - else if (mOpcode == LIR_callq) retType = ARGTYPE_Q; -#endif - else if (mOpcode == LIR_calld) retType = ARGTYPE_D; - else nyi("callh"); - ci->_typesig = CallInfo::typeSigN(retType, argc, argTypes); - } - - return mLir->insCall(ci, args); -} - -LIns * -FragmentAssembler::assemble_ret(ReturnType rt) -{ - need(1); - mReturnTypeBits |= rt; - return mLir->ins1(mOpcode, ref(mTokens[0])); -} - -LasmSideExit* -FragmentAssembler::createSideExit() -{ - LasmSideExit* exit = new (mParent.mAlloc) LasmSideExit(); - memset(exit, 0, sizeof(LasmSideExit)); - exit->from = mFragment; - exit->target = NULL; - exit->line = mLineno; - return exit; -} - -GuardRecord* -FragmentAssembler::createGuardRecord(LasmSideExit *exit) -{ - GuardRecord *rec = new (mParent.mAlloc) GuardRecord; - memset(rec, 0, sizeof(GuardRecord)); - rec->exit = exit; - exit->addGuard(rec); - return rec; -} - -LIns * -FragmentAssembler::assemble_guard(bool isCond) -{ - GuardRecord* guard = createGuardRecord(createSideExit()); - - LIns *ins_cond; - if (isCond) { - need(1); - ins_cond = ref(pop_front(mTokens)); - } else { - need(0); - ins_cond = NULL; - } - - mReturnTypeBits |= RT_GUARD; - - if (!mTokens.empty()) - bad("too many arguments"); - - return mLir->insGuard(mOpcode, ins_cond, guard); -} - -LIns* -FragmentAssembler::assemble_guard_xov() -{ - GuardRecord* guard = createGuardRecord(createSideExit()); - - need(2); - - mReturnTypeBits |= RT_GUARD; - - return mLir->insGuardXov(mOpcode, ref(mTokens[0]), ref(mTokens[1]), guard); -} - -LIns * -FragmentAssembler::assemble_jump_jov() -{ - need(3); - - LIns *a = ref(mTokens[0]); - LIns *b = ref(mTokens[1]); - string name = mTokens[2]; - - LIns *ins = mLir->insBranchJov(mOpcode, a, b, NULL); - mJumps.push_back(make_pair(name, ins)); - return ins; -} - -void -FragmentAssembler::endFragment() -{ - // Resolve all of the jumps in this fragment. - resolve_jumps(); - - if (mReturnTypeBits == 0) { - cerr << "warning: no return type in fragment '" - << mFragName << "'" << endl; - - } else if (mReturnTypeBits != RT_INT && -#ifdef NANOJIT_64BIT - mReturnTypeBits != RT_QUAD && -#endif - mReturnTypeBits != RT_DOUBLE && - mReturnTypeBits != RT_GUARD) - { - cerr << "warning: multiple return types in fragment '" - << mFragName << "'" << endl; - } - - mFragment->lastIns = - mLir->insGuard(LIR_x, NULL, createGuardRecord(createSideExit())); - - mParent.mAssm.compile(mFragment, mParent.mAlloc, optimize - verbose_only(, mParent.mLirbuf->printer)); - - if (mParent.mAssm.error() != nanojit::None) { - cerr << "error during assembly: "; - switch (mParent.mAssm.error()) { - case nanojit::BranchTooFar: cerr << "BranchTooFar"; break; - case nanojit::StackFull: cerr << "StackFull"; break; - case nanojit::UnknownBranch: cerr << "UnknownBranch"; break; - case nanojit::None: cerr << "None"; break; - default: NanoAssert(0); break; - } - cerr << endl; - std::exit(1); - } - - LirasmFragment *f; - f = &mParent.mFragments[mFragName]; - - switch (mReturnTypeBits) { - case RT_INT: - f->rint = (RetInt)((uintptr_t)mFragment->code()); - f->mReturnType = RT_INT; - break; -#ifdef NANOJIT_64BIT - case RT_QUAD: - f->rquad = (RetQuad)((uintptr_t)mFragment->code()); - f->mReturnType = RT_QUAD; - break; -#endif - case RT_DOUBLE: - f->rdouble = (RetDouble)((uintptr_t)mFragment->code()); - f->mReturnType = RT_DOUBLE; - break; - case RT_GUARD: - f->rguard = (RetGuard)((uintptr_t)mFragment->code()); - f->mReturnType = RT_GUARD; - break; - default: - NanoAssert(0); - break; - } - - mParent.mFragments[mFragName].mLabels = mLabels; -} - -void -FragmentAssembler::tokenizeLine(LirTokenStream &in, LirToken &token) -{ - mTokens.clear(); - mTokens.push_back(token.data); - - while (in.get(token)) { - if (token.type == NEWLINE) - break; - mTokens.push_back(token.data); - } -} - -void -FragmentAssembler::extract_any_label(string &lab, char lab_delim) -{ - if (mTokens.size() > 2 && mTokens[1].size() == 1 && mTokens[1][0] == lab_delim) { - lab = pop_front(mTokens); - pop_front(mTokens); // remove punctuation - - if (mLabels.find(lab) != mLabels.end()) - bad("duplicate label"); - } -} - -void -FragmentAssembler::resolve_jumps() -{ - typedef vector< pair > pairvec; - typedef pairvec::const_iterator pv_ci; - - typedef map< string, LIns* > labelmap; - typedef labelmap::const_iterator lm_ci; - - for ( pv_ci i = mJumps.begin(); i != mJumps.end(); ++i ) { - lm_ci target = mJumpLabels.find(i->first); - if ( target == mJumpLabels.end() ) - bad("No label exists for jump target '" + i->first + "'"); - i->second->setTarget( target->second ); - } -} - -void -FragmentAssembler::add_jump_label(const string& lab, LIns* ins) -{ - if ( mJumpLabels.find(lab) != mJumpLabels.end() ) - bad("Label '" + lab + "' found at multiple locations."); - mJumpLabels[lab] = ins; -} - -void -FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, const LirToken *firstToken) -{ - LirToken token; - while (true) { - if (firstToken) { - token = *firstToken; - firstToken = NULL; - } else if (!in.get(token)) { - if (!implicitBegin) - bad("unexpected end of file in fragment '" + mFragName + "'"); - break; - } - if (token.type == NEWLINE) - continue; - if (token.type != NAME) - bad("unexpected token '" + token.data + "'"); - - string op = token.data; - if (op == ".begin") - bad("nested fragments are not supported"); - if (op == ".end") { - if (implicitBegin) - bad(".end without .begin"); - if (!in.eat(NEWLINE)) - bad("extra junk after .end"); - break; - } - - mLineno = token.lineno; - tokenizeLine(in, token); - - string lab; - LIns *ins = NULL; - extract_any_label(lab, ':'); - - /* Save label as a jump label */ - if (!lab.empty()) { - ins = mLir->ins0(LIR_label); - add_jump_label(lab, ins); - lab.clear(); - } - extract_any_label(lab, '='); - - assert(!mTokens.empty()); - op = pop_front(mTokens); - if (mParent.mOpMap.find(op) == mParent.mOpMap.end()) - bad("unknown instruction '" + op + "'"); - - mOpcode = mParent.mOpMap[op]; - - switch (mOpcode) { - case LIR_start: - bad("start instructions cannot be specified explicitly"); - break; - - case LIR_regfence: - need(0); - ins = mLir->ins0(mOpcode); - break; - - case LIR_livei: - CASE64(LIR_liveq:) - case LIR_lived: - case LIR_negi: - case LIR_negd: - case LIR_noti: - CASESF(LIR_dlo2i:) - CASESF(LIR_dhi2i:) - CASE64(LIR_q2i:) - CASE64(LIR_i2q:) - CASE64(LIR_ui2uq:) - CASE64(LIR_dasq:) - CASE64(LIR_qasd:) - case LIR_i2d: - case LIR_ui2d: - case LIR_d2i: -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - case LIR_modi: -#endif - need(1); - ins = mLir->ins1(mOpcode, - ref(mTokens[0])); - break; - - case LIR_addi: - case LIR_subi: - case LIR_muli: -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - case LIR_divi: -#endif - case LIR_addd: - case LIR_subd: - case LIR_muld: - case LIR_divd: - CASE64(LIR_addq:) - CASE64(LIR_subq:) - case LIR_andi: - case LIR_ori: - case LIR_xori: - CASE64(LIR_andq:) - CASE64(LIR_orq:) - CASE64(LIR_xorq:) - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - CASE64(LIR_lshq:) - CASE64(LIR_rshq:) - CASE64(LIR_rshuq:) - case LIR_eqi: - case LIR_lti: - case LIR_gti: - case LIR_lei: - case LIR_gei: - case LIR_ltui: - case LIR_gtui: - case LIR_leui: - case LIR_geui: - case LIR_eqd: - case LIR_ltd: - case LIR_gtd: - case LIR_led: - case LIR_ged: - CASE64(LIR_eqq:) - CASE64(LIR_ltq:) - CASE64(LIR_gtq:) - CASE64(LIR_leq:) - CASE64(LIR_geq:) - CASE64(LIR_ltuq:) - CASE64(LIR_gtuq:) - CASE64(LIR_leuq:) - CASE64(LIR_geuq:) - CASESF(LIR_ii2d:) - need(2); - ins = mLir->ins2(mOpcode, - ref(mTokens[0]), - ref(mTokens[1])); - break; - - case LIR_cmovi: - CASE64(LIR_cmovq:) - case LIR_cmovd: - need(3); - ins = mLir->ins3(mOpcode, - ref(mTokens[0]), - ref(mTokens[1]), - ref(mTokens[2])); - break; - - case LIR_j: - ins = assemble_jump(/*isCond*/false); - break; - - case LIR_jt: - case LIR_jf: - ins = assemble_jump(/*isCond*/true); - break; - - case LIR_immi: - need(1); - ins = mLir->insImmI(immI(mTokens[0])); - break; - -#ifdef NANOJIT_64BIT - case LIR_immq: - need(1); - ins = mLir->insImmQ(immQ(mTokens[0])); - break; -#endif - - case LIR_immd: - need(1); - ins = mLir->insImmD(immD(mTokens[0])); - break; - -#if NJ_EXPANDED_LOADSTORE_SUPPORTED - case LIR_sti2c: - case LIR_sti2s: - case LIR_std2f: -#endif - case LIR_sti: - CASE64(LIR_stq:) - case LIR_std: - need(3); - ins = mLir->insStore(mOpcode, ref(mTokens[0]), - ref(mTokens[1]), - immI(mTokens[2]), ACCSET_OTHER); - break; - -#if NJ_EXPANDED_LOADSTORE_SUPPORTED - case LIR_ldc2i: - case LIR_lds2i: - case LIR_ldf2d: -#endif - case LIR_lduc2ui: - case LIR_ldus2ui: - case LIR_ldi: - CASE64(LIR_ldq:) - case LIR_ldd: - ins = assemble_load(); - break; - - // XXX: insParam gives the one appropriate for the platform. Eg. if - // you specify qparam on x86 you'll end up with iparam anyway. Fix - // this. - case LIR_paramp: - need(2); - ins = mLir->insParam(immI(mTokens[0]), - immI(mTokens[1])); - break; - - // XXX: similar to iparam/qparam above. - case LIR_allocp: - need(1); - ins = mLir->insAlloc(immI(mTokens[0])); - break; - - case LIR_skip: - bad("skip instruction is deprecated"); - break; - - case LIR_x: - case LIR_xbarrier: - ins = assemble_guard(/*isCond*/false); - break; - - case LIR_xt: - case LIR_xf: - ins = assemble_guard(/*isCond*/true); - break; - - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - ins = assemble_guard_xov(); - break; - - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - CASE64(LIR_addjovq:) - CASE64(LIR_subjovq:) - ins = assemble_jump_jov(); - break; - - case LIR_callv: - case LIR_calli: - CASESF(LIR_hcalli:) - CASE64(LIR_callq:) - case LIR_calld: - ins = assemble_call(op); - break; - - case LIR_reti: - ins = assemble_ret(RT_INT); - break; - -#ifdef NANOJIT_64BIT - case LIR_retq: - ins = assemble_ret(RT_QUAD); - break; -#endif - - case LIR_retd: - ins = assemble_ret(RT_DOUBLE); - break; - - case LIR_label: - ins = mLir->ins0(LIR_label); - add_jump_label(lab, ins); - lab.clear(); - break; - - case LIR_file: - case LIR_line: - case LIR_jtbl: - nyi(op); - break; - - default: - nyi(op); - break; - } - - assert(ins); - if (!lab.empty()) - mLabels.insert(make_pair(lab, ins)); - - } - endFragment(); -} - -/* ------------------ Support for --random -------------------------- */ - -// Returns a positive integer in the range 0..(lim-1). -static inline size_t -rnd(size_t lim) -{ - size_t i = size_t(rand()); - return i % lim; -} - -// Returns an int32_t in the range -RAND_MAX..RAND_MAX. -static inline int32_t -rndI32() -{ - return (rnd(2) ? 1 : -1) * rand(); -} - -// The maximum number of live values (per type, ie. B/I/Q/F) that are -// available to be used as operands. If we make it too high we're prone to -// run out of stack space due to spilling. Needs to be set in consideration -// with spillStackSzB. -const size_t maxLiveValuesPerType = 20; - -// Returns a uint32_t in the range 0..(RAND_MAX*2). -static inline uint32_t -rndU32() -{ - return uint32_t(rnd(2) ? 0 : RAND_MAX) + uint32_t(rand()); -} - -template t -rndPick(vector &v) -{ - assert(!v.empty()); - return v[rnd(v.size())]; -} - -// Add the operand, and retire an old one if we have too many. -template void -addOrReplace(vector &v, t x) -{ - if (v.size() > maxLiveValuesPerType) { - v[rnd(v.size())] = x; // we're full: overwrite an existing element - } else { - v.push_back(x); // add to end - } -} - -// Returns a 4-aligned address within the given size. -static int32_t rndOffset32(size_t szB) -{ - return int32_t(rnd(szB)) & ~3; -} - -// Returns an 8-aligned address within the give size. -static int32_t rndOffset64(size_t szB) -{ - return int32_t(rnd(szB)) & ~7; -} - -static int32_t f_I_I1(int32_t a) -{ - return a; -} - -static int32_t f_I_I6(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f) -{ - return a + b + c + d + e + f; -} - -#ifdef NANOJIT_64BIT -static uint64_t f_Q_Q2(uint64_t a, uint64_t b) -{ - return a + b; -} - -static uint64_t f_Q_Q7(uint64_t a, uint64_t b, uint64_t c, uint64_t d, - uint64_t e, uint64_t f, uint64_t g) -{ - return a + b + c + d + e + f + g; -} -#endif - -static double f_F_F3(double a, double b, double c) -{ - return a + b + c; -} - -static double f_F_F8(double a, double b, double c, double d, - double e, double f, double g, double h) -{ - return a + b + c + d + e + f + g + h; -} - -#ifdef NANOJIT_64BIT -static void f_V_IQF(int32_t, uint64_t, double) -{ - return; // no need to do anything -} -#endif - -const CallInfo ci_I_I1 = CI(f_I_I1, CallInfo::typeSig1(ARGTYPE_I, ARGTYPE_I)); -const CallInfo ci_I_I6 = CI(f_I_I6, CallInfo::typeSig6(ARGTYPE_I, ARGTYPE_I, ARGTYPE_I, ARGTYPE_I, - ARGTYPE_I, ARGTYPE_I, ARGTYPE_I)); - -#ifdef NANOJIT_64BIT -const CallInfo ci_Q_Q2 = CI(f_Q_Q2, CallInfo::typeSig2(ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q)); -const CallInfo ci_Q_Q7 = CI(f_Q_Q7, CallInfo::typeSig7(ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q, - ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q)); -#endif - -const CallInfo ci_F_F3 = CI(f_F_F3, CallInfo::typeSig3(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D)); -const CallInfo ci_F_F8 = CI(f_F_F8, CallInfo::typeSig8(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, - ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, - ARGTYPE_D)); - -#ifdef NANOJIT_64BIT -const CallInfo ci_V_IQF = CI(f_V_IQF, CallInfo::typeSig3(ARGTYPE_V, ARGTYPE_I, ARGTYPE_Q, ARGTYPE_D)); -#endif - -// Generate a random block containing nIns instructions, plus a few more -// setup/shutdown ones at the start and end. -// -// Basic operation: -// - We divide LIR into numerous classes, mostly according to their type. -// (See LInsClasses.tbl for details.) Each time around the loop we choose -// the class randomly, but there is weighting so that some classes are more -// common than others, in an attempt to reflect the structure of real code. -// - Each instruction that produces a value is put in a buffer of the -// appropriate type, for possible use as an operand of a later instruction. -// This buffer is trimmed when its size exceeds 'maxLiveValuesPerType'. -// - If not enough operands are present in a buffer for the particular -// instruction, we don't add it. -// - Skips aren't explicitly generated, but they do occcur if the fragment is -// sufficiently big that it's spread across multiple chunks. -// -// The following instructions aren't generated yet: -// - LIR_parami/LIR_paramq (hard to test beyond what is auto-generated in fragment -// prologues) -// - LIR_livei/LIR_liveq/LIR_lived -// - LIR_hcalli -// - LIR_x/LIR_xt/LIR_xf/LIR_addxovi/LIR_subxovi/LIR_mulxovi (hard to -// test without having multiple fragments; when we only have one fragment -// we don't really want to leave it early) -// - LIR_reti/LIR_retq/LIR_retd (hard to test without having multiple fragments) -// - LIR_j/LIR_jt/LIR_jf/LIR_jtbl/LIR_label -// - LIR_file/LIR_line (#ifdef VTUNE only) -// - LIR_modd (not implemented in NJ backends) -// -// Other limitations: -// - Loads always use accSet==ACCSET_OTHER -// - Stores always use accSet==ACCSET_OTHER -// -void -FragmentAssembler::assembleRandomFragment(int nIns) -{ - vector Bs; // boolean values, ie. 32-bit int values produced by tests - vector Is; // 32-bit int values - vector Qs; // 64-bit int values - vector Ds; // 64-bit double values - vector M4s; // 4 byte allocs - vector M8ps; // 8+ byte allocs - - vector I_I_ops; - I_I_ops.push_back(LIR_negi); - I_I_ops.push_back(LIR_noti); - - // Nb: there are no Q_Q_ops. - - vector D_D_ops; - D_D_ops.push_back(LIR_negd); - - vector I_II_ops; - I_II_ops.push_back(LIR_addi); - I_II_ops.push_back(LIR_subi); - I_II_ops.push_back(LIR_muli); -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - I_II_ops.push_back(LIR_divi); - I_II_ops.push_back(LIR_modi); -#endif - I_II_ops.push_back(LIR_andi); - I_II_ops.push_back(LIR_ori); - I_II_ops.push_back(LIR_xori); - I_II_ops.push_back(LIR_lshi); - I_II_ops.push_back(LIR_rshi); - I_II_ops.push_back(LIR_rshui); - -#ifdef NANOJIT_64BIT - vector Q_QQ_ops; - Q_QQ_ops.push_back(LIR_addq); - Q_QQ_ops.push_back(LIR_andq); - Q_QQ_ops.push_back(LIR_orq); - Q_QQ_ops.push_back(LIR_xorq); - - vector Q_QI_ops; - Q_QI_ops.push_back(LIR_lshq); - Q_QI_ops.push_back(LIR_rshq); - Q_QI_ops.push_back(LIR_rshuq); -#endif - - vector D_DD_ops; - D_DD_ops.push_back(LIR_addd); - D_DD_ops.push_back(LIR_subd); - D_DD_ops.push_back(LIR_muld); - D_DD_ops.push_back(LIR_divd); - - vector I_BII_ops; - I_BII_ops.push_back(LIR_cmovi); - -#ifdef NANOJIT_64BIT - vector Q_BQQ_ops; - Q_BQQ_ops.push_back(LIR_cmovq); -#endif - - vector D_BDD_ops; - D_BDD_ops.push_back(LIR_cmovd); - - vector B_II_ops; - B_II_ops.push_back(LIR_eqi); - B_II_ops.push_back(LIR_lti); - B_II_ops.push_back(LIR_gti); - B_II_ops.push_back(LIR_lei); - B_II_ops.push_back(LIR_gei); - B_II_ops.push_back(LIR_ltui); - B_II_ops.push_back(LIR_gtui); - B_II_ops.push_back(LIR_leui); - B_II_ops.push_back(LIR_geui); - -#ifdef NANOJIT_64BIT - vector B_QQ_ops; - B_QQ_ops.push_back(LIR_eqq); - B_QQ_ops.push_back(LIR_ltq); - B_QQ_ops.push_back(LIR_gtq); - B_QQ_ops.push_back(LIR_leq); - B_QQ_ops.push_back(LIR_geq); - B_QQ_ops.push_back(LIR_ltuq); - B_QQ_ops.push_back(LIR_gtuq); - B_QQ_ops.push_back(LIR_leuq); - B_QQ_ops.push_back(LIR_geuq); -#endif - - vector B_DD_ops; - B_DD_ops.push_back(LIR_eqd); - B_DD_ops.push_back(LIR_ltd); - B_DD_ops.push_back(LIR_gtd); - B_DD_ops.push_back(LIR_led); - B_DD_ops.push_back(LIR_ged); - -#ifdef NANOJIT_64BIT - vector Q_I_ops; - Q_I_ops.push_back(LIR_i2q); - Q_I_ops.push_back(LIR_ui2uq); - - vector I_Q_ops; - I_Q_ops.push_back(LIR_q2i); -#endif - - vector D_I_ops; -#if !NJ_SOFTFLOAT_SUPPORTED - // Don't emit LIR_{ui,i}2d for soft-float platforms because the soft-float filter removes them. - D_I_ops.push_back(LIR_i2d); - D_I_ops.push_back(LIR_ui2d); -#elif defined(NANOJIT_ARM) - // The ARM back-end can detect FP support at run-time. - if (mParent.mConfig.arm_vfp) { - D_I_ops.push_back(LIR_i2d); - D_I_ops.push_back(LIR_ui2d); - } -#endif - - vector I_D_ops; -#if NJ_SOFTFLOAT_SUPPORTED - I_D_ops.push_back(LIR_dlo2i); - I_D_ops.push_back(LIR_dhi2i); -#endif -#if !NJ_SOFTFLOAT_SUPPORTED - // Don't emit LIR_d2i for soft-float platforms because the soft-float filter removes it. - I_D_ops.push_back(LIR_d2i); -#elif defined(NANOJIT_ARM) - // The ARM back-end can detect FP support at run-time. - if (mParent.mConfig.arm_vfp) { - I_D_ops.push_back(LIR_d2i); - } -#endif - -#ifdef NANOJIT_64BIT - vector Q_D_ops; - Q_D_ops.push_back(LIR_dasq); - - vector D_Q_ops; - D_Q_ops.push_back(LIR_qasd); -#endif - - vector D_II_ops; -#if NJ_SOFTFLOAT_SUPPORTED - D_II_ops.push_back(LIR_ii2d); -#endif - - vector I_loads; - I_loads.push_back(LIR_ldi); // weight LIR_ldi more heavily - I_loads.push_back(LIR_ldi); - I_loads.push_back(LIR_ldi); - I_loads.push_back(LIR_lduc2ui); - I_loads.push_back(LIR_ldus2ui); -#if NJ_EXPANDED_LOADSTORE_SUPPORTED - I_loads.push_back(LIR_ldc2i); - I_loads.push_back(LIR_lds2i); -#endif - -#ifdef NANOJIT_64BIT - vector Q_loads; - Q_loads.push_back(LIR_ldq); -#endif - - vector D_loads; - D_loads.push_back(LIR_ldd); -#if NJ_EXPANDED_LOADSTORE_SUPPORTED - // this loads a 32-bit float and expands it to 64-bit float - D_loads.push_back(LIR_ldf2d); -#endif - - enum LInsClass { -#define CL___(name, relFreq) name, -#include "LInsClasses.tbl" -#undef CL___ - LLAST - }; - - int relFreqs[LLAST]; - memset(relFreqs, 0, sizeof(relFreqs)); -#define CL___(name, relFreq) relFreqs[name] = relFreq; -#include "LInsClasses.tbl" -#undef CL___ - - int relFreqsSum = 0; // the sum of the individual relative frequencies - for (int c = 0; c < LLAST; c++) { - relFreqsSum += relFreqs[c]; - } - - // The number of times each LInsClass value appears in classGenerator[] - // matches 'relFreqs' (see LInsClasses.tbl). Eg. if relFreqs[LIMM_I] == - // 10, then LIMM_I appears in classGenerator[] 10 times. - LInsClass* classGenerator = new LInsClass[relFreqsSum]; - int j = 0; - for (int c = 0; c < LLAST; c++) { - for (int i = 0; i < relFreqs[c]; i++) { - classGenerator[j++] = LInsClass(c); - } - } - - // Used to keep track of how much stack we've explicitly used via - // LIR_allocp. We then need to keep some reserve for spills as well. - const size_t stackSzB = NJ_MAX_STACK_ENTRY * 4; - const size_t spillStackSzB = 1024; - const size_t maxExplicitlyUsedStackSzB = stackSzB - spillStackSzB; - size_t explicitlyUsedStackSzB = 0; - - // Do an 8-byte stack alloc right at the start so that loads and stores - // can be done immediately. - addOrReplace(M8ps, mLir->insAlloc(8)); - - int n = 0; - while (n < nIns) { - - LIns *ins; - - switch (classGenerator[rnd(relFreqsSum)]) { - - case LFENCE: - if (rnd(2)) { - mLir->ins0(LIR_regfence); - } else { - mLir->insGuard(LIR_xbarrier, NULL, createGuardRecord(createSideExit())); - } - n++; - break; - - case LALLOC: { - // The stack has a limited size, so we (a) don't want chunks to be - // too big, and (b) have to stop allocating them after a while. - size_t szB = 0; - switch (rnd(3)) { - case 0: szB = 4; break; - case 1: szB = 8; break; - case 2: szB = 4 * (rnd(6) + 3); break; // 12, 16, ..., 32 - } - if (explicitlyUsedStackSzB + szB <= maxExplicitlyUsedStackSzB) { - ins = mLir->insAlloc(szB); - // We add the result to Is/Qs so it can be used as an ordinary - // operand, and to M4s/M8ps so that loads/stores can be done from - // it. -#if defined NANOJIT_64BIT - addOrReplace(Qs, ins); -#else - addOrReplace(Is, ins); -#endif - if (szB == 4) - addOrReplace(M4s, ins); - else - addOrReplace(M8ps, ins); - - // It's possible that we will exceed maxExplicitlyUsedStackSzB - // by up to 28 bytes. Doesn't matter. - explicitlyUsedStackSzB += szB; - n++; - } - break; - } - - // For the immediates, we bias towards smaller numbers, especially 0 - // and 1 and small multiples of 4 which are common due to memory - // addressing. This puts some realistic stress on CseFilter. - case LIMM_I: { - int32_t immI = 0; // shut gcc up - switch (rnd(5)) { - case 0: immI = 0; break; - case 1: immI = 1; break; - case 2: immI = 4 * (rnd(256) + 1); break; // 4, 8, ..., 1024 - case 3: immI = rnd(19999) - 9999; break; // -9999..9999 - case 4: immI = rndI32(); break; // -RAND_MAX..RAND_MAX - } - ins = mLir->insImmI(immI); - addOrReplace(Is, ins); - n++; - break; - } - -#ifdef NANOJIT_64BIT - case LIMM_Q: { - uint64_t imm64 = 0; - switch (rnd(5)) { - case 0: imm64 = 0; break; - case 1: imm64 = 1; break; - case 2: imm64 = 4 * (rnd(256) + 1); break; // 4, 8, ..., 1024 - case 3: imm64 = rnd(19999) - 9999; break; // -9999..9999 - case 4: imm64 = uint64_t(rndU32()) << 32 | rndU32(); break; // possibly big! - } - ins = mLir->insImmQ(imm64); - addOrReplace(Qs, ins); - n++; - break; - } -#endif - - case LIMM_D: { - // We don't explicitly generate infinities and NaNs here, but they - // end up occurring due to ExprFilter evaluating expressions like - // divd(1,0) and divd(Infinity,Infinity). - double imm64f = 0; - switch (rnd(5)) { - case 0: imm64f = 0.0; break; - case 1: imm64f = 1.0; break; - case 2: - case 3: imm64f = double(rnd(1000)); break; // 0.0..9999.0 - case 4: - union { - double d; - uint64_t q; - } u; - u.q = uint64_t(rndU32()) << 32 | rndU32(); - imm64f = u.d; - break; - } - ins = mLir->insImmD(imm64f); - addOrReplace(Ds, ins); - n++; - break; - } - - case LOP_I_I: - if (!Is.empty()) { - ins = mLir->ins1(rndPick(I_I_ops), rndPick(Is)); - addOrReplace(Is, ins); - n++; - } - break; - - // case LOP_Q_Q: no instruction in this category - - case LOP_D_D: - if (!Ds.empty()) { - ins = mLir->ins1(rndPick(D_D_ops), rndPick(Ds)); - addOrReplace(Ds, ins); - n++; - } - break; - - case LOP_I_II: - if (!Is.empty()) { - LOpcode op = rndPick(I_II_ops); - LIns* lhs = rndPick(Is); - LIns* rhs = rndPick(Is); -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - if (op == LIR_divi || op == LIR_modi) { - // XXX: ExprFilter can't fold a div/mod with constant - // args, due to the horrible semantics of LIR_modi. So we - // just don't generate anything if we hit that case. - if (!lhs->isImmI() || !rhs->isImmI()) { - // If the divisor is positive, no problems. If it's zero, we get an - // exception. If it's -1 and the dividend is -2147483648 (-2^31) we get - // an exception (and this has been encountered in practice). So we only - // allow positive divisors, ie. compute: lhs / (rhs > 0 ? rhs : -k), - // where k is a random number in the range 2..100 (this ensures we have - // some negative divisors). - LIns* gt0 = mLir->ins2ImmI(LIR_gti, rhs, 0); - LIns* rhs2 = mLir->ins3(LIR_cmovi, gt0, rhs, mLir->insImmI(-((int32_t)rnd(99)) - 2)); - LIns* div = mLir->ins2(LIR_divi, lhs, rhs2); - if (op == LIR_divi) { - ins = div; - addOrReplace(Is, ins); - n += 5; - } else { - ins = mLir->ins1(LIR_modi, div); - // Add 'div' to the operands too so it might be used again, because - // the code generated is different as compared to the case where 'div' - // isn't used again. - addOrReplace(Is, div); - addOrReplace(Is, ins); - n += 6; - } - } - } else -#endif - { - ins = mLir->ins2(op, lhs, rhs); - addOrReplace(Is, ins); - n++; - } - } - break; - -#ifdef NANOJIT_64BIT - case LOP_Q_QQ: - if (!Qs.empty()) { - ins = mLir->ins2(rndPick(Q_QQ_ops), rndPick(Qs), rndPick(Qs)); - addOrReplace(Qs, ins); - n++; - } - break; - - case LOP_Q_QI: - if (!Qs.empty() && !Is.empty()) { - ins = mLir->ins2(rndPick(Q_QI_ops), rndPick(Qs), rndPick(Is)); - addOrReplace(Qs, ins); - n++; - } - break; -#endif - - case LOP_D_DD: - if (!Ds.empty()) { - ins = mLir->ins2(rndPick(D_DD_ops), rndPick(Ds), rndPick(Ds)); - addOrReplace(Ds, ins); - n++; - } - break; - - case LOP_I_BII: - if (!Bs.empty() && !Is.empty()) { - ins = mLir->ins3(rndPick(I_BII_ops), rndPick(Bs), rndPick(Is), rndPick(Is)); - addOrReplace(Is, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LOP_Q_BQQ: - if (!Bs.empty() && !Qs.empty()) { - ins = mLir->ins3(rndPick(Q_BQQ_ops), rndPick(Bs), rndPick(Qs), rndPick(Qs)); - addOrReplace(Qs, ins); - n++; - } - break; -#endif - - case LOP_D_BDD: - if (!Bs.empty() && !Ds.empty()) { - ins = mLir->ins3(rndPick(D_BDD_ops), rndPick(Bs), rndPick(Ds), rndPick(Ds)); - addOrReplace(Ds, ins); - n++; - } - break; - - case LOP_B_II: - if (!Is.empty()) { - ins = mLir->ins2(rndPick(B_II_ops), rndPick(Is), rndPick(Is)); - addOrReplace(Bs, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LOP_B_QQ: - if (!Qs.empty()) { - ins = mLir->ins2(rndPick(B_QQ_ops), rndPick(Qs), rndPick(Qs)); - addOrReplace(Bs, ins); - n++; - } - break; -#endif - - case LOP_B_DD: - if (!Ds.empty()) { - ins = mLir->ins2(rndPick(B_DD_ops), rndPick(Ds), rndPick(Ds)); - // XXX: we don't push the result, because most (all?) of the - // backends currently can't handle cmovs/qcmovs that take - // float comparisons for the test (see bug 520944). This means - // that all B_DD values are dead, unfortunately. - //addOrReplace(Bs, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LOP_Q_I: - if (!Is.empty()) { - ins = mLir->ins1(rndPick(Q_I_ops), rndPick(Is)); - addOrReplace(Qs, ins); - n++; - } - break; -#endif - - case LOP_D_I: - if (!Is.empty() && !D_I_ops.empty()) { - ins = mLir->ins1(rndPick(D_I_ops), rndPick(Is)); - addOrReplace(Ds, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LOP_I_Q: - if (!Qs.empty()) { - ins = mLir->ins1(rndPick(I_Q_ops), rndPick(Qs)); - addOrReplace(Is, ins); - n++; - } - break; -#endif - - case LOP_I_D: -// XXX: NativeX64 doesn't implement qhi yet (and it may not need to). -#if !defined NANOJIT_X64 - if (!Ds.empty()) { - ins = mLir->ins1(rndPick(I_D_ops), rndPick(Ds)); - addOrReplace(Is, ins); - n++; - } -#endif - break; - -#if defined NANOJIT_X64 - case LOP_Q_D: - if (!Ds.empty()) { - ins = mLir->ins1(rndPick(Q_D_ops), rndPick(Ds)); - addOrReplace(Qs, ins); - n++; - } - break; - - case LOP_D_Q: - if (!Qs.empty()) { - ins = mLir->ins1(rndPick(D_Q_ops), rndPick(Qs)); - addOrReplace(Ds, ins); - n++; - } - break; -#endif - - case LOP_D_II: - if (!Is.empty() && !D_II_ops.empty()) { - ins = mLir->ins2(rndPick(D_II_ops), rndPick(Is), rndPick(Is)); - addOrReplace(Ds, ins); - n++; - } - break; - - case LLD_I: { - vector Ms = rnd(2) ? M4s : M8ps; - if (!Ms.empty()) { - LIns* base = rndPick(Ms); - ins = mLir->insLoad(rndPick(I_loads), base, rndOffset32(base->size()), ACCSET_OTHER); - addOrReplace(Is, ins); - n++; - } - break; - } - -#ifdef NANOJIT_64BIT - case LLD_Q: - if (!M8ps.empty()) { - LIns* base = rndPick(M8ps); - ins = mLir->insLoad(rndPick(Q_loads), base, rndOffset64(base->size()), ACCSET_OTHER); - addOrReplace(Qs, ins); - n++; - } - break; -#endif - - case LLD_D: - if (!M8ps.empty()) { - LIns* base = rndPick(M8ps); - ins = mLir->insLoad(rndPick(D_loads), base, rndOffset64(base->size()), ACCSET_OTHER); - addOrReplace(Ds, ins); - n++; - } - break; - - case LST_I: { - vector Ms = rnd(2) ? M4s : M8ps; - if (!Ms.empty() && !Is.empty()) { - LIns* base = rndPick(Ms); - mLir->insStore(rndPick(Is), base, rndOffset32(base->size()), ACCSET_OTHER); - n++; - } - break; - } - -#ifdef NANOJIT_64BIT - case LST_Q: - if (!M8ps.empty() && !Qs.empty()) { - LIns* base = rndPick(M8ps); - mLir->insStore(rndPick(Qs), base, rndOffset64(base->size()), ACCSET_OTHER); - n++; - } - break; -#endif - - case LST_D: - if (!M8ps.empty() && !Ds.empty()) { - LIns* base = rndPick(M8ps); - mLir->insStore(rndPick(Ds), base, rndOffset64(base->size()), ACCSET_OTHER); - n++; - } - break; - - case LCALL_I_I1: - if (!Is.empty()) { - LIns* args[1] = { rndPick(Is) }; - ins = mLir->insCall(&ci_I_I1, args); - addOrReplace(Is, ins); - n++; - } - break; - - case LCALL_I_I6: - if (!Is.empty()) { - LIns* args[6] = { rndPick(Is), rndPick(Is), rndPick(Is), - rndPick(Is), rndPick(Is), rndPick(Is) }; - ins = mLir->insCall(&ci_I_I6, args); - addOrReplace(Is, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LCALL_Q_Q2: - if (!Qs.empty()) { - LIns* args[2] = { rndPick(Qs), rndPick(Qs) }; - ins = mLir->insCall(&ci_Q_Q2, args); - addOrReplace(Qs, ins); - n++; - } - break; - - case LCALL_Q_Q7: - if (!Qs.empty()) { - LIns* args[7] = { rndPick(Qs), rndPick(Qs), rndPick(Qs), rndPick(Qs), - rndPick(Qs), rndPick(Qs), rndPick(Qs) }; - ins = mLir->insCall(&ci_Q_Q7, args); - addOrReplace(Qs, ins); - n++; - } - break; -#endif - - case LCALL_D_D3: - if (!Ds.empty()) { - LIns* args[3] = { rndPick(Ds), rndPick(Ds), rndPick(Ds) }; - ins = mLir->insCall(&ci_F_F3, args); - addOrReplace(Ds, ins); - n++; - } - break; - - case LCALL_D_D8: - if (!Ds.empty()) { - LIns* args[8] = { rndPick(Ds), rndPick(Ds), rndPick(Ds), rndPick(Ds), - rndPick(Ds), rndPick(Ds), rndPick(Ds), rndPick(Ds) }; - ins = mLir->insCall(&ci_F_F8, args); - addOrReplace(Ds, ins); - n++; - } - break; - -#ifdef NANOJIT_64BIT - case LCALL_V_IQD: - if (!Is.empty() && !Qs.empty() && !Ds.empty()) { - // Nb: args[] holds the args in reverse order... sigh. - LIns* args[3] = { rndPick(Ds), rndPick(Qs), rndPick(Is) }; - ins = mLir->insCall(&ci_V_IQF, args); - n++; - } - break; -#endif - - case LLABEL: - // Although no jumps are generated yet, labels are important - // because they delimit areas where CSE can be applied. Without - // them, CSE can be applied over very long regions, which leads to - // values that have very large live ranges, which leads to stack - // overflows. - mLir->ins0(LIR_label); - n++; - break; - - default: - NanoAssert(0); - break; - } - } - - delete[] classGenerator; - - // Return 0. - mReturnTypeBits |= RT_INT; - mLir->ins1(LIR_reti, mLir->insImmI(0)); - - endFragment(); -} - -Lirasm::Lirasm(bool verbose, Config& config) : - mConfig(config), - mAssm(mCodeAlloc, mAlloc, mAlloc, &mLogc, mConfig) -{ - mVerbose = verbose; - mLogc.lcbits = 0; - - mLirbuf = new (mAlloc) LirBuffer(mAlloc); -#ifdef DEBUG - if (mVerbose) { - mLogc.lcbits = LC_ReadLIR | LC_AfterDCE | LC_Native | LC_RegAlloc | LC_Activation; - mLirbuf->printer = new (mAlloc) LInsPrinter(mAlloc, LIRASM_NUM_USED_ACCS); - } -#endif - - // Populate the mOpMap table. -#define OP___(op, number, repKind, retType, isCse) \ - mOpMap[#op] = LIR_##op; -#include "nanojit/LIRopcode.tbl" -#undef OP___ - - // XXX: could add more pointer-sized synonyms here - mOpMap["paramp"] = mOpMap[PTR_SIZE("parami", "paramq")]; - mOpMap["livep"] = mOpMap[PTR_SIZE("livei", "liveq")]; -} - -Lirasm::~Lirasm() -{ - Fragments::iterator i; - for (i = mFragments.begin(); i != mFragments.end(); ++i) { - delete i->second.fragptr; - } -} - - -bool -Lirasm::lookupFunction(const string &name, CallInfo *&ci) -{ - const size_t nfuns = sizeof(functions) / sizeof(functions[0]); - for (size_t i = 0; i < nfuns; i++) { - if (name == functions[i].name) { - *ci = functions[i].callInfo; - return true; - } - } - - Fragments::const_iterator func = mFragments.find(name); - if (func != mFragments.end()) { - // The ABI, arg types and ret type will be overridden by the caller. - if (func->second.mReturnType == RT_DOUBLE) { - CallInfo target = {(uintptr_t) func->second.rdouble, - 0, ABI_FASTCALL, /*isPure*/0, ACCSET_STORE_ANY - verbose_only(, func->first.c_str()) }; - *ci = target; - - } else { - CallInfo target = {(uintptr_t) func->second.rint, - 0, ABI_FASTCALL, /*isPure*/0, ACCSET_STORE_ANY - verbose_only(, func->first.c_str()) }; - *ci = target; - } - return false; - - } else { - bad("invalid function reference " + name); - return false; - } -} - -void -Lirasm::assemble(istream &in, bool optimize) -{ - LirTokenStream ts(in); - bool first = true; - - LirToken token; - while (ts.get(token)) { - - if (token.type == NEWLINE) - continue; - if (token.type != NAME) - bad("unexpected token '" + token.data + "'"); - - const string &op = token.data; - if (op == ".patch") { - handlePatch(ts); - } else if (op == ".begin") { - string name; - if (!ts.getName(name)) - bad("expected fragment name after .begin"); - if (!ts.eat(NEWLINE)) - bad("extra junk after .begin " + name); - - FragmentAssembler assembler(*this, name, optimize); - assembler.assembleFragment(ts, false, NULL); - first = false; - } else if (op == ".end") { - bad(".end without .begin"); - } else if (first) { - FragmentAssembler assembler(*this, "main", optimize); - assembler.assembleFragment(ts, true, &token); - break; - } else { - bad("unexpected stray opcode '" + op + "'"); - } - } -} - -void -Lirasm::assembleRandom(int nIns, bool optimize) -{ - string name = "main"; - FragmentAssembler assembler(*this, name, optimize); - assembler.assembleRandomFragment(nIns); -} - -void -Lirasm::handlePatch(LirTokenStream &in) -{ - string src, fragName, guardName, destName; - - if (!in.getName(src) || !in.eat(PUNCT, "->") || !in.getName(destName)) - bad("incorrect syntax"); - - // Break the src at '.'. This is awkward but the syntax looks nice. - size_t j = src.find('.'); - if (j == string::npos || j == 0 || j == src.size() - 1) - bad("incorrect syntax"); - fragName = src.substr(0, j); - guardName = src.substr(j + 1); - - Fragments::iterator i; - if ((i=mFragments.find(fragName)) == mFragments.end()) - bad("invalid fragment reference"); - LirasmFragment *frag = &i->second; - if (frag->mLabels.find(guardName) == frag->mLabels.end()) - bad("invalid guard reference"); - LIns *ins = frag->mLabels.find(guardName)->second; - if ((i=mFragments.find(destName)) == mFragments.end()) - bad("invalid guard reference"); - ins->record()->exit->target = i->second.fragptr; - - mAssm.patch(ins->record()->exit); -} - -void -usageAndQuit(const string& progname) -{ - cout << - "usage: " << progname << " [options] [filename]\n" - "Options:\n" - " -h --help print this message\n" - " -v --verbose print LIR and assembly code\n" - " --execute execute LIR\n" - " --[no-]optimize enable or disable optimization of the LIR (default=off)\n" - " --random [N] generate a random LIR block of size N (default=100)\n" - " --stkskip [N] push approximately N Kbytes of stack before execution (default=100)\n" - "\n" - "Build query options (these print a value for this build of lirasm and exit)\n" - " --show-arch show the architecture ('i386', 'X64', 'arm', 'ppc',\n" - " 'sparc', 'mips', or 'sh4')\n" - " --show-word-size show the word size ('32' or '64')\n" - " --show-endianness show the endianness ('little-endian' or 'big-endian')\n" - "\n" - "i386-specific options:\n" - " --[no]sse use SSE2 instructions (default=on)\n" - "\n" - "ARM-specific options:\n" - " --arch N use ARM architecture version N instructions (default=7)\n" - " --[no]vfp use ARM VFP instructions (default=on)\n" - "\n" - ; - exit(0); -} - -void -errMsgAndQuit(const string& progname, const string& msg) -{ - cerr << progname << ": " << msg << endl; - exit(1); -} - -struct CmdLineOptions { - string progname; - bool verbose; - bool execute; - bool optimize; - int random; - int stkskip; - string filename; - Config config; -}; - - -bool parseOptionalInt(int argc, char** argv, int* i, int* value, int defaultValue) -{ - if (*i == argc - 1) { - *value = defaultValue; // no numeric argument, use default - } else { - char* endptr; - int res = strtol(argv[*i+1], &endptr, 10); - if ('\0' == *endptr) { - // We don't bother checking for overflow. - if (res <= 0) { - return false; - } - *value = res; // next arg is a number, use that for the value - (*i)++; - } else { - *value = defaultValue; // next arg is not a number - } - } - return true; -} - -static void -processCmdLine(int argc, char **argv, CmdLineOptions& opts) -{ - opts.progname = argv[0]; - opts.verbose = false; - opts.execute = false; - opts.random = 0; - opts.optimize = false; - opts.stkskip = 0; - - // Architecture-specific options. -#if defined NANOJIT_IA32 - bool i386_sse = true; -#elif defined NANOJIT_ARM - unsigned int arm_arch = 7; - bool arm_vfp = true; -#endif - - for (int i = 1; i < argc; i++) { - string arg = argv[i]; - - // Common flags for every architecture. - if (arg == "-h" || arg == "--help") - usageAndQuit(opts.progname); - else if (arg == "-v" || arg == "--verbose") - opts.verbose = true; - else if (arg == "--execute") - opts.execute = true; - else if (arg == "--optimize") - opts.optimize = true; - else if (arg == "--no-optimize") - opts.optimize = false; - else if (arg == "--random") { - if (!parseOptionalInt(argc, argv, &i, &opts.random, 100)) - errMsgAndQuit(opts.progname, "--random argument must be greater than zero"); - } - else if (arg == "--stkskip") { - if (!parseOptionalInt(argc, argv, &i, &opts.stkskip, 100)) - errMsgAndQuit(opts.progname, "--stkskip argument must be greater than zero"); - } - else if (arg == "--show-arch") { - const char* str = -#if defined NANOJIT_IA32 - "i386"; -#elif defined NANOJIT_X64 - "X64"; -#elif defined NANOJIT_ARM - "arm"; -#elif defined NANOJIT_PPC - "ppc"; -#elif defined NANOJIT_SPARC - "sparc"; -#elif defined NANOJIT_MIPS - "mips"; -#elif defined NANOJIT_SH4 - "sh4"; -#else -# error "unknown arch" -#endif - cout << str << "\n"; - exit(0); - } - else if (arg == "--show-word-size") { - cout << sizeof(void*) * 8 << "\n"; - exit(0); - } - else if (arg == "--show-endianness") { - int32_t x = 0x01020304; - if (*(char*)&x == 0x1) { - cout << "big-endian" << "\n"; - } else { - cout << "little-endian" << "\n"; - } - exit(0); - } - - // Architecture-specific flags. -#if defined NANOJIT_IA32 - else if (arg == "--sse") { - i386_sse = true; - } - else if (arg == "--nosse") { - i386_sse = false; - } -#elif defined NANOJIT_ARM - else if ((arg == "--arch") && (i < argc-1)) { - char* endptr; - arm_arch = strtoul(argv[i+1], &endptr, 10); - // Check that the argument was a number. - if ('\0' == *endptr) { - if ((arm_arch < 4) || (arm_arch > 7)) { - errMsgAndQuit(opts.progname, "Unsupported argument to --arch.\n"); - } - } else { - errMsgAndQuit(opts.progname, "Unrecognized argument to --arch.\n"); - } - i++; - } else if (arg == "--vfp") { - arm_vfp = true; - } else if (arg == "--novfp") { - arm_vfp = false; - } -#endif - // Input file names. - else if (arg[0] != '-') { - if (opts.filename.empty()) - opts.filename = arg; - else - errMsgAndQuit(opts.progname, "you can only specify one filename"); - } - // No matching flag found, so report the error. - else - errMsgAndQuit(opts.progname, "bad option: " + arg); - } - - if ((!opts.random && opts.filename.empty()) || (opts.random && !opts.filename.empty())) - errMsgAndQuit(opts.progname, - "you must specify either a filename or --random (but not both)"); - - // Handle the architecture-specific options. -#if defined NANOJIT_IA32 - opts.config.i386_use_cmov = opts.config.i386_sse2 = i386_sse; - opts.config.i386_fixed_esp = true; -#elif defined NANOJIT_ARM - // Warn about untested configurations. - if ( ((arm_arch == 5) && (arm_vfp)) || ((arm_arch >= 6) && (!arm_vfp)) ) { - char const * vfp_string = (arm_vfp) ? ("VFP") : ("no VFP"); - cerr << "Warning: This configuration (ARMv" << arm_arch << ", " << vfp_string << ") " << - "is not regularly tested." << endl; - } - - opts.config.arm_arch = arm_arch; - opts.config.arm_vfp = arm_vfp; - opts.config.soft_float = !arm_vfp; -#endif -} - -int32_t* dummy; - -void -executeFragment(const LirasmFragment& fragment, int skip) -{ - // Allocate a large frame, and make sure we don't optimize it away. - int32_t space[512]; - dummy = space; - - if (skip > 0) { - executeFragment(fragment, skip-1); - } else { - switch (fragment.mReturnType) { - case RT_INT: { - int res = fragment.rint(); - cout << "Output is: " << res << endl; - break; - } -#ifdef NANOJIT_64BIT - case RT_QUAD: { - int res = fragment.rquad(); - cout << "Output is: " << res << endl; - break; - } -#endif - case RT_DOUBLE: { - double res = fragment.rdouble(); - cout << "Output is: " << res << endl; - break; - } - case RT_GUARD: { - LasmSideExit *ls = (LasmSideExit*) fragment.rguard()->exit; - cout << "Exited block on line: " << ls->line << endl; - break; - } - } - } -} - -int -main(int argc, char **argv) -{ - CmdLineOptions opts; - processCmdLine(argc, argv, opts); - - Lirasm lasm(opts.verbose, opts.config); - if (opts.random) { - lasm.assembleRandom(opts.random, opts.optimize); - } else { - ifstream in(opts.filename.c_str()); - if (!in) - errMsgAndQuit(opts.progname, "unable to open file " + opts.filename); - lasm.assemble(in, opts.optimize); - } - - Fragments::const_iterator i; - if (opts.execute) { - i = lasm.mFragments.find("main"); - if (i == lasm.mFragments.end()) - errMsgAndQuit(opts.progname, "error: at least one fragment must be named 'main'"); - executeFragment(i->second, opts.stkskip); - } else { - for (i = lasm.mFragments.begin(); i != lasm.mFragments.end(); i++) - dump_srecords(cout, i->second.fragptr); - } -} diff --git a/deps/mozjs/js/src/lirasm/testlirc.sh b/deps/mozjs/js/src/lirasm/testlirc.sh deleted file mode 100755 index 7dbb20ae053..00000000000 --- a/deps/mozjs/js/src/lirasm/testlirc.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash - -set -eu - -exitcode=0 - -LIRASM=$1 - -TESTS_DIR=`dirname "$0"`/tests - -function runtest { - local infile=$1 - local options=${2-} - - # Catch a request for the random tests. - if [[ $infile == --random* ]] ; then - local outfile=$TESTS_DIR/random.out - else - local outfile=`echo $infile | sed 's/\.in/\.out/'` - fi - - if [[ ! -e "$outfile" ]] ; then - echo "$0: error: no out file $outfile" - exit 1 - fi - - # sed used to strip extra leading zeros from exponential values 'e+00' (see bug 602786) - if $LIRASM $options --execute $infile | tr -d '\r' | sed -e 's/e+00*/e+0/g' > testoutput.txt && cmp -s testoutput.txt $outfile ; then - echo "TEST-PASS | lirasm | lirasm $options --execute $infile" - else - echo "TEST-UNEXPECTED-FAIL | lirasm | lirasm $options --execute $infile" - echo "expected output" - cat $outfile - echo "actual output" - cat testoutput.txt - exitcode=1 - fi -} - -function runtests { - local testdir=$1 - local options=${2-} - for infile in "$TESTS_DIR"/"$testdir"/*.in ; do - runtest $infile "$options" - done -} - -if [[ $($LIRASM --show-arch 2>/dev/null) == "i386" ]] ; then - # i386 with SSE2. - runtests "." - runtests "hardfloat" - runtests "32-bit" - runtests "littleendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - - # i386 without SSE2. - runtests "." "--nosse" - runtests "hardfloat" "--nosse" - runtests "32-bit" "--nosse" - runtests "littleendian" "--nosse" - runtest "--random 1000000" "--nosse" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "X64" ]] ; then - # X64. - runtests "." - runtests "hardfloat" - runtests "64-bit" - runtests "littleendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "arm" ]] ; then - # ARMv7 with VFP. We could test without VFP but such a platform seems - # unlikely. ARM is bi-endian but usually configured as little-endian. - runtests "." - runtests "hardfloat" - runtests "32-bit" - runtests "littleendian" - runtest "--random 1000000" - runtest "--random 1000000" "--optimize" - - # ARMv6 with VFP. ARMv6 without VFP doesn't seem worth testing. - # Random tests are reduced. - runtests "." "--arch 6" - runtests "hardfloat" "--arch 6" - runtests "32-bit" "--arch 6" - runtests "littleendian" "--arch 6" - runtest "--random 100000" "--arch 6" - - # ARMv5 without VFP. ARMv5 with VFP doesn't seem worth testing. - # Random tests are reduced. - runtests "." "--arch 5 --novfp" - runtests "softfloat" "--arch 5 --novfp" - runtests "32-bit" "--arch 5 --novfp" - runtests "littleendian" "--arch 5 --novfp" - runtest "--random 100000" "--arch 5 --novfp" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "ppc" ]] ; then - # PPC is bi-endian but usually configured as big-endian. - runtests "." - runtests "hardfloat" - if [[ $($LIRASM --show-word-size) == "32" ]] ; then - runtests "32-bit" - else - runtests "64-bit" - fi - runtests "bigendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "sparc" ]] ; then - # Sparc is bi-endian but usually configured as big-endian. - runtests "." - runtests "hardfloat" - runtests "32-bit" - runtests "bigendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "mips" ]] ; then - # MIPS is bi-endian but usually configured as big-endian. - # XXX: should we run softfloat tests as well? Seems to depend on - # the configuration... - runtests "." - runtests "hardfloat" - runtests "32-bit" - runtests "bigendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - -elif [[ $($LIRASM --show-arch 2>/dev/null) == "sh4" ]] ; then - # SH4 is bi-endian but usually configured as big-endian. - runtests "." - runtests "hardfloat" - runtests "32-bit" - runtests "bigendian" - runtest "--random 1000000" - runtest "--random 1000000 --optimize" - -else - echo "bad arch" - exit 1 -fi - -rm testoutput.txt - -exit $exitcode diff --git a/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.in b/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.in deleted file mode 100644 index f92ae19beb5..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.in +++ /dev/null @@ -1,31 +0,0 @@ -.begin many_params -pa = parami 0 0 -pb = parami 1 0 -pc = parami 2 0 -pd = parami 3 0 -pe = parami 4 0 -pf = parami 5 0 -pg = parami 6 0 -ph = parami 7 0 -res1 = addi pa pb -res2 = addi res1 pc -res3 = addi res2 pd -res4 = addi res3 pe -res5 = addi res4 pf -res6 = addi res5 pg -res7 = addi res6 ph -reti res7 -.end - -.begin main -a = immi 1 -b = immi 2 -c = immi 3 -d = immi 4 -e = immi 5 -f = immi 6 -g = immi 7 -h = immi 8 -res = calli many_params fastcall a b c d e f g h -reti res -.end diff --git a/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.out b/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.out deleted file mode 100644 index 541c0fd9fd7..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/32-bit/many_params.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 36 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.in b/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.in deleted file mode 100644 index c12a3c6e91d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.in +++ /dev/null @@ -1,8 +0,0 @@ -q = immq 12345 -d = qasd q -q2 = dasq d -one = immd 1.0 ; do some intermediate stuff to make it less trivial -two = immd 2.0 -three = addd one two -i2 = q2i q2 -reti i2 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.out b/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.out deleted file mode 100644 index 8c7a20845fb..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/dasq.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 12345 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.in b/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.in deleted file mode 100644 index fe57a689f82..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.in +++ /dev/null @@ -1,7 +0,0 @@ -one = immq 1 -d = immd 123.45 -q = dasq d -q2 = addq q one ; do some intermediate stuff just to complicate things -q3 = subq q2 one -d2 = qasd q3 -retd d2 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.out b/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.out deleted file mode 100644 index f70b33e9302..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/qasd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 123.45 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/shq.in b/deps/mozjs/js/src/lirasm/tests/64-bit/shq.in deleted file mode 100644 index 7e9686a03d5..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/shq.in +++ /dev/null @@ -1,32 +0,0 @@ -; Only the bottom 6 bits of the shift amount in lshq/rshq/rshuq are used. - -two = immq 2 - -sh1 = immi 1 -sh2 = immi 65 ; 0100_0001b -sh3 = immi 268435393 ; 0000_1111_1111_1111_1111_1111_1100_0001b - -a1 = lshq two sh1 ; --> 4 -a2 = lshq two sh2 ; --> 4 -a3 = lshq two sh3 ; --> 4 - -b1 = rshq two sh1 ; --> 1 -b2 = rshq two sh2 ; --> 1 -b3 = rshq two sh3 ; --> 1 - -c1 = rshuq two sh1 ; --> 1 -c2 = rshuq two sh2 ; --> 1 -c3 = rshuq two sh3 ; --> 1 - -s0 = immq 0 -s1 = addq s0 a1 -s2 = addq s1 a2 -s3 = addq s2 a3 -s4 = addq s3 b1 -s5 = addq s4 b2 -s6 = addq s5 b3 -s7 = addq s6 c1 -s8 = addq s7 c2 -s9 = addq s8 c2 ; --> 18 - -retq s9 diff --git a/deps/mozjs/js/src/lirasm/tests/64-bit/shq.out b/deps/mozjs/js/src/lirasm/tests/64-bit/shq.out deleted file mode 100644 index 5b7e7754b5d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/64-bit/shq.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 18 diff --git a/deps/mozjs/js/src/lirasm/tests/add.in b/deps/mozjs/js/src/lirasm/tests/add.in deleted file mode 100644 index 70b723d8b0e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/add.in +++ /dev/null @@ -1,4 +0,0 @@ -two = immi 2 -three = immi 3 -res = addi two three -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/add.out b/deps/mozjs/js/src/lirasm/tests/add.out deleted file mode 100644 index 3af94829617..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/add.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/addd.in b/deps/mozjs/js/src/lirasm/tests/addd.in deleted file mode 100644 index 6972068afe9..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addd.in +++ /dev/null @@ -1,31 +0,0 @@ -; Try to exercise as many different possibilities for the register allocator as -; is feasible. - -p1 = allocp 8 -p2 = allocp 8 - -d1a = immd 1.5 -d1 = addd d1a d1a ; X = X + X -std d1 p1 0 - -d2a = immd 2.5 -d2b = immd 3.0 -d2 = addd d2a d2b ; X = X + Y -std d2b p2 0 -std d2 p2 0 - -d3a = ldd p1 0 -d3b = ldd p2 0 -d3 = addd d3a d3b ; X = Y + X -std d3a p2 0 -std d3 p2 0 - -d4a = ldd p2 0 -d4b = ldd p1 0 -d4 = addd d4a d4b ; X = Y + Z -std d4a p1 0 -std d4b p2 0 -std d4 p1 0 - -d = ldd p1 0 -retd d diff --git a/deps/mozjs/js/src/lirasm/tests/addd.out b/deps/mozjs/js/src/lirasm/tests/addd.out deleted file mode 100644 index 9108cf23357..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 11.5 diff --git a/deps/mozjs/js/src/lirasm/tests/addjovi.in b/deps/mozjs/js/src/lirasm/tests/addjovi.in deleted file mode 100644 index f2e1de527a3..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addjovi.in +++ /dev/null @@ -1,14 +0,0 @@ - ptr = allocp 8 - - a = immi 2147483647 - b = immi 0 - c = addjovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res diff --git a/deps/mozjs/js/src/lirasm/tests/addjovi.out b/deps/mozjs/js/src/lirasm/tests/addjovi.out deleted file mode 100644 index f475781fc73..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addjovi.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147483647 diff --git a/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.in b/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.in deleted file mode 100644 index de0cc2569b2..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.in +++ /dev/null @@ -1,14 +0,0 @@ - ptr = allocp 8 - - a = immi 2147483647 - b = immi 1 - c = addjovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res diff --git a/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.out b/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.out deleted file mode 100644 index 5404b11a606..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addjovi_ovf.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 12345678 diff --git a/deps/mozjs/js/src/lirasm/tests/addsub.in b/deps/mozjs/js/src/lirasm/tests/addsub.in deleted file mode 100644 index db641c30088..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addsub.in +++ /dev/null @@ -1,5 +0,0 @@ -two = immi 7 -three = immi 3 -targ = addi two three -res = subi targ three -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/addsub.out b/deps/mozjs/js/src/lirasm/tests/addsub.out deleted file mode 100644 index 3795758937a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/addsub.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 7 diff --git a/deps/mozjs/js/src/lirasm/tests/backjump.in b/deps/mozjs/js/src/lirasm/tests/backjump.in deleted file mode 100644 index de54e5eb5b2..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/backjump.in +++ /dev/null @@ -1,17 +0,0 @@ - ptr = allocp 8 - zero = immi 0 - one = immi 1 - neg_one = immi -1 - sti one ptr 0 - sti zero ptr 4 -start: a = ldi ptr 0 - x = ldi ptr 4 - b = muli a neg_one - y = addi x one - sti y ptr 4 - sti b ptr 0 - t = eqi b one - jf t start -end: reti y - - diff --git a/deps/mozjs/js/src/lirasm/tests/backjump.out b/deps/mozjs/js/src/lirasm/tests/backjump.out deleted file mode 100644 index ca4935610b4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/backjump.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2 diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.in b/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.in deleted file mode 100644 index 2065f31e64f..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.in +++ /dev/null @@ -1,5 +0,0 @@ -base = allocp 512 -five = immi 5 -sti five base 256 -x = ldus2ui base 258 -reti x diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.out b/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.out deleted file mode 100644 index 3af94829617..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/fuzz-527178.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.in b/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.in deleted file mode 100644 index 24233eed2f7..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi 1288908529 ; 0x4cd32ef1 -p = allocp 4 -sti full p 0 - -n0chk = immi -15 ; sign_extend(0xf1) -n1chk = immi 46 ; sign_extend(0x2e) -n2chk = immi -45 ; sign_extend(0xd3) -n3chk = immi 76 ; sign_extend(0x4c) - -n0 = ldc2i p 3 -n1 = ldc2i p 2 -n2 = ldc2i p 1 -n3 = ldc2i p 0 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.out b/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/ldc2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.in b/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.in deleted file mode 100644 index 1b179294f5b..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi -3805 ; sign_extend(0xf123) - -n0 = lds2i p 2 -n1 = lds2i p 0 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.out b/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/lds2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.in b/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.in deleted file mode 100644 index dd767636b7a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi -992746767 ; 0xc4d3e2f1 -p = allocp 4 -sti full p 0 - -n0chk = immi 241 ; 0xf1 -n1chk = immi 226 ; 0xe2 -n2chk = immi 211 ; 0xd3 -n3chk = immi 196 ; 0xc4 - -n0 = lduc2ui p 3 -n1 = lduc2ui p 2 -n2 = lduc2ui p 1 -n3 = lduc2ui p 0 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.out b/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/lduc2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.in b/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.in deleted file mode 100644 index c4351c7b3ff..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi 61731 ; sign_extend(0xf123) - -n0 = ldus2ui p 2 -n1 = ldus2ui p 0 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.out b/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bigendian/ldus2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/bug596923.in b/deps/mozjs/js/src/lirasm/tests/bug596923.in deleted file mode 100644 index 67c18dd9471..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bug596923.in +++ /dev/null @@ -1,17 +0,0 @@ -; Try to get asm_load64 (ldd) to store straight to a stack slot with a large -; offset from FP (on ARM, at least). - -a = immd 1.1 -p = allocp 1024 -std a p 0 - -; Emit the load just before a regfence, so the result does not have a register -; assigned. -d = ldd p 0 -regfence - -retd d - -; Ensure that 'p' is live here, so it gets a stack slot before 'd' does (and so -; extends the range required to store out 'd'). -livep p diff --git a/deps/mozjs/js/src/lirasm/tests/bug596923.out b/deps/mozjs/js/src/lirasm/tests/bug596923.out deleted file mode 100644 index bac53040870..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bug596923.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1.1 diff --git a/deps/mozjs/js/src/lirasm/tests/bug643969.in b/deps/mozjs/js/src/lirasm/tests/bug643969.in deleted file mode 100644 index 4be05294da2..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bug643969.in +++ /dev/null @@ -1,27 +0,0 @@ - - ptr = allocp 8 - j start - -success: zero = immd 0.0 - retd zero - - - ; do a store+load so we don't optimize away the test -start: a = immd 1.0 - b = immd 2.0 - c = addd a b - std c ptr 0 - - expect = immd 4.0 - actual = ldd ptr 0 - - ; Test is for this code pattern, i386/x64 backend - ; could generate bad code for the != case of LIR_jf - ; if the branch is backwards. (Note that we compare 3.0 - ; to 4.0 because we need the test to fail.) - cond = eqd expect actual - jf cond success - - bad = immd -1.0 - retd bad - diff --git a/deps/mozjs/js/src/lirasm/tests/bug643969.out b/deps/mozjs/js/src/lirasm/tests/bug643969.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/bug643969.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/call1.in b/deps/mozjs/js/src/lirasm/tests/call1.in deleted file mode 100644 index ca65f6cf690..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/call1.in +++ /dev/null @@ -1,12 +0,0 @@ -ptr = allocp 8 -a = immi 65 -sti2c a ptr 0 -b = immi 66 -sti2c b ptr 1 -c = immi 67 -sti2c c ptr 2 -zero = immi 0 -sti2c zero ptr 3 -ss = calli puts cdecl ptr -nn = gei ss zero -reti nn diff --git a/deps/mozjs/js/src/lirasm/tests/call1.out b/deps/mozjs/js/src/lirasm/tests/call1.out deleted file mode 100644 index bf667e19442..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/call1.out +++ /dev/null @@ -1,2 +0,0 @@ -ABC -Output is: 1 diff --git a/deps/mozjs/js/src/lirasm/tests/call2.in b/deps/mozjs/js/src/lirasm/tests/call2.in deleted file mode 100644 index 39357e0ee02..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/call2.in +++ /dev/null @@ -1,5 +0,0 @@ -pi = immd 3.14 -half = immd 0.5 -halfpi = muld pi half -res = calld sin cdecl halfpi -retd res diff --git a/deps/mozjs/js/src/lirasm/tests/call2.out b/deps/mozjs/js/src/lirasm/tests/call2.out deleted file mode 100644 index 25ee5c06706..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/call2.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1 diff --git a/deps/mozjs/js/src/lirasm/tests/calld1.in b/deps/mozjs/js/src/lirasm/tests/calld1.in deleted file mode 100644 index 02b5ed90bc0..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/calld1.in +++ /dev/null @@ -1,11 +0,0 @@ -a = immd 1.1; -b = immd 2.2 -c = immd 3.3; -d = immd 4.4; -e = immd 5.5; -f = immd 6.6; -g = immd 7.7; -h = immd 8.8; - -res = calld calld1 cdecl a b c d e f g h -retd res diff --git a/deps/mozjs/js/src/lirasm/tests/calld1.out b/deps/mozjs/js/src/lirasm/tests/calld1.out deleted file mode 100644 index feb78937370..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/calld1.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -62.9667 diff --git a/deps/mozjs/js/src/lirasm/tests/callid1.in b/deps/mozjs/js/src/lirasm/tests/callid1.in deleted file mode 100644 index 3b5358b4ad2..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid1.in +++ /dev/null @@ -1,10 +0,0 @@ -i = immi -1; -j = immi 3; -k = immi 6; -x = immd 1.1; -y = immd 3.3; -z = immd 4.4; - -res = calld callid1 cdecl i x y j k z -retd res - diff --git a/deps/mozjs/js/src/lirasm/tests/callid1.out b/deps/mozjs/js/src/lirasm/tests/callid1.out deleted file mode 100644 index bac53040870..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid1.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1.1 diff --git a/deps/mozjs/js/src/lirasm/tests/callid2.in b/deps/mozjs/js/src/lirasm/tests/callid2.in deleted file mode 100644 index d8a5266b0c6..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid2.in +++ /dev/null @@ -1,8 +0,0 @@ -i = immi -1; -j = immi 3; -k = immi 6; -x = immd 8.8; - -res = calld callid2 cdecl i j k x -retd res - diff --git a/deps/mozjs/js/src/lirasm/tests/callid2.out b/deps/mozjs/js/src/lirasm/tests/callid2.out deleted file mode 100644 index bac53040870..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid2.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1.1 diff --git a/deps/mozjs/js/src/lirasm/tests/callid3.in b/deps/mozjs/js/src/lirasm/tests/callid3.in deleted file mode 100644 index 69dbd5e10ff..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid3.in +++ /dev/null @@ -1,10 +0,0 @@ -i = immi -1; -j = immi 3; -k = immi 6; -x = immd 1.1; -y = immd 3.3; -z = immd 4.4; - -res = calld callid3 cdecl i j x k y z -retd res - diff --git a/deps/mozjs/js/src/lirasm/tests/callid3.out b/deps/mozjs/js/src/lirasm/tests/callid3.out deleted file mode 100644 index bac53040870..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callid3.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1.1 diff --git a/deps/mozjs/js/src/lirasm/tests/callv.in b/deps/mozjs/js/src/lirasm/tests/callv.in deleted file mode 100644 index ffc7c2b1012..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callv.in +++ /dev/null @@ -1,5 +0,0 @@ -; test call to void function - -forty_two = immi 42 -callv printi cdecl forty_two -reti forty_two diff --git a/deps/mozjs/js/src/lirasm/tests/callv.out b/deps/mozjs/js/src/lirasm/tests/callv.out deleted file mode 100644 index 57f53296735..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/callv.out +++ /dev/null @@ -1,2 +0,0 @@ -42 -Output is: 42 diff --git a/deps/mozjs/js/src/lirasm/tests/cmov.in b/deps/mozjs/js/src/lirasm/tests/cmov.in deleted file mode 100644 index fe5c55b6228..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cmov.in +++ /dev/null @@ -1,68 +0,0 @@ -i = immi -1 -j = immi 0 -k = immi 1 - -; Test each comparison operator. In each case, the result is set to k (1) if it -; did what it should do, or i (-1) if not. - -c1 = eqi j k -r1 = cmovi c1 i k - -c2 = lti j k -r2 = cmovi c2 k i - -c3 = lei j k -r3 = cmovi c3 k i - -c4 = gti j k -r4 = cmovi c4 i k - -c5 = gei j k -r5 = cmovi c5 i k - -c6 = ltui j k -r6 = cmovi c6 k i - -c7 = leui j k -r7 = cmovi c7 k i - -c8 = gtui j k -r8 = cmovi c8 i k - -c9 = geui j k -r9 = cmovi c9 i k - -; A few weird cases: Perform unsigned tests on a signed quantity to ensure that -; they do what they should. This is dependent on the platform using two's -; complement arithmetic, but we don't support anything that doesn't do that. - -c10 = ltui i k -r10 = cmovi c10 i k - -c11 = leui i k -r11 = cmovi c11 i k - -c12 = gtui i k -r12 = cmovi c12 k i - -c13 = geui i k -r13 = cmovi c13 k i - -; Sum the results. They should all be 1 so an overall result of 13 is a pass. - -r1_2 = addi r1 r2 -r3_4 = addi r3 r4 -r5_6 = addi r5 r6 -r7_8 = addi r7 r8 -r9_10 = addi r9 r10 -r11_12 = addi r11 r12 - -r1_4 = addi r1_2 r3_4 -r5_8 = addi r5_6 r7_8 -r9_12 = addi r9_10 r11_12 - -r1_8 = addi r1_4 r5_8 -r9_13 = addi r9_12 r13 - -res = addi r1_8 r9_13 -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/cmov.out b/deps/mozjs/js/src/lirasm/tests/cmov.out deleted file mode 100644 index 828a4dd62c0..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cmov.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 13 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_eqd.in b/deps/mozjs/js/src/lirasm/tests/cond_eqd.in deleted file mode 100644 index aaaae0281f0..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_eqd.in +++ /dev/null @@ -1,54 +0,0 @@ -; A few utility constants. -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immd 0 -i1 = immd 1 -i2 = immd -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'eqd'. -eq0 = eqd i0 i0 ; 1 -eq1 = eqd i0 i1 ; 0 -eq2 = eqd i0 i2 ; 0 -eq3 = eqd i1 i0 ; 0 -eq4 = eqd i1 i1 ; 1 -eq5 = eqd i1 i2 ; 0 -eq6 = eqd i2 i0 ; 0 -eq7 = eqd i2 i1 ; 0 -eq8 = eqd i2 i2 ; 1 - -; Aggregate the results. -eq0sh = lshi eq0 sh0 -eq1sh = lshi eq1 sh1 -eq2sh = lshi eq2 sh2 -eq3sh = lshi eq3 sh3 -eq4sh = lshi eq4 sh4 -eq5sh = lshi eq5 sh5 -eq6sh = lshi eq6 sh6 -eq7sh = lshi eq7 sh7 -eq8sh = lshi eq8 sh8 - -eq0_1 = ori eq0sh eq1sh -eq2_3 = ori eq2sh eq3sh -eq4_5 = ori eq4sh eq5sh -eq6_7 = ori eq6sh eq7sh - -eq0_3 = ori eq0_1 eq2_3 -eq4_7 = ori eq4_5 eq6_7 - -eq0_7 = ori eq0_3 eq4_7 - -eq = ori eq0_7 eq8sh - -; The result should be {0001,0001,0001}, 0x111, or 273. - -reti eq - diff --git a/deps/mozjs/js/src/lirasm/tests/cond_eqd.out b/deps/mozjs/js/src/lirasm/tests/cond_eqd.out deleted file mode 100644 index f32253b18b1..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_eqd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 273 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_eqi.in b/deps/mozjs/js/src/lirasm/tests/cond_eqi.in deleted file mode 100644 index 74ff521bf5d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_eqi.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'eqi'. -eq0 = eqi i0 i0 ; 1 -eq1 = eqi i0 i1 ; 0 -eq2 = eqi i0 i2 ; 0 -eq3 = eqi i1 i0 ; 0 -eq4 = eqi i1 i1 ; 1 -eq5 = eqi i1 i2 ; 0 -eq6 = eqi i2 i0 ; 0 -eq7 = eqi i2 i1 ; 0 -eq8 = eqi i2 i2 ; 1 - -; Aggregate the results. -eq0sh = lshi eq0 sh0 -eq1sh = lshi eq1 sh1 -eq2sh = lshi eq2 sh2 -eq3sh = lshi eq3 sh3 -eq4sh = lshi eq4 sh4 -eq5sh = lshi eq5 sh5 -eq6sh = lshi eq6 sh6 -eq7sh = lshi eq7 sh7 -eq8sh = lshi eq8 sh8 - -eq0_1 = ori eq0sh eq1sh -eq2_3 = ori eq2sh eq3sh -eq4_5 = ori eq4sh eq5sh -eq6_7 = ori eq6sh eq7sh - -eq0_3 = ori eq0_1 eq2_3 -eq4_7 = ori eq4_5 eq6_7 - -eq0_7 = ori eq0_3 eq4_7 - -eq = ori eq0_7 eq8sh - -; The result should be {0001,0001,0001}, 0x111, or 273. - -reti eq diff --git a/deps/mozjs/js/src/lirasm/tests/cond_eqi.out b/deps/mozjs/js/src/lirasm/tests/cond_eqi.out deleted file mode 100644 index f32253b18b1..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_eqi.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 273 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ged.in b/deps/mozjs/js/src/lirasm/tests/cond_ged.in deleted file mode 100644 index 1bdd13b278f..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ged.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immd 0 -i1 = immd 1 -i2 = immd -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'ged'. -ge0 = ged i0 i0 ; 1 -ge1 = ged i0 i1 ; 0 -ge2 = ged i0 i2 ; 1 -ge3 = ged i1 i0 ; 1 -ge4 = ged i1 i1 ; 1 -ge5 = ged i1 i2 ; 1 -ge6 = ged i2 i0 ; 0 -ge7 = ged i2 i1 ; 0 -ge8 = ged i2 i2 ; 1 - -; Aggregate the results. -ge0sh = lshi ge0 sh0 -ge1sh = lshi ge1 sh1 -ge2sh = lshi ge2 sh2 -ge3sh = lshi ge3 sh3 -ge4sh = lshi ge4 sh4 -ge5sh = lshi ge5 sh5 -ge6sh = lshi ge6 sh6 -ge7sh = lshi ge7 sh7 -ge8sh = lshi ge8 sh8 - -ge0_1 = ori ge0sh ge1sh -ge2_3 = ori ge2sh ge3sh -ge4_5 = ori ge4sh ge5sh -ge6_7 = ori ge6sh ge7sh - -ge0_3 = ori ge0_1 ge2_3 -ge4_7 = ori ge4_5 ge6_7 - -ge0_7 = ori ge0_3 ge4_7 - -ge = ori ge0_7 ge8sh - -; The result should be {0001,0011,1101}, 0x13d, or 44. - -reti ge diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ged.out b/deps/mozjs/js/src/lirasm/tests/cond_ged.out deleted file mode 100644 index 185083dd45a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ged.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 317 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gei.in b/deps/mozjs/js/src/lirasm/tests/cond_gei.in deleted file mode 100644 index c9a4f133830..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gei.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'gei'. -ge0 = gei i0 i0 ; 1 -ge1 = gei i0 i1 ; 0 -ge2 = gei i0 i2 ; 1 -ge3 = gei i1 i0 ; 1 -ge4 = gei i1 i1 ; 1 -ge5 = gei i1 i2 ; 1 -ge6 = gei i2 i0 ; 0 -ge7 = gei i2 i1 ; 0 -ge8 = gei i2 i2 ; 1 - -; Aggregate the results. -ge0sh = lshi ge0 sh0 -ge1sh = lshi ge1 sh1 -ge2sh = lshi ge2 sh2 -ge3sh = lshi ge3 sh3 -ge4sh = lshi ge4 sh4 -ge5sh = lshi ge5 sh5 -ge6sh = lshi ge6 sh6 -ge7sh = lshi ge7 sh7 -ge8sh = lshi ge8 sh8 - -ge0_1 = ori ge0sh ge1sh -ge2_3 = ori ge2sh ge3sh -ge4_5 = ori ge4sh ge5sh -ge6_7 = ori ge6sh ge7sh - -ge0_3 = ori ge0_1 ge2_3 -ge4_7 = ori ge4_5 ge6_7 - -ge0_7 = ori ge0_3 ge4_7 - -ge = ori ge0_7 ge8sh - -; The result should be {0001,0011,1101}, 0x13d, or 44. - -reti ge diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gei.out b/deps/mozjs/js/src/lirasm/tests/cond_gei.out deleted file mode 100644 index 185083dd45a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gei.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 317 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_geui.in b/deps/mozjs/js/src/lirasm/tests/cond_geui.in deleted file mode 100644 index 8dee8eb640d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_geui.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'geui'. -ge0 = geui i0 i0 ; 1 -ge1 = geui i0 i1 ; 0 -ge2 = geui i0 i2 ; 0 -ge3 = geui i1 i0 ; 1 -ge4 = geui i1 i1 ; 1 -ge5 = geui i1 i2 ; 0 -ge6 = geui i2 i0 ; 1 -ge7 = geui i2 i1 ; 1 -ge8 = geui i2 i2 ; 1 - -; Aggregate the results. -ge0sh = lshi ge0 sh0 -ge1sh = lshi ge1 sh1 -ge2sh = lshi ge2 sh2 -ge3sh = lshi ge3 sh3 -ge4sh = lshi ge4 sh4 -ge5sh = lshi ge5 sh5 -ge6sh = lshi ge6 sh6 -ge7sh = lshi ge7 sh7 -ge8sh = lshi ge8 sh8 - -ge0_1 = ori ge0sh ge1sh -ge2_3 = ori ge2sh ge3sh -ge4_5 = ori ge4sh ge5sh -ge6_7 = ori ge6sh ge7sh - -ge0_3 = ori ge0_1 ge2_3 -ge4_7 = ori ge4_5 ge6_7 - -ge0_7 = ori ge0_3 ge4_7 - -ge = ori ge0_7 ge8sh - -; The result should be {0001,1101,1001}, 0x1d9, or 473. - -reti ge diff --git a/deps/mozjs/js/src/lirasm/tests/cond_geui.out b/deps/mozjs/js/src/lirasm/tests/cond_geui.out deleted file mode 100644 index 1ba4fef5f87..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_geui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 473 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gtd.in b/deps/mozjs/js/src/lirasm/tests/cond_gtd.in deleted file mode 100644 index 56deeb0735e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gtd.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immd 0 -i1 = immd 1 -i2 = immd -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'gtd'. -gt0 = gtd i0 i0 ; 0 -gt1 = gtd i0 i1 ; 0 -gt2 = gtd i0 i2 ; 1 -gt3 = gtd i1 i0 ; 1 -gt4 = gtd i1 i1 ; 0 -gt5 = gtd i1 i2 ; 1 -gt6 = gtd i2 i0 ; 0 -gt7 = gtd i2 i1 ; 0 -gt8 = gtd i2 i2 ; 0 - -; Aggregate the results. -gt0sh = lshi gt0 sh0 -gt1sh = lshi gt1 sh1 -gt2sh = lshi gt2 sh2 -gt3sh = lshi gt3 sh3 -gt4sh = lshi gt4 sh4 -gt5sh = lshi gt5 sh5 -gt6sh = lshi gt6 sh6 -gt7sh = lshi gt7 sh7 -gt8sh = lshi gt8 sh8 - -gt0_1 = ori gt0sh gt1sh -gt2_3 = ori gt2sh gt3sh -gt4_5 = ori gt4sh gt5sh -gt6_7 = ori gt6sh gt7sh - -gt0_3 = ori gt0_1 gt2_3 -gt4_7 = ori gt4_5 gt6_7 - -gt0_7 = ori gt0_3 gt4_7 - -gt = ori gt0_7 gt8sh - -; The result should be {0000,0010,1100}, 0x02c, or 194. - -reti gt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gtd.out b/deps/mozjs/js/src/lirasm/tests/cond_gtd.out deleted file mode 100644 index de596f7577a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gtd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 44 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gti.in b/deps/mozjs/js/src/lirasm/tests/cond_gti.in deleted file mode 100644 index 9f37953d707..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gti.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'gti'. -gt0 = gti i0 i0 ; 0 -gt1 = gti i0 i1 ; 0 -gt2 = gti i0 i2 ; 1 -gt3 = gti i1 i0 ; 1 -gt4 = gti i1 i1 ; 0 -gt5 = gti i1 i2 ; 1 -gt6 = gti i2 i0 ; 0 -gt7 = gti i2 i1 ; 0 -gt8 = gti i2 i2 ; 0 - -; Aggregate the results. -gt0sh = lshi gt0 sh0 -gt1sh = lshi gt1 sh1 -gt2sh = lshi gt2 sh2 -gt3sh = lshi gt3 sh3 -gt4sh = lshi gt4 sh4 -gt5sh = lshi gt5 sh5 -gt6sh = lshi gt6 sh6 -gt7sh = lshi gt7 sh7 -gt8sh = lshi gt8 sh8 - -gt0_1 = ori gt0sh gt1sh -gt2_3 = ori gt2sh gt3sh -gt4_5 = ori gt4sh gt5sh -gt6_7 = ori gt6sh gt7sh - -gt0_3 = ori gt0_1 gt2_3 -gt4_7 = ori gt4_5 gt6_7 - -gt0_7 = ori gt0_3 gt4_7 - -gt = ori gt0_7 gt8sh - -; The result should be {0000,0010,1100}, 0x02c, or 194. - -reti gt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gti.out b/deps/mozjs/js/src/lirasm/tests/cond_gti.out deleted file mode 100644 index de596f7577a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gti.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 44 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gtui.in b/deps/mozjs/js/src/lirasm/tests/cond_gtui.in deleted file mode 100644 index a28d2547418..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gtui.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants. -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'gtui'. -gt0 = gtui i0 i0 ; 0 -gt1 = gtui i0 i1 ; 0 -gt2 = gtui i0 i2 ; 0 -gt3 = gtui i1 i0 ; 1 -gt4 = gtui i1 i1 ; 0 -gt5 = gtui i1 i2 ; 0 -gt6 = gtui i2 i0 ; 1 -gt7 = gtui i2 i1 ; 1 -gt8 = gtui i2 i2 ; 0 - -; Aggregate the results. -gt0sh = lshi gt0 sh0 -gt1sh = lshi gt1 sh1 -gt2sh = lshi gt2 sh2 -gt3sh = lshi gt3 sh3 -gt4sh = lshi gt4 sh4 -gt5sh = lshi gt5 sh5 -gt6sh = lshi gt6 sh6 -gt7sh = lshi gt7 sh7 -gt8sh = lshi gt8 sh8 - -gt0_1 = ori gt0sh gt1sh -gt2_3 = ori gt2sh gt3sh -gt4_5 = ori gt4sh gt5sh -gt6_7 = ori gt6sh gt7sh - -gt0_3 = ori gt0_1 gt2_3 -gt4_7 = ori gt4_5 gt6_7 - -gt0_7 = ori gt0_3 gt4_7 - -gt = ori gt0_7 gt8sh - -; The result should be {0000,1100,1000}, 0x0c8, or 200. - -reti gt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_gtui.out b/deps/mozjs/js/src/lirasm/tests/cond_gtui.out deleted file mode 100644 index 166c88680a1..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_gtui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 200 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_led.in b/deps/mozjs/js/src/lirasm/tests/cond_led.in deleted file mode 100644 index 3596169ee14..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_led.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immd 0 -i1 = immd 1 -i2 = immd -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'led'. -le0 = led i0 i0 ; 1 -le1 = led i0 i1 ; 1 -le2 = led i0 i2 ; 0 -le3 = led i1 i0 ; 0 -le4 = led i1 i1 ; 1 -le5 = led i1 i2 ; 0 -le6 = led i2 i0 ; 1 -le7 = led i2 i1 ; 1 -le8 = led i2 i2 ; 1 - -; Aggregate the results. -le0sh = lshi le0 sh0 -le1sh = lshi le1 sh1 -le2sh = lshi le2 sh2 -le3sh = lshi le3 sh3 -le4sh = lshi le4 sh4 -le5sh = lshi le5 sh5 -le6sh = lshi le6 sh6 -le7sh = lshi le7 sh7 -le8sh = lshi le8 sh8 - -le0_1 = ori le0sh le1sh -le2_3 = ori le2sh le3sh -le4_5 = ori le4sh le5sh -le6_7 = ori le6sh le7sh - -le0_3 = ori le0_1 le2_3 -le4_7 = ori le4_5 le6_7 - -le0_7 = ori le0_3 le4_7 - -le = ori le0_7 le8sh - -; The result should be {0001,1101,0011}, 0x1d3, or 194. - -reti le diff --git a/deps/mozjs/js/src/lirasm/tests/cond_led.out b/deps/mozjs/js/src/lirasm/tests/cond_led.out deleted file mode 100644 index e232760598a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_led.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 467 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_lei.in b/deps/mozjs/js/src/lirasm/tests/cond_lei.in deleted file mode 100644 index a11a3f3c801..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_lei.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'lei'. -le0 = lei i0 i0 ; 1 -le1 = lei i0 i1 ; 1 -le2 = lei i0 i2 ; 0 -le3 = lei i1 i0 ; 0 -le4 = lei i1 i1 ; 1 -le5 = lei i1 i2 ; 0 -le6 = lei i2 i0 ; 1 -le7 = lei i2 i1 ; 1 -le8 = lei i2 i2 ; 1 - -; Aggregate the results. -le0sh = lshi le0 sh0 -le1sh = lshi le1 sh1 -le2sh = lshi le2 sh2 -le3sh = lshi le3 sh3 -le4sh = lshi le4 sh4 -le5sh = lshi le5 sh5 -le6sh = lshi le6 sh6 -le7sh = lshi le7 sh7 -le8sh = lshi le8 sh8 - -le0_1 = ori le0sh le1sh -le2_3 = ori le2sh le3sh -le4_5 = ori le4sh le5sh -le6_7 = ori le6sh le7sh - -le0_3 = ori le0_1 le2_3 -le4_7 = ori le4_5 le6_7 - -le0_7 = ori le0_3 le4_7 - -le = ori le0_7 le8sh - -; The result should be {0001,1101,0011}, 0x1d3, or 194. - -reti le diff --git a/deps/mozjs/js/src/lirasm/tests/cond_lei.out b/deps/mozjs/js/src/lirasm/tests/cond_lei.out deleted file mode 100644 index e232760598a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_lei.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 467 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_leui.in b/deps/mozjs/js/src/lirasm/tests/cond_leui.in deleted file mode 100644 index a53bf1b66bd..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_leui.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'leui'. -le0 = leui i0 i0 ; 1 -le1 = leui i0 i1 ; 1 -le2 = leui i0 i2 ; 1 -le3 = leui i1 i0 ; 0 -le4 = leui i1 i1 ; 1 -le5 = leui i1 i2 ; 1 -le6 = leui i2 i0 ; 0 -le7 = leui i2 i1 ; 0 -le8 = leui i2 i2 ; 1 - -; Aggregate the results. -le0sh = lshi le0 sh0 -le1sh = lshi le1 sh1 -le2sh = lshi le2 sh2 -le3sh = lshi le3 sh3 -le4sh = lshi le4 sh4 -le5sh = lshi le5 sh5 -le6sh = lshi le6 sh6 -le7sh = lshi le7 sh7 -le8sh = lshi le8 sh8 - -le0_1 = ori le0sh le1sh -le2_3 = ori le2sh le3sh -le4_5 = ori le4sh le5sh -le6_7 = ori le6sh le7sh - -le0_3 = ori le0_1 le2_3 -le4_7 = ori le4_5 le6_7 - -le0_7 = ori le0_3 le4_7 - -le = ori le0_7 le8sh - -; The result should be {0001,0011,0111}, 0x137, or 311. - -reti le diff --git a/deps/mozjs/js/src/lirasm/tests/cond_leui.out b/deps/mozjs/js/src/lirasm/tests/cond_leui.out deleted file mode 100644 index 4e71b2b300c..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_leui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 311 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ltd.in b/deps/mozjs/js/src/lirasm/tests/cond_ltd.in deleted file mode 100644 index 11e028ff093..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ltd.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immd 0 -i1 = immd 1 -i2 = immd -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'ltd'. -lt0 = ltd i0 i0 ; 0 -lt1 = ltd i0 i1 ; 1 -lt2 = ltd i0 i2 ; 0 -lt3 = ltd i1 i0 ; 0 -lt4 = ltd i1 i1 ; 0 -lt5 = ltd i1 i2 ; 0 -lt6 = ltd i2 i0 ; 1 -lt7 = ltd i2 i1 ; 1 -lt8 = ltd i2 i2 ; 0 - -; Aggregate the results. -lt0sh = lshi lt0 sh0 -lt1sh = lshi lt1 sh1 -lt2sh = lshi lt2 sh2 -lt3sh = lshi lt3 sh3 -lt4sh = lshi lt4 sh4 -lt5sh = lshi lt5 sh5 -lt6sh = lshi lt6 sh6 -lt7sh = lshi lt7 sh7 -lt8sh = lshi lt8 sh8 - -lt0_1 = ori lt0sh lt1sh -lt2_3 = ori lt2sh lt3sh -lt4_5 = ori lt4sh lt5sh -lt6_7 = ori lt6sh lt7sh - -lt0_3 = ori lt0_1 lt2_3 -lt4_7 = ori lt4_5 lt6_7 - -lt0_7 = ori lt0_3 lt4_7 - -lt = ori lt0_7 lt8sh - -; The result should be {0000,1100,0010}, 0x0c2, or 194. - -reti lt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ltd.out b/deps/mozjs/js/src/lirasm/tests/cond_ltd.out deleted file mode 100644 index a932edb2331..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ltd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 194 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_lti.in b/deps/mozjs/js/src/lirasm/tests/cond_lti.in deleted file mode 100644 index 8a8d3d2e183..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_lti.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'lti'. -lt0 = lti i0 i0 ; 0 -lt1 = lti i0 i1 ; 1 -lt2 = lti i0 i2 ; 0 -lt3 = lti i1 i0 ; 0 -lt4 = lti i1 i1 ; 0 -lt5 = lti i1 i2 ; 0 -lt6 = lti i2 i0 ; 1 -lt7 = lti i2 i1 ; 1 -lt8 = lti i2 i2 ; 0 - -; Aggregate the results. -lt0sh = lshi lt0 sh0 -lt1sh = lshi lt1 sh1 -lt2sh = lshi lt2 sh2 -lt3sh = lshi lt3 sh3 -lt4sh = lshi lt4 sh4 -lt5sh = lshi lt5 sh5 -lt6sh = lshi lt6 sh6 -lt7sh = lshi lt7 sh7 -lt8sh = lshi lt8 sh8 - -lt0_1 = ori lt0sh lt1sh -lt2_3 = ori lt2sh lt3sh -lt4_5 = ori lt4sh lt5sh -lt6_7 = ori lt6sh lt7sh - -lt0_3 = ori lt0_1 lt2_3 -lt4_7 = ori lt4_5 lt6_7 - -lt0_7 = ori lt0_3 lt4_7 - -lt = ori lt0_7 lt8sh - -; The result should be {0000,1100,0010}, 0x0c2, or 194. - -reti lt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_lti.out b/deps/mozjs/js/src/lirasm/tests/cond_lti.out deleted file mode 100644 index a932edb2331..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_lti.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 194 diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ltui.in b/deps/mozjs/js/src/lirasm/tests/cond_ltui.in deleted file mode 100644 index 421d637e5f6..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ltui.in +++ /dev/null @@ -1,53 +0,0 @@ -; A few utility constants . -sh0 = immi 0 -sh1 = immi 1 -sh2 = immi 2 -sh3 = immi 3 -sh4 = immi 4 -sh5 = immi 5 -sh6 = immi 6 -sh7 = immi 7 -sh8 = immi 8 - -i0 = immi 0 -i1 = immi 1 -i2 = immi -1 - -; ---------------------------------------------------------------------------- -; Combinations of 'ltui'. -lt0 = ltui i0 i0 ; 0 -lt1 = ltui i0 i1 ; 1 -lt2 = ltui i0 i2 ; 1 -lt3 = ltui i1 i0 ; 0 -lt4 = ltui i1 i1 ; 0 -lt5 = ltui i1 i2 ; 1 -lt6 = ltui i2 i0 ; 0 -lt7 = ltui i2 i1 ; 0 -lt8 = ltui i2 i2 ; 0 - -; Aggregate the results. -lt0sh = lshi lt0 sh0 -lt1sh = lshi lt1 sh1 -lt2sh = lshi lt2 sh2 -lt3sh = lshi lt3 sh3 -lt4sh = lshi lt4 sh4 -lt5sh = lshi lt5 sh5 -lt6sh = lshi lt6 sh6 -lt7sh = lshi lt7 sh7 -lt8sh = lshi lt8 sh8 - -lt0_1 = ori lt0sh lt1sh -lt2_3 = ori lt2sh lt3sh -lt4_5 = ori lt4sh lt5sh -lt6_7 = ori lt6sh lt7sh - -lt0_3 = ori lt0_1 lt2_3 -lt4_7 = ori lt4_5 lt6_7 - -lt0_7 = ori lt0_3 lt4_7 - -lt = ori lt0_7 lt8sh - -; The result should be {0000,0010,0110}, 0x026, or 38. - -reti lt diff --git a/deps/mozjs/js/src/lirasm/tests/cond_ltui.out b/deps/mozjs/js/src/lirasm/tests/cond_ltui.out deleted file mode 100644 index c91e4bb88d7..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/cond_ltui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 38 diff --git a/deps/mozjs/js/src/lirasm/tests/divd.in b/deps/mozjs/js/src/lirasm/tests/divd.in deleted file mode 100644 index 0a99761a471..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/divd.in +++ /dev/null @@ -1,31 +0,0 @@ -; Try to exercise as many different possibilities for the register allocator as -; is feasible. - -p1 = allocp 8 -p2 = allocp 8 - -d1a = immd 1.5 -d1 = divd d1a d1a ; X = X / X -std d1 p1 0 - -d2a = immd 4.0 -d2b = immd 8.0 -d2 = divd d2a d2b ; X = X / Y -std d2b p2 0 -std d2 p2 0 - -d3a = ldd p1 0 -d3b = ldd p2 0 -d3 = divd d3a d3b ; X = Y / X -std d3a p2 0 -std d3 p2 0 - -d4a = ldd p2 0 -d4b = ldd p1 0 -d4 = divd d4a d4b ; X = Y / Z -std d4a p1 0 -std d4b p2 0 -std d4 p1 0 - -d = ldd p1 0 -retd d diff --git a/deps/mozjs/js/src/lirasm/tests/divd.out b/deps/mozjs/js/src/lirasm/tests/divd.out deleted file mode 100644 index ca4935610b4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/divd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2 diff --git a/deps/mozjs/js/src/lirasm/tests/float_double.in b/deps/mozjs/js/src/lirasm/tests/float_double.in deleted file mode 100644 index 0ea31e78110..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/float_double.in +++ /dev/null @@ -1,20 +0,0 @@ -doubleA = immd 1.25 ; single precision -doubleB = immd 4.5 ; single precision -doubleC = immd 1.2 ; not single precision - -ptr = allocp 12 -std2f doubleA ptr 0 -std2f doubleB ptr 4 -std2f doubleC ptr 8 - -doubleD = ldf2d ptr 0 -doubleE = ldf2d ptr 4 -doubleF = ldf2d ptr 8 - -resA = eqd doubleA doubleD ; true -resB = eqd doubleB doubleE ; true -resC = eqd doubleB doubleF ; false -resD = addi resA resB -resE = addi resC resD - -reti resE diff --git a/deps/mozjs/js/src/lirasm/tests/float_double.out b/deps/mozjs/js/src/lirasm/tests/float_double.out deleted file mode 100644 index ca4935610b4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/float_double.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2 diff --git a/deps/mozjs/js/src/lirasm/tests/floatingpoint.in b/deps/mozjs/js/src/lirasm/tests/floatingpoint.in deleted file mode 100644 index f2f5b63fd37..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/floatingpoint.in +++ /dev/null @@ -1,4 +0,0 @@ -pi = immd 3.14 -two = immd 2.0 -TwoPi = muld pi two -retd two diff --git a/deps/mozjs/js/src/lirasm/tests/floatingpoint.out b/deps/mozjs/js/src/lirasm/tests/floatingpoint.out deleted file mode 100644 index ca4935610b4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/floatingpoint.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2 diff --git a/deps/mozjs/js/src/lirasm/tests/fneg.in b/deps/mozjs/js/src/lirasm/tests/fneg.in deleted file mode 100644 index caf466e8ca4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fneg.in +++ /dev/null @@ -1,3 +0,0 @@ -p = immd 123.456 -n = negd p -retd n diff --git a/deps/mozjs/js/src/lirasm/tests/fneg.out b/deps/mozjs/js/src/lirasm/tests/fneg.out deleted file mode 100644 index 7e170f3a92a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fneg.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -123.456 diff --git a/deps/mozjs/js/src/lirasm/tests/fpu1-598151.in b/deps/mozjs/js/src/lirasm/tests/fpu1-598151.in deleted file mode 100644 index e2d154c63da..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fpu1-598151.in +++ /dev/null @@ -1,12 +0,0 @@ - ptr = allocp 8 - - a = immd 1.0 - b = immd 2.5 - c = addd a b - std c ptr 0 - j done - -foo: std c ptr 0 - -done: res = ldd ptr 0 - retd res diff --git a/deps/mozjs/js/src/lirasm/tests/fpu1-598151.out b/deps/mozjs/js/src/lirasm/tests/fpu1-598151.out deleted file mode 100644 index 5eec694e5e4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fpu1-598151.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 3.5 diff --git a/deps/mozjs/js/src/lirasm/tests/fpu2-598151.in b/deps/mozjs/js/src/lirasm/tests/fpu2-598151.in deleted file mode 100644 index 6276f7a1faf..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fpu2-598151.in +++ /dev/null @@ -1,21 +0,0 @@ - ptr = allocp 8 - - k = immd 1.0 - std k ptr 0 - - a = immd 1.0 - b = immd 2.5 - c = addd a b - std c ptr 0 - - x = ldd ptr 0 - z = immd 0.0 - p = eqd x z - jt p skip - - j done - -skip: std c ptr 0 - -done: res = ldd ptr 0 - retd res diff --git a/deps/mozjs/js/src/lirasm/tests/fpu2-598151.out b/deps/mozjs/js/src/lirasm/tests/fpu2-598151.out deleted file mode 100644 index 5eec694e5e4..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fpu2-598151.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 3.5 diff --git a/deps/mozjs/js/src/lirasm/tests/fuzz-527178.in b/deps/mozjs/js/src/lirasm/tests/fuzz-527178.in deleted file mode 100644 index 12403ddcd00..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fuzz-527178.in +++ /dev/null @@ -1,5 +0,0 @@ -base = allocp 512 -five = immi 5 -sti five base 256 -x = ldus2ui base 256 -reti x diff --git a/deps/mozjs/js/src/lirasm/tests/fuzz-527178.out b/deps/mozjs/js/src/lirasm/tests/fuzz-527178.out deleted file mode 100644 index 3af94829617..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/fuzz-527178.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.in b/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.in deleted file mode 100644 index c60a169462e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.in +++ /dev/null @@ -1,26 +0,0 @@ -; The rounding rules for d2i are pretty relaxed, but integer values should -; convert cleanly. -c1 = immi -2147483648 -d1 = immd -2147483648 -i1 = d2i d1 -r1 = xori c1 i1 - -c2 = immi 2147483647 -d2 = immd 2147483647 -i2 = d2i d2 -r2 = xori i2 c2 - -; The ARM back-end will do something different if there is no machine register -; allocated for d2i, and it doesn't hurt to test the same thing on other -; platforms too: - -c3 = immi 123456 -d3 = immd 123456 -i3 = d2i d3 -regfence -r3 = xori i3 c3 - -res1_2 = ori r1 r2 -res1_3 = ori res1_2 r3 - -reti res1_3 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.out b/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/d2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.in b/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.in deleted file mode 100644 index 9b3806e5c19..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.in +++ /dev/null @@ -1,6 +0,0 @@ -a = allocp 8 -d = immd 5.0 -std d a 0 -x = ldd a 0 -i = d2i x -reti i diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.out b/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.out deleted file mode 100644 index 3af94829617..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/f2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.in b/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.in deleted file mode 100644 index 68c77591335..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.in +++ /dev/null @@ -1,24 +0,0 @@ -; This test assumes that d2i and 32-bit integer arithmetic works properly. - -; Start with 0x80000000 -i1 = immi -2147483648 -d1 = i2d i1 -o1 = d2i d1 - -; Test with -1 -c2 = immi 2147483647 -i2 = addi o1 c2 -d2 = i2d i2 -o2 = d2i d2 - -; Test with 2147483647 -i3 = subi o2 i1 -d3 = i2d i3 -o3 = d2i d3 - -; Test with 0 -i4 = subi o3 c2 -d4 = i2d i4 -o4 = d2i d4 - -reti o4 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.out b/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/i2d.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.in b/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.in deleted file mode 100644 index dcb88028f50..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.in +++ /dev/null @@ -1,5 +0,0 @@ -i = immi -1 -d = ui2d i - -retd d - diff --git a/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.out b/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.out deleted file mode 100644 index b34d0a3a7ca..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/hardfloat/ui2d.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 4.29497e+09 diff --git a/deps/mozjs/js/src/lirasm/tests/largeframe.in b/deps/mozjs/js/src/lirasm/tests/largeframe.in deleted file mode 100644 index 4c81e2ea940..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/largeframe.in +++ /dev/null @@ -1,12 +0,0 @@ -; Allocate frame larger than a Win32 page -; and write to the end of the frame first. -; This test is just an exerciser for the page -; probing code, as lirasm will not crash without it. - -foo = allocp 8192 -bar = allocp 4 -k = immi 555 -sti k bar 0 -sti k foo 0 -res = ldi bar 0 -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/largeframe.out b/deps/mozjs/js/src/lirasm/tests/largeframe.out deleted file mode 100644 index 8df33e7789d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/largeframe.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 555 diff --git a/deps/mozjs/js/src/lirasm/tests/ldc2i.in b/deps/mozjs/js/src/lirasm/tests/ldc2i.in deleted file mode 100644 index 8db7961a2fa..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/ldc2i.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi 1288908529 ; 0x4cd32ef1 -p = allocp 4 -sti full p 0 - -n0chk = immi -15 ; sign_extend(0xf1) -n1chk = immi 46 ; sign_extend(0x2e) -n2chk = immi -45 ; sign_extend(0xd3) -n3chk = immi 76 ; sign_extend(0x4c) - -n0 = ldc2i p 0 -n1 = ldc2i p 1 -n2 = ldc2i p 2 -n3 = ldc2i p 3 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/ldc2i.out b/deps/mozjs/js/src/lirasm/tests/ldc2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/ldc2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/lds2i.in b/deps/mozjs/js/src/lirasm/tests/lds2i.in deleted file mode 100644 index 9546cb87f3d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/lds2i.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi -3805 ; sign_extend(0xf123) - -n0 = lds2i p 0 -n1 = lds2i p 2 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/lds2i.out b/deps/mozjs/js/src/lirasm/tests/lds2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/lds2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/lduc2ui.in b/deps/mozjs/js/src/lirasm/tests/lduc2ui.in deleted file mode 100644 index 3a4fb0d4356..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/lduc2ui.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi -992746767 ; 0xc4d3e2f1 -p = allocp 4 -sti full p 0 - -n0chk = immi 241 ; 0xf1 -n1chk = immi 226 ; 0xe2 -n2chk = immi 211 ; 0xd3 -n3chk = immi 196 ; 0xc4 - -n0 = lduc2ui p 0 -n1 = lduc2ui p 1 -n2 = lduc2ui p 2 -n3 = lduc2ui p 3 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/lduc2ui.out b/deps/mozjs/js/src/lirasm/tests/lduc2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/lduc2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/ldus2ui.in b/deps/mozjs/js/src/lirasm/tests/ldus2ui.in deleted file mode 100644 index 69811e7721d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/ldus2ui.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi 61731 ; sign_extend(0xf123) - -n0 = ldus2ui p 0 -n1 = ldus2ui p 2 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/ldus2ui.out b/deps/mozjs/js/src/lirasm/tests/ldus2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/ldus2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.in b/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.in deleted file mode 100644 index 12403ddcd00..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.in +++ /dev/null @@ -1,5 +0,0 @@ -base = allocp 512 -five = immi 5 -sti five base 256 -x = ldus2ui base 256 -reti x diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.out b/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.out deleted file mode 100644 index 3af94829617..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/fuzz-527178.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.in b/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.in deleted file mode 100644 index 8db7961a2fa..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi 1288908529 ; 0x4cd32ef1 -p = allocp 4 -sti full p 0 - -n0chk = immi -15 ; sign_extend(0xf1) -n1chk = immi 46 ; sign_extend(0x2e) -n2chk = immi -45 ; sign_extend(0xd3) -n3chk = immi 76 ; sign_extend(0x4c) - -n0 = ldc2i p 0 -n1 = ldc2i p 1 -n2 = ldc2i p 2 -n3 = ldc2i p 3 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.out b/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/ldc2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.in b/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.in deleted file mode 100644 index 9546cb87f3d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi -3805 ; sign_extend(0xf123) - -n0 = lds2i p 0 -n1 = lds2i p 2 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.out b/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/lds2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.in b/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.in deleted file mode 100644 index 3a4fb0d4356..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.in +++ /dev/null @@ -1,26 +0,0 @@ -full = immi -992746767 ; 0xc4d3e2f1 -p = allocp 4 -sti full p 0 - -n0chk = immi 241 ; 0xf1 -n1chk = immi 226 ; 0xe2 -n2chk = immi 211 ; 0xd3 -n3chk = immi 196 ; 0xc4 - -n0 = lduc2ui p 0 -n1 = lduc2ui p 1 -n2 = lduc2ui p 2 -n3 = lduc2ui p 3 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 -r2 = xori n2chk n2 -r3 = xori n3chk n3 - -r0_1 = ori r0 r1 -r2_3 = ori r2 r3 - -r = ori r0_1 r2_3 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.out b/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/lduc2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.in b/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.in deleted file mode 100644 index 69811e7721d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.in +++ /dev/null @@ -1,17 +0,0 @@ -full = immi -249334698 ; 0xf1237456 -p = allocp 4 -sti full p 0 - -n0chk = immi 29782 ; sign_extend(0x7456) -n1chk = immi 61731 ; sign_extend(0xf123) - -n0 = ldus2ui p 0 -n1 = ldus2ui p 2 - -; Collate the results. -r0 = xori n0chk n0 -r1 = xori n1chk n1 - -r = ori r0 r1 -reti r - diff --git a/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.out b/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/littleendian/ldus2ui.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/loadstore.in b/deps/mozjs/js/src/lirasm/tests/loadstore.in deleted file mode 100644 index 8f8911eaa4d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/loadstore.in +++ /dev/null @@ -1,9 +0,0 @@ -ptr = allocp 8 -five = immi 5 -sti five ptr 0 -three = immi 3 -sti three ptr 4 -v = ldi ptr 0 -u = ldi ptr 4 -res = addi u v -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/loadstore.out b/deps/mozjs/js/src/lirasm/tests/loadstore.out deleted file mode 100644 index c6b20cd3984..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/loadstore.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 8 diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xxx.in b/deps/mozjs/js/src/lirasm/tests/mul_xxx.in deleted file mode 100644 index 2210ffd9c8f..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xxx.in +++ /dev/null @@ -1,6 +0,0 @@ -; 46340 * 46340 < 2^31, and will not overflow. -big = immi 46340 - -res = muli big big - -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xxx.out b/deps/mozjs/js/src/lirasm/tests/mul_xxx.out deleted file mode 100644 index 9a99d925d9e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xxx.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147395600 diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xxy.in b/deps/mozjs/js/src/lirasm/tests/mul_xxy.in deleted file mode 100644 index 75cb8d67307..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xxy.in +++ /dev/null @@ -1,7 +0,0 @@ -; 1073741823 * 2 < 2^31, and will not overflow. -big = immi 1073741823 -two = immi 2 - -res = muli big two - -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xxy.out b/deps/mozjs/js/src/lirasm/tests/mul_xxy.out deleted file mode 100644 index 2c181c6c528..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xxy.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147483646 diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xyy.in b/deps/mozjs/js/src/lirasm/tests/mul_xyy.in deleted file mode 100644 index 39d594f0f45..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xyy.in +++ /dev/null @@ -1,10 +0,0 @@ -; 46340 * 46340 < 2^31, and will not overflow. -big = immi 46340 - -res = muli big big - -; Ensure that 'big' gets its own register and isn't shared with 'res'. -m = allocp 8 -sti big m 0 - -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xyy.out b/deps/mozjs/js/src/lirasm/tests/mul_xyy.out deleted file mode 100644 index 9a99d925d9e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xyy.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147395600 diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xyz.in b/deps/mozjs/js/src/lirasm/tests/mul_xyz.in deleted file mode 100644 index 864c301f6a9..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xyz.in +++ /dev/null @@ -1,13 +0,0 @@ -; 1073741823 * 2 < 2^31, and will not overflow. -big = immi 1073741823 -two = immi 2 - -res = muli big two - -; Ensure that 'big' and 'two' get their own registers and -; aren't shared with 'res'. -m = allocp 12 -sti big m 0 -sti two m 4 - -reti res diff --git a/deps/mozjs/js/src/lirasm/tests/mul_xyz.out b/deps/mozjs/js/src/lirasm/tests/mul_xyz.out deleted file mode 100644 index 2c181c6c528..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mul_xyz.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147483646 diff --git a/deps/mozjs/js/src/lirasm/tests/muld.in b/deps/mozjs/js/src/lirasm/tests/muld.in deleted file mode 100644 index d4f5ea27b03..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muld.in +++ /dev/null @@ -1,31 +0,0 @@ -; Try to exercise as many different possibilities for the register allocator as -; is feasible. - -p1 = allocp 8 -p2 = allocp 8 - -d1a = immd 2.0 -d1 = muld d1a d1a ; X = X * X -std d1 p1 0 - -d2a = immd 2.5 -d2b = immd 3.0 -d2 = muld d2a d2b ; X = X * Y -std d2b p2 0 -std d2 p2 0 - -d3a = ldd p1 0 -d3b = ldd p2 0 -d3 = muld d3a d3b ; X = Y * X -std d3a p2 0 -std d3 p2 0 - -d4a = ldd p2 0 -d4b = ldd p1 0 -d4 = muld d4a d4b ; X = Y * Z -std d4a p1 0 -std d4b p2 0 -std d4 p1 0 - -d = ldd p1 0 -retd d diff --git a/deps/mozjs/js/src/lirasm/tests/muld.out b/deps/mozjs/js/src/lirasm/tests/muld.out deleted file mode 100644 index 30488f0aee3..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muld.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 120 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi.in b/deps/mozjs/js/src/lirasm/tests/muljovi.in deleted file mode 100644 index 1d5e17f9206..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi.in +++ /dev/null @@ -1,15 +0,0 @@ - ptr = allocp 8 - - a = immi 65536 - b = immi 32767 - c = muljovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res - diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi.out b/deps/mozjs/js/src/lirasm/tests/muljovi.out deleted file mode 100644 index 00047aac348..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147418112 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.in b/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.in deleted file mode 100644 index 97e2ffb94f7..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.in +++ /dev/null @@ -1,14 +0,0 @@ - ptr = allocp 8 - - a = immi 65536 - b = immi 32768 - c = muljovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.out b/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.out deleted file mode 100644 index 5404b11a606..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_ovf.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 12345678 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.in b/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.in deleted file mode 100644 index 4bb0b8e15da..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.in +++ /dev/null @@ -1,11 +0,0 @@ - ; 46340 * 46340 < 2^31, and will not overflow. - big = immi 46340 - - res = muljovi big big err - - reti res - -; ---- Exit route for the error handler. - -err: e = immi -1 - reti e diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.out b/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.out deleted file mode 100644 index 9a99d925d9e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xxx.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147395600 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.in b/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.in deleted file mode 100644 index faa82421e51..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.in +++ /dev/null @@ -1,12 +0,0 @@ - ; 1073741823 * 2 < 2^31, and will not overflow. - big = immi 1073741823 - two = immi 2 - - res = muljovi big two err - - reti res - -; ---- Exit route for the error handler. - -err: e = immi -1 - reti e diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.out b/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.out deleted file mode 100644 index 2c181c6c528..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xxy.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147483646 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.in b/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.in deleted file mode 100644 index 79771d4a300..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.in +++ /dev/null @@ -1,15 +0,0 @@ - ; 46340 * 46340 < 2^31, and will not overflow. - big = immi 46340 - - res = muljovi big big err - - ; Ensure that 'big' gets its own register and isn't shared with 'res'. - m = allocp 8 - sti big m 0 - - reti res - -; ---- Exit route for the error handler. - -err: e = immi -1 - reti e diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.out b/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.out deleted file mode 100644 index 9a99d925d9e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xyy.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147395600 diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.in b/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.in deleted file mode 100644 index e8dd92a481e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.in +++ /dev/null @@ -1,18 +0,0 @@ - ; 1073741823 * 2 < 2^31, and will not overflow. - big = immi 1073741823 - two = immi 2 - - res = muljovi big two err - - ; Ensure that 'big' and 'two' get their own registers and - ; aren't shared with 'res'. - m = allocp 12 - sti big m 0 - sti two m 4 - - reti res - -; ---- Exit route for the error handler. - -err: e = immi -1 - reti e diff --git a/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.out b/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.out deleted file mode 100644 index 2c181c6c528..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/muljovi_xyz.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 2147483646 diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xxx.in b/deps/mozjs/js/src/lirasm/tests/mulov_xxx.in deleted file mode 100644 index 97d2fd755b3..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xxx.in +++ /dev/null @@ -1,14 +0,0 @@ -; 46341 * 46341 >= 2^31, and will overflow. -big = immi 46341 - -; Because 'big' isn't used after mul, it _may_ get allocated to the same -; register as 'res'. This is the case with the ARM back-end, and that is where -; this test is important as rX=rX*rX isn't possible on ARMv5 without some -; trickery. -res = mulxovi big big ; overflow, so we exit here - -; Store 'res' so it isn't dead. -m = allocp 4 -sti res m 0 -x ; we don't exit here - diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xxx.out b/deps/mozjs/js/src/lirasm/tests/mulov_xxx.out deleted file mode 100644 index c5b8999f67c..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xxx.out +++ /dev/null @@ -1 +0,0 @@ -Exited block on line: 8 diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xxy.in b/deps/mozjs/js/src/lirasm/tests/mulov_xxy.in deleted file mode 100644 index 77db0734421..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xxy.in +++ /dev/null @@ -1,14 +0,0 @@ -; 1073741824 * 2 >= 2^31, and will overflow. -big = immi 1073741824 -two = immi 2 - -; Because 'big' isn't used after mul, it _may_ get allocated to the same -; register as 'res'. This is the case with the ARM back-end, and that is where -; this test is important as rX=rX*rY isn't possible on ARMv5 without some -; trickery. -res = mulxovi big two ; overflow, so we exit here - -; Store 'res' so it isn't dead. -m = allocp 4 -sti res m 0 -x ; we don't exit here diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xxy.out b/deps/mozjs/js/src/lirasm/tests/mulov_xxy.out deleted file mode 100644 index a648897bd6c..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xxy.out +++ /dev/null @@ -1 +0,0 @@ -Exited block on line: 9 diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xyy.in b/deps/mozjs/js/src/lirasm/tests/mulov_xyy.in deleted file mode 100644 index bde56db1bc2..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xyy.in +++ /dev/null @@ -1,11 +0,0 @@ -; 46341 * 46341 >= 2^31, and will overflow. -big = immi 46341 - -res = mulxovi big big ; overflow, so we exit here - -; Ensure that 'big' gets its own register and isn't shared with 'res'. -; Also store 'res' so it isn't dead. -m = allocp 8 -sti big m 0 -sti res m 4 -x ; we don't exit here diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xyy.out b/deps/mozjs/js/src/lirasm/tests/mulov_xyy.out deleted file mode 100644 index ac1b199b386..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xyy.out +++ /dev/null @@ -1 +0,0 @@ -Exited block on line: 4 diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xyz.in b/deps/mozjs/js/src/lirasm/tests/mulov_xyz.in deleted file mode 100644 index 49674403055..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xyz.in +++ /dev/null @@ -1,13 +0,0 @@ -; 1073741824 * 2 >= 2^31, and will overflow. -big = immi 1073741824 -two = immi 2 - -res = mulxovi big two ; overflow, so we exit here - -; Ensure that 'big' and 'two' get their own registers and -; aren't shared with 'res'. Also store 'res' so it isn't dead. -m = allocp 12 -sti big m 0 -sti two m 4 -sti res m 8 -x ; we don't exit here diff --git a/deps/mozjs/js/src/lirasm/tests/mulov_xyz.out b/deps/mozjs/js/src/lirasm/tests/mulov_xyz.out deleted file mode 100644 index 6fe165b358a..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/mulov_xyz.out +++ /dev/null @@ -1 +0,0 @@ -Exited block on line: 5 diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag1.in b/deps/mozjs/js/src/lirasm/tests/multfrag1.in deleted file mode 100644 index fd97e1a82f5..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag1.in +++ /dev/null @@ -1,27 +0,0 @@ -.begin a -ptr = allocp 8 -a = immi 65 -sti2c a ptr 0 -b = immi 66 -sti2c b ptr 1 -c = immi 67 -sti2c c ptr 2 -zero = immi 0 -sti2c zero ptr 3 -ss = calli puts cdecl ptr -nn = gei ss zero -reti nn -.end - -.begin b -rr = calli a fastcall -reti rr -.end - -.begin main -ans = calli b fastcall -five = immi 5 -res = addi five ans -reti res -.end - diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag1.out b/deps/mozjs/js/src/lirasm/tests/multfrag1.out deleted file mode 100644 index 40b88a2de74..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag1.out +++ /dev/null @@ -1,2 +0,0 @@ -ABC -Output is: 6 diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag2.in b/deps/mozjs/js/src/lirasm/tests/multfrag2.in deleted file mode 100644 index 6a769a964d6..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag2.in +++ /dev/null @@ -1,14 +0,0 @@ -.begin sinpibytwo -pi = immd 3.14 -half = immd 0.5 -halfpi = muld pi half -res = calld sin cdecl halfpi -retd res -.end - -.begin main -aa = calld sinpibytwo fastcall -bb = immd 5.53 -res = addd aa bb -retd res -.end diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag2.out b/deps/mozjs/js/src/lirasm/tests/multfrag2.out deleted file mode 100644 index 5beea1febdc..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag2.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 6.53 diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag3.in b/deps/mozjs/js/src/lirasm/tests/multfrag3.in deleted file mode 100644 index 5fba3bdf9a0..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag3.in +++ /dev/null @@ -1,16 +0,0 @@ -; See bug 541232 for why the params are commented out. -.begin avg -oneh = immi 100 ; should be: p1 = paramp 0 0 -twoh = immi 200 ; should be: p2 = paramp 1 0 -sum = addi oneh twoh ; should be: sum = addp p1 p2 -one = immi 1 -avg = rshi sum one -reti avg -.end - -.begin main -oneh = immi 100 -twoh = immi 200 -res = calli avg fastcall twoh oneh -reti res -.end diff --git a/deps/mozjs/js/src/lirasm/tests/multfrag3.out b/deps/mozjs/js/src/lirasm/tests/multfrag3.out deleted file mode 100644 index 1779f25b1fe..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/multfrag3.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 150 diff --git a/deps/mozjs/js/src/lirasm/tests/negnot.in b/deps/mozjs/js/src/lirasm/tests/negnot.in deleted file mode 100644 index 8e6907a6f7e..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/negnot.in +++ /dev/null @@ -1,4 +0,0 @@ -i = immi 0 -a = noti i -b = negi a -reti b diff --git a/deps/mozjs/js/src/lirasm/tests/negnot.out b/deps/mozjs/js/src/lirasm/tests/negnot.out deleted file mode 100644 index 25ee5c06706..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/negnot.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 1 diff --git a/deps/mozjs/js/src/lirasm/tests/random.out b/deps/mozjs/js/src/lirasm/tests/random.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/random.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/shi.in b/deps/mozjs/js/src/lirasm/tests/shi.in deleted file mode 100644 index 41bf0517995..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/shi.in +++ /dev/null @@ -1,32 +0,0 @@ -; Only the bottom 5 bits of the shift amount in lshi/rshi/rshui are used. - -two = immi 2 - -sh1 = immi 1 -sh2 = immi 33 ; 0010_0001b -sh3 = immi 268435425 ; 0000_1111_1111_1111_1111_1111_1110_0001b - -a1 = lshi two sh1 ; --> 4 -a2 = lshi two sh2 ; --> 4 -a3 = lshi two sh3 ; --> 4 - -b1 = rshi two sh1 ; --> 1 -b2 = rshi two sh2 ; --> 1 -b3 = rshi two sh3 ; --> 1 - -c1 = rshui two sh1 ; --> 1 -c2 = rshui two sh2 ; --> 1 -c3 = rshui two sh3 ; --> 1 - -s0 = immi 0 -s1 = addi s0 a1 -s2 = addi s1 a2 -s3 = addi s2 a3 -s4 = addi s3 b1 -s5 = addi s4 b2 -s6 = addi s5 b3 -s7 = addi s6 c1 -s8 = addi s7 c2 -s9 = addi s8 c2 ; --> 18 - -reti s9 diff --git a/deps/mozjs/js/src/lirasm/tests/shi.out b/deps/mozjs/js/src/lirasm/tests/shi.out deleted file mode 100644 index 5b7e7754b5d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/shi.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 18 diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.in b/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.in deleted file mode 100644 index 89b7d55daec..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.in +++ /dev/null @@ -1,4 +0,0 @@ -; IEEE-754 encodes -0.0 as 0x80000000,00000000 -d = immd -0.0 -hi = dhi2i d -reti hi diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.out b/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.out deleted file mode 100644 index b0b45d5343b..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/dhi2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -2147483648 diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.in b/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.in deleted file mode 100644 index cdf01dc9665..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.in +++ /dev/null @@ -1,4 +0,0 @@ -; IEEE-754 encodes the following constant as 0x3ff00000,ffffffff -d = immd 1.0000009536743162 -lo = dlo2i d -reti lo diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.out b/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.out deleted file mode 100644 index 7bf0fb6c35d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/dlo2i.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -1 diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.in b/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.in deleted file mode 100644 index 7189917b863..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.in +++ /dev/null @@ -1,20 +0,0 @@ -; It's very difficult to test that the 'lo' part is actually doing anything -; because it tends to get lost when lirasm dumps the result to the console. -; By far the easiest way to check this is to use dlo2i and dhi2i to see if we -; get what we started with (assuming that those instructions work). - -hi = immi 1127219201 ; 0x43300001 (positive, exponent of +52 ... -lo = immi -1 ; 0xffffffff (... mantissa of 0x100001ffffffff) -d = ii2d lo hi -hi2 = dhi2i d -lo2 = dlo2i d - -; XOR to check for differences, then OR the two results. 0 indicates success, -; but anything else indicates some kind of loss of data. -rhi = xori hi hi2 -rlo = xori lo lo2 - -res = ori rhi rlo - -reti res - diff --git a/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.out b/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.out deleted file mode 100644 index e1b88ffcb7d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/softfloat/ii2d.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0 diff --git a/deps/mozjs/js/src/lirasm/tests/std2f.in b/deps/mozjs/js/src/lirasm/tests/std2f.in deleted file mode 100644 index 8f5f4139ba5..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/std2f.in +++ /dev/null @@ -1,7 +0,0 @@ -ptr = allocp 8 -d = immd -1 -; Store the value twice in consecutive slots to check that it's really stored as 32 bits. -std2f d ptr 4 -std2f d ptr 0 -d2 = ldf2d ptr 4 -retd d2 diff --git a/deps/mozjs/js/src/lirasm/tests/std2f.out b/deps/mozjs/js/src/lirasm/tests/std2f.out deleted file mode 100644 index 7bf0fb6c35d..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/std2f.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -1 diff --git a/deps/mozjs/js/src/lirasm/tests/subd.in b/deps/mozjs/js/src/lirasm/tests/subd.in deleted file mode 100644 index 991086151de..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subd.in +++ /dev/null @@ -1,31 +0,0 @@ -; Try to exercise as many different possibilities for the register allocator as -; is feasible. - -p1 = allocp 8 -p2 = allocp 8 - -d1a = immd 1.5 -d1 = subd d1a d1a ; X = X - X -std d1 p1 0 - -d2a = immd 2.5 -d2b = immd 3.0 -d2 = subd d2a d2b ; X = X - Y -std d2b p2 0 -std d2 p2 0 - -d3a = ldd p1 0 -d3b = ldd p2 0 -d3 = subd d3a d3b ; X = Y - X -std d3a p2 0 -std d3 p2 0 - -d4a = ldd p2 0 -d4b = ldd p1 0 -d4 = subd d4a d4b ; X = Y - Z -std d4a p1 0 -std d4b p2 0 -std d4 p1 0 - -d = ldd p1 0 -retd d diff --git a/deps/mozjs/js/src/lirasm/tests/subd.out b/deps/mozjs/js/src/lirasm/tests/subd.out deleted file mode 100644 index 8da73fbd381..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subd.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 0.5 diff --git a/deps/mozjs/js/src/lirasm/tests/subjovi.in b/deps/mozjs/js/src/lirasm/tests/subjovi.in deleted file mode 100644 index 1b0cce50c27..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subjovi.in +++ /dev/null @@ -1,15 +0,0 @@ - ptr = allocp 8 - - a = immi -2147483647 - b = immi 1 - c = subjovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res - diff --git a/deps/mozjs/js/src/lirasm/tests/subjovi.out b/deps/mozjs/js/src/lirasm/tests/subjovi.out deleted file mode 100644 index b0b45d5343b..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subjovi.out +++ /dev/null @@ -1 +0,0 @@ -Output is: -2147483648 diff --git a/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.in b/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.in deleted file mode 100644 index 6e0ac66cd6b..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.in +++ /dev/null @@ -1,14 +0,0 @@ - ptr = allocp 8 - - a = immi -2147483647 - b = immi 2 - c = subjovi a b ovf - sti c ptr 0 - - j done - -ovf: i = immi 12345678 - sti i ptr 0 - -done: res = ldi ptr 0 - reti res diff --git a/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.out b/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.out deleted file mode 100644 index 5404b11a606..00000000000 --- a/deps/mozjs/js/src/lirasm/tests/subjovi_ovf.out +++ /dev/null @@ -1 +0,0 @@ -Output is: 12345678 diff --git a/deps/mozjs/js/src/lock_sparcv8plus.il b/deps/mozjs/js/src/lock_sparcv8plus.il deleted file mode 100644 index e7b55d5f6ec..00000000000 --- a/deps/mozjs/js/src/lock_sparcv8plus.il +++ /dev/null @@ -1,84 +0,0 @@ -! -! ***** BEGIN LICENSE BLOCK ***** -! Version: MPL 1.1/GPL 2.0/LGPL 2.1 -! -! The contents of this file are subject to the Mozilla Public License Version -! 1.1 (the "License"); you may not use this file except in compliance with -! the License. You may obtain a copy of the License at -! http://www.mozilla.org/MPL/ -! -! Software distributed under the License is distributed on an "AS IS" basis, -! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -! for the specific language governing rights and limitations under the -! License. -! -! The Original Code is Mozilla Communicator client code, released -! March 31, 1998. -! -! The Initial Developer of the Original Code is -! Netscape Communications Corporation. -! Portions created by the Initial Developer are Copyright (C) 1998-1999 -! the Initial Developer. All Rights Reserved. -! -! Contributor(s): -! -! Alternatively, the contents of this file may be used under the terms of -! either the GNU General Public License Version 2 or later (the "GPL"), or -! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -! in which case the provisions of the GPL or the LGPL are applicable instead -! of those above. If you wish to allow use of your version of this file only -! under the terms of either the GPL or the LGPL, and not to allow others to -! use your version of this file under the terms of the MPL, indicate your -! decision by deleting the provisions above and replace them with the notice -! and other provisions required by the GPL or the LGPL. If you do not delete -! the provisions above, a recipient may use your version of this file under -! the terms of any one of the MPL, the GPL or the LGPL. -! -! ***** END LICENSE BLOCK ***** - -! -! atomic compare-and-swap routines for V8+ (ultrasparc) -! -! ====================================================================== -! -! Perform the sequence *a = b atomically with respect to previous value -! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. -! Returns 1 if assignment happened, and 0 otherwise. -! -! usage : old_val = compare_and_swap(address, oldval, newval) -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created; -! we use the caller stack frame so what would normally be %i (input) -! registers are actually %o (output registers). Also, we must not -! overwrite the contents of %l (local) registers as they are not -! assumed to be volatile during calls. -! -! So, the registers used are: -! %o0 [input] - the address of the value to increment -! %o1 [input] - the old value to compare with -! %o2 [input] - the new value to set for [%o0] -! %o3 [local] - work register -! ----------------------- -! ====================================================================== -! -! v8plus - - .inline NativeCompareAndSwap,3 - - stbar - cas [%o0],%o1,%o2 ! compare *w with old value and set to new if equal - cmp %o1,%o2 ! did we succeed? - be,a 1f ! yes - mov 1,%o0 ! return true (annulled when no jump) - mov 0,%o0 ! return false -1: - - .end - -! -! end -! -! ====================================================================== -! diff --git a/deps/mozjs/js/src/lock_sparcv9.il b/deps/mozjs/js/src/lock_sparcv9.il deleted file mode 100644 index e87e065d76e..00000000000 --- a/deps/mozjs/js/src/lock_sparcv9.il +++ /dev/null @@ -1,84 +0,0 @@ -! -! ***** BEGIN LICENSE BLOCK ***** -! Version: MPL 1.1/GPL 2.0/LGPL 2.1 -! -! The contents of this file are subject to the Mozilla Public License Version -! 1.1 (the "License"); you may not use this file except in compliance with -! the License. You may obtain a copy of the License at -! http://www.mozilla.org/MPL/ -! -! Software distributed under the License is distributed on an "AS IS" basis, -! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -! for the specific language governing rights and limitations under the -! License. -! -! The Original Code is Mozilla Communicator client code, released -! March 31, 1998. -! -! The Initial Developer of the Original Code is -! Netscape Communications Corporation. -! Portions created by the Initial Developer are Copyright (C) 1998-1999 -! the Initial Developer. All Rights Reserved. -! -! Contributor(s): -! -! Alternatively, the contents of this file may be used under the terms of -! either the GNU General Public License Version 2 or later (the "GPL"), or -! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -! in which case the provisions of the GPL or the LGPL are applicable instead -! of those above. If you wish to allow use of your version of this file only -! under the terms of either the GPL or the LGPL, and not to allow others to -! use your version of this file under the terms of the MPL, indicate your -! decision by deleting the provisions above and replace them with the notice -! and other provisions required by the GPL or the LGPL. If you do not delete -! the provisions above, a recipient may use your version of this file under -! the terms of any one of the MPL, the GPL or the LGPL. -! -! ***** END LICENSE BLOCK ***** - -! -! atomic compare-and-swap routines for V9 (ultrasparc) -! -! ====================================================================== -! -! Perform the sequence *a = b atomically with respect to previous value -! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. -! Returns 1 if assignment happened, and 0 otherwise. -! -! usage : old_val = compare_and_swap(address, oldval, newval) -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created; -! we use the caller stack frame so what would normally be %i (input) -! registers are actually %o (output registers). Also, we must not -! overwrite the contents of %l (local) registers as they are not -! assumed to be volatile during calls. -! -! So, the registers used are: -! %o0 [input] - the address of the value to increment -! %o1 [input] - the old value to compare with -! %o2 [input] - the new value to set for [%o0] -! %o3 [local] - work register -! ----------------------- -! ====================================================================== -! -! v9 - - .inline NativeCompareAndSwap,3 - - stbar - casx [%o0],%o1,%o2 ! compare *w with old value and set to new if equal - cmp %o1,%o2 ! did we succeed? - be,a 1f ! yes - mov 1,%o0 ! return true (annulled when no jump) - mov 0,%o0 ! return false -1: - - .end - -! -! end -! -! ====================================================================== -! diff --git a/deps/mozjs/js/src/methodjit/BaseAssembler.h b/deps/mozjs/js/src/methodjit/BaseAssembler.h index 0d404c67860..a7de3d80dce 100644 --- a/deps/mozjs/js/src/methodjit/BaseAssembler.h +++ b/deps/mozjs/js/src/methodjit/BaseAssembler.h @@ -42,7 +42,6 @@ #define jsjaeger_baseassembler_h__ #include "jscntxt.h" -#include "jstl.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" #include "assembler/assembler/MacroAssembler.h" #include "assembler/assembler/LinkBuffer.h" @@ -56,45 +55,13 @@ namespace js { namespace mjit { -class MaybeRegisterID { - typedef JSC::MacroAssembler::RegisterID RegisterID; - - public: - MaybeRegisterID() - : reg_(Registers::ReturnReg), set(false) - { } - - MaybeRegisterID(RegisterID reg) - : reg_(reg), set(true) - { } - - inline RegisterID reg() const { JS_ASSERT(set); return reg_; } - inline void setReg(const RegisterID r) { reg_ = r; set = true; } - inline bool isSet() const { return set; } - - MaybeRegisterID & operator =(const MaybeRegisterID &other) { - set = other.set; - reg_ = other.reg_; - return *this; - } - - MaybeRegisterID & operator =(RegisterID r) { - setReg(r); - return *this; - } - - private: - RegisterID reg_; - bool set; -}; - -// Represents an int32 property name in generated code, which must be either +// Represents an int32_t property name in generated code, which must be either // a RegisterID or a constant value. struct Int32Key { typedef JSC::MacroAssembler::RegisterID RegisterID; MaybeRegisterID reg_; - int32 index_; + int32_t index_; Int32Key() : index_(0) { } @@ -103,13 +70,13 @@ struct Int32Key { key.reg_ = reg; return key; } - static Int32Key FromConstant(int32 index) { + static Int32Key FromConstant(int32_t index) { Int32Key key; key.index_ = index; return key; } - int32 index() const { + int32_t index() const { JS_ASSERT(!reg_.isSet()); return index_; } @@ -118,28 +85,9 @@ struct Int32Key { bool isConstant() const { return !reg_.isSet(); } }; -class MaybeJump { - typedef JSC::MacroAssembler::Jump Jump; - public: - MaybeJump() - : set(false) - { } - - inline Jump getJump() const { JS_ASSERT(set); return jump; } - inline Jump get() const { JS_ASSERT(set); return jump; } - inline void setJump(const Jump &j) { jump = j; set = true; } - inline bool isSet() const { return set; } - - inline MaybeJump &operator=(Jump j) { setJump(j); return *this; } - - private: - Jump jump; - bool set; -}; - struct FrameAddress : JSC::MacroAssembler::Address { - FrameAddress(int32 offset) + FrameAddress(int32_t offset) : Address(JSC::MacroAssembler::stackPointerRegister, offset) { } }; @@ -152,10 +100,10 @@ struct ImmIntPtr : public JSC::MacroAssembler::ImmPtr }; struct StackMarker { - uint32 base; - uint32 bytes; + uint32_t base; + uint32_t bytes; - StackMarker(uint32 base, uint32 bytes) + StackMarker(uint32_t base, uint32_t bytes) : base(base), bytes(bytes) { } }; @@ -171,32 +119,29 @@ class Assembler : public ValueAssembler JSC::FunctionPtr fun; }; - /* Need a temp reg that is not ArgReg1. */ -#if defined(JS_CPU_X86) || defined(JS_CPU_X64) - static const RegisterID ClobberInCall = JSC::X86Registers::ecx; -#elif defined(JS_CPU_ARM) - static const RegisterID ClobberInCall = JSC::ARMRegisters::r2; -#elif defined(JS_CPU_SPARC) - static const RegisterID ClobberInCall = JSC::SparcRegisters::l1; -#endif + struct DoublePatch { + double d; + DataLabelPtr label; + }; /* :TODO: OOM */ Label startLabel; Vector callPatches; + Vector doublePatches; // Registers that can be clobbered during a call sequence. Registers availInCall; // Extra number of bytes that can be used for storing structs/references // across calls. - uint32 extraStackSpace; + uint32_t extraStackSpace; // Calling convention used by the currently in-progress call. Registers::CallConvention callConvention; // Amount of stack space reserved for the currently in-progress call. This // includes alignment and parameters. - uint32 stackAdjust; + uint32_t stackAdjust; // Debug flag to make sure calls do not nest. #ifdef DEBUG @@ -206,6 +151,7 @@ class Assembler : public ValueAssembler public: Assembler() : callPatches(SystemAllocPolicy()), + availInCall(0), extraStackSpace(0), stackAdjust(0) #ifdef DEBUG @@ -215,53 +161,56 @@ class Assembler : public ValueAssembler startLabel = label(); } - /* Total number of floating-point registers. */ - static const uint32 TotalFPRegisters = FPRegisters::TotalFPRegisters; - /* Register pair storing returned type/data for calls. */ #if defined(JS_CPU_X86) || defined(JS_CPU_X64) -static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::X86Registers::ecx; -static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::X86Registers::edx; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::X86Registers::edi; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::X86Registers::esi; static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::X86Registers::ecx; #elif defined(JS_CPU_ARM) -static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::ARMRegisters::r2; -static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::ARMRegisters::r1; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::ARMRegisters::r5; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::ARMRegisters::r4; static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegisters::r1; #elif defined(JS_CPU_SPARC) -static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::SparcRegisters::i0; -static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::SparcRegisters::i1; -static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegisters::i2; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::SparcRegisters::l2; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::SparcRegisters::l3; +static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegisters::l4; +#elif defined(JS_CPU_MIPS) +static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::MIPSRegisters::a0; +static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::MIPSRegisters::a2; +static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::MIPSRegisters::a1; #endif size_t distanceOf(Label l) { return differenceBetween(startLabel, l); } - void load32FromImm(void *ptr, RegisterID reg) { - load32(ptr, reg); + void loadPtrFromImm(void *ptr, RegisterID reg) { + loadPtr(ptr, reg); } void loadShape(RegisterID obj, RegisterID shape) { - load32(Address(obj, offsetof(JSObject, objShape)), shape); + loadPtr(Address(obj, JSObject::offsetOfShape()), shape); } - Jump guardShape(RegisterID objReg, JSObject *obj) { - return branch32(NotEqual, Address(objReg, offsetof(JSObject, objShape)), - Imm32(obj->shape())); + Jump guardShape(RegisterID objReg, const Shape *shape) { + return branchPtr(NotEqual, Address(objReg, JSObject::offsetOfShape()), ImmPtr(shape)); } - Jump testFunction(Condition cond, RegisterID fun) { - return branchPtr(cond, Address(fun, offsetof(JSObject, clasp)), - ImmPtr(&js_FunctionClass)); + Jump guardShape(RegisterID objReg, JSObject *obj) { + return guardShape(objReg, obj->lastProperty()); } /* * Finds and returns the address of a known object and slot. */ - Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) { - move(ImmPtr(&obj->slots), reg); - loadPtr(reg, reg); - return Address(reg, slot * sizeof(Value)); + Address objSlotRef(JSObject *obj, RegisterID reg, uint32_t slot) { + move(ImmPtr(obj), reg); + if (obj->isFixedSlot(slot)) { + return Address(reg, JSObject::getFixedSlotOffset(slot)); + } else { + loadPtr(Address(reg, JSObject::offsetOfSlots()), reg); + return Address(reg, obj->dynamicSlotIndex(slot) * sizeof(Value)); + } } #ifdef JS_CPU_X86 @@ -269,7 +218,79 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist m_assembler.cdq(); m_assembler.idivl_r(reg); } + + void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg) { + JS_ASSERT(fpReg != Registers::FPConversionTemp); + if (MacroAssemblerX86Common::getSSEState() >= HasSSE4_1) { + m_assembler.movd_rr(lo, fpReg); + m_assembler.pinsrd_rr(hi, fpReg); + } else { + m_assembler.movd_rr(lo, fpReg); + m_assembler.movd_rr(hi, Registers::FPConversionTemp); + m_assembler.unpcklps_rr(Registers::FPConversionTemp, fpReg); + } + } +#endif + + /* + * Move a register pair which may indicate either an int32_t or double into fpreg, + * converting to double in the int32_t case. + */ + void moveInt32OrDouble(RegisterID data, RegisterID type, Address address, FPRegisterID fpreg) + { +#ifdef JS_CPU_X86 + fastLoadDouble(data, type, fpreg); + Jump notInteger = testInt32(Assembler::NotEqual, type); + convertInt32ToDouble(data, fpreg); + notInteger.linkTo(label(), this); +#else + Jump notInteger = testInt32(Assembler::NotEqual, type); + convertInt32ToDouble(data, fpreg); + Jump fallthrough = jump(); + notInteger.linkTo(label(), this); + + /* Store the components, then read it back out as a double. */ + storeValueFromComponents(type, data, address); + loadDouble(address, fpreg); + + fallthrough.linkTo(label(), this); +#endif + } + + /* + * Move a memory address which contains either an int32_t or double into fpreg, + * converting to double in the int32_t case. + */ + template + void moveInt32OrDouble(T address, FPRegisterID fpreg) + { + Jump notInteger = testInt32(Assembler::NotEqual, address); + convertInt32ToDouble(payloadOf(address), fpreg); + Jump fallthrough = jump(); + notInteger.linkTo(label(), this); + loadDouble(address, fpreg); + fallthrough.linkTo(label(), this); + } + + /* Ensure that an in-memory address is definitely a double. */ + void ensureInMemoryDouble(Address address) + { + Jump notInteger = testInt32(Assembler::NotEqual, address); + convertInt32ToDouble(payloadOf(address), Registers::FPConversionTemp); + storeDouble(Registers::FPConversionTemp, address); + notInteger.linkTo(label(), this); + } + + void negateDouble(FPRegisterID fpreg) + { +#if defined JS_CPU_X86 || defined JS_CPU_X64 + static const uint64_t DoubleNegMask = 0x8000000000000000ULL; + loadDouble(&DoubleNegMask, Registers::FPConversionTemp); + xorDouble(Registers::FPConversionTemp, fpreg); +#elif defined JS_CPU_ARM || defined JS_CPU_SPARC || defined JS_CPU_MIPS + negDouble(fpreg, fpreg); #endif + } /* Prepare for a call that might THROW. */ void *getFallibleCallTarget(void *fun) { @@ -289,6 +310,24 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist */ moveWithPatch(Imm32(intptr_t(fun)), JSC::ARMRegisters::ip); + return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer); +#elif defined(JS_CPU_SPARC) + /* + * We can simulate the situation in jited code to let call return to a + * target address located on stack without veneer. We record the return + * address and jump to that address after call return to jited code. The + * reason we take veneer back is jited code maybe released when + * exceptions happened. That will make the call have no chance to return + * back to jited code. + */ + moveWithPatch(Imm32(intptr_t(fun)), JSC::SparcRegisters::i0); + return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer); +#elif defined(JS_CPU_MIPS) + /* + * For MIPS, we need to call JaegerStubVeneer by passing + * the real target address in v0. + */ + moveWithPatch(Imm32(intptr_t(fun)), JSC::MIPSRegisters::v0); return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer); #else /* @@ -300,7 +339,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist #endif } - static inline uint32 align(uint32 bytes, uint32 alignment) { + static inline uint32_t align(uint32_t bytes, uint32_t alignment) { return (alignment - (bytes % alignment)) % alignment; } @@ -310,7 +349,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // boundary. // // Returns an offset that can be used to index into this stack - StackMarker allocStack(uint32 bytes, uint32 alignment = 4) { + StackMarker allocStack(uint32_t bytes, uint32_t alignment = 4) { bytes += align(bytes + extraStackSpace, alignment); subPtr(Imm32(bytes), stackPointerRegister); extraStackSpace += bytes; @@ -330,10 +369,14 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist pop(reg); } - static const uint32 StackAlignment = 16; +#if defined JS_CPU_MIPS + static const uint32_t StackAlignment = 8; +#else + static const uint32_t StackAlignment = 16; +#endif - static inline uint32 alignForCall(uint32 stackBytes) { -#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + static inline uint32_t alignForCall(uint32_t stackBytes) { +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) || defined(JS_CPU_MIPS) // If StackAlignment is a power of two, % is just two shifts. // 16 - (x % 16) gives alignment, extra % 16 handles total == 0. return align(stackBytes, StackAlignment); @@ -353,11 +396,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // the constant is provided here in order to appropriately adjust the // stack. #ifdef _WIN64 - static const uint32 ReturnStackAdjustment = 32; + static const uint32_t ReturnStackAdjustment = 32; #elif defined(JS_CPU_X86) && defined(JS_NO_FASTCALL) - static const uint32 ReturnStackAdjustment = 16; + static const uint32_t ReturnStackAdjustment = 16; #else - static const uint32 ReturnStackAdjustment = 0; + static const uint32_t ReturnStackAdjustment = 0; #endif void throwInJIT() { @@ -369,17 +412,17 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // Windows x64 requires extra space in between calls. #ifdef _WIN64 - static const uint32 ShadowStackSpace = 32; + static const uint32_t ShadowStackSpace = 32; #elif defined(JS_CPU_SPARC) - static const uint32 ShadowStackSpace = 92; + static const uint32_t ShadowStackSpace = 92; #else - static const uint32 ShadowStackSpace = 0; + static const uint32_t ShadowStackSpace = 0; #endif #if defined(JS_CPU_SPARC) - static const uint32 BaseStackSpace = 104; + static const uint32_t BaseStackSpace = 104; #else - static const uint32 BaseStackSpace = 0; + static const uint32_t BaseStackSpace = 0; #endif // Prepare the stack for a call sequence. This must be called AFTER all @@ -391,11 +434,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // Attempts to perform loads, nested calls, or anything that can clobber // a register, is asking for breaking on some platform or some situation. // Be careful to limit to storeArg() during setupABICall. - void setupABICall(Registers::CallConvention convention, uint32 generalArgs) { + void setupABICall(Registers::CallConvention convention, uint32_t generalArgs) { JS_ASSERT(!callIsAligned); - uint32 numArgRegs = Registers::numArgRegs(convention); - uint32 pushCount = (generalArgs > numArgRegs) + uint32_t numArgRegs = Registers::numArgRegs(convention); + uint32_t pushCount = (generalArgs > numArgRegs) ? generalArgs - numArgRegs : 0; @@ -404,7 +447,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // Find the total number of bytes the stack will have been adjusted by, // in order to compute alignment. - uint32 total = (pushCount * sizeof(void *)) + + uint32_t total = (pushCount * sizeof(void *)) + extraStackSpace; stackAdjust = (pushCount * sizeof(void *)) + @@ -426,7 +469,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } // Computes an interior pointer into VMFrame during a call. - Address vmFrameOffset(uint32 offs) { + Address vmFrameOffset(uint32_t offs) { return Address(stackPointerRegister, stackAdjust + extraStackSpace + offs); } @@ -446,18 +489,18 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // This is an internal function only for use inside a setupABICall(), // callWithABI() sequence, and only for arguments known to fit in // registers. - Address addressOfArg(uint32 i) { - uint32 numArgRegs = Registers::numArgRegs(callConvention); + Address addressOfArg(uint32_t i) { + uint32_t numArgRegs = Registers::numArgRegs(callConvention); JS_ASSERT(i >= numArgRegs); // Note that shadow space is for the callee to spill, and thus it must // be skipped when writing its arguments. - int32 spOffset = ((i - numArgRegs) * sizeof(void *)) + ShadowStackSpace; + int32_t spOffset = ((i - numArgRegs) * sizeof(void *)) + ShadowStackSpace; return Address(stackPointerRegister, spOffset); } // Push an argument for a call. - void storeArg(uint32 i, RegisterID reg) { + void storeArg(uint32_t i, RegisterID reg) { JS_ASSERT(callIsAligned); RegisterID to; if (Registers::regForArg(callConvention, i, &to)) { @@ -471,7 +514,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // This variant can clobber temporary registers. However, it will NOT // clobber any registers that have already been set via storeArg(). - void storeArg(uint32 i, Address address) { + void storeArg(uint32_t i, Address address) { JS_ASSERT(callIsAligned); RegisterID to; if (Registers::regForArg(callConvention, i, &to)) { @@ -479,7 +522,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist availInCall.takeRegUnchecked(to); } else if (!availInCall.empty()) { // Memory-to-memory, and there is a temporary register free. - RegisterID reg = availInCall.takeAnyReg(); + RegisterID reg = availInCall.takeAnyReg().reg(); loadPtr(address, reg); storeArg(i, reg); availInCall.putReg(reg); @@ -493,7 +536,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // This variant can clobber temporary registers. However, it will NOT // clobber any registers that have already been set via storeArg(). - void storeArgAddr(uint32 i, Address address) { + void storeArgAddr(uint32_t i, Address address) { JS_ASSERT(callIsAligned); RegisterID to; if (Registers::regForArg(callConvention, i, &to)) { @@ -501,7 +544,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist availInCall.takeRegUnchecked(to); } else if (!availInCall.empty()) { // Memory-to-memory, and there is a temporary register free. - RegisterID reg = availInCall.takeAnyReg(); + RegisterID reg = availInCall.takeAnyReg().reg(); lea(address, reg); storeArg(i, reg); availInCall.putReg(reg); @@ -513,14 +556,14 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } } - void storeArg(uint32 i, Imm32 imm) { + void storeArg(uint32_t i, ImmPtr imm) { JS_ASSERT(callIsAligned); RegisterID to; if (Registers::regForArg(callConvention, i, &to)) { move(imm, to); availInCall.takeRegUnchecked(to); } else { - store32(imm, addressOfArg(i)); + storePtr(imm, addressOfArg(i)); } } @@ -530,6 +573,16 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // // After callWithABI(), the call state is reset, so a new call may begin. Call callWithABI(void *fun, bool canThrow) { +#ifdef JS_CPU_ARM + // the repatcher requires that these instructions are adjacent in + // memory, make sure that they are in fact adjacent. + // Theoretically, this requires only 12 bytes of space, however + // there are at least a couple of off-by-one errors that I've noticed + // that make 12 insufficent. In case 16 is also insufficent, I've bumped + // it to 20. + ensureSpace(20); + int initFlushCount = flushCount(); +#endif // [Bug 614953]: This can only be made conditional once the ARM back-end // is able to distinguish and patch both call sequences. Other // architecutres are unaffected regardless. @@ -544,7 +597,9 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist Call cl = call(); callPatches.append(CallPatch(cl, fun)); - +#ifdef JS_CPU_ARM + JS_ASSERT(initFlushCount == flushCount()); +#endif if (stackAdjust) addPtr(Imm32(stackAdjust), stackPointerRegister); @@ -571,9 +626,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } -#define STUB_CALL_TYPE(type) \ - Call callWithVMFrame(type stub, jsbytecode *pc, uint32 fd) { \ - return fallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stub), pc, fd); \ +#define STUB_CALL_TYPE(type) \ + Call callWithVMFrame(bool inlining, type stub, jsbytecode *pc, \ + DataLabelPtr *pinlined, uint32_t fd) { \ + return fallibleVMCall(inlining, JS_FUNC_TO_DATA_PTR(void *, stub), \ + pc, pinlined, fd); \ } STUB_CALL_TYPE(JSObjStub); @@ -583,7 +640,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist #undef STUB_CALL_TYPE - void setupInfallibleVMFrame(int32 frameDepth) { + void setupFrameDepth(int32_t frameDepth) { // |frameDepth < 0| implies ic::SplatApplyArgs has been called which // means regs.sp has already been set in the VMFrame. if (frameDepth >= 0) { @@ -591,9 +648,13 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist // regs->sp = sp addPtr(Imm32(sizeof(StackFrame) + frameDepth * sizeof(jsval)), JSFrameReg, - ClobberInCall); - storePtr(ClobberInCall, FrameAddress(offsetof(VMFrame, regs.sp))); + Registers::ClobberInCall); + storePtr(Registers::ClobberInCall, FrameAddress(VMFrame::offsetOfRegsSp())); } + } + + void setupInfallibleVMFrame(int32_t frameDepth) { + setupFrameDepth(frameDepth); // The JIT has moved Arg1 already, and we've guaranteed to not clobber // it. Move ArgReg0 into place now. setupFallibleVMFrame will not @@ -601,31 +662,73 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist move(MacroAssembler::stackPointerRegister, Registers::ArgReg0); } - void setupFallibleVMFrame(jsbytecode *pc, int32 frameDepth) { + void setupFallibleVMFrame(bool inlining, jsbytecode *pc, + DataLabelPtr *pinlined, int32_t frameDepth) { setupInfallibleVMFrame(frameDepth); /* regs->fp = fp */ storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp)); /* PC -> regs->pc :( */ - storePtr(ImmPtr(pc), - FrameAddress(offsetof(VMFrame, regs) + offsetof(FrameRegs, pc))); + storePtr(ImmPtr(pc), FrameAddress(VMFrame::offsetOfRegsPc())); + + if (inlining) { + /* inlined -> regs->inlined :( */ + DataLabelPtr ptr = storePtrWithPatch(ImmPtr(NULL), + FrameAddress(VMFrame::offsetOfInlined)); + if (pinlined) + *pinlined = ptr; + } + + restoreStackBase(); + } + + void setupFallibleABICall(bool inlining, jsbytecode *pc, int32_t frameDepth) { + setupFrameDepth(frameDepth); + + /* Store fp and pc */ + storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp)); + storePtr(ImmPtr(pc), FrameAddress(VMFrame::offsetOfRegsPc())); + + if (inlining) { + /* ABI calls cannot be made from inlined frames. */ + storePtr(ImmPtr(NULL), FrameAddress(VMFrame::offsetOfInlined)); + } + } + + void restoreStackBase() { +#if defined(JS_CPU_X86) + /* + * We use the %ebp base stack pointer on x86 to store the JSStackFrame. + * Restore this before calling so that debuggers can construct a + * coherent stack if we crash outside of JIT code. + */ + JS_STATIC_ASSERT(JSFrameReg == JSC::X86Registers::ebp); + move(JSC::X86Registers::esp, JSFrameReg); + addPtr(Imm32(VMFrame::STACK_BASE_DIFFERENCE), JSFrameReg); +#endif } // An infallible VM call is a stub call (taking a VMFrame & and one // optional parameter) that does not need |pc| and |fp| updated, since // the call is guaranteed to not fail. However, |sp| is always coherent. - Call infallibleVMCall(void *ptr, int32 frameDepth) { + Call infallibleVMCall(void *ptr, int32_t frameDepth) { setupInfallibleVMFrame(frameDepth); return wrapVMCall(ptr); } // A fallible VM call is a stub call (taking a VMFrame & and one optional // parameter) that needs the entire VMFrame to be coherent, meaning that - // |pc| and |fp| are guaranteed to be up-to-date. - Call fallibleVMCall(void *ptr, jsbytecode *pc, int32 frameDepth) { - setupFallibleVMFrame(pc, frameDepth); - return wrapVMCall(ptr); + // |pc|, |inlined| and |fp| are guaranteed to be up-to-date. + Call fallibleVMCall(bool inlining, void *ptr, jsbytecode *pc, + DataLabelPtr *pinlined, int32_t frameDepth) { + setupFallibleVMFrame(inlining, pc, pinlined, frameDepth); + Call call = wrapVMCall(ptr); + + // Restore the frame pointer from the VM. + loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + + return call; } Call wrapVMCall(void *ptr) { @@ -649,11 +752,27 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist return callWithABI(ptr, true); } - void finalize(JSC::LinkBuffer &linker) { + // Constant doubles can't be directly moved into a register, we need to put + // them in memory and load them back with. + void slowLoadConstantDouble(double d, FPRegisterID fpreg) { + DoublePatch patch; + patch.d = d; + patch.label = loadDouble(NULL, fpreg); + doublePatches.append(patch); + } + + size_t numDoubles() { return doublePatches.length(); } + + void finalize(JSC::LinkBuffer &linker, double *doubleVec = NULL) { for (size_t i = 0; i < callPatches.length(); i++) { CallPatch &patch = callPatches[i]; linker.link(patch.call, JSC::FunctionPtr(patch.fun)); } + for (size_t i = 0; i < doublePatches.length(); i++) { + DoublePatch &patch = doublePatches[i]; + doubleVec[i] = patch.d; + linker.patch(patch.label, &doubleVec[i]); + } } struct FastArrayLoadFails { @@ -661,13 +780,27 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist Jump holeCheck; }; - Jump guardArrayCapacity(RegisterID objReg, const Int32Key &key) { - Address capacity(objReg, offsetof(JSObject, capacity)); + // Guard an extent (capacity, length or initialized length) on an array or typed array. + Jump guardArrayExtent(int offset, RegisterID reg, + const Int32Key &key, Condition cond) { + Address extent(reg, offset); + if (key.isConstant()) + return branch32(cond, extent, Imm32(key.index())); + return branch32(cond, extent, key.reg()); + } + + Jump guardElementNotHole(RegisterID elements, const Int32Key &key) { + Jump jmp; + if (key.isConstant()) { - JS_ASSERT(key.index() >= 0); - return branch32(BelowOrEqual, capacity, Imm32(key.index())); + Address slot(elements, key.index() * sizeof(Value)); + jmp = guardNotHole(slot); + } else { + BaseIndex slot(elements, key.reg(), JSVAL_SCALE); + jmp = guardNotHole(slot); } - return branch32(BelowOrEqual, capacity, key.reg()); + + return jmp; } // Load a jsval from an array slot, given a key. |objReg| is clobbered. @@ -675,34 +808,102 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist RegisterID typeReg, RegisterID dataReg) { JS_ASSERT(objReg != typeReg); - FastArrayLoadFails fails; - fails.rangeCheck = guardArrayCapacity(objReg, key); + RegisterID elementsReg = objReg; + loadPtr(Address(objReg, JSObject::offsetOfElements()), elementsReg); - RegisterID dslotsReg = objReg; - loadPtr(Address(objReg, offsetof(JSObject, slots)), dslotsReg); + FastArrayLoadFails fails; + fails.rangeCheck = guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + objReg, key, BelowOrEqual); // Load the slot out of the array. if (key.isConstant()) { - Address slot(objReg, key.index() * sizeof(Value)); - fails.holeCheck = fastArrayLoadSlot(slot, typeReg, dataReg); + Address slot(elementsReg, key.index() * sizeof(Value)); + fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg); } else { - BaseIndex slot(objReg, key.reg(), JSVAL_SCALE); - fails.holeCheck = fastArrayLoadSlot(slot, typeReg, dataReg); + BaseIndex slot(elementsReg, key.reg(), JSVAL_SCALE); + fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg); } return fails; } - void loadObjClass(RegisterID objReg, RegisterID destReg) { - loadPtr(Address(objReg, offsetof(JSObject, clasp)), destReg); + void storeKey(const Int32Key &key, Address address) { + if (key.isConstant()) + store32(Imm32(key.index()), address); + else + store32(key.reg(), address); + } + + void bumpKey(Int32Key &key, int32_t delta) { + if (key.isConstant()) + key.index_ += delta; + else + add32(Imm32(delta), key.reg()); + } + + void loadFrameActuals(JSFunction *fun, RegisterID reg) { + /* Bias for the case where there was an arguments overflow. */ + load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), reg); + add32(Imm32(fun->nargs + 2), reg); + Jump overflowArgs = branchTest32(Assembler::NonZero, + Address(JSFrameReg, StackFrame::offsetOfFlags()), + Imm32(StackFrame::OVERFLOW_ARGS)); + move(Imm32(fun->nargs), reg); + overflowArgs.linkTo(label(), this); + lshiftPtr(Imm32(3), reg); + negPtr(reg); + addPtr(JSFrameReg, reg); + } + + void loadBaseShape(RegisterID obj, RegisterID dest) { + loadPtr(Address(obj, JSObject::offsetOfShape()), dest); + loadPtr(Address(dest, Shape::offsetOfBase()), dest); + } + + void loadObjClass(RegisterID obj, RegisterID dest) { + loadBaseShape(obj, dest); + loadPtr(Address(dest, BaseShape::offsetOfClass()), dest); } Jump testClass(Condition cond, RegisterID claspReg, js::Class *clasp) { return branchPtr(cond, claspReg, ImmPtr(clasp)); } - Jump testObjClass(Condition cond, RegisterID objReg, js::Class *clasp) { - return branchPtr(cond, Address(objReg, offsetof(JSObject, clasp)), ImmPtr(clasp)); + Jump testObjClass(Condition cond, RegisterID obj, RegisterID temp, js::Class *clasp) { + loadBaseShape(obj, temp); + return branchPtr(cond, Address(temp, BaseShape::offsetOfClass()), ImmPtr(clasp)); + } + + Jump testFunction(Condition cond, RegisterID fun, RegisterID temp) { + return testObjClass(cond, fun, temp, &js::FunctionClass); + } + + void branchValue(Condition cond, RegisterID reg, int32_t value, RegisterID result) + { + if (Registers::maskReg(result) & Registers::SingleByteRegs) { + set32(cond, reg, Imm32(value), result); + } else { + Jump j = branch32(cond, reg, Imm32(value)); + move(Imm32(0), result); + Jump skip = jump(); + j.linkTo(label(), this); + move(Imm32(1), result); + skip.linkTo(label(), this); + } + } + + void branchValue(Condition cond, RegisterID lreg, RegisterID rreg, RegisterID result) + { + if (Registers::maskReg(result) & Registers::SingleByteRegs) { + set32(cond, lreg, rreg, result); + } else { + Jump j = branch32(cond, lreg, rreg); + move(Imm32(0), result); + Jump skip = jump(); + j.linkTo(label(), this); + move(Imm32(1), result); + skip.linkTo(label(), this); + } } void rematPayload(const StateRemat &remat, RegisterID reg) { @@ -712,32 +913,485 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist move(remat.reg(), reg); } - void loadDynamicSlot(RegisterID objReg, uint32 slot, + void loadDynamicSlot(RegisterID objReg, uint32_t index, RegisterID typeReg, RegisterID dataReg) { - loadPtr(Address(objReg, offsetof(JSObject, slots)), dataReg); - loadValueAsComponents(Address(dataReg, slot * sizeof(Value)), typeReg, dataReg); + loadPtr(Address(objReg, JSObject::offsetOfSlots()), dataReg); + loadValueAsComponents(Address(dataReg, index * sizeof(Value)), typeReg, dataReg); } void loadObjProp(JSObject *obj, RegisterID objReg, const js::Shape *shape, RegisterID typeReg, RegisterID dataReg) { - if (shape->isMethod()) - loadValueAsComponents(ObjectValue(shape->methodObject()), typeReg, dataReg); - else if (obj->hasSlotsArray()) - loadDynamicSlot(objReg, shape->slot, typeReg, dataReg); + if (obj->isFixedSlot(shape->slot())) + loadInlineSlot(objReg, shape->slot(), typeReg, dataReg); else - loadInlineSlot(objReg, shape->slot, typeReg, dataReg); + loadDynamicSlot(objReg, obj->dynamicSlotIndex(shape->slot()), typeReg, dataReg); } - static uint32 maskAddress(Address address) { +#ifdef JS_METHODJIT_TYPED_ARRAY + // Load a value from a typed array's packed data vector into dataReg. + // This function expects the following combinations of typeReg, dataReg and tempReg: + // 1) for all INT arrays other than UINT32: + // - dataReg is a GP-register + // - typeReg is optional + // - tempReg is not set + // 2) for UINT32: + // - dataReg is either a FP-register or a GP-register + // - typeReg is set if dataReg is a GP-register + // - tempReg is set if dataReg is a FP-register + // 3) for FLOAT32 and FLOAT64: + // - dataReg is either a FP-register or a GP-register + // - typeReg is set if dataReg is a GP-register + // - tempReg is not set + template + void loadFromTypedArray(int atype, T address, MaybeRegisterID typeReg, + AnyRegisterID dataReg, MaybeRegisterID tempReg) + { + // If dataReg is an FP-register we don't use typeReg. + JS_ASSERT_IF(dataReg.isFPReg(), !typeReg.isSet()); + + // We only need tempReg for Uint32Array and only if dataReg is an FP-register. + JS_ASSERT_IF(atype != js::TypedArray::TYPE_UINT32 || dataReg.isReg(), !tempReg.isSet()); + + switch (atype) { + case js::TypedArray::TYPE_INT8: + load8SignExtend(address, dataReg.reg()); + if (typeReg.isSet()) + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + break; + case js::TypedArray::TYPE_UINT8: + case js::TypedArray::TYPE_UINT8_CLAMPED: + load8ZeroExtend(address, dataReg.reg()); + if (typeReg.isSet()) + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + break; + case js::TypedArray::TYPE_INT16: + load16SignExtend(address, dataReg.reg()); + if (typeReg.isSet()) + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + break; + case js::TypedArray::TYPE_UINT16: + load16(address, dataReg.reg()); + if (typeReg.isSet()) + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + break; + case js::TypedArray::TYPE_INT32: + load32(address, dataReg.reg()); + if (typeReg.isSet()) + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + break; + case js::TypedArray::TYPE_UINT32: + { + // For Uint32Array the result is either int32_t or double. + // If dataReg is a GP-register, load a double or int32_t into dataReg/typeReg. + // If dataReg is a FP-register, load the value as double. + if (dataReg.isReg()) { + load32(address, dataReg.reg()); + move(ImmType(JSVAL_TYPE_INT32), typeReg.reg()); + Jump safeInt = branch32(Assembler::Below, dataReg.reg(), Imm32(0x80000000)); + convertUInt32ToDouble(dataReg.reg(), Registers::FPConversionTemp); + breakDouble(Registers::FPConversionTemp, typeReg.reg(), dataReg.reg()); + safeInt.linkTo(label(), this); + } else { + load32(address, tempReg.reg()); + convertUInt32ToDouble(tempReg.reg(), dataReg.fpreg()); + } + break; + } + case js::TypedArray::TYPE_FLOAT32: + case js::TypedArray::TYPE_FLOAT64: + { + FPRegisterID fpreg = dataReg.isReg() + ? Registers::FPConversionTemp + : dataReg.fpreg(); + if (atype == js::TypedArray::TYPE_FLOAT32) + loadFloat(address, fpreg); + else + loadDouble(address, fpreg); + // Make sure NaN gets canonicalized. If dataReg is not an FP-register + // we have to use loadStaticDouble as we were probably called from an + // IC and we can't use slowLoadConstantDouble. + Jump notNaN = branchDouble(Assembler::DoubleEqual, fpreg, fpreg); + if (dataReg.isReg()) + loadStaticDouble(&js_NaN, Registers::FPConversionTemp, dataReg.reg()); + else + slowLoadConstantDouble(js_NaN, fpreg); + notNaN.linkTo(label(), this); + if (dataReg.isReg()) + breakDouble(Registers::FPConversionTemp, typeReg.reg(), dataReg.reg()); + break; + } + } + } + + void loadFromTypedArray(int atype, RegisterID objReg, Int32Key key, + MaybeRegisterID typeReg, AnyRegisterID dataReg, + MaybeRegisterID tempReg) + { + int shift = TypedArray::slotWidth(atype); + + if (key.isConstant()) { + Address addr(objReg, key.index() * shift); + loadFromTypedArray(atype, addr, typeReg, dataReg, tempReg); + } else { + Assembler::Scale scale = Assembler::TimesOne; + switch (shift) { + case 2: + scale = Assembler::TimesTwo; + break; + case 4: + scale = Assembler::TimesFour; + break; + case 8: + scale = Assembler::TimesEight; + break; + } + BaseIndex addr(objReg, key.reg(), scale); + loadFromTypedArray(atype, addr, typeReg, dataReg, tempReg); + } + } + + template + void storeToTypedIntArray(int atype, S src, T address) + { + switch (atype) { + case js::TypedArray::TYPE_INT8: + case js::TypedArray::TYPE_UINT8: + case js::TypedArray::TYPE_UINT8_CLAMPED: + store8(src, address); + break; + case js::TypedArray::TYPE_INT16: + case js::TypedArray::TYPE_UINT16: + store16(src, address); + break; + case js::TypedArray::TYPE_INT32: + case js::TypedArray::TYPE_UINT32: + store32(src, address); + break; + default: + JS_NOT_REACHED("unknown int array type"); + } + } + + template + void storeToTypedFloatArray(int atype, S src, T address) + { + if (atype == js::TypedArray::TYPE_FLOAT32) + storeFloat(src, address); + else + storeDouble(src, address); + } + + template + void storeToTypedArray(int atype, ValueRemat vr, T address) + { + if (atype == js::TypedArray::TYPE_FLOAT32 || atype == js::TypedArray::TYPE_FLOAT64) { + if (vr.isConstant()) + storeToTypedFloatArray(atype, ImmDouble(vr.value().toDouble()), address); + else + storeToTypedFloatArray(atype, vr.fpReg(), address); + } else { + if (vr.isConstant()) + storeToTypedIntArray(atype, Imm32(vr.value().toInt32()), address); + else + storeToTypedIntArray(atype, vr.dataReg(), address); + } + } + + void storeToTypedArray(int atype, RegisterID objReg, Int32Key key, ValueRemat vr) + { + int shift = TypedArray::slotWidth(atype); + if (key.isConstant()) { + Address addr(objReg, key.index() * shift); + storeToTypedArray(atype, vr, addr); + } else { + Assembler::Scale scale = Assembler::TimesOne; + switch (shift) { + case 2: + scale = Assembler::TimesTwo; + break; + case 4: + scale = Assembler::TimesFour; + break; + case 8: + scale = Assembler::TimesEight; + break; + } + BaseIndex addr(objReg, key.reg(), scale); + storeToTypedArray(atype, vr, addr); + } + } + + void clampInt32ToUint8(RegisterID reg) + { + Jump j = branch32(Assembler::GreaterThanOrEqual, reg, Imm32(0)); + move(Imm32(0), reg); + Jump done = jump(); + j.linkTo(label(), this); + j = branch32(Assembler::LessThanOrEqual, reg, Imm32(255)); + move(Imm32(255), reg); + j.linkTo(label(), this); + done.linkTo(label(), this); + } + + // Inline version of js_TypedArray_uint8_clamp_double. + void clampDoubleToUint8(FPRegisterID fpReg, FPRegisterID fpTemp, RegisterID reg) + { + JS_ASSERT(fpTemp != Registers::FPConversionTemp); + + // <= 0 or NaN ==> 0 + zeroDouble(fpTemp); + Jump positive = branchDouble(Assembler::DoubleGreaterThan, fpReg, fpTemp); + move(Imm32(0), reg); + Jump done1 = jump(); + + // Add 0.5 and truncate. + positive.linkTo(label(), this); + slowLoadConstantDouble(0.5, fpTemp); + addDouble(fpReg, fpTemp); + Jump notInt = branchTruncateDoubleToInt32(fpTemp, reg); + + // > 255 ==> 255 + Jump inRange = branch32(Assembler::BelowOrEqual, reg, Imm32(255)); + notInt.linkTo(label(), this); + move(Imm32(255), reg); + Jump done2 = jump(); + + // Check if we had a tie. + inRange.linkTo(label(), this); + convertInt32ToDouble(reg, Registers::FPConversionTemp); + Jump done3 = branchDouble(Assembler::DoubleNotEqual, fpTemp, Registers::FPConversionTemp); + + // It was a tie. Mask out the ones bit to get an even value. + // See js_TypedArray_uint8_clamp_double for the reasoning behind this. + and32(Imm32(~1), reg); + + done1.linkTo(label(), this); + done2.linkTo(label(), this); + done3.linkTo(label(), this); + } +#endif /* JS_METHODJIT_TYPED_ARRAY */ + + Address objPropAddress(JSObject *obj, RegisterID objReg, uint32_t slot) + { + if (obj->isFixedSlot(slot)) + return Address(objReg, JSObject::getFixedSlotOffset(slot)); + loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg); + return Address(objReg, obj->dynamicSlotIndex(slot) * sizeof(Value)); + } + + static uint32_t maskAddress(Address address) { return Registers::maskReg(address.base); } - static uint32 maskAddress(BaseIndex address) { + static uint32_t maskAddress(BaseIndex address) { return Registers::maskReg(address.base) | Registers::maskReg(address.index); } + + /* + * Generate code testing whether an in memory value at address has a type + * in the specified set. Updates mismatches with any failure jumps. Assumes + * that no temporary (caller save) registers are live. + */ + bool generateTypeCheck(JSContext *cx, Address address, + types::TypeSet *types, Vector *mismatches) + { + if (types->unknown()) + return true; + + Vector matches(cx); + + if (types->hasType(types::Type::DoubleType())) { + /* Type sets containing double also contain int. */ + if (!matches.append(testNumber(Assembler::Equal, address))) + return false; + } else if (types->hasType(types::Type::Int32Type())) { + if (!matches.append(testInt32(Assembler::Equal, address))) + return false; + } + + if (types->hasType(types::Type::UndefinedType())) { + if (!matches.append(testUndefined(Assembler::Equal, address))) + return false; + } + + if (types->hasType(types::Type::BooleanType())) { + if (!matches.append(testBoolean(Assembler::Equal, address))) + return false; + } + + if (types->hasType(types::Type::StringType())) { + if (!matches.append(testString(Assembler::Equal, address))) + return false; + } + + if (types->hasType(types::Type::NullType())) { + if (!matches.append(testNull(Assembler::Equal, address))) + return false; + } + + unsigned count = 0; + if (types->hasType(types::Type::AnyObjectType())) { + if (!matches.append(testObject(Assembler::Equal, address))) + return false; + } else { + count = types->getObjectCount(); + } + + if (count != 0) { + if (!mismatches->append(testObject(Assembler::NotEqual, address))) + return false; + RegisterID reg = Registers::ArgReg1; + + loadPayload(address, reg); + + for (unsigned i = 0; i < count; i++) { + if (JSObject *object = types->getSingleObject(i)) { + if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object)))) + return false; + } + } + + loadPtr(Address(reg, JSObject::offsetOfType()), reg); + + for (unsigned i = 0; i < count; i++) { + if (types::TypeObject *object = types->getTypeObject(i)) { + if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object)))) + return false; + } + } + } + + if (!mismatches->append(jump())) + return false; + + for (unsigned i = 0; i < matches.length(); i++) + matches[i].linkTo(label(), this); + + return true; + } + + /* + * Get a free object for the specified GC kind in compartment, writing it + * to result and filling it in according to templateObject. Returns a jump + * taken if a free thing was not retrieved. + */ + Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject) + { + gc::AllocKind allocKind = templateObject->getAllocKind(); + + JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); + int thingSize = (int)gc::Arena::thingSize(allocKind); + + JS_ASSERT(cx->typeInferenceEnabled()); + JS_ASSERT(!templateObject->hasDynamicSlots()); + JS_ASSERT(!templateObject->hasDynamicElements()); + +#ifdef JS_GC_ZEAL + if (cx->runtime->needZealousGC()) + return jump(); +#endif + + /* + * Inline FreeSpan::allocate. Only the case where the current freelist + * span is not empty is handled. + */ + gc::FreeSpan *list = const_cast + (cx->compartment->arenas.getFreeList(allocKind)); + loadPtr(&list->first, result); + + Jump jump = branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result); + + addPtr(Imm32(thingSize), result); + storePtr(result, &list->first); + + /* + * Fill in the blank object. Order doesn't matter here, from here + * everything is infallible. Note that this bakes GC thing pointers + * into the code without explicitly pinning them. With type inference + * enabled, JIT code is collected on GC except when analysis or + * compilation is active, in which case type objects won't be collected + * but other things may be. The shape held by templateObject *must* be + * pinned against GC either by the script or by some type object. + */ + + int elementsOffset = JSObject::offsetOfFixedElements(); + + /* + * Write out the elements pointer before readjusting the result register, + * as for dense arrays we will need to get the address of the fixed + * elements first. + */ + if (templateObject->isDenseArray()) { + JS_ASSERT(!templateObject->getDenseArrayInitializedLength()); + addPtr(Imm32(-thingSize + elementsOffset), result); + storePtr(result, Address(result, -elementsOffset + JSObject::offsetOfElements())); + addPtr(Imm32(-elementsOffset), result); + } else { + addPtr(Imm32(-thingSize), result); + storePtr(ImmPtr(emptyObjectElements), Address(result, JSObject::offsetOfElements())); + } + + storePtr(ImmPtr(templateObject->lastProperty()), Address(result, JSObject::offsetOfShape())); + storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType())); + storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots())); + + if (templateObject->isDenseArray()) { + /* Fill in the elements header. */ + store32(Imm32(templateObject->getDenseArrayCapacity()), + Address(result, elementsOffset + ObjectElements::offsetOfCapacity())); + store32(Imm32(templateObject->getDenseArrayInitializedLength()), + Address(result, elementsOffset + ObjectElements::offsetOfInitializedLength())); + store32(Imm32(templateObject->getArrayLength()), + Address(result, elementsOffset + ObjectElements::offsetOfLength())); + } else { + /* + * Fixed slots of non-array objects are required to be initialized; + * Use the values currently in the template object. + */ + for (unsigned i = 0; i < templateObject->slotSpan(); i++) { + storeValue(templateObject->getFixedSlot(i), + Address(result, JSObject::getFixedSlotOffset(i))); + } + } + + if (templateObject->hasPrivate()) { + uint32_t nfixed = templateObject->numFixedSlots(); + storePtr(ImmPtr(templateObject->getPrivate()), + Address(result, JSObject::getPrivateDataOffset(nfixed))); + } + + return jump; + } + + /* Add the value stored in 'value' to the accumulator 'counter'. */ + void addCounter(const double *value, double *counter, RegisterID scratch) + { + loadDouble(value, Registers::FPConversionTemp); + move(ImmPtr(counter), scratch); + addDouble(Address(scratch), Registers::FPConversionTemp); + storeDouble(Registers::FPConversionTemp, Address(scratch)); + } + + /* Add one to the accumulator 'counter'. */ + void bumpCounter(double *counter, RegisterID scratch) + { + addCounter(&oneDouble, counter, scratch); + } + + /* Bump the stub call count for script/pc if they are being counted. */ + void bumpStubCounter(JSScript *script, jsbytecode *pc, RegisterID scratch) + { + if (script->pcCounters) { + OpcodeCounts counts = script->getCounts(pc); + double *counter = &counts.get(OpcodeCounts::BASE_METHODJIT_STUBS); + bumpCounter(counter, scratch); + } + } + + static const double oneDouble; }; /* Return f if the script is strict mode code, f otherwise. */ @@ -761,7 +1415,7 @@ class PreserveRegisters { typedef JSC::MacroAssembler::RegisterID RegisterID; Assembler &masm; - uint32 count; + uint32_t count; RegisterID regs[JSC::MacroAssembler::TotalRegisters]; public: @@ -772,7 +1426,7 @@ class PreserveRegisters { JS_ASSERT(!count); while (!mask.empty()) { - RegisterID reg = mask.takeAnyReg(); + RegisterID reg = mask.takeAnyReg().reg(); regs[count++] = reg; masm.saveReg(reg); } diff --git a/deps/mozjs/js/src/methodjit/BaseCompiler.h b/deps/mozjs/js/src/methodjit/BaseCompiler.h index 33417c9e184..f8ee87130df 100644 --- a/deps/mozjs/js/src/methodjit/BaseCompiler.h +++ b/deps/mozjs/js/src/methodjit/BaseCompiler.h @@ -41,7 +41,6 @@ #define jsjaeger_compilerbase_h__ #include "jscntxt.h" -#include "jstl.h" #include "assembler/assembler/MacroAssembler.h" #include "assembler/assembler/LinkBuffer.h" #include "assembler/assembler/RepatchBuffer.h" @@ -76,6 +75,7 @@ struct MacroAssemblerTypedefs { typedef JSC::RepatchBuffer RepatchBuffer; typedef JSC::CodeLocationLabel CodeLocationLabel; typedef JSC::CodeLocationDataLabel32 CodeLocationDataLabel32; + typedef JSC::CodeLocationDataLabelPtr CodeLocationDataLabelPtr; typedef JSC::CodeLocationJump CodeLocationJump; typedef JSC::CodeLocationCall CodeLocationCall; typedef JSC::CodeLocationInstruction CodeLocationInstruction; @@ -112,7 +112,8 @@ class LinkerHelper : public JSC::LinkBuffer #endif public: - LinkerHelper(Assembler &masm) : masm(masm) + LinkerHelper(Assembler &masm, JSC::CodeKind kind) : JSC::LinkBuffer(kind) + , masm(masm) #ifdef DEBUG , verifiedRange(false) #endif @@ -139,15 +140,17 @@ class LinkerHelper : public JSC::LinkBuffer #endif } - bool verifyRange(JITScript *jit) { - return verifyRange(JSC::JITCode(jit->code.m_code.executableAddress(), jit->code.m_size)); + bool verifyRange(JITChunk *chunk) { + return verifyRange(JSC::JITCode(chunk->code.m_code.executableAddress(), + chunk->code.m_size)); } JSC::ExecutablePool *init(JSContext *cx) { // The pool is incref'd after this call, so it's necessary to release() // on any failure. JSScript *script = cx->fp()->script(); - JSC::ExecutableAllocator *allocator = script->compartment->jaegerCompartment->execAlloc(); + JSC::ExecutableAllocator *allocator = script->compartment()->jaegerCompartment()->execAlloc(); + allocator->setDestroyCallback(Probes::discardExecutableRegion); JSC::ExecutablePool *pool; m_code = executableAllocAndCopy(masm, allocator, &pool); if (!m_code) { @@ -158,9 +161,12 @@ class LinkerHelper : public JSC::LinkBuffer return pool; } - JSC::CodeLocationLabel finalize() { + JSC::CodeLocationLabel finalize(VMFrame &f) { masm.finalize(*this); - return finalizeCodeAddendum(); + JSC::CodeLocationLabel label = finalizeCodeAddendum(); + Probes::registerICCode(f.cx, f.jit(), f.script(), f.pc(), + label.executableAddress(), masm.size()); + return label; } void maybeLink(MaybeJump jump, JSC::CodeLocationLabel label) { @@ -174,6 +180,40 @@ class LinkerHelper : public JSC::LinkBuffer } }; +class NativeStubLinker : public LinkerHelper +{ + public: +#ifdef JS_CPU_X64 + typedef JSC::MacroAssembler::DataLabelPtr FinalJump; +#else + typedef JSC::MacroAssembler::Jump FinalJump; +#endif + + NativeStubLinker(Assembler &masm, JITChunk *chunk, jsbytecode *pc, FinalJump done) + : LinkerHelper(masm, JSC::METHOD_CODE), chunk(chunk), pc(pc), done(done) + {} + + bool init(JSContext *cx); + + void patchJump(JSC::CodeLocationLabel target) { +#ifdef JS_CPU_X64 + patch(done, target); +#else + link(done, target); +#endif + } + + private: + JITChunk *chunk; + jsbytecode *pc; + FinalJump done; +}; + +bool +NativeStubEpilogue(VMFrame &f, Assembler &masm, NativeStubLinker::FinalJump *result, + int32_t initialFrameDepth, int32_t vpOffset, + MaybeRegisterID typeReg, MaybeRegisterID dataReg); + /* * On ARM, we periodically flush a constant pool into the instruction stream * where constants are found using PC-relative addressing. This is necessary @@ -192,67 +232,43 @@ class AutoReserveICSpace { typedef Assembler::Label Label; Assembler &masm; -#ifdef DEBUG - Label startLabel; bool didCheck; -#endif + bool *overflowSpace; + int flushCount; public: - AutoReserveICSpace(Assembler &masm) : masm(masm) { + AutoReserveICSpace(Assembler &masm, bool *overflowSpace) + : masm(masm), didCheck(false), overflowSpace(overflowSpace) + { masm.ensureSpace(reservedSpace); -#ifdef DEBUG - didCheck = false; - - startLabel = masm.label(); - - /* Assert that the constant pool is not flushed until we reach a safe point. */ - masm.allowPoolFlush(false); - - JaegerSpew(JSpew_Insns, " -- BEGIN CONSTANT-POOL-FREE REGION -- \n"); -#endif + flushCount = masm.flushCount(); } /* Allow manual IC space checks so that non-patchable code at the end of an IC section can be * free to use constant pools. */ void check() { -#ifdef DEBUG JS_ASSERT(!didCheck); didCheck = true; - Label endLabel = masm.label(); - int spaceUsed = masm.differenceBetween(startLabel, endLabel); - - /* Spew the space used, to help tuning of reservedSpace. */ - JaegerSpew(JSpew_Insns, - " -- END CONSTANT-POOL-FREE REGION: %u bytes used of %u reserved. -- \n", - spaceUsed, reservedSpace); - - /* Assert that we didn't emit more code than we protected. */ - JS_ASSERT(spaceUsed >= 0); - JS_ASSERT(size_t(spaceUsed) <= reservedSpace); - - /* Allow the pool to be flushed. */ - masm.allowPoolFlush(true); -#endif + if (masm.flushCount() != flushCount) + *overflowSpace = true; } ~AutoReserveICSpace() { -#ifdef DEBUG /* Automatically check the IC space if we didn't already do it manually. */ if (!didCheck) { check(); } -#endif } }; -# define RESERVE_IC_SPACE(__masm) AutoReserveICSpace<128> arics(__masm) +# define RESERVE_IC_SPACE(__masm) AutoReserveICSpace<256> arics(__masm, &this->overflowICSpace) # define CHECK_IC_SPACE() arics.check() /* The OOL path can need a lot of space because we save and restore a lot of registers. The actual * sequene varies. However, dumping the literal pool before an OOL block is probably a good idea * anyway, as we branch directly to the start of the block from the fast path. */ -# define RESERVE_OOL_SPACE(__masm) AutoReserveICSpace<256> arics_ool(__masm) +# define RESERVE_OOL_SPACE(__masm) AutoReserveICSpace<2048> arics_ool(__masm, &this->overflowICSpace) /* Allow the OOL patch to be checked before object destruction. Often, non-patchable epilogues or * rejoining sequences are emitted, and it isn't necessary to protect these from literal pools. */ diff --git a/deps/mozjs/js/src/methodjit/Compiler.cpp b/deps/mozjs/js/src/methodjit/Compiler.cpp index dc89f99476e..774fc11616c 100644 --- a/deps/mozjs/js/src/methodjit/Compiler.cpp +++ b/deps/mozjs/js/src/methodjit/Compiler.cpp @@ -42,7 +42,6 @@ #include "MethodJIT.h" #include "jsnum.h" #include "jsbool.h" -#include "jsemit.h" #include "jsiter.h" #include "Compiler.h" #include "StubCalls.h" @@ -59,15 +58,22 @@ #include "jscompartment.h" #include "jsobjinlines.h" #include "jsopcodeinlines.h" -#include "jshotloop.h" + +#include "builtin/RegExp.h" +#include "frontend/BytecodeEmitter.h" +#include "vm/RegExpStatics.h" +#include "vm/RegExpObject.h" #include "jsautooplen.h" +#include "jstypedarrayinlines.h" +#include "vm/RegExpObject-inl.h" using namespace js; using namespace js::mjit; #if defined(JS_POLYIC) || defined(JS_MONOIC) using namespace js::mjit::ic; #endif +using namespace js::analyze; #define RETURN_IF_OOM(retval) \ JS_BEGIN_MACRO \ @@ -83,194 +89,926 @@ static const char *OpcodeNames[] = { }; #endif -mjit::Compiler::Compiler(JSContext *cx, StackFrame *fp) +/* + * Number of times a script must be called or had a backedge before we try to + * inline its calls. + */ +static const size_t USES_BEFORE_INLINING = 10000; + +mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, + unsigned chunkIndex, bool isConstructing) : BaseCompiler(cx), - fp(fp), - script(fp->script()), - scopeChain(&fp->scopeChain()), - globalObj(scopeChain->getGlobal()), - fun(fp->isFunctionFrame() && !fp->isEvalFrame() - ? fp->fun() - : NULL), - isConstructing(fp->isConstructing()), - analysis(NULL), jumpMap(NULL), savedTraps(NULL), - frame(cx, script, fun, masm), + outerScript(outerScript), + chunkIndex(chunkIndex), + isConstructing(isConstructing), + outerChunk(outerJIT()->chunkDescriptor(chunkIndex)), + ssa(cx, outerScript), + globalObj(outerScript->hasGlobal() ? outerScript->global() : NULL), + globalSlots(globalObj ? globalObj->getRawSlots() : NULL), + frame(cx, *thisFromCtor(), masm, stubcc), + a(NULL), outer(NULL), script(NULL), PC(NULL), loop(NULL), + inlineFrames(CompilerAllocPolicy(cx, *thisFromCtor())), branchPatches(CompilerAllocPolicy(cx, *thisFromCtor())), #if defined JS_MONOIC getGlobalNames(CompilerAllocPolicy(cx, *thisFromCtor())), setGlobalNames(CompilerAllocPolicy(cx, *thisFromCtor())), callICs(CompilerAllocPolicy(cx, *thisFromCtor())), equalityICs(CompilerAllocPolicy(cx, *thisFromCtor())), - traceICs(CompilerAllocPolicy(cx, *thisFromCtor())), #endif #if defined JS_POLYIC - pics(CompilerAllocPolicy(cx, *thisFromCtor())), + pics(CompilerAllocPolicy(cx, *thisFromCtor())), getElemICs(CompilerAllocPolicy(cx, *thisFromCtor())), setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())), #endif callPatches(CompilerAllocPolicy(cx, *thisFromCtor())), - callSites(CompilerAllocPolicy(cx, *thisFromCtor())), + callSites(CompilerAllocPolicy(cx, *thisFromCtor())), doubleList(CompilerAllocPolicy(cx, *thisFromCtor())), + fixedIntToDoubleEntries(CompilerAllocPolicy(cx, *thisFromCtor())), + fixedDoubleToAnyEntries(CompilerAllocPolicy(cx, *thisFromCtor())), jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())), - jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())), - stubcc(cx, *thisFromCtor(), frame, script), - debugMode_(cx->compartment->debugMode), -#if defined JS_TRACER - addTraceHints(cx->traceJitEnabled), -#endif + jumpTableEdges(CompilerAllocPolicy(cx, *thisFromCtor())), + loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())), + chunkEdges(CompilerAllocPolicy(cx, *thisFromCtor())), + stubcc(cx, *thisFromCtor(), frame), + debugMode_(cx->compartment->debugMode()), + inlining_(false), + hasGlobalReallocation(false), oomInVector(false), - applyTricks(NoApplyTricks) + overflowICSpace(false), + gcNumber(cx->runtime->gcNumber), + applyTricks(NoApplyTricks), + pcLengths(NULL) { + /* Once a script starts getting really hot we will inline calls in it. */ + if (!debugMode() && cx->typeInferenceEnabled() && globalObj && + (outerScript->getUseCount() >= USES_BEFORE_INLINING || + cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) { + inlining_ = true; + } } CompileStatus mjit::Compiler::compile() { - JS_ASSERT_IF(isConstructing, !script->jitCtor); - JS_ASSERT_IF(!isConstructing, !script->jitNormal); + JS_ASSERT(!outerChunk.chunk); - JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal; void **checkAddr = isConstructing - ? &script->jitArityCheckCtor - : &script->jitArityCheckNormal; + ? &outerScript->jitArityCheckCtor + : &outerScript->jitArityCheckNormal; - CompileStatus status = performCompilation(jit); - if (status == Compile_Okay) { - // Global scripts don't have an arity check entry. That's okay, we - // just need a pointer so the VM can quickly decide whether this - // method can be JIT'd or not. Global scripts cannot be IC'd, since - // they have no functions, so there is no danger. - *checkAddr = (*jit)->arityCheckEntry - ? (*jit)->arityCheckEntry - : (*jit)->invokeEntry; - } else { + CompileStatus status = performCompilation(); + if (status != Compile_Okay && status != Compile_Retry) { *checkAddr = JS_UNJITTABLE_SCRIPT; + if (outerScript->function()) { + outerScript->uninlineable = true; + types::MarkTypeObjectFlags(cx, outerScript->function(), + types::OBJECT_FLAG_UNINLINEABLE); + } } return status; } -#define CHECK_STATUS(expr) \ - JS_BEGIN_MACRO \ - CompileStatus status_ = (expr); \ - if (status_ != Compile_Okay) { \ - if (oomInVector || masm.oom() || stubcc.masm.oom()) \ - js_ReportOutOfMemory(cx); \ - return status_; \ - } \ - JS_END_MACRO - CompileStatus -mjit::Compiler::performCompilation(JITScript **jitp) +mjit::Compiler::checkAnalysis(JSScript *script) { - JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", - script->filename, script->lineno, script->length); + if (script->hasClearedGlobal()) { + JaegerSpew(JSpew_Abort, "script has a cleared global\n"); + return Compile_Abort; + } + + if (!script->ensureRanAnalysis(cx, NULL)) + return Compile_Error; + if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx)) + return Compile_Error; + + ScriptAnalysis *analysis = script->analysis(); + analysis->assertMatchingDebugMode(); + if (analysis->failed()) { + JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n"); + return Compile_Abort; + } + + return Compile_Okay; +} - analyze::Script analysis; - PodZero(&analysis); +CompileStatus +mjit::Compiler::addInlineFrame(JSScript *script, uint32_t depth, + uint32_t parent, jsbytecode *parentpc) +{ + JS_ASSERT(inlining()); - analysis.analyze(cx, script); + CompileStatus status = checkAnalysis(script); + if (status != Compile_Okay) + return status; - if (analysis.OOM()) { - js_ReportOutOfMemory(cx); + if (!ssa.addInlineFrame(script, depth, parent, parentpc)) return Compile_Error; + + uint32_t index = ssa.iterFrame(ssa.numFrames() - 1).index; + return scanInlineCalls(index, depth); +} + +CompileStatus +mjit::Compiler::scanInlineCalls(uint32_t index, uint32_t depth) +{ + /* Maximum number of calls we will inline at the same site. */ + static const uint32_t INLINE_SITE_LIMIT = 5; + + JS_ASSERT(inlining() && globalObj); + + /* Not inlining yet from 'new' scripts. */ + if (isConstructing) + return Compile_Okay; + + JSScript *script = ssa.getFrame(index).script; + ScriptAnalysis *analysis = script->analysis(); + + /* Don't inline from functions which could have a non-global scope object. */ + if (!script->hasGlobal() || + script->global() != globalObj || + (script->function() && script->function()->getParent() != globalObj) || + (script->function() && script->function()->isHeavyweight()) || + script->isActiveEval) { + return Compile_Okay; } - if (analysis.failed()) { - JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n"); - return Compile_Abort; + + uint32_t nextOffset = 0; + uint32_t lastOffset = script->length; + + if (index == CrossScriptSSA::OUTER_FRAME) { + nextOffset = outerChunk.begin; + lastOffset = outerChunk.end; + } + + while (nextOffset < lastOffset) { + uint32_t offset = nextOffset; + jsbytecode *pc = script->code + offset; + nextOffset = offset + GetBytecodeLength(pc); + + Bytecode *code = analysis->maybeCode(pc); + if (!code) + continue; + + /* :XXX: Not yet inlining 'new' calls. */ + if (JSOp(*pc) != JSOP_CALL) + continue; + + /* Not inlining at monitored call sites or those with type barriers. */ + if (code->monitoredTypes || code->monitoredTypesReturn || analysis->typeBarriers(cx, pc) != NULL) + continue; + + uint32_t argc = GET_ARGC(pc); + types::TypeSet *calleeTypes = analysis->poppedTypes(pc, argc + 1); + + if (calleeTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT) + continue; + + if (calleeTypes->getObjectCount() >= INLINE_SITE_LIMIT) + continue; + + /* + * Compute the maximum height we can grow the stack for inlined frames. + * We always reserve space for loop temporaries, for an extra stack + * frame pushed when making a call from the deepest inlined frame, and + * for the temporary slot used by type barriers. + */ + uint32_t stackLimit = outerScript->nslots + StackSpace::STACK_JIT_EXTRA + - VALUES_PER_STACK_FRAME - FrameState::TEMPORARY_LIMIT - 1; + + /* Compute the depth of any frames inlined at this site. */ + uint32_t nextDepth = depth + VALUES_PER_STACK_FRAME + script->nfixed + code->stackDepth; + + /* + * Scan each of the possible callees for other conditions precluding + * inlining. We only inline at a call site if all callees are inlineable. + */ + unsigned count = calleeTypes->getObjectCount(); + bool okay = true; + for (unsigned i = 0; i < count; i++) { + if (calleeTypes->getTypeObject(i) != NULL) { + okay = false; + break; + } + + JSObject *obj = calleeTypes->getSingleObject(i); + if (!obj) + continue; + + if (!obj->isFunction()) { + okay = false; + break; + } + + JSFunction *fun = obj->toFunction(); + if (!fun->isInterpreted()) { + okay = false; + break; + } + JSScript *script = fun->script(); + + /* + * Don't inline calls to scripts which haven't been analyzed. + * We need to analyze the inlined scripts to compile them, and + * doing so can change type information we have queried already + * in making inlining decisions. + */ + if (!script->hasAnalysis() || !script->analysis()->ranInference()) { + okay = false; + break; + } + + /* + * The outer and inner scripts must have the same scope. This only + * allows us to inline calls between non-inner functions. Also + * check for consistent strictness between the functions. + */ + if (!globalObj || + fun->getParent() != globalObj || + outerScript->strictModeCode != script->strictModeCode) { + okay = false; + break; + } + + /* We can't cope with inlining recursive functions yet. */ + uint32_t nindex = index; + while (nindex != CrossScriptSSA::INVALID_FRAME) { + if (ssa.getFrame(nindex).script == script) + okay = false; + nindex = ssa.getFrame(nindex).parent; + } + if (!okay) + break; + + /* Watch for excessively deep nesting of inlined frames. */ + if (nextDepth + script->nslots >= stackLimit) { + okay = false; + break; + } + + if (!script->types || !script->types->hasScope()) { + okay = false; + break; + } + + CompileStatus status = checkAnalysis(script); + if (status != Compile_Okay) + return status; + + if (!script->analysis()->inlineable(argc)) { + okay = false; + break; + } + + if (types::TypeSet::HasObjectFlags(cx, fun->getType(cx), + types::OBJECT_FLAG_UNINLINEABLE)) { + okay = false; + break; + } + + /* + * Don't inline scripts which use 'this' if it is possible they + * could be called with a 'this' value requiring wrapping. During + * inlining we do not want to modify frame entries belonging to the + * caller. + */ + if (script->analysis()->usesThisValue() && + types::TypeScript::ThisTypes(script)->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT) { + okay = false; + break; + } + } + if (!okay) + continue; + + calleeTypes->addFreeze(cx); + + /* + * Add the inline frames to the cross script SSA. We will pick these + * back up when compiling the call site. + */ + for (unsigned i = 0; i < count; i++) { + JSObject *obj = calleeTypes->getSingleObject(i); + if (!obj) + continue; + + JSFunction *fun = obj->toFunction(); + JSScript *script = fun->script(); + + CompileStatus status = addInlineFrame(script, nextDepth, index, pc); + if (status != Compile_Okay) + return status; + } } - this->analysis = &analysis; + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::pushActiveFrame(JSScript *script, uint32_t argc) +{ + if (cx->runtime->profilingScripts && !script->pcCounters) + script->initCounts(cx); + + ActiveFrame *newa = OffTheBooks::new_(cx); + if (!newa) + return Compile_Error; + + newa->parent = a; + if (a) + newa->parentPC = PC; + newa->script = script; + newa->mainCodeStart = masm.size(); + newa->stubCodeStart = stubcc.size(); + + if (outer) { + newa->inlineIndex = uint32_t(inlineFrames.length()); + inlineFrames.append(newa); + } else { + newa->inlineIndex = CrossScriptSSA::OUTER_FRAME; + outer = newa; + } + JS_ASSERT(ssa.getFrame(newa->inlineIndex).script == script); - if (!frame.init()) { + newa->inlinePCOffset = ssa.frameLength(newa->inlineIndex); + + ScriptAnalysis *newAnalysis = script->analysis(); + +#ifdef JS_METHODJIT_SPEW + if (cx->typeInferenceEnabled() && IsJaegerSpewChannelActive(JSpew_Regalloc)) { + unsigned nargs = script->function() ? script->function()->nargs : 0; + for (unsigned i = 0; i < nargs; i++) { + uint32_t slot = ArgSlot(i); + if (!newAnalysis->slotEscapes(slot)) { + JaegerSpew(JSpew_Regalloc, "Argument %u:", i); + newAnalysis->liveness(slot).print(); + } + } + for (unsigned i = 0; i < script->nfixed; i++) { + uint32_t slot = LocalSlot(script, i); + if (!newAnalysis->slotEscapes(slot)) { + JaegerSpew(JSpew_Regalloc, "Local %u:", i); + newAnalysis->liveness(slot).print(); + } + } + } +#endif + + if (!frame.pushActiveFrame(script, argc)) { js_ReportOutOfMemory(cx); return Compile_Error; } - jumpMap = (Label *)cx->malloc_(sizeof(Label) * script->length); - if (!jumpMap) { + newa->jumpMap = (Label *)OffTheBooks::malloc_(sizeof(Label) * script->length); + if (!newa->jumpMap) { js_ReportOutOfMemory(cx); return Compile_Error; } #ifdef DEBUG - for (uint32 i = 0; i < script->length; i++) - jumpMap[i] = Label(); + for (uint32_t i = 0; i < script->length; i++) + newa->jumpMap[i] = Label(); #endif + if (cx->typeInferenceEnabled()) { + CompileStatus status = prepareInferenceTypes(script, newa); + if (status != Compile_Okay) + return status; + } + + this->script = script; + this->analysis = newAnalysis; + this->PC = script->code; + this->a = newa; + + return Compile_Okay; +} + +void +mjit::Compiler::popActiveFrame() +{ + JS_ASSERT(a->parent); + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); + this->PC = a->parentPC; + this->a = (ActiveFrame *) a->parent; + this->script = a->script; + this->analysis = this->script->analysis(); + + frame.popActiveFrame(); +} + +#define CHECK_STATUS(expr) \ + JS_BEGIN_MACRO \ + CompileStatus status_ = (expr); \ + if (status_ != Compile_Okay) { \ + if (oomInVector || masm.oom() || stubcc.masm.oom()) \ + js_ReportOutOfMemory(cx); \ + return status_; \ + } \ + JS_END_MACRO + +CompileStatus +mjit::Compiler::performCompilation() +{ + JaegerSpew(JSpew_Scripts, + "compiling script (file \"%s\") (line \"%d\") (length \"%d\") (chunk \"%d\")\n", + outerScript->filename, outerScript->lineno, outerScript->length, chunkIndex); + + if (inlining()) { + JaegerSpew(JSpew_Inlining, + "inlining calls in script (file \"%s\") (line \"%d\")\n", + outerScript->filename, outerScript->lineno); + } + #ifdef JS_METHODJIT_SPEW Profiler prof; prof.start(); #endif - /* Initialize PC early so stub calls in the prologue can be fallible. */ - PC = script->code; - #ifdef JS_METHODJIT - script->debugMode = debugMode(); + outerScript->debugMode = debugMode(); #endif - for (uint32 i = 0; i < script->nClosedVars; i++) - frame.setClosedVar(script->getClosedVar(i)); - for (uint32 i = 0; i < script->nClosedArgs; i++) - frame.setClosedArg(script->getClosedArg(i)); + JS_ASSERT(cx->compartment->activeInference); - CHECK_STATUS(generatePrologue()); - CHECK_STATUS(generateMethod()); - CHECK_STATUS(generateEpilogue()); - CHECK_STATUS(finishThisUp(jitp)); + { + types::AutoEnterCompilation enter(cx, outerScript, isConstructing, chunkIndex); + + CHECK_STATUS(checkAnalysis(outerScript)); + if (inlining()) + CHECK_STATUS(scanInlineCalls(CrossScriptSSA::OUTER_FRAME, 0)); + CHECK_STATUS(pushActiveFrame(outerScript, 0)); + if (chunkIndex == 0) + CHECK_STATUS(generatePrologue()); + CHECK_STATUS(generateMethod()); + if (outerJIT() && chunkIndex == outerJIT()->nchunks - 1) + CHECK_STATUS(generateEpilogue()); + CHECK_STATUS(finishThisUp()); + } #ifdef JS_METHODJIT_SPEW prof.stop(); JaegerSpew(JSpew_Prof, "compilation took %d us\n", prof.time_us()); #endif - JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%ld\")\n", - (*jitp)->code.m_code.executableAddress(), (*jitp)->code.m_size); + JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%u\")\n", + outerChunk.chunk->code.m_code.executableAddress(), + unsigned(outerChunk.chunk->code.m_size)); return Compile_Okay; } #undef CHECK_STATUS +mjit::JSActiveFrame::JSActiveFrame() + : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX) +{ +} + +mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx) + : jumpMap(NULL), + varTypes(NULL), needReturnValue(false), + syncReturnValue(false), returnValueDouble(false), returnSet(false), + returnEntry(NULL), returnJumps(NULL), exitState(NULL) +{} + +mjit::Compiler::ActiveFrame::~ActiveFrame() +{ + js::Foreground::free_(jumpMap); + if (varTypes) + js::Foreground::free_(varTypes); +} + mjit::Compiler::~Compiler() { - cx->free_(jumpMap); - cx->free_(savedTraps); + if (outer) + cx->delete_(outer); + for (unsigned i = 0; i < inlineFrames.length(); i++) + cx->delete_(inlineFrames[i]); + while (loop) { + LoopState *nloop = loop->outer; + cx->delete_(loop); + loop = nloop; + } } -CompileStatus JS_NEVER_INLINE -mjit::TryCompile(JSContext *cx, StackFrame *fp) +CompileStatus +mjit::Compiler::prepareInferenceTypes(JSScript *script, ActiveFrame *a) { - JS_ASSERT(cx->fp() == fp); + /* + * During our walk of the script, we need to preserve the invariant that at + * join points the in memory type tag is always in sync with the known type + * tag of the variable's SSA value at that join point. In particular, SSA + * values inferred as (int|double) must in fact be doubles, stored either + * in floating point registers or in memory. There is an exception for + * locals whose value is currently dead, whose type might not be synced. + * + * To ensure this, we need to know the SSA values for each variable at each + * join point, which the SSA analysis does not store explicitly. These can + * be recovered, though. During the forward walk, the SSA value of a var + * (and its associated type set) change only when we see an explicit assign + * to the var or get to a join point with a phi node for that var. So we + * can duplicate the effects of that walk here by watching for writes to + * vars (updateVarTypes) and new phi nodes at join points. + * + * When we get to a branch and need to know a variable's value at the + * branch target, we know it will either be a phi node at the target or + * the variable's current value, as no phi node is created at the target + * only if a variable has the same value on all incoming edges. + */ -#if JS_HAS_SHARP_VARS - if (fp->script()->hasSharps) - return Compile_Abort; + a->varTypes = (VarType *) + OffTheBooks::calloc_(TotalSlots(script) * sizeof(VarType)); + if (!a->varTypes) + return Compile_Error; + + for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) { + VarType &vt = a->varTypes[slot]; + vt.setTypes(types::TypeScript::SlotTypes(script, slot)); + } + + return Compile_Okay; +} + +/* + * Number of times a script must be called or have back edges taken before we + * run it in the methodjit. We wait longer if type inference is enabled, to + * allow more gathering of type information and less recompilation. + */ +static const size_t USES_BEFORE_COMPILE = 16; +static const size_t INFER_USES_BEFORE_COMPILE = 40; + +/* Target maximum size, in bytecode length, for a compiled chunk of a script. */ +static uint32_t CHUNK_LIMIT = 1500; + +void +mjit::SetChunkLimit(uint32_t limit) +{ + if (limit) + CHUNK_LIMIT = limit; +} + +JITScript * +MakeJITScript(JSContext *cx, JSScript *script, bool construct) +{ + if (!script->ensureRanAnalysis(cx, NULL)) + return NULL; + + ScriptAnalysis *analysis = script->analysis(); + + JITScript *&location = construct ? script->jitCtor : script->jitNormal; + + Vector chunks(cx); + Vector edges(cx); + + /* + * Chunk compilation is not supported on x64, since there is no guarantee + * that cross chunk jumps will be patchable even to go to the default shim. + */ +#ifndef JS_CPU_X64 + if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) { #endif + ChunkDescriptor desc; + desc.begin = 0; + desc.end = script->length; + if (!chunks.append(desc)) + return NULL; +#ifndef JS_CPU_X64 + } else { + if (!script->ensureRanInference(cx)) + return NULL; - // Ensure that constructors have at least one slot. - if (fp->isConstructing() && !fp->script()->nslots) - fp->script()->nslots++; + /* Outgoing edges within the current chunk. */ + Vector currentEdges(cx); + uint32_t chunkStart = 0; + + unsigned offset, nextOffset = 0; + while (nextOffset < script->length) { + offset = nextOffset; + + jsbytecode *pc = script->code + offset; + JSOp op = JSOp(*pc); + + nextOffset = offset + GetBytecodeLength(pc); + + Bytecode *code = analysis->maybeCode(offset); + if (!code) + continue; + + /* Whether this should be the last opcode in the chunk. */ + bool finishChunk = false; + + /* Keep going, override finishChunk. */ + bool preserveChunk = false; - Compiler cc(cx, fp); + /* + * Add an edge for opcodes which perform a branch. Skip LABEL ops, + * which do not actually branch. XXX LABEL should not be JOF_JUMP. + */ + uint32_t type = JOF_TYPE(js_CodeSpec[op].format); + if (type == JOF_JUMP && op != JSOP_LABEL) { + CrossChunkEdge edge; + edge.source = offset; + edge.target = FollowBranch(cx, script, pc - script->code); + if (edge.target < offset) { + /* Always end chunks after loop back edges. */ + finishChunk = true; + if (edge.target < chunkStart) { + analysis->getCode(edge.target).safePoint = true; + if (!edges.append(edge)) + return NULL; + } + } else if (edge.target == nextOffset) { + /* + * Override finishChunk for bytecodes which directly + * jump to their fallthrough opcode ('if (x) {}'). This + * creates two CFG edges with the same source/target, which + * will confuse the compiler's edge patching code. + */ + preserveChunk = true; + } else { + if (!currentEdges.append(edge)) + return NULL; + } + } + + if (op == JSOP_TABLESWITCH) { + jsbytecode *pc2 = pc; + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + pc2 += JUMP_OFFSET_LEN; + jsint low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + jsint high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + + CrossChunkEdge edge; + edge.source = offset; + edge.target = defaultOffset; + if (!currentEdges.append(edge)) + return NULL; + + for (jsint i = low; i <= high; i++) { + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + if (targetOffset != offset) { + /* + * This can end up inserting duplicate edges, all but + * the first of which will be ignored. + */ + CrossChunkEdge edge; + edge.source = offset; + edge.target = targetOffset; + if (!currentEdges.append(edge)) + return NULL; + } + pc2 += JUMP_OFFSET_LEN; + } + } + + if (op == JSOP_LOOKUPSWITCH) { + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; + unsigned npairs = GET_UINT16(pc2); + pc2 += UINT16_LEN; + + CrossChunkEdge edge; + edge.source = offset; + edge.target = defaultOffset; + if (!currentEdges.append(edge)) + return NULL; + + while (npairs) { + pc2 += INDEX_LEN; + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + CrossChunkEdge edge; + edge.source = offset; + edge.target = targetOffset; + if (!currentEdges.append(edge)) + return NULL; + pc2 += JUMP_OFFSET_LEN; + npairs--; + } + } + + if (unsigned(offset - chunkStart) > CHUNK_LIMIT) + finishChunk = true; + + if (nextOffset >= script->length || !analysis->maybeCode(nextOffset)) { + /* Ensure that chunks do not start on unreachable opcodes. */ + preserveChunk = true; + } else { + /* + * Start new chunks at the opcode before each loop head. + * This ensures that the initial goto for loops is included in + * the same chunk as the loop itself. + */ + jsbytecode *nextpc = script->code + nextOffset; + + /* + * Don't insert a chunk boundary in the middle of two opcodes + * which may be fused together. + */ + switch (JSOp(*nextpc)) { + case JSOP_POP: + case JSOP_IFNE: + case JSOP_IFEQ: + preserveChunk = true; + break; + default: + break; + } + + uint32_t afterOffset = nextOffset + GetBytecodeLength(nextpc); + if (afterOffset < script->length) { + if (analysis->maybeCode(afterOffset) && + JSOp(script->code[afterOffset]) == JSOP_LOOPHEAD && + analysis->getLoop(afterOffset)) + { + finishChunk = true; + } + } + } + + if (finishChunk && !preserveChunk) { + ChunkDescriptor desc; + desc.begin = chunkStart; + desc.end = nextOffset; + if (!chunks.append(desc)) + return NULL; + + /* Add an edge for fallthrough from this chunk to the next one. */ + if (!BytecodeNoFallThrough(op)) { + CrossChunkEdge edge; + edge.source = offset; + edge.target = nextOffset; + analysis->getCode(edge.target).safePoint = true; + if (!edges.append(edge)) + return NULL; + } - return cc.compile(); + chunkStart = nextOffset; + for (unsigned i = 0; i < currentEdges.length(); i++) { + const CrossChunkEdge &edge = currentEdges[i]; + if (edge.target >= nextOffset) { + analysis->getCode(edge.target).safePoint = true; + if (!edges.append(edge)) + return NULL; + } + } + currentEdges.clear(); + } + } + + if (chunkStart != script->length) { + ChunkDescriptor desc; + desc.begin = chunkStart; + desc.end = script->length; + if (!chunks.append(desc)) + return NULL; + } + } +#endif /* !JS_CPU_X64 */ + + size_t dataSize = sizeof(JITScript) + + (chunks.length() * sizeof(ChunkDescriptor)) + + (edges.length() * sizeof(CrossChunkEdge)); + uint8_t *cursor = (uint8_t *) OffTheBooks::calloc_(dataSize); + if (!cursor) + return NULL; + + JITScript *jit = (JITScript *) cursor; + cursor += sizeof(JITScript); + + jit->script = script; + JS_INIT_CLIST(&jit->callers); + + jit->nchunks = chunks.length(); + for (unsigned i = 0; i < chunks.length(); i++) { + const ChunkDescriptor &a = chunks[i]; + ChunkDescriptor &b = jit->chunkDescriptor(i); + b.begin = a.begin; + b.end = a.end; + + if (chunks.length() == 1) { + /* Seed the chunk's count so it is immediately compiled. */ + b.counter = INFER_USES_BEFORE_COMPILE; + } + } + + if (edges.empty()) { + location = jit; + return jit; + } + + jit->nedges = edges.length(); + CrossChunkEdge *jitEdges = jit->edges(); + for (unsigned i = 0; i < edges.length(); i++) { + const CrossChunkEdge &a = edges[i]; + CrossChunkEdge &b = jitEdges[i]; + b.source = a.source; + b.target = a.target; + } + + /* Generate a pool with all cross chunk shims, and set shimLabel for each edge. */ + Assembler masm; + for (unsigned i = 0; i < jit->nedges; i++) { + jsbytecode *pc = script->code + jitEdges[i].target; + jitEdges[i].shimLabel = (void *) masm.distanceOf(masm.label()); + masm.move(JSC::MacroAssembler::ImmPtr(&jitEdges[i]), Registers::ArgReg1); + masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::CrossChunkShim), + pc, NULL, script->nfixed + analysis->getCode(pc).stackDepth); + } + LinkerHelper linker(masm, JSC::METHOD_CODE); + JSC::ExecutablePool *ep = linker.init(cx); + if (!ep) + return NULL; + jit->shimPool = ep; + + masm.finalize(linker); + uint8_t *shimCode = (uint8_t *) linker.finalizeCodeAddendum().executableAddress(); + + JS_ALWAYS_TRUE(linker.verifyRange(JSC::JITCode(shimCode, masm.size()))); + + JaegerSpew(JSpew_PICs, "generated SHIM POOL stub %p (%lu bytes)\n", + shimCode, (unsigned long)masm.size()); + + for (unsigned i = 0; i < jit->nedges; i++) { + CrossChunkEdge &edge = jitEdges[i]; + edge.shimLabel = shimCode + (size_t) edge.shimLabel; + } + + location = jit; + return jit; } -bool -mjit::Compiler::loadOldTraps(const Vector &sites) +CompileStatus +mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc, + bool construct, CompileRequest request) { - savedTraps = (bool *)cx->calloc_(sizeof(bool) * script->length); - if (!savedTraps) - return false; - - for (size_t i = 0; i < sites.length(); i++) { - const CallSite &site = sites[i]; - if (site.isTrap()) - savedTraps[site.pcOffset] = true; + restart: + if (!cx->methodJitEnabled) + return Compile_Abort; + + void *addr = construct ? script->jitArityCheckCtor : script->jitArityCheckNormal; + if (addr == JS_UNJITTABLE_SCRIPT) + return Compile_Abort; + + JITScript *jit = script->getJIT(construct); + + if (request == CompileRequest_Interpreter && + !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && + (cx->typeInferenceEnabled() + ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE + : script->incUseCount() <= USES_BEFORE_COMPILE)) + { + return Compile_Skipped; } - return true; + if (!cx->compartment->ensureJaegerCompartmentExists(cx)) + return Compile_Error; + + // Ensure that constructors have at least one slot. + if (construct && !script->nslots) + script->nslots++; + + if (!jit) { + jit = MakeJITScript(cx, script, construct); + if (!jit) + return Compile_Error; + } + unsigned chunkIndex = jit->chunkIndex(pc); + ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex); + + if (desc.chunk) + return Compile_Okay; + + if (request == CompileRequest_Interpreter && + !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && + ++desc.counter <= INFER_USES_BEFORE_COMPILE) + { + return Compile_Skipped; + } + + CompileStatus status; + { + types::AutoEnterTypeInference enter(cx, true); + + Compiler cc(cx, script, chunkIndex, construct); + status = cc.compile(); + } + + if (status == Compile_Okay) { + /* + * Compiling a script can occasionally trigger its own recompilation, + * so go back through the compilation logic. + */ + goto restart; + } + + /* Non-OOM errors should have an associated exception. */ + JS_ASSERT_IF(status == Compile_Error, + cx->isExceptionPending() || cx->runtime->hadOutOfMemory); + + return status; } CompileStatus @@ -282,7 +1020,7 @@ mjit::Compiler::generatePrologue() * If there is no function, then this can only be called via JaegerShot(), * which expects an existing frame to be initialized like the interpreter. */ - if (fun) { + if (script->function()) { Jump j = masm.jump(); /* @@ -294,7 +1032,8 @@ mjit::Compiler::generatePrologue() Label fastPath = masm.label(); /* Store this early on so slow paths can access it. */ - masm.storePtr(ImmPtr(fun), Address(JSFrameReg, StackFrame::offsetOfExec())); + masm.storePtr(ImmPtr(script->function()), + Address(JSFrameReg, StackFrame::offsetOfExec())); { /* @@ -304,84 +1043,208 @@ mjit::Compiler::generatePrologue() * This loops back to entry point #2. */ arityLabel = stubcc.masm.label(); + Jump argMatch = stubcc.masm.branch32(Assembler::Equal, JSParamReg_Argc, - Imm32(fun->nargs)); - stubcc.crossJump(argMatch, fastPath); + Imm32(script->function()->nargs)); if (JSParamReg_Argc != Registers::ArgReg1) stubcc.masm.move(JSParamReg_Argc, Registers::ArgReg1); /* Slow path - call the arity check function. Returns new fp. */ - stubcc.masm.storePtr(ImmPtr(fun), Address(JSFrameReg, StackFrame::offsetOfExec())); - stubcc.masm.storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp)); - OOL_STUBCALL(stubs::FixupArity); + stubcc.masm.storePtr(ImmPtr(script->function()), + Address(JSFrameReg, StackFrame::offsetOfExec())); + OOL_STUBCALL(stubs::FixupArity, REJOIN_NONE); stubcc.masm.move(Registers::ReturnReg, JSFrameReg); + argMatch.linkTo(stubcc.masm.label(), &stubcc.masm); + + argsCheckLabel = stubcc.masm.label(); + + /* Type check the arguments as well. */ + if (cx->typeInferenceEnabled()) { +#ifdef JS_MONOIC + this->argsCheckJump = stubcc.masm.jump(); + this->argsCheckStub = stubcc.masm.label(); + this->argsCheckJump.linkTo(this->argsCheckStub, &stubcc.masm); +#endif + stubcc.masm.storePtr(ImmPtr(script->function()), + Address(JSFrameReg, StackFrame::offsetOfExec())); + OOL_STUBCALL(stubs::CheckArgumentTypes, REJOIN_CHECK_ARGUMENTS); +#ifdef JS_MONOIC + this->argsCheckFallthrough = stubcc.masm.label(); +#endif + } + stubcc.crossJump(stubcc.masm.jump(), fastPath); } /* - * Guard that there is enough stack space. Note we include the size of - * a second frame, to ensure we can create a frame from call sites. + * Guard that there is enough stack space. Note we reserve space for + * any inline frames we end up generating, or a callee's stack frame + * we write to before the callee checks the stack. */ - masm.addPtr(Imm32((script->nslots + VALUES_PER_STACK_FRAME * 2) * sizeof(Value)), - JSFrameReg, - Registers::ReturnReg); + uint32_t nvals = VALUES_PER_STACK_FRAME + script->nslots + StackSpace::STACK_JIT_EXTRA; + masm.addPtr(Imm32(nvals * sizeof(Value)), JSFrameReg, Registers::ReturnReg); Jump stackCheck = masm.branchPtr(Assembler::AboveOrEqual, Registers::ReturnReg, FrameAddress(offsetof(VMFrame, stackLimit))); - /* If the stack check fails... */ + /* + * If the stack check fails then we need to either commit more of the + * reserved stack space or throw an error. Specify that the number of + * local slots is 0 (instead of the default script->nfixed) since the + * range [fp->slots(), fp->base()) may not be commited. (The calling + * contract requires only that the caller has reserved space for fp.) + */ { stubcc.linkExitDirect(stackCheck, stubcc.masm.label()); - OOL_STUBCALL(stubs::HitStackQuota); + OOL_STUBCALL(stubs::HitStackQuota, REJOIN_NONE); stubcc.crossJump(stubcc.masm.jump(), masm.label()); } + markUndefinedLocals(); + + types::TypeScriptNesting *nesting = script->nesting(); + /* - * Set locals to undefined, as in initCallFrameLatePrologue. - * Skip locals which aren't closed and are known to be defined before used, - * :FIXME: bug 604541: write undefined if we might be using the tracer, so it works. + * Run the function prologue if necessary. This is always done in a + * stub for heavyweight functions (including nesting outer functions). */ - for (uint32 i = 0; i < script->nfixed; i++) { - if (analysis->localHasUseBeforeDef(i) || addTraceHints) { - Address local(JSFrameReg, sizeof(StackFrame) + i * sizeof(Value)); - masm.storeValue(UndefinedValue(), local); + JS_ASSERT_IF(nesting && nesting->children, script->function()->isHeavyweight()); + if (script->function()->isHeavyweight()) { + prepareStubCall(Uses(0)); + INLINE_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE); + } else { + /* + * Load the scope chain into the frame if it will be needed by NAME + * opcodes or by the nesting prologue below. The scope chain is + * always set for global and eval frames, and will have been set by + * CreateFunCallObject for heavyweight function frames. + */ + if (analysis->usesScopeChain() || nesting) { + RegisterID t0 = Registers::ReturnReg; + Jump hasScope = masm.branchTest32(Assembler::NonZero, + FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN)); + masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0); + masm.loadPtr(Address(t0, JSFunction::offsetOfEnvironment()), t0); + masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain())); + hasScope.linkTo(masm.label(), &masm); + } + + if (nesting) { + /* + * Inline the common case for the nesting prologue: the + * function is a non-heavyweight inner function with no + * children of its own. We ensure during inference that the + * outer function does not add scope objects for 'let' or + * 'with', so that the frame's scope chain will be + * the parent's call object, and if it differs from the + * parent's current activation then the parent is reentrant. + */ + JSScript *parent = nesting->parent; + JS_ASSERT(parent); + JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(), + !parent->analysis()->addsScopeObjects()); + + RegisterID t0 = Registers::ReturnReg; + masm.move(ImmPtr(&parent->nesting()->activeCall), t0); + masm.loadPtr(Address(t0), t0); + + Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain()); + Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain); + masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames)); + + stubcc.linkExitDirect(mismatch, stubcc.masm.label()); + OOL_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); } } - /* Create the call object. */ - if (fun->isHeavyweight()) { - prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::CreateFunCallObject); + if (outerScript->usesArguments && !script->function()->isHeavyweight()) { + /* + * Make sure that fp->u.nactual is always coherent. This may be + * inspected directly by JIT code, and is not guaranteed to be + * correct if the UNDERFLOW and OVERFLOW flags are not set. + */ + Jump hasArgs = masm.branchTest32(Assembler::NonZero, FrameFlagsAddress(), + Imm32(StackFrame::OVERRIDE_ARGS | + StackFrame::UNDERFLOW_ARGS | + StackFrame::OVERFLOW_ARGS | + StackFrame::HAS_ARGS_OBJ)); + masm.storePtr(ImmPtr((void *)(size_t) script->function()->nargs), + Address(JSFrameReg, StackFrame::offsetOfNumActual())); + hasArgs.linkTo(masm.label(), &masm); } j.linkTo(masm.label(), &masm); + } - if (analysis->usesScopeChain() && !fun->isHeavyweight()) { - /* - * Load the scope chain into the frame if necessary. The scope chain - * is always set for global and eval frames, and will have been set by - * CreateFunCallObject for heavyweight function frames. - */ - RegisterID t0 = Registers::ReturnReg; - Jump hasScope = masm.branchTest32(Assembler::NonZero, - FrameFlagsAddress(), - Imm32(StackFrame::HAS_SCOPECHAIN)); - masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(fun)), t0); - masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0); - masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain())); - hasScope.linkTo(masm.label(), &masm); + if (cx->typeInferenceEnabled()) { +#ifdef DEBUG + if (script->function()) { + prepareStubCall(Uses(0)); + INLINE_STUBCALL(stubs::AssertArgumentTypes, REJOIN_NONE); } +#endif + ensureDoubleArguments(); } - if (isConstructing) - constructThis(); + if (isConstructing) { + if (!constructThis()) + return Compile_Error; + } + + if (debugMode()) { + prepareStubCall(Uses(0)); + INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME); + } else if (Probes::callTrackingActive(cx)) { + prepareStubCall(Uses(0)); + INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME); + } + + recompileCheckHelper(); - if (debugMode() || Probes::callTrackingActive(cx)) - INLINE_STUBCALL(stubs::ScriptDebugPrologue); + if (outerScript->pcCounters || Probes::wantNativeAddressInfo(cx)) { + size_t length = ssa.frameLength(ssa.numFrames() - 1); + pcLengths = (PCLengthEntry *) OffTheBooks::calloc_(sizeof(pcLengths[0]) * length); + if (!pcLengths) + return Compile_Error; + } return Compile_Okay; } +void +mjit::Compiler::ensureDoubleArguments() +{ + /* Convert integer arguments which were inferred as (int|double) to doubles. */ + for (uint32_t i = 0; script->function() && i < script->function()->nargs; i++) { + uint32_t slot = ArgSlot(i); + if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot)) + frame.ensureDouble(frame.getArg(i)); + } +} + +void +mjit::Compiler::markUndefinedLocals() +{ + uint32_t depth = ssa.getFrame(a->inlineIndex).depth; + + /* + * Set locals to undefined, as in initCallFrameLatePrologue. + * Skip locals which aren't closed and are known to be defined before used, + */ + for (uint32_t i = 0; i < script->nfixed; i++) { + uint32_t slot = LocalSlot(script, i); + Address local(JSFrameReg, sizeof(StackFrame) + (depth + i) * sizeof(Value)); + if (!cx->typeInferenceEnabled() || !analysis->trackSlot(slot)) { + masm.storeValue(UndefinedValue(), local); + } else { + Lifetime *lifetime = analysis->liveness(slot).live(0); + if (lifetime) + masm.storeValue(UndefinedValue(), local); + } + } +} + CompileStatus mjit::Compiler::generateEpilogue() { @@ -389,12 +1252,38 @@ mjit::Compiler::generateEpilogue() } CompileStatus -mjit::Compiler::finishThisUp(JITScript **jitp) +mjit::Compiler::finishThisUp() { RETURN_IF_OOM(Compile_Error); + /* + * Watch for reallocation of the global slots while we were in the middle + * of compiling due to, e.g. standard class initialization. + */ + if (globalSlots && globalObj->getRawSlots() != globalSlots) + return Compile_Retry; + + /* + * Watch for GCs which occurred during compilation. These may have + * renumbered shapes baked into the jitcode. + */ + if (cx->runtime->gcNumber != gcNumber) + return Compile_Retry; + + /* The JIT will not have been cleared if no GC has occurred. */ + JITScript *jit = outerJIT(); + JS_ASSERT(jit != NULL); + + if (overflowICSpace) { + JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n"); + return Compile_Abort; + } + + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); + for (size_t i = 0; i < branchPatches.length(); i++) { - Label label = labelOf(branchPatches[i].pc); + Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex); branchPatches[i].jump.linkTo(label, &masm); } @@ -402,16 +1291,28 @@ mjit::Compiler::finishThisUp(JITScript **jitp) masm.forceFlushConstantPool(); stubcc.masm.forceFlushConstantPool(); #endif - JaegerSpew(JSpew_Insns, "## Fast code (masm) size = %u, Slow code (stubcc) size = %u.\n", masm.size(), stubcc.size()); + JaegerSpew(JSpew_Insns, "## Fast code (masm) size = %lu, Slow code (stubcc) size = %lu.\n", + (unsigned long) masm.size(), (unsigned long) stubcc.size()); + /* To make inlineDoubles and oolDoubles aligned to sizeof(double) bytes, + MIPS adds extra sizeof(double) bytes to codeSize. */ size_t codeSize = masm.size() + +#if defined(JS_CPU_MIPS) + stubcc.size() + sizeof(double) + +#else stubcc.size() + - doubleList.length() * sizeof(double) + - jumpTableOffsets.length() * sizeof(void *); +#endif + (masm.numDoubles() * sizeof(double)) + + (stubcc.masm.numDoubles() * sizeof(double)) + + jumpTableEdges.length() * sizeof(void *); + + Vector chunkJumps(cx); + if (!chunkJumps.reserve(jumpTableEdges.length())) + return Compile_Error; JSC::ExecutablePool *execPool; - uint8 *result = - (uint8 *)script->compartment->jaegerCompartment->execAlloc()->alloc(codeSize, &execPool); + uint8_t *result = (uint8_t *)script->compartment()->jaegerCompartment()->execAlloc()-> + alloc(codeSize, &execPool, JSC::METHOD_CODE); if (!result) { js_ReportOutOfMemory(cx); return Compile_Error; @@ -420,81 +1321,169 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JSC::ExecutableAllocator::makeWritable(result, codeSize); masm.executableCopy(result); stubcc.masm.executableCopy(result + masm.size()); - - JSC::LinkBuffer fullCode(result, codeSize); - JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size()); - size_t nNmapLive = 0; - for (size_t i = 0; i < script->length; i++) { - analyze::Bytecode *opinfo = analysis->maybeCode(i); + JSC::LinkBuffer fullCode(result, codeSize, JSC::METHOD_CODE); + JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size(), JSC::METHOD_CODE); + + JS_ASSERT(!loop); + + size_t nNmapLive = loopEntries.length(); + for (size_t i = outerChunk.begin; i < outerChunk.end; i++) { + Bytecode *opinfo = analysis->maybeCode(i); if (opinfo && opinfo->safePoint) nNmapLive++; } - /* Please keep in sync with JITScript::scriptDataSize! */ - size_t dataSize = sizeof(JITScript) + + /* Please keep in sync with JITChunk::sizeOfIncludingThis! */ + size_t dataSize = sizeof(JITChunk) + sizeof(NativeMapEntry) * nNmapLive + + sizeof(InlineFrame) * inlineFrames.length() + + sizeof(CallSite) * callSites.length() + #if defined JS_MONOIC sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() + sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() + sizeof(ic::CallICInfo) * callICs.length() + sizeof(ic::EqualityICInfo) * equalityICs.length() + - sizeof(ic::TraceICInfo) * traceICs.length() + #endif #if defined JS_POLYIC - sizeof(ic::PICInfo) * pics.length() + - sizeof(ic::GetElementIC) * getElemICs.length() + - sizeof(ic::SetElementIC) * setElemICs.length() + + sizeof(ic::PICInfo) * pics.length() + + sizeof(ic::GetElementIC) * getElemICs.length() + + sizeof(ic::SetElementIC) * setElemICs.length() + #endif - sizeof(CallSite) * callSites.length(); + 0; - uint8 *cursor = (uint8 *)cx->calloc_(dataSize); + uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(dataSize); if (!cursor) { execPool->release(); js_ReportOutOfMemory(cx); return Compile_Error; } - JITScript *jit = new(cursor) JITScript; - cursor += sizeof(JITScript); + JITChunk *chunk = new(cursor) JITChunk; + cursor += sizeof(JITChunk); + + JS_ASSERT(outerScript == script); + + chunk->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); + chunk->pcLengths = pcLengths; - jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); - jit->invokeEntry = result; - jit->singleStepMode = script->singleStepMode; - if (fun) { - jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); - jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); + if (chunkIndex == 0) { + jit->invokeEntry = result; + if (script->function()) { + jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); + jit->argsCheckEntry = stubCode.locationOf(argsCheckLabel).executableAddress(); + jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); + void *&addr = isConstructing ? script->jitArityCheckCtor : script->jitArityCheckNormal; + addr = jit->arityCheckEntry; + } } - /* + /* * WARNING: mics(), callICs() et al depend on the ordering of these - * variable-length sections. See JITScript's declaration for details. + * variable-length sections. See JITChunk's declaration for details. */ + /* ICs can only refer to bytecodes in the outermost script, not inlined calls. */ + Label *jumpMap = a->jumpMap; + /* Build the pc -> ncode mapping. */ NativeMapEntry *jitNmap = (NativeMapEntry *)cursor; - jit->nNmapPairs = nNmapLive; - cursor += sizeof(NativeMapEntry) * jit->nNmapPairs; + chunk->nNmapPairs = nNmapLive; + cursor += sizeof(NativeMapEntry) * chunk->nNmapPairs; size_t ix = 0; - if (jit->nNmapPairs > 0) { - for (size_t i = 0; i < script->length; i++) { - analyze::Bytecode *opinfo = analysis->maybeCode(i); + if (chunk->nNmapPairs > 0) { + for (size_t i = outerChunk.begin; i < outerChunk.end; i++) { + Bytecode *opinfo = analysis->maybeCode(i); if (opinfo && opinfo->safePoint) { Label L = jumpMap[i]; - JS_ASSERT(L.isValid()); + JS_ASSERT(L.isSet()); jitNmap[ix].bcOff = i; - jitNmap[ix].ncode = (uint8 *)(result + masm.distanceOf(L)); + jitNmap[ix].ncode = (uint8_t *)(result + masm.distanceOf(L)); ix++; } } + for (size_t i = 0; i < loopEntries.length(); i++) { + /* Insert the entry at the right position. */ + const LoopEntry &entry = loopEntries[i]; + size_t j; + for (j = 0; j < ix; j++) { + if (jitNmap[j].bcOff > entry.pcOffset) { + memmove(jitNmap + j + 1, jitNmap + j, (ix - j) * sizeof(NativeMapEntry)); + break; + } + } + jitNmap[j].bcOff = entry.pcOffset; + jitNmap[j].ncode = (uint8_t *) stubCode.locationOf(entry.label).executableAddress(); + ix++; + } + } + JS_ASSERT(ix == chunk->nNmapPairs); + + /* Build the table of inlined frames. */ + InlineFrame *jitInlineFrames = (InlineFrame *)cursor; + chunk->nInlineFrames = inlineFrames.length(); + cursor += sizeof(InlineFrame) * chunk->nInlineFrames; + for (size_t i = 0; i < chunk->nInlineFrames; i++) { + InlineFrame &to = jitInlineFrames[i]; + ActiveFrame *from = inlineFrames[i]; + if (from->parent != outer) + to.parent = &jitInlineFrames[from->parent->inlineIndex]; + else + to.parent = NULL; + to.parentpc = from->parentPC; + to.fun = from->script->function(); + to.depth = ssa.getFrame(from->inlineIndex).depth; + } + + /* Build the table of call sites. */ + CallSite *jitCallSites = (CallSite *)cursor; + chunk->nCallSites = callSites.length(); + cursor += sizeof(CallSite) * chunk->nCallSites; + for (size_t i = 0; i < chunk->nCallSites; i++) { + CallSite &to = jitCallSites[i]; + InternalCallSite &from = callSites[i]; + + /* Patch stores of f.regs.inlined for stubs called from within inline frames. */ + if (cx->typeInferenceEnabled() && + from.rejoin != REJOIN_TRAP && + from.rejoin != REJOIN_SCRIPTED && + from.inlineIndex != UINT32_MAX) { + if (from.ool) + stubCode.patch(from.inlinePatch, &to); + else + fullCode.patch(from.inlinePatch, &to); + } + + JSScript *script = + (from.inlineIndex == UINT32_MAX) ? outerScript : inlineFrames[from.inlineIndex]->script; + uint32_t codeOffset = from.ool + ? masm.size() + from.returnOffset + : from.returnOffset; + to.initialize(codeOffset, from.inlineIndex, from.inlinepc - script->code, from.rejoin); + + /* + * Patch stores of the base call's return address for InvariantFailure + * calls. InvariantFailure will patch its own return address to this + * pointer before triggering recompilation. + */ + if (from.loopPatch.hasPatch) + stubCode.patch(from.loopPatch.codePatch, result + codeOffset); } - JS_ASSERT(ix == jit->nNmapPairs); #if defined JS_MONOIC + if (chunkIndex == 0 && script->function()) { + JS_ASSERT(jit->argsCheckPool == NULL); + if (cx->typeInferenceEnabled()) { + jit->argsCheckStub = stubCode.locationOf(argsCheckStub); + jit->argsCheckFallthrough = stubCode.locationOf(argsCheckFallthrough); + jit->argsCheckJump = stubCode.locationOf(argsCheckJump); + } + } + ic::GetGlobalNameIC *getGlobalNames_ = (ic::GetGlobalNameIC *)cursor; - jit->nGetGlobalNames = getGlobalNames.length(); - cursor += sizeof(ic::GetGlobalNameIC) * jit->nGetGlobalNames; - for (size_t i = 0; i < jit->nGetGlobalNames; i++) { + chunk->nGetGlobalNames = getGlobalNames.length(); + cursor += sizeof(ic::GetGlobalNameIC) * chunk->nGetGlobalNames; + for (size_t i = 0; i < chunk->nGetGlobalNames; i++) { ic::GetGlobalNameIC &to = getGlobalNames_[i]; GetGlobalNameICInfo &from = getGlobalNames[i]; from.copyTo(to, fullCode, stubCode); @@ -507,9 +1496,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } ic::SetGlobalNameIC *setGlobalNames_ = (ic::SetGlobalNameIC *)cursor; - jit->nSetGlobalNames = setGlobalNames.length(); - cursor += sizeof(ic::SetGlobalNameIC) * jit->nSetGlobalNames; - for (size_t i = 0; i < jit->nSetGlobalNames; i++) { + chunk->nSetGlobalNames = setGlobalNames.length(); + cursor += sizeof(ic::SetGlobalNameIC) * chunk->nSetGlobalNames; + for (size_t i = 0; i < chunk->nSetGlobalNames; i++) { ic::SetGlobalNameIC &to = setGlobalNames_[i]; SetGlobalNameICInfo &from = setGlobalNames[i]; from.copyTo(to, fullCode, stubCode); @@ -540,16 +1529,17 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } ic::CallICInfo *jitCallICs = (ic::CallICInfo *)cursor; - jit->nCallICs = callICs.length(); - cursor += sizeof(ic::CallICInfo) * jit->nCallICs; - for (size_t i = 0; i < jit->nCallICs; i++) { + chunk->nCallICs = callICs.length(); + cursor += sizeof(ic::CallICInfo) * chunk->nCallICs; + for (size_t i = 0; i < chunk->nCallICs; i++) { jitCallICs[i].reset(); jitCallICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard); jitCallICs[i].funJump = fullCode.locationOf(callICs[i].funJump); jitCallICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); + jitCallICs[i].typeMonitored = callICs[i].typeMonitored; /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotJump) - + uint32_t offset = fullCode.locationOf(callICs[i].hotJump) - fullCode.locationOf(callICs[i].funGuard); jitCallICs[i].hotJumpOffset = offset; JS_ASSERT(jitCallICs[i].hotJumpOffset == offset); @@ -559,7 +1549,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) fullCode.locationOf(callICs[i].funGuard); jitCallICs[i].joinPointOffset = offset; JS_ASSERT(jitCallICs[i].joinPointOffset == offset); - + /* Compute the OOL call offset. */ offset = stubCode.locationOf(callICs[i].oolCall) - stubCode.locationOf(callICs[i].slowPathStart); @@ -590,21 +1580,24 @@ mjit::Compiler::finishThisUp(JITScript **jitp) jitCallICs[i].hotPathOffset = offset; JS_ASSERT(jitCallICs[i].hotPathOffset == offset); - jitCallICs[i].pc = callICs[i].pc; + jitCallICs[i].call = &jitCallSites[callICs[i].callIndex]; jitCallICs[i].frameSize = callICs[i].frameSize; jitCallICs[i].funObjReg = callICs[i].funObjReg; - jitCallICs[i].funPtrReg = callICs[i].funPtrReg; stubCode.patch(callICs[i].addrLabel1, &jitCallICs[i]); stubCode.patch(callICs[i].addrLabel2, &jitCallICs[i]); } ic::EqualityICInfo *jitEqualityICs = (ic::EqualityICInfo *)cursor; - jit->nEqualityICs = equalityICs.length(); - cursor += sizeof(ic::EqualityICInfo) * jit->nEqualityICs; - for (size_t i = 0; i < jit->nEqualityICs; i++) { - uint32 offs = uint32(equalityICs[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - jitEqualityICs[i].target = fullCode.locationOf(jumpMap[offs]); + chunk->nEqualityICs = equalityICs.length(); + cursor += sizeof(ic::EqualityICInfo) * chunk->nEqualityICs; + for (size_t i = 0; i < chunk->nEqualityICs; i++) { + if (equalityICs[i].trampoline) { + jitEqualityICs[i].target = stubCode.locationOf(equalityICs[i].trampolineStart); + } else { + uint32_t offs = uint32_t(equalityICs[i].jumpTarget - script->code); + JS_ASSERT(jumpMap[offs].isSet()); + jitEqualityICs[i].target = fullCode.locationOf(jumpMap[offs]); + } jitEqualityICs[i].stubEntry = stubCode.locationOf(equalityICs[i].stubEntry); jitEqualityICs[i].stubCall = stubCode.locationOf(equalityICs[i].stubCall); jitEqualityICs[i].stub = equalityICs[i].stub; @@ -615,55 +1608,29 @@ mjit::Compiler::finishThisUp(JITScript **jitp) if (equalityICs[i].jumpToStub.isSet()) jitEqualityICs[i].jumpToStub = fullCode.locationOf(equalityICs[i].jumpToStub.get()); jitEqualityICs[i].fallThrough = fullCode.locationOf(equalityICs[i].fallThrough); - - stubCode.patch(equalityICs[i].addrLabel, &jitEqualityICs[i]); - } - - ic::TraceICInfo *jitTraceICs = (ic::TraceICInfo *)cursor; - jit->nTraceICs = traceICs.length(); - cursor += sizeof(ic::TraceICInfo) * jit->nTraceICs; - for (size_t i = 0; i < jit->nTraceICs; i++) { - jitTraceICs[i].initialized = traceICs[i].initialized; - if (!traceICs[i].initialized) - continue; - uint32 offs = uint32(traceICs[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - jitTraceICs[i].traceHint = fullCode.locationOf(traceICs[i].traceHint); - jitTraceICs[i].jumpTarget = fullCode.locationOf(jumpMap[offs]); - jitTraceICs[i].stubEntry = stubCode.locationOf(traceICs[i].stubEntry); - jitTraceICs[i].traceData = NULL; -#ifdef DEBUG - jitTraceICs[i].jumpTargetPC = traceICs[i].jumpTarget; -#endif - jitTraceICs[i].hasSlowTraceHint = traceICs[i].slowTraceHint.isSet(); - if (traceICs[i].slowTraceHint.isSet()) - jitTraceICs[i].slowTraceHint = stubCode.locationOf(traceICs[i].slowTraceHint.get()); -#ifdef JS_TRACER - uint32 hotloop = GetHotloop(cx); - uint32 prevCount = cx->compartment->backEdgeCount(traceICs[i].jumpTarget); - jitTraceICs[i].loopCounterStart = hotloop; - jitTraceICs[i].loopCounter = hotloop < prevCount ? 1 : hotloop - prevCount; -#endif - - stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]); + stubCode.patch(equalityICs[i].addrLabel, &jitEqualityICs[i]); } #endif /* JS_MONOIC */ for (size_t i = 0; i < callPatches.length(); i++) { CallPatchInfo &patch = callPatches[i]; + CodeLocationLabel joinPoint = patch.joinSlow + ? stubCode.locationOf(patch.joinPoint) + : fullCode.locationOf(patch.joinPoint); + if (patch.hasFastNcode) - fullCode.patch(patch.fastNcodePatch, fullCode.locationOf(patch.joinPoint)); + fullCode.patch(patch.fastNcodePatch, joinPoint); if (patch.hasSlowNcode) - stubCode.patch(patch.slowNcodePatch, fullCode.locationOf(patch.joinPoint)); + stubCode.patch(patch.slowNcodePatch, joinPoint); } #ifdef JS_POLYIC ic::GetElementIC *jitGetElems = (ic::GetElementIC *)cursor; - jit->nGetElems = getElemICs.length(); - cursor += sizeof(ic::GetElementIC) * jit->nGetElems; - for (size_t i = 0; i < jit->nGetElems; i++) { + chunk->nGetElems = getElemICs.length(); + cursor += sizeof(ic::GetElementIC) * chunk->nGetElems; + for (size_t i = 0; i < chunk->nGetElems; i++) { ic::GetElementIC &to = jitGetElems[i]; GetElementICInfo &from = getElemICs[i]; @@ -680,18 +1647,18 @@ mjit::Compiler::finishThisUp(JITScript **jitp) to.inlineTypeGuard = inlineTypeGuard; JS_ASSERT(to.inlineTypeGuard == inlineTypeGuard); } - int inlineClaspGuard = fullCode.locationOf(from.claspGuard) - + int inlineShapeGuard = fullCode.locationOf(from.shapeGuard) - fullCode.locationOf(from.fastPathStart); - to.inlineClaspGuard = inlineClaspGuard; - JS_ASSERT(to.inlineClaspGuard == inlineClaspGuard); + to.inlineShapeGuard = inlineShapeGuard; + JS_ASSERT(to.inlineShapeGuard == inlineShapeGuard); stubCode.patch(from.paramAddr, &to); } ic::SetElementIC *jitSetElems = (ic::SetElementIC *)cursor; - jit->nSetElems = setElemICs.length(); - cursor += sizeof(ic::SetElementIC) * jit->nSetElems; - for (size_t i = 0; i < jit->nSetElems; i++) { + chunk->nSetElems = setElemICs.length(); + cursor += sizeof(ic::SetElementIC) * chunk->nSetElems; + for (size_t i = 0; i < chunk->nSetElems; i++) { ic::SetElementIC &to = jitSetElems[i]; SetElementICInfo &from = setElemICs[i]; @@ -710,10 +1677,10 @@ mjit::Compiler::finishThisUp(JITScript **jitp) else to.keyReg = from.key.reg(); - int inlineClaspGuard = fullCode.locationOf(from.claspGuard) - + int inlineShapeGuard = fullCode.locationOf(from.shapeGuard) - fullCode.locationOf(from.fastPathStart); - to.inlineClaspGuard = inlineClaspGuard; - JS_ASSERT(to.inlineClaspGuard == inlineClaspGuard); + to.inlineShapeGuard = inlineShapeGuard; + JS_ASSERT(to.inlineShapeGuard == inlineShapeGuard); int inlineHoleGuard = fullCode.locationOf(from.holeGuard) - fullCode.locationOf(from.fastPathStart); @@ -729,9 +1696,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } ic::PICInfo *jitPics = (ic::PICInfo *)cursor; - jit->nPICs = pics.length(); - cursor += sizeof(ic::PICInfo) * jit->nPICs; - for (size_t i = 0; i < jit->nPICs; i++) { + chunk->nPICs = pics.length(); + cursor += sizeof(ic::PICInfo) * chunk->nPICs; + for (size_t i = 0; i < chunk->nPICs; i++) { new (&jitPics[i]) ic::PICInfo(); pics[i].copyTo(jitPics[i], fullCode, stubCode); pics[i].copySimpleMembersTo(jitPics[i]); @@ -748,7 +1715,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) jitPics[i].u.vr = pics[i].vr; } else if (pics[i].kind != ic::PICInfo::NAME) { if (pics[i].hasTypeCheck) { - int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - + int32_t distance = stubcc.masm.distanceOf(pics[i].typeCheck) - stubcc.masm.distanceOf(pics[i].slowPathStart); JS_ASSERT(distance <= 0); jitPics[i].u.get.typeCheckOffset = distance; @@ -758,28 +1725,42 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } #endif + JS_ASSERT(size_t(cursor - (uint8_t*)chunk) == dataSize); + /* Use the computed size here -- we don't want slop bytes to be counted. */ + JS_ASSERT(chunk->computedSizeOfIncludingThis() == dataSize); + /* Link fast and slow paths together. */ stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size()); - /* Patch all double references. */ +#if defined(JS_CPU_MIPS) + /* Make sure doubleOffset is aligned to sizeof(double) bytes. */ + size_t doubleOffset = (((size_t)result + masm.size() + stubcc.size() + + sizeof(double) - 1) & (~(sizeof(double) - 1))) - + (size_t)result; + JS_ASSERT((((size_t)result + doubleOffset) & 7) == 0); +#else size_t doubleOffset = masm.size() + stubcc.size(); - double *doubleVec = (double *)(result + doubleOffset); - for (size_t i = 0; i < doubleList.length(); i++) { - DoublePatch &patch = doubleList[i]; - doubleVec[i] = patch.d; - if (patch.ool) - stubCode.patch(patch.label, &doubleVec[i]); - else - fullCode.patch(patch.label, &doubleVec[i]); - } +#endif + + double *inlineDoubles = (double *) (result + doubleOffset); + double *oolDoubles = (double*) (result + doubleOffset + + masm.numDoubles() * sizeof(double)); /* Generate jump tables. */ - void **jumpVec = (void **)(doubleVec + doubleList.length()); + void **jumpVec = (void **)(oolDoubles + stubcc.masm.numDoubles()); - for (size_t i = 0; i < jumpTableOffsets.length(); i++) { - uint32 offset = jumpTableOffsets[i]; - JS_ASSERT(jumpMap[offset].isValid()); - jumpVec[i] = (void *)(result + masm.distanceOf(jumpMap[offset])); + for (size_t i = 0; i < jumpTableEdges.length(); i++) { + JumpTableEdge edge = jumpTableEdges[i]; + if (bytecodeInChunk(script->code + edge.target)) { + JS_ASSERT(jumpMap[edge.target].isSet()); + jumpVec[i] = (void *)(result + masm.distanceOf(jumpMap[edge.target])); + } else { + ChunkJumpTableEdge nedge; + nedge.edge = edge; + nedge.jumpTableEntry = &jumpVec[i]; + chunkJumps.infallibleAppend(nedge); + jumpVec[i] = NULL; + } } /* Patch jump table references. */ @@ -789,73 +1770,98 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } /* Patch all outgoing calls. */ - masm.finalize(fullCode); - stubcc.masm.finalize(stubCode); + masm.finalize(fullCode, inlineDoubles); + stubcc.masm.finalize(stubCode, oolDoubles); JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - /* Build the table of call sites. */ - CallSite *jitCallSites = (CallSite *)cursor; - jit->nCallSites = callSites.length(); - cursor += sizeof(CallSite) * jit->nCallSites; - for (size_t i = 0; i < jit->nCallSites; i++) { - CallSite &to = jitCallSites[i]; - InternalCallSite &from = callSites[i]; - uint32 codeOffset = from.ool - ? masm.size() + from.returnOffset - : from.returnOffset; - to.initialize(codeOffset, from.pc - script->code, from.id); + Probes::registerMJITCode(cx, jit, + a, + (JSActiveFrame**) inlineFrames.begin(), + result, masm.size(), + result + masm.size(), stubcc.size()); + + outerChunk.chunk = chunk; + + Repatcher repatch(chunk); + + /* Patch all incoming and outgoing cross-chunk jumps. */ + CrossChunkEdge *crossEdges = jit->edges(); + for (unsigned i = 0; i < jit->nedges; i++) { + CrossChunkEdge &edge = crossEdges[i]; + if (bytecodeInChunk(outerScript->code + edge.source)) { + JS_ASSERT(!edge.sourceJump1 && !edge.sourceJump2); + void *label = edge.targetLabel ? edge.targetLabel : edge.shimLabel; + CodeLocationLabel targetLabel(label); + JSOp op = JSOp(script->code[edge.source]); + if (op == JSOP_TABLESWITCH) { + if (edge.jumpTableEntries) + cx->free_(edge.jumpTableEntries); + CrossChunkEdge::JumpTableEntryVector *jumpTableEntries = NULL; + bool failed = false; + for (unsigned j = 0; j < chunkJumps.length(); j++) { + ChunkJumpTableEdge nedge = chunkJumps[j]; + if (nedge.edge.source == edge.source && nedge.edge.target == edge.target) { + if (!jumpTableEntries) { + jumpTableEntries = OffTheBooks::new_(); + if (!jumpTableEntries) + failed = true; + } + if (!jumpTableEntries->append(nedge.jumpTableEntry)) + failed = true; + *nedge.jumpTableEntry = label; + } + } + if (failed) { + execPool->release(); + cx->free_(chunk); + js_ReportOutOfMemory(cx); + return Compile_Error; + } + edge.jumpTableEntries = jumpTableEntries; + } + for (unsigned j = 0; j < chunkEdges.length(); j++) { + const OutgoingChunkEdge &oedge = chunkEdges[j]; + if (oedge.source == edge.source && oedge.target == edge.target) { + /* + * Only a single edge needs to be patched; we ensured while + * generating chunks that no two cross chunk edges can have + * the same source and target. Note that there may not be + * an edge to patch, if constant folding determined the + * jump is never taken. + */ + edge.sourceJump1 = fullCode.locationOf(oedge.fastJump).executableAddress(); + repatch.relink(CodeLocationJump(edge.sourceJump1), targetLabel); + if (oedge.slowJump.isSet()) { + edge.sourceJump2 = + stubCode.locationOf(oedge.slowJump.get()).executableAddress(); + repatch.relink(CodeLocationJump(edge.sourceJump2), targetLabel); + } + break; + } + } + } else if (bytecodeInChunk(outerScript->code + edge.target)) { + JS_ASSERT(!edge.targetLabel); + JS_ASSERT(jumpMap[edge.target].isSet()); + edge.targetLabel = fullCode.locationOf(jumpMap[edge.target]).executableAddress(); + jit->patchEdge(edge, edge.targetLabel); + } } - JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize); - - *jitp = jit; - - /* We tolerate a race in the stats. */ - cx->runtime->mjitDataSize += dataSize; - return Compile_Okay; } -class SrcNoteLineScanner { - ptrdiff_t offset; - jssrcnote *sn; - -public: - SrcNoteLineScanner(jssrcnote *sn) : offset(SN_DELTA(sn)), sn(sn) {} - - bool firstOpInLine(ptrdiff_t relpc) { - while ((offset < relpc) && !SN_IS_TERMINATOR(sn)) { - sn = SN_NEXT(sn); - offset += SN_DELTA(sn); - } - - while ((offset == relpc) && !SN_IS_TERMINATOR(sn)) { - JSSrcNoteType type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE || type == SRC_NEWLINE) - return true; - - sn = SN_NEXT(sn); - offset += SN_DELTA(sn); - } - - return false; - } -}; - #ifdef DEBUG #define SPEW_OPCODE() \ JS_BEGIN_MACRO \ if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \ - JaegerSpew(JSpew_JSOps, " %2d ", frame.stackDepth()); \ - void *mark = JS_ARENA_MARK(&cx->tempPool); \ - Sprinter sprinter; \ - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); \ + Sprinter sprinter(cx); \ + sprinter.init(); \ js_Disassemble1(cx, script, PC, PC - script->code, \ JS_TRUE, &sprinter); \ - fprintf(stdout, "%s", sprinter.base); \ - JS_ARENA_RELEASE(&cx->tempPool, mark); \ + JaegerSpew(JSpew_JSOps, " %2d %s", \ + frame.stackDepth(), sprinter.string()); \ } \ JS_END_MACRO; #else @@ -869,25 +1875,93 @@ class SrcNoteLineScanner { JS_END_MACRO; \ break; +static inline void +FixDouble(Value &val) +{ + if (val.isInt32()) + val.setDouble((double)val.toInt32()); +} + +inline bool +mjit::Compiler::shouldStartLoop(jsbytecode *head) +{ + /* + * Don't do loop based optimizations or register allocation for loops which + * span multiple chunks. + */ + if (*head == JSOP_LOOPHEAD && analysis->getLoop(head)) { + uint32_t backedge = analysis->getLoop(head)->backedge; + if (!bytecodeInChunk(script->code + backedge)) + return false; + return true; + } + return false; +} + CompileStatus mjit::Compiler::generateMethod() { - mjit::AutoScriptRetrapper trapper(cx, script); - SrcNoteLineScanner scanner(script->notes()); + SrcNoteLineScanner scanner(script->notes(), script->lineno); + + /* For join points, whether there was fallthrough from the previous opcode. */ + bool fallthrough = true; + + /* Last bytecode processed. */ + jsbytecode *lastPC = NULL; + + if (!outerJIT()) + return Compile_Retry; + + uint32_t chunkBegin = 0, chunkEnd = script->length; + if (!a->parent) { + const ChunkDescriptor &desc = + outerJIT()->chunkDescriptor(chunkIndex); + chunkBegin = desc.begin; + chunkEnd = desc.end; + + while (PC != script->code + chunkBegin) { + Bytecode *opinfo = analysis->maybeCode(PC); + if (opinfo) { + if (opinfo->jumpTarget) { + /* Update variable types for all new values at this bytecode. */ + const SlotValue *newv = analysis->newValues(PC); + if (newv) { + while (newv->slot) { + if (newv->slot < TotalSlots(script)) { + VarType &vt = a->varTypes[newv->slot]; + vt.setTypes(analysis->getValueTypes(newv->value)); + } + newv++; + } + } + } + if (analyze::BytecodeUpdatesSlot(JSOp(*PC))) { + uint32_t slot = GetBytecodeSlot(script, PC); + if (analysis->trackSlot(slot)) { + VarType &vt = a->varTypes[slot]; + vt.setTypes(analysis->pushedTypes(PC, 0)); + } + } + } + + PC += GetBytecodeLength(PC); + } + + if (chunkIndex != 0) { + uint32_t depth = analysis->getCode(PC).stackDepth; + for (uint32_t i = 0; i < depth; i++) + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + } + } for (;;) { JSOp op = JSOp(*PC); int trap = stubs::JSTRAP_NONE; - if (op == JSOP_TRAP) { - if (!trapper.untrap(PC)) - return Compile_Error; - op = JSOp(*PC); + + if (script->hasBreakpointsAt(PC)) trap |= stubs::JSTRAP_TRAP; - } - if (script->singleStepMode && scanner.firstOpInLine(PC - script->code)) - trap |= stubs::JSTRAP_SINGLESTEP; - analyze::Bytecode *opinfo = analysis->maybeCode(PC); + Bytecode *opinfo = analysis->maybeCode(PC); if (!opinfo) { if (op == JSOP_STOP) @@ -899,70 +1973,190 @@ mjit::Compiler::generateMethod() continue; } + if (PC >= script->code + script->length) + break; + + scanner.advanceTo(PC - script->code); + if (script->stepModeEnabled() && + (scanner.isLineHeader() || opinfo->jumpTarget)) + { + trap |= stubs::JSTRAP_SINGLESTEP; + } + + frame.setPC(PC); frame.setInTryBlock(opinfo->inTryBlock); + + if (fallthrough) { + /* + * If there is fallthrough from the previous opcode and we changed + * any entries into doubles for a branch at that previous op, + * revert those entries into integers. Similarly, if we forgot that + * an entry is a double then make it a double again, as the frame + * may have assigned it a normal register. + */ + for (unsigned i = 0; i < fixedIntToDoubleEntries.length(); i++) { + FrameEntry *fe = frame.getSlotEntry(fixedIntToDoubleEntries[i]); + frame.ensureInteger(fe); + } + for (unsigned i = 0; i < fixedDoubleToAnyEntries.length(); i++) { + FrameEntry *fe = frame.getSlotEntry(fixedDoubleToAnyEntries[i]); + frame.syncAndForgetFe(fe); + } + } + fixedIntToDoubleEntries.clear(); + fixedDoubleToAnyEntries.clear(); + + if (PC >= script->code + chunkEnd) { + if (fallthrough) { + if (opinfo->jumpTarget) + fixDoubleTypes(PC); + frame.syncAndForgetEverything(); + jsbytecode *curPC = PC; + do { + PC--; + } while (!analysis->maybeCode(PC)); + if (!jumpAndRun(masm.jump(), curPC, NULL, NULL, /* fallthrough = */ true)) + return Compile_Error; + PC = curPC; + } + break; + } + if (opinfo->jumpTarget || trap) { - frame.syncAndForgetEverything(opinfo->stackDepth); - opinfo->safePoint = true; + if (fallthrough) { + fixDoubleTypes(PC); + fixedIntToDoubleEntries.clear(); + fixedDoubleToAnyEntries.clear(); + + /* + * Watch for fallthrough to the head of a 'do while' loop. + * We don't know what register state we will be using at the head + * of the loop so sync, branch, and fix it up after the loop + * has been processed. + */ + if (cx->typeInferenceEnabled() && shouldStartLoop(PC)) { + frame.syncAndForgetEverything(); + Jump j = masm.jump(); + if (!startLoop(PC, j, PC)) + return Compile_Error; + } else { + Label start = masm.label(); + if (!frame.syncForBranch(PC, Uses(0))) + return Compile_Error; + if (pcLengths && lastPC) { + /* Track this sync code for the previous op. */ + size_t length = masm.size() - masm.distanceOf(start); + uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code; + pcLengths[offset].codeLength += length; + } + JS_ASSERT(frame.consistentRegisters(PC)); + } + } + + if (!frame.discardForJoin(analysis->getAllocation(PC), opinfo->stackDepth)) + return Compile_Error; + updateJoinVarTypes(); + fallthrough = true; + + if (!cx->typeInferenceEnabled()) { + /* All join points have synced state if we aren't doing cross-branch regalloc. */ + opinfo->safePoint = true; + } + } else if (opinfo->safePoint) { + frame.syncAndForgetEverything(); } - jumpMap[uint32(PC - script->code)] = masm.label(); + frame.assertValidRegisterState(); + a->jumpMap[uint32_t(PC - script->code)] = masm.label(); + + // Now that we have the PC's register allocation, make sure it gets + // explicitly updated if this is the loop entry and new loop registers + // are allocated later on. + if (loop && !a->parent) + loop->setOuterPC(PC); SPEW_OPCODE(); JS_ASSERT(frame.stackDepth() == opinfo->stackDepth); + if (op == JSOP_LOOPHEAD && analysis->getLoop(PC)) { + jsbytecode *backedge = script->code + analysis->getLoop(PC)->backedge; + if (!bytecodeInChunk(backedge)){ + for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) { + if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) { + FrameEntry *fe = frame.getSlotEntry(slot); + masm.ensureInMemoryDouble(frame.addressOf(fe)); + } + } + } + } + + // If this is an exception entry point, then jsl_InternalThrow has set + // VMFrame::fp to the correct fp for the entry point. We need to copy + // that value here to FpReg so that FpReg also has the correct sp. + // Otherwise, we would simply be using a stale FpReg value. + if (op == JSOP_ENTERBLOCK && analysis->getCode(PC).exceptionEntry) + masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + if (trap) { prepareStubCall(Uses(0)); masm.move(Imm32(trap), Registers::ArgReg1); - Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::Trap)); - InternalCallSite site(masm.callReturnOffset(cl), PC, - CallSite::MAGIC_TRAP_ID, true, false); + Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::Trap), NULL); + InternalCallSite site(masm.callReturnOffset(cl), a->inlineIndex, PC, + REJOIN_TRAP, false); addCallSite(site); - } else if (savedTraps && savedTraps[PC - script->code]) { - // Normally when we patch return addresses, we have generated the - // same exact code at that site. For example, patching a stub call's - // return address will resume at the same stub call. - // - // In the case we're handling here, we could potentially be - // recompiling to remove a trap, and therefore we won't generate - // a call to the trap. However, we could be re-entering from that - // trap. The callsite will be missing, and fixing the stack will - // fail! Worse, we can't just put a label here, because on some - // platforms the stack needs to be adjusted when returning from - // the old trap call. - // - // To deal with this, we add a small bit of code in the OOL path - // that will adjust the stack and jump back into the script. - // Note that this uses MAGIC_TRAP_ID, which is necessary for - // repatching to detect the callsite as identical to the return - // address. - // - // Unfortunately, this means that if a bytecode is ever trapped, - // we will always generate a CallSite (either Trapped or not) for - // every debug recompilation of the script thereafter. The reason - // is that MAGIC_TRAP_ID callsites always propagate to the next - // recompilation. That's okay, and not worth fixing - it's a small - // amount of memory. - uint32 offset = stubcc.masm.distanceOf(stubcc.masm.label()); - if (Assembler::ReturnStackAdjustment) { - stubcc.masm.addPtr(Imm32(Assembler::ReturnStackAdjustment), - Assembler::stackPointerRegister); - } - stubcc.crossJump(stubcc.masm.jump(), masm.label()); + } - InternalCallSite site(offset, PC, CallSite::MAGIC_TRAP_ID, false, true); - addCallSite(site); + /* Don't compile fat opcodes, run the decomposed version instead. */ + if (js_CodeSpec[op].format & JOF_DECOMPOSE) { + PC += js_CodeSpec[op].length; + continue; + } + + Label codeStart = masm.label(); + bool countersUpdated = false; + bool arithUpdated = false; + + JSValueType arithFirstUseType = JSVAL_TYPE_UNKNOWN; + JSValueType arithSecondUseType = JSVAL_TYPE_UNKNOWN; + if (script->pcCounters && !!(js_CodeSpec[op].format & JOF_ARITH)) { + if (GetUseCount(script, PC - script->code) == 1) { + FrameEntry *use = frame.peek(-1); + /* + * Pretend it's a binary operation and the second operand has + * the same type as the first one. + */ + if (use->isTypeKnown()) + arithFirstUseType = arithSecondUseType = use->getKnownType(); + } else { + FrameEntry *use = frame.peek(-1); + if (use->isTypeKnown()) + arithFirstUseType = use->getKnownType(); + use = frame.peek(-2); + if (use->isTypeKnown()) + arithSecondUseType = use->getKnownType(); + } } + /* + * Update PC counters for jump opcodes at their start, so that we don't + * miss them when taking the jump. This is delayed for other opcodes, + * as we want to skip updating for ops we didn't generate any code for. + */ + if (script->pcCounters && JOF_OPTYPE(op) == JOF_JUMP) + updatePCCounters(PC, &codeStart, &countersUpdated); + /********************** * BEGIN COMPILER OPS * - **********************/ + **********************/ + + lastPC = PC; switch (op) { BEGIN_CASE(JSOP_NOP) END_CASE(JSOP_NOP) - BEGIN_CASE(JSOP_PUSH) + BEGIN_CASE(JSOP_UNDEFINED) frame.push(UndefinedValue()); - END_CASE(JSOP_PUSH) + END_CASE(JSOP_UNDEFINED) BEGIN_CASE(JSOP_POPV) BEGIN_CASE(JSOP_SETRVAL) @@ -973,6 +2167,9 @@ mjit::Compiler::generateMethod() masm.store32(reg, FrameFlagsAddress()); frame.freeReg(reg); + /* Scripts which write to the frame's return slot aren't inlined. */ + JS_ASSERT(a == outer); + FrameEntry *fe = frame.peek(-1); frame.storeTo(fe, Address(JSFrameReg, StackFrame::offsetOfReturnValue()), true); frame.pop(); @@ -980,24 +2177,57 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_RETURN) + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); emitReturn(frame.peek(-1)); + fallthrough = false; END_CASE(JSOP_RETURN) BEGIN_CASE(JSOP_GOTO) BEGIN_CASE(JSOP_DEFAULT) { - /* :XXX: this isn't really necessary if we follow the branch. */ - frame.syncAndForgetEverything(); - Jump j = masm.jump(); - if (!jumpAndTrace(j, PC + GET_JUMP_OFFSET(PC))) - return Compile_Error; + unsigned targetOffset = FollowBranch(cx, script, PC - script->code); + jsbytecode *target = script->code + targetOffset; + + fixDoubleTypes(target); + + /* + * Watch for gotos which are entering a 'for' or 'while' loop. + * These jump to the loop condition test and are immediately + * followed by the head of the loop. + */ + jsbytecode *next = PC + js_CodeSpec[op].length; + if (cx->typeInferenceEnabled() && + analysis->maybeCode(next) && + shouldStartLoop(next)) + { + frame.syncAndForgetEverything(); + Jump j = masm.jump(); + if (!startLoop(next, j, target)) + return Compile_Error; + } else { + if (!frame.syncForBranch(target, Uses(0))) + return Compile_Error; + Jump j = masm.jump(); + if (!jumpAndRun(j, target)) + return Compile_Error; + } + fallthrough = false; + PC += js_CodeSpec[op].length; + break; } END_CASE(JSOP_GOTO) BEGIN_CASE(JSOP_IFEQ) BEGIN_CASE(JSOP_IFNE) - if (!jsop_ifneq(op, PC + GET_JUMP_OFFSET(PC))) + { + jsbytecode *target = PC + GET_JUMP_OFFSET(PC); + fixDoubleTypes(target); + if (!jsop_ifneq(op, target)) return Compile_Error; + PC += js_CodeSpec[op].length; + break; + } END_CASE(JSOP_IFNE) BEGIN_CASE(JSOP_ARGUMENTS) @@ -1008,24 +2238,28 @@ mjit::Compiler::generateMethod() * 'apply' actually refers to js_fun_apply. If this is not true, * the slow path in JSOP_FUNAPPLY will create the args object. */ - if (canUseApplyTricks()) + if (canUseApplyTricks()) { + /* + * Check for interrupts at the JSOP_ARGUMENTS when using + * apply tricks, see inlineCallHelper(). + */ + interruptCheckHelper(); + applyTricks = LazyArgsObj; - else - jsop_arguments(); - frame.pushSynced(); + pushSyncedEntry(0); + } else if (cx->typeInferenceEnabled() && !script->strictModeCode && + !types::TypeSet::HasObjectFlags(cx, script->function()->getType(cx), + types::OBJECT_FLAG_CREATED_ARGUMENTS)) { + frame.push(MagicValue(JS_LAZY_ARGUMENTS)); + } else { + jsop_arguments(REJOIN_FALLTHROUGH); + pushSyncedEntry(0); + } END_CASE(JSOP_ARGUMENTS) - BEGIN_CASE(JSOP_FORARG) - iterNext(); - frame.storeArg(GET_SLOTNO(PC), true); - frame.pop(); - END_CASE(JSOP_FORARG) - - BEGIN_CASE(JSOP_FORLOCAL) - iterNext(); - frame.storeLocal(GET_SLOTNO(PC), true); - frame.pop(); - END_CASE(JSOP_FORLOCAL) + BEGIN_CASE(JSOP_ITERNEXT) + iterNext(GET_INT8(PC)); + END_CASE(JSOP_ITERNEXT) BEGIN_CASE(JSOP_DUP) frame.dup(); @@ -1035,6 +2269,39 @@ mjit::Compiler::generateMethod() frame.dup2(); END_CASE(JSOP_DUP2) + BEGIN_CASE(JSOP_SWAP) + frame.dup2(); + frame.shift(-3); + frame.shift(-1); + END_CASE(JSOP_SWAP) + + BEGIN_CASE(JSOP_PICK) + { + uint32_t amt = GET_UINT8(PC); + + // Push -(amt + 1), say amt == 2 + // Stack before: X3 X2 X1 + // Stack after: X3 X2 X1 X3 + frame.dupAt(-int32_t(amt + 1)); + + // For each item X[i...1] push it then move it down. + // The above would transition like so: + // X3 X2 X1 X3 X2 (dupAt) + // X2 X2 X1 X3 (shift) + // X2 X2 X1 X3 X1 (dupAt) + // X2 X1 X1 X3 (shift) + for (int32_t i = -int32_t(amt); i < 0; i++) { + frame.dupAt(i - 1); + frame.shift(i - 2); + } + + // The stack looks like: + // Xn ... X1 X1 X{n+1} + // So shimmy the last value down. + frame.shimmy(1); + } + END_CASE(JSOP_PICK) + BEGIN_CASE(JSOP_BITOR) BEGIN_CASE(JSOP_BITXOR) BEGIN_CASE(JSOP_BITAND) @@ -1048,6 +2315,11 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_EQ) BEGIN_CASE(JSOP_NE) { + if (script->pcCounters) { + updateArithCounters(PC, NULL, arithFirstUseType, arithSecondUseType); + arithUpdated = true; + } + /* Detect fusions. */ jsbytecode *next = &PC[JSOP_GE_LENGTH]; JSOp fused = JSOp(*next); @@ -1056,8 +2328,12 @@ mjit::Compiler::generateMethod() /* Get jump target, if any. */ jsbytecode *target = NULL; - if (fused != JSOP_NOP) + if (fused != JSOP_NOP) { + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); target = next + GET_JUMP_OFFSET(next); + fixDoubleTypes(target); + } BoolStub stub = NULL; switch (op) { @@ -1084,6 +2360,12 @@ mjit::Compiler::generateMethod() break; } + /* + * We need to ensure in the target case that we always rejoin + * before the rval test. In the non-target case we will rejoin + * correctly after the op finishes. + */ + FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); @@ -1104,14 +2386,8 @@ mjit::Compiler::generateMethod() } else { if (fused == JSOP_IFEQ) result = !result; - - /* Branch is never taken, don't bother doing anything. */ - if (result) { - frame.syncAndForgetEverything(); - Jump j = masm.jump(); - if (!jumpAndTrace(j, target)) - return Compile_Error; - } + if (!constantFoldBranch(target, result)) + return Compile_Error; } } else { if (!emitStubCmpOp(stub, target, fused)) @@ -1144,7 +2420,7 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_LSH) BEGIN_CASE(JSOP_RSH) - jsop_rsh(); + jsop_bitop(op); END_CASE(JSOP_RSH) BEGIN_CASE(JSOP_URSH) @@ -1152,23 +2428,28 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_URSH) BEGIN_CASE(JSOP_ADD) - jsop_binary(op, stubs::Add); + if (!jsop_binary(op, stubs::Add, knownPushedType(0), pushedTypeSet(0))) + return Compile_Retry; END_CASE(JSOP_ADD) BEGIN_CASE(JSOP_SUB) - jsop_binary(op, stubs::Sub); + if (!jsop_binary(op, stubs::Sub, knownPushedType(0), pushedTypeSet(0))) + return Compile_Retry; END_CASE(JSOP_SUB) BEGIN_CASE(JSOP_MUL) - jsop_binary(op, stubs::Mul); + if (!jsop_binary(op, stubs::Mul, knownPushedType(0), pushedTypeSet(0))) + return Compile_Retry; END_CASE(JSOP_MUL) BEGIN_CASE(JSOP_DIV) - jsop_binary(op, stubs::Div); + if (!jsop_binary(op, stubs::Div, knownPushedType(0), pushedTypeSet(0))) + return Compile_Retry; END_CASE(JSOP_DIV) BEGIN_CASE(JSOP_MOD) - jsop_mod(); + if (!jsop_mod()) + return Compile_Retry; END_CASE(JSOP_MOD) BEGIN_CASE(JSOP_NOT) @@ -1180,7 +2461,7 @@ mjit::Compiler::generateMethod() FrameEntry *top = frame.peek(-1); if (top->isConstant() && top->getValue().isPrimitive()) { int32_t i; - ValueToECMAInt32(cx, top->getValue(), &i); + JS_ALWAYS_TRUE(ToInt32(cx, top->getValue(), &i)); i = ~i; frame.pop(); frame.push(Int32Value(i)); @@ -1195,10 +2476,19 @@ mjit::Compiler::generateMethod() FrameEntry *top = frame.peek(-1); if (top->isConstant() && top->getValue().isPrimitive()) { double d; - ValueToNumber(cx, top->getValue(), &d); + JS_ALWAYS_TRUE(ToNumber(cx, top->getValue(), &d)); d = -d; + Value v = NumberValue(d); + + /* Watch for overflow in constant propagation. */ + types::TypeSet *pushed = pushedTypeSet(0); + if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) { + types::TypeScript::MonitorOverflow(cx, script, PC); + return Compile_Retry; + } + frame.pop(); - frame.push(NumberValue(d)); + frame.push(v); } else { jsop_neg(); } @@ -1211,167 +2501,71 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_DELNAME) { - uint32 index = fullAtomIndex(PC); - JSAtom *atom = script->getAtom(index); + uint32_t index = fullAtomIndex(PC); + PropertyName *name = script->getName(index); prepareStubCall(Uses(0)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::DelName); - frame.pushSynced(); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(stubs::DelName, REJOIN_FALLTHROUGH); + pushSyncedEntry(0); } END_CASE(JSOP_DELNAME) BEGIN_CASE(JSOP_DELPROP) { - uint32 index = fullAtomIndex(PC); - JSAtom *atom = script->getAtom(index); - - prepareStubCall(Uses(1)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(STRICT_VARIANT(stubs::DelProp)); - frame.pop(); - frame.pushSynced(); - } - END_CASE(JSOP_DELPROP) - - BEGIN_CASE(JSOP_DELELEM) - prepareStubCall(Uses(2)); - INLINE_STUBCALL(STRICT_VARIANT(stubs::DelElem)); - frame.popn(2); - frame.pushSynced(); - END_CASE(JSOP_DELELEM) - - BEGIN_CASE(JSOP_TYPEOF) - BEGIN_CASE(JSOP_TYPEOFEXPR) - jsop_typeof(); - END_CASE(JSOP_TYPEOF) - - BEGIN_CASE(JSOP_VOID) - frame.pop(); - frame.push(UndefinedValue()); - END_CASE(JSOP_VOID) - - BEGIN_CASE(JSOP_INCNAME) - if (!jsop_nameinc(op, STRICT_VARIANT(stubs::IncName), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_INCNAME) - - BEGIN_CASE(JSOP_INCGNAME) - jsop_gnameinc(op, STRICT_VARIANT(stubs::IncGlobalName), fullAtomIndex(PC)); - break; - END_CASE(JSOP_INCGNAME) - - BEGIN_CASE(JSOP_INCPROP) - if (!jsop_propinc(op, STRICT_VARIANT(stubs::IncProp), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_INCPROP) - - BEGIN_CASE(JSOP_INCELEM) - jsop_eleminc(op, STRICT_VARIANT(stubs::IncElem)); - END_CASE(JSOP_INCELEM) - - BEGIN_CASE(JSOP_DECNAME) - if (!jsop_nameinc(op, STRICT_VARIANT(stubs::DecName), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_DECNAME) - - BEGIN_CASE(JSOP_DECGNAME) - jsop_gnameinc(op, STRICT_VARIANT(stubs::DecGlobalName), fullAtomIndex(PC)); - break; - END_CASE(JSOP_DECGNAME) - - BEGIN_CASE(JSOP_DECPROP) - if (!jsop_propinc(op, STRICT_VARIANT(stubs::DecProp), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_DECPROP) - - BEGIN_CASE(JSOP_DECELEM) - jsop_eleminc(op, STRICT_VARIANT(stubs::DecElem)); - END_CASE(JSOP_DECELEM) - - BEGIN_CASE(JSOP_NAMEINC) - if (!jsop_nameinc(op, STRICT_VARIANT(stubs::NameInc), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_NAMEINC) - - BEGIN_CASE(JSOP_GNAMEINC) - jsop_gnameinc(op, STRICT_VARIANT(stubs::GlobalNameInc), fullAtomIndex(PC)); - break; - END_CASE(JSOP_GNAMEINC) - - BEGIN_CASE(JSOP_PROPINC) - if (!jsop_propinc(op, STRICT_VARIANT(stubs::PropInc), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_PROPINC) - - BEGIN_CASE(JSOP_ELEMINC) - jsop_eleminc(op, STRICT_VARIANT(stubs::ElemInc)); - END_CASE(JSOP_ELEMINC) - - BEGIN_CASE(JSOP_NAMEDEC) - if (!jsop_nameinc(op, STRICT_VARIANT(stubs::NameDec), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_NAMEDEC) - - BEGIN_CASE(JSOP_GNAMEDEC) - jsop_gnameinc(op, STRICT_VARIANT(stubs::GlobalNameDec), fullAtomIndex(PC)); - break; - END_CASE(JSOP_GNAMEDEC) - - BEGIN_CASE(JSOP_PROPDEC) - if (!jsop_propinc(op, STRICT_VARIANT(stubs::PropDec), fullAtomIndex(PC))) - return Compile_Error; - break; - END_CASE(JSOP_PROPDEC) + uint32_t index = fullAtomIndex(PC); + PropertyName *name = script->getName(index); - BEGIN_CASE(JSOP_ELEMDEC) - jsop_eleminc(op, STRICT_VARIANT(stubs::ElemDec)); - END_CASE(JSOP_ELEMDEC) + prepareStubCall(Uses(1)); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(STRICT_VARIANT(stubs::DelProp), REJOIN_FALLTHROUGH); + frame.pop(); + pushSyncedEntry(0); + } + END_CASE(JSOP_DELPROP) - BEGIN_CASE(JSOP_GETTHISPROP) - /* Push thisv onto stack. */ - jsop_this(); - if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)))) - return Compile_Error; - END_CASE(JSOP_GETTHISPROP); + BEGIN_CASE(JSOP_DELELEM) + { + prepareStubCall(Uses(2)); + INLINE_STUBCALL(STRICT_VARIANT(stubs::DelElem), REJOIN_FALLTHROUGH); + frame.popn(2); + pushSyncedEntry(0); + } + END_CASE(JSOP_DELELEM) - BEGIN_CASE(JSOP_GETARGPROP) - /* Push arg onto stack. */ - frame.pushArg(GET_SLOTNO(PC)); - if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN])))) - return Compile_Error; - END_CASE(JSOP_GETARGPROP) + BEGIN_CASE(JSOP_TYPEOF) + BEGIN_CASE(JSOP_TYPEOFEXPR) + jsop_typeof(); + END_CASE(JSOP_TYPEOF) - BEGIN_CASE(JSOP_GETLOCALPROP) - frame.pushLocal(GET_SLOTNO(PC)); - if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN])))) - return Compile_Error; - END_CASE(JSOP_GETLOCALPROP) + BEGIN_CASE(JSOP_VOID) + frame.pop(); + frame.push(UndefinedValue()); + END_CASE(JSOP_VOID) BEGIN_CASE(JSOP_GETPROP) - if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)))) - return Compile_Error; - END_CASE(JSOP_GETPROP) - + BEGIN_CASE(JSOP_CALLPROP) BEGIN_CASE(JSOP_LENGTH) - if (!jsop_length()) + if (!jsop_getprop(script->getName(fullAtomIndex(PC)), knownPushedType(0))) return Compile_Error; - END_CASE(JSOP_LENGTH) + END_CASE(JSOP_GETPROP) BEGIN_CASE(JSOP_GETELEM) - if (!jsop_getelem(false)) + BEGIN_CASE(JSOP_CALLELEM) + if (script->pcCounters) + updateElemCounters(PC, frame.peek(-2), frame.peek(-1)); + if (!jsop_getelem()) return Compile_Error; END_CASE(JSOP_GETELEM) + BEGIN_CASE(JSOP_TOID) + jsop_toid(); + END_CASE(JSOP_TOID) + BEGIN_CASE(JSOP_SETELEM) { + if (script->pcCounters) + updateElemCounters(PC, frame.peek(-3), frame.peek(-2)); jsbytecode *next = &PC[JSOP_SETELEM_LENGTH]; bool pop = (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next)); if (!jsop_setelem(pop)) @@ -1379,14 +2573,6 @@ mjit::Compiler::generateMethod() } END_CASE(JSOP_SETELEM); - BEGIN_CASE(JSOP_CALLNAME) - prepareStubCall(Uses(0)); - masm.move(Imm32(fullAtomIndex(PC)), Registers::ArgReg1); - INLINE_STUBCALL(stubs::CallName); - frame.pushSynced(); - frame.pushSynced(); - END_CASE(JSOP_CALLNAME) - BEGIN_CASE(JSOP_EVAL) { JaegerSpew(JSpew_Insns, " --- EVAL --- \n"); @@ -1396,22 +2582,65 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_EVAL) BEGIN_CASE(JSOP_CALL) + BEGIN_CASE(JSOP_NEW) BEGIN_CASE(JSOP_FUNAPPLY) BEGIN_CASE(JSOP_FUNCALL) { - JaegerSpew(JSpew_Insns, " --- SCRIPTED CALL --- \n"); - inlineCallHelper(GET_ARGC(PC), false); - JaegerSpew(JSpew_Insns, " --- END SCRIPTED CALL --- \n"); + bool callingNew = (op == JSOP_NEW); + + bool done = false; + if ((op == JSOP_CALL || op == JSOP_NEW) && !monitored(PC)) { + CompileStatus status = inlineNativeFunction(GET_ARGC(PC), callingNew); + if (status == Compile_Okay) + done = true; + else if (status != Compile_InlineAbort) + return status; + } + if (!done && inlining()) { + CompileStatus status = inlineScriptedFunction(GET_ARGC(PC), callingNew); + if (status == Compile_Okay) + done = true; + else if (status != Compile_InlineAbort) + return status; + if (script->pcCounters) { + /* Code generated while inlining has been accounted for. */ + updatePCCounters(PC, &codeStart, &countersUpdated); + } + } + + FrameSize frameSize; + frameSize.initStatic(frame.totalDepth(), GET_ARGC(PC)); + + if (!done) { + JaegerSpew(JSpew_Insns, " --- SCRIPTED CALL --- \n"); + if (!inlineCallHelper(GET_ARGC(PC), callingNew, frameSize)) + return Compile_Error; + JaegerSpew(JSpew_Insns, " --- END SCRIPTED CALL --- \n"); + } } END_CASE(JSOP_CALL) BEGIN_CASE(JSOP_NAME) - jsop_name(script->getAtom(fullAtomIndex(PC))); + BEGIN_CASE(JSOP_CALLNAME) + { + PropertyName *name = script->getName(fullAtomIndex(PC)); + jsop_name(name, knownPushedType(0)); + frame.extra(frame.peek(-1)).name = name; + } END_CASE(JSOP_NAME) + BEGIN_CASE(JSOP_IMPLICITTHIS) + { + prepareStubCall(Uses(0)); + masm.move(ImmPtr(script->getName(fullAtomIndex(PC))), Registers::ArgReg1); + INLINE_STUBCALL(stubs::ImplicitThis, REJOIN_FALLTHROUGH); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + } + END_CASE(JSOP_IMPLICITTHIS) + BEGIN_CASE(JSOP_DOUBLE) { - uint32 index = fullAtomIndex(PC); + uint32_t index = fullAtomIndex(PC); double d = script->getConst(index).toDouble(); frame.push(Value(DoubleValue(d))); } @@ -1422,11 +2651,11 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_STRING) BEGIN_CASE(JSOP_ZERO) - frame.push(Valueify(JSVAL_ZERO)); + frame.push(JSVAL_ZERO); END_CASE(JSOP_ZERO) BEGIN_CASE(JSOP_ONE) - frame.push(Valueify(JSVAL_ONE)); + frame.push(JSVAL_ONE); END_CASE(JSOP_ONE) BEGIN_CASE(JSOP_NULL) @@ -1447,17 +2676,30 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_OR) BEGIN_CASE(JSOP_AND) - if (!jsop_andor(op, PC + GET_JUMP_OFFSET(PC))) + { + jsbytecode *target = PC + GET_JUMP_OFFSET(PC); + fixDoubleTypes(target); + if (!jsop_andor(op, target)) return Compile_Error; + } END_CASE(JSOP_AND) BEGIN_CASE(JSOP_TABLESWITCH) + /* + * Note: there is no need to syncForBranch for the various targets of + * switch statement. The liveness analysis has already marked these as + * allocated with no registers in use. There is also no need to fix + * double types, as we don't track types of slots in scripts with + * switch statements (could be fixed). + */ + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); #if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */ - frame.syncAndForgetEverything(); + frame.syncAndKillEverything(); masm.move(ImmPtr(PC), Registers::ArgReg1); /* prepareStubCall() is not needed due to syncAndForgetEverything() */ - INLINE_STUBCALL(stubs::TableSwitch); + INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); @@ -1470,11 +2712,13 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_TABLESWITCH) BEGIN_CASE(JSOP_LOOKUPSWITCH) + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); frame.syncAndForgetEverything(); masm.move(ImmPtr(PC), Registers::ArgReg1); /* prepareStubCall() is not needed due to syncAndForgetEverything() */ - INLINE_STUBCALL(stubs::LookupSwitch); + INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); @@ -1496,23 +2740,37 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_CASE) BEGIN_CASE(JSOP_STRICTEQ) - jsop_stricteq(op); - END_CASE(JSOP_STRICTEQ) - BEGIN_CASE(JSOP_STRICTNE) + if (script->pcCounters) { + updateArithCounters(PC, NULL, arithFirstUseType, arithSecondUseType); + arithUpdated = true; + } jsop_stricteq(op); - END_CASE(JSOP_STRICTNE) + END_CASE(JSOP_STRICTEQ) BEGIN_CASE(JSOP_ITER) - if (!iter(PC[1])) + if (!iter(GET_UINT8(PC))) return Compile_Error; END_CASE(JSOP_ITER) BEGIN_CASE(JSOP_MOREITER) + { /* At the byte level, this is always fused with IFNE or IFNEX. */ - if (!iterMore()) + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); + jsbytecode *target = &PC[JSOP_MOREITER_LENGTH]; + JSOp next = JSOp(*target); + JS_ASSERT(next == JSOP_IFNE); + + target += GET_JUMP_OFFSET(target); + + fixDoubleTypes(target); + if (!iterMore(target)) return Compile_Error; + PC += JSOP_MOREITER_LENGTH; + PC += js_CodeSpec[next].length; break; + } END_CASE(JSOP_MOREITER) BEGIN_CASE(JSOP_ENDITER) @@ -1523,20 +2781,15 @@ mjit::Compiler::generateMethod() frame.pop(); END_CASE(JSOP_POP) - BEGIN_CASE(JSOP_NEW) - { - JaegerSpew(JSpew_Insns, " --- NEW OPERATOR --- \n"); - inlineCallHelper(GET_ARGC(PC), true); - JaegerSpew(JSpew_Insns, " --- END NEW OPERATOR --- \n"); - } - END_CASE(JSOP_NEW) - BEGIN_CASE(JSOP_GETARG) BEGIN_CASE(JSOP_CALLARG) { - frame.pushArg(GET_SLOTNO(PC)); - if (op == JSOP_CALLARG) - frame.push(UndefinedValue()); + restoreVarType(); + uint32_t arg = GET_SLOTNO(PC); + if (JSObject *singleton = pushedSingleton(0)) + frame.push(ObjectValue(*singleton)); + else + frame.pushArg(arg); } END_CASE(JSOP_GETARG) @@ -1546,9 +2799,11 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_SETARG) { - jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH]; + jsbytecode *next = &PC[JSOP_SETARG_LENGTH]; bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); frame.storeArg(GET_SLOTNO(PC), pop); + updateVarType(); + if (pop) { frame.pop(); PC += JSOP_SETARG_LENGTH + JSOP_POP_LENGTH; @@ -1558,9 +2813,21 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_SETARG) BEGIN_CASE(JSOP_GETLOCAL) + BEGIN_CASE(JSOP_CALLLOCAL) { - uint32 slot = GET_SLOTNO(PC); - frame.pushLocal(slot); + /* + * Update the var type unless we are about to pop the variable. + * Sync is not guaranteed for types of dead locals, and GETLOCAL + * followed by POP is not regarded as a use of the variable. + */ + jsbytecode *next = &PC[JSOP_GETLOCAL_LENGTH]; + if (JSOp(*next) != JSOP_POP || analysis->jumpTarget(next)) + restoreVarType(); + uint32_t slot = GET_SLOTNO(PC); + if (JSObject *singleton = pushedSingleton(0)) + frame.push(ObjectValue(*singleton)); + else + frame.pushLocal(slot); } END_CASE(JSOP_GETLOCAL) @@ -1569,6 +2836,8 @@ mjit::Compiler::generateMethod() jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH]; bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); frame.storeLocal(GET_SLOTNO(PC), pop); + updateVarType(); + if (pop) { frame.pop(); PC += JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH; @@ -1578,8 +2847,12 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_SETLOCAL) BEGIN_CASE(JSOP_SETLOCALPOP) - frame.storeLocal(GET_SLOTNO(PC), true); + { + uint32_t slot = GET_SLOTNO(PC); + frame.storeLocal(slot, true); frame.pop(); + updateVarType(); + } END_CASE(JSOP_SETLOCALPOP) BEGIN_CASE(JSOP_UINT16) @@ -1587,15 +2860,18 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_UINT16) BEGIN_CASE(JSOP_NEWINIT) - jsop_newinit(); + if (!jsop_newinit()) + return Compile_Error; END_CASE(JSOP_NEWINIT) BEGIN_CASE(JSOP_NEWARRAY) - jsop_newinit(); + if (!jsop_newinit()) + return Compile_Error; END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWOBJECT) - jsop_newinit(); + if (!jsop_newinit()) + return Compile_Error; END_CASE(JSOP_NEWOBJECT) BEGIN_CASE(JSOP_ENDINIT) @@ -1620,82 +2896,78 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_DECARG) BEGIN_CASE(JSOP_ARGINC) BEGIN_CASE(JSOP_ARGDEC) - { - jsbytecode *next = &PC[JSOP_ARGINC_LENGTH]; - bool popped = false; - if (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next)) - popped = true; - jsop_arginc(op, GET_SLOTNO(PC), popped); - PC += JSOP_ARGINC_LENGTH; - if (popped) - PC += JSOP_POP_LENGTH; - break; - } + if (script->pcCounters) { + restoreVarType(); + FrameEntry *fe = frame.getArg(GET_SLOTNO(PC)); + if (fe->isTypeKnown()) + arithFirstUseType = fe->getKnownType(); + } + + if (!jsop_arginc(op, GET_SLOTNO(PC))) + return Compile_Retry; + + if (script->pcCounters) { + FrameEntry *fe = frame.getArg(GET_SLOTNO(PC)); + updateArithCounters(PC, fe, arithFirstUseType, JSVAL_TYPE_INT32); + arithUpdated = true; + } END_CASE(JSOP_ARGDEC) BEGIN_CASE(JSOP_INCLOCAL) BEGIN_CASE(JSOP_DECLOCAL) BEGIN_CASE(JSOP_LOCALINC) BEGIN_CASE(JSOP_LOCALDEC) - { - jsbytecode *next = &PC[JSOP_LOCALINC_LENGTH]; - bool popped = false; - if (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next)) - popped = true; - /* These manually advance the PC. */ - jsop_localinc(op, GET_SLOTNO(PC), popped); - PC += JSOP_LOCALINC_LENGTH; - if (popped) - PC += JSOP_POP_LENGTH; - break; - } - END_CASE(JSOP_LOCALDEC) - - BEGIN_CASE(JSOP_FORNAME) - jsop_forname(script->getAtom(fullAtomIndex(PC))); - END_CASE(JSOP_FORNAME) - - BEGIN_CASE(JSOP_FORGNAME) - jsop_forgname(script->getAtom(fullAtomIndex(PC))); - END_CASE(JSOP_FORGNAME) + if (script->pcCounters) { + restoreVarType(); + FrameEntry *fe = frame.getLocal(GET_SLOTNO(PC)); + if (fe->isTypeKnown()) + arithFirstUseType = fe->getKnownType(); + } - BEGIN_CASE(JSOP_FORPROP) - jsop_forprop(script->getAtom(fullAtomIndex(PC))); - END_CASE(JSOP_FORPROP) + if (!jsop_localinc(op, GET_SLOTNO(PC))) + return Compile_Retry; - BEGIN_CASE(JSOP_FORELEM) - // This opcode is for the decompiler; it is succeeded by an - // ENUMELEM, which performs the actual array store. - iterNext(); - END_CASE(JSOP_FORELEM) + if (script->pcCounters) { + FrameEntry *fe = frame.getLocal(GET_SLOTNO(PC)); + updateArithCounters(PC, fe, arithFirstUseType, JSVAL_TYPE_INT32); + arithUpdated = true; + } + END_CASE(JSOP_LOCALDEC) BEGIN_CASE(JSOP_BINDNAME) - jsop_bindname(script->getAtom(fullAtomIndex(PC)), true); + jsop_bindname(script->getName(fullAtomIndex(PC))); END_CASE(JSOP_BINDNAME) BEGIN_CASE(JSOP_SETPROP) - if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true)) + { + jsbytecode *next = &PC[JSOP_SETPROP_LENGTH]; + bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); + if (!jsop_setprop(script->getName(fullAtomIndex(PC)), pop)) return Compile_Error; + } END_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_SETMETHOD) - if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true)) + { + jsbytecode *next = &PC[JSOP_SETNAME_LENGTH]; + bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); + if (!jsop_setprop(script->getName(fullAtomIndex(PC)), pop)) return Compile_Error; + } END_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_THROW) prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::Throw); + INLINE_STUBCALL(stubs::Throw, REJOIN_NONE); frame.pop(); + fallthrough = false; END_CASE(JSOP_THROW) BEGIN_CASE(JSOP_IN) - prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::In); - frame.popn(2); - frame.takeReg(Registers::ReturnReg); - frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg); + { + jsop_in(); + } END_CASE(JSOP_IN) BEGIN_CASE(JSOP_INSTANCEOF) @@ -1704,9 +2976,11 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_INSTANCEOF) BEGIN_CASE(JSOP_EXCEPTION) + { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::Exception); - frame.pushSynced(); + INLINE_STUBCALL(stubs::Exception, REJOIN_FALLTHROUGH); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + } END_CASE(JSOP_EXCEPTION) BEGIN_CASE(JSOP_LINENO) @@ -1736,105 +3010,95 @@ mjit::Compiler::generateMethod() frame.popn(2); END_CASE(JSOP_ENUMELEM) - BEGIN_CASE(JSOP_BLOCKCHAIN) - END_CASE(JSOP_BLOCKCHAIN) - - BEGIN_CASE(JSOP_NULLBLOCKCHAIN) - END_CASE(JSOP_NULLBLOCKCHAIN) - BEGIN_CASE(JSOP_CONDSWITCH) /* No-op for the decompiler. */ END_CASE(JSOP_CONDSWITCH) + BEGIN_CASE(JSOP_LABEL) + END_CASE(JSOP_LABEL) + BEGIN_CASE(JSOP_DEFFUN) { - uint32 index = fullAtomIndex(PC); - JSFunction *innerFun = script->getFunction(index); - - if (fun && script->bindings.hasBinding(cx, innerFun->atom)) - frame.syncAndForgetEverything(); + JSFunction *innerFun = script->getFunction(GET_UINT32_INDEX(PC)); prepareStubCall(Uses(0)); masm.move(ImmPtr(innerFun), Registers::ArgReg1); - INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun)); + INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun), REJOIN_FALLTHROUGH); } END_CASE(JSOP_DEFFUN) BEGIN_CASE(JSOP_DEFVAR) BEGIN_CASE(JSOP_DEFCONST) { - uint32 index = fullAtomIndex(PC); - JSAtom *atom = script->getAtom(index); + uint32_t index = fullAtomIndex(PC); + PropertyName *name = script->getName(index); prepareStubCall(Uses(0)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::DefVarOrConst); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(stubs::DefVarOrConst, REJOIN_FALLTHROUGH); } END_CASE(JSOP_DEFVAR) BEGIN_CASE(JSOP_SETCONST) { - uint32 index = fullAtomIndex(PC); - JSAtom *atom = script->getAtom(index); - - if (fun && script->bindings.hasBinding(cx, atom)) - frame.syncAndForgetEverything(); + uint32_t index = fullAtomIndex(PC); + PropertyName *name = script->getName(index); prepareStubCall(Uses(1)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::SetConst); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(stubs::SetConst, REJOIN_FALLTHROUGH); } END_CASE(JSOP_SETCONST) BEGIN_CASE(JSOP_DEFLOCALFUN_FC) { - uint32 slot = GET_SLOTNO(PC); - JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN])); + uint32_t slot = GET_SLOTNO(PC); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC + SLOTNO_LEN)); prepareStubCall(Uses(frame.frameSlots())); masm.move(ImmPtr(fun), Registers::ArgReg1); - INLINE_STUBCALL(stubs::DefLocalFun_FC); + INLINE_STUBCALL(stubs::DefLocalFun_FC, REJOIN_DEFLOCALFUN); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); frame.storeLocal(slot, true); frame.pop(); + updateVarType(); } END_CASE(JSOP_DEFLOCALFUN_FC) BEGIN_CASE(JSOP_LAMBDA) { - JSFunction *fun = script->getFunction(fullAtomIndex(PC)); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC)); JSObjStubFun stub = stubs::Lambda; - uint32 uses = 0; - - jsbytecode *pc2 = AdvanceOverBlockchainOp(PC + JSOP_LAMBDA_LENGTH); - JSOp next = JSOp(*pc2); - - if (next == JSOP_INITMETHOD) { - stub = stubs::LambdaForInit; - } else if (next == JSOP_SETMETHOD) { - stub = stubs::LambdaForSet; - uses = 1; - } else if (fun->joinable()) { - if (next == JSOP_CALL) { - stub = stubs::LambdaJoinableForCall; - uses = frame.frameSlots(); + uint32_t uses = 0; + + jsbytecode *pc2 = NULL; + if (fun->joinable()) { + pc2 = PC + JSOP_LAMBDA_LENGTH; + JSOp next = JSOp(*pc2); + + if (next == JSOP_INITMETHOD) { + stub = stubs::LambdaJoinableForInit; + } else if (next == JSOP_SETMETHOD) { + stub = stubs::LambdaJoinableForSet; + uses = 1; + } else if (next == JSOP_CALL) { + int iargc = GET_ARGC(pc2); + if (iargc == 1 || iargc == 2) { + stub = stubs::LambdaJoinableForCall; + uses = frame.frameSlots(); + } } else if (next == JSOP_NULL) { - stub = stubs::LambdaJoinableForNull; + pc2 += JSOP_NULL_LENGTH; + if (JSOp(*pc2) == JSOP_CALL && GET_ARGC(pc2) == 0) + stub = stubs::LambdaJoinableForNull; } } prepareStubCall(Uses(uses)); masm.move(ImmPtr(fun), Registers::ArgReg1); - if (stub == stubs::Lambda) { - INLINE_STUBCALL(stub); - } else { - jsbytecode *savedPC = PC; - PC = pc2; - INLINE_STUBCALL(stub); - PC = savedPC; - } + INLINE_STUBCALL(stub, REJOIN_PUSH_OBJECT); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); @@ -1856,116 +3120,93 @@ mjit::Compiler::generateMethod() frame.pop(); // obj->getFlatClosureUpvars() - masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg); - Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value)); + Address upvarAddress(reg, JSFunction::getFlatClosureUpvarsOffset()); masm.loadPrivate(upvarAddress, reg); // push ((Value *) reg)[index] - frame.freeReg(reg); - frame.push(Address(reg, index * sizeof(Value))); - if (op == JSOP_CALLFCSLOT) - frame.push(UndefinedValue()); + + BarrierState barrier = pushAddressMaybeBarrier(Address(reg, index * sizeof(Value)), + knownPushedType(0), true); + finishBarrier(barrier, REJOIN_GETTER, 0); } END_CASE(JSOP_CALLFCSLOT) - BEGIN_CASE(JSOP_ARGSUB) - prepareStubCall(Uses(0)); - masm.move(Imm32(GET_ARGNO(PC)), Registers::ArgReg1); - INLINE_STUBCALL(stubs::ArgSub); - frame.pushSynced(); - END_CASE(JSOP_ARGSUB) - - BEGIN_CASE(JSOP_ARGCNT) - prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::ArgCnt); - frame.pushSynced(); - END_CASE(JSOP_ARGCNT) - BEGIN_CASE(JSOP_DEFLOCALFUN) { - uint32 slot = GET_SLOTNO(PC); - JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN])); + uint32_t slot = GET_SLOTNO(PC); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC + SLOTNO_LEN)); prepareStubCall(Uses(0)); masm.move(ImmPtr(fun), Registers::ArgReg1); - INLINE_STUBCALL(stubs::DefLocalFun); + INLINE_STUBCALL(stubs::DefLocalFun, REJOIN_DEFLOCALFUN); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); frame.storeLocal(slot, true); frame.pop(); + updateVarType(); } END_CASE(JSOP_DEFLOCALFUN) BEGIN_CASE(JSOP_RETRVAL) emitReturn(NULL); + fallthrough = false; END_CASE(JSOP_RETRVAL) BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) - jsop_getgname(fullAtomIndex(PC)); - if (op == JSOP_CALLGNAME) - jsop_callgname_epilogue(); + { + uint32_t index = fullAtomIndex(PC); + jsop_getgname(index); + frame.extra(frame.peek(-1)).name = script->getName(index); + } END_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_SETGNAME) - jsop_setgname(script->getAtom(fullAtomIndex(PC)), true); + { + jsbytecode *next = &PC[JSOP_SETGNAME_LENGTH]; + bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); + jsop_setgname(script->getName(fullAtomIndex(PC)), pop); + } END_CASE(JSOP_SETGNAME) BEGIN_CASE(JSOP_REGEXP) - { - JSObject *regex = script->getRegExp(fullAtomIndex(PC)); - prepareStubCall(Uses(0)); - masm.move(ImmPtr(regex), Registers::ArgReg1); - INLINE_STUBCALL(stubs::RegExp); - frame.takeReg(Registers::ReturnReg); - frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); - } + if (!jsop_regexp()) + return Compile_Error; END_CASE(JSOP_REGEXP) BEGIN_CASE(JSOP_OBJECT) { - JSObject *object = script->getObject(fullAtomIndex(PC)); + JSObject *object = script->getObject(GET_UINT32_INDEX(PC)); RegisterID reg = frame.allocReg(); masm.move(ImmPtr(object), reg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); } END_CASE(JSOP_OBJECT) - BEGIN_CASE(JSOP_CALLPROP) - if (!jsop_callprop(script->getAtom(fullAtomIndex(PC)))) - return Compile_Error; - END_CASE(JSOP_CALLPROP) - BEGIN_CASE(JSOP_UINT24) frame.push(Value(Int32Value((int32_t) GET_UINT24(PC)))); END_CASE(JSOP_UINT24) - BEGIN_CASE(JSOP_CALLELEM) - jsop_getelem(true); - END_CASE(JSOP_CALLELEM) - BEGIN_CASE(JSOP_STOP) - /* Safe point! */ + if (script->pcCounters) + updatePCCounters(PC, &codeStart, &countersUpdated); emitReturn(NULL); goto done; END_CASE(JSOP_STOP) BEGIN_CASE(JSOP_GETXPROP) - if (!jsop_xname(script->getAtom(fullAtomIndex(PC)))) + if (!jsop_xname(script->getName(fullAtomIndex(PC)))) return Compile_Error; END_CASE(JSOP_GETXPROP) BEGIN_CASE(JSOP_ENTERBLOCK) - enterBlock(script->getObject(fullAtomIndex(PC))); + BEGIN_CASE(JSOP_ENTERLET0) + BEGIN_CASE(JSOP_ENTERLET1) + enterBlock(&script->getObject(GET_UINT32_INDEX(PC))->asStaticBlock()); END_CASE(JSOP_ENTERBLOCK); BEGIN_CASE(JSOP_LEAVEBLOCK) leaveBlock(); END_CASE(JSOP_LEAVEBLOCK) - BEGIN_CASE(JSOP_CALLLOCAL) - frame.pushLocal(GET_SLOTNO(PC)); - frame.push(UndefinedValue()); - END_CASE(JSOP_CALLLOCAL) - BEGIN_CASE(JSOP_INT8) frame.push(Value(Int32Value(GET_INT8(PC)))); END_CASE(JSOP_INT8) @@ -1980,46 +3221,35 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_LAMBDA_FC) { - JSFunction *fun = script->getFunction(fullAtomIndex(PC)); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC)); prepareStubCall(Uses(frame.frameSlots())); masm.move(ImmPtr(fun), Registers::ArgReg1); - INLINE_STUBCALL(stubs::FlatLambda); + INLINE_STUBCALL(stubs::FlatLambda, REJOIN_PUSH_OBJECT); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); } END_CASE(JSOP_LAMBDA_FC) - BEGIN_CASE(JSOP_TRACE) - BEGIN_CASE(JSOP_NOTRACE) + BEGIN_CASE(JSOP_LOOPHEAD) { - if (analysis->jumpTarget(PC)) + if (analysis->jumpTarget(PC)) { interruptCheckHelper(); + recompileCheckHelper(); + } } - END_CASE(JSOP_TRACE) + END_CASE(JSOP_LOOPHEAD) + + BEGIN_CASE(JSOP_LOOPENTRY) + END_CASE(JSOP_LOOPENTRY) BEGIN_CASE(JSOP_DEBUGGER) + { prepareStubCall(Uses(0)); masm.move(ImmPtr(PC), Registers::ArgReg1); - INLINE_STUBCALL(stubs::Debugger); + INLINE_STUBCALL(stubs::DebuggerStatement, REJOIN_FALLTHROUGH); + } END_CASE(JSOP_DEBUGGER) - BEGIN_CASE(JSOP_UNBRAND) - jsop_unbrand(); - END_CASE(JSOP_UNBRAND) - - BEGIN_CASE(JSOP_UNBRANDTHIS) - jsop_this(); - jsop_unbrand(); - frame.pop(); - END_CASE(JSOP_UNBRANDTHIS) - - BEGIN_CASE(JSOP_GETGLOBAL) - BEGIN_CASE(JSOP_CALLGLOBAL) - jsop_getglobal(GET_SLOTNO(PC)); - if (op == JSOP_CALLGLOBAL) - frame.push(UndefinedValue()); - END_CASE(JSOP_GETGLOBAL) - default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -2031,11 +3261,66 @@ mjit::Compiler::generateMethod() /********************** * END COMPILER OPS * - **********************/ + **********************/ + + if (cx->typeInferenceEnabled() && PC == lastPC + GetBytecodeLength(lastPC)) { + /* + * Inform the frame of the type sets for values just pushed. Skip + * this if we did any opcode fusions, we don't keep track of the + * associated type sets in such cases. + */ + unsigned nuses = GetUseCount(script, lastPC - script->code); + unsigned ndefs = GetDefCount(script, lastPC - script->code); + for (unsigned i = 0; i < ndefs; i++) { + FrameEntry *fe = frame.getStack(opinfo->stackDepth - nuses + i); + if (fe) { + /* fe may be NULL for conditionally pushed entries, e.g. JSOP_AND */ + frame.extra(fe).types = analysis->pushedTypes(lastPC - script->code, i); + } + } + } + + if (script->pcCounters) { + size_t length = masm.size() - masm.distanceOf(codeStart); + bool typesUpdated = false; + + /* Update information about the type of value pushed by arithmetic ops. */ + if ((js_CodeSpec[op].format & JOF_ARITH) && !arithUpdated) { + FrameEntry *pushed = NULL; + if (PC == lastPC + GetBytecodeLength(lastPC)) + pushed = frame.peek(-1); + updateArithCounters(lastPC, pushed, arithFirstUseType, arithSecondUseType); + typesUpdated = true; + } + + /* Update information about the result type of access operations. */ + if (OpcodeCounts::accessOp(op) && + op != JSOP_SETPROP && op != JSOP_SETMETHOD && op != JSOP_SETELEM) { + FrameEntry *fe = (GetDefCount(script, lastPC - script->code) == 1) + ? frame.peek(-1) + : frame.peek(-2); + updatePCTypes(lastPC, fe); + typesUpdated = true; + } + + if (countersUpdated || typesUpdated || length != 0) { + if (!countersUpdated) + updatePCCounters(lastPC, &codeStart, &countersUpdated); + + if (pcLengths) { + /* Fill in the amount of inline code generated for the op. */ + uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code; + pcLengths[offset].codeLength += length; + } + } + } else if (pcLengths) { + /* Fill in the amount of inline code generated for the op. */ + size_t length = masm.size() - masm.distanceOf(codeStart); + uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code; + pcLengths[offset].codeLength += length; + } -#ifdef DEBUG frame.assertValidRegisterState(); -#endif } done: @@ -2045,22 +3330,271 @@ mjit::Compiler::generateMethod() #undef END_CASE #undef BEGIN_CASE +void +mjit::Compiler::updatePCCounters(jsbytecode *pc, Label *start, bool *updated) +{ + JS_ASSERT(script->pcCounters); + + /* + * Bump the METHODJIT count for the opcode, read the METHODJIT_CODE_LENGTH + * and METHODJIT_PICS_LENGTH counts, indicating the amounts of inline path + * code and generated code, respectively, and add them to the accumulated + * total for the op. + */ + uint32_t offset = ssa.frameLength(a->inlineIndex) + pc - script->code; + + /* + * Base register for addresses, we can't use AbsoluteAddress in all places. + * This may hold a live value, so write it out to the top of the stack + * first. This cannot overflow the stack, as space is always reserved for + * an extra callee frame. + */ + RegisterID reg = Registers::ReturnReg; + masm.storePtr(reg, frame.addressOfTop()); + + OpcodeCounts counts = script->getCounts(pc); + + double *code = &counts.get(OpcodeCounts::BASE_METHODJIT_CODE); + double *codeLength = &pcLengths[offset].codeLength; + masm.addCounter(codeLength, code, reg); + + double *pics = &counts.get(OpcodeCounts::BASE_METHODJIT_PICS); + double *picsLength = &pcLengths[offset].picsLength; + masm.addCounter(picsLength, pics, reg); + + double *counter = &counts.get(OpcodeCounts::BASE_METHODJIT); + masm.bumpCounter(counter, reg); + + /* Reload the base register's original value. */ + masm.loadPtr(frame.addressOfTop(), reg); + + /* The start label should reflect the code for the op, not instrumentation. */ + *start = masm.label(); + *updated = true; +} + +static inline bool +HasPayloadType(types::TypeSet *types) +{ + if (types->unknown()) + return false; + + types::TypeFlags flags = types->baseFlags(); + bool objects = !!(flags & types::TYPE_FLAG_ANYOBJECT) || !!types->getObjectCount(); + + if (objects && !!(flags & types::TYPE_FLAG_STRING)) + return false; + + flags = flags & ~(types::TYPE_FLAG_ANYOBJECT | types::TYPE_FLAG_STRING); + + return (flags == types::TYPE_FLAG_UNDEFINED) + || (flags == types::TYPE_FLAG_NULL) + || (flags == types::TYPE_FLAG_BOOLEAN); +} + +void +mjit::Compiler::updatePCTypes(jsbytecode *pc, FrameEntry *fe) +{ + JS_ASSERT(script->pcCounters); + + /* + * Get a temporary register, as for updatePCCounters. Don't overlap with + * the backing store for the entry's type tag, if there is one. + */ + RegisterID reg = Registers::ReturnReg; + if (frame.peekTypeInRegister(fe) && reg == frame.tempRegForType(fe)) { + JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1); + reg = Registers::ArgReg1; + } + masm.push(reg); + + OpcodeCounts counts = script->getCounts(pc); + + /* Update the counters for pushed type tags and possible access types. */ + if (fe->isTypeKnown()) { + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_MONOMORPHIC), reg); + OpcodeCounts::AccessCounts counter = OpcodeCounts::ACCESS_OBJECT; + switch (fe->getKnownType()) { + case JSVAL_TYPE_UNDEFINED: counter = OpcodeCounts::ACCESS_UNDEFINED; break; + case JSVAL_TYPE_NULL: counter = OpcodeCounts::ACCESS_NULL; break; + case JSVAL_TYPE_BOOLEAN: counter = OpcodeCounts::ACCESS_BOOLEAN; break; + case JSVAL_TYPE_INT32: counter = OpcodeCounts::ACCESS_INT32; break; + case JSVAL_TYPE_DOUBLE: counter = OpcodeCounts::ACCESS_DOUBLE; break; + case JSVAL_TYPE_STRING: counter = OpcodeCounts::ACCESS_STRING; break; + case JSVAL_TYPE_OBJECT: counter = OpcodeCounts::ACCESS_OBJECT; break; + default:; + } + if (counter) + masm.bumpCounter(&counts.get(counter), reg); + } else { + types::TypeSet *types = frame.extra(fe).types; + if (types && HasPayloadType(types)) + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_DIMORPHIC), reg); + else + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_POLYMORPHIC), reg); + + frame.loadTypeIntoReg(fe, reg); + + Jump j = masm.testUndefined(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_UNDEFINED), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testNull(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_NULL), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testBoolean(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_BOOLEAN), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testInt32(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_INT32), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testDouble(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_DOUBLE), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testString(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_STRING), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + + j = masm.testObject(Assembler::NotEqual, reg); + masm.bumpCounter(&counts.get(OpcodeCounts::ACCESS_OBJECT), reg); + frame.loadTypeIntoReg(fe, reg); + j.linkTo(masm.label(), &masm); + } + + /* Update the counter for accesses with type barriers. */ + if (js_CodeSpec[*pc].format & JOF_TYPESET) { + double *counter = &counts.get(hasTypeBarriers(pc) + ? OpcodeCounts::ACCESS_BARRIER + : OpcodeCounts::ACCESS_NOBARRIER); + masm.bumpCounter(counter, reg); + } + + /* Reload the base register's original value. */ + masm.pop(reg); +} + +void +mjit::Compiler::updateArithCounters(jsbytecode *pc, FrameEntry *fe, + JSValueType firstUseType, JSValueType secondUseType) +{ + JS_ASSERT(script->pcCounters); + + RegisterID reg = Registers::ReturnReg; + masm.push(reg); + + /* + * What counter we bump for arithmetic expressions depend on the + * known types of its operands. + * + * ARITH_INT: operands are known ints, result is int + * ARITH_OVERFLOW: operands are known ints, result is double + * ARITH_DOUBLE: either operand is a known double, result is double + * ARITH_OTHER: operands are monomorphic but not int or double + * ARITH_UNKNOWN: operands are polymorphic + */ + + OpcodeCounts::ArithCounts counter; + if (firstUseType == JSVAL_TYPE_INT32 && secondUseType == JSVAL_TYPE_INT32 && + (!fe || fe->isNotType(JSVAL_TYPE_DOUBLE))) { + counter = OpcodeCounts::ARITH_INT; + } else if (firstUseType == JSVAL_TYPE_INT32 || firstUseType == JSVAL_TYPE_DOUBLE || + secondUseType == JSVAL_TYPE_INT32 || secondUseType == JSVAL_TYPE_DOUBLE) { + counter = OpcodeCounts::ARITH_DOUBLE; + } else if (firstUseType != JSVAL_TYPE_UNKNOWN && secondUseType != JSVAL_TYPE_UNKNOWN && + (!fe || fe->isTypeKnown())) { + counter = OpcodeCounts::ARITH_OTHER; + } else { + counter = OpcodeCounts::ARITH_UNKNOWN; + } + + masm.bumpCounter(&script->getCounts(pc).get(counter), reg); + masm.pop(reg); +} + +void +mjit::Compiler::updateElemCounters(jsbytecode *pc, FrameEntry *obj, FrameEntry *id) +{ + JS_ASSERT(script->pcCounters); + + RegisterID reg = Registers::ReturnReg; + masm.push(reg); + + OpcodeCounts counts = script->getCounts(pc); + + OpcodeCounts::ElementCounts counter; + if (id->isTypeKnown()) { + switch (id->getKnownType()) { + case JSVAL_TYPE_INT32: counter = OpcodeCounts::ELEM_ID_INT; break; + case JSVAL_TYPE_DOUBLE: counter = OpcodeCounts::ELEM_ID_DOUBLE; break; + default: counter = OpcodeCounts::ELEM_ID_OTHER; break; + } + } else { + counter = OpcodeCounts::ELEM_ID_UNKNOWN; + } + masm.bumpCounter(&counts.get(counter), reg); + + if (obj->mightBeType(JSVAL_TYPE_OBJECT)) { + types::TypeSet *types = frame.extra(obj).types; + if (types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY) && + types->getTypedArrayType(cx) != TypedArray::TYPE_MAX) { + counter = OpcodeCounts::ELEM_OBJECT_TYPED; + } else if (types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) { + if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY)) + counter = OpcodeCounts::ELEM_OBJECT_PACKED; + else + counter = OpcodeCounts::ELEM_OBJECT_DENSE; + } else { + counter = OpcodeCounts::ELEM_OBJECT_OTHER; + } + masm.bumpCounter(&counts.get(counter), reg); + } else { + masm.bumpCounter(&counts.get(OpcodeCounts::ELEM_OBJECT_OTHER), reg); + } + + masm.pop(reg); +} + +void +mjit::Compiler::bumpPropCounter(jsbytecode *pc, int counter) +{ + /* Don't accumulate counts for property ops fused with other ops. */ + if (!(js_CodeSpec[*pc].format & JOF_PROP)) + return; + RegisterID reg = Registers::ReturnReg; + masm.push(reg); + masm.bumpCounter(&script->getCounts(pc).get(counter), reg); + masm.pop(reg); +} + JSC::MacroAssembler::Label -mjit::Compiler::labelOf(jsbytecode *pc) +mjit::Compiler::labelOf(jsbytecode *pc, uint32_t inlineIndex) { - uint32 offs = uint32(pc - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - return jumpMap[offs]; + ActiveFrame *a = (inlineIndex == UINT32_MAX) ? outer : inlineFrames[inlineIndex]; + JS_ASSERT(uint32_t(pc - a->script->code) < a->script->length); + + uint32_t offs = uint32_t(pc - a->script->code); + JS_ASSERT(a->jumpMap[offs].isSet()); + return a->jumpMap[offs]; } -uint32 +uint32_t mjit::Compiler::fullAtomIndex(jsbytecode *pc) { return GET_SLOTNO(pc); /* If we ever enable INDEXBASE garbage, use this below. */ #if 0 - return GET_SLOTNO(pc) + (atoms - script->atomMap.vector); + return GET_SLOTNO(pc) + (atoms - script->atoms); #endif } @@ -2070,51 +3604,16 @@ mjit::Compiler::knownJump(jsbytecode *pc) return pc < PC; } -void * -mjit::Compiler::findCallSite(const CallSite &callSite) -{ - JS_ASSERT(callSite.pcOffset < script->length); - - JITScript *jit = script->getJIT(fp->isConstructing()); - uint8* ilPath = (uint8 *)jit->code.m_code.executableAddress(); - uint8* oolPath = ilPath + masm.size(); - - for (uint32 i = 0; i < callSites.length(); i++) { - InternalCallSite &cs = callSites[i]; - if (cs.pc == script->code + callSite.pcOffset && cs.id == callSite.id) { - if (cs.ool) - return oolPath + cs.returnOffset; - return ilPath + cs.returnOffset; - } - } - - /* We have no idea where to patch up to. */ - JS_NOT_REACHED("Call site vanished."); - return NULL; -} - bool mjit::Compiler::jumpInScript(Jump j, jsbytecode *pc) { - JS_ASSERT(pc >= script->code && uint32(pc - script->code) < script->length); + JS_ASSERT(pc >= script->code && uint32_t(pc - script->code) < script->length); if (pc < PC) { - j.linkTo(jumpMap[uint32(pc - script->code)], &masm); + j.linkTo(a->jumpMap[uint32_t(pc - script->code)], &masm); return true; } - return branchPatches.append(BranchPatch(j, pc)); -} - -void -mjit::Compiler::jsop_getglobal(uint32 index) -{ - JS_ASSERT(globalObj); - uint32 slot = script->getGlobalSlot(index); - - RegisterID reg = frame.allocReg(); - Address address = masm.objSlotRef(globalObj, reg, slot); - frame.freeReg(reg); - frame.push(address); + return branchPatches.append(BranchPatch(j, pc, a->inlineIndex)); } void @@ -2150,7 +3649,7 @@ mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe) stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); } else { Address rval(frame.addressOf(fe)); - if (fe->isTypeKnown()) { + if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE)) { stubcc.masm.loadPayload(rval, dataReg); stubcc.masm.move(ImmType(fe->getKnownType()), typeReg); } else { @@ -2185,7 +3684,7 @@ mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) JS_ASSERT(isConstructing); bool ool = (masm != &this->masm); - Address thisv(JSFrameReg, StackFrame::offsetOfThis(fun)); + Address thisv(JSFrameReg, StackFrame::offsetOfThis(script->function())); // We can just load |thisv| if either of the following is true: // (1) There is no explicit return value, AND fp->rval is not used. @@ -2207,7 +3706,9 @@ mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) } // There's a return value, and its type is unknown. Test the type and load - // |thisv| if necessary. + // |thisv| if necessary. Sync the 'this' entry before doing so, as it may + // be stored in registers if we constructed it inline. + frame.syncThis(); loadReturnValue(masm, fe); Jump j = masm->testObject(Assembler::Equal, JSReturnReg_Type); masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); @@ -2226,34 +3727,139 @@ mjit::Compiler::emitReturnValue(Assembler *masm, FrameEntry *fe) loadReturnValue(masm, fe); } +void +mjit::Compiler::emitInlineReturnValue(FrameEntry *fe) +{ + JS_ASSERT(!isConstructing && a->needReturnValue); + + if (a->syncReturnValue) { + /* Needed return value with unknown type, the caller's entry is synced. */ + Address address = frame.addressForInlineReturn(); + if (fe) + frame.storeTo(fe, address); + else + masm.storeValue(UndefinedValue(), address); + return; + } + + /* + * For inlined functions that simply return an entry present in the outer + * script (e.g. a loop invariant term), mark the copy and propagate it + * after popping the frame. + */ + if (!a->exitState && fe && fe->isCopy() && frame.isOuterSlot(fe->backing())) { + a->returnEntry = fe->backing(); + return; + } + + if (a->returnValueDouble) { + JS_ASSERT(fe); + frame.ensureDouble(fe); + Registers mask(a->returnSet + ? Registers::maskReg(a->returnRegister) + : Registers::AvailFPRegs); + FPRegisterID fpreg; + if (!fe->isConstant()) { + fpreg = frame.tempRegInMaskForData(fe, mask.freeMask).fpreg(); + frame.syncAndForgetFe(fe, true); + frame.takeReg(fpreg); + } else { + fpreg = frame.allocReg(mask.freeMask).fpreg(); + masm.slowLoadConstantDouble(fe->getValue().toDouble(), fpreg); + } + JS_ASSERT_IF(a->returnSet, fpreg == a->returnRegister.fpreg()); + a->returnRegister = fpreg; + } else { + Registers mask(a->returnSet + ? Registers::maskReg(a->returnRegister) + : Registers::AvailRegs); + RegisterID reg; + if (fe && !fe->isConstant()) { + reg = frame.tempRegInMaskForData(fe, mask.freeMask).reg(); + frame.syncAndForgetFe(fe, true); + frame.takeReg(reg); + } else { + reg = frame.allocReg(mask.freeMask).reg(); + Value val = fe ? fe->getValue() : UndefinedValue(); + masm.loadValuePayload(val, reg); + } + JS_ASSERT_IF(a->returnSet, reg == a->returnRegister.reg()); + a->returnRegister = reg; + } + + a->returnSet = true; + if (a->exitState) + a->exitState->setUnassigned(a->returnRegister); +} + void mjit::Compiler::emitReturn(FrameEntry *fe) { - JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP); + JS_ASSERT_IF(!script->function(), JSOp(*PC) == JSOP_STOP); /* Only the top of the stack can be returned. */ JS_ASSERT_IF(fe, fe == frame.peek(-1)); if (debugMode() || Probes::callTrackingActive(cx)) { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::ScriptDebugEpilogue); + INLINE_STUBCALL(stubs::ScriptDebugEpilogue, REJOIN_RESUME); + } + + if (a != outer) { + /* + * Returning from an inlined script. The checks we do for inlineability + * and recompilation triggered by args object construction ensure that + * there can't be an arguments or call object. + */ + + if (a->needReturnValue) + emitInlineReturnValue(fe); + + if (a->exitState) { + /* + * Restore the register state to reflect that at the original call, + * modulo entries which will be popped once the call finishes and any + * entry which will be clobbered by the return value register. + */ + frame.syncForAllocation(a->exitState, true, Uses(0)); + } + + /* + * Simple tests to see if we are at the end of the script and will + * fallthrough after the script body finishes, thus won't need to jump. + */ + bool endOfScript = + (JSOp(*PC) == JSOP_STOP) || + (JSOp(*PC) == JSOP_RETURN && + (JSOp(PC[JSOP_RETURN_LENGTH]) == JSOP_STOP && + !analysis->maybeCode(PC + JSOP_RETURN_LENGTH))); + if (!endOfScript) + a->returnJumps->append(masm.jump()); + + if (a->returnSet) + frame.freeReg(a->returnRegister); + return; } /* - * Outside the mjit, activation objects are put by StackSpace::pop* - * members. For JSOP_RETURN, the interpreter only calls popInlineFrame if - * fp != entryFrame since the VM protocol is that Invoke/Execute are - * responsible for pushing/popping the initial frame. The mjit does not - * perform this branch (by instead using a trampoline at the return address - * to handle exiting mjit code) and thus always puts activation objects, - * even on the entry frame. To avoid double-putting, EnterMethodJIT clears - * out the entry frame's activation objects. + * Outside the mjit, activation objects (call objects and arguments objects) are put + * by ContextStack::pop* members. For JSOP_RETURN, the interpreter only calls + * popInlineFrame if fp != entryFrame since the VM protocol is that Invoke/Execute are + * responsible for pushing/popping the initial frame. However, an mjit function + * epilogue doesn't treat the initial StackFrame of its VMFrame specially: it always + * puts activation objects. And furthermore, if the last mjit frame throws, the mjit + * does *not* put the activation objects. So we can't assume any particular state of + * puttedness upon exit from the mjit. + * + * To avoid double-putting, EnterMethodJIT calls updateEpilogueFlags to clear the + * entry frame's hasArgsObj() and hasCallObj() flags if the given objects have already + * been put. */ - if (fun) { - if (fun->isHeavyweight()) { - /* There will always be a call object. */ + if (script->function()) { + types::TypeScriptNesting *nesting = script->nesting(); + if (script->function()->isHeavyweight() || (nesting && nesting->children)) { prepareStubCall(Uses(fe ? 1 : 0)); - INLINE_STUBCALL(stubs::PutActivationObjects); + INLINE_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE); } else { /* if (hasCallObj() || hasArgsObj()) */ Jump putObjs = masm.branchTest32(Assembler::NonZero, @@ -2262,16 +3868,17 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubcc.linkExit(putObjs, Uses(frame.frameSlots())); stubcc.leave(); - OOL_STUBCALL(stubs::PutActivationObjects); + OOL_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE); emitReturnValue(&stubcc.masm, fe); emitFinalReturn(stubcc.masm); - } - } else { - if (fp->isStrictEvalFrame()) { - /* There will always be a call object. */ - prepareStubCall(Uses(fe ? 1 : 0)); - INLINE_STUBCALL(stubs::PutActivationObjects); + + /* + * Do frame count balancing inline for inner functions in a nesting + * with no children of their own. + */ + if (nesting) + masm.sub32(Imm32(1), AbsoluteAddress(&nesting->activeFrames)); } } @@ -2291,15 +3898,19 @@ void mjit::Compiler::prepareStubCall(Uses uses) { JaegerSpew(JSpew_Insns, " ---- STUB CALL, SYNCING FRAME ---- \n"); - frame.syncAndKill(Registers(Registers::TempRegs), uses); + frame.syncAndKill(Registers(Registers::TempAnyRegs), uses); JaegerSpew(JSpew_Insns, " ---- FRAME SYNCING DONE ---- \n"); } JSC::MacroAssembler::Call -mjit::Compiler::emitStubCall(void *ptr) +mjit::Compiler::emitStubCall(void *ptr, DataLabelPtr *pinline) { JaegerSpew(JSpew_Insns, " ---- CALLING STUB ---- \n"); - Call cl = masm.fallibleVMCall(ptr, PC, frame.stackDepth() + script->nfixed); + + masm.bumpStubCounter(script, PC, Registers::tempCallReg()); + + Call cl = masm.fallibleVMCall(cx->typeInferenceEnabled(), + ptr, outerPC(), pinline, frame.totalDepth()); JaegerSpew(JSpew_Insns, " ---- END STUB CALL ---- \n"); return cl; } @@ -2307,78 +3918,84 @@ mjit::Compiler::emitStubCall(void *ptr) void mjit::Compiler::interruptCheckHelper() { - RegisterID reg = frame.allocReg(); - - /* - * Bake in and test the address of the interrupt counter for the runtime. - * This is faster than doing two additional loads for the context's - * thread data, but will cause this thread to run slower if there are - * pending interrupts on some other thread. For non-JS_THREADSAFE builds - * we can skip this, as there is only one flag to poll. - */ -#ifdef JS_THREADSAFE - void *interrupt = (void*) &cx->runtime->interruptCounter; -#else - void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags; -#endif - -#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) - Jump jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0)); + Jump jump; + if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) { + /* For barrier verification, always take the interrupt so we can verify. */ + jump = masm.jump(); + } else { + void *interrupt = (void*) &cx->runtime->interrupt; +#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS) + jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0)); #else - /* Handle processors that can't load from absolute addresses. */ - masm.move(ImmPtr(interrupt), reg); - Jump jump = masm.branchTest32(Assembler::NonZero, Address(reg, 0)); + /* Handle processors that can't load from absolute addresses. */ + RegisterID reg = frame.allocReg(); + masm.move(ImmPtr(interrupt), reg); + jump = masm.branchTest32(Assembler::NonZero, Address(reg, 0)); + frame.freeReg(reg); #endif + } stubcc.linkExitDirect(jump, stubcc.masm.label()); -#ifdef JS_THREADSAFE - /* - * Do a slightly slower check for an interrupt on this thread. - * We don't want this thread to slow down excessively if the pending - * interrupt is on another thread. - */ - stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), reg); - stubcc.masm.loadPtr(Address(reg, JSContext::threadOffset()), reg); - Address flag(reg, offsetof(JSThread, data.interruptFlags)); - Jump noInterrupt = stubcc.masm.branchTest32(Assembler::Zero, flag); -#endif - frame.sync(stubcc.masm, Uses(0)); stubcc.masm.move(ImmPtr(PC), Registers::ArgReg1); - OOL_STUBCALL(stubs::Interrupt); + OOL_STUBCALL(stubs::Interrupt, REJOIN_RESUME); stubcc.rejoin(Changes(0)); +} -#ifdef JS_THREADSAFE - stubcc.linkRejoin(noInterrupt); -#endif +void +mjit::Compiler::recompileCheckHelper() +{ + if (inlining() || debugMode() || !globalObj || + !analysis->hasFunctionCalls() || !cx->typeInferenceEnabled()) { + return; + } + size_t *addr = script->addressOfUseCount(); + masm.add32(Imm32(1), AbsoluteAddress(addr)); +#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) + Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr), + Imm32(USES_BEFORE_INLINING)); +#else + /* Handle processors that can't load from absolute addresses. */ + RegisterID reg = frame.allocReg(); + masm.move(ImmPtr(addr), reg); + Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, Address(reg, 0), + Imm32(USES_BEFORE_INLINING)); frame.freeReg(reg); +#endif + stubcc.linkExit(jump, Uses(0)); + stubcc.leave(); + + OOL_STUBCALL(stubs::RecompileForInline, REJOIN_RESUME); + stubcc.rejoin(Changes(0)); } void -mjit::Compiler::addReturnSite(Label joinPoint, uint32 id) +mjit::Compiler::addReturnSite() { - InternalCallSite site(masm.distanceOf(joinPoint), PC, id, false, false); + InternalCallSite site(masm.distanceOf(masm.label()), a->inlineIndex, PC, + REJOIN_SCRIPTED, false); addCallSite(site); + masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfPrev()), JSFrameReg); } void -mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) +mjit::Compiler::emitUncachedCall(uint32_t argc, bool callingNew) { CallPatchInfo callPatch; RegisterID r0 = Registers::ReturnReg; VoidPtrStubUInt32 stub = callingNew ? stubs::UncachedNew : stubs::UncachedCall; - frame.syncAndKill(Registers(Registers::AvailRegs), Uses(argc + 2)); + frame.syncAndKill(Uses(argc + 2)); prepareStubCall(Uses(argc + 2)); masm.move(Imm32(argc), Registers::ArgReg1); - INLINE_STUBCALL(stub); + INLINE_STUBCALL(stub, REJOIN_CALL_PROLOGUE); Jump notCompiled = masm.branchTestPtr(Assembler::Zero, r0, r0); - masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + masm.loadPtr(FrameAddress(VMFrame::offsetOfRegsSp()), JSFrameReg); callPatch.hasFastNcode = true; callPatch.fastNcodePatch = masm.storePtrWithPatch(ImmPtr(NULL), @@ -2386,17 +4003,23 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) masm.jump(r0); callPatch.joinPoint = masm.label(); - addReturnSite(callPatch.joinPoint, __LINE__); - masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfPrev()), JSFrameReg); + addReturnSite(); frame.popn(argc + 2); + frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); - frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, knownPushedType(0)); + + BarrierState barrier = testBarrier(JSReturnReg_Type, JSReturnReg_Data, + /* testUndefined = */ false, + /* testReturn = */ true); stubcc.linkExitDirect(notCompiled, stubcc.masm.label()); - stubcc.rejoin(Changes(0)); + stubcc.rejoin(Changes(1)); callPatches.append(callPatch); + + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); } static bool @@ -2411,7 +4034,7 @@ IsLowerableFunCallOrApply(jsbytecode *pc) } void -mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedArgc, +mjit::Compiler::checkCallApplySpeculation(uint32_t callImmArgc, uint32_t speculatedArgc, FrameEntry *origCallee, FrameEntry *origThis, MaybeRegisterID origCalleeType, RegisterID origCalleeData, MaybeRegisterID origThisType, RegisterID origThisData, @@ -2419,16 +4042,25 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA { JS_ASSERT(IsLowerableFunCallOrApply(PC)); + RegisterID temp; + Registers tempRegs(Registers::AvailRegs); + if (origCalleeType.isSet()) + tempRegs.takeReg(origCalleeType.reg()); + tempRegs.takeReg(origCalleeData); + if (origThisType.isSet()) + tempRegs.takeReg(origThisType.reg()); + tempRegs.takeReg(origThisData); + temp = tempRegs.takeAnyReg().reg(); + /* * if (origCallee.isObject() && * origCallee.toObject().isFunction && - * origCallee.toObject().getFunctionPrivate() == js_fun_{call,apply}) + * origCallee.toObject().toFunction() == js_fun_{call,apply}) */ MaybeJump isObj; if (origCalleeType.isSet()) isObj = masm.testObject(Assembler::NotEqual, origCalleeType.reg()); - Jump isFun = masm.testFunction(Assembler::NotEqual, origCalleeData); - masm.loadObjPrivate(origCalleeData, origCalleeData); + Jump isFun = masm.testFunction(Assembler::NotEqual, origCalleeData, temp); Native native = *PC == JSOP_FUNCALL ? js_fun_call : js_fun_apply; Jump isNative = masm.branchPtr(Assembler::NotEqual, Address(origCalleeData, JSFunction::offsetOfNativeOrScript()), @@ -2444,9 +4076,9 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA stubcc.linkExitDirect(isFun, stubcc.masm.label()); stubcc.linkExitDirect(isNative, stubcc.masm.label()); - int32 frameDepthAdjust; + int32_t frameDepthAdjust; if (applyTricks == LazyArgsObj) { - OOL_STUBCALL(stubs::Arguments); + OOL_STUBCALL(stubs::Arguments, REJOIN_RESUME); frameDepthAdjust = +1; } else { frameDepthAdjust = 0; @@ -2454,23 +4086,10 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA stubcc.masm.move(Imm32(callImmArgc), Registers::ArgReg1); JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW CALL CODE ---- \n"); - OOL_STUBCALL_LOCAL_SLOTS(JS_FUNC_TO_DATA_PTR(void *, stubs::UncachedCall), - frame.localSlots() + frameDepthAdjust); + OOL_STUBCALL_LOCAL_SLOTS(JS_FUNC_TO_DATA_PTR(void *, stubs::SlowCall), + REJOIN_FALLTHROUGH, frame.totalDepth() + frameDepthAdjust); JaegerSpew(JSpew_Insns, " ---- END SLOW CALL CODE ---- \n"); - RegisterID r0 = Registers::ReturnReg; - Jump notCompiled = stubcc.masm.branchTestPtr(Assembler::Zero, r0, r0); - - stubcc.masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); - Address ncodeAddr(JSFrameReg, StackFrame::offsetOfNcode()); - uncachedCallPatch->hasSlowNcode = true; - uncachedCallPatch->slowNcodePatch = stubcc.masm.storePtrWithPatch(ImmPtr(NULL), ncodeAddr); - - stubcc.masm.jump(r0); - addReturnSite(masm.label(), __LINE__); - - notCompiled.linkTo(stubcc.masm.label(), &stubcc.masm); - /* * inlineCallHelper will link uncachedCallSlowRejoin to the join point * at the end of the ic. At that join point, the return value of the @@ -2478,6 +4097,8 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA */ JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW RESTORE CODE ---- \n"); Address rval = frame.addressOf(origCallee); /* vp[0] == rval */ + if (knownPushedType(0) == JSVAL_TYPE_DOUBLE) + stubcc.masm.ensureInMemoryDouble(rval); stubcc.masm.loadValueAsComponents(rval, JSReturnReg_Type, JSReturnReg_Data); *uncachedCallSlowRejoin = stubcc.masm.jump(); JaegerSpew(JSpew_Insns, " ---- END SLOW RESTORE CODE ---- \n"); @@ -2490,7 +4111,7 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA */ if (*PC == JSOP_FUNAPPLY) { masm.store32(Imm32(applyTricks == LazyArgsObj), - FrameAddress(offsetof(VMFrame, u.call.lazyArgsObj))); + FrameAddress(VMFrame::offsetOfLazyArgsObj())); } } @@ -2503,62 +4124,96 @@ mjit::Compiler::canUseApplyTricks() return *nextpc == JSOP_FUNAPPLY && IsLowerableFunCallOrApply(nextpc) && !analysis->jumpTarget(nextpc) && - !debugMode(); + !debugMode() && + !a->parent && + bytecodeInChunk(nextpc); } /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ -void -mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) +bool +mjit::Compiler::inlineCallHelper(uint32_t callImmArgc, bool callingNew, FrameSize &callFrameSize) { - /* Check for interrupts on function call */ - interruptCheckHelper(); - - int32 speculatedArgc; + int32_t speculatedArgc; if (applyTricks == LazyArgsObj) { frame.pop(); speculatedArgc = 1; } else { + /* + * Check for interrupts on function call. We don't do this for lazy + * arguments objects as the interrupt may kick this frame into the + * interpreter, which doesn't know about the apply tricks. Instead, we + * do the interrupt check at the start of the JSOP_ARGUMENTS. + */ + interruptCheckHelper(); + speculatedArgc = callImmArgc; } FrameEntry *origCallee = frame.peek(-(speculatedArgc + 2)); FrameEntry *origThis = frame.peek(-(speculatedArgc + 1)); - /* 'this' does not need to be synced for constructing. */ - if (callingNew) + /* + * 'this' does not need to be synced for constructing. :FIXME: is it + * possible that one of the arguments is directly copying the 'this' + * entry (something like 'new x.f(x)')? + */ + if (callingNew) { frame.discardFe(origThis); + /* + * If inference is enabled, the 'this' value of the pushed frame always + * needs to be coherent. If a GC gets triggered before the callee can + * fill in the slot (i.e. the GC happens on constructing the 'new' + * object or the call object for a heavyweight callee), it needs to be + * able to read the 'this' value to tell whether newScript constraints + * will need to be regenerated afterwards. + */ + if (cx->typeInferenceEnabled()) + masm.storeValue(NullValue(), frame.addressOf(origThis)); + } + + if (!cx->typeInferenceEnabled()) { + CompileStatus status = callArrayBuiltin(callImmArgc, callingNew); + if (status != Compile_InlineAbort) + return (status == Compile_Okay); + } + /* * From the presence of JSOP_FUN{CALL,APPLY}, we speculate that we are * going to call js_fun_{call,apply}. Normally, this call would go through * js::Invoke to ultimately call 'this'. We can do much better by having * the callIC cache and call 'this' directly. However, if it turns out that * we are not actually calling js_fun_call, the callIC must act as normal. + * + * Note: do *NOT* use type information or inline state in any way when + * deciding whether to lower a CALL or APPLY. The stub calls here store + * their return values in a different slot, so when recompiling we need + * to go down the exact same path. */ bool lowerFunCallOrApply = IsLowerableFunCallOrApply(PC); - /* - * Currently, constant values are not functions, so don't even try to - * optimize. This lets us assume that callee/this have regs below. - */ + bool newType = callingNew && cx->typeInferenceEnabled() && types::UseNewType(cx, script, PC); + #ifdef JS_MONOIC - if (debugMode() || - origCallee->isConstant() || origCallee->isNotType(JSVAL_TYPE_OBJECT) || - (lowerFunCallOrApply && - (origThis->isConstant() || origThis->isNotType(JSVAL_TYPE_OBJECT)))) { + if (debugMode() || newType) { #endif if (applyTricks == LazyArgsObj) { /* frame.pop() above reset us to pre-JSOP_ARGUMENTS state */ - jsop_arguments(); - frame.pushSynced(); + jsop_arguments(REJOIN_RESUME); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); } emitUncachedCall(callImmArgc, callingNew); - return; + applyTricks = NoApplyTricks; + return true; #ifdef JS_MONOIC } + frame.forgetMismatchedObject(origCallee); + if (lowerFunCallOrApply) + frame.forgetMismatchedObject(origThis); + /* Initialized by both branches below. */ - CallGenInfo callIC(PC); + CallGenInfo callIC; CallPatchInfo callPatch; MaybeRegisterID icCalleeType; /* type to test for function-ness */ RegisterID icCalleeData; /* data to call */ @@ -2602,7 +4257,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) PinRegAcrossSyncAndKill p3(frame, origThisData), p4(frame, origThisType); /* Leaves pinned regs untouched. */ - frame.syncAndKill(Registers(Registers::AvailRegs), Uses(speculatedArgc + 2)); + frame.syncAndKill(Uses(speculatedArgc + 2)); } checkCallApplySpeculation(callImmArgc, speculatedArgc, @@ -2622,38 +4277,41 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) * length of the array passed to apply. */ if (*PC == JSOP_FUNCALL) - callIC.frameSize.initStatic(frame.localSlots(), speculatedArgc - 1); + callIC.frameSize.initStatic(frame.totalDepth(), speculatedArgc - 1); else callIC.frameSize.initDynamic(); } else { /* Leaves pinned regs untouched. */ - frame.syncAndKill(Registers(Registers::AvailRegs), Uses(speculatedArgc + 2)); + frame.syncAndKill(Uses(speculatedArgc + 2)); icCalleeType = origCalleeType; icCalleeData = origCalleeData; icRvalAddr = frame.addressOf(origCallee); - callIC.frameSize.initStatic(frame.localSlots(), speculatedArgc); + callIC.frameSize.initStatic(frame.totalDepth(), speculatedArgc); } } + callFrameSize = callIC.frameSize; + + callIC.typeMonitored = monitored(PC) || hasTypeBarriers(PC); + /* Test the type if necessary. Failing this always takes a really slow path. */ MaybeJump notObjectJump; if (icCalleeType.isSet()) notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg()); /* - * For an optimized apply, keep icCalleeData and funPtrReg in a - * callee-saved registers for the subsequent ic::SplatApplyArgs call. + * For an optimized apply, keep icCalleeData in a callee-saved register for + * the subsequent ic::SplatApplyArgs call. */ - Registers tempRegs; + Registers tempRegs(Registers::AvailRegs); if (callIC.frameSize.isDynamic() && !Registers::isSaved(icCalleeData)) { - RegisterID x = tempRegs.takeRegInMask(Registers::SavedRegs); + RegisterID x = tempRegs.takeAnyReg(Registers::SavedRegs).reg(); masm.move(icCalleeData, x); icCalleeData = x; } else { tempRegs.takeReg(icCalleeData); } - RegisterID funPtrReg = tempRegs.takeRegInMask(Registers::SavedRegs); /* Reserve space just before initialization of funGuard. */ RESERVE_IC_SPACE(masm); @@ -2675,16 +4333,16 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) stubcc.linkExitDirect(j, stubcc.masm.label()); callIC.slowPathStart = stubcc.masm.label(); + RegisterID tmp = tempRegs.takeAnyReg().reg(); + /* * Test if the callee is even a function. If this doesn't match, we * take a _really_ slow path later. */ - Jump notFunction = stubcc.masm.testFunction(Assembler::NotEqual, icCalleeData); + Jump notFunction = stubcc.masm.testFunction(Assembler::NotEqual, icCalleeData, tmp); /* Test if the function is scripted. */ - RegisterID tmp = tempRegs.takeAnyReg(); - stubcc.masm.loadObjPrivate(icCalleeData, funPtrReg); - stubcc.masm.load16(Address(funPtrReg, offsetof(JSFunction, flags)), tmp); + stubcc.masm.load16(Address(icCalleeData, offsetof(JSFunction, flags)), tmp); stubcc.masm.and32(Imm32(JSFUN_KINDMASK), tmp); Jump isNative = stubcc.masm.branch32(Assembler::Below, tmp, Imm32(JSFUN_INTERPRETED)); tempRegs.putReg(tmp); @@ -2695,7 +4353,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) * catch-all/native path has a static depth. */ if (callIC.frameSize.isDynamic()) - OOL_STUBCALL(ic::SplatApplyArgs); + OOL_STUBCALL(ic::SplatApplyArgs, REJOIN_CALL_SPLAT); /* * No-op jump that gets patched by ic::New/Call to the stub generated @@ -2706,6 +4364,8 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) callIC.oolJump = toPatch; callIC.icCall = stubcc.masm.label(); + RejoinState rejoinState = callIC.frameSize.rejoinState(PC, false); + /* * At this point the function is definitely scripted, so we try to * compile it and patch either funGuard/funJump or oolJump. This code @@ -2713,32 +4373,33 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) */ callIC.addrLabel1 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); void *icFunPtr = JS_FUNC_TO_DATA_PTR(void *, callingNew ? ic::New : ic::Call); - if (callIC.frameSize.isStatic()) - callIC.oolCall = OOL_STUBCALL_LOCAL_SLOTS(icFunPtr, frame.localSlots()); - else - callIC.oolCall = OOL_STUBCALL_LOCAL_SLOTS(icFunPtr, -1); + if (callIC.frameSize.isStatic()) { + callIC.oolCall = OOL_STUBCALL_LOCAL_SLOTS(icFunPtr, rejoinState, frame.totalDepth()); + } else { + callIC.oolCall = OOL_STUBCALL_LOCAL_SLOTS(icFunPtr, rejoinState, -1); + } callIC.funObjReg = icCalleeData; - callIC.funPtrReg = funPtrReg; /* * The IC call either returns NULL, meaning call completed, or a - * function pointer to jump to. Caveat: Must restore JSFrameReg - * because a new frame has been pushed. + * function pointer to jump to. */ rejoin1 = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg, Registers::ReturnReg); if (callIC.frameSize.isStatic()) stubcc.masm.move(Imm32(callIC.frameSize.staticArgc()), JSParamReg_Argc); else - stubcc.masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), JSParamReg_Argc); - stubcc.masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + stubcc.masm.load32(FrameAddress(VMFrame::offsetOfDynamicArgc()), JSParamReg_Argc); + stubcc.masm.loadPtr(FrameAddress(VMFrame::offsetOfRegsSp()), JSFrameReg); callPatch.hasSlowNcode = true; callPatch.slowNcodePatch = stubcc.masm.storePtrWithPatch(ImmPtr(NULL), Address(JSFrameReg, StackFrame::offsetOfNcode())); stubcc.masm.jump(Registers::ReturnReg); + + /* * This ool path is the catch-all for everything but scripted function * callees. For native functions, ic::NativeNew/NativeCall will repatch @@ -2752,7 +4413,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) isNative.linkTo(stubcc.masm.label(), &stubcc.masm); callIC.addrLabel2 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); - OOL_STUBCALL(callingNew ? ic::NativeNew : ic::NativeCall); + OOL_STUBCALL(callingNew ? ic::NativeNew : ic::NativeCall, rejoinState); rejoin2 = stubcc.masm.jump(); } @@ -2763,20 +4424,20 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) */ callIC.hotPathLabel = masm.label(); - uint32 flags = 0; + uint32_t flags = 0; if (callingNew) flags |= StackFrame::CONSTRUCTING; InlineFrameAssembler inlFrame(masm, callIC, flags); callPatch.hasFastNcode = true; - callPatch.fastNcodePatch = inlFrame.assemble(NULL); + callPatch.fastNcodePatch = inlFrame.assemble(NULL, PC); callIC.hotJump = masm.jump(); callIC.joinPoint = callPatch.joinPoint = masm.label(); - addReturnSite(callPatch.joinPoint, __LINE__); + callIC.callIndex = callSites.length(); + addReturnSite(); if (lowerFunCallOrApply) uncachedCallPatch.joinPoint = callIC.joinPoint; - masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfPrev()), JSFrameReg); /* * We've placed hotJump, joinPoint and hotPathLabel, and no other labels are located by offset @@ -2784,10 +4445,16 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) */ CHECK_IC_SPACE(); + JSValueType type = knownPushedType(0); + frame.popn(speculatedArgc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); - frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, type); + + BarrierState barrier = testBarrier(JSReturnReg_Type, JSReturnReg_Data, + /* testUndefined = */ false, + /* testReturn = */ true); /* * Now that the frame state is set, generate the rejoin path. Note that, if @@ -2799,7 +4466,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) rejoin1.linkTo(callIC.slowJoinPoint, &stubcc.masm); rejoin2.linkTo(callIC.slowJoinPoint, &stubcc.masm); JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW RESTORE CODE ---- \n"); - stubcc.masm.loadValueAsComponents(icRvalAddr, JSReturnReg_Type, JSReturnReg_Data); + frame.reloadEntry(stubcc.masm, icRvalAddr, frame.peek(-1)); stubcc.crossJump(stubcc.masm.jump(), masm.label()); JaegerSpew(JSpew_Insns, " ---- END SLOW RESTORE CODE ---- \n"); @@ -2813,10 +4480,272 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew) if (lowerFunCallOrApply) callPatches.append(uncachedCallPatch); + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); + applyTricks = NoApplyTricks; + return true; #endif } +CompileStatus +mjit::Compiler::callArrayBuiltin(uint32_t argc, bool callingNew) +{ + if (!globalObj) + return Compile_InlineAbort; + + if (applyTricks == LazyArgsObj) + return Compile_InlineAbort; + + FrameEntry *origCallee = frame.peek(-((int)argc + 2)); + if (origCallee->isNotType(JSVAL_TYPE_OBJECT)) + return Compile_InlineAbort; + + if (frame.extra(origCallee).name != cx->runtime->atomState.classAtoms[JSProto_Array]) + return Compile_InlineAbort; + + JSObject *arrayObj; + if (!js_GetClassObject(cx, globalObj, JSProto_Array, &arrayObj)) + return Compile_Error; + + JSObject *arrayProto = globalObj->global().getOrCreateArrayPrototype(cx); + if (!arrayProto) + return Compile_Error; + + if (argc > 1) + return Compile_InlineAbort; + FrameEntry *origArg = (argc == 1) ? frame.peek(-1) : NULL; + if (origArg) { + if (origArg->isNotType(JSVAL_TYPE_INT32)) + return Compile_InlineAbort; + if (origArg->isConstant() && origArg->getValue().toInt32() < 0) + return Compile_InlineAbort; + } + + if (!origCallee->isTypeKnown()) { + Jump notObject = frame.testObject(Assembler::NotEqual, origCallee); + stubcc.linkExit(notObject, Uses(argc + 2)); + } + + RegisterID reg = frame.tempRegForData(origCallee); + Jump notArray = masm.branchPtr(Assembler::NotEqual, reg, ImmPtr(arrayObj)); + stubcc.linkExit(notArray, Uses(argc + 2)); + + int32_t knownSize = 0; + MaybeRegisterID sizeReg; + if (origArg) { + if (origArg->isConstant()) { + knownSize = origArg->getValue().toInt32(); + } else { + if (!origArg->isTypeKnown()) { + Jump notInt = frame.testInt32(Assembler::NotEqual, origArg); + stubcc.linkExit(notInt, Uses(argc + 2)); + } + sizeReg = frame.tempRegForData(origArg); + Jump belowZero = masm.branch32(Assembler::LessThan, sizeReg.reg(), Imm32(0)); + stubcc.linkExit(belowZero, Uses(argc + 2)); + } + } else { + knownSize = 0; + } + + stubcc.leave(); + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + OOL_STUBCALL(callingNew ? stubs::SlowNew : stubs::SlowCall, REJOIN_FALLTHROUGH); + + { + PinRegAcrossSyncAndKill p1(frame, sizeReg); + frame.popn(argc + 2); + frame.syncAndKill(Uses(0)); + } + + prepareStubCall(Uses(0)); + masm.storePtr(ImmPtr(arrayProto), FrameAddress(offsetof(VMFrame, scratch))); + if (sizeReg.isSet()) + masm.move(sizeReg.reg(), Registers::ArgReg1); + else + masm.move(Imm32(knownSize), Registers::ArgReg1); + INLINE_STUBCALL(stubs::NewDenseUnallocatedArray, REJOIN_PUSH_OBJECT); + + frame.takeReg(Registers::ReturnReg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); + frame.forgetType(frame.peek(-1)); + + stubcc.rejoin(Changes(1)); + + return Compile_Okay; +} + +/* Maximum number of calls we will inline at the same site. */ +static const uint32_t INLINE_SITE_LIMIT = 5; + +CompileStatus +mjit::Compiler::inlineScriptedFunction(uint32_t argc, bool callingNew) +{ + JS_ASSERT(inlining()); + + /* We already know which frames we are inlining at each PC, so scan the list of inline frames. */ + bool calleeMultipleReturns = false; + Vector inlineCallees(CompilerAllocPolicy(cx, *this)); + for (unsigned i = 0; i < ssa.numFrames(); i++) { + if (ssa.iterFrame(i).parent == a->inlineIndex && ssa.iterFrame(i).parentpc == PC) { + JSScript *script = ssa.iterFrame(i).script; + inlineCallees.append(script); + if (script->analysis()->numReturnSites() > 1) + calleeMultipleReturns = true; + } + } + + if (inlineCallees.empty()) + return Compile_InlineAbort; + + JS_ASSERT(!monitored(PC)); + + /* + * Remove all dead entries from the frame's tracker. We will not recognize + * them as dead after pushing the new frame. + */ + frame.pruneDeadEntries(); + + RegisterAllocation *exitState = NULL; + if (inlineCallees.length() > 1 || calleeMultipleReturns) { + /* + * Multiple paths through the callees, get a register allocation for + * the various incoming edges. + */ + exitState = frame.computeAllocation(PC + JSOP_CALL_LENGTH); + } + + /* + * If this is a polymorphic callsite, get a register for the callee too. + * After this, do not touch the register state in the current frame until + * stubs for all callees have been generated. + */ + FrameEntry *origCallee = frame.peek(-((int)argc + 2)); + FrameEntry *entrySnapshot = NULL; + MaybeRegisterID calleeReg; + if (inlineCallees.length() > 1) { + frame.forgetMismatchedObject(origCallee); + calleeReg = frame.tempRegForData(origCallee); + + entrySnapshot = frame.snapshotState(); + if (!entrySnapshot) + return Compile_Error; + } + MaybeJump calleePrevious; + + JSValueType returnType = knownPushedType(0); + + bool needReturnValue = JSOP_POP != (JSOp)*(PC + JSOP_CALL_LENGTH); + bool syncReturnValue = needReturnValue && returnType == JSVAL_TYPE_UNKNOWN; + + /* Track register state after the call. */ + bool returnSet = false; + AnyRegisterID returnRegister; + const FrameEntry *returnEntry = NULL; + + Vector returnJumps(CompilerAllocPolicy(cx, *this)); + + for (unsigned i = 0; i < inlineCallees.length(); i++) { + if (entrySnapshot) + frame.restoreFromSnapshot(entrySnapshot); + + JSScript *script = inlineCallees[i]; + CompileStatus status; + + status = pushActiveFrame(script, argc); + if (status != Compile_Okay) + return status; + + a->exitState = exitState; + + JaegerSpew(JSpew_Inlining, "inlining call to script (file \"%s\") (line \"%d\")\n", + script->filename, script->lineno); + + if (calleePrevious.isSet()) { + calleePrevious.get().linkTo(masm.label(), &masm); + calleePrevious = MaybeJump(); + } + + if (i + 1 != inlineCallees.length()) { + /* Guard on the callee, except when this object must be the callee. */ + JS_ASSERT(calleeReg.isSet()); + calleePrevious = masm.branchPtr(Assembler::NotEqual, calleeReg.reg(), ImmPtr(script->function())); + } + + a->returnJumps = &returnJumps; + a->needReturnValue = needReturnValue; + a->syncReturnValue = syncReturnValue; + a->returnValueDouble = returnType == JSVAL_TYPE_DOUBLE; + if (returnSet) { + a->returnSet = true; + a->returnRegister = returnRegister; + } + + /* + * Update the argument frame entries in place if the callee has had an + * argument inferred as double but we are passing an int. + */ + ensureDoubleArguments(); + + markUndefinedLocals(); + + status = generateMethod(); + if (status != Compile_Okay) { + popActiveFrame(); + if (status == Compile_Abort) { + /* The callee is uncompileable, mark it as uninlineable and retry. */ + script->uninlineable = true; + types::MarkTypeObjectFlags(cx, script->function(), + types::OBJECT_FLAG_UNINLINEABLE); + return Compile_Retry; + } + return status; + } + + if (needReturnValue && !returnSet) { + if (a->returnSet) { + returnSet = true; + returnRegister = a->returnRegister; + } else { + returnEntry = a->returnEntry; + } + } + + popActiveFrame(); + + if (i + 1 != inlineCallees.length()) + returnJumps.append(masm.jump()); + } + + for (unsigned i = 0; i < returnJumps.length(); i++) + returnJumps[i].linkTo(masm.label(), &masm); + + frame.popn(argc + 2); + + if (entrySnapshot) + cx->array_delete(entrySnapshot); + + if (exitState) + frame.discardForJoin(exitState, analysis->getCode(PC).stackDepth - (argc + 2)); + + if (returnSet) { + frame.takeReg(returnRegister); + if (returnRegister.isReg()) + frame.pushTypedPayload(returnType, returnRegister.reg()); + else + frame.pushDouble(returnRegister.fpreg()); + } else if (returnEntry) { + frame.pushCopyOf((FrameEntry *) returnEntry); + } else { + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + } + + JaegerSpew(JSpew_Inlining, "finished inlining call to script (file \"%s\") (line \"%d\")\n", + script->filename, script->lineno); + + return Compile_Okay; +} + /* * This function must be called immediately after any instruction which could * cause a new StackFrame to be pushed and could lead to a new debug trap @@ -2829,9 +4758,19 @@ mjit::Compiler::addCallSite(const InternalCallSite &site) } void -mjit::Compiler::restoreFrameRegs(Assembler &masm) +mjit::Compiler::inlineStubCall(void *stub, RejoinState rejoin, Uses uses) { - masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + DataLabelPtr inlinePatch; + Call cl = emitStubCall(stub, &inlinePatch); + InternalCallSite site(masm.callReturnOffset(cl), a->inlineIndex, PC, + rejoin, false); + site.inlinePatch = inlinePatch; + if (loop && loop->generatingInvariants()) { + Jump j = masm.jump(); + Label l = masm.label(); + loop->addInvariantCall(j, l, false, false, callSites.length(), uses); + } + addCallSite(site); } bool @@ -2841,7 +4780,7 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const JS_ASSERT(rhs.isPrimitive()); if (lhs.isString() && rhs.isString()) { - int32 cmp; + int32_t cmp; CompareStrings(cx, lhs.toString(), rhs.toString(), &cmp); switch (op) { case JSOP_LT: @@ -2861,10 +4800,10 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const } } else { double ld, rd; - + /* These should be infallible w/ primitives. */ - ValueToNumber(cx, lhs, &ld); - ValueToNumber(cx, rhs, &rd); + JS_ALWAYS_TRUE(ToNumber(cx, lhs, &ld)); + JS_ALWAYS_TRUE(ToNumber(cx, rhs, &rd)); switch(op) { case JSOP_LT: return ld < rd; @@ -2896,13 +4835,37 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const return false; } +bool +mjit::Compiler::constantFoldBranch(jsbytecode *target, bool taken) +{ + if (taken) { + if (!frame.syncForBranch(target, Uses(0))) + return false; + Jump j = masm.jump(); + if (!jumpAndRun(j, target)) + return false; + } else { + /* + * Branch is never taken, but clean up any loop + * if this is a backedge. + */ + if (target < PC && !finishLoop(target)) + return false; + } + return true; +} + bool mjit::Compiler::emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused) { + if (target) + frame.syncAndKillEverything(); + else + frame.syncAndKill(Uses(2)); + prepareStubCall(Uses(2)); - INLINE_STUBCALL(stub); - frame.pop(); - frame.pop(); + INLINE_STUBCALL(stub, target ? REJOIN_BRANCH : REJOIN_PUSH_BOOLEAN); + frame.popn(2); if (!target) { frame.takeReg(Registers::ReturnReg); @@ -2911,64 +4874,84 @@ mjit::Compiler::emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused) } JS_ASSERT(fused == JSOP_IFEQ || fused == JSOP_IFNE); - frame.syncAndForgetEverything(); - Assembler::Condition cond = (fused == JSOP_IFEQ) - ? Assembler::Zero - : Assembler::NonZero; - Jump j = masm.branchTest32(cond, Registers::ReturnReg, + Jump j = masm.branchTest32(GetStubCompareCondition(fused), Registers::ReturnReg, Registers::ReturnReg); - return jumpAndTrace(j, target); + return jumpAndRun(j, target); } void -mjit::Compiler::jsop_setprop_slow(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop_slow(PropertyName *name) { prepareStubCall(Uses(2)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - if (usePropCache) - INLINE_STUBCALL(STRICT_VARIANT(stubs::SetName)); - else - INLINE_STUBCALL(STRICT_VARIANT(stubs::SetPropNoCache)); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH); JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); frame.shimmy(1); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_OTHER); } void -mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_getprop_slow(PropertyName *name, bool forPrototype) { + /* See ::jsop_getprop */ + RejoinState rejoin = forPrototype ? REJOIN_THIS_PROTOTYPE : REJOIN_GETTER; + prepareStubCall(Uses(1)); - if (usePropCache) { - INLINE_STUBCALL(stubs::GetProp); - } else { - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::GetPropNoCache); - } + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(forPrototype ? stubs::GetPropNoCache : stubs::GetProp, rejoin); + + if (!forPrototype) + testPushedType(rejoin, -1, /* ool = */ false); + frame.pop(); - frame.pushSynced(); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_OTHER); } -bool -mjit::Compiler::jsop_callprop_slow(JSAtom *atom) +#ifdef JS_MONOIC +void +mjit::Compiler::passMICAddress(GlobalNameICInfo &ic) { - prepareStubCall(Uses(1)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::CallProp); - frame.pop(); - frame.pushSynced(); - frame.pushSynced(); - return true; + ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} +#endif + +#if defined JS_POLYIC +void +mjit::Compiler::passICAddress(BaseICInfo *ic) +{ + ic->paramAddr = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); } bool -mjit::Compiler::jsop_length() +mjit::Compiler::jsop_getprop(PropertyName *name, JSValueType knownType, + bool doTypeCheck, bool forPrototype) { FrameEntry *top = frame.peek(-1); - if (top->isTypeKnown() && top->getKnownType() == JSVAL_TYPE_STRING) { + /* + * Use a different rejoin for GETPROP computing the 'this' object, as we + * can't use the current bytecode within InternalInterpret to tell this is + * fetching the 'this' value. + */ + RejoinState rejoin = REJOIN_GETTER; + if (forPrototype) { + JS_ASSERT(top->isType(JSVAL_TYPE_OBJECT) && + name == cx->runtime->atomState.classPrototypeAtom); + rejoin = REJOIN_THIS_PROTOTYPE; + } + + /* Handle length accesses on known strings without using a PIC. */ + if (name == cx->runtime->atomState.lengthAtom && + top->isType(JSVAL_TYPE_STRING) && + (!cx->typeInferenceEnabled() || knownPushedType(0) == JSVAL_TYPE_INT32)) { if (top->isConstant()) { JSString *str = top->getValue().toString(); Value v; - v.setNumber(uint32(str->length())); + v.setNumber(uint32_t(str->length())); frame.pop(); frame.push(v); } else { @@ -2981,60 +4964,222 @@ mjit::Compiler::jsop_length() return true; } -#if defined JS_POLYIC - return jsop_getprop(cx->runtime->atomState.lengthAtom); -#else - prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::Length); - frame.pop(); - frame.pushSynced(); - return true; -#endif -} + if (top->mightBeType(JSVAL_TYPE_OBJECT) && + JSOp(*PC) == JSOP_LENGTH && cx->typeInferenceEnabled() && + !hasTypeBarriers(PC) && knownPushedType(0) == JSVAL_TYPE_INT32) { + /* Check if this is an array we can make a loop invariant entry for. */ + if (loop && loop->generatingInvariants()) { + CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0)); + FrameEntry *fe = loop->invariantLength(topv); + if (fe) { + frame.learnType(fe, JSVAL_TYPE_INT32, false); + frame.pop(); + frame.pushCopyOf(fe); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_STATIC); + return true; + } + } -#ifdef JS_MONOIC -void -mjit::Compiler::passMICAddress(GlobalNameICInfo &ic) -{ - ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} -#endif + types::TypeSet *types = analysis->poppedTypes(PC, 0); -#if defined JS_POLYIC -void -mjit::Compiler::passICAddress(BaseICInfo *ic) -{ - ic->paramAddr = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} + /* + * Check if we are accessing the 'length' property of a known dense array. + * Note that if the types are known to indicate dense arrays, their lengths + * must fit in an int32. + */ + if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) { + bool isObject = top->isTypeKnown(); + if (!isObject) { + Jump notObject = frame.testObject(Assembler::NotEqual, top); + stubcc.linkExit(notObject, Uses(1)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::GetProp, rejoin); + if (rejoin == REJOIN_GETTER) + testPushedType(rejoin, -1); + } + RegisterID result = frame.allocReg(); + RegisterID reg = frame.tempRegForData(top); + frame.pop(); + masm.loadPtr(Address(reg, JSObject::offsetOfElements()), result); + masm.load32(Address(result, ObjectElements::offsetOfLength()), result); + frame.pushTypedPayload(JSVAL_TYPE_INT32, result); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + if (!isObject) + stubcc.rejoin(Changes(1)); + return true; + } -bool -mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) -{ - FrameEntry *top = frame.peek(-1); + /* + * Check if we're accessing the 'length' property of a typed array. + * The typed array length always fits in an int32. + */ + if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) { + bool isObject = top->isTypeKnown(); + if (!isObject) { + Jump notObject = frame.testObject(Assembler::NotEqual, top); + stubcc.linkExit(notObject, Uses(1)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::GetProp, rejoin); + if (rejoin == REJOIN_GETTER) + testPushedType(rejoin, -1); + } + RegisterID reg = frame.copyDataIntoReg(top); + frame.pop(); + masm.loadPayload(Address(reg, TypedArray::lengthOffset()), reg); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + if (!isObject) + stubcc.rejoin(Changes(1)); + return true; + } + + /* + * Check if we are accessing the 'length' of the lazy arguments for the + * current frame. + */ + if (types->isLazyArguments(cx)) { + frame.pop(); + frame.pushWord(Address(JSFrameReg, StackFrame::offsetOfNumActual()), JSVAL_TYPE_INT32); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + return true; + } + } + + /* If the access will definitely be fetching a particular value, nop it. */ + bool testObject; + JSObject *singleton = + (*PC == JSOP_GETPROP || *PC == JSOP_CALLPROP) ? pushedSingleton(0) : NULL; + if (singleton && singleton->isFunction() && !hasTypeBarriers(PC) && + testSingletonPropertyTypes(top, ATOM_TO_JSID(name), &testObject)) { + if (testObject) { + Jump notObject = frame.testObject(Assembler::NotEqual, top); + stubcc.linkExit(notObject, Uses(1)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::GetProp, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -1); + } + + frame.pop(); + frame.push(ObjectValue(*singleton)); + + if (script->pcCounters && cx->typeInferenceEnabled()) + bumpPropCounter(PC, OpcodeCounts::PROP_STATIC); + + if (testObject) + stubcc.rejoin(Changes(1)); + + return true; + } + + /* Check if this is a property access we can make a loop invariant entry for. */ + if (loop && loop->generatingInvariants() && !hasTypeBarriers(PC)) { + CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0)); + if (FrameEntry *fe = loop->invariantProperty(topv, ATOM_TO_JSID(name))) { + if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE) + frame.learnType(fe, knownType, false); + frame.pop(); + frame.pushCopyOf(fe); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_STATIC); + return true; + } + } /* If the incoming type will never PIC, take slow path. */ - if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) { - JS_ASSERT_IF(atom == cx->runtime->atomState.lengthAtom, - top->getKnownType() != JSVAL_TYPE_STRING); - jsop_getprop_slow(atom, usePropCache); + if (top->isNotType(JSVAL_TYPE_OBJECT)) { + jsop_getprop_slow(name, forPrototype); return true; } + frame.forgetMismatchedObject(top); + + /* + * Check if we are accessing a known type which always has the property + * in a particular inline slot. Get the property directly in this case, + * without using an IC. + */ + jsid id = ATOM_TO_JSID(name); + types::TypeSet *types = frame.extra(top).types; + if (types && !types->unknownObject() && + types->getObjectCount() == 1 && + types->getTypeObject(0) != NULL && + !types->getTypeObject(0)->unknownProperties() && + id == types::MakeTypeId(cx, id)) { + JS_ASSERT(!forPrototype); + types::TypeObject *object = types->getTypeObject(0); + types::TypeSet *propertyTypes = object->getProperty(cx, id, false); + if (!propertyTypes) + return false; + if (propertyTypes->isDefiniteProperty() && + !propertyTypes->isOwnProperty(cx, object, true)) { + types->addFreeze(cx); + uint32_t slot = propertyTypes->definiteSlot(); + bool isObject = top->isTypeKnown(); + if (!isObject) { + Jump notObject = frame.testObject(Assembler::NotEqual, top); + stubcc.linkExit(notObject, Uses(1)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::GetProp, rejoin); + if (rejoin == REJOIN_GETTER) + testPushedType(rejoin, -1); + } + RegisterID reg = frame.tempRegForData(top); + frame.pop(); + + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + + Address address(reg, JSObject::getFixedSlotOffset(slot)); + BarrierState barrier = pushAddressMaybeBarrier(address, knownType, false); + if (!isObject) + stubcc.rejoin(Changes(1)); + finishBarrier(barrier, rejoin, 0); + + return true; + } + } + + /* Check for a dynamic dispatch. */ + if (cx->typeInferenceEnabled()) { + if (*PC == JSOP_CALLPROP && jsop_getprop_dispatch(name)) + return true; + } + + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_OTHER); + /* * These two must be loaded first. The objReg because the string path * wants to read it, and the shapeReg because it could cause a spill that * the string path wouldn't sink back. */ - RegisterID objReg = Registers::ReturnReg; - RegisterID shapeReg = Registers::ReturnReg; - if (atom == cx->runtime->atomState.lengthAtom) { - objReg = frame.copyDataIntoReg(top); - shapeReg = frame.allocReg(); - } + RegisterID objReg = frame.copyDataIntoReg(top); + RegisterID shapeReg = frame.allocReg(); RESERVE_IC_SPACE(masm); - PICGenInfo pic(ic::PICInfo::GET, JSOp(*PC), usePropCache); + PICGenInfo pic(ic::PICInfo::GET, JSOp(*PC)); + + /* + * If this access has been on a shape with a getter hook, make preparations + * so that we can generate a stub to call the hook directly (rather than be + * forced to make a stub call). Sync the stack up front and kill all + * registers so that PIC stubs can contain calls, and always generate a + * type barrier if inference is enabled (known property types do not + * reflect properties with getter hooks). + */ + pic.canCallHook = pic.forcedTypeBarrier = + !forPrototype && + JSOp(*PC) == JSOP_GETPROP && + analysis->getCode(PC).accessGetter; /* Guard that the type is an object. */ Label typeCheck; @@ -3042,6 +5187,11 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) RegisterID reg = frame.tempRegForType(top); pic.typeReg = reg; + if (pic.canCallHook) { + PinRegAcrossSyncAndKill p1(frame, reg); + frame.syncAndKillEverything(); + } + /* Start the hot path where it's easy to patch it. */ pic.fastPathStart = masm.label(); Jump j = masm.testObject(Assembler::NotEqual, reg); @@ -3051,27 +5201,24 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) pic.typeCheck = stubcc.linkExit(j, Uses(1)); pic.hasTypeCheck = true; } else { + if (pic.canCallHook) + frame.syncAndKillEverything(); + pic.fastPathStart = masm.label(); pic.hasTypeCheck = false; pic.typeReg = Registers::ReturnReg; } - if (atom != cx->runtime->atomState.lengthAtom) { - objReg = frame.copyDataIntoReg(top); - shapeReg = frame.allocReg(); - } - pic.shapeReg = shapeReg; - pic.atom = atom; + pic.name = name; /* Guard on shape. */ masm.loadShape(objReg, shapeReg); pic.shapeGuard = masm.label(); - DataLabel32 inlineShapeLabel; - Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg, - Imm32(int32(INVALID_SHAPE)), - inlineShapeLabel); + DataLabelPtr inlineShapeLabel; + Jump j = masm.branchPtrWithPatch(Assembler::NotEqual, shapeReg, + inlineShapeLabel, ImmPtr(NULL)); Label inlineShapeJump = masm.label(); RESERVE_OOL_SPACE(stubcc.masm); @@ -3079,11 +5226,13 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) stubcc.leave(); passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::GetProp); + pic.slowPathCall = OOL_STUBCALL(forPrototype ? ic::GetPropNoCache : ic::GetProp, rejoin); CHECK_OOL_SPACE(); + if (rejoin == REJOIN_GETTER) + testPushedType(rejoin, -1); /* Load the base slot address. */ - Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)), + Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()), objReg); /* Copy the slot value to the expression stack. */ @@ -3103,317 +5252,414 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) labels.setValueLoad(masm, pic.fastPathRejoin, fastValueLoad); if (pic.hasTypeCheck) labels.setInlineTypeJump(masm, pic.fastPathStart, typeCheck); -#ifdef JS_CPU_X64 - labels.setInlineShapeJump(masm, inlineShapeLabel, inlineShapeJump); -#else labels.setInlineShapeJump(masm, pic.shapeGuard, inlineShapeJump); -#endif + + CHECK_IC_SPACE(); pic.objReg = objReg; - frame.pushRegs(shapeReg, objReg); + frame.pushRegs(shapeReg, objReg, knownType); + BarrierState barrier = testBarrier(pic.shapeReg, pic.objReg, false, false, + /* force = */ pic.canCallHook); stubcc.rejoin(Changes(1)); - pics.append(pic); + + finishBarrier(barrier, rejoin, 0); return true; } bool -mjit::Compiler::jsop_callprop_generic(JSAtom *atom) +mjit::Compiler::testSingletonProperty(JSObject *obj, jsid id) { - FrameEntry *top = frame.peek(-1); - /* - * These two must be loaded first. The objReg because the string path - * wants to read it, and the shapeReg because it could cause a spill that - * the string path wouldn't sink back. + * We would like to completely no-op property/global accesses which can + * produce only a particular JSObject or undefined, provided we can + * determine the pushed value must not be undefined (or, if it could be + * undefined, a recompilation will be triggered). + * + * If the access definitely goes through obj, either directly or on the + * prototype chain, then if obj has a defined property now, and the + * property has a default or method shape, the only way it can produce + * undefined in the future is if it is deleted. Deletion causes type + * properties to be explicitly marked with undefined. */ - RegisterID objReg = frame.copyDataIntoReg(top); - RegisterID shapeReg = frame.allocReg(); - PICGenInfo pic(ic::PICInfo::CALL, JSOp(*PC), true); - - pic.pc = PC; - - /* Guard that the type is an object. */ - pic.typeReg = frame.copyTypeIntoReg(top); - - RESERVE_IC_SPACE(masm); - - /* Start the hot path where it's easy to patch it. */ - pic.fastPathStart = masm.label(); - - /* - * Guard that the value is an object. This part needs some extra gunk - * because the leave() after the shape guard will emit a jump from this - * path to the final call. We need a label in between that jump, which - * will be the target of patched jumps in the PIC. - */ - Jump typeCheckJump = masm.testObject(Assembler::NotEqual, pic.typeReg); - Label typeCheck = masm.label(); - RETURN_IF_OOM(false); + JSObject *nobj = obj; + while (nobj) { + if (!nobj->isNative()) + return false; + if (nobj->getClass()->ops.lookupGeneric) + return false; + nobj = nobj->getProto(); + } - pic.typeCheck = stubcc.linkExit(typeCheckJump, Uses(1)); - pic.hasTypeCheck = true; - pic.objReg = objReg; - pic.shapeReg = shapeReg; - pic.atom = atom; + JSObject *holder; + JSProperty *prop = NULL; + if (!obj->lookupGeneric(cx, id, &holder, &prop)) + return false; + if (!prop) + return false; - /* - * Store the type and object back. Don't bother keeping them in registers, - * since a sync will be needed for the upcoming call. - */ - uint32 thisvSlot = frame.localSlots(); - Address thisv = Address(JSFrameReg, sizeof(StackFrame) + thisvSlot * sizeof(Value)); - -#if defined JS_NUNBOX32 - masm.storeValueFromComponents(pic.typeReg, pic.objReg, thisv); -#elif defined JS_PUNBOX64 - masm.orPtr(pic.objReg, pic.typeReg); - masm.storePtr(pic.typeReg, thisv); -#endif + Shape *shape = (Shape *) prop; + if (shape->hasDefaultGetter()) { + if (!shape->hasSlot()) + return false; + if (holder->getSlot(shape->slot()).isUndefined()) + return false; + } else if (!shape->isMethod()) { + return false; + } - frame.freeReg(pic.typeReg); + return true; +} - /* Guard on shape. */ - masm.loadShape(objReg, shapeReg); - pic.shapeGuard = masm.label(); +bool +mjit::Compiler::testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testObject) +{ + *testObject = false; - DataLabel32 inlineShapeLabel; - Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg, - Imm32(int32(INVALID_SHAPE)), - inlineShapeLabel); - Label inlineShapeJump = masm.label(); + types::TypeSet *types = frame.extra(top).types; + if (!types || types->unknownObject()) + return false; - /* Slow path. */ - RESERVE_OOL_SPACE(stubcc.masm); - pic.slowPathStart = stubcc.linkExit(j, Uses(1)); - stubcc.leave(); - passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::CallProp); - CHECK_OOL_SPACE(); + JSObject *singleton = types->getSingleton(cx); + if (singleton) + return testSingletonProperty(singleton, id); - /* Adjust the frame. None of this will generate code. */ - frame.pop(); - frame.pushRegs(shapeReg, objReg); - frame.pushSynced(); + if (!globalObj) + return false; - /* Load the base slot address. */ - Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)), - objReg); + JSProtoKey key; + JSValueType type = types->getKnownTypeTag(cx); + switch (type) { + case JSVAL_TYPE_STRING: + key = JSProto_String; + break; - /* Copy the slot value to the expression stack. */ - Address slot(objReg, 1 << 24); + case JSVAL_TYPE_INT32: + case JSVAL_TYPE_DOUBLE: + key = JSProto_Number; + break; - Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg); - pic.fastPathRejoin = masm.label(); + case JSVAL_TYPE_BOOLEAN: + key = JSProto_Boolean; + break; - RETURN_IF_OOM(false); + case JSVAL_TYPE_OBJECT: + case JSVAL_TYPE_UNKNOWN: + if (types->getObjectCount() == 1 && !top->isNotType(JSVAL_TYPE_OBJECT)) { + JS_ASSERT_IF(top->isTypeKnown(), top->isType(JSVAL_TYPE_OBJECT)); + types::TypeObject *object = types->getTypeObject(0); + if (object && object->proto) { + if (!testSingletonProperty(object->proto, id)) + return false; + types->addFreeze(cx); + + /* If we don't know this is an object, we will need a test. */ + *testObject = (type != JSVAL_TYPE_OBJECT) && !top->isTypeKnown(); + return true; + } + } + return false; - /* - * Initialize op labels. We use GetPropLabels here because we have the same patching - * requirements for CallProp. - */ - GetPropLabels &labels = pic.getPropLabels(); - labels.setDslotsLoadOffset(masm.differenceBetween(pic.fastPathRejoin, dslotsLoadLabel)); - labels.setInlineShapeOffset(masm.differenceBetween(pic.shapeGuard, inlineShapeLabel)); - labels.setValueLoad(masm, pic.fastPathRejoin, fastValueLoad); - labels.setInlineTypeJump(masm, pic.fastPathStart, typeCheck); -#ifdef JS_CPU_X64 - labels.setInlineShapeJump(masm, inlineShapeLabel, inlineShapeJump); -#else - labels.setInlineShapeJump(masm, pic.shapeGuard, inlineShapeJump); -#endif + default: + return false; + } - stubcc.rejoin(Changes(2)); - pics.append(pic); + JSObject *proto; + if (!js_GetClassPrototype(cx, globalObj, key, &proto, NULL)) + return NULL; - return true; + return testSingletonProperty(proto, id); } bool -mjit::Compiler::jsop_callprop_str(JSAtom *atom) +mjit::Compiler::jsop_getprop_dispatch(PropertyName *name) { - if (!script->compileAndGo) { - jsop_callprop_slow(atom); - return true; - } - /* - * Bake in String.prototype. This is safe because of compileAndGo. - * We must pass an explicit scope chain only because JSD calls into - * here via the recompiler with a dummy context, and we need to use - * the global object for the script we are now compiling. + * Check for a CALLPROP which is a dynamic dispatch: every value it can + * push is a singleton, and the pushed value is determined by the type of + * the object being accessed. Return true if the CALLPROP has been fully + * processed, false if no code was generated. */ - JSObject *obj; - if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj)) + FrameEntry *top = frame.peek(-1); + if (top->isNotType(JSVAL_TYPE_OBJECT)) return false; - /* Force into a register because getprop won't expect a constant. */ - RegisterID reg = frame.allocReg(); - - masm.move(ImmPtr(obj), reg); - frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); - - /* Get the property. */ - if (!jsop_getprop(atom)) + jsid id = ATOM_TO_JSID(name); + if (id != types::MakeTypeId(cx, id)) return false; - /* Perform a swap. */ - frame.dup2(); - frame.shift(-3); - frame.shift(-1); - - /* 4) Test if the function can take a primitive. */ -#ifdef DEBUG - FrameEntry *funFe = frame.peek(-2); -#endif - JS_ASSERT(!funFe->isTypeKnown()); + types::TypeSet *pushedTypes = pushedTypeSet(0); + if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0) + return false; - /* - * See bug 584579 - need to forget string type, since wrapping could - * create an object. forgetType() alone is not valid because it cannot be - * used on copies or constants. - */ - RegisterID strReg; - FrameEntry *strFe = frame.peek(-1); - if (strFe->isConstant()) { - strReg = frame.allocReg(); - masm.move(ImmPtr(strFe->getValue().toString()), strReg); - } else { - strReg = frame.ownRegForData(strFe); + /* Check every pushed value is a singleton. */ + for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) { + if (pushedTypes->getTypeObject(i) != NULL) + return false; } - frame.pop(); - frame.pushTypedPayload(JSVAL_TYPE_STRING, strReg); - frame.forgetType(frame.peek(-1)); - - return true; -} -bool -mjit::Compiler::jsop_callprop_obj(JSAtom *atom) -{ - FrameEntry *top = frame.peek(-1); - - PICGenInfo pic(ic::PICInfo::CALL, JSOp(*PC), true); - - JS_ASSERT(top->isTypeKnown()); - JS_ASSERT(top->getKnownType() == JSVAL_TYPE_OBJECT); - - RESERVE_IC_SPACE(masm); + types::TypeSet *objTypes = analysis->poppedTypes(PC, 0); + if (objTypes->unknownObject() || objTypes->getObjectCount() == 0) + return false; - pic.pc = PC; - pic.fastPathStart = masm.label(); - pic.hasTypeCheck = false; - pic.typeReg = Registers::ReturnReg; + pushedTypes->addFreeze(cx); - RegisterID objReg = frame.copyDataIntoReg(top); - RegisterID shapeReg = frame.allocReg(); + /* Map each type in the object to the resulting pushed value. */ + Vector results(CompilerAllocPolicy(cx, *this)); - pic.shapeReg = shapeReg; - pic.atom = atom; + /* + * For each type of the base object, check it has no 'own' property for the + * accessed id and that its prototype does have such a property. + */ + uint32_t last = 0; + for (unsigned i = 0; i < objTypes->getObjectCount(); i++) { + if (objTypes->getSingleObject(i) != NULL) + return false; + types::TypeObject *object = objTypes->getTypeObject(i); + if (!object) { + results.append((JSObject *) NULL); + continue; + } + if (object->unknownProperties() || !object->proto) + return false; + types::TypeSet *ownTypes = object->getProperty(cx, id, false); + if (ownTypes->isOwnProperty(cx, object, false)) + return false; - /* Guard on shape. */ - masm.loadShape(objReg, shapeReg); - pic.shapeGuard = masm.label(); + if (!testSingletonProperty(object->proto, id)) + return false; - DataLabel32 inlineShapeLabel; - Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg, - Imm32(int32(INVALID_SHAPE)), - inlineShapeLabel); - Label inlineShapeJump = masm.label(); + if (object->proto->getType(cx)->unknownProperties()) + return false; + types::TypeSet *protoTypes = object->proto->type()->getProperty(cx, id, false); + if (!protoTypes) + return false; + JSObject *singleton = protoTypes->getSingleton(cx); + if (!singleton) + return false; - /* Slow path. */ - RESERVE_OOL_SPACE(stubcc.masm); - pic.slowPathStart = stubcc.linkExit(j, Uses(1)); - stubcc.leave(); - passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::CallProp); - CHECK_OOL_SPACE(); + results.append(singleton); + last = i; + } - /* Load the base slot address. */ - Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)), - objReg); + if (oomInVector) + return false; - /* Copy the slot value to the expression stack. */ - Address slot(objReg, 1 << 24); + objTypes->addFreeze(cx); - Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg); + /* Done filtering, now generate code which dispatches on the type. */ - pic.fastPathRejoin = masm.label(); - pic.objReg = objReg; + frame.forgetMismatchedObject(top); - /* - * 1) Dup the |this| object. - * 2) Push the property value onto the stack. - * 3) Move the value below the dup'd |this|, uncopying it. This could - * generate code, thus the fastPathRejoin label being prior. This is safe - * as a stack transition, because JSOP_CALLPROP has JOF_TMPSLOT. It is - * also safe for correctness, because if we know the LHS is an object, it - * is the resulting vp[1]. - */ - frame.dup(); - frame.pushRegs(shapeReg, objReg); - frame.shift(-2); + if (!top->isType(JSVAL_TYPE_OBJECT)) { + Jump notObject = frame.testObject(Assembler::NotEqual, top); + stubcc.linkExit(notObject, Uses(1)); + } - /* - * Assert correctness of hardcoded offsets. - * No type guard: type is asserted. - */ - RETURN_IF_OOM(false); + RegisterID reg = frame.tempRegForData(top); + frame.pinReg(reg); + RegisterID pushreg = frame.allocReg(); + frame.unpinReg(reg); - GetPropLabels &labels = pic.getPropLabels(); - labels.setDslotsLoadOffset(masm.differenceBetween(pic.fastPathRejoin, dslotsLoadLabel)); - labels.setInlineShapeOffset(masm.differenceBetween(pic.shapeGuard, inlineShapeLabel)); - labels.setValueLoad(masm, pic.fastPathRejoin, fastValueLoad); -#ifdef JS_CPU_X64 - labels.setInlineShapeJump(masm, inlineShapeLabel, inlineShapeJump); -#else - labels.setInlineShapeJump(masm, pic.shapeGuard, inlineShapeJump); -#endif + Address typeAddress(reg, JSObject::offsetOfType()); - stubcc.rejoin(Changes(2)); - pics.append(pic); + Vector rejoins(CompilerAllocPolicy(cx, *this)); + MaybeJump lastMiss; - return true; -} + for (unsigned i = 0; i < objTypes->getObjectCount(); i++) { + types::TypeObject *object = objTypes->getTypeObject(i); + if (!object) { + JS_ASSERT(results[i] == NULL); + continue; + } + if (lastMiss.isSet()) + lastMiss.get().linkTo(masm.label(), &masm); -bool -mjit::Compiler::jsop_callprop(JSAtom *atom) -{ - FrameEntry *top = frame.peek(-1); + /* + * Check that the pushed result is actually in the known pushed types + * for the bytecode; this bytecode may have type barriers. Redirect to + * the stub to update said pushed types. + */ + if (!pushedTypes->hasType(types::Type::ObjectType(results[i]))) { + JS_ASSERT(hasTypeBarriers(PC)); + if (i == last) { + stubcc.linkExit(masm.jump(), Uses(1)); + break; + } else { + lastMiss.setJump(masm.branchPtr(Assembler::NotEqual, typeAddress, ImmPtr(object))); + stubcc.linkExit(masm.jump(), Uses(1)); + continue; + } + } - /* If the incoming type will never PIC, take slow path. */ - if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) { - if (top->getKnownType() == JSVAL_TYPE_STRING) - return jsop_callprop_str(atom); - return jsop_callprop_slow(atom); + if (i == last) { + masm.move(ImmPtr(results[i]), pushreg); + break; + } else { + lastMiss.setJump(masm.branchPtr(Assembler::NotEqual, typeAddress, ImmPtr(object))); + masm.move(ImmPtr(results[i]), pushreg); + rejoins.append(masm.jump()); + } } - if (top->isTypeKnown()) - return jsop_callprop_obj(atom); - return jsop_callprop_generic(atom); + for (unsigned i = 0; i < rejoins.length(); i++) + rejoins[i].linkTo(masm.label(), &masm); + + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::GetProp, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -1); + + frame.pop(); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, pushreg); + + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + + stubcc.rejoin(Changes(2)); + return true; } bool -mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed) { FrameEntry *lhs = frame.peek(-2); FrameEntry *rhs = frame.peek(-1); /* If the incoming type will never PIC, take slow path. */ if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) { - jsop_setprop_slow(atom, usePropCache); + jsop_setprop_slow(name); return true; } + /* + * If this is a SETNAME to a variable of a non-reentrant outer function, + * set the variable's slot directly for the active call object. + */ + if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(name), true); + if (access.nesting) { + /* Use a SavedReg so it isn't clobbered by the stub call. */ + RegisterID nameReg = frame.allocReg(Registers::SavedRegs).reg(); + Address address = frame.loadNameAddress(access, nameReg); + +#ifdef JSGC_INCREMENTAL_MJ + /* Write barrier. */ + if (cx->compartment->needsBarrier()) { + stubcc.linkExit(masm.jump(), Uses(0)); + stubcc.leave(); + + /* sync() may have overwritten nameReg, so we reload its data. */ + JS_ASSERT(address.base == nameReg); + stubcc.masm.move(ImmPtr(access.basePointer()), nameReg); + stubcc.masm.loadPtr(Address(nameReg), nameReg); + stubcc.masm.addPtr(Imm32(address.offset), nameReg, Registers::ArgReg1); + + OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE); + stubcc.rejoin(Changes(0)); + } +#endif + + frame.storeTo(rhs, address, popGuaranteed); + frame.shimmy(1); + frame.freeReg(address.base); + return true; + } + } + + /* + * Set the property directly if we are accessing a known object which + * always has the property in a particular inline slot. + */ + jsid id = ATOM_TO_JSID(name); + types::TypeSet *types = frame.extra(lhs).types; + if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) && + types && !types->unknownObject() && + types->getObjectCount() == 1 && + types->getTypeObject(0) != NULL && + !types->getTypeObject(0)->unknownProperties()) { + types::TypeObject *object = types->getTypeObject(0); + types::TypeSet *propertyTypes = object->getProperty(cx, id, false); + if (!propertyTypes) + return false; + if (propertyTypes->isDefiniteProperty() && + !propertyTypes->isOwnProperty(cx, object, true)) { + types->addFreeze(cx); + uint32_t slot = propertyTypes->definiteSlot(); + RegisterID reg = frame.tempRegForData(lhs); + bool isObject = lhs->isTypeKnown(); + MaybeJump notObject; + if (!isObject) + notObject = frame.testObject(Assembler::NotEqual, lhs); +#ifdef JSGC_INCREMENTAL_MJ + frame.pinReg(reg); + if (cx->compartment->needsBarrier() && propertyTypes->needsBarrier(cx)) { + /* Write barrier. */ + Jump j = masm.testGCThing(Address(reg, JSObject::getFixedSlotOffset(slot))); + stubcc.linkExit(j, Uses(0)); + stubcc.leave(); + stubcc.masm.addPtr(Imm32(JSObject::getFixedSlotOffset(slot)), + reg, Registers::ArgReg1); + OOL_STUBCALL(stubs::GCThingWriteBarrier, REJOIN_NONE); + stubcc.rejoin(Changes(0)); + } + frame.unpinReg(reg); +#endif + if (!isObject) { + stubcc.linkExit(notObject.get(), Uses(2)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH); + } + frame.storeTo(rhs, Address(reg, JSObject::getFixedSlotOffset(slot)), popGuaranteed); + frame.shimmy(1); + if (!isObject) + stubcc.rejoin(Changes(1)); + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE); + return true; + } + } + + if (script->pcCounters) + bumpPropCounter(PC, OpcodeCounts::PROP_OTHER); + JSOp op = JSOp(*PC); +#ifdef JSGC_INCREMENTAL_MJ + /* Write barrier. We don't have type information for JSOP_SETNAME. */ + if (cx->compartment->needsBarrier() && + (!types || op == JSOP_SETNAME || types->propertyNeedsBarrier(cx, id))) + { + jsop_setprop_slow(name); + return true; + } +#endif + ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD) ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET; - PICGenInfo pic(kind, op, usePropCache); - pic.atom = atom; + PICGenInfo pic(kind, op); + pic.name = name; + + if (monitored(PC)) { + pic.typeMonitored = true; + types::TypeSet *types = frame.extra(rhs).types; + if (!types) { + /* Handle FORNAME and other compound opcodes. Yuck. */ + types = types::TypeSet::make(cx, "unknownRHS"); + if (!types) + return false; + types->addType(cx, types::Type::UnknownType()); + } + pic.rhsTypes = types; + } else { + pic.typeMonitored = false; + pic.rhsTypes = NULL; + } RESERVE_IC_SPACE(masm); RESERVE_OOL_SPACE(stubcc.masm); @@ -3431,11 +5677,9 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) pic.typeCheck = stubcc.linkExit(j, Uses(2)); stubcc.leave(); - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); - if (usePropCache) - OOL_STUBCALL(STRICT_VARIANT(stubs::SetName)); - else - OOL_STUBCALL(STRICT_VARIANT(stubs::SetPropNoCache)); + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH); + typeCheck = stubcc.masm.jump(); pic.hasTypeCheck = true; } else { @@ -3444,6 +5688,8 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) pic.typeReg = Registers::ReturnReg; } + frame.forgetMismatchedObject(lhs); + /* Get the object into a mutable register. */ RegisterID objReg = frame.copyDataIntoReg(lhs); pic.objReg = objReg; @@ -3461,10 +5707,9 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) /* Guard on shape. */ masm.loadShape(objReg, shapeReg); pic.shapeGuard = masm.label(); - DataLabel32 inlineShapeData; - Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg, - Imm32(int32(INVALID_SHAPE)), - inlineShapeData); + DataLabelPtr inlineShapeData; + Jump j = masm.branchPtrWithPatch(Assembler::NotEqual, shapeReg, + inlineShapeData, ImmPtr(NULL)); Label afterInlineShapeJump = masm.label(); /* Slow path. */ @@ -3473,12 +5718,12 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) stubcc.leave(); passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::SetProp); + pic.slowPathCall = OOL_STUBCALL(ic::SetProp, REJOIN_FALLTHROUGH); CHECK_OOL_SPACE(); } /* Load dslots. */ - Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)), + Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()), objReg); /* Store RHS into object slot. */ @@ -3503,8 +5748,8 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) SetPropLabels &labels = pic.setPropLabels(); labels.setInlineShapeData(masm, pic.shapeGuard, inlineShapeData); - labels.setDslotsLoad(masm, pic.fastPathRejoin, dslotsLoadLabel, vr); - labels.setInlineValueStore(masm, pic.fastPathRejoin, inlineValueStore, vr); + labels.setDslotsLoad(masm, pic.fastPathRejoin, dslotsLoadLabel); + labels.setInlineValueStore(masm, pic.fastPathRejoin, inlineValueStore); labels.setInlineShapeJump(masm, pic.shapeGuard, afterInlineShapeJump); pics.append(pic); @@ -3512,16 +5757,34 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) } void -mjit::Compiler::jsop_name(JSAtom *atom) +mjit::Compiler::jsop_name(PropertyName *name, JSValueType type) { - PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC), true); + /* + * If this is a NAME for a variable of a non-reentrant outer function, get + * the variable's slot directly for the active call object. We always need + * to check for undefined, however. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(name), true); + if (access.nesting) { + Address address = frame.loadNameAddress(access); + JSValueType type = knownPushedType(0); + BarrierState barrier = pushAddressMaybeBarrier(address, type, true, + /* testUndefined = */ true); + finishBarrier(barrier, REJOIN_GETTER, 0); + return; + } + } + + PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC)); RESERVE_IC_SPACE(masm); pic.shapeReg = frame.allocReg(); pic.objReg = frame.allocReg(); pic.typeReg = Registers::ReturnReg; - pic.atom = atom; + pic.name = name; pic.hasTypeCheck = false; pic.fastPathStart = masm.label(); @@ -3533,8 +5796,9 @@ mjit::Compiler::jsop_name(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(inlineJump, Uses(0)); stubcc.leave(); passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::Name); + pic.slowPathCall = OOL_STUBCALL(ic::Name, REJOIN_GETTER); CHECK_OOL_SPACE(); + testPushedType(REJOIN_GETTER, 0); } pic.fastPathRejoin = masm.label(); @@ -3542,21 +5806,56 @@ mjit::Compiler::jsop_name(JSAtom *atom) ScopeNameLabels &labels = pic.scopeNameLabels(); labels.setInlineJump(masm, pic.fastPathStart, inlineJump); - frame.pushRegs(pic.shapeReg, pic.objReg); + CHECK_IC_SPACE(); + + /* + * We can't optimize away the PIC for the NAME access itself, but if we've + * only seen a single value pushed by this access, mark it as such and + * recompile if a different value becomes possible. + */ + JSObject *singleton = pushedSingleton(0); + if (singleton) { + frame.push(ObjectValue(*singleton)); + frame.freeReg(pic.shapeReg); + frame.freeReg(pic.objReg); + } else { + frame.pushRegs(pic.shapeReg, pic.objReg, type); + } + BarrierState barrier = testBarrier(pic.shapeReg, pic.objReg, /* testUndefined = */ true); stubcc.rejoin(Changes(1)); pics.append(pic); + + finishBarrier(barrier, REJOIN_GETTER, 0); } bool -mjit::Compiler::jsop_xname(JSAtom *atom) +mjit::Compiler::jsop_xname(PropertyName *name) { - PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true); + /* + * If this is a GETXPROP for a variable of a non-reentrant outer function, + * treat in the same way as a NAME. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(name), true); + if (access.nesting) { + frame.pop(); + Address address = frame.loadNameAddress(access); + JSValueType type = knownPushedType(0); + BarrierState barrier = pushAddressMaybeBarrier(address, type, true, + /* testUndefined = */ true); + finishBarrier(barrier, REJOIN_GETTER, 0); + return true; + } + } + + PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC)); FrameEntry *fe = frame.peek(-1); if (fe->isNotType(JSVAL_TYPE_OBJECT)) { - return jsop_getprop(atom); + return jsop_getprop(name, knownPushedType(0)); } if (!fe->isTypeKnown()) { @@ -3564,12 +5863,14 @@ mjit::Compiler::jsop_xname(JSAtom *atom) stubcc.linkExit(notObject, Uses(1)); } + frame.forgetMismatchedObject(fe); + RESERVE_IC_SPACE(masm); pic.shapeReg = frame.allocReg(); pic.objReg = frame.copyDataIntoReg(fe); pic.typeReg = Registers::ReturnReg; - pic.atom = atom; + pic.name = name; pic.hasTypeCheck = false; pic.fastPathStart = masm.label(); @@ -3581,8 +5882,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(inlineJump, Uses(1)); stubcc.leave(); passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::XName); + pic.slowPathCall = OOL_STUBCALL(ic::XName, REJOIN_GETTER); CHECK_OOL_SPACE(); + testPushedType(REJOIN_GETTER, -1); } pic.fastPathRejoin = masm.label(); @@ -3593,19 +5895,42 @@ mjit::Compiler::jsop_xname(JSAtom *atom) ScopeNameLabels &labels = pic.scopeNameLabels(); labels.setInlineJumpOffset(masm.differenceBetween(pic.fastPathStart, inlineJump)); + CHECK_IC_SPACE(); + frame.pop(); - frame.pushRegs(pic.shapeReg, pic.objReg); + frame.pushRegs(pic.shapeReg, pic.objReg, knownPushedType(0)); + + BarrierState barrier = testBarrier(pic.shapeReg, pic.objReg, /* testUndefined = */ true); stubcc.rejoin(Changes(1)); pics.append(pic); + + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); return true; } void -mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_bindname(PropertyName *name) { - PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache); + /* + * If this is a BINDNAME for a variable of a non-reentrant outer function, + * the object is definitely the outer function's active call object. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(name), true); + if (access.nesting) { + RegisterID reg = frame.allocReg(); + JSObject **pobj = &access.nesting->activeCall; + masm.move(ImmPtr(pobj), reg); + masm.loadPtr(Address(reg), reg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); + return; + } + } + + PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC)); // This code does not check the frame flags to see if scopeChain has been // set. Rather, it relies on the up-front analysis statically determining @@ -3616,23 +5941,25 @@ mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) pic.shapeReg = frame.allocReg(); pic.objReg = frame.allocReg(); pic.typeReg = Registers::ReturnReg; - pic.atom = atom; + pic.name = name; pic.hasTypeCheck = false; RESERVE_IC_SPACE(masm); pic.fastPathStart = masm.label(); - Address parent(pic.objReg, offsetof(JSObject, parent)); masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); + masm.loadPtr(Address(pic.objReg, JSObject::offsetOfShape()), pic.shapeReg); + masm.loadPtr(Address(pic.shapeReg, Shape::offsetOfBase()), pic.shapeReg); + Address parent(pic.shapeReg, BaseShape::offsetOfParent()); pic.shapeGuard = masm.label(); - Jump inlineJump = masm.branchPtr(Assembler::NotEqual, parent, ImmPtr(0)); + Jump inlineJump = masm.branchPtr(Assembler::NotEqual, parent, ImmPtr(NULL)); { RESERVE_OOL_SPACE(stubcc.masm); pic.slowPathStart = stubcc.linkExit(inlineJump, Uses(0)); stubcc.leave(); passICAddress(&pic); - pic.slowPathCall = OOL_STUBCALL(ic::BindName); + pic.slowPathCall = OOL_STUBCALL(ic::BindName, REJOIN_FALLTHROUGH); CHECK_OOL_SPACE(); } @@ -3653,41 +5980,39 @@ mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) #else /* !JS_POLYIC */ void -mjit::Compiler::jsop_name(JSAtom *atom) +mjit::Compiler::jsop_name(PropertyName *name, JSValueType type, bool isCall) { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::Name); - frame.pushSynced(); + INLINE_STUBCALL(isCall ? stubs::CallName : stubs::Name, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, 0, /* ool = */ false); + frame.pushSynced(type); + if (isCall) + frame.pushSynced(JSVAL_TYPE_UNKNOWN); } bool -mjit::Compiler::jsop_xname(JSAtom *atom) +mjit::Compiler::jsop_xname(PropertyName *name) { - return jsop_getprop(atom); + return jsop_getprop(name, knownPushedType(0), pushedTypeSet(0)); } bool -mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck, bool usePropCache) +mjit::Compiler::jsop_getprop(PropertyName *name, JSValueType knownType, types::TypeSet *typeSet, + bool typecheck, bool forPrototype) { - jsop_getprop_slow(atom, usePropCache); + jsop_getprop_slow(name, forPrototype); return true; } bool -mjit::Compiler::jsop_callprop(JSAtom *atom) -{ - return jsop_callprop_slow(atom); -} - -bool -mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop(PropertyName *name) { - jsop_setprop_slow(atom, usePropCache); + jsop_setprop_slow(name); return true; } void -mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_bindname(PropertyName *name) { RegisterID reg = frame.allocReg(); Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain()); @@ -3699,12 +6024,8 @@ mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) stubcc.linkExit(j, Uses(0)); stubcc.leave(); - if (usePropCache) { - OOL_STUBCALL(stubs::BindName); - } else { - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); - OOL_STUBCALL(stubs::BindNameNoCache); - } + stubcc.masm.move(ImmPtr(name), Registers::ArgReg1); + OOL_STUBCALL(stubs::BindName, REJOIN_FALLTHROUGH); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); @@ -3717,23 +6038,42 @@ mjit::Compiler::jsop_this() { frame.pushThis(); - /* + /* * In strict mode code, we don't wrap 'this'. * In direct-call eval code, we wrapped 'this' before entering the eval. * In global code, 'this' is always an object. */ - if (fun && !script->strictModeCode) { + if (script->function() && !script->strictModeCode) { FrameEntry *thisFe = frame.peek(-1); - if (!thisFe->isTypeKnown()) { - Jump notObj = frame.testObject(Assembler::NotEqual, thisFe); - stubcc.linkExit(notObj, Uses(1)); - stubcc.leave(); - OOL_STUBCALL(stubs::This); - stubcc.rejoin(Changes(1)); + + if (!thisFe->isType(JSVAL_TYPE_OBJECT)) { + /* + * Watch out for an obscure case where we don't know we are pushing + * an object: the script has not yet had a 'this' value assigned, + * so no pushed 'this' type has been inferred. Don't mark the type + * as known in this case, preserving the invariant that compiler + * types reflect inferred types. + */ + if (cx->typeInferenceEnabled() && knownPushedType(0) != JSVAL_TYPE_OBJECT) { + prepareStubCall(Uses(1)); + INLINE_STUBCALL(stubs::This, REJOIN_FALLTHROUGH); + return; + } + + JSValueType type = cx->typeInferenceEnabled() + ? types::TypeScript::ThisTypes(script)->getKnownTypeTag(cx) + : JSVAL_TYPE_UNKNOWN; + if (type != JSVAL_TYPE_OBJECT) { + Jump notObj = frame.testObject(Assembler::NotEqual, thisFe); + stubcc.linkExit(notObj, Uses(1)); + stubcc.leave(); + OOL_STUBCALL(stubs::This, REJOIN_FALLTHROUGH); + stubcc.rejoin(Changes(1)); + } // Now we know that |this| is an object. frame.pop(); - frame.learnThisIsObject(); + frame.learnThisIsObject(type != JSVAL_TYPE_OBJECT); frame.pushThis(); } @@ -3741,274 +6081,6 @@ mjit::Compiler::jsop_this() } } -void -mjit::Compiler::jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index) -{ - JSAtom *atom = script->getAtom(index); - -#if defined JS_MONOIC - jsbytecode *next = &PC[JSOP_GNAMEINC_LENGTH]; - bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next); - int amt = (op == JSOP_GNAMEINC || op == JSOP_INCGNAME) ? -1 : 1; - - if (pop || (op == JSOP_INCGNAME || op == JSOP_DECGNAME)) { - /* These cases are easy, the original value is not observed. */ - - jsop_getgname(index); - // V - - frame.push(Int32Value(amt)); - // V 1 - - /* Use sub since it calls ValueToNumber instead of string concat. */ - jsop_binary(JSOP_SUB, stubs::Sub); - // N+1 - - jsop_bindgname(); - // V+1 OBJ - - frame.dup2(); - // V+1 OBJ V+1 OBJ - - frame.shift(-3); - // OBJ OBJ V+1 - - frame.shift(-1); - // OBJ V+1 - - jsop_setgname(atom, false); - // V+1 - - if (pop) - frame.pop(); - } else { - /* The pre-value is observed, making this more tricky. */ - - jsop_getgname(index); - // V - - jsop_pos(); - // N - - frame.dup(); - // N N - - frame.push(Int32Value(-amt)); - // N N 1 - - jsop_binary(JSOP_ADD, stubs::Add); - // N N+1 - - jsop_bindgname(); - // N N+1 OBJ - - frame.dup2(); - // N N+1 OBJ N+1 OBJ - - frame.shift(-3); - // N OBJ OBJ N+1 - - frame.shift(-1); - // N OBJ N+1 - - jsop_setgname(atom, false); - // N N+1 - - frame.pop(); - // N - } - - if (pop) - PC += JSOP_POP_LENGTH; -#else - prepareStubCall(Uses(0)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stub); - frame.pushSynced(); -#endif - - PC += JSOP_GNAMEINC_LENGTH; -} - -bool -mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index) -{ - JSAtom *atom = script->getAtom(index); -#if defined JS_POLYIC - jsbytecode *next = &PC[JSOP_NAMEINC_LENGTH]; - bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next); - int amt = (op == JSOP_NAMEINC || op == JSOP_INCNAME) ? -1 : 1; - - if (pop || (op == JSOP_INCNAME || op == JSOP_DECNAME)) { - /* These cases are easy, the original value is not observed. */ - - jsop_name(atom); - // V - - frame.push(Int32Value(amt)); - // V 1 - - /* Use sub since it calls ValueToNumber instead of string concat. */ - jsop_binary(JSOP_SUB, stubs::Sub); - // N+1 - - jsop_bindname(atom, false); - // V+1 OBJ - - frame.dup2(); - // V+1 OBJ V+1 OBJ - - frame.shift(-3); - // OBJ OBJ V+1 - - frame.shift(-1); - // OBJ V+1 - - if (!jsop_setprop(atom, false)) - return false; - // V+1 - - if (pop) - frame.pop(); - } else { - /* The pre-value is observed, making this more tricky. */ - - jsop_name(atom); - // V - - jsop_pos(); - // N - - frame.dup(); - // N N - - frame.push(Int32Value(-amt)); - // N N 1 - - jsop_binary(JSOP_ADD, stubs::Add); - // N N+1 - - jsop_bindname(atom, false); - // N N+1 OBJ - - frame.dup2(); - // N N+1 OBJ N+1 OBJ - - frame.shift(-3); - // N OBJ OBJ N+1 - - frame.shift(-1); - // N OBJ N+1 - - if (!jsop_setprop(atom, false)) - return false; - // N N+1 - - frame.pop(); - // N - } - - if (pop) - PC += JSOP_POP_LENGTH; -#else - prepareStubCall(Uses(0)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stub); - frame.pushSynced(); -#endif - - PC += JSOP_NAMEINC_LENGTH; - return true; -} - -bool -mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index) -{ - JSAtom *atom = script->getAtom(index); -#if defined JS_POLYIC - FrameEntry *objFe = frame.peek(-1); - if (!objFe->isTypeKnown() || objFe->getKnownType() == JSVAL_TYPE_OBJECT) { - jsbytecode *next = &PC[JSOP_PROPINC_LENGTH]; - bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next); - int amt = (op == JSOP_PROPINC || op == JSOP_INCPROP) ? -1 : 1; - - if (pop || (op == JSOP_INCPROP || op == JSOP_DECPROP)) { - /* These cases are easy, the original value is not observed. */ - - frame.dup(); - // OBJ OBJ - - if (!jsop_getprop(atom)) - return false; - // OBJ V - - frame.push(Int32Value(amt)); - // OBJ V 1 - - /* Use sub since it calls ValueToNumber instead of string concat. */ - jsop_binary(JSOP_SUB, stubs::Sub); - // OBJ V+1 - - if (!jsop_setprop(atom, false)) - return false; - // V+1 - - if (pop) - frame.pop(); - } else { - /* The pre-value is observed, making this more tricky. */ - - frame.dup(); - // OBJ OBJ - - if (!jsop_getprop(atom)) - return false; - // OBJ V - - jsop_pos(); - // OBJ N - - frame.dup(); - // OBJ N N - - frame.push(Int32Value(-amt)); - // OBJ N N 1 - - jsop_binary(JSOP_ADD, stubs::Add); - // OBJ N N+1 - - frame.dupAt(-3); - // OBJ N N+1 OBJ - - frame.dupAt(-2); - // OBJ N N+1 OBJ N+1 - - if (!jsop_setprop(atom, false)) - return false; - // OBJ N N+1 N+1 - - frame.popn(2); - // OBJ N - - frame.shimmy(1); - // N - } - if (pop) - PC += JSOP_POP_LENGTH; - } else -#endif - { - prepareStubCall(Uses(1)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stub); - frame.pop(); - frame.pushSynced(); - } - - PC += JSOP_PROPINC_LENGTH; - return true; -} - bool mjit::Compiler::iter(uintN flags) { @@ -4021,9 +6093,9 @@ mjit::Compiler::iter(uintN flags) if ((flags != JSITER_ENUMERATE) || fe->isNotType(JSVAL_TYPE_OBJECT)) { prepareStubCall(Uses(1)); masm.move(Imm32(flags), Registers::ArgReg1); - INLINE_STUBCALL(stubs::Iter); + INLINE_STUBCALL(stubs::Iter, REJOIN_FALLTHROUGH); frame.pop(); - frame.pushSynced(); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); return true; } @@ -4032,6 +6104,8 @@ mjit::Compiler::iter(uintN flags) stubcc.linkExit(notObject, Uses(1)); } + frame.forgetMismatchedObject(fe); + RegisterID reg = frame.tempRegForData(fe); frame.pinReg(reg); @@ -4042,14 +6116,14 @@ mjit::Compiler::iter(uintN flags) frame.unpinReg(reg); /* Fetch the most recent iterator. */ - masm.loadPtr(&script->compartment->nativeIterCache.last, ioreg); + masm.loadPtr(&script->compartment()->nativeIterCache.last, ioreg); /* Test for NULL. */ Jump nullIterator = masm.branchTest32(Assembler::Zero, ioreg, ioreg); stubcc.linkExit(nullIterator, Uses(1)); /* Get NativeIterator from iter obj. */ - masm.loadObjPrivate(ioreg, nireg); + masm.loadObjPrivate(ioreg, nireg, JSObject::ITER_CLASS_NFIXED_SLOTS); /* Test for active iterator. */ Address flagsAddr(nireg, offsetof(NativeIterator, flags)); @@ -4061,16 +6135,17 @@ mjit::Compiler::iter(uintN flags) /* Compare shape of object with iterator. */ masm.loadShape(reg, T1); masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2); - masm.load32(Address(T2, 0), T2); - Jump mismatchedObject = masm.branch32(Assembler::NotEqual, T1, T2); + masm.loadPtr(Address(T2, 0), T2); + Jump mismatchedObject = masm.branchPtr(Assembler::NotEqual, T1, T2); stubcc.linkExit(mismatchedObject, Uses(1)); /* Compare shape of object's prototype with iterator. */ - masm.loadPtr(Address(reg, offsetof(JSObject, proto)), T1); + masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1); + masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1); masm.loadShape(T1, T1); masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2); - masm.load32(Address(T2, sizeof(uint32)), T2); - Jump mismatchedProto = masm.branch32(Assembler::NotEqual, T1, T2); + masm.loadPtr(Address(T2, sizeof(Shape *)), T2); + Jump mismatchedProto = masm.branchPtr(Assembler::NotEqual, T1, T2); stubcc.linkExit(mismatchedProto, Uses(1)); /* @@ -4079,11 +6154,25 @@ mjit::Compiler::iter(uintN flags) * (i.e. it must be a plain object), so we do not need to generate * a loop here. */ - masm.loadPtr(Address(reg, offsetof(JSObject, proto)), T1); - masm.loadPtr(Address(T1, offsetof(JSObject, proto)), T1); + masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1); + masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1); + masm.loadPtr(Address(T1, JSObject::offsetOfType()), T1); + masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1); Jump overlongChain = masm.branchPtr(Assembler::NonZero, T1, T1); stubcc.linkExit(overlongChain, Uses(1)); +#ifdef JSGC_INCREMENTAL_MJ + /* + * Write barrier for stores to the iterator. We only need to take a write + * barrier if NativeIterator::obj is actually going to change. + */ + if (cx->compartment->needsBarrier()) { + Jump j = masm.branchPtr(Assembler::NotEqual, + Address(nireg, offsetof(NativeIterator, obj)), reg); + stubcc.linkExit(j, Uses(1)); + } +#endif + /* Found a match with the most recent iterator. Hooray! */ /* Mark iterator as active. */ @@ -4104,7 +6193,7 @@ mjit::Compiler::iter(uintN flags) stubcc.leave(); stubcc.masm.move(Imm32(flags), Registers::ArgReg1); - OOL_STUBCALL(stubs::Iter); + OOL_STUBCALL(stubs::Iter, REJOIN_FALLTHROUGH); /* Push the iterator object. */ frame.pop(); @@ -4116,13 +6205,13 @@ mjit::Compiler::iter(uintN flags) } /* - * This big nasty function emits a fast-path for native iterators, producing - * a temporary value on the stack for FORLOCAL,ARG,GLOBAL,etc ops to use. + * This big nasty function implements JSOP_ITERNEXT, which is used in the head + * of a for-in loop to put the next value on the stack. */ void -mjit::Compiler::iterNext() +mjit::Compiler::iterNext(ptrdiff_t offset) { - FrameEntry *fe = frame.peek(-1); + FrameEntry *fe = frame.peek(-offset); RegisterID reg = frame.tempRegForData(fe); /* Is it worth trying to pin this longer? Prolly not. */ @@ -4131,11 +6220,11 @@ mjit::Compiler::iterNext() frame.unpinReg(reg); /* Test clasp */ - Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, T1, &IteratorClass); stubcc.linkExit(notFast, Uses(1)); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, T1); + masm.loadObjPrivate(reg, T1, JSObject::ITER_CLASS_NFIXED_SLOTS); RegisterID T3 = frame.allocReg(); RegisterID T4 = frame.allocReg(); @@ -4150,15 +6239,11 @@ mjit::Compiler::iterNext() /* Get cursor. */ masm.loadPtr(Address(T1, offsetof(NativeIterator, props_cursor)), T2); - /* Test if the jsid is a string. */ + /* Get the next string in the iterator. */ masm.loadPtr(T2, T3); - masm.move(T3, T4); - masm.andPtr(Imm32(JSID_TYPE_MASK), T4); - notFast = masm.branchTestPtr(Assembler::NonZero, T4, T4); - stubcc.linkExit(notFast, Uses(1)); /* It's safe to increase the cursor now. */ - masm.addPtr(Imm32(sizeof(jsid)), T2, T4); + masm.addPtr(Imm32(sizeof(JSString*)), T2, T4); masm.storePtr(T4, Address(T1, offsetof(NativeIterator, props_cursor))); frame.freeReg(T4); @@ -4166,7 +6251,8 @@ mjit::Compiler::iterNext() frame.freeReg(T2); stubcc.leave(); - OOL_STUBCALL(stubs::IterNext); + stubcc.masm.move(Imm32(offset), Registers::ArgReg1); + OOL_STUBCALL(stubs::IterNext, REJOIN_FALLTHROUGH); frame.pushUntypedPayload(JSVAL_TYPE_STRING, T3); @@ -4175,53 +6261,42 @@ mjit::Compiler::iterNext() } bool -mjit::Compiler::iterMore() +mjit::Compiler::iterMore(jsbytecode *target) { - FrameEntry *fe = frame.peek(-1); - RegisterID reg = frame.tempRegForData(fe); + if (!frame.syncForBranch(target, Uses(1))) + return false; - frame.pinReg(reg); - RegisterID T1 = frame.allocReg(); - frame.unpinReg(reg); + FrameEntry *fe = frame.peek(-1); + RegisterID reg = frame.tempRegForData(fe); + RegisterID tempreg = frame.allocReg(); /* Test clasp */ - Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, tempreg, &IteratorClass); stubcc.linkExitForBranch(notFast); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, T1); + masm.loadObjPrivate(reg, reg, JSObject::ITER_CLASS_NFIXED_SLOTS); /* Test that the iterator supports fast iteration. */ - notFast = masm.branchTest32(Assembler::NonZero, Address(T1, offsetof(NativeIterator, flags)), + notFast = masm.branchTest32(Assembler::NonZero, Address(reg, offsetof(NativeIterator, flags)), Imm32(JSITER_FOREACH)); stubcc.linkExitForBranch(notFast); /* Get props_cursor, test */ - RegisterID T2 = frame.allocReg(); - frame.syncAndForgetEverything(); - masm.loadPtr(Address(T1, offsetof(NativeIterator, props_cursor)), T2); - masm.loadPtr(Address(T1, offsetof(NativeIterator, props_end)), T1); - Jump jFast = masm.branchPtr(Assembler::LessThan, T2, T1); + masm.loadPtr(Address(reg, offsetof(NativeIterator, props_cursor)), tempreg); + masm.loadPtr(Address(reg, offsetof(NativeIterator, props_end)), reg); - jsbytecode *target = &PC[JSOP_MOREITER_LENGTH]; - JSOp next = JSOp(*target); - JS_ASSERT(next == JSOP_IFNE || next == JSOP_IFNEX); - - target += (next == JSOP_IFNE) - ? GET_JUMP_OFFSET(target) - : GET_JUMPX_OFFSET(target); + Jump jFast = masm.branchPtr(Assembler::LessThan, tempreg, reg); stubcc.leave(); - OOL_STUBCALL(stubs::IterMore); + OOL_STUBCALL(stubs::IterMore, REJOIN_BRANCH); Jump j = stubcc.masm.branchTest32(Assembler::NonZero, Registers::ReturnReg, Registers::ReturnReg); - PC += JSOP_MOREITER_LENGTH; - PC += js_CodeSpec[next].length; - stubcc.rejoin(Changes(1)); + frame.freeReg(tempreg); - return jumpAndTrace(jFast, target, &j); + return jumpAndRun(jFast, target, &j); } void @@ -4235,11 +6310,11 @@ mjit::Compiler::iterEnd() frame.unpinReg(reg); /* Test clasp */ - Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, T1, &IteratorClass); stubcc.linkExit(notIterator, Uses(1)); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, T1); + masm.loadObjPrivate(reg, T1, JSObject::ITER_CLASS_NFIXED_SLOTS); RegisterID T2 = frame.allocReg(); @@ -4268,7 +6343,7 @@ mjit::Compiler::iterEnd() frame.freeReg(T2); stubcc.leave(); - OOL_STUBCALL(stubs::EndIter); + OOL_STUBCALL(stubs::EndIter, REJOIN_FALLTHROUGH); frame.pop(); @@ -4276,54 +6351,83 @@ mjit::Compiler::iterEnd() } void -mjit::Compiler::jsop_eleminc(JSOp op, VoidStub stub) -{ - prepareStubCall(Uses(2)); - INLINE_STUBCALL(stub); - frame.popn(2); - frame.pushSynced(); -} - -void -mjit::Compiler::jsop_getgname_slow(uint32 index) +mjit::Compiler::jsop_getgname_slow(uint32_t index) { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::GetGlobalName); - frame.pushSynced(); + INLINE_STUBCALL(stubs::Name, REJOIN_GETTER); + testPushedType(REJOIN_GETTER, 0, /* ool = */ false); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); } void mjit::Compiler::jsop_bindgname() { - if (script->compileAndGo && globalObj) { + if (globalObj) { frame.push(ObjectValue(*globalObj)); return; } /* :TODO: this is slower than it needs to be. */ prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::BindGlobalName); + INLINE_STUBCALL(stubs::BindGlobalName, REJOIN_NONE); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg); } void -mjit::Compiler::jsop_getgname(uint32 index) +mjit::Compiler::jsop_getgname(uint32_t index) { /* Optimize undefined, NaN and Infinity. */ - JSAtom *atom = script->getAtom(index); - if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID]) { + PropertyName *name = script->getName(index); + if (name == cx->runtime->atomState.typeAtoms[JSTYPE_VOID]) { frame.push(UndefinedValue()); return; } - if (atom == cx->runtime->atomState.NaNAtom) { + if (name == cx->runtime->atomState.NaNAtom) { frame.push(cx->runtime->NaNValue); return; } - if (atom == cx->runtime->atomState.InfinityAtom) { + if (name == cx->runtime->atomState.InfinityAtom) { frame.push(cx->runtime->positiveInfinityValue); return; } + + /* Optimize singletons like Math for JSOP_CALLPROP. */ + JSObject *obj = pushedSingleton(0); + if (obj && !hasTypeBarriers(PC) && testSingletonProperty(globalObj, ATOM_TO_JSID(name))) { + frame.push(ObjectValue(*obj)); + return; + } + + jsid id = ATOM_TO_JSID(name); + JSValueType type = knownPushedType(0); + if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) && + !globalObj->getType(cx)->unknownProperties()) { + types::TypeSet *propertyTypes = globalObj->getType(cx)->getProperty(cx, id, false); + if (!propertyTypes) + return; + + /* + * If we are accessing a defined global which is a normal data property + * then bake its address into the jitcode and guard against future + * reallocation of the global object's slots. + */ + const js::Shape *shape = globalObj->nativeLookup(cx, ATOM_TO_JSID(name)); + if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) { + HeapValue *value = &globalObj->getSlotRef(shape->slot()); + if (!value->isUndefined() && + !propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) { + watchGlobalReallocation(); + RegisterID reg = frame.allocReg(); + masm.move(ImmPtr(value), reg); + + BarrierState barrier = pushAddressMaybeBarrier(Address(reg), type, true); + finishBarrier(barrier, REJOIN_GETTER, 0); + return; + } + } + } + #if defined JS_MONOIC jsop_bindgname(); @@ -4335,8 +6439,6 @@ mjit::Compiler::jsop_getgname(uint32 index) RegisterID objReg; Jump shapeGuard; - ic.usePropertyCache = true; - ic.fastPathStart = masm.label(); if (fe->isConstant()) { JSObject *obj = &fe->getValue().toObject(); @@ -4345,9 +6447,9 @@ mjit::Compiler::jsop_getgname(uint32 index) objReg = frame.allocReg(); - masm.load32FromImm(&obj->objShape, objReg); - shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, objReg, - Imm32(int32(INVALID_SHAPE)), ic.shape); + masm.loadPtrFromImm(obj->addressOfShape(), objReg); + shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, objReg, + ic.shape, ImmPtr(NULL)); masm.move(ImmPtr(obj), objReg); } else { objReg = frame.ownRegForData(fe); @@ -4355,22 +6457,26 @@ mjit::Compiler::jsop_getgname(uint32 index) RegisterID reg = frame.allocReg(); masm.loadShape(objReg, reg); - shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg, - Imm32(int32(INVALID_SHAPE)), ic.shape); + shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, reg, + ic.shape, ImmPtr(NULL)); frame.freeReg(reg); } stubcc.linkExit(shapeGuard, Uses(0)); stubcc.leave(); passMICAddress(ic); - ic.slowPathCall = OOL_STUBCALL(ic::GetGlobalName); + ic.slowPathCall = OOL_STUBCALL(ic::GetGlobalName, REJOIN_GETTER); + + CHECK_IC_SPACE(); + + testPushedType(REJOIN_GETTER, 0); /* Garbage value. */ - uint32 slot = 1 << 24; + uint32_t slot = 1 << 24; - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg); Address address(objReg, slot); - + /* Allocate any register other than objReg. */ RegisterID treg = frame.allocReg(); /* After dreg is loaded, it's safe to clobber objReg. */ @@ -4378,115 +6484,105 @@ mjit::Compiler::jsop_getgname(uint32 index) ic.load = masm.loadValueWithAddressOffsetPatch(address, treg, dreg); - frame.pushRegs(treg, dreg); + frame.pushRegs(treg, dreg, type); + + /* + * Note: no undefined check is needed for GNAME opcodes. These were not + * declared with 'var', so cannot be undefined without triggering an error + * or having been a pre-existing global whose value is undefined (which + * type inference will know about). + */ + BarrierState barrier = testBarrier(treg, dreg); stubcc.rejoin(Changes(1)); getGlobalNames.append(ic); - + finishBarrier(barrier, REJOIN_GETTER, 0); #else jsop_getgname_slow(index); #endif -} - -/* - * Generate just the epilogue code that is specific to callgname. The rest - * is shared with getgname. - */ -void -mjit::Compiler::jsop_callgname_epilogue() -{ - /* - * This slow path does the same thing as the interpreter. - */ - if (!script->compileAndGo) { - prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::PushImplicitThisForGlobal); - frame.pushSynced(); - return; - } - - /* Fast path for known-not-an-object callee. */ - FrameEntry *fval = frame.peek(-1); - if (fval->isNotType(JSVAL_TYPE_OBJECT)) { - frame.push(UndefinedValue()); - return; - } - - /* - * Optimized version. This inlines the common case, calling a - * (non-proxied) function that has the same global as the current - * script. To make the code simpler, we: - * 1. test the stronger property that the callee's parent is - * equal to the global of the current script, and - * 2. bake in the global of the current script, which is why - * this optimized path requires compile-and-go. - */ - - /* If the callee is not an object, jump to the inline fast path. */ - MaybeRegisterID typeReg = frame.maybePinType(fval); - RegisterID objReg = frame.copyDataIntoReg(fval); - - MaybeJump isNotObj; - if (!fval->isType(JSVAL_TYPE_OBJECT)) { - isNotObj = frame.testObject(Assembler::NotEqual, fval); - frame.maybeUnpinReg(typeReg); - } - - /* - * If the callee is not a function, jump to OOL slow path. - */ - Jump notFunction = masm.testFunction(Assembler::NotEqual, objReg); - stubcc.linkExit(notFunction, Uses(1)); - /* - * If the callee's parent is not equal to the global, jump to - * OOL slow path. - */ - masm.loadPtr(Address(objReg, offsetof(JSObject, parent)), objReg); - Jump globalMismatch = masm.branchPtr(Assembler::NotEqual, objReg, ImmPtr(globalObj)); - stubcc.linkExit(globalMismatch, Uses(1)); - frame.freeReg(objReg); - - /* OOL stub call path. */ - stubcc.leave(); - OOL_STUBCALL(stubs::PushImplicitThisForGlobal); - - /* Fast path. */ - if (isNotObj.isSet()) - isNotObj.getJump().linkTo(masm.label(), &masm); - frame.pushUntypedValue(UndefinedValue()); - - stubcc.rejoin(Changes(1)); } void -mjit::Compiler::jsop_setgname_slow(JSAtom *atom, bool usePropertyCache) +mjit::Compiler::jsop_setgname_slow(PropertyName *name) { prepareStubCall(Uses(2)); - masm.move(ImmPtr(atom), Registers::ArgReg1); - if (usePropertyCache) - INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalName)); - else - INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalNameNoCache)); + masm.move(ImmPtr(name), Registers::ArgReg1); + INLINE_STUBCALL(STRICT_VARIANT(stubs::SetGlobalName), REJOIN_FALLTHROUGH); frame.popn(2); - frame.pushSynced(); + pushSyncedEntry(0); } void -mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache) +mjit::Compiler::jsop_setgname(PropertyName *name, bool popGuaranteed) { + if (monitored(PC)) { + /* Global accesses are monitored only for a few names like __proto__. */ + jsop_setgname_slow(name); + return; + } + + jsid id = ATOM_TO_JSID(name); + if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) && + !globalObj->getType(cx)->unknownProperties()) { + /* + * Note: object branding is disabled when inference is enabled. With + * branding there is no way to ensure that a non-function property + * can't get a function later and cause the global object to become + * branded, requiring a shape change if it changes again. + */ + types::TypeSet *types = globalObj->getType(cx)->getProperty(cx, id, false); + if (!types) + return; + const js::Shape *shape = globalObj->nativeLookup(cx, ATOM_TO_JSID(name)); + if (shape && !shape->isMethod() && shape->hasDefaultSetter() && + shape->writable() && shape->hasSlot() && + !types->isOwnProperty(cx, globalObj->getType(cx), true)) { + watchGlobalReallocation(); + HeapValue *value = &globalObj->getSlotRef(shape->slot()); + RegisterID reg = frame.allocReg(); +#ifdef JSGC_INCREMENTAL_MJ + /* Write barrier. */ + if (cx->compartment->needsBarrier() && types->needsBarrier(cx)) { + stubcc.linkExit(masm.jump(), Uses(0)); + stubcc.leave(); + stubcc.masm.move(ImmPtr(value), Registers::ArgReg1); + OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE); + stubcc.rejoin(Changes(0)); + } +#endif + masm.move(ImmPtr(value), reg); + frame.storeTo(frame.peek(-1), Address(reg), popGuaranteed); + frame.shimmy(1); + frame.freeReg(reg); + return; + } + } + +#ifdef JSGC_INCREMENTAL_MJ + /* Write barrier. */ + if (cx->compartment->needsBarrier()) { + jsop_setgname_slow(name); + return; + } +#endif + #if defined JS_MONOIC FrameEntry *objFe = frame.peek(-2); FrameEntry *fe = frame.peek(-1); JS_ASSERT_IF(objFe->isTypeKnown(), objFe->getKnownType() == JSVAL_TYPE_OBJECT); + if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) + frame.forgetKnownDouble(fe); + SetGlobalNameICInfo ic; frame.pinEntry(fe, ic.vr); Jump shapeGuard; RESERVE_IC_SPACE(masm); + ic.fastPathStart = masm.label(); if (objFe->isConstant()) { JSObject *obj = &objFe->getValue().toObject(); @@ -4496,10 +6592,9 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache) ic.shapeReg = ic.objReg; ic.objConst = true; - masm.load32FromImm(&obj->objShape, ic.shapeReg); - shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, ic.shapeReg, - Imm32(int32(INVALID_SHAPE)), - ic.shape); + masm.loadPtrFromImm(obj->addressOfShape(), ic.shapeReg); + shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, ic.shapeReg, + ic.shape, ImmPtr(NULL)); masm.move(ImmPtr(obj), ic.objReg); } else { ic.objReg = frame.copyDataIntoReg(objFe); @@ -4507,9 +6602,8 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache) ic.objConst = false; masm.loadShape(ic.objReg, ic.shapeReg); - shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, ic.shapeReg, - Imm32(int32(INVALID_SHAPE)), - ic.shape); + shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, ic.shapeReg, + ic.shape, ImmPtr(NULL)); frame.freeReg(ic.shapeReg); } ic.shapeGuardJump = shapeGuard; @@ -4517,14 +6611,12 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache) stubcc.leave(); passMICAddress(ic); - ic.slowPathCall = OOL_STUBCALL(ic::SetGlobalName); + ic.slowPathCall = OOL_STUBCALL(ic::SetGlobalName, REJOIN_FALLTHROUGH); /* Garbage value. */ - uint32 slot = 1 << 24; + uint32_t slot = 1 << 24; - ic.usePropertyCache = usePropertyCache; - - masm.loadPtr(Address(ic.objReg, offsetof(JSObject, slots)), ic.objReg); + masm.loadPtr(Address(ic.objReg, JSObject::offsetOfSlots()), ic.objReg); Address address(ic.objReg, slot); if (ic.vr.isConstant()) { @@ -4545,7 +6637,7 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache) ic.fastPathRejoin = masm.label(); setGlobalNames.append(ic); #else - jsop_setgname_slow(atom, usePropertyCache); + jsop_setgname_slow(name); #endif } @@ -4553,25 +6645,19 @@ void mjit::Compiler::jsop_setelem_slow() { prepareStubCall(Uses(3)); - INLINE_STUBCALL(STRICT_VARIANT(stubs::SetElem)); + INLINE_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH); frame.popn(3); - frame.pushSynced(); + frame.pushSynced(JSVAL_TYPE_UNKNOWN); } void mjit::Compiler::jsop_getelem_slow() { prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::GetElem); + INLINE_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -2, /* ool = */ false); frame.popn(2); - frame.pushSynced(); -} - -void -mjit::Compiler::jsop_unbrand() -{ - prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::Unbrand); + pushSyncedEntry(0); } bool @@ -4582,12 +6668,9 @@ mjit::Compiler::jsop_instanceof() // The fast path applies only when both operands are objects. if (rhs->isNotType(JSVAL_TYPE_OBJECT) || lhs->isNotType(JSVAL_TYPE_OBJECT)) { - prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::InstanceOf); - frame.popn(2); - frame.takeReg(Registers::ReturnReg); - frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg); - return true; + stubcc.linkExit(masm.jump(), Uses(2)); + frame.discardFe(lhs); + frame.discardFe(rhs); } MaybeJump firstSlow; @@ -4596,25 +6679,36 @@ mjit::Compiler::jsop_instanceof() stubcc.linkExit(j, Uses(2)); } + frame.forgetMismatchedObject(lhs); + frame.forgetMismatchedObject(rhs); + + RegisterID tmp = frame.allocReg(); RegisterID obj = frame.tempRegForData(rhs); - Jump notFunction = masm.testFunction(Assembler::NotEqual, obj); + + masm.loadBaseShape(obj, tmp); + Jump notFunction = masm.branchPtr(Assembler::NotEqual, + Address(tmp, BaseShape::offsetOfClass()), + ImmPtr(&FunctionClass)); + stubcc.linkExit(notFunction, Uses(2)); /* Test for bound functions. */ - Jump isBound = masm.branchTest32(Assembler::NonZero, Address(obj, offsetof(JSObject, flags)), - Imm32(JSObject::BOUND_FUNCTION)); + Jump isBound = masm.branchTest32(Assembler::NonZero, + Address(tmp, BaseShape::offsetOfFlags()), + Imm32(BaseShape::BOUND_FUNCTION)); { stubcc.linkExit(isBound, Uses(2)); stubcc.leave(); - OOL_STUBCALL(stubs::InstanceOf); + OOL_STUBCALL(stubs::InstanceOf, REJOIN_FALLTHROUGH); firstSlow = stubcc.masm.jump(); } - + + frame.freeReg(tmp); /* This is sadly necessary because the error case needs the object. */ frame.dup(); - if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false)) + if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN)) return false; /* Primitive prototypes are invalid. */ @@ -4631,11 +6725,11 @@ mjit::Compiler::jsop_instanceof() if (!lhs->isTypeKnown()) isFalse = frame.testPrimitive(Assembler::Equal, lhs); - Address protoAddr(obj, offsetof(JSObject, proto)); Label loop = masm.label(); /* Walk prototype chain, break out on NULL or hit. */ - masm.loadPtr(protoAddr, obj); + masm.loadPtr(Address(obj, JSObject::offsetOfType()), obj); + masm.loadPtr(Address(obj, offsetof(types::TypeObject, proto)), obj); Jump isFalse2 = masm.branchTestPtr(Assembler::Zero, obj, obj); Jump isTrue = masm.branchPtr(Assembler::NotEqual, obj, proto); isTrue.linkTo(loop, &masm); @@ -4652,7 +6746,7 @@ mjit::Compiler::jsop_instanceof() frame.freeReg(obj); stubcc.leave(); - OOL_STUBCALL(stubs::FastInstanceOf); + OOL_STUBCALL(stubs::FastInstanceOf, REJOIN_FALLTHROUGH); frame.popn(3); frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, temp); @@ -4664,27 +6758,27 @@ mjit::Compiler::jsop_instanceof() } void -mjit::Compiler::emitEval(uint32 argc) +mjit::Compiler::emitEval(uint32_t argc) { /* Check for interrupts on function call */ interruptCheckHelper(); - frame.syncAndKill(Registers(Registers::AvailRegs), Uses(argc + 2)); + frame.syncAndKill(Uses(argc + 2)); prepareStubCall(Uses(argc + 2)); masm.move(Imm32(argc), Registers::ArgReg1); - INLINE_STUBCALL(stubs::Eval); + INLINE_STUBCALL(stubs::Eval, REJOIN_FALLTHROUGH); frame.popn(argc + 2); - frame.pushSynced(); + pushSyncedEntry(0); } void -mjit::Compiler::jsop_arguments() +mjit::Compiler::jsop_arguments(RejoinState rejoin) { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::Arguments); + INLINE_STUBCALL(stubs::Arguments, rejoin); } -void +bool mjit::Compiler::jsop_newinit() { bool isArray; @@ -4692,160 +6786,440 @@ mjit::Compiler::jsop_newinit() JSObject *baseobj = NULL; switch (*PC) { case JSOP_NEWINIT: - isArray = (PC[1] == JSProto_Array); + isArray = (GET_UINT8(PC) == JSProto_Array); break; case JSOP_NEWARRAY: isArray = true; count = GET_UINT24(PC); break; case JSOP_NEWOBJECT: + /* + * Scripts with NEWOBJECT must be compileAndGo, but treat these like + * NEWINIT if the script's associated global is not known (or is not + * actually a global object). This should only happen in chrome code. + */ isArray = false; - baseobj = script->getObject(fullAtomIndex(PC)); + baseobj = globalObj ? script->getObject(GET_UINT32_INDEX(PC)) : NULL; break; default: JS_NOT_REACHED("Bad op"); - return; + return false; } - prepareStubCall(Uses(0)); + void *stub, *stubArg; if (isArray) { - masm.move(Imm32(count), Registers::ArgReg1); - INLINE_STUBCALL(stubs::NewInitArray); + stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitArray); + stubArg = (void *) uintptr_t(count); } else { - masm.move(ImmPtr(baseobj), Registers::ArgReg1); - INLINE_STUBCALL(stubs::NewInitObject); + stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitObject); + stubArg = (void *) baseobj; } - frame.takeReg(Registers::ReturnReg); - frame.pushInitializerObject(Registers::ReturnReg, *PC == JSOP_NEWARRAY, baseobj); + + /* Don't bake in types for non-compileAndGo scripts. */ + types::TypeObject *type = NULL; + if (globalObj) { + type = types::TypeScript::InitObject(cx, script, PC, + isArray ? JSProto_Array : JSProto_Object); + if (!type) + return false; + } + + size_t maxArraySlots = + gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST) - ObjectElements::VALUES_PER_HEADER; + + if (!cx->typeInferenceEnabled() || + !globalObj || + (isArray && count > maxArraySlots) || + (!isArray && !baseobj) || + (!isArray && baseobj->hasDynamicSlots())) { + prepareStubCall(Uses(0)); + masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch))); + masm.move(ImmPtr(stubArg), Registers::ArgReg1); + INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH); + frame.pushSynced(JSVAL_TYPE_OBJECT); + + frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY); + frame.extra(frame.peek(-1)).initObject = baseobj; + + return true; + } + + JSObject *templateObject; + if (isArray) { + templateObject = NewDenseUnallocatedArray(cx, count); + if (!templateObject) + return false; + templateObject->setType(type); + } else { + templateObject = CopyInitializerObject(cx, baseobj, type); + if (!templateObject) + return false; + } + + RegisterID result = frame.allocReg(); + Jump emptyFreeList = masm.getNewObject(cx, result, templateObject); + + stubcc.linkExit(emptyFreeList, Uses(0)); + stubcc.leave(); + + stubcc.masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch))); + stubcc.masm.move(ImmPtr(stubArg), Registers::ArgReg1); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); + + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result); + + stubcc.rejoin(Changes(1)); + + frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY); + frame.extra(frame.peek(-1)).initObject = baseobj; + + return true; } -/* - * Note: This function emits tracer hooks into the OOL path. This means if - * it is used in the middle of an in-progress slow path, the stream will be - * hopelessly corrupted. Take care to only call this before linkExits() and - * after rejoin()s. - */ bool -mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow) +mjit::Compiler::jsop_regexp() { - // XXX refactor this little bit -#ifndef JS_TRACER - if (!jumpInScript(j, target)) + JSObject *obj = script->getRegExp(GET_UINT32_INDEX(PC)); + RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL; + + if (!globalObj || + &obj->global() != globalObj || + !cx->typeInferenceEnabled() || + analysis->localsAliasStack() || + types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx), + types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { + prepareStubCall(Uses(0)); + masm.move(ImmPtr(obj), Registers::ArgReg1); + INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH); + frame.pushSynced(JSVAL_TYPE_OBJECT); + return true; + } + + RegExpObject *reobj = &obj->asRegExp(); + + DebugOnly origFlags = reobj->getFlags(); + DebugOnly staticsFlags = res->getFlags(); + JS_ASSERT((origFlags & staticsFlags) == staticsFlags); + + /* + * JS semantics require regular expression literals to create different + * objects every time they execute. We only need to do this cloning if the + * script could actually observe the effect of such cloning, by getting + * or setting properties on it. Particular RegExp and String natives take + * regular expressions as 'this' or an argument, and do not let that + * expression escape and be accessed by the script, so avoid cloning in + * these cases. + */ + analyze::SSAUseChain *uses = + analysis->useChain(analyze::SSAValue::PushedValue(PC - script->code, 0)); + if (uses && uses->popped && !uses->next) { + jsbytecode *use = script->code + uses->offset; + uint32_t which = uses->u.which; + if (JSOp(*use) == JSOP_CALLPROP) { + JSObject *callee = analysis->pushedTypes(use, 0)->getSingleton(cx); + if (callee && callee->isFunction()) { + Native native = callee->toFunction()->maybeNative(); + if (native == js::regexp_exec || native == js::regexp_test) { + frame.push(ObjectValue(*obj)); + return true; + } + } + } else if (JSOp(*use) == JSOP_CALL && which == 0) { + uint32_t argc = GET_ARGC(use); + JSObject *callee = analysis->poppedTypes(use, argc + 1)->getSingleton(cx); + if (callee && callee->isFunction() && argc >= 1 && which == argc - 1) { + Native native = callee->toFunction()->maybeNative(); + if (native == js::str_match || + native == js::str_search || + native == js::str_replace || + native == js::str_split) { + frame.push(ObjectValue(*obj)); + return true; + } + } + } + } + + /* + * Force creation of the RegExpShared in the script's RegExpObject + * so that we grab it in the getNewObject template copy. Note that + * JIT code is discarded on every GC, which permits us to burn in + * the pointer to the RegExpShared. + */ + if (!reobj->getShared(cx)) return false; - if (slow) { - if (!stubcc.jumpInScript(*slow, target)) - return false; + RegisterID result = frame.allocReg(); + Jump emptyFreeList = masm.getNewObject(cx, result, obj); + + stubcc.linkExit(emptyFreeList, Uses(0)); + stubcc.leave(); + + stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1); + OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH); + + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result); + + stubcc.rejoin(Changes(1)); + return true; +} + +bool +mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget) +{ + JS_ASSERT(cx->typeInferenceEnabled() && script == outerScript); + JS_ASSERT(shouldStartLoop(head)); + + if (loop) { + /* + * Convert all loop registers in the outer loop into unassigned registers. + * We don't keep track of which registers the inner loop uses, so the only + * registers that can be carried in the outer loop must be mentioned before + * the inner loop starts. + */ + loop->clearLoopRegisters(); + } + + LoopState *nloop = OffTheBooks::new_(cx, &ssa, this, &frame); + if (!nloop || !nloop->init(head, entry, entryTarget)) + return false; + + nloop->outer = loop; + loop = nloop; + frame.setLoop(loop); + + return true; +} + +bool +mjit::Compiler::finishLoop(jsbytecode *head) +{ + if (!cx->typeInferenceEnabled() || !bytecodeInChunk(head)) + return true; + + /* + * We're done processing the current loop. Every loop has exactly one backedge + * at the end ('continue' statements are forward jumps to the loop test), + * and after jumpAndRun'ing on that edge we can pop it from the frame. + */ + JS_ASSERT(loop && loop->headOffset() == uint32_t(head - script->code)); + + jsbytecode *entryTarget = script->code + loop->entryOffset(); + + /* + * Fix up the jump entering the loop. We are doing this after all code has + * been emitted for the backedge, so that we are now in the loop's fallthrough + * (where we will emit the entry code). + */ + Jump fallthrough = masm.jump(); + +#ifdef DEBUG + if (IsJaegerSpewChannelActive(JSpew_Regalloc)) { + RegisterAllocation *alloc = analysis->getAllocation(head); + JaegerSpew(JSpew_Regalloc, "loop allocation at %u:", unsigned(head - script->code)); + frame.dumpAllocation(alloc); } -#else - if (!addTraceHints || target >= PC || - (JSOp(*target) != JSOP_TRACE && JSOp(*target) != JSOP_NOTRACE) -#ifdef JS_MONOIC - || GET_UINT16(target) == BAD_TRACEIC_INDEX #endif - ) + + loop->entryJump().linkTo(masm.label(), &masm); + + jsbytecode *oldPC = PC; + + PC = entryTarget; { - if (!jumpInScript(j, target)) - return false; - if (slow && !stubcc.jumpInScript(*slow, target)) - return false; - return true; + OOL_STUBCALL(stubs::MissedBoundsCheckEntry, REJOIN_RESUME); + + if (loop->generatingInvariants()) { + /* + * To do the initial load of the invariants, jump to the invariant + * restore point after the call just emitted. :XXX: fix hackiness. + */ + if (oomInVector) + return false; + Label label = callSites[callSites.length() - 1].loopJumpLabel; + stubcc.linkExitDirect(masm.jump(), label); + } + stubcc.crossJump(stubcc.masm.jump(), masm.label()); } + PC = oldPC; + + frame.prepareForJump(entryTarget, masm, true); + + if (!jumpInScript(masm.jump(), entryTarget)) + return false; + + PC = head; + if (!analysis->getCode(head).safePoint) { + /* + * Emit a stub into the OOL path which loads registers from a synced state + * and jumps to the loop head, for rejoining from the interpreter. + */ + LoopEntry entry; + entry.pcOffset = head - script->code; + + OOL_STUBCALL(stubs::MissedBoundsCheckHead, REJOIN_RESUME); -# if JS_MONOIC - TraceGenInfo ic; + if (loop->generatingInvariants()) { + if (oomInVector) + return false; + entry.label = callSites[callSites.length() - 1].loopJumpLabel; + } else { + entry.label = stubcc.masm.label(); + } - ic.initialized = true; - ic.stubEntry = stubcc.masm.label(); - ic.jumpTarget = target; - ic.traceHint = j; - if (slow) - ic.slowTraceHint = *slow; + /* + * The interpreter may store integers in slots we assume are doubles, + * make sure state is consistent before joining. Note that we don't + * need any handling for other safe points the interpreter can enter + * from, i.e. from switch and try blocks, as we don't assume double + * variables are coherent in such cases. + */ + for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) { + if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) { + FrameEntry *fe = frame.getSlotEntry(slot); + stubcc.masm.ensureInMemoryDouble(frame.addressOf(fe)); + } + } - uint16 index = GET_UINT16(target); - if (traceICs.length() <= index) - if (!traceICs.resize(index+1)) + frame.prepareForJump(head, stubcc.masm, true); + if (!stubcc.jumpInScript(stubcc.masm.jump(), head)) return false; -# endif - Label traceStart = stubcc.masm.label(); + loopEntries.append(entry); + } + PC = oldPC; + + /* Write out loads and tests of loop invariants at all calls in the loop body. */ + loop->flushLoop(stubcc); + + LoopState *nloop = loop->outer; + cx->delete_(loop); + loop = nloop; + frame.setLoop(loop); + + fallthrough.linkTo(masm.label(), &masm); /* - * We make a trace IC even if the trace is currently disabled, in case it is - * enabled later, but set up the jumps so that InvokeTracer is initially skipped. + * Clear all registers used for loop temporaries. In the case of loop + * nesting, we do not allocate temporaries for the outer loop. */ - if (JSOp(*target) == JSOP_TRACE) { - stubcc.linkExitDirect(j, traceStart); + frame.clearTemporaries(); + + return true; +} + +/* + * The state at the fast jump must reflect the frame's current state. If specified + * the state at the slow jump must be fully synced. + * + * The 'trampoline' argument indicates whether a trampoline was emitted into + * the OOL path loading some registers for the target. If this is the case, + * the fast path jump was redirected to the stub code's initial label, and the + * same must happen for any other fast paths for the target (i.e. paths from + * inline caches). + * + * The 'fallthrough' argument indicates this is a jump emitted for a fallthrough + * at the end of the compiled chunk. In this case the opcode may not be a + * JOF_JUMP opcode, and the compiler should not watch for fusions. + */ +bool +mjit::Compiler::jumpAndRun(Jump j, jsbytecode *target, Jump *slow, bool *trampoline, + bool fallthrough) +{ + if (trampoline) + *trampoline = false; + + if (!a->parent && !bytecodeInChunk(target)) { + /* + * syncForBranch() must have ensured the stack is synced. Figure out + * the source of the jump, which may be the opcode after PC if two ops + * were fused for a branch. + */ + OutgoingChunkEdge edge; + edge.source = PC - outerScript->code; + JSOp op = JSOp(*PC); + if (!fallthrough && !(js_CodeSpec[op].format & JOF_JUMP) && op != JSOP_TABLESWITCH) + edge.source += GetBytecodeLength(PC); + edge.target = target - outerScript->code; + edge.fastJump = j; if (slow) - slow->linkTo(traceStart, &stubcc.masm); - } else { + edge.slowJump = *slow; + chunkEdges.append(edge); + return true; + } + + /* + * Unless we are coming from a branch which synced everything, syncForBranch + * must have been called and ensured an allocation at the target. + */ + RegisterAllocation *lvtarget = NULL; + bool consistent = true; + if (cx->typeInferenceEnabled()) { + RegisterAllocation *&alloc = analysis->getAllocation(target); + if (!alloc) { + alloc = cx->typeLifoAlloc().new_(false); + if (!alloc) + return false; + } + lvtarget = alloc; + consistent = frame.consistentRegisters(target); + } + + if (!lvtarget || lvtarget->synced()) { + JS_ASSERT(consistent); if (!jumpInScript(j, target)) return false; if (slow && !stubcc.jumpInScript(*slow, target)) return false; - } - -# if JS_MONOIC - ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); - traceICs[index] = ic; - - Jump nonzero = stubcc.masm.branchSub32(Assembler::NonZero, Imm32(1), - Address(Registers::ArgReg1, - offsetof(TraceICInfo, loopCounter))); - stubcc.jumpInScript(nonzero, target); -# endif - - /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ - { - jsbytecode* pc = PC; - PC = target; - - OOL_STUBCALL(stubs::InvokeTracer); + } else { + if (consistent) { + if (!jumpInScript(j, target)) + return false; + } else { + /* + * Make a trampoline to issue remaining loads for the register + * state at target. + */ + Label start = stubcc.masm.label(); + stubcc.linkExitDirect(j, start); + frame.prepareForJump(target, stubcc.masm, false); + if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) + return false; + if (trampoline) + *trampoline = true; + if (pcLengths) { + /* + * This is OOL code but will usually be executed, so track + * it in the CODE_LENGTH for the opcode. + */ + uint32_t offset = ssa.frameLength(a->inlineIndex) + PC - script->code; + size_t length = stubcc.masm.size() - stubcc.masm.distanceOf(start); + pcLengths[offset].codeLength += length; + } + } - PC = pc; + if (slow) { + slow->linkTo(stubcc.masm.label(), &stubcc.masm); + frame.prepareForJump(target, stubcc.masm, true); + if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) + return false; + } } - Jump no = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg, - Registers::ReturnReg); - if (!stubcc.jumpInScript(no, target)) - return false; - restoreFrameRegs(stubcc.masm); - stubcc.masm.jump(Registers::ReturnReg); -#endif + if (target < PC) + return finishLoop(target); return true; } void -mjit::Compiler::enterBlock(JSObject *obj) +mjit::Compiler::enterBlock(StaticBlockObject *block) { - // If this is an exception entry point, then jsl_InternalThrow has set - // VMFrame::fp to the correct fp for the entry point. We need to copy - // that value here to FpReg so that FpReg also has the correct sp. - // Otherwise, we would simply be using a stale FpReg value. - // Additionally, we check the interrupt flag to allow interrupting - // deeply nested exception handling. - if (analysis->getCode(PC).exceptionEntry) { - restoreFrameRegs(masm); - interruptCheckHelper(); - } - - uint32 oldFrameDepth = frame.localSlots(); - /* For now, don't bother doing anything for this opcode. */ frame.syncAndForgetEverything(); - masm.move(ImmPtr(obj), Registers::ArgReg1); - uint32 n = js_GetEnterBlockStackDefs(cx, script, PC); - INLINE_STUBCALL(stubs::EnterBlock); - frame.enterBlock(n); - - uintN base = JSSLOT_FREE(&js_BlockClass); - uintN count = OBJ_BLOCK_COUNT(cx, obj); - uintN limit = base + count; - for (uintN slot = base, i = 0; slot < limit; slot++, i++) { - const Value &v = obj->getSlotRef(slot); - if (v.isBoolean() && v.toBoolean()) - frame.setClosedVar(oldFrameDepth + i); - } + masm.move(ImmPtr(block), Registers::ArgReg1); + INLINE_STUBCALL(stubs::EnterBlock, REJOIN_NONE); + if (*PC == JSOP_ENTERBLOCK) + frame.enterBlock(StackDefs(script, PC)); } void @@ -4855,11 +7229,9 @@ mjit::Compiler::leaveBlock() * Note: After bug 535912, we can pass the block obj directly, inline * PutBlockObject, and do away with the muckiness in PutBlockObject. */ - uint32 n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC); - JSObject *obj = script->getObject(fullAtomIndex(PC + UINT16_LEN)); + uint32_t n = StackUses(script, PC); prepareStubCall(Uses(n)); - masm.move(ImmPtr(obj), Registers::ArgReg1); - INLINE_STUBCALL(stubs::LeaveBlock); + INLINE_STUBCALL(stubs::LeaveBlock, REJOIN_NONE); frame.leaveBlock(n); } @@ -4876,11 +7248,66 @@ mjit::Compiler::constructThis() { JS_ASSERT(isConstructing); + JSFunction *fun = script->function(); + + do { + if (!cx->typeInferenceEnabled() || + !fun->hasSingletonType() || + fun->getType(cx)->unknownProperties()) + { + break; + } + + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + types::TypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false); + + JSObject *proto = protoTypes->getSingleton(cx, true); + if (!proto) + break; + + /* + * Generate an inline path to create a 'this' object with the given + * prototype. Only do this if the type is actually known as a possible + * 'this' type of the script. + */ + types::TypeObject *type = proto->getNewType(cx, fun); + if (!type) + return false; + if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::ObjectType(type))) + break; + + JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, fun, proto); + if (!templateObject) + return false; + + /* + * The template incorporates a shape and/or fixed slots from any + * newScript on its type, so make sure recompilation is triggered + * should this information change later. + */ + if (templateObject->type()->newScript) + types::TypeSet::WatchObjectStateChange(cx, templateObject->type()); + + RegisterID result = frame.allocReg(); + Jump emptyFreeList = masm.getNewObject(cx, result, templateObject); + + stubcc.linkExit(emptyFreeList, Uses(0)); + stubcc.leave(); + + stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1); + OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME); + + frame.setThis(result); + + stubcc.rejoin(Changes(1)); + return true; + } while (false); + // Load the callee. frame.pushCallee(); // Get callee.prototype. - if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false, false)) + if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, /* forPrototype = */ true)) return false; // Reach into the proto Value and grab a register for its data. @@ -4888,10 +7315,13 @@ mjit::Compiler::constructThis() RegisterID protoReg = frame.ownRegForData(protoFe); // Now, get the type. If it's not an object, set protoReg to NULL. - Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); - stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); - stubcc.masm.move(ImmPtr(NULL), protoReg); - stubcc.crossJump(stubcc.masm.jump(), masm.label()); + JS_ASSERT_IF(protoFe->isTypeKnown(), protoFe->isType(JSVAL_TYPE_OBJECT)); + if (!protoFe->isType(JSVAL_TYPE_OBJECT)) { + Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); + stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); + stubcc.masm.move(ImmPtr(NULL), protoReg); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + } // Done with the protoFe. frame.pop(); @@ -4899,7 +7329,7 @@ mjit::Compiler::constructThis() prepareStubCall(Uses(0)); if (protoReg != Registers::ArgReg1) masm.move(protoReg, Registers::ArgReg1); - INLINE_STUBCALL(stubs::CreateThis); + INLINE_STUBCALL(stubs::CreateThis, REJOIN_RESUME); frame.freeReg(protoReg); return true; } @@ -4912,8 +7342,10 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) return true; #else jsbytecode *originalPC = pc; + DebugOnly op = JSOp(*originalPC); + JS_ASSERT(op == JSOP_TABLESWITCH); - uint32 defaultTarget = GET_JUMP_OFFSET(pc); + uint32_t defaultTarget = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; jsint low = GET_JUMP_OFFSET(pc); @@ -4923,22 +7355,13 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) int numJumps = high + 1 - low; JS_ASSERT(numJumps >= 0); - /* - * If there are no cases, this is a no-op. The default case immediately - * follows in the bytecode and is always taken. - */ - if (numJumps == 0) { - frame.pop(); - return true; - } - FrameEntry *fe = frame.peek(-1); if (fe->isNotType(JSVAL_TYPE_INT32) || numJumps > 256) { frame.syncAndForgetEverything(); masm.move(ImmPtr(originalPC), Registers::ArgReg1); /* prepareStubCall() is not needed due to forgetEverything() */ - INLINE_STUBCALL(stubs::TableSwitch); + INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); return true; @@ -4961,16 +7384,18 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) notInt = masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)); JumpTable jt; - jt.offsetIndex = jumpTableOffsets.length(); + jt.offsetIndex = jumpTableEdges.length(); jt.label = masm.moveWithPatch(ImmPtr(NULL), reg); jumpTables.append(jt); for (int i = 0; i < numJumps; i++) { - uint32 target = GET_JUMP_OFFSET(pc); + uint32_t target = GET_JUMP_OFFSET(pc); if (!target) target = defaultTarget; - uint32 offset = (originalPC + target) - script->code; - jumpTableOffsets.append(offset); + JumpTableEdge edge; + edge.source = originalPC - script->code; + edge.target = (originalPC + target) - script->code; + jumpTableEdges.append(edge); pc += JUMP_OFFSET_LEN; } if (low != 0) @@ -4983,82 +7408,587 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) stubcc.linkExitDirect(notInt.get(), stubcc.masm.label()); stubcc.leave(); stubcc.masm.move(ImmPtr(originalPC), Registers::ArgReg1); - OOL_STUBCALL(stubs::TableSwitch); + OOL_STUBCALL(stubs::TableSwitch, REJOIN_NONE); stubcc.masm.jump(Registers::ReturnReg); } frame.pop(); - return jumpAndTrace(defaultCase, originalPC + defaultTarget); + return jumpAndRun(defaultCase, originalPC + defaultTarget); #endif } void -mjit::Compiler::jsop_callelem_slow() +mjit::Compiler::jsop_toid() +{ + /* Leave integers alone, stub everything else. */ + FrameEntry *top = frame.peek(-1); + + if (top->isType(JSVAL_TYPE_INT32)) + return; + + if (top->isNotType(JSVAL_TYPE_INT32)) { + prepareStubCall(Uses(2)); + INLINE_STUBCALL(stubs::ToId, REJOIN_FALLTHROUGH); + frame.pop(); + pushSyncedEntry(0); + return; + } + + frame.syncAt(-1); + + Jump j = frame.testInt32(Assembler::NotEqual, top); + stubcc.linkExit(j, Uses(2)); + + stubcc.leave(); + OOL_STUBCALL(stubs::ToId, REJOIN_FALLTHROUGH); + + frame.pop(); + pushSyncedEntry(0); + + stubcc.rejoin(Changes(1)); +} + +void +mjit::Compiler::jsop_in() { + FrameEntry *obj = frame.peek(-1); + FrameEntry *id = frame.peek(-2); + + if (cx->typeInferenceEnabled() && id->isType(JSVAL_TYPE_INT32)) { + types::TypeSet *types = analysis->poppedTypes(PC, 0); + + if (obj->mightBeType(JSVAL_TYPE_OBJECT) && + !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && + !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) + { + bool isPacked = !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); + + if (!obj->isTypeKnown()) { + Jump guard = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(guard, Uses(2)); + } + + RegisterID dataReg = frame.copyDataIntoReg(obj); + + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + + masm.loadPtr(Address(dataReg, JSObject::offsetOfElements()), dataReg); + + // Guard on the array's initialized length. + Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + dataReg, key, Assembler::BelowOrEqual); + + // Guard to make sure we don't have a hole. Skip it if the array is packed. + MaybeJump holeCheck; + if (!isPacked) + holeCheck = masm.guardElementNotHole(dataReg, key); + + masm.move(Imm32(1), dataReg); + Jump done = masm.jump(); + + Label falseBranch = masm.label(); + initlenGuard.linkTo(falseBranch, &masm); + if (!isPacked) + holeCheck.getJump().linkTo(falseBranch, &masm); + masm.move(Imm32(0), dataReg); + + done.linkTo(masm.label(), &masm); + + stubcc.leave(); + OOL_STUBCALL_USES(stubs::In, REJOIN_PUSH_BOOLEAN, Uses(2)); + + frame.popn(2); + if (dataReg != Registers::ReturnReg) + stubcc.masm.move(Registers::ReturnReg, dataReg); + + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, dataReg); + + stubcc.rejoin(Changes(2)); + + return; + } + } + prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::CallElem); + INLINE_STUBCALL(stubs::In, REJOIN_PUSH_BOOLEAN); frame.popn(2); - frame.pushSynced(); - frame.pushSynced(); + frame.takeReg(Registers::ReturnReg); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, Registers::ReturnReg); +} + +/* + * For any locals or args which we know to be integers but are treated as + * doubles by the type inference, convert to double. These will be assumed to be + * doubles at control flow join points. This function must be called before + * branching to another opcode. + * + * We can only carry entries as doubles when we can track all incoming edges to + * a join point (no try blocks etc.) and when we can track all writes to the + * local/arg (the slot does not escape) and ensure the Compiler representation + * matches the inferred type for the variable's SSA value. These properties are + * both ensured by analysis->trackSlot. + */ +void +mjit::Compiler::fixDoubleTypes(jsbytecode *target) +{ + if (!cx->typeInferenceEnabled()) + return; + + /* + * Fill fixedIntToDoubleEntries with all variables that are known to be an + * int here and a double at the branch target, and fixedDoubleToAnyEntries + * with all variables that are known to be a double here but not at the + * branch target. + * + * Per prepareInferenceTypes, the target state consists of the current + * state plus any phi nodes or other new values introduced at the target. + */ + JS_ASSERT(fixedIntToDoubleEntries.empty()); + JS_ASSERT(fixedDoubleToAnyEntries.empty()); + const SlotValue *newv = analysis->newValues(target); + if (newv) { + while (newv->slot) { + if (newv->value.kind() != SSAValue::PHI || + newv->value.phiOffset() != uint32_t(target - script->code) || + !analysis->trackSlot(newv->slot)) { + newv++; + continue; + } + JS_ASSERT(newv->slot < TotalSlots(script)); + types::TypeSet *targetTypes = analysis->getValueTypes(newv->value); + FrameEntry *fe = frame.getSlotEntry(newv->slot); + VarType &vt = a->varTypes[newv->slot]; + JSValueType type = vt.getTypeTag(cx); + if (targetTypes->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) { + if (type == JSVAL_TYPE_INT32) { + fixedIntToDoubleEntries.append(newv->slot); + frame.ensureDouble(fe); + frame.forgetLoopReg(fe); + } else if (type == JSVAL_TYPE_UNKNOWN) { + /* + * Unknown here but a double at the target. The type + * set for the existing value must be empty, so this + * code is doomed and we can just mark the value as + * a double. + */ + frame.ensureDouble(fe); + } else { + JS_ASSERT(type == JSVAL_TYPE_DOUBLE); + } + } else if (type == JSVAL_TYPE_DOUBLE) { + fixedDoubleToAnyEntries.append(newv->slot); + frame.syncAndForgetFe(fe); + frame.forgetLoopReg(fe); + } + newv++; + } + } } void -mjit::Compiler::jsop_forprop(JSAtom *atom) +mjit::Compiler::watchGlobalReallocation() { - // Before: ITER OBJ - // After: ITER OBJ ITER - frame.dupAt(-2); + JS_ASSERT(cx->typeInferenceEnabled()); + if (hasGlobalReallocation) + return; + types::TypeSet::WatchObjectStateChange(cx, globalObj->getType(cx)); + hasGlobalReallocation = true; +} - // Before: ITER OBJ ITER - // After: ITER OBJ ITER VALUE - iterNext(); +void +mjit::Compiler::updateVarType() +{ + if (!cx->typeInferenceEnabled()) + return; - // Before: ITER OBJ ITER VALUE - // After: ITER OBJ VALUE - frame.shimmy(1); + /* + * For any non-escaping variable written at the current opcode, update the + * associated type sets according to the written type, keeping the type set + * for each variable in sync with what the SSA analysis has determined + * (see prepareInferenceTypes). + */ - // Before: ITER OBJ VALUE - // After: ITER VALUE - jsop_setprop(atom, false); + types::TypeSet *types = pushedTypeSet(0); + uint32_t slot = GetBytecodeSlot(script, PC); - // Before: ITER VALUE - // After: ITER - frame.pop(); + if (analysis->trackSlot(slot)) { + VarType &vt = a->varTypes[slot]; + vt.setTypes(types); + + /* + * Variables whose type has been inferred as a double need to be + * maintained by the frame as a double. We might forget the exact + * representation used by the next call to fixDoubleTypes, fix it now. + */ + if (vt.getTypeTag(cx) == JSVAL_TYPE_DOUBLE) + frame.ensureDouble(frame.getSlotEntry(slot)); + } } void -mjit::Compiler::jsop_forname(JSAtom *atom) +mjit::Compiler::updateJoinVarTypes() { - // Before: ITER - // After: ITER SCOPEOBJ - jsop_bindname(atom, false); - jsop_forprop(atom); + if (!cx->typeInferenceEnabled()) + return; + + /* Update variable types for all new values at this bytecode. */ + const SlotValue *newv = analysis->newValues(PC); + if (newv) { + while (newv->slot) { + if (newv->slot < TotalSlots(script)) { + VarType &vt = a->varTypes[newv->slot]; + JSValueType type = vt.getTypeTag(cx); + vt.setTypes(analysis->getValueTypes(newv->value)); + if (vt.getTypeTag(cx) != type) { + /* + * If the known type of a variable changes (even if the + * variable itself has not been reassigned) then we can't + * carry a loop register for the var. + */ + FrameEntry *fe = frame.getSlotEntry(newv->slot); + frame.forgetLoopReg(fe); + } + } + newv++; + } + } } void -mjit::Compiler::jsop_forgname(JSAtom *atom) +mjit::Compiler::restoreVarType() { - // Before: ITER - // After: ITER GLOBAL - jsop_bindgname(); + if (!cx->typeInferenceEnabled()) + return; + + uint32_t slot = GetBytecodeSlot(script, PC); - // Before: ITER GLOBAL - // After: ITER GLOBAL ITER - frame.dupAt(-2); + if (slot >= analyze::TotalSlots(script)) + return; - // Before: ITER GLOBAL ITER - // After: ITER GLOBAL ITER VALUE - iterNext(); + /* + * Restore the known type of a live local or argument. We ensure that types + * of tracked variables match their inferred type (as tracked in varTypes), + * but may have forgotten it due to a branch or syncAndForgetEverything. + */ + JSValueType type = a->varTypes[slot].getTypeTag(cx); + if (type != JSVAL_TYPE_UNKNOWN && + (type != JSVAL_TYPE_DOUBLE || analysis->trackSlot(slot))) { + FrameEntry *fe = frame.getSlotEntry(slot); + JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(type)); + if (!fe->isTypeKnown()) + frame.learnType(fe, type, false); + } +} - // Before: ITER GLOBAL ITER VALUE - // After: ITER GLOBAL VALUE - frame.shimmy(1); +JSValueType +mjit::Compiler::knownPushedType(uint32_t pushed) +{ + if (!cx->typeInferenceEnabled()) + return JSVAL_TYPE_UNKNOWN; + types::TypeSet *types = analysis->pushedTypes(PC, pushed); + return types->getKnownTypeTag(cx); +} + +bool +mjit::Compiler::mayPushUndefined(uint32_t pushed) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + /* + * This should only be used when the compiler is checking if it is OK to push + * undefined without going to a stub that can trigger recompilation. + * If this returns false and undefined subsequently becomes a feasible + * value pushed by the bytecode, recompilation will *NOT* be triggered. + */ + types::TypeSet *types = analysis->pushedTypes(PC, pushed); + return types->hasType(types::Type::UndefinedType()); +} + +types::TypeSet * +mjit::Compiler::pushedTypeSet(uint32_t pushed) +{ + if (!cx->typeInferenceEnabled()) + return NULL; + return analysis->pushedTypes(PC, pushed); +} + +bool +mjit::Compiler::monitored(jsbytecode *pc) +{ + if (!cx->typeInferenceEnabled()) + return false; + return analysis->getCode(pc).monitoredTypes; +} + +bool +mjit::Compiler::hasTypeBarriers(jsbytecode *pc) +{ + if (!cx->typeInferenceEnabled()) + return false; + + return analysis->typeBarriers(cx, pc) != NULL; +} + +void +mjit::Compiler::pushSyncedEntry(uint32_t pushed) +{ + frame.pushSynced(knownPushedType(pushed)); +} + +JSObject * +mjit::Compiler::pushedSingleton(unsigned pushed) +{ + if (!cx->typeInferenceEnabled()) + return NULL; + + types::TypeSet *types = analysis->pushedTypes(PC, pushed); + return types->getSingleton(cx); +} + +/* + * Barriers overview. + * + * After a property fetch finishes, we may need to do type checks on it to make + * sure it matches the pushed type set for this bytecode. This can be either + * because there is a type barrier at the bytecode, or because we cannot rule + * out an undefined result. For such accesses, we push a register pair, and + * then use those registers to check the fetched type matches the inferred + * types for the pushed set. The flow here is tricky: + * + * frame.pushRegs(type, data, knownType); + * --- Depending on knownType, the frame's representation for the pushed entry + * may not be a register pair anymore. knownType is based on the observed + * types that have been pushed here and may not actually match type/data. + * pushRegs must not clobber either register, for the test below. + * + * testBarrier(type, data) + * --- Use the type/data regs and generate a single jump taken if the barrier + * has been violated. + * + * --- Rearrange stack, rejoin from stub paths. No code must be emitted into + * the inline path between testBarrier and finishBarrier. Since a stub path + * may be in progress we can't call finishBarrier before stubcc.rejoin, + * and since typeReg/dataReg may not be intact after the stub call rejoin + * (if knownType != JSVAL_TYPE_UNKNOWN) we can't testBarrier after calling + * stubcc.rejoin. + * + * finishBarrier() + * --- Link the barrier jump to a new stub code path which updates the pushed + * types (possibly triggering recompilation). The frame has changed since + * pushRegs to reflect the final state of the op, which is OK as no inline + * code has been emitted since the barrier jump. + */ + +mjit::Compiler::BarrierState +mjit::Compiler::pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase, + bool testUndefined) +{ + if (!hasTypeBarriers(PC) && !testUndefined) { + frame.push(address, type, reuseBase); + return BarrierState(); + } + + RegisterID typeReg, dataReg; + frame.loadIntoRegisters(address, reuseBase, &typeReg, &dataReg); + + frame.pushRegs(typeReg, dataReg, type); + return testBarrier(typeReg, dataReg, testUndefined); +} + +MaybeJump +mjit::Compiler::trySingleTypeTest(types::TypeSet *types, RegisterID typeReg) +{ + /* + * If a type set we have a barrier on is monomorphic, generate a single + * jump taken if a type register has a match. This doesn't handle type sets + * containing objects, as these require two jumps regardless (test for + * object, then test the type of the object). + */ + MaybeJump res; + + switch (types->getKnownTypeTag(cx)) { + case JSVAL_TYPE_INT32: + res.setJump(masm.testInt32(Assembler::NotEqual, typeReg)); + return res; + + case JSVAL_TYPE_DOUBLE: + res.setJump(masm.testNumber(Assembler::NotEqual, typeReg)); + return res; + + case JSVAL_TYPE_BOOLEAN: + res.setJump(masm.testBoolean(Assembler::NotEqual, typeReg)); + return res; + + case JSVAL_TYPE_STRING: + res.setJump(masm.testString(Assembler::NotEqual, typeReg)); + return res; + + default: + return res; + } +} + +JSC::MacroAssembler::Jump +mjit::Compiler::addTypeTest(types::TypeSet *types, RegisterID typeReg, RegisterID dataReg) +{ + /* + * :TODO: It would be good to merge this with GenerateTypeCheck, but the + * two methods have a different format for the tested value (in registers + * vs. in memory). + */ + + Vector matches(CompilerAllocPolicy(cx, *this)); + + if (types->hasType(types::Type::Int32Type())) + matches.append(masm.testInt32(Assembler::Equal, typeReg)); + + if (types->hasType(types::Type::DoubleType())) + matches.append(masm.testDouble(Assembler::Equal, typeReg)); + + if (types->hasType(types::Type::UndefinedType())) + matches.append(masm.testUndefined(Assembler::Equal, typeReg)); + + if (types->hasType(types::Type::BooleanType())) + matches.append(masm.testBoolean(Assembler::Equal, typeReg)); + + if (types->hasType(types::Type::StringType())) + matches.append(masm.testString(Assembler::Equal, typeReg)); + + if (types->hasType(types::Type::NullType())) + matches.append(masm.testNull(Assembler::Equal, typeReg)); + + unsigned count = 0; + if (types->hasType(types::Type::AnyObjectType())) + matches.append(masm.testObject(Assembler::Equal, typeReg)); + else + count = types->getObjectCount(); + + if (count != 0) { + Jump notObject = masm.testObject(Assembler::NotEqual, typeReg); + Address typeAddress(dataReg, JSObject::offsetOfType()); + + for (unsigned i = 0; i < count; i++) { + if (JSObject *object = types->getSingleObject(i)) + matches.append(masm.branchPtr(Assembler::Equal, dataReg, ImmPtr(object))); + } + + for (unsigned i = 0; i < count; i++) { + if (types::TypeObject *object = types->getTypeObject(i)) + matches.append(masm.branchPtr(Assembler::Equal, typeAddress, ImmPtr(object))); + } + + notObject.linkTo(masm.label(), &masm); + } + + Jump mismatch = masm.jump(); + + for (unsigned i = 0; i < matches.length(); i++) + matches[i].linkTo(masm.label(), &masm); + + return mismatch; +} + +mjit::Compiler::BarrierState +mjit::Compiler::testBarrier(RegisterID typeReg, RegisterID dataReg, + bool testUndefined, bool testReturn, bool force) +{ + BarrierState state; + state.typeReg = typeReg; + state.dataReg = dataReg; + + if (!cx->typeInferenceEnabled() || !(js_CodeSpec[*PC].format & JOF_TYPESET)) + return state; + + types::TypeSet *types = analysis->bytecodeTypes(PC); + if (types->unknown()) { + /* + * If the result of this opcode is already unknown, there is no way for + * a type barrier to fail. + */ + return state; + } + + if (testReturn) { + JS_ASSERT(!testUndefined); + if (!analysis->getCode(PC).monitoredTypesReturn) + return state; + } else if (!hasTypeBarriers(PC) && !force) { + if (testUndefined && !types->hasType(types::Type::UndefinedType())) + state.jump.setJump(masm.testUndefined(Assembler::Equal, typeReg)); + return state; + } + + types->addFreeze(cx); + + /* Cannot have type barriers when the result of the operation is already unknown. */ + JS_ASSERT(!types->unknown()); + + state.jump = trySingleTypeTest(types, typeReg); + if (!state.jump.isSet()) + state.jump.setJump(addTypeTest(types, typeReg, dataReg)); + + return state; +} + +void +mjit::Compiler::finishBarrier(const BarrierState &barrier, RejoinState rejoin, uint32_t which) +{ + if (!barrier.jump.isSet()) + return; - // Before: ITER GLOBAL VALUE - // After: ITER VALUE - jsop_setgname(atom, false); + stubcc.linkExitDirect(barrier.jump.get(), stubcc.masm.label()); - // Before: ITER VALUE - // After: ITER + /* + * Before syncing, store the entry to sp[0]. (scanInlineCalls accounted for + * this when making sure there is enough froom for all frames). The known + * type in the frame may be wrong leading to an incorrect sync, and this + * sync may also clobber typeReg and/or dataReg. + */ + frame.pushSynced(JSVAL_TYPE_UNKNOWN); + stubcc.masm.storeValueFromComponents(barrier.typeReg, barrier.dataReg, + frame.addressOf(frame.peek(-1))); frame.pop(); + + stubcc.syncExit(Uses(0)); + stubcc.leave(); + + stubcc.masm.move(ImmIntPtr(intptr_t(which)), Registers::ArgReg1); + OOL_STUBCALL(stubs::TypeBarrierHelper, rejoin); + stubcc.rejoin(Changes(0)); } +void +mjit::Compiler::testPushedType(RejoinState rejoin, int which, bool ool) +{ + if (!cx->typeInferenceEnabled() || !(js_CodeSpec[*PC].format & JOF_TYPESET)) + return; + + types::TypeSet *types = analysis->bytecodeTypes(PC); + if (types->unknown()) + return; + + Assembler &masm = ool ? stubcc.masm : this->masm; + + JS_ASSERT(which <= 0); + Address address = (which == 0) ? frame.addressOfTop() : frame.addressOf(frame.peek(which)); + + Vector mismatches(cx); + if (!masm.generateTypeCheck(cx, address, types, &mismatches)) { + oomInVector = true; + return; + } + + Jump j = masm.jump(); + + for (unsigned i = 0; i < mismatches.length(); i++) + mismatches[i].linkTo(masm.label(), &masm); + + masm.move(Imm32(which), Registers::ArgReg1); + if (ool) + OOL_STUBCALL(stubs::StubTypeHelper, rejoin); + else + INLINE_STUBCALL(stubs::StubTypeHelper, rejoin); + + j.linkTo(masm.label(), &masm); +} diff --git a/deps/mozjs/js/src/methodjit/Compiler.h b/deps/mozjs/js/src/methodjit/Compiler.h index f9a7d95b639..6cb7caa1aea 100644 --- a/deps/mozjs/js/src/methodjit/Compiler.h +++ b/deps/mozjs/js/src/methodjit/Compiler.h @@ -42,7 +42,6 @@ #include "jsanalyze.h" #include "jscntxt.h" -#include "jstl.h" #include "MethodJIT.h" #include "CodeGenIncludes.h" #include "BaseCompiler.h" @@ -53,26 +52,57 @@ namespace js { namespace mjit { +/* + * Patch for storing call site and rejoin site return addresses at, for + * redirecting the return address in InvariantFailure. + */ +struct InvariantCodePatch { + bool hasPatch; + JSC::MacroAssembler::DataLabelPtr codePatch; + InvariantCodePatch() : hasPatch(false) {} +}; + +struct JSActiveFrame { + JSActiveFrame *parent; + jsbytecode *parentPC; + JSScript *script; + + /* + * Index into inlineFrames or OUTER_FRAME, matches this frame's index in + * the cross script SSA. + */ + uint32_t inlineIndex; + + /* JIT code generation tracking state */ + size_t mainCodeStart; + size_t stubCodeStart; + size_t mainCodeEnd; + size_t stubCodeEnd; + size_t inlinePCOffset; + + JSActiveFrame(); +}; + class Compiler : public BaseCompiler { friend class StubCompiler; struct BranchPatch { - BranchPatch(const Jump &j, jsbytecode *pc) - : jump(j), pc(pc) + BranchPatch(const Jump &j, jsbytecode *pc, uint32_t inlineIndex) + : jump(j), pc(pc), inlineIndex(inlineIndex) { } Jump jump; jsbytecode *pc; + uint32_t inlineIndex; }; #if defined JS_MONOIC struct GlobalNameICInfo { Label fastPathStart; Call slowPathCall; - DataLabel32 shape; + DataLabelPtr shape; DataLabelPtr addrLabel; - bool usePropertyCache; void copyTo(ic::GlobalNameIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) { to.fastPathStart = full.locationOf(fastPathStart); @@ -82,7 +112,6 @@ class Compiler : public BaseCompiler JS_ASSERT(to.shapeOffset == offset); to.slowPathCall = stub.locationOf(slowPathCall); - to.usePropertyCache = usePropertyCache; } }; @@ -109,32 +138,21 @@ class Compiler : public BaseCompiler MaybeJump jumpToStub; Label fallThrough; jsbytecode *jumpTarget; + bool trampoline; + Label trampolineStart; ValueRemat lvr, rvr; Assembler::Condition cond; JSC::MacroAssembler::RegisterID tempReg; }; - - struct TraceGenInfo { - bool initialized; - Label stubEntry; - DataLabelPtr addrLabel; - jsbytecode *jumpTarget; - Jump traceHint; - MaybeJump slowTraceHint; - - TraceGenInfo() : initialized(false) {} - }; /* InlineFrameAssembler wants to see this. */ public: struct CallGenInfo { - CallGenInfo(jsbytecode *pc) : pc(pc) {} - /* * These members map to members in CallICInfo. See that structure for * more comments. */ - jsbytecode *pc; + uint32_t callIndex; DataLabelPtr funGuard; Jump funJump; Jump hotJump; @@ -148,8 +166,8 @@ class Compiler : public BaseCompiler Jump oolJump; Label icCall; RegisterID funObjReg; - RegisterID funPtrReg; FrameSize frameSize; + bool typeMonitored; }; private: @@ -160,16 +178,17 @@ class Compiler : public BaseCompiler * absolute address of the join point is known. */ struct CallPatchInfo { - CallPatchInfo() : hasFastNcode(false), hasSlowNcode(false) {} + CallPatchInfo() : hasFastNcode(false), hasSlowNcode(false), joinSlow(false) {} Label joinPoint; DataLabelPtr fastNcodePatch; DataLabelPtr slowNcodePatch; bool hasFastNcode; bool hasSlowNcode; + bool joinSlow; }; struct BaseICInfo { - BaseICInfo(JSOp op) : op(op) + BaseICInfo(JSOp op) : op(op), canCallHook(false), forcedTypeBarrier(false) { } Label fastPathStart; Label fastPathRejoin; @@ -177,12 +196,16 @@ class Compiler : public BaseCompiler Call slowPathCall; DataLabelPtr paramAddr; JSOp op; + bool canCallHook; + bool forcedTypeBarrier; void copyTo(ic::BaseIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) { to.fastPathStart = full.locationOf(fastPathStart); to.fastPathRejoin = full.locationOf(fastPathRejoin); to.slowPathStart = stub.locationOf(slowPathStart); to.slowPathCall = stub.locationOf(slowPathCall); + to.canCallHook = canCallHook; + to.forcedTypeBarrier = forcedTypeBarrier; to.op = op; JS_ASSERT(to.op == op); } @@ -195,7 +218,7 @@ class Compiler : public BaseCompiler RegisterID objReg; ValueRemat id; MaybeJump typeGuard; - Jump claspGuard; + Jump shapeGuard; }; struct SetElementICInfo : public BaseICInfo { @@ -205,28 +228,28 @@ class Compiler : public BaseCompiler StateRemat objRemat; ValueRemat vr; Jump capacityGuard; - Jump claspGuard; + Jump shapeGuard; Jump holeGuard; Int32Key key; - uint32 volatileMask; + uint32_t volatileMask; }; struct PICGenInfo : public BaseICInfo { - PICGenInfo(ic::PICInfo::Kind kind, JSOp op, bool usePropCache) - : BaseICInfo(op), kind(kind), usePropCache(usePropCache) + PICGenInfo(ic::PICInfo::Kind kind, JSOp op) + : BaseICInfo(op), kind(kind), typeMonitored(false) { } ic::PICInfo::Kind kind; Label typeCheck; RegisterID shapeReg; RegisterID objReg; RegisterID typeReg; - bool usePropCache; Label shapeGuard; jsbytecode *pc; - JSAtom *atom; + PropertyName *name; bool hasTypeCheck; + bool typeMonitored; + types::TypeSet *rhsTypes; ValueRemat vr; -#ifdef JS_HAS_IC_LABELS union { ic::GetPropLabels getPropLabels_; ic::SetPropLabels setPropLabels_; @@ -235,7 +258,7 @@ class Compiler : public BaseCompiler }; ic::GetPropLabels &getPropLabels() { - JS_ASSERT(kind == ic::PICInfo::GET || kind == ic::PICInfo::CALL); + JS_ASSERT(kind == ic::PICInfo::GET); return getPropLabels_; } ic::SetPropLabels &setPropLabels() { @@ -247,41 +270,24 @@ class Compiler : public BaseCompiler return bindNameLabels_; } ic::ScopeNameLabels &scopeNameLabels() { - JS_ASSERT(kind == ic::PICInfo::NAME || kind == ic::PICInfo::XNAME); + JS_ASSERT(kind == ic::PICInfo::NAME || + kind == ic::PICInfo::XNAME); return scopeNameLabels_; } -#else - ic::GetPropLabels &getPropLabels() { - JS_ASSERT(kind == ic::PICInfo::GET || kind == ic::PICInfo::CALL); - return ic::PICInfo::getPropLabels_; - } - ic::SetPropLabels &setPropLabels() { - JS_ASSERT(kind == ic::PICInfo::SET || kind == ic::PICInfo::SETMETHOD); - return ic::PICInfo::setPropLabels_; - } - ic::BindNameLabels &bindNameLabels() { - JS_ASSERT(kind == ic::PICInfo::BIND); - return ic::PICInfo::bindNameLabels_; - } - ic::ScopeNameLabels &scopeNameLabels() { - JS_ASSERT(kind == ic::PICInfo::NAME || kind == ic::PICInfo::XNAME); - return ic::PICInfo::scopeNameLabels_; - } -#endif void copySimpleMembersTo(ic::PICInfo &ic) { ic.kind = kind; ic.shapeReg = shapeReg; ic.objReg = objReg; - ic.atom = atom; - ic.usePropCache = usePropCache; + ic.name = name; if (ic.isSet()) { ic.u.vr = vr; } else if (ic.isGet()) { ic.u.get.typeReg = typeReg; ic.u.get.hasTypeCheck = hasTypeCheck; } -#ifdef JS_HAS_IC_LABELS + ic.typeMonitored = typeMonitored; + ic.rhsTypes = rhsTypes; if (ic.isGet()) ic.setLabels(getPropLabels()); else if (ic.isSet()) @@ -290,28 +296,33 @@ class Compiler : public BaseCompiler ic.setLabels(bindNameLabels()); else if (ic.isScopeName()) ic.setLabels(scopeNameLabels()); -#endif } }; struct Defs { - Defs(uint32 ndefs) + Defs(uint32_t ndefs) : ndefs(ndefs) { } - uint32 ndefs; + uint32_t ndefs; }; struct InternalCallSite { - uint32 returnOffset; - jsbytecode *pc; - uint32 id; - bool call; + uint32_t returnOffset; + DataLabelPtr inlinePatch; + uint32_t inlineIndex; + jsbytecode *inlinepc; + RejoinState rejoin; bool ool; - - InternalCallSite(uint32 returnOffset, jsbytecode *pc, uint32 id, - bool call, bool ool) - : returnOffset(returnOffset), pc(pc), id(id), call(call), ool(ool) + Label loopJumpLabel; + InvariantCodePatch loopPatch; + + InternalCallSite(uint32_t returnOffset, + uint32_t inlineIndex, jsbytecode *inlinepc, + RejoinState rejoin, bool ool) + : returnOffset(returnOffset), + inlineIndex(inlineIndex), inlinepc(inlinepc), + rejoin(rejoin), ool(ool) { } }; @@ -326,25 +337,124 @@ class Compiler : public BaseCompiler size_t offsetIndex; }; - StackFrame *fp; - JSScript *script; - JSObject *scopeChain; - JSObject *globalObj; - JSFunction *fun; + struct JumpTableEdge { + uint32_t source; + uint32_t target; + }; + + struct ChunkJumpTableEdge { + JumpTableEdge edge; + void **jumpTableEntry; + }; + + struct LoopEntry { + uint32_t pcOffset; + Label label; + }; + + /* + * Information about the current type of an argument or local in the + * script. The known type tag of these types is cached when possible to + * avoid generating duplicate dependency constraints. + */ + class VarType { + JSValueType type; + types::TypeSet *types; + + public: + void setTypes(types::TypeSet *types) { + this->types = types; + this->type = JSVAL_TYPE_MISSING; + } + + types::TypeSet *getTypes() { return types; } + + JSValueType getTypeTag(JSContext *cx) { + if (type == JSVAL_TYPE_MISSING) + type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN; + return type; + } + }; + + struct OutgoingChunkEdge { + uint32_t source; + uint32_t target; + + Jump fastJump; + MaybeJump slowJump; + }; + + struct SlotType + { + uint32_t slot; + VarType vt; + SlotType(uint32_t slot, VarType vt) : slot(slot), vt(vt) {} + }; + + JSScript *outerScript; + unsigned chunkIndex; bool isConstructing; - analyze::Script *analysis; - Label *jumpMap; - bool *savedTraps; - jsbytecode *PC; + ChunkDescriptor &outerChunk; + + /* SSA information for the outer script and all frames we will be inlining. */ + analyze::CrossScriptSSA ssa; + + GlobalObject *globalObj; + const HeapValue *globalSlots; /* Original slots pointer. */ + Assembler masm; FrameState frame; + + /* + * State for the current stack frame, and links to its parents going up to + * the outermost script. + */ + +public: + struct ActiveFrame : public JSActiveFrame { + Label *jumpMap; + + /* Current types for non-escaping vars in the script. */ + VarType *varTypes; + + /* State for managing return from inlined frames. */ + bool needReturnValue; /* Return value will be used. */ + bool syncReturnValue; /* Return value should be fully synced. */ + bool returnValueDouble; /* Return value should be a double. */ + bool returnSet; /* Whether returnRegister is valid. */ + AnyRegisterID returnRegister; /* Register holding return value. */ + const FrameEntry *returnEntry; /* Entry copied by return value. */ + Vector *returnJumps; + + /* + * Snapshot of the heap state to use after the call, in case + * there are multiple return paths the inlined frame could take. + */ + RegisterAllocation *exitState; + + ActiveFrame(JSContext *cx); + ~ActiveFrame(); + }; + +private: + ActiveFrame *a; + ActiveFrame *outer; + + JSScript *script; + analyze::ScriptAnalysis *analysis; + jsbytecode *PC; + + LoopState *loop; + + /* State spanning all stack frames. */ + + js::Vector inlineFrames; js::Vector branchPatches; #if defined JS_MONOIC js::Vector getGlobalNames; js::Vector setGlobalNames; js::Vector callICs; js::Vector equalityICs; - js::Vector traceICs; #endif #if defined JS_POLYIC js::Vector pics; @@ -354,61 +464,157 @@ class Compiler : public BaseCompiler js::Vector callPatches; js::Vector callSites; js::Vector doubleList; + js::Vector fixedIntToDoubleEntries; + js::Vector fixedDoubleToAnyEntries; js::Vector jumpTables; - js::Vector jumpTableOffsets; + js::Vector jumpTableEdges; + js::Vector loopEntries; + js::Vector chunkEdges; StubCompiler stubcc; Label invokeLabel; Label arityLabel; + Label argsCheckLabel; +#ifdef JS_MONOIC + Label argsCheckStub; + Label argsCheckFallthrough; + Jump argsCheckJump; +#endif bool debugMode_; - bool addTraceHints; + bool inlining_; + bool hasGlobalReallocation; bool oomInVector; // True if we have OOM'd appending to a vector. + bool overflowICSpace; // True if we added a constant pool in a reserved space. + uint32_t gcNumber; enum { NoApplyTricks, LazyArgsObj } applyTricks; + PCLengthEntry *pcLengths; Compiler *thisFromCtor() { return this; } friend class CompilerAllocPolicy; public: - // Special atom index used to indicate that the atom is 'length'. This - // follows interpreter usage in JSOP_LENGTH. - enum { LengthAtomIndex = uint32(-2) }; - - Compiler(JSContext *cx, StackFrame *fp); + Compiler(JSContext *cx, JSScript *outerScript, unsigned chunkIndex, bool isConstructing); ~Compiler(); CompileStatus compile(); - jsbytecode *getPC() { return PC; } Label getLabel() { return masm.label(); } bool knownJump(jsbytecode *pc); - Label labelOf(jsbytecode *target); - void *findCallSite(const CallSite &callSite); + Label labelOf(jsbytecode *target, uint32_t inlineIndex); void addCallSite(const InternalCallSite &callSite); - void addReturnSite(Label joinPoint, uint32 id); - bool loadOldTraps(const Vector &site); + void addReturnSite(); + void inlineStubCall(void *stub, RejoinState rejoin, Uses uses); bool debugMode() { return debugMode_; } + bool inlining() { return inlining_; } + bool constructing() { return isConstructing; } + + jsbytecode *outerPC() { + if (a == outer) + return PC; + ActiveFrame *scan = a; + while (scan && scan->parent != outer) + scan = static_cast(scan->parent); + return scan->parentPC; + } + + JITScript *outerJIT() { + return outerScript->getJIT(isConstructing); + } + + bool bytecodeInChunk(jsbytecode *pc) { + return (unsigned(pc - outerScript->code) >= outerChunk.begin) + && (unsigned(pc - outerScript->code) < outerChunk.end); + } + + jsbytecode *inlinePC() { return PC; } + uint32_t inlineIndex() { return a->inlineIndex; } + + Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; } + + InvariantCodePatch *getInvariantPatch(unsigned index) { + return &callSites[index].loopPatch; + } + jsbytecode *getInvariantPC(unsigned index) { + return callSites[index].inlinepc; + } + + bool activeFrameHasMultipleExits() { + ActiveFrame *na = a; + while (na->parent) { + if (na->exitState) + return true; + na = static_cast(na->parent); + } + return false; + } private: - CompileStatus performCompilation(JITScript **jitp); + CompileStatus performCompilation(); CompileStatus generatePrologue(); CompileStatus generateMethod(); CompileStatus generateEpilogue(); - CompileStatus finishThisUp(JITScript **jitp); + CompileStatus finishThisUp(); + CompileStatus pushActiveFrame(JSScript *script, uint32_t argc); + void popActiveFrame(); + void updatePCCounters(jsbytecode *pc, Label *start, bool *updated); + void updatePCTypes(jsbytecode *pc, FrameEntry *fe); + void updateArithCounters(jsbytecode *pc, FrameEntry *fe, + JSValueType firstUseType, JSValueType secondUseType); + void updateElemCounters(jsbytecode *pc, FrameEntry *obj, FrameEntry *id); + void bumpPropCounter(jsbytecode *pc, int counter); + + /* Analysis helpers. */ + CompileStatus prepareInferenceTypes(JSScript *script, ActiveFrame *a); + void ensureDoubleArguments(); + void markUndefinedLocals(); + void fixDoubleTypes(jsbytecode *target); + void watchGlobalReallocation(); + void updateVarType(); + void updateJoinVarTypes(); + void restoreVarType(); + JSValueType knownPushedType(uint32_t pushed); + bool mayPushUndefined(uint32_t pushed); + types::TypeSet *pushedTypeSet(uint32_t which); + bool monitored(jsbytecode *pc); + bool hasTypeBarriers(jsbytecode *pc); + bool testSingletonProperty(JSObject *obj, jsid id); + bool testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testObject); + CompileStatus addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc); + CompileStatus scanInlineCalls(uint32_t index, uint32_t depth); + CompileStatus checkAnalysis(JSScript *script); + + struct BarrierState { + MaybeJump jump; + RegisterID typeReg; + RegisterID dataReg; + }; + + MaybeJump trySingleTypeTest(types::TypeSet *types, RegisterID typeReg); + Jump addTypeTest(types::TypeSet *types, RegisterID typeReg, RegisterID dataReg); + BarrierState pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase, + bool testUndefined = false); + BarrierState testBarrier(RegisterID typeReg, RegisterID dataReg, + bool testUndefined = false, bool testReturn = false, + bool force = false); + void finishBarrier(const BarrierState &barrier, RejoinState rejoin, uint32_t which); + + void testPushedType(RejoinState rejoin, int which, bool ool = true); /* Non-emitting helpers. */ - uint32 fullAtomIndex(jsbytecode *pc); + void pushSyncedEntry(uint32_t pushed); + uint32_t fullAtomIndex(jsbytecode *pc); bool jumpInScript(Jump j, jsbytecode *pc); bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs); bool canUseApplyTricks(); /* Emitting helpers. */ - void restoreFrameRegs(Assembler &masm); + bool constantFoldBranch(jsbytecode *target, bool taken); bool emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused); bool iter(uintN flags); - void iterNext(); - bool iterMore(); + void iterNext(ptrdiff_t offset); + bool iterMore(jsbytecode *target); void iterEnd(); - MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); + MaybeJump loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated); #ifdef JS_POLYIC void passICAddress(BaseICInfo *ic); #endif @@ -416,70 +622,82 @@ class Compiler : public BaseCompiler void passMICAddress(GlobalNameICInfo &mic); #endif bool constructThis(); + void ensureDouble(FrameEntry *fe); + + /* + * Ensure fe is an integer, truncating from double if necessary, or jump to + * the slow path per uses. + */ + void ensureInteger(FrameEntry *fe, Uses uses); + + /* Convert fe from a double to integer (per ValueToECMAInt32) in place. */ + void truncateDoubleToInt32(FrameEntry *fe, Uses uses); + + /* + * Try to convert a double fe to an integer, with no truncation performed, + * or jump to the slow path per uses. + */ + void tryConvertInteger(FrameEntry *fe, Uses uses); /* Opcode handlers. */ - bool jumpAndTrace(Jump j, jsbytecode *target, Jump *slow = NULL); - void jsop_bindname(JSAtom *atom, bool usePropCache); - void jsop_setglobal(uint32 index); - void jsop_getglobal(uint32 index); - void jsop_getprop_slow(JSAtom *atom, bool usePropCache = true); - void jsop_getarg(uint32 slot); - void jsop_setarg(uint32 slot, bool popped); + bool jumpAndRun(Jump j, jsbytecode *target, + Jump *slow = NULL, bool *trampoline = NULL, + bool fallthrough = false); + bool startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget); + bool finishLoop(jsbytecode *head); + inline bool shouldStartLoop(jsbytecode *head); + void jsop_bindname(PropertyName *name); + void jsop_setglobal(uint32_t index); + void jsop_getprop_slow(PropertyName *name, bool forPrototype = false); + void jsop_getarg(uint32_t slot); + void jsop_setarg(uint32_t slot, bool popped); void jsop_this(); void emitReturn(FrameEntry *fe); void emitFinalReturn(Assembler &masm); void loadReturnValue(Assembler *masm, FrameEntry *fe); void emitReturnValue(Assembler *masm, FrameEntry *fe); - void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); + void emitInlineReturnValue(FrameEntry *fe); + void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc); void interruptCheckHelper(); - void emitUncachedCall(uint32 argc, bool callingNew); - void checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedArgc, + void recompileCheckHelper(); + void emitUncachedCall(uint32_t argc, bool callingNew); + void checkCallApplySpeculation(uint32_t callImmArgc, uint32_t speculatedArgc, FrameEntry *origCallee, FrameEntry *origThis, MaybeRegisterID origCalleeType, RegisterID origCalleeData, MaybeRegisterID origThisType, RegisterID origThisData, Jump *uncachedCallSlowRejoin, CallPatchInfo *uncachedCallPatch); - void inlineCallHelper(uint32 argc, bool callingNew); + bool inlineCallHelper(uint32_t argc, bool callingNew, FrameSize &callFrameSize); void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe); - void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index); - bool jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index); - bool jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index); - void jsop_eleminc(JSOp op, VoidStub); - void jsop_getgname(uint32 index); - void jsop_getgname_slow(uint32 index); - void jsop_callgname_epilogue(); - void jsop_setgname(JSAtom *atom, bool usePropertyCache); - void jsop_setgname_slow(JSAtom *atom, bool usePropertyCache); + void jsop_getgname(uint32_t index); + void jsop_getgname_slow(uint32_t index); + void jsop_setgname(PropertyName *name, bool popGuaranteed); + void jsop_setgname_slow(PropertyName *name); void jsop_bindgname(); void jsop_setelem_slow(); void jsop_getelem_slow(); - void jsop_callelem_slow(); - void jsop_unbrand(); - bool jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true); - bool jsop_length(); - bool jsop_setprop(JSAtom *atom, bool usePropCache = true); - void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true); - bool jsop_callprop_slow(JSAtom *atom); - bool jsop_callprop(JSAtom *atom); - bool jsop_callprop_obj(JSAtom *atom); - bool jsop_callprop_str(JSAtom *atom); - bool jsop_callprop_generic(JSAtom *atom); + bool jsop_getprop(PropertyName *name, JSValueType type, + bool typeCheck = true, bool forPrototype = false); + bool jsop_getprop_dispatch(PropertyName *name); + bool jsop_setprop(PropertyName *name, bool popGuaranteed); + void jsop_setprop_slow(PropertyName *name); bool jsop_instanceof(); - void jsop_name(JSAtom *atom); - bool jsop_xname(JSAtom *atom); - void enterBlock(JSObject *obj); + void jsop_name(PropertyName *name, JSValueType type); + bool jsop_xname(PropertyName *name); + void enterBlock(StaticBlockObject *block); void leaveBlock(); - void emitEval(uint32 argc); - void jsop_arguments(); + void emitEval(uint32_t argc); + void jsop_arguments(RejoinState rejoin); bool jsop_tableswitch(jsbytecode *pc); - void jsop_forprop(JSAtom *atom); - void jsop_forname(JSAtom *atom); - void jsop_forgname(JSAtom *atom); /* Fast arithmetic. */ - void jsop_binary(JSOp op, VoidStub stub); - void jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub); - void jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub); - void jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub); + bool jsop_binary_slow(JSOp op, VoidStub stub, JSValueType type, FrameEntry *lhs, FrameEntry *rhs); + bool jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet); + void jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub, + JSValueType type, bool cannotOverflow, bool ignoreOverflow); + void jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub, + JSValueType type); + void jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub, + JSValueType type); void slowLoadConstantDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg); void maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe, @@ -487,9 +705,9 @@ class Compiler : public BaseCompiler void maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe, MaybeRegisterID &mreg); bool jsop_relational(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); - bool jsop_relational_self(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); bool jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); bool jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); + bool jsop_relational_int(JSOp op, jsbytecode *target, JSOp fused); void emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s, MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber, @@ -497,21 +715,11 @@ class Compiler : public BaseCompiler void emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s, MaybeJump &rhsNotNumber2); bool tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, - FrameEntry *lhs, FrameEntry *rhs); + FrameEntry *lhs, FrameEntry *rhs, Value *vp); /* Fast opcodes. */ void jsop_bitop(JSOp op); - void jsop_rsh(); - RegisterID rightRegForShift(FrameEntry *rhs); - void jsop_rsh_int_int(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_const_int(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_int_const(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_int_unknown(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_const_const(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_const_unknown(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_unknown_const(FrameEntry *lhs, FrameEntry *rhs); - void jsop_rsh_unknown_any(FrameEntry *lhs, FrameEntry *rhs); - void jsop_mod(); + bool jsop_mod(); void jsop_neg(); void jsop_bitnot(); void jsop_not(); @@ -519,46 +727,115 @@ class Compiler : public BaseCompiler bool booleanJumpScript(JSOp op, jsbytecode *target); bool jsop_ifneq(JSOp op, jsbytecode *target); bool jsop_andor(JSOp op, jsbytecode *target); - void jsop_arginc(JSOp op, uint32 slot, bool popped); - void jsop_localinc(JSOp op, uint32 slot, bool popped); - void jsop_newinit(); + bool jsop_arginc(JSOp op, uint32_t slot); + bool jsop_localinc(JSOp op, uint32_t slot); + bool jsop_newinit(); + bool jsop_regexp(); void jsop_initmethod(); void jsop_initprop(); void jsop_initelem(); + void jsop_setelem_dense(); +#ifdef JS_METHODJIT_TYPED_ARRAY + void jsop_setelem_typed(int atype); + void convertForTypedArray(int atype, ValueRemat *vr, bool *allocated); +#endif bool jsop_setelem(bool popGuaranteed); - bool jsop_getelem(bool isCall); + bool jsop_getelem(); + void jsop_getelem_dense(bool isPacked); + void jsop_getelem_args(); +#ifdef JS_METHODJIT_TYPED_ARRAY + bool jsop_getelem_typed(int atype); +#endif + void jsop_toid(); bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id); void jsop_stricteq(JSOp op); bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); + CompileStatus jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused); bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); void jsop_pos(); + void jsop_in(); + + static inline Assembler::Condition + GetCompareCondition(JSOp op, JSOp fused) + { + bool ifeq = fused == JSOP_IFEQ; + switch (op) { + case JSOP_GT: + return ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan; + case JSOP_GE: + return ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual; + case JSOP_LT: + return ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan; + case JSOP_LE: + return ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual; + case JSOP_STRICTEQ: + case JSOP_EQ: + return ifeq ? Assembler::NotEqual : Assembler::Equal; + case JSOP_STRICTNE: + case JSOP_NE: + return ifeq ? Assembler::Equal : Assembler::NotEqual; + default: + JS_NOT_REACHED("unrecognized op"); + return Assembler::Equal; + } + } + + static inline Assembler::Condition + GetStubCompareCondition(JSOp fused) + { + return fused == JSOP_IFEQ ? Assembler::Zero : Assembler::NonZero; + } + + /* Fast builtins. */ + JSObject *pushedSingleton(unsigned pushed); + CompileStatus callArrayBuiltin(uint32_t argc, bool callingNew); + CompileStatus inlineNativeFunction(uint32_t argc, bool callingNew); + CompileStatus inlineScriptedFunction(uint32_t argc, bool callingNew); + CompileStatus compileMathAbsInt(FrameEntry *arg); + CompileStatus compileMathAbsDouble(FrameEntry *arg); + CompileStatus compileMathSqrt(FrameEntry *arg); + CompileStatus compileMathMinMaxDouble(FrameEntry *arg1, FrameEntry *arg2, + Assembler::DoubleCondition cond); + CompileStatus compileMathMinMaxInt(FrameEntry *arg1, FrameEntry *arg2, + Assembler::Condition cond); + CompileStatus compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2); + CompileStatus compileArrayPush(FrameEntry *thisv, FrameEntry *arg); + CompileStatus compileArrayConcat(types::TypeSet *thisTypes, types::TypeSet *argTypes, + FrameEntry *thisValue, FrameEntry *argValue); + CompileStatus compileArrayPopShift(FrameEntry *thisv, bool isPacked, bool isArrayPop); + CompileStatus compileArrayWithLength(uint32_t argc); + CompileStatus compileArrayWithArgs(uint32_t argc); + + enum RoundingMode { Floor, Round }; + CompileStatus compileRound(FrameEntry *arg, RoundingMode mode); + + enum GetCharMode { GetChar, GetCharCode }; + CompileStatus compileGetChar(FrameEntry *thisValue, FrameEntry *arg, GetCharMode mode); + + CompileStatus compileStringFromCode(FrameEntry *arg); + CompileStatus compileParseInt(JSValueType argType, uint32_t argc); - void prepareStubCall(Uses uses); - Call emitStubCall(void *ptr); + Call emitStubCall(void *ptr, DataLabelPtr *pinline); }; -// Given a stub call, emits the call into the inline assembly path. If -// debug mode is on, adds the appropriate instrumentation for recompilation. -#define INLINE_STUBCALL(stub) \ - do { \ - Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub))); \ - if (debugMode()) { \ - InternalCallSite site(masm.callReturnOffset(cl), PC, __LINE__, \ - true, false); \ - addCallSite(site); \ - } \ - } while (0) \ - -// Given a stub call, emits the call into the out-of-line assembly path. If -// debug mode is on, adds the appropriate instrumentation for recompilation. +// Given a stub call, emits the call into the inline assembly path. rejoin +// indicates how to rejoin should this call trigger expansion/discarding. +#define INLINE_STUBCALL(stub, rejoin) \ + inlineStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), rejoin, Uses(0)) +#define INLINE_STUBCALL_USES(stub, rejoin, uses) \ + inlineStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), rejoin, uses) + +// Given a stub call, emits the call into the out-of-line assembly path. // Unlike the INLINE_STUBCALL variant, this returns the Call offset. -#define OOL_STUBCALL(stub) \ - stubcc.emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), __LINE__) \ +#define OOL_STUBCALL(stub, rejoin) \ + stubcc.emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), rejoin, Uses(0)) +#define OOL_STUBCALL_USES(stub, rejoin, uses) \ + stubcc.emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), rejoin, uses) // Same as OOL_STUBCALL, but specifies a slot depth. -#define OOL_STUBCALL_LOCAL_SLOTS(stub, slots) \ - stubcc.emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), (slots), __LINE__) \ +#define OOL_STUBCALL_LOCAL_SLOTS(stub, rejoin, slots) \ + stubcc.emitStubCall(JS_FUNC_TO_DATA_PTR(void *, (stub)), rejoin, Uses(0), (slots)) } /* namespace js */ } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/FastArithmetic.cpp b/deps/mozjs/js/src/methodjit/FastArithmetic.cpp index 3eb151b3a82..7f7377e930c 100644 --- a/deps/mozjs/js/src/methodjit/FastArithmetic.cpp +++ b/deps/mozjs/js/src/methodjit/FastArithmetic.cpp @@ -48,13 +48,14 @@ using namespace js; using namespace js::mjit; +using namespace js::analyze; using namespace JSC; typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; bool mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, - FrameEntry *lhs, FrameEntry *rhs) + FrameEntry *lhs, FrameEntry *rhs, Value *vp) { if (!lhs->isConstant() || !rhs->isConstant()) return false; @@ -81,10 +82,6 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, L.toInt32() >= 0 && R.toInt32() > 0); break; - case JSOP_RSH: - needInt = true; - break; - default: JS_NOT_REACHED("NYI"); needInt = false; /* Silence compiler warning. */ @@ -98,11 +95,11 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, * is infallible. */ if (needInt) { - ValueToECMAInt32(cx, L, &nL); - ValueToECMAInt32(cx, R, &nR); + JS_ALWAYS_TRUE(ToInt32(cx, L, &nL)); + JS_ALWAYS_TRUE(ToInt32(cx, R, &nR)); } else { - ValueToNumber(cx, L, &dL); - ValueToNumber(cx, R, &dR); + JS_ALWAYS_TRUE(ToNumber(cx, L, &dL)); + JS_ALWAYS_TRUE(ToNumber(cx, R, &dR)); } switch (op) { @@ -116,21 +113,7 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, dL *= dR; break; case JSOP_DIV: - if (dR == 0) { -#ifdef XP_WIN - if (JSDOUBLE_IS_NaN(dR)) - dL = js_NaN; - else -#endif - if (dL == 0 || JSDOUBLE_IS_NaN(dL)) - dL = js_NaN; - else if (JSDOUBLE_IS_NEG(dL) != JSDOUBLE_IS_NEG(dR)) - dL = cx->runtime->negativeInfinityValue.toDouble(); - else - dL = cx->runtime->positiveInfinityValue.toDouble(); - } else { - dL /= dR; - } + dL = js::NumberDiv(dL, dR); break; case JSOP_MOD: if (needInt) @@ -141,22 +124,15 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op, dL = js_fmod(dL, dR); break; - case JSOP_RSH: - nL >>= (nR & 31); - break; - default: JS_NOT_REACHED("NYI"); break; } - Value v; if (needInt) - v.setInt32(nL); + vp->setInt32(nL); else - v.setNumber(dL); - frame.popn(2); - frame.push(v); + vp->setNumber(dL); return true; } @@ -165,15 +141,10 @@ void mjit::Compiler::slowLoadConstantDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) { - DoublePatch patch; - if (fe->getKnownType() == JSVAL_TYPE_INT32) - patch.d = (double)fe->getValue().toInt32(); + if (fe->getValue().isInt32()) + masm.slowLoadConstantDouble((double) fe->getValue().toInt32(), fpreg); else - patch.d = fe->getValue().toDouble(); - patch.label = masm.loadDouble(NULL, fpreg); - patch.ool = &masm != &this->masm; - JS_ASSERT_IF(patch.ool, &masm == &stubcc.masm); - doubleList.append(patch); + masm.slowLoadConstantDouble(fe->getValue().toDouble(), fpreg); } void @@ -204,50 +175,84 @@ mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry } } -void -mjit::Compiler::jsop_binary(JSOp op, VoidStub stub) +bool +mjit::Compiler::jsop_binary_slow(JSOp op, VoidStub stub, JSValueType type, + FrameEntry *lhs, FrameEntry *rhs) +{ + bool isStringResult = (op == JSOP_ADD) && + (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)); + JS_ASSERT_IF(isStringResult && type != JSVAL_TYPE_UNKNOWN, type == JSVAL_TYPE_STRING); + + prepareStubCall(Uses(2)); + INLINE_STUBCALL(stub, REJOIN_BINARY); + frame.popn(2); + frame.pushSynced(isStringResult ? JSVAL_TYPE_STRING : type); + return true; +} + +bool +mjit::Compiler::jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet) { FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); - if (tryBinaryConstantFold(cx, frame, op, lhs, rhs)) - return; + Value v; + if (tryBinaryConstantFold(cx, frame, op, lhs, rhs, &v)) { + if (!v.isInt32() && typeSet && !typeSet->hasType(types::Type::DoubleType())) { + /* + * OK to ignore failure here, we aren't performing the operation + * itself. Note that monitorOverflow will propagate the type as + * necessary if a *INC operation overflowed. + */ + types::TypeScript::MonitorOverflow(cx, script, PC); + return false; + } + frame.popn(2); + frame.push(v); + return true; + } /* * Bail out if there are unhandled types or ops. * This is temporary while ops are still being implemented. */ - if ((op == JSOP_MOD) || + if ((lhs->isConstant() && rhs->isConstant()) || (lhs->isTypeKnown() && (lhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)) || - (rhs->isTypeKnown() && (rhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)) -#if defined(JS_CPU_ARM) - /* ARM cannot detect integer overflow with multiplication. */ - || op == JSOP_MUL -#endif /* JS_CPU_ARM */ - ) { - bool isStringResult = (op == JSOP_ADD) && - (lhs->isType(JSVAL_TYPE_STRING) || - rhs->isType(JSVAL_TYPE_STRING)); + (rhs->isTypeKnown() && (rhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET))) + { + return jsop_binary_slow(op, stub, type, lhs, rhs); + } - prepareStubCall(Uses(2)); - INLINE_STUBCALL(stub); - frame.popn(2); - if (isStringResult) - frame.pushSyncedType(JSVAL_TYPE_STRING); - else - frame.pushSynced(); - return; + /* + * If this is an operation on which integer overflows can be ignored, treat + * the result as an integer even if it has been marked as overflowing by + * the interpreter. Doing this changes the values we maintain on the stack + * from those the interpreter would maintain; this is OK as values derived + * from ignored overflows are not live across points where the interpreter + * can join into JIT code (loop heads and safe points). + */ + CrossSSAValue pushv(a->inlineIndex, SSAValue::PushedValue(PC - script->code, 0)); + bool cannotOverflow = loop && loop->cannotIntegerOverflow(pushv); + bool ignoreOverflow = loop && loop->ignoreIntegerOverflow(pushv); + + if (rhs->isType(JSVAL_TYPE_INT32) && lhs->isType(JSVAL_TYPE_INT32) && + op == JSOP_ADD && ignoreOverflow) { + type = JSVAL_TYPE_INT32; } /* Can do int math iff there is no double constant and the op is not division. */ - bool canDoIntMath = op != JSOP_DIV && - !((rhs->isTypeKnown() && rhs->getKnownType() == JSVAL_TYPE_DOUBLE) || - (lhs->isTypeKnown() && lhs->getKnownType() == JSVAL_TYPE_DOUBLE)); + bool canDoIntMath = op != JSOP_DIV && type != JSVAL_TYPE_DOUBLE && + !(rhs->isType(JSVAL_TYPE_DOUBLE) || lhs->isType(JSVAL_TYPE_DOUBLE)); + + if (!masm.supportsFloatingPoint() && (!canDoIntMath || frame.haveSameBacking(lhs, rhs))) + return jsop_binary_slow(op, stub, type, lhs, rhs); if (canDoIntMath) - jsop_binary_full(lhs, rhs, op, stub); + jsop_binary_full(lhs, rhs, op, stub, type, cannotOverflow, ignoreOverflow); else - jsop_binary_double(lhs, rhs, op, stub); + jsop_binary_double(lhs, rhs, op, stub, type); + + return true; } static void @@ -276,28 +281,35 @@ EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm } mjit::MaybeJump -mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg) +mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated) { MaybeJump notNumber; + if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) { + *fpReg = frame.tempFPRegForData(fe); + *allocated = false; + return notNumber; + } + + *fpReg = frame.allocFPReg(); + *allocated = true; + if (fe->isConstant()) { - slowLoadConstantDouble(masm, fe, fpReg); + slowLoadConstantDouble(masm, fe, *fpReg); } else if (!fe->isTypeKnown()) { frame.tempRegForType(fe); Jump j = frame.testDouble(Assembler::Equal, fe); notNumber = frame.testInt32(Assembler::NotEqual, fe); - frame.convertInt32ToDouble(masm, fe, fpReg); + frame.convertInt32ToDouble(masm, fe, *fpReg); Jump converted = masm.jump(); j.linkTo(masm.label(), &masm); // CANDIDATE - frame.loadDouble(fe, fpReg, masm); + frame.loadDouble(fe, *fpReg, masm); converted.linkTo(masm.label(), &masm); - } else if (fe->getKnownType() == JSVAL_TYPE_INT32) { - frame.tempRegForData(fe); - frame.convertInt32ToDouble(masm, fe, fpReg); } else { - JS_ASSERT(fe->getKnownType() == JSVAL_TYPE_DOUBLE); - frame.loadDouble(fe, fpReg, masm); + JS_ASSERT(fe->isType(JSVAL_TYPE_INT32)); + frame.tempRegForData(fe); + frame.convertInt32ToDouble(masm, fe, *fpReg); } return notNumber; @@ -308,20 +320,30 @@ mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg) * Unlike jsop_binary_full(), all integers are converted to doubles. */ void -mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub) +mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, + VoidStub stub, JSValueType type) { - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; + FPRegisterID fpLeft, fpRight; + bool allocateLeft, allocateRight; - MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft); + MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft); if (lhsNotNumber.isSet()) stubcc.linkExit(lhsNotNumber.get(), Uses(2)); + /* The left register holds the result, and needs to be mutable. */ + if (!allocateLeft) { + FPRegisterID res = frame.allocFPReg(); + masm.moveDouble(fpLeft, res); + fpLeft = res; + allocateLeft = true; + } + MaybeJump rhsNotNumber; if (frame.haveSameBacking(lhs, rhs)) { - masm.moveDouble(fpLeft, fpRight); + fpRight = fpLeft; + allocateRight = false; } else { - rhsNotNumber = loadDouble(rhs, fpRight); + rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight); if (rhsNotNumber.isSet()) stubcc.linkExit(rhsNotNumber.get(), Uses(2)); } @@ -329,56 +351,90 @@ mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Vo EmitDoubleOp(op, fpRight, fpLeft, masm); MaybeJump done; - + /* - * Try to convert result to integer. Skip this for 1/x or -1/x, as the - * result is unlikely to fit in an int. + * Try to convert result to integer, if the result has unknown or integer type. + * Skip this for 1/x or -1/x, as the result is unlikely to fit in an int. */ - if (op == JSOP_DIV && !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) && - abs(lhs->getValue().toInt32()) == 1)) { + if (op == JSOP_DIV && + (type == JSVAL_TYPE_INT32 || + (type == JSVAL_TYPE_UNKNOWN && + !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) && + abs(lhs->getValue().toInt32()) == 1)))) { RegisterID reg = frame.allocReg(); + FPRegisterID fpReg = frame.allocFPReg(); JumpList isDouble; - masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpRight); + masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpReg); masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg, frame.addressOf(lhs)); frame.freeReg(reg); + frame.freeReg(fpReg); done.setJump(masm.jump()); + isDouble.linkTo(masm.label(), &masm); } - masm.storeDouble(fpLeft, frame.addressOf(lhs)); + /* + * Inference needs to know about any operation on integers that produces a + * double result. Unless the pushed type set already contains the double + * type, we need to call a stub rather than push. Note that looking at + * the pushed type tag is not sufficient, as it will be UNKNOWN if + * we do not yet know the possible types of the division's operands. + */ + types::TypeSet *resultTypes = pushedTypeSet(0); + if (resultTypes && !resultTypes->hasType(types::Type::DoubleType())) { + /* + * Call a stub and try harder to convert to int32, failing that trigger + * recompilation of this script. + */ + stubcc.linkExit(masm.jump(), Uses(2)); + } else { + JS_ASSERT(type != JSVAL_TYPE_INT32); + if (type != JSVAL_TYPE_DOUBLE) + masm.storeDouble(fpLeft, frame.addressOf(lhs)); + } if (done.isSet()) done.getJump().linkTo(masm.label(), &masm); - if (lhsNotNumber.isSet() || rhsNotNumber.isSet()) { - stubcc.leave(); - OOL_STUBCALL(stub); - } + stubcc.leave(); + OOL_STUBCALL(stub, REJOIN_BINARY); + + if (allocateRight) + frame.freeReg(fpRight); frame.popn(2); - frame.pushNumber(MaybeRegisterID()); - if (lhsNotNumber.isSet() || rhsNotNumber.isSet()) - stubcc.rejoin(Changes(1)); + if (type == JSVAL_TYPE_DOUBLE) { + frame.pushDouble(fpLeft); + } else { + frame.freeReg(fpLeft); + frame.pushSynced(type); + } + + stubcc.rejoin(Changes(1)); } /* * Simpler version of jsop_binary_full() for when lhs == rhs. */ void -mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) +mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub, JSValueType type) { FrameEntry *lhs = frame.peek(-2); /* Easiest case: known double. Don't bother conversion back yet? */ - if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_DOUBLE) { - loadDouble(fe, FPRegisters::First); - EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, masm); + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + FPRegisterID fpreg = frame.allocFPReg(); + FPRegisterID lhs = frame.tempFPRegForData(fe); + masm.moveDouble(lhs, fpreg); + EmitDoubleOp(op, fpreg, fpreg, masm); frame.popn(2); - frame.pushNumber(MaybeRegisterID()); + + JS_ASSERT(type == JSVAL_TYPE_DOUBLE); /* :XXX: can fail */ + frame.pushDouble(fpreg); return; } @@ -393,12 +449,12 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) stubcc.linkExitDirect(notInt, stubcc.masm.label()); notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg()); - frame.loadDouble(fe, FPRegisters::First, stubcc.masm); - EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm); + frame.loadDouble(fe, regs.lhsFP, stubcc.masm); + EmitDoubleOp(op, regs.lhsFP, regs.lhsFP, stubcc.masm); /* Force the double back to memory. */ Address result = frame.addressOf(lhs); - stubcc.masm.storeDouble(FPRegisters::First, result); + stubcc.masm.storeDouble(regs.lhsFP, result); /* Load the payload into the result reg so the rejoin is safe. */ stubcc.masm.loadPayload(result, regs.result); @@ -417,11 +473,9 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result); break; -#if !defined(JS_CPU_ARM) case JSOP_MUL: overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result); break; -#endif default: JS_NOT_REACHED("unrecognized op"); @@ -430,48 +484,35 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) JS_ASSERT(overflow.isSet()); /* - * Integer overflow path. Separate from the first double path, since we - * know never to try and convert back to integer. + * Integer overflow path. Restore the original values and make a stub call, + * which could trigger recompilation. */ - MaybeJump overflowDone; stubcc.linkExitDirect(overflow.get(), stubcc.masm.label()); - { - if (regs.lhsNeedsRemat) { - Address address = masm.payloadOf(frame.addressForDataRemat(lhs)); - stubcc.masm.convertInt32ToDouble(address, FPRegisters::First); - } else if (!lhs->isConstant()) { - stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), FPRegisters::First); - } else { - slowLoadConstantDouble(stubcc.masm, lhs, FPRegisters::First); - } - - EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm); - - Address address = frame.addressOf(lhs); - stubcc.masm.storeDouble(FPRegisters::First, address); - stubcc.masm.loadPayload(address, regs.result); - - overflowDone = stubcc.masm.jump(); - } + frame.rematBinary(fe, NULL, regs, stubcc.masm); + stubcc.syncExitAndJump(Uses(2)); /* Slow paths funnel here. */ if (notNumber.isSet()) notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm); - overflowDone.get().linkTo(stubcc.masm.label(), &stubcc.masm); /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */ frame.sync(stubcc.masm, Uses(2)); stubcc.leave(); - OOL_STUBCALL(stub); + OOL_STUBCALL(stub, REJOIN_BINARY); /* Finish up stack operations. */ frame.popn(2); - frame.pushNumber(regs.result, true); - /* Merge back OOL double paths. */ + if (type == JSVAL_TYPE_INT32) + frame.pushTypedPayload(type, regs.result); + else + frame.pushNumber(regs.result, true); + + frame.freeReg(regs.lhsFP); + + /* Merge back OOL double path. */ if (doublePathDone.isSet()) stubcc.linkRejoin(doublePathDone.get()); - stubcc.linkRejoin(overflowDone.get()); stubcc.rejoin(Changes(1)); } @@ -513,10 +554,12 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) * <--------------------------------- Rejoin */ void -mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub) +mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, + VoidStub stub, JSValueType type, + bool cannotOverflow, bool ignoreOverflow) { if (frame.haveSameBacking(lhs, rhs)) { - jsop_binary_full_simple(lhs, op, stub); + jsop_binary_full_simple(lhs, op, stub, type); return; } @@ -528,9 +571,6 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32); JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32); - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; - MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone; if (!lhs->isTypeKnown()) emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone); @@ -541,17 +581,17 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void /* Perform the double addition. */ MaybeJump doublePathDone; - if (!rhs->isTypeKnown() || lhsUnknownDone.isSet()) { + if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown())) { /* If the LHS type was not known, link its path here. */ if (lhsUnknownDone.isSet()) lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm); /* Perform the double operation. */ - EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm); + EmitDoubleOp(op, regs.rhsFP, regs.lhsFP, stubcc.masm); /* Force the double back to memory. */ Address result = frame.addressOf(lhs); - stubcc.masm.storeDouble(fpLeft, result); + stubcc.masm.storeDouble(regs.lhsFP, result); /* Load the payload into the result reg so the rejoin is safe. */ stubcc.masm.loadPayload(result, regs.result); @@ -561,7 +601,7 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void } /* Time to do the integer path. Figure out the immutable side. */ - int32 value = 0; + int32_t value = 0; JSOp origOp = op; MaybeRegisterID reg; MaybeJump preOverflow; @@ -585,32 +625,43 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void } /* Okay - good to emit the integer fast-path. */ - MaybeJump overflow, negZeroDone; + MaybeJump overflow; switch (op) { case JSOP_ADD: - if (reg.isSet()) - overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result); - else - overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result); + if (cannotOverflow || ignoreOverflow) { + if (reg.isSet()) + masm.add32(reg.reg(), regs.result); + else + masm.add32(Imm32(value), regs.result); + } else { + if (reg.isSet()) + overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result); + else + overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result); + } break; case JSOP_SUB: - if (reg.isSet()) - overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result); - else - overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result); + if (cannotOverflow) { + if (reg.isSet()) + masm.sub32(reg.reg(), regs.result); + else + masm.sub32(Imm32(value), regs.result); + } else { + if (reg.isSet()) + overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result); + else + overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result); + } break; -#if !defined(JS_CPU_ARM) case JSOP_MUL: { - JS_ASSERT(reg.isSet()); - MaybeJump storeNegZero; - bool maybeNegZero = true; + bool maybeNegZero = !ignoreOverflow; bool hasConstant = (lhs->isConstant() || rhs->isConstant()); - - if (hasConstant) { + + if (hasConstant && maybeNegZero) { value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32(); RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg(); @@ -621,13 +672,28 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void else storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0)); } - overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result); + + if (cannotOverflow) { + if (reg.isSet()) + masm.mul32(reg.reg(), regs.result); + else + masm.mul32(Imm32(value), regs.result, regs.result); + } else { + if (reg.isSet()) { + overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result); + } else { + overflow = masm.branchMul32(Assembler::Overflow, Imm32(value), regs.result, + regs.result); + } + } if (maybeNegZero) { - if (!hasConstant) { + if (hasConstant) { + stubcc.linkExit(storeNegZero.get(), Uses(2)); + } else { Jump isZero = masm.branchTest32(Assembler::Zero, regs.result); stubcc.linkExitDirect(isZero, stubcc.masm.label()); - + /* Restore original value. */ if (regs.resultHasRhs) { if (regs.rhsNeedsRemat) @@ -644,61 +710,45 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void stubcc.masm.xor32(regs.result, regs.result); stubcc.crossJump(stubcc.masm.jump(), masm.label()); storeNegZero.getJump().linkTo(stubcc.masm.label(), &stubcc.masm); - } else { - JS_ASSERT(storeNegZero.isSet()); - stubcc.linkExitDirect(storeNegZero.get(), stubcc.masm.label()); + frame.rematBinary(lhs, rhs, regs, stubcc.masm); } - stubcc.masm.storeValue(DoubleValue(-0.0), frame.addressOf(lhs)); - stubcc.masm.loadPayload(frame.addressOf(lhs), regs.result); - negZeroDone = stubcc.masm.jump(); + stubcc.syncExitAndJump(Uses(2)); } break; } -#endif default: JS_NOT_REACHED("unrecognized op"); } op = origOp; - JS_ASSERT(overflow.isSet()); - /* - * Integer overflow path. Separate from the first double path, since we - * know never to try and convert back to integer. + * Integer overflow path. Restore the original values and make a stub call, + * which could trigger recompilation. */ MaybeJump overflowDone; if (preOverflow.isSet()) stubcc.linkExitDirect(preOverflow.get(), stubcc.masm.label()); - stubcc.linkExitDirect(overflow.get(), stubcc.masm.label()); - { - if (regs.lhsNeedsRemat) { - Address address = masm.payloadOf(frame.addressForDataRemat(lhs)); - stubcc.masm.convertInt32ToDouble(address, fpLeft); - } else if (!lhs->isConstant()) { - stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft); + if (overflow.isSet()) + stubcc.linkExitDirect(overflow.get(), stubcc.masm.label()); + + /* Restore the original operand registers for ADD. */ + if (regs.undoResult) { + if (reg.isSet()) { + JS_ASSERT(op == JSOP_ADD); + stubcc.masm.neg32(reg.reg()); + stubcc.masm.add32(reg.reg(), regs.result); + stubcc.masm.neg32(reg.reg()); } else { - slowLoadConstantDouble(stubcc.masm, lhs, fpLeft); + JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB); + int32_t fixValue = (op == JSOP_ADD) ? -value : value; + stubcc.masm.add32(Imm32(fixValue), regs.result); } - - if (regs.rhsNeedsRemat) { - Address address = masm.payloadOf(frame.addressForDataRemat(rhs)); - stubcc.masm.convertInt32ToDouble(address, fpRight); - } else if (!rhs->isConstant()) { - stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight); - } else { - slowLoadConstantDouble(stubcc.masm, rhs, fpRight); - } - - EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm); - - Address address = frame.addressOf(lhs); - stubcc.masm.storeDouble(fpLeft, address); - stubcc.masm.loadPayload(address, regs.result); - - overflowDone = stubcc.masm.jump(); } + frame.rematBinary(lhs, rhs, regs, stubcc.masm); + stubcc.syncExitAndJump(Uses(2)); + /* The register allocator creates at most one temporary. */ if (regs.extraFree.isSet()) frame.freeReg(regs.extraFree.reg()); @@ -715,39 +765,97 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */ frame.sync(stubcc.masm, Uses(2)); stubcc.leave(); - OOL_STUBCALL(stub); + OOL_STUBCALL(stub, REJOIN_BINARY); /* Finish up stack operations. */ frame.popn(2); - frame.pushNumber(regs.result, true); - /* Merge back OOL double paths. */ + /* + * Steal the result register if we remat the LHS/RHS by undoing the operation. + * In this case the result register was still assigned to the corresponding + * frame entry (so it is synced properly in OOL paths), so steal it back. + */ + if (regs.undoResult) + frame.takeReg(regs.result); + + if (type == JSVAL_TYPE_INT32) + frame.pushTypedPayload(type, regs.result); + else + frame.pushNumber(regs.result, true); + + frame.freeReg(regs.lhsFP); + frame.freeReg(regs.rhsFP); + + /* Merge back OOL double path. */ if (doublePathDone.isSet()) stubcc.linkRejoin(doublePathDone.get()); - if (negZeroDone.isSet()) - stubcc.linkRejoin(negZeroDone.get()); - stubcc.linkRejoin(overflowDone.get()); stubcc.rejoin(Changes(1)); } -static const uint64 DoubleNegMask = 0x8000000000000000ULL; - void mjit::Compiler::jsop_neg() { FrameEntry *fe = frame.peek(-1); + JSValueType type = knownPushedType(0); - if (fe->isTypeKnown() && fe->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) { + if ((fe->isTypeKnown() && fe->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) || + !masm.supportsFloatingPoint()) + { prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::Neg); + INLINE_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH); frame.pop(); - frame.pushSynced(); + frame.pushSynced(type); return; } JS_ASSERT(!fe->isConstant()); + /* Handle negation of a known double, or of a known integer which has previously overflowed. */ + if (fe->isType(JSVAL_TYPE_DOUBLE) || + (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_DOUBLE)) + { + FPRegisterID fpreg; + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + fpreg = frame.tempFPRegForData(fe); + } else { + fpreg = frame.allocFPReg(); + frame.convertInt32ToDouble(masm, fe, fpreg); + } + + FPRegisterID res = frame.allocFPReg(); + masm.moveDouble(fpreg, res); + masm.negateDouble(res); + + if (!fe->isType(JSVAL_TYPE_DOUBLE)) + frame.freeReg(fpreg); + + frame.pop(); + frame.pushDouble(res); + + return; + } + + /* Inline integer path for known integers. */ + if (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_INT32) { + RegisterID reg = frame.copyDataIntoReg(fe); + + /* Test for 0 and -2147483648 (both result in a double). */ + Jump zeroOrMinInt = masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff)); + stubcc.linkExit(zeroOrMinInt, Uses(1)); + + masm.neg32(reg); + + stubcc.leave(); + OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH); + + frame.pop(); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + + stubcc.rejoin(Changes(1)); + return; + } + /* Load type information into register. */ MaybeRegisterID feTypeReg; if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) { @@ -766,38 +874,36 @@ mjit::Compiler::jsop_neg() { maybeJumpIfNotDouble(masm, jmpNotDbl, fe, feTypeReg); - FPRegisterID fpreg = frame.copyEntryIntoFPReg(fe, FPRegisters::First); - -#if defined JS_CPU_X86 || defined JS_CPU_X64 - masm.loadDouble(&DoubleNegMask, FPRegisters::Second); - masm.xorDouble(FPRegisters::Second, fpreg); -#elif defined JS_CPU_ARM || defined JS_CPU_SPARC - masm.negDouble(fpreg, fpreg); -#endif + FPRegisterID fpreg = frame.allocFPReg(); + frame.loadDouble(fe, fpreg, masm); + masm.negateDouble(fpreg); /* Overwrite pushed frame's memory (before push). */ masm.storeDouble(fpreg, frame.addressOf(fe)); + frame.freeReg(fpreg); } /* Try an integer path (out-of-line). */ MaybeJump jmpNotInt; - MaybeJump jmpIntZero; - MaybeJump jmpMinInt; + MaybeJump jmpMinIntOrIntZero; MaybeJump jmpIntRejoin; Label lblIntPath = stubcc.masm.label(); { maybeJumpIfNotInt32(stubcc.masm, jmpNotInt, fe, feTypeReg); - /* 0 (int) -> -0 (double). */ - jmpIntZero.setJump(stubcc.masm.branch32(Assembler::Equal, reg, Imm32(0))); - /* int32 negation on (-2147483648) yields (-2147483648). */ - jmpMinInt.setJump(stubcc.masm.branch32(Assembler::Equal, reg, Imm32(1 << 31))); + /* Test for 0 and -2147483648 (both result in a double). */ + jmpMinIntOrIntZero = stubcc.masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff)); stubcc.masm.neg32(reg); /* Sync back with double path. */ - stubcc.masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg, - frame.addressOf(fe)); + if (type == JSVAL_TYPE_DOUBLE) { + stubcc.masm.convertInt32ToDouble(reg, Registers::FPConversionTemp); + stubcc.masm.storeDouble(Registers::FPConversionTemp, frame.addressOf(fe)); + } else { + stubcc.masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg, + frame.addressOf(fe)); + } jmpIntRejoin.setJump(stubcc.masm.jump()); } @@ -807,10 +913,10 @@ mjit::Compiler::jsop_neg() frame.unpinReg(feTypeReg.reg()); stubcc.leave(); - OOL_STUBCALL(stubs::Neg); + OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH); frame.pop(); - frame.pushSynced(); + frame.pushSynced(type); /* Link jumps. */ if (jmpNotDbl.isSet()) @@ -818,38 +924,48 @@ mjit::Compiler::jsop_neg() if (jmpNotInt.isSet()) jmpNotInt.getJump().linkTo(feSyncTarget, &stubcc.masm); - if (jmpIntZero.isSet()) - jmpIntZero.getJump().linkTo(feSyncTarget, &stubcc.masm); - if (jmpMinInt.isSet()) - jmpMinInt.getJump().linkTo(feSyncTarget, &stubcc.masm); + if (jmpMinIntOrIntZero.isSet()) + jmpMinIntOrIntZero.getJump().linkTo(feSyncTarget, &stubcc.masm); if (jmpIntRejoin.isSet()) stubcc.crossJump(jmpIntRejoin.getJump(), masm.label()); stubcc.rejoin(Changes(1)); } -void +bool mjit::Compiler::jsop_mod() { -#if defined(JS_CPU_X86) +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + JSValueType type = knownPushedType(0); FrameEntry *lhs = frame.peek(-2); FrameEntry *rhs = frame.peek(-1); - if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs)) - return; + Value v; + if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs, &v)) { + types::TypeSet *pushed = pushedTypeSet(0); + if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) { + types::TypeScript::MonitorOverflow(cx, script, PC); + return false; + } + frame.popn(2); + frame.push(v); + return true; + } - if ((lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) || - (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32)) + if ((lhs->isConstant() && rhs->isConstant()) || + (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) || + (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32) || + (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_UNKNOWN)) #endif { prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::Mod); + INLINE_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH); frame.popn(2); - frame.pushSynced(); - return; + frame.pushSynced(knownPushedType(0)); + return true; } -#if defined(JS_CPU_X86) +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) if (!lhs->isTypeKnown()) { Jump j = frame.testInt32(Assembler::NotEqual, lhs); stubcc.linkExit(j, Uses(2)); @@ -870,12 +986,12 @@ mjit::Compiler::jsop_mod() /* Get RHS into anything but EDX - could avoid more spilling? */ MaybeRegisterID temp; RegisterID rhsReg; + uint32_t mask = Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx); if (!rhs->isConstant()) { - uint32 mask = Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx); - rhsReg = frame.tempRegInMaskForData(rhs, mask); + rhsReg = frame.tempRegInMaskForData(rhs, mask).reg(); JS_ASSERT(rhsReg != X86Registers::edx); } else { - rhsReg = frame.allocReg(Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx)); + rhsReg = frame.allocReg(mask).reg(); JS_ASSERT(rhsReg != X86Registers::edx); masm.move(Imm32(rhs->getValue().toInt32()), rhsReg); temp = rhsReg; @@ -918,6 +1034,7 @@ mjit::Compiler::jsop_mod() lhsMaybeNeg = lhsIsNeg = (lhs->getValue().toInt32() < 0); } + MaybeJump gotNegZero; MaybeJump done; if (lhsMaybeNeg) { MaybeRegisterID lhsData; @@ -927,8 +1044,11 @@ mjit::Compiler::jsop_mod() MaybeJump negZero2; if (!lhsIsNeg) negZero2 = masm.branchTest32(Assembler::Zero, lhsData.reg(), Imm32(0x80000000)); - /* Darn, negative 0. */ - masm.storeValue(DoubleValue(-0.0), frame.addressOf(lhs)); + /* + * Darn, negative 0. This goes to a stub call (after our in progress call) + * which triggers recompilation if necessary. + */ + gotNegZero = masm.jump(); /* :TODO: This is wrong, must load into EDX as well. */ @@ -946,19 +1066,33 @@ mjit::Compiler::jsop_mod() if (slowPath) { stubcc.leave(); - OOL_STUBCALL(stubs::Mod); + OOL_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH); } frame.popn(2); - frame.pushNumber(X86Registers::edx); + + if (type == JSVAL_TYPE_INT32) + frame.pushTypedPayload(type, X86Registers::edx); + else + frame.pushNumber(X86Registers::edx); if (slowPath) stubcc.rejoin(Changes(1)); + + if (gotNegZero.isSet()) { + stubcc.linkExit(gotNegZero.getJump(), Uses(2)); + stubcc.leave(); + OOL_STUBCALL(stubs::NegZeroHelper, REJOIN_FALLTHROUGH); + stubcc.rejoin(Changes(1)); + } #endif + + return true; } bool -mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused) +mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, + jsbytecode *target, JSOp fused) { FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); @@ -1011,10 +1145,6 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar RegisterID tempReg = frame.allocReg(); - frame.pop(); - frame.pop(); - frame.discardFrame(); - JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n"); RESERVE_OOL_SPACE(stubcc.masm); @@ -1026,6 +1156,12 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar frame.ensureValueSynced(stubcc.masm, lhs, lvr); frame.ensureValueSynced(stubcc.masm, rhs, rvr); + bool needIntPath = (!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt); + + frame.pop(); + frame.pop(); + frame.discardFrame(); + bool needStub = true; #ifdef JS_MONOIC @@ -1038,30 +1174,27 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar ic.stubEntry = stubEntry; ic.stub = stub; - bool useIC = !addTraceHints || target >= PC; + bool useIC = !a->parent && bytecodeInChunk(target); /* Call the IC stub, which may generate a fast path. */ if (useIC) { /* Adjust for the two values just pushed. */ ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); - ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality, - frame.stackDepth() + script->nfixed + 2); + ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality, REJOIN_BRANCH, + frame.totalDepth() + 2); needStub = false; } #endif if (needStub) - OOL_STUBCALL_LOCAL_SLOTS(stub, frame.stackDepth() + script->nfixed + 2); + OOL_STUBCALL_LOCAL_SLOTS(stub, REJOIN_BRANCH, frame.totalDepth() + 2); /* * The stub call has no need to rejoin, since state is synced. * Instead, we can just test the return value. */ - Assembler::Condition ncond = (fused == JSOP_IFEQ) - ? Assembler::Zero - : Assembler::NonZero; - Jump stubBranch = - stubcc.masm.branchTest32(ncond, Registers::ReturnReg, Registers::ReturnReg); + Jump stubBranch = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); Jump stubFallthrough = stubcc.masm.jump(); JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n"); @@ -1070,7 +1203,7 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar Jump fast; MaybeJump firstStubJump; - if ((!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt)) { + if (needIntPath) { if (!lhsInt) { Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg()); stubcc.linkExitDirect(lhsFail, stubEntry); @@ -1087,9 +1220,6 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32())); else fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg()); - - if (!jumpInScript(fast, target)) - return false; } else { Jump j = masm.jump(); stubcc.linkExitDirect(j, stubEntry); @@ -1099,24 +1229,34 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar fast = masm.jump(); } + /* Jump from the stub call fallthrough to here. */ + stubcc.crossJump(stubFallthrough, masm.label()); + + bool *ptrampoline = NULL; +#ifdef JS_MONOIC + /* Remember the stub label in case there is a trampoline for the IC. */ + ic.trampoline = false; + ic.trampolineStart = stubcc.masm.label(); + if (useIC) + ptrampoline = &ic.trampoline; +#endif + + /* + * NB: jumpAndRun emits to the OOL path, so make sure not to use it + * in the middle of an in-progress slow path. + */ + if (!jumpAndRun(fast, target, &stubBranch, ptrampoline)) + return false; + #ifdef JS_MONOIC - ic.jumpToStub = firstStubJump; if (useIC) { + ic.jumpToStub = firstStubJump; ic.fallThrough = masm.label(); ic.jumpTarget = target; equalityICs.append(ic); } #endif - /* Jump from the stub call fallthrough to here. */ - stubcc.crossJump(stubFallthrough, masm.label()); - - /* - * NB: jumpAndTrace emits to the OOL path, so make sure not to use it - * in the middle of an in-progress slow path. - */ - if (!jumpAndTrace(fast, target, &stubBranch)) - return false; } else { /* No fusing. Compare, set, and push a boolean. */ @@ -1138,14 +1278,14 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar } stubcc.leave(); - OOL_STUBCALL(stub); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); RegisterID reg = frame.ownRegForData(lhs); /* x86/64's SET instruction can only take single-byte regs.*/ RegisterID resultReg = reg; if (!(Registers::maskReg(reg) & Registers::SingleByteRegs)) - resultReg = frame.allocReg(Registers::SingleByteRegs); + resultReg = frame.allocReg(Registers::SingleByteRegs).reg(); /* Emit the compare & set. */ if (rhs->isConstant()) { @@ -1170,20 +1310,22 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar } /* - * Emit an OOL path for a possibly double LHS, and possibly int32 or number RHS. + * Emit an OOL path for a possibly double LHS, and possibly int32_t or number RHS. */ void mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s, MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber, MaybeJump &lhsUnknownDone) { - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; - /* If the LHS is not a 32-bit integer, take OOL path. */ Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg()); stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label()); + if (!masm.supportsFloatingPoint()) { + lhsNotDouble = stubcc.masm.jump(); + return; + } + /* OOL path for LHS as a double - first test LHS is double. */ lhsNotDouble = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg()); @@ -1196,9 +1338,9 @@ mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState: /* If RHS is constant, convert now. */ if (rhs->isConstant()) - slowLoadConstantDouble(stubcc.masm, rhs, fpRight); + slowLoadConstantDouble(stubcc.masm, rhs, regs.rhsFP); else - stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight); + stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), regs.rhsFP); if (!rhs->isTypeKnown()) { /* Jump past double load, bind double type check. */ @@ -1207,14 +1349,14 @@ mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState: /* Load the double. */ frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(), - rhs, fpRight, stubcc.masm); + rhs, regs.rhsFP, stubcc.masm); converted.linkTo(stubcc.masm.label(), &stubcc.masm); } /* Load the LHS. */ frame.loadDouble(regs.lhsType.reg(), regs.lhsData.reg(), - lhs, fpLeft, stubcc.masm); + lhs, regs.lhsFP, stubcc.masm); lhsUnknownDone = stubcc.masm.jump(); } @@ -1225,25 +1367,27 @@ void mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s, MaybeJump &rhsNotNumber2) { - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; - /* If the RHS is not a double, take OOL path. */ Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg()); stubcc.linkExitDirect(notInt32, stubcc.masm.label()); + if (!masm.supportsFloatingPoint()) { + rhsNotNumber2 = stubcc.masm.jump(); + return; + } + /* Now test if RHS is a double. */ rhsNotNumber2 = stubcc.masm.testDouble(Assembler::NotEqual, regs.rhsType.reg()); /* We know LHS is an integer. */ if (lhs->isConstant()) - slowLoadConstantDouble(stubcc.masm, lhs, fpLeft); + slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP); else - stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft); + stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP); /* Load the RHS. */ frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(), - rhs, fpRight, stubcc.masm); + rhs, regs.rhsFP, stubcc.masm); } static inline Assembler::DoubleCondition @@ -1279,55 +1423,73 @@ mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *targe FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; - JS_ASSERT_IF(!target, fused != JSOP_IFEQ); - MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft); - MaybeJump rhsNotNumber = loadDouble(rhs, fpRight); + FPRegisterID fpLeft, fpRight; + bool allocateLeft, allocateRight; + + MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft); + if (lhsNotNumber.isSet()) { + if (target) + stubcc.linkExitForBranch(lhsNotNumber.get()); + else + stubcc.linkExit(lhsNotNumber.get(), Uses(2)); + } + if (!allocateLeft) + frame.pinReg(fpLeft); + + MaybeJump rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight); + if (rhsNotNumber.isSet()) { + if (target) + stubcc.linkExitForBranch(rhsNotNumber.get()); + else + stubcc.linkExit(rhsNotNumber.get(), Uses(2)); + } + if (!allocateLeft) + frame.unpinReg(fpLeft); Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused); if (target) { - if (lhsNotNumber.isSet()) - stubcc.linkExitForBranch(lhsNotNumber.get()); - if (rhsNotNumber.isSet()) - stubcc.linkExitForBranch(rhsNotNumber.get()); stubcc.leave(); - OOL_STUBCALL(stub); + OOL_STUBCALL(stub, REJOIN_BRANCH); - frame.popn(2); - frame.syncAndForgetEverything(); + if (!allocateLeft) + frame.pinReg(fpLeft); + if (!allocateRight) + frame.pinReg(fpRight); + + frame.syncAndKillEverything(); Jump j = masm.branchDouble(dblCond, fpLeft, fpRight); - /* - * The stub call has no need to rejoin since the state is synced. - * Instead, we can just test the return value. - */ - Assembler::Condition cond = (fused == JSOP_IFEQ) - ? Assembler::Zero - : Assembler::NonZero; - Jump sj = stubcc.masm.branchTest32(cond, Registers::ReturnReg, Registers::ReturnReg); + if (allocateLeft) + frame.freeReg(fpLeft); + else + frame.unpinKilledReg(fpLeft); + + if (allocateRight) + frame.freeReg(fpRight); + else + frame.unpinKilledReg(fpRight); + + frame.popn(2); + + Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); /* Rejoin from the slow path. */ - Jump j2 = stubcc.masm.jump(); - stubcc.crossJump(j2, masm.label()); + stubcc.rejoin(Changes(0)); /* - * NB: jumpAndTrace emits to the OOL path, so make sure not to use it + * NB: jumpAndRun emits to the OOL path, so make sure not to use it * in the middle of an in-progress slow path. */ - if (!jumpAndTrace(j, target, &sj)) + if (!jumpAndRun(j, target, &sj)) return false; } else { - if (lhsNotNumber.isSet()) - stubcc.linkExit(lhsNotNumber.get(), Uses(2)); - if (rhsNotNumber.isSet()) - stubcc.linkExit(rhsNotNumber.get(), Uses(2)); stubcc.leave(); - OOL_STUBCALL(stub); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); frame.popn(2); @@ -1342,22 +1504,72 @@ mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *targe frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg); stubcc.rejoin(Changes(1)); + + if (allocateLeft) + frame.freeReg(fpLeft); + if (allocateRight) + frame.freeReg(fpRight); } + return true; } bool -mjit::Compiler::jsop_relational_self(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused) +mjit::Compiler::jsop_relational_int(JSOp op, jsbytecode *target, JSOp fused) { -#ifdef DEBUG FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); - JS_ASSERT(frame.haveSameBacking(lhs, rhs)); -#endif + /* Reverse N cmp A comparisons. The left side must be in a register. */ + if (lhs->isConstant()) { + JS_ASSERT(!rhs->isConstant()); + FrameEntry *tmp = lhs; + lhs = rhs; + rhs = tmp; + op = ReverseCompareOp(op); + } + + JS_ASSERT_IF(!target, fused != JSOP_IFEQ); + Assembler::Condition cond = GetCompareCondition(op, fused); + + if (target) { + if (!frame.syncForBranch(target, Uses(2))) + return false; + + RegisterID lreg = frame.tempRegForData(lhs); + Jump fast; + if (rhs->isConstant()) { + fast = masm.branch32(cond, lreg, Imm32(rhs->getValue().toInt32())); + } else { + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + fast = masm.branch32(cond, lreg, rreg); + } + frame.popn(2); + + Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); + + return jumpAndRun(fast, target, &sj); + } else { + RegisterID result = frame.allocReg(); + RegisterID lreg = frame.tempRegForData(lhs); + + if (rhs->isConstant()) { + masm.branchValue(cond, lreg, rhs->getValue().toInt32(), result); + } else { + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + masm.branchValue(cond, lreg, rreg, result); + } - /* :TODO: optimize this? */ - return emitStubCmpOp(stub, target, fused); + frame.popn(2); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); + } + + return true; } /* See jsop_binary_full() for more information on how this works. */ @@ -1371,9 +1583,6 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, FrameState::BinaryAlloc regs; frame.allocForBinary(lhs, rhs, op, regs, !target); - FPRegisterID fpLeft = FPRegisters::First; - FPRegisterID fpRight = FPRegisters::Second; - MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone; if (!lhs->isTypeKnown()) emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone); @@ -1384,12 +1593,12 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, /* Both double paths will join here. */ bool hasDoublePath = false; - if (!rhs->isTypeKnown() || lhsUnknownDone.isSet()) + if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown())) hasDoublePath = true; /* Integer path - figure out the immutable side. */ JSOp cmpOp = op; - int32 value = 0; + int32_t value = 0; RegisterID cmpReg; MaybeRegisterID reg; if (regs.lhsData.isSet()) { @@ -1401,23 +1610,7 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, } else { cmpReg = regs.rhsData.reg(); value = lhs->getValue().toInt32(); - switch (op) { - case JSOP_GT: - cmpOp = JSOP_LT; - break; - case JSOP_GE: - cmpOp = JSOP_LE; - break; - case JSOP_LT: - cmpOp = JSOP_GT; - break; - case JSOP_LE: - cmpOp = JSOP_GE; - break; - default: - JS_NOT_REACHED("unrecognized op"); - break; - } + cmpOp = ReverseCompareOp(op); } /* @@ -1439,27 +1632,27 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, if (lhsUnknownDone.isSet()) lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm); frame.sync(stubcc.masm, Uses(frame.frameSlots())); - doubleTest = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight); + doubleTest = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP); doubleFall = stubcc.masm.jump(); + } - /* Link all incoming slow paths to here. */ - if (lhsNotDouble.isSet()) { - lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm); - if (rhsNotNumber.isSet()) - rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm); - } - if (rhsNotNumber2.isSet()) - rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm); - - /* - * For fusions, spill the tracker state. xmm* remain intact. Note - * that frame.sync() must be used directly, to avoid syncExit()'s - * jumping logic. - */ - frame.sync(stubcc.masm, Uses(frame.frameSlots())); - stubcc.leave(); - OOL_STUBCALL(stub); + /* Link all incoming slow paths to here. */ + if (lhsNotDouble.isSet()) { + lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm); + if (rhsNotNumber.isSet()) + rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm); } + if (rhsNotNumber2.isSet()) + rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm); + + /* + * For fusions, spill the tracker state. xmm* remain intact. Note + * that frame.sync() must be used directly, to avoid syncExit()'s + * jumping logic. + */ + frame.sync(stubcc.masm, Uses(frame.frameSlots())); + stubcc.leave(); + OOL_STUBCALL(stub, REJOIN_BRANCH); /* Forget the world, preserving data. */ frame.pinReg(cmpReg); @@ -1472,28 +1665,11 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, frame.unpinKilledReg(cmpReg); if (reg.isSet()) frame.unpinKilledReg(reg.reg()); - frame.syncAndForgetEverything(); - + frame.freeReg(regs.lhsFP); + frame.freeReg(regs.rhsFP); + /* Operands could have been reordered, so use cmpOp. */ - Assembler::Condition i32Cond; - bool ifeq = fused == JSOP_IFEQ; - switch (cmpOp) { - case JSOP_GT: - i32Cond = ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan; - break; - case JSOP_GE: - i32Cond = ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual; - break; - case JSOP_LT: - i32Cond = ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan; - break; - case JSOP_LE: - i32Cond = ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual; - break; - default: - JS_NOT_REACHED("unrecognized op"); - return false; - } + Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused); /* Emit the i32 path. */ Jump fast; @@ -1506,16 +1682,13 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, * The stub call has no need to rejoin since state is synced. Instead, * we can just test the return value. */ - Assembler::Condition cond = (fused == JSOP_IFEQ) - ? Assembler::Zero - : Assembler::NonZero; - Jump j = stubcc.masm.branchTest32(cond, Registers::ReturnReg, Registers::ReturnReg); + Jump j = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); /* Rejoin from the slow path. */ Jump j2 = stubcc.masm.jump(); stubcc.crossJump(j2, masm.label()); - /* :TODO: make double path invoke tracer. */ if (hasDoublePath) { j.linkTo(stubcc.masm.label(), &stubcc.masm); doubleTest.get().linkTo(stubcc.masm.label(), &stubcc.masm); @@ -1523,10 +1696,10 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, } /* - * NB: jumpAndTrace emits to the OOL path, so make sure not to use it + * NB: jumpAndRun emits to the OOL path, so make sure not to use it * in the middle of an in-progress slow path. */ - if (!jumpAndTrace(fast, target, &j)) + if (!jumpAndRun(fast, target, &j)) return false; /* Rejoin from the double path. */ @@ -1543,67 +1716,37 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, if (lhsUnknownDone.isSet()) lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm); /* :FIXME: Use SET if we can? */ - Jump test = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight); + Jump test = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP); stubcc.masm.move(Imm32(0), regs.result); Jump skip = stubcc.masm.jump(); test.linkTo(stubcc.masm.label(), &stubcc.masm); stubcc.masm.move(Imm32(1), regs.result); skip.linkTo(stubcc.masm.label(), &stubcc.masm); doubleDone = stubcc.masm.jump(); + } - /* Link all incoming slow paths to here. */ - if (lhsNotDouble.isSet()) { - lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm); - if (rhsNotNumber.isSet()) - rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm); - } - if (rhsNotNumber2.isSet()) - rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm); - - /* Emit the slow path - note full frame syncage. */ - frame.sync(stubcc.masm, Uses(2)); - stubcc.leave(); - OOL_STUBCALL(stub); + /* Link all incoming slow paths to here. */ + if (lhsNotDouble.isSet()) { + lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm); + if (rhsNotNumber.isSet()) + rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm); } + if (rhsNotNumber2.isSet()) + rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm); + + /* Emit the slow path - note full frame syncage. */ + frame.sync(stubcc.masm, Uses(2)); + stubcc.leave(); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); /* Get an integer comparison condition. */ - Assembler::Condition i32Cond; - switch (cmpOp) { - case JSOP_GT: - i32Cond = Assembler::GreaterThan; - break; - case JSOP_GE: - i32Cond = Assembler::GreaterThanOrEqual; - break; - case JSOP_LT: - i32Cond = Assembler::LessThan; - break; - case JSOP_LE: - i32Cond = Assembler::LessThanOrEqual; - break; - default: - JS_NOT_REACHED("unrecognized op"); - return false; - } + Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused); /* Emit the compare & set. */ - if (Registers::maskReg(regs.result) & Registers::SingleByteRegs) { - if (reg.isSet()) - masm.set32(i32Cond, cmpReg, reg.reg(), regs.result); - else - masm.set32(i32Cond, cmpReg, Imm32(value), regs.result); - } else { - Jump j; - if (reg.isSet()) - j = masm.branch32(i32Cond, cmpReg, reg.reg()); - else - j = masm.branch32(i32Cond, cmpReg, Imm32(value)); - masm.move(Imm32(0), regs.result); - Jump skip = masm.jump(); - j.linkTo(masm.label(), &masm); - masm.move(Imm32(1), regs.result); - skip.linkTo(masm.label(), &masm); - } + if (reg.isSet()) + masm.branchValue(i32Cond, cmpReg, reg.reg(), regs.result); + else + masm.branchValue(i32Cond, cmpReg, value, regs.result); frame.popn(2); frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, regs.result); @@ -1611,7 +1754,11 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, if (hasDoublePath) stubcc.crossJump(doubleDone.get(), masm.label()); stubcc.rejoin(Changes(1)); + + frame.freeReg(regs.lhsFP); + frame.freeReg(regs.rhsFP); } + return true; } diff --git a/deps/mozjs/js/src/methodjit/FastBuiltins.cpp b/deps/mozjs/js/src/methodjit/FastBuiltins.cpp new file mode 100644 index 00000000000..7e8f35af904 --- /dev/null +++ b/deps/mozjs/js/src/methodjit/FastBuiltins.cpp @@ -0,0 +1,1073 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Brendan Eich + * + * Contributor(s): + * Jan de Mooij + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "jsbool.h" +#include "jslibmath.h" +#include "jsmath.h" +#include "jsnum.h" +#include "methodjit/MethodJIT.h" +#include "methodjit/Compiler.h" +#include "methodjit/StubCalls.h" +#include "methodjit/FrameState-inl.h" + +using namespace js; +using namespace js::mjit; +using namespace JSC; + +typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; + +CompileStatus +mjit::Compiler::compileMathAbsInt(FrameEntry *arg) +{ + RegisterID reg; + if (arg->isConstant()) { + reg = frame.allocReg(); + masm.move(Imm32(arg->getValue().toInt32()), reg); + } else { + reg = frame.copyDataIntoReg(arg); + } + + Jump isPositive = masm.branch32(Assembler::GreaterThanOrEqual, reg, Imm32(0)); + + /* Math.abs(INT32_MIN) results in a double */ + Jump isMinInt = masm.branch32(Assembler::Equal, reg, Imm32(INT32_MIN)); + stubcc.linkExit(isMinInt, Uses(3)); + + masm.neg32(reg); + + isPositive.linkTo(masm.label(), &masm); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(3); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileMathAbsDouble(FrameEntry *arg) +{ + FPRegisterID fpResultReg = frame.allocFPReg(); + + FPRegisterID fpReg; + bool allocate; + + DebugOnly notNumber = loadDouble(arg, &fpReg, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + masm.absDouble(fpReg, fpResultReg); + + if (allocate) + frame.freeReg(fpReg); + + frame.popn(3); + frame.pushDouble(fpResultReg); + + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileRound(FrameEntry *arg, RoundingMode mode) +{ + FPRegisterID fpScratchReg = frame.allocFPReg(); + + FPRegisterID fpReg; + bool allocate; + + DebugOnly notNumber = loadDouble(arg, &fpReg, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + masm.zeroDouble(fpScratchReg); + + /* Slow path for NaN and numbers <= 0. */ + Jump negOrNan = masm.branchDouble(Assembler::DoubleLessThanOrEqualOrUnordered, fpReg, fpScratchReg); + stubcc.linkExit(negOrNan, Uses(3)); + + /* For round add 0.5 and floor. */ + FPRegisterID fpSourceReg; + if (mode == Round) { + masm.slowLoadConstantDouble(0.5, fpScratchReg); + masm.addDouble(fpReg, fpScratchReg); + fpSourceReg = fpScratchReg; + } else { + fpSourceReg = fpReg; + } + + /* Truncate to integer, slow path if this overflows. */ + RegisterID reg = frame.allocReg(); + Jump overflow = masm.branchTruncateDoubleToInt32(fpSourceReg, reg); + stubcc.linkExit(overflow, Uses(3)); + + if (allocate) + frame.freeReg(fpReg); + frame.freeReg(fpScratchReg); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(3); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileMathSqrt(FrameEntry *arg) +{ + FPRegisterID fpResultReg = frame.allocFPReg(); + + FPRegisterID fpReg; + bool allocate; + + DebugOnly notNumber = loadDouble(arg, &fpReg, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + masm.sqrtDouble(fpReg, fpResultReg); + + if (allocate) + frame.freeReg(fpReg); + + frame.popn(3); + frame.pushDouble(fpResultReg); + + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileMathMinMaxDouble(FrameEntry *arg1, FrameEntry *arg2, + Assembler::DoubleCondition cond) +{ + FPRegisterID fpReg1; + FPRegisterID fpReg2; + bool allocate; + + DebugOnly notNumber = loadDouble(arg1, &fpReg1, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + if (!allocate) { + FPRegisterID fpResultReg = frame.allocFPReg(); + masm.moveDouble(fpReg1, fpResultReg); + fpReg1 = fpResultReg; + } + + DebugOnly notNumber2 = loadDouble(arg2, &fpReg2, &allocate); + JS_ASSERT(!((MaybeJump)notNumber2).isSet()); + + + /* Slow path for 0 and NaN, because they have special requriments. */ + masm.zeroDouble(Registers::FPConversionTemp); + Jump zeroOrNan = masm.branchDouble(Assembler::DoubleEqualOrUnordered, fpReg1, + Registers::FPConversionTemp); + stubcc.linkExit(zeroOrNan, Uses(4)); + Jump zeroOrNan2 = masm.branchDouble(Assembler::DoubleEqualOrUnordered, fpReg2, + Registers::FPConversionTemp); + stubcc.linkExit(zeroOrNan2, Uses(4)); + + + Jump ifTrue = masm.branchDouble(cond, fpReg1, fpReg2); + masm.moveDouble(fpReg2, fpReg1); + + ifTrue.linkTo(masm.label(), &masm); + + if (allocate) + frame.freeReg(fpReg2); + + stubcc.leave(); + stubcc.masm.move(Imm32(2), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(4); + frame.pushDouble(fpReg1); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileMathMinMaxInt(FrameEntry *arg1, FrameEntry *arg2, Assembler::Condition cond) +{ + /* Get this case out of the way */ + if (arg1->isConstant() && arg2->isConstant()) { + int32_t a = arg1->getValue().toInt32(); + int32_t b = arg2->getValue().toInt32(); + + frame.popn(4); + if (cond == Assembler::LessThan) + frame.push(Int32Value(a < b ? a : b)); + else + frame.push(Int32Value(a > b ? a : b)); + return Compile_Okay; + } + + Jump ifTrue; + RegisterID reg; + if (arg1->isConstant()) { + reg = frame.copyDataIntoReg(arg2); + int32_t v = arg1->getValue().toInt32(); + + ifTrue = masm.branch32(cond, reg, Imm32(v)); + masm.move(Imm32(v), reg); + } else if (arg2->isConstant()) { + reg = frame.copyDataIntoReg(arg1); + int32_t v = arg2->getValue().toInt32(); + + ifTrue = masm.branch32(cond, reg, Imm32(v)); + masm.move(Imm32(v), reg); + } else { + reg = frame.copyDataIntoReg(arg1); + RegisterID regB = frame.tempRegForData(arg2); + + ifTrue = masm.branch32(cond, reg, regB); + masm.move(regB, reg); + } + + ifTrue.linkTo(masm.label(), &masm); + frame.popn(4); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2) +{ + FPRegisterID fpScratchReg = frame.allocFPReg(); + FPRegisterID fpResultReg = frame.allocFPReg(); + + FPRegisterID fpReg; + bool allocate; + + DebugOnly notNumber = loadDouble(arg1, &fpReg, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + /* Slow path for -Infinity (must return Infinity, not NaN). */ + masm.slowLoadConstantDouble(js_NegativeInfinity, fpResultReg); + Jump isNegInfinity = masm.branchDouble(Assembler::DoubleEqual, fpReg, fpResultReg); + stubcc.linkExit(isNegInfinity, Uses(4)); + + /* Convert -0 to +0. */ + masm.zeroDouble(fpResultReg); + masm.moveDouble(fpReg, fpScratchReg); + masm.addDouble(fpResultReg, fpScratchReg); + + double y = arg2->getValue().toDouble(); + if (y == 0.5) { + /* pow(x, 0.5) => sqrt(x) */ + masm.sqrtDouble(fpScratchReg, fpResultReg); + + } else if (y == -0.5) { + /* pow(x, -0.5) => 1/sqrt(x) */ + masm.sqrtDouble(fpScratchReg, fpScratchReg); + masm.slowLoadConstantDouble(1, fpResultReg); + masm.divDouble(fpScratchReg, fpResultReg); + } + + frame.freeReg(fpScratchReg); + + if (allocate) + frame.freeReg(fpReg); + + stubcc.leave(); + stubcc.masm.move(Imm32(2), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(4); + frame.pushDouble(fpResultReg); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileGetChar(FrameEntry *thisValue, FrameEntry *arg, GetCharMode mode) +{ + RegisterID reg1 = frame.allocReg(); + RegisterID reg2 = frame.allocReg(); + + /* Load string in strReg. */ + RegisterID strReg; + if (thisValue->isConstant()) { + strReg = frame.allocReg(); + masm.move(ImmPtr(thisValue->getValue().toString()), strReg); + } else { + strReg = frame.tempRegForData(thisValue); + frame.pinReg(strReg); + } + + /* Load index in argReg. */ + RegisterID argReg; + if (arg->isConstant()) { + argReg = frame.allocReg(); + masm.move(Imm32(arg->getValue().toInt32()), argReg); + } else { + argReg = frame.tempRegForData(arg); + } + if (!thisValue->isConstant()) + frame.unpinReg(strReg); + + Address lengthAndFlagsAddr(strReg, JSString::offsetOfLengthAndFlags()); + + /* Load lengthAndFlags in reg1 and reg2 */ + masm.loadPtr(lengthAndFlagsAddr, reg1); + masm.move(reg1, reg2); + + /* Slow path if string is a rope */ + masm.andPtr(ImmPtr((void *)JSString::ROPE_BIT), reg1); + Jump isRope = masm.branchTestPtr(Assembler::NonZero, reg1); + stubcc.linkExit(isRope, Uses(3)); + + /* Slow path if out-of-range. */ + masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), reg2); + Jump outOfRange = masm.branchPtr(Assembler::AboveOrEqual, argReg, reg2); + stubcc.linkExit(outOfRange, Uses(3)); + + /* Load char code in reg2. */ + masm.move(argReg, reg1); + masm.loadPtr(Address(strReg, JSString::offsetOfChars()), reg2); + masm.lshiftPtr(Imm32(1), reg1); + masm.addPtr(reg1, reg2); + masm.load16(Address(reg2), reg2); + + /* Convert char code to string. */ + if (mode == GetChar) { + /* Slow path if there's no unit string for this character. */ + Jump notUnitString = masm.branch32(Assembler::AboveOrEqual, reg2, + Imm32(StaticStrings::UNIT_STATIC_LIMIT)); + stubcc.linkExit(notUnitString, Uses(3)); + + /* Load unit string in reg2. */ + masm.lshiftPtr(Imm32(sizeof(JSAtom *) == 4 ? 2 : 3), reg2); + masm.addPtr(ImmPtr(&cx->runtime->staticStrings.unitStaticTable), reg2); + masm.loadPtr(Address(reg2), reg2); + } + + if (thisValue->isConstant()) + frame.freeReg(strReg); + if (arg->isConstant()) + frame.freeReg(argReg); + frame.freeReg(reg1); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(3); + switch(mode) { + case GetCharCode: + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg2); + break; + case GetChar: + frame.pushTypedPayload(JSVAL_TYPE_STRING, reg2); + break; + default: + JS_NOT_REACHED("unknown getchar mode"); + } + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileStringFromCode(FrameEntry *arg) +{ + /* Load Char-Code into argReg */ + RegisterID argReg; + if (arg->isConstant()) { + argReg = frame.allocReg(); + masm.move(Imm32(arg->getValue().toInt32()), argReg); + } else { + argReg = frame.copyDataIntoReg(arg); + } + + /* Slow path if there's no unit string for this character. */ + Jump notUnitString = masm.branch32(Assembler::AboveOrEqual, argReg, + Imm32(StaticStrings::UNIT_STATIC_LIMIT)); + stubcc.linkExit(notUnitString, Uses(3)); + + /* Load unit string in reg. */ + masm.lshiftPtr(Imm32(sizeof(JSAtom *) == 4 ? 2 : 3), argReg); + masm.addPtr(ImmPtr(&cx->runtime->staticStrings.unitStaticTable), argReg); + masm.loadPtr(Address(argReg), argReg); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(3); + frame.pushTypedPayload(JSVAL_TYPE_STRING, argReg); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileArrayPush(FrameEntry *thisValue, FrameEntry *arg) +{ + /* This behaves like an assignment this[this.length] = arg; */ + + /* Filter out silly cases. */ + if (frame.haveSameBacking(thisValue, arg) || thisValue->isConstant()) + return Compile_InlineAbort; + + /* Allocate registers. */ + ValueRemat vr; + frame.pinEntry(arg, vr, /* breakDouble = */ false); + + RegisterID objReg = frame.tempRegForData(thisValue); + frame.pinReg(objReg); + + RegisterID slotsReg = frame.allocReg(); + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); + + RegisterID lengthReg = frame.allocReg(); + masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), lengthReg); + + frame.unpinReg(objReg); + + Int32Key key = Int32Key::FromRegister(lengthReg); + + /* Test for 'length == initializedLength' */ + Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::NotEqual); + stubcc.linkExit(initlenGuard, Uses(3)); + + /* Test for 'length < capacity' */ + Jump capacityGuard = masm.guardArrayExtent(ObjectElements::offsetOfCapacity(), + slotsReg, key, Assembler::BelowOrEqual); + stubcc.linkExit(capacityGuard, Uses(3)); + + masm.storeValue(vr, BaseIndex(slotsReg, lengthReg, masm.JSVAL_SCALE)); + + masm.bumpKey(key, 1); + masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfLength())); + masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfInitializedLength())); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.unpinEntry(vr); + frame.freeReg(slotsReg); + frame.popn(3); + + frame.pushTypedPayload(JSVAL_TYPE_INT32, lengthReg); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileArrayPopShift(FrameEntry *thisValue, bool isPacked, bool isArrayPop) +{ + /* Filter out silly cases. */ + if (thisValue->isConstant()) + return Compile_InlineAbort; + +#ifdef JSGC_INCREMENTAL_MJ + /* Write barrier. */ + if (cx->compartment->needsBarrier()) + return Compile_InlineAbort; +#endif + + RegisterID objReg = frame.tempRegForData(thisValue); + frame.pinReg(objReg); + + RegisterID lengthReg = frame.allocReg(); + RegisterID slotsReg = frame.allocReg(); + + JSValueType type = knownPushedType(0); + + MaybeRegisterID dataReg, typeReg; + if (!analysis->popGuaranteed(PC)) { + dataReg = frame.allocReg(); + if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE) + typeReg = frame.allocReg(); + } + + if (isArrayPop) { + frame.unpinReg(objReg); + } else { + /* + * Sync up front for shift() so we can jump over the inline stub. + * The result will be stored in memory rather than registers. + */ + frame.syncAndKillEverything(); + frame.unpinKilledReg(objReg); + } + + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); + masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), lengthReg); + + /* Test for 'length == initializedLength' */ + Int32Key key = Int32Key::FromRegister(lengthReg); + Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::NotEqual); + stubcc.linkExit(initlenGuard, Uses(3)); + + /* + * Test for length != 0. On zero length either take a slow call or generate + * an undefined value, depending on whether the call is known to produce + * undefined. + */ + bool maybeUndefined = pushedTypeSet(0)->hasType(types::Type::UndefinedType()); + Jump emptyGuard = masm.branch32(Assembler::Equal, lengthReg, Imm32(0)); + if (!maybeUndefined) + stubcc.linkExit(emptyGuard, Uses(3)); + + masm.bumpKey(key, -1); + + if (dataReg.isSet()) { + Jump holeCheck; + if (isArrayPop) { + BaseIndex slot(slotsReg, lengthReg, masm.JSVAL_SCALE); + holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg.reg()); + } else { + holeCheck = masm.fastArrayLoadSlot(Address(slotsReg), !isPacked, typeReg, dataReg.reg()); + Address addr = frame.addressOf(frame.peek(-2)); + if (typeReg.isSet()) + masm.storeValueFromComponents(typeReg.reg(), dataReg.reg(), addr); + else + masm.storeValueFromComponents(ImmType(type), dataReg.reg(), addr); + } + if (!isPacked) + stubcc.linkExit(holeCheck, Uses(3)); + } + + masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfLength())); + masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfInitializedLength())); + + if (!isArrayPop) + INLINE_STUBCALL(stubs::ArrayShift, REJOIN_NONE); + + stubcc.leave(); + stubcc.masm.move(Imm32(0), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.freeReg(slotsReg); + frame.freeReg(lengthReg); + frame.popn(2); + + if (dataReg.isSet()) { + if (isArrayPop) { + if (typeReg.isSet()) + frame.pushRegs(typeReg.reg(), dataReg.reg(), type); + else + frame.pushTypedPayload(type, dataReg.reg()); + } else { + frame.pushSynced(type); + if (typeReg.isSet()) + frame.freeReg(typeReg.reg()); + frame.freeReg(dataReg.reg()); + } + } else { + frame.push(UndefinedValue()); + } + + stubcc.rejoin(Changes(1)); + + if (maybeUndefined) { + /* Generate an OOL path to push an undefined value, and rejoin. */ + if (dataReg.isSet()) { + stubcc.linkExitDirect(emptyGuard, stubcc.masm.label()); + if (isArrayPop) { + if (typeReg.isSet()) { + stubcc.masm.loadValueAsComponents(UndefinedValue(), typeReg.reg(), dataReg.reg()); + } else { + JS_ASSERT(type == JSVAL_TYPE_UNDEFINED); + stubcc.masm.loadValuePayload(UndefinedValue(), dataReg.reg()); + } + } else { + stubcc.masm.storeValue(UndefinedValue(), frame.addressOf(frame.peek(-1))); + } + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + } else { + emptyGuard.linkTo(masm.label(), &masm); + } + } + + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileArrayConcat(types::TypeSet *thisTypes, types::TypeSet *argTypes, + FrameEntry *thisValue, FrameEntry *argValue) +{ + /* + * Require the 'this' types to have a specific type matching the current + * global, so we can create the result object inline. + */ + if (thisTypes->getObjectCount() != 1) + return Compile_InlineAbort; + types::TypeObject *thisType = thisTypes->getTypeObject(0); + if (!thisType || &thisType->proto->global() != globalObj) + return Compile_InlineAbort; + + /* + * Constraints modeling this concat have not been generated by inference, + * so check that type information already reflects possible side effects of + * this call. + */ + thisTypes->addFreeze(cx); + argTypes->addFreeze(cx); + types::TypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false); + if (!thisElemTypes) + return Compile_Error; + if (!pushedTypeSet(0)->hasType(types::Type::ObjectType(thisType))) + return Compile_InlineAbort; + for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { + if (argTypes->getSingleObject(i)) + return Compile_InlineAbort; + types::TypeObject *argType = argTypes->getTypeObject(i); + if (!argType) + continue; + types::TypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false); + if (!elemTypes) + return Compile_Error; + if (!elemTypes->knownSubset(cx, thisElemTypes)) + return Compile_InlineAbort; + } + + /* Test for 'length == initializedLength' on both arrays. */ + + RegisterID slotsReg = frame.allocReg(); + RegisterID reg = frame.allocReg(); + + Int32Key key = Int32Key::FromRegister(reg); + + RegisterID objReg = frame.tempRegForData(thisValue); + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); + masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), reg); + Jump initlenOneGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::NotEqual); + stubcc.linkExit(initlenOneGuard, Uses(3)); + + objReg = frame.tempRegForData(argValue); + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); + masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), reg); + Jump initlenTwoGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::NotEqual); + stubcc.linkExit(initlenTwoGuard, Uses(3)); + + frame.freeReg(reg); + frame.freeReg(slotsReg); + frame.syncAndForgetEverything(); + + /* + * The current stack layout is 'CALLEE THIS ARG'. Allocate the result and + * scribble it over the callee, which will be its final position after the + * call. + */ + + JSObject *templateObject = NewDenseEmptyArray(cx, thisType->proto); + if (!templateObject) + return Compile_Error; + templateObject->setType(thisType); + + RegisterID result = Registers::ReturnReg; + Jump emptyFreeList = masm.getNewObject(cx, result, templateObject); + stubcc.linkExit(emptyFreeList, Uses(3)); + + masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), result, frame.addressOf(frame.peek(-3))); + INLINE_STUBCALL(stubs::ArrayConcatTwoArrays, REJOIN_FALLTHROUGH); + + stubcc.leave(); + stubcc.masm.move(Imm32(1), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(3); + frame.pushSynced(JSVAL_TYPE_OBJECT); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileArrayWithLength(uint32_t argc) +{ + /* Match Array() or Array(n) for constant n. */ + JS_ASSERT(argc == 0 || argc == 1); + + int32_t length = 0; + if (argc == 1) { + FrameEntry *arg = frame.peek(-1); + if (!arg->isConstant() || !arg->getValue().isInt32()) + return Compile_InlineAbort; + length = arg->getValue().toInt32(); + if (length < 0) + return Compile_InlineAbort; + } + + types::TypeObject *type = types::TypeScript::InitObject(cx, script, PC, JSProto_Array); + if (!type) + return Compile_Error; + + JSObject *templateObject = NewDenseUnallocatedArray(cx, length, type->proto); + if (!templateObject) + return Compile_Error; + templateObject->setType(type); + + RegisterID result = frame.allocReg(); + Jump emptyFreeList = masm.getNewObject(cx, result, templateObject); + + stubcc.linkExit(emptyFreeList, Uses(0)); + stubcc.leave(); + + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(argc + 2); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileArrayWithArgs(uint32_t argc) +{ + /* + * Match Array(x, y, z) with at least two arguments. Don't inline the case + * where a non-number argument is passed, so we don't need to care about + * the types of the arguments. + */ + JS_ASSERT(argc >= 2); + + size_t maxArraySlots = + gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST) - ObjectElements::VALUES_PER_HEADER; + + if (argc > maxArraySlots) + return Compile_InlineAbort; + + types::TypeObject *type = types::TypeScript::InitObject(cx, script, PC, JSProto_Array); + if (!type) + return Compile_Error; + + JSObject *templateObject = NewDenseUnallocatedArray(cx, argc, type->proto); + if (!templateObject) + return Compile_Error; + templateObject->setType(type); + + JS_ASSERT(templateObject->getDenseArrayCapacity() >= argc); + + RegisterID result = frame.allocReg(); + Jump emptyFreeList = masm.getNewObject(cx, result, templateObject); + stubcc.linkExit(emptyFreeList, Uses(0)); + + int offset = JSObject::offsetOfFixedElements(); + masm.store32(Imm32(argc), + Address(result, offset + ObjectElements::offsetOfInitializedLength())); + + for (unsigned i = 0; i < argc; i++) { + FrameEntry *arg = frame.peek(-(int32_t)argc + i); + frame.storeTo(arg, Address(result, offset), /* popped = */ true); + offset += sizeof(Value); + } + + stubcc.leave(); + + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(argc + 2); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result); + + stubcc.rejoin(Changes(1)); + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::compileParseInt(JSValueType argType, uint32_t argc) +{ + bool needStubCall = false; + + if (argc > 1) { + FrameEntry *arg = frame.peek(-(int32_t)argc + 1); + + if (!arg->isTypeKnown() || arg->getKnownType() != JSVAL_TYPE_INT32) + return Compile_InlineAbort; + + if (arg->isConstant()) { + int32_t base = arg->getValue().toInt32(); + if (base != 0 && base != 10) + return Compile_InlineAbort; + } else { + RegisterID baseReg = frame.tempRegForData(arg); + needStubCall = true; + + Jump isTen = masm.branch32(Assembler::Equal, baseReg, Imm32(10)); + Jump isNotZero = masm.branch32(Assembler::NotEqual, baseReg, Imm32(0)); + stubcc.linkExit(isNotZero, Uses(2 + argc)); + + isTen.linkTo(masm.label(), &masm); + } + } + + if (argType == JSVAL_TYPE_INT32) { + if (needStubCall) { + stubcc.leave(); + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + } + + /* + * Stack looks like callee, this, arg1, arg2, argN. + * First pop all args other than arg1. + */ + frame.popn(argc - 1); + /* "Shimmy" arg1 to the callee slot and pop this + arg1. */ + frame.shimmy(2); + + if (needStubCall) { + stubcc.rejoin(Changes(1)); + } + } else { + FrameEntry *arg = frame.peek(-(int32_t)argc); + FPRegisterID fpScratchReg = frame.allocFPReg(); + FPRegisterID fpReg; + bool allocate; + + DebugOnly notNumber = loadDouble(arg, &fpReg, &allocate); + JS_ASSERT(!((MaybeJump)notNumber).isSet()); + + masm.slowLoadConstantDouble(1, fpScratchReg); + + /* Slow path for NaN and numbers < 1. */ + Jump lessThanOneOrNan = masm.branchDouble(Assembler::DoubleLessThanOrUnordered, + fpReg, fpScratchReg); + stubcc.linkExit(lessThanOneOrNan, Uses(2 + argc)); + + frame.freeReg(fpScratchReg); + + /* Truncate to integer, slow path if this overflows. */ + RegisterID reg = frame.allocReg(); + Jump overflow = masm.branchTruncateDoubleToInt32(fpReg, reg); + stubcc.linkExit(overflow, Uses(2 + argc)); + + if (allocate) + frame.freeReg(fpReg); + + stubcc.leave(); + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); + + frame.popn(2 + argc); + frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); + + stubcc.rejoin(Changes(1)); + } + + return Compile_Okay; +} + +CompileStatus +mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) +{ + if (!cx->typeInferenceEnabled()) + return Compile_InlineAbort; + + if (applyTricks == LazyArgsObj) + return Compile_InlineAbort; + + FrameEntry *origCallee = frame.peek(-((int)argc + 2)); + FrameEntry *thisValue = frame.peek(-((int)argc + 1)); + types::TypeSet *thisTypes = analysis->poppedTypes(PC, argc); + + if (!origCallee->isConstant() || !origCallee->isType(JSVAL_TYPE_OBJECT)) + return Compile_InlineAbort; + + JSObject *callee = &origCallee->getValue().toObject(); + if (!callee->isFunction()) + return Compile_InlineAbort; + + /* + * The callee must have the same parent as the script's global, otherwise + * inference may not have accounted for any side effects correctly. + */ + if (!globalObj || globalObj != &callee->global()) + return Compile_InlineAbort; + + Native native = callee->toFunction()->maybeNative(); + + if (!native) + return Compile_InlineAbort; + + JSValueType type = knownPushedType(0); + JSValueType thisType = thisValue->isTypeKnown() + ? thisValue->getKnownType() + : JSVAL_TYPE_UNKNOWN; + + /* + * Note: when adding new natives which operate on properties, add relevant + * constraint generation to the behavior of TypeConstraintCall. + */ + + /* Handle natives that can be called either with or without 'new'. */ + + if (native == js_Array && type == JSVAL_TYPE_OBJECT && globalObj) { + if (argc == 0 || argc == 1) + return compileArrayWithLength(argc); + return compileArrayWithArgs(argc); + } + + /* Remaining natives must not be called with 'new'. */ + if (callingNew) + return Compile_InlineAbort; + + if (native == js::num_parseInt && argc >= 1) { + FrameEntry *arg = frame.peek(-(int32_t)argc); + JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; + + if ((argType == JSVAL_TYPE_DOUBLE || argType == JSVAL_TYPE_INT32) && + type == JSVAL_TYPE_INT32) { + return compileParseInt(argType, argc); + } + } + + if (argc == 0) { + if ((native == js::array_pop || native == js::array_shift) && thisType == JSVAL_TYPE_OBJECT) { + /* + * Only handle pop/shift on dense arrays which have never been used + * in an iterator --- when popping elements we don't account for + * suppressing deleted properties in active iterators. + * + * Constraints propagating properties directly into the result + * type set are generated by TypeConstraintCall during inference. + */ + if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY | + types::OBJECT_FLAG_ITERATED) && + !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { + bool packed = !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); + return compileArrayPopShift(thisValue, packed, native == js::array_pop); + } + } + } else if (argc == 1) { + FrameEntry *arg = frame.peek(-1); + types::TypeSet *argTypes = frame.extra(arg).types; + if (!argTypes) + return Compile_InlineAbort; + JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; + + if (native == js_math_abs) { + if (argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32) + return compileMathAbsInt(arg); + + if (argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_DOUBLE) + return compileMathAbsDouble(arg); + } + if (native == js_math_floor && argType == JSVAL_TYPE_DOUBLE && + type == JSVAL_TYPE_INT32) { + return compileRound(arg, Floor); + } + if (native == js_math_round && argType == JSVAL_TYPE_DOUBLE && + type == JSVAL_TYPE_INT32) { + return compileRound(arg, Round); + } + if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && + (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) { + return compileMathSqrt(arg); + } + if (native == js_str_charCodeAt && argType == JSVAL_TYPE_INT32 && + thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_INT32) { + return compileGetChar(thisValue, arg, GetCharCode); + } + if (native == js_str_charAt && argType == JSVAL_TYPE_INT32 && + thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_STRING) { + return compileGetChar(thisValue, arg, GetChar); + } + if (native == js::str_fromCharCode && argType == JSVAL_TYPE_INT32 && + type == JSVAL_TYPE_STRING) { + return compileStringFromCode(arg); + } + if (native == js::array_push && + thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_INT32) { + /* + * Constraints propagating properties into the 'this' object are + * generated by TypeConstraintCall during inference. + */ + if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && + !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { + return compileArrayPush(thisValue, arg); + } + } + if (native == js::array_concat && argType == JSVAL_TYPE_OBJECT && + thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_OBJECT && + !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && + !argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) { + return compileArrayConcat(thisTypes, argTypes, thisValue, arg); + } + } else if (argc == 2) { + FrameEntry *arg1 = frame.peek(-2); + FrameEntry *arg2 = frame.peek(-1); + + JSValueType arg1Type = arg1->isTypeKnown() ? arg1->getKnownType() : JSVAL_TYPE_UNKNOWN; + JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN; + + if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && + (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) && + arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant()) + { + Value arg2Value = arg2->getValue(); + if (arg2Value.toDouble() == -0.5 || arg2Value.toDouble() == 0.5) + return compileMathPowSimple(arg1, arg2); + } + if ((native == js_math_min || native == js_math_max)) { + if (arg1Type == JSVAL_TYPE_INT32 && arg2Type == JSVAL_TYPE_INT32 && + type == JSVAL_TYPE_INT32) { + return compileMathMinMaxInt(arg1, arg2, + native == js_math_min ? Assembler::LessThan : Assembler::GreaterThan); + } + if ((arg1Type == JSVAL_TYPE_INT32 || arg1Type == JSVAL_TYPE_DOUBLE) && + (arg2Type == JSVAL_TYPE_INT32 || arg2Type == JSVAL_TYPE_DOUBLE) && + type == JSVAL_TYPE_DOUBLE) { + return compileMathMinMaxDouble(arg1, arg2, + (native == js_math_min) + ? Assembler::DoubleLessThan + : Assembler::DoubleGreaterThan); + } + } + } + return Compile_InlineAbort; +} + diff --git a/deps/mozjs/js/src/methodjit/FastOps.cpp b/deps/mozjs/js/src/methodjit/FastOps.cpp index fbcf91b06bb..011507b07fe 100644 --- a/deps/mozjs/js/src/methodjit/FastOps.cpp +++ b/deps/mozjs/js/src/methodjit/FastOps.cpp @@ -37,15 +37,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + #include "jsbool.h" #include "jscntxt.h" -#include "jsemit.h" #include "jslibmath.h" #include "jsnum.h" #include "jsscope.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" +#include "jstypedarrayinlines.h" +#include "frontend/BytecodeEmitter.h" #include "methodjit/MethodJIT.h" #include "methodjit/Compiler.h" #include "methodjit/StubCalls.h" @@ -58,234 +60,83 @@ using namespace js::mjit; typedef JSC::MacroAssembler::RegisterID RegisterID; -RegisterID -mjit::Compiler::rightRegForShift(FrameEntry *rhs) -{ -#if defined(JS_CPU_X86) || defined(JS_CPU_X64) - /* - * Gross: RHS _must_ be in ECX, on x86. - * Note that we take this first so that we can't up with other register - * allocations (below) owning ecx before rhs. - */ - RegisterID reg = JSC::X86Registers::ecx; - if (!rhs->isConstant()) - frame.copyDataIntoReg(rhs, reg); - return reg; -#else - if (rhs->isConstant()) - return frame.allocReg(); - return frame.copyDataIntoReg(rhs); -#endif -} - -void -mjit::Compiler::jsop_rsh_const_int(FrameEntry *lhs, FrameEntry *rhs) -{ - RegisterID rhsData = rightRegForShift(rhs); - RegisterID result = frame.allocReg(); - masm.move(Imm32(lhs->getValue().toInt32()), result); - masm.rshift32(rhsData, result); - - frame.freeReg(rhsData); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, result); -} - -void -mjit::Compiler::jsop_rsh_int_int(FrameEntry *lhs, FrameEntry *rhs) -{ - RegisterID rhsData = rightRegForShift(rhs); - RegisterID lhsData = frame.copyDataIntoReg(lhs); - masm.rshift32(rhsData, lhsData); - frame.freeReg(rhsData); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData); -} - -void -mjit::Compiler::jsop_rsh_int_const(FrameEntry *lhs, FrameEntry *rhs) -{ - int32 shiftAmount = rhs->getValue().toInt32(); - - if (!shiftAmount) { - frame.pop(); - return; - } - - RegisterID result = frame.copyDataIntoReg(lhs); - masm.rshift32(Imm32(shiftAmount), result); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, result); -} - -void -mjit::Compiler::jsop_rsh_unknown_const(FrameEntry *lhs, FrameEntry *rhs) -{ - int32 shiftAmount = rhs->getValue().toInt32(); - - RegisterID lhsType = frame.tempRegForType(lhs); - frame.pinReg(lhsType); - RegisterID lhsData = frame.copyDataIntoReg(lhs); - frame.unpinReg(lhsType); - - Jump lhsIntGuard = masm.testInt32(Assembler::NotEqual, lhsType); - stubcc.linkExitDirect(lhsIntGuard, stubcc.masm.label()); - - Jump lhsDoubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, lhsType); - frame.loadDouble(lhs, FPRegisters::First, stubcc.masm); - Jump lhsTruncateGuard = stubcc.masm.branchTruncateDoubleToInt32(FPRegisters::First, lhsData); - stubcc.crossJump(stubcc.masm.jump(), masm.label()); - - lhsDoubleGuard.linkTo(stubcc.masm.label(), &stubcc.masm); - lhsTruncateGuard.linkTo(stubcc.masm.label(), &stubcc.masm); - - frame.sync(stubcc.masm, Uses(2)); - OOL_STUBCALL(stubs::Rsh); - - if (shiftAmount) - masm.rshift32(Imm32(shiftAmount), lhsData); - - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData); - - stubcc.rejoin(Changes(1)); -} - -void -mjit::Compiler::jsop_rsh_const_unknown(FrameEntry *lhs, FrameEntry *rhs) -{ - RegisterID rhsData = rightRegForShift(rhs); - RegisterID rhsType = frame.tempRegForType(rhs); - frame.pinReg(rhsType); - RegisterID result = frame.allocReg(); - frame.unpinReg(rhsType); - - Jump rhsIntGuard = masm.testInt32(Assembler::NotEqual, rhsType); - stubcc.linkExit(rhsIntGuard, Uses(2)); - stubcc.leave(); - OOL_STUBCALL(stubs::Rsh); - masm.move(Imm32(lhs->getValue().toInt32()), result); - masm.rshift32(rhsData, result); - frame.freeReg(rhsData); - - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, result); - stubcc.rejoin(Changes(1)); -} - -void -mjit::Compiler::jsop_rsh_int_unknown(FrameEntry *lhs, FrameEntry *rhs) -{ - RegisterID rhsData = rightRegForShift(rhs); - RegisterID rhsType = frame.tempRegForType(rhs); - frame.pinReg(rhsType); - RegisterID lhsData = frame.copyDataIntoReg(lhs); - frame.unpinReg(rhsType); - - Jump rhsIntGuard = masm.testInt32(Assembler::NotEqual, rhsType); - stubcc.linkExit(rhsIntGuard, Uses(2)); - stubcc.leave(); - OOL_STUBCALL(stubs::Rsh); - - masm.rshift32(rhsData, lhsData); - frame.freeReg(rhsData); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData); - - stubcc.rejoin(Changes(1)); -} - void -mjit::Compiler::jsop_rsh_unknown_any(FrameEntry *lhs, FrameEntry *rhs) +mjit::Compiler::ensureInteger(FrameEntry *fe, Uses uses) { - JS_ASSERT(!lhs->isTypeKnown()); - JS_ASSERT(!rhs->isNotType(JSVAL_TYPE_INT32)); - - /* Allocate registers. */ - RegisterID rhsData = rightRegForShift(rhs); - - MaybeRegisterID rhsType; - if (!rhs->isTypeKnown()) { - rhsType.setReg(frame.tempRegForType(rhs)); - frame.pinReg(rhsType.reg()); - } - - RegisterID lhsData = frame.copyDataIntoReg(lhs); - MaybeRegisterID lhsType; - if (rhsType.isSet() && frame.haveSameBacking(lhs, rhs)) - lhsType = rhsType; - else - lhsType = frame.tempRegForType(lhs); - - /* Non-integer rhs jumps to stub. */ - MaybeJump rhsIntGuard; - if (rhsType.isSet()) { - rhsIntGuard.setJump(masm.testInt32(Assembler::NotEqual, rhsType.reg())); - frame.unpinReg(rhsType.reg()); - } - - /* Non-integer lhs jumps to double guard. */ - Jump lhsIntGuard = masm.testInt32(Assembler::NotEqual, lhsType.reg()); - stubcc.linkExitDirect(lhsIntGuard, stubcc.masm.label()); - - /* Attempt to convert lhs double to int32. */ - Jump lhsDoubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, lhsType.reg()); - frame.loadDouble(lhs, FPRegisters::First, stubcc.masm); - Jump lhsTruncateGuard = stubcc.masm.branchTruncateDoubleToInt32(FPRegisters::First, lhsData); - stubcc.crossJump(stubcc.masm.jump(), masm.label()); - - lhsDoubleGuard.linkTo(stubcc.masm.label(), &stubcc.masm); - lhsTruncateGuard.linkTo(stubcc.masm.label(), &stubcc.masm); - - if (rhsIntGuard.isSet()) - stubcc.linkExitDirect(rhsIntGuard.getJump(), stubcc.masm.label()); - frame.sync(stubcc.masm, Uses(2)); - OOL_STUBCALL(stubs::Rsh); - - masm.rshift32(rhsData, lhsData); + if (fe->isConstant()) { + if (!fe->isType(JSVAL_TYPE_INT32)) { + JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE)); + fe->convertConstantDoubleToInt32(cx); + } + } else if (fe->isType(JSVAL_TYPE_DOUBLE)) { + FPRegisterID fpreg = frame.tempFPRegForData(fe); + FPRegisterID fptemp = frame.allocFPReg(); + RegisterID data = frame.allocReg(); + Jump truncateGuard = masm.branchTruncateDoubleToInt32(fpreg, data); - frame.freeReg(rhsData); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData); + Label syncPath = stubcc.syncExitAndJump(uses); + stubcc.linkExitDirect(truncateGuard, stubcc.masm.label()); - stubcc.rejoin(Changes(1)); -} + /* + * Try an OOL path to convert doubles representing integers within 2^32 + * of a signed integer, by adding/subtracting 2^32 and then trying to + * convert to int32. This has to be an exact conversion, as otherwise + * the truncation works incorrectly on the modified value. + */ -void -mjit::Compiler::jsop_rsh() -{ - FrameEntry *rhs = frame.peek(-1); - FrameEntry *lhs = frame.peek(-2); + stubcc.masm.zeroDouble(fptemp); + Jump positive = stubcc.masm.branchDouble(Assembler::DoubleGreaterThan, fpreg, fptemp); + stubcc.masm.slowLoadConstantDouble(double(4294967296.0), fptemp); + Jump skip = stubcc.masm.jump(); + positive.linkTo(stubcc.masm.label(), &stubcc.masm); + stubcc.masm.slowLoadConstantDouble(double(-4294967296.0), fptemp); + skip.linkTo(stubcc.masm.label(), &stubcc.masm); + + JumpList isDouble; + stubcc.masm.addDouble(fpreg, fptemp); + stubcc.masm.branchConvertDoubleToInt32(fptemp, data, isDouble, Registers::FPConversionTemp); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + isDouble.linkTo(syncPath, &stubcc.masm); + + frame.freeReg(fptemp); + frame.learnType(fe, JSVAL_TYPE_INT32, data); + } else if (!fe->isType(JSVAL_TYPE_INT32)) { + if (masm.supportsFloatingPoint()) { + FPRegisterID fptemp = frame.allocFPReg(); + RegisterID typeReg = frame.tempRegForType(fe); + frame.pinReg(typeReg); + RegisterID dataReg = frame.copyDataIntoReg(fe); + frame.unpinReg(typeReg); + + Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg); + + Label syncPath = stubcc.syncExitAndJump(uses); + stubcc.linkExitDirect(intGuard, stubcc.masm.label()); + + /* Try an OOL path to truncate doubles representing int32s. */ + Jump doubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, typeReg); + doubleGuard.linkTo(syncPath, &stubcc.masm); + + frame.loadDouble(fe, fptemp, stubcc.masm); + Jump truncateGuard = stubcc.masm.branchTruncateDoubleToInt32(fptemp, dataReg); + truncateGuard.linkTo(syncPath, &stubcc.masm); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + + frame.freeReg(fptemp); + frame.learnType(fe, JSVAL_TYPE_INT32, dataReg); + } else { + RegisterID typeReg = frame.tempRegForType(fe); + frame.pinReg(typeReg); + RegisterID dataReg = frame.copyDataIntoReg(fe); + frame.unpinReg(typeReg); - if (tryBinaryConstantFold(cx, frame, JSOP_RSH, lhs, rhs)) - return; + Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg); - if (lhs->isNotType(JSVAL_TYPE_INT32) || rhs->isNotType(JSVAL_TYPE_INT32)) { - prepareStubCall(Uses(2)); - INLINE_STUBCALL(stubs::Rsh); - frame.popn(2); - frame.pushSyncedType(JSVAL_TYPE_INT32); - return; - } + Label syncPath = stubcc.syncExitAndJump(uses); + stubcc.linkExitDirect(intGuard, syncPath); - JS_ASSERT(!(lhs->isConstant() && rhs->isConstant())); - if (lhs->isConstant()) { - if (rhs->isType(JSVAL_TYPE_INT32)) - jsop_rsh_const_int(lhs, rhs); - else - jsop_rsh_const_unknown(lhs, rhs); - } else if (rhs->isConstant()) { - if (lhs->isType(JSVAL_TYPE_INT32)) - jsop_rsh_int_const(lhs, rhs); - else - jsop_rsh_unknown_const(lhs, rhs); - } else { - if (lhs->isType(JSVAL_TYPE_INT32) && rhs->isType(JSVAL_TYPE_INT32)) - jsop_rsh_int_int(lhs, rhs); - else if (lhs->isType(JSVAL_TYPE_INT32)) - jsop_rsh_int_unknown(lhs, rhs); - else - jsop_rsh_unknown_any(lhs, rhs); + frame.learnType(fe, JSVAL_TYPE_INT32, dataReg); + } } } @@ -295,35 +146,25 @@ mjit::Compiler::jsop_bitnot() FrameEntry *top = frame.peek(-1); /* We only want to handle integers here. */ - if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_INT32) { + if (top->isNotType(JSVAL_TYPE_INT32) && top->isNotType(JSVAL_TYPE_DOUBLE)) { prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::BitNot); + INLINE_STUBCALL(stubs::BitNot, REJOIN_FALLTHROUGH); frame.pop(); - frame.pushSyncedType(JSVAL_TYPE_INT32); + frame.pushSynced(JSVAL_TYPE_INT32); return; } - - /* Test the type. */ - bool stubNeeded = false; - if (!top->isTypeKnown()) { - Jump intFail = frame.testInt32(Assembler::NotEqual, top); - stubcc.linkExit(intFail, Uses(1)); - frame.learnType(top, JSVAL_TYPE_INT32); - stubNeeded = true; - } - if (stubNeeded) { - stubcc.leave(); - OOL_STUBCALL(stubs::BitNot); - } + ensureInteger(top, Uses(1)); + + stubcc.leave(); + OOL_STUBCALL(stubs::BitNot, REJOIN_FALLTHROUGH); RegisterID reg = frame.ownRegForData(top); masm.not32(reg); frame.pop(); frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - if (stubNeeded) - stubcc.rejoin(Changes(1)); + stubcc.rejoin(Changes(1)); } void @@ -332,6 +173,9 @@ mjit::Compiler::jsop_bitop(JSOp op) FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); + /* The operands we ensure are integers cannot be copied by each other. */ + frame.separateBinaryEntries(lhs, rhs); + VoidStub stub; switch (op) { case JSOP_BITOR: @@ -346,6 +190,9 @@ mjit::Compiler::jsop_bitop(JSOp op) case JSOP_LSH: stub = stubs::Lsh; break; + case JSOP_RSH: + stub = stubs::Rsh; + break; case JSOP_URSH: stub = stubs::Ursh; break; @@ -354,71 +201,27 @@ mjit::Compiler::jsop_bitop(JSOp op) return; } - bool lhsIntOrDouble = !(lhs->isNotType(JSVAL_TYPE_DOUBLE) && - lhs->isNotType(JSVAL_TYPE_INT32)); - - /* Fast-path double to int conversion. */ - if (!lhs->isConstant() && rhs->isConstant() && lhsIntOrDouble && - rhs->isType(JSVAL_TYPE_INT32) && rhs->getValue().toInt32() == 0 && - (op == JSOP_BITOR || op == JSOP_LSH)) { - RegisterID reg = frame.copyDataIntoReg(lhs); - if (lhs->isType(JSVAL_TYPE_INT32)) { - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - return; - } - MaybeJump isInt; - if (!lhs->isType(JSVAL_TYPE_DOUBLE)) { - RegisterID typeReg = frame.tempRegForType(lhs); - isInt = masm.testInt32(Assembler::Equal, typeReg); - Jump notDouble = masm.testDouble(Assembler::NotEqual, typeReg); - stubcc.linkExit(notDouble, Uses(2)); - } - frame.loadDouble(lhs, FPRegisters::First, masm); - - Jump truncateGuard = masm.branchTruncateDoubleToInt32(FPRegisters::First, reg); - stubcc.linkExit(truncateGuard, Uses(2)); - stubcc.leave(); - OOL_STUBCALL(stub); - - if (isInt.isSet()) - isInt.get().linkTo(masm.label(), &masm); - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - stubcc.rejoin(Changes(1)); - return; - } + /* Convert a double RHS to integer if it's constant for the test below. */ + if (rhs->isConstant() && rhs->getValue().isDouble()) + rhs->convertConstantDoubleToInt32(cx); /* We only want to handle integers here. */ - if (rhs->isNotType(JSVAL_TYPE_INT32) || lhs->isNotType(JSVAL_TYPE_INT32) || + if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE)) || + (rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE)) || (op == JSOP_URSH && rhs->isConstant() && rhs->getValue().toInt32() % 32 == 0)) { prepareStubCall(Uses(2)); - INLINE_STUBCALL(stub); + INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH); frame.popn(2); - if (op == JSOP_URSH) - frame.pushSynced(); - else - frame.pushSyncedType(JSVAL_TYPE_INT32); + frame.pushSynced(op != JSOP_URSH ? JSVAL_TYPE_INT32 : knownPushedType(0)); return; } - - /* Test the types. */ - bool stubNeeded = false; - if (!rhs->isTypeKnown()) { - Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs); - stubcc.linkExit(rhsFail, Uses(2)); - frame.learnType(rhs, JSVAL_TYPE_INT32); - stubNeeded = true; - } - if (!lhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) { - Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs); - stubcc.linkExit(lhsFail, Uses(2)); - stubNeeded = true; - } + + ensureInteger(lhs, Uses(2)); + ensureInteger(rhs, Uses(2)); if (lhs->isConstant() && rhs->isConstant()) { - int32 L = lhs->getValue().toInt32(); - int32 R = rhs->getValue().toInt32(); + int32_t L = lhs->getValue().toInt32(); + int32_t R = rhs->getValue().toInt32(); frame.popn(2); switch (op) { @@ -432,16 +235,19 @@ mjit::Compiler::jsop_bitop(JSOp op) frame.push(Int32Value(L & R)); return; case JSOP_LSH: - frame.push(Int32Value(L << R)); + frame.push(Int32Value(L << (R & 31))); return; - case JSOP_URSH: + case JSOP_RSH: + frame.push(Int32Value(L >> (R & 31))); + return; + case JSOP_URSH: { - uint32 unsignedL; - if (ValueToECMAUint32(cx, lhs->getValue(), (uint32_t*)&unsignedL)) { - frame.push(NumberValue(uint32(unsignedL >> (R & 31)))); - return; - } - break; + uint32_t unsignedL; + ToUint32(cx, Int32Value(L), (uint32_t*)&unsignedL); /* Can't fail. */ + Value v = NumberValue(uint32_t(unsignedL >> (R & 31))); + JS_ASSERT(v.isInt32()); + frame.push(v); + return; } default: JS_NOT_REACHED("say wat"); @@ -465,19 +271,21 @@ mjit::Compiler::jsop_bitop(JSOp op) reg = frame.ownRegForData(lhs); if (rhs->isConstant()) { + int32_t rhsInt = rhs->getValue().toInt32(); if (op == JSOP_BITAND) - masm.and32(Imm32(rhs->getValue().toInt32()), reg); + masm.and32(Imm32(rhsInt), reg); else if (op == JSOP_BITXOR) - masm.xor32(Imm32(rhs->getValue().toInt32()), reg); - else - masm.or32(Imm32(rhs->getValue().toInt32()), reg); + masm.xor32(Imm32(rhsInt), reg); + else if (rhsInt != 0) + masm.or32(Imm32(rhsInt), reg); } else if (frame.shouldAvoidDataRemat(rhs)) { + Address rhsAddr = masm.payloadOf(frame.addressOf(rhs)); if (op == JSOP_BITAND) - masm.and32(masm.payloadOf(frame.addressOf(rhs)), reg); + masm.and32(rhsAddr, reg); else if (op == JSOP_BITXOR) - masm.xor32(masm.payloadOf(frame.addressOf(rhs)), reg); + masm.xor32(rhsAddr, reg); else - masm.or32(masm.payloadOf(frame.addressOf(rhs)), reg); + masm.or32(rhsAddr, reg); } else { RegisterID rhsReg = frame.tempRegForData(rhs); if (op == JSOP_BITAND) @@ -492,6 +300,7 @@ mjit::Compiler::jsop_bitop(JSOp op) } case JSOP_LSH: + case JSOP_RSH: case JSOP_URSH: { /* Not commutative. */ @@ -499,31 +308,30 @@ mjit::Compiler::jsop_bitop(JSOp op) RegisterID reg = frame.ownRegForData(lhs); int shift = rhs->getValue().toInt32() & 0x1F; + stubcc.leave(); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); + if (shift) { if (op == JSOP_LSH) masm.lshift32(Imm32(shift), reg); + else if (op == JSOP_RSH) + masm.rshift32(Imm32(shift), reg); else masm.urshift32(Imm32(shift), reg); } - if (stubNeeded) { - stubcc.leave(); - OOL_STUBCALL(stub); - } frame.popn(2); /* x >>> 0 may result in a double, handled above. */ JS_ASSERT_IF(op == JSOP_URSH, shift >= 1); frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - if (stubNeeded) - stubcc.rejoin(Changes(1)); - + stubcc.rejoin(Changes(1)); return; } #if defined(JS_CPU_X86) || defined(JS_CPU_X64) /* Grosssssss! RHS _must_ be in ECX, on x86 */ RegisterID rr = frame.tempRegInMaskForData(rhs, - Registers::maskReg(JSC::X86Registers::ecx)); + Registers::maskReg(JSC::X86Registers::ecx)).reg(); #else RegisterID rr = frame.tempRegForData(rhs); #endif @@ -547,12 +355,13 @@ mjit::Compiler::jsop_bitop(JSOp op) if (op == JSOP_LSH) { masm.lshift32(rr, reg); + } else if (op == JSOP_RSH) { + masm.rshift32(rr, reg); } else { masm.urshift32(rr, reg); Jump isNegative = masm.branch32(Assembler::LessThan, reg, Imm32(0)); stubcc.linkExit(isNegative, Uses(2)); - stubNeeded = true; } break; } @@ -562,21 +371,22 @@ mjit::Compiler::jsop_bitop(JSOp op) return; } - if (stubNeeded) { - stubcc.leave(); - OOL_STUBCALL(stub); - } + stubcc.leave(); + OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); frame.pop(); frame.pop(); - if (op == JSOP_URSH) + JSValueType type = knownPushedType(0); + + if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE) + frame.pushTypedPayload(type, reg); + else if (op == JSOP_URSH) frame.pushNumber(reg, true); else frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - if (stubNeeded) - stubcc.rejoin(Changes(1)); + stubcc.rejoin(Changes(1)); } static inline bool @@ -588,6 +398,58 @@ CheckNullOrUndefined(FrameEntry *fe) return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED; } +CompileStatus +mjit::Compiler::jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused) +{ + FrameEntry *rhs = frame.peek(-1); + FrameEntry *lhs = frame.peek(-2); + + JS_ASSERT(cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)); + + /* + * Handle equality between two objects. We have to ensure there is no + * special equality operator on either object, if that passes then + * this is a pointer comparison. + */ + types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1); + types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0); + if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) && + !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) { + /* :TODO: Merge with jsop_relational_int? */ + JS_ASSERT_IF(!target, fused != JSOP_IFEQ); + frame.forgetMismatchedObject(lhs); + frame.forgetMismatchedObject(rhs); + Assembler::Condition cond = GetCompareCondition(op, fused); + if (target) { + Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); + if (!frame.syncForBranch(target, Uses(2))) + return Compile_Error; + RegisterID lreg = frame.tempRegForData(lhs); + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + Jump fast = masm.branchPtr(cond, lreg, rreg); + frame.popn(2); + return jumpAndRun(fast, target, &sj) ? Compile_Okay : Compile_Error; + } else { + RegisterID result = frame.allocReg(); + RegisterID lreg = frame.tempRegForData(lhs); + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + masm.branchValue(cond, lreg, rreg, result); + + frame.popn(2); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); + return Compile_Okay; + } + } + + return Compile_Skipped; +} + bool mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused) { @@ -602,8 +464,18 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp f /* What's the other mask? */ FrameEntry *test = lhsTest ? rhs : lhs; - if (test->isTypeKnown()) + if (test->isType(JSVAL_TYPE_NULL) || test->isType(JSVAL_TYPE_UNDEFINED)) { return emitStubCmpOp(stub, target, fused); + } else if (test->isTypeKnown()) { + /* The test will not succeed, constant fold the compare. */ + bool result = GetCompareCondition(op, fused) == Assembler::NotEqual; + frame.pop(); + frame.pop(); + if (target) + return constantFoldBranch(target, result); + frame.push(BooleanValue(result)); + return true; + } /* The other side must be null or undefined. */ RegisterID reg = frame.ownRegForType(test); @@ -616,30 +488,27 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp f */ if (target) { - frame.syncAndForgetEverything(); + frame.syncAndKillEverything(); + frame.freeReg(reg); + + Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); if ((op == JSOP_EQ && fused == JSOP_IFNE) || (op == JSOP_NE && fused == JSOP_IFEQ)) { - /* - * It would be easier to just have two jumpAndTrace calls here, but since - * each jumpAndTrace creates a TRACE IC, and since we want the bytecode - * to have a reference to the TRACE IC at the top of the loop, it's much - * better to have only one TRACE IC per loop, and hence at most one - * jumpAndTrace. - */ Jump b1 = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_UNDEFINED)); Jump b2 = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_NULL)); Jump j1 = masm.jump(); b1.linkTo(masm.label(), &masm); b2.linkTo(masm.label(), &masm); Jump j2 = masm.jump(); - if (!jumpAndTrace(j2, target)) + if (!jumpAndRun(j2, target, &sj)) return false; j1.linkTo(masm.label(), &masm); } else { Jump j = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_UNDEFINED)); Jump j2 = masm.branchPtr(Assembler::NotEqual, reg, ImmType(JSVAL_TYPE_NULL)); - if (!jumpAndTrace(j2, target)) + if (!jumpAndRun(j2, target, &sj)) return false; j.linkTo(masm.label(), &masm); } @@ -657,17 +526,27 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp f return true; } + if (cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) + { + CompileStatus status = jsop_equality_obj_obj(op, target, fused); + if (status == Compile_Okay) return true; + else if (status == Compile_Error) return false; + } + return emitStubCmpOp(stub, target, fused); } bool -mjit::Compiler::jsop_relational(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused) +mjit::Compiler::jsop_relational(JSOp op, BoolStub stub, + jsbytecode *target, JSOp fused) { FrameEntry *rhs = frame.peek(-1); FrameEntry *lhs = frame.peek(-2); /* The compiler should have handled constant folding. */ JS_ASSERT(!(rhs->isConstant() && lhs->isConstant())); + JS_ASSERT(fused == JSOP_NOP || fused == JSOP_IFEQ || fused == JSOP_IFNE); /* Always slow path... */ if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE) && @@ -693,11 +572,16 @@ mjit::Compiler::jsop_relational(JSOp op, BoolStub stub, jsbytecode *target, JSOp } if (frame.haveSameBacking(lhs, rhs)) { - return jsop_relational_self(op, stub, target, fused); + return emitStubCmpOp(stub, target, fused); } else if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) { return emitStubCmpOp(stub, target, fused); } else if (lhs->isType(JSVAL_TYPE_DOUBLE) || rhs->isType(JSVAL_TYPE_DOUBLE)) { + if (!masm.supportsFloatingPoint()) + return emitStubCmpOp(stub, target, fused); return jsop_relational_double(op, stub, target, fused); + } else if (cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_INT32) && rhs->isType(JSVAL_TYPE_INT32)) { + return jsop_relational_int(op, target, fused); } else { return jsop_relational_full(op, stub, target, fused); } @@ -720,7 +604,7 @@ mjit::Compiler::jsop_not() switch (type) { case JSVAL_TYPE_INT32: { - RegisterID data = frame.allocReg(Registers::SingleByteRegs); + RegisterID data = frame.allocReg(Registers::SingleByteRegs).reg(); if (frame.shouldAvoidDataRemat(top)) masm.loadPayload(frame.addressOf(top), data); else @@ -746,15 +630,18 @@ mjit::Compiler::jsop_not() case JSVAL_TYPE_OBJECT: { + RegisterID reg = frame.allocReg(); + masm.move(Imm32(0), reg); + frame.pop(); - frame.push(BooleanValue(false)); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg); break; } default: { prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::ValueToBoolean); + INLINE_STUBCALL_USES(stubs::ValueToBoolean, REJOIN_NONE, Uses(1)); RegisterID reg = Registers::ReturnReg; frame.takeReg(reg); @@ -769,7 +656,7 @@ mjit::Compiler::jsop_not() return; } - RegisterID data = frame.allocReg(Registers::SingleByteRegs); + RegisterID data = frame.allocReg(Registers::SingleByteRegs).reg(); if (frame.shouldAvoidDataRemat(top)) masm.loadPayload(frame.addressOf(top), data); else @@ -811,7 +698,7 @@ mjit::Compiler::jsop_not() /* Leave. */ stubcc.leave(); - OOL_STUBCALL(stubs::Not); + OOL_STUBCALL(stubs::Not, REJOIN_FALLTHROUGH); frame.pop(); frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, data); @@ -863,7 +750,7 @@ mjit::Compiler::jsop_typeof() if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) { JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH)); JSRuntime *rt = cx->runtime; - JSValueType type = JSVAL_TYPE_BOXED; + JSValueType type = JSVAL_TYPE_UNKNOWN; Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ) ? Assembler::Equal : Assembler::NotEqual; @@ -881,11 +768,12 @@ mjit::Compiler::jsop_typeof() cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above; } - if (type != JSVAL_TYPE_BOXED) { - PC += JSOP_STRING_LENGTH;; - PC += JSOP_EQ_LENGTH; + jsbytecode *afterPC = PC + JSOP_STRING_LENGTH + JSOP_EQ_LENGTH; + + if (type != JSVAL_TYPE_UNKNOWN && bytecodeInChunk(afterPC)) { + PC = afterPC; - RegisterID result = frame.allocReg(Registers::SingleByteRegs); + RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg(); #if defined JS_NUNBOX32 if (frame.shouldAvoidTypeRemat(fe)) @@ -904,7 +792,7 @@ mjit::Compiler::jsop_typeof() } prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::TypeOf); + INLINE_STUBCALL(stubs::TypeOf, REJOIN_NONE); frame.pop(); frame.takeReg(Registers::ReturnReg); frame.pushTypedPayload(JSVAL_TYPE_STRING, Registers::ReturnReg); @@ -913,80 +801,68 @@ mjit::Compiler::jsop_typeof() bool mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target) { - FrameEntry *fe = frame.peek(-1); - - MaybeRegisterID type; - MaybeRegisterID data; - - if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) - type.setReg(frame.copyTypeIntoReg(fe)); - data.setReg(frame.copyDataIntoReg(fe)); - - frame.syncAndForgetEverything(); + // JSOP_AND and JSOP_OR may leave the value on the stack (despite + // the frame.pop() below), so we need to sync it. + if (op == JSOP_AND || op == JSOP_OR) { + frame.syncForBranch(target, Uses(0)); + } else { + JS_ASSERT(op == JSOP_IFEQ || op == JSOP_IFNE); + frame.syncForBranch(target, Uses(1)); + } + FrameEntry *fe = frame.peek(-1); Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_OR) ? Assembler::NonZero : Assembler::Zero; - Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_OR) - ? Assembler::Zero - : Assembler::NonZero; - - /* Inline path: Boolean guard + call script. */ - MaybeJump jmpNotBool; - MaybeJump jmpNotExecScript; - if (type.isSet()) { - jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.reg())); - } else { - if (!fe->isTypeKnown()) { - jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, - frame.addressOf(fe))); - } else if (fe->isNotType(JSVAL_TYPE_BOOLEAN) && - fe->isNotType(JSVAL_TYPE_INT32)) { - jmpNotBool.setJump(masm.jump()); - } + + // Load data register and pin it so that frame.testBoolean + // below cannot evict it. + MaybeRegisterID data; + if (!fe->isType(JSVAL_TYPE_DOUBLE)) { + data = frame.tempRegForData(fe); + frame.pinReg(data.reg()); } - /* - * TODO: We don't need the second jump if - * jumpInScript() can go from ool path to inline path. - */ - jmpNotExecScript.setJump(masm.branchTest32(ncond, data.reg(), data.reg())); - Label lblExecScript = masm.label(); - Jump j = masm.jump(); + // Test for boolean if needed. + bool needStub = false; + if (!fe->isType(JSVAL_TYPE_BOOLEAN) && !fe->isType(JSVAL_TYPE_INT32)) { + Jump notBool; + if (fe->mightBeType(JSVAL_TYPE_BOOLEAN)) + notBool = frame.testBoolean(Assembler::NotEqual, fe); + else + notBool = masm.jump(); + stubcc.linkExitForBranch(notBool); + needStub = true; + } + if (data.isSet()) + frame.unpinReg(data.reg()); - /* OOL path: Conversion to boolean. */ - MaybeJump jmpCvtExecScript; - MaybeJump jmpCvtRejoin; - Label lblCvtPath = stubcc.masm.label(); + // Test + branch. + Jump branch; + if (!fe->isType(JSVAL_TYPE_DOUBLE)) + branch = masm.branchTest32(cond, data.reg()); + else + branch = masm.jump(); // dummy jump - if (!fe->isTypeKnown() || - !(fe->isType(JSVAL_TYPE_BOOLEAN) || fe->isType(JSVAL_TYPE_INT32))) { - stubcc.masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::ValueToBoolean), - frame.localSlots()); + // OOL path: call ValueToBoolean and branch. + if (needStub) { + stubcc.leave(); - jmpCvtExecScript.setJump(stubcc.masm.branchTest32(cond, Registers::ReturnReg, - Registers::ReturnReg)); - jmpCvtRejoin.setJump(stubcc.masm.jump()); + // Note: this cannot overwrite slots holding loop invariants. + stubcc.masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::ValueToBoolean), + frame.totalDepth()); } - /* Rejoin tag. */ - Label lblAfterScript = masm.label(); - - /* Patch up jumps. */ - if (jmpNotBool.isSet()) - stubcc.linkExitDirect(jmpNotBool.getJump(), lblCvtPath); - if (jmpNotExecScript.isSet()) - jmpNotExecScript.getJump().linkTo(lblAfterScript, &masm); + Jump stubBranch = stubcc.masm.branchTest32(cond, Registers::ReturnReg); - if (jmpCvtExecScript.isSet()) - stubcc.crossJump(jmpCvtExecScript.getJump(), lblExecScript); - if (jmpCvtRejoin.isSet()) - stubcc.crossJump(jmpCvtRejoin.getJump(), lblAfterScript); + // Rejoin from the stub call fallthrough. + if (needStub) + stubcc.rejoin(Changes(0)); frame.pop(); - return jumpAndTrace(j, target); + return jumpAndRun(branch, target, &stubBranch); } bool @@ -1002,8 +878,12 @@ mjit::Compiler::jsop_ifneq(JSOp op, jsbytecode *target) if (op == JSOP_IFEQ) b = !b; if (b) { - frame.syncAndForgetEverything(); - if (!jumpAndTrace(masm.jump(), target)) + if (!frame.syncForBranch(target, Uses(0))) + return false; + if (!jumpAndRun(masm.jump(), target)) + return false; + } else { + if (target < PC && !finishLoop(target)) return false; } return true; @@ -1023,8 +903,9 @@ mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target) /* Short-circuit. */ if ((op == JSOP_OR && b == JS_TRUE) || (op == JSOP_AND && b == JS_FALSE)) { - frame.syncAndForgetEverything(); - if (!jumpAndTrace(masm.jump(), target)) + if (!frame.syncForBranch(target, Uses(0))) + return false; + if (!jumpAndRun(masm.jump(), target)) return false; } @@ -1035,34 +916,35 @@ mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target) return booleanJumpScript(op, target); } -void -mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped) +bool +mjit::Compiler::jsop_localinc(JSOp op, uint32_t slot) { - if (popped || (op == JSOP_INCLOCAL || op == JSOP_DECLOCAL)) { - int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? -1 : 1; + restoreVarType(); + types::TypeSet *types = pushedTypeSet(0); + JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN; + + int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1; + + if (!analysis->incrementInitialValueObserved(PC)) { // Before: // After: V frame.pushLocal(slot); // Before: V // After: V 1 - frame.push(Int32Value(amt)); + frame.push(Int32Value(-amt)); // Note, SUB will perform integer conversion for us. // Before: V 1 // After: N+1 - jsop_binary(JSOP_SUB, stubs::Sub); + if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types)) + return false; // Before: N+1 // After: N+1 - frame.storeLocal(slot, popped); - - if (popped) - frame.pop(); + frame.storeLocal(slot, analysis->popGuaranteed(PC)); } else { - int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1; - // Before: // After: V frame.pushLocal(slot); @@ -1081,7 +963,8 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped) // Before: N N 1 // After: N N+1 - jsop_binary(JSOP_ADD, stubs::Add); + if (!jsop_binary(JSOP_ADD, stubs::Add, type, types)) + return false; // Before: N N+1 // After: N N+1 @@ -1091,36 +974,40 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped) // After: N frame.pop(); } + + updateVarType(); + return true; } -void -mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped) +bool +mjit::Compiler::jsop_arginc(JSOp op, uint32_t slot) { - if (popped || (op == JSOP_INCARG || op == JSOP_DECARG)) { - int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? -1 : 1; + restoreVarType(); + + types::TypeSet *types = pushedTypeSet(0); + JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN; + + int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1; + if (!analysis->incrementInitialValueObserved(PC)) { // Before: // After: V frame.pushArg(slot); // Before: V // After: V 1 - frame.push(Int32Value(amt)); + frame.push(Int32Value(-amt)); // Note, SUB will perform integer conversion for us. // Before: V 1 // After: N+1 - jsop_binary(JSOP_SUB, stubs::Sub); + if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types)) + return false; // Before: N+1 // After: N+1 - frame.storeArg(slot, popped); - - if (popped) - frame.pop(); + frame.storeArg(slot, analysis->popGuaranteed(PC)); } else { - int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1; - // Before: // After: V frame.pushArg(slot); @@ -1139,7 +1026,8 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped) // Before: N N 1 // After: N N+1 - jsop_binary(JSOP_ADD, stubs::Add); + if (!jsop_binary(JSOP_ADD, stubs::Add, type, types)) + return false; // Before: N N+1 // After: N N+1 @@ -1149,6 +1037,9 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped) // After: N frame.pop(); } + + updateVarType(); + return true; } static inline bool @@ -1156,10 +1047,16 @@ IsCacheableSetElem(FrameEntry *obj, FrameEntry *id, FrameEntry *value) { if (obj->isNotType(JSVAL_TYPE_OBJECT)) return false; - if (id->isNotType(JSVAL_TYPE_INT32)) - return false; - if (id->isConstant() && id->getValue().toInt32() < 0) + if (id->isNotType(JSVAL_TYPE_INT32) && id->isNotType(JSVAL_TYPE_DOUBLE)) return false; + if (id->isConstant()) { + if (id->isNotType(JSVAL_TYPE_INT32)) + return false; + if (id->getValue().toInt32() < 0) + return false; + if (id->getValue().toInt32() + 1 < 0) // watch for overflow in hole paths + return false; + } // obj[obj] * is not allowed, since it will never optimize. // obj[id] = id is allowed. @@ -1170,48 +1067,547 @@ IsCacheableSetElem(FrameEntry *obj, FrameEntry *id, FrameEntry *value) return true; } -bool -mjit::Compiler::jsop_setelem(bool popGuaranteed) +void +mjit::Compiler::jsop_setelem_dense() { FrameEntry *obj = frame.peek(-3); FrameEntry *id = frame.peek(-2); FrameEntry *value = frame.peek(-1); - if (!IsCacheableSetElem(obj, id, value)) { - jsop_setelem_slow(); - return true; + // We might not know whether this is an object, but if it is an object we + // know it is a dense array. + if (!obj->isTypeKnown()) { + Jump guard = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(guard, Uses(3)); } - SetElementICInfo ic = SetElementICInfo(JSOp(*PC)); + if (id->isType(JSVAL_TYPE_DOUBLE)) + tryConvertInteger(id, Uses(2)); - // One by one, check if the most important stack entries have registers, - // and if so, pin them. This is to avoid spilling and reloading from the - // stack as we incrementally allocate other registers. - MaybeRegisterID pinnedValueType = frame.maybePinType(value); - MaybeRegisterID pinnedValueData = frame.maybePinData(value); + // Test for integer index. + if (!id->isTypeKnown()) { + Jump guard = frame.testInt32(Assembler::NotEqual, id); + stubcc.linkExit(guard, Uses(3)); + } - // Pin |obj| if it doesn't share a backing with |value|. - MaybeRegisterID pinnedObjData; - if (!obj->hasSameBacking(value)) - pinnedObjData = frame.maybePinData(obj); + // Allocate registers. - // Pin |id| if it doesn't share a backing with |value|. - MaybeRegisterID pinnedIdData; - if (!id->hasSameBacking(value)) - pinnedIdData = frame.maybePinData(id); + ValueRemat vr; + frame.pinEntry(value, vr, /* breakDouble = */ false); - // Note: The fact that |obj| and |value|, or |id| and |value| can be - // copies, is a little complicated, but it is safe. Explanations - // follow at each point. Keep in mind two points: - // 1) maybePin() never allocates a register, it only pins if a register - // already existed. - // 2) tempRegForData() will work fine on a pinned register. - - // Guard that the object is an object. - if (!obj->isTypeKnown()) { - Jump j = frame.testObject(Assembler::NotEqual, obj); - stubcc.linkExit(j, Uses(3)); - } + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value); + if (pinKey) + frame.pinReg(key.reg()); + + // Register to hold the computed slots pointer for the object. If we can + // hoist the initialized length check, we make the slots pointer loop + // invariant and never access the object itself. + RegisterID slotsReg; + analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 2)); + analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 1)); + bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) && + loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv); + + MaybeJump initlenExit; + + if (hoisted) { + FrameEntry *slotsFe = loop->invariantArraySlots(objv); + slotsReg = frame.tempRegForData(slotsFe); + + frame.unpinEntry(vr); + if (pinKey) + frame.unpinReg(key.reg()); + } else { + // Get a register for the object which we can clobber, and load its elements. + if (frame.haveSameBacking(obj, value)) { + slotsReg = frame.allocReg(); + masm.move(vr.dataReg(), slotsReg); + } else if (frame.haveSameBacking(obj, id)) { + slotsReg = frame.allocReg(); + masm.move(key.reg(), slotsReg); + } else { + slotsReg = frame.copyDataIntoReg(obj); + } + masm.loadPtr(Address(slotsReg, JSObject::offsetOfElements()), slotsReg); + + frame.unpinEntry(vr); + if (pinKey) + frame.unpinReg(key.reg()); + + // Make an OOL path for setting exactly the initialized length. + Label syncTarget = stubcc.syncExitAndJump(Uses(3)); + + Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::BelowOrEqual); + stubcc.linkExitDirect(initlenGuard, stubcc.masm.label()); + + // Recheck for an exact initialized length. :TODO: would be nice to + // reuse the condition bits from the previous test. + Jump exactlenGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::NotEqual); + exactlenGuard.linkTo(syncTarget, &stubcc.masm); + + // Check array capacity. + Jump capacityGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfCapacity(), + slotsReg, key, Assembler::BelowOrEqual); + capacityGuard.linkTo(syncTarget, &stubcc.masm); + + // Bump the index for setting the array length. The above guard + // ensures this won't overflow, due to NSLOTS_LIMIT. + stubcc.masm.bumpKey(key, 1); + + // Update the initialized length. + stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfInitializedLength())); + + // Update the array length if needed. + Jump lengthGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfLength(), + slotsReg, key, Assembler::AboveOrEqual); + stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfLength())); + lengthGuard.linkTo(stubcc.masm.label(), &stubcc.masm); + + // Restore the index. + stubcc.masm.bumpKey(key, -1); + + initlenExit = stubcc.masm.jump(); + } + +#ifdef JSGC_INCREMENTAL_MJ + /* + * Write barrier. + * We skip over the barrier if we incremented initializedLength above, + * because in that case the slot we're overwriting was previously + * undefined. + */ + types::TypeSet *types = frame.extra(obj).types; + if (cx->compartment->needsBarrier() && (!types || types->propertyNeedsBarrier(cx, JSID_VOID))) { + Label barrierStart = stubcc.masm.label(); + stubcc.linkExitDirect(masm.jump(), barrierStart); + + /* + * The sync call below can potentially clobber key.reg() and slotsReg. + * We pin key.reg() to avoid it being clobbered. If |hoisted| is true, + * we can also pin slotsReg. If not, then slotsReg is owned by the + * compiler and we save in manually to VMFrame::scratch. + * + * Additionally, the WriteBarrier stub can clobber both registers. The + * rejoin call will restore key.reg() but not slotsReg. So we save + * slotsReg in the frame and restore it after the stub call. + */ + stubcc.masm.storePtr(slotsReg, FrameAddress(offsetof(VMFrame, scratch))); + if (hoisted) + frame.pinReg(slotsReg); + if (!key.isConstant()) + frame.pinReg(key.reg()); + frame.sync(stubcc.masm, Uses(3)); + if (!key.isConstant()) + frame.unpinReg(key.reg()); + if (hoisted) + frame.unpinReg(slotsReg); + else + stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, scratch)), slotsReg); + + if (key.isConstant()) + stubcc.masm.lea(Address(slotsReg, key.index() * sizeof(Value)), Registers::ArgReg1); + else + stubcc.masm.lea(BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE), Registers::ArgReg1); + OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE); + stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, scratch)), slotsReg); + stubcc.rejoin(Changes(0)); + } +#endif + + /* Jump over the write barrier in the initlen case. */ + if (initlenExit.isSet()) + stubcc.crossJump(initlenExit.get(), masm.label()); + + // Fully store the value. :TODO: don't need to do this in the non-initlen case + // if the array is packed and monomorphic. + if (key.isConstant()) + masm.storeValue(vr, Address(slotsReg, key.index() * sizeof(Value))); + else + masm.storeValue(vr, BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE)); + + stubcc.leave(); + OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH); + + if (!hoisted) + frame.freeReg(slotsReg); + frame.shimmy(2); + stubcc.rejoin(Changes(2)); +} + +#ifdef JS_METHODJIT_TYPED_ARRAY +void +mjit::Compiler::convertForTypedArray(int atype, ValueRemat *vr, bool *allocated) +{ + FrameEntry *value = frame.peek(-1); + bool floatArray = (atype == TypedArray::TYPE_FLOAT32 || + atype == TypedArray::TYPE_FLOAT64); + *allocated = false; + + if (value->isConstant()) { + Value v = value->getValue(); + if (floatArray) { + double d = v.isDouble() ? v.toDouble() : v.toInt32(); + *vr = ValueRemat::FromConstant(DoubleValue(d)); + } else { + int i32; + if (v.isInt32()) { + i32 = v.toInt32(); + if (atype == TypedArray::TYPE_UINT8_CLAMPED) + i32 = ClampIntForUint8Array(i32); + } else { + i32 = (atype == TypedArray::TYPE_UINT8_CLAMPED) + ? js_TypedArray_uint8_clamp_double(v.toDouble()) + : js_DoubleToECMAInt32(v.toDouble()); + } + *vr = ValueRemat::FromConstant(Int32Value(i32)); + } + } else { + if (floatArray) { + FPRegisterID fpReg; + MaybeJump notNumber = loadDouble(value, &fpReg, allocated); + if (notNumber.isSet()) + stubcc.linkExit(notNumber.get(), Uses(3)); + + if (atype == TypedArray::TYPE_FLOAT32) { + if (!*allocated) { + frame.pinReg(fpReg); + FPRegisterID newFpReg = frame.allocFPReg(); + masm.convertDoubleToFloat(fpReg, newFpReg); + frame.unpinReg(fpReg); + fpReg = newFpReg; + *allocated = true; + } else { + masm.convertDoubleToFloat(fpReg, fpReg); + } + } + *vr = ValueRemat::FromFPRegister(fpReg); + } else { + /* + * Allocate a register with the following properties: + * 1) For byte arrays the value must be in a byte register. + * 2) For Uint8ClampedArray the register must be writable. + * 3) If the value is definitely int32_t (and the array is not + * Uint8ClampedArray) we don't have to allocate a new register. + * 4) If id and value have the same backing (e.g. arr[i] = i) and + * we need a byte register, we have to allocate a new register + * because we've already pinned a key register and can't use + * tempRegInMaskForData. + */ + MaybeRegisterID reg, dataReg; + bool needsByteReg = (atype == TypedArray::TYPE_INT8 || + atype == TypedArray::TYPE_UINT8 || + atype == TypedArray::TYPE_UINT8_CLAMPED); + FrameEntry *id = frame.peek(-2); + if (!value->isType(JSVAL_TYPE_INT32) || atype == TypedArray::TYPE_UINT8_CLAMPED || + (needsByteReg && frame.haveSameBacking(id, value))) { + // Grab data register before branching. + if (value->mightBeType(JSVAL_TYPE_INT32)) { + dataReg = frame.tempRegForData(value); + + // Make sure it's not clobbered by allocReg or tempRegForType. + if (!frame.haveSameBacking(id, value)) + frame.pinReg(dataReg.reg()); + } + + // x86 has 4 single byte registers. Worst case we've pinned 3 + // registers, one for each of object, key and value. This means + // there must be at least one single byte register available. + if (needsByteReg) + reg = frame.allocReg(Registers::SingleByteRegs).reg(); + else + reg = frame.allocReg(); + *allocated = true; + } else { + if (needsByteReg) + reg = frame.tempRegInMaskForData(value, Registers::SingleByteRegs).reg(); + else + reg = frame.tempRegForData(value); + } + + // Get type register before branching. + MaybeRegisterID typeReg; + if (!value->isTypeKnown()) { + // Note: we don't need to pin reg, it's never a temporary register if the + // type of value is not known. + JS_ASSERT(*allocated); + typeReg = frame.tempRegForType(value); + } + + MaybeJump intDone; + if (value->mightBeType(JSVAL_TYPE_INT32)) { + // Check if the value is an integer. + MaybeJump notInt; + if (!value->isTypeKnown()) { + JS_ASSERT(*allocated); + notInt = masm.testInt32(Assembler::NotEqual, typeReg.reg()); + } + + if (*allocated) { + masm.move(dataReg.reg(), reg.reg()); + if (!frame.haveSameBacking(id, value)) + frame.unpinReg(dataReg.reg()); + } + + if (atype == TypedArray::TYPE_UINT8_CLAMPED) + masm.clampInt32ToUint8(reg.reg()); + + if (notInt.isSet()) { + intDone = masm.jump(); + notInt.get().linkTo(masm.label(), &masm); + } + } + if (value->mightBeType(JSVAL_TYPE_DOUBLE)) { + // Check if the value is a double. + if (!value->isTypeKnown()) { + Jump notNumber = masm.testDouble(Assembler::NotEqual, typeReg.reg()); + stubcc.linkExit(notNumber, Uses(3)); + } + + // Load value in fpReg. + FPRegisterID fpReg; + if (value->isTypeKnown()) { + fpReg = frame.tempFPRegForData(value); + } else { + fpReg = frame.allocFPReg(); + frame.loadDouble(value, fpReg, masm); + } + + // Convert double to integer. + if (atype == TypedArray::TYPE_UINT8_CLAMPED) { + if (value->isTypeKnown()) + frame.pinReg(fpReg); + FPRegisterID fpTemp = frame.allocFPReg(); + if (value->isTypeKnown()) + frame.unpinReg(fpReg); + masm.clampDoubleToUint8(fpReg, fpTemp, reg.reg()); + frame.freeReg(fpTemp); + } else { + Jump j = masm.branchTruncateDoubleToInt32(fpReg, reg.reg()); + stubcc.linkExit(j, Uses(3)); + } + if (!value->isTypeKnown()) + frame.freeReg(fpReg); + } + if (intDone.isSet()) + intDone.get().linkTo(masm.label(), &masm); + *vr = ValueRemat::FromKnownType(JSVAL_TYPE_INT32, reg.reg()); + } + } +} + +void +mjit::Compiler::jsop_setelem_typed(int atype) +{ + FrameEntry *obj = frame.peek(-3); + FrameEntry *id = frame.peek(-2); + FrameEntry *value = frame.peek(-1); + + // We might not know whether this is an object, but if it is an object we + // know it's a typed array. + if (!obj->isTypeKnown()) { + Jump guard = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(guard, Uses(3)); + } + + if (id->isType(JSVAL_TYPE_DOUBLE)) + tryConvertInteger(id, Uses(2)); + + // Test for integer index. + if (!id->isTypeKnown()) { + Jump guard = frame.testInt32(Assembler::NotEqual, id); + stubcc.linkExit(guard, Uses(3)); + } + + // Pin value. + ValueRemat vr; + frame.pinEntry(value, vr, /* breakDouble = */ false); + + // Allocate and pin object and key regs. + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + + bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value); + if (pinKey) + frame.pinReg(key.reg()); + + analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1)); + analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0)); + bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) && + loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv); + + RegisterID objReg; + if (hoisted) { + FrameEntry *slotsFe = loop->invariantArraySlots(objv); + objReg = frame.tempRegForData(slotsFe); + frame.pinReg(objReg); + } else { + objReg = frame.copyDataIntoReg(obj); + + // Bounds check. + int lengthOffset = TypedArray::lengthOffset() + offsetof(jsval_layout, s.payload); + Jump lengthGuard = masm.guardArrayExtent(lengthOffset, + objReg, key, Assembler::BelowOrEqual); + stubcc.linkExit(lengthGuard, Uses(3)); + + // Load the array's packed data vector. + masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg); + } + + // Unpin value so that convertForTypedArray can assign a new data + // register using tempRegInMaskForData. + frame.unpinEntry(vr); + + // Make sure key is pinned. + if (frame.haveSameBacking(id, value)) { + frame.pinReg(key.reg()); + pinKey = true; + } + JS_ASSERT(pinKey == !id->isConstant()); + + bool allocated; + convertForTypedArray(atype, &vr, &allocated); + + // Store the value. + masm.storeToTypedArray(atype, objReg, key, vr); + if (allocated) { + if (vr.isFPRegister()) + frame.freeReg(vr.fpReg()); + else + frame.freeReg(vr.dataReg()); + } + if (pinKey) + frame.unpinReg(key.reg()); + if (hoisted) + frame.unpinReg(objReg); + else + frame.freeReg(objReg); + + stubcc.leave(); + OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH); + + frame.shimmy(2); + stubcc.rejoin(Changes(2)); +} +#endif /* JS_METHODJIT_TYPED_ARRAY */ + +void +mjit::Compiler::tryConvertInteger(FrameEntry *fe, Uses uses) +{ + JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE)); + + JumpList isDouble; + FPRegisterID fpreg = frame.tempFPRegForData(fe); + RegisterID reg = frame.allocReg(); + masm.branchConvertDoubleToInt32(fpreg, reg, isDouble, Registers::FPConversionTemp); + Jump j = masm.jump(); + isDouble.linkTo(masm.label(), &masm); + stubcc.linkExit(masm.jump(), uses); + j.linkTo(masm.label(), &masm); + frame.learnType(fe, JSVAL_TYPE_INT32, reg); +} + +/* Get the common shape used by all dense arrays with a prototype at globalObj. */ +static inline Shape * +GetDenseArrayShape(JSContext *cx, JSObject *globalObj) +{ + JS_ASSERT(globalObj); + + JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx); + if (!proto) + return NULL; + + return EmptyShape::getInitialShape(cx, &ArrayClass, proto, + proto->getParent(), gc::FINALIZE_OBJECT0); +} + +bool +mjit::Compiler::jsop_setelem(bool popGuaranteed) +{ + FrameEntry *obj = frame.peek(-3); + FrameEntry *id = frame.peek(-2); + FrameEntry *value = frame.peek(-1); + + if (!IsCacheableSetElem(obj, id, value) || monitored(PC)) { + jsop_setelem_slow(); + return true; + } + + frame.forgetMismatchedObject(obj); + + // If the object is definitely a dense array or a typed array we can generate + // code directly without using an inline cache. + if (cx->typeInferenceEnabled()) { + types::TypeSet *types = analysis->poppedTypes(PC, 2); + + if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && + !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { + // Inline dense array path. + jsop_setelem_dense(); + return true; + } + +#ifdef JS_METHODJIT_TYPED_ARRAY + if ((value->mightBeType(JSVAL_TYPE_INT32) || value->mightBeType(JSVAL_TYPE_DOUBLE)) && + !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) { + // Inline typed array path. + int atype = types->getTypedArrayType(cx); + if (atype != TypedArray::TYPE_MAX) { + jsop_setelem_typed(atype); + return true; + } + } +#endif + } + + if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) { + jsop_setelem_slow(); + return true; + } + +#ifdef JSGC_INCREMENTAL_MJ + // Write barrier. + if (cx->compartment->needsBarrier()) { + jsop_setelem_slow(); + return true; + } +#endif + + SetElementICInfo ic = SetElementICInfo(JSOp(*PC)); + + // One by one, check if the most important stack entries have registers, + // and if so, pin them. This is to avoid spilling and reloading from the + // stack as we incrementally allocate other registers. + MaybeRegisterID pinnedValueType = frame.maybePinType(value); + MaybeRegisterID pinnedValueData = frame.maybePinData(value); + + // Pin |obj| if it doesn't share a backing with |value|. + MaybeRegisterID pinnedObjData; + if (!obj->hasSameBacking(value)) + pinnedObjData = frame.maybePinData(obj); + + // Pin |id| if it doesn't share a backing with |value|. + MaybeRegisterID pinnedIdData; + if (!id->hasSameBacking(value)) + pinnedIdData = frame.maybePinData(id); + + // Note: The fact that |obj| and |value|, or |id| and |value| can be + // copies, is a little complicated, but it is safe. Explanations + // follow at each point. Keep in mind two points: + // 1) maybePin() never allocates a register, it only pins if a register + // already existed. + // 2) tempRegForData() will work fine on a pinned register. + + // Guard that the object is an object. + if (!obj->isTypeKnown()) { + Jump j = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(j, Uses(3)); + } // Guard that the id is int32. if (!id->isTypeKnown()) { @@ -1265,15 +1661,19 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed) ic.slowPathStart = stubcc.syncExit(Uses(3)); // Guard obj is a dense array. - ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &js_ArrayClass); - stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart); + Shape *shape = GetDenseArrayShape(cx, globalObj); + if (!shape) + return false; + ic.shapeGuard = masm.guardShape(ic.objReg, shape); + stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart); - // Guard capacity in range. - Jump capacityGuard = masm.guardArrayCapacity(ic.objReg, ic.key); - stubcc.linkExitDirect(capacityGuard, ic.slowPathStart); + // Load the dynamic elements vector. + masm.loadPtr(Address(ic.objReg, JSObject::offsetOfElements()), ic.objReg); - // Load the dynamic slots vector. - masm.loadPtr(Address(ic.objReg, offsetof(JSObject, slots)), ic.objReg); + // Guard in range of initialized length. + Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + ic.objReg, ic.key, Assembler::BelowOrEqual); + stubcc.linkExitDirect(initlenGuard, ic.slowPathStart); // Guard there's no hole, then store directly to the slot. if (ic.key.isConstant()) { @@ -1290,9 +1690,9 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed) stubcc.leave(); #if defined JS_POLYIC passICAddress(&ic); - ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(ic::SetElement)); + ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(ic::SetElement), REJOIN_FALLTHROUGH); #else - OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem)); + OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH); #endif ic.fastPathRejoin = masm.label(); @@ -1339,18 +1739,19 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed) static inline bool IsCacheableGetElem(FrameEntry *obj, FrameEntry *id) { - if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT) - return false; if (id->isTypeKnown() && - !(id->getKnownType() == JSVAL_TYPE_INT32 + !(id->isType(JSVAL_TYPE_INT32) || id->isType(JSVAL_TYPE_DOUBLE) #if defined JS_POLYIC - || id->getKnownType() == JSVAL_TYPE_STRING + || id->isType(JSVAL_TYPE_STRING) #endif )) { return false; } - if (id->isTypeKnown() && id->getKnownType() == JSVAL_TYPE_INT32 && id->isConstant() && + if (id->isType(JSVAL_TYPE_DOUBLE) && id->isConstant()) + return false; + + if (id->isType(JSVAL_TYPE_INT32) && id->isConstant() && id->getValue().toInt32() < 0) { return false; } @@ -1362,17 +1763,403 @@ IsCacheableGetElem(FrameEntry *obj, FrameEntry *id) return true; } +void +mjit::Compiler::jsop_getelem_dense(bool isPacked) +{ + FrameEntry *obj = frame.peek(-2); + FrameEntry *id = frame.peek(-1); + + // We might not know whether this is an object, but if it is an object we + // know it is a dense array. + if (!obj->isTypeKnown()) { + Jump guard = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(guard, Uses(2)); + } + + if (id->isType(JSVAL_TYPE_DOUBLE)) + tryConvertInteger(id, Uses(2)); + + // Test for integer index. + if (!id->isTypeKnown()) { + Jump guard = frame.testInt32(Assembler::NotEqual, id); + stubcc.linkExit(guard, Uses(2)); + } + + JSValueType type = knownPushedType(0); + + // Allocate registers. + + // If we know the result of the GETELEM may be undefined, then misses on the + // initialized length or hole checks can just produce an undefined value. + // We checked in the caller that prototypes do not have indexed properties. + bool allowUndefined = mayPushUndefined(0); + + analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1)); + analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0)); + bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) && + loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv); + + // Get a register with either the object or its slots, depending on whether + // we are hoisting the slots computation. + RegisterID baseReg; + if (hoisted) { + FrameEntry *slotsFe = loop->invariantArraySlots(objv); + baseReg = frame.tempRegForData(slotsFe); + } else { + baseReg = frame.tempRegForData(obj); + } + frame.pinReg(baseReg); + + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + bool pinKey = !key.isConstant() && key.reg() != baseReg; + if (pinKey) + frame.pinReg(key.reg()); + + RegisterID dataReg = frame.allocReg(); + + MaybeRegisterID typeReg; + if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE || hasTypeBarriers(PC)) + typeReg = frame.allocReg(); + + frame.unpinReg(baseReg); + if (pinKey) + frame.unpinReg(key.reg()); + + RegisterID slotsReg; + if (hoisted) { + slotsReg = baseReg; + } else { + masm.loadPtr(Address(baseReg, JSObject::offsetOfElements()), dataReg); + slotsReg = dataReg; + } + + // Guard on the array's initialized length. + MaybeJump initlenGuard; + if (!hoisted) { + initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + slotsReg, key, Assembler::BelowOrEqual); + if (!allowUndefined) + stubcc.linkExit(initlenGuard.get(), Uses(2)); + } + + // Get the slot, skipping the hole check if the array is known to be packed. + Jump holeCheck; + if (key.isConstant()) { + Address slot(slotsReg, key.index() * sizeof(Value)); + holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg); + } else { + JS_ASSERT(key.reg() != dataReg); + BaseIndex slot(slotsReg, key.reg(), masm.JSVAL_SCALE); + holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg); + } + + if (!isPacked && !allowUndefined) + stubcc.linkExit(holeCheck, Uses(2)); + + stubcc.leave(); + OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -2); + + frame.popn(2); + + BarrierState barrier; + if (typeReg.isSet()) { + frame.pushRegs(typeReg.reg(), dataReg, type); + barrier = testBarrier(typeReg.reg(), dataReg, false); + } else { + frame.pushTypedPayload(type, dataReg); + } + + stubcc.rejoin(Changes(2)); + + if (allowUndefined) { + if (!hoisted) + stubcc.linkExitDirect(initlenGuard.get(), stubcc.masm.label()); + if (!isPacked) + stubcc.linkExitDirect(holeCheck, stubcc.masm.label()); + JS_ASSERT(type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_UNDEFINED); + if (type == JSVAL_TYPE_UNDEFINED) + stubcc.masm.loadValuePayload(UndefinedValue(), dataReg); + else + stubcc.masm.loadValueAsComponents(UndefinedValue(), typeReg.reg(), dataReg); + stubcc.linkRejoin(stubcc.masm.jump()); + } + + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); +} + +void +mjit::Compiler::jsop_getelem_args() +{ + FrameEntry *id = frame.peek(-1); + + if (id->isType(JSVAL_TYPE_DOUBLE)) + tryConvertInteger(id, Uses(2)); + + // Test for integer index. + if (!id->isTypeKnown()) { + Jump guard = frame.testInt32(Assembler::NotEqual, id); + stubcc.linkExit(guard, Uses(2)); + } + + // Allocate registers. + + analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0)); + bool hoistedLength = loop && id->isType(JSVAL_TYPE_INT32) && + loop->hoistArgsLengthCheck(indexv); + FrameEntry *actualsFe = loop ? loop->invariantArguments() : NULL; + + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + if (!key.isConstant()) + frame.pinReg(key.reg()); + + RegisterID dataReg = frame.allocReg(); + RegisterID typeReg = frame.allocReg(); + + // Guard on nactual. + if (!hoistedLength) { + Address nactualAddr(JSFrameReg, StackFrame::offsetOfNumActual()); + MaybeJump rangeGuard; + if (key.isConstant()) { + JS_ASSERT(key.index() >= 0); + rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, Imm32(key.index())); + } else { + rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, key.reg()); + } + stubcc.linkExit(rangeGuard.get(), Uses(2)); + } + + RegisterID actualsReg; + if (actualsFe) { + actualsReg = frame.tempRegForData(actualsFe); + } else { + actualsReg = dataReg; + masm.loadFrameActuals(outerScript->function(), actualsReg); + } + + if (!key.isConstant()) + frame.unpinReg(key.reg()); + + if (key.isConstant()) { + Address arg(actualsReg, key.index() * sizeof(Value)); + masm.loadValueAsComponents(arg, typeReg, dataReg); + } else { + JS_ASSERT(key.reg() != dataReg); + BaseIndex arg(actualsReg, key.reg(), masm.JSVAL_SCALE); + masm.loadValueAsComponents(arg, typeReg, dataReg); + } + + stubcc.leave(); + OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -2); + + frame.popn(2); + frame.pushRegs(typeReg, dataReg, knownPushedType(0)); + BarrierState barrier = testBarrier(typeReg, dataReg, false); + + stubcc.rejoin(Changes(2)); + + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); +} + +#ifdef JS_METHODJIT_TYPED_ARRAY +bool +mjit::Compiler::jsop_getelem_typed(int atype) +{ + // Unlike dense arrays, the types of elements in typed arrays are not + // guaranteed to be present in the object's type, and we need to use + // knowledge about the possible contents of the array vs. the types + // that have been read out of it to figure out how to do the load. + + // Array contents + // Float Uint32_t Int32 + // Observed types + // + // {int} XXX reg pair+test reg + // {int,float} FP reg FP reg reg pair + // {X,int} XXX reg pair+test reg pair + // {X,int,float} reg pair reg pair reg pair + // {X} XXX XXX XXX + + // Reject entries marked 'XXX' above, and compile a normal GETELEM. + types::TypeSet *pushedTypes = pushedTypeSet(0); + if (atype == TypedArray::TYPE_FLOAT32 || atype == TypedArray::TYPE_FLOAT64) { + if (!pushedTypes->hasType(types::Type::DoubleType())) + return false; + } else { + if (!pushedTypes->hasType(types::Type::Int32Type())) + return false; + } + + FrameEntry *obj = frame.peek(-2); + FrameEntry *id = frame.peek(-1); + + // We might not know whether this is an object, but if it's an object we + // know it is a typed array. + if (!obj->isTypeKnown()) { + Jump guard = frame.testObject(Assembler::NotEqual, obj); + stubcc.linkExit(guard, Uses(2)); + } + + if (id->isType(JSVAL_TYPE_DOUBLE)) + tryConvertInteger(id, Uses(2)); + + // Test for integer index. + if (!id->isTypeKnown()) { + Jump guard = frame.testInt32(Assembler::NotEqual, id); + stubcc.linkExit(guard, Uses(2)); + } + + // Load object and key. + Int32Key key = id->isConstant() + ? Int32Key::FromConstant(id->getValue().toInt32()) + : Int32Key::FromRegister(frame.tempRegForData(id)); + if (!key.isConstant()) + frame.pinReg(key.reg()); + + analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1)); + analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0)); + bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) && + loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv); + + RegisterID objReg; + if (hoisted) { + FrameEntry *slotsFe = loop->invariantArraySlots(objv); + objReg = frame.tempRegForData(slotsFe); + frame.pinReg(objReg); + } else { + objReg = frame.copyDataIntoReg(obj); + + // Bounds check. + int lengthOffset = TypedArray::lengthOffset() + offsetof(jsval_layout, s.payload); + Jump lengthGuard = masm.guardArrayExtent(lengthOffset, + objReg, key, Assembler::BelowOrEqual); + stubcc.linkExit(lengthGuard, Uses(2)); + + // Load the array's packed data vector. + masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg); + } + + // We can load directly into an FP-register if the following conditions + // are met: + // 1) The array is an Uint32Array or a float array (loadFromTypedArray + // can't load into an FP-register for other arrays). + // 2) The result is definitely a double (the result type set can include + // other types after reading out-of-bound values). + AnyRegisterID dataReg; + MaybeRegisterID typeReg, tempReg; + JSValueType type = knownPushedType(0); + bool maybeReadFloat = (atype == TypedArray::TYPE_FLOAT32 || + atype == TypedArray::TYPE_FLOAT64 || + atype == TypedArray::TYPE_UINT32); + if (maybeReadFloat && type == JSVAL_TYPE_DOUBLE) { + dataReg = frame.allocFPReg(); + // Need an extra reg to convert uint32_t to double. + if (atype == TypedArray::TYPE_UINT32) + tempReg = frame.allocReg(); + } else { + dataReg = frame.allocReg(); + // loadFromTypedArray expects a type register for Uint32Array or + // float arrays. Also allocate a type register if the result may not + // be int32_t (due to reading out-of-bound values) or if there's a + // type barrier. + if (maybeReadFloat || type != JSVAL_TYPE_INT32) + typeReg = frame.allocReg(); + } + + // Load value from the array. + masm.loadFromTypedArray(atype, objReg, key, typeReg, dataReg, tempReg); + + if (hoisted) + frame.unpinReg(objReg); + else + frame.freeReg(objReg); + if (!key.isConstant()) + frame.unpinReg(key.reg()); + if (tempReg.isSet()) + frame.freeReg(tempReg.reg()); + + if (atype == TypedArray::TYPE_UINT32 && + !pushedTypes->hasType(types::Type::DoubleType())) { + Jump isDouble = masm.testDouble(Assembler::Equal, typeReg.reg()); + stubcc.linkExit(isDouble, Uses(2)); + } + + stubcc.leave(); + OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH); + testPushedType(REJOIN_FALLTHROUGH, -2); + + frame.popn(2); + + BarrierState barrier; + if (dataReg.isFPReg()) { + frame.pushDouble(dataReg.fpreg()); + } else if (typeReg.isSet()) { + frame.pushRegs(typeReg.reg(), dataReg.reg(), knownPushedType(0)); + } else { + JS_ASSERT(type == JSVAL_TYPE_INT32); + frame.pushTypedPayload(JSVAL_TYPE_INT32, dataReg.reg()); + } + stubcc.rejoin(Changes(2)); + + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); + + return true; +} +#endif /* JS_METHODJIT_TYPED_ARRAY */ + bool -mjit::Compiler::jsop_getelem(bool isCall) +mjit::Compiler::jsop_getelem() { FrameEntry *obj = frame.peek(-2); FrameEntry *id = frame.peek(-1); if (!IsCacheableGetElem(obj, id)) { - if (isCall) - jsop_callelem_slow(); - else - jsop_getelem_slow(); + jsop_getelem_slow(); + return true; + } + + // If the object is definitely an arguments object, a dense array or a typed array + // we can generate code directly without using an inline cache. + if (cx->typeInferenceEnabled() && !id->isType(JSVAL_TYPE_STRING)) { + types::TypeSet *types = analysis->poppedTypes(PC, 1); + if (types->isLazyArguments(cx) && !outerScript->analysis()->modifiesArguments()) { + // Inline arguments path. + jsop_getelem_args(); + return true; + } + + if (obj->mightBeType(JSVAL_TYPE_OBJECT) && + !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && + !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { + // Inline dense array path. + bool packed = !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); + jsop_getelem_dense(packed); + return true; + } + +#ifdef JS_METHODJIT_TYPED_ARRAY + if (obj->mightBeType(JSVAL_TYPE_OBJECT) && + !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) { + // Inline typed array path. + int atype = types->getTypedArrayType(cx); + if (atype != TypedArray::TYPE_MAX) { + if (jsop_getelem_typed(atype)) + return true; + // Fallthrough to the normal GETELEM path. + } + } +#endif + } + + frame.forgetMismatchedObject(obj); + + if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) { + jsop_getelem_slow(); return true; } @@ -1400,14 +2187,6 @@ mjit::Compiler::jsop_getelem(bool isCall) // Get a mutable register for the object. This will be the data reg. ic.objReg = frame.copyDataIntoReg(obj); - // For potential dense array calls, grab an extra reg to save the - // outgoing object. - MaybeRegisterID thisReg; - if (isCall && id->mightBeType(JSVAL_TYPE_INT32)) { - thisReg = frame.allocReg(); - masm.move(ic.objReg, thisReg.reg()); - } - // Get a mutable register for pushing the result type. We kill two birds // with one stone by making sure, if the key type is not known, to be loaded // into this register. In this case it is both an input and an output. @@ -1444,9 +2223,12 @@ mjit::Compiler::jsop_getelem(bool isCall) stubcc.linkExitDirect(ic.typeGuard.get(), ic.slowPathStart); } - // Guard on the clasp. - ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &js_ArrayClass); - stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart); + // Guard obj is a dense array. + Shape *shape = GetDenseArrayShape(cx, globalObj); + if (!shape) + return false; + ic.shapeGuard = masm.guardShape(ic.objReg, shape); + stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart); Int32Key key = id->isConstant() ? Int32Key::FromConstant(id->getValue().toInt32()) @@ -1455,21 +2237,13 @@ mjit::Compiler::jsop_getelem(bool isCall) Assembler::FastArrayLoadFails fails = masm.fastArrayLoad(ic.objReg, key, ic.typeReg, ic.objReg); - // Store the object back to sp[-1] for calls. This must occur after - // all guards because otherwise sp[-1] will be clobbered. - if (isCall) { - Address thisSlot = frame.addressOf(id); - masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), thisReg.reg(), thisSlot); - frame.freeReg(thisReg.reg()); - } - stubcc.linkExitDirect(fails.rangeCheck, ic.slowPathStart); stubcc.linkExitDirect(fails.holeCheck, ic.slowPathStart); } else { // The type is known to not be dense-friendly ahead of time, so always // fall back to a slow path. - ic.claspGuard = masm.jump(); - stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart); + ic.shapeGuard = masm.jump(); + stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart); } stubcc.leave(); @@ -1477,31 +2251,31 @@ mjit::Compiler::jsop_getelem(bool isCall) objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm); #ifdef JS_POLYIC passICAddress(&ic); - if (isCall) - ic.slowPathCall = OOL_STUBCALL(ic::CallElement); - else - ic.slowPathCall = OOL_STUBCALL(ic::GetElement); + ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH); #else - if (isCall) - ic.slowPathCall = OOL_STUBCALL(stubs::CallElem); - else - ic.slowPathCall = OOL_STUBCALL(stubs::GetElem); + ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH); #endif + testPushedType(REJOIN_FALLTHROUGH, -2); + ic.fastPathRejoin = masm.label(); + ic.forcedTypeBarrier = analysis->getCode(PC).getStringElement; + + CHECK_IC_SPACE(); frame.popn(2); - frame.pushRegs(ic.typeReg, ic.objReg); - if (isCall) - frame.pushSynced(); + frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0)); + BarrierState barrier = testBarrier(ic.typeReg, ic.objReg, false, false, + /* force = */ ic.forcedTypeBarrier); - stubcc.rejoin(Changes(2)); + stubcc.rejoin(Changes(1)); #ifdef JS_POLYIC if (!getElemICs.append(ic)) return false; #endif + finishBarrier(barrier, REJOIN_FALLTHROUGH, 0); return true; } @@ -1535,7 +2309,7 @@ mjit::Compiler::jsop_stricteq(JSOp op) /* Constant-fold. */ if (lhs->isConstant() && rhs->isConstant()) { - JSBool b; + bool b; StrictlyEqual(cx, lhs->getValue(), rhs->getValue(), &b); frame.popn(2); frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b)); @@ -1544,21 +2318,45 @@ mjit::Compiler::jsop_stricteq(JSOp op) if (frame.haveSameBacking(lhs, rhs)) { /* False iff NaN. */ + frame.pop(); + if (lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_DOUBLE)) { - frame.popn(2); + frame.pop(); frame.push(BooleanValue(op == JSOP_STRICTEQ)); return; } - + + if (lhs->isType(JSVAL_TYPE_DOUBLE)) + frame.forgetKnownDouble(lhs); + /* Assume NaN is either in canonical form or has the sign bit set (by jsop_neg). */ - RegisterID result = frame.allocReg(Registers::SingleByteRegs); + RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg(); RegisterID treg = frame.copyTypeIntoReg(lhs); Assembler::Condition oppositeCond = (op == JSOP_STRICTEQ) ? Assembler::NotEqual : Assembler::Equal; /* Ignore the sign bit. */ masm.lshiftPtr(Imm32(1), treg); -#ifndef JS_CPU_X64 +#ifdef JS_CPU_SPARC + /* On Sparc the result 0/0 is 0x7FFFFFFF not 0x7FF80000 */ + static const int ShiftedCanonicalNaNType1 = 0x7FFFFFFF << 1; + static const int ShiftedCanonicalNaNType2 = 0x7FF80000 << 1; + RegisterID result1 = frame.allocReg(); + masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType1), result1); + masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType2), result); + if(op == JSOP_STRICTEQ) { + masm.and32(result1, result); + } else { + masm.or32(result1, result); + } + frame.freeReg(result1); +#elif defined(JS_CPU_MIPS) + /* On MIPS the result 0.0/0.0 is 0x7FF7FFFF. + We need to manually set it to 0x7FF80000. */ + static const int ShiftedCanonicalNaNType = 0x7FF80000 << 1; + masm.setShiftedCanonicalNaN(treg, treg); + masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType), result); +#elif !defined(JS_CPU_X64) static const int ShiftedCanonicalNaNType = 0x7FF80000 << 1; masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType), result); #else @@ -1568,7 +2366,7 @@ mjit::Compiler::jsop_stricteq(JSOp op) #endif frame.freeReg(treg); - frame.popn(2); + frame.pop(); frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); return; } @@ -1578,16 +2376,17 @@ mjit::Compiler::jsop_stricteq(JSOp op) if ((lhsTest = ReallySimpleStrictTest(lhs)) || ReallySimpleStrictTest(rhs)) { FrameEntry *test = lhsTest ? rhs : lhs; FrameEntry *known = lhsTest ? lhs : rhs; + RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg(); if (test->isTypeKnown()) { + masm.move(Imm32((test->getKnownType() == known->getKnownType()) == + (op == JSOP_STRICTEQ)), result); frame.popn(2); - frame.push(BooleanValue((test->getKnownType() == known->getKnownType()) == - (op == JSOP_STRICTEQ))); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); return; } /* This is only true if the other side is |null|. */ - RegisterID result = frame.allocReg(Registers::SingleByteRegs); #ifndef JS_CPU_X64 JSValueTag mask = known->getKnownTag(); if (frame.shouldAvoidTypeRemat(test)) @@ -1613,8 +2412,11 @@ mjit::Compiler::jsop_stricteq(JSOp op) FrameEntry *test = lhsTest ? rhs : lhs; if (test->isTypeKnown() && test->isNotType(JSVAL_TYPE_BOOLEAN)) { + RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg(); frame.popn(2); - frame.push(BooleanValue(op == JSOP_STRICTNE)); + + masm.move(Imm32(op == JSOP_STRICTNE), result); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); return; } @@ -1630,7 +2432,7 @@ mjit::Compiler::jsop_stricteq(JSOp op) RegisterID result = data; if (!(Registers::maskReg(data) & Registers::SingleByteRegs)) - result = frame.allocReg(Registers::SingleByteRegs); + result = frame.allocReg(Registers::SingleByteRegs).reg(); Jump notBoolean; if (!test->isTypeKnown()) @@ -1655,18 +2457,104 @@ mjit::Compiler::jsop_stricteq(JSOp op) return; } + if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) { + FrameEntry *maybeNotStr = lhs->isType(JSVAL_TYPE_STRING) ? rhs : lhs; + + if (maybeNotStr->isNotType(JSVAL_TYPE_STRING)) { + frame.popn(2); + frame.push(BooleanValue(op == JSOP_STRICTNE)); + return; + } + + if (!maybeNotStr->isTypeKnown()) { + JS_ASSERT(!maybeNotStr->isConstant()); + Jump j = frame.testString(Assembler::NotEqual, maybeNotStr); + stubcc.linkExit(j, Uses(2)); + } + + FrameEntry *op1 = lhs->isConstant() ? rhs : lhs; + FrameEntry *op2 = lhs->isConstant() ? lhs : rhs; + JS_ASSERT(!op1->isConstant()); + + /* ReturnReg is safely usable with set32, since %ah can be accessed. */ + RegisterID resultReg = Registers::ReturnReg; + frame.takeReg(resultReg); + RegisterID tmpReg = frame.allocReg(); + RegisterID reg1 = frame.tempRegForData(op1); + frame.pinReg(reg1); + + RegisterID reg2; + if (op2->isConstant()) { + reg2 = frame.allocReg(); + JSString *str = op2->getValue().toString(); + JS_ASSERT(str->isAtom()); + masm.move(ImmPtr(str), reg2); + } else { + reg2 = frame.tempRegForData(op2); + frame.pinReg(reg2); + } + + JS_ASSERT(reg1 != resultReg); + JS_ASSERT(reg1 != tmpReg); + JS_ASSERT(reg2 != resultReg); + JS_ASSERT(reg2 != tmpReg); + + /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */ + JS_STATIC_ASSERT(JSString::ATOM_FLAGS == 0); + Imm32 atomMask(JSString::ATOM_MASK); + + masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg); + Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask); + stubcc.linkExit(op1NotAtomized, Uses(2)); + + if (!op2->isConstant()) { + masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg); + Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask); + stubcc.linkExit(op2NotAtomized, Uses(2)); + } + + masm.set32(cond, reg1, reg2, resultReg); + + frame.unpinReg(reg1); + if (op2->isConstant()) + frame.freeReg(reg2); + else + frame.unpinReg(reg2); + frame.freeReg(tmpReg); + + stubcc.leave(); + if (op == JSOP_STRICTEQ) + OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2)); + else + OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2)); + + frame.popn(2); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg); + + stubcc.rejoin(Changes(1)); + return; + } + + if (cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) + { + CompileStatus status = jsop_equality_obj_obj(op, NULL, JSOP_NOP); + if (status == Compile_Okay) return; + JS_ASSERT(status == Compile_Skipped); + } + /* Is it impossible that both Values are ints? */ if ((lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_INT32)) || (rhs->isTypeKnown() && rhs->isNotType(JSVAL_TYPE_INT32))) { prepareStubCall(Uses(2)); if (op == JSOP_STRICTEQ) - INLINE_STUBCALL(stubs::StrictEq); + INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2)); else - INLINE_STUBCALL(stubs::StrictNe); + INLINE_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2)); frame.popn(2); - frame.pushSyncedType(JSVAL_TYPE_BOOLEAN); + frame.pushSynced(JSVAL_TYPE_BOOLEAN); return; } @@ -1715,9 +2603,9 @@ mjit::Compiler::jsop_stricteq(JSOp op) if (needStub) { stubcc.leave(); if (op == JSOP_STRICTEQ) - OOL_STUBCALL(stubs::StrictEq); + OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2)); else - OOL_STUBCALL(stubs::StrictNe); + OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2)); } frame.popn(2); @@ -1730,12 +2618,12 @@ mjit::Compiler::jsop_stricteq(JSOp op) prepareStubCall(Uses(2)); if (op == JSOP_STRICTEQ) - INLINE_STUBCALL(stubs::StrictEq); + INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2)); else - INLINE_STUBCALL(stubs::StrictNe); + INLINE_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2)); frame.popn(2); - frame.pushSyncedType(JSVAL_TYPE_BOOLEAN); + frame.pushSynced(JSVAL_TYPE_BOOLEAN); return; #endif } @@ -1749,9 +2637,9 @@ mjit::Compiler::jsop_pos() if (top->getKnownType() <= JSVAL_TYPE_INT32) return; prepareStubCall(Uses(1)); - INLINE_STUBCALL(stubs::Pos); + INLINE_STUBCALL(stubs::Pos, REJOIN_POS); frame.pop(); - frame.pushSynced(); + frame.pushSynced(knownPushedType(0)); return; } @@ -1765,7 +2653,7 @@ mjit::Compiler::jsop_pos() stubcc.linkExit(j, Uses(1)); stubcc.leave(); - OOL_STUBCALL(stubs::Pos); + OOL_STUBCALL(stubs::Pos, REJOIN_POS); stubcc.rejoin(Changes(1)); } @@ -1779,11 +2667,11 @@ mjit::Compiler::jsop_initmethod() JSAtom *atom = script->getAtom(fullAtomIndex(PC)); /* Initializers with INITMETHOD are not fast yet. */ - JS_ASSERT(!obj->initializerObject()); + JS_ASSERT(!frame.extra(obj).initObject); prepareStubCall(Uses(2)); masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::InitMethod); + INLINE_STUBCALL(stubs::InitMethod, REJOIN_FALLTHROUGH); } void @@ -1793,30 +2681,30 @@ mjit::Compiler::jsop_initprop() FrameEntry *fe = frame.peek(-1); JSAtom *atom = script->getAtom(fullAtomIndex(PC)); - JSObject *baseobj = obj->initializerObject(); + JSObject *baseobj = frame.extra(obj).initObject; - if (!baseobj) { + if (!baseobj || monitored(PC)) { prepareStubCall(Uses(2)); masm.move(ImmPtr(atom), Registers::ArgReg1); - INLINE_STUBCALL(stubs::InitProp); + INLINE_STUBCALL(stubs::InitProp, REJOIN_FALLTHROUGH); return; } JSObject *holder; JSProperty *prop = NULL; #ifdef DEBUG - int res = + bool res = #endif - js_LookupPropertyWithFlags(cx, baseobj, ATOM_TO_JSID(atom), - JSRESOLVE_QUALIFIED, &holder, &prop); - JS_ASSERT(res >= 0 && prop && holder == baseobj); + LookupPropertyWithFlags(cx, baseobj, ATOM_TO_JSID(atom), + JSRESOLVE_QUALIFIED, &holder, &prop); + JS_ASSERT(res && prop && holder == baseobj); RegisterID objReg = frame.copyDataIntoReg(obj); - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Perform the store. */ Shape *shape = (Shape *) prop; - frame.storeTo(fe, Address(objReg, shape->slot * sizeof(Value))); + Address address = masm.objPropAddress(baseobj, objReg, shape->slot()); + frame.storeTo(fe, address); frame.freeReg(objReg); } @@ -1834,26 +2722,24 @@ mjit::Compiler::jsop_initelem() * cases, as well as those where INITELEM is used on an object initializer * or a non-fast array initializer. */ - if (!id->isConstant() || !obj->initializerArray()) { + if (!id->isConstant() || !frame.extra(obj).initArray) { JSOp next = JSOp(PC[JSOP_INITELEM_LENGTH]); prepareStubCall(Uses(3)); masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1); - INLINE_STUBCALL(stubs::InitElem); + INLINE_STUBCALL(stubs::InitElem, REJOIN_FALLTHROUGH); return; } - JS_ASSERT(id->getValue().isInt32()); - - if (fe->isConstant() && fe->getValue().isMagic(JS_ARRAY_HOLE)) { - /* The array already has the correct length, nothing to do. */ - return; - } + int32_t idx = id->getValue().toInt32(); RegisterID objReg = frame.copyDataIntoReg(obj); - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), objReg); + + /* Update the initialized length. */ + masm.store32(Imm32(idx + 1), Address(objReg, ObjectElements::offsetOfInitializedLength())); /* Perform the store. */ - frame.storeTo(fe, Address(objReg, id->getValue().toInt32() * sizeof(Value))); + frame.storeTo(fe, Address(objReg, idx * sizeof(Value))); frame.freeReg(objReg); } diff --git a/deps/mozjs/js/src/methodjit/FrameEntry.h b/deps/mozjs/js/src/methodjit/FrameEntry.h index 950bdedf84a..dead1d15873 100644 --- a/deps/mozjs/js/src/methodjit/FrameEntry.h +++ b/deps/mozjs/js/src/methodjit/FrameEntry.h @@ -41,6 +41,7 @@ #define jsjaeger_valueinfo_h__ #include "jsapi.h" +#include "jsnum.h" #include "jstypes.h" #include "methodjit/MachineRegs.h" #include "methodjit/RematInfo.h" @@ -55,7 +56,12 @@ class FrameEntry friend class ImmutableSync; public: + + /* Accessors for entries which are known constants. */ + bool isConstant() const { + if (isCopy()) + return false; return data.isConstant(); } @@ -64,27 +70,59 @@ class FrameEntry return v_; } - const Value &getValue() const { + Value getValue() const { + JS_ASSERT(isConstant()); + return IMPL_TO_JSVAL(v_); + } + +#if defined JS_NUNBOX32 + uint32_t getPayload() const { + JS_ASSERT(isConstant()); + return v_.s.payload.u32; + } +#elif defined JS_PUNBOX64 + uint64_t getPayload() const { JS_ASSERT(isConstant()); - return Valueify(JSVAL_FROM_LAYOUT(v_)); + return v_.asBits & JSVAL_PAYLOAD_MASK; + } +#endif + + /* For a constant double FrameEntry, truncate to an int32. */ + void convertConstantDoubleToInt32(JSContext *cx) { + JS_ASSERT(isType(JSVAL_TYPE_DOUBLE) && isConstant()); + int32_t value; + ToInt32(cx, getValue(), &value); + + Value newValue = Int32Value(value); + setConstant(newValue); } + /* + * Accessors for entries whose type is known. Any entry can have a known + * type, and constant entries must have one. + */ + bool isTypeKnown() const { - return type.isConstant(); + return backing()->type.isConstant(); } + /* + * The known type should not be used in generated code if it is JSVAL_TYPE_DOUBLE. + * In such cases either the value is constant, in memory or in a floating point register. + */ JSValueType getKnownType() const { JS_ASSERT(isTypeKnown()); - return knownType; + return backing()->knownType; } #if defined JS_NUNBOX32 JSValueTag getKnownTag() const { - return v_.s.tag; + JS_ASSERT(backing()->v_.s.tag != JSVAL_TAG_CLEAR); + return backing()->v_.s.tag; } #elif defined JS_PUNBOX64 JSValueShiftedTag getKnownTag() const { - return JSValueShiftedTag(v_.asBits & JSVAL_TAG_MASK); + return JSValueShiftedTag(backing()->v_.asBits & JSVAL_TAG_MASK); } #endif @@ -104,38 +142,22 @@ class FrameEntry return !isNotType(type_); } -#if defined JS_NUNBOX32 - uint32 getPayload() const { - //JS_ASSERT(!Valueify(v_.asBits).isDouble() || type.synced()); - return v_.s.payload.u32; - } -#elif defined JS_PUNBOX64 - uint64 getPayload() const { - return v_.asBits & JSVAL_PAYLOAD_MASK; - } -#endif - - bool isCachedNumber() const { - return isNumber; - } - - bool hasSameBacking(const FrameEntry *other) const { - return backing() == other->backing(); - } + /* Accessors for entries which are copies of other mutable entries. */ bool isCopy() const { return !!copy; } - bool isCopied() const { return copied; } + bool isCopied() const { return copied != 0; } - inline bool initializerArray() { - return initArray; + const FrameEntry *backing() const { + return isCopy() ? copyOf() : this; } - inline JSObject *initializerObject() { - return initObject; + bool hasSameBacking(const FrameEntry *other) const { + return backing() == other->backing(); } private: void setType(JSValueType type_) { + JS_ASSERT(!isCopy() && type_ != JSVAL_TYPE_UNKNOWN); type.setConstant(); #if defined JS_NUNBOX32 v_.s.tag = JSVAL_TYPE_TO_TAG(type_); @@ -144,22 +166,25 @@ class FrameEntry v_.asBits |= JSVAL_TYPE_TO_SHIFTED_TAG(type_); #endif knownType = type_; - JS_ASSERT(!isNumber); } - void track(uint32 index) { - clear(); + void track(uint32_t index) { + copied = 0; + copy = NULL; index_ = index; tracked = true; } void clear() { - copied = false; - copy = NULL; - isNumber = false; + JS_ASSERT(copied == 0); + if (copy) { + JS_ASSERT(copy->copied != 0); + copy->copied--; + copy = NULL; + } } - uint32 trackerIndex() { + uint32_t trackerIndex() { return index_; } @@ -170,10 +195,8 @@ class FrameEntry clear(); type.unsync(); data.unsync(); -#ifdef DEBUG type.invalidate(); data.invalidate(); -#endif } /* @@ -188,46 +211,36 @@ class FrameEntry /* * Marks the FE as having a constant. */ - void setConstant(const jsval &v) { + void setConstant(const Value &v) { clear(); type.unsync(); data.unsync(); type.setConstant(); data.setConstant(); - v_.asBits = JSVAL_BITS(v); - Value cv = Valueify(v); - if (cv.isDouble()) + v_ = JSVAL_TO_IMPL(v); + if (v.isDouble()) knownType = JSVAL_TYPE_DOUBLE; else - knownType = cv.extractNonDoubleType(); - } - - void setCopied() { - JS_ASSERT(!isCopy()); - copied = true; + knownType = v.extractNonDoubleType(); } FrameEntry *copyOf() const { JS_ASSERT(isCopy()); - JS_ASSERT(copy < this); + JS_ASSERT_IF(!copy->temporary, copy < this); return copy; } - const FrameEntry *backing() const { - return isCopy() ? copyOf() : this; - } - - void setNotCopied() { - copied = false; - } - /* * Set copy index. */ void setCopyOf(FrameEntry *fe) { - JS_ASSERT_IF(fe, !fe->isConstant()); - JS_ASSERT(!isCopied()); + clear(); copy = fe; + if (fe) { + type.invalidate(); + data.invalidate(); + fe->copied++; + } } inline bool isTracked() const { @@ -238,22 +251,31 @@ class FrameEntry tracked = false; } + inline bool dataInRegister(AnyRegisterID reg) const { + JS_ASSERT(!copy); + return reg.isReg() + ? (data.inRegister() && data.reg() == reg.reg()) + : (data.inFPRegister() && data.fpreg() == reg.fpreg()); + } + private: JSValueType knownType; jsval_layout v_; RematInfo type; RematInfo data; - uint32 index_; + uint32_t index_; FrameEntry *copy; - bool copied; - bool isNumber; bool tracked; - bool initArray; - JSObject *initObject; + bool temporary; -#if (JS_BITS_PER_WORD == 32) - void *padding; -#endif + /* Number of copies of this entry. */ + uint32_t copied; + + /* + * Offset of the last loop in which this entry was written or had a loop + * register assigned. + */ + uint32_t lastLoop; }; } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/FrameState-inl.h b/deps/mozjs/js/src/methodjit/FrameState-inl.h index 3010ecb17dc..196fbbc38a8 100644 --- a/deps/mozjs/js/src/methodjit/FrameState-inl.h +++ b/deps/mozjs/js/src/methodjit/FrameState-inl.h @@ -40,6 +40,8 @@ #if !defined jsjaeger_framestate_inl_h__ && defined JS_METHODJIT #define jsjaeger_framestate_inl_h__ +#include "methodjit/LoopState.h" + namespace js { namespace mjit { @@ -49,15 +51,14 @@ FrameState::addToTracker(FrameEntry *fe) JS_ASSERT(!fe->isTracked()); fe->track(tracker.nentries); tracker.add(fe); - JS_ASSERT(tracker.nentries <= feLimit()); } inline FrameEntry * -FrameState::peek(int32 depth) +FrameState::peek(int32_t depth) { JS_ASSERT(depth < 0); - JS_ASSERT(sp + depth >= spBase); - FrameEntry *fe = &sp[depth]; + JS_ASSERT(a->sp + depth >= a->spBase); + FrameEntry *fe = a->sp + depth; if (!fe->isTracked()) { addToTracker(fe); fe->resetSynced(); @@ -66,9 +67,9 @@ FrameState::peek(int32 depth) } inline void -FrameState::popn(uint32 n) +FrameState::popn(uint32_t n) { - for (uint32 i = 0; i < n; i++) + for (uint32_t i = 0; i < n; i++) pop(); } @@ -82,50 +83,91 @@ FrameState::haveSameBacking(FrameEntry *lhs, FrameEntry *rhs) return lhs == rhs; } -inline JSC::MacroAssembler::RegisterID -FrameState::allocReg() +inline FrameEntry * +FrameState::getTemporary(uint32_t which) { - RegisterID reg; - if (!freeRegs.empty()) { - reg = freeRegs.takeAnyReg(); - } else { - reg = evictSomeReg(); - regstate[reg].forget(); - } + JS_ASSERT(which < TEMPORARY_LIMIT); - return reg; + FrameEntry *fe = temporaries + which; + JS_ASSERT(fe < temporariesTop); + + return getOrTrack(uint32_t(fe - entries)); } -inline JSC::MacroAssembler::RegisterID -FrameState::allocReg(uint32 mask) +inline AnyRegisterID +FrameState::allocReg(uint32_t mask) { - RegisterID reg; if (freeRegs.hasRegInMask(mask)) { - reg = freeRegs.takeRegInMask(mask); - } else { - reg = evictSomeReg(mask); - regstate[reg].forget(); + AnyRegisterID reg = freeRegs.takeAnyReg(mask); + modifyReg(reg); + return reg; } + AnyRegisterID reg = evictSomeReg(mask); + modifyReg(reg); return reg; } inline JSC::MacroAssembler::RegisterID -FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type) +FrameState::allocReg() { - RegisterID reg; - if (!freeRegs.empty()) { - reg = freeRegs.takeAnyReg(); - } else { - reg = evictSomeReg(); - regstate[reg].forget(); + return allocReg(Registers::AvailRegs).reg(); +} + +inline JSC::MacroAssembler::FPRegisterID +FrameState::allocFPReg() +{ + return allocReg(Registers::AvailFPRegs).fpreg(); +} + +inline AnyRegisterID +FrameState::allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type) +{ + AnyRegisterID reg; + uint32_t mask = fp ? (uint32_t) Registers::AvailFPRegs : (uint32_t) Registers::AvailRegs; + + /* + * Decide whether to retroactively mark a register as holding the entry + * at the start of the current loop. We can do this if (a) the register has + * not been touched since the start of the loop (it is in loopRegs), (b) + * the entry has also not been written to or already had a loop register + * assigned, and (c) we are not in an inline call with multiple callees or + * exit points --- we won't pick up the new loop register when restoring. + */ + if (loop && freeRegs.hasRegInMask(loop->getLoopRegs() & mask) && + type == RematInfo::DATA && isOuterSlot(fe) && !cc.activeFrameHasMultipleExits() && + fe->lastLoop < loop->headOffset()) { + reg = freeRegs.takeAnyReg(loop->getLoopRegs() & mask); + regstate(reg).associate(fe, RematInfo::DATA); + fe->lastLoop = loop->headOffset(); + loop->setLoopReg(reg, fe); + return reg; } - regstate[reg].associate(fe, type); + if (!freeRegs.empty(mask)) + reg = freeRegs.takeAnyReg(mask); + else + reg = evictSomeReg(mask); + modifyReg(reg); + + if (fp) + masm.loadDouble(addressOf(fe), reg.fpreg()); + else if (type == RematInfo::TYPE) + masm.loadTypeTag(addressOf(fe), reg.reg()); + else + masm.loadPayload(addressOf(fe), reg.reg()); + regstate(reg).associate(fe, type); return reg; } +inline void +FrameState::modifyReg(AnyRegisterID reg) +{ + if (loop) + loop->clearLoopReg(reg); +} + inline void FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const { @@ -151,78 +193,78 @@ FrameState::peekTypeInRegister(FrameEntry *fe) const inline void FrameState::pop() { - JS_ASSERT(sp > spBase); + JS_ASSERT(a->sp > a->spBase); - FrameEntry *fe = --sp; + FrameEntry *fe = --a->sp; if (!fe->isTracked()) return; forgetAllRegs(fe); + fe->type.invalidate(); + fe->data.invalidate(); + fe->clear(); + + extraArray[fe - entries].reset(); } inline void -FrameState::freeReg(RegisterID reg) +FrameState::freeReg(AnyRegisterID reg) { - JS_ASSERT(!regstate[reg].usedBy()); + JS_ASSERT(!regstate(reg).usedBy()); freeRegs.putReg(reg); } inline void -FrameState::forgetReg(RegisterID reg) +FrameState::forgetReg(AnyRegisterID reg) { /* * Important: Do not touch the fe here. We can peephole optimize away * loads and stores by re-using the contents of old FEs. */ - JS_ASSERT_IF(regstate[reg].fe(), !regstate[reg].fe()->isCopy()); + JS_ASSERT_IF(regstate(reg).fe(), !regstate(reg).fe()->isCopy()); - if (!regstate[reg].isPinned()) { - regstate[reg].forget(); + if (!regstate(reg).isPinned()) { + regstate(reg).forget(); freeRegs.putReg(reg); } } -inline void -FrameState::syncAndForgetEverything(uint32 newStackDepth) -{ - syncAndForgetEverything(); - sp = spBase + newStackDepth; -} - inline FrameEntry * FrameState::rawPush() { - JS_ASSERT(unsigned(sp - entries) < feLimit()); + JS_ASSERT(a->sp < temporaries); + FrameEntry *fe = a->sp++; - if (!sp->isTracked()) - addToTracker(sp); + if (!fe->isTracked()) + addToTracker(fe); + fe->type.invalidate(); + fe->data.invalidate(); + fe->clear(); + + extraArray[fe - entries].reset(); - return sp++; + return fe; } inline void FrameState::push(const Value &v) { FrameEntry *fe = rawPush(); - fe->setConstant(Jsvalify(v)); + fe->setConstant(v); } inline void -FrameState::pushSynced() -{ - if (sp->isTracked()) - sp->resetSynced(); - sp++; -} - -inline void -FrameState::pushSyncedType(JSValueType type) +FrameState::pushSynced(JSValueType type) { FrameEntry *fe = rawPush(); fe->resetSynced(); - fe->setType(type); + if (type != JSVAL_TYPE_UNKNOWN) { + fe->setType(type); + if (type == JSVAL_TYPE_DOUBLE) + masm.ensureInMemoryDouble(addressOf(fe)); + } } inline void @@ -235,24 +277,32 @@ FrameState::pushSynced(JSValueType type, RegisterID reg) fe->data.sync(); fe->setType(type); fe->data.setRegister(reg); - regstate[reg].associate(fe, RematInfo::DATA); + regstate(reg).associate(fe, RematInfo::DATA); } inline void -FrameState::push(Address address) +FrameState::loadIntoRegisters(Address address, bool reuseBase, + RegisterID *ptypeReg, RegisterID *pdataReg) { + #ifdef JS_PUNBOX64 + // It's okay if either of these clobbers address.base, since we guarantee // eviction will not physically clobber. It's also safe, on x64, for // loadValueAsComponents() to take either type or data regs as address.base. RegisterID typeReg = allocReg(); - RegisterID dataReg = allocReg(); + RegisterID dataReg = reuseBase ? address.base : allocReg(); masm.loadValueAsComponents(address, typeReg, dataReg); + #elif JS_NUNBOX32 + // Prevent us from clobbering this reg. bool free = freeRegs.hasReg(address.base); + bool needsPin = !free && regstate(address.base).fe(); if (free) freeRegs.takeReg(address.base); + if (needsPin) + pinReg(address.base); RegisterID typeReg = allocReg(); @@ -263,31 +313,120 @@ FrameState::push(Address address) // writes to the register. if (free) freeRegs.putReg(address.base); + if (needsPin) + unpinReg(address.base); - RegisterID dataReg = allocReg(); + RegisterID dataReg = reuseBase ? address.base : allocReg(); masm.loadPayload(address, dataReg); + #endif - pushRegs(typeReg, dataReg); + *ptypeReg = typeReg; + *pdataReg = dataReg; +} + +inline void +FrameState::push(Address address, JSValueType knownType, bool reuseBase) +{ + if (knownType == JSVAL_TYPE_DOUBLE) { + FPRegisterID fpreg = allocFPReg(); + masm.moveInt32OrDouble(address, fpreg); + pushDouble(fpreg); + if (reuseBase) + freeReg(address.base); + return; + } + + if (knownType != JSVAL_TYPE_UNKNOWN) { + RegisterID dataReg = reuseBase ? address.base : allocReg(); + masm.loadPayload(address, dataReg); + pushTypedPayload(knownType, dataReg); + return; + } + + RegisterID typeReg, dataReg; + loadIntoRegisters(address, reuseBase, &typeReg, &dataReg); + + pushRegs(typeReg, dataReg, JSVAL_TYPE_UNKNOWN); } inline void -FrameState::pushRegs(RegisterID type, RegisterID data) +FrameState::pushWord(Address address, JSValueType knownType, bool reuseBase) { - JS_ASSERT(!freeRegs.hasReg(type) && !freeRegs.hasReg(data)); + JS_ASSERT(knownType != JSVAL_TYPE_DOUBLE); + JS_ASSERT(knownType != JSVAL_TYPE_UNKNOWN); - FrameEntry *fe = rawPush(); + RegisterID dataReg = reuseBase ? address.base : allocReg(); + masm.loadPtr(address, dataReg); + pushTypedPayload(knownType, dataReg); +} +inline JSC::MacroAssembler::FPRegisterID +FrameState::storeRegs(int32_t depth, RegisterID type, RegisterID data, JSValueType knownType) +{ + FrameEntry *fe = peek(depth); + forgetEntry(fe); fe->resetUnsynced(); - fe->type.setRegister(type); + + /* + * Even if the type or data gets freed due to knownType or a double result, + * neither register should be clobbered (see Compiler::testBarrier). + */ + JS_ASSERT(!freeRegs.hasReg(type) && !freeRegs.hasReg(data)); + + if (knownType == JSVAL_TYPE_UNKNOWN) { + fe->type.setRegister(type); + fe->data.setRegister(data); + regstate(type).associate(fe, RematInfo::TYPE); + regstate(data).associate(fe, RematInfo::DATA); + return Registers::FPConversionTemp; + } + + if (knownType == JSVAL_TYPE_DOUBLE) { + FPRegisterID fpreg = allocFPReg(); + masm.moveInt32OrDouble(data, type, addressOf(fe), fpreg); + fe->setType(JSVAL_TYPE_DOUBLE); + fe->data.setFPRegister(fpreg); + regstate(fpreg).associate(fe, RematInfo::DATA); + freeReg(type); + freeReg(data); + return fpreg; + } + + freeReg(type); + fe->setType(knownType); fe->data.setRegister(data); - regstate[type].associate(fe, RematInfo::TYPE); - regstate[data].associate(fe, RematInfo::DATA); + regstate(data).associate(fe, RematInfo::DATA); + return Registers::FPConversionTemp; +} + +inline JSC::MacroAssembler::FPRegisterID +FrameState::pushRegs(RegisterID type, RegisterID data, JSValueType knownType) +{ + pushSynced(JSVAL_TYPE_UNKNOWN); + return storeRegs(-1, type, data, knownType); +} + +inline void +FrameState::reloadEntry(Assembler &masm, Address address, FrameEntry *fe) +{ + if (fe->data.inRegister()) { + if (fe->type.inRegister()) { + masm.loadValueAsComponents(address, fe->type.reg(), fe->data.reg()); + } else { + JS_ASSERT(fe->isTypeKnown()); + masm.loadPayload(address, fe->data.reg()); + } + } else { + JS_ASSERT(fe->data.inFPRegister()); + masm.moveInt32OrDouble(address, fe->data.fpreg()); + } } inline void FrameState::pushTypedPayload(JSValueType type, RegisterID payload) { + JS_ASSERT(type != JSVAL_TYPE_DOUBLE); JS_ASSERT(!freeRegs.hasReg(payload)); FrameEntry *fe = rawPush(); @@ -295,18 +434,15 @@ FrameState::pushTypedPayload(JSValueType type, RegisterID payload) fe->resetUnsynced(); fe->setType(type); fe->data.setRegister(payload); - regstate[payload].associate(fe, RematInfo::DATA); + regstate(payload).associate(fe, RematInfo::DATA); } inline void -FrameState::pushNumber(MaybeRegisterID payload, bool asInt32) +FrameState::pushNumber(RegisterID payload, bool asInt32) { - JS_ASSERT_IF(payload.isSet(), !freeRegs.hasReg(payload.reg())); + JS_ASSERT(!freeRegs.hasReg(payload)); FrameEntry *fe = rawPush(); - fe->clear(); - - JS_ASSERT(!fe->isNumber); if (asInt32) { if (!fe->type.synced()) @@ -316,40 +452,22 @@ FrameState::pushNumber(MaybeRegisterID payload, bool asInt32) fe->type.setMemory(); } - fe->isNumber = true; - if (payload.isSet()) { - fe->data.unsync(); - fe->data.setRegister(payload.reg()); - regstate[payload.reg()].associate(fe, RematInfo::DATA); - } else { - fe->data.setMemory(); - } + fe->data.unsync(); + fe->data.setRegister(payload); + regstate(payload).associate(fe, RematInfo::DATA); } inline void FrameState::pushInt32(RegisterID payload) { FrameEntry *fe = rawPush(); - fe->clear(); - JS_ASSERT(!fe->isNumber); masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), addressOf(fe)); fe->type.setMemory(); - fe->isNumber = true; fe->data.unsync(); fe->data.setRegister(payload); - regstate[payload].associate(fe, RematInfo::DATA); -} - -inline void -FrameState::pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj) -{ - pushTypedPayload(JSVAL_TYPE_OBJECT, payload); - - FrameEntry *fe = peek(-1); - fe->initArray = array; - fe->initObject = baseobj; + regstate(payload).associate(fe, RematInfo::DATA); } inline void @@ -359,8 +477,6 @@ FrameState::pushUntypedPayload(JSValueType type, RegisterID payload) FrameEntry *fe = rawPush(); - fe->clear(); - masm.storeTypeTag(ImmType(type), addressOf(fe)); /* The forceful type sync will assert otherwise. */ @@ -369,10 +485,8 @@ FrameState::pushUntypedPayload(JSValueType type, RegisterID payload) #endif fe->type.setMemory(); fe->data.unsync(); - fe->setNotCopied(); - fe->setCopyOf(NULL); fe->data.setRegister(payload); - regstate[payload].associate(fe, RematInfo::DATA); + regstate(payload).associate(fe, RematInfo::DATA); } inline void @@ -380,8 +494,6 @@ FrameState::pushUntypedValue(const Value &v) { FrameEntry *fe = rawPush(); - fe->clear(); - masm.storeValue(v, addressOf(fe)); /* The forceful type sync will assert otherwise. */ @@ -391,14 +503,12 @@ FrameState::pushUntypedValue(const Value &v) fe->type.setMemory(); fe->data.unsync(); fe->data.setMemory(); - fe->setNotCopied(); - fe->setCopyOf(NULL); } inline JSC::MacroAssembler::RegisterID FrameState::tempRegForType(FrameEntry *fe, RegisterID fallback) { - JS_ASSERT(!regstate[fallback].fe()); + JS_ASSERT(!regstate(fallback).fe()); if (fe->isCopy()) fe = fe->copyOf(); @@ -427,16 +537,52 @@ FrameState::tempRegForType(FrameEntry *fe) /* :XXX: X86 */ - RegisterID reg = allocReg(fe, RematInfo::TYPE); - masm.loadTypeTag(addressOf(fe), reg); + RegisterID reg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg(); fe->type.setRegister(reg); return reg; } +inline void +FrameState::loadTypeIntoReg(const FrameEntry *fe, RegisterID reg) +{ + if (fe->isCopy()) + fe = fe->copyOf(); + + JS_ASSERT(!fe->type.isConstant()); + + if (fe->type.inRegister()) { + if (fe->type.reg() == reg) + return; + masm.move(fe->type.reg(), reg); + return; + } + + masm.loadTypeTag(addressOf(fe), reg); +} + +inline void +FrameState::loadDataIntoReg(const FrameEntry *fe, RegisterID reg) +{ + if (fe->isCopy()) + fe = fe->copyOf(); + + JS_ASSERT(!fe->data.isConstant()); + + if (fe->data.inRegister()) { + if (fe->data.reg() == reg) + return; + masm.move(fe->data.reg(), reg); + return; + } + + masm.loadPayload(addressOf(fe), reg); +} + inline JSC::MacroAssembler::RegisterID FrameState::tempRegForData(FrameEntry *fe) { - JS_ASSERT(!fe->data.isConstant()); + JS_ASSERT(!fe->isConstant()); + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); if (fe->isCopy()) fe = fe->copyOf(); @@ -444,37 +590,93 @@ FrameState::tempRegForData(FrameEntry *fe) if (fe->data.inRegister()) return fe->data.reg(); - RegisterID reg = allocReg(fe, RematInfo::DATA); - masm.loadPayload(addressOf(fe), reg); + RegisterID reg = allocAndLoadReg(fe, false, RematInfo::DATA).reg(); fe->data.setRegister(reg); return reg; } -inline JSC::MacroAssembler::RegisterID -FrameState::tempRegInMaskForData(FrameEntry *fe, uint32 mask) +inline void +FrameState::forgetMismatchedObject(FrameEntry *fe) +{ + if (fe->isNotType(JSVAL_TYPE_OBJECT)) { + if (fe->isCopied()) { + syncFe(fe); + uncopy(fe); + fe->resetSynced(); + } else { + syncAndForgetFe(fe); + } + fe->clear(); + } + + if (fe->isConstant()) { + RegisterID reg = allocReg(); + regstate(reg).associate(fe, RematInfo::DATA); + + masm.move(JSC::MacroAssembler::ImmPtr(&fe->getValue().toObject()), reg); + fe->data.setRegister(reg); + } +} + +inline JSC::MacroAssembler::FPRegisterID +FrameState::tempFPRegForData(FrameEntry *fe) { - JS_ASSERT(!fe->data.isConstant()); + JS_ASSERT(!fe->isConstant()); + JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE)); if (fe->isCopy()) fe = fe->copyOf(); - RegisterID reg; - if (fe->data.inRegister()) { - RegisterID old = fe->data.reg(); + JS_ASSERT(!fe->data.inRegister()); + + if (fe->data.inFPRegister()) + return fe->data.fpreg(); + + FPRegisterID reg = allocAndLoadReg(fe, true, RematInfo::DATA).fpreg(); + fe->data.setFPRegister(reg); + return reg; +} + +inline AnyRegisterID +FrameState::tempRegInMaskForData(FrameEntry *fe, uint32_t mask) +{ + JS_ASSERT(!fe->isConstant()); + JS_ASSERT_IF(fe->isType(JSVAL_TYPE_DOUBLE), !(mask & ~Registers::AvailFPRegs)); + JS_ASSERT_IF(!fe->isType(JSVAL_TYPE_DOUBLE), !(mask & ~Registers::AvailRegs)); + + if (fe->isCopy()) + fe = fe->copyOf(); + + AnyRegisterID reg; + if (fe->data.inRegister() || fe->data.inFPRegister()) { + AnyRegisterID old; + if (fe->data.inRegister()) + old = fe->data.reg(); + else + old = fe->data.fpreg(); if (Registers::maskReg(old) & mask) return old; /* Keep the old register pinned. */ - regstate[old].forget(); + regstate(old).forget(); reg = allocReg(mask); - masm.move(old, reg); + if (reg.isReg()) + masm.move(old.reg(), reg.reg()); + else + masm.moveDouble(old.fpreg(), reg.fpreg()); freeReg(old); } else { reg = allocReg(mask); - masm.loadPayload(addressOf(fe), reg); + if (reg.isReg()) + masm.loadPayload(addressOf(fe), reg.reg()); + else + masm.loadDouble(addressOf(fe), reg.fpreg()); } - regstate[reg].associate(fe, RematInfo::DATA); - fe->data.setRegister(reg); + regstate(reg).associate(fe, RematInfo::DATA); + if (reg.isReg()) + fe->data.setRegister(reg.reg()); + else + fe->data.setFPRegister(reg.fpreg()); return reg; } @@ -486,6 +688,8 @@ FrameState::tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) cons if (fe->isCopy()) fe = fe->copyOf(); + JS_ASSERT(!fe->data.inFPRegister()); + if (fe->data.inRegister()) { JS_ASSERT(fe->data.reg() != reg); return fe->data.reg(); @@ -498,24 +702,43 @@ FrameState::tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) cons inline bool FrameState::shouldAvoidTypeRemat(FrameEntry *fe) { - return fe->type.inMemory(); + return !fe->isCopy() && fe->type.inMemory(); } inline bool FrameState::shouldAvoidDataRemat(FrameEntry *fe) { - return fe->data.inMemory(); + return !fe->isCopy() && fe->data.inMemory(); } inline void FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const { -#if defined JS_PUNBOX64 Address to = addressOf(fe); const FrameEntry *backing = fe; if (fe->isCopy()) backing = fe->copyOf(); + if (backing->isType(JSVAL_TYPE_DOUBLE)) { + if (fe->data.synced()) { + /* Entries representing known doubles can't be partially synced. */ + JS_ASSERT(fe->type.synced()); + return; + } + if (backing->isConstant()) { + masm.storeValue(backing->getValue(), to); + } else if (backing->data.inFPRegister()) { + masm.storeDouble(backing->data.fpreg(), to); + } else { + /* Use a temporary so the entry can be synced without allocating a register. */ + JS_ASSERT(backing->data.inMemory() && backing != fe); + masm.loadDouble(addressOf(backing), Registers::FPConversionTemp); + masm.storeDouble(Registers::FPConversionTemp, to); + } + return; + } + +#if defined JS_PUNBOX64 /* If we can, sync the type and data in one go. */ if (!fe->data.synced() && !fe->type.synced()) { if (backing->isConstant()) @@ -604,10 +827,25 @@ FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const inline void FrameState::syncFe(FrameEntry *fe) { + if (fe->type.synced() && fe->data.synced()) + return; + FrameEntry *backing = fe; if (fe->isCopy()) backing = fe->copyOf(); + if (backing->isType(JSVAL_TYPE_DOUBLE)) { + if (!backing->isConstant()) + tempFPRegForData(backing); + ensureFeSynced(fe, masm); + + if (!fe->type.synced()) + fe->type.sync(); + if (!fe->data.synced()) + fe->data.sync(); + return; + } + bool needTypeReg = !fe->type.synced() && backing->type.inMemory(); bool needDataReg = !fe->data.synced() && backing->data.inMemory(); @@ -634,13 +872,13 @@ FrameState::syncFe(FrameEntry *fe) /* Get a register if necessary, without clobbering its pair. */ if (needTypeReg) { - if (backing->data.inRegister() && !regstate[backing->data.reg()].isPinned()) { + if (backing->data.inRegister() && !regstate(backing->data.reg()).isPinned()) { pairReg = backing->data.reg(); pinReg(backing->data.reg()); } tempRegForType(backing); } else if (needDataReg) { - if (backing->type.inRegister() && !regstate[backing->type.reg()].isPinned()) { + if (backing->type.inRegister() && !regstate(backing->type.reg()).isPinned()) { pairReg = backing->type.reg(); pinReg(backing->type.reg()); } @@ -660,9 +898,56 @@ FrameState::syncFe(FrameEntry *fe) #endif } +inline void +FrameState::syncAndForgetFe(FrameEntry *fe, bool markSynced) +{ + if (markSynced) { + if (!fe->type.synced()) + fe->type.sync(); + if (!fe->data.synced()) + fe->data.sync(); + } + + syncFe(fe); + forgetAllRegs(fe); + fe->type.setMemory(); + fe->data.setMemory(); +} + +inline JSC::MacroAssembler::Address +FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access, RegisterID reg) +{ + JS_ASSERT(access.script && access.nesting); + + masm.move(ImmPtr(access.basePointer()), reg); + masm.loadPtr(Address(reg), reg); + + return Address(reg, access.index * sizeof(Value)); +} + +inline JSC::MacroAssembler::Address +FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access) +{ + RegisterID reg = allocReg(); + return loadNameAddress(access, reg); +} + +inline void +FrameState::forgetLoopReg(FrameEntry *fe) +{ + /* + * Don't use a loop register for fe in the active loop, as its underlying + * representation may have changed since the start of the loop. + */ + if (loop) + fe->lastLoop = loop->headOffset(); +} + inline void FrameState::syncType(FrameEntry *fe) { + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); + FrameEntry *backing = fe; if (fe->isCopy()) backing = fe->copyOf(); @@ -679,6 +964,8 @@ FrameState::syncType(FrameEntry *fe) inline void FrameState::syncData(FrameEntry *fe) { + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); + FrameEntry *backing = fe; if (fe->isCopy()) backing = fe->copyOf(); @@ -692,6 +979,19 @@ FrameState::syncData(FrameEntry *fe) fe->data.sync(); } +inline void +FrameState::fakeSync(FrameEntry *fe) +{ + /* + * If a frame entry's value will no longer be used, we can mark it as being + * synced without actually performing the sync: the value is not observed. + */ + if (!fe->data.synced()) + fe->data.sync(); + if (!fe->type.synced()) + fe->type.sync(); +} + inline void FrameState::forgetType(FrameEntry *fe) { @@ -706,10 +1006,11 @@ FrameState::forgetType(FrameEntry *fe) /* * Likewise, storeLocal() may have set this FE, with a known type, * to be a copy of another FE, which has an unknown type. - * Just forget the type, since the backing is used in all cases. */ if (fe->isCopy()) { - fe->type.invalidate(); + syncFe(fe); + fe->clear(); + fe->resetSynced(); return; } @@ -718,30 +1019,116 @@ FrameState::forgetType(FrameEntry *fe) } inline void -FrameState::learnType(FrameEntry *fe, JSValueType type) +FrameState::learnType(FrameEntry *fe, JSValueType type, bool unsync) { + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); + JS_ASSERT(type != JSVAL_TYPE_UNKNOWN); + + if (fe->isCopy()) + fe = fe->copyOf(); + + if (type == JSVAL_TYPE_DOUBLE) + JS_ASSERT(!fe->data.inRegister()); + else + JS_ASSERT(!fe->data.inFPRegister()); + if (fe->type.inRegister()) forgetReg(fe->type.reg()); -#ifdef DEBUG - fe->isNumber = false; -#endif fe->setType(type); + if (unsync) + fe->type.unsync(); +} + +inline void +FrameState::learnType(FrameEntry *fe, JSValueType type, RegisterID data) +{ + JS_ASSERT(!fe->isCopied()); + JS_ASSERT(type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE); + + forgetAllRegs(fe); + fe->clear(); + + fe->type.setConstant(); + fe->knownType = type; + + fe->data.setRegister(data); + regstate(data).associate(fe, RematInfo::DATA); + + fe->data.unsync(); + fe->type.unsync(); +} + +inline int32_t +FrameState::frameOffset(const FrameEntry *fe, ActiveFrame *a) const +{ + /* + * The stored frame offsets for analysis temporaries are immediately above + * the script's normal slots (and will thus be clobbered should a C++ or + * scripted call push another frame). There must be enough room in the + * reserved stack space. + */ + JS_STATIC_ASSERT(StackSpace::STACK_JIT_EXTRA >= TEMPORARY_LIMIT); + + /* Note: fe == a->sp is allowed for addressOfTop */ + JS_ASSERT(fe >= a->callee_ && fe <= a->sp); + + if (fe >= a->locals) + return StackFrame::offsetOfFixed(uint32_t(fe - a->locals)); + if (fe >= a->args) + return StackFrame::offsetOfFormalArg(a->script->function(), uint32_t(fe - a->args)); + if (fe == a->this_) + return StackFrame::offsetOfThis(a->script->function()); + if (fe == a->callee_) + return StackFrame::offsetOfCallee(a->script->function()); + JS_NOT_REACHED("Bad fe"); + return 0; } inline JSC::MacroAssembler::Address FrameState::addressOf(const FrameEntry *fe) const { - int32 frameOffset = 0; - if (fe >= locals) - frameOffset = StackFrame::offsetOfFixed(uint32(fe - locals)); - else if (fe >= args) - frameOffset = StackFrame::offsetOfFormalArg(fun, uint32(fe - args)); - else if (fe == this_) - frameOffset = StackFrame::offsetOfThis(fun); - else if (fe == callee_) - frameOffset = StackFrame::offsetOfCallee(fun); - JS_ASSERT(frameOffset); - return Address(JSFrameReg, frameOffset); + if (isTemporary(fe)) { + /* + * Temporary addresses are common to the outermost loop, and are shared + * by all active frames. + */ + return Address(JSFrameReg, (loop->temporariesStart + fe - temporaries) * sizeof(Value)); + } + + ActiveFrame *na = a; + while (fe < na->callee_) + na = na->parent; + + int32_t offset = frameOffset(fe, na); + return Address(JSFrameReg, offset + (na->depth * sizeof(Value))); +} + +inline uint32_t +FrameState::frameSlot(ActiveFrame *a, const FrameEntry *fe) const +{ + if (isTemporary(fe)) + return fe - entries; + + JS_ASSERT(fe >= a->callee_ && fe < a->sp); + + if (fe >= a->locals) + return analyze::LocalSlot(a->script, fe - a->locals); + if (fe >= a->args) + return analyze::ArgSlot(fe - a->args); + if (fe == a->this_) + return analyze::ThisSlot(); + if (fe == a->callee_) + return analyze::CalleeSlot(); + JS_NOT_REACHED("Bad fe"); + return 0; +} + +inline JSC::MacroAssembler::Address +FrameState::addressForInlineReturn() +{ + if (a->callee_->isTracked()) + discardFe(a->callee_); + return addressOf(a->callee_); } inline JSC::MacroAssembler::Address @@ -798,6 +1185,14 @@ FrameState::testObject(Assembler::Condition cond, FrameEntry *fe) return masm.testObject(cond, tempRegForType(fe)); } +inline JSC::MacroAssembler::Jump +FrameState::testGCThing(FrameEntry *fe) +{ + if (shouldAvoidTypeRemat(fe)) + return masm.testGCThing(addressOf(fe)); + return masm.testGCThing(tempRegForType(fe)); +} + inline JSC::MacroAssembler::Jump FrameState::testDouble(Assembler::Condition cond, FrameEntry *fe) { @@ -826,7 +1221,7 @@ FrameState::testString(Assembler::Condition cond, FrameEntry *fe) } inline FrameEntry * -FrameState::getOrTrack(uint32 index) +FrameState::getOrTrack(uint32_t index) { FrameEntry *fe = &entries[index]; if (!fe->isTracked()) { @@ -837,71 +1232,79 @@ FrameState::getOrTrack(uint32 index) } inline FrameEntry * -FrameState::getLocal(uint32 slot) +FrameState::getStack(uint32_t slot) { - JS_ASSERT(slot < script->nslots); - return getOrTrack(uint32(&locals[slot] - entries)); + if (slot >= uint32_t(a->sp - a->spBase)) + return NULL; + return getOrTrack(uint32_t(a->spBase + slot - entries)); } inline FrameEntry * -FrameState::getArg(uint32 slot) +FrameState::getLocal(uint32_t slot) { - JS_ASSERT(slot < nargs); - return getOrTrack(uint32(&args[slot] - entries)); + JS_ASSERT(slot < a->script->nslots); + return getOrTrack(uint32_t(a->locals + slot - entries)); } inline FrameEntry * -FrameState::getThis() +FrameState::getArg(uint32_t slot) { - return getOrTrack(uint32(this_ - entries)); + JS_ASSERT(slot < a->script->function()->nargs); + return getOrTrack(uint32_t(a->args + slot - entries)); } inline FrameEntry * -FrameState::getCallee() +FrameState::getThis() { - // Callee can only be used in function code, and it's always an object. - JS_ASSERT(fun); - if (!callee_->isTracked()) { - addToTracker(callee_); - callee_->resetSynced(); - callee_->setType(JSVAL_TYPE_OBJECT); - } - return callee_; + return getOrTrack(uint32_t(a->this_ - entries)); } -inline void -FrameState::pinReg(RegisterID reg) +inline FrameEntry * +FrameState::getSlotEntry(uint32_t slot) { - regstate[reg].pin(); + JS_ASSERT(slot < analyze::TotalSlots(a->script)); + return getOrTrack(uint32_t(a->callee_ + slot - entries)); } -inline void -FrameState::unpinReg(RegisterID reg) +inline FrameEntry * +FrameState::getCallee() { - regstate[reg].unpin(); + // Callee can only be used in function code, and it's always an object. + JS_ASSERT(a->script->function()); + FrameEntry *fe = a->callee_; + if (!fe->isTracked()) { + addToTracker(fe); + fe->resetSynced(); + fe->setType(JSVAL_TYPE_OBJECT); + } + return fe; } inline void -FrameState::unpinKilledReg(RegisterID reg) +FrameState::unpinKilledReg(AnyRegisterID reg) { - regstate[reg].unpinUnsafe(); + regstate(reg).unpinUnsafe(); freeRegs.putReg(reg); } inline void FrameState::forgetAllRegs(FrameEntry *fe) { + if (fe->isCopy()) + return; if (fe->type.inRegister()) forgetReg(fe->type.reg()); if (fe->data.inRegister()) forgetReg(fe->data.reg()); + if (fe->data.inFPRegister()) + forgetReg(fe->data.fpreg()); } inline void FrameState::swapInTracker(FrameEntry *lhs, FrameEntry *rhs) { - uint32 li = lhs->trackerIndex(); - uint32 ri = rhs->trackerIndex(); + uint32_t li = lhs->trackerIndex(); + uint32_t ri = rhs->trackerIndex(); JS_ASSERT(tracker[li] == lhs); JS_ASSERT(tracker[ri] == rhs); tracker.entries[ri] = lhs; @@ -921,24 +1324,32 @@ FrameState::dup2() { FrameEntry *lhs = peek(-2); FrameEntry *rhs = peek(-1); - pushCopyOf(indexOfFe(lhs)); - pushCopyOf(indexOfFe(rhs)); + pushCopyOf(lhs); + pushCopyOf(rhs); } inline void -FrameState::dupAt(int32 n) +FrameState::dupAt(int32_t n) { JS_ASSERT(n < 0); FrameEntry *fe = peek(n); - pushCopyOf(indexOfFe(fe)); + pushCopyOf(fe); } inline void -FrameState::pushLocal(uint32 n) +FrameState::syncAt(int32_t n) +{ + JS_ASSERT(n < 0); + FrameEntry *fe = peek(n); + syncFe(fe); +} + +inline void +FrameState::pushLocal(uint32_t n) { FrameEntry *fe = getLocal(n); - if (!isClosedVar(n)) { - pushCopyOf(indexOfFe(fe)); + if (!a->analysis->slotEscapes(analyze::LocalSlot(a->script, n))) { + pushCopyOf(fe); } else { #ifdef DEBUG /* @@ -946,31 +1357,29 @@ FrameState::pushLocal(uint32 n) * SETLOCAL equivocation of stack slots, and let expressions, just * weakly assert on the fixed local vars. */ - FrameEntry *fe = &locals[n]; - if (fe->isTracked() && n < script->nfixed) { - JS_ASSERT(fe->type.inMemory()); + if (fe->isTracked() && n < a->script->nfixed) JS_ASSERT(fe->data.inMemory()); - } #endif - push(addressOf(fe)); + if (n >= a->script->nfixed) + syncFe(fe); + JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN; + push(addressOf(fe), type); } } inline void -FrameState::pushArg(uint32 n) +FrameState::pushArg(uint32_t n) { FrameEntry *fe = getArg(n); - if (!isClosedArg(n)) { - pushCopyOf(indexOfFe(fe)); + if (!a->analysis->slotEscapes(analyze::ArgSlot(n))) { + pushCopyOf(fe); } else { #ifdef DEBUG - FrameEntry *fe = &args[n]; - if (fe->isTracked()) { - JS_ASSERT(fe->type.inMemory()); + if (fe->isTracked()) JS_ASSERT(fe->data.inMemory()); - } #endif - push(addressOf(fe)); + JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN; + push(addressOf(fe), type); } } @@ -978,65 +1387,70 @@ inline void FrameState::pushCallee() { FrameEntry *fe = getCallee(); - pushCopyOf(indexOfFe(fe)); + pushCopyOf(fe); } inline void FrameState::pushThis() { FrameEntry *fe = getThis(); - pushCopyOf(indexOfFe(fe)); + pushCopyOf(fe); } void -FrameState::learnThisIsObject() +FrameState::learnThisIsObject(bool unsync) { - // This is safe, albeit hacky. This is only called from the compiler, - // and only on the first use of |this| inside a basic block. Thus, - // there are no copies of |this| anywhere. - learnType(this_, JSVAL_TYPE_OBJECT); + // If the 'this' object is a copy, this must be an inline frame, in which + // case we will trigger recompilation if the 'this' entry isn't actually + // an object (thus, it is OK to modify the backing directly). + FrameEntry *fe = getThis(); + if (fe->isCopy()) + fe = fe->copyOf(); + learnType(fe, JSVAL_TYPE_OBJECT, unsync); } -inline void -FrameState::leaveBlock(uint32 n) +void +FrameState::setThis(RegisterID reg) { - popn(n); + FrameEntry *fe = getThis(); + JS_ASSERT(!fe->isCopy()); + learnType(fe, JSVAL_TYPE_OBJECT, reg); } -inline void -FrameState::enterBlock(uint32 n) +void +FrameState::syncThis() { - /* expect that tracker has 0 entries, for now. */ - JS_ASSERT(!tracker.nentries); - JS_ASSERT(uint32(sp + n - locals) <= script->nslots); + FrameEntry *fe = getThis(); + syncFe(fe); +} - if (!eval) - memset(&closedVars[uint32(sp - locals)], 0, n * sizeof(*closedVars)); - sp += n; +inline bool +FrameState::isConstructorThis(const FrameEntry *fe) const +{ + return isThis(fe) && cc.constructing(); } inline void -FrameState::eviscerate(FrameEntry *fe) +FrameState::leaveBlock(uint32_t n) { - forgetAllRegs(fe); - fe->type.invalidate(); - fe->data.invalidate(); - fe->setNotCopied(); - fe->setCopyOf(NULL); + popn(n); } inline void -FrameState::setClosedVar(uint32 slot) +FrameState::enterBlock(uint32_t n) { - if (!eval) - closedVars[slot] = true; + /* expect that tracker has 0 entries, for now. */ + JS_ASSERT(!tracker.nentries); + JS_ASSERT(uint32_t(a->sp + n - a->locals) <= a->script->nslots); + + a->sp += n; } inline void -FrameState::setClosedArg(uint32 slot) +FrameState::eviscerate(FrameEntry *fe) { - if (!eval && !usesArguments) - closedArgs[slot] = true; + forgetAllRegs(fe); + fe->resetUnsynced(); } inline StateRemat @@ -1069,18 +1483,18 @@ FrameState::giveOwnRegs(FrameEntry *fe) } else { RegisterID type = copyTypeIntoReg(fe); pop(); - pushRegs(type, data); + pushRegs(type, data, JSVAL_TYPE_UNKNOWN); } } inline void -FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpReg, +FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpreg, Assembler &masm) const { #ifdef JS_CPU_X86 - masm.fastLoadDouble(d, t, fpReg); + masm.fastLoadDouble(d, t, fpreg); #else - loadDouble(fe, fpReg, masm); + loadDouble(fe, fpreg, masm); #endif } @@ -1088,7 +1502,7 @@ inline bool FrameState::tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const { #ifdef JS_CPU_X86 - if (fe->type.inRegister() && fe->data.inRegister()) { + if (!fe->isCopy() && fe->type.inRegister() && fe->data.inRegister()) { masm.fastLoadDouble(fe->data.reg(), fe->type.reg(), fpReg); return true; } @@ -1101,39 +1515,18 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons { if (fe->isCopy()) { FrameEntry *backing = fe->copyOf(); - if (tryFastDoubleLoad(fe, fpReg, masm)) - return; - if (backing->isCachedNumber() || (backing->type.synced() && backing->data.synced())) { - masm.loadDouble(addressOf(backing), fpReg); + if (tryFastDoubleLoad(fe, fpReg, masm)) return; - } fe = backing; } if (tryFastDoubleLoad(fe, fpReg, masm)) return; - if ((fe->type.synced() && fe->data.synced()) || fe->isCachedNumber()) { - masm.loadDouble(addressOf(fe), fpReg); - return; - } - ensureFeSynced(fe, masm); masm.loadDouble(addressOf(fe), fpReg); } -inline bool -FrameState::isClosedVar(uint32 slot) -{ - return eval || closedVars[slot]; -} - -inline bool -FrameState::isClosedArg(uint32 slot) -{ - return eval || usesArguments || closedArgs[slot]; -} - class PinRegAcrossSyncAndKill { typedef JSC::MacroAssembler::RegisterID RegisterID; diff --git a/deps/mozjs/js/src/methodjit/FrameState.cpp b/deps/mozjs/js/src/methodjit/FrameState.cpp index 5dbc015902a..a9b24b677d4 100644 --- a/deps/mozjs/js/src/methodjit/FrameState.cpp +++ b/deps/mozjs/js/src/methodjit/FrameState.cpp @@ -39,184 +39,506 @@ #include "jscntxt.h" #include "FrameState.h" #include "FrameState-inl.h" +#include "StubCompiler.h" using namespace js; using namespace js::mjit; +using namespace js::analyze; /* Because of Value alignment */ JS_STATIC_ASSERT(sizeof(FrameEntry) % 8 == 0); -FrameState::FrameState(JSContext *cx, JSScript *script, JSFunction *fun, Assembler &masm) - : cx(cx), script(script), fun(fun), - nargs(fun ? fun->nargs : 0), - masm(masm), entries(NULL), -#if defined JS_NUNBOX32 - reifier(cx, *thisFromCtor()), -#endif - closedVars(NULL), - closedArgs(NULL), - usesArguments(script->usesArguments), - inTryBlock(false) +FrameState::FrameState(JSContext *cx, mjit::Compiler &cc, + Assembler &masm, StubCompiler &stubcc) + : cx(cx), + masm(masm), cc(cc), stubcc(stubcc), + a(NULL), entries(NULL), nentries(0), freeRegs(Registers::AvailAnyRegs), + loop(NULL), inTryBlock(false) { } FrameState::~FrameState() { + while (a) { + ActiveFrame *parent = a->parent; + a->script->analysis()->clearAllocations(); + cx->free_(a); + a = parent; + } cx->free_(entries); } -bool -FrameState::init() +void +FrameState::pruneDeadEntries() { - // nslots + nargs + 2 (callee, this) - uint32 nentries = feLimit(); - if (!nentries) { - sp = spBase = locals = args = NULL; - return true; + unsigned shift = 0; + for (unsigned i = 0; i < tracker.nentries; i++) { + FrameEntry *fe = tracker[i]; + if (deadEntry(fe)) { + fe->untrack(); + shift++; + } else if (shift) { + fe->index_ -= shift; + tracker.entries[fe->index_] = fe; + } } + tracker.nentries -= shift; +} + +bool +FrameState::pushActiveFrame(JSScript *script, uint32_t argc) +{ + if (!a) { + this->nentries = analyze::TotalSlots(script) + (script->nslots - script->nfixed) + + StackSpace::STACK_JIT_EXTRA - VALUES_PER_STACK_FRAME; + size_t totalBytes = sizeof(FrameEntry) * nentries + // entries[] + sizeof(FrameEntry *) * nentries + // tracker.entries + sizeof(StackEntryExtra) * nentries; // extraArray + uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(totalBytes); + if (!cursor) + return false; - eval = script->usesEval || cx->compartment->debugMode; + this->entries = (FrameEntry *) cursor; + cursor += sizeof(FrameEntry) * nentries; - size_t totalBytes = sizeof(FrameEntry) * nentries + // entries[], w/ callee+this - sizeof(FrameEntry *) * nentries + // tracker.entries - (eval - ? 0 - : sizeof(JSPackedBool) * script->nslots) + // closedVars[] - (eval || usesArguments - ? 0 - : sizeof(JSPackedBool) * nargs); // closedArgs[] + this->tracker.entries = (FrameEntry **)cursor; + cursor += sizeof(FrameEntry *) * nentries; - uint8 *cursor = (uint8 *)cx->calloc_(totalBytes); - if (!cursor) - return false; + this->extraArray = (StackEntryExtra *)cursor; + cursor += sizeof(StackEntryExtra) * nentries; + + JS_ASSERT(reinterpret_cast(this->entries) + totalBytes == cursor); #if defined JS_NUNBOX32 - if (!reifier.init(nentries)) - return false; + if (!reifier.init(cx, *this, nentries)) + return false; #endif - entries = (FrameEntry *)cursor; - cursor += sizeof(FrameEntry) * nentries; + this->temporaries = this->temporariesTop = this->entries + nentries - TEMPORARY_LIMIT; + } - callee_ = entries; - this_ = entries + 1; - args = entries + 2; - locals = args + nargs; - spBase = locals + script->nfixed; - sp = spBase; + /* We should have already checked that argc == nargs */ + JS_ASSERT_IF(a, argc == script->function()->nargs); - tracker.entries = (FrameEntry **)cursor; - cursor += sizeof(FrameEntry *) * nentries; + ActiveFrame *newa = OffTheBooks::new_(); + if (!newa) + return false; - if (!eval) { - if (script->nslots) { - closedVars = (JSPackedBool *)cursor; - cursor += sizeof(JSPackedBool) * script->nslots; - } - if (!usesArguments && nargs) { - closedArgs = (JSPackedBool *)cursor; - cursor += sizeof(JSPackedBool) * nargs; - } - } + newa->parent = a; + newa->depth = a ? (totalDepth() + VALUES_PER_STACK_FRAME) : 0; + + newa->script = script; + newa->PC = script->code; + newa->analysis = script->analysis(); + + /* + * The callee/this/args in the new frame reuse the same entries as are on + * the stack in the old frame. + */ + FrameEntry *entriesStart = a ? a->sp - (argc + 2) : entries; + newa->callee_ = entriesStart + analyze::CalleeSlot(); + newa->this_ = entriesStart + analyze::ThisSlot(); + newa->args = entriesStart + analyze::ArgSlot(0); + newa->locals = entriesStart + analyze::LocalSlot(script, 0); + newa->spBase = entriesStart + analyze::TotalSlots(script); + newa->sp = newa->spBase; - JS_ASSERT(reinterpret_cast(entries) + totalBytes == cursor); + this->a = newa; return true; } void -FrameState::takeReg(RegisterID reg) +FrameState::associateReg(FrameEntry *fe, RematInfo::RematType type, AnyRegisterID reg) { + freeRegs.takeReg(reg); + + if (type == RematInfo::TYPE) + fe->type.setRegister(reg.reg()); + else if (reg.isReg()) + fe->data.setRegister(reg.reg()); + else + fe->data.setFPRegister(reg.fpreg()); + regstate(reg).associate(fe, type); +} + +void +FrameState::popActiveFrame() +{ + a->analysis->clearAllocations(); + + if (a->parent) { + /* Clear registers and copies used by local variables and stack slots. */ + for (FrameEntry *fe = a->sp - 1; fe >= a->locals; fe--) { + if (!fe->isTracked()) + continue; + forgetAllRegs(fe); + fe->clear(); + } + } + + ActiveFrame *parent = a->parent; + cx->delete_(a); + a = parent; +} + +void +FrameState::takeReg(AnyRegisterID reg) +{ + modifyReg(reg); if (freeRegs.hasReg(reg)) { freeRegs.takeReg(reg); - JS_ASSERT(!regstate[reg].usedBy()); + JS_ASSERT(!regstate(reg).usedBy()); } else { - JS_ASSERT(regstate[reg].fe()); + JS_ASSERT(regstate(reg).fe()); evictReg(reg); - regstate[reg].forget(); } } +#ifdef DEBUG +const char * +FrameState::entryName(const FrameEntry *fe) const +{ + static char bufs[4][50]; + static unsigned which = 0; + which = (which + 1) & 3; + char *buf = bufs[which]; + + if (isTemporary(fe)) { + JS_snprintf(buf, 50, "temp%d", fe - temporaries); + return buf; + } + + if (fe < a->callee_) + return "parent"; + + JS_ASSERT(fe >= a->callee_ && fe < a->sp); + + if (fe == a->callee_) + return "callee"; + if (fe == a->this_) + return "'this'"; + + if (isArg(fe)) + JS_snprintf(buf, 50, "arg%d", fe - a->args); + else if (isLocal(fe)) + JS_snprintf(buf, 50, "local%d", fe - a->locals); + else + JS_snprintf(buf, 50, "slot%d", fe - a->spBase); + return buf; +} +#endif + void -FrameState::evictReg(RegisterID reg) +FrameState::evictReg(AnyRegisterID reg) { - FrameEntry *fe = regstate[reg].fe(); + FrameEntry *fe = regstate(reg).fe(); + + JaegerSpew(JSpew_Regalloc, "evicting %s from %s\n", entryName(fe), reg.name()); - if (regstate[reg].type() == RematInfo::TYPE) { - ensureTypeSynced(fe, masm); + if (regstate(reg).type() == RematInfo::TYPE) { + syncType(fe); fe->type.setMemory(); + } else if (reg.isReg()) { + syncData(fe); + fe->data.setMemory(); } else { - ensureDataSynced(fe, masm); + syncFe(fe); fe->data.setMemory(); } + + regstate(reg).forget(); } -JSC::MacroAssembler::RegisterID -FrameState::evictSomeReg(uint32 mask) +inline Lifetime * +FrameState::variableLive(FrameEntry *fe, jsbytecode *pc) const { -#ifdef DEBUG - bool fallbackSet = false; -#endif - RegisterID fallback = Registers::ReturnReg; + /* + * Whether an argument, local or 'this' entry is live at pc. Note: this + * does not account for the 'this' entry when the script is used as a + * constructor, in which case it is live the entire frame. + */ + JS_ASSERT(cx->typeInferenceEnabled()); + JS_ASSERT(fe > a->callee_ && fe < a->spBase); - for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { - RegisterID reg = RegisterID(i); + uint32_t offset = pc - a->script->code; + return a->analysis->liveness(entrySlot(fe)).live(offset); +} + +AnyRegisterID +FrameState::bestEvictReg(uint32_t mask, bool includePinned) const +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + /* Must be looking for a specific type of register. */ + JS_ASSERT((mask & Registers::AvailRegs) != (mask & Registers::AvailFPRegs)); + + AnyRegisterID fallback; + uint32_t fallbackOffset = UINT32_MAX; + + JaegerSpew(JSpew_Regalloc, "picking best register to evict:\n"); + + for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); /* Register is not allocatable, don't bother. */ if (!(Registers::maskReg(reg) & mask)) continue; /* Register is not owned by the FrameState. */ - FrameEntry *fe = regstate[i].fe(); + FrameEntry *fe = includePinned ? regstate(reg).usedBy() : regstate(reg).fe(); if (!fe) continue; - /* Try to find a candidate... that doesn't need spilling. */ -#ifdef DEBUG - fallbackSet = true; -#endif - fallback = reg; + /* + * Liveness is not tracked for the callee or for stack slot frame entries. + * The callee is evicted as early as needed, stack slots are evicted as + * late as possible. :XXX: This is unfortunate if the stack slot lives + * a long time (especially if it gets spilled anyways when we hit a branch). + */ - if (regstate[i].type() == RematInfo::TYPE && fe->type.synced()) { - fe->type.setMemory(); - return fallback; + if (fe == a->callee_) { + JaegerSpew(JSpew_Regalloc, "result: %s is callee\n", reg.name()); + return reg; } - if (regstate[i].type() == RematInfo::DATA && fe->data.synced()) { - fe->data.setMemory(); - return fallback; + + if (fe >= a->spBase && !isTemporary(fe)) { + if (!fallback.isSet()) { + fallback = reg; + fallbackOffset = 0; + } + JaegerSpew(JSpew_Regalloc, " %s is on stack\n", reg.name()); + continue; + } + + /* Prioritize keeping copied entries in registers. */ + if (fe->isCopied()) { + if (!fallback.isSet()) { + fallback = reg; + fallbackOffset = 0; + } + JaegerSpew(JSpew_Regalloc, " %s has copies\n", reg.name()); + continue; + } + + if (isTemporary(fe) || (a->parent && fe < a->locals)) { + /* + * All temporaries we currently generate are for loop invariants, + * which we treat as being live everywhere within the loop. + * Additionally, if this is an inlined frame then any entries + * belonging to parents are treated as live everywhere in the call. + */ + uint32_t offset = a->parent ? a->script->length : loop->backedgeOffset(); + if (!fallback.isSet() || offset > fallbackOffset) { + fallback = reg; + fallbackOffset = offset; + } + JaegerSpew(JSpew_Regalloc, " %s is a LICM or inline parent entry\n", reg.name()); + continue; + } + + /* + * All entries still in registers should have a lifetime, except 'this' + * in constructors which are not accessed later on. + */ + Lifetime *lifetime = variableLive(fe, a->PC); + + if (!lifetime) { + JS_ASSERT(isConstructorThis(fe)); + fallback = reg; + fallbackOffset = a->script->length; + JaegerSpew(JSpew_Regalloc, " %s is 'this' in a constructor\n", reg.name()); + continue; + } + + /* + * Evict variables which are only live in future loop iterations, and are + * not carried around the loop in a register. + */ + if (lifetime->loopTail && (!loop || !loop->carriesLoopReg(fe))) { + JaegerSpew(JSpew_Regalloc, "result: %s (%s) only live in later iterations\n", + entryName(fe), reg.name()); + return reg; + } + + JaegerSpew(JSpew_Regalloc, " %s (%s): %u\n", entryName(fe), reg.name(), lifetime->end); + + /* + * The best live register to evict is the one that will be live for the + * longest time. This may need tweaking for variables that are used in + * many places throughout their lifetime. Note that we don't pay attention + * to whether the register is synced or not --- it is more efficient to + * have things in registers when they're needed than to emit some extra + * writes for things that won't be used again for a while. + */ + + if (!fallback.isSet() || lifetime->end > fallbackOffset) { + fallback = reg; + fallbackOffset = lifetime->end; } } - JS_ASSERT(fallbackSet); + JS_ASSERT(fallback.isSet()); - evictReg(fallback); + JaegerSpew(JSpew_Regalloc, "result %s\n", fallback.name()); return fallback; } - void -FrameState::syncAndForgetEverything() +FrameState::evictDeadEntries(bool includePinned) +{ + for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + + /* Follow along with the same filters as bestEvictReg. */ + + if (!(Registers::maskReg(reg) & Registers::AvailAnyRegs)) + continue; + + FrameEntry *fe = includePinned ? regstate(reg).usedBy() : regstate(reg).fe(); + if (!fe) + continue; + + if (fe == a->callee_ || isConstructorThis(fe) || + fe >= a->spBase || fe->isCopied() || (a->parent && fe < a->locals)) { + continue; + } + + Lifetime *lifetime = variableLive(fe, a->PC); + if (lifetime) + continue; + + /* + * If we are about to fake sync for an entry with known type, reset + * that type. We don't want to regard it as correctly synced later. + */ + if (!fe->type.synced() && fe->isTypeKnown()) + fe->type.setMemory(); + + /* + * Mark the entry as synced to avoid emitting a store, we don't need + * to keep this value around. + */ + fakeSync(fe); + if (regstate(reg).type() == RematInfo::DATA) + fe->data.setMemory(); + else + fe->type.setMemory(); + forgetReg(reg); + } +} + +AnyRegisterID +FrameState::evictSomeReg(uint32_t mask) { - syncAndKill(Registers(Registers::AvailRegs), Uses(frameSlots())); - forgetEverything(); + JS_ASSERT(!freeRegs.hasRegInMask(mask)); + + if (cx->typeInferenceEnabled()) { + evictDeadEntries(false); + + if (freeRegs.hasRegInMask(mask)) { + /* There was a register in use by a dead local variable. */ + AnyRegisterID reg = freeRegs.takeAnyReg(mask); + modifyReg(reg); + return reg; + } + + AnyRegisterID reg = bestEvictReg(mask, false); + evictReg(reg); + return reg; + } + + /* With inference disabled, only general purpose registers are managed. */ + JS_ASSERT((mask & ~Registers::AvailRegs) == 0); + + MaybeRegisterID fallback; + + for (uint32_t i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { + RegisterID reg = RegisterID(i); + + /* Register is not allocatable, don't bother. */ + if (!(Registers::maskReg(reg) & mask)) + continue; + + /* Register is not owned by the FrameState. */ + FrameEntry *fe = regstate(reg).fe(); + if (!fe) + continue; + + /* Try to find a candidate... that doesn't need spilling. */ + fallback = reg; + + if (regstate(reg).type() == RematInfo::TYPE && fe->type.synced()) { + fe->type.setMemory(); + regstate(reg).forget(); + return reg; + } + if (regstate(reg).type() == RematInfo::DATA && fe->data.synced()) { + fe->data.setMemory(); + regstate(reg).forget(); + return reg; + } + } + + evictReg(fallback.reg()); + return fallback.reg(); } void FrameState::resetInternalState() { - for (uint32 i = 0; i < tracker.nentries; i++) + for (uint32_t i = 0; i < tracker.nentries; i++) tracker[i]->untrack(); tracker.reset(); - freeRegs.reset(); + freeRegs = Registers(Registers::AvailAnyRegs); } void FrameState::discardFrame() { resetInternalState(); + PodArrayZero(regstate_); +} + +FrameEntry * +FrameState::snapshotState() +{ + /* Everything can be recovered from a copy of the frame entries. */ + FrameEntry *snapshot = cx->array_new(nentries); + if (!snapshot) + return NULL; + PodCopy(snapshot, entries, nentries); + return snapshot; +} + +void +FrameState::restoreFromSnapshot(FrameEntry *snapshot) +{ + discardFrame(); + PodCopy(entries, snapshot, nentries); - memset(regstate, 0, sizeof(regstate)); + for (unsigned i = 0; i < nentries; i++) { + FrameEntry *fe = entries + i; + if (!fe->isTracked()) + continue; + tracker.entries[fe->index_] = fe; + tracker.nentries = Max(tracker.nentries, fe->index_ + 1); + if (fe->isCopy()) + continue; + if (fe->type.inRegister()) { + freeRegs.takeReg(fe->type.reg()); + regstate(fe->type.reg()).associate(fe, RematInfo::TYPE); + } + if (fe->data.inRegister()) { + freeRegs.takeReg(fe->data.reg()); + regstate(fe->data.reg()).associate(fe, RematInfo::DATA); + } + if (fe->data.inFPRegister()) { + freeRegs.takeReg(fe->data.fpreg()); + regstate(fe->data.fpreg()).associate(fe, RematInfo::DATA); + } + } } void @@ -225,12 +547,423 @@ FrameState::forgetEverything() resetInternalState(); #ifdef DEBUG - for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { - JS_ASSERT(!regstate[i].usedBy()); + for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + JS_ASSERT(!regstate(reg).usedBy()); } #endif } +#ifdef DEBUG +void +FrameState::dumpAllocation(RegisterAllocation *alloc) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + if (alloc->assigned(reg)) { + printf(" (%s: %s%s)", reg.name(), entryName(entries + alloc->index(reg)), + alloc->synced(reg) ? "" : " unsynced"); + } + } + printf("\n"); +} +#endif + +RegisterAllocation * +FrameState::computeAllocation(jsbytecode *target) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + RegisterAllocation *alloc = cx->typeLifoAlloc().new_(false); + if (!alloc) + return NULL; + + /* + * State must be synced at exception and switch targets, at traps and when + * crossing between compilation chunks. + */ + if (a->analysis->getCode(target).safePoint || + (!a->parent && !cc.bytecodeInChunk(target))) { +#ifdef DEBUG + if (IsJaegerSpewChannelActive(JSpew_Regalloc)) { + JaegerSpew(JSpew_Regalloc, "allocation at %u:", unsigned(target - a->script->code)); + dumpAllocation(alloc); + } +#endif + return alloc; + } + + /* + * The allocation to use at the target consists of all parent, temporary + * and non-stack entries currently in registers which are live at target. + */ + Registers regs = Registers::AvailAnyRegs; + while (!regs.empty()) { + AnyRegisterID reg = regs.takeAnyReg(); + if (freeRegs.hasReg(reg) || regstate(reg).type() == RematInfo::TYPE) + continue; + FrameEntry *fe = regstate(reg).fe(); + if (fe < a->callee_ || + isConstructorThis(fe) || + (fe > a->callee_ && fe < a->spBase && variableLive(fe, target)) || + (isTemporary(fe) && (a->parent || uint32_t(target - a->script->code) <= loop->backedgeOffset()))) { + /* + * For entries currently in floating point registers, check they + * are known to be doubles at the target. We don't need to do this + * for entries in normal registers, as fixDoubleTypes must have been + * called to convert them to floats. + */ + if (!reg.isReg() && !isTemporary(fe) && fe >= a->callee_ && fe < a->spBase) { + if (!a->analysis->trackSlot(entrySlot(fe))) + continue; + bool nonDoubleTarget = false; + const SlotValue *newv = a->analysis->newValues(target); + while (newv && newv->slot) { + if (newv->value.kind() == SSAValue::PHI && + newv->value.phiOffset() == uint32_t(target - a->script->code) && + newv->slot == entrySlot(fe)) { + types::TypeSet *types = a->analysis->getValueTypes(newv->value); + if (types->getKnownTypeTag(cx) != JSVAL_TYPE_DOUBLE) + nonDoubleTarget = true; + } + newv++; + } + if (nonDoubleTarget) + continue; + } + alloc->set(reg, fe - entries, fe->data.synced()); + } + } + +#ifdef DEBUG + if (IsJaegerSpewChannelActive(JSpew_Regalloc)) { + JaegerSpew(JSpew_Regalloc, "allocation at %u:", unsigned(target - a->script->code)); + dumpAllocation(alloc); + } +#endif + + return alloc; +} + +void +FrameState::relocateReg(AnyRegisterID reg, RegisterAllocation *alloc, Uses uses) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + /* + * The reg needs to be freed to make room for a variable carried across + * a branch. Either evict its entry, or try to move it to a different + * register if it is needed to test the branch condition. :XXX: could also + * watch for variables which are carried across the branch but are in a + * the register for a different carried entry, we just spill these for now. + */ + JS_ASSERT(!freeRegs.hasReg(reg)); + + for (unsigned i = 0; i < uses.nuses; i++) { + FrameEntry *fe = peek(-1 - i); + if (fe->isCopy()) + fe = fe->copyOf(); + if (reg.isReg() && fe->data.inRegister() && fe->data.reg() == reg.reg()) { + pinReg(reg); + RegisterID nreg = allocReg(); + unpinReg(reg); + + JaegerSpew(JSpew_Regalloc, "relocating %s\n", reg.name()); + + masm.move(reg.reg(), nreg); + regstate(reg).forget(); + regstate(nreg).associate(fe, RematInfo::DATA); + fe->data.setRegister(nreg); + freeRegs.putReg(reg); + return; + } + } + + JaegerSpew(JSpew_Regalloc, "could not relocate %s\n", reg.name()); + + takeReg(reg); + freeRegs.putReg(reg); +} + +bool +FrameState::syncForBranch(jsbytecode *target, Uses uses) +{ + /* There should be no unowned or pinned registers. */ +#ifdef DEBUG + Registers checkRegs(Registers::AvailAnyRegs); + while (!checkRegs.empty()) { + AnyRegisterID reg = checkRegs.takeAnyReg(); + JS_ASSERT_IF(!freeRegs.hasReg(reg), regstate(reg).fe()); + } +#endif + + if (!cx->typeInferenceEnabled()) { + syncAndForgetEverything(); + return true; + } + + RegisterAllocation *&alloc = a->analysis->getAllocation(target); + if (!alloc) { + alloc = computeAllocation(target); + if (!alloc) + return false; + } + + syncForAllocation(alloc, false, uses); + + return true; +} + +void +FrameState::syncForAllocation(RegisterAllocation *alloc, bool inlineReturn, Uses uses) +{ + /* + * First pass. Sync all entries which will not be carried in a register, + * and uncopy everything except values popped by the branch or before the + * call returns. + */ + + FrameEntry *topEntry = NULL; + if (inlineReturn) + topEntry = a->parent->sp - (GET_ARGC(a->parent->PC) + 2); + + for (uint32_t i = tracker.nentries - 1; i < tracker.nentries; i--) { + FrameEntry *fe = tracker[i]; + + if (deadEntry(fe, uses.nuses)) + continue; + if (inlineReturn && fe >= topEntry && !isTemporary(fe)) { + /* + * The return value has already been stored, so there is no need to + * keep any of the entries for this frame or for values popped once + * the call returns intact. Forcibly evict any registers for these, + * so that we don't emit sync code for them if we need a register + * in syncFe below. + */ + forgetAllRegs(fe); + fe->resetSynced(); + continue; + } + + /* Force syncs for locals which are dead at the current PC. */ + if (isLocal(fe) && !fe->copied && !a->analysis->slotEscapes(entrySlot(fe))) { + Lifetime *lifetime = a->analysis->liveness(entrySlot(fe)).live(a->PC - a->script->code); + if (!lifetime) + fakeSync(fe); + } + + /* If returning from a script, fake syncs for dead locals in the immediate parent. */ + if (inlineReturn && fe >= a->parent->locals && + fe - a->parent->locals < a->parent->script->nfixed && + !a->parent->analysis->slotEscapes(frameSlot(a->parent, fe))) { + const LifetimeVariable &var = a->parent->analysis->liveness(frameSlot(a->parent, fe)); + Lifetime *lifetime = var.live(a->parent->PC - a->parent->script->code); + if (!lifetime) + fakeSync(fe); + } + + if (!fe->isCopy() && alloc->hasAnyReg(fe - entries)) { + /* Types are always synced, except for known doubles. */ + if (!fe->isType(JSVAL_TYPE_DOUBLE)) + syncType(fe); + } else { + syncFe(fe); + if (fe->isCopy()) + fe->resetSynced(); + } + } + + /* + * Second pass. Move entries carried in registers to the right register + * provided no value used in the branch is evicted. After this pass, + * everything will either be in the right register or will be in memory. + */ + + Registers regs = Registers(Registers::AvailAnyRegs); + while (!regs.empty()) { + AnyRegisterID reg = regs.takeAnyReg(); + if (!alloc->assigned(reg)) + continue; + FrameEntry *fe = getOrTrack(alloc->index(reg)); + JS_ASSERT(!fe->isCopy()); + + JS_ASSERT_IF(!fe->isType(JSVAL_TYPE_DOUBLE), fe->type.synced()); + if (!fe->data.synced() && alloc->synced(reg)) + syncFe(fe); + + if (fe->dataInRegister(reg)) + continue; + + if (!freeRegs.hasReg(reg)) + relocateReg(reg, alloc, uses); + + if (reg.isReg()) { + RegisterID nreg = reg.reg(); + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + JS_ASSERT(!a->analysis->trackSlot(entrySlot(fe))); + syncFe(fe); + forgetAllRegs(fe); + fe->type.setMemory(); + fe->data.setMemory(); + } + if (fe->data.inMemory()) { + masm.loadPayload(addressOf(fe), nreg); + } else if (fe->isConstant()) { + masm.loadValuePayload(fe->getValue(), nreg); + } else { + JS_ASSERT(fe->data.inRegister() && fe->data.reg() != nreg); + masm.move(fe->data.reg(), nreg); + freeRegs.putReg(fe->data.reg()); + regstate(fe->data.reg()).forget(); + } + fe->data.setRegister(nreg); + } else { + FPRegisterID nreg = reg.fpreg(); + JS_ASSERT(!fe->isNotType(JSVAL_TYPE_DOUBLE)); + if (!fe->isTypeKnown()) + learnType(fe, JSVAL_TYPE_DOUBLE, false); + if (fe->data.inMemory()) { + masm.loadDouble(addressOf(fe), nreg); + } else if (fe->isConstant()) { + masm.slowLoadConstantDouble(fe->getValue().toDouble(), nreg); + } else { + JS_ASSERT(fe->data.inFPRegister() && fe->data.fpreg() != nreg); + masm.moveDouble(fe->data.fpreg(), nreg); + freeRegs.putReg(fe->data.fpreg()); + regstate(fe->data.fpreg()).forget(); + } + fe->data.setFPRegister(nreg); + } + + freeRegs.takeReg(reg); + regstate(reg).associate(fe, RematInfo::DATA); + } +} + +bool +FrameState::discardForJoin(RegisterAllocation *&alloc, uint32_t stackDepth) +{ + if (!cx->typeInferenceEnabled()) { + resetInternalState(); + PodArrayZero(regstate_); + a->sp = a->spBase + stackDepth; + return true; + } + + if (!alloc) { + /* + * This shows up for loop entries which are not reachable from the + * loop head, and for exception, switch target and trap safe points. + */ + alloc = cx->typeLifoAlloc().new_(false); + if (!alloc) + return false; + } + + resetInternalState(); + PodArrayZero(regstate_); + + Registers regs(Registers::AvailAnyRegs); + while (!regs.empty()) { + AnyRegisterID reg = regs.takeAnyReg(); + if (!alloc->assigned(reg)) + continue; + FrameEntry *fe = getOrTrack(alloc->index(reg)); + + freeRegs.takeReg(reg); + + /* + * We can't look at the type of the fe as we haven't restored analysis types yet, + * but if this is an FP reg it will be set to double type. + */ + if (reg.isReg()) { + fe->data.setRegister(reg.reg()); + } else { + fe->setType(JSVAL_TYPE_DOUBLE); + fe->data.setFPRegister(reg.fpreg()); + } + + regstate(reg).associate(fe, RematInfo::DATA); + if (!alloc->synced(reg)) { + fe->data.unsync(); + if (!reg.isReg()) + fe->type.unsync(); + } + } + + a->sp = a->spBase + stackDepth; + + for (unsigned i = 0; i < stackDepth; i++) + extraArray[a->spBase + i - entries].reset(); + + return true; +} + +bool +FrameState::consistentRegisters(jsbytecode *target) +{ + if (!cx->typeInferenceEnabled()) { + JS_ASSERT(freeRegs.freeMask == Registers::AvailAnyRegs); + return true; + } + + /* + * Before calling this, either the entire state should have been synced or + * syncForBranch should have been called. These will ensure that any FE + * which is not consistent with the target's register state has already + * been synced, and no stores will need to be issued by prepareForJump. + */ + RegisterAllocation *alloc = a->analysis->getAllocation(target); + JS_ASSERT(alloc); + + Registers regs(Registers::AvailAnyRegs); + while (!regs.empty()) { + AnyRegisterID reg = regs.takeAnyReg(); + if (alloc->assigned(reg)) { + FrameEntry *needed = getOrTrack(alloc->index(reg)); + if (!freeRegs.hasReg(reg)) { + FrameEntry *fe = regstate(reg).fe(); + if (fe != needed) + return false; + } else { + return false; + } + } + } + + return true; +} + +void +FrameState::prepareForJump(jsbytecode *target, Assembler &masm, bool synced) +{ + if (!cx->typeInferenceEnabled()) + return; + + JS_ASSERT_IF(!synced, !consistentRegisters(target)); + + RegisterAllocation *alloc = a->analysis->getAllocation(target); + JS_ASSERT(alloc); + + Registers regs = 0; + + regs = Registers(Registers::AvailAnyRegs); + while (!regs.empty()) { + AnyRegisterID reg = regs.takeAnyReg(); + if (!alloc->assigned(reg)) + continue; + + const FrameEntry *fe = getOrTrack(alloc->index(reg)); + if (synced || !fe->backing()->dataInRegister(reg)) { + JS_ASSERT_IF(!synced, fe->data.synced()); + if (reg.isReg()) + masm.loadPayload(addressOf(fe), reg.reg()); + else + masm.loadDouble(addressOf(fe), reg.fpreg()); + } + } +} + void FrameState::storeTo(FrameEntry *fe, Address address, bool popped) { @@ -242,7 +975,6 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) if (fe->isCopy()) fe = fe->copyOf(); - /* Cannot clobber the address's register. */ JS_ASSERT(!freeRegs.hasReg(address.base)); /* If loading from memory, ensure destination differs. */ @@ -250,15 +982,36 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) addressOf(fe).base != address.base || addressOf(fe).offset != address.offset); + if (fe->data.inFPRegister()) { + masm.storeDouble(fe->data.fpreg(), address); + return; + } + + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + JS_ASSERT(fe->data.inMemory()); + masm.loadDouble(addressOf(fe), Registers::FPConversionTemp); + masm.storeDouble(Registers::FPConversionTemp, address); + return; + } + + /* Don't clobber the address's register. */ + bool pinAddressReg = !!regstate(address.base).fe(); + if (pinAddressReg) + pinReg(address.base); + #if defined JS_PUNBOX64 if (fe->type.inMemory() && fe->data.inMemory()) { /* Future optimization: track that the Value is in a register. */ RegisterID vreg = Registers::ValueReg; masm.loadPtr(addressOf(fe), vreg); masm.storePtr(vreg, address); + if (pinAddressReg) + unpinReg(address.base); return; } + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); + /* * If dreg is obtained via allocReg(), then calling * pinReg() trips an assertion. But in all other cases, @@ -276,12 +1029,12 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) JS_ASSERT(fe->data.inMemory()); if (popped) { dreg = allocReg(); + masm.loadPayload(addressOf(fe), dreg.reg()); canPinDreg = false; } else { - dreg = allocReg(fe, RematInfo::DATA); + dreg = allocAndLoadReg(fe, false, RematInfo::DATA).reg(); fe->data.setRegister(dreg.reg()); } - masm.loadPayload(addressOf(fe), dreg.reg()); } /* Store the Value. */ @@ -294,8 +1047,13 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) if (canPinDreg) pinReg(dreg.reg()); - RegisterID treg = popped ? allocReg() : allocReg(fe, RematInfo::TYPE); - masm.loadTypeTag(addressOf(fe), treg); + RegisterID treg; + if (popped) { + treg = allocReg(); + masm.loadTypeTag(addressOf(fe), treg); + } else { + treg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg(); + } masm.storeValueFromComponents(treg, dreg.reg(), address); if (popped) @@ -317,8 +1075,13 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) masm.storePayload(fe->data.reg(), address); } else { JS_ASSERT(fe->data.inMemory()); - RegisterID reg = popped ? allocReg() : allocReg(fe, RematInfo::DATA); - masm.loadPayload(addressOf(fe), reg); + RegisterID reg; + if (popped) { + reg = allocReg(); + masm.loadPayload(addressOf(fe), reg); + } else { + reg = allocAndLoadReg(fe, false, RematInfo::DATA).reg(); + } masm.storePayload(reg, address); if (popped) freeReg(reg); @@ -332,8 +1095,13 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) masm.storeTypeTag(fe->type.reg(), address); } else { JS_ASSERT(fe->type.inMemory()); - RegisterID reg = popped ? allocReg() : allocReg(fe, RematInfo::TYPE); - masm.loadTypeTag(addressOf(fe), reg); + RegisterID reg; + if (popped) { + reg = allocReg(); + masm.loadTypeTag(addressOf(fe), reg); + } else { + reg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg(); + } masm.storeTypeTag(reg, address); if (popped) freeReg(reg); @@ -341,6 +1109,8 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) fe->type.setRegister(reg); } #endif + if (pinAddressReg) + unpinReg(address.base); } void @@ -358,6 +1128,12 @@ void FrameState::loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID da return; } + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + FPRegisterID fpreg = tempFPRegForData(fe); + masm.breakDouble(fpreg, typeReg, dataReg); + return; + } + if (fe->isCopy()) fe = fe->copyOf(); @@ -431,59 +1207,83 @@ void FrameState::loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID da void FrameState::assertValidRegisterState() const { - Registers checkedFreeRegs; + Registers checkedFreeRegs(Registers::AvailAnyRegs); + + /* Check that copied and copy info balance out. */ + int32_t copyCount = 0; - for (uint32 i = 0; i < tracker.nentries; i++) { + for (uint32_t i = 0; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; - if (fe >= sp) + if (deadEntry(fe)) continue; JS_ASSERT(i == fe->trackerIndex()); - JS_ASSERT_IF(fe->isCopy(), - fe->trackerIndex() > fe->copyOf()->trackerIndex()); - JS_ASSERT_IF(fe->isCopy(), fe > fe->copyOf()); - JS_ASSERT_IF(fe->isCopy(), !fe->type.inRegister() && !fe->data.inRegister()); - JS_ASSERT_IF(fe->isCopy(), fe->copyOf() < sp); - JS_ASSERT_IF(fe->isCopy(), fe->copyOf()->isCopied()); - if (fe->isCopy()) + if (fe->isCopy()) { + JS_ASSERT_IF(!fe->copyOf()->temporary, fe > fe->copyOf()); + JS_ASSERT(fe->trackerIndex() > fe->copyOf()->trackerIndex()); + JS_ASSERT(!deadEntry(fe->copyOf())); + JS_ASSERT(fe->copyOf()->isCopied()); + JS_ASSERT(!fe->isCopied()); + copyCount--; continue; + } + + copyCount += fe->copied; + if (fe->type.inRegister()) { checkedFreeRegs.takeReg(fe->type.reg()); - JS_ASSERT(regstate[fe->type.reg()].fe() == fe); + JS_ASSERT(regstate(fe->type.reg()).fe() == fe); + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); } if (fe->data.inRegister()) { checkedFreeRegs.takeReg(fe->data.reg()); - JS_ASSERT(regstate[fe->data.reg()].fe() == fe); + JS_ASSERT(regstate(fe->data.reg()).fe() == fe); + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); + } + if (fe->data.inFPRegister()) { + JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE)); + checkedFreeRegs.takeReg(fe->data.fpreg()); + JS_ASSERT(regstate(fe->data.fpreg()).fe() == fe); } } + JS_ASSERT(copyCount == 0); JS_ASSERT(checkedFreeRegs == freeRegs); - for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { - JS_ASSERT(!regstate[i].isPinned()); - JS_ASSERT_IF(regstate[i].fe(), !freeRegs.hasReg(RegisterID(i))); - JS_ASSERT_IF(regstate[i].fe(), regstate[i].fe()->isTracked()); + for (uint32_t i = 0; i < Registers::TotalRegisters; i++) { + AnyRegisterID reg = (RegisterID) i; + JS_ASSERT(!regstate(reg).isPinned()); + JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg)); + JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked()); + } + + for (uint32_t i = 0; i < Registers::TotalFPRegisters; i++) { + AnyRegisterID reg = (FPRegisterID) i; + JS_ASSERT(!regstate(reg).isPinned()); + JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg)); + JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked()); + JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).type() == RematInfo::DATA); } } #endif #if defined JS_NUNBOX32 void -FrameState::syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, - FrameEntry *bottom) const +FrameState::syncFancy(Assembler &masm, Registers avail, int trackerIndex) const { - reifier.reset(&masm, avail, resumeAt, bottom); + reifier.reset(&masm, avail, a->sp, entries); - for (FrameEntry *fe = resumeAt; fe >= bottom; fe--) { - if (!fe->isTracked()) + for (; trackerIndex >= 0; trackerIndex--) { + FrameEntry *fe = tracker[trackerIndex]; + if (fe >= a->sp) continue; reifier.sync(fe); } } -#endif +#endif void FrameState::sync(Assembler &masm, Uses uses) const { @@ -491,10 +1291,10 @@ FrameState::sync(Assembler &masm, Uses uses) const return; /* Sync all registers up-front. */ - Registers allRegs(Registers::AvailRegs); + Registers allRegs(Registers::AvailAnyRegs); while (!allRegs.empty()) { - RegisterID reg = allRegs.takeAnyReg(); - FrameEntry *fe = regstate[reg].usedBy(); + AnyRegisterID reg = allRegs.takeAnyReg(); + FrameEntry *fe = regstate(reg).usedBy(); if (!fe) continue; @@ -505,17 +1305,19 @@ FrameState::sync(Assembler &masm, Uses uses) const ensureFeSynced(fe, masm); /* Take the other register in the pair, if one exists. */ - if (regstate[reg].type() == RematInfo::DATA && fe->type.inRegister()) + if (regstate(reg).type() == RematInfo::DATA && fe->type.inRegister()) allRegs.takeReg(fe->type.reg()); - else if (regstate[reg].type() == RematInfo::TYPE && fe->data.inRegister()) + else if (regstate(reg).type() == RematInfo::TYPE && fe->data.inRegister()) allRegs.takeReg(fe->data.reg()); #elif defined JS_NUNBOX32 /* Sync register if unsynced. */ - if (regstate[reg].type() == RematInfo::DATA) { - JS_ASSERT(fe->data.reg() == reg); + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + ensureFeSynced(fe, masm); + } else if (regstate(reg).type() == RematInfo::DATA) { + JS_ASSERT(fe->data.reg() == reg.reg()); ensureDataSynced(fe, masm); } else { - JS_ASSERT(fe->type.reg() == reg); + JS_ASSERT(fe->type.reg() == reg.reg()); ensureTypeSynced(fe, masm); } #endif @@ -524,25 +1326,31 @@ FrameState::sync(Assembler &masm, Uses uses) const /* * Keep track of free registers using a bitmask. If we have to drop into * syncFancy(), then this mask will help avoid eviction. - */ - Registers avail(freeRegs); - Registers temp(Registers::TempRegs); - - FrameEntry *bottom = sp - uses.nuses; - - for (FrameEntry *fe = sp - 1; fe >= bottom; fe--) { - if (!fe->isTracked()) + */ + Registers avail(freeRegs.freeMask & Registers::AvailRegs); + Registers temp(Registers::TempAnyRegs); + + unsigned nentries = tracker.nentries; + for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) { + JS_ASSERT(tracker.nentries == nentries); + FrameEntry *fe = tracker[trackerIndex]; + if (fe >= a->sp) continue; - FrameEntry *backing = fe; + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + /* Copies of in-memory doubles can be synced without spilling. */ + if (fe->isCopy() || !fe->data.inFPRegister()) + ensureFeSynced(fe, masm); + continue; + } if (!fe->isCopy()) { - if (fe->data.inRegister()) + if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned()) avail.putReg(fe->data.reg()); - if (fe->type.inRegister()) + if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned()) avail.putReg(fe->type.reg()); } else { - backing = fe->copyOf(); + FrameEntry *backing = fe->copyOf(); JS_ASSERT(!backing->isConstant() && !fe->isConstant()); #if defined JS_PUNBOX64 @@ -573,22 +1381,24 @@ FrameState::sync(Assembler &masm, Uses uses) const /* Fall back to a slower sync algorithm if load required. */ if ((!fe->type.synced() && backing->type.inMemory()) || (!fe->data.synced() && backing->data.inMemory())) { - syncFancy(masm, avail, fe, bottom); + syncFancy(masm, avail, trackerIndex); return; } #endif } + bool copy = fe->isCopy(); + /* If a part still needs syncing, it is either a copy or constant. */ #if defined JS_PUNBOX64 /* All register-backed FEs have been entirely synced up-front. */ - if (!fe->type.inRegister() && !fe->data.inRegister()) + if (copy || (!fe->type.inRegister() && !fe->data.inRegister())) ensureFeSynced(fe, masm); #elif defined JS_NUNBOX32 /* All components held in registers have been already synced. */ - if (!fe->data.inRegister()) + if (copy || !fe->data.inRegister()) ensureDataSynced(fe, masm); - if (!fe->type.inRegister()) + if (copy || !fe->type.inRegister()) ensureTypeSynced(fe, masm); #endif } @@ -597,14 +1407,20 @@ FrameState::sync(Assembler &masm, Uses uses) const void FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) { - FrameEntry *spStop = sp - ignore.nuses; + if (loop) { + /* + * Drop any remaining loop registers so we don't do any more after-the-fact + * allocation of the initial register state. + */ + loop->clearLoopRegisters(); + } /* Sync all kill-registers up-front. */ Registers search(kill.freeMask & ~freeRegs.freeMask); while (!search.empty()) { - RegisterID reg = search.takeAnyReg(); - FrameEntry *fe = regstate[reg].usedBy(); - if (!fe || fe >= spStop) + AnyRegisterID reg = search.takeAnyReg(); + FrameEntry *fe = regstate(reg).usedBy(); + if (!fe || deadEntry(fe, ignore.nuses)) continue; JS_ASSERT(fe->isTracked()); @@ -619,49 +1435,55 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) fe->data.sync(); /* Take the other register in the pair, if one exists. */ - if (regstate[reg].type() == RematInfo::DATA) { - JS_ASSERT(fe->data.reg() == reg); - if (fe->type.inRegister() && search.hasReg(fe->type.reg())) - search.takeReg(fe->type.reg()); + if (regstate(reg).type() == RematInfo::DATA) { + if (!fe->isType(JSVAL_TYPE_DOUBLE)) { + JS_ASSERT(fe->data.reg() == reg.reg()); + if (fe->type.inRegister() && search.hasReg(fe->type.reg())) + search.takeReg(fe->type.reg()); + } } else { - JS_ASSERT(fe->type.reg() == reg); + JS_ASSERT(fe->type.reg() == reg.reg()); if (fe->data.inRegister() && search.hasReg(fe->data.reg())) search.takeReg(fe->data.reg()); } #elif defined JS_NUNBOX32 /* Sync this register. */ - if (regstate[reg].type() == RematInfo::DATA) { - JS_ASSERT(fe->data.reg() == reg); + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + syncFe(fe); + } else if (regstate(reg).type() == RematInfo::DATA) { + JS_ASSERT(fe->data.reg() == reg.reg()); syncData(fe); } else { - JS_ASSERT(fe->type.reg() == reg); + JS_ASSERT(fe->type.reg() == reg.reg()); syncType(fe); } #endif } - uint32 maxvisits = tracker.nentries; - FrameEntry *bottom = sp - uses.nuses; - - for (FrameEntry *fe = sp - 1; fe >= bottom && maxvisits; fe--) { - if (!fe->isTracked()) - continue; - maxvisits--; + unsigned nentries = tracker.nentries; + for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) { + JS_ASSERT(tracker.nentries == nentries); + FrameEntry *fe = tracker[trackerIndex]; - if (fe >= spStop) + if (fe >= a->sp || deadEntry(fe, ignore.nuses)) continue; syncFe(fe); + if (fe->isCopy()) + continue; + /* Forget registers. */ - if (fe->data.inRegister() && kill.hasReg(fe->data.reg()) && - !regstate[fe->data.reg()].isPinned()) { + if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned()) { forgetReg(fe->data.reg()); fe->data.setMemory(); } - if (fe->type.inRegister() && kill.hasReg(fe->type.reg()) && - !regstate[fe->type.reg()].isPinned()) { + if (fe->data.inFPRegister() && !regstate(fe->data.fpreg()).isPinned()) { + forgetReg(fe->data.fpreg()); + fe->data.setMemory(); + } + if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned()) { forgetReg(fe->type.reg()); fe->type.setMemory(); } @@ -673,19 +1495,20 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) */ search = Registers(kill.freeMask & ~freeRegs.freeMask); while (!search.empty()) { - RegisterID reg = search.takeAnyReg(); - FrameEntry *fe = regstate[reg].usedBy(); - if (!fe || fe >= spStop) + AnyRegisterID reg = search.takeAnyReg(); + FrameEntry *fe = regstate(reg).usedBy(); + if (!fe || deadEntry(fe, ignore.nuses)) continue; JS_ASSERT(fe->isTracked()); - if (regstate[reg].type() == RematInfo::DATA) { - JS_ASSERT(fe->data.reg() == reg); + if (regstate(reg).type() == RematInfo::DATA) { + JS_ASSERT_IF(reg.isFPReg(), fe->data.fpreg() == reg.fpreg()); + JS_ASSERT_IF(!reg.isFPReg(), fe->data.reg() == reg.reg()); JS_ASSERT(fe->data.synced()); fe->data.setMemory(); } else { - JS_ASSERT(fe->type.reg() == reg); + JS_ASSERT(fe->type.reg() == reg.reg()); JS_ASSERT(fe->type.synced()); fe->type.setMemory(); } @@ -697,18 +1520,43 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) void FrameState::merge(Assembler &masm, Changes changes) const { - Registers search(Registers::AvailRegs & ~freeRegs.freeMask); + /* + * Note: this should only be called by StubCompiler::rejoin, which will notify + * this FrameState about the jump to patch up in case a new loop register is + * allocated later. + */ - while (!search.empty()) { - RegisterID reg = search.peekReg(); - FrameEntry *fe = regstate[reg].usedBy(); + /* + * For any changed values we are merging back which we consider to be doubles, + * ensure they actually are doubles. They must be doubles or ints, but we + * do not require stub paths to always generate a double when needed. + * :FIXME: we check this on OOL stub calls, but not inline stub calls. + */ + if (cx->typeInferenceEnabled()) { + for (unsigned i = 0; i < changes.nchanges; i++) { + FrameEntry *fe = a->sp - 1 - i; + if (fe->isTracked() && fe->isType(JSVAL_TYPE_DOUBLE)) + masm.ensureInMemoryDouble(addressOf(fe)); + } + } + + uint32_t mask = Registers::AvailAnyRegs & ~freeRegs.freeMask; + Registers search(mask); + + while (!search.empty(mask)) { + AnyRegisterID reg = search.peekReg(mask); + FrameEntry *fe = regstate(reg).usedBy(); if (!fe) { search.takeReg(reg); continue; } - if (fe->data.inRegister() && fe->type.inRegister()) { + if (fe->isType(JSVAL_TYPE_DOUBLE)) { + JS_ASSERT(fe->data.fpreg() == reg.fpreg()); + search.takeReg(fe->data.fpreg()); + masm.loadDouble(addressOf(fe), fe->data.fpreg()); + } else if (fe->data.inRegister() && fe->type.inRegister()) { search.takeReg(fe->data.reg()); search.takeReg(fe->type.reg()); masm.loadValueAsComponents(addressOf(fe), fe->type.reg(), fe->data.reg()); @@ -734,7 +1582,8 @@ FrameState::copyDataIntoReg(FrameEntry *fe) void FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint) { - JS_ASSERT(!fe->data.isConstant()); + JS_ASSERT(!fe->isConstant()); + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); if (fe->isCopy()) fe = fe->copyOf(); @@ -744,38 +1593,41 @@ FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint) RegisterID reg = fe->data.reg(); if (reg == hint) { - if (freeRegs.empty()) { + if (freeRegs.empty(Registers::AvailRegs)) { ensureDataSynced(fe, masm); fe->data.setMemory(); } else { reg = allocReg(); masm.move(hint, reg); fe->data.setRegister(reg); - regstate[reg].associate(regstate[hint].fe(), RematInfo::DATA); + regstate(reg).associate(regstate(hint).fe(), RematInfo::DATA); } - regstate[hint].forget(); + regstate(hint).forget(); } else { pinReg(reg); takeReg(hint); unpinReg(reg); masm.move(reg, hint); } + + modifyReg(hint); } JSC::MacroAssembler::RegisterID FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe) { - JS_ASSERT(!fe->data.isConstant()); + JS_ASSERT(!fe->isConstant()); if (fe->isCopy()) fe = fe->copyOf(); if (fe->data.inRegister()) { RegisterID reg = fe->data.reg(); - if (freeRegs.empty()) { + if (freeRegs.empty(Registers::AvailRegs)) { ensureDataSynced(fe, masm); fe->data.setMemory(); - regstate[reg].forget(); + regstate(reg).forget(); + modifyReg(reg); } else { RegisterID newReg = allocReg(); masm.move(reg, newReg); @@ -786,7 +1638,7 @@ FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe) RegisterID reg = allocReg(); - if (!freeRegs.empty()) + if (!freeRegs.empty(Registers::AvailRegs)) masm.move(tempRegForData(fe), reg); else masm.loadPayload(addressOf(fe),reg); @@ -797,17 +1649,18 @@ FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe) JSC::MacroAssembler::RegisterID FrameState::copyTypeIntoReg(FrameEntry *fe) { - JS_ASSERT(!fe->type.isConstant()); - if (fe->isCopy()) fe = fe->copyOf(); + JS_ASSERT(!fe->type.isConstant()); + if (fe->type.inRegister()) { RegisterID reg = fe->type.reg(); - if (freeRegs.empty()) { + if (freeRegs.empty(Registers::AvailRegs)) { ensureTypeSynced(fe, masm); fe->type.setMemory(); - regstate[reg].forget(); + regstate(reg).forget(); + modifyReg(reg); } else { RegisterID newReg = allocReg(); masm.move(reg, newReg); @@ -818,7 +1671,7 @@ FrameState::copyTypeIntoReg(FrameEntry *fe) RegisterID reg = allocReg(); - if (!freeRegs.empty()) + if (!freeRegs.empty(Registers::AvailRegs)) masm.move(tempRegForType(fe), reg); else masm.loadTypeTag(addressOf(fe), reg); @@ -845,28 +1698,10 @@ FrameState::copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe) return reg; } -JSC::MacroAssembler::FPRegisterID -FrameState::copyEntryIntoFPReg(FrameEntry *fe, FPRegisterID fpreg) -{ - return copyEntryIntoFPReg(this->masm, fe, fpreg); -} - -JSC::MacroAssembler::FPRegisterID -FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) -{ - if (fe->isCopy()) - fe = fe->copyOf(); - - ensureFeSynced(fe, masm); - masm.loadDouble(addressOf(fe), fpreg); - - return fpreg; -} - JSC::MacroAssembler::RegisterID FrameState::ownRegForType(FrameEntry *fe) { - JS_ASSERT(!fe->type.isConstant()); + JS_ASSERT(!fe->isTypeKnown()); RegisterID reg; if (fe->isCopy()) { @@ -877,12 +1712,13 @@ FrameState::ownRegForType(FrameEntry *fe) tempRegForType(backing); } - if (freeRegs.empty()) { + if (freeRegs.empty(Registers::AvailRegs)) { /* For now... just steal the register that already exists. */ ensureTypeSynced(backing, masm); reg = backing->type.reg(); backing->type.setMemory(); - regstate[reg].forget(); + regstate(reg).forget(); + modifyReg(reg); } else { reg = allocReg(); masm.move(backing->type.reg(), reg); @@ -894,10 +1730,11 @@ FrameState::ownRegForType(FrameEntry *fe) reg = fe->type.reg(); /* Remove ownership of this register. */ - JS_ASSERT(regstate[reg].fe() == fe); - JS_ASSERT(regstate[reg].type() == RematInfo::TYPE); - regstate[reg].forget(); - fe->type.invalidate(); + JS_ASSERT(regstate(reg).fe() == fe); + JS_ASSERT(regstate(reg).type() == RematInfo::TYPE); + regstate(reg).forget(); + fe->type.setMemory(); + modifyReg(reg); } else { JS_ASSERT(fe->type.inMemory()); reg = allocReg(); @@ -909,7 +1746,8 @@ FrameState::ownRegForType(FrameEntry *fe) JSC::MacroAssembler::RegisterID FrameState::ownRegForData(FrameEntry *fe) { - JS_ASSERT(!fe->data.isConstant()); + JS_ASSERT(!fe->isConstant()); + JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE)); RegisterID reg; if (fe->isCopy()) { @@ -920,12 +1758,13 @@ FrameState::ownRegForData(FrameEntry *fe) tempRegForData(backing); } - if (freeRegs.empty()) { + if (freeRegs.empty(Registers::AvailRegs)) { /* For now... just steal the register that already exists. */ ensureDataSynced(backing, masm); reg = backing->data.reg(); backing->data.setMemory(); - regstate[reg].forget(); + regstate(reg).forget(); + modifyReg(reg); } else { reg = allocReg(); masm.move(backing->data.reg(), reg); @@ -933,22 +1772,17 @@ FrameState::ownRegForData(FrameEntry *fe) return reg; } - if (fe->isCopied()) { - FrameEntry *copy = uncopy(fe); - if (fe->isCopied()) { - fe->type.invalidate(); - fe->data.invalidate(); - return copyDataIntoReg(copy); - } - } - + if (fe->isCopied()) + uncopy(fe); + if (fe->data.inRegister()) { reg = fe->data.reg(); /* Remove ownership of this register. */ - JS_ASSERT(regstate[reg].fe() == fe); - JS_ASSERT(regstate[reg].type() == RematInfo::DATA); - regstate[reg].forget(); - fe->data.invalidate(); + JS_ASSERT(regstate(reg).fe() == fe); + JS_ASSERT(regstate(reg).type() == RematInfo::DATA); + regstate(reg).forget(); + fe->data.setMemory(); + modifyReg(reg); } else { JS_ASSERT(fe->data.inMemory()); reg = allocReg(); @@ -963,30 +1797,147 @@ FrameState::discardFe(FrameEntry *fe) forgetEntry(fe); fe->type.setMemory(); fe->data.setMemory(); + fe->clear(); +} + +void +FrameState::pushDouble(FPRegisterID fpreg) +{ + FrameEntry *fe = rawPush(); + fe->resetUnsynced(); + fe->setType(JSVAL_TYPE_DOUBLE); + fe->data.setFPRegister(fpreg); + regstate(fpreg).associate(fe, RematInfo::DATA); +} + +void +FrameState::pushDouble(Address address) +{ + FPRegisterID fpreg = allocFPReg(); + masm.loadDouble(address, fpreg); + pushDouble(fpreg); +} + +void +FrameState::ensureDouble(FrameEntry *fe) +{ + if (fe->isType(JSVAL_TYPE_DOUBLE)) + return; + + if (fe->isConstant()) { + JS_ASSERT(fe->getValue().isInt32()); + Value newValue = DoubleValue(double(fe->getValue().toInt32())); + fe->setConstant(newValue); + return; + } + + FrameEntry *backing = fe; + if (fe->isCopy()) { + /* Forget this entry is a copy. We are converting this entry, not the backing. */ + backing = fe->copyOf(); + fe->clear(); + } else if (fe->isCopied()) { + /* Sync and forget any copies of this entry. */ + for (uint32_t i = fe->trackerIndex() + 1; i < tracker.nentries; i++) { + FrameEntry *nfe = tracker[i]; + if (!deadEntry(nfe) && nfe->isCopy() && nfe->copyOf() == fe) { + syncFe(nfe); + nfe->resetSynced(); + } + } + } + + FPRegisterID fpreg = allocFPReg(); + + if (backing->isType(JSVAL_TYPE_INT32)) { + RegisterID data = tempRegForData(backing); + masm.convertInt32ToDouble(data, fpreg); + } else { + syncFe(backing); + masm.moveInt32OrDouble(addressOf(backing), fpreg); + } + + if (fe == backing) + forgetAllRegs(fe); + fe->resetUnsynced(); + fe->setType(JSVAL_TYPE_DOUBLE); + fe->data.setFPRegister(fpreg); + regstate(fpreg).associate(fe, RematInfo::DATA); + + fe->data.unsync(); + fe->type.unsync(); +} + +void +FrameState::ensureInteger(FrameEntry *fe) +{ + /* + * This method is used to revert a previous ensureDouble call made for a + * branch. The entry is definitely a double, and has had no copies made. + */ + + if (fe->isConstant()) { + Value newValue = Int32Value(int32_t(fe->getValue().toDouble())); + fe->setConstant(newValue); + return; + } + + JS_ASSERT(!fe->isCopy() && !fe->isCopied()); + JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(JSVAL_TYPE_DOUBLE)); + + if (!fe->isType(JSVAL_TYPE_DOUBLE)) { + /* + * A normal register may have been allocated after calling + * syncAndForgetEverything. + */ + if (fe->data.inRegister()) { + syncFe(fe); + forgetReg(fe->data.reg()); + fe->data.setMemory(); + } + learnType(fe, JSVAL_TYPE_DOUBLE, false); + } + + RegisterID reg = allocReg(); + FPRegisterID fpreg = tempFPRegForData(fe); + Jump j = masm.branchTruncateDoubleToInt32(fpreg, reg); + j.linkTo(masm.label(), &masm); + + forgetAllRegs(fe); + fe->resetUnsynced(); + fe->setType(JSVAL_TYPE_INT32); + fe->data.setRegister(reg); + regstate(reg).associate(fe, RematInfo::DATA); + + fe->data.unsync(); + fe->type.unsync(); +} + +void +FrameState::ensureInMemoryDoubles(Assembler &masm) +{ + JS_ASSERT(!a->parent); + for (uint32_t i = 0; i < tracker.nentries; i++) { + FrameEntry *fe = tracker[i]; + if (!deadEntry(fe) && fe->isType(JSVAL_TYPE_DOUBLE) && + !fe->isCopy() && !fe->isConstant()) { + masm.ensureInMemoryDouble(addressOf(fe)); + } + } } void -FrameState::pushCopyOf(uint32 index) +FrameState::pushCopyOf(FrameEntry *backing) { - FrameEntry *backing = entryFor(index); + JS_ASSERT(backing->isTracked()); FrameEntry *fe = rawPush(); fe->resetUnsynced(); if (backing->isConstant()) { - fe->setConstant(Jsvalify(backing->getValue())); + fe->setConstant(backing->getValue()); } else { - if (backing->isTypeKnown()) - fe->setType(backing->getKnownType()); - else - fe->type.invalidate(); - fe->isNumber = backing->isNumber; - fe->data.invalidate(); - if (backing->isCopy()) { + if (backing->isCopy()) backing = backing->copyOf(); - fe->setCopyOf(backing); - } else { - fe->setCopyOf(backing); - backing->setCopied(); - } + fe->setCopyOf(backing); /* Maintain tracker ordering guarantees for copies. */ JS_ASSERT(backing->isCopied()); @@ -998,12 +1949,12 @@ FrameState::pushCopyOf(uint32 index) FrameEntry * FrameState::walkTrackerForUncopy(FrameEntry *original) { - uint32 firstCopy = InvalidIndex; + uint32_t firstCopy = InvalidIndex; FrameEntry *bestFe = NULL; - uint32 ncopies = 0; - for (uint32 i = original->trackerIndex() + 1; i < tracker.nentries; i++) { + uint32_t ncopies = 0; + for (uint32_t i = original->trackerIndex() + 1; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; - if (fe >= sp) + if (deadEntry(fe)) continue; if (fe->isCopy() && fe->copyOf() == original) { if (firstCopy == InvalidIndex) { @@ -1024,15 +1975,14 @@ FrameState::walkTrackerForUncopy(FrameEntry *original) JS_ASSERT(firstCopy != InvalidIndex); JS_ASSERT(bestFe); - JS_ASSERT(bestFe > original); + JS_ASSERT_IF(!isTemporary(original), bestFe > original); /* Mark all extra copies as copies of the new backing index. */ bestFe->setCopyOf(NULL); if (ncopies > 1) { - bestFe->setCopied(); - for (uint32 i = firstCopy; i < tracker.nentries; i++) { + for (uint32_t i = firstCopy; i < tracker.nentries; i++) { FrameEntry *other = tracker[i]; - if (other >= sp || other == bestFe) + if (deadEntry(other) || other == bestFe) continue; /* The original must be tracked before copies. */ @@ -1053,8 +2003,6 @@ FrameState::walkTrackerForUncopy(FrameEntry *original) if (other->trackerIndex() < bestFe->trackerIndex()) swapInTracker(bestFe, other); } - } else { - bestFe->setNotCopied(); } return bestFe; @@ -1064,12 +2012,12 @@ FrameEntry * FrameState::walkFrameForUncopy(FrameEntry *original) { FrameEntry *bestFe = NULL; - uint32 ncopies = 0; + uint32_t ncopies = 0; /* It's only necessary to visit as many FEs are being tracked. */ - uint32 maxvisits = tracker.nentries; + uint32_t maxvisits = tracker.nentries; - for (FrameEntry *fe = original + 1; fe < sp && maxvisits; fe++) { + for (FrameEntry *fe = original + 1; fe < a->sp && maxvisits; fe++) { if (!fe->isTracked()) continue; @@ -1088,9 +2036,6 @@ FrameState::walkFrameForUncopy(FrameEntry *original) } } - if (ncopies) - bestFe->setCopied(); - return bestFe; } @@ -1123,14 +2068,11 @@ FrameState::uncopy(FrameEntry *original) * the tracker is walked twice, so we multiply by 2 for pessimism. */ FrameEntry *fe; - if ((tracker.nentries - original->trackerIndex()) * 2 > uint32(sp - original)) + if ((tracker.nentries - original->trackerIndex()) * 2 > uint32_t(a->sp - original)) fe = walkFrameForUncopy(original); else fe = walkTrackerForUncopy(original); - if (!fe) { - original->setNotCopied(); - return NULL; - } + JS_ASSERT(fe); /* * Switch the new backing store to the old backing store. During @@ -1147,55 +2089,95 @@ FrameState::uncopy(FrameEntry *original) tempRegForType(original); fe->type.inherit(original->type); if (fe->type.inRegister()) - regstate[fe->type.reg()].reassociate(fe); + regstate(fe->type.reg()).reassociate(fe); + } else { + fe->setType(original->getKnownType()); + } + if (original->isType(JSVAL_TYPE_DOUBLE)) { + if (original->data.inMemory() && !fe->data.synced()) + tempFPRegForData(original); + fe->data.inherit(original->data); + if (fe->data.inFPRegister()) + regstate(fe->data.fpreg()).reassociate(fe); } else { - JS_ASSERT(fe->isTypeKnown()); - JS_ASSERT(fe->getKnownType() == original->getKnownType()); + if (fe->type.inRegister()) + pinReg(fe->type.reg()); + if (original->data.inMemory() && !fe->data.synced()) + tempRegForData(original); + if (fe->type.inRegister()) + unpinReg(fe->type.reg()); + fe->data.inherit(original->data); + if (fe->data.inRegister()) + regstate(fe->data.reg()).reassociate(fe); } - if (original->data.inMemory() && !fe->data.synced()) - tempRegForData(original); - fe->data.inherit(original->data); - if (fe->data.inRegister()) - regstate[fe->data.reg()].reassociate(fe); return fe; } +bool +FrameState::hasOnlyCopy(FrameEntry *backing, FrameEntry *fe) +{ + JS_ASSERT(backing->isCopied() && fe->copyOf() == backing); + + for (uint32_t i = backing->trackerIndex() + 1; i < tracker.nentries; i++) { + FrameEntry *nfe = tracker[i]; + if (nfe != fe && !deadEntry(nfe) && nfe->isCopy() && nfe->copyOf() == backing) + return false; + } + + return true; +} + void -FrameState::finishStore(FrameEntry *fe, bool closed) -{ - // Make sure the backing store entry is synced to memory, then if it's - // closed, forget it entirely (removing all copies) and reset it to a - // synced, in-memory state. - syncFe(fe); - if (closed) { - if (!fe->isCopy()) - forgetEntry(fe); - fe->resetSynced(); +FrameState::separateBinaryEntries(FrameEntry *lhs, FrameEntry *rhs) +{ + JS_ASSERT(lhs == a->sp - 2 && rhs == a->sp - 1); + if (rhs->isCopy() && rhs->copyOf() == lhs) { + syncAndForgetFe(rhs); + syncAndForgetFe(lhs); + uncopy(lhs); } } void -FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange) +FrameState::storeLocal(uint32_t n, bool popGuaranteed) { FrameEntry *local = getLocal(n); - storeTop(local, popGuaranteed, typeChange); - bool closed = isClosedVar(n); - if (!closed && !inTryBlock) + if (a->analysis->slotEscapes(entrySlot(local))) { + JS_ASSERT(local->data.inMemory()); + storeTo(peek(-1), addressOf(local), popGuaranteed); return; + } - finishStore(local, closed); + storeTop(local); + + if (loop) + local->lastLoop = loop->headOffset(); + + if (inTryBlock) + syncFe(local); } void -FrameState::storeArg(uint32 n, bool popGuaranteed) +FrameState::storeArg(uint32_t n, bool popGuaranteed) { // Note that args are always immediately synced, because they can be // aliased (but not written to) via f.arguments. FrameEntry *arg = getArg(n); - storeTop(arg, popGuaranteed, true); - finishStore(arg, isClosedArg(n)); + + if (a->analysis->slotEscapes(entrySlot(arg))) { + JS_ASSERT(arg->data.inMemory()); + storeTo(peek(-1), addressOf(arg), popGuaranteed); + return; + } + + storeTop(arg); + + if (loop) + arg->lastLoop = loop->headOffset(); + + syncFe(arg); } void @@ -1203,17 +2185,19 @@ FrameState::forgetEntry(FrameEntry *fe) { if (fe->isCopied()) { uncopy(fe); - if (!fe->isCopied()) - forgetAllRegs(fe); + fe->resetUnsynced(); } else { forgetAllRegs(fe); } + + extraArray[fe - entries].reset(); } void -FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) +FrameState::storeTop(FrameEntry *target) { - bool wasSynced = target->type.synced(); + JS_ASSERT(!isTemporary(target)); + /* Detect something like (x = x) which is a no-op. */ FrameEntry *top = peek(-1); if (top->isCopy() && top->copyOf() == target) { @@ -1221,15 +2205,25 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) return; } + /* + * If this is overwriting a known non-double type with another value of the + * same type, then make sure we keep the type marked as synced after doing + * the copy. + */ + bool wasSynced = target->type.synced(); + JSValueType oldType = target->isTypeKnown() ? target->getKnownType() : JSVAL_TYPE_UNKNOWN; + bool trySyncType = wasSynced && oldType != JSVAL_TYPE_UNKNOWN && oldType != JSVAL_TYPE_DOUBLE; + /* Completely invalidate the local variable. */ forgetEntry(target); target->resetUnsynced(); /* Constants are easy to propagate. */ if (top->isConstant()) { - target->setCopyOf(NULL); - target->setNotCopied(); - target->setConstant(Jsvalify(top->getValue())); + target->clear(); + target->setConstant(top->getValue()); + if (trySyncType && target->isType(oldType)) + target->type.sync(); return; } @@ -1247,23 +2241,17 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) * condition does not hold, force it to hold by swapping in-place. */ FrameEntry *backing = top; - bool copied = false; if (top->isCopy()) { backing = top->copyOf(); JS_ASSERT(backing->trackerIndex() < top->trackerIndex()); - if (backing < target) { + if (backing < target || isTemporary(backing)) { /* local.idx < backing.idx means local cannot be a copy yet */ if (target->trackerIndex() < backing->trackerIndex()) swapInTracker(backing, target); - target->setNotCopied(); target->setCopyOf(backing); - if (backing->isTypeKnown()) - target->setType(backing->getKnownType()); - else - target->type.invalidate(); - target->data.invalidate(); - target->isNumber = backing->isNumber; + if (trySyncType && target->isType(oldType)) + target->type.sync(); return; } @@ -1285,17 +2273,14 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) * but even so there's a quick workaround. We take all copies of the * backing fe, and redirect them to be copies of the destination. */ - for (uint32 i = backing->trackerIndex() + 1; i < tracker.nentries; i++) { + for (uint32_t i = backing->trackerIndex() + 1; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; - if (fe >= sp) + if (deadEntry(fe)) continue; - if (fe->isCopy() && fe->copyOf() == backing) { + if (fe->isCopy() && fe->copyOf() == backing) fe->setCopyOf(target); - copied = true; - } } } - backing->setNotCopied(); /* * This is valid from the top->isCopy() path because we're guaranteed a @@ -1305,72 +2290,121 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) if (backing->trackerIndex() < target->trackerIndex()) swapInTracker(backing, target); - /* - * Move the backing store down - we spill registers here, but we could be - * smarter and re-use the type reg. - */ - RegisterID reg = tempRegForData(backing); - target->data.setRegister(reg); - regstate[reg].reassociate(target); + if (backing->isType(JSVAL_TYPE_DOUBLE)) { + FPRegisterID fpreg = tempFPRegForData(backing); + target->setType(JSVAL_TYPE_DOUBLE); + target->data.setFPRegister(fpreg); + regstate(fpreg).reassociate(target); + } else { + /* + * Move the backing store down - we spill registers here, but we could be + * smarter and re-use the type reg. If we need registers for both the type + * and data in the backing, make sure we keep the other components pinned. + * There is nothing else to keep us from evicting the backing's registers. + */ + if (backing->type.inRegister()) + pinReg(backing->type.reg()); + RegisterID reg = tempRegForData(backing); + if (backing->type.inRegister()) + unpinReg(backing->type.reg()); + target->data.setRegister(reg); + regstate(reg).reassociate(target); - if (typeChange) { if (backing->isTypeKnown()) { target->setType(backing->getKnownType()); } else { - RegisterID reg = tempRegForType(backing); - target->type.setRegister(reg); - regstate[reg].reassociate(target); + pinReg(reg); + RegisterID typeReg = tempRegForType(backing); + unpinReg(reg); + target->type.setRegister(typeReg); + regstate(typeReg).reassociate(target); } - } else { - if (!wasSynced) - masm.storeTypeTag(ImmType(backing->getKnownType()), addressOf(target)); - target->type.setMemory(); } - if (!backing->isTypeKnown()) - backing->type.invalidate(); - backing->data.invalidate(); backing->setCopyOf(target); - backing->isNumber = target->isNumber; - JS_ASSERT(top->copyOf() == target); - /* - * Right now, |backing| is a copy of |target| (note the reversal), but - * |target| is not marked as copied. This is an optimization so uncopy() - * may avoid frame traversal. - * - * There are two cases where we must set the copy bit, however: - * - The fixup phase redirected more copies to |target|. - * - An immediate pop is not guaranteed. - */ - if (copied || !popGuaranteed) - target->setCopied(); + if (trySyncType && target->isType(oldType)) + target->type.sync(); } void -FrameState::shimmy(uint32 n) +FrameState::shimmy(uint32_t n) { - JS_ASSERT(sp - n >= spBase); - int32 depth = 0 - int32(n); - storeTop(peek(depth - 1), true); + JS_ASSERT(a->sp - n >= a->spBase); + int32_t depth = 0 - int32_t(n); + storeTop(peek(depth - 1)); popn(n); } void -FrameState::shift(int32 n) +FrameState::shift(int32_t n) { JS_ASSERT(n < 0); - JS_ASSERT(sp + n - 1 >= spBase); - storeTop(peek(n - 1), true); + JS_ASSERT(a->sp + n - 1 >= a->spBase); + storeTop(peek(n - 1)); pop(); } void -FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr) +FrameState::swap() +{ + // A B + + dupAt(-2); + // A B A + + dupAt(-2); + // A B A B + + shift(-3); + // B B A + + shimmy(1); + // B A +} + +void +FrameState::forgetKnownDouble(FrameEntry *fe) { + /* + * Forget all information indicating fe is a double, so we can use GPRs for its + * contents. We currently need to do this in order to use the entry in MICs/PICs + * or to construct its ValueRemat. :FIXME: this needs to get fixed. + */ + JS_ASSERT(!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)); + + RegisterID typeReg = allocReg(); + RegisterID dataReg = allocReg(); + + /* Copy into a different FP register, as breakDouble can modify fpreg. */ + FPRegisterID fpreg = allocFPReg(); + masm.moveDouble(tempFPRegForData(fe), fpreg); + masm.breakDouble(fpreg, typeReg, dataReg); + + forgetAllRegs(fe); + fe->resetUnsynced(); + fe->clear(); + + regstate(typeReg).associate(fe, RematInfo::TYPE); + regstate(dataReg).associate(fe, RematInfo::DATA); + fe->type.setRegister(typeReg); + fe->data.setRegister(dataReg); + freeReg(fpreg); +} + +void +FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr, bool breakDouble) +{ + if (breakDouble && !fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) + forgetKnownDouble(fe); + if (fe->isConstant()) { vr = ValueRemat::FromConstant(fe->getValue()); + } else if (fe->isType(JSVAL_TYPE_DOUBLE)) { + FPRegisterID fpreg = tempFPRegForData(fe); + pinReg(fpreg); + vr = ValueRemat::FromFPRegister(fpreg); } else { // Pin the type register so it can't spill. MaybeRegisterID maybePinnedType = maybePinType(fe); @@ -1398,7 +2432,9 @@ FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr) void FrameState::unpinEntry(const ValueRemat &vr) { - if (!vr.isConstant()) { + if (vr.isFPRegister()) { + unpinReg(vr.fpReg()); + } else if (!vr.isConstant()) { if (!vr.isTypeKnown()) unpinReg(vr.typeReg()); unpinReg(vr.dataReg()); @@ -1412,7 +2448,7 @@ FrameState::ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat if (!vr.isDataSynced || !vr.isTypeSynced) masm.storeValue(vr, addressOf(fe)); #elif defined JS_NUNBOX32 - if (vr.isConstant()) { + if (vr.isConstant() || vr.isFPRegister()) { if (!vr.isDataSynced || !vr.isTypeSynced) masm.storeValue(vr.value(), addressOf(fe)); } else { @@ -1441,6 +2477,8 @@ AllocHelper(RematInfo &info, MaybeRegisterID &maybe) void FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc) { + alloc.rhsNeedsRemat = false; + if (!fe->isTypeKnown()) { alloc.lhsType = tempRegForType(fe); pinReg(alloc.lhsType.reg()); @@ -1448,7 +2486,7 @@ FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc) alloc.lhsData = tempRegForData(fe); - if (!freeRegs.empty()) { + if (!freeRegs.empty(Registers::AvailRegs)) { alloc.result = allocReg(); masm.move(alloc.lhsData.reg(), alloc.result); alloc.lhsNeedsRemat = false; @@ -1460,6 +2498,8 @@ FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc) if (alloc.lhsType.isSet()) unpinReg(alloc.lhsType.reg()); + + alloc.lhsFP = alloc.rhsFP = allocFPReg(); } void @@ -1504,6 +2544,46 @@ FrameState::ensureFullRegs(FrameEntry *fe, MaybeRegisterID *type, MaybeRegisterI } } +inline bool +FrameState::binaryEntryLive(FrameEntry *fe) const +{ + /* + * Compute whether fe is live after the binary operation performed at the current + * bytecode. This is similar to variableLive except that it returns false for the + * top two stack entries and special cases LOCALINC/ARGINC and friends, which fuse + * a binary operation before writing over the local/arg. + */ + JS_ASSERT(cx->typeInferenceEnabled()); + + if (deadEntry(fe, 2)) + return false; + + switch (JSOp(*a->PC)) { + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + if (fe - a->locals == (int) GET_SLOTNO(a->PC)) + return false; + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + if (fe - a->args == (int) GET_SLOTNO(a->PC)) + return false; + default:; + } + + JS_ASSERT(fe != a->callee_); + + /* Arguments are always treated as live within inline frames, see bestEvictReg. */ + if (a->parent && fe < a->locals) + return true; + + /* Caller must check that no copies are invalidated by rewriting the entry. */ + return fe >= a->spBase || variableLive(fe, a->PC); +} + void FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc, bool needsResult) @@ -1539,6 +2619,16 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo pinReg(alloc.rhsType.reg()); } + /* + * Allocate floating point registers. These are temporaries with no pre-existing data; + * floating point registers are only allocated for known doubles, and BinaryAlloc is not + * used for such operations. + */ + JS_ASSERT(!backingLeft->isType(JSVAL_TYPE_DOUBLE)); + JS_ASSERT(!backingRight->isType(JSVAL_TYPE_DOUBLE)); + alloc.lhsFP = allocFPReg(); + alloc.rhsFP = allocFPReg(); + bool commu; switch (op) { case JSOP_EQ: @@ -1563,9 +2653,8 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo } /* - * Data is a little more complicated. If the op is MUL, not all CPUs - * have multiplication on immediates, so a register is needed. Also, - * if the op is not commutative, the LHS _must_ be in a register. + * Allocate data registers. If the op is not commutative, the LHS + * _must_ be in a register. */ JS_ASSERT_IF(lhs->isConstant(), !rhs->isConstant()); JS_ASSERT_IF(rhs->isConstant(), !lhs->isConstant()); @@ -1574,27 +2663,22 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo if (backingLeft->data.inMemory()) { alloc.lhsData = tempRegForData(lhs); pinReg(alloc.lhsData.reg()); - } else if (op == JSOP_MUL || !commu) { + } else if (!commu) { JS_ASSERT(lhs->isConstant()); alloc.lhsData = allocReg(); alloc.extraFree = alloc.lhsData; masm.move(Imm32(lhs->getValue().toInt32()), alloc.lhsData.reg()); } } - if (!alloc.rhsData.isSet()) { - if (backingRight->data.inMemory()) { - alloc.rhsData = tempRegForData(rhs); - pinReg(alloc.rhsData.reg()); - } else if (op == JSOP_MUL) { - JS_ASSERT(rhs->isConstant()); - alloc.rhsData = allocReg(); - alloc.extraFree = alloc.rhsData; - masm.move(Imm32(rhs->getValue().toInt32()), alloc.rhsData.reg()); - } + if (!alloc.rhsData.isSet() && backingRight->data.inMemory()) { + alloc.rhsData = tempRegForData(rhs); + pinReg(alloc.rhsData.reg()); } alloc.lhsNeedsRemat = false; alloc.rhsNeedsRemat = false; + alloc.resultHasRhs = false; + alloc.undoResult = false; if (!needsResult) goto skip; @@ -1605,7 +2689,25 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo * this point, if for some reason either must be in a register, that has * already been guaranteed at this point. */ - if (!freeRegs.empty()) { + + /* + * Try to reuse operand registers without syncing for ADD and constant SUB, + * so long as the backing for the operand is dead. + */ + if (cx->typeInferenceEnabled() && + backingLeft->data.inRegister() && !binaryEntryLive(backingLeft) && + (op == JSOP_ADD || (op == JSOP_SUB && backingRight->isConstant())) && + (lhs == backingLeft || hasOnlyCopy(backingLeft, lhs))) { + alloc.result = backingLeft->data.reg(); + alloc.undoResult = true; + alloc.resultHasRhs = false; + goto skip; + } + + if (cx->typeInferenceEnabled()) + evictDeadEntries(true); + + if (!freeRegs.empty(Registers::AvailRegs)) { /* Free reg - just grab it. */ alloc.result = allocReg(); if (!alloc.lhsData.isSet()) { @@ -1617,6 +2719,45 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo masm.move(alloc.lhsData.reg(), alloc.result); alloc.resultHasRhs = false; } + } else if (cx->typeInferenceEnabled()) { + /* No free regs. Evict a register or reuse one of the operands. */ + bool leftInReg = backingLeft->data.inRegister(); + bool rightInReg = backingRight->data.inRegister(); + + /* If the LHS/RHS types are in registers, don't use them for the result. */ + uint32_t mask = Registers::AvailRegs; + if (backingLeft->type.inRegister()) + mask &= ~Registers::maskReg(backingLeft->type.reg()); + if (backingRight->type.inRegister()) + mask &= ~Registers::maskReg(backingRight->type.reg()); + + RegisterID result = bestEvictReg(mask, true).reg(); + if (!commu && rightInReg && backingRight->data.reg() == result) { + /* Can't put the result in the RHS for non-commutative operations. */ + alloc.result = allocReg(); + masm.move(alloc.lhsData.reg(), alloc.result); + } else { + alloc.result = result; + if (leftInReg && result == backingLeft->data.reg()) { + alloc.lhsNeedsRemat = true; + unpinReg(result); + takeReg(result); + } else if (rightInReg && result == backingRight->data.reg()) { + alloc.rhsNeedsRemat = true; + alloc.resultHasRhs = true; + unpinReg(result); + takeReg(result); + } else { + JS_ASSERT(!regstate(result).isPinned()); + takeReg(result); + if (leftInReg) { + masm.move(alloc.lhsData.reg(), result); + } else { + masm.move(alloc.rhsData.reg(), result); + alloc.resultHasRhs = true; + } + } + } } else { /* * No free regs. Find a good candidate to re-use. Best candidates don't @@ -1664,6 +2805,15 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo unpinReg(backingRight->data.reg()); } +void +FrameState::rematBinary(FrameEntry *lhs, FrameEntry *rhs, const BinaryAlloc &alloc, Assembler &masm) +{ + if (alloc.rhsNeedsRemat) + masm.loadPayload(addressForDataRemat(rhs), alloc.rhsData.reg()); + if (alloc.lhsNeedsRemat) + masm.loadPayload(addressForDataRemat(lhs), alloc.lhsData.reg()); +} + MaybeRegisterID FrameState::maybePinData(FrameEntry *fe) { @@ -1693,3 +2843,54 @@ FrameState::maybeUnpinReg(MaybeRegisterID reg) unpinReg(reg.reg()); } +uint32_t +FrameState::allocTemporary() +{ + if (temporariesTop == temporaries + TEMPORARY_LIMIT) + return UINT32_MAX; + FrameEntry *fe = temporariesTop++; + fe->lastLoop = 0; + fe->temporary = true; + return fe - temporaries; +} + +void +FrameState::clearTemporaries() +{ + JS_ASSERT(!a->parent); + + for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) { + if (!fe->isTracked()) + continue; + if (fe->isCopied()) + uncopy(fe); + forgetAllRegs(fe); + fe->resetSynced(); + } + + temporariesTop = temporaries; +} + +Vector * +FrameState::getTemporaryCopies(Uses uses) +{ + /* :XXX: handle OOM */ + Vector *res = NULL; + + for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) { + if (!fe->isTracked()) + continue; + if (fe->isCopied()) { + for (uint32_t i = fe->trackerIndex() + 1; i < tracker.nentries; i++) { + FrameEntry *nfe = tracker[i]; + if (!deadEntry(nfe, uses.nuses) && nfe->isCopy() && nfe->copyOf() == fe) { + if (!res) + res = OffTheBooks::new_< Vector >(cx); + res->append(TemporaryCopy(addressOf(nfe), addressOf(fe))); + } + } + } + } + + return res; +} diff --git a/deps/mozjs/js/src/methodjit/FrameState.h b/deps/mozjs/js/src/methodjit/FrameState.h index b95016bd02f..ea929301414 100644 --- a/deps/mozjs/js/src/methodjit/FrameState.h +++ b/deps/mozjs/js/src/methodjit/FrameState.h @@ -40,6 +40,7 @@ #if !defined jsjaeger_framestate_h__ && defined JS_METHODJIT #define jsjaeger_framestate_h__ +#include "jsanalyze.h" #include "jsapi.h" #include "methodjit/MachineRegs.h" #include "methodjit/FrameEntry.h" @@ -51,19 +52,30 @@ namespace js { namespace mjit { struct Uses { - explicit Uses(uint32 nuses) + explicit Uses(uint32_t nuses) : nuses(nuses) { } - uint32 nuses; + uint32_t nuses; }; struct Changes { - explicit Changes(uint32 nchanges) + explicit Changes(uint32_t nchanges) : nchanges(nchanges) { } - uint32 nchanges; + uint32_t nchanges; }; +struct TemporaryCopy { + TemporaryCopy(JSC::MacroAssembler::Address copy, JSC::MacroAssembler::Address temporary) + : copy(copy), temporary(temporary) + {} + JSC::MacroAssembler::Address copy; + JSC::MacroAssembler::Address temporary; +}; + +class StubCompiler; +class LoopState; + /* * The FrameState keeps track of values on the frame during compilation. * The compiler can query FrameState for information about arguments, locals, @@ -81,11 +93,11 @@ struct Changes { * * Observations: * - * 1) We totally blow away known information quite often; branches, merge points. - * 2) Every time we need a slow call, we must sync everything. - * 3) Efficient side-exits need to quickly deltize state snapshots. - * 4) Syncing is limited to constants and registers. - * 5) Once a value is tracked, there is no reason to "forget" it until #1. + * 1) Every time we need a slow call, we must sync everything. + * 2) Efficient side-exits need to quickly deltize state snapshots. + * 3) Syncing is limited to constants and registers. + * 4) Entries are not forgotten unless they are entirely in memory and are + * not constants or copies. * * With these in mind, we want to make sure that the compiler doesn't degrade * badly as functions get larger. @@ -108,10 +120,12 @@ class FrameState typedef JSC::MacroAssembler::RegisterID RegisterID; typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; typedef JSC::MacroAssembler::Address Address; + typedef JSC::MacroAssembler::AbsoluteAddress AbsoluteAddress; typedef JSC::MacroAssembler::Jump Jump; typedef JSC::MacroAssembler::Imm32 Imm32; + typedef JSC::MacroAssembler::ImmPtr ImmPtr; - static const uint32 InvalidIndex = 0xFFFFFFFF; + static const uint32_t InvalidIndex = 0xFFFFFFFF; struct Tracker { Tracker() @@ -126,13 +140,13 @@ class FrameState nentries = 0; } - FrameEntry * operator [](uint32 n) const { + FrameEntry * operator [](uint32_t n) const { JS_ASSERT(n < nentries); return entries[n]; } FrameEntry **entries; - uint32 nentries; + uint32_t nentries; }; /* @@ -188,7 +202,6 @@ class FrameState fe_ = fe; type_ = type; - JS_ASSERT(!save_); } /* Change ownership. */ @@ -231,41 +244,49 @@ class FrameState /* Hack - simplifies register allocation for pairs. */ FrameEntry *save_; - + /* Part of the FrameEntry that owns the FE. */ RematInfo::RematType type_; }; + struct ActiveFrame; + FrameState *thisFromCtor() { return this; } public: - FrameState(JSContext *cx, JSScript *script, JSFunction *fun, Assembler &masm); + FrameState(JSContext *cx, Compiler &cc, Assembler &masm, StubCompiler &stubcc); ~FrameState(); - bool init(); /* - * Pushes a synced slot. + * Pushes a synced slot that may have a known type. */ - inline void pushSynced(); + inline void pushSynced(JSValueType knownType); /* * Pushes a slot that has a known, synced type and payload. */ - inline void pushSyncedType(JSValueType type); + inline void pushSynced(JSValueType knownType, RegisterID reg); /* - * Pushes a slot that has a known, synced type and payload. + * Pushes a constant value. */ - inline void pushSynced(JSValueType type, RegisterID reg); + inline void push(const Value &v); /* - * Pushes a constant value. + * Loads a value from memory and pushes it. If reuseBase is set, the + * Compiler owns the register and it should be reused if possible. */ - inline void push(const Value &v); + inline void push(Address address, JSValueType knownType, bool reuseBase = false); /* - * Loads a value from memory and pushes it. + * Loads a word from memory and pushes it. If reuseBase is set, the + * Compiler owns the register and it should be reused if possible. + * It takes an address and loads/pushes an unboxed word of a given non-double type. */ - inline void push(Address address); + inline void pushWord(Address address, JSValueType knownType, bool reuseBase = false); + + /* Loads a value from memory into a register pair, returning the register. */ + inline void loadIntoRegisters(Address address, bool reuseBase, + RegisterID *ptypeReg, RegisterID *pdataReg); /* * Pushes a known type and allocated payload onto the operation stack. @@ -273,9 +294,39 @@ class FrameState inline void pushTypedPayload(JSValueType type, RegisterID payload); /* - * Pushes a type register and data register pair. + * Clobbers a stack entry with a type register and data register pair, + * converting to the specified known type if necessary. If the type is + * JSVAL_TYPE_DOUBLE, the registers are converted into a floating point + * register, which is returned. + */ + inline FPRegisterID storeRegs(int32_t depth, RegisterID type, RegisterID data, + JSValueType knownType); + inline FPRegisterID pushRegs(RegisterID type, RegisterID data, JSValueType knownType); + + /* + * Load an address into a frame entry in registers. For handling call paths + * where merge() would otherwise reload from the wrong address. */ - inline void pushRegs(RegisterID type, RegisterID data); + inline void reloadEntry(Assembler &masm, Address address, FrameEntry *fe); + + /* Push a value which is definitely a double. */ + void pushDouble(FPRegisterID fpreg); + void pushDouble(Address address); + + /* Ensure that fe is definitely a double. It must already be either int or double. */ + void ensureDouble(FrameEntry *fe); + + /* Revert an entry just converted to double by ensureDouble. */ + void ensureInteger(FrameEntry *fe); + + /* + * Emit code to masm ensuring that all in memory slots thought to be + * doubles are in fact doubles. + */ + void ensureInMemoryDoubles(Assembler &masm); + + /* Forget that fe is definitely a double. */ + void forgetKnownDouble(FrameEntry *fe); /* * Pushes a known type and allocated payload onto the operation stack. @@ -300,28 +351,22 @@ class FrameState * * If asInt32 is set to true, then the FS will attempt to optimize * syncing the type as int32. Only use this parameter when the fast-path - * guaranteed that the stack slot was guarded to be an int32 originally. + * guaranteed that the stack slot was guarded to be an int32_t originally. * * For example, checking LHS and RHS as ints guarantees that if the LHS - * was synced, then popping both and pushing a maybe-int32 does not need + * was synced, then popping both and pushing a maybe-int32_t does not need * to be synced. */ - inline void pushNumber(MaybeRegisterID payload, bool asInt32 = false); + inline void pushNumber(RegisterID payload, bool asInt32 = false); /* - * Pushes an int32 onto the operation stack. This is a specialized version - * of pushNumber. The caller must guarantee that (a) an int32 is to be + * Pushes an int32_t onto the operation stack. This is a specialized version + * of pushNumber. The caller must guarantee that (a) an int32_t is to be * pushed on the inline path, and (b) if any slow path pushes a double, * the slow path also stores the double to memory. */ inline void pushInt32(RegisterID payload); - /* - * Pushes an initializer with specified payload, storing whether it is an array - * or object whose contents can be initialized in fast paths. - */ - inline void pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj); - /* * Pops a value off the operation stack, freeing any of its resources. */ @@ -331,26 +376,37 @@ class FrameState * Pops a number of values off the operation stack, freeing any of their * resources. */ - inline void popn(uint32 n); + inline void popn(uint32_t n); /* * Returns true iff lhs and rhs are copies of the same FrameEntry. */ inline bool haveSameBacking(FrameEntry *lhs, FrameEntry *rhs); + /* If the rhs to a binary operation directly copies the lhs, uncopy the lhs. */ + void separateBinaryEntries(FrameEntry *lhs, FrameEntry *rhs); + /* * Temporarily increase and decrease local variable depth. */ - inline void enterBlock(uint32 n); - inline void leaveBlock(uint32 n); + inline void enterBlock(uint32_t n); + inline void leaveBlock(uint32_t n); // Pushes a copy of a slot (formal argument, local variable, or stack slot) // onto the operation stack. - void pushLocal(uint32 n); - void pushArg(uint32 n); + void pushLocal(uint32_t n); + void pushArg(uint32_t n); void pushCallee(); void pushThis(); - inline void learnThisIsObject(); + void pushCopyOf(FrameEntry *fe); + inline void setThis(RegisterID reg); + inline void syncThis(); + inline void learnThisIsObject(bool unsync = true); + + inline FrameEntry *getStack(uint32_t slot); + inline FrameEntry *getLocal(uint32_t slot); + inline FrameEntry *getArg(uint32_t slot); + inline FrameEntry *getSlotEntry(uint32_t slot); /* * Allocates a temporary register for a FrameEntry's type. The register @@ -368,17 +424,21 @@ class FrameState */ inline RegisterID tempRegForType(FrameEntry *fe, RegisterID fallback); + inline void loadTypeIntoReg(const FrameEntry *fe, RegisterID reg); + inline void loadDataIntoReg(const FrameEntry *fe, RegisterID reg); + /* * Returns a register that is guaranteed to contain the frame entry's * data payload. The compiler may not modify the contents of the register. * The compiler should NOT explicitly free it. */ inline RegisterID tempRegForData(FrameEntry *fe); + inline FPRegisterID tempFPRegForData(FrameEntry *fe); /* * Same as above, except register must match identically. */ - inline RegisterID tempRegInMaskForData(FrameEntry *fe, uint32 mask); + inline AnyRegisterID tempRegInMaskForData(FrameEntry *fe, uint32_t mask); /* * Same as above, except loads into reg (using masm) if the entry does not @@ -386,6 +446,13 @@ class FrameState */ inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const; + /* + * For opcodes which expect to operate on an object, forget the entry if it + * is either a known constant or a non-object. This simplifies path + * generation in the Compiler for such unusual cases. + */ + inline void forgetMismatchedObject(FrameEntry *fe); + /* * Convert an integer to a double without applying * additional Register pressure. @@ -428,14 +495,6 @@ class FrameState void copyDataIntoReg(FrameEntry *fe, RegisterID exact); RegisterID copyDataIntoReg(Assembler &masm, FrameEntry *fe); - /* - * Allocates a FPRegister for a FrameEntry, such that the compiler - * can modify it in-place. The FrameState is not modified. - */ - FPRegisterID copyEntryIntoFPReg(FrameEntry *fe, FPRegisterID fpreg); - FPRegisterID copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, - FPRegisterID fpreg); - /* * Allocates a register for a FrameEntry's type, such that the compiler * can modify it in-place. The actual FE is not modified. @@ -452,10 +511,11 @@ class FrameState RegisterID copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe); /* - * Gets registers for the components of fe where needed, - * pins them and stores into vr. + * Gets registers for the components of fe where needed, pins them and + * stores into vr. If breakDouble is set, vr is guaranteed not to be a + * floating point register. */ - void pinEntry(FrameEntry *fe, ValueRemat &vr); + void pinEntry(FrameEntry *fe, ValueRemat &vr, bool breakDouble = true); /* Unpins registers from a call to pinEntry. */ void unpinEntry(const ValueRemat &vr); @@ -470,9 +530,12 @@ class FrameState MaybeRegisterID rhsData; MaybeRegisterID extraFree; RegisterID result; // mutable result reg + FPRegisterID lhsFP; // mutable scratch floating point reg + FPRegisterID rhsFP; // mutable scratch floating point reg bool resultHasRhs; // whether the result has the RHS instead of the LHS bool lhsNeedsRemat; // whether LHS needs memory remat bool rhsNeedsRemat; // whether RHS needs memory remat + bool undoResult; // whether to remat LHS/RHS by undoing operation }; /* @@ -486,6 +549,12 @@ class FrameState void allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc, bool resultNeeded = true); + /* + * After the result register in a BinaryAlloc has been clobbered, rematerialize + * the left or right side if necessary to restore the original values. + */ + void rematBinary(FrameEntry *lhs, FrameEntry *rhs, const BinaryAlloc &alloc, Assembler &masm); + /* Ensures that an FE has both type and data remat'd in registers. */ void ensureFullRegs(FrameEntry *fe, MaybeRegisterID *typeReg, MaybeRegisterID *dataReg); @@ -527,7 +596,7 @@ class FrameState * Frees a temporary register. If this register is being tracked, then it * is not spilled; the backing data becomes invalidated! */ - inline void freeReg(RegisterID reg); + inline void freeReg(AnyRegisterID reg); /* * Allocates a register. If none are free, one may be spilled from the @@ -535,21 +604,22 @@ class FrameState * then this is considered a compiler bug and an assert will fire. */ inline RegisterID allocReg(); + inline FPRegisterID allocFPReg(); /* * Allocates a register, except using a mask. */ - inline RegisterID allocReg(uint32 mask); + inline AnyRegisterID allocReg(uint32_t mask); /* * Allocates a specific register, evicting it if it's not avaliable. */ - void takeReg(RegisterID reg); + void takeReg(AnyRegisterID reg); /* * Returns a FrameEntry * for a slot on the operation stack. */ - inline FrameEntry *peek(int32 depth); + inline FrameEntry *peek(int32_t depth); /* * Fully stores a FrameEntry at an arbitrary address. popHint specifies @@ -564,13 +634,10 @@ class FrameState void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); void loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); - /* - * Stores the top stack slot back to a slot. - */ - void storeLocal(uint32 n, bool popGuaranteed = false, bool typeChange = true); - void storeArg(uint32 n, bool popGuaranteed = false); - void storeTop(FrameEntry *target, bool popGuaranteed = false, bool typeChange = true); - void finishStore(FrameEntry *fe, bool closed); + /* Stores the top stack slot back to a local or slot. */ + void storeLocal(uint32_t n, bool popGuaranteed = false); + void storeArg(uint32_t n, bool popGuaranteed = false); + void storeTop(FrameEntry *target); /* * Restores state from a slow path. @@ -588,40 +655,66 @@ class FrameState */ void syncAndKill(Registers kill, Uses uses, Uses ignored); void syncAndKill(Registers kill, Uses uses) { syncAndKill(kill, uses, Uses(0)); } + void syncAndKill(Uses uses) { syncAndKill(Registers(Registers::AvailAnyRegs), uses, Uses(0)); } /* Syncs and kills everything. */ void syncAndKillEverything() { - syncAndKill(Registers(Registers::AvailRegs), Uses(frameSlots())); + syncAndKill(Registers(Registers::AvailAnyRegs), Uses(frameSlots())); } /* - * Clear all tracker entries, syncing all outstanding stores in the process. - * The stack depth is in case some merge points' edges did not immediately - * precede the current instruction. + * Throw away the entire frame state, without syncing anything. + * This can only be called after a syncAndKill() against all registers. */ - inline void syncAndForgetEverything(uint32 newStackDepth); + void forgetEverything(); + + void syncAndForgetEverything() + { + syncAndKillEverything(); + forgetEverything(); + } /* - * Same as above, except the stack depth is not changed. This is used for - * branching opcodes. + * Discard the entire framestate forcefully. */ - void syncAndForgetEverything(); + void discardFrame(); /* - * Throw away the entire frame state, without syncing anything. - * This can only be called after a syncAndKill() against all registers. + * Make a copy of the current frame state, and restore from that snapshot. + * The stack depth must match between the snapshot and restore points. */ - void forgetEverything(); + FrameEntry *snapshotState(); + void restoreFromSnapshot(FrameEntry *snapshot); /* - * Discard the entire framestate forcefully. + * Tries to sync and shuffle registers in accordance with the register state + * at target, constructing that state if necessary. Forgets all constants and + * copies, and nothing can be pinned. Keeps the top Uses in registers; if Uses + * is non-zero the state may not actually be consistent with target. */ - void discardFrame(); + bool syncForBranch(jsbytecode *target, Uses uses); + void syncForAllocation(RegisterAllocation *alloc, bool inlineReturn, Uses uses); + + /* Discards the current frame state and updates to a new register allocation. */ + bool discardForJoin(RegisterAllocation *&alloc, uint32_t stackDepth); + + RegisterAllocation * computeAllocation(jsbytecode *target); + + /* Return whether the register state is consistent with that at target. */ + bool consistentRegisters(jsbytecode *target); /* - * Mark an existing slot with a type. + * Load all registers to update from either the current register state (if synced + * is unset) or a synced state (if synced is set) to target. */ - inline void learnType(FrameEntry *fe, JSValueType type); + void prepareForJump(jsbytecode *target, Assembler &masm, bool synced); + + /* + * Mark an existing slot with a type. unsync indicates whether type is already synced. + * Do not call this on entries which might be copied. + */ + inline void learnType(FrameEntry *fe, JSValueType type, bool unsync = true); + inline void learnType(FrameEntry *fe, JSValueType type, RegisterID payload); /* * Forget a type, syncing in the process. @@ -633,6 +726,20 @@ class FrameState */ void discardFe(FrameEntry *fe); + /* Compiler-owned metadata about stack entries, reset on push/pop/copy. */ + struct StackEntryExtra { + bool initArray; + JSObject *initObject; + types::TypeSet *types; + JSAtom *name; + void reset() { PodZero(this); } + }; + StackEntryExtra& extra(const FrameEntry *fe) { + JS_ASSERT(fe >= a->args && fe < a->sp); + return extraArray[fe - entries]; + } + StackEntryExtra& extra(uint32_t slot) { return extra(entries + slot); } + /* * Helper function. Tests if a slot's type is null. Condition must * be Equal or NotEqual. @@ -675,6 +782,8 @@ class FrameState */ inline Jump testObject(Assembler::Condition cond, FrameEntry *fe); + inline Jump testGCThing(FrameEntry *fe); + /* * Helper function. Tests if a slot's type is primitive. Condition must * be Equal or NotEqual. @@ -687,17 +796,17 @@ class FrameState * no matter what. In addition, pinReg() can only be used on registers * which are associated with FrameEntries. */ - inline void pinReg(RegisterID reg); + inline void pinReg(AnyRegisterID reg) { regstate(reg).pin(); } /* * Unpins a previously pinned register. */ - inline void unpinReg(RegisterID reg); + inline void unpinReg(AnyRegisterID reg) { regstate(reg).unpin(); } /* * Same as unpinReg(), but does not restore the FrameEntry. */ - inline void unpinKilledReg(RegisterID reg); + inline void unpinKilledReg(AnyRegisterID reg); /* Pins a data or type register if one exists. */ MaybeRegisterID maybePinData(FrameEntry *fe); @@ -717,7 +826,12 @@ class FrameState /* * Dups an item n-deep in the stack. n must be < 0 */ - inline void dupAt(int32 n); + inline void dupAt(int32_t n); + + /* + * Syncs an item n-deep in the stack. + */ + inline void syncAt(int32_t n); /* * If the frameentry is a copy, give it its own registers. @@ -725,20 +839,26 @@ class FrameState */ inline void giveOwnRegs(FrameEntry *fe); - uint32 stackDepth() const { return sp - spBase; } + uint32_t stackDepth() const { return a->sp - a->spBase; } + + /* + * The stack depth of the current frame plus any locals and space + * for inlined frames, i.e. the difference between the end of the + * current fp and sp. + */ + uint32_t totalDepth() const { return a->depth + a->script->nfixed + stackDepth(); } // Returns the number of entries in the frame, that is: // 2 for callee, this + // nargs + // nfixed + // currently pushed stack slots - uint32 frameSlots() const { return uint32(sp - entries); } - - // Returns the number of local variables and active stack slots. - uint32 localSlots() const { return uint32(sp - locals); } + uint32_t frameSlots() const { return uint32_t(a->sp - a->callee_); } #ifdef DEBUG void assertValidRegisterState() const; +#else + inline void assertValidRegisterState() const {}; #endif // Return an address, relative to the StackFrame, that represents where @@ -746,6 +866,9 @@ class FrameState // address, not its backing store. There is no guarantee that the memory // is coherent. Address addressOf(const FrameEntry *fe) const; + Address addressOf(uint32_t slot) const { return addressOf(a->callee_ + slot); } + + Address addressOfTop() const { return addressOf(a->sp); } // Returns an address, relative to the StackFrame, that represents where // this FrameEntry is backed in memory. This is not necessarily its @@ -753,6 +876,9 @@ class FrameState // to memory. The caller guarantees that the payload has been synced. Address addressForDataRemat(const FrameEntry *fe) const; + // Inside an inline frame, the address for the return value in the caller. + Address addressForInlineReturn(); + inline StateRemat dataRematInfo(const FrameEntry *fe) const; /* @@ -767,30 +893,87 @@ class FrameState * Moves the top of the stack down N slots, popping each item above it. * Caller guarantees the slots below have been observed and eviscerated. */ - void shimmy(uint32 n); + void shimmy(uint32_t n); /* * Stores the top item on the stack to a stack slot, count down from the * current stack depth. For example, to move the top (-1) to -3, you would * call shift(-2). */ - void shift(int32 n); + void shift(int32_t n); - // Notifies the frame that a local variable or argument slot is closed over. - inline void setClosedVar(uint32 slot); - inline void setClosedArg(uint32 slot); + /* Swaps the top two items on the stack. Requires two temp slots. */ + void swap(); inline void setInTryBlock(bool inTryBlock) { this->inTryBlock = inTryBlock; } - inline uint32 regsInUse() const { return Registers::AvailRegs & ~freeRegs.freeMask; } + inline uint32_t regsInUse() const { return Registers::AvailRegs & ~freeRegs.freeMask; } + + void setPC(jsbytecode *PC) { a->PC = PC; } + void setLoop(LoopState *loop) { this->loop = loop; } + + void pruneDeadEntries(); + + bool pushActiveFrame(JSScript *script, uint32_t argc); + void popActiveFrame(); + + uint32_t entrySlot(const FrameEntry *fe) const { + return frameSlot(a, fe); + } + + uint32_t outerSlot(const FrameEntry *fe) const { + ActiveFrame *na = a; + while (na->parent) { na = na->parent; } + return frameSlot(na, fe); + } + + bool isOuterSlot(const FrameEntry *fe) const { + if (isTemporary(fe)) + return true; + ActiveFrame *na = a; + while (na->parent) { na = na->parent; } + return fe < na->spBase && fe != na->callee_; + } + +#ifdef DEBUG + const char * entryName(const FrameEntry *fe) const; + void dumpAllocation(RegisterAllocation *alloc); +#else + const char * entryName(const FrameEntry *fe) const { return NULL; } +#endif + const char * entryName(uint32_t slot) { return entryName(entries + slot); } + + /* Maximum number of analysis temporaries the FrameState can track. */ + static const uint32_t TEMPORARY_LIMIT = 10; + + uint32_t allocTemporary(); /* -1 if limit reached. */ + void clearTemporaries(); + inline FrameEntry *getTemporary(uint32_t which); + + /* + * Return NULL or a new vector with all current copies of temporaries, + * excluding those about to be popped per 'uses'. + */ + Vector *getTemporaryCopies(Uses uses); + + inline void syncAndForgetFe(FrameEntry *fe, bool markSynced = false); + inline void forgetLoopReg(FrameEntry *fe); + + /* + * Get an address for the specified name access in another script. + * The compiler owns the result's base register. + */ + inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access); + inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access, + RegisterID reg); private: - inline RegisterID allocReg(FrameEntry *fe, RematInfo::RematType type); - inline void forgetReg(RegisterID reg); - RegisterID evictSomeReg(uint32 mask); - void evictReg(RegisterID reg); + inline AnyRegisterID allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type); + inline void forgetReg(AnyRegisterID reg); + AnyRegisterID evictSomeReg(uint32_t mask); + void evictReg(AnyRegisterID reg); inline FrameEntry *rawPush(); inline void addToTracker(FrameEntry *fe); @@ -804,17 +987,17 @@ class FrameState inline void syncType(FrameEntry *fe); inline void syncData(FrameEntry *fe); - inline FrameEntry *getOrTrack(uint32 index); - inline FrameEntry *getLocal(uint32 slot); - inline FrameEntry *getArg(uint32 slot); + /* For a frame entry whose value is dead, mark as synced. */ + inline void fakeSync(FrameEntry *fe); + inline FrameEntry *getCallee(); inline FrameEntry *getThis(); + inline FrameEntry *getOrTrack(uint32_t index); + inline void forgetAllRegs(FrameEntry *fe); inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); - void pushCopyOf(uint32 index); #if defined JS_NUNBOX32 - void syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, - FrameEntry *bottom) const; + void syncFancy(Assembler &masm, Registers avail, int trackerIndex) const; #endif inline bool tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const; void resetInternalState(); @@ -831,79 +1014,246 @@ class FrameState FrameEntry *walkTrackerForUncopy(FrameEntry *original); FrameEntry *walkFrameForUncopy(FrameEntry *original); + /* Whether fe is the only copy of backing. */ + bool hasOnlyCopy(FrameEntry *backing, FrameEntry *fe); + /* * All registers in the FE are forgotten. If it is copied, it is uncopied * beforehand. */ void forgetEntry(FrameEntry *fe); - FrameEntry *entryFor(uint32 index) const { - JS_ASSERT(entries[index].isTracked()); - return &entries[index]; + /* Stack and temporary entries whose contents should be disregarded. */ + bool deadEntry(const FrameEntry *fe, unsigned uses = 0) const { + return (fe >= (a->sp - uses) && fe < temporaries) || fe >= temporariesTop; + } + + RegisterState & regstate(AnyRegisterID reg) { + JS_ASSERT(reg.reg_ < Registers::TotalAnyRegisters); + return regstate_[reg.reg_]; } - RegisterID evictSomeReg() { return evictSomeReg(Registers::AvailRegs); } - uint32 indexOf(int32 depth) const { - JS_ASSERT(uint32((sp + depth) - entries) < feLimit()); - return uint32((sp + depth) - entries); + const RegisterState & regstate(AnyRegisterID reg) const { + JS_ASSERT(reg.reg_ < Registers::TotalAnyRegisters); + return regstate_[reg.reg_]; } - uint32 indexOfFe(FrameEntry *fe) const { - JS_ASSERT(uint32(fe - entries) < feLimit()); - return uint32(fe - entries); + + AnyRegisterID bestEvictReg(uint32_t mask, bool includePinned) const; + void evictDeadEntries(bool includePinned); + + inline analyze::Lifetime * variableLive(FrameEntry *fe, jsbytecode *pc) const; + inline bool binaryEntryLive(FrameEntry *fe) const; + void relocateReg(AnyRegisterID reg, RegisterAllocation *alloc, Uses uses); + + bool isThis(const FrameEntry *fe) const { + return fe == a->this_; + } + + inline bool isConstructorThis(const FrameEntry *fe) const; + + bool isArg(const FrameEntry *fe) const { + return a->script->function() && fe >= a->args && fe - a->args < a->script->function()->nargs; } - uint32 feLimit() const { return script->nslots + nargs + 2; } - inline bool isClosedVar(uint32 slot); - inline bool isClosedArg(uint32 slot); + bool isLocal(const FrameEntry *fe) const { + return fe >= a->locals && fe - a->locals < a->script->nfixed; + } + + bool isTemporary(const FrameEntry *fe) const { + JS_ASSERT_IF(fe >= temporaries, fe < temporariesTop); + return fe >= temporaries; + } + + int32_t frameOffset(const FrameEntry *fe, ActiveFrame *a) const; + Address addressOf(const FrameEntry *fe, ActiveFrame *a) const; + uint32_t frameSlot(ActiveFrame *a, const FrameEntry *fe) const; + + void associateReg(FrameEntry *fe, RematInfo::RematType type, AnyRegisterID reg); + + inline void modifyReg(AnyRegisterID reg); + + MaybeJump guardArrayLengthBase(FrameEntry *obj, Int32Key key); private: JSContext *cx; - JSScript *script; - JSFunction *fun; - uint32 nargs; Assembler &masm; + Compiler &cc; + StubCompiler &stubcc; - /* All allocated registers. */ - Registers freeRegs; + /* State for the active stack frame. */ - /* Cache of FrameEntry objects. */ - FrameEntry *entries; + struct ActiveFrame { + ActiveFrame() { PodZero(this); } - FrameEntry *callee_; - FrameEntry *this_; + ActiveFrame *parent; - /* Base pointer for arguments. */ - FrameEntry *args; + /* Number of values between the start of the outer frame and the start of this frame. */ + uint32_t depth; - /* Base pointer for local variables. */ - FrameEntry *locals; + JSScript *script; + jsbytecode *PC; + analyze::ScriptAnalysis *analysis; - /* Base pointer for the stack. */ - FrameEntry *spBase; + /* Indexes into the main FrameEntry buffer of entries for this frame. */ + FrameEntry *callee_; + FrameEntry *this_; + FrameEntry *args; + FrameEntry *locals; + FrameEntry *spBase; + FrameEntry *sp; + }; + ActiveFrame *a; - /* Dynamic stack pointer. */ - FrameEntry *sp; + /* Common buffer of frame entries. */ + FrameEntry *entries; + uint32_t nentries; + + /* Compiler-owned metadata for stack contents. */ + StackEntryExtra *extraArray; /* Vector of tracked slot indexes. */ Tracker tracker; +#if defined JS_NUNBOX32 + mutable ImmutableSync reifier; +#endif + /* * Register ownership state. This can't be used alone; to find whether an * entry is active, you must check the allocated registers. */ - RegisterState regstate[Assembler::TotalRegisters]; + RegisterState regstate_[Registers::TotalAnyRegisters]; -#if defined JS_NUNBOX32 - mutable ImmutableSync reifier; -#endif + /* All unallocated registers. */ + Registers freeRegs; + + /* Stack of active loops. */ + LoopState *loop; + + /* + * Track state for analysis temporaries. The meaning of these temporaries + * is opaque to the frame state, which just tracks where they are stored. + */ + FrameEntry *temporaries; + FrameEntry *temporariesTop; - JSPackedBool *closedVars; - JSPackedBool *closedArgs; - bool eval; - bool usesArguments; bool inTryBlock; }; +/* + * Register allocation overview. We want to allocate registers at the same time + * as we emit code, in a single forward pass over the script. This is good both + * for compilation speed and for design simplicity; we allocate registers for + * variables and temporaries as the compiler needs them. To get a good allocation, + * however, we need knowledge of which variables will be used in the future and + * in what order --- we must prioritize keeping variables in registers which + * will be used soon, and evict variables after they are no longer needed. + * We get this from the analyze::LifetimeScript analysis, an initial backwards + * pass over the script. + * + * Combining a backwards lifetime pass with a forward allocation pass in this + * way produces a Linear-scan register allocator. These can generate code at + * a speed close to that produced by a graph coloring register allocator, + * at a fraction of the compilation time. + */ + +/* Register allocation to use at a join point. */ +struct RegisterAllocation { + private: + typedef JSC::MacroAssembler::RegisterID RegisterID; + typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; + + /* Entry for an unassigned register at the join point. */ + static const uint32_t UNASSIGNED_REGISTER = UINT32_MAX; + + /* + * In the body of a loop, entry for an unassigned register that has not been + * used since the start of the loop. We do not finalize the register state + * at the start of a loop body until after generating code for the entire loop, + * so we can decide on which variables to carry around the loop after seeing + * them accessed early on in the body. + */ + static const uint32_t LOOP_REGISTER = uint32_t(-2); + + /* + * Assignment of registers to payloads. Type tags are always in memory, + * except for known doubles in FP registers. These are indexes into the + * frame's entries[] buffer, not slots. + */ + uint32_t regstate_[Registers::TotalAnyRegisters]; + + /* Mask for regstate entries indicating if the slot is synced. */ + static const uint32_t SYNCED = 0x80000000; + + uint32_t & regstate(AnyRegisterID reg) { + JS_ASSERT(reg.reg_ < Registers::TotalAnyRegisters); + return regstate_[reg.reg_]; + } + + public: + RegisterAllocation(bool forLoop) + { + uint32_t entry = forLoop ? (uint32_t) LOOP_REGISTER : (uint32_t) UNASSIGNED_REGISTER; + for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + bool avail = Registers::maskReg(reg) & Registers::AvailAnyRegs; + regstate_[i] = avail ? entry : UNASSIGNED_REGISTER; + } + } + + bool assigned(AnyRegisterID reg) { + return regstate(reg) != UNASSIGNED_REGISTER && regstate(reg) != LOOP_REGISTER; + } + + bool loop(AnyRegisterID reg) { + return regstate(reg) == LOOP_REGISTER; + } + + bool synced(AnyRegisterID reg) { + JS_ASSERT(assigned(reg)); + return regstate(reg) & SYNCED; + } + + uint32_t index(AnyRegisterID reg) { + JS_ASSERT(assigned(reg)); + return regstate(reg) & ~SYNCED; + } + + void set(AnyRegisterID reg, uint32_t index, bool synced) { + JS_ASSERT(index != LOOP_REGISTER && index != UNASSIGNED_REGISTER); + regstate(reg) = index | (synced ? SYNCED : 0); + } + + void setUnassigned(AnyRegisterID reg) { + regstate(reg) = UNASSIGNED_REGISTER; + } + + bool synced() { + for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) { + if (assigned(AnyRegisterID::fromRaw(i))) + return false; + } + return true; + } + + void clearLoops() { + for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + if (loop(reg)) + setUnassigned(reg); + } + } + + bool hasAnyReg(uint32_t n) { + for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) { + AnyRegisterID reg = AnyRegisterID::fromRaw(i); + if (assigned(reg) && index(reg) == n) + return true; + } + return false; + } +}; + class AutoPreserveAcrossSyncAndKill; } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/ICChecker.h b/deps/mozjs/js/src/methodjit/ICChecker.h index 01119bf6b0c..0ee107a5673 100644 --- a/deps/mozjs/js/src/methodjit/ICChecker.h +++ b/deps/mozjs/js/src/methodjit/ICChecker.h @@ -50,21 +50,21 @@ namespace mjit { #if defined DEBUG && defined JS_CPU_ARM static inline void -CheckInstMask(void *addr, uint32 mask, uint32 expected) +CheckInstMask(void *addr, uint32_t mask, uint32_t expected) { - uint32 inst = *static_cast(addr); + uint32_t inst = *static_cast(addr); JS_ASSERT((inst & mask) == expected); } static inline void -CheckIsLDR(JSC::CodeLocationLabel label, uint8 rd) +CheckIsLDR(JSC::CodeLocationLabel label, uint8_t rd) { JS_ASSERT((rd & 0xf) == rd); CheckInstMask(label.executableAddress(), 0xfc50f000, 0xe4100000 | (rd << 12)); } static inline void -CheckIsBLX(JSC::CodeLocationLabel label, uint8 rsrc) +CheckIsBLX(JSC::CodeLocationLabel label, uint8_t rsrc) { JS_ASSERT((rsrc & 0xf) == rsrc); CheckInstMask(label.executableAddress(), 0xfff000ff, 0xe1200030 | rsrc); diff --git a/deps/mozjs/js/src/methodjit/ICLabels.h b/deps/mozjs/js/src/methodjit/ICLabels.h index 2edaead2d23..ca87fb08a6b 100644 --- a/deps/mozjs/js/src/methodjit/ICLabels.h +++ b/deps/mozjs/js/src/methodjit/ICLabels.h @@ -54,29 +54,14 @@ namespace js { namespace mjit { namespace ic { -/* - * On x64 and ARM, we record offsets into the labels data structures at runtime - * instead of using hardcoded offsets into the instruction stream, as we do on - * x86. - * - * This is done on x64 because of variable-width instruction encoding when - * using the extended register set. It is done on ARM for ease of - * implementation. - */ - -#if defined JS_CPU_X64 || defined JS_CPU_ARM || defined JS_CPU_SPARC -# define JS_HAS_IC_LABELS -#endif - /* GetPropCompiler */ struct GetPropLabels : MacroAssemblerTypedefs { friend class ::ICOffsetInitializer; void setValueLoad(MacroAssembler &masm, Label fastPathRejoin, Label fastValueLoad) { int offset = masm.differenceBetween(fastPathRejoin, fastValueLoad); -#ifdef JS_HAS_IC_LABELS inlineValueLoadOffset = offset; -#endif + /* * Note: the offset between the type and data loads for x86 is asserted * in NunboxAssembler::loadValueWithAddressOffsetPatch. @@ -98,17 +83,17 @@ struct GetPropLabels : MacroAssemblerTypedefs { return fastPathRejoin.instructionAtOffset(getDslotsLoadOffset()); } - void setInlineShapeData(MacroAssembler &masm, Label shapeGuard, DataLabel32 inlineShape) { + void setInlineShapeData(MacroAssembler &masm, Label shapeGuard, DataLabelPtr inlineShape) { int offset = masm.differenceBetween(shapeGuard, inlineShape); setInlineShapeOffset(offset); } - CodeLocationDataLabel32 getInlineShapeData(CodeLocationLabel fastShapeGuard) { - return fastShapeGuard.dataLabel32AtOffset(getInlineShapeOffset()); + CodeLocationDataLabelPtr getInlineShapeData(CodeLocationLabel fastShapeGuard) { + return fastShapeGuard.dataLabelPtrAtOffset(getInlineShapeOffset()); } /* - * Note: on x64, the base is the inlineShapeLabel DataLabel32, whereas on other + * Note: on x64, the base is the inlineShapeLabel DataLabelPtr, whereas on other * platforms the base is the shapeGuard. */ template @@ -137,53 +122,36 @@ struct GetPropLabels : MacroAssemblerTypedefs { /* Offset-based interface */ void setDslotsLoadOffset(int offset) { -#ifdef JS_HAS_IC_LABELS dslotsLoadOffset = offset; -#endif JS_ASSERT(offset == dslotsLoadOffset); } void setInlineShapeOffset(int offset) { -#ifdef JS_HAS_IC_LABELS inlineShapeOffset = offset; -#endif JS_ASSERT(offset == inlineShapeOffset); } void setStubShapeJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS stubShapeJumpOffset = offset; -#endif JS_ASSERT(offset == stubShapeJumpOffset); } int getInlineShapeJumpOffset() { -#if defined JS_CPU_X64 - return getInlineShapeOffset() + INLINE_SHAPE_JUMP; -#else - return POST_INST_OFFSET(INLINE_SHAPE_JUMP); -#endif + return POST_INST_OFFSET(inlineShapeJumpOffset); } void setInlineShapeJumpOffset(int offset) { - JS_ASSERT(INLINE_SHAPE_JUMP == offset); + inlineShapeJumpOffset = offset; + JS_ASSERT(offset == inlineShapeJumpOffset); } int getInlineTypeJumpOffset() { -#if defined JS_CPU_X86 || defined JS_CPU_X64 - return INLINE_TYPE_JUMP; -#elif defined JS_CPU_ARM || defined JS_CPU_SPARC return POST_INST_OFFSET(inlineTypeJumpOffset); -#endif } void setInlineTypeJumpOffset(int offset) { -#if defined JS_CPU_X86 || defined JS_CPU_X64 - JS_ASSERT(INLINE_TYPE_JUMP == offset); -#elif defined JS_CPU_ARM || defined JS_CPU_SPARC inlineTypeJumpOffset = offset; JS_ASSERT(offset == inlineTypeJumpOffset); -#endif } int getInlineShapeOffset() { @@ -198,69 +166,53 @@ struct GetPropLabels : MacroAssemblerTypedefs { private: /* Offset from storeBack to beginning of 'mov dslots, addr' */ - int32 dslotsLoadOffset : 8; + int32_t dslotsLoadOffset : 8; /* Offset from shapeGuard to end of shape comparison. */ - int32 inlineShapeOffset : 8; + int32_t inlineShapeOffset : 8; /* Offset from storeBack to end of value load. */ - int32 inlineValueLoadOffset : 8; + int32_t inlineValueLoadOffset : 8; /* * Offset from lastStubStart to end of shape jump. * TODO: We can redefine the location of lastStubStart to be * after the jump -- at which point this is always 0. */ - int32 stubShapeJumpOffset : 8; - -#if defined JS_CPU_X86 - static const int32 INLINE_SHAPE_JUMP = 12; - static const int32 INLINE_TYPE_JUMP = 12; -#elif defined JS_CPU_X64 - static const int32 INLINE_SHAPE_JUMP = 6; - static const int32 INLINE_TYPE_JUMP = 19; -#elif defined JS_CPU_ARM + int32_t stubShapeJumpOffset : 8; + /* Offset from the shape guard start to the shape guard jump. */ - static const int32 INLINE_SHAPE_JUMP = 12; + int32_t inlineShapeJumpOffset : 8; /* Offset from the fast path to the type guard jump. */ - int32 inlineTypeJumpOffset : 8; -#elif defined JS_CPU_SPARC - static const int32 INLINE_SHAPE_JUMP = 48; - static const int32 INLINE_TYPE_JUMP = 48; - /* Offset from the fast path to the type guard jump. */ - int32 inlineTypeJumpOffset : 8; -#endif + int32_t inlineTypeJumpOffset : 8; }; /* SetPropCompiler */ struct SetPropLabels : MacroAssemblerTypedefs { friend class ::ICOffsetInitializer; - void setInlineValueStore(MacroAssembler &masm, Label fastPathRejoin, DataLabel32 inlineValueStore, - const ValueRemat &vr) { + void setInlineValueStore(MacroAssembler &masm, Label fastPathRejoin, DataLabel32 inlineValueStore) { int offset = masm.differenceBetween(fastPathRejoin, inlineValueStore); - setInlineValueStoreOffset(offset, vr.isConstant(), vr.isTypeKnown()); + setInlineValueStoreOffset(offset); } - CodeLocationLabel getInlineValueStore(CodeLocationLabel fastPathRejoin, const ValueRemat &vr) { - return fastPathRejoin.labelAtOffset(getInlineValueStoreOffset(vr.isConstant(), - vr.isTypeKnown())); + CodeLocationLabel getInlineValueStore(CodeLocationLabel fastPathRejoin) { + return fastPathRejoin.labelAtOffset(getInlineValueStoreOffset()); } - void setInlineShapeData(MacroAssembler &masm, Label shapeGuard, DataLabel32 inlineShapeData) { + void setInlineShapeData(MacroAssembler &masm, Label shapeGuard, DataLabelPtr inlineShapeData) { int offset = masm.differenceBetween(shapeGuard, inlineShapeData); setInlineShapeDataOffset(offset); } - CodeLocationDataLabel32 getInlineShapeData(CodeLocationLabel fastPathStart, int shapeGuardOffset) { - return fastPathStart.dataLabel32AtOffset(shapeGuardOffset + getInlineShapeDataOffset()); + CodeLocationDataLabelPtr getInlineShapeData(CodeLocationLabel fastPathStart, int shapeGuardOffset) { + return fastPathStart.dataLabelPtrAtOffset(shapeGuardOffset + getInlineShapeDataOffset()); } - void setDslotsLoad(MacroAssembler &masm, Label fastPathRejoin, Label beforeLoad, - const ValueRemat &rhs) { + void setDslotsLoad(MacroAssembler &masm, Label fastPathRejoin, Label beforeLoad) { int offset = masm.differenceBetween(fastPathRejoin, beforeLoad); - setDslotsLoadOffset(offset, rhs.isConstant(), rhs.isTypeKnown()); + setDslotsLoadOffset(offset); } CodeLocationInstruction getDslotsLoad(CodeLocationLabel fastPathRejoin, const ValueRemat &vr) { @@ -288,61 +240,33 @@ struct SetPropLabels : MacroAssemblerTypedefs { /* Offset-based interface. */ - void setDslotsLoadOffset(int offset, bool isConstant, bool isTypeKnown) { -#if defined JS_HAS_IC_LABELS + void setDslotsLoadOffset(int offset) { dslotsLoadOffset = offset; JS_ASSERT(offset == dslotsLoadOffset); -#elif defined JS_CPU_X86 - JS_ASSERT_IF(isConstant, offset == INLINE_DSLOTS_BEFORE_CONSTANT); - JS_ASSERT_IF(isTypeKnown && !isConstant, offset == INLINE_DSLOTS_BEFORE_KTYPE); - JS_ASSERT_IF(!isTypeKnown, offset == INLINE_DSLOTS_BEFORE_DYNAMIC); -#else -# error -#endif } int getDslotsLoadOffset(const ValueRemat &vr) { -#if defined JS_CPU_X86 - if (vr.isConstant()) - return INLINE_DSLOTS_BEFORE_CONSTANT; - if (vr.isTypeKnown()) - return INLINE_DSLOTS_BEFORE_KTYPE; - return INLINE_DSLOTS_BEFORE_DYNAMIC; -#else (void) vr; return dslotsLoadOffset; -#endif } void setInlineShapeDataOffset(int offset) { -#ifdef JS_HAS_IC_LABELS inlineShapeDataOffset = offset; -#endif JS_ASSERT(offset == inlineShapeDataOffset); } void setStubShapeJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS stubShapeJumpOffset = offset; -#endif JS_ASSERT(offset == stubShapeJumpOffset); } - void setInlineValueStoreOffset(int offset, bool isConstant, bool isTypeKnown) { -#ifdef JS_HAS_IC_LABELS + void setInlineValueStoreOffset(int offset) { inlineValueStoreOffset = offset; JS_ASSERT(offset == inlineValueStoreOffset); -#elif defined JS_CPU_X86 - JS_ASSERT_IF(isConstant, offset == INLINE_VALUE_STORE_CONSTANT); - JS_ASSERT_IF(isTypeKnown && !isConstant, offset == INLINE_VALUE_STORE_KTYPE); - JS_ASSERT_IF(!isTypeKnown && !isConstant, offset == INLINE_VALUE_STORE_DYNAMIC); -#endif } void setInlineShapeJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS inlineShapeJumpOffset = offset; -#endif JS_ASSERT(offset == inlineShapeJumpOffset); } @@ -358,48 +282,27 @@ struct SetPropLabels : MacroAssemblerTypedefs { return POST_INST_OFFSET(stubShapeJumpOffset); } - int getInlineValueStoreOffset(bool isConstant, bool isTypeKnown) { -#ifdef JS_HAS_IC_LABELS + int getInlineValueStoreOffset() { return inlineValueStoreOffset; -#elif defined JS_CPU_X86 - if (isConstant) - return INLINE_VALUE_STORE_CONSTANT; - else if (isTypeKnown) - return INLINE_VALUE_STORE_KTYPE; - else - return INLINE_VALUE_STORE_DYNAMIC; -#endif } /* Offset from storeBack to beginning of 'mov dslots, addr'. */ -#if defined JS_CPU_X86 - static const int INLINE_DSLOTS_BEFORE_CONSTANT = -23; - static const int INLINE_DSLOTS_BEFORE_KTYPE = -19; - static const int INLINE_DSLOTS_BEFORE_DYNAMIC = -15; -#else - int32 dslotsLoadOffset : 8; -#endif + int32_t dslotsLoadOffset : 8; /* Offset from shapeGuard to end of shape comparison. */ - int32 inlineShapeDataOffset : 8; + int32_t inlineShapeDataOffset : 8; /* * Offset from lastStubStart to end of shape jump. * TODO: We can redefine the location of lastStubStart to be * after the jump -- at which point this is always 0. */ - int32 stubShapeJumpOffset : 8; + int32_t stubShapeJumpOffset : 8; -#if defined JS_CPU_X86 - static const int INLINE_VALUE_STORE_CONSTANT = -20; - static const int INLINE_VALUE_STORE_KTYPE = -16; - static const int INLINE_VALUE_STORE_DYNAMIC = -12; -#else - int32 inlineValueStoreOffset : 8; -#endif + int32_t inlineValueStoreOffset : 8; /* Offset from shapeGuard to the end of the shape jump. */ - int32 inlineShapeJumpOffset : 8; + int32_t inlineShapeJumpOffset : 8; }; /* BindNameCompiler */ @@ -407,9 +310,7 @@ struct BindNameLabels : MacroAssemblerTypedefs { friend class ::ICOffsetInitializer; void setInlineJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS inlineJumpOffset = offset; -#endif JS_ASSERT(offset == inlineJumpOffset); } @@ -427,9 +328,7 @@ struct BindNameLabels : MacroAssemblerTypedefs { } void setStubJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS stubJumpOffset = offset; -#endif JS_ASSERT(offset == stubJumpOffset); } @@ -448,10 +347,10 @@ struct BindNameLabels : MacroAssemblerTypedefs { private: /* Offset from shapeGuard to end of shape jump. */ - int32 inlineJumpOffset : 8; + int32_t inlineJumpOffset : 8; /* Offset from lastStubStart to end of the shape jump. */ - int32 stubJumpOffset : 8; + int32_t stubJumpOffset : 8; }; /* ScopeNameCompiler */ @@ -459,9 +358,7 @@ struct ScopeNameLabels : MacroAssemblerTypedefs { friend class ::ICOffsetInitializer; void setInlineJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS inlineJumpOffset = offset; -#endif JS_ASSERT(offset == inlineJumpOffset); } @@ -479,9 +376,7 @@ struct ScopeNameLabels : MacroAssemblerTypedefs { } void setStubJumpOffset(int offset) { -#ifdef JS_HAS_IC_LABELS stubJumpOffset = offset; -#endif JS_ASSERT(offset == stubJumpOffset); } @@ -500,10 +395,10 @@ struct ScopeNameLabels : MacroAssemblerTypedefs { private: /* Offset from fastPathStart to end of shape jump. */ - int32 inlineJumpOffset : 8; + int32_t inlineJumpOffset : 8; /* Offset from lastStubStart to end of the shape jump. */ - int32 stubJumpOffset : 8; + int32_t stubJumpOffset : 8; }; } /* namespace ic */ diff --git a/deps/mozjs/js/src/methodjit/ICRepatcher.h b/deps/mozjs/js/src/methodjit/ICRepatcher.h index 24b08b9634d..ca57a7f10bf 100644 --- a/deps/mozjs/js/src/methodjit/ICRepatcher.h +++ b/deps/mozjs/js/src/methodjit/ICRepatcher.h @@ -60,7 +60,7 @@ class Repatcher : public JSC::RepatchBuffer CodeLocationLabel label; public: - explicit Repatcher(JITScript *js) + explicit Repatcher(JITChunk *js) : JSC::RepatchBuffer(js->code), label(js->code.m_code.executableAddress()) { } @@ -88,14 +88,31 @@ class Repatcher : public JSC::RepatchBuffer */ CheckIsStubCall(call.labelAtOffset(0)); JSC::RepatchBuffer::relink(call.callAtOffset(-4), stub); +#elif defined JS_CPU_MIPS + /* + * Stub calls on MIPS look like this: + * + * lui v0, hi(stub) + * ori v0, v0, lo(stub) + * lui t9, hi(JaegerStubVeneer) + * ori t9, t9, lo(JaegerStubVeneer) + * jalr t9 + * nop + * call label -> xxx + * + * MIPS has to run stub calls through a veneer in order for THROW to + * work properly. The address that must be patched is the load into + * 'v0', not the load into 't9'. + */ + JSC::RepatchBuffer::relink(call.callAtOffset(-8), stub); #else # error #endif } /* Patch the offset of a Value load emitted by loadValueWithAddressOffsetPatch. */ - void patchAddressOffsetForValueLoad(CodeLocationLabel label, uint32 offset) { -#if defined JS_CPU_X64 || defined JS_CPU_ARM || defined JS_CPU_SPARC + void patchAddressOffsetForValueLoad(CodeLocationLabel label, uint32_t offset) { +#if defined JS_CPU_X64 || defined JS_CPU_ARM || defined JS_CPU_SPARC || defined JS_CPU_MIPS repatch(label.dataLabel32AtOffset(0), offset); #elif defined JS_CPU_X86 static const unsigned LOAD_TYPE_OFFSET = 6; @@ -114,8 +131,8 @@ class Repatcher : public JSC::RepatchBuffer #endif } - void patchAddressOffsetForValueStore(CodeLocationLabel label, uint32 offset, bool typeConst) { -#if defined JS_CPU_ARM || defined JS_CPU_X64 || defined JS_CPU_SPARC + void patchAddressOffsetForValueStore(CodeLocationLabel label, uint32_t offset, bool typeConst) { +#if defined JS_CPU_ARM || defined JS_CPU_X64 || defined JS_CPU_SPARC || defined JS_CPU_MIPS (void) typeConst; repatch(label.dataLabel32AtOffset(0), offset); #elif defined JS_CPU_X86 diff --git a/deps/mozjs/js/src/methodjit/ImmutableSync.cpp b/deps/mozjs/js/src/methodjit/ImmutableSync.cpp index 0f6787dc72e..d9f913aa7b6 100644 --- a/deps/mozjs/js/src/methodjit/ImmutableSync.cpp +++ b/deps/mozjs/js/src/methodjit/ImmutableSync.cpp @@ -47,20 +47,24 @@ using namespace js; using namespace js::mjit; -ImmutableSync::ImmutableSync(JSContext *cx, const FrameState &frame) - : cx(cx), entries(NULL), frame(frame), generation(0) +ImmutableSync::ImmutableSync() + : cx(NULL), entries(NULL), frame(NULL), avail(Registers::AvailRegs), generation(0) { } ImmutableSync::~ImmutableSync() { - cx->free_(entries); + if (cx) + cx->free_(entries); } bool -ImmutableSync::init(uint32 nentries) +ImmutableSync::init(JSContext *cx, const FrameState &frame, uint32_t nentries) { - entries = (SyncEntry *)cx->calloc_(sizeof(SyncEntry) * nentries); + this->cx = cx; + this->frame = &frame; + + entries = (SyncEntry *)OffTheBooks::calloc_(sizeof(SyncEntry) * nentries); return !!entries; } @@ -75,26 +79,29 @@ ImmutableSync::reset(Assembler *masm, Registers avail, FrameEntry *top, FrameEnt memset(regs, 0, sizeof(regs)); } -JSC::MacroAssembler::RegisterID -ImmutableSync::allocReg() +inline JSC::MacroAssembler::RegisterID +ImmutableSync::doAllocReg() { if (!avail.empty()) - return avail.takeAnyReg(); + return avail.takeAnyReg().reg(); - uint32 lastResort = FrameState::InvalidIndex; - uint32 evictFromFrame = FrameState::InvalidIndex; + uint32_t lastResort = FrameState::InvalidIndex; + uint32_t evictFromFrame = FrameState::InvalidIndex; /* Find something to evict. */ - for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { + for (uint32_t i = 0; i < Registers::TotalRegisters; i++) { RegisterID reg = RegisterID(i); if (!(Registers::maskReg(reg) & Registers::AvailRegs)) continue; - lastResort = 0; + if (frame->regstate(reg).isPinned()) + continue; + + lastResort = i; if (!regs[i]) { /* If the frame does not own this register, take it! */ - FrameEntry *fe = frame.regstate[i].usedBy(); + FrameEntry *fe = frame->regstate(reg).usedBy(); if (!fe) return reg; @@ -110,16 +117,17 @@ ImmutableSync::allocReg() } if (evictFromFrame != FrameState::InvalidIndex) { - FrameEntry *fe = frame.regstate[evictFromFrame].usedBy(); + RegisterID evict = RegisterID(evictFromFrame); + FrameEntry *fe = frame->regstate(evict).usedBy(); SyncEntry &e = entryFor(fe); - if (frame.regstate[evictFromFrame].type() == RematInfo::TYPE) { + if (frame->regstate(evict).type() == RematInfo::TYPE) { JS_ASSERT(!e.typeClobbered); e.typeClobbered = true; } else { JS_ASSERT(!e.dataClobbered); e.dataClobbered = true; } - return RegisterID(evictFromFrame); + return evict; } JS_ASSERT(lastResort != FrameState::InvalidIndex); @@ -138,11 +146,26 @@ ImmutableSync::allocReg() return reg; } +JSC::MacroAssembler::RegisterID +ImmutableSync::allocReg() +{ + RegisterID reg = doAllocReg(); + JS_ASSERT(!frame->regstate(reg).isPinned()); + return reg; +} + +void +ImmutableSync::freeReg(JSC::MacroAssembler::RegisterID reg) +{ + if (!frame->regstate(reg).isPinned()) + avail.putReg(reg); +} + inline ImmutableSync::SyncEntry & ImmutableSync::entryFor(FrameEntry *fe) { - JS_ASSERT(fe <= top); - SyncEntry &e = entries[frame.indexOfFe(fe)]; + JS_ASSERT(fe <= top || frame->isTemporary(fe)); + SyncEntry &e = entries[fe - frame->entries]; if (e.generation != generation) e.reset(generation); return e; @@ -151,10 +174,6 @@ ImmutableSync::entryFor(FrameEntry *fe) void ImmutableSync::sync(FrameEntry *fe) { -#ifdef DEBUG - top = fe; -#endif - if (fe->isCopy()) syncCopy(fe); else @@ -185,7 +204,7 @@ ImmutableSync::ensureTypeReg(FrameEntry *fe, SyncEntry &e) e.typeReg = allocReg(); e.hasTypeReg = true; regs[e.typeReg] = &e; - masm->loadTypeTag(frame.addressOf(fe), e.typeReg); + masm->loadTypeTag(frame->addressOf(fe), e.typeReg); return e.typeReg; } @@ -199,7 +218,7 @@ ImmutableSync::ensureDataReg(FrameEntry *fe, SyncEntry &e) e.dataReg = allocReg(); e.hasDataReg = true; regs[e.dataReg] = &e; - masm->loadPayload(frame.addressOf(fe), e.dataReg); + masm->loadPayload(frame->addressOf(fe), e.dataReg); return e.dataReg; } @@ -213,9 +232,9 @@ ImmutableSync::syncCopy(FrameEntry *fe) JS_ASSERT(!backing->isConstant()); - Address addr = frame.addressOf(fe); + Address addr = frame->addressOf(fe); - if (fe->isTypeKnown() && !e.learnedType) { + if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE) && !e.learnedType) { e.learnedType = true; e.type = fe->getKnownType(); } @@ -236,9 +255,9 @@ ImmutableSync::syncNormal(FrameEntry *fe) { SyncEntry &e = entryFor(fe); - Address addr = frame.addressOf(fe); + Address addr = frame->addressOf(fe); - if (fe->isTypeKnown()) { + if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE)) { e.learnedType = true; e.type = fe->getKnownType(); } @@ -259,21 +278,21 @@ ImmutableSync::syncNormal(FrameEntry *fe) } if (e.hasDataReg) { - avail.putReg(e.dataReg); + freeReg(e.dataReg); regs[e.dataReg] = NULL; } else if (!e.dataClobbered && fe->data.inRegister() && - frame.regstate[fe->data.reg()].usedBy()) { - avail.putReg(fe->data.reg()); + frame->regstate(fe->data.reg()).usedBy()) { + freeReg(fe->data.reg()); } if (e.hasTypeReg) { - avail.putReg(e.typeReg); + freeReg(e.typeReg); regs[e.typeReg] = NULL; } else if (!e.typeClobbered && fe->type.inRegister() && - frame.regstate[fe->type.reg()].usedBy()) { - avail.putReg(fe->type.reg()); + frame->regstate(fe->type.reg()).usedBy()) { + freeReg(fe->type.reg()); } } diff --git a/deps/mozjs/js/src/methodjit/ImmutableSync.h b/deps/mozjs/js/src/methodjit/ImmutableSync.h index 204dbab4189..d185cc7402b 100644 --- a/deps/mozjs/js/src/methodjit/ImmutableSync.h +++ b/deps/mozjs/js/src/methodjit/ImmutableSync.h @@ -70,7 +70,7 @@ class ImmutableSync * * They are separated for readability. */ - uint32 generation; + uint32_t generation; bool dataClobbered; bool typeClobbered; bool hasDataReg; @@ -80,7 +80,7 @@ class ImmutableSync RegisterID typeReg; JSValueType type; - void reset(uint32 gen) { + void reset(uint32_t gen) { dataClobbered = false; typeClobbered = false; hasDataReg = false; @@ -91,9 +91,9 @@ class ImmutableSync }; public: - ImmutableSync(JSContext *cx, const FrameState &frame); + ImmutableSync(); ~ImmutableSync(); - bool init(uint32 nentries); + bool init(JSContext *cx, const FrameState &frame, uint32_t nentries); void reset(Assembler *masm, Registers avail, FrameEntry *top, FrameEntry *bottom); void sync(FrameEntry *fe); @@ -103,7 +103,12 @@ class ImmutableSync void syncNormal(FrameEntry *fe); RegisterID ensureDataReg(FrameEntry *fe, SyncEntry &e); RegisterID ensureTypeReg(FrameEntry *fe, SyncEntry &e); + RegisterID allocReg(); + void freeReg(RegisterID reg); + + /* To be called only by allocReg. */ + RegisterID doAllocReg(); inline SyncEntry &entryFor(FrameEntry *fe); @@ -113,14 +118,14 @@ class ImmutableSync private: JSContext *cx; SyncEntry *entries; - const FrameState &frame; - uint32 nentries; + const FrameState *frame; + uint32_t nentries; Registers avail; Assembler *masm; SyncEntry *regs[Assembler::TotalRegisters]; FrameEntry *top; FrameEntry *bottom; - uint32 generation; + uint32_t generation; }; } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/InlineFrameAssembler.h b/deps/mozjs/js/src/methodjit/InlineFrameAssembler.h index 4a00c9ab67e..563702dc366 100644 --- a/deps/mozjs/js/src/methodjit/InlineFrameAssembler.h +++ b/deps/mozjs/js/src/methodjit/InlineFrameAssembler.h @@ -49,13 +49,13 @@ namespace js { namespace mjit { struct AdjustedFrame { - AdjustedFrame(uint32 baseOffset) + AdjustedFrame(uint32_t baseOffset) : baseOffset(baseOffset) { } - uint32 baseOffset; + uint32_t baseOffset; - JSC::MacroAssembler::Address addrOf(uint32 offset) { + JSC::MacroAssembler::Address addrOf(uint32_t offset) { return JSC::MacroAssembler::Address(JSFrameReg, baseOffset + offset); } }; @@ -76,8 +76,7 @@ class InlineFrameAssembler { Assembler &masm; FrameSize frameSize; // size of the caller's frame RegisterID funObjReg; // register containing the function object (callee) - jsbytecode *pc; // bytecode location at the caller call site - uint32 flags; // frame flags + uint32_t flags; // frame flags public: /* @@ -86,36 +85,41 @@ class InlineFrameAssembler { */ Registers tempRegs; - InlineFrameAssembler(Assembler &masm, ic::CallICInfo &ic, uint32 flags) - : masm(masm), pc(ic.pc), flags(flags) + InlineFrameAssembler(Assembler &masm, ic::CallICInfo &ic, uint32_t flags) + : masm(masm), flags(flags), tempRegs(Registers::AvailRegs) { frameSize = ic.frameSize; funObjReg = ic.funObjReg; - tempRegs.takeReg(ic.funPtrReg); tempRegs.takeReg(funObjReg); } - InlineFrameAssembler(Assembler &masm, Compiler::CallGenInfo &gen, uint32 flags) - : masm(masm), pc(gen.pc), flags(flags) + InlineFrameAssembler(Assembler &masm, Compiler::CallGenInfo &gen, uint32_t flags) + : masm(masm), flags(flags), tempRegs(Registers::AvailRegs) { frameSize = gen.frameSize; funObjReg = gen.funObjReg; tempRegs.takeReg(funObjReg); } - DataLabelPtr assemble(void *ncode) + DataLabelPtr assemble(void *ncode, jsbytecode *pc) { JS_ASSERT((flags & ~StackFrame::CONSTRUCTING) == 0); /* Generate StackFrame::initCallFrameCallerHalf. */ + /* Get the actual flags to write. */ + JS_ASSERT(!(flags & ~StackFrame::CONSTRUCTING)); + uint32_t flags = this->flags | StackFrame::FUNCTION; + if (frameSize.lowered(pc)) + flags |= StackFrame::LOWERED_CALL_APPLY; + DataLabelPtr ncodePatch; if (frameSize.isStatic()) { - uint32 frameDepth = frameSize.staticLocalSlots(); + uint32_t frameDepth = frameSize.staticLocalSlots(); AdjustedFrame newfp(sizeof(StackFrame) + frameDepth * sizeof(Value)); Address flagsAddr = newfp.addrOf(StackFrame::offsetOfFlags()); - masm.store32(Imm32(StackFrame::FUNCTION | flags), flagsAddr); + masm.store32(Imm32(flags), flagsAddr); Address prevAddr = newfp.addrOf(StackFrame::offsetOfPrev()); masm.storePtr(JSFrameReg, prevAddr); Address ncodeAddr = newfp.addrOf(StackFrame::offsetOfNcode()); @@ -131,11 +135,11 @@ class InlineFrameAssembler { * dynamic number of arguments) to VMFrame.regs, so we just load it * here to get the new frame pointer. */ - RegisterID newfp = tempRegs.takeAnyReg(); - masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.sp)), newfp); + RegisterID newfp = tempRegs.takeAnyReg().reg(); + masm.loadPtr(FrameAddress(VMFrame::offsetOfRegsSp()), newfp); Address flagsAddr(newfp, StackFrame::offsetOfFlags()); - masm.store32(Imm32(StackFrame::FUNCTION | flags), flagsAddr); + masm.store32(Imm32(flags), flagsAddr); Address prevAddr(newfp, StackFrame::offsetOfPrev()); masm.storePtr(JSFrameReg, prevAddr); Address ncodeAddr(newfp, StackFrame::offsetOfNcode()); diff --git a/deps/mozjs/js/src/methodjit/InvokeHelpers.cpp b/deps/mozjs/js/src/methodjit/InvokeHelpers.cpp index d65a4bb1b6c..f6f4c95f605 100644 --- a/deps/mozjs/js/src/methodjit/InvokeHelpers.cpp +++ b/deps/mozjs/js/src/methodjit/InvokeHelpers.cpp @@ -45,30 +45,25 @@ #include "jsiter.h" #include "jsnum.h" #include "jsxml.h" -#include "jsstaticcheck.h" #include "jsbool.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" #include "assembler/assembler/CodeLocation.h" #include "jsiter.h" #include "jstypes.h" #include "methodjit/StubCalls.h" -#include "jstracer.h" -#include "jspropertycache.h" #include "methodjit/MonoIC.h" #include "jsanalyze.h" #include "methodjit/BaseCompiler.h" #include "methodjit/ICRepatcher.h" +#include "vm/Debugger.h" #include "jsinterpinlines.h" -#include "jspropertycacheinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" -#include "jsstrinlines.h" #include "jsobjinlines.h" #include "jscntxtinlines.h" #include "jsatominlines.h" #include "StubCalls-inl.h" -#include "MethodJIT-inl.h" #include "jsautooplen.h" @@ -87,7 +82,7 @@ FindExceptionHandler(JSContext *cx) top: if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) { // The PC is updated before every stub call, so we can use it here. - unsigned offset = cx->regs().pc - script->main; + unsigned offset = cx->regs().pc - script->main(); JSTryNoteArray *tnarray = script->trynotes(); for (unsigned i = 0; i < tnarray->length; ++i) { @@ -113,13 +108,15 @@ FindExceptionHandler(JSContext *cx) if (tn->stackDepth > cx->regs().sp - fp->base()) continue; - jsbytecode *pc = script->main + tn->start + tn->length; - JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE); - JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth); + UnwindScope(cx, tn->stackDepth); + + jsbytecode *pc = script->main() + tn->start + tn->length; + cx->regs().pc = pc; + cx->regs().sp = fp->base() + tn->stackDepth; switch (tn->kind) { case JSTRY_CATCH: - JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK); + JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK); #if JS_HAS_GENERATORS /* Catch cannot intercept the closing of a generator. */ @@ -155,9 +152,9 @@ FindExceptionHandler(JSContext *cx) * pending exception. */ Value v = cx->getPendingException(); - JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENDITER); + JS_ASSERT(JSOp(*pc) == JSOP_ENDITER); cx->clearPendingException(); - ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject()); + bool ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject()); cx->regs().sp -= 1; if (!ok) goto top; @@ -177,37 +174,53 @@ static void InlineReturn(VMFrame &f) { JS_ASSERT(f.fp() != f.entryfp); - JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0)); - f.cx->stack.popInlineFrame(); + JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0)); + JS_ASSERT(!f.fp()->hasBlockChain()); + f.cx->stack.popInlineFrame(f.regs); + + DebugOnly op = JSOp(*f.regs.pc); + JS_ASSERT(op == JSOP_CALL || + op == JSOP_NEW || + op == JSOP_EVAL || + op == JSOP_FUNCALL || + op == JSOP_FUNAPPLY); + f.regs.pc += JSOP_CALL_LENGTH; } void JS_FASTCALL -stubs::SlowCall(VMFrame &f, uint32 argc) +stubs::SlowCall(VMFrame &f, uint32_t argc) { - Value *vp = f.regs.sp - (argc + 2); - - if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + CallArgs args = CallArgsFromSp(argc, f.regs.sp); + if (!InvokeKernel(f.cx, args)) THROW(); + + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); } void JS_FASTCALL -stubs::SlowNew(VMFrame &f, uint32 argc) +stubs::SlowNew(VMFrame &f, uint32_t argc) { - JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - - if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + CallArgs args = CallArgsFromSp(argc, f.regs.sp); + if (!InvokeConstructorKernel(f.cx, args)) THROW(); + + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); } -/* - * This function must only be called after the early prologue, since it depends - * on fp->exec.fun. - */ -static inline void -RemovePartialFrame(JSContext *cx, StackFrame *fp) +static inline bool +CheckStackQuota(VMFrame &f) { - cx->stack.popInlineFrame(); + JS_ASSERT(f.regs.sp == f.fp()->base()); + + f.stackLimit = f.cx->stack.space().getStackLimit(f.cx, DONT_REPORT_ERROR); + if (f.stackLimit) + return true; + + /* Remove the current partially-constructed frame before throwing. */ + f.cx->stack.popFrameAfterOverflow(); + js_ReportOverRecursed(f.cx); + + return false; } /* @@ -217,17 +230,8 @@ RemovePartialFrame(JSContext *cx, StackFrame *fp) void JS_FASTCALL stubs::HitStackQuota(VMFrame &f) { - /* Include space to push another frame. */ - uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME; - JS_ASSERT(f.regs.sp == f.fp()->base()); - StackSpace &space = f.cx->stack.space(); - if (space.bumpLimitWithinQuota(NULL, f.entryfp, f.regs.sp, nvals, &f.stackLimit)) - return; - - /* Remove the current partially-constructed frame before throwing. */ - RemovePartialFrame(f.cx, f.fp()); - js_ReportOverRecursed(f.cx); - THROW(); + if (!CheckStackQuota(f)) + THROW(); } /* @@ -235,7 +239,7 @@ stubs::HitStackQuota(VMFrame &f) * on fp->exec.fun. */ void * JS_FASTCALL -stubs::FixupArity(VMFrame &f, uint32 nactual) +stubs::FixupArity(VMFrame &f, uint32_t nactual) { JSContext *cx = f.cx; StackFrame *oldfp = f.fp(); @@ -245,148 +249,154 @@ stubs::FixupArity(VMFrame &f, uint32 nactual) /* * Grossssss! *move* the stack frame. If this ends up being perf-critical, * we can figure out how to spot-optimize it. Be careful to touch only the - * members that have been initialized by initCallFrameCallerHalf and the - * early prologue. + * members that have been initialized by the caller and early prologue. */ - uint32 flags = oldfp->isConstructingFlag(); - JSFunction *fun = oldfp->fun(); - void *ncode = oldfp->nativeReturnAddress(); + InitialFrameFlags initial = oldfp->initialFlags(); + JSFunction *fun = oldfp->fun(); + JSScript *script = fun->script(); + void *ncode = oldfp->nativeReturnAddress(); /* Pop the inline frame. */ f.regs.popPartialFrame((Value *)oldfp); /* Reserve enough space for a callee frame. */ - StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual, - fun, fun->script(), &flags, - f.entryfp, &f.stackLimit); - if (!newfp) { - /* - * The PC is not coherent with the current frame, so fix it up for - * exception handling. - */ - f.regs.pc = f.jit()->nativeToPC(ncode); + CallArgs args = CallArgsFromSp(nactual, f.regs.sp); + StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun, + script, ncode, initial, &f.stackLimit); + + if (!fp) { + f.regs.updateForNcode(f.jit(), ncode); + js_ReportOverRecursed(cx); THROWV(NULL); } - /* Reset the part of the stack frame set by the caller. */ - newfp->initCallFrameCallerHalf(cx, flags, ncode); - - /* Reset the part of the stack frame set by the prologue up to now. */ - newfp->initCallFrameEarlyPrologue(fun, nactual); - /* The caller takes care of assigning fp to regs. */ - return newfp; + return fp; } +struct ResetStubRejoin { + VMFrame &f; + ResetStubRejoin(VMFrame &f) : f(f) {} + ~ResetStubRejoin() { f.stubRejoin = 0; } +}; + void * JS_FASTCALL -stubs::CompileFunction(VMFrame &f, uint32 nactual) +stubs::CompileFunction(VMFrame &f, uint32_t argc) { /* - * We have a partially constructed frame. That's not really good enough to - * compile though because we could throw, so get a full, adjusted frame. + * Note: the stubRejoin kind for the frame was written before the call, and + * needs to be cleared out on all return paths (doing this directly in the + * IC stub will not handle cases where we recompiled or threw). */ - JSContext *cx = f.cx; - StackFrame *fp = f.fp(); - - /* - * Since we can only use members set by initCallFrameCallerHalf, - * we must carefully extract the callee from the nactual. - */ - JSObject &callee = fp->formalArgsEnd()[-(int(nactual) + 2)].toObject(); - JSFunction *fun = callee.getFunctionPrivate(); - JSScript *script = fun->script(); - - /* - * FixupArity/RemovePartialFrame expect to be called after the early - * prologue. - */ - fp->initCallFrameEarlyPrologue(fun, nactual); - - if (nactual != fp->numFormalArgs()) { - fp = (StackFrame *)FixupArity(f, nactual); - if (!fp) - return NULL; - } - - /* Finish frame initialization. */ - fp->initCallFrameLatePrologue(); + JS_ASSERT_IF(f.cx->typeInferenceEnabled(), f.stubRejoin); + ResetStubRejoin reset(f); + + InitialFrameFlags initial = f.fp()->initialFlags(); + f.regs.popPartialFrame((Value *)f.fp()); + + if (InitialFrameFlagsAreConstructing(initial)) + return UncachedNew(f, argc); + else if (InitialFrameFlagsAreLowered(initial)) + return UncachedLoweredCall(f, argc); + else + return UncachedCall(f, argc); +} - /* These would have been initialized by the prologue. */ - f.regs.prepareToRun(fp, script); +static inline bool +UncachedInlineCall(VMFrame &f, InitialFrameFlags initial, + void **pret, bool *unjittable, uint32_t argc) +{ + JSContext *cx = f.cx; + CallArgs args = CallArgsFromSp(argc, f.regs.sp); + JSFunction *newfun = args.callee().toFunction(); + JSScript *newscript = newfun->script(); - if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp)) - THROWV(NULL); + bool construct = InitialFrameFlagsAreConstructing(initial); - CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT); - if (status == Compile_Okay) - return script->getJIT(fp->isConstructing())->invokeEntry; + bool newType = construct && cx->typeInferenceEnabled() && + types::UseNewType(cx, f.script(), f.pc()); - /* Function did not compile... interpret it. */ - JSBool ok = Interpret(cx, fp); - InlineReturn(f); + types::TypeMonitorCall(cx, args, construct); - if (!ok) - THROWV(NULL); + /* Try to compile if not already compiled. */ + CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_Interpreter); + if (status == Compile_Error) { + /* A runtime exception was thrown, get out. */ + return false; + } + if (status == Compile_Abort) + *unjittable = true; - return NULL; -} + /* + * Make sure we are not calling from an inline frame if we need to make a + * call object for the callee, as doing so could trigger GC and cause + * jitcode discarding / frame expansion. + */ + if (f.regs.inlined() && newfun->isHeavyweight()) { + ExpandInlineFrames(cx->compartment); + JS_ASSERT(!f.regs.inlined()); + } -static inline bool -UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc) -{ - JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - JSObject &callee = vp->toObject(); - JSFunction *newfun = callee.getFunctionPrivate(); - JSScript *newscript = newfun->script(); + /* + * Preserve f.regs.fp while pushing the new frame, for the invariant that + * f.regs reflects the state when we entered the stub call. This handoff is + * tricky: we need to make sure that f.regs is not updated to the new + * frame, and we also need to ensure that cx->regs still points to f.regs + * when space is reserved, in case doing so throws an exception. + */ + FrameRegs regs = f.regs; /* Get pointer to new frame/slots, prepare arguments. */ - StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc, - newfun, newscript, &flags, - f.entryfp, &f.stackLimit); - if (JS_UNLIKELY(!newfp)) + if (!cx->stack.pushInlineFrame(cx, regs, args, *newfun, newscript, initial, &f.stackLimit)) return false; - /* Initialize frame, locals. */ - newfp->initCallFrame(cx, callee, newfun, argc, flags); - SetValueRangeToUndefined(newfp->slots(), newscript->nfixed); - - /* Officially push the frame. */ - cx->stack.pushInlineFrame(newscript, newfp, f.regs); - JS_ASSERT(newfp == f.fp()); + /* Finish the handoff to the new frame regs. */ + PreserveRegsGuard regsGuard(cx, regs); /* Scope with a call object parented by callee's parent. */ - if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, newfp)) + if (!regs.fp()->functionPrologue(cx)) return false; - /* Try to compile if not already compiled. */ - if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { - CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter); - if (status == Compile_Error) { - /* A runtime exception was thrown, get out. */ - InlineReturn(f); - return false; + /* + * If newscript was successfully compiled, run it. Skip for calls which + * will be constructing a new type object for 'this'. + */ + if (!newType) { + if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing())) { + if (jit->invokeEntry) { + *pret = jit->invokeEntry; + + /* Restore the old fp around and let the JIT code repush the new fp. */ + regs.popFrame((Value *) regs.fp()); + return true; + } } - if (status == Compile_Abort) - *unjittable = true; } - /* If newscript was successfully compiled, run it. */ - if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) { - *pret = jit->invokeEntry; - return true; + /* + * Otherwise, run newscript in the interpreter. Expand any inlined frame we + * are calling from, as the new frame is not associated with the VMFrame + * and will not have its prevpc info updated if frame expansion is + * triggered while interpreting. + */ + if (f.regs.inlined()) { + ExpandInlineFrames(cx->compartment); + JS_ASSERT(!f.regs.inlined()); + regs.fp()->resetInlinePrev(f.fp(), f.regs.pc); } - /* Otherwise, run newscript in the interpreter. */ bool ok = !!Interpret(cx, cx->fp()); - InlineReturn(f); + f.cx->stack.popInlineFrame(regs); + + if (ok) + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); *pret = NULL; return ok; } void * JS_FASTCALL -stubs::UncachedNew(VMFrame &f, uint32 argc) +stubs::UncachedNew(VMFrame &f, uint32_t argc) { UncachedCallResult ucr; UncachedNewHelper(f, argc, &ucr); @@ -394,84 +404,114 @@ stubs::UncachedNew(VMFrame &f, uint32 argc) } void -stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) +stubs::UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult *ucr) { ucr->init(); JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); + CallArgs args = CallArgsFromSp(argc, f.regs.sp); + /* Try to do a fast inline call before the general Invoke path. */ - if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpretedConstructor()) { - ucr->callee = &vp->toObject(); - if (!UncachedInlineCall(f, StackFrame::CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc)) + if (IsFunctionObject(args.calleev(), &ucr->fun) && ucr->fun->isInterpretedConstructor()) { + if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc)) THROW(); } else { - if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + if (!InvokeConstructorKernel(cx, args)) THROW(); + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); } } void * JS_FASTCALL -stubs::UncachedCall(VMFrame &f, uint32 argc) +stubs::UncachedCall(VMFrame &f, uint32_t argc) +{ + UncachedCallResult ucr; + UncachedCallHelper(f, argc, false, &ucr); + return ucr.codeAddr; +} + +void * JS_FASTCALL +stubs::UncachedLoweredCall(VMFrame &f, uint32_t argc) { UncachedCallResult ucr; - UncachedCallHelper(f, argc, &ucr); + UncachedCallHelper(f, argc, true, &ucr); return ucr.codeAddr; } void JS_FASTCALL -stubs::Eval(VMFrame &f, uint32 argc) +stubs::Eval(VMFrame &f, uint32_t argc) { - Value *vp = f.regs.sp - (argc + 2); + CallArgs args = CallArgsFromSp(argc, f.regs.sp); - if (!IsBuiltinEvalForScope(&f.fp()->scopeChain(), *vp)) { - if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + if (!IsBuiltinEvalForScope(&f.fp()->scopeChain(), args.calleev())) { + if (!InvokeKernel(f.cx, args)) THROW(); + + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); return; } JS_ASSERT(f.fp() == f.cx->fp()); - if (!DirectEval(f.cx, CallArgsFromVp(argc, vp))) + if (!DirectEval(f.cx, args)) THROW(); - f.regs.sp = vp + 1; + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); } void -stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) +stubs::UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallResult *ucr) { ucr->init(); JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - - if (IsFunctionObject(*vp, &ucr->callee)) { - ucr->callee = &vp->toObject(); - ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee); + CallArgs args = CallArgsFromSp(argc, f.regs.sp); + if (IsFunctionObject(args.calleev(), &ucr->fun)) { if (ucr->fun->isInterpreted()) { - if (!UncachedInlineCall(f, 0, &ucr->codeAddr, &ucr->unjittable, argc)) + InitialFrameFlags initial = lowered ? INITIAL_LOWERED : INITIAL_NONE; + if (!UncachedInlineCall(f, initial, &ucr->codeAddr, &ucr->unjittable, argc)) THROW(); return; } if (ucr->fun->isNative()) { - if (!CallJSNative(cx, ucr->fun->u.n.native, argc, vp)) + if (!CallJSNative(cx, ucr->fun->u.n.native, args)) THROW(); + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); return; } } - if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(argc, vp))) + if (!InvokeKernel(f.cx, args)) THROW(); + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); return; } -void JS_FASTCALL -stubs::PutActivationObjects(VMFrame &f) +static void +RemoveOrphanedNative(JSContext *cx, StackFrame *fp) { - JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj()); - f.fp()->putActivationObjects(); + /* + * Remove fp from the list of frames holding a reference on the orphaned + * native pools. If all the references have been removed, release all the + * pools. We don't release pools piecemeal as a pool can be referenced by + * multiple frames. + */ + JaegerCompartment *jc = cx->compartment->jaegerCompartment(); + if (jc->orphanedNativeFrames.empty()) + return; + for (unsigned i = 0; i < jc->orphanedNativeFrames.length(); i++) { + if (fp == jc->orphanedNativeFrames[i]) { + jc->orphanedNativeFrames[i] = jc->orphanedNativeFrames.back(); + jc->orphanedNativeFrames.popBack(); + break; + } + } + if (jc->orphanedNativeFrames.empty()) { + for (unsigned i = 0; i < jc->orphanedNativePools.length(); i++) + jc->orphanedNativePools[i]->release(); + jc->orphanedNativePools.clear(); + } } extern "C" void * @@ -479,51 +519,53 @@ js_InternalThrow(VMFrame &f) { JSContext *cx = f.cx; - // It's possible that from within RunTracer(), Interpret() returned with - // an error and finished the frame (i.e., called ScriptEpilogue), but has - // not yet performed an inline return. - // - // In this case, RunTracer() has no choice but to propagate the error - // up to the method JIT, and thus to this function. But ScriptEpilogue() - // has already been called. Detect this, and avoid double-finishing the - // frame. See HandleErrorInExcessFrame() and bug 624100. - if (f.fp()->finishedInInterpreter()) { - // If it's the last frame, just propagate the failure up again. - if (f.fp() == f.entryfp) - return NULL; + ExpandInlineFrames(cx->compartment); - InlineReturn(f); - } + // The current frame may have an associated orphaned native, if the native + // or SplatApplyArgs threw an exception. + RemoveOrphanedNative(cx, f.fp()); + + JS_ASSERT(!f.fp()->finishedInInterpreter()); // Make sure sp is up to date. JS_ASSERT(&cx->regs() == &f.regs); - // Call the throw hook if necessary - JSThrowHook handler = f.cx->debugHooks->throwHook; - if (handler) { - Value rval; - switch (handler(cx, cx->fp()->script(), cx->regs().pc, Jsvalify(&rval), - cx->debugHooks->throwHookData)) { - case JSTRAP_ERROR: - cx->clearPendingException(); - return NULL; - - case JSTRAP_RETURN: - cx->clearPendingException(); - cx->fp()->setReturnValue(rval); - return cx->jaegerCompartment()->forceReturnFromExternC(); - - case JSTRAP_THROW: - cx->setPendingException(rval); - break; + jsbytecode *pc = NULL; + for (;;) { + if (cx->isExceptionPending()) { + // Call the throw hook if necessary + JSThrowHook handler = cx->debugHooks->throwHook; + if (handler || !cx->compartment->getDebuggees().empty()) { + Value rval; + JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval); + if (st == JSTRAP_CONTINUE && handler) { + st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval, + cx->debugHooks->throwHookData); + } - default: - break; + switch (st) { + case JSTRAP_ERROR: + cx->clearPendingException(); + break; + + case JSTRAP_CONTINUE: + break; + + case JSTRAP_RETURN: + cx->clearPendingException(); + cx->fp()->setReturnValue(rval); + return cx->jaegerCompartment()->forceReturnFromExternC(); + + case JSTRAP_THROW: + cx->setPendingException(rval); + break; + + default: + JS_NOT_REACHED("bad onExceptionUnwind status"); + } + } } - } - jsbytecode *pc = NULL; - for (;;) { pc = FindExceptionHandler(cx); if (pc) break; @@ -534,7 +576,12 @@ js_InternalThrow(VMFrame &f) // and epilogues. RunTracer(), Interpret(), and Invoke() all // rely on this property. JS_ASSERT(!f.fp()->finishedInInterpreter()); - js_UnwindScope(cx, 0, cx->isExceptionPending()); + UnwindScope(cx, 0); + f.regs.sp = f.fp()->base(); + + if (cx->compartment->debugMode()) + js::ScriptDebugEpilogue(cx, f.fp(), false); + ScriptEpilogue(f.cx, f.fp(), false); // Don't remove the last frame, this is the responsibility of @@ -543,26 +590,56 @@ js_InternalThrow(VMFrame &f) if (f.entryfp == f.fp()) break; - JS_ASSERT(f.regs.sp == cx->regs().sp); + JS_ASSERT(&cx->regs() == &f.regs); InlineReturn(f); } - JS_ASSERT(f.regs.sp == cx->regs().sp); + JS_ASSERT(&cx->regs() == &f.regs); if (!pc) return NULL; StackFrame *fp = cx->fp(); JSScript *script = fp->script(); - return script->nativeCodeForPC(fp->isConstructing(), pc); -} -void JS_FASTCALL -stubs::CreateFunCallObject(VMFrame &f) -{ - JS_ASSERT(f.fp()->fun()->isHeavyweight()); - if (!js::CreateFunCallObject(f.cx, f.fp())) - THROW(); + /* + * Fall back to EnterMethodJIT and finish the frame in the interpreter. + * With type inference enabled, we may wipe out all JIT code on the + * stack without patching ncode values to jump to the interpreter, and + * thus can only enter JIT code via EnterMethodJIT (which overwrites + * its entry frame's ncode). See ClearAllFrames. + */ + cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); + + if (!script->ensureRanAnalysis(cx, NULL)) { + js_ReportOutOfMemory(cx); + return NULL; + } + + analyze::AutoEnterAnalysis enter(cx); + + /* + * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go + * back into the interpreter with a pending exception. This will cause + * it to immediately rethrow. + */ + if (cx->isExceptionPending()) { + JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK); + StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock(); + Value *vp = cx->regs().sp + blockObj.slotCount(); + SetValueRangeToUndefined(cx->regs().sp, vp); + cx->regs().sp = vp; + JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION); + cx->regs().sp[0] = cx->getPendingException(); + cx->clearPendingException(); + cx->regs().sp++; + cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; + cx->regs().fp()->setBlockChain(&blockObj); + } + + *f.oldregs = f.regs; + + return NULL; } void JS_FASTCALL @@ -580,490 +657,455 @@ stubs::CreateThis(VMFrame &f, JSObject *proto) void JS_FASTCALL stubs::ScriptDebugPrologue(VMFrame &f) { - js::ScriptDebugPrologue(f.cx, f.fp()); + Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script()); + JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp()); + switch (status) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromFastCall(); + return; + case JSTRAP_ERROR: + case JSTRAP_THROW: + THROW(); + default: + JS_NOT_REACHED("bad ScriptDebugPrologue status"); + } } void JS_FASTCALL stubs::ScriptDebugEpilogue(VMFrame &f) { + Probes::exitJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script()); if (!js::ScriptDebugEpilogue(f.cx, f.fp(), JS_TRUE)) THROW(); } -#ifdef JS_TRACER - -/* - * Called when an error is in progress and the topmost frame could not handle - * it. This will unwind to a given frame, or find and align to an exception - * handler in the process. - */ -static inline bool -HandleErrorInExcessFrame(VMFrame &f, StackFrame *stopFp, bool searchedTopmostFrame = true) +void JS_FASTCALL +stubs::ScriptProbeOnlyPrologue(VMFrame &f) { - JSContext *cx = f.cx; - - /* - * Callers of this called either Interpret() or JaegerShot(), which would - * have searched for exception handlers already. If we see stopFp, just - * return false. Otherwise, pop the frame, since it's guaranteed useless. - * - * Note that this also guarantees ScriptEpilogue() has been called. - */ - StackFrame *fp = cx->fp(); - if (searchedTopmostFrame) { - /* - * This is a special case meaning that fp->finishedInInterpreter() is - * true. If so, and fp == stopFp, our only choice is to propagate this - * error up, back to the method JIT, and then to js_InternalThrow, - * where this becomes a special case. See the comment there and bug - * 624100. - */ - if (fp == stopFp) - return false; - - /* - * Otherwise, the protocol here (like Invoke) is to assume that the - * execution mode finished the frame, and to just pop it. - */ - InlineReturn(f); - } + Probes::enterJSFun(f.cx, f.fp()->fun(), f.fp()->script()); +} - /* Remove the bottom frame. */ - bool returnOK = false; - for (;;) { - fp = cx->fp(); +void JS_FASTCALL +stubs::ScriptProbeOnlyEpilogue(VMFrame &f) +{ + Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script()); +} - /* Clear imacros. */ - if (fp->hasImacropc()) { - cx->regs().pc = fp->imacropc(); - fp->clearImacropc(); - } - JS_ASSERT(!fp->hasImacropc()); +void JS_FASTCALL +stubs::CrossChunkShim(VMFrame &f, void *edge_) +{ + DebugOnly edge = (CrossChunkEdge *) edge_; - /* If there's an exception and a handler, set the pc and leave. */ - if (cx->isExceptionPending()) { - jsbytecode *pc = FindExceptionHandler(cx); - if (pc) { - cx->regs().pc = pc; - returnOK = true; - break; - } - } + mjit::ExpandInlineFrames(f.cx->compartment); - /* Don't unwind if this was the entry frame. */ - if (fp == stopFp) - break; + JSScript *script = f.script(); + JS_ASSERT(edge->target < script->length); + JS_ASSERT(script->code + edge->target == f.pc()); - /* Unwind and return. */ - returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending())); - returnOK = ScriptEpilogue(cx, fp, returnOK); - InlineReturn(f); - } + CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(), + CompileRequest_Interpreter); + if (status == Compile_Error) + THROW(); - JS_ASSERT(&f.regs == &cx->regs()); - JS_ASSERT_IF(!returnOK, cx->fp() == stopFp); + void **addr = f.returnAddressLocation(); + *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline); - return returnOK; + f.fp()->setRejoin(StubRejoin(REJOIN_RESUME)); } -/* Returns whether the current PC has method JIT'd code. */ -static inline void * -AtSafePoint(JSContext *cx) -{ - StackFrame *fp = cx->fp(); - if (fp->hasImacropc()) - return NULL; +JS_STATIC_ASSERT(JSOP_NOP == 0); - JSScript *script = fp->script(); - return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc); -} +/* :XXX: common out with identical copy in Compiler.cpp */ +#if defined(JS_METHODJIT_SPEW) +static const char *OpcodeNames[] = { +# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name, +# include "jsopcode.tbl" +# undef OPDEF +}; +#endif -/* - * Interprets until either a safe point is reached that has method JIT'd - * code, or the current frame tries to return. - */ -static inline JSBool -PartialInterpret(VMFrame &f) +static void +FinishVarIncOp(VMFrame &f, RejoinState rejoin, Value ov, Value nv, Value *vp) { - JSContext *cx = f.cx; - StackFrame *fp = cx->fp(); + /* Finish an increment operation on a LOCAL or ARG. These do not involve property accesses. */ + JS_ASSERT(rejoin == REJOIN_POS || rejoin == REJOIN_BINARY); -#ifdef DEBUG - JSScript *script = fp->script(); - JS_ASSERT(!fp->finishedInInterpreter()); - JS_ASSERT(fp->hasImacropc() || - !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc)); -#endif + JSContext *cx = f.cx; - JSBool ok = JS_TRUE; - ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); + JSOp op = JSOp(*f.pc()); + const JSCodeSpec *cs = &js_CodeSpec[op]; - return ok; -} + unsigned i = GET_SLOTNO(f.pc()); + Value *var = (JOF_TYPE(cs->format) == JOF_LOCAL) ? f.fp()->slots() + i : &f.fp()->formalArg(i); -JS_STATIC_ASSERT(JSOP_NOP == 0); + if (rejoin == REJOIN_POS) { + double d = ov.toNumber(); + double N = (cs->format & JOF_INC) ? 1 : -1; + if (!nv.setNumber(d + N)) + types::TypeScript::MonitorOverflow(cx, f.script(), f.pc()); + } -/* - * Returns whether the current PC would return, or if the frame has already - * been completed. This distinction avoids re-entering the interpreter or JIT - * to complete a JSOP_RETURN. Instead, that edge case is handled in - * HandleFinishedFrame. We could consider reducing complexity, and making this - * function return only "finishedInInterpreter", and always using the full VM - * machinery to fully finish frames. - */ -static inline bool -FrameIsFinished(JSContext *cx) -{ - JSOp op = JSOp(*cx->regs().pc); - return (op == JSOP_RETURN || - op == JSOP_RETRVAL || - op == JSOP_STOP) - ? true - : cx->fp()->finishedInInterpreter(); + *var = nv; + *vp = (cs->format & JOF_POST) ? ov : nv; } - -/* Simulate an inline_return by advancing the pc. */ -static inline void -AdvanceReturnPC(JSContext *cx) +extern "C" void * +js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f) { - JS_ASSERT(*cx->regs().pc == JSOP_CALL || - *cx->regs().pc == JSOP_NEW || - *cx->regs().pc == JSOP_EVAL || - *cx->regs().pc == JSOP_FUNCALL || - *cx->regs().pc == JSOP_FUNAPPLY); - cx->regs().pc += JSOP_CALL_LENGTH; -} - + JSRejoinState jsrejoin = f.fp()->rejoin(); + RejoinState rejoin; + if (jsrejoin & 0x1) { + /* Rejoin after a scripted call finished. Restore f.regs.pc and f.regs.inlined (NULL) */ + uint32_t pcOffset = jsrejoin >> 1; + f.regs.pc = f.fp()->script()->code + pcOffset; + f.regs.clearInlined(); + rejoin = REJOIN_SCRIPTED; + } else { + rejoin = (RejoinState) (jsrejoin >> 1); + } -/* - * Given a frame that is about to return, make sure its return value and - * activation objects are fixed up. Then, pop the frame and advance the - * current PC. Note that while we could enter the JIT at this point, the - * logic would still be necessary for the interpreter, so it's easier - * (and faster) to finish frames in C++ even if at a safe point here. - */ -static bool -HandleFinishedFrame(VMFrame &f, StackFrame *entryFrame) -{ JSContext *cx = f.cx; + StackFrame *fp = f.regs.fp(); + JSScript *script = fp->script(); - JS_ASSERT(FrameIsFinished(cx)); - - /* - * This is the most difficult and complicated piece of the tracer - * integration, and historically has been very buggy. The problem is that - * although this frame has to be popped (see RemoveExcessFrames), it may - * be at a JSOP_RETURN opcode, and it might not have ever been executed. - * That is, fp->rval may not be set to the top of the stack, and if it - * has, the stack has already been decremented. Note that fp->rval is not - * the only problem: the epilogue may never have been executed. - * - * Here are the edge cases and whether the frame has been exited cleanly: - * 1. No: A trace exited directly before a RETURN op, and the - * interpreter never ran. - * 2. Yes: The interpreter exited cleanly. - * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT - * is not used in between JSOP_RETURN and advancing the PC, - * therefore, it cannot have been run if at a safe point. - * 4. No: Somewhere in the RunTracer call tree, we removed a frame, - * and we returned to a JSOP_RETURN opcode. Note carefully - * that in this situation, FrameIsFinished() returns true! - * 5. Yes: The function exited in the method JIT, during - * FinishExcessFrames() However, in this case, we'll never enter - * HandleFinishedFrame(): we always immediately pop JIT'd frames. - * - * Since the only scenario where this fixup is NOT needed is a normal exit - * from the interpreter, we can cleanly check for this scenario by checking - * a bit it sets in the frame. - */ - bool returnOK = true; - if (!cx->fp()->finishedInInterpreter()) { - if (JSOp(*cx->regs().pc) == JSOP_RETURN) - cx->fp()->setReturnValue(f.regs.sp[-1]); + jsbytecode *pc = f.regs.pc; - returnOK = ScriptEpilogue(cx, cx->fp(), true); - } + JSOp op = JSOp(*pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; - if (cx->fp() != entryFrame) { - InlineReturn(f); - AdvanceReturnPC(cx); + if (!script->ensureRanAnalysis(cx, NULL)) { + js_ReportOutOfMemory(cx); + return js_InternalThrow(f); } - return returnOK; -} - -/* - * Given a frame newer than the entry frame, try to finish it. If it's at a - * return position, pop the frame. If it's at a safe point, execute it in - * Jaeger code. Otherwise, try to interpret until a safe point. - * - * While this function is guaranteed to make progress, it may not actually - * finish or pop the current frame. It can either: - * 1) Finalize a finished frame, or - * 2) Finish and finalize the frame in the Method JIT, or - * 3) Interpret, which can: - * a) Propagate an error, or - * b) Finish the frame, but not finalize it, or - * c) Abruptly leave at any point in the frame, or in a newer frame - * pushed by a call, that has method JIT'd code. - */ -static bool -EvaluateExcessFrame(VMFrame &f, StackFrame *entryFrame) -{ - JSContext *cx = f.cx; - StackFrame *fp = cx->fp(); + analyze::AutoEnterAnalysis enter(cx); + analyze::ScriptAnalysis *analysis = script->analysis(); /* - * A "finished" frame is when the interpreter rested on a STOP, - * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking - * for a safe point. If the frame was finished, we could have already - * called ScriptEpilogue(), and entering the JIT could call it twice. + * f.regs.sp is not normally maintained by stubs (except for call prologues + * where it indicates the new frame), so is not expected to be coherent + * here. Update it to its value at the start of the opcode. */ - if (!fp->hasImacropc() && FrameIsFinished(cx)) - return HandleFinishedFrame(f, entryFrame); + Value *oldsp = f.regs.sp; + f.regs.sp = fp->base() + analysis->getCode(pc).stackDepth; - if (void *ncode = AtSafePoint(cx)) { - if (!JaegerShotAtSafePoint(cx, ncode)) - return false; - InlineReturn(f); - AdvanceReturnPC(cx); - return true; - } + jsbytecode *nextpc = pc + GetBytecodeLength(pc); + Value *nextsp = NULL; + if (nextpc != script->code + script->length && analysis->maybeCode(nextpc)) + nextsp = fp->base() + analysis->getCode(nextpc).stackDepth; - return PartialInterpret(f); -} + JS_ASSERT(&cx->regs() == &f.regs); -/* - * Evaluate frames newer than the entry frame until all are gone. This will - * always leave f.regs.fp == entryFrame. - */ -static bool -FinishExcessFrames(VMFrame &f, StackFrame *entryFrame) -{ - JSContext *cx = f.cx; +#ifdef JS_METHODJIT_SPEW + JaegerSpew(JSpew_Recompile, "interpreter rejoin (file \"%s\") (line \"%d\") (op %s) (opline \"%d\")\n", + script->filename, script->lineno, OpcodeNames[op], js_PCToLineNumber(cx, script, pc)); +#endif + + uint32_t nextDepth = UINT32_MAX; + bool skipTrap = false; + + if ((cs->format & (JOF_INC | JOF_DEC)) && + (rejoin == REJOIN_POS || rejoin == REJOIN_BINARY)) { + /* + * We may reenter the interpreter while finishing the INC/DEC operation + * on a local or arg (property INC/DEC operations will rejoin into the + * decomposed version of the op. + */ + JS_ASSERT(cs->format & (JOF_LOCAL | JOF_QARG)); + + nextDepth = analysis->getCode(nextpc).stackDepth; + enter.leave(); - while (cx->fp() != entryFrame || entryFrame->hasImacropc()) { - if (!EvaluateExcessFrame(f, entryFrame)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) - return false; + if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) { + /* Stack layout is 'V', 'N' or 'N+1' (only if the N is not needed) */ + FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[-1], &nextsp[-1]); + } else { + /* Stack layout is 'N N+1' */ + FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[0], &nextsp[-1]); } - } - return true; -} + rejoin = REJOIN_FALLTHROUGH; + } -#if defined JS_MONOIC -static void -UpdateTraceHintSingle(Repatcher &repatcher, JSC::CodeLocationJump jump, JSC::CodeLocationLabel target) -{ - /* - * Hack: The value that will be patched is before the executable address, - * so to get protection right, just unprotect the general region around - * the jump. - */ - repatcher.relink(jump, target); + switch (rejoin) { + case REJOIN_SCRIPTED: { + jsval_layout rval; +#ifdef JS_NUNBOX32 + rval.asBits = ((uint64_t)returnType << 32) | (uint32_t)returnData; +#elif JS_PUNBOX64 + rval.asBits = (uint64_t)returnType | (uint64_t)returnData; +#else +#error "Unknown boxing format" +#endif - JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n", - jump.executableAddress(), target.executableAddress()); -} + nextsp[-1] = IMPL_TO_JSVAL(rval); -static void -DisableTraceHint(JITScript *jit, ic::TraceICInfo &ic) -{ - Repatcher repatcher(jit); - UpdateTraceHintSingle(repatcher, ic.traceHint, ic.jumpTarget); + /* + * When making a scripted call at monitored sites, it is the caller's + * responsibility to update the pushed type set. + */ + types::TypeScript::Monitor(cx, script, pc, nextsp[-1]); + f.regs.pc = nextpc; + break; + } - if (ic.hasSlowTraceHint) - UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.jumpTarget); -} + case REJOIN_NONE: + JS_NOT_REACHED("Unpossible rejoin!"); + break; -static void -ResetTraceHintAt(JSScript *script, js::mjit::JITScript *jit, - jsbytecode *pc, uint16_t index, bool full) -{ - if (index >= jit->nTraceICs) - return; - ic::TraceICInfo &ic = jit->traceICs()[index]; - if (!ic.initialized) - return; - - JS_ASSERT(ic.jumpTargetPC == pc); + case REJOIN_RESUME: + break; - JaegerSpew(JSpew_PICs, "Enabling trace IC %u in script %p\n", index, script); + case REJOIN_TRAP: + /* + * Make sure when resuming in the interpreter we do not execute the + * trap again. Watch out for the case where the trap removed itself. + */ + if (script->hasBreakpointsAt(pc)) + skipTrap = true; + break; - Repatcher repatcher(jit); + case REJOIN_FALLTHROUGH: + f.regs.pc = nextpc; + break; - UpdateTraceHintSingle(repatcher, ic.traceHint, ic.stubEntry); + case REJOIN_NATIVE: + case REJOIN_NATIVE_LOWERED: + case REJOIN_NATIVE_GETTER: { + /* + * We don't rejoin until after the native stub finishes execution, in + * which case the return value will be in memory. For lowered natives, + * the return value will be in the 'this' value's slot. + */ + if (rejoin != REJOIN_NATIVE) + nextsp[-1] = nextsp[0]; - if (ic.hasSlowTraceHint) - UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.stubEntry); + /* Release this reference on the orphaned native stub. */ + RemoveOrphanedNative(cx, fp); - if (full) { - ic.traceData = NULL; - ic.loopCounterStart = 1; - ic.loopCounter = ic.loopCounterStart; - } -} -#endif + f.regs.pc = nextpc; + break; + } -void -js::mjit::ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full) -{ -#if JS_MONOIC - if (script->jitNormal) - ResetTraceHintAt(script, script->jitNormal, pc, index, full); + case REJOIN_PUSH_BOOLEAN: + nextsp[-1].setBoolean(returnReg != NULL); + f.regs.pc = nextpc; + break; - if (script->jitCtor) - ResetTraceHintAt(script, script->jitCtor, pc, index, full); -#endif -} + case REJOIN_PUSH_OBJECT: + nextsp[-1].setObject(* (JSObject *) returnReg); + f.regs.pc = nextpc; + break; -#if JS_MONOIC -void * -RunTracer(VMFrame &f, ic::TraceICInfo &ic) -#else -void * -RunTracer(VMFrame &f) -#endif -{ - JSContext *cx = f.cx; - StackFrame *entryFrame = f.fp(); - TracePointAction tpa; + case REJOIN_DEFLOCALFUN: + fp->slots()[GET_SLOTNO(pc)].setObject(* (JSObject *) returnReg); + f.regs.pc = nextpc; + break; - /* :TODO: nuke PIC? */ - if (!cx->traceJitEnabled) - return NULL; + case REJOIN_THIS_PROTOTYPE: { + JSObject *callee = &fp->callee(); + JSObject *proto = f.regs.sp[0].isObject() ? &f.regs.sp[0].toObject() : NULL; + JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); + if (!obj) + return js_InternalThrow(f); + fp->formalArgs()[-1].setObject(*obj); + + if (Probes::callTrackingActive(cx)) + Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script()); + + if (script->debugMode) { + JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp()); + switch (status) { + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromExternC(); + return NULL; + case JSTRAP_THROW: + case JSTRAP_ERROR: + return js_InternalThrow(f); + default: + JS_NOT_REACHED("bad ScriptDebugPrologue status"); + } + } - /* - * Force initialization of the entry frame's scope chain and return value, - * if necessary. The tracer can query the scope chain without needing to - * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the - * correct return value stored if we trace/interpret through to the end - * of the frame. - */ - entryFrame->scopeChain(); - entryFrame->returnValue(); - - bool blacklist; - uintN inlineCallCount = 0; - void **traceData; - uintN *traceEpoch; - uint32 *loopCounter; - uint32 hits; -#if JS_MONOIC - traceData = &ic.traceData; - traceEpoch = &ic.traceEpoch; - loopCounter = &ic.loopCounter; - *loopCounter = 1; - hits = ic.loopCounterStart; -#else - traceData = NULL; - traceEpoch = NULL; - loopCounter = NULL; - hits = 1; -#endif - tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch, - loopCounter, hits); - JS_ASSERT(!TRACE_RECORDER(cx)); - -#if JS_MONOIC - ic.loopCounterStart = *loopCounter; - if (blacklist) - DisableTraceHint(entryFrame->jit(), ic); -#endif + break; + } - // Even though ExecuteTree() bypasses the interpreter, it should propagate - // error failures correctly. - JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error); + case REJOIN_CHECK_ARGUMENTS: + /* + * Do all the work needed in arity check JIT prologues after the + * arguments check occurs (FixupArity has been called if needed, but + * the stack check and late prologue have not been performed. + */ + if (!CheckStackQuota(f)) + return js_InternalThrow(f); + + SetValueRangeToUndefined(fp->slots(), script->nfixed); + + if (!fp->functionPrologue(cx)) + return js_InternalThrow(f); + /* FALLTHROUGH */ + + case REJOIN_FUNCTION_PROLOGUE: + fp->scopeChain(); + + /* Construct the 'this' object for the frame if necessary. */ + if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp))) + return js_InternalThrow(f); + + /* + * Having called ScriptPrologueOrGeneratorResume, we would normally call + * ScriptDebugPrologue here. But in debug mode, we only use JITted + * functions' invokeEntry entry point, whereas CheckArgumentTypes + * (REJOIN_CHECK_ARGUMENTS) and FunctionFramePrologue + * (REJOIN_FUNCTION_PROLOGUE) are only reachable via the other entry + * points. So we should never need either of these rejoin tails in debug + * mode. + * + * If we fix bug 699196 ("Debug mode code could use inline caches + * now"), then these cases will become reachable again. + */ + JS_ASSERT(!cx->compartment->debugMode()); - JS_ASSERT(f.fp() == cx->fp()); - switch (tpa) { - case TPA_Nothing: - return NULL; + break; - case TPA_Error: - if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter())) - THROWV(NULL); - JS_ASSERT(!cx->fp()->hasImacropc()); + case REJOIN_CALL_PROLOGUE: + case REJOIN_CALL_PROLOGUE_LOWERED_CALL: + case REJOIN_CALL_PROLOGUE_LOWERED_APPLY: + if (returnReg) { + uint32_t argc = 0; + if (rejoin == REJOIN_CALL_PROLOGUE) + argc = GET_ARGC(pc); + else if (rejoin == REJOIN_CALL_PROLOGUE_LOWERED_CALL) + argc = GET_ARGC(pc) - 1; + else + argc = f.u.call.dynamicArgc; + + /* + * The caller frame's code was discarded, but we still need to + * execute the callee and have a JIT code pointer to do so. + * Set the argc and frame registers as the call path does, but set + * the callee frame's return address to jump back into the + * Interpoline, and change the caller frame's rejoin to reflect the + * state after the call. + */ + f.regs.restorePartialFrame(oldsp); /* f.regs.sp stored the new frame */ + f.scratch = (void *) uintptr_t(argc); /* The interpoline will load f.scratch into argc */ + f.fp()->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted)); + fp->setRejoin(REJOIN_SCRIPTED | ((pc - script->code) << 1)); + return returnReg; + } else { + /* + * The call has already finished, and the return value is on the + * stack. For lowered call/apply, the return value has been stored + * in the wrong slot, so adjust it here. + */ + f.regs.pc = nextpc; + if (rejoin != REJOIN_CALL_PROLOGUE) { + /* Same offset return value as for lowered native calls. */ + nextsp[-1] = nextsp[0]; + } + } break; - case TPA_RanStuff: - case TPA_Recorded: + case REJOIN_CALL_SPLAT: { + /* Leave analysis early and do the Invoke which SplatApplyArgs prepared. */ + nextDepth = analysis->getCode(nextpc).stackDepth; + enter.leave(); + f.regs.sp = nextsp + 2 + f.u.call.dynamicArgc; + if (!InvokeKernel(cx, CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp))) + return js_InternalThrow(f); + nextsp[-1] = nextsp[0]; + f.regs.pc = nextpc; break; - } + } - /* - * The tracer could have dropped us off on any frame at any position. - * Well, it could not have removed frames (recursion is disabled). - * - * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint() - * unless each is at a safe point. We can JaegerShotAtSafePoint these - * frames individually, but we must unwind to the entryFrame. - * - * Note carefully that JaegerShotAtSafePoint can resume methods at - * arbitrary safe points whereas JaegerShot cannot. - * - * If we land on entryFrame without a safe point in sight, we'll end up - * at the RETURN op. This is an edge case with two paths: - * - * 1) The entryFrame is the last inline frame. If it fell on a RETURN, - * move the return value down. - * 2) The entryFrame is NOT the last inline frame. Pop the frame. - * - * In both cases, we hijack the stub to return to the force-return - * trampoline. This trampoline simulates the frame-popping portion of - * emitReturn (except without the benefit of the FrameState) and will - * produce the necessary register state to return to the caller. - */ + case REJOIN_GETTER: + /* + * Match the PC to figure out whether this property fetch is part of a + * fused opcode which needs to be finished. + */ + switch (op) { + case JSOP_INSTANCEOF: { + /* + * If we recompiled from a getprop used within JSOP_INSTANCEOF, + * the stack looks like 'LHS RHS protov'. Inline the remaining + * portion of fun_hasInstance. + */ + if (f.regs.sp[0].isPrimitive()) { + js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-1], NULL); + return js_InternalThrow(f); + } + nextsp[-1].setBoolean(js_IsDelegate(cx, &f.regs.sp[0].toObject(), f.regs.sp[-2])); + f.regs.pc = nextpc; + break; + } - restart: - /* Step 1. Finish frames created after the entry frame. */ - if (!FinishExcessFrames(f, entryFrame)) - THROWV(NULL); + default: + f.regs.pc = nextpc; + break; + } + break; - /* IMacros are guaranteed to have been removed by now. */ - JS_ASSERT(f.fp() == entryFrame); - JS_ASSERT(!entryFrame->hasImacropc()); + case REJOIN_POS: + /* Convert-to-number which might be part of an INC* op. */ + JS_ASSERT(op == JSOP_POS); + f.regs.pc = nextpc; + break; - /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */ - if (FrameIsFinished(cx)) { - if (!HandleFinishedFrame(f, entryFrame)) - THROWV(NULL); - *f.returnAddressLocation() = cx->jaegerCompartment()->forceReturnFromFastCall(); - return NULL; - } + case REJOIN_BINARY: + /* Binary arithmetic op which might be part of an INC* op. */ + JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB || op == JSOP_MUL || op == JSOP_DIV); + f.regs.pc = nextpc; + break; - /* Step 3. If entryFrame is at a safe point, just leave. */ - if (void *ncode = AtSafePoint(cx)) - return ncode; + case REJOIN_BRANCH: { + /* + * This must be an opcode fused with IFNE/IFEQ. Unfused IFNE/IFEQ are + * implemented in terms of ValueToBoolean, which is infallible and + * cannot trigger recompilation. + */ + bool takeBranch = false; + switch (JSOp(*nextpc)) { + case JSOP_IFNE: + takeBranch = returnReg != NULL; + break; + case JSOP_IFEQ: + takeBranch = returnReg == NULL; + break; + default: + JS_NOT_REACHED("Bad branch op"); + } + if (takeBranch) + f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc); + else + f.regs.pc = nextpc + GetBytecodeLength(nextpc); + break; + } - /* Step 4. Do a partial interp, then restart the whole process. */ - if (!PartialInterpret(f)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) - THROWV(NULL); + default: + JS_NOT_REACHED("Missing rejoin"); } - goto restart; -} - -#endif /* JS_TRACER */ + if (nextDepth == UINT32_MAX) + nextDepth = analysis->getCode(f.regs.pc).stackDepth; + f.regs.sp = fp->base() + nextDepth; -#if defined JS_TRACER -# if defined JS_MONOIC -void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, ic::TraceICInfo *ic) -{ - return RunTracer(f, *ic); -} + /* + * Monitor the result of the previous op when finishing a JOF_TYPESET op. + * The result may not have been marked if we bailed out while inside a stub + * for the op. + */ + if (f.regs.pc == nextpc && (js_CodeSpec[op].format & JOF_TYPESET)) + types::TypeScript::Monitor(cx, script, pc, f.regs.sp[-1]); -# else + /* Mark the entry frame as unfinished, and update the regs to resume at. */ + JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished; + cx->compartment->jaegerCompartment()->setLastUnfinished(status); + *f.oldregs = f.regs; -void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f) -{ - return RunTracer(f); + return NULL; } -# endif /* JS_MONOIC */ -#endif /* JS_TRACER */ - diff --git a/deps/mozjs/js/src/methodjit/Logging.cpp b/deps/mozjs/js/src/methodjit/Logging.cpp index ed7b0ef3463..df212006c4c 100644 --- a/deps/mozjs/js/src/methodjit/Logging.cpp +++ b/deps/mozjs/js/src/methodjit/Logging.cpp @@ -46,10 +46,12 @@ #include "MethodJIT.h" #include "Logging.h" +#include "jsobjinlines.h" + #if defined(JS_METHODJIT_SPEW) static bool LoggingChecked = false; -static uint32 LoggingBits = 0; +static uint32_t LoggingBits = 0; static const char *ChannelNames[] = { @@ -79,46 +81,70 @@ js::JMCheckLogging() " scripts ???\n" " profile ???\n" #ifdef DEBUG + " pcprofile Runtime hit counts of every JS opcode executed\n" " jsops JS opcodes\n" #endif " insns JS opcodes and generated insns\n" " vmframe VMFrame contents\n" " pics PIC patching activity\n" " slowcalls Calls to slow path functions\n" - " full everything\n" - " notrace disable trace hints\n" + " analysis LICM and other analysis behavior\n" + " regalloc Register allocation behavior\n" + " inlin Call inlining behavior\n" + " recompile Dynamic recompilations\n" + " full everything not affecting codegen\n" "\n" ); exit(0); /*NOTREACHED*/ } if (strstr(env, "abort") || strstr(env, "aborts")) - LoggingBits |= (1 << uint32(JSpew_Abort)); + LoggingBits |= (1 << uint32_t(JSpew_Abort)); if (strstr(env, "scripts")) - LoggingBits |= (1 << uint32(JSpew_Scripts)); + LoggingBits |= (1 << uint32_t(JSpew_Scripts)); if (strstr(env, "profile")) - LoggingBits |= (1 << uint32(JSpew_Prof)); + LoggingBits |= (1 << uint32_t(JSpew_Prof)); #ifdef DEBUG if (strstr(env, "jsops")) - LoggingBits |= (1 << uint32(JSpew_JSOps)); + LoggingBits |= (1 << uint32_t(JSpew_JSOps)); #endif if (strstr(env, "insns")) - LoggingBits |= (1 << uint32(JSpew_Insns) | (1 << uint32(JSpew_JSOps))); + LoggingBits |= (1 << uint32_t(JSpew_Insns) | (1 << uint32_t(JSpew_JSOps))); if (strstr(env, "vmframe")) - LoggingBits |= (1 << uint32(JSpew_VMFrame)); + LoggingBits |= (1 << uint32_t(JSpew_VMFrame)); if (strstr(env, "pics")) - LoggingBits |= (1 << uint32(JSpew_PICs)); + LoggingBits |= (1 << uint32_t(JSpew_PICs)); if (strstr(env, "slowcalls")) - LoggingBits |= (1 << uint32(JSpew_SlowCalls)); + LoggingBits |= (1 << uint32_t(JSpew_SlowCalls)); + if (strstr(env, "analysis")) + LoggingBits |= (1 << uint32_t(JSpew_Analysis)); + if (strstr(env, "regalloc")) + LoggingBits |= (1 << uint32_t(JSpew_Regalloc)); + if (strstr(env, "recompile")) + LoggingBits |= (1 << uint32_t(JSpew_Recompile)); + if (strstr(env, "inlin")) + LoggingBits |= (1 << uint32_t(JSpew_Inlining)); if (strstr(env, "full")) LoggingBits |= 0xFFFFFFFF; } +js::ConditionalLog::ConditionalLog(bool logging) + : oldBits(LoggingBits), logging(logging) +{ + if (logging) + LoggingBits = 0xFFFFFFFF; +} + +js::ConditionalLog::~ConditionalLog() { + if (logging) + LoggingBits = oldBits; +} + bool js::IsJaegerSpewChannelActive(JaegerSpewChannel channel) { JS_ASSERT(LoggingChecked); - return !!(LoggingBits & (1 << uint32(channel))); + return !!(LoggingBits & (1 << uint32_t(channel))); } void @@ -126,7 +152,7 @@ js::JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...) { JS_ASSERT(LoggingChecked); - if (!(LoggingBits & (1 << uint32(channel)))) + if (!(LoggingBits & (1 << uint32_t(channel)))) return; fprintf(stderr, "[jaeger] %-7s ", ChannelNames[channel]); diff --git a/deps/mozjs/js/src/methodjit/Logging.h b/deps/mozjs/js/src/methodjit/Logging.h index 717ab65e977..6a941e05ad9 100644 --- a/deps/mozjs/js/src/methodjit/Logging.h +++ b/deps/mozjs/js/src/methodjit/Logging.h @@ -56,7 +56,11 @@ namespace js { _(Insns) \ _(VMFrame) \ _(PICs) \ - _(SlowCalls) + _(SlowCalls) \ + _(Analysis) \ + _(Regalloc) \ + _(Inlining) \ + _(Recompile) enum JaegerSpewChannel { #define _(name) JSpew_##name, @@ -73,14 +77,25 @@ enum JaegerSpewChannel { void JMCheckLogging(); +struct ConditionalLog { + uint32_t oldBits; + bool logging; + ConditionalLog(bool logging); + ~ConditionalLog(); +}; + bool IsJaegerSpewChannelActive(JaegerSpewChannel channel); +#ifdef __GNUC__ +void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +#else void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...); +#endif struct Profiler { - JSInt64 t_start; - JSInt64 t_stop; + int64_t t_start; + int64_t t_stop; - static inline JSInt64 now() { + static inline int64_t now() { return PRMJ_Now(); } @@ -92,12 +107,12 @@ struct Profiler { t_stop = now(); } - inline uint32 time_ms() { - return uint32((t_stop - t_start) / PRMJ_USEC_PER_MSEC); + inline uint32_t time_ms() { + return uint32_t((t_stop - t_start) / PRMJ_USEC_PER_MSEC); } - inline uint32 time_us() { - return uint32(t_stop - t_start); + inline uint32_t time_us() { + return uint32_t(t_stop - t_start); } }; diff --git a/deps/mozjs/js/src/methodjit/LoopState.cpp b/deps/mozjs/js/src/methodjit/LoopState.cpp new file mode 100644 index 00000000000..3e74ca4fbc6 --- /dev/null +++ b/deps/mozjs/js/src/methodjit/LoopState.cpp @@ -0,0 +1,2351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Brendan Eich + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "methodjit/Compiler.h" +#include "methodjit/LoopState.h" +#include "methodjit/FrameState-inl.h" +#include "methodjit/StubCalls.h" + +#include "jstypedarrayinlines.h" + +using namespace js; +using namespace js::mjit; +using namespace js::analyze; +using namespace js::types; + +inline bool +SafeAdd(int32_t one, int32_t two, int32_t *res) +{ + *res = one + two; + int64_t ores = (int64_t)one + (int64_t)two; + if (ores == (int64_t)*res) + return true; + JaegerSpew(JSpew_Analysis, "Overflow computing %d + %d\n", one, two); + return false; +} + +inline bool +SafeSub(int32_t one, int32_t two, int32_t *res) +{ + *res = one - two; + int64_t ores = (int64_t)one - (int64_t)two; + if (ores == (int64_t)*res) + return true; + JaegerSpew(JSpew_Analysis, "Overflow computing %d - %d\n", one, two); + return false; +} + +inline bool +SafeMul(int32_t one, int32_t two, int32_t *res) +{ + *res = one * two; + int64_t ores = (int64_t)one * (int64_t)two; + if (ores == (int64_t)*res) + return true; + JaegerSpew(JSpew_Analysis, "Overflow computing %d * %d\n", one, two); + return false; +} + +LoopState::LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa, + mjit::Compiler *cc, FrameState *frame) + : cx(cx), ssa(ssa), + outerScript(ssa->outerScript()), outerAnalysis(outerScript->analysis()), + cc(*cc), frame(*frame), + lifetime(NULL), alloc(NULL), reachedEntryPoint(false), loopRegs(0), skipAnalysis(false), + loopJoins(CompilerAllocPolicy(cx, *cc)), + loopPatches(CompilerAllocPolicy(cx, *cc)), + restoreInvariantCalls(CompilerAllocPolicy(cx, *cc)), + invariantEntries(CompilerAllocPolicy(cx, *cc)), + outer(NULL), temporariesStart(0), + testLHS(UNASSIGNED), testRHS(UNASSIGNED), + testConstant(0), testLessEqual(false), + increments(CompilerAllocPolicy(cx, *cc)), unknownModset(false), + growArrays(CompilerAllocPolicy(cx, *cc)), + modifiedProperties(CompilerAllocPolicy(cx, *cc)), + constrainedLoop(true) +{ + JS_ASSERT(cx->typeInferenceEnabled()); +} + +bool +LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget) +{ + this->lifetime = outerAnalysis->getLoop(head); + JS_ASSERT(lifetime && + lifetime->head == uint32_t(head - outerScript->code) && + lifetime->entry == uint32_t(entryTarget - outerScript->code)); + + this->entry = entry; + + analyzeLoopTest(); + analyzeLoopIncrements(); + for (unsigned i = 0; i < ssa->numFrames(); i++) { + /* Only analyze this frame if it is nested within the loop itself. */ + uint32_t index = ssa->iterFrame(i).index; + if (index != CrossScriptSSA::OUTER_FRAME) { + unsigned pframe = index; + while (ssa->getFrame(pframe).parent != CrossScriptSSA::OUTER_FRAME) + pframe = ssa->getFrame(pframe).parent; + uint32_t offset = ssa->getFrame(pframe).parentpc - outerScript->code; + JS_ASSERT(offset < outerScript->length); + if (offset < lifetime->head || offset > lifetime->backedge) + continue; + } + analyzeLoopBody(index); + } + + if (testLHS != UNASSIGNED) { + JaegerSpew(JSpew_Analysis, "loop test at %u: %s %s %s + %d\n", lifetime->head, + frame.entryName(testLHS), + testLessEqual ? "<=" : ">=", + (testRHS == UNASSIGNED) ? "" : frame.entryName(testRHS), + testConstant); + } + + for (unsigned i = 0; i < increments.length(); i++) { + JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head, + frame.entryName(increments[i].slot), + increments[i].offset); + } + + for (unsigned i = 0; i < growArrays.length(); i++) { + JaegerSpew(JSpew_Analysis, "loop grow array at %u: %s\n", lifetime->head, + types::TypeString(types::Type::ObjectType(growArrays[i]))); + } + + for (unsigned i = 0; i < modifiedProperties.length(); i++) { + JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head, + types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)), + TypeIdString(modifiedProperties[i].id)); + } + + RegisterAllocation *&alloc = outerAnalysis->getAllocation(head); + JS_ASSERT(!alloc); + + alloc = cx->typeLifoAlloc().new_(true); + if (!alloc) + return false; + + this->alloc = alloc; + this->loopRegs = Registers::AvailAnyRegs; + + /* + * Don't hoist bounds checks or loop invariant code in scripts that have + * had indirect modification of their arguments. + */ + if (outerScript->function()) { + if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE)) + this->skipAnalysis = true; + } + + /* + * Don't hoist bounds checks or loop invariant code in loops with safe + * points in the middle, which the interpreter can join at directly without + * performing hoisted bounds checks or doing initial computation of loop + * invariant terms. + */ + if (lifetime->hasSafePoints) + this->skipAnalysis = true; + + return true; +} + +void +LoopState::addJoin(unsigned index, bool script) +{ + StubJoin r; + r.index = index; + r.script = script; + loopJoins.append(r); +} + +void +LoopState::addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses) +{ + RestoreInvariantCall call; + call.jump = jump; + call.label = label; + call.ool = ool; + call.entry = entry; + call.patchIndex = patchIndex; + call.temporaryCopies = frame.getTemporaryCopies(uses); + + restoreInvariantCalls.append(call); +} + +void +LoopState::flushLoop(StubCompiler &stubcc) +{ + clearLoopRegisters(); + + /* + * Patch stub compiler rejoins with loads of loop carried registers + * discovered after the fact. + */ + for (unsigned i = 0; i < loopPatches.length(); i++) { + const StubJoinPatch &p = loopPatches[i]; + stubcc.patchJoin(p.join.index, p.join.script, p.address, p.reg); + } + loopJoins.clear(); + loopPatches.clear(); + + if (hasInvariants()) { + for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) { + RestoreInvariantCall &call = restoreInvariantCalls[i]; + Assembler &masm = cc.getAssembler(true); + Vector failureJumps(cx); + + jsbytecode *pc = cc.getInvariantPC(call.patchIndex); + + if (call.ool) { + call.jump.linkTo(masm.label(), &masm); + restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps); + masm.jump().linkTo(call.label, &masm); + } else { + stubcc.linkExitDirect(call.jump, masm.label()); + restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps); + stubcc.crossJump(masm.jump(), call.label); + } + + if (!failureJumps.empty()) { + for (unsigned i = 0; i < failureJumps.length(); i++) + failureJumps[i].linkTo(masm.label(), &masm); + + /* + * Call InvariantFailure, setting up the return address to + * patch and any value for the call to return. + */ + InvariantCodePatch *patch = cc.getInvariantPatch(call.patchIndex); + patch->hasPatch = true; + patch->codePatch = masm.storePtrWithPatch(ImmPtr(NULL), + FrameAddress(offsetof(VMFrame, scratch))); + JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1); + masm.move(Registers::ReturnReg, Registers::ArgReg1); + + if (call.entry) { + masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), + pc, NULL, 0); + } else { + /* f.regs are already coherent, don't write new values to them. */ + masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), -1); + } + } + } + } else { + for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) { + RestoreInvariantCall &call = restoreInvariantCalls[i]; + Assembler &masm = cc.getAssembler(call.ool); + call.jump.linkTo(call.label, &masm); + } + } + restoreInvariantCalls.clear(); +} + +void +LoopState::clearLoopRegisters() +{ + alloc->clearLoops(); + loopRegs = 0; +} + +bool +LoopState::loopInvariantEntry(uint32_t slot) +{ + if (slot == UNASSIGNED) + return true; + + /* Watch for loop temporaries. :XXX: this is really gross. */ + if (slot >= analyze::LocalSlot(outerScript, outerScript->nslots)) + return true; + + if (slot == analyze::CalleeSlot() || outerAnalysis->slotEscapes(slot)) + return false; + return outerAnalysis->liveness(slot).firstWrite(lifetime) == UINT32_MAX; +} + +inline bool +LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1) +{ + JS_ASSERT(e0.isCheck() && e1.isCheck()); + + uint32_t array0 = e0.u.check.arraySlot; + uint32_t array1 = e1.u.check.arraySlot; + + uint32_t value01 = e0.u.check.valueSlot1; + uint32_t value02 = e0.u.check.valueSlot2; + + uint32_t value11 = e1.u.check.valueSlot1; + uint32_t value12 = e1.u.check.valueSlot2; + + int32_t c0 = e0.u.check.constant; + int32_t c1 = e1.u.check.constant; + + /* + * initialized lengths are always <= JSObject::NELEMENTS_LIMIT, check for + * integer overflow checks redundant given initialized length checks. + * If Y <= c0 and Y + c1 < initlen(array): + * + * Y <= c0 + * initlen(array) - c1 <= c0 + * NSLOTS_LIMIT <= c0 + c1 + */ + if (e0.kind == InvariantEntry::RANGE_CHECK && e1.isBoundsCheck() && + value01 == value11 && value02 == value12) { + int32_t constant; + if (c1 >= 0) + constant = c0; + else if (!SafeAdd(c0, c1, &constant)) + return false; + return constant >= (int32_t) JSObject::NELEMENTS_LIMIT; + } + + /* Look for matching tests that differ only in their constants. */ + if (e0.kind == e1.kind && array0 == array1 && value01 == value11 && value02 == value12) { + if (e0.isBoundsCheck()) { + /* If e0 is X >= Y + c0 and e1 is X >= Y + c1, e0 is redundant if c0 <= c1 */ + return (c0 <= c1); + } else { + /* If e0 is c0 >= Y and e1 is c1 >= Y, e0 is redundant if c0 >= c1 */ + return (c0 >= c1); + } + } + + return false; +} + +bool +LoopState::checkRedundantEntry(const InvariantEntry &entry) +{ + /* + * Return true if entry is implied by an existing entry, otherwise filter + * out any existing entries which entry implies. + */ + JS_ASSERT(entry.isCheck()); + + /* Maintain this separately, GCC miscompiles if the loop test is invariantEntries.length(). */ + unsigned length = invariantEntries.length(); + + for (unsigned i = 0; i < length; i++) { + InvariantEntry &baseEntry = invariantEntries[i]; + if (!baseEntry.isCheck()) + continue; + if (entryRedundant(entry, baseEntry)) + return true; + if (entryRedundant(baseEntry, entry)) { + /* + * Make sure to maintain the existing ordering on how invariant + * entries are generated, this is required for e.g. entries which + * use temporaries or slot computations which appear before any + * bounds checks on the arrays. + */ + for (unsigned j = i; j < length - 1; j++) + invariantEntries[j] = invariantEntries[j + 1]; + invariantEntries.popBack(); + i--; + length--; + } + } + + return false; +} + +bool +LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot, + uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant) +{ +#ifdef DEBUG + JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED); + const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length"; + if (valueSlot1 == UNASSIGNED) { + JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant); + } else if (valueSlot2 == UNASSIGNED) { + JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field, + frame.entryName(valueSlot1), constant); + } else { + JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %s + %d\n", field, + frame.entryName(valueSlot1), frame.entryName(valueSlot2), constant); + } +#endif + + InvariantEntry entry; + entry.kind = (arrayKind == DENSE_ARRAY) + ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK + : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK; + entry.u.check.arraySlot = arraySlot; + entry.u.check.valueSlot1 = valueSlot1; + entry.u.check.valueSlot2 = valueSlot2; + entry.u.check.constant = constant; + + if (checkRedundantEntry(entry)) + return true; + + /* + * Maintain an invariant that for any array with a hoisted bounds check, + * we also have a loop invariant slot to hold the array's slots pointer. + * The compiler gets invariant array slots only for accesses with a hoisted + * bounds check, so this makes invariantSlots infallible. + */ + bool hasInvariantSlots = false; + InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY) + ? InvariantEntry::DENSE_ARRAY_SLOTS + : InvariantEntry::TYPED_ARRAY_SLOTS; + for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot) + hasInvariantSlots = true; + } + if (!hasInvariantSlots) { + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return false; + FrameEntry *fe = frame.getTemporary(which); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n", + frame.entryName(fe), frame.entryName(arraySlot)); + + InvariantEntry slotsEntry; + slotsEntry.kind = slotsKind; + slotsEntry.u.array.arraySlot = arraySlot; + slotsEntry.u.array.temporary = which; + invariantEntries.append(slotsEntry); + } + + invariantEntries.append(entry); + return true; +} + +void +LoopState::addNegativeCheck(uint32_t valueSlot, int32_t constant) +{ + JaegerSpew(JSpew_Analysis, "Nonnegative check %s + %d >= 0\n", + frame.entryName(valueSlot), constant); + + InvariantEntry entry; + entry.kind = InvariantEntry::NEGATIVE_CHECK; + entry.u.check.valueSlot1 = valueSlot; + entry.u.check.constant = constant; + + if (!checkRedundantEntry(entry)) + invariantEntries.append(entry); +} + +void +LoopState::addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant) +{ + JaegerSpew(JSpew_Analysis, "Range check %d >= %s + %s\n", + constant, frame.entryName(valueSlot1), + valueSlot2 == UINT32_MAX ? "" : frame.entryName(valueSlot2)); + + InvariantEntry entry; + entry.kind = InvariantEntry::RANGE_CHECK; + entry.u.check.valueSlot1 = valueSlot1; + entry.u.check.valueSlot2 = valueSlot2; + entry.u.check.constant = constant; + + if (!checkRedundantEntry(entry)) + invariantEntries.append(entry); +} + +void +LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe) +{ + JS_ASSERT(alloc->loop(reg)); + loopRegs.takeReg(reg); + + uint32_t slot = frame.outerSlot(fe); + JaegerSpew(JSpew_Regalloc, "allocating loop register %s for %s\n", + reg.name(), frame.entryName(fe)); + + alloc->set(reg, slot, true); + + /* + * Mark pending rejoins to patch up with the load. We don't do this now as that would + * cause us to emit into the slow path, which may be in progress. + */ + for (unsigned i = 0; i < loopJoins.length(); i++) { + StubJoinPatch p; + p.join = loopJoins[i]; + p.address = frame.addressOf(fe); + p.reg = reg; + loopPatches.append(p); + } + + if (reachedEntryPoint) { + /* + * We've advanced past the entry point of the loop (we're analyzing the condition), + * so need to update the register state at that entry point so that the right + * things get loaded when we enter the loop. + */ + RegisterAllocation *alloc = outerAnalysis->getAllocation(lifetime->entry); + JS_ASSERT(alloc && !alloc->assigned(reg)); + alloc->set(reg, slot, true); + } +} + +bool +LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAValue &obj, + const CrossSSAValue &index) +{ + /* + * Note: this method requires that the index is definitely an integer, and + * that obj is either a dense array, a typed array or not an object. + */ + if (skipAnalysis) + return false; + + uint32_t objSlot; + int32_t objConstant; + if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) + return false; + + JaegerSpew(JSpew_Analysis, "Trying to hoist bounds check on %s\n", + frame.entryName(objSlot)); + + if (!loopInvariantEntry(objSlot)) { + JaegerSpew(JSpew_Analysis, "Object is not loop invariant\n"); + return false; + } + + /* + * Check for an overlap with the arrays we think might grow in this loop. + * This information is only a guess; if we don't think the array can grow + * but it actually can, we will probably recompile after the hoisted + * bounds check fails. + */ + TypeSet *objTypes = ssa->getValueTypes(obj); + if (arrayKind == DENSE_ARRAY && !growArrays.empty()) { + unsigned count = objTypes->getObjectCount(); + for (unsigned i = 0; i < count; i++) { + if (objTypes->getSingleObject(i) != NULL) { + JaegerSpew(JSpew_Analysis, "Object might be a singleton"); + return false; + } + TypeObject *object = objTypes->getTypeObject(i); + if (object && hasGrowArray(object)) { + JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n"); + return false; + } + } + } + + /* + * Get an expression for the index 'index + indexConstant', where index + * is the value of a slot at loop entry. + */ + uint32_t indexSlot; + int32_t indexConstant; + if (!getEntryValue(index, &indexSlot, &indexConstant)) { + JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n"); + return false; + } + + if (indexSlot == UNASSIGNED) { + /* Hoist checks on x[n] accesses for constant n. */ + if (indexConstant < 0) { + JaegerSpew(JSpew_Analysis, "Constant index is negative\n"); + return false; + } + return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant); + } + + if (loopInvariantEntry(indexSlot)) { + /* Hoist checks on x[y] accesses when y is loop invariant. */ + addNegativeCheck(indexSlot, indexConstant); + return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant); + } + + /* + * If the LHS can decrease in the loop, it could become negative and + * underflow the array. We currently only hoist bounds checks for loops + * which walk arrays going forward. + */ + if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) { + JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n"); + return false; + } + + /* + * If the access is of the form x[y + a] where we know that y <= z + b + * (both in terms of state at the head of the loop), hoist as follows: + * + * y + a < initlen(x) + * y < initlen(x) - a + * z + b < initlen(x) - a + * z + b + a < initlen(x) + */ + if (indexSlot == testLHS && testLessEqual) { + int32_t constant; + if (!SafeAdd(testConstant, indexConstant, &constant)) + return false; + + /* + * Check that the LHS is nonnegative every time we rejoin the loop. + * This is only really necessary on initial loop entry. Note that this + * test is not sensitive to changes to the LHS between when we make + * the test and the start of the next iteration, as we've ensured the + * LHS is nondecreasing within the body of the loop. + */ + addNegativeCheck(indexSlot, indexConstant); + + return addHoistedCheck(arrayKind, objSlot, testRHS, UNASSIGNED, constant); + } + + /* + * If the access is of the form x[y + a] where we know that z >= b at the + * head of the loop and y has a linear relationship with z such that + * (y + z) always has the same value at the head of the loop, hoist as + * follows: + * + * y + a < initlen(x) + * y + z < initlen(x) + z - a + * y + z < initlen(x) + b - a + * y + z + a - b < initlen(x) + */ + if (hasTestLinearRelationship(indexSlot)) { + int32_t constant; + if (!SafeSub(indexConstant, testConstant, &constant)) + return false; + + addNegativeCheck(indexSlot, indexConstant); + return addHoistedCheck(arrayKind, objSlot, indexSlot, testLHS, constant); + } + + JaegerSpew(JSpew_Analysis, "No match found\n"); + return false; +} + +bool +LoopState::hoistArgsLengthCheck(const CrossSSAValue &index) +{ + if (skipAnalysis) + return false; + + JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n"); + + uint32_t indexSlot; + int32_t indexConstant; + if (!getEntryValue(index, &indexSlot, &indexConstant)) { + JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n"); + return false; + } + + /* + * We only hoist arguments checks which can be completely eliminated, for + * now just tests with 'i < arguments.length' or similar in the condition. + */ + + if (indexSlot == UNASSIGNED || loopInvariantEntry(indexSlot)) { + JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n"); + return false; + } + + if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) { + JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n"); + return false; + } + + if (indexSlot == testLHS && indexConstant == 0 && testConstant == -1 && testLessEqual) { + bool found = false; + for (unsigned i = 0; i < invariantEntries.length(); i++) { + const InvariantEntry &entry = invariantEntries[i]; + if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) { + uint32_t slot = frame.outerSlot(frame.getTemporary(entry.u.array.temporary)); + if (slot == testRHS) + found = true; + break; + } + } + if (found) { + addNegativeCheck(indexSlot, indexConstant); + JaegerSpew(JSpew_Analysis, "Access implied by loop test\n"); + return true; + } + } + + JaegerSpew(JSpew_Analysis, "No match found\n"); + return false; +} + +bool +LoopState::hasTestLinearRelationship(uint32_t slot) +{ + /* + * Determine whether slot has a linear relationship with the loop test + * variable 'test', such that (slot + test) always has the same value at + * the head of the loop. + */ + + if (testLHS == UNASSIGNED || testRHS != UNASSIGNED || testLessEqual) + return false; + + uint32_t incrementOffset = getIncrement(slot); + if (incrementOffset == UINT32_MAX) { + /* + * Variable is not always incremented in the loop, or is incremented + * multiple times. Note that the nonDecreasing test done earlier + * ensures that if there is a single write, it is an increment. + */ + return false; + } + + uint32_t decrementOffset = getIncrement(testLHS); + if (decrementOffset == UINT32_MAX) + return false; + + JSOp op = JSOp(outerScript->code[decrementOffset]); + switch (op) { + case JSOP_DECLOCAL: + case JSOP_LOCALDEC: + case JSOP_DECARG: + case JSOP_ARGDEC: + return true; + default: + return false; + } +} + +FrameEntry * +LoopState::invariantArraySlots(const CrossSSAValue &obj) +{ + JS_ASSERT(!skipAnalysis); + + uint32_t objSlot; + int32_t objConstant; + if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) { + JS_NOT_REACHED("Bad value"); + return NULL; + } + + /* + * Note: we don't have to check arrayKind (dense array or typed array) here, + * because an array cannot have entries for both dense array slots and typed + * array slots. + */ + for (unsigned i = 0; i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS || + entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) && + entry.u.array.arraySlot == objSlot) { + return frame.getTemporary(entry.u.array.temporary); + } + } + + /* addHoistedCheck should have ensured there is an entry for the slots. */ + JS_NOT_REACHED("Missing invariant slots"); + return NULL; +} + +FrameEntry * +LoopState::invariantArguments() +{ + if (skipAnalysis) + return NULL; + + for (unsigned i = 0; i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if (entry.kind == InvariantEntry::INVARIANT_ARGS_BASE) + return frame.getTemporary(entry.u.array.temporary); + } + + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return NULL; + FrameEntry *fe = frame.getTemporary(which); + + InvariantEntry entry; + entry.kind = InvariantEntry::INVARIANT_ARGS_BASE; + entry.u.array.temporary = which; + invariantEntries.append(entry); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n", + frame.entryName(fe)); + return fe; +} + +FrameEntry * +LoopState::invariantLength(const CrossSSAValue &obj) +{ + if (skipAnalysis) + return NULL; + + uint32_t objSlot; + int32_t objConstant; + if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) + return NULL; + TypeSet *objTypes = ssa->getValueTypes(obj); + + /* Check for 'length' on the lazy arguments for the current frame. */ + if (objTypes->isLazyArguments(cx)) { + JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME); + + for (unsigned i = 0; i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) + return frame.getTemporary(entry.u.array.temporary); + } + + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return NULL; + FrameEntry *fe = frame.getTemporary(which); + + InvariantEntry entry; + entry.kind = InvariantEntry::INVARIANT_ARGS_LENGTH; + entry.u.array.temporary = which; + invariantEntries.append(entry); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n", + frame.entryName(fe)); + return fe; + } + + /* + * Note: we don't have to check arrayKind (dense array or typed array) here, + * because an array cannot have entries for both dense array length and typed + * array length. + */ + for (unsigned i = 0; i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH || + entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) && + entry.u.array.arraySlot == objSlot) { + return frame.getTemporary(entry.u.array.temporary); + } + } + + if (!loopInvariantEntry(objSlot)) + return NULL; + + /* Hoist 'length' access on typed arrays. */ + if (!objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_TYPED_ARRAY)) { + /* Recompile if object type changes. */ + objTypes->addFreeze(cx); + + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return NULL; + FrameEntry *fe = frame.getTemporary(which); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant typed array length of %s\n", + frame.entryName(fe), frame.entryName(objSlot)); + + InvariantEntry entry; + entry.kind = InvariantEntry::TYPED_ARRAY_LENGTH; + entry.u.array.arraySlot = objSlot; + entry.u.array.temporary = which; + invariantEntries.append(entry); + + return fe; + } + + if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY)) + return NULL; + + /* + * Don't make 'length' loop invariant if the loop might directly write + * to the elements of any of the accessed arrays. This could invoke an + * inline path which updates the length. There is no need to check the + * modset for direct 'length' writes, as we don't generate inline paths + * updating array lengths. + */ + for (unsigned i = 0; i < objTypes->getObjectCount(); i++) { + if (objTypes->getSingleObject(i) != NULL) + return NULL; + TypeObject *object = objTypes->getTypeObject(i); + if (object && hasModifiedProperty(object, JSID_VOID)) + return NULL; + } + objTypes->addFreeze(cx); + + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return NULL; + FrameEntry *fe = frame.getTemporary(which); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant dense array length of %s\n", + frame.entryName(fe), frame.entryName(objSlot)); + + InvariantEntry entry; + entry.kind = InvariantEntry::DENSE_ARRAY_LENGTH; + entry.u.array.arraySlot = objSlot; + entry.u.array.temporary = which; + invariantEntries.append(entry); + + return fe; +} + +FrameEntry * +LoopState::invariantProperty(const CrossSSAValue &obj, jsid id) +{ + if (skipAnalysis) + return NULL; + + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) + return NULL; + + uint32_t objSlot; + int32_t objConstant; + if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) + return NULL; + + for (unsigned i = 0; i < invariantEntries.length(); i++) { + InvariantEntry &entry = invariantEntries[i]; + if (entry.kind == InvariantEntry::INVARIANT_PROPERTY && + entry.u.property.objectSlot == objSlot && + entry.u.property.id == id) { + return frame.getTemporary(entry.u.property.temporary); + } + } + + if (!loopInvariantEntry(objSlot)) + return NULL; + + /* Check that the property is definite and not written anywhere in the loop. */ + TypeSet *objTypes = ssa->getValueTypes(obj); + if (objTypes->unknownObject() || objTypes->getObjectCount() != 1) + return NULL; + TypeObject *object = objTypes->getTypeObject(0); + if (!object || object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id)) + return NULL; + TypeSet *propertyTypes = object->getProperty(cx, id, false); + if (!propertyTypes) + return NULL; + if (!propertyTypes->isDefiniteProperty() || propertyTypes->isOwnProperty(cx, object, true)) + return NULL; + objTypes->addFreeze(cx); + + uint32_t which = frame.allocTemporary(); + if (which == UINT32_MAX) + return NULL; + FrameEntry *fe = frame.getTemporary(which); + + JaegerSpew(JSpew_Analysis, "Using %s for loop invariant property of %s\n", + frame.entryName(fe), frame.entryName(objSlot)); + + InvariantEntry entry; + entry.kind = InvariantEntry::INVARIANT_PROPERTY; + entry.u.property.objectSlot = objSlot; + entry.u.property.propertySlot = propertyTypes->definiteSlot(); + entry.u.property.temporary = which; + entry.u.property.id = id; + invariantEntries.append(entry); + + return fe; +} + +bool +LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed) +{ + if (skipAnalysis) + return false; + + int32_t min, max; + if (computeInterval(pushed, &min, &max)) { + JaegerSpew(JSpew_Analysis, "Integer operation fits in range [%d, %d]\n", min, max); + return true; + } + + /* + * Compute a slot and constant such that the result of the binary op is + * 'slot + constant', where slot is expressed in terms of its value at + * the head of the loop. + */ + JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED); + jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset(); + ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis(); + + if (!analysis->integerOperation(cx, PC)) + return false; + + uint32_t baseSlot = UNASSIGNED; + int32_t baseConstant = 0; + JSOp op = JSOp(*PC); + switch (op) { + + case JSOP_INCLOCAL: + case JSOP_LOCALINC: + case JSOP_INCARG: + case JSOP_ARGINC: { + CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0)); + if (!getEntryValue(cv, &baseSlot, &baseConstant)) + return false; + if (!SafeAdd(baseConstant, 1, &baseConstant)) + return false; + break; + } + + case JSOP_DECLOCAL: + case JSOP_LOCALDEC: + case JSOP_DECARG: + case JSOP_ARGDEC: { + CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0)); + if (!getEntryValue(cv, &baseSlot, &baseConstant)) + return false; + if (!SafeSub(baseConstant, 1, &baseConstant)) + return false; + break; + } + + case JSOP_ADD: + case JSOP_SUB: { + uint32_t lhs = UNASSIGNED, rhs = UNASSIGNED; + int32_t lhsconstant = 0, rhsconstant = 0; + CrossSSAValue lcv(pushed.frame, analysis->poppedValue(PC, 1)); + CrossSSAValue rcv(pushed.frame, analysis->poppedValue(PC, 0)); + if (!getEntryValue(lcv, &lhs, &lhsconstant)) + return false; + if (!getEntryValue(rcv, &rhs, &rhsconstant)) + return false; + if (op == JSOP_ADD) { + if (!SafeAdd(lhsconstant, rhsconstant, &baseConstant)) + return false; + if (lhs != UNASSIGNED && rhs != UNASSIGNED) + return false; + baseSlot = (lhs == UNASSIGNED) ? rhs : lhs; + } else { + if (!SafeSub(lhsconstant, rhsconstant, &baseConstant)) + return false; + if (rhs != UNASSIGNED) + return false; + baseSlot = lhs; + } + break; + } + + default: + return false; + } + + if (baseSlot == UNASSIGNED) + return false; + + JaegerSpew(JSpew_Analysis, "Trying to hoist integer overflow check on %s + %d\n", + frame.entryName(baseSlot), baseConstant); + + if (baseConstant == 0) { + JaegerSpew(JSpew_Analysis, "Vacuously holds\n"); + return true; + } + + if (baseConstant < 0) { + /* + * If the access is of the form 'y + a' where a is negative and we know + * that y >= b at the head of the loop, we can eliminate as follows: + * + * y + a >= INT_MIN + * b + a >= INT_MIN + */ + if (baseSlot == testLHS && !testLessEqual && testRHS == UNASSIGNED) { + int32_t constant; + if (!SafeAdd(testConstant, baseConstant, &constant)) + return false; + + JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n"); + return true; + } + + JaegerSpew(JSpew_Analysis, "No match found\n"); + return false; + } + + /* + * If the access is of the form 'y + a' where we know that y <= z + b + * (both in terms of state at the head of the loop), hoist as follows: + * + * y + a <= INT_MAX + * y <= INT_MAX - a + * z + b <= INT_MAX - a + * z <= INT_MAX - (a + b) + */ + if (baseSlot == testLHS && testLessEqual) { + int32_t constant; + if (!SafeAdd(testConstant, baseConstant, &constant)) + return false; + + if (testRHS == UNASSIGNED || constant <= 0) { + /* + * Reduces to '(a + b) <= INT_MAX', which SafeAdd ensures, + * or 'z <= INT_MAX', which integer checks on z ensure. + */ + JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n"); + return true; + } + + constant = JSVAL_INT_MAX - constant; + + addRangeCheck(testRHS, UNASSIGNED, constant); + return true; + } + + /* + * If the access is of the form 'y + a' where we know that z >= b at the + * head of the loop and y has a linear relationship with z such that + * (y + z) always has the same value at the head of the loop, hoist as + * follows: + * + * y + a <= INT_MAX + * y + z <= INT_MAX + z - a + * y + z <= INT_MAX + b - a + */ + if (hasTestLinearRelationship(baseSlot)) { + int32_t constant; + if (!SafeSub(testConstant, baseConstant, &constant)) + return false; + + if (constant >= 0) + constant = 0; + constant = JSVAL_INT_MAX + constant; + + addRangeCheck(baseSlot, testLHS, constant); + return true; + } + + JaegerSpew(JSpew_Analysis, "No match found\n"); + return false; +} + +bool +LoopState::ignoreIntegerOverflow(const CrossSSAValue &pushed) +{ + if (skipAnalysis || unknownModset || !constrainedLoop) + return false; + + /* + * Under certain circumstances, we can ignore arithmetic overflow in adds + * and multiplies. As long as the result of the add/mul is either only used + * in bitwise arithmetic or is only used in additions whose result is only + * used in bitwise arithmetic, then the conversion to integer performed by + * the bitop will undo the effect of the earlier overflow. There are two + * additional things to watch for before performing this transformation: + * + * 1. If the overflowing double is sufficiently large that it loses + * precision in its lower bits (with a 48 bit mantissa, this may happen for + * values of N >= 2^48), the resulting rounding could change the result. + * We don't ignore overflow on multiplications without range information, + * though assume that no amount of integer additions we perform in a single + * loop iteration will overflow 2^48. + * + * 2. If used in an addition with a string, the overflowing and truncated + * results may produce different values (e.g. '(x + "e3") & y'). We must + * restrict the loop body in such a way that no string operand is possible + * or becomes possible due to dynamic type changes for such additions. + * constrainedLoop indicates whether the only operations which can happen + * in the loop body are int/double arithmetic and bitops, and reads/writes + * from known dense arrays which can only produce ints and doubles. + */ + + /* This value must be in the outer loop: loops with inline calls are not constrained. */ + JS_ASSERT(pushed.frame == CrossScriptSSA::OUTER_FRAME); + + JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED); + jsbytecode *PC = outerScript->code + pushed.v.pushedOffset(); + + JSOp op = JSOp(*PC); + if (op != JSOP_MUL && op != JSOP_ADD) + return false; + + if (valueFlowsToBitops(pushed.v)) { + JaegerSpew(JSpew_Analysis, "Integer result flows to bitops\n"); + return true; + } + + if (op == JSOP_MUL) { + /* + * If the multiply will only be used in an addition, negative zero can + * be ignored as long as the other operand in the addition cannot be + * negative zero. + */ + if (!outerAnalysis->trackUseChain(pushed.v)) + return false; + + SSAUseChain *use = outerAnalysis->useChain(pushed.v); + if (!use || use->next || !use->popped || outerScript->code[use->offset] != JSOP_ADD) + return false; + + if (use->u.which == 1) { + /* + * Only ignore negative zero if this is the RHS of an addition. + * Otherwise the result of the other side could change to a double + * after the first LHS has been computed, and be affected by a + * negative zero LHS. + */ + return false; + } + + TypeSet *lhsTypes = outerAnalysis->poppedTypes(use->offset, 1); + if (lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) + return false; + + JaegerSpew(JSpew_Analysis, "Integer result is RHS in integer addition\n"); + return true; + } + + return false; +} + +bool +LoopState::valueFlowsToBitops(const analyze::SSAValue &v) +{ + /* + * Determine whether v can only be used in a bitop later in the same + * iteration of this loop, or in additions whose result is also only + * used in such a bitop. + */ + if (!outerAnalysis->trackUseChain(v)) + return false; + + SSAUseChain *use = outerAnalysis->useChain(v); + while (use) { + if (!use->popped) { + /* + * Ignore variables used in phi nodes, so long as the variable is + * dead at the phi. We don't track live variables across back edges + * or complex control flow. + */ + if (v.kind() == SSAValue::VAR) { + analyze::Lifetime *lifetime = outerAnalysis->liveness(v.varSlot()).live(use->offset); + if (!lifetime) { + use = use->next; + continue; + } + } + return false; + } + + if (use->offset > lifetime->backedge) + return false; + + jsbytecode *pc = outerScript->code + use->offset; + JSOp op = JSOp(*pc); + switch (op) { + case JSOP_ADD: + case JSOP_GETLOCAL: { + SSAValue pushv; + pushv.initPushed(use->offset, 0); + if (!valueFlowsToBitops(pushv)) + return false; + break; + } + + case JSOP_SETLOCAL: { + uint32_t slot = GetBytecodeSlot(outerScript, pc); + if (!outerAnalysis->trackSlot(slot)) + return false; + SSAValue writev; + writev.initWritten(slot, use->offset); + if (!valueFlowsToBitops(writev)) + return false; + break; + } + + case JSOP_BITAND: + case JSOP_BITOR: + case JSOP_BITXOR: + case JSOP_RSH: + case JSOP_LSH: + case JSOP_URSH: + case JSOP_BITNOT: + break; + + default: + return false; + } + + use = use->next; + } + + return true; +} + +void +LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm, + Vector *temporaryCopies, Vector *jumps) +{ + /* + * Restore all invariants in memory when entering the loop or after any + * scripted or C++ call, and check that all hoisted conditions still hold. + * Care should be taken not to clobber the return register or callee-saved + * registers, which may still be live after some calls. + */ + + Registers regs(Registers::TempRegs); + regs.takeReg(Registers::ReturnReg); + if (regs.hasReg(JSReturnReg_Data)) + regs.takeReg(JSReturnReg_Data); + if (regs.hasReg(JSReturnReg_Type)) + regs.takeReg(JSReturnReg_Type); + + RegisterID T0 = regs.takeAnyReg().reg(); + RegisterID T1 = regs.takeAnyReg().reg(); + + for (unsigned i = 0; i < invariantEntries.length(); i++) { + const InvariantEntry &entry = invariantEntries[i]; + switch (entry.kind) { + + case InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK: + case InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK: { + /* + * Hoisted bounds checks always have preceding invariant slots + * in the invariant list, so don't recheck this is an object. + */ + masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0); + if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) { + masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0); + masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0); + } else { + masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0); + } + + int32_t constant = entry.u.check.constant; + + if (entry.u.check.valueSlot1 != UINT32_MAX) { + constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1); + masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1); + if (entry.u.check.valueSlot2 != UINT32_MAX) { + constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2); + Jump overflow = masm.branchAdd32(Assembler::Overflow, + frame.addressOf(entry.u.check.valueSlot2), T1); + jumps->append(overflow); + } + if (constant != 0) { + Jump overflow = masm.branchAdd32(Assembler::Overflow, + Imm32(constant), T1); + jumps->append(overflow); + } + Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, T1); + jumps->append(j); + } else { + Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, + Imm32(constant)); + jumps->append(j); + } + break; + } + + case InvariantEntry::RANGE_CHECK: { + int32_t constant = 0; + + constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1); + masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0); + if (entry.u.check.valueSlot2 != UINT32_MAX) { + constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2); + Jump overflow = masm.branchAdd32(Assembler::Overflow, + frame.addressOf(entry.u.check.valueSlot2), T0); + jumps->append(overflow); + } + if (constant != 0) { + Jump overflow = masm.branchAdd32(Assembler::Overflow, Imm32(constant), T0); + jumps->append(overflow); + } + Jump j = masm.branch32(Assembler::GreaterThan, T0, Imm32(entry.u.check.constant)); + jumps->append(j); + break; + } + + case InvariantEntry::NEGATIVE_CHECK: { + masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0); + if (entry.u.check.constant != 0) { + Jump overflow = masm.branchAdd32(Assembler::Overflow, + Imm32(entry.u.check.constant), T0); + jumps->append(overflow); + } + Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0)); + jumps->append(j); + break; + } + + case InvariantEntry::DENSE_ARRAY_SLOTS: + case InvariantEntry::DENSE_ARRAY_LENGTH: { + uint32_t array = entry.u.array.arraySlot; + Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array)); + jumps->append(notObject); + masm.loadPayload(frame.addressOf(array), T0); + masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0); + + Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary)); + + if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) { + masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0); + masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address); + } else { + masm.storePayload(T0, address); + } + break; + } + + case InvariantEntry::TYPED_ARRAY_SLOTS: + case InvariantEntry::TYPED_ARRAY_LENGTH: { + uint32_t array = entry.u.array.arraySlot; + Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array)); + jumps->append(notObject); + masm.loadPayload(frame.addressOf(array), T0); + + Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary)); + + if (entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) { + masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0); + masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address); + } else { + masm.loadPtr(Address(T0, js::TypedArray::dataOffset()), T0); + masm.storePayload(T0, address); + } + break; + } + + case InvariantEntry::INVARIANT_ARGS_BASE: { + Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary)); + masm.loadFrameActuals(outerScript->function(), T0); + masm.storePayload(T0, address); + break; + } + + case InvariantEntry::INVARIANT_ARGS_LENGTH: { + Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary)); + masm.load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), T0); + masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address); + break; + } + + case InvariantEntry::INVARIANT_PROPERTY: { + uint32_t object = entry.u.property.objectSlot; + Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(object)); + jumps->append(notObject); + masm.loadPayload(frame.addressOf(object), T0); + + masm.loadInlineSlot(T0, entry.u.property.propertySlot, T1, T0); + masm.storeValueFromComponents(T1, T0, + frame.addressOf(frame.getTemporary(entry.u.property.temporary))); + break; + } + + default: + JS_NOT_REACHED("Bad invariant kind"); + } + } + + /* + * If there were any copies of temporaries on the stack, make sure the + * value we just reconstructed matches the stored value of that temporary. + * We sync the entire stack before calls, so the copy's slot holds the old + * value, but in future code we will assume the copy is valid and use the + * changed value of the invariant. + */ + + for (unsigned i = 0; temporaryCopies && i < temporaryCopies->length(); i++) { + const TemporaryCopy © = (*temporaryCopies)[i]; + masm.compareValue(copy.copy, copy.temporary, T0, T1, jumps); + } + + if (temporaryCopies) + cx->delete_(temporaryCopies); +} + +/* Loop analysis methods. */ + +/* + * Get any slot/constant accessed by a loop test operand, in terms of its value + * at the start of the next loop iteration. + */ +bool +LoopState::getLoopTestAccess(const SSAValue &v, uint32_t *pslot, int32_t *pconstant) +{ + *pslot = UNASSIGNED; + *pconstant = 0; + + if (v.kind() == SSAValue::PHI || v.kind() == SSAValue::VAR) { + /* + * Getting the value of a variable at a previous offset. Check that it + * is not updated before the start of the next loop iteration. + */ + uint32_t slot; + uint32_t offset; + if (v.kind() == SSAValue::PHI) { + slot = v.phiSlot(); + offset = v.phiOffset(); + } else { + slot = v.varSlot(); + offset = v.varInitial() ? 0 : v.varOffset(); + } + if (outerAnalysis->slotEscapes(slot)) + return false; + if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX) + return false; + *pslot = slot; + *pconstant = 0; + return true; + } + + jsbytecode *pc = outerScript->code + v.pushedOffset(); + + JSOp op = JSOp(*pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + + /* + * If the pc is modifying a variable and the value tested is its earlier value + * (e.g. 'x++ < n'), we need to account for the modification --- at the start + * of the next iteration, the value compared will have been 'x - 1'. + * Note that we don't need to worry about other accesses to the variable + * in the condition like 'x++ < x', as loop tests where both operands are + * modified by the loop are rejected. + */ + + switch (op) { + + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: { + if (!outerAnalysis->integerOperation(cx, pc)) + return false; + uint32_t slot = GetBytecodeSlot(outerScript, pc); + if (outerAnalysis->slotEscapes(slot)) + return false; + + *pslot = slot; + if (cs->format & JOF_POST) { + if (cs->format & JOF_INC) + *pconstant = -1; + else + *pconstant = 1; + } + return true; + } + + case JSOP_ZERO: + case JSOP_ONE: + case JSOP_UINT16: + case JSOP_UINT24: + case JSOP_INT8: + case JSOP_INT32: + *pconstant = GetBytecodeInteger(pc); + return true; + + default: + return false; + } +} + +void +LoopState::analyzeLoopTest() +{ + if (cc.debugMode()) + return; + + /* Don't handle do-while loops. */ + if (lifetime->entry == lifetime->head) + return; + + /* Don't handle loops with branching inside their condition. */ + if (lifetime->entry < lifetime->lastBlock) + return; + + /* Get the test performed before branching. */ + jsbytecode *backedge = outerScript->code + lifetime->backedge; + if (JSOp(*backedge) != JSOP_IFNE) + return; + const SSAValue &test = outerAnalysis->poppedValue(backedge, 0); + if (test.kind() != SSAValue::PUSHED) + return; + JSOp cmpop = JSOp(outerScript->code[test.pushedOffset()]); + switch (cmpop) { + case JSOP_GT: + case JSOP_GE: + case JSOP_LT: + case JSOP_LE: + break; + default: + return; + } + + SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1); + SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0); + + /* The test must be comparing known integers. */ + if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 || + outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) { + return; + } + + /* Reverse the condition if the RHS is modified by the loop. */ + uint32_t swapRHS; + int32_t swapConstant; + if (getLoopTestAccess(two, &swapRHS, &swapConstant)) { + if (swapRHS != UNASSIGNED && outerAnalysis->liveness(swapRHS).firstWrite(lifetime) != UINT32_MAX) { + SSAValue tmp = one; + one = two; + two = tmp; + cmpop = ReverseCompareOp(cmpop); + } + } + + uint32_t lhs; + int32_t lhsConstant; + if (!getLoopTestAccess(one, &lhs, &lhsConstant)) + return; + + uint32_t rhs = UNASSIGNED; + int32_t rhsConstant = 0; + CrossSSAValue rhsv(CrossScriptSSA::OUTER_FRAME, two); + if (!getEntryValue(rhsv, &rhs, &rhsConstant)) + return; + if (!loopInvariantEntry(rhs)) + return; + + if (lhs == UNASSIGNED) + return; + + int32_t constant; + if (!SafeSub(rhsConstant, lhsConstant, &constant)) + return; + + /* x > y ==> x >= y + 1 */ + if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant)) + return; + + /* x < y ==> x <= y - 1 */ + if (cmpop == JSOP_LT && !SafeSub(constant, 1, &constant)) + return; + + /* Passed all filters, this is a loop test we can capture. */ + + this->testLHS = lhs; + this->testRHS = rhs; + this->testConstant = constant; + this->testLessEqual = (cmpop == JSOP_LT || cmpop == JSOP_LE); +} + +void +LoopState::analyzeLoopIncrements() +{ + if (cc.debugMode()) + return; + + /* + * Find locals and arguments which are used in exactly one inc/dec operation in every + * iteration of the loop (we only match against the last basic block, but could + * also handle the first basic block). + */ + + for (uint32_t slot = ArgSlot(0); slot < LocalSlot(outerScript, outerScript->nfixed); slot++) { + if (outerAnalysis->slotEscapes(slot)) + continue; + + uint32_t offset = outerAnalysis->liveness(slot).onlyWrite(lifetime); + if (offset == UINT32_MAX || offset < lifetime->lastBlock) + continue; + + jsbytecode *pc = outerScript->code + offset; + JSOp op = JSOp(*pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + if (cs->format & (JOF_INC | JOF_DEC)) { + if (!outerAnalysis->integerOperation(cx, pc)) + continue; + + Increment inc; + inc.slot = slot; + inc.offset = offset; + increments.append(inc); + } + } +} + +bool +LoopState::definiteArrayAccess(const SSAValue &obj, const SSAValue &index) +{ + /* + * Check that an index on obj is definitely accessing a dense array, giving + * either a value modelled by the pushed types or a hole. This needs to be + * robust against recompilations that could be triggered inside the loop: + * the array must be loop invariant, and the index must definitely be an + * integer. + * + * This is used to determine if we can ignore possible integer overflow in + * an operation; if this site could read a non-integer element out of the + * array or invoke a scripted getter/setter, it could produce a string or + * other value by which the overflow could be observed. + */ + + TypeSet *objTypes = outerAnalysis->getValueTypes(obj); + TypeSet *elemTypes = outerAnalysis->getValueTypes(index); + + if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT || + elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) { + return false; + } + + if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY)) + return false; + + if (ArrayPrototypeHasIndexedProperty(cx, outerScript)) + return false; + + uint32_t objSlot; + int32_t objConstant; + CrossSSAValue objv(CrossScriptSSA::OUTER_FRAME, obj); + if (!getEntryValue(objv, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) + return false; + if (!loopInvariantEntry(objSlot)) + return false; + + /* Bitops must produce integers. */ + if (index.kind() == SSAValue::PUSHED) { + JSOp op = JSOp(outerScript->code[index.pushedOffset()]); + switch (op) { + case JSOP_BITAND: + case JSOP_BITOR: + case JSOP_BITXOR: + case JSOP_BITNOT: + case JSOP_RSH: + case JSOP_LSH: + case JSOP_URSH: + return true; + default:; + } + } + + uint32_t indexSlot; + int32_t indexConstant; + CrossSSAValue indexv(CrossScriptSSA::OUTER_FRAME, index); + if (!getEntryValue(indexv, &indexSlot, &indexConstant)) + return false; + + /* + * The index is determined from a variable's value at loop entry. We don't + * carry values with ignored overflows around loop back edges, so will know + * the index is a non-integer before such overflows are ignored. + */ + return true; +} + +void +LoopState::analyzeLoopBody(unsigned frame) +{ + if (cc.debugMode()) { + skipAnalysis = true; + return; + } + + JSScript *script = ssa->getFrame(frame).script; + analyze::ScriptAnalysis *analysis = script->analysis(); + JS_ASSERT(analysis && !analysis->failed() && analysis->ranInference()); + + /* + * The temporaries need to be positioned after all values in the deepest + * inlined frame plus another stack frame pushed by, e.g. ic::Call. + * This new frame will have been partially initialized by the call, and + * we don't want to scribble on that frame when restoring invariants. + */ + temporariesStart = + Max(temporariesStart, + ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots); + + if (script->failedBoundsCheck || analysis->localsAliasStack()) + skipAnalysis = true; + + /* Analyze the entire script for frames inlined in the loop body. */ + unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0; + unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length; + + unsigned offset = start; + while (offset < end) { + jsbytecode *pc = script->code + offset; + uint32_t successorOffset = offset + GetBytecodeLength(pc); + + analyze::Bytecode *opinfo = analysis->maybeCode(offset); + if (!opinfo) { + offset = successorOffset; + continue; + } + + JSOp op = JSOp(*pc); + + /* Don't do any hoisting for outer loops in case of nesting. */ + if (op == JSOP_LOOPHEAD) + skipAnalysis = true; + + switch (op) { + + case JSOP_CALL: { + /* + * Don't hoist within this loop unless calls at this site are inlined. + * :XXX: also recognize native calls which will be inlined. + */ + bool foundInline = false; + for (unsigned i = 0; !foundInline && i < ssa->numFrames(); i++) { + if (ssa->iterFrame(i).parent == frame && ssa->iterFrame(i).parentpc == pc) + foundInline = true; + } + if (!foundInline) + skipAnalysis = true; + break; + } + + case JSOP_EVAL: + case JSOP_FUNCALL: + case JSOP_FUNAPPLY: + case JSOP_NEW: + skipAnalysis = true; + break; + + case JSOP_SETELEM: { + SSAValue objValue = analysis->poppedValue(pc, 2); + SSAValue elemValue = analysis->poppedValue(pc, 1); + + TypeSet *objTypes = analysis->getValueTypes(objValue); + TypeSet *elemTypes = analysis->getValueTypes(elemValue); + + /* + * Mark the modset as unknown if the index might be non-integer, + * we don't want to consider the SETELEM PIC here. + */ + if (objTypes->unknownObject() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) { + unknownModset = true; + break; + } + + objTypes->addFreeze(cx); + for (unsigned i = 0; i < objTypes->getObjectCount(); i++) { + TypeObject *object = objTypes->getTypeObject(i); + if (!object) + continue; + if (!addModifiedProperty(object, JSID_VOID)) + return; + if (analysis->getCode(pc).arrayWriteHole && !addGrowArray(object)) + return; + } + + if (constrainedLoop && !definiteArrayAccess(objValue, elemValue)) + constrainedLoop = false; + break; + } + + case JSOP_GETELEM: { + SSAValue objValue = analysis->poppedValue(pc, 1); + SSAValue elemValue = analysis->poppedValue(pc, 0); + + if (constrainedLoop && !definiteArrayAccess(objValue, elemValue)) + constrainedLoop = false; + break; + } + + case JSOP_SETPROP: + case JSOP_SETMETHOD: { + JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); + jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom)); + + TypeSet *objTypes = analysis->poppedTypes(pc, 1); + if (objTypes->unknownObject()) { + unknownModset = true; + break; + } + + objTypes->addFreeze(cx); + for (unsigned i = 0; i < objTypes->getObjectCount(); i++) { + TypeObject *object = objTypes->getTypeObject(i); + if (!object) + continue; + if (!addModifiedProperty(object, id)) + continue; + } + + constrainedLoop = false; + break; + } + + case JSOP_ENUMELEM: + case JSOP_ENUMCONSTELEM: + unknownModset = true; + break; + + case JSOP_LOOPHEAD: + case JSOP_LOOPENTRY: + case JSOP_POP: + case JSOP_ZERO: + case JSOP_ONE: + case JSOP_INT8: + case JSOP_INT32: + case JSOP_UINT16: + case JSOP_UINT24: + case JSOP_FALSE: + case JSOP_TRUE: + case JSOP_GETARG: + case JSOP_SETARG: + case JSOP_INCARG: + case JSOP_DECARG: + case JSOP_ARGINC: + case JSOP_ARGDEC: + case JSOP_THIS: + case JSOP_GETLOCAL: + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_AND: + case JSOP_OR: + case JSOP_GOTO: + break; + + case JSOP_ADD: + case JSOP_SUB: + case JSOP_MUL: + case JSOP_MOD: + case JSOP_DIV: + case JSOP_BITAND: + case JSOP_BITOR: + case JSOP_BITXOR: + case JSOP_RSH: + case JSOP_LSH: + case JSOP_URSH: + case JSOP_EQ: + case JSOP_NE: + case JSOP_LT: + case JSOP_LE: + case JSOP_GT: + case JSOP_GE: + case JSOP_STRICTEQ: + case JSOP_STRICTNE: { + JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx); + if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE) + constrainedLoop = false; + } + /* FALLTHROUGH */ + + case JSOP_POS: + case JSOP_NEG: + case JSOP_BITNOT: { + JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx); + if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE) + constrainedLoop = false; + break; + } + + default: + constrainedLoop = false; + break; + } + + offset = successorOffset; + } +} + +bool +LoopState::addGrowArray(TypeObject *object) +{ + static const uint32_t MAX_SIZE = 10; + for (unsigned i = 0; i < growArrays.length(); i++) { + if (growArrays[i] == object) + return true; + } + if (growArrays.length() >= MAX_SIZE) { + unknownModset = true; + return false; + } + growArrays.append(object); + + return true; +} + +bool +LoopState::addModifiedProperty(TypeObject *object, jsid id) +{ + static const uint32_t MAX_SIZE = 20; + for (unsigned i = 0; i < modifiedProperties.length(); i++) { + if (modifiedProperties[i].object == object && modifiedProperties[i].id == id) + return true; + } + if (modifiedProperties.length() >= MAX_SIZE) { + unknownModset = true; + return false; + } + + ModifiedProperty property; + property.object = object; + property.id = id; + modifiedProperties.append(property); + + return true; +} + +bool +LoopState::hasGrowArray(TypeObject *object) +{ + if (unknownModset) + return true; + for (unsigned i = 0; i < growArrays.length(); i++) { + if (growArrays[i] == object) + return true; + } + return false; +} + +bool +LoopState::hasModifiedProperty(TypeObject *object, jsid id) +{ + if (unknownModset) + return true; + id = MakeTypeId(cx, id); + for (unsigned i = 0; i < modifiedProperties.length(); i++) { + if (modifiedProperties[i].object == object && modifiedProperties[i].id == id) + return true; + } + return false; +} + +uint32_t +LoopState::getIncrement(uint32_t slot) +{ + for (unsigned i = 0; i < increments.length(); i++) { + if (increments[i].slot == slot) + return increments[i].offset; + } + return UINT32_MAX; +} + +int32_t +LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32_t slot) +{ + /* + * The only terms that can appear in a hoisted bounds check are either + * loop invariant or are incremented or decremented exactly once in each + * iteration of the loop. Depending on the current pc in the body of the + * loop, return a constant adjustment if an increment/decrement for slot + * has not yet happened, such that 'slot + n' at this point is the value + * of slot at the start of the next iteration. + */ + uint32_t offset = getIncrement(slot); + + /* + * Note the '<' here. If this PC is at one of the increment opcodes, then + * behave as if the increment has not happened yet. This is needed for loop + * entry points, which can be directly at an increment. We won't rejoin + * after the increment, as we only take stub calls in such situations on + * integer overflow, which will disable hoisted conditions involving the + * variable anyways. + */ + if (offset == UINT32_MAX || offset < uint32_t(pc - outerScript->code)) + return 0; + + switch (JSOp(outerScript->code[offset])) { + case JSOP_INCLOCAL: + case JSOP_LOCALINC: + case JSOP_INCARG: + case JSOP_ARGINC: + return 1; + case JSOP_DECLOCAL: + case JSOP_LOCALDEC: + case JSOP_DECARG: + case JSOP_ARGDEC: + return -1; + default: + JS_NOT_REACHED("Bad op"); + return 0; + } +} + +bool +LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pconstant) +{ + CrossSSAValue cv = ssa->foldValue(iv); + + JSScript *script = ssa->getFrame(cv.frame).script; + ScriptAnalysis *analysis = script->analysis(); + const SSAValue &v = cv.v; + + /* + * For a stack value popped by the bytecode at offset, try to get an + * expression 'slot + constant' with the same value as the stack value + * and expressed in terms of the state at loop entry. + */ + + if (v.kind() == SSAValue::PHI) { + if (cv.frame != CrossScriptSSA::OUTER_FRAME) + return false; + if (v.phiSlot() >= TotalSlots(script)) + return false; + if (v.phiOffset() > lifetime->head && + outerAnalysis->liveness(v.phiSlot()).firstWrite(lifetime) < v.phiOffset()) { + return false; + } + *pslot = v.phiSlot(); + *pconstant = 0; + return true; + } + + if (v.kind() == SSAValue::VAR) { + if (cv.frame != CrossScriptSSA::OUTER_FRAME) + return false; + if (v.varInitial() || v.varOffset() < lifetime->head) { + *pslot = v.varSlot(); + *pconstant = 0; + return true; + } + } + + if (v.kind() != SSAValue::PUSHED) + return false; + + jsbytecode *pc = script->code + v.pushedOffset(); + JSOp op = (JSOp)*pc; + + switch (op) { + + case JSOP_GETLOCAL: + case JSOP_LOCALINC: + case JSOP_INCLOCAL: + case JSOP_GETARG: + case JSOP_ARGINC: + case JSOP_INCARG: { + if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc)) + return false; + uint32_t slot = GetBytecodeSlot(outerScript, pc); + if (outerAnalysis->slotEscapes(slot)) + return false; + uint32_t write = outerAnalysis->liveness(slot).firstWrite(lifetime); + if (write != UINT32_MAX && write < v.pushedOffset()) { + /* Variable has been modified since the start of the loop. */ + return false; + } + *pslot = slot; + *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0; + return true; + } + + case JSOP_THIS: + if (cv.frame != CrossScriptSSA::OUTER_FRAME) + return false; + *pslot = ThisSlot(); + *pconstant = 0; + return true; + + case JSOP_ZERO: + case JSOP_ONE: + case JSOP_UINT16: + case JSOP_UINT24: + case JSOP_INT8: + case JSOP_INT32: + *pslot = UNASSIGNED; + *pconstant = GetBytecodeInteger(pc); + return true; + + case JSOP_LENGTH: { + CrossSSAValue lengthcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0)); + FrameEntry *tmp = invariantLength(lengthcv); + if (!tmp) + return false; + *pslot = frame.outerSlot(tmp); + *pconstant = 0; + return true; + } + + case JSOP_GETPROP: { + JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); + jsid id = ATOM_TO_JSID(atom); + CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0)); + FrameEntry *tmp = invariantProperty(objcv, id); + if (!tmp) + return false; + *pslot = frame.outerSlot(tmp); + *pconstant = 0; + return true; + } + + default: + return false; + } +} + +bool +LoopState::computeInterval(const CrossSSAValue &cv, int32_t *pmin, int32_t *pmax) +{ + JSScript *script = ssa->getFrame(cv.frame).script; + ScriptAnalysis *analysis = script->analysis(); + const SSAValue &v = cv.v; + + if (v.kind() == SSAValue::VAR && !v.varInitial()) { + jsbytecode *pc = script->code + v.varOffset(); + switch (JSOp(*pc)) { + case JSOP_SETLOCAL: + case JSOP_SETARG: { + CrossSSAValue ncv(cv.frame, analysis->poppedValue(pc, 0)); + return computeInterval(ncv, pmin, pmax); + } + + default: + return false; + } + } + + if (v.kind() != SSAValue::PUSHED) + return false; + + jsbytecode *pc = script->code + v.pushedOffset(); + JSOp op = (JSOp)*pc; + + /* Note: this was adapted from similar code in nanojit/LIR.cpp */ + switch (op) { + + case JSOP_ZERO: + case JSOP_ONE: + case JSOP_UINT16: + case JSOP_UINT24: + case JSOP_INT8: + case JSOP_INT32: { + int32_t constant = GetBytecodeInteger(pc); + *pmin = constant; + *pmax = constant; + return true; + } + + case JSOP_BITAND: { + int32_t lhsmin, lhsmax, rhsmin, rhsmax; + CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1)); + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + bool haslhs = computeInterval(lhsv, &lhsmin, &lhsmax); + bool hasrhs = computeInterval(rhsv, &rhsmin, &rhsmax); + + /* Only handle bitand with a constant operand. */ + haslhs = haslhs && lhsmin == lhsmax && lhsmin >= 0; + hasrhs = hasrhs && rhsmin == rhsmax && rhsmin >= 0; + + if (haslhs && hasrhs) { + *pmin = 0; + *pmax = Min(lhsmax, rhsmax); + } else if (haslhs) { + *pmin = 0; + *pmax = lhsmax; + } else if (hasrhs) { + *pmin = 0; + *pmax = rhsmax; + } else { + return false; + } + return true; + } + + case JSOP_RSH: { + int32_t rhsmin, rhsmax; + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax) + return false; + + /* Only use the bottom 5 bits. */ + int32_t shift = rhsmin & 0x1f; + *pmin = -(1 << (31 - shift)); + *pmax = (1 << (31 - shift)) - 1; + return true; + } + + case JSOP_URSH: { + int32_t rhsmin, rhsmax; + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax) + return false; + + /* Only use the bottom 5 bits. */ + int32_t shift = rhsmin & 0x1f; + if (shift == 0) + return false; + *pmin = 0; + *pmax = (1 << (31 - shift)) - 1; + return true; + } + + case JSOP_MOD: { + int32_t rhsmin, rhsmax; + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax) + return false; + + int32_t rhs = abs(rhsmax); + *pmin = -(rhs - 1); + *pmax = rhs - 1; + return true; + } + + case JSOP_ADD: { + int32_t lhsmin, lhsmax, rhsmin, rhsmax; + CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1)); + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax)) + return false; + return SafeAdd(lhsmin, rhsmin, pmin) && SafeAdd(lhsmax, rhsmax, pmax); + } + + case JSOP_SUB: { + int32_t lhsmin, lhsmax, rhsmin, rhsmax; + CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1)); + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax)) + return false; + return SafeSub(lhsmin, rhsmax, pmin) && SafeSub(lhsmax, rhsmin, pmax); + } + + case JSOP_MUL: { + int32_t lhsmin, lhsmax, rhsmin, rhsmax; + CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1)); + CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0)); + if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax)) + return false; + int32_t nlhs = Max(abs(lhsmin), abs(lhsmax)); + int32_t nrhs = Max(abs(rhsmin), abs(rhsmax)); + + if (!SafeMul(nlhs, nrhs, pmax)) + return false; + + if (lhsmin < 0 || rhsmin < 0) { + /* pmax is nonnegative, so can be negated without overflow. */ + *pmin = -*pmax; + } else { + *pmin = 0; + } + + return true; + } + + default: + return false; + } +} diff --git a/deps/mozjs/js/src/methodjit/LoopState.h b/deps/mozjs/js/src/methodjit/LoopState.h new file mode 100644 index 00000000000..a9c5ca47c27 --- /dev/null +++ b/deps/mozjs/js/src/methodjit/LoopState.h @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Brendan Eich + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if !defined jsjaeger_loopstate_h__ && defined JS_METHODJIT +#define jsjaeger_loopstate_h__ + +#include "jsanalyze.h" +#include "methodjit/Compiler.h" + +namespace js { +namespace mjit { + +/* + * The LoopState keeps track of register and analysis state within the loop + * currently being processed by the Compiler. + * + * There are several analyses we do that are specific to loops: loop carried + * registers, bounds check hoisting, and loop invariant code motion. Brief + * descriptions of these analyses: + * + * Loop carried registers. We allocate registers as we emit code, in a single + * forward pass over the script. Normally this would mean we need to pick the + * register allocation at the head of the loop before any of the body has been + * processed. Instead, while processing the loop body we retroactively mark + * registers as holding the payload of certain entries at the head (being + * carried around the loop), so that the head's allocation ends up holding + * registers that are likely to be used shortly. This can be done provided that + * (a) the register has not been touched since the loop head, (b) the slot + * has not been modified or separately assigned a different register, and (c) + * all prior slow path rejoins in the loop are patched with reloads of the + * register. The register allocation at the loop head must have all entries + * synced, so that prior slow path syncs do not also need patching. + * + * Bounds check hoisting. If we can determine a loop invariant test which + * implies the bounds check at one or more array accesses, we hoist that and + * check it when initially entering the loop (from JIT code or the + * interpreter) and after every stub or C++ call. + * + * Loop invariant code motion. If we can determine a computation (arithmetic, + * array slot pointer or property access) is loop invariant, we give it a slot + * on the stack and preserve its value throughout the loop. We can allocate + * and carry registers for loop invariant slots as for normal slots. These + * slots sit above the frame's normal slots, and are transient --- they are + * clobbered whenever a new frame is pushed. We thus regenerate the loop + * invariant slots after every C++ and scripted call, and avoid doing LICM on + * loops which have such calls. This has a nice property that the slots only + * need to be loop invariant wrt the side effects that happen directly in the + * loop; if C++ calls a getter which scribbles on the object properties + * involved in an 'invariant' then we will reload the invariant's new value + * after the call finishes. + */ + +struct TemporaryCopy; + +enum InvariantArrayKind { DENSE_ARRAY, TYPED_ARRAY }; + +class LoopState : public MacroAssemblerTypedefs +{ + JSContext *cx; + analyze::CrossScriptSSA *ssa; + JSScript *outerScript; + analyze::ScriptAnalysis *outerAnalysis; + + Compiler &cc; + FrameState &frame; + + /* Basic information about this loop. */ + analyze::LoopAnalysis *lifetime; + + /* Allocation at the head of the loop, has all loop carried variables. */ + RegisterAllocation *alloc; + + /* + * Set if this is not a do-while loop and the compiler has advanced past + * the loop's entry point. + */ + bool reachedEntryPoint; + + /* + * Jump which initially enters the loop. The state is synced when this jump + * occurs, and needs a trampoline generated to load the right registers + * before going to entryTarget. + */ + Jump entry; + + /* Registers available for loop variables. */ + Registers loopRegs; + + /* Whether to skip all bounds check hoisting and loop invariant code analysis. */ + bool skipAnalysis; + + /* Prior stub rejoins to patch when new loop registers are allocated. */ + struct StubJoin { + unsigned index; + bool script; + }; + Vector loopJoins; + + /* Pending loads to patch for stub rejoins. */ + struct StubJoinPatch { + StubJoin join; + Address address; + AnyRegisterID reg; + }; + Vector loopPatches; + + /* + * Pair of a jump/label immediately after each call in the loop, to patch + * with restores of the loop invariant stack values. + */ + struct RestoreInvariantCall { + Jump jump; + Label label; + bool ool; + bool entry; + unsigned patchIndex; /* Index into Compiler's callSites. */ + + /* Any copies of temporaries on the stack */ + Vector *temporaryCopies; + }; + Vector restoreInvariantCalls; + + /* + * Aggregate structure for all loop invariant code and hoisted checks we + * can perform. These are all stored in the same vector as they may depend + * on each other and we need to emit code restoring them in order. + */ + struct InvariantEntry { + enum EntryKind { + /* + * initializedLength(array) > value1 + value2 + constant. + * Unsigned comparison, so will fail if value + constant < 0 + */ + DENSE_ARRAY_BOUNDS_CHECK, + TYPED_ARRAY_BOUNDS_CHECK, + + /* value1 + constant >= 0 */ + NEGATIVE_CHECK, + + /* constant >= value1 + value2 */ + RANGE_CHECK, + + /* For dense arrays */ + DENSE_ARRAY_SLOTS, + DENSE_ARRAY_LENGTH, + + /* For typed arrays */ + TYPED_ARRAY_SLOTS, + TYPED_ARRAY_LENGTH, + + /* For lazy arguments */ + INVARIANT_ARGS_BASE, + INVARIANT_ARGS_LENGTH, + + /* For definite properties */ + INVARIANT_PROPERTY + } kind; + union { + struct { + uint32_t arraySlot; + uint32_t valueSlot1; + uint32_t valueSlot2; + int32_t constant; + } check; + struct { + uint32_t arraySlot; + uint32_t temporary; + } array; + struct { + uint32_t objectSlot; + uint32_t propertySlot; + uint32_t temporary; + jsid id; + } property; + } u; + InvariantEntry() { PodZero(this); } + bool isBoundsCheck() const { + return kind == DENSE_ARRAY_BOUNDS_CHECK || kind == TYPED_ARRAY_BOUNDS_CHECK; + } + bool isCheck() const { + return isBoundsCheck() || kind == NEGATIVE_CHECK || kind == RANGE_CHECK; + } + }; + Vector invariantEntries; + + static inline bool entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1); + bool checkRedundantEntry(const InvariantEntry &entry); + + bool loopInvariantEntry(uint32_t slot); + bool addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot, + uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant); + void addNegativeCheck(uint32_t valueSlot, int32_t constant); + void addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant); + bool hasTestLinearRelationship(uint32_t slot); + + bool hasInvariants() { return !invariantEntries.empty(); } + void restoreInvariants(jsbytecode *pc, Assembler &masm, + Vector *temporaryCopies, Vector *jumps); + + public: + + /* Outer loop to this one, in case of loop nesting. */ + LoopState *outer; + + /* Offset from the outermost frame at which temporaries should be allocated. */ + uint32_t temporariesStart; + + LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa, + Compiler *cc, FrameState *frame); + bool init(jsbytecode *head, Jump entry, jsbytecode *entryTarget); + + void setOuterPC(jsbytecode *pc) + { + if (uint32_t(pc - outerScript->code) == lifetime->entry && lifetime->entry != lifetime->head) + reachedEntryPoint = true; + } + + bool generatingInvariants() { return !skipAnalysis; } + + /* Add a call with trailing jump/label, after which invariants need to be restored. */ + void addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses); + + uint32_t headOffset() { return lifetime->head; } + uint32_t getLoopRegs() { return loopRegs.freeMask; } + + Jump entryJump() { return entry; } + uint32_t entryOffset() { return lifetime->entry; } + uint32_t backedgeOffset() { return lifetime->backedge; } + + /* Whether the payload of slot is carried around the loop in a register. */ + bool carriesLoopReg(FrameEntry *fe) { return alloc->hasAnyReg(frame.entrySlot(fe)); } + + void setLoopReg(AnyRegisterID reg, FrameEntry *fe); + + void clearLoopReg(AnyRegisterID reg) + { + /* + * Mark reg as having been modified since the start of the loop; it + * cannot subsequently be marked to carry a register around the loop. + */ + JS_ASSERT(loopRegs.hasReg(reg) == alloc->loop(reg)); + if (loopRegs.hasReg(reg)) { + loopRegs.takeReg(reg); + alloc->setUnassigned(reg); + JaegerSpew(JSpew_Regalloc, "clearing loop register %s\n", reg.name()); + } + } + + void addJoin(unsigned index, bool script); + void clearLoopRegisters(); + + void flushLoop(StubCompiler &stubcc); + + /* + * These should only be used for entries which are known to be dense arrays + * (if they are objects at all). + */ + bool hoistArrayLengthCheck(InvariantArrayKind arrayKind, + const analyze::CrossSSAValue &obj, + const analyze::CrossSSAValue &index); + FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj); + + /* Methods for accesses on lazy arguments. */ + bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index); + FrameEntry *invariantArguments(); + + FrameEntry *invariantLength(const analyze::CrossSSAValue &obj); + FrameEntry *invariantProperty(const analyze::CrossSSAValue &obj, jsid id); + + /* Whether a binary or inc/dec op's result cannot overflow. */ + bool cannotIntegerOverflow(const analyze::CrossSSAValue &pushed); + + /* + * Whether integer overflow in addition or negative zeros in multiplication + * at a binary op can be safely ignored. + */ + bool ignoreIntegerOverflow(const analyze::CrossSSAValue &pushed); + + private: + /* Analysis information for the loop. */ + + /* + * Any inequality known to hold at the head of the loop. This has the + * form 'lhs <= rhs + constant' or 'lhs >= rhs + constant', depending on + * lessEqual. The lhs may be modified within the loop body (the test is + * invalid afterwards), and the rhs is invariant. This information is only + * valid if the LHS/RHS are known integers. + */ + enum { UNASSIGNED = UINT32_MAX }; + uint32_t testLHS; + uint32_t testRHS; + int32_t testConstant; + bool testLessEqual; + + /* + * A variable which will be incremented or decremented exactly once in each + * iteration of the loop. The offset of the operation is indicated, which + * may or may not run after the initial entry into the loop. + */ + struct Increment { + uint32_t slot; + uint32_t offset; + }; + Vector increments; + + /* It is unknown which arrays grow or which objects are modified in this loop. */ + bool unknownModset; + + /* + * Arrays which might grow during this loop. This is a guess, and may + * underapproximate the actual set of such arrays. + */ + Vector growArrays; + + /* Properties which might be modified during this loop. */ + struct ModifiedProperty { + types::TypeObject *object; + jsid id; + }; + Vector modifiedProperties; + + /* + * Whether this loop only performs integer and double arithmetic and dense + * array accesses. Integer overflows in this loop which only flow to bitops + * can be ignored. + */ + bool constrainedLoop; + + void analyzeLoopTest(); + void analyzeLoopIncrements(); + void analyzeLoopBody(unsigned frame); + + bool definiteArrayAccess(const analyze::SSAValue &obj, const analyze::SSAValue &index); + bool getLoopTestAccess(const analyze::SSAValue &v, uint32_t *pslot, int32_t *pconstant); + + bool addGrowArray(types::TypeObject *object); + bool addModifiedProperty(types::TypeObject *object, jsid id); + + bool hasGrowArray(types::TypeObject *object); + bool hasModifiedProperty(types::TypeObject *object, jsid id); + + uint32_t getIncrement(uint32_t slot); + int32_t adjustConstantForIncrement(jsbytecode *pc, uint32_t slot); + + bool getEntryValue(const analyze::CrossSSAValue &v, uint32_t *pslot, int32_t *pconstant); + bool computeInterval(const analyze::CrossSSAValue &v, int32_t *pmin, int32_t *pmax); + bool valueFlowsToBitops(const analyze::SSAValue &v); +}; + +} /* namespace mjit */ +} /* namespace js */ + +#endif /* jsjaeger_loopstate_h__ */ diff --git a/deps/mozjs/js/src/methodjit/MachineRegs.h b/deps/mozjs/js/src/methodjit/MachineRegs.h index 64443bb8474..e6c612301aa 100644 --- a/deps/mozjs/js/src/methodjit/MachineRegs.h +++ b/deps/mozjs/js/src/methodjit/MachineRegs.h @@ -40,14 +40,64 @@ #if !defined jsjaeger_regstate_h__ && defined JS_METHODJIT #define jsjaeger_regstate_h__ -#include "jsbit.h" +#include "mozilla/Util.h" + #include "assembler/assembler/MacroAssembler.h" namespace js { namespace mjit { +/* Common handling for both general purpose and floating point registers. */ + +struct AnyRegisterID { + unsigned reg_; + + AnyRegisterID() + : reg_((unsigned)-1) + { pin(); } + + AnyRegisterID(const AnyRegisterID &o) + : reg_(o.reg_) + { pin(); } + + AnyRegisterID(JSC::MacroAssembler::RegisterID reg) + : reg_((unsigned)reg) + { pin(); } + + AnyRegisterID(JSC::MacroAssembler::FPRegisterID reg) + : reg_(JSC::MacroAssembler::TotalRegisters + (unsigned)reg) + { pin(); } + + static inline AnyRegisterID fromRaw(unsigned reg); + + inline JSC::MacroAssembler::RegisterID reg(); + inline JSC::MacroAssembler::FPRegisterID fpreg(); + + bool isSet() { return reg_ != unsigned(-1); } + bool isReg() { return reg_ < JSC::MacroAssembler::TotalRegisters; } + bool isFPReg() { return isSet() && !isReg(); } + + inline const char * name(); + + private: + unsigned * pin() { + /* + * Workaround for apparent compiler bug in GCC 4.2. If GCC thinks that reg_ + * cannot escape then it compiles isReg() and other accesses to reg_ incorrectly. + */ + static unsigned *v; + v = ®_; + return v; + } +}; + struct Registers { + + /* General purpose registers. */ + + static const uint32_t TotalRegisters = JSC::MacroAssembler::TotalRegisters; + enum CallConvention { NormalCall, FastCall @@ -63,13 +113,17 @@ struct Registers { static const RegisterID ScratchReg = JSC::X86Registers::r11; #endif - // Register that homes the current StackFrame. -#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + // Register that homes the current JSStackFrame. +#if defined(JS_CPU_X86) + static const RegisterID JSFrameReg = JSC::X86Registers::ebp; +#elif defined(JS_CPU_X64) static const RegisterID JSFrameReg = JSC::X86Registers::ebx; #elif defined(JS_CPU_ARM) - static const RegisterID JSFrameReg = JSC::ARMRegisters::r11; + static const RegisterID JSFrameReg = JSC::ARMRegisters::r10; #elif defined(JS_CPU_SPARC) static const RegisterID JSFrameReg = JSC::SparcRegisters::l0; +#elif defined(JS_CPU_MIPS) + static const RegisterID JSFrameReg = JSC::MIPSRegisters::s0; #endif #if defined(JS_CPU_X86) || defined(JS_CPU_X64) @@ -79,11 +133,13 @@ struct Registers { static const RegisterID ArgReg1 = JSC::X86Registers::edx; # if defined(JS_CPU_X64) static const RegisterID ArgReg2 = JSC::X86Registers::r8; + static const RegisterID ArgReg3 = JSC::X86Registers::r9; # endif # else static const RegisterID ArgReg0 = JSC::X86Registers::edi; static const RegisterID ArgReg1 = JSC::X86Registers::esi; static const RegisterID ArgReg2 = JSC::X86Registers::edx; + static const RegisterID ArgReg3 = JSC::X86Registers::ecx; # endif #elif JS_CPU_ARM static const RegisterID ReturnReg = JSC::ARMRegisters::r0; @@ -98,25 +154,34 @@ struct Registers { static const RegisterID ArgReg3 = JSC::SparcRegisters::o3; static const RegisterID ArgReg4 = JSC::SparcRegisters::o4; static const RegisterID ArgReg5 = JSC::SparcRegisters::o5; +#elif JS_CPU_MIPS + static const RegisterID ReturnReg = JSC::MIPSRegisters::v0; + static const RegisterID ArgReg0 = JSC::MIPSRegisters::a0; + static const RegisterID ArgReg1 = JSC::MIPSRegisters::a1; + static const RegisterID ArgReg2 = JSC::MIPSRegisters::a2; + static const RegisterID ArgReg3 = JSC::MIPSRegisters::a3; #endif static const RegisterID StackPointer = JSC::MacroAssembler::stackPointerRegister; - static inline uint32 maskReg(RegisterID reg) { + static inline uint32_t maskReg(RegisterID reg) { return (1 << reg); } - static inline uint32 mask2Regs(RegisterID reg1, RegisterID reg2) { + static inline uint32_t mask2Regs(RegisterID reg1, RegisterID reg2) { return maskReg(reg1) | maskReg(reg2); } - static inline uint32 mask3Regs(RegisterID reg1, RegisterID reg2, RegisterID reg3) { + static inline uint32_t mask3Regs(RegisterID reg1, RegisterID reg2, RegisterID reg3) { return maskReg(reg1) | maskReg(reg2) | maskReg(reg3); } #if defined(JS_CPU_X86) || defined(JS_CPU_X64) - static const uint32 TempRegs = + static const uint32_t TempRegs = (1 << JSC::X86Registers::eax) +# if defined(JS_CPU_X86) + | (1 << JSC::X86Registers::ebx) +# endif | (1 << JSC::X86Registers::ecx) | (1 << JSC::X86Registers::edx) # if defined(JS_CPU_X64) @@ -130,7 +195,7 @@ struct Registers { ; # if defined(JS_CPU_X64) - static const uint32 SavedRegs = + static const uint32_t SavedRegs = /* r11 is scratchRegister, used by JSC. */ (1 << JSC::X86Registers::r12) // r13 is TypeMaskReg. @@ -141,46 +206,45 @@ struct Registers { | (1 << JSC::X86Registers::edi) # endif # else - static const uint32 SavedRegs = + static const uint32_t SavedRegs = (1 << JSC::X86Registers::esi) | (1 << JSC::X86Registers::edi) # endif ; # if defined(JS_CPU_X86) - static const uint32 SingleByteRegs = (TempRegs | SavedRegs) & + static const uint32_t SingleByteRegs = (TempRegs | SavedRegs) & ~((1 << JSC::X86Registers::esi) | (1 << JSC::X86Registers::edi) | (1 << JSC::X86Registers::ebp) | (1 << JSC::X86Registers::esp)); # elif defined(JS_CPU_X64) - static const uint32 SingleByteRegs = TempRegs | SavedRegs; + static const uint32_t SingleByteRegs = TempRegs | SavedRegs; # endif #elif defined(JS_CPU_ARM) - static const uint32 TempRegs = + static const uint32_t TempRegs = (1 << JSC::ARMRegisters::r0) | (1 << JSC::ARMRegisters::r1) | (1 << JSC::ARMRegisters::r2); // r3 is reserved as a scratch register for the assembler. + // r12 is IP, and is used for stub calls. - static const uint32 SavedRegs = + static const uint32_t SavedRegs = (1 << JSC::ARMRegisters::r4) | (1 << JSC::ARMRegisters::r5) | (1 << JSC::ARMRegisters::r6) | (1 << JSC::ARMRegisters::r7) // r8 is reserved as a scratch register for the assembler. - | (1 << JSC::ARMRegisters::r9) - | (1 << JSC::ARMRegisters::r10); - // r11 is reserved for JSFrameReg. - // r12 is IP, and is used for stub calls. + | (1 << JSC::ARMRegisters::r9); + // r10 is reserved for JSFrameReg. // r13 is SP and must always point to VMFrame whilst in generated code. // r14 is LR and is used for return sequences. // r15 is PC (program counter). - static const uint32 SingleByteRegs = TempRegs | SavedRegs; + static const uint32_t SingleByteRegs = TempRegs | SavedRegs; #elif defined(JS_CPU_SPARC) - static const uint32 TempRegs = + static const uint32_t TempRegs = (1 << JSC::SparcRegisters::o0) | (1 << JSC::SparcRegisters::o1) | (1 << JSC::SparcRegisters::o2) @@ -188,34 +252,60 @@ struct Registers { | (1 << JSC::SparcRegisters::o4) | (1 << JSC::SparcRegisters::o5); - static const uint32 SavedRegs = + static const uint32_t SavedRegs = (1 << JSC::SparcRegisters::l2) | (1 << JSC::SparcRegisters::l3) | (1 << JSC::SparcRegisters::l4) | (1 << JSC::SparcRegisters::l5) | (1 << JSC::SparcRegisters::l6) - | (1 << JSC::SparcRegisters::l7) - | (1 << JSC::SparcRegisters::i0) - | (1 << JSC::SparcRegisters::i1) - | (1 << JSC::SparcRegisters::i2) - | (1 << JSC::SparcRegisters::i3) - | (1 << JSC::SparcRegisters::i4) - | (1 << JSC::SparcRegisters::i5); - - static const uint32 SingleByteRegs = TempRegs | SavedRegs; + | (1 << JSC::SparcRegisters::l7); + + static const uint32_t SingleByteRegs = TempRegs | SavedRegs; +#elif defined(JS_CPU_MIPS) + static const uint32_t TempRegs = + (1 << JSC::MIPSRegisters::at) + | (1 << JSC::MIPSRegisters::v0) + | (1 << JSC::MIPSRegisters::v1) + | (1 << JSC::MIPSRegisters::a0) + | (1 << JSC::MIPSRegisters::a1) + | (1 << JSC::MIPSRegisters::a2) + | (1 << JSC::MIPSRegisters::a3) + | (1 << JSC::MIPSRegisters::t5) + | (1 << JSC::MIPSRegisters::t6) + | (1 << JSC::MIPSRegisters::t7); + /* t0-t4,t9 is reserved as a scratch register for the assembler. + We don't use t8 ($24), as we limit ourselves within $0 to $23 to + leave the bitmask for 8 FP registers. */ + + static const uint32_t SavedRegs = + (1 << JSC::MIPSRegisters::s1) + | (1 << JSC::MIPSRegisters::s2) + | (1 << JSC::MIPSRegisters::s3) + | (1 << JSC::MIPSRegisters::s4) + | (1 << JSC::MIPSRegisters::s5) + | (1 << JSC::MIPSRegisters::s6) + | (1 << JSC::MIPSRegisters::s7); + // s0 is reserved for JSFrameReg. + + static const uint32_t SingleByteRegs = TempRegs | SavedRegs; #else # error "Unsupported platform" #endif - static const uint32 AvailRegs = SavedRegs | TempRegs; + static const uint32_t AvailRegs = SavedRegs | TempRegs; + + static bool isAvail(RegisterID reg) { + uint32_t mask = maskReg(reg); + return bool(mask & AvailRegs); + } static bool isSaved(RegisterID reg) { - uint32 mask = maskReg(reg); + uint32_t mask = maskReg(reg); JS_ASSERT(mask & AvailRegs); return bool(mask & SavedRegs); } - static inline uint32 numArgRegs(CallConvention convention) { + static inline uint32_t numArgRegs(CallConvention convention) { #if defined(JS_CPU_X86) # if defined(JS_NO_FASTCALL) return 0; @@ -232,10 +322,12 @@ struct Registers { return 4; #elif defined(JS_CPU_SPARC) return 6; +#elif defined(JS_CPU_MIPS) + return 4; #endif } - static inline bool regForArg(CallConvention conv, uint32 i, RegisterID *reg) { + static inline bool regForArg(CallConvention conv, uint32_t i, RegisterID *reg) { #if defined(JS_CPU_X86) static const RegisterID regs[] = { JSC::X86Registers::ecx, @@ -282,237 +374,246 @@ struct Registers { JSC::SparcRegisters::o4, JSC::SparcRegisters::o5 }; +#elif defined(JS_CPU_MIPS) + static const RegisterID regs[] = { + JSC::MIPSRegisters::a0, + JSC::MIPSRegisters::a1, + JSC::MIPSRegisters::a2, + JSC::MIPSRegisters::a3, + }; #endif - JS_ASSERT(numArgRegs(conv) == JS_ARRAY_LENGTH(regs)); - if (i > JS_ARRAY_LENGTH(regs)) + JS_ASSERT(numArgRegs(conv) == mozilla::ArrayLength(regs)); + if (i > mozilla::ArrayLength(regs)) return false; *reg = regs[i]; return true; } - Registers() - : freeMask(AvailRegs) - { } - - Registers(uint32 freeMask) - : freeMask(freeMask) - { } - - Registers(const Registers &other) - : freeMask(other.freeMask) - { } - - Registers & operator =(const Registers &other) - { - freeMask = other.freeMask; - return *this; - } - - void reset() { - freeMask = AvailRegs; - } - - bool empty() const { - return !freeMask; - } - - bool empty(uint32 mask) const { - return !(freeMask & mask); - } - - RegisterID peekReg() { - JS_ASSERT(!empty()); - int ireg; - JS_FLOOR_LOG2(ireg, freeMask); - RegisterID reg = (RegisterID)ireg; - return reg; - } - - RegisterID takeAnyReg() { - RegisterID reg = peekReg(); - takeReg(reg); - return reg; - } - - bool hasRegInMask(uint32 mask) const { - Registers temp(freeMask & mask); - return !temp.empty(); - } - - RegisterID takeRegInMask(uint32 mask) { - Registers temp(freeMask & mask); - RegisterID reg = temp.takeAnyReg(); - takeReg(reg); - return reg; - } - - bool hasReg(RegisterID reg) const { - return !!(freeMask & (1 << reg)); - } - - void putRegUnchecked(RegisterID reg) { - freeMask |= (1 << reg); - } - - void putReg(RegisterID reg) { - JS_ASSERT(!hasReg(reg)); - putRegUnchecked(reg); - } - - void takeReg(RegisterID reg) { - JS_ASSERT(hasReg(reg)); - takeRegUnchecked(reg); - } - - void takeRegUnchecked(RegisterID reg) { - freeMask &= ~(1 << reg); - } - - bool operator ==(const Registers &other) { - return freeMask == other.freeMask; - } - - uint32 freeMask; -}; - - -struct FPRegisters { + /* Floating point registers. */ typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; #if defined(JS_CPU_X86) || defined(JS_CPU_X64) - static const uint32 TotalFPRegisters = 8; - static const uint32 TempFPRegs = +#ifdef _WIN64 + /* xmm0-xmm5 are scratch register on Win64 ABI */ + static const uint32_t TotalFPRegisters = 5; + static const FPRegisterID FPConversionTemp = JSC::X86Registers::xmm5; +#else + static const uint32_t TotalFPRegisters = 7; + static const FPRegisterID FPConversionTemp = JSC::X86Registers::xmm7; +#endif + static const uint32_t TempFPRegs = ( (1 << JSC::X86Registers::xmm0) | (1 << JSC::X86Registers::xmm1) | (1 << JSC::X86Registers::xmm2) | (1 << JSC::X86Registers::xmm3) | (1 << JSC::X86Registers::xmm4) +#ifndef _WIN64 | (1 << JSC::X86Registers::xmm5) | (1 << JSC::X86Registers::xmm6) - | (1 << JSC::X86Registers::xmm7); - /* FIXME: Temporary hack until FPRegister allocation exists. */ - static const FPRegisterID First = JSC::X86Registers::xmm0; - static const FPRegisterID Second = JSC::X86Registers::xmm1; - static const FPRegisterID Temp0 = JSC::X86Registers::xmm2; - static const FPRegisterID Temp1 = JSC::X86Registers::xmm3; +#endif + ) << TotalRegisters; #elif defined(JS_CPU_ARM) - static const uint32 TotalFPRegisters = 4; - static const uint32 TempFPRegs = + static const uint32_t TotalFPRegisters = 3; + static const uint32_t TempFPRegs = ( (1 << JSC::ARMRegisters::d0) | (1 << JSC::ARMRegisters::d1) | (1 << JSC::ARMRegisters::d2) - | (1 << JSC::ARMRegisters::d3); - /* FIXME: Temporary hack until FPRegister allocation exists. */ - static const FPRegisterID First = JSC::ARMRegisters::d0; - static const FPRegisterID Second = JSC::ARMRegisters::d1; - static const FPRegisterID Temp0 = JSC::ARMRegisters::d2; - static const FPRegisterID Temp1 = JSC::ARMRegisters::d3; + ) << TotalRegisters; + static const FPRegisterID FPConversionTemp = JSC::ARMRegisters::d3; #elif defined(JS_CPU_SPARC) - static const uint32 TotalFPRegisters = 16; - static const uint32 TempFPRegs = + static const uint32_t TotalFPRegisters = 8; + static const uint32_t TempFPRegs = (uint32_t)( (1 << JSC::SparcRegisters::f0) | (1 << JSC::SparcRegisters::f2) | (1 << JSC::SparcRegisters::f4) | (1 << JSC::SparcRegisters::f6) - | (1 << JSC::SparcRegisters::f8) - | (1 << JSC::SparcRegisters::f10) - | (1 << JSC::SparcRegisters::f12) - | (1 << JSC::SparcRegisters::f14) - | (1 << JSC::SparcRegisters::f16) - | (1 << JSC::SparcRegisters::f18) - | (1 << JSC::SparcRegisters::f20) - | (1 << JSC::SparcRegisters::f22) - | (1 << JSC::SparcRegisters::f24) - | (1 << JSC::SparcRegisters::f26) - | (1 << JSC::SparcRegisters::f28); - /* FIXME: Temporary hack until FPRegister allocation exists. */ - static const FPRegisterID First = JSC::SparcRegisters::f0; - static const FPRegisterID Second = JSC::SparcRegisters::f2; + ) << TotalRegisters; + static const FPRegisterID FPConversionTemp = JSC::SparcRegisters::f8; +#elif defined(JS_CPU_MIPS) + /* TotalRegisters is 24, so TotalFPRegisters can be 8 to have a 32-bit + bit mask. + Note that the O32 ABI can access only even FP registers. */ + static const uint32_t TotalFPRegisters = 8; + static const uint32_t TempFPRegs = (uint32_t)( + (1 << JSC::MIPSRegisters::f0) + | (1 << JSC::MIPSRegisters::f2) + | (1 << JSC::MIPSRegisters::f4) + | (1 << JSC::MIPSRegisters::f6) + ) << TotalRegisters; + // f16 is reserved as a scratch register for the assembler. + static const FPRegisterID FPConversionTemp = JSC::MIPSRegisters::f18; #else # error "Unsupported platform" #endif - static const uint32 AvailFPRegs = TempFPRegs; + /* Temp reg that can be clobbered when setting up a fallible fast or ABI call. */ +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + static const RegisterID ClobberInCall = JSC::X86Registers::ecx; +#elif defined(JS_CPU_ARM) + static const RegisterID ClobberInCall = JSC::ARMRegisters::r2; +#elif defined(JS_CPU_SPARC) + static const RegisterID ClobberInCall = JSC::SparcRegisters::l1; +#elif defined(JS_CPU_MIPS) + static const RegisterID ClobberInCall = JSC::MIPSRegisters::at; +#endif + + static const uint32_t AvailFPRegs = TempFPRegs; - FPRegisters() - : freeFPMask(AvailFPRegs) - { } + static inline uint32_t maskReg(FPRegisterID reg) { + return (1 << reg) << TotalRegisters; + } + + /* Common code. */ + + static const uint32_t TotalAnyRegisters = TotalRegisters + TotalFPRegisters; + static const uint32_t TempAnyRegs = TempRegs | TempFPRegs; + static const uint32_t AvailAnyRegs = AvailRegs | AvailFPRegs; + + static inline uint32_t maskReg(AnyRegisterID reg) { + return (1 << reg.reg_); + } + + /* Get a register which is not live before a FASTCALL. */ + static inline RegisterID tempCallReg() { + Registers regs(TempRegs); + regs.takeReg(Registers::ArgReg0); + regs.takeReg(Registers::ArgReg1); + return regs.takeAnyReg().reg(); + } - FPRegisters(uint32 freeFPMask) - : freeFPMask(freeFPMask) + /* Get a register which is not live before a normal ABI call with at most four args. */ + static inline Registers tempCallRegMask() { + Registers regs(AvailRegs); +#ifndef JS_CPU_X86 + regs.takeReg(ArgReg0); + regs.takeReg(ArgReg1); + regs.takeReg(ArgReg2); +#if defined(JS_CPU_SPARC) || defined(JS_CPU_X64) + regs.takeReg(ArgReg3); +#endif +#endif + return regs; + } + + Registers(uint32_t freeMask) + : freeMask(freeMask) { } - FPRegisters(const FPRegisters &other) - : freeFPMask(other.freeFPMask) + Registers(const Registers &other) + : freeMask(other.freeMask) { } - FPRegisters & operator =(const FPRegisters &other) + Registers & operator =(const Registers &other) { - freeFPMask = other.freeFPMask; + freeMask = other.freeMask; return *this; } - void reset() { - freeFPMask = AvailFPRegs; + bool empty(uint32_t mask) const { + return !(freeMask & mask); } bool empty() const { - return !freeFPMask; + return !freeMask; + } + + AnyRegisterID peekReg(uint32_t mask) { + JS_ASSERT(!empty(mask)); + unsigned ireg; + JS_FLOOR_LOG2(ireg, freeMask & mask); + return AnyRegisterID::fromRaw(ireg); } - bool empty(uint32 mask) const { - return !(freeFPMask & mask); + AnyRegisterID peekReg() { + return peekReg(freeMask); } - FPRegisterID takeAnyReg() { - JS_ASSERT(!empty()); - int ireg; - JS_FLOOR_LOG2(ireg, freeFPMask); - FPRegisterID reg = (FPRegisterID)ireg; + AnyRegisterID takeAnyReg(uint32_t mask) { + AnyRegisterID reg = peekReg(mask); takeReg(reg); return reg; } - bool hasRegInMask(uint32 mask) const { - FPRegisters temp(freeFPMask & mask); - return !temp.empty(); + AnyRegisterID takeAnyReg() { + return takeAnyReg(freeMask); } - FPRegisterID takeRegInMask(uint32 mask) { - FPRegisters temp(freeFPMask & mask); - FPRegisterID reg = temp.takeAnyReg(); - takeReg(reg); - return reg; + bool hasReg(AnyRegisterID reg) const { + return !!(freeMask & (1 << reg.reg_)); + } + + bool hasRegInMask(uint32_t mask) const { + return !!(freeMask & mask); + } + + bool hasAllRegs(uint32_t mask) const { + return (freeMask & mask) == mask; } - bool hasReg(FPRegisterID fpreg) const { - return !!(freeFPMask & (1 << fpreg)); + void putRegUnchecked(AnyRegisterID reg) { + freeMask |= (1 << reg.reg_); } - void putRegUnchecked(FPRegisterID fpreg) { - freeFPMask |= (1 << fpreg); + void putReg(AnyRegisterID reg) { + JS_ASSERT(!hasReg(reg)); + putRegUnchecked(reg); } - void putReg(FPRegisterID fpreg) { - JS_ASSERT(!hasReg(fpreg)); - putRegUnchecked(fpreg); + void takeReg(AnyRegisterID reg) { + JS_ASSERT(hasReg(reg)); + takeRegUnchecked(reg); } - void takeReg(FPRegisterID fpreg) { - JS_ASSERT(hasReg(fpreg)); - freeFPMask &= ~(1 << fpreg); + void takeRegUnchecked(AnyRegisterID reg) { + freeMask &= ~(1 << reg.reg_); } - bool operator ==(const FPRegisters &other) { - return freeFPMask == other.freeFPMask; + bool operator ==(const Registers &other) { + return freeMask == other.freeMask; } - uint32 freeFPMask; + uint32_t freeMask; }; static const JSC::MacroAssembler::RegisterID JSFrameReg = Registers::JSFrameReg; +AnyRegisterID +AnyRegisterID::fromRaw(unsigned reg_) +{ + JS_ASSERT(reg_ < Registers::TotalAnyRegisters); + AnyRegisterID reg; + reg.reg_ = reg_; + return reg; +} + +JSC::MacroAssembler::RegisterID +AnyRegisterID::reg() +{ + JS_ASSERT(reg_ < Registers::TotalRegisters); + return (JSC::MacroAssembler::RegisterID) reg_; +} + +JSC::MacroAssembler::FPRegisterID +AnyRegisterID::fpreg() +{ + JS_ASSERT(reg_ >= Registers::TotalRegisters && + reg_ < Registers::TotalAnyRegisters); + return (JSC::MacroAssembler::FPRegisterID) (reg_ - Registers::TotalRegisters); +} + +const char * +AnyRegisterID::name() +{ +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + return isReg() ? JSC::X86Registers::nameIReg(reg()) : JSC::X86Registers::nameFPReg(fpreg()); +#elif defined(JS_CPU_ARM) + return isReg() ? JSC::ARMAssembler::nameGpReg(reg()) : JSC::ARMAssembler::nameFpRegD(fpreg()); +#else + return "???"; +#endif +} + } /* namespace mjit */ } /* namespace js */ diff --git a/deps/mozjs/js/src/methodjit/MethodJIT-inl.h b/deps/mozjs/js/src/methodjit/MethodJIT-inl.h index d5ecbb1ea79..ad7cc5bedb0 100644 --- a/deps/mozjs/js/src/methodjit/MethodJIT-inl.h +++ b/deps/mozjs/js/src/methodjit/MethodJIT-inl.h @@ -50,29 +50,31 @@ enum CompileRequest CompileRequest_JIT }; -/* Number of times a script must be called before we run it in the methodjit. */ -static const size_t CALLS_BEFORE_COMPILE = 16; - -/* Number of loop back-edges we execute in the interpreter before methodjitting. */ -static const size_t BACKEDGES_BEFORE_COMPILE = 16; +/* + * We wait before compiling a function with Type Inference to allow more gathering + * of type information and less recompilation. + */ +static const size_t USES_BEFORE_COMPILE = 16; +static const size_t INFER_USES_BEFORE_COMPILE = 40; static inline CompileStatus -CanMethodJIT(JSContext *cx, JSScript *script, StackFrame *fp, CompileRequest request) +CanMethodJIT(JSContext *cx, JSScript *script, bool construct, CompileRequest request) { if (!cx->methodJitEnabled) return Compile_Abort; - JITScriptStatus status = script->getJITStatus(fp->isConstructing()); + JITScriptStatus status = script->getJITStatus(construct); if (status == JITScript_Invalid) return Compile_Abort; - if (request == CompileRequest_Interpreter && - status == JITScript_None && + if (request == CompileRequest_Interpreter && status == JITScript_None && !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && - script->incCallCount() <= CALLS_BEFORE_COMPILE) + (cx->typeInferenceEnabled() + ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE + : script->incUseCount() <= USES_BEFORE_COMPILE)) { return Compile_Skipped; } if (status == JITScript_None) - return TryCompile(cx, fp); + return TryCompile(cx, script, construct); return Compile_Okay; } @@ -88,14 +90,15 @@ CanMethodJITAtBranch(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecode JITScriptStatus status = script->getJITStatus(fp->isConstructing()); if (status == JITScript_Invalid) return Compile_Abort; - if (status == JITScript_None && - !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && - cx->compartment->incBackEdgeCount(pc) <= BACKEDGES_BEFORE_COMPILE) - { - return Compile_Skipped; + if (status == JITScript_None && !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS)) { + if ((cx->typeInferenceEnabled()) + ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE + : script->incUseCount() <= USES_BEFORE_COMPILE) { + return Compile_Skipped; + } } if (status == JITScript_None) - return TryCompile(cx, fp); + return TryCompile(cx, fp->script(), fp->isConstructing()); return Compile_Okay; } diff --git a/deps/mozjs/js/src/methodjit/MethodJIT.cpp b/deps/mozjs/js/src/methodjit/MethodJIT.cpp index a0f5834942b..1f856064f66 100644 --- a/deps/mozjs/js/src/methodjit/MethodJIT.cpp +++ b/deps/mozjs/js/src/methodjit/MethodJIT.cpp @@ -39,7 +39,9 @@ #include "MethodJIT.h" #include "Logging.h" #include "assembler/jit/ExecutableAllocator.h" -#include "jstracer.h" +#include "assembler/assembler/RepatchBuffer.h" +#include "js/MemoryMetrics.h" +#include "jsgcmark.h" #include "BaseAssembler.h" #include "Compiler.h" #include "MonoIC.h" @@ -48,6 +50,7 @@ #include "jscntxtinlines.h" #include "jscompartment.h" #include "jsscope.h" +#include "jsgcmark.h" #include "jsgcinlines.h" #include "jsinterpinlines.h" @@ -55,9 +58,20 @@ using namespace js; using namespace js::mjit; +#ifdef __GCC_HAVE_DWARF2_CFI_ASM +# define CFI(str) str +#else +# define CFI(str) +#endif + +// Put manually-inserted call frame unwinding information into .debug_frame +// rather than .eh_frame, because we compile with -fno-exceptions which might +// discard the .eh_frame section. (See +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43232). +CFI(asm(".cfi_sections .debug_frame");) js::mjit::CompilerAllocPolicy::CompilerAllocPolicy(JSContext *cx, Compiler &compiler) -: ContextAllocPolicy(cx), +: TempAllocPolicy(cx), oomFlag(&compiler.oomInVector) { } @@ -84,7 +98,8 @@ StackFrame::methodjitStaticAsserts() * * JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function * creates a VMFrame on the machine stack and jumps into JIT'd code. The JIT'd - * code will eventually jump back to the VMFrame. + * code will eventually jump back to JaegerTrampolineReturn, clean up the + * VMFrame and return into C++. * * - Called from C++ function EnterMethodJIT. * - Parameters: cx, fp, code, stackLimit @@ -105,34 +120,43 @@ StackFrame::methodjitStaticAsserts() * at. Because the jit-code ABI conditions are satisfied, we can just jump to * that point. * - * - Used by RunTracer() + * JaegerInterpoline - After returning from a stub or scripted call made by JIT'd + * code, calls into Interpret and has it finish execution of the JIT'd script. + * If we have to throw away the JIT code for a script for some reason (either + * a new trap is added for debug code, or assumptions made by the JIT code + * have broken and forced its invalidation), the call returns into the + * Interpoline which calls Interpret to finish the JIT frame. The Interpret + * call may eventually recompile the script, in which case it will join into + * that code with a new VMFrame activation and JaegerTrampoline. + * + * - Returned into from stub calls originally made from JIT code. + * - An alternate version, JaegerInterpolineScripted, returns from scripted + * calls originally made from JIT code, and fixes up state to match the + * stub call ABI. */ #ifdef JS_METHODJIT_PROFILE_STUBS static const size_t STUB_CALLS_FOR_OP_COUNT = 255; -static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT]; +static uint32_t StubCallsForOp[STUB_CALLS_FOR_OP_COUNT]; #endif -extern "C" void JaegerTrampolineReturn(); - +// Called from JaegerTrampoline only extern "C" void JS_FASTCALL PushActiveVMFrame(VMFrame &f) { - f.entryfp->script()->compartment->jaegerCompartment->pushActiveFrame(&f); - f.regs.fp()->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn)); + f.oldregs = &f.cx->stack.regs(); + f.cx->stack.repointRegs(&f.regs); + f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f); + f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn)); + f.regs.clearInlined(); } +// Called from JaegerTrampolineReturn, JaegerThrowpoline, JaegerInterpoline extern "C" void JS_FASTCALL PopActiveVMFrame(VMFrame &f) { - f.entryfp->script()->compartment->jaegerCompartment->popActiveFrame(); -} - -extern "C" void JS_FASTCALL -SetVMFrameRegs(VMFrame &f) -{ - /* Restored on exit from EnterMethodJIT. */ - f.cx->stack.repointRegs(&f.regs); + f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame(); + f.cx->stack.repointRegs(f.oldregs); } #if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2) @@ -163,10 +187,73 @@ JS_STATIC_ASSERT(offsetof(FrameRegs, sp) == 0); # define HIDE_SYMBOL(name) #endif +/* + * Notes about DWARF Call Frame Information (CFI) annotations: + * + * A .cfi directive placed in assembly code describes how to recover the + * caller's registers when control is at or after that directive. That is, + * they describe the states that hold between one instruction and the next, + * not the instructions themselves. Later directives override earlier + * directives. + * + * In DWARF CFI, each stack frame has a Canonical Frame Address (CFA) that + * remains constant throughout the frame's lifetime. Exactly where it is is + * a matter of convention; on the x86 and x86_64, for example, the CFA + * points just after the end of the current stack frame: the address of the + * next word after the return address. The CFI annotations describe 1) how + * to compute the CFA at each point in the function, and 2) given the CFA, + * where the caller's value of each register has been saved. (CFI specifies + * saved registers' locations relative to the CFA, instead of the stack + * pointer, so that when we push or pop the stack, we need only adjust our + * rule for computing the CFA, not the rule for each saved register.) + * + * Quick reference: + * + * .cfi_startproc, .cfi_endproc + * Put these at the beginning and end of the block of code you're + * annotating. + * + * (The following directives apply starting at the point they appear until + * they are overridden or until the .cfi_endproc.) + * + * .cfi_def_cfa REGISTER, OFFSET + * The CFA is the value of REGISTER plus OFFSET. + * + * .cfi_def_cfa_offset OFFSET + * The CFA is the value of the same register as before, but now adding OFFSET. + * + * .cfi_def_cfa_register REGISTER + * The CFA is now the value of REGISTER, adding the same offset as before. + * + * .cfi_offset REGISTER, OFFSET + * The caller's value of REGISTER is saved at OFFSET from the current CFA. + * (This is the directive that actually says something interesting.) + * + * There are other directives that compute the CFA, a saved register's address, + * or a saved register's value, in more complex ways, but the above are the ones + * we use here. + * + * Special rules for JaegerThrowpoline and friends: + * + * In ordinary code, return addresses always point directly after a call + * instruction. When GDB looks up the CFI for a return address it got from the + * stack (as opposed to the current PC), it uses the CFI just before the return + * address --- the CFI associated with the call instruction --- to do the + * unwinding. However, JaegerMonkey uses hacks that edit return addresses to + * point directly at the first instruction of JaegerThrowpoline, + * JaegerInterpoline, and their ilk, so GDB ends up trying to use the CFI + * associated with whatever instruction lies immediately *before* the given + * entry point. + * + * We make sure our CFI covers the code address GDB will actually use, by + * placing a 'nop' *before* the entry point --- it is never executed --- and + * having our CFI apply starting at that nop. + */ + #if defined(__GNUC__) && !defined(_WIN64) /* If this assert fails, you need to realign VMFrame to 16 bytes. */ -#ifdef JS_CPU_ARM +#if defined(JS_CPU_ARM) || defined(JS_CPU_MIPS) || defined(JS_CPU_SPARC) JS_STATIC_ASSERT(sizeof(VMFrame) % 8 == 0); #else JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0); @@ -179,7 +266,8 @@ JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0); * If these assertions break, update the constants below. * *** DANGER *** */ -JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58); +JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x68); +JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0x18); JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x38); JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL); @@ -190,14 +278,24 @@ asm ( ".globl " SYMBOL_STRING(JaegerTrampoline) "\n" SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Prologue. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa rsp, 8" "\n") "pushq %rbp" "\n" + CFI(".cfi_def_cfa_offset 16" "\n") + CFI(".cfi_offset rbp, -16" "\n") "movq %rsp, %rbp" "\n" + CFI(".cfi_def_cfa_register rbp" "\n") /* Save non-volatile registers. */ "pushq %r12" "\n" + CFI(".cfi_offset r12, -24" "\n") "pushq %r13" "\n" + CFI(".cfi_offset r13, -32" "\n") "pushq %r14" "\n" + CFI(".cfi_offset r14, -40" "\n") "pushq %r15" "\n" + CFI(".cfi_offset r15, -48" "\n") "pushq %rbx" "\n" + CFI(".cfi_offset rbx, -56" "\n") /* Load mask registers. */ "movq $0xFFFF800000000000, %r13" "\n" @@ -209,6 +307,8 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" * rcx = inlineCallCount * fp must go into rbx */ + "pushq $0x0" "\n" /* stubRejoin */ + "pushq %rsi" "\n" /* entryncode */ "pushq %rsi" "\n" /* entryfp */ "pushq %rcx" "\n" /* inlineCallCount */ "pushq %rdi" "\n" /* cx */ @@ -224,36 +324,57 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ "pushq %rdx" "\n" "movq %rsp, %rdi" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" /* Jump into the JIT'd code. */ "jmp *0(%rsp)" "\n" + CFI(".cfi_endproc" "\n") ); asm ( ".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa rbp, 16" "\n") + CFI(".cfi_offset rbp, -16" "\n") + CFI(".cfi_offset r12, -24" "\n") + CFI(".cfi_offset r13, -32" "\n") + CFI(".cfi_offset r14, -40" "\n") + CFI(".cfi_offset r15, -48" "\n") + CFI(".cfi_offset rbx, -56" "\n") + CFI("nop" "\n") ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" - "or %rdx, %rcx" "\n" - "movq %rcx, 0x30(%rbx)" "\n" + "or %rdi, %rsi" "\n" + "movq %rsi, 0x30(%rbx)" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" - "addq $0x58, %rsp" "\n" + "addq $0x68, %rsp" "\n" "popq %rbx" "\n" "popq %r15" "\n" "popq %r14" "\n" "popq %r13" "\n" "popq %r12" "\n" "popq %rbp" "\n" + CFI(".cfi_def_cfa rsp, 8" "\n") "movq $1, %rax" "\n" "ret" "\n" + CFI(".cfi_endproc" "\n") ); asm ( ".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa rbp, 16" "\n") + CFI(".cfi_offset rbp, -16" "\n") + CFI(".cfi_offset r12, -24" "\n") + CFI(".cfi_offset r13, -32" "\n") + CFI(".cfi_offset r14, -40" "\n") + CFI(".cfi_offset r15, -48" "\n") + CFI(".cfi_offset rbx, -56" "\n") + CFI("nop" "\n") ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n" SYMBOL_STRING(JaegerThrowpoline) ":" "\n" "movq %rsp, %rdi" "\n" @@ -264,15 +385,79 @@ SYMBOL_STRING(JaegerThrowpoline) ":" "\n" "throwpoline_exit:" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" - "addq $0x58, %rsp" "\n" + "addq $0x68, %rsp" "\n" + "popq %rbx" "\n" + "popq %r15" "\n" + "popq %r14" "\n" + "popq %r13" "\n" + "popq %r12" "\n" + "popq %rbp" "\n" + CFI(".cfi_def_cfa rsp, 8" "\n") + "xorq %rax,%rax" "\n" + "ret" "\n" + CFI(".cfi_endproc" "\n") +); + +asm ( +".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa rbp, 16" "\n") + CFI(".cfi_offset rbp, -16" "\n") + CFI(".cfi_offset r12, -24" "\n") + CFI(".cfi_offset r13, -32" "\n") + CFI(".cfi_offset r14, -40" "\n") + CFI(".cfi_offset r15, -48" "\n") + CFI(".cfi_offset rbx, -56" "\n") + CFI("nop" "\n") +".globl " SYMBOL_STRING(JaegerInterpoline) "\n" +SYMBOL_STRING(JaegerInterpoline) ":" "\n" + "movq %rsp, %rcx" "\n" + "movq %rax, %rdx" "\n" + "call " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n" + "movq 0x38(%rsp), %rbx" "\n" /* Load frame */ + "movq 0x30(%rbx), %rsi" "\n" /* Load rval payload */ + "and %r14, %rsi" "\n" /* Mask rval payload */ + "movq 0x30(%rbx), %rdi" "\n" /* Load rval type */ + "and %r13, %rdi" "\n" /* Mask rval type */ + "movq 0x18(%rsp), %rcx" "\n" /* Load scratch -> argc */ + "testq %rax, %rax" "\n" + "je interpoline_exit" "\n" + "jmp *%rax" "\n" + "interpoline_exit:" "\n" + "movq %rsp, %rdi" "\n" + "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" + "addq $0x68, %rsp" "\n" "popq %rbx" "\n" "popq %r15" "\n" "popq %r14" "\n" "popq %r13" "\n" "popq %r12" "\n" "popq %rbp" "\n" + CFI(".cfi_def_cfa rsp, 8" "\n") "xorq %rax,%rax" "\n" "ret" "\n" + CFI(".cfi_endproc" "\n") +); + +asm ( +".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa rbp, 16" "\n") + CFI(".cfi_offset rbp, -16" "\n") + CFI(".cfi_offset r12, -24" "\n") + CFI(".cfi_offset r13, -32" "\n") + CFI(".cfi_offset r14, -40" "\n") + CFI(".cfi_offset r15, -48" "\n") + CFI(".cfi_offset rbx, -56" "\n") + CFI("nop" "\n") +".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n" +SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n" + "movq 0x20(%rbx), %rbx" "\n" /* load prev */ + "movq %rbx, 0x38(%rsp)" "\n" + "jmp " SYMBOL_STRING_RELOC(JaegerInterpoline) "\n" + CFI(".cfi_endproc" "\n") ); # elif defined(JS_CPU_X86) @@ -284,24 +469,37 @@ SYMBOL_STRING(JaegerThrowpoline) ":" "\n" * up the argument. * *** DANGER *** */ -JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c); -JS_STATIC_ASSERT((VMFrame::offsetOfFp) == 0x1C); +JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x3C); +JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0xC); +JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x1C); asm ( ".text\n" ".globl " SYMBOL_STRING(JaegerTrampoline) "\n" SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Prologue. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa esp, 4" "\n") "pushl %ebp" "\n" + CFI(".cfi_def_cfa_offset 8" "\n") + CFI(".cfi_offset ebp, -8" "\n") "movl %esp, %ebp" "\n" + CFI(".cfi_def_cfa_register ebp" "\n") /* Save non-volatile registers. */ "pushl %esi" "\n" + CFI(".cfi_offset esi, -12" "\n") "pushl %edi" "\n" + CFI(".cfi_offset edi, -16" "\n") "pushl %ebx" "\n" + CFI(".cfi_offset ebx, -20" "\n") /* Build the JIT frame. Push fields in order, * then align the stack to form esp == VMFrame. */ "movl 12(%ebp), %ebx" "\n" /* load fp */ + "pushl %ebx" "\n" /* unused1 */ + "pushl %ebx" "\n" /* unused0 */ + "pushl $0x0" "\n" /* stubRejoin */ + "pushl %ebx" "\n" /* entryncode */ "pushl %ebx" "\n" /* entryfp */ "pushl 20(%ebp)" "\n" /* stackLimit */ "pushl 8(%ebp)" "\n" /* cx */ @@ -310,33 +508,53 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Jump into the JIT'd code. */ "movl %esp, %ecx" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" - "jmp *16(%ebp)" "\n" + "movl 28(%esp), %ebp" "\n" /* load fp for JIT code */ + "jmp *88(%esp)" "\n" + CFI(".cfi_endproc" "\n") ); asm ( ".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa ebp, 8" "\n") + CFI(".cfi_offset ebp, -8" "\n") + CFI(".cfi_offset esi, -12" "\n") + CFI(".cfi_offset edi, -16" "\n") + CFI(".cfi_offset ebx, -20" "\n") + CFI("nop" "\n") ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" - "movl %edx, 0x18(%ebx)" "\n" - "movl %ecx, 0x1C(%ebx)" "\n" + "movl %esi, 0x18(%ebp)" "\n" + "movl %edi, 0x1C(%ebp)" "\n" + "movl %esp, %ebp" "\n" + "addl $0x48, %ebp" "\n" /* Restore stack at STACK_BASE_DIFFERENCE */ "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" - "addl $0x2C, %esp" "\n" + "addl $0x3C, %esp" "\n" "popl %ebx" "\n" "popl %edi" "\n" "popl %esi" "\n" "popl %ebp" "\n" + CFI(".cfi_def_cfa esp, 4" "\n") "movl $1, %eax" "\n" "ret" "\n" + CFI(".cfi_endproc" "\n") ); asm ( ".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa ebp, 8" "\n") + CFI(".cfi_offset ebp, -8" "\n") + CFI(".cfi_offset esi, -12" "\n") + CFI(".cfi_offset edi, -16" "\n") + CFI(".cfi_offset ebx, -20" "\n") + CFI("nop" "\n") ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n" SYMBOL_STRING(JaegerThrowpoline) ":" "\n" /* Align the stack to 16 bytes. */ @@ -355,29 +573,90 @@ SYMBOL_STRING(JaegerThrowpoline) ":" "\n" "throwpoline_exit:" "\n" "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" - "addl $0x2c, %esp" "\n" + "addl $0x3c, %esp" "\n" + "popl %ebx" "\n" + "popl %edi" "\n" + "popl %esi" "\n" + "popl %ebp" "\n" + CFI(".cfi_def_cfa esp, 4" "\n") + "xorl %eax, %eax" "\n" + "ret" "\n" + CFI(".cfi_endproc" "\n") +); + +asm ( +".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa ebp, 8" "\n") + CFI(".cfi_offset ebp, -8" "\n") + CFI(".cfi_offset esi, -12" "\n") + CFI(".cfi_offset edi, -16" "\n") + CFI(".cfi_offset ebx, -20" "\n") + CFI("nop" "\n") +".globl " SYMBOL_STRING(JaegerInterpoline) "\n" +SYMBOL_STRING(JaegerInterpoline) ":" "\n" + /* Align the stack to 16 bytes. */ + "pushl %esp" "\n" + "pushl %eax" "\n" + "pushl %edi" "\n" + "pushl %esi" "\n" + "call " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n" + "addl $0x10, %esp" "\n" + "movl 0x1C(%esp), %ebp" "\n" /* Load frame */ + "movl 0x18(%ebp), %esi" "\n" /* Load rval payload */ + "movl 0x1C(%ebp), %edi" "\n" /* Load rval type */ + "movl 0xC(%esp), %ecx" "\n" /* Load scratch -> argc, for any scripted call */ + "testl %eax, %eax" "\n" + "je interpoline_exit" "\n" + "jmp *%eax" "\n" + "interpoline_exit:" "\n" + "movl %esp, %ecx" "\n" + "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" + "addl $0x3c, %esp" "\n" "popl %ebx" "\n" "popl %edi" "\n" "popl %esi" "\n" "popl %ebp" "\n" + CFI(".cfi_def_cfa esp, 4" "\n") "xorl %eax, %eax" "\n" "ret" "\n" + CFI(".cfi_endproc" "\n") +); + +asm ( +".text\n" + /* See "Special rules for JaegerThrowpoline and friends", above. */ + CFI(".cfi_startproc" "\n") + CFI(".cfi_def_cfa ebp, 8" "\n") + CFI(".cfi_offset ebp, -8" "\n") + CFI(".cfi_offset esi, -12" "\n") + CFI(".cfi_offset edi, -16" "\n") + CFI(".cfi_offset ebx, -20" "\n") + CFI("nop" "\n") +".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n" +SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n" + "movl 0x10(%ebp), %ebp" "\n" /* load prev. :XXX: STATIC_ASSERT this */ + "movl %ebp, 0x1C(%esp)" "\n" + "jmp " SYMBOL_STRING_RELOC(JaegerInterpoline) "\n" + CFI(".cfi_endproc" "\n") ); # elif defined(JS_CPU_ARM) -JS_STATIC_ASSERT(sizeof(VMFrame) == 80); -JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == (4*19)); +JS_STATIC_ASSERT(sizeof(VMFrame) == 88); +JS_STATIC_ASSERT(sizeof(VMFrame)%8 == 0); /* We need 8-byte stack alignment for EABI. */ +JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == (4*21)); JS_STATIC_ASSERT(offsetof(VMFrame, entryfp) == (4*10)); JS_STATIC_ASSERT(offsetof(VMFrame, stackLimit) == (4*9)); JS_STATIC_ASSERT(offsetof(VMFrame, cx) == (4*8)); JS_STATIC_ASSERT(VMFrame::offsetOfFp == (4*7)); -JS_STATIC_ASSERT(offsetof(VMFrame, unused) == (4*4)); -JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*3)); +JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == (4*3)); +JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*2)); -JS_STATIC_ASSERT(JSFrameReg == JSC::ARMRegisters::r11); -JS_STATIC_ASSERT(JSReturnReg_Data == JSC::ARMRegisters::r1); -JS_STATIC_ASSERT(JSReturnReg_Type == JSC::ARMRegisters::r2); +JS_STATIC_ASSERT(JSFrameReg == JSC::ARMRegisters::r10); +JS_STATIC_ASSERT(JSReturnReg_Type == JSC::ARMRegisters::r5); +JS_STATIC_ASSERT(JSReturnReg_Data == JSC::ARMRegisters::r4); #ifdef MOZ_THUMB2 #define FUNCTION_HEADER_EXTRA \ @@ -401,31 +680,36 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" * r3 = stackLimit * * The VMFrame for ARM looks like this: - * [ lr ] \ - * [ r11 ] | - * [ r10 ] | - * [ r9 ] | Callee-saved registers. - * [ r8 ] | VFP registers d8-d15 may be required here too, but - * [ r7 ] | unconditionally preserving them might be expensive - * [ r6 ] | considering that we might not use them anyway. - * [ r5 ] | - * [ r4 ] / - * [ entryfp ] - * [ stkLimit ] - * [ cx ] - * [ regs.fp ] - * [ regs.pc ] - * [ regs.sp ] - * [ unused ] - * [ previous ] - * [ args.ptr3 ] - * [ args.ptr2 ] - * [ args.ptr ] + * [ lr ] \ + * [ r11 ] | + * [ r10 ] | + * [ r9 ] | Callee-saved registers. + * [ r8 ] | VFP registers d8-d15 may be required here too, but + * [ r7 ] | unconditionally preserving them might be expensive + * [ r6 ] | considering that we might not use them anyway. + * [ r5 ] | + * [ r4 ] / + * [ stubRejoin ] + * [ entryncode ] + * [ entryfp ] + * [ stkLimit ] + * [ cx ] + * [ regs.fp ] + * [ regs.inlined ] + * [ regs.pc ] + * [ regs.sp ] + * [ scratch ] + * [ previous ] + * [ args.ptr2 ] [ dynamicArgc ] (union) + * [ args.ptr ] [ lazyArgsObj ] (union) */ /* Push callee-saved registers. */ " push {r4-r11,lr}" "\n" /* Push interesting VMFrame content. */ +" mov ip, #0" "\n" +" push {ip}" "\n" /* stubRejoin */ +" push {r1}" "\n" /* entryncode */ " push {r1}" "\n" /* entryfp */ " push {r3}" "\n" /* stackLimit */ " push {r0}" "\n" /* cx */ @@ -435,11 +719,9 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Preserve 'code' (r2) in an arbitrary callee-saved register. */ " mov r4, r2" "\n" - /* Preserve 'fp' (r1) in r11 (JSFrameReg). */ -" mov r11, r1" "\n" + /* Preserve 'fp' (r1) in r10 (JSFrameReg). */ +" mov r10, r1" "\n" -" mov r0, sp" "\n" -" blx " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" " mov r0, sp" "\n" " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n" @@ -452,15 +734,14 @@ asm ( FUNCTION_HEADER_EXTRA ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" -" str r1, [r11, #24]" "\n" /* fp->rval data */ -" str r2, [r11, #28]" "\n" /* fp->rval type */ +" strd r4, r5, [r10, #24]" "\n" /* fp->rval type,data */ /* Tidy up. */ -" mov r0, sp" "\n" +" mov r0, sp" "\n" " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" /* Skip past the parameters we pushed (such as cx and the like). */ -" add sp, sp, #(4*7 + 4*4)" "\n" +" add sp, sp, #(4*7 + 4*6)" "\n" /* Set a 'true' return value to indicate successful completion. */ " mov r0, #1" "\n" @@ -485,9 +766,42 @@ SYMBOL_STRING(JaegerThrowpoline) ":" "\n" " bxne r0" "\n" /* Tidy up, then return '0' to represent an unhandled exception. */ -" mov r0, sp" "\n" +" mov r0, sp" "\n" +" blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" +" add sp, sp, #(4*7 + 4*6)" "\n" +" mov r0, #0" "\n" +" pop {r4-r11,pc}" "\n" +); + +asm ( +".text\n" +FUNCTION_HEADER_EXTRA +".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n" +SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n" + /* The only difference between JaegerInterpoline and JaegerInpolineScripted is that the + * scripted variant has to walk up to the previous StackFrame first. */ +" ldr r10, [r10, #(4*4)]" "\n" /* Load f->prev_ */ +" str r10, [sp, #(4*7)]" "\n" /* Update f->regs->fp_ */ + /* Fall through into JaegerInterpoline. */ + +FUNCTION_HEADER_EXTRA +".globl " SYMBOL_STRING(JaegerInterpoline) "\n" +SYMBOL_STRING(JaegerInterpoline) ":" "\n" +" mov r3, sp" "\n" /* f */ +" mov r2, r0" "\n" /* returnReg */ +" mov r1, r5" "\n" /* returnType */ +" mov r0, r4" "\n" /* returnData */ +" blx " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n" +" cmp r0, #0" "\n" +" ldr r10, [sp, #(4*7)]" "\n" /* Load (StackFrame*)f->regs->fp_ */ +" ldrd r4, r5, [r10, #(4*6)]" "\n" /* Load rval payload and type. */ +" ldr r1, [sp, #(4*3)]" "\n" /* Load scratch. */ +" it ne" "\n" +" bxne r0" "\n" + /* Tidy up, then return 0. */ +" mov r0, sp" "\n" " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" -" add sp, sp, #(4*7 + 4*4)" "\n" +" add sp, sp, #(4*7 + 4*6)" "\n" " mov r0, #0" "\n" " pop {r4-r11,pc}" "\n" ); @@ -508,6 +822,7 @@ SYMBOL_STRING(JaegerStubVeneer) ":" "\n" ); # elif defined(JS_CPU_SPARC) +# elif defined(JS_CPU_MIPS) # else # error "Unsupported CPU!" # endif @@ -520,7 +835,8 @@ SYMBOL_STRING(JaegerStubVeneer) ":" "\n" * up the argument. * *** DANGER *** */ -JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c); +JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x3C); +JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0xC); JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x1C); extern "C" { @@ -541,6 +857,10 @@ extern "C" { * then align the stack to form esp == VMFrame. */ mov ebx, [ebp + 12]; push ebx; + push ebx; + push 0x0; + push ebx; + push ebx; push [ebp + 20]; push [ebp + 8]; push ebx; @@ -548,23 +868,24 @@ extern "C" { /* Jump into into the JIT'd code. */ mov ecx, esp; - call SetVMFrameRegs; - mov ecx, esp; call PushActiveVMFrame; - jmp dword ptr [ebp + 16]; + mov ebp, [esp + 28]; /* load fp for JIT code */ + jmp dword ptr [esp + 88]; } } __declspec(naked) void JaegerTrampolineReturn() { __asm { - mov [ebx + 0x18], edx; - mov [ebx + 0x1C], ecx; + mov [ebp + 0x18], esi; + mov [ebp + 0x1C], edi; + mov ebp, esp; + add ebp, 0x48; /* Restore stack at STACK_BASE_DIFFERENCE */ mov ecx, esp; call PopActiveVMFrame; - add esp, 0x2C; + add esp, 0x3C; pop ebx; pop edi; @@ -595,7 +916,39 @@ extern "C" { throwpoline_exit: mov ecx, esp; call PopActiveVMFrame; - add esp, 0x2c; + add esp, 0x3c; + pop ebx; + pop edi; + pop esi; + pop ebp; + xor eax, eax + ret; + } + } + + extern "C" void * + js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f); + + __declspec(naked) void JaegerInterpoline() { + __asm { + /* Align the stack to 16 bytes. */ + push esp; + push eax; + push edi; + push esi; + call js_InternalInterpret; + add esp, 0x10; + mov ebp, [esp + 0x1C]; /* Load frame */ + mov esi, [ebp + 0x18]; /* Load rval payload */ + mov edi, [ebp + 0x1C]; /* Load rval type */ + mov ecx, [esp + 0xC]; /* Load scratch -> argc */ + test eax, eax; + je interpoline_exit; + jmp eax; + interpoline_exit: + mov ecx, esp; + call PopActiveVMFrame; + add esp, 0x3c; pop ebx; pop edi; pop esi; @@ -604,6 +957,14 @@ extern "C" { ret; } } + + __declspec(naked) void JaegerInterpolineScripted() { + __asm { + mov ebp, [ebp + 0x10]; /* Load prev */ + mov [esp + 0x1C], ebp; /* fp -> regs.fp */ + jmp JaegerInterpoline; + } + } } // Windows x64 uses assembler version since compiler doesn't support @@ -615,23 +976,30 @@ extern "C" { * If these assertions break, update the constants below. * *** DANGER *** */ -JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58); +JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x68); +JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0x18); JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x38); JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL); JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL); #endif /* _WIN64 */ +JaegerCompartment::JaegerCompartment() + : orphanedNativeFrames(SystemAllocPolicy()), orphanedNativePools(SystemAllocPolicy()) +{} + bool -JaegerCompartment::Initialize() +JaegerCompartment::Initialize(JSContext *cx) { - execAlloc_ = js::OffTheBooks::new_(); + execAlloc_ = js::OffTheBooks::new_( + cx->runtime->getJitHardening() ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic); if (!execAlloc_) return false; TrampolineCompiler tc(execAlloc_, &trampolines); if (!tc.compile()) { - delete execAlloc_; + js::Foreground::delete_(execAlloc_); + execAlloc_ = NULL; return false; } @@ -641,6 +1009,7 @@ JaegerCompartment::Initialize() #endif activeFrame_ = NULL; + lastUnfinished_ = (JaegerStatus) 0; return true; } @@ -663,8 +1032,8 @@ JaegerCompartment::Finish() extern "C" JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit); -JSBool -mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit) +JaegerStatus +mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit, bool partial) { #ifdef JS_METHODJIT_SPEW Profiler prof; @@ -676,166 +1045,203 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi #endif JS_ASSERT(cx->fp() == fp); - FrameRegs &oldRegs = cx->regs(); JSBool ok; { AssertCompartmentUnchanged pcc(cx); - JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); + JSAutoResolveFlags rf(cx, RESOLVE_INFER); ok = JaegerTrampoline(cx, fp, code, stackLimit); } - /* Undo repointRegs in SetVMFrameRegs. */ - cx->stack.repointRegs(&oldRegs); - JS_ASSERT(fp == cx->fp()); - - /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */ - fp->markReturnValue(); - - /* See comment in mjit::Compiler::emitReturn. */ - fp->markActivationObjectsAsPut(); - #ifdef JS_METHODJIT_SPEW prof.stop(); JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms()); #endif - return ok; + JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished(); + if (status) { + if (partial) { + /* + * Being called from the interpreter, which will resume execution + * where the JIT left off. + */ + return status; + } + + /* + * Call back into the interpreter to finish the initial frame. This may + * invoke EnterMethodJIT again, but will allow partial execution for + * that recursive invocation, so we can have at most two VM frames for + * a range of inline frames. + */ + InterpMode mode = (status == Jaeger_UnfinishedAtTrap) + ? JSINTERP_SKIP_TRAP + : JSINTERP_REJOIN; + ok = Interpret(cx, fp, mode); + + return ok ? Jaeger_Returned : Jaeger_Throwing; + } + + /* The entry frame should have finished. */ + JS_ASSERT(fp == cx->fp()); + + if (ok) { + /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */ + fp->markReturnValue(); + } + + /* See comment in mjit::Compiler::emitReturn. */ + if (fp->isFunctionFrame()) + fp->updateEpilogueFlags(); + + return ok ? Jaeger_Returned : Jaeger_Throwing; } -static inline JSBool -CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code) +static inline JaegerStatus +CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, bool partial) { - JS_CHECK_RECURSION(cx, return false); + JS_CHECK_RECURSION(cx, return Jaeger_Throwing); - Value *stackLimit = cx->stack.space().getStackLimit(cx); + JS_ASSERT(!cx->compartment->activeAnalysis); + JS_ASSERT(code); + + Value *stackLimit = cx->stack.space().getStackLimit(cx, REPORT_ERROR); if (!stackLimit) - return false; + return Jaeger_Throwing; - return EnterMethodJIT(cx, fp, code, stackLimit); + return EnterMethodJIT(cx, fp, code, stackLimit, partial); } -JSBool -mjit::JaegerShot(JSContext *cx) +JaegerStatus +mjit::JaegerShot(JSContext *cx, bool partial) { StackFrame *fp = cx->fp(); JSScript *script = fp->script(); JITScript *jit = script->getJIT(fp->isConstructing()); -#ifdef JS_TRACER - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "attempt to enter method JIT while recording"); -#endif - JS_ASSERT(cx->regs().pc == script->code); - return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry); + return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry, partial); } -JSBool -js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint) +JaegerStatus +js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial) { -#ifdef JS_TRACER - JS_ASSERT(!TRACE_RECORDER(cx)); -#endif - - return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint); + return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint, partial); } NativeMapEntry * -JITScript::nmap() const +JITChunk::nmap() const +{ + return (NativeMapEntry *)((char*)this + sizeof(*this)); +} + +js::mjit::InlineFrame * +JITChunk::inlineFrames() const +{ + return (js::mjit::InlineFrame *)((char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs); +} + +js::mjit::CallSite * +JITChunk::callSites() const { - return (NativeMapEntry *)((char*)this + sizeof(JITScript)); + return (js::mjit::CallSite *)&inlineFrames()[nInlineFrames]; } char * -JITScript::nmapSectionLimit() const +JITChunk::commonSectionLimit() const { - return (char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs; + return (char *)&callSites()[nCallSites]; } #ifdef JS_MONOIC ic::GetGlobalNameIC * -JITScript::getGlobalNames() const +JITChunk::getGlobalNames() const { - return (ic::GetGlobalNameIC *)nmapSectionLimit(); + return (ic::GetGlobalNameIC *) commonSectionLimit(); } ic::SetGlobalNameIC * -JITScript::setGlobalNames() const +JITChunk::setGlobalNames() const { - return (ic::SetGlobalNameIC *)((char *)nmapSectionLimit() + + return (ic::SetGlobalNameIC *)((char *)getGlobalNames() + sizeof(ic::GetGlobalNameIC) * nGetGlobalNames); } ic::CallICInfo * -JITScript::callICs() const +JITChunk::callICs() const { - return (ic::CallICInfo *)((char *)setGlobalNames() + - sizeof(ic::SetGlobalNameIC) * nSetGlobalNames); + return (ic::CallICInfo *)&setGlobalNames()[nSetGlobalNames]; } ic::EqualityICInfo * -JITScript::equalityICs() const +JITChunk::equalityICs() const { - return (ic::EqualityICInfo *)((char *)callICs() + sizeof(ic::CallICInfo) * nCallICs); -} - -ic::TraceICInfo * -JITScript::traceICs() const -{ - return (ic::TraceICInfo *)((char *)equalityICs() + sizeof(ic::EqualityICInfo) * nEqualityICs); + return (ic::EqualityICInfo *)&callICs()[nCallICs]; } char * -JITScript::monoICSectionsLimit() const +JITChunk::monoICSectionsLimit() const { - return (char *)traceICs() + sizeof(ic::TraceICInfo) * nTraceICs; + return (char *)&equalityICs()[nEqualityICs]; } #else // JS_MONOIC char * -JITScript::monoICSectionsLimit() const +JITChunk::monoICSectionsLimit() const { - return nmapSectionsLimit(); + return commonSectionLimit(); } #endif // JS_MONOIC #ifdef JS_POLYIC ic::GetElementIC * -JITScript::getElems() const +JITChunk::getElems() const { return (ic::GetElementIC *)monoICSectionsLimit(); } ic::SetElementIC * -JITScript::setElems() const +JITChunk::setElems() const { return (ic::SetElementIC *)((char *)getElems() + sizeof(ic::GetElementIC) * nGetElems); } ic::PICInfo * -JITScript::pics() const +JITChunk::pics() const { return (ic::PICInfo *)((char *)setElems() + sizeof(ic::SetElementIC) * nSetElems); } char * -JITScript::polyICSectionsLimit() const +JITChunk::polyICSectionsLimit() const { return (char *)pics() + sizeof(ic::PICInfo) * nPICs; } #else // JS_POLYIC char * -JITScript::polyICSectionsLimit() const +JITChunk::polyICSectionsLimit() const { return monoICSectionsLimit(); } #endif // JS_POLYIC -js::mjit::CallSite * -JITScript::callSites() const +void +JITScript::patchEdge(const CrossChunkEdge &edge, void *label) { - return (js::mjit::CallSite *)polyICSectionsLimit(); + if (edge.sourceJump1 || edge.sourceJump2) { + JITChunk *sourceChunk = chunk(script->code + edge.source); + JSC::CodeLocationLabel targetLabel(label); + ic::Repatcher repatch(sourceChunk); + + if (edge.sourceJump1) + repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel); + if (edge.sourceJump2) + repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel); + } + if (edge.jumpTableEntries) { + for (unsigned i = 0; i < edge.jumpTableEntries->length(); i++) + *(*edge.jumpTableEntries)[i] = label; + } } template @@ -844,24 +1250,22 @@ static inline void Destroy(T &t) t.~T(); } -mjit::JITScript::~JITScript() +JITChunk::~JITChunk() { -#if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64) - void *addr = code.m_code.executableAddress(); - memset(addr, 0xcc, code.m_size); -#endif + code.release(); - code.m_executablePool->release(); + if (pcLengths) + Foreground::free_(pcLengths); #if defined JS_POLYIC ic::GetElementIC *getElems_ = getElems(); ic::SetElementIC *setElems_ = setElems(); ic::PICInfo *pics_ = pics(); - for (uint32 i = 0; i < nGetElems; i++) + for (uint32_t i = 0; i < nGetElems; i++) Destroy(getElems_[i]); - for (uint32 i = 0; i < nSetElems; i++) + for (uint32_t i = 0; i < nSetElems; i++) Destroy(setElems_[i]); - for (uint32 i = 0; i < nPICs; i++) + for (uint32_t i = 0; i < nPICs; i++) Destroy(pics_[i]); #endif @@ -872,58 +1276,159 @@ mjit::JITScript::~JITScript() { (*pExecPool)->release(); } - + + for (unsigned i = 0; i < nativeCallStubs.length(); i++) { + JSC::ExecutablePool *pool = nativeCallStubs[i].pool; + if (pool) + pool->release(); + } + ic::CallICInfo *callICs_ = callICs(); - for (uint32 i = 0; i < nCallICs; i++) + for (uint32_t i = 0; i < nCallICs; i++) { callICs_[i].releasePools(); + if (callICs_[i].fastGuardedObject) + callICs_[i].purgeGuardedObject(); + } #endif } +void +JITScript::destroy(JSContext *cx) +{ + for (unsigned i = 0; i < nchunks; i++) + destroyChunk(cx, i); + + if (shimPool) + shimPool->release(); +} + +void +JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses) +{ + ChunkDescriptor &desc = chunkDescriptor(chunkIndex); + + if (desc.chunk) { + Probes::discardMJITCode(cx, this, script, desc.chunk->code.m_code.executableAddress()); + cx->delete_(desc.chunk); + desc.chunk = NULL; + + CrossChunkEdge *edges = this->edges(); + for (unsigned i = 0; i < nedges; i++) { + CrossChunkEdge &edge = edges[i]; + if (edge.source >= desc.begin && edge.source < desc.end) { + edge.sourceJump1 = edge.sourceJump2 = NULL; + if (edge.jumpTableEntries) { + cx->delete_(edge.jumpTableEntries); + edge.jumpTableEntries = NULL; + } + } else if (edge.target >= desc.begin && edge.target < desc.end) { + edge.targetLabel = NULL; + patchEdge(edge, edge.shimLabel); + } + } + } + + if (resetUses) + desc.counter = 0; + + if (chunkIndex == 0) { + if (argsCheckPool) { + argsCheckPool->release(); + argsCheckPool = NULL; + } + + invokeEntry = NULL; + fastEntry = NULL; + arityCheckEntry = NULL; + argsCheckEntry = NULL; + + if (script->jitNormal == this) + script->jitArityCheckNormal = NULL; + else + script->jitArityCheckCtor = NULL; + + // Fixup any ICs still referring to this chunk. + while (!JS_CLIST_IS_EMPTY(&callers)) { + JS_STATIC_ASSERT(offsetof(ic::CallICInfo, links) == 0); + ic::CallICInfo *ic = (ic::CallICInfo *) callers.next; + + uint8_t *start = (uint8_t *)ic->funGuard.executableAddress(); + JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64)); + + repatch.repatch(ic->funGuard, NULL); + repatch.relink(ic->funJump, ic->slowPathStart); + ic->purgeGuardedObject(); + } + } +} + +size_t +JSScript::sizeOfJitScripts(JSMallocSizeOfFun mallocSizeOf) +{ + size_t n = 0; + if (jitNormal) + n += jitNormal->sizeOfIncludingThis(mallocSizeOf); + if (jitCtor) + n += jitCtor->sizeOfIncludingThis(mallocSizeOf); + return n; +} + +size_t +mjit::JITScript::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) +{ + size_t n = mallocSizeOf(this); + for (unsigned i = 0; i < nchunks; i++) { + const ChunkDescriptor &desc = chunkDescriptor(i); + if (desc.chunk) + n += desc.chunk->sizeOfIncludingThis(mallocSizeOf); + } + return n; +} + /* Please keep in sync with Compiler::finishThisUp! */ size_t -mjit::JITScript::scriptDataSize() +mjit::JITChunk::computedSizeOfIncludingThis() { - return sizeof(JITScript) + - sizeof(NativeMapEntry) * nNmapPairs + + return sizeof(JITChunk) + + sizeof(NativeMapEntry) * nNmapPairs + + sizeof(InlineFrame) * nInlineFrames + + sizeof(CallSite) * nCallSites + #if defined JS_MONOIC - sizeof(ic::GetGlobalNameIC) * nGetGlobalNames + - sizeof(ic::SetGlobalNameIC) * nSetGlobalNames + - sizeof(ic::CallICInfo) * nCallICs + - sizeof(ic::EqualityICInfo) * nEqualityICs + - sizeof(ic::TraceICInfo) * nTraceICs + + sizeof(ic::GetGlobalNameIC) * nGetGlobalNames + + sizeof(ic::SetGlobalNameIC) * nSetGlobalNames + + sizeof(ic::CallICInfo) * nCallICs + + sizeof(ic::EqualityICInfo) * nEqualityICs + #endif #if defined JS_POLYIC - sizeof(ic::PICInfo) * nPICs + - sizeof(ic::GetElementIC) * nGetElems + - sizeof(ic::SetElementIC) * nSetElems + + sizeof(ic::PICInfo) * nPICs + + sizeof(ic::GetElementIC) * nGetElems + + sizeof(ic::SetElementIC) * nSetElems + #endif - sizeof(CallSite) * nCallSites; + 0; +} + +/* Please keep in sync with Compiler::finishThisUp! */ +size_t +mjit::JITChunk::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) +{ + return mallocSizeOf(this); } void -mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct) { // NB: The recompiler may call ReleaseScriptCode, in which case it // will get called again when the script is destroyed, so we // must protect against calling ReleaseScriptCode twice. - JITScript *jscr; - if ((jscr = script->jitNormal)) { - cx->runtime->mjitDataSize -= jscr->scriptDataSize(); + JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal; + void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal; - jscr->~JITScript(); - cx->free_(jscr); - script->jitNormal = NULL; - script->jitArityCheckNormal = NULL; - } - - if ((jscr = script->jitCtor)) { - cx->runtime->mjitDataSize -= jscr->scriptDataSize(); - - jscr->~JITScript(); - cx->free_(jscr); - script->jitCtor = NULL; - script->jitArityCheckCtor = NULL; + if (*pjit) { + (*pjit)->destroy(cx); + cx->free_(*pjit); + *pjit = NULL; + *parity = NULL; } } @@ -936,65 +1441,26 @@ mjit::ProfileStubCall(VMFrame &f) } #endif -#ifdef JS_POLYIC -static int -PICPCComparator(const void *key, const void *entry) -{ - const jsbytecode *pc = (const jsbytecode *)key; - const ic::PICInfo *pic = (const ic::PICInfo *)entry; - - if (ic::PICInfo::CALL != pic->kind) - return ic::PICInfo::CALL - pic->kind; - - /* - * We can't just return |pc - pic->pc| because the pointers may be - * far apart and an int (or even a ptrdiff_t) may not be large - * enough to hold the difference. C says that pointer subtraction - * is only guaranteed to work for two pointers into the same array. - */ - if (pc < pic->pc) - return -1; - else if (pc == pic->pc) - return 0; - else - return 1; -} - -uintN -mjit::GetCallTargetCount(JSScript *script, jsbytecode *pc) +JITChunk * +JITScript::findCodeChunk(void *addr) { - ic::PICInfo *pic; - - if (mjit::JITScript *jit = script->getJIT(false)) { - pic = (ic::PICInfo *)bsearch(pc, jit->pics(), jit->nPICs, sizeof(ic::PICInfo), - PICPCComparator); - if (pic) - return pic->stubsGenerated + 1; /* Add 1 for the inline path. */ - } - - if (mjit::JITScript *jit = script->getJIT(true)) { - pic = (ic::PICInfo *)bsearch(pc, jit->pics(), jit->nPICs, sizeof(ic::PICInfo), - PICPCComparator); - if (pic) - return pic->stubsGenerated + 1; /* Add 1 for the inline path. */ + for (unsigned i = 0; i < nchunks; i++) { + ChunkDescriptor &desc = chunkDescriptor(i); + if (desc.chunk && desc.chunk->isValidCode(addr)) + return desc.chunk; } - - return 1; -} -#else -uintN -mjit::GetCallTargetCount(JSScript *script, jsbytecode *pc) -{ - return 1; + return NULL; } -#endif jsbytecode * -JITScript::nativeToPC(void *returnAddress) const +JITScript::nativeToPC(void *returnAddress, CallSite **pinline) { + JITChunk *chunk = findCodeChunk(returnAddress); + JS_ASSERT(chunk); + size_t low = 0; - size_t high = nCallICs; - js::mjit::ic::CallICInfo *callICs_ = callICs(); + size_t high = chunk->nCallICs; + js::mjit::ic::CallICInfo *callICs_ = chunk->callICs(); while (high > low + 1) { /* Could overflow here on a script with 2 billion calls. Oh well. */ size_t mid = (high + low) / 2; @@ -1011,8 +1477,26 @@ JITScript::nativeToPC(void *returnAddress) const } js::mjit::ic::CallICInfo &ic = callICs_[low]; + JS_ASSERT((uint8_t*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress); + + if (ic.call->inlineIndex != UINT32_MAX) { + if (pinline) + *pinline = ic.call; + InlineFrame *frame = &chunk->inlineFrames()[ic.call->inlineIndex]; + while (frame && frame->parent) + frame = frame->parent; + return frame->parentpc; + } - JS_ASSERT((uint8*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress); - return ic.pc; + if (pinline) + *pinline = NULL; + return script->code + ic.call->pcOffset; +} + +jsbytecode * +mjit::NativeToPC(JITScript *jit, void *ncode, mjit::CallSite **pinline) +{ + return jit->nativeToPC(ncode, pinline); } +/* static */ const double mjit::Assembler::oneDouble = 1.0; diff --git a/deps/mozjs/js/src/methodjit/MethodJIT.h b/deps/mozjs/js/src/methodjit/MethodJIT.h index bcfb4b8f7eb..eb23d88ad1b 100644 --- a/deps/mozjs/js/src/methodjit/MethodJIT.h +++ b/deps/mozjs/js/src/methodjit/MethodJIT.h @@ -32,21 +32,28 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. + * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #if !defined jsjaeger_h__ && defined JS_METHODJIT #define jsjaeger_h__ +#ifdef JSGC_INCREMENTAL +#define JSGC_INCREMENTAL_MJ +#endif + #include "jscntxt.h" +#include "jscompartment.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" +#include "assembler/assembler/CodeLocation.h" #if !defined JS_CPU_X64 && \ !defined JS_CPU_X86 && \ !defined JS_CPU_SPARC && \ - !defined JS_CPU_ARM + !defined JS_CPU_ARM && \ + !defined JS_CPU_MIPS # error "Oh no, you should define a platform so this compiles." #endif @@ -56,7 +63,10 @@ namespace js { -namespace mjit { struct JITScript; } +namespace mjit { + struct JITChunk; + struct JITScript; +} struct VMFrame { @@ -91,26 +101,55 @@ struct VMFrame void *reserve_0; void *reserve_1; + +#elif defined(JS_CPU_MIPS) + /* Reserved 16 bytes for a0-a3 space in MIPS O32 ABI */ + void *unused0; + void *unused1; + void *unused2; + void *unused3; #endif union Arguments { struct { void *ptr; void *ptr2; - void *ptr3; } x; struct { - uint32 lazyArgsObj; - uint32 dynamicArgc; + uint32_t lazyArgsObj; + uint32_t dynamicArgc; } call; } u; + static size_t offsetOfLazyArgsObj() { + return offsetof(VMFrame, u.call.lazyArgsObj); + } + + static size_t offsetOfDynamicArgc() { + return offsetof(VMFrame, u.call.dynamicArgc); + } + VMFrame *previous; - void *unused; + void *scratch; FrameRegs regs; + + static size_t offsetOfRegsSp() { + return offsetof(VMFrame, regs.sp); + } + + static size_t offsetOfRegsPc() { + return offsetof(VMFrame, regs.pc); + } + JSContext *cx; Value *stackLimit; StackFrame *entryfp; + FrameRegs *oldregs; + JSRejoinState stubRejoin; /* How to rejoin if inside a call from an IC stub. */ + +#if defined(JS_CPU_X86) + void *unused0, *unused1; /* For 16 byte alignment */ +#endif #if defined(JS_CPU_X86) void *savedEBX; @@ -128,6 +167,10 @@ struct VMFrame return reinterpret_cast(this) - 1; } # endif + + /* The gap between ebp and esp in JaegerTrampoline frames on X86 platforms. */ + static const uint32_t STACK_BASE_DIFFERENCE = 0x38; + #elif defined(JS_CPU_X64) void *savedRBX; # ifdef _WIN64 @@ -172,28 +215,209 @@ struct VMFrame inline void** returnAddressLocation() { return reinterpret_cast(&this->veneerReturn); } +#elif defined(JS_CPU_MIPS) + void *savedS0; + void *savedS1; + void *savedS2; + void *savedS3; + void *savedS4; + void *savedS5; + void *savedS6; + void *savedS7; + void *savedGP; + void *savedRA; + void *unused4; // For alignment. + + inline void** returnAddressLocation() { + return reinterpret_cast(this) - 1; + } #else # error "The VMFrame layout isn't defined for your processor architecture!" #endif JSRuntime *runtime() { return cx->runtime; } + /* + * Get the current frame and JIT. Note that these are NOT stable in case + * of recompilations; all code which expects these to be stable should + * check that cx->recompilations() has not changed across a call that could + * trigger recompilation (pretty much any time the VM is called into). + */ StackFrame *fp() { return regs.fp(); } mjit::JITScript *jit() { return fp()->jit(); } - static const size_t offsetOfFp = 5 * sizeof(void *) + FrameRegs::offsetOfFp; + inline mjit::JITChunk *chunk(); + inline unsigned chunkIndex(); + + /* Get the inner script/PC in case of inlining. */ + inline JSScript *script(); + inline jsbytecode *pc(); + +#if defined(JS_CPU_SPARC) + static const size_t offsetOfFp = 30 * sizeof(void *) + FrameRegs::offsetOfFp; + static const size_t offsetOfInlined = 30 * sizeof(void *) + FrameRegs::offsetOfInlined; +#elif defined(JS_CPU_MIPS) + static const size_t offsetOfFp = 8 * sizeof(void *) + FrameRegs::offsetOfFp; + static const size_t offsetOfInlined = 8 * sizeof(void *) + FrameRegs::offsetOfInlined; +#else + static const size_t offsetOfFp = 4 * sizeof(void *) + FrameRegs::offsetOfFp; + static const size_t offsetOfInlined = 4 * sizeof(void *) + FrameRegs::offsetOfInlined; +#endif + static void staticAssert() { JS_STATIC_ASSERT(offsetOfFp == offsetof(VMFrame, regs) + FrameRegs::offsetOfFp); + JS_STATIC_ASSERT(offsetOfInlined == offsetof(VMFrame, regs) + FrameRegs::offsetOfInlined); } }; -#ifdef JS_CPU_ARM +#if defined(JS_CPU_ARM) || defined(JS_CPU_SPARC) || defined(JS_CPU_MIPS) // WARNING: Do not call this function directly from C(++) code because it is not ABI-compliant. extern "C" void JaegerStubVeneer(void); #endif namespace mjit { +/* + * For a C++ or scripted call made from JIT code, indicates properties of the + * register and stack state after the call finishes, which js_InternalInterpret + * must use to construct a coherent state for rejoining into the interpreter. + */ +enum RejoinState { + /* + * Return value of call at this bytecode is held in ReturnReg_{Data,Type} + * and needs to be restored before starting the next bytecode. f.regs.pc + * is *not* intact when rejoining from a scripted call (unlike all other + * rejoin states). The pc's offset into the script is stored in the upper + * 31 bits of the rejoin state, and the remaining values for RejoinState + * are shifted left by one in stack frames to leave the lower bit set only + * for scripted calls. + */ + REJOIN_SCRIPTED = 1, + + /* Recompilations and frame expansion are impossible for this call. */ + REJOIN_NONE, + + /* State is coherent for the start of the current bytecode. */ + REJOIN_RESUME, + + /* + * State is coherent for the start of the current bytecode, which is a TRAP + * that has already been invoked and should not be invoked again. + */ + REJOIN_TRAP, + + /* State is coherent for the start of the next (fallthrough) bytecode. */ + REJOIN_FALLTHROUGH, + + /* + * As for REJOIN_FALLTHROUGH, but holds a reference on the compartment's + * orphaned native pools which needs to be reclaimed by InternalInterpret. + * The return value needs to be adjusted if REJOIN_NATIVE_LOWERED, and + * REJOIN_NATIVE_GETTER is for ABI calls made for property accesses. + */ + REJOIN_NATIVE, + REJOIN_NATIVE_LOWERED, + REJOIN_NATIVE_GETTER, + + /* + * Dummy rejoin stored in VMFrames to indicate they return into a native + * stub (and their FASTCALL return address should not be observed) but + * that they have already been patched and can be ignored. + */ + REJOIN_NATIVE_PATCHED, + + /* Call returns a payload, which should be pushed before starting next bytecode. */ + REJOIN_PUSH_BOOLEAN, + REJOIN_PUSH_OBJECT, + + /* Call returns an object, which should be assigned to a local per the current bytecode. */ + REJOIN_DEFLOCALFUN, + + /* + * During the prologue of constructing scripts, after the function's + * .prototype property has been fetched. + */ + REJOIN_THIS_PROTOTYPE, + + /* + * Type check on arguments failed during prologue, need stack check and + * the rest of the JIT prologue before the script can execute. + */ + REJOIN_CHECK_ARGUMENTS, + + /* + * The script's jitcode was discarded after marking an outer function as + * reentrant or due to a GC while creating a call object. + */ + REJOIN_FUNCTION_PROLOGUE, + + /* + * State after calling a stub which returns a JIT code pointer for a call + * or NULL for an already-completed call. + */ + REJOIN_CALL_PROLOGUE, + REJOIN_CALL_PROLOGUE_LOWERED_CALL, + REJOIN_CALL_PROLOGUE_LOWERED_APPLY, + + /* Triggered a recompilation while placing the arguments to an apply on the stack. */ + REJOIN_CALL_SPLAT, + + /* FALLTHROUGH ops which can be implemented as part of an IncOp. */ + REJOIN_GETTER, + REJOIN_POS, + REJOIN_BINARY, + + /* + * For an opcode fused with IFEQ/IFNE, call returns a boolean indicating + * the result of the comparison and whether to take or not take the branch. + */ + REJOIN_BRANCH +}; + +/* Get the rejoin state for a StackFrame after returning from a scripted call. */ +static inline JSRejoinState +ScriptedRejoin(uint32_t pcOffset) +{ + return REJOIN_SCRIPTED | (pcOffset << 1); +} + +/* Get the rejoin state for a StackFrame after returning from a stub call. */ +static inline JSRejoinState +StubRejoin(RejoinState rejoin) +{ + return rejoin << 1; +} + +/* Helper to watch for recompilation and frame expansion activity on a compartment. */ +struct RecompilationMonitor +{ + JSContext *cx; + + /* + * If either inline frame expansion or recompilation occurs, then ICs and + * stubs should not depend on the frame or JITs being intact. The two are + * separated for logging. + */ + unsigned recompilations; + unsigned frameExpansions; + + /* If a GC occurs it may discard jit code on the stack. */ + unsigned gcNumber; + + RecompilationMonitor(JSContext *cx) + : cx(cx), + recompilations(cx->compartment->types.recompilations), + frameExpansions(cx->compartment->types.frameExpansions), + gcNumber(cx->runtime->gcNumber) + {} + + bool recompiled() { + return cx->compartment->types.recompilations != recompilations + || cx->compartment->types.frameExpansions != frameExpansions + || cx->runtime->gcNumber != gcNumber; + } +}; + /* * Trampolines to force returns from jit code. * See also TrampolineCompiler::generateForceReturn(Fast). @@ -210,6 +434,29 @@ struct Trampolines { #endif }; +/* Result status of executing mjit code on a frame. */ +enum JaegerStatus +{ + /* Entry frame finished, and is throwing an exception. */ + Jaeger_Throwing = 0, + + /* Entry frame finished, and is returning. */ + Jaeger_Returned = 1, + + /* + * Entry frame did not finish. cx->regs reflects where to resume execution. + * This result is only possible if 'partial' is passed as true below. + */ + Jaeger_Unfinished = 2, + + /* + * As for Unfinished, but stopped after a TRAP triggered recompilation. + * The trap has been reinstalled, but should not execute again when + * resuming execution. + */ + Jaeger_UnfinishedAtTrap = 3 +}; + /* * Method JIT compartment data. Currently, there is exactly one per * JS compartment. It would be safe for multiple JS compartments to @@ -220,12 +467,15 @@ class JaegerCompartment { JSC::ExecutableAllocator *execAlloc_; // allocator for jit code Trampolines trampolines; // force-return trampolines VMFrame *activeFrame_; // current active VMFrame + JaegerStatus lastUnfinished_;// result status of last VM frame, + // if unfinished void Finish(); public: - bool Initialize(); + bool Initialize(JSContext *cx); + JaegerCompartment(); ~JaegerCompartment() { Finish(); } JSC::ExecutableAllocator *execAlloc() { @@ -237,7 +487,9 @@ class JaegerCompartment { } void pushActiveFrame(VMFrame *f) { + JS_ASSERT(!lastUnfinished_); f->previous = activeFrame_; + f->scratch = NULL; activeFrame_ = f; } @@ -246,10 +498,32 @@ class JaegerCompartment { activeFrame_ = activeFrame_->previous; } + void setLastUnfinished(JaegerStatus status) { + JS_ASSERT(!lastUnfinished_); + lastUnfinished_ = status; + } + + JaegerStatus lastUnfinished() { + JaegerStatus result = lastUnfinished_; + lastUnfinished_ = (JaegerStatus) 0; + return result; + } + + /* + * To force the top StackFrame in a VMFrame to return, when that VMFrame + * has called an extern "C" function (say, js_InternalThrow or + * js_InternalInterpret), change the extern "C" function's return address + * to the value this method returns. + */ void *forceReturnFromExternC() const { return JS_FUNC_TO_DATA_PTR(void *, trampolines.forceReturn); } + /* + * To force the top StackFrame in a VMFrame to return, when that VMFrame has + * called a fastcall function (say, most stubs:: functions), change the + * fastcall function's return address to the value this method returns. + */ void *forceReturnFromFastCall() const { #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) return JS_FUNC_TO_DATA_PTR(void *, trampolines.forceReturnFast); @@ -257,6 +531,14 @@ class JaegerCompartment { return JS_FUNC_TO_DATA_PTR(void *, trampolines.forceReturn); #endif } + + /* + * References held on pools created for native ICs, where the IC was + * destroyed and we are waiting for the pool to finish use and jump + * into the interpoline. + */ + Vector orphanedNativeFrames; + Vector orphanedNativePools; }; /* @@ -266,7 +548,7 @@ class JaegerCompartment { * setting a flag on the compiler when OOM occurs. The compiler is required * to check for OOM only before trying to use the contents of the list. */ -class CompilerAllocPolicy : public ContextAllocPolicy +class CompilerAllocPolicy : public TempAllocPolicy { bool *oomFlag; @@ -278,12 +560,12 @@ class CompilerAllocPolicy : public ContextAllocPolicy public: CompilerAllocPolicy(JSContext *cx, bool *oomFlag) - : ContextAllocPolicy(cx), oomFlag(oomFlag) {} + : TempAllocPolicy(cx), oomFlag(oomFlag) {} CompilerAllocPolicy(JSContext *cx, Compiler &compiler); - void *malloc_(size_t bytes) { return checkAlloc(ContextAllocPolicy::malloc_(bytes)); } - void *realloc_(void *p, size_t bytes) { - return checkAlloc(ContextAllocPolicy::realloc_(p, bytes)); + void *malloc_(size_t bytes) { return checkAlloc(TempAllocPolicy::malloc_(bytes)); } + void *realloc_(void *p, size_t oldBytes, size_t bytes) { + return checkAlloc(TempAllocPolicy::realloc_(p, oldBytes, bytes)); } }; @@ -297,7 +579,6 @@ namespace ic { struct GetGlobalNameIC; struct SetGlobalNameIC; struct EqualityICInfo; - struct TraceICInfo; struct CallICInfo; # endif } @@ -305,30 +586,29 @@ namespace ic { typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); -typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32); -typedef void (JS_FASTCALL *VoidStubInt32)(VMFrame &, int32); +typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32_t); +typedef void (JS_FASTCALL *VoidStubInt32)(VMFrame &, int32_t); typedef JSBool (JS_FASTCALL *BoolStub)(VMFrame &); typedef void * (JS_FASTCALL *VoidPtrStub)(VMFrame &); typedef void * (JS_FASTCALL *VoidPtrStubPC)(VMFrame &, jsbytecode *); -typedef void * (JS_FASTCALL *VoidPtrStubUInt32)(VMFrame &, uint32); +typedef void * (JS_FASTCALL *VoidPtrStubUInt32)(VMFrame &, uint32_t); typedef JSObject * (JS_FASTCALL *JSObjStub)(VMFrame &); -typedef JSObject * (JS_FASTCALL *JSObjStubUInt32)(VMFrame &, uint32); +typedef JSObject * (JS_FASTCALL *JSObjStubUInt32)(VMFrame &, uint32_t); typedef JSObject * (JS_FASTCALL *JSObjStubFun)(VMFrame &, JSFunction *); typedef void (JS_FASTCALL *VoidStubFun)(VMFrame &, JSFunction *); typedef JSObject * (JS_FASTCALL *JSObjStubJSObj)(VMFrame &, JSObject *); -typedef void (JS_FASTCALL *VoidStubAtom)(VMFrame &, JSAtom *); +typedef void (JS_FASTCALL *VoidStubName)(VMFrame &, PropertyName *); typedef JSString * (JS_FASTCALL *JSStrStub)(VMFrame &); -typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); +typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32_t); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); -typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); +typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32_t); #ifdef JS_MONOIC typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void (JS_FASTCALL *VoidStubGetGlobal)(VMFrame &, js::mjit::ic::GetGlobalNameIC *); typedef void (JS_FASTCALL *VoidStubSetGlobal)(VMFrame &, js::mjit::ic::SetGlobalNameIC *); typedef JSBool (JS_FASTCALL *BoolStubEqualityIC)(VMFrame &, js::mjit::ic::EqualityICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubTraceIC)(VMFrame &, js::mjit::ic::TraceICInfo *); #endif #ifdef JS_POLYIC typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); @@ -338,6 +618,7 @@ typedef void (JS_FASTCALL *VoidStubSetElemIC)(VMFrame &f, js::mjit::ic::SetEleme namespace mjit { +struct InlineFrame; struct CallSite; struct NativeMapEntry { @@ -345,14 +626,42 @@ struct NativeMapEntry { void *ncode; /* pointer to native code */ }; -struct JITScript { +/* Per-op counts of performance metrics. */ +struct PCLengthEntry { + double codeLength; /* amount of inline code generated */ + double picsLength; /* amount of PIC stub code generated */ +}; + +/* + * Pools and patch locations for managing stubs for non-FASTCALL C++ calls made + * from native call and PropertyOp stubs. Ownership of these may be transferred + * into the orphanedNativePools for the compartment. + */ +struct NativeCallStub { + /* PC for the stub. Native call stubs cannot be added for inline frames. */ + jsbytecode *pc; + + /* Pool for the stub, NULL if it has been removed from the script. */ + JSC::ExecutablePool *pool; + + /* + * Fallthrough jump returning to jitcode which may be patched during + * recompilation. On x64 this is an indirect jump to avoid issues with far + * jumps on relative branches. + */ +#ifdef JS_CPU_X64 + JSC::CodeLocationDataLabelPtr jump; +#else + JSC::CodeLocationJump jump; +#endif +}; + +struct JITChunk +{ typedef JSC::MacroAssemblerCodeRef CodeRef; CodeRef code; /* pool & code addresses */ - - void *invokeEntry; /* invoke address */ - void *fastEntry; /* cached entry, fastest */ - void *arityCheckEntry; /* arity check address */ + PCLengthEntry *pcLengths; /* lengths for outer and inline frames */ /* * This struct has several variable-length sections that are allocated on @@ -362,22 +671,21 @@ struct JITScript { * Therefore, do not change the section ordering in finishThisUp() without * changing nMICs() et al as well. */ - uint32 nNmapPairs:31; /* The NativeMapEntrys are sorted by .bcOff. + uint32_t nNmapPairs; /* The NativeMapEntrys are sorted by .bcOff. .ncode values may not be NULL. */ - bool singleStepMode:1; /* compiled in "single step mode" */ + uint32_t nInlineFrames; + uint32_t nCallSites; #ifdef JS_MONOIC - uint32 nGetGlobalNames; - uint32 nSetGlobalNames; - uint32 nCallICs; - uint32 nEqualityICs; - uint32 nTraceICs; + uint32_t nGetGlobalNames; + uint32_t nSetGlobalNames; + uint32_t nCallICs; + uint32_t nEqualityICs; #endif #ifdef JS_POLYIC - uint32 nGetElems; - uint32 nSetElems; - uint32 nPICs; + uint32_t nGetElems; + uint32_t nSetElems; + uint32_t nPICs; #endif - uint32 nCallSites; #ifdef JS_MONOIC // Additional ExecutablePools that IC stubs were generated into. @@ -385,22 +693,23 @@ struct JITScript { ExecPoolVector execPools; #endif + // Additional ExecutablePools for native call and getter stubs. + Vector nativeCallStubs; + NativeMapEntry *nmap() const; + js::mjit::InlineFrame *inlineFrames() const; + js::mjit::CallSite *callSites() const; #ifdef JS_MONOIC ic::GetGlobalNameIC *getGlobalNames() const; ic::SetGlobalNameIC *setGlobalNames() const; ic::CallICInfo *callICs() const; ic::EqualityICInfo *equalityICs() const; - ic::TraceICInfo *traceICs() const; #endif #ifdef JS_POLYIC ic::GetElementIC *getElems() const; ic::SetElementIC *setElems() const; ic::PICInfo *pics() const; #endif - js::mjit::CallSite *callSites() const; - - ~JITScript(); bool isValidCode(void *ptr) { char *jitcode = (char *)code.m_code.executableAddress(); @@ -409,82 +718,227 @@ struct JITScript { } void nukeScriptDependentICs(); - void sweepCallICs(JSContext *cx, bool purgeAll); - void purgeMICs(); - void purgePICs(); - size_t scriptDataSize(); + size_t computedSizeOfIncludingThis(); + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf); - jsbytecode *nativeToPC(void *returnAddress) const; + ~JITChunk(); private: /* Helpers used to navigate the variable-length sections. */ - char *nmapSectionLimit() const; + char *commonSectionLimit() const; char *monoICSectionsLimit() const; char *polyICSectionsLimit() const; }; +void +SetChunkLimit(uint32_t limit); + +/* Information about a compilation chunk within a script. */ +struct ChunkDescriptor +{ + /* Bytecode range of the chunk: [begin,end) */ + uint32_t begin; + uint32_t end; + + /* Use counter for the chunk. */ + uint32_t counter; + + /* Optional compiled code for the chunk. */ + JITChunk *chunk; + + ChunkDescriptor() { PodZero(this); } +}; + +/* Jump or fallthrough edge in the bytecode which crosses a chunk boundary. */ +struct CrossChunkEdge +{ + /* Bytecode offsets of the source and target of the edge. */ + uint32_t source; + uint32_t target; + + /* Locations of the jump(s) for the source, NULL if not compiled. */ + void *sourceJump1; + void *sourceJump2; + + /* Any jump table entries along this edge. */ + typedef Vector JumpTableEntryVector; + JumpTableEntryVector *jumpTableEntries; + + /* Location of the label for the target, NULL if not compiled. */ + void *targetLabel; + + /* + * Location of a shim which will transfer control to the interpreter at the + * target bytecode. The source jumps are patched to jump to this label if + * the source is compiled but not the target. + */ + void *shimLabel; + + CrossChunkEdge() { PodZero(this); } +}; + +struct JITScript +{ + JSScript *script; + + void *invokeEntry; /* invoke address */ + void *fastEntry; /* cached entry, fastest */ + void *arityCheckEntry; /* arity check address */ + void *argsCheckEntry; /* arguments check address */ + + /* List of inline caches jumping to the fastEntry. */ + JSCList callers; + + uint32_t nchunks; + uint32_t nedges; + + /* + * Pool for shims which transfer control to the interpreter on cross chunk + * edges to chunks which do not have compiled code. + */ + JSC::ExecutablePool *shimPool; + +#ifdef JS_MONOIC + /* Inline cache at function entry for checking this/argument types. */ + JSC::CodeLocationLabel argsCheckStub; + JSC::CodeLocationLabel argsCheckFallthrough; + JSC::CodeLocationJump argsCheckJump; + JSC::ExecutablePool *argsCheckPool; + void resetArgsCheck(); +#endif + + ChunkDescriptor &chunkDescriptor(unsigned i) { + JS_ASSERT(i < nchunks); + ChunkDescriptor *descs = (ChunkDescriptor *) ((char *) this + sizeof(JITScript)); + return descs[i]; + } + + unsigned chunkIndex(jsbytecode *pc) { + unsigned offset = pc - script->code; + JS_ASSERT(offset < script->length); + for (unsigned i = 0; i < nchunks; i++) { + const ChunkDescriptor &desc = chunkDescriptor(i); + JS_ASSERT(desc.begin <= offset); + if (offset < desc.end) + return i; + } + JS_NOT_REACHED("Bad chunk layout"); + return 0; + } + + JITChunk *chunk(jsbytecode *pc) { + return chunkDescriptor(chunkIndex(pc)).chunk; + } + + JITChunk *findCodeChunk(void *addr); + + CrossChunkEdge *edges() { + return (CrossChunkEdge *) (&chunkDescriptor(0) + nchunks); + } + + /* Patch any compiled sources in edge to jump to label. */ + void patchEdge(const CrossChunkEdge &edge, void *label); + + jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline); + + size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf); + + void destroy(JSContext *cx); + void destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses = true); +}; + /* * Execute the given mjit code. This is a low-level call and callers must * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT. */ -JSBool EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit); +JaegerStatus EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit, + bool partial); /* Execute a method that has been JIT compiled. */ -JSBool JaegerShot(JSContext *cx); +JaegerStatus JaegerShot(JSContext *cx, bool partial); /* Drop into the middle of a method at an arbitrary point, and execute. */ -JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint); +JaegerStatus JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial); enum CompileStatus { Compile_Okay, - Compile_Abort, - Compile_Error, + Compile_Abort, // abort compilation + Compile_InlineAbort, // inlining attempt failed, continue compilation + Compile_Retry, // static overflow or failed inline, try to recompile + Compile_Error, // OOM Compile_Skipped }; void JS_FASTCALL ProfileStubCall(VMFrame &f); -CompileStatus JS_NEVER_INLINE -TryCompile(JSContext *cx, StackFrame *fp); +enum CompileRequest +{ + CompileRequest_Interpreter, + CompileRequest_JIT +}; + +CompileStatus +CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc, + bool construct, CompileRequest request); void -ReleaseScriptCode(JSContext *cx, JSScript *script); +ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct); -struct CallSite +inline void +ReleaseScriptCode(JSContext *cx, JSScript *script) { - uint32 codeOffset; - uint32 pcOffset; - uint32 id; + if (script->jitCtor) + mjit::ReleaseScriptCode(cx, script, true); + if (script->jitNormal) + mjit::ReleaseScriptCode(cx, script, false); +} - // Normally, callsite ID is the __LINE__ in the program that added the - // callsite. Since traps can be removed, we make sure they carry over - // from each compilation, and identify them with a single, canonical - // ID. Hopefully a SpiderMonkey file won't have two billion source lines. - static const uint32 MAGIC_TRAP_ID = 0xFEDCBABC; +// Expand all stack frames inlined by the JIT within a compartment. +void +ExpandInlineFrames(JSCompartment *compartment); + +// Return all VMFrames in a compartment to the interpreter. This must be +// followed by destroying all JIT code in the compartment. +void +ClearAllFrames(JSCompartment *compartment); + +// Information about a frame inlined during compilation. +struct InlineFrame +{ + InlineFrame *parent; + jsbytecode *parentpc; + HeapPtrFunction fun; + + // Total distance between the start of the outer JSStackFrame and the start + // of this frame, in multiples of sizeof(Value). + uint32_t depth; +}; + +struct CallSite +{ + uint32_t codeOffset; + uint32_t inlineIndex; + uint32_t pcOffset; + RejoinState rejoin; - void initialize(uint32 codeOffset, uint32 pcOffset, uint32 id) { + void initialize(uint32_t codeOffset, uint32_t inlineIndex, uint32_t pcOffset, + RejoinState rejoin) { this->codeOffset = codeOffset; + this->inlineIndex = inlineIndex; this->pcOffset = pcOffset; - this->id = id; + this->rejoin = rejoin; } bool isTrap() const { - return id == MAGIC_TRAP_ID; + return rejoin == REJOIN_TRAP; } }; -/* - * Re-enables a tracepoint in the method JIT. When full is true, we - * also reset the iteration counter. - */ void -ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full); - -uintN -GetCallTargetCount(JSScript *script, jsbytecode *pc); +DumpAllProfiles(JSContext *cx); inline void * bsearch_nmap(NativeMapEntry *nmap, size_t nPairs, size_t bcOff) { @@ -509,33 +963,61 @@ inline void * bsearch_nmap(NativeMapEntry *nmap, size_t nPairs, size_t bcOff) } /* namespace mjit */ -} /* namespace js */ +inline mjit::JITChunk * +VMFrame::chunk() +{ + return jit()->chunk(regs.pc); +} -inline void * -JSScript::maybeNativeCodeForPC(bool constructing, jsbytecode *pc) +inline unsigned +VMFrame::chunkIndex() { - js::mjit::JITScript *jit = getJIT(constructing); - if (!jit) - return NULL; - JS_ASSERT(pc >= code && pc < code + length); - return bsearch_nmap(jit->nmap(), jit->nNmapPairs, (size_t)(pc - code)); + return jit()->chunkIndex(regs.pc); +} + +inline JSScript * +VMFrame::script() +{ + if (regs.inlined()) + return chunk()->inlineFrames()[regs.inlined()->inlineIndex].fun->script(); + return fp()->script(); +} + +inline jsbytecode * +VMFrame::pc() +{ + if (regs.inlined()) + return script()->code + regs.inlined()->pcOffset; + return regs.pc; } +} /* namespace js */ + inline void * JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc) { js::mjit::JITScript *jit = getJIT(constructing); - JS_ASSERT(pc >= code && pc < code + length); - void* native = bsearch_nmap(jit->nmap(), jit->nNmapPairs, (size_t)(pc - code)); - JS_ASSERT(native); - return native; + if (!jit) + return NULL; + js::mjit::JITChunk *chunk = jit->chunk(pc); + if (!chunk) + return NULL; + return bsearch_nmap(chunk->nmap(), chunk->nNmapPairs, (size_t)(pc - code)); } +extern "C" void JaegerTrampolineReturn(); +extern "C" void JaegerInterpoline(); +extern "C" void JaegerInterpolineScripted(); + #if defined(_MSC_VER) || defined(_WIN64) extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else extern "C" void JaegerThrowpoline(); #endif +#if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) +extern "C" void JaegerInterpolinePatched(); +#endif + #endif /* jsjaeger_h__ */ diff --git a/deps/mozjs/js/src/methodjit/MonoIC.cpp b/deps/mozjs/js/src/methodjit/MonoIC.cpp index 8d072baabbb..70ff14fc60e 100644 --- a/deps/mozjs/js/src/methodjit/MonoIC.cpp +++ b/deps/mozjs/js/src/methodjit/MonoIC.cpp @@ -52,6 +52,8 @@ #include "InlineFrameAssembler.h" #include "jsobj.h" +#include "builtin/RegExp.h" + #include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsscopeinlines.h" @@ -69,196 +71,93 @@ typedef JSC::MacroAssembler::ImmPtr ImmPtr; typedef JSC::MacroAssembler::Call Call; typedef JSC::MacroAssembler::Label Label; typedef JSC::MacroAssembler::DataLabel32 DataLabel32; +typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr; #if defined JS_MONOIC static void PatchGetFallback(VMFrame &f, ic::GetGlobalNameIC *ic) { - Repatcher repatch(f.jit()); - JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName)); + Repatcher repatch(f.chunk()); + JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::Name)); repatch.relink(ic->slowPathCall, fptr); } void JS_FASTCALL ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic) { - JSObject *obj = f.fp()->scopeChain().getGlobal(); - JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); - jsid id = ATOM_TO_JSID(atom); + JSObject &obj = f.fp()->scopeChain().global(); + PropertyName *name = f.script()->getName(GET_INDEX(f.pc())); + + RecompilationMonitor monitor(f.cx); + + const Shape *shape = obj.nativeLookup(f.cx, js_CheckForStringIndex(ATOM_TO_JSID(name))); + + if (monitor.recompiled()) { + stubs::Name(f); + return; + } - const Shape *shape = obj->nativeLookup(id); if (!shape || !shape->hasDefaultGetterOrIsMethod() || !shape->hasSlot()) { if (shape) PatchGetFallback(f, ic); - stubs::GetGlobalName(f); + stubs::Name(f); return; } - uint32 slot = shape->slot; + uint32_t slot = shape->slot(); /* Patch shape guard. */ - Repatcher repatcher(f.jit()); - repatcher.repatch(ic->fastPathStart.dataLabel32AtOffset(ic->shapeOffset), obj->shape()); + Repatcher repatcher(f.chunk()); + repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj.lastProperty()); /* Patch loads. */ + uint32_t index = obj.dynamicSlotIndex(slot); JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset); - repatcher.patchAddressOffsetForValueLoad(label, slot * sizeof(Value)); + repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value)); /* Do load anyway... this time. */ - stubs::GetGlobalName(f); + stubs::Name(f); } template static void JS_FASTCALL DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic) { - JSScript *script = f.fp()->script(); - JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc)); - stubs::SetGlobalName(f, atom); + stubs::SetGlobalName(f, f.script()->getName(GET_INDEX(f.pc()))); } template void JS_FASTCALL DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic); template void JS_FASTCALL DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic); -template -static void JS_FASTCALL -DisabledSetGlobalNoCache(VMFrame &f, ic::SetGlobalNameIC *ic) -{ - JSScript *script = f.fp()->script(); - JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc)); - stubs::SetGlobalNameNoCache(f, atom); -} - -template void JS_FASTCALL DisabledSetGlobalNoCache(VMFrame &f, ic::SetGlobalNameIC *ic); -template void JS_FASTCALL DisabledSetGlobalNoCache(VMFrame &f, ic::SetGlobalNameIC *ic); - static void PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic) { - JSScript *script = f.fp()->script(); - - Repatcher repatch(f.jit()); - VoidStubSetGlobal stub = ic->usePropertyCache - ? STRICT_VARIANT(DisabledSetGlobal) - : STRICT_VARIANT(DisabledSetGlobalNoCache); + JSScript *script = f.script(); + Repatcher repatch(f.chunk()); + VoidStubSetGlobal stub = STRICT_VARIANT(DisabledSetGlobal); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub)); repatch.relink(ic->slowPathCall, fptr); } void -SetGlobalNameIC::patchExtraShapeGuard(Repatcher &repatcher, int32 shape) +SetGlobalNameIC::patchExtraShapeGuard(Repatcher &repatcher, const Shape *shape) { JS_ASSERT(hasExtraStub); JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(extraStub.start())); - repatcher.repatch(label.dataLabel32AtOffset(extraShapeGuard), shape); + repatcher.repatch(label.dataLabelPtrAtOffset(extraShapeGuard), shape); } void -SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, int32 shape) +SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, const Shape *shape) { - JSC::CodeLocationDataLabel32 label = fastPathStart.dataLabel32AtOffset(shapeOffset); + JSC::CodeLocationDataLabelPtr label = fastPathStart.dataLabelPtrAtOffset(shapeOffset); repatcher.repatch(label, shape); } -static LookupStatus -UpdateSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape) -{ - Repatcher repatcher(ic->extraStub); - - ic->patchExtraShapeGuard(repatcher, obj->shape()); - - JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(ic->extraStub.start())); - label = label.labelAtOffset(ic->extraStoreOffset); - repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value), - ic->vr.isTypeKnown()); - - return Lookup_Cacheable; -} - -static LookupStatus -AttachSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape) -{ - Assembler masm; - - Label start = masm.label(); - - DataLabel32 shapeLabel; - Jump guard = masm.branch32WithPatch(Assembler::NotEqual, ic->shapeReg, Imm32(obj->shape()), - shapeLabel); - - /* A constant object needs rematerialization. */ - if (ic->objConst) - masm.move(ImmPtr(obj), ic->objReg); - - JS_ASSERT(obj->branded()); - - /* - * Load obj->slots. If ic->objConst, then this clobbers objReg, because - * ic->objReg == ic->shapeReg. - */ - masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg); - - /* Test if overwriting a function-tagged slot. */ - Address slot(ic->shapeReg, sizeof(Value) * shape->slot); - Jump isNotObject = masm.testObject(Assembler::NotEqual, slot); - - /* Now, test if the object is a function object. */ - masm.loadPayload(slot, ic->shapeReg); - Jump isFun = masm.testFunction(Assembler::Equal, ic->shapeReg); - - /* Restore shapeReg to obj->slots, since we clobbered it. */ - if (ic->objConst) - masm.move(ImmPtr(obj), ic->objReg); - masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg); - - /* If the object test fails, shapeReg is still obj->slots. */ - isNotObject.linkTo(masm.label(), &masm); - DataLabel32 store = masm.storeValueWithAddressOffsetPatch(ic->vr, slot); - - Jump done = masm.jump(); - - JITScript *jit = f.jit(); - LinkerHelper linker(masm); - JSC::ExecutablePool *ep = linker.init(f.cx); - if (!ep) - return Lookup_Error; - if (!jit->execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return Lookup_Error; - } - - if (!linker.verifyRange(jit)) - return Lookup_Uncacheable; - - linker.link(done, ic->fastPathStart.labelAtOffset(ic->fastRejoinOffset)); - linker.link(guard, ic->slowPathStart); - linker.link(isFun, ic->slowPathStart); - - JSC::CodeLocationLabel cs = linker.finalize(); - JaegerSpew(JSpew_PICs, "generated setgname stub at %p\n", cs.executableAddress()); - - Repatcher repatcher(f.jit()); - repatcher.relink(ic->fastPathStart.jumpAtOffset(ic->inlineShapeJump), cs); - - int offset = linker.locationOf(shapeLabel) - linker.locationOf(start); - ic->extraShapeGuard = offset; - JS_ASSERT(ic->extraShapeGuard == offset); - - ic->extraStub = JSC::JITCode(cs.executableAddress(), linker.size()); - offset = linker.locationOf(store) - linker.locationOf(start); - ic->extraStoreOffset = offset; - JS_ASSERT(ic->extraStoreOffset == offset); - - ic->hasExtraStub = true; - - return Lookup_Cacheable; -} - static LookupStatus UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape) { @@ -269,44 +168,21 @@ UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Sh if (shape->isMethod() || !shape->hasDefaultSetter() || !shape->writable() || - !shape->hasSlot()) + !shape->hasSlot() || + obj->watched()) { - /* Disable the IC for weird shape attributes. */ + /* Disable the IC for weird shape attributes and watchpoints. */ PatchSetFallback(f, ic); return Lookup_Uncacheable; } - /* Branded sets must guard that they don't overwrite method-valued properties. */ - if (obj->branded()) { - /* - * If this slot has a function valued property, the tail of this opcode - * could change the shape. Even if it doesn't, the IC is probably - * pointless, because it will always hit the function-test path and - * bail out. In these cases, don't bother building or updating the IC. - */ - const Value &v = obj->getSlot(shape->slot); - if (v.isObject() && v.toObject().isFunction()) { - /* - * If we're going to rebrand, the object may unbrand, allowing this - * IC to come back to life. In that case, we don't disable the IC. - */ - if (!ChangesMethodValue(v, f.regs.sp[-1])) - PatchSetFallback(f, ic); - return Lookup_Uncacheable; - } - - if (ic->hasExtraStub) - return UpdateSetGlobalNameStub(f, ic, obj, shape); - - return AttachSetGlobalNameStub(f, ic, obj, shape); - } - /* Object is not branded, so we can use the inline path. */ - Repatcher repatcher(f.jit()); - ic->patchInlineShapeGuard(repatcher, obj->shape()); + Repatcher repatcher(f.chunk()); + ic->patchInlineShapeGuard(repatcher, obj->lastProperty()); + uint32_t index = obj->dynamicSlotIndex(shape->slot()); JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset); - repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value), + repatcher.patchAddressOffsetForValueStore(label, index * sizeof(Value), ic->vr.isTypeKnown()); return Lookup_Cacheable; @@ -315,19 +191,21 @@ UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Sh void JS_FASTCALL ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic) { - JSObject *obj = f.fp()->scopeChain().getGlobal(); - JSScript *script = f.fp()->script(); - JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc)); - const Shape *shape = obj->nativeLookup(ATOM_TO_JSID(atom)); - - LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape); - if (status == Lookup_Error) - THROW(); - - if (ic->usePropertyCache) - STRICT_VARIANT(stubs::SetGlobalName)(f, atom); - else - STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, atom); + JSObject &obj = f.fp()->scopeChain().global(); + JSScript *script = f.script(); + PropertyName *name = script->getName(GET_INDEX(f.pc())); + + RecompilationMonitor monitor(f.cx); + + const Shape *shape = obj.nativeLookup(f.cx, ATOM_TO_JSID(name)); + + if (!monitor.recompiled()) { + LookupStatus status = UpdateSetGlobalName(f, ic, &obj, shape); + if (status == Lookup_Error) + THROW(); + } + + STRICT_VARIANT(stubs::SetGlobalName)(f, name); } class EqualityICLinker : public LinkerHelper @@ -336,16 +214,15 @@ class EqualityICLinker : public LinkerHelper public: EqualityICLinker(Assembler &masm, VMFrame &f) - : LinkerHelper(masm), f(f) + : LinkerHelper(masm, JSC::METHOD_CODE), f(f) { } bool init(JSContext *cx) { JSC::ExecutablePool *pool = LinkerHelper::init(cx); if (!pool) return false; - JSScript *script = f.fp()->script(); - JITScript *jit = script->getJIT(f.fp()->isConstructing()); - if (!jit->execPools.append(pool)) { + JS_ASSERT(!f.regs.inlined()); + if (!f.chunk()->execPools.append(pool)) { pool->release(); js_ReportOutOfMemory(cx); return false; @@ -355,7 +232,7 @@ class EqualityICLinker : public LinkerHelper }; /* Rough over-estimate of how much memory we need to unprotect. */ -static const uint32 INLINE_PATH_LENGTH = 64; +static const uint32_t INLINE_PATH_LENGTH = 64; class EqualityCompiler : public BaseCompiler { @@ -450,10 +327,10 @@ class EqualityCompiler : public BaseCompiler linkToStub(rhsFail); } - Jump lhsHasEq = masm.branchTest32(Assembler::NonZero, - Address(lvr.dataReg(), - offsetof(JSObject, flags)), - Imm32(JSObject::HAS_EQUALITY)); + masm.loadObjClass(lvr.dataReg(), ic.tempReg); + Jump lhsHasEq = masm.branchPtr(Assembler::NotEqual, + Address(ic.tempReg, offsetof(Class, ext.equality)), + ImmPtr(NULL)); linkToStub(lhsHasEq); if (rvr.isConstant()) { @@ -475,14 +352,14 @@ class EqualityCompiler : public BaseCompiler if (!buffer.init(cx)) return false; - Repatcher repatcher(f.jit()); + Repatcher repatcher(f.chunk()); /* Overwrite the call to the IC with a call to the stub. */ JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub)); repatcher.relink(ic.stubCall, fptr); // Silently fail, the IC is disabled now. - if (!buffer.verifyRange(f.jit())) + if (!buffer.verifyRange(f.chunk())) return true; /* Set the targets of all type test failures to go to the stub. */ @@ -494,7 +371,7 @@ class EqualityCompiler : public BaseCompiler buffer.link(trueJump, ic.target); buffer.link(falseJump, ic.fallThrough); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); /* Jump to the newly generated code instead of to the IC. */ repatcher.relink(ic.jumpToStub, cs); @@ -550,6 +427,97 @@ SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic) return NULL; } +bool +NativeStubLinker::init(JSContext *cx) +{ + JSC::ExecutablePool *pool = LinkerHelper::init(cx); + if (!pool) + return false; + + NativeCallStub stub; + stub.pc = pc; + stub.pool = pool; + stub.jump = locationOf(done); + if (!chunk->nativeCallStubs.append(stub)) { + pool->release(); + return false; + } + + return true; +} + +/* + * Generate epilogue code to run after a stub ABI call to a native or getter. + * This checks for an exception, and either type checks the result against the + * observed types for the opcode or loads the result into a register pair + * (it will go through a type barrier afterwards). + */ +bool +mjit::NativeStubEpilogue(VMFrame &f, Assembler &masm, NativeStubLinker::FinalJump *result, + int32_t initialFrameDepth, int32_t vpOffset, + MaybeRegisterID typeReg, MaybeRegisterID dataReg) +{ + /* Reload fp, which may have been clobbered by restoreStackBase(). */ + masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + + Jump hasException = masm.branchTest32(Assembler::Zero, Registers::ReturnReg, + Registers::ReturnReg); + + Address resultAddress(JSFrameReg, vpOffset); + + Vector mismatches(f.cx); + if (f.cx->typeInferenceEnabled() && !typeReg.isSet()) { + /* + * Test the result of this native against the known result type set for + * the call. We don't assume knowledge about the types that natives can + * return, except when generating specialized paths in FastBuiltins. + */ + types::TypeSet *types = f.script()->analysis()->bytecodeTypes(f.pc()); + if (!masm.generateTypeCheck(f.cx, resultAddress, types, &mismatches)) + THROWV(false); + } + + /* + * Can no longer trigger recompilation in this stub, clear the stub rejoin + * on the VMFrame. + */ + masm.storePtr(ImmPtr(NULL), FrameAddress(offsetof(VMFrame, stubRejoin))); + + if (typeReg.isSet()) + masm.loadValueAsComponents(resultAddress, typeReg.reg(), dataReg.reg()); + + /* + * The final jump is a indirect on x64, so that we'll always be able + * to repatch it to the interpoline later. + */ + Label finished = masm.label(); +#ifdef JS_CPU_X64 + JSC::MacroAssembler::DataLabelPtr done = masm.moveWithPatch(ImmPtr(NULL), Registers::ValueReg); + masm.jump(Registers::ValueReg); +#else + Jump done = masm.jump(); +#endif + + /* Generate a call for type check failures on the native result. */ + if (!mismatches.empty()) { + for (unsigned i = 0; i < mismatches.length(); i++) + mismatches[i].linkTo(masm.label(), &masm); + masm.addPtr(Imm32(vpOffset), JSFrameReg, Registers::ArgReg1); + masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::TypeBarrierReturn), + f.regs.pc, NULL, initialFrameDepth); + masm.storePtr(ImmPtr(NULL), FrameAddress(offsetof(VMFrame, stubRejoin))); + masm.jump().linkTo(finished, &masm); + } + + /* Move JaegerThrowpoline into register for very far jump on x64. */ + hasException.linkTo(masm.label(), &masm); + masm.storePtr(ImmPtr(NULL), FrameAddress(offsetof(VMFrame, stubRejoin))); + masm.throwInJIT(); + + *result = done; + return true; +} + /* * Calls have an inline path and an out-of-line path. The inline path is used * in the fastest case: the method has JIT'd code, and |argc == nargs|. @@ -610,17 +578,17 @@ class CallCompiler : public BaseCompiler return ep; } - void disable(JITScript *jit) + void disable() { JSC::CodeLocationCall oolCall = ic.slowPathStart.callAtOffset(ic.oolCallOffset); - Repatcher repatch(jit); + Repatcher repatch(f.chunk()); JSC::FunctionPtr fptr = callingNew ? JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowNewFromIC)) : JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowCallFromIC)); repatch.relink(oolCall, fptr); } - bool generateFullCallStub(JITScript *from, JSScript *script, uint32 flags) + bool generateFullCallStub(JSScript *script, uint32_t flags) { /* * Create a stub that works with arity mismatches. Like the fast-path, @@ -630,14 +598,14 @@ class CallCompiler : public BaseCompiler */ Assembler masm; InlineFrameAssembler inlFrame(masm, ic, flags); - RegisterID t0 = inlFrame.tempRegs.takeAnyReg(); + RegisterID t0 = inlFrame.tempRegs.takeAnyReg().reg(); /* Generate the inline frame creation. */ - inlFrame.assemble(ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress()); + void *ncode = ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress(); + inlFrame.assemble(ncode, f.pc()); - /* funPtrReg is still valid. Check if a compilation is needed. */ - Address scriptAddr(ic.funPtrReg, offsetof(JSFunction, u) + - offsetof(JSFunction::U::Scripted, script)); + /* funObjReg is still valid. Check if a compilation is needed. */ + Address scriptAddr(ic.funObjReg, JSFunction::offsetOfNativeOrScript()); masm.loadPtr(scriptAddr, t0); /* @@ -651,20 +619,36 @@ class CallCompiler : public BaseCompiler masm.loadPtr(Address(t0, offset), t0); Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT)); + /* + * Write the rejoin state to indicate this is a compilation call made + * from an IC (the recompiler cannot detect calls made from ICs + * automatically). + */ + masm.storePtr(ImmPtr((void *) ic.frameSize.rejoinState(f.pc(), false)), + FrameAddress(offsetof(VMFrame, stubRejoin))); + + masm.bumpStubCounter(f.script(), f.pc(), Registers::tempCallReg()); + /* Try and compile. On success we get back the nmap pointer. */ - masm.storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp)); void *compilePtr = JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction); + DataLabelPtr inlined; if (ic.frameSize.isStatic()) { masm.move(Imm32(ic.frameSize.staticArgc()), Registers::ArgReg1); - masm.fallibleVMCall(compilePtr, script->code, ic.frameSize.staticLocalSlots()); + masm.fallibleVMCall(cx->typeInferenceEnabled(), + compilePtr, f.regs.pc, &inlined, ic.frameSize.staticLocalSlots()); } else { - masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), Registers::ArgReg1); - masm.fallibleVMCall(compilePtr, script->code, -1); + masm.load32(FrameAddress(VMFrame::offsetOfDynamicArgc()), Registers::ArgReg1); + masm.fallibleVMCall(cx->typeInferenceEnabled(), + compilePtr, f.regs.pc, &inlined, -1); } - masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); Jump notCompiled = masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg, Registers::ReturnReg); + masm.loadPtr(FrameAddress(VMFrame::offsetOfRegsSp()), JSFrameReg); + + /* Compute the value of ncode to use at this call site. */ + ncode = (uint8_t *) f.chunk()->code.m_code.executableAddress() + ic.call->codeOffset; + masm.storePtr(ImmPtr(ncode), Address(JSFrameReg, StackFrame::offsetOfNcode())); masm.jump(Registers::ReturnReg); @@ -674,99 +658,112 @@ class CallCompiler : public BaseCompiler if (ic.frameSize.isStatic()) masm.move(Imm32(ic.frameSize.staticArgc()), JSParamReg_Argc); else - masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), JSParamReg_Argc); + masm.load32(FrameAddress(VMFrame::offsetOfDynamicArgc()), JSParamReg_Argc); masm.jump(t0); - LinkerHelper linker(masm); + LinkerHelper linker(masm, JSC::METHOD_CODE); JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ScriptStub); if (!ep) return false; - if (!linker.verifyRange(from)) { - disable(from); + if (!linker.verifyRange(f.chunk())) { + disable(); return true; } linker.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset)); - JSC::CodeLocationLabel cs = linker.finalize(); + JSC::CodeLocationLabel cs = linker.finalize(f); + + JaegerSpew(JSpew_PICs, "generated CALL stub %p (%lu bytes)\n", cs.executableAddress(), + (unsigned long) masm.size()); - JaegerSpew(JSpew_PICs, "generated CALL stub %p (%d bytes)\n", cs.executableAddress(), - masm.size()); + if (f.regs.inlined()) { + JSC::LinkBuffer code((uint8_t *) cs.executableAddress(), masm.size(), JSC::METHOD_CODE); + code.patch(inlined, f.regs.inlined()); + } - Repatcher repatch(from); + Repatcher repatch(f.chunk()); JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset); repatch.relink(oolJump, cs); return true; } - bool patchInlinePath(JITScript *from, JSScript *script, JSObject *obj) + bool patchInlinePath(JSScript *script, JSObject *obj) { JS_ASSERT(ic.frameSize.isStatic()); JITScript *jit = script->getJIT(callingNew); /* Very fast path. */ - Repatcher repatch(from); + Repatcher repatch(f.chunk()); + + /* + * Use the arguments check entry if this is a monitored call, we might + * not have accounted for all possible argument types. + */ + void *entry = ic.typeMonitored ? jit->argsCheckEntry : jit->fastEntry; if (!repatch.canRelink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(jit->fastEntry))) { + JSC::CodeLocationLabel(entry))) { return false; } ic.fastGuardedObject = obj; + JS_APPEND_LINK(&ic.links, &jit->callers); repatch.repatch(ic.funGuard, obj); repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(jit->fastEntry)); + JSC::CodeLocationLabel(entry)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", - ic.funGuard.executableAddress(), ic.fastGuardedObject); + ic.funGuard.executableAddress(), + static_cast(ic.fastGuardedObject)); return true; } - bool generateStubForClosures(JITScript *from, JSObject *obj) + bool generateStubForClosures(JSObject *obj) { JS_ASSERT(ic.frameSize.isStatic()); - /* Slightly less fast path - guard on fun->getFunctionPrivate() instead. */ + /* Slightly less fast path - guard on fun->script() instead. */ Assembler masm; - Registers tempRegs; + Registers tempRegs(Registers::AvailRegs); tempRegs.takeReg(ic.funObjReg); - RegisterID t0 = tempRegs.takeAnyReg(); + RegisterID t0 = tempRegs.takeAnyReg().reg(); /* Guard that it's actually a function object. */ - Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, &js_FunctionClass); + Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, t0, &FunctionClass); - /* Guard that it's the same function. */ - JSFunction *fun = obj->getFunctionPrivate(); - masm.loadObjPrivate(ic.funObjReg, t0); - Jump funGuard = masm.branchPtr(Assembler::NotEqual, t0, ImmPtr(fun)); + /* Guard that it's the same script. */ + Address scriptAddr(ic.funObjReg, JSFunction::offsetOfNativeOrScript()); + Jump funGuard = masm.branchPtr(Assembler::NotEqual, scriptAddr, + ImmPtr(obj->toFunction()->script())); Jump done = masm.jump(); - LinkerHelper linker(masm); + LinkerHelper linker(masm, JSC::METHOD_CODE); JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ClosureStub); if (!ep) return false; ic.hasJsFunCheck = true; - if (!linker.verifyRange(from)) { - disable(from); + if (!linker.verifyRange(f.chunk())) { + disable(); return true; } linker.link(claspGuard, ic.slowPathStart); linker.link(funGuard, ic.slowPathStart); linker.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset)); - JSC::CodeLocationLabel cs = linker.finalize(); + JSC::CodeLocationLabel cs = linker.finalize(f); - JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%d bytes)\n", - cs.executableAddress(), masm.size()); + JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%lu bytes)\n", + cs.executableAddress(), (unsigned long) masm.size()); - Repatcher repatch(from); + Repatcher repatch(f.chunk()); repatch.relink(ic.funJump, cs); return true; @@ -774,40 +771,59 @@ class CallCompiler : public BaseCompiler bool generateNativeStub() { - JITScript *jit = f.jit(); - /* Snapshot the frameDepth before SplatApplyArgs modifies it. */ - uintN initialFrameDepth = f.regs.sp - f.regs.fp()->slots(); + uintN initialFrameDepth = f.regs.sp - f.fp()->slots(); /* * SplatApplyArgs has not been called, so we call it here before * potentially touching f.u.call.dynamicArgc. */ - Value *vp; + CallArgs args; if (ic.frameSize.isStatic()) { - JS_ASSERT(f.regs.sp - f.regs.fp()->slots() == (int)ic.frameSize.staticLocalSlots()); - vp = f.regs.sp - (2 + ic.frameSize.staticArgc()); + JS_ASSERT(f.regs.sp - f.fp()->slots() == (int)ic.frameSize.staticLocalSlots()); + args = CallArgsFromSp(ic.frameSize.staticArgc(), f.regs.sp); } else { + JS_ASSERT(!f.regs.inlined()); JS_ASSERT(*f.regs.pc == JSOP_FUNAPPLY && GET_ARGC(f.regs.pc) == 2); if (!ic::SplatApplyArgs(f)) /* updates regs.sp */ THROWV(true); - vp = f.regs.sp - (2 + f.u.call.dynamicArgc); + args = CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp); } - JSObject *obj; - if (!IsFunctionObject(*vp, &obj)) + JSFunction *fun; + if (!IsFunctionObject(args.calleev(), &fun)) return false; - JSFunction *fun = obj->getFunctionPrivate(); - if ((!callingNew && !fun->isNative()) || (callingNew && !fun->isConstructor())) + if ((!callingNew && !fun->isNative()) || (callingNew && !fun->isNativeConstructor())) return false; if (callingNew) - vp[1].setMagicWithObjectOrNullPayload(NULL); + args.thisv().setMagic(JS_IS_CONSTRUCTING); - if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp)) + RecompilationMonitor monitor(cx); + + if (!CallJSNative(cx, fun->u.n.native, args)) THROWV(true); + types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval()); + + /* + * Native stubs are not generated for inline frames. The overhead of + * bailing out from the IC is far greater than the time saved by + * inlining the parent frame in the first place, so mark the immediate + * caller as uninlineable. + */ + if (f.script()->function()) { + f.script()->uninlineable = true; + MarkTypeObjectFlags(cx, f.script()->function(), types::OBJECT_FLAG_UNINLINEABLE); + } + + /* Don't touch the IC if the call triggered a recompilation. */ + if (monitor.recompiled()) + return true; + + JS_ASSERT(!f.regs.inlined()); + /* Right now, take slow-path for IC misses or multiple stubs. */ if (ic.fastGuardedNative || ic.hasJsFunCheck) return true; @@ -822,81 +838,69 @@ class CallCompiler : public BaseCompiler Assembler masm; /* Guard on the function object identity, for now. */ - Jump funGuard = masm.branchPtr(Assembler::NotEqual, ic.funObjReg, ImmPtr(obj)); + Jump funGuard = masm.branchPtr(Assembler::NotEqual, ic.funObjReg, ImmPtr(fun)); + + /* + * Write the rejoin state for the recompiler to use if this call + * triggers recompilation. Natives use a different stack address to + * store the return value than FASTCALLs, and without additional + * information we cannot tell which one is active on a VMFrame. + */ + masm.storePtr(ImmPtr((void *) ic.frameSize.rejoinState(f.pc(), true)), + FrameAddress(offsetof(VMFrame, stubRejoin))); /* N.B. After this call, the frame will have a dynamic frame size. */ if (ic.frameSize.isDynamic()) { - masm.fallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, ic::SplatApplyArgs), - f.regs.pc, initialFrameDepth); + masm.bumpStubCounter(f.script(), f.pc(), Registers::tempCallReg()); + masm.fallibleVMCall(cx->typeInferenceEnabled(), + JS_FUNC_TO_DATA_PTR(void *, ic::SplatApplyArgs), + f.regs.pc, NULL, initialFrameDepth); } - Registers tempRegs; -#ifndef JS_CPU_X86 - tempRegs.takeReg(Registers::ArgReg0); - tempRegs.takeReg(Registers::ArgReg1); - tempRegs.takeReg(Registers::ArgReg2); -#endif - RegisterID t0 = tempRegs.takeAnyReg(); - - /* Store pc. */ - masm.storePtr(ImmPtr(cx->regs().pc), - FrameAddress(offsetof(VMFrame, regs.pc))); - - /* Store sp (if not already set by ic::SplatApplyArgs). */ - if (ic.frameSize.isStatic()) { - uint32 spOffset = sizeof(StackFrame) + initialFrameDepth * sizeof(Value); - masm.addPtr(Imm32(spOffset), JSFrameReg, t0); - masm.storePtr(t0, FrameAddress(offsetof(VMFrame, regs.sp))); - } + Registers tempRegs = Registers::tempCallRegMask(); + RegisterID t0 = tempRegs.takeAnyReg().reg(); + masm.bumpStubCounter(f.script(), f.pc(), t0); - /* Store fp. */ - masm.storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp)); + int32_t storeFrameDepth = ic.frameSize.isStatic() ? initialFrameDepth : -1; + masm.setupFallibleABICall(cx->typeInferenceEnabled(), f.regs.pc, storeFrameDepth); /* Grab cx. */ #ifdef JS_CPU_X86 - RegisterID cxReg = tempRegs.takeAnyReg(); + RegisterID cxReg = tempRegs.takeAnyReg().reg(); #else RegisterID cxReg = Registers::ArgReg0; #endif masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), cxReg); - /* Compute vp. */ + /* + * Compute vp. This will always be at the same offset from fp for a + * given callsite, regardless of any dynamically computed argc, + * so get that offset from the active call. + */ #ifdef JS_CPU_X86 RegisterID vpReg = t0; #else RegisterID vpReg = Registers::ArgReg2; #endif + uint32_t vpOffset = (uint32_t) ((char *) args.base() - (char *) f.fp()); + masm.addPtr(Imm32(vpOffset), JSFrameReg, vpReg); + + /* Compute argc. */ MaybeRegisterID argcReg; - if (ic.frameSize.isStatic()) { - uint32 vpOffset = sizeof(StackFrame) + (vp - f.regs.fp()->slots()) * sizeof(Value); - masm.addPtr(Imm32(vpOffset), JSFrameReg, vpReg); - } else { - argcReg = tempRegs.takeAnyReg(); - masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), argcReg.reg()); - masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.sp)), vpReg); - - /* vpOff = (argc + 2) * sizeof(Value) */ - RegisterID vpOff = tempRegs.takeAnyReg(); - masm.move(argcReg.reg(), vpOff); - masm.add32(Imm32(2), vpOff); /* callee, this */ - JS_STATIC_ASSERT(sizeof(Value) == 8); - masm.lshift32(Imm32(3), vpOff); - masm.subPtr(vpOff, vpReg); - - tempRegs.putReg(vpOff); + if (!ic.frameSize.isStatic()) { + argcReg = tempRegs.takeAnyReg().reg(); + masm.load32(FrameAddress(VMFrame::offsetOfDynamicArgc()), argcReg.reg()); } /* Mark vp[1] as magic for |new|. */ - if (callingNew) { - Value v; - v.setMagicWithObjectOrNullPayload(NULL); - masm.storeValue(v, Address(vpReg, sizeof(Value))); - } + if (callingNew) + masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(vpReg, sizeof(Value))); + masm.restoreStackBase(); masm.setupABICall(Registers::NormalCall, 3); masm.storeArg(2, vpReg); if (ic.frameSize.isStatic()) - masm.storeArg(1, Imm32(ic.frameSize.staticArgc())); + masm.storeArg(1, ImmIntPtr(intptr_t(ic.frameSize.staticArgc()))); else masm.storeArg(1, argcReg.reg()); masm.storeArg(0, cxReg); @@ -905,63 +909,67 @@ class CallCompiler : public BaseCompiler /* * Call RegExp.test instead of exec if the result will not be used or - * will only be used to test for existence. + * will only be used to test for existence. Note that this will not + * break inferred types for the call's result and any subsequent test, + * as RegExp.exec has a type handler with unknown result. */ - if (native == js_regexp_exec && !CallResultEscapes(f.regs.pc)) - native = js_regexp_test; + if (native == regexp_exec && !CallResultEscapes(f.pc())) + native = regexp_test; masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, native), false); - Jump hasException = masm.branchTest32(Assembler::Zero, Registers::ReturnReg, - Registers::ReturnReg); - - - Jump done = masm.jump(); - - /* Move JaegerThrowpoline into register for very far jump on x64. */ - hasException.linkTo(masm.label(), &masm); - masm.throwInJIT(); - - LinkerHelper linker(masm); - JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_NativeStub); - if (!ep) + NativeStubLinker::FinalJump done; + if (!NativeStubEpilogue(f, masm, &done, initialFrameDepth, vpOffset, MaybeRegisterID(), MaybeRegisterID())) + return false; + NativeStubLinker linker(masm, f.chunk(), f.regs.pc, done); + if (!linker.init(f.cx)) THROWV(true); - ic.fastGuardedNative = obj; - - if (!linker.verifyRange(jit)) { - disable(jit); + if (!linker.verifyRange(f.chunk())) { + disable(); return true; } - linker.link(done, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset)); + linker.patchJump(ic.slowPathStart.labelAtOffset(ic.slowJoinOffset)); + + ic.fastGuardedNative = fun; + linker.link(funGuard, ic.slowPathStart); - JSC::CodeLocationLabel cs = linker.finalize(); + JSC::CodeLocationLabel start = linker.finalize(f); - JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%d bytes)\n", - cs.executableAddress(), masm.size()); + JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%lu bytes)\n", + start.executableAddress(), (unsigned long) masm.size()); - Repatcher repatch(jit); - repatch.relink(ic.funJump, cs); + Repatcher repatch(f.chunk()); + repatch.relink(ic.funJump, start); return true; } void *update() { - JITScript *jit = f.jit(); + RecompilationMonitor monitor(cx); + + bool lowered = ic.frameSize.lowered(f.pc()); + JS_ASSERT_IF(lowered, !callingNew); stubs::UncachedCallResult ucr; if (callingNew) stubs::UncachedNewHelper(f, ic.frameSize.staticArgc(), &ucr); else - stubs::UncachedCallHelper(f, ic.frameSize.getArgc(f), &ucr); + stubs::UncachedCallHelper(f, ic.frameSize.getArgc(f), lowered, &ucr); + + // Watch out in case the IC was invalidated by a recompilation on the calling + // script. This can happen either if the callee is executed or if it compiles + // and the compilation has a static overflow. + if (monitor.recompiled()) + return ucr.codeAddr; // If the function cannot be jitted (generally unjittable or empty script), // patch this site to go to a slow path always. if (!ucr.codeAddr) { if (ucr.unjittable) - disable(jit); + disable(); return NULL; } @@ -969,10 +977,8 @@ class CallCompiler : public BaseCompiler JS_ASSERT(fun); JSScript *script = fun->script(); JS_ASSERT(script); - JSObject *callee = ucr.callee; - JS_ASSERT(callee); - uint32 flags = callingNew ? StackFrame::CONSTRUCTING : 0; + uint32_t flags = callingNew ? StackFrame::CONSTRUCTING : 0; if (!ic.hit) { ic.hit = true; @@ -980,23 +986,23 @@ class CallCompiler : public BaseCompiler } if (!ic.frameSize.isStatic() || ic.frameSize.staticArgc() != fun->nargs) { - if (!generateFullCallStub(jit, script, flags)) + if (!generateFullCallStub(script, flags)) THROWV(NULL); } else { - if (!ic.fastGuardedObject && patchInlinePath(jit, script, callee)) { + if (!ic.fastGuardedObject && patchInlinePath(script, fun)) { // Nothing, done. } else if (ic.fastGuardedObject && !ic.hasJsFunCheck && !ic.fastGuardedNative && - ic.fastGuardedObject->getFunctionPrivate() == fun) { + ic.fastGuardedObject->toFunction()->script() == fun->script()) { /* * Note: Multiple "function guard" stubs are not yet * supported, thus the fastGuardedNative check. */ - if (!generateStubForClosures(jit, callee)) + if (!generateStubForClosures(fun)) THROWV(NULL); } else { - if (!generateFullCallStub(jit, script, flags)) + if (!generateFullCallStub(script, flags)) THROWV(NULL); } } @@ -1019,58 +1025,30 @@ ic::New(VMFrame &f, CallICInfo *ic) return cc.update(); } -void JS_FASTCALL +void * JS_FASTCALL ic::NativeCall(VMFrame &f, CallICInfo *ic) { CallCompiler cc(f, *ic, false); if (!cc.generateNativeStub()) stubs::SlowCall(f, ic->frameSize.getArgc(f)); + return NULL; } -void JS_FASTCALL +void * JS_FASTCALL ic::NativeNew(VMFrame &f, CallICInfo *ic) { CallCompiler cc(f, *ic, true); if (!cc.generateNativeStub()) stubs::SlowNew(f, ic->frameSize.staticArgc()); -} - -static const unsigned MANY_ARGS = 1024; - -static bool -BumpStackFull(VMFrame &f, uintN inc) -{ - /* If we are not passing many args, treat this as a normal call. */ - if (inc < MANY_ARGS) { - if (f.regs.sp + inc < f.stackLimit) - return true; - StackSpace &space = f.cx->stack.space(); - return space.bumpLimitWithinQuota(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit); - } - - /* - * The purpose of f.stackLimit is to catch over-recursion based on - * assumptions about the average frame size. 'apply' with a large number of - * arguments breaks these assumptions and can result in premature "out of - * script quota" errors. Normally, apply will go through js::Invoke, which - * effectively starts a fresh stackLimit. Here, we bump f.stackLimit, - * if necessary, to allow for this 'apply' call, and a reasonable number of - * subsequent calls, to succeed without hitting the stackLimit. In theory, - * this a recursive chain containing apply to circumvent the stackLimit. - * However, since each apply call must consume at least MANY_ARGS slots, - * this sequence will quickly reach the end of the stack and OOM. - */ - StackSpace &space = f.cx->stack.space(); - return space.bumpLimit(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit); + return NULL; } static JS_ALWAYS_INLINE bool BumpStack(VMFrame &f, uintN inc) { - /* Fast path BumpStackFull. */ - if (inc < MANY_ARGS && f.regs.sp + inc < f.stackLimit) + if (f.regs.sp + inc < f.stackLimit) return true; - return BumpStackFull(f, inc); + return f.cx->stack.space().tryBumpLimit(f.cx, f.regs.sp, inc, &f.stackLimit); } /* @@ -1083,6 +1061,7 @@ JSBool JS_FASTCALL ic::SplatApplyArgs(VMFrame &f) { JSContext *cx = f.cx; + JS_ASSERT(!f.regs.inlined()); JS_ASSERT(GET_ARGC(f.regs.pc) == 2); /* @@ -1098,7 +1077,7 @@ ic::SplatApplyArgs(VMFrame &f) */ if (f.u.call.lazyArgsObj) { Value *vp = f.regs.sp - 3; - JS_ASSERT(JS_CALLEE(cx, vp).toObject().getFunctionPrivate()->u.n.native == js_fun_apply); + JS_ASSERT(JS_CALLEE(cx, vp).toObject().toFunction()->u.n.native == js_fun_apply); StackFrame *fp = f.regs.fp(); if (!fp->hasOverriddenArgs()) { @@ -1120,10 +1099,14 @@ ic::SplatApplyArgs(VMFrame &f) if (!js_GetLengthProperty(cx, aobj, &length)) THROWV(false); - /* Step 6 */ - JS_ASSERT(length <= JS_ARGS_LENGTH_MAX); - n = length; + /* Step 6. */ + if (length > StackSpace::ARGS_LENGTH_MAX) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_APPLY_ARGS); + THROWV(false); + } + n = length; if (!BumpStack(f, n)) THROWV(false); @@ -1148,7 +1131,7 @@ ic::SplatApplyArgs(VMFrame &f) } Value *vp = f.regs.sp - 4; - JS_ASSERT(JS_CALLEE(cx, vp).toObject().getFunctionPrivate()->u.n.native == js_fun_apply); + JS_ASSERT(JS_CALLEE(cx, vp).toObject().toFunction()->u.n.native == js_fun_apply); /* * This stub should mimic the steps taken by js_fun_apply. Step 1 and part @@ -1174,185 +1157,90 @@ ic::SplatApplyArgs(VMFrame &f) if (!js_GetLengthProperty(cx, aobj, &length)) THROWV(false); - JS_ASSERT(!JS_ON_TRACE(cx)); - /* Step 6. */ - uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX)); + if (length > StackSpace::ARGS_LENGTH_MAX) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_APPLY_ARGS); + THROWV(false); + } - intN delta = n - 1; + intN delta = length - 1; if (delta > 0 && !BumpStack(f, delta)) THROWV(false); f.regs.sp += delta; /* Steps 7-8. */ - if (!GetElements(cx, aobj, n, f.regs.sp - n)) + if (!GetElements(cx, aobj, length, f.regs.sp - length)) THROWV(false); - f.u.call.dynamicArgc = n; + f.u.call.dynamicArgc = length; return true; } void -JITScript::purgeMICs() +ic::GenerateArgumentCheckStub(VMFrame &f) { - if (!nGetGlobalNames || !nSetGlobalNames) - return; + JS_ASSERT(f.cx->typeInferenceEnabled()); - Repatcher repatch(this); + JITScript *jit = f.jit(); + StackFrame *fp = f.fp(); + JSFunction *fun = fp->fun(); + JSScript *script = fun->script(); - ic::GetGlobalNameIC *getGlobalNames_ = getGlobalNames(); - for (uint32 i = 0; i < nGetGlobalNames; i++) { - ic::GetGlobalNameIC &ic = getGlobalNames_[i]; - JSC::CodeLocationDataLabel32 label = ic.fastPathStart.dataLabel32AtOffset(ic.shapeOffset); - repatch.repatch(label, int(INVALID_SHAPE)); - } + if (jit->argsCheckPool) + jit->resetArgsCheck(); - ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames(); - for (uint32 i = 0; i < nSetGlobalNames; i++) { - ic::SetGlobalNameIC &ic = setGlobalNames_[i]; - ic.patchInlineShapeGuard(repatch, int32(INVALID_SHAPE)); + Assembler masm; + Vector mismatches(f.cx); - if (ic.hasExtraStub) { - Repatcher repatcher(ic.extraStub); - ic.patchExtraShapeGuard(repatcher, int32(INVALID_SHAPE)); - } + if (!f.fp()->isConstructing()) { + types::TypeSet *types = types::TypeScript::ThisTypes(script); + Address address(JSFrameReg, StackFrame::offsetOfThis(fun)); + if (!masm.generateTypeCheck(f.cx, address, types, &mismatches)) + return; } -} - -void -ic::PurgeMICs(JSContext *cx, JSScript *script) -{ - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - if (script->jitNormal) - script->jitNormal->purgeMICs(); - if (script->jitCtor) - script->jitCtor->purgeMICs(); -} -void -JITScript::nukeScriptDependentICs() -{ - if (!nCallICs) - return; - - Repatcher repatcher(this); - - ic::CallICInfo *callICs_ = callICs(); - for (uint32 i = 0; i < nCallICs; i++) { - ic::CallICInfo &ic = callICs_[i]; - if (!ic.fastGuardedObject) - continue; - repatcher.repatch(ic.funGuard, NULL); - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.releasePool(CallICInfo::Pool_ClosureStub); - ic.fastGuardedObject = NULL; - ic.hasJsFunCheck = false; + for (unsigned i = 0; i < fun->nargs; i++) { + types::TypeSet *types = types::TypeScript::ArgTypes(script, i); + Address address(JSFrameReg, StackFrame::offsetOfFormalArg(fun, i)); + if (!masm.generateTypeCheck(f.cx, address, types, &mismatches)) + return; } -} - -void -JITScript::sweepCallICs(JSContext *cx, bool purgeAll) -{ - Repatcher repatcher(this); - - /* - * If purgeAll is set, purge stubs in the script except those covered by PurgePICs - * (which is always called during GC). We want to remove references which can keep - * alive pools that we are trying to destroy (see JSCompartment::sweep). - */ - - ic::CallICInfo *callICs_ = callICs(); - for (uint32 i = 0; i < nCallICs; i++) { - ic::CallICInfo &ic = callICs_[i]; - - /* - * If the object is unreachable, we're guaranteed not to be currently - * executing a stub generated by a guard on that object. This lets us - * precisely GC call ICs while keeping the identity guard safe. - */ - bool fastFunDead = ic.fastGuardedObject && - (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedObject)); - bool nativeDead = ic.fastGuardedNative && - (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative)); - /* - * There are three conditions where we need to relink: - * (1) purgeAll is true. - * (2) The native is dead, since it always has a stub. - * (3) The fastFun is dead *and* there is a closure stub. - * - * Note although both objects can be non-NULL, there can only be one - * of [closure, native] stub per call IC. - */ - if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) { - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.hit = false; - } - - if (fastFunDead) { - repatcher.repatch(ic.funGuard, NULL); - ic.releasePool(CallICInfo::Pool_ClosureStub); - ic.hasJsFunCheck = false; - ic.fastGuardedObject = NULL; - } + Jump done = masm.jump(); - if (nativeDead) { - ic.releasePool(CallICInfo::Pool_NativeStub); - ic.fastGuardedNative = NULL; - } + LinkerHelper linker(masm, JSC::METHOD_CODE); + JSC::ExecutablePool *ep = linker.init(f.cx); + if (!ep) + return; + jit->argsCheckPool = ep; - if (purgeAll) { - ic.releasePool(CallICInfo::Pool_ScriptStub); - JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset); - JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); - repatcher.relink(oolJump, icCall); - } + if (!linker.verifyRange(f.chunk())) { + jit->resetArgsCheck(); + return; } - if (purgeAll) { - /* Purge ICs generating stubs into execPools. */ - uint32 released = 0; + for (unsigned i = 0; i < mismatches.length(); i++) + linker.link(mismatches[i], jit->argsCheckStub); + linker.link(done, jit->argsCheckFallthrough); - ic::EqualityICInfo *equalityICs_ = equalityICs(); - for (uint32 i = 0; i < nEqualityICs; i++) { - ic::EqualityICInfo &ic = equalityICs_[i]; - if (!ic.generated) - continue; + JSC::CodeLocationLabel cs = linker.finalize(f); - JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality)); - repatcher.relink(ic.stubCall, fptr); - repatcher.relink(ic.jumpToStub, ic.stubEntry); + JaegerSpew(JSpew_PICs, "generated ARGS CHECK stub %p (%lu bytes)\n", + cs.executableAddress(), (unsigned long)masm.size()); - ic.generated = false; - released++; - } - - ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames(); - for (uint32 i = 0; i < nSetGlobalNames; i ++) { - ic::SetGlobalNameIC &ic = setGlobalNames_[i]; - if (!ic.hasExtraStub) - continue; - repatcher.relink(ic.fastPathStart.jumpAtOffset(ic.inlineShapeJump), ic.slowPathStart); - ic.hasExtraStub = false; - released++; - } - - JS_ASSERT(released == execPools.length()); - for (uint32 i = 0; i < released; i++) - execPools[i]->release(); - execPools.clear(); - } + Repatcher repatch(f.chunk()); + repatch.relink(jit->argsCheckJump, cs); } void -ic::SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll) +JITScript::resetArgsCheck() { - if (script->jitNormal) - script->jitNormal->sweepCallICs(cx, purgeAll); - if (script->jitCtor) - script->jitCtor->sweepCallICs(cx, purgeAll); + argsCheckPool->release(); + argsCheckPool = NULL; + + Repatcher repatch(chunk(script->code)); + repatch.relink(argsCheckJump, argsCheckStub); } #endif /* JS_MONOIC */ diff --git a/deps/mozjs/js/src/methodjit/MonoIC.h b/deps/mozjs/js/src/methodjit/MonoIC.h index ec33db7e7bd..71aec95d361 100644 --- a/deps/mozjs/js/src/methodjit/MonoIC.h +++ b/deps/mozjs/js/src/methodjit/MonoIC.h @@ -53,10 +53,10 @@ namespace mjit { class FrameSize { - uint32 frameDepth_ : 16; - uint32 argc_; + uint32_t frameDepth_ : 16; + uint32_t argc_; public: - void initStatic(uint32 frameDepth, uint32 argc) { + void initStatic(uint32_t frameDepth, uint32_t argc) { JS_ASSERT(frameDepth > 0); frameDepth_ = frameDepth; argc_ = argc; @@ -75,19 +75,37 @@ class FrameSize return frameDepth_ == 0; } - uint32 staticLocalSlots() const { + uint32_t staticLocalSlots() const { JS_ASSERT(isStatic()); return frameDepth_; } - uint32 staticArgc() const { + uint32_t staticArgc() const { JS_ASSERT(isStatic()); return argc_; } - uint32 getArgc(VMFrame &f) const { + uint32_t getArgc(VMFrame &f) const { return isStatic() ? staticArgc() : f.u.call.dynamicArgc; } + + bool lowered(jsbytecode *pc) const { + return isDynamic() || staticArgc() != GET_ARGC(pc); + } + + RejoinState rejoinState(jsbytecode *pc, bool native) { + if (isStatic()) { + if (staticArgc() == GET_ARGC(pc)) + return native ? REJOIN_NATIVE : REJOIN_CALL_PROLOGUE; + JS_ASSERT(staticArgc() == GET_ARGC(pc) - 1); + return native ? REJOIN_NATIVE_LOWERED : REJOIN_CALL_PROLOGUE_LOWERED_CALL; + } + return native ? REJOIN_NATIVE_LOWERED : REJOIN_CALL_PROLOGUE_LOWERED_APPLY; + } + + bool lowered(jsbytecode *pc) { + return !isStatic() || staticArgc() != GET_ARGC(pc); + } }; namespace ic { @@ -108,9 +126,8 @@ struct GlobalNameIC * of this, x86 is the only platform which requires non-trivial patching * code. */ - int32 loadStoreOffset : 15; - int32 shapeOffset : 15; - bool usePropertyCache : 1; + int32_t loadStoreOffset : 15; + int32_t shapeOffset : 15; }; struct GetGlobalNameIC : public GlobalNameIC @@ -125,46 +142,23 @@ struct SetGlobalNameIC : public GlobalNameIC JSC::JITCode extraStub; /* SET only, if we had to generate an out-of-line path. */ - int32 inlineShapeJump : 10; /* Offset into inline path for shape jump. */ - int32 extraShapeGuard : 6; /* Offset into stub for shape guard. */ + int32_t inlineShapeJump : 10; /* Offset into inline path for shape jump. */ + int32_t extraShapeGuard : 6; /* Offset into stub for shape guard. */ bool objConst : 1; /* True if the object is constant. */ RegisterID objReg : 5; /* Register for object, if objConst is false. */ RegisterID shapeReg : 5; /* Register for shape; volatile. */ bool hasExtraStub : 1; /* Extra stub is preset. */ - int32 fastRejoinOffset : 16; /* Offset from fastPathStart to rejoin. */ - int32 extraStoreOffset : 16; /* Offset into store code. */ + int32_t fastRejoinOffset : 16; /* Offset from fastPathStart to rejoin. */ + int32_t extraStoreOffset : 16; /* Offset into store code. */ /* SET only. */ ValueRemat vr; /* RHS value. */ - void patchInlineShapeGuard(Repatcher &repatcher, int32 shape); - void patchExtraShapeGuard(Repatcher &repatcher, int32 shape); + void patchInlineShapeGuard(Repatcher &repatcher, const Shape *shape); + void patchExtraShapeGuard(Repatcher &repatcher, const Shape *shape); }; -struct TraceICInfo { - TraceICInfo() {} - - JSC::CodeLocationLabel stubEntry; - JSC::CodeLocationLabel jumpTarget; - JSC::CodeLocationJump traceHint; - JSC::CodeLocationJump slowTraceHint; -#ifdef DEBUG - jsbytecode *jumpTargetPC; -#endif - - /* This data is used by the tracing JIT. */ - void *traceData; - uintN traceEpoch; - uint32 loopCounter; - uint32 loopCounterStart; - - bool initialized : 1; - bool hasSlowTraceHint : 1; -}; - -static const uint16 BAD_TRACEIC_INDEX = (uint16)0xffff; - void JS_FASTCALL GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic); void JS_FASTCALL SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic); @@ -191,10 +185,12 @@ JSBool JS_FASTCALL Equality(VMFrame &f, ic::EqualityICInfo *ic); struct CallICInfo { typedef JSC::MacroAssembler::RegisterID RegisterID; + /* Linked list entry for all ICs guarding on the same JIT entry point in fastGuardedObject. */ + JSCList links; + enum PoolIndex { Pool_ScriptStub, Pool_ClosureStub, - Pool_NativeStub, Total_Pools }; @@ -204,8 +200,8 @@ struct CallICInfo { JSObject *fastGuardedObject; JSObject *fastGuardedNative; - /* PC at the call site. */ - jsbytecode *pc; + /* Return site for scripted calls at this site, with PC and inlining state. */ + CallSite *call; FrameSize frameSize; @@ -219,41 +215,40 @@ struct CallICInfo { JSC::CodeLocationJump funJump; /* Offset to inline scripted call, from funGuard. */ - uint32 hotJumpOffset : 16; - uint32 joinPointOffset : 16; + uint32_t hotJumpOffset : 16; + uint32_t joinPointOffset : 16; /* Out of line slow call. */ - uint32 oolCallOffset : 16; + uint32_t oolCallOffset : 16; /* Jump to patch for out-of-line scripted calls. */ - uint32 oolJumpOffset : 16; + uint32_t oolJumpOffset : 16; /* Label for out-of-line call to IC function. */ - uint32 icCallOffset : 16; + uint32_t icCallOffset : 16; /* Offset for deep-fun check to rejoin at. */ - uint32 hotPathOffset : 16; + uint32_t hotPathOffset : 16; /* Join point for all slow call paths. */ - uint32 slowJoinOffset : 16; + uint32_t slowJoinOffset : 16; RegisterID funObjReg : 5; - RegisterID funPtrReg : 5; bool hit : 1; bool hasJsFunCheck : 1; + bool typeMonitored : 1; inline void reset() { fastGuardedObject = NULL; fastGuardedNative = NULL; hit = false; hasJsFunCheck = false; - pools[0] = pools[1] = pools[2] = NULL; + PodArrayZero(pools); } inline void releasePools() { releasePool(Pool_ScriptStub); releasePool(Pool_ClosureStub); - releasePool(Pool_NativeStub); } inline void releasePool(PoolIndex index) { @@ -262,16 +257,23 @@ struct CallICInfo { pools[index] = NULL; } } + + inline void purgeGuardedObject() { + JS_ASSERT(fastGuardedObject); + releasePool(CallICInfo::Pool_ClosureStub); + hasJsFunCheck = false; + fastGuardedObject = NULL; + JS_REMOVE_LINK(&links); + } }; void * JS_FASTCALL New(VMFrame &f, ic::CallICInfo *ic); void * JS_FASTCALL Call(VMFrame &f, ic::CallICInfo *ic); -void JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); -void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); JSBool JS_FASTCALL SplatApplyArgs(VMFrame &f); -void PurgeMICs(JSContext *cx, JSScript *script); -void SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll); +void GenerateArgumentCheckStub(VMFrame &f); } /* namespace ic */ } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/NunboxAssembler.h b/deps/mozjs/js/src/methodjit/NunboxAssembler.h index fbac37f9909..1153c886667 100644 --- a/deps/mozjs/js/src/methodjit/NunboxAssembler.h +++ b/deps/mozjs/js/src/methodjit/NunboxAssembler.h @@ -52,7 +52,7 @@ namespace mjit { struct ImmTag : JSC::MacroAssembler::Imm32 { ImmTag(JSValueTag mask) - : Imm32(int32(mask)) + : Imm32(int32_t(mask)) { } }; @@ -60,12 +60,14 @@ struct ImmType : ImmTag { ImmType(JSValueType type) : ImmTag(JSVAL_TYPE_TO_TAG(type)) - { } + { + JS_ASSERT(type > JSVAL_TYPE_DOUBLE); + } }; struct ImmPayload : JSC::MacroAssembler::Imm32 { - ImmPayload(uint32 payload) + ImmPayload(uint32_t payload) : Imm32(payload) { } }; @@ -74,11 +76,11 @@ class NunboxAssembler : public JSC::MacroAssembler { public: #ifdef IS_BIG_ENDIAN - static const uint32 PAYLOAD_OFFSET = 4; - static const uint32 TAG_OFFSET = 0; + static const uint32_t PAYLOAD_OFFSET = 4; + static const uint32_t TAG_OFFSET = 0; #else - static const uint32 PAYLOAD_OFFSET = 0; - static const uint32 TAG_OFFSET = 4; + static const uint32_t PAYLOAD_OFFSET = 0; + static const uint32_t TAG_OFFSET = 4; #endif public: @@ -100,7 +102,7 @@ class NunboxAssembler : public JSC::MacroAssembler return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET); } - void loadInlineSlot(RegisterID objReg, uint32 slot, + void loadInlineSlot(RegisterID objReg, uint32_t slot, RegisterID typeReg, RegisterID dataReg) { Address address(objReg, JSObject::getFixedSlotOffset(slot)); if (objReg == typeReg) { @@ -161,13 +163,16 @@ class NunboxAssembler : public JSC::MacroAssembler } void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { - jsval_layout jv; - jv.asBits = JSVAL_BITS(Jsvalify(val)); - + jsval_layout jv = JSVAL_TO_IMPL(val); move(ImmTag(jv.s.tag), type); move(Imm32(jv.s.payload.u32), payload); } + void loadValuePayload(const Value &val, RegisterID payload) { + jsval_layout jv = JSVAL_TO_IMPL(val); + move(Imm32(jv.s.payload.u32), payload); + } + /* * Load a (64b) js::Value from 'address' into 'type' and 'payload', and * return a label which can be used by @@ -202,6 +207,12 @@ class NunboxAssembler : public JSC::MacroAssembler JS_ASSERT(differenceBetween(start, load) == 0); (void) load; return start; +#elif defined JS_CPU_MIPS + /* + * On MIPS there are LUI/ORI to patch. + */ + load64WithPatch(address, treg, dreg, TAG_OFFSET, PAYLOAD_OFFSET); + return start; #endif } @@ -227,6 +238,12 @@ class NunboxAssembler : public JSC::MacroAssembler return start; #elif defined JS_CPU_ARM || defined JS_CPU_SPARC return store64WithAddressOffsetPatch(treg, dreg, address); +#elif defined JS_CPU_MIPS + /* + * On MIPS there are LUI/ORI to patch. + */ + store64WithPatch(address, treg, dreg, TAG_OFFSET, PAYLOAD_OFFSET); + return start; #endif } @@ -243,13 +260,18 @@ class NunboxAssembler : public JSC::MacroAssembler return start; #elif defined JS_CPU_ARM || defined JS_CPU_SPARC return store64WithAddressOffsetPatch(type, dreg, address); +#elif defined JS_CPU_MIPS + /* + * On MIPS there are LUI/ORI to patch. + */ + store64WithPatch(address, type, dreg, TAG_OFFSET, PAYLOAD_OFFSET); + return start; #endif } /* Overloaded for storing constant type and data. */ DataLabel32 storeValueWithAddressOffsetPatch(const Value &v, Address address) { - jsval_layout jv; - jv.asBits = JSVAL_BITS(Jsvalify(v)); + jsval_layout jv = JSVAL_TO_IMPL(v); ImmTag type(jv.s.tag); Imm32 payload(jv.s.payload.u32); DataLabel32 start = dataLabel32(); @@ -263,11 +285,18 @@ class NunboxAssembler : public JSC::MacroAssembler return start; #elif defined JS_CPU_ARM || defined JS_CPU_SPARC return store64WithAddressOffsetPatch(type, payload, address); +#elif defined JS_CPU_MIPS + /* + * On MIPS there are LUI/ORI to patch. + */ + store64WithPatch(address, type, payload, TAG_OFFSET, PAYLOAD_OFFSET); + return start; #endif } /* Overloaded for store with value remat info. */ DataLabel32 storeValueWithAddressOffsetPatch(const ValueRemat &vr, Address address) { + JS_ASSERT(!vr.isFPRegister()); if (vr.isConstant()) { return storeValueWithAddressOffsetPatch(vr.value(), address); } else if (vr.isTypeKnown()) { @@ -286,9 +315,7 @@ class NunboxAssembler : public JSC::MacroAssembler */ template Label storeValue(const Value &v, T address) { - jsval_layout jv; - jv.asBits = JSVAL_BITS(Jsvalify(v)); - + jsval_layout jv = JSVAL_TO_IMPL(v); store32(ImmTag(jv.s.tag), tagOf(address)); Label l = label(); store32(Imm32(jv.s.payload.u32), payloadOf(address)); @@ -311,6 +338,10 @@ class NunboxAssembler : public JSC::MacroAssembler Label storeValue(const ValueRemat &vr, T address) { if (vr.isConstant()) { return storeValue(vr.value(), address); + } else if (vr.isFPRegister()) { + Label l = label(); + storeDouble(vr.fpReg(), address); + return l; } else { if (vr.isTypeKnown()) storeTypeTag(ImmType(vr.knownType()), address); @@ -331,8 +362,8 @@ class NunboxAssembler : public JSC::MacroAssembler loadPtr(payloadOf(privAddr), to); } - void loadObjPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); + void loadObjPrivate(RegisterID base, RegisterID to, uint32_t nfixed) { + Address priv(base, JSObject::getPrivateDataOffset(nfixed)); loadPtr(priv, to); } @@ -388,6 +419,14 @@ class NunboxAssembler : public JSC::MacroAssembler return branch32(cond, tagOf(address), ImmTag(JSVAL_TAG_OBJECT)); } + Jump testGCThing(RegisterID reg) { + return branch32(AboveOrEqual, reg, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET)); + } + + Jump testGCThing(Address address) { + return branch32(AboveOrEqual, tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET)); + } + Jump testDouble(Condition cond, RegisterID reg) { Condition opcond; if (cond == Equal) @@ -422,6 +461,13 @@ class NunboxAssembler : public JSC::MacroAssembler return branch32(cond, tagOf(address), ImmTag(JSVAL_TAG_STRING)); } + void compareValue(Address one, Address two, RegisterID T0, RegisterID T1, + Vector *mismatches) { + loadValueAsComponents(one, T0, T1); + mismatches->append(branch32(NotEqual, T0, tagOf(two))); + mismatches->append(branch32(NotEqual, T1, payloadOf(two))); + } + #ifdef JS_CPU_X86 void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg) { if (MacroAssemblerX86Common::getSSEState() >= HasSSE4_1) { @@ -429,8 +475,8 @@ class NunboxAssembler : public JSC::MacroAssembler m_assembler.pinsrd_rr(hi, fpReg); } else { m_assembler.movd_rr(lo, fpReg); - m_assembler.movd_rr(hi, FPRegisters::Temp0); - m_assembler.unpcklps_rr(FPRegisters::Temp0, fpReg); + m_assembler.movd_rr(hi, Registers::FPConversionTemp); + m_assembler.unpcklps_rr(Registers::FPConversionTemp, fpReg); } } #endif @@ -445,6 +491,15 @@ class NunboxAssembler : public JSC::MacroAssembler m_assembler.movd_rr(srcDest, typeReg); #elif defined JS_CPU_SPARC breakDoubleTo32(srcDest, typeReg, dataReg); +#elif defined JS_CPU_ARM + // Yes, we are backwards from SPARC. + fastStoreDouble(srcDest, dataReg, typeReg); +#elif defined JS_CPU_MIPS +#if defined(IS_LITTLE_ENDIAN) + fastStoreDouble(srcDest, dataReg, typeReg); +#else + fastStoreDouble(srcDest, typeReg, dataReg); +#endif #else JS_NOT_REACHED("implement this - push double, pop pop is easiest"); #endif @@ -456,9 +511,17 @@ class NunboxAssembler : public JSC::MacroAssembler } template - Jump fastArrayLoadSlot(T address, RegisterID typeReg, RegisterID dataReg) { - loadTypeTag(address, typeReg); - Jump notHole = branch32(Equal, typeReg, ImmType(JSVAL_TYPE_MAGIC)); + Jump fastArrayLoadSlot(T address, bool holeCheck, + MaybeRegisterID typeReg, RegisterID dataReg) + { + Jump notHole; + if (typeReg.isSet()) { + loadTypeTag(address, typeReg.reg()); + if (holeCheck) + notHole = branch32(Equal, typeReg.reg(), ImmType(JSVAL_TYPE_MAGIC)); + } else if (holeCheck) { + notHole = branch32(Equal, tagOf(address), ImmType(JSVAL_TYPE_MAGIC)); + } loadPayload(address, dataReg); return notHole; } diff --git a/deps/mozjs/js/src/methodjit/PolyIC.cpp b/deps/mozjs/js/src/methodjit/PolyIC.cpp index 4dc89e59631..3d06b51a205 100644 --- a/deps/mozjs/js/src/methodjit/PolyIC.cpp +++ b/deps/mozjs/js/src/methodjit/PolyIC.cpp @@ -49,11 +49,11 @@ #include "jsatominlines.h" #include "jsobjinlines.h" #include "jsscopeinlines.h" -#include "jspropertycache.h" -#include "jspropertycacheinlines.h" #include "jsinterpinlines.h" #include "jsautooplen.h" +#include "vm/ScopeObject-inl.h" + #if defined JS_POLYIC using namespace js; @@ -66,52 +66,7 @@ typedef JSC::MacroAssembler::Jump Jump; typedef JSC::MacroAssembler::Imm32 Imm32; /* Rough over-estimate of how much memory we need to unprotect. */ -static const uint32 INLINE_PATH_LENGTH = 64; - -/* Static initializer to prime platforms that use constant offsets for ICs. */ -#ifndef JS_HAS_IC_LABELS -ICOffsetInitializer::ICOffsetInitializer() -{ - { - GetPropLabels &labels = PICInfo::getPropLabels_; -#if defined JS_CPU_X86 - labels.dslotsLoadOffset = -15; - labels.inlineShapeOffset = 6; - labels.stubShapeJumpOffset = 12; - labels.inlineValueLoadOffset = -12; -#endif - } - { - SetPropLabels &labels = PICInfo::setPropLabels_; -#if defined JS_CPU_X86 - labels.inlineShapeDataOffset = 6; - /* Store w/ address offset patch is two movs. */ - labels.inlineShapeJumpOffset = 12; - labels.stubShapeJumpOffset = 12; -#endif - } - { - BindNameLabels &labels = PICInfo::bindNameLabels_; -#if defined JS_CPU_X86 - labels.inlineJumpOffset = 10; - labels.stubJumpOffset = 5; -#endif - } - { - ScopeNameLabels &labels = PICInfo::scopeNameLabels_; -#if defined JS_CPU_X86 - labels.inlineJumpOffset = 5; - labels.stubJumpOffset = 5; -#endif - } -} - -ICOffsetInitializer s_ICOffsetInitializer; -GetPropLabels PICInfo::getPropLabels_; -SetPropLabels PICInfo::setPropLabels_; -BindNameLabels PICInfo::bindNameLabels_; -ScopeNameLabels PICInfo::scopeNameLabels_; -#endif +static const uint32_t INLINE_PATH_LENGTH = 64; // Helper class to simplify LinkBuffer usage in PIC stub generators. // This guarantees correct OOM and refcount handling for buffers while they @@ -122,7 +77,7 @@ class PICLinker : public LinkerHelper public: PICLinker(Assembler &masm, ic::BasePolyIC &ic) - : LinkerHelper(masm), ic(ic) + : LinkerHelper(masm, JSC::METHOD_CODE), ic(ic) { } bool init(JSContext *cx) { @@ -146,20 +101,21 @@ class PICStubCompiler : public BaseCompiler JSScript *script; ic::PICInfo &pic; void *stub; + uint32_t gcNumber; public: + bool canCallHook; + PICStubCompiler(const char *type, VMFrame &f, JSScript *script, ic::PICInfo &pic, void *stub) - : BaseCompiler(f.cx), type(type), f(f), script(script), pic(pic), stub(stub) + : BaseCompiler(f.cx), type(type), f(f), script(script), pic(pic), stub(stub), + gcNumber(f.cx->runtime->gcNumber), canCallHook(pic.canCallHook) { } - bool isCallOp() const { - if (pic.kind == ic::PICInfo::CALL) - return true; - return !!(js_CodeSpec[pic.op].format & JOF_CALLOP); - } - LookupStatus error() { - disable("error"); + /* + * N.B. Do not try to disable the IC, we do not want to guard on + * whether the IC has been recompiled when propagating errors. + */ return Lookup_Error; } @@ -172,30 +128,82 @@ class PICStubCompiler : public BaseCompiler } LookupStatus disable(JSContext *cx, const char *reason) { - return pic.disable(cx, reason, stub); + return pic.disable(f, reason, stub); + } + + LookupStatus disable(VMFrame &f, const char *reason) { + return pic.disable(f, reason, stub); + } + + bool hadGC() { + return gcNumber != f.cx->runtime->gcNumber; } protected: void spew(const char *event, const char *op) { #ifdef JS_METHODJIT_SPEW JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n", - type, event, op, script->filename, - js_FramePCToLineNumber(cx, f.fp())); + type, event, op, script->filename, CurrentLine(cx)); #endif } }; +static bool +GeneratePrototypeGuards(JSContext *cx, Vector &mismatches, Assembler &masm, + JSObject *obj, JSObject *holder, + JSC::MacroAssembler::RegisterID objReg, + JSC::MacroAssembler::RegisterID scratchReg) +{ + typedef JSC::MacroAssembler::Address Address; + typedef JSC::MacroAssembler::AbsoluteAddress AbsoluteAddress; + typedef JSC::MacroAssembler::ImmPtr ImmPtr; + typedef JSC::MacroAssembler::Jump Jump; + + if (obj->hasUncacheableProto()) { + masm.loadPtr(Address(objReg, JSObject::offsetOfType()), scratchReg); + Jump j = masm.branchPtr(Assembler::NotEqual, + Address(scratchReg, offsetof(types::TypeObject, proto)), + ImmPtr(obj->getProto())); + if (!mismatches.append(j)) + return false; + } + + JSObject *pobj = obj->getProto(); + while (pobj != holder) { + if (pobj->hasUncacheableProto()) { + Jump j; + if (pobj->hasSingletonType()) { + types::TypeObject *type = pobj->getType(cx); + j = masm.branchPtr(Assembler::NotEqual, + AbsoluteAddress(&type->proto), + ImmPtr(pobj->getProto()), + scratchReg); + } else { + j = masm.branchPtr(Assembler::NotEqual, + AbsoluteAddress(pobj->addressOfType()), + ImmPtr(pobj->type()), + scratchReg); + } + if (!mismatches.append(j)) + return false; + } + pobj = pobj->getProto(); + } + + return true; +} + class SetPropCompiler : public PICStubCompiler { JSObject *obj; - JSAtom *atom; + PropertyName *name; int lastStubSecondShapeGuard; public: - SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, + SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, PropertyName *name, VoidStubPIC stub) : PICStubCompiler("setprop", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)), - obj(obj), atom(atom), lastStubSecondShapeGuard(pic.secondShapeGuard) + obj(obj), name(name), lastStubSecondShapeGuard(pic.secondShapeGuard) { } static void reset(Repatcher &repatcher, ic::PICInfo &pic) @@ -203,7 +211,7 @@ class SetPropCompiler : public PICStubCompiler SetPropLabels &labels = pic.setPropLabels(); repatcher.repatchLEAToLoadPtr(labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr)); repatcher.repatch(labels.getInlineShapeData(pic.fastPathStart, pic.shapeGuard), - int32(INVALID_SHAPE)); + NULL); repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)), pic.slowPathStart); @@ -211,38 +219,37 @@ class SetPropCompiler : public PICStubCompiler repatcher.relink(pic.slowPathCall, target); } - LookupStatus patchInline(const Shape *shape, bool inlineSlot) + LookupStatus patchInline(const Shape *shape) { JS_ASSERT(!pic.inlinePathPatched); JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress()); - Repatcher repatcher(f.jit()); + Repatcher repatcher(f.chunk()); SetPropLabels &labels = pic.setPropLabels(); - int32 offset; - if (inlineSlot) { + int32_t offset; + if (obj->isFixedSlot(shape->slot())) { CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr); repatcher.repatchLoadPtrToLEA(istr); - // + // // We've patched | mov dslots, [obj + DSLOTS_OFFSET] // To: | lea fslots, [obj + DSLOTS_OFFSET] // // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(JSObject::getFixedSlotOffset(0)) - - int32(offsetof(JSObject, slots)); + int32_t diff = int32_t(JSObject::getFixedSlotOffset(0)) - + int32_t(JSObject::offsetOfSlots()); JS_ASSERT(diff != 0); - offset = (int32(shape->slot) * sizeof(Value)) + diff; + offset = (int32_t(shape->slot()) * sizeof(Value)) + diff; } else { - offset = shape->slot * sizeof(Value); + offset = obj->dynamicSlotIndex(shape->slot()) * sizeof(Value); } repatcher.repatch(labels.getInlineShapeData(pic.fastPathStart, pic.shapeGuard), - obj->shape()); - repatcher.patchAddressOffsetForValueStore(labels.getInlineValueStore(pic.fastPathRejoin, - pic.u.vr), + obj->lastProperty()); + repatcher.patchAddressOffsetForValueStore(labels.getInlineValueStore(pic.fastPathRejoin), offset, pic.u.vr.isTypeKnown()); pic.inlinePathPatched = true; @@ -256,7 +263,7 @@ class SetPropCompiler : public PICStubCompiler void patchPreviousToHere(CodeLocationLabel cs) { - Repatcher repatcher(pic.lastCodeBlock(f.jit())); + Repatcher repatcher(pic.lastCodeBlock(f.chunk())); CodeLocationLabel label = pic.lastPathStart(); // Patch either the inline fast path or a generated stub. The stub @@ -272,8 +279,11 @@ class SetPropCompiler : public PICStubCompiler repatcher.relink(label.jumpAtOffset(secondGuardOffset), cs); } - LookupStatus generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot) + LookupStatus generateStub(const Shape *initialShape, const Shape *shape, bool adding) { + if (hadGC()) + return Lookup_Uncacheable; + /* Exits to the slow path. */ Vector slowExits(cx); Vector otherGuards(cx); @@ -287,14 +297,32 @@ class SetPropCompiler : public PICStubCompiler } Label start = masm.label(); - Jump shapeGuard = masm.branch32FixedLength(Assembler::NotEqual, pic.shapeReg, - Imm32(initialShape)); + Jump shapeGuard = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(initialShape)); Label stubShapeJumpLabel = masm.label(); pic.setPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel); - JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->getClass() == &js_CallClass); + if (pic.typeMonitored) { + /* + * Inference does not know the type of the object being updated, + * and we need to make sure that the updateMonitoredTypes() call + * covers this stub, i.e. we will be writing to an object with the + * same type. Add a type guard in addition to the shape guard. + * Note: it is possible that this test gets a spurious hit if the + * object has a lazy type, but in such cases no analyzed scripts + * depend on the object and we will reconstruct its type from the + * value being written here. + */ + Jump typeGuard = masm.branchPtr(Assembler::NotEqual, + Address(pic.objReg, JSObject::offsetOfType()), + ImmPtr(obj->getType(cx))); + if (!otherGuards.append(typeGuard)) + return error(); + } + + JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->isCall()); MaybeJump skipOver; @@ -302,11 +330,17 @@ class SetPropCompiler : public PICStubCompiler JS_ASSERT(shape->hasSlot()); pic.shapeRegHasBaseShape = false; + if (!GeneratePrototypeGuards(cx, otherGuards, masm, obj, NULL, + pic.objReg, pic.shapeReg)) { + return error(); + } + /* Emit shape guards for the object's prototype chain. */ JSObject *proto = obj->getProto(); RegisterID lastReg = pic.objReg; while (proto) { - masm.loadPtr(Address(lastReg, offsetof(JSObject, proto)), pic.shapeReg); + masm.loadPtr(Address(lastReg, JSObject::offsetOfType()), pic.shapeReg); + masm.loadPtr(Address(pic.shapeReg, offsetof(types::TypeObject, proto)), pic.shapeReg); Jump protoGuard = masm.guardShape(pic.shapeReg, proto); if (!otherGuards.append(protoGuard)) return error(); @@ -321,7 +355,7 @@ class SetPropCompiler : public PICStubCompiler * We already know it is a function, so test the payload. */ JS_ASSERT(shape->isMethod()); - JSObject *funobj = &shape->methodObject(); + JSObject *funobj = obj->nativeGetMethod(shape); if (pic.u.vr.isConstant()) { JS_ASSERT(funobj == &pic.u.vr.value().toObject()); } else { @@ -332,60 +366,30 @@ class SetPropCompiler : public PICStubCompiler } } - if (inlineSlot) { + if (obj->isFixedSlot(shape->slot())) { Address address(pic.objReg, - JSObject::getFixedSlotOffset(shape->slot)); + JSObject::getFixedSlotOffset(shape->slot())); masm.storeValue(pic.u.vr, address); } else { - /* Check capacity. */ - Address capacity(pic.objReg, offsetof(JSObject, capacity)); - masm.load32(capacity, pic.shapeReg); - Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg, - Imm32(shape->slot)); - if (!slowExits.append(overCapacity)) - return error(); - - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.shapeReg); - Address address(pic.shapeReg, shape->slot * sizeof(Value)); + /* + * Note: the guard on the initial shape determines the object's + * number of fixed slots and slot span, which in turn determine + * the number of dynamic slots allocated for the object. + * We don't need to check capacity here. + */ + masm.loadPtr(Address(pic.objReg, JSObject::offsetOfSlots()), pic.shapeReg); + Address address(pic.shapeReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value)); masm.storeValue(pic.u.vr, address); } - uint32 newShape = obj->shape(); - JS_ASSERT(newShape != initialShape); + JS_ASSERT(shape == obj->lastProperty()); + JS_ASSERT(shape != initialShape); /* Write the object's new shape. */ - masm.storePtr(ImmPtr(shape), Address(pic.objReg, offsetof(JSObject, lastProp))); - masm.store32(Imm32(newShape), Address(pic.objReg, offsetof(JSObject, objShape))); - - /* If this is a method shape, update the object's flags. */ - if (shape->isMethod()) { - Address flags(pic.objReg, offsetof(JSObject, flags)); - - /* Use shapeReg to load, bitwise-or, and store flags. */ - masm.load32(flags, pic.shapeReg); - masm.or32(Imm32(JSObject::METHOD_BARRIER), pic.shapeReg); - masm.store32(pic.shapeReg, flags); - } + masm.storePtr(ImmPtr(shape), Address(pic.objReg, JSObject::offsetOfShape())); } else if (shape->hasDefaultSetter()) { - Address address(pic.objReg, JSObject::getFixedSlotOffset(shape->slot)); - if (!inlineSlot) { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - address = Address(pic.objReg, shape->slot * sizeof(Value)); - } - - // If the scope is branded, or has a method barrier. It's now necessary - // to guard that we're not overwriting a function-valued property. - if (obj->brandedOrHasMethodBarrier()) { - masm.loadTypeTag(address, pic.shapeReg); - Jump skip = masm.testObject(Assembler::NotEqual, pic.shapeReg); - masm.loadPayload(address, pic.shapeReg); - Jump rebrand = masm.testFunction(Assembler::Equal, pic.shapeReg); - if (!slowExits.append(rebrand)) - return error(); - skip.linkTo(masm.label(), &masm); - pic.shapeRegHasBaseShape = false; - } - + JS_ASSERT(!shape->isMethod()); + Address address = masm.objPropAddress(obj, pic.objReg, shape->slot()); masm.storeValue(pic.u.vr, address); } else { // \ / In general, two function objects with different JSFunctions @@ -395,11 +399,11 @@ class SetPropCompiler : public PICStubCompiler // \\ V and getters, and // \===/ 2. arguments and locals have different getters // then we can rely on fun->nargs remaining invariant. - JSFunction *fun = obj->getCallObjCalleeFunction(); - uint16 slot = uint16(shape->shortid); + JSFunction *fun = obj->asCall().getCalleeFunction(); + uint16_t slot = uint16_t(shape->shortid()); /* Guard that the call object has a frame. */ - masm.loadObjPrivate(pic.objReg, pic.shapeReg); + masm.loadObjPrivate(pic.objReg, pic.shapeReg, obj->numFixedSlots()); Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg); { @@ -414,10 +418,11 @@ class SetPropCompiler : public PICStubCompiler { if (shape->setterOp() == SetCallVar) slot += fun->nargs; - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); - masm.storeValue(pic.u.vr, dslot); + slot += CallObject::RESERVED_SLOTS; + Address address = masm.objPropAddress(obj, pic.objReg, slot); + + masm.storeValue(pic.u.vr, address); } pic.shapeRegHasBaseShape = false; @@ -436,12 +441,14 @@ class SetPropCompiler : public PICStubCompiler pic.secondShapeGuard = 0; } + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } @@ -453,10 +460,10 @@ class SetPropCompiler : public PICStubCompiler buffer.link(done, pic.fastPathRejoin); if (skipOver.isSet()) buffer.link(skipOver.get(), pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); - JaegerSpew(JSpew_PICs, "generate setprop stub %p %d %d at %p\n", + CodeLocationLabel cs = buffer.finalize(f); + JaegerSpew(JSpew_PICs, "generate setprop stub %p %p %d at %p\n", (void*)&pic, - initialShape, + (void*)initialShape, pic.stubsGenerated, cs.executableAddress()); @@ -474,6 +481,24 @@ class SetPropCompiler : public PICStubCompiler return Lookup_Cacheable; } + bool updateMonitoredTypes() + { + JS_ASSERT(pic.typeMonitored); + + RecompilationMonitor monitor(cx); + jsid id = ATOM_TO_JSID(name); + + if (!obj->getType(cx)->unknownProperties()) { + types::AutoEnterTypeInference enter(cx); + types::TypeSet *types = obj->getType(cx)->getProperty(cx, types::MakeTypeId(cx, id), true); + if (!types) + return false; + pic.rhsTypes->addSubset(cx, types); + } + + return !monitor.recompiled(); + } + LookupStatus update() { JS_ASSERT(pic.hit); @@ -482,22 +507,27 @@ class SetPropCompiler : public PICStubCompiler return disable("dense array"); if (!obj->isNative()) return disable("non-native"); + if (obj->watched()) + return disable("watchpoint"); Class *clasp = obj->getClass(); - if (clasp->setProperty != StrictPropertyStub) + if (clasp->setProperty != JS_StrictPropertyStub) return disable("set property hook"); if (clasp->ops.lookupProperty) return disable("ops lookup property hook"); if (clasp->ops.setProperty) return disable("ops set property hook"); - jsid id = ATOM_TO_JSID(atom); - JSObject *holder; JSProperty *prop = NULL; - if (!obj->lookupProperty(cx, id, &holder, &prop)) + + /* lookupProperty can trigger recompilations. */ + RecompilationMonitor monitor(cx); + if (!obj->lookupProperty(cx, name, &holder, &prop)) return error(); + if (monitor.recompiled()) + return Lookup_Uncacheable; /* If the property exists but is on a prototype, treat as addprop. */ if (prop && holder != obj) { @@ -525,21 +555,25 @@ class SetPropCompiler : public PICStubCompiler if (!obj->isExtensible()) return disable("not extensible"); - if (clasp->addProperty != PropertyStub) + if (clasp->addProperty != JS_PropertyStub) return disable("add property hook"); if (clasp->ops.defineProperty) return disable("ops define property hook"); - uint32 index; - if (js_IdIsIndex(id, &index)) - return disable("index"); - - uint32 initialShape = obj->shape(); + /* + * When adding a property we need to check shapes along the entire + * prototype chain to watch for an added setter. + */ + JSObject *proto = obj; + while (proto) { + if (!proto->isNative()) + return disable("non-native proto"); + proto = proto->getProto(); + } - if (!obj->ensureClassReservedSlots(cx)) - return error(); + const Shape *initialShape = obj->lastProperty(); + uint32_t slots = obj->numDynamicSlots(); - uint32 slots = obj->numSlots(); uintN flags = 0; PropertyOp getter = clasp->getProperty; @@ -548,11 +582,10 @@ class SetPropCompiler : public PICStubCompiler return disable("can't have method barrier"); JSObject *funobj = &f.regs.sp[-1].toObject(); - if (funobj != GET_FUNCTION_PRIVATE(cx, funobj)) + if (funobj->toFunction()->isClonedMethod()) return disable("mismatched function"); flags |= Shape::METHOD; - getter = CastAsPropertyOp(funobj); } /* @@ -560,14 +593,16 @@ class SetPropCompiler : public PICStubCompiler * populate the slot to satisfy the method invariant (in case we * hit an early return below). */ - id = js_CheckForStringIndex(id); const Shape *shape = - obj->putProperty(cx, id, getter, clasp->setProperty, + obj->putProperty(cx, name, getter, clasp->setProperty, SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0); if (!shape) return error(); if (flags & Shape::METHOD) - obj->nativeSetSlot(shape->slot, f.regs.sp[-1]); + obj->nativeSetSlot(shape->slot(), f.regs.sp[-1]); + + if (monitor.recompiled()) + return Lookup_Uncacheable; /* * Test after calling putProperty since it can switch obj into @@ -594,10 +629,13 @@ class SetPropCompiler : public PICStubCompiler * usually be a slowdown even if there *are* other shapes that * don't realloc. */ - if (obj->numSlots() != slots) + if (obj->numDynamicSlots() != slots) return disable("insufficient slot capacity"); - return generateStub(initialShape, shape, true, !obj->hasSlotsArray()); + if (pic.typeMonitored && !updateMonitoredTypes()) + return Lookup_Uncacheable; + + return generateStub(initialShape, shape, true); } const Shape *shape = (const Shape *) prop; @@ -605,10 +643,14 @@ class SetPropCompiler : public PICStubCompiler return disable("set method on non-method shape"); if (!shape->writable()) return disable("readonly"); + if (shape->isMethod()) + return disable("method"); if (shape->hasDefaultSetter()) { if (!shape->hasSlot()) return disable("invalid slot"); + if (pic.typeMonitored && !updateMonitoredTypes()) + return Lookup_Uncacheable; } else { if (shape->hasSetterValue()) return disable("scripted setter"); @@ -616,17 +658,45 @@ class SetPropCompiler : public PICStubCompiler shape->setterOp() != SetCallVar) { return disable("setter"); } + JS_ASSERT(obj->isCall()); + if (pic.typeMonitored) { + /* + * Update the types of the locals/args in the script according + * to the possible RHS types of the assignment. Note that the + * shape guards we have performed do not by themselves + * guarantee that future call objects hit will be for the same + * script. We also depend on the fact that the scope chains hit + * at the same bytecode are all isomorphic: the same scripts, + * in the same order (though the properties on their call + * objects may differ due to eval(), DEFFUN, etc.). + */ + RecompilationMonitor monitor(cx); + JSFunction *fun = obj->asCall().getCalleeFunction(); + JSScript *script = fun->script(); + uint16_t slot = uint16_t(shape->shortid()); + if (!script->ensureHasTypes(cx)) + return error(); + { + types::AutoEnterTypeInference enter(cx); + if (shape->setterOp() == SetCallArg) + pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot)); + else + pic.rhsTypes->addSubset(cx, types::TypeScript::LocalTypes(script, slot)); + } + if (monitor.recompiled()) + return Lookup_Uncacheable; + } } JS_ASSERT(obj == holder); if (!pic.inlinePathPatched && - !obj->brandedOrHasMethodBarrier() && shape->hasDefaultSetter() && + !pic.typeMonitored && !obj->isDenseArray()) { - return patchInline(shape, !obj->hasSlotsArray()); - } + return patchInline(shape); + } - return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray()); + return generateStub(obj->lastProperty(), shape, false); } }; @@ -634,8 +704,13 @@ static bool IsCacheableProtoChain(JSObject *obj, JSObject *holder) { while (obj != holder) { + /* + * We cannot assume that we find the holder object on the prototype + * chain and must check for null proto. The prototype chain can be + * altered during the lookupProperty call. + */ JSObject *proto = obj->getProto(); - if (!proto->isNative()) + if (!proto || !proto->isNative()) return false; obj = proto; } @@ -643,31 +718,38 @@ IsCacheableProtoChain(JSObject *obj, JSObject *holder) } template -struct GetPropertyHelper { +struct GetPropHelper { // These fields are set in the constructor and describe a property lookup. JSContext *cx; JSObject *obj; - JSAtom *atom; + PropertyName *name; IC ⁣ + VMFrame &f; // These fields are set by |bind| and |lookup|. After a call to either // function, these are set exactly as they are in JSOP_GETPROP or JSOP_NAME. JSObject *aobj; JSObject *holder; JSProperty *prop; - + // This field is set by |bind| and |lookup| only if they returned // Lookup_Cacheable, otherwise it is NULL. const Shape *shape; - GetPropertyHelper(JSContext *cx, JSObject *obj, JSAtom *atom, IC &ic) - : cx(cx), obj(obj), atom(atom), ic(ic), holder(NULL), prop(NULL), shape(NULL) + GetPropHelper(JSContext *cx, JSObject *obj, PropertyName *name, IC &ic, VMFrame &f) + : cx(cx), obj(obj), name(name), ic(ic), f(f), holder(NULL), prop(NULL), shape(NULL) { } public: LookupStatus bind() { - if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &holder, &prop)) + RecompilationMonitor monitor(cx); + JSObject *scopeChain = cx->stack.currentScriptedScopeChain(); + if (js_CodeSpec[*f.pc()].format & JOF_GNAME) + scopeChain = &scopeChain->global(); + if (!FindProperty(cx, name, scopeChain, &obj, &holder, &prop)) return ic.error(cx); + if (monitor.recompiled()) + return Lookup_Uncacheable; if (!prop) return ic.disable(cx, "lookup failed"); if (!obj->isNative()) @@ -681,25 +763,48 @@ struct GetPropertyHelper { LookupStatus lookup() { JSObject *aobj = js_GetProtoIfDenseArray(obj); if (!aobj->isNative()) - return ic.disable(cx, "non-native"); - if (!aobj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop)) + return ic.disable(f, "non-native"); + + RecompilationMonitor monitor(cx); + if (!aobj->lookupProperty(cx, name, &holder, &prop)) return ic.error(cx); + if (monitor.recompiled()) + return Lookup_Uncacheable; + if (!prop) - return ic.disable(cx, "lookup failed"); + return ic.disable(f, "lookup failed"); if (!IsCacheableProtoChain(obj, holder)) - return ic.disable(cx, "non-native holder"); + return ic.disable(f, "non-native holder"); shape = (const Shape *)prop; return Lookup_Cacheable; } LookupStatus testForGet() { if (!shape->hasDefaultGetter()) { - if (!shape->isMethod()) - return ic.disable(cx, "getter"); - if (!ic.isCallOp()) - return ic.disable(cx, "method valued shape"); + if (shape->isMethod()) { + if (JSOp(*f.pc()) != JSOP_CALLPROP) + return ic.disable(f, "method valued shape"); + } else { + if (shape->hasGetterValue()) + return ic.disable(f, "getter value shape"); + if (shape->hasSlot() && holder != obj) + return ic.disable(f, "slotful getter hook through prototype"); + if (!ic.canCallHook) + return ic.disable(f, "can't call getter hook"); + if (f.regs.inlined()) { + /* + * As with native stubs, getter hook stubs can't be + * generated for inline frames. Mark the inner function + * as uninlineable and recompile. + */ + f.script()->uninlineable = true; + MarkTypeObjectFlags(cx, f.script()->function(), + types::OBJECT_FLAG_UNINLINEABLE); + return Lookup_Uncacheable; + } + } } else if (!shape->hasSlot()) { - return ic.disable(cx, "no slot"); + return ic.disable(f, "no slot"); } return Lookup_Cacheable; @@ -716,16 +821,16 @@ struct GetPropertyHelper { class GetPropCompiler : public PICStubCompiler { JSObject *obj; - JSAtom *atom; + PropertyName *name; int lastStubSecondShapeGuard; public: - GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, + GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, PropertyName *name, VoidStubPIC stub) - : PICStubCompiler(pic.kind == ic::PICInfo::CALL ? "callprop" : "getprop", f, script, pic, + : PICStubCompiler("getprop", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)), obj(obj), - atom(atom), + name(name), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -737,8 +842,7 @@ class GetPropCompiler : public PICStubCompiler { GetPropLabels &labels = pic.getPropLabels(); repatcher.repatchLEAToLoadPtr(labels.getDslotsLoad(pic.fastPathRejoin)); - repatcher.repatch(labels.getInlineShapeData(pic.getFastShapeGuard()), - int32(INVALID_SHAPE)); + repatcher.repatch(labels.getInlineShapeData(pic.getFastShapeGuard()), NULL); repatcher.relink(labels.getInlineShapeJump(pic.getFastShapeGuard()), pic.slowPathStart); if (pic.hasTypeCheck()) { @@ -746,20 +850,9 @@ class GetPropCompiler : public PICStubCompiler repatcher.relink(labels.getInlineTypeJump(pic.fastPathStart), pic.getSlowTypeCheck()); } - VoidStubPIC stub; - switch (pic.kind) { - case ic::PICInfo::GET: - stub = ic::GetProp; - break; - case ic::PICInfo::CALL: - stub = ic::CallProp; - break; - default: - JS_NOT_REACHED("invalid pic kind for GetPropCompiler::reset"); - return; - } + JS_ASSERT(pic.kind == ic::PICInfo::GET); - FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); + FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::GetProp)); repatcher.relink(pic.slowPathCall, target); } @@ -767,25 +860,25 @@ class GetPropCompiler : public PICStubCompiler { Assembler masm; - Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass()); + Jump notArgs = masm.guardShape(pic.objReg, obj); - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), - pic.objReg); + masm.load32(Address(pic.objReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), pic.objReg); masm.move(pic.objReg, pic.shapeReg); Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, - Imm32(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); - masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg); - + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT)); + masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), pic.objReg); + masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } @@ -793,7 +886,7 @@ class GetPropCompiler : public PICStubCompiler buffer.link(overridden, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel start = buffer.finalize(); + CodeLocationLabel start = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generate args length stub at %p\n", start.executableAddress()); @@ -809,21 +902,24 @@ class GetPropCompiler : public PICStubCompiler Assembler masm; masm.loadObjClass(pic.objReg, pic.shapeReg); - Jump isDense = masm.testClass(Assembler::Equal, pic.shapeReg, &js_ArrayClass); - Jump notArray = masm.testClass(Assembler::NotEqual, pic.shapeReg, &js_SlowArrayClass); + Jump isDense = masm.testClass(Assembler::Equal, pic.shapeReg, &ArrayClass); + Jump notArray = masm.testClass(Assembler::NotEqual, pic.shapeReg, &SlowArrayClass); isDense.linkTo(masm.label(), &masm); - masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg); + masm.loadPtr(Address(pic.objReg, JSObject::offsetOfElements()), pic.objReg); + masm.load32(Address(pic.objReg, ObjectElements::offsetOfLength()), pic.objReg); Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX)); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } @@ -831,7 +927,7 @@ class GetPropCompiler : public PICStubCompiler buffer.link(oob, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel start = buffer.finalize(); + CodeLocationLabel start = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generate array length stub at %p\n", start.executableAddress()); @@ -846,28 +942,29 @@ class GetPropCompiler : public PICStubCompiler { Assembler masm; - Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass()); - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - masm.loadPayload(Address(pic.objReg, JSObject::JSSLOT_PRIMITIVE_THIS * sizeof(Value)), - pic.objReg); + Jump notStringObj = masm.guardShape(pic.objReg, obj); + + masm.loadPayload(Address(pic.objReg, JSObject::getPrimitiveThisOffset()), pic.objReg); masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg); masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } buffer.link(notStringObj, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel start = buffer.finalize(); + CodeLocationLabel start = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generate string object length stub at %p\n", start.executableAddress()); @@ -878,20 +975,30 @@ class GetPropCompiler : public PICStubCompiler return Lookup_Cacheable; } - LookupStatus generateStringCallStub() + LookupStatus generateStringPropertyStub() { - JS_ASSERT(pic.hasTypeCheck()); - JS_ASSERT(pic.kind == ic::PICInfo::CALL); + if (!f.fp()->script()->hasGlobal()) + return disable("String.prototype without compile-and-go global"); + + RecompilationMonitor monitor(f.cx); + + JSObject *obj = f.fp()->scopeChain().global().getOrCreateStringPrototype(f.cx); + if (!obj) + return error(); - if (!f.fp()->script()->compileAndGo) - return disable("String.prototype without compile-and-go"); + if (monitor.recompiled()) + return Lookup_Uncacheable; - GetPropertyHelper getprop(cx, obj, atom, *this); + GetPropHelper getprop(cx, obj, name, *this, f); LookupStatus status = getprop.lookupAndTest(); if (status != Lookup_Cacheable) return status; if (getprop.obj != getprop.holder) return disable("proto walk on String.prototype"); + if (!getprop.shape->hasDefaultGetterOrIsMethod()) + return disable("getter hook on String.prototype"); + if (hadGC()) + return Lookup_Uncacheable; Assembler masm; @@ -899,18 +1006,6 @@ class GetPropCompiler : public PICStubCompiler Jump notString = masm.branchPtr(Assembler::NotEqual, pic.typeReg(), ImmType(JSVAL_TYPE_STRING)); - /* - * Sink pic.objReg, since we're about to lose it. - * - * Note: This is really hacky, and relies on f.regs.sp being set - * correctly in ic::CallProp. Should we just move the store higher - * up in the fast path, or put this offset in PICInfo? - */ - uint32 thisvOffset = uint32(f.regs.sp - f.fp()->slots()) - 1; - Address thisv(JSFrameReg, sizeof(StackFrame) + thisvOffset * sizeof(Value)); - masm.storeValueFromComponents(ImmType(JSVAL_TYPE_STRING), - pic.objReg, thisv); - /* * Clobber objReg with String.prototype and do some PIC stuff. Well, * really this is now a MIC, except it won't ever be patched, so we @@ -919,18 +1014,20 @@ class GetPropCompiler : public PICStubCompiler */ masm.move(ImmPtr(obj), pic.objReg); masm.loadShape(pic.objReg, pic.shapeReg); - Jump shapeMismatch = masm.branch32(Assembler::NotEqual, pic.shapeReg, - Imm32(obj->shape())); + Jump shapeMismatch = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(obj->lastProperty())); masm.loadObjProp(obj, pic.objReg, getprop.shape, pic.shapeReg, pic.objReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } @@ -938,19 +1035,18 @@ class GetPropCompiler : public PICStubCompiler buffer.link(shapeMismatch, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generate string call stub at %p\n", cs.executableAddress()); /* Patch the type check to jump here. */ if (pic.hasTypeCheck()) { - Repatcher repatcher(f.jit()); + Repatcher repatcher(f.chunk()); repatcher.relink(pic.getPropLabels().getInlineTypeJump(pic.fastPathStart), cs); } /* Disable the PIC so we don't keep generating stubs on the above shape mismatch. */ disable("generated string call stub"); - return Lookup_Cacheable; } @@ -967,24 +1063,26 @@ class GetPropCompiler : public PICStubCompiler masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } buffer.link(notString, pic.getSlowTypeCheck()); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel start = buffer.finalize(); + CodeLocationLabel start = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generate string length stub at %p\n", start.executableAddress()); if (pic.hasTypeCheck()) { - Repatcher repatcher(f.jit()); + Repatcher repatcher(f.chunk()); repatcher.relink(pic.getPropLabels().getInlineTypeJump(pic.fastPathStart), start); } @@ -996,30 +1094,30 @@ class GetPropCompiler : public PICStubCompiler LookupStatus patchInline(JSObject *holder, const Shape *shape) { spew("patch", "inline"); - Repatcher repatcher(f.jit()); + Repatcher repatcher(f.chunk()); GetPropLabels &labels = pic.getPropLabels(); - int32 offset; - if (!holder->hasSlotsArray()) { + int32_t offset; + if (holder->isFixedSlot(shape->slot())) { CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin); repatcher.repatchLoadPtrToLEA(istr); - // + // // We've patched | mov dslots, [obj + DSLOTS_OFFSET] // To: | lea fslots, [obj + DSLOTS_OFFSET] // // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(JSObject::getFixedSlotOffset(0)) - - int32(offsetof(JSObject, slots)); + int32_t diff = int32_t(JSObject::getFixedSlotOffset(0)) - + int32_t(JSObject::offsetOfSlots()); JS_ASSERT(diff != 0); - offset = (int32(shape->slot) * sizeof(Value)) + diff; + offset = (int32_t(shape->slot()) * sizeof(Value)) + diff; } else { - offset = shape->slot * sizeof(Value); + offset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); } - repatcher.repatch(labels.getInlineShapeData(pic.getFastShapeGuard()), obj->shape()); + repatcher.repatch(labels.getInlineShapeData(pic.getFastShapeGuard()), obj->lastProperty()); repatcher.patchAddressOffsetForValueLoad(labels.getValueLoad(pic.fastPathRejoin), offset); pic.inlinePathPatched = true; @@ -1027,6 +1125,92 @@ class GetPropCompiler : public PICStubCompiler return Lookup_Cacheable; } + void generateGetterStub(Assembler &masm, const Shape *shape, + Label start, Vector &shapeMismatches) + { + /* + * Getter hook needs to be called from the stub. The state is fully + * synced and no registers are live except the result registers. + */ + JS_ASSERT(pic.canCallHook); + PropertyOp getter = shape->getterOp(); + + masm.storePtr(ImmPtr((void *) REJOIN_NATIVE_GETTER), + FrameAddress(offsetof(VMFrame, stubRejoin))); + + Registers tempRegs = Registers::tempCallRegMask(); + if (tempRegs.hasReg(Registers::ClobberInCall)) + tempRegs.takeReg(Registers::ClobberInCall); + + /* Get a register to hold obj while we set up the rest of the frame. */ + RegisterID holdObjReg = pic.objReg; + if (tempRegs.hasReg(pic.objReg)) { + tempRegs.takeReg(pic.objReg); + } else { + holdObjReg = tempRegs.takeAnyReg().reg(); + masm.move(pic.objReg, holdObjReg); + } + + RegisterID t0 = tempRegs.takeAnyReg().reg(); + masm.bumpStubCounter(f.script(), f.pc(), t0); + + /* + * Initialize vp, which is either a slot in the object (the holder, + * actually, which must equal the object here) or undefined. + * Use vp == sp (which for CALLPROP will actually be the original + * sp + 1), to avoid clobbering stack values. + */ + int32_t vpOffset = (char *) f.regs.sp - (char *) f.fp(); + if (shape->hasSlot()) { + masm.loadObjProp(obj, holdObjReg, shape, + Registers::ClobberInCall, t0); + masm.storeValueFromComponents(Registers::ClobberInCall, t0, Address(JSFrameReg, vpOffset)); + } else { + masm.storeValue(UndefinedValue(), Address(JSFrameReg, vpOffset)); + } + + int32_t initialFrameDepth = f.regs.sp - f.fp()->slots(); + masm.setupFallibleABICall(cx->typeInferenceEnabled(), f.regs.pc, initialFrameDepth); + + /* Grab cx. */ +#ifdef JS_CPU_X86 + RegisterID cxReg = tempRegs.takeAnyReg().reg(); +#else + RegisterID cxReg = Registers::ArgReg0; +#endif + masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), cxReg); + + /* Grap vp. */ + RegisterID vpReg = t0; + masm.addPtr(Imm32(vpOffset), JSFrameReg, vpReg); + + masm.restoreStackBase(); + masm.setupABICall(Registers::NormalCall, 4); + masm.storeArg(3, vpReg); + masm.storeArg(2, ImmPtr((void *) JSID_BITS(shape->getUserId()))); + masm.storeArg(1, holdObjReg); + masm.storeArg(0, cxReg); + + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, getter), false); + + NativeStubLinker::FinalJump done; + if (!NativeStubEpilogue(f, masm, &done, 0, vpOffset, pic.shapeReg, pic.objReg)) + return; + NativeStubLinker linker(masm, f.chunk(), f.regs.pc, done); + if (!linker.init(f.cx)) + THROW(); + + if (!linker.verifyRange(pic.lastCodeBlock(f.chunk())) || + !linker.verifyRange(f.chunk())) { + disable("code memory is out of range"); + return; + } + + linker.patchJump(pic.fastPathRejoin); + + linkerEpilogue(linker, start, shapeMismatches); + } + LookupStatus generateStub(JSObject *holder, const Shape *shape) { Vector shapeMismatches(cx); @@ -1040,9 +1224,11 @@ class GetPropCompiler : public PICStubCompiler bool setStubShapeOffset = true; if (obj->isDenseArray()) { start = masm.label(); - shapeGuardJump = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass()); + shapeGuardJump = masm.branchPtr(Assembler::NotEqual, + Address(pic.objReg, JSObject::offsetOfShape()), + ImmPtr(obj->lastProperty())); - /* + /* * No need to assert validity of GETPROP_STUB_SHAPE_JUMP in this case: * the IC is disabled after a dense array hit, so no patching can occur. */ @@ -1056,8 +1242,8 @@ class GetPropCompiler : public PICStubCompiler } start = masm.label(); - shapeGuardJump = masm.branch32FixedLength(Assembler::NotEqual, pic.shapeReg, - Imm32(obj->shape())); + shapeGuardJump = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(obj->lastProperty())); } Label stubShapeJumpLabel = masm.label(); @@ -1066,6 +1252,11 @@ class GetPropCompiler : public PICStubCompiler RegisterID holderReg = pic.objReg; if (obj != holder) { + if (!GeneratePrototypeGuards(cx, shapeMismatches, masm, obj, holder, + pic.objReg, pic.shapeReg)) { + return error(); + } + // Bake in the holder identity. Careful not to clobber |objReg|, since we can't remat it. holderReg = pic.shapeReg; masm.move(ImmPtr(holder), holderReg); @@ -1081,26 +1272,45 @@ class GetPropCompiler : public PICStubCompiler pic.secondShapeGuard = 0; } + if (!shape->hasDefaultGetterOrIsMethod()) { + generateGetterStub(masm, shape, start, shapeMismatches); + if (setStubShapeOffset) + pic.getPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel); + return Lookup_Cacheable; + } + /* Load the value out of the object. */ masm.loadObjProp(holder, holderReg, shape, pic.shapeReg, pic.objReg); Jump done = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } + // The final exit jumps to the store-back in the inline stub. + buffer.link(done, pic.fastPathRejoin); + + linkerEpilogue(buffer, start, shapeMismatches); + + if (setStubShapeOffset) + pic.getPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel); + return Lookup_Cacheable; + } + + void linkerEpilogue(LinkerHelper &buffer, Label start, Vector &shapeMismatches) + { // The guard exit jumps to the original slow case. for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj) buffer.link(*pj, pic.slowPathStart); - // The final exit jumps to the store-back in the inline stub. - buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress()); patchPreviousToHere(cs); @@ -1108,20 +1318,15 @@ class GetPropCompiler : public PICStubCompiler pic.stubsGenerated++; pic.updateLastPath(buffer, start); - if (setStubShapeOffset) - pic.getPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel); - if (pic.stubsGenerated == MAX_PIC_STUBS) disable("max stubs reached"); if (obj->isDenseArray()) disable("dense array"); - - return Lookup_Cacheable; } void patchPreviousToHere(CodeLocationLabel cs) { - Repatcher repatcher(pic.lastCodeBlock(f.jit())); + Repatcher repatcher(pic.lastCodeBlock(f.chunk())); CodeLocationLabel label = pic.lastPathStart(); // Patch either the inline fast path or a generated stub. The stub @@ -1132,8 +1337,14 @@ class GetPropCompiler : public PICStubCompiler shapeGuardJumpOffset = pic.getPropLabels().getStubShapeJumpOffset(); else shapeGuardJumpOffset = pic.shapeGuard + pic.getPropLabels().getInlineShapeJumpOffset(); + int secondGuardOffset = getLastStubSecondShapeGuard(); + + JaegerSpew(JSpew_PICs, "Patching previous (%d stubs) (start %p) (offset %d) (second %d)\n", + (int) pic.stubsGenerated, label.executableAddress(), + shapeGuardJumpOffset, secondGuardOffset); + repatcher.relink(label.jumpAtOffset(shapeGuardJumpOffset), cs); - if (int secondGuardOffset = getLastStubSecondShapeGuard()) + if (secondGuardOffset) repatcher.relink(label.jumpAtOffset(secondGuardOffset), cs); } @@ -1141,14 +1352,19 @@ class GetPropCompiler : public PICStubCompiler { JS_ASSERT(pic.hit); - GetPropertyHelper getprop(cx, obj, atom, *this); + GetPropHelper getprop(cx, obj, name, *this, f); LookupStatus status = getprop.lookupAndTest(); if (status != Lookup_Cacheable) return status; + if (hadGC()) + return Lookup_Uncacheable; - if (obj == getprop.holder && !pic.inlinePathPatched) + if (obj == getprop.holder && + getprop.shape->hasDefaultGetterOrIsMethod() && + !pic.inlinePathPatched) { return patchInline(getprop.holder, getprop.shape); - + } + return generateStub(getprop.holder, getprop.shape); } }; @@ -1156,20 +1372,20 @@ class GetPropCompiler : public PICStubCompiler class ScopeNameCompiler : public PICStubCompiler { private: - typedef Vector JumpList; + typedef Vector JumpList; JSObject *scopeChain; - JSAtom *atom; - GetPropertyHelper getprop; + PropertyName *name; + GetPropHelper getprop; ScopeNameCompiler *thisFromCtor() { return this; } void patchPreviousToHere(CodeLocationLabel cs) { ScopeNameLabels & labels = pic.scopeNameLabels(); - Repatcher repatcher(pic.lastCodeBlock(f.jit())); + Repatcher repatcher(pic.lastCodeBlock(f.chunk())); CodeLocationLabel start = pic.lastPathStart(); JSC::CodeLocationJump jump; - + // Patch either the inline fast path or a generated stub. if (pic.stubsGenerated) jump = labels.getStubJump(start); @@ -1192,24 +1408,18 @@ class ScopeNameCompiler : public PICStubCompiler return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); - if (tobj != scopeChain) { - /* scopeChain will never be NULL, but parents can be NULL. */ - Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); - if (!fails.append(j)) - return error(); - } - /* Guard on intervening shapes. */ masm.loadShape(pic.objReg, pic.shapeReg); - Jump j = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(tobj->shape())); + Jump j = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(tobj->lastProperty())); if (!fails.append(j)) return error(); /* Load the next link in the scope chain. */ - Address parent(pic.objReg, offsetof(JSObject, parent)); - masm.loadPtr(parent, pic.objReg); + Address parent(pic.objReg, ScopeObject::offsetOfEnclosingScope()); + masm.loadPayload(parent, pic.objReg); - tobj = tobj->getParent(); + tobj = &tobj->asScope().enclosingScope(); } if (tobj != getprop.holder) @@ -1220,10 +1430,10 @@ class ScopeNameCompiler : public PICStubCompiler public: ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubPIC stub) + PropertyName *name, VoidStubPIC stub) : PICStubCompiler("name", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)), - scopeChain(scopeChain), atom(atom), - getprop(f.cx, NULL, atom, *thisFromCtor()) + scopeChain(scopeChain), name(name), + getprop(f.cx, NULL, name, *thisFromCtor(), f) { } static void reset(Repatcher &repatcher, ic::PICInfo &pic) @@ -1234,7 +1444,18 @@ class ScopeNameCompiler : public PICStubCompiler JSC::CodeLocationJump inlineJump = labels.getInlineJump(pic.fastPathStart); repatcher.relink(inlineJump, pic.slowPathStart); - VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; + VoidStubPIC stub; + switch (pic.kind) { + case ic::PICInfo::NAME: + stub = ic::Name; + break; + case ic::PICInfo::XNAME: + stub = ic::XName; + break; + default: + JS_NOT_REACHED("Invalid pic kind in ScopeNameCompiler::reset"); + return; + } FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); repatcher.relink(pic.slowPathCall, target); } @@ -1250,7 +1471,7 @@ class ScopeNameCompiler : public PICStubCompiler masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); JS_ASSERT(obj == getprop.holder); - JS_ASSERT(getprop.holder == scopeChain->getGlobal()); + JS_ASSERT(getprop.holder == &scopeChain->global()); LookupStatus status = walkScopeChain(masm, fails); if (status != Lookup_Cacheable) @@ -1261,9 +1482,11 @@ class ScopeNameCompiler : public PICStubCompiler if (pic.kind == ic::PICInfo::NAME) finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); masm.loadShape(pic.objReg, pic.shapeReg); - Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(getprop.holder->shape())); + Jump finalShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(getprop.holder->lastProperty())); masm.loadObjProp(obj, pic.objReg, getprop.shape, pic.shapeReg, pic.objReg); + Jump done = masm.jump(); /* All failures flow to here, so there is a common point to patch. */ @@ -1275,18 +1498,20 @@ class ScopeNameCompiler : public PICStubCompiler Label failLabel = masm.label(); Jump failJump = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generated %s global stub at %p\n", type, cs.executableAddress()); spew("NAME stub", "global"); @@ -1310,7 +1535,7 @@ class ScopeNameCompiler : public PICStubCompiler LookupStatus generateCallStub(JSObject *obj) { Assembler masm; - Vector fails(cx); + Vector fails(cx); ScopeNameLabels &labels = pic.scopeNameLabels(); /* For GETXPROP, the object is already in objReg. */ @@ -1318,7 +1543,7 @@ class ScopeNameCompiler : public PICStubCompiler masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); JS_ASSERT(obj == getprop.holder); - JS_ASSERT(getprop.holder != scopeChain->getGlobal()); + JS_ASSERT(getprop.holder != &scopeChain->global()); CallObjPropKind kind; const Shape *shape = getprop.shape; @@ -1339,13 +1564,14 @@ class ScopeNameCompiler : public PICStubCompiler if (pic.kind == ic::PICInfo::NAME) finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); masm.loadShape(pic.objReg, pic.shapeReg); - Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(getprop.holder->shape())); + Jump finalShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(getprop.holder->lastProperty())); /* Get callobj's stack frame. */ - masm.loadObjPrivate(pic.objReg, pic.shapeReg); + masm.loadObjPrivate(pic.objReg, pic.shapeReg, getprop.holder->numFixedSlots()); - JSFunction *fun = getprop.holder->getCallObjCalleeFunction(); - uint16 slot = uint16(shape->shortid); + JSFunction *fun = getprop.holder->asCall().getCalleeFunction(); + uint16_t slot = uint16_t(shape->shortid()); Jump skipOver; Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg); @@ -1362,14 +1588,14 @@ class ScopeNameCompiler : public PICStubCompiler escapedFrame.linkTo(masm.label(), &masm); { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - if (kind == VAR) slot += fun->nargs; - Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); + + slot += CallObject::RESERVED_SLOTS; + Address address = masm.objPropAddress(obj, pic.objReg, slot); /* Safe because type is loaded first. */ - masm.loadValueAsComponents(dslot, pic.shapeReg, pic.objReg); + masm.loadValueAsComponents(address, pic.shapeReg, pic.objReg); } skipOver.linkTo(masm.label(), &masm); @@ -1384,18 +1610,20 @@ class ScopeNameCompiler : public PICStubCompiler Label failLabel = masm.label(); Jump failJump = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generated %s call stub at %p\n", type, cs.executableAddress()); patchPreviousToHere(cs); @@ -1436,20 +1664,20 @@ class ScopeNameCompiler : public PICStubCompiler if (obj != getprop.holder) return disable("property is on proto of a scope object"); - if (obj->getClass() == &js_CallClass) + if (obj->isCall()) return generateCallStub(obj); LookupStatus status = getprop.testForGet(); if (status != Lookup_Cacheable) return status; - if (!obj->getParent()) + if (obj->isGlobal()) return generateGlobalStub(obj); return disable("scope object not handled yet"); } - bool retrieve(Value *vp) + bool retrieve(Value *vp, PICInfo::Kind kind) { JSObject *obj = getprop.obj; JSObject *holder = getprop.holder; @@ -1457,43 +1685,44 @@ class ScopeNameCompiler : public PICStubCompiler if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ - disable("property not found"); - if (pic.kind == ic::PICInfo::NAME) { - JSOp op2 = js_GetOpcode(cx, script, cx->regs().pc + JSOP_NAME_LENGTH); + if (kind == ic::PICInfo::NAME) { + JSOp op2 = JSOp(f.pc()[JSOP_NAME_LENGTH]); if (op2 == JSOP_TYPEOF) { vp->setUndefined(); return true; } } - ReportAtomNotDefined(cx, atom); + ReportAtomNotDefined(cx, name); return false; } // If the property was found, but we decided not to cache it, then // take a slow path and do a full property fetch. - if (!getprop.shape) - return obj->getProperty(cx, ATOM_TO_JSID(atom), vp); + if (!getprop.shape) { + if (!obj->getProperty(cx, name, vp)) + return false; + return true; + } const Shape *shape = getprop.shape; JSObject *normalized = obj; - if (obj->getClass() == &js_WithClass && !shape->hasDefaultGetter()) - normalized = js_UnwrapWithObject(cx, obj); + if (obj->isWith() && !shape->hasDefaultGetter()) + normalized = &obj->asWith().object(); NATIVE_GET(cx, normalized, holder, shape, JSGET_METHOD_BARRIER, vp, return false); - return true; } }; - + class BindNameCompiler : public PICStubCompiler { JSObject *scopeChain; - JSAtom *atom; + PropertyName *name; public: BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubPIC stub) + PropertyName *name, VoidStubPIC stub) : PICStubCompiler("bind", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)), - scopeChain(scopeChain), atom(atom) + scopeChain(scopeChain), name(name) { } static void reset(Repatcher &repatcher, ic::PICInfo &pic) @@ -1512,9 +1741,9 @@ class BindNameCompiler : public PICStubCompiler void patchPreviousToHere(CodeLocationLabel cs) { BindNameLabels &labels = pic.bindNameLabels(); - Repatcher repatcher(pic.lastCodeBlock(f.jit())); + Repatcher repatcher(pic.lastCodeBlock(f.chunk())); JSC::CodeLocationJump jump; - + /* Patch either the inline fast path or a generated stub. */ if (pic.stubsGenerated) jump = labels.getStubJump(pic.lastPathStart()); @@ -1526,32 +1755,29 @@ class BindNameCompiler : public PICStubCompiler LookupStatus generateStub(JSObject *obj) { Assembler masm; - js::Vector fails(cx); + Vector fails(cx); BindNameLabels &labels = pic.bindNameLabels(); /* Guard on the shape of the scope chain. */ masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg); masm.loadShape(pic.objReg, pic.shapeReg); - Jump firstShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, - Imm32(scopeChain->shape())); + Jump firstShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(scopeChain->lastProperty())); /* Walk up the scope chain. */ JSObject *tobj = scopeChain; - Address parent(pic.objReg, offsetof(JSObject, parent)); + Address parent(pic.objReg, ScopeObject::offsetOfEnclosingScope()); while (tobj && tobj != obj) { if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); - masm.loadPtr(parent, pic.objReg); - Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); - if (!fails.append(nullTest)) - return error(); + masm.loadPayload(parent, pic.objReg); masm.loadShape(pic.objReg, pic.shapeReg); - Jump shapeTest = masm.branch32(Assembler::NotEqual, pic.shapeReg, - Imm32(tobj->shape())); + Jump shapeTest = masm.branchPtr(Assembler::NotEqual, pic.shapeReg, + ImmPtr(tobj->lastProperty())); if (!fails.append(shapeTest)) return error(); - tobj = tobj->getParent(); + tobj = &tobj->asScope().enclosingScope(); } if (tobj != obj) return disable("indirect hit"); @@ -1565,18 +1791,20 @@ class BindNameCompiler : public PICStubCompiler Label failLabel = masm.label(); Jump failJump = masm.jump(); + pic.updatePCCounters(f, masm); + PICLinker buffer(masm, pic); if (!buffer.init(cx)) return error(); - if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) || - !buffer.verifyRange(f.jit())) { + if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) || + !buffer.verifyRange(f.chunk())) { return disable("code memory is out of range"); } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress()); patchPreviousToHere(cs); @@ -1593,10 +1821,10 @@ class BindNameCompiler : public PICStubCompiler JSObject *update() { - JS_ASSERT(scopeChain->getParent()); + RecompilationMonitor monitor(cx); - JSObject *obj = js_FindIdentifierBase(cx, scopeChain, ATOM_TO_JSID(atom)); - if (!obj) + JSObject *obj = FindIdentifierBase(cx, scopeChain, name); + if (!obj || monitor.recompiled()) return obj; if (!pic.hit) { @@ -1613,44 +1841,36 @@ class BindNameCompiler : public PICStubCompiler } }; -static void JS_FASTCALL -DisabledLengthIC(VMFrame &f, ic::PICInfo *pic) -{ - stubs::Length(f); -} - static void JS_FASTCALL DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic) { - stubs::GetProp(f); + stubs::GetProp(f, pic->name); } static void JS_FASTCALL -DisabledGetPropICNoCache(VMFrame &f, ic::PICInfo *pic) +DisabledGetPropNoCacheIC(VMFrame &f, ic::PICInfo *pic) { - stubs::GetPropNoCache(f, pic->atom); + stubs::GetPropNoCache(f, pic->name); } -void JS_FASTCALL -ic::GetProp(VMFrame &f, ic::PICInfo *pic) +static inline void +GetPropMaybeCached(VMFrame &f, ic::PICInfo *pic, bool cached) { + VoidStubPIC stub = cached ? DisabledGetPropIC : DisabledGetPropNoCacheIC; + JSScript *script = f.fp()->script(); - JSAtom *atom = pic->atom; - if (atom == f.cx->runtime->atomState.lengthAtom) { - if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledLengthIC); - LookupStatus status = cc.generateStringLengthStub(); - if (status == Lookup_Error) - THROW(); - JSString *str = f.regs.sp[-1].toString(); - f.regs.sp[-1].setInt32(str->length()); + PropertyName *name = pic->name; + if (name == f.cx->runtime->atomState.lengthAtom) { + if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) { + f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs()); return; } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); - if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden()) || + if (obj->isArray() || + (obj->isArguments() && !obj->asArguments().hasOverriddenLength()) || obj->isString()) { - GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC); + GetPropCompiler cc(f, script, obj, *pic, NULL, stub); if (obj->isArray()) { LookupStatus status = cc.generateArrayLengthStub(); if (status == Lookup_Error) @@ -1660,7 +1880,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) LookupStatus status = cc.generateArgsLengthStub(); if (status == Lookup_Error) THROW(); - f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength())); + f.regs.sp[-1].setInt32(int32_t(obj->asArguments().initialLength())); } else if (obj->isString()) { LookupStatus status = cc.generateStringObjLengthStub(); if (status == Lookup_Error) @@ -1671,191 +1891,100 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) return; } } - atom = f.cx->runtime->atomState.lengthAtom; } - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]); + if (f.regs.sp[-1].isString()) { + GetPropCompiler cc(f, script, NULL, *pic, name, stub); + if (name == f.cx->runtime->atomState.lengthAtom) { + LookupStatus status = cc.generateStringLengthStub(); + if (status == Lookup_Error) + THROW(); + JSString *str = f.regs.sp[-1].toString(); + f.regs.sp[-1].setInt32(str->length()); + } else { + LookupStatus status = cc.generateStringPropertyStub(); + if (status == Lookup_Error) + THROW(); + JSObject *obj = ValueToObject(f.cx, f.regs.sp[-1]); + if (!obj) + THROW(); + if (!obj->getProperty(f.cx, name, &f.regs.sp[-1])) + THROW(); + } + return; + } + + RecompilationMonitor monitor(f.cx); + + JSObject *obj = ValueToObject(f.cx, f.regs.sp[-1]); if (!obj) THROW(); - if (pic->shouldUpdate(f.cx)) { - VoidStubPIC stub = pic->usePropCache - ? DisabledGetPropIC - : DisabledGetPropICNoCache; - GetPropCompiler cc(f, script, obj, *pic, atom, stub); - if (!cc.update()) { - cc.disable("error"); + if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) { + GetPropCompiler cc(f, script, obj, *pic, name, stub); + if (!cc.update()) THROW(); - } } Value v; - if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v)) - THROW(); + if (cached) { + if (!GetPropertyOperation(f.cx, f.pc(), f.regs.sp[-1], &v)) + THROW(); + } else { + if (!obj->getProperty(f.cx, name, &v)) + THROW(); + } + f.regs.sp[-1] = v; } -template -static void JS_FASTCALL -DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic) +void JS_FASTCALL +ic::GetProp(VMFrame &f, ic::PICInfo *pic) { - stubs::SetName(f, pic->atom); + GetPropMaybeCached(f, pic, /* cache = */ true); } -template -static void JS_FASTCALL -DisabledSetPropICNoCache(VMFrame &f, ic::PICInfo *pic) +void JS_FASTCALL +ic::GetPropNoCache(VMFrame &f, ic::PICInfo *pic) { - stubs::SetPropNoCache(f, pic->atom); -} - -void JS_FASTCALL -ic::SetProp(VMFrame &f, ic::PICInfo *pic) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - - JSScript *script = f.fp()->script(); - JS_ASSERT(pic->isSet()); - - VoidStubPIC stub = pic->usePropCache - ? STRICT_VARIANT(DisabledSetPropIC) - : STRICT_VARIANT(DisabledSetPropICNoCache); - - // - // Important: We update the PIC before looking up the property so that the - // PIC is updated only if the property already exists. The PIC doesn't try - // to optimize adding new properties; that is for the slow case. - // - // Also note, we can't use SetName for PROPINC PICs because the property - // cache can't handle a GET and SET from the same scripted PC. - if (pic->shouldUpdate(f.cx)) { - - SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); - LookupStatus status = cc.update(); - if (status == Lookup_Error) - THROW(); - } - - stub(f, pic); + GetPropMaybeCached(f, pic, /* cache = */ false); } +template static void JS_FASTCALL -DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic) +DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic) { - stubs::CallProp(f, pic->atom); + stubs::SetName(f, pic->name); } void JS_FASTCALL -ic::CallProp(VMFrame &f, ic::PICInfo *pic) +ic::SetProp(VMFrame &f, ic::PICInfo *pic) { - JSContext *cx = f.cx; - FrameRegs ®s = f.regs; - JSScript *script = f.fp()->script(); + JS_ASSERT(pic->isSet()); - Value lval; - lval = regs.sp[-1]; + VoidStubPIC stub = STRICT_VARIANT(DisabledSetPropIC); - Value objv; - if (lval.isObject()) { - objv = lval; - } else { - JSProtoKey protoKey; - if (lval.isString()) { - protoKey = JSProto_String; - } else if (lval.isNumber()) { - protoKey = JSProto_Number; - } else if (lval.isBoolean()) { - protoKey = JSProto_Boolean; - } else { - JS_ASSERT(lval.isNull() || lval.isUndefined()); - js_ReportIsNullOrUndefined(cx, -1, lval, NULL); - THROW(); - } - JSObject *pobj; - if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) - THROW(); - objv.setObject(*pobj); - } + // Save this in case the compiler triggers a recompilation of this script. + PropertyName *name = pic->name; + VoidStubName nstub = STRICT_VARIANT(stubs::SetName); - JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject()); - Value rval; + RecompilationMonitor monitor(f.cx); - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom); - if (!atom) { - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - const Shape *shape = entry->vword.toShape(); - NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval, - THROW()); - } - regs.sp++; - regs.sp[-2] = rval; - regs.sp[-1] = lval; - } else { - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(pic->atom); - - regs.sp++; - regs.sp[-1].setNull(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!objv.toObject().getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; - } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - } - } + JSObject *obj = ValueToObject(f.cx, f.regs.sp[-2]); + if (!obj) + THROW(); - GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC); - if (lval.isObject()) { - if (pic->shouldUpdate(cx)) { - LookupStatus status = cc.update(); - if (status == Lookup_Error) - THROW(); - } - } else if (lval.isString()) { - LookupStatus status = cc.generateStringCallStub(); + // Note, we can't use SetName for PROPINC PICs because the property + // cache can't handle a GET and SET from the same scripted PC. + if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) { + SetPropCompiler cc(f, script, obj, *pic, name, stub); + LookupStatus status = cc.update(); if (status == Lookup_Error) THROW(); - } else { - cc.disable("non-string primitive"); } -#if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { - regs.sp[-2].setString(pic->atom); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) - THROW(); - } -#endif + nstub(f, name); } static void JS_FASTCALL @@ -1867,7 +1996,7 @@ DisabledNameIC(VMFrame &f, ic::PICInfo *pic) static void JS_FASTCALL DisabledXNameIC(VMFrame &f, ic::PICInfo *pic) { - stubs::GetProp(f); + stubs::GetProp(f, pic->name); } void JS_FASTCALL @@ -1878,14 +2007,14 @@ ic::XName(VMFrame &f, ic::PICInfo *pic) /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, DisabledXNameIC); + ScopeNameCompiler cc(f, script, obj, *pic, pic->name, DisabledXNameIC); LookupStatus status = cc.updateForXName(); if (status == Lookup_Error) THROW(); Value rval; - if (!cc.retrieve(&rval)) + if (!cc.retrieve(&rval, PICInfo::XNAME)) THROW(); f.regs.sp[-1] = rval; } @@ -1895,14 +2024,14 @@ ic::Name(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, DisabledNameIC); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->name, DisabledNameIC); LookupStatus status = cc.updateForName(); if (status == Lookup_Error) THROW(); Value rval; - if (!cc.retrieve(&rval)) + if (!cc.retrieve(&rval, PICInfo::NAME)) THROW(); f.regs.sp[0] = rval; } @@ -1910,13 +2039,7 @@ ic::Name(VMFrame &f, ic::PICInfo *pic) static void JS_FASTCALL DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic) { - stubs::BindName(f); -} - -static void JS_FASTCALL -DisabledBindNameICNoCache(VMFrame &f, ic::PICInfo *pic) -{ - stubs::BindNameNoCache(f, pic->atom); + stubs::BindName(f, pic->name); } void JS_FASTCALL @@ -1924,45 +2047,69 @@ ic::BindName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - VoidStubPIC stub = pic->usePropCache - ? DisabledBindNameIC - : DisabledBindNameICNoCache; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, stub); + VoidStubPIC stub = DisabledBindNameIC; + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->name, stub); JSObject *obj = cc.update(); - if (!obj) { - cc.disable("error"); + if (!obj) THROW(); - } f.regs.sp[0].setObject(*obj); } -bool -BaseIC::isCallOp() -{ - return !!(js_CodeSpec[op].format & JOF_CALLOP); -} - void BaseIC::spew(JSContext *cx, const char *event, const char *message) { #ifdef JS_METHODJIT_SPEW JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n", - js_CodeName[op], event, message, cx->fp()->script()->filename, - js_FramePCToLineNumber(cx, cx->fp())); + js_CodeName[op], event, message, cx->fp()->script()->filename, CurrentLine(cx)); #endif } +/* Total length of scripts preceding a frame. */ +inline uint32_t frameCountersOffset(VMFrame &f) +{ + JSContext *cx = f.cx; + + uint32_t offset = 0; + if (cx->regs().inlined()) { + offset += cx->fp()->script()->length; + uint32_t index = cx->regs().inlined()->inlineIndex; + InlineFrame *frames = f.chunk()->inlineFrames(); + for (unsigned i = 0; i < index; i++) + offset += frames[i].fun->script()->length; + } + + jsbytecode *pc; + JSScript *script = cx->stack.currentScript(&pc); + offset += pc - script->code; + + return offset; +} + LookupStatus -BaseIC::disable(JSContext *cx, const char *reason, void *stub) +BaseIC::disable(VMFrame &f, const char *reason, void *stub) { - spew(cx, "disabled", reason); - Repatcher repatcher(cx->fp()->jit()); + if (f.chunk()->pcLengths) { + uint32_t offset = frameCountersOffset(f); + f.chunk()->pcLengths[offset].picsLength = 0; + } + + spew(f.cx, "disabled", reason); + Repatcher repatcher(f.chunk()); repatcher.relink(slowPathCall, FunctionPtr(stub)); return Lookup_Uncacheable; } +void +BaseIC::updatePCCounters(VMFrame &f, Assembler &masm) +{ + if (f.chunk()->pcLengths) { + uint32_t offset = frameCountersOffset(f); + f.chunk()->pcLengths[offset].picsLength += masm.size(); + } +} + bool BaseIC::shouldUpdate(JSContext *cx) { @@ -1981,12 +2128,6 @@ DisabledGetElem(VMFrame &f, ic::GetElementIC *ic) stubs::GetElem(f); } -static void JS_FASTCALL -DisabledCallElem(VMFrame &f, ic::GetElementIC *ic) -{ - stubs::CallElem(f); -} - bool GetElementIC::shouldUpdate(JSContext *cx) { @@ -2000,20 +2141,17 @@ GetElementIC::shouldUpdate(JSContext *cx) } LookupStatus -GetElementIC::disable(JSContext *cx, const char *reason) +GetElementIC::disable(VMFrame &f, const char *reason) { slowCallPatched = true; - void *stub = (op == JSOP_GETELEM) - ? JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem) - : JS_FUNC_TO_DATA_PTR(void *, DisabledCallElem); - BaseIC::disable(cx, reason, stub); + void *stub = JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem); + BaseIC::disable(f, reason, stub); return Lookup_Uncacheable; } LookupStatus GetElementIC::error(JSContext *cx) { - disable(cx, "error"); return Lookup_Error; } @@ -2023,32 +2161,35 @@ GetElementIC::purge(Repatcher &repatcher) // Repatch the inline jumps. if (inlineTypeGuardPatched) repatcher.relink(fastPathStart.jumpAtOffset(inlineTypeGuard), slowPathStart); - if (inlineClaspGuardPatched) - repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart); + if (inlineShapeGuardPatched) + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), slowPathStart); if (slowCallPatched) { - if (op == JSOP_GETELEM) { - repatcher.relink(slowPathCall, - FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::GetElement))); - } else if (op == JSOP_CALLELEM) { - repatcher.relink(slowPathCall, - FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::CallElement))); - } + repatcher.relink(slowPathCall, + FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::GetElement))); } reset(); } LookupStatus -GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp) +GetElementIC::attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name, + Value *vp) { JS_ASSERT(v.isString()); + JSContext *cx = f.cx; - GetPropertyHelper getprop(cx, obj, JSID_TO_ATOM(id), *this); + GetPropHelper getprop(cx, obj, name, *this, f); LookupStatus status = getprop.lookupAndTest(); if (status != Lookup_Cacheable) return status; + // With TI enabled, string property stubs can only be added to an opcode if + // the value read will go through a type barrier afterwards. TI only + // accounts for integer-valued properties accessed by GETELEM/CALLELEM. + if (cx->typeInferenceEnabled() && !forcedTypeBarrier) + return disable(f, "string element access may not have type barrier"); + Assembler masm; // Guard on the string's type and identity. @@ -2078,19 +2219,19 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i if (!idRemat.isConstant()) atomIdGuard = masm.branchPtr(Assembler::NotEqual, idRemat.dataReg(), ImmPtr(v.toString())); - // Guard on the base shape (or in the dense array case, the clasp). - Jump shapeGuard; - if (obj->isDenseArray()) { - shapeGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass()); - } else { - shapeGuard = masm.branch32(Assembler::NotEqual, typeReg, Imm32(obj->shape())); - } + // Guard on the base shape. + Jump shapeGuard = masm.branchPtr(Assembler::NotEqual, typeReg, ImmPtr(obj->lastProperty())); + + Vector otherGuards(cx); // Guard on the prototype, if applicable. MaybeJump protoGuard; JSObject *holder = getprop.holder; RegisterID holderReg = objReg; if (obj != holder) { + if (!GeneratePrototypeGuards(cx, otherGuards, masm, obj, holder, objReg, typeReg)) + return error(cx); + // Bake in the holder identity. Careful not to clobber |objReg|, since we can't remat it. holderReg = typeReg; masm.move(ImmPtr(holder), holderReg); @@ -2113,34 +2254,38 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i Jump done = masm.jump(); + updatePCCounters(f, masm); + PICLinker buffer(masm, *this); if (!buffer.init(cx)) return error(cx); if (hasLastStringStub && !buffer.verifyRange(lastStringStub)) - return disable(cx, "code memory is out of range"); - if (!buffer.verifyRange(cx->fp()->jit())) - return disable(cx, "code memory is out of range"); + return disable(f, "code memory is out of range"); + if (!buffer.verifyRange(f.chunk())) + return disable(f, "code memory is out of range"); // Patch all guards. buffer.maybeLink(atomIdGuard, slowPathStart); buffer.maybeLink(atomTypeGuard, slowPathStart); buffer.link(shapeGuard, slowPathStart); buffer.maybeLink(protoGuard, slowPathStart); + for (Jump *pj = otherGuards.begin(); pj != otherGuards.end(); ++pj) + buffer.link(*pj, slowPathStart); buffer.link(done, fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); #if DEBUG - char *chars = js_DeflateString(cx, v.toString()->getChars(cx), v.toString()->length()); - JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n", - js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(), - cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp())); + char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length()); + JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom %p (\"%s\") shape %p (%s: %d)\n", + js_CodeName[op], cs.executableAddress(), (void*)name, chars, + (void*)holder->lastProperty(), cx->fp()->script()->filename, CurrentLine(cx)); cx->free_(chars); #endif // Update the inline guards, if needed. - if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalClaspGuard()) { - Repatcher repatcher(cx->fp()->jit()); + if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalShapeGuard()) { + Repatcher repatcher(f.chunk()); if (shouldPatchInlineTypeGuard()) { // A type guard is present in the inline path, and this is the @@ -2152,15 +2297,15 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i inlineTypeGuardPatched = true; } - if (shouldPatchUnconditionalClaspGuard()) { - // The clasp guard is unconditional, meaning there is no type + if (shouldPatchUnconditionalShapeGuard()) { + // The shape guard is unconditional, meaning there is no type // check. This is the first stub, so it has to be patched. Note - // that it is wrong to patch the inline clasp guard otherwise, + // that it is wrong to patch the inline shape guard otherwise, // because it follows an integer-id guard. JS_ASSERT(!hasInlineTypeGuard()); - repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), cs); - inlineClaspGuardPatched = true; + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), cs); + inlineShapeGuardPatched = true; } } @@ -2199,42 +2344,190 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i stubsGenerated++; if (stubsGenerated == MAX_GETELEM_IC_STUBS) - disable(cx, "max stubs reached"); + disable(f, "max stubs reached"); // Finally, fetch the value to avoid redoing the property lookup. - if (shape->isMethod()) - *vp = ObjectValue(shape->methodObject()); + *vp = holder->getSlot(shape->slot()); + + return Lookup_Cacheable; +} + +LookupStatus +GetElementIC::attachArguments(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp) +{ + JSContext *cx = f.cx; + + if (!v.isInt32()) + return disable(f, "arguments object with non-integer key"); + + if (op == JSOP_CALLELEM) + return disable(f, "arguments object with call"); + + JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32); + + Assembler masm; + + Jump shapeGuard = masm.testObjClass(Assembler::NotEqual, objReg, typeReg, obj->getClass()); + + masm.move(objReg, typeReg); + masm.load32(Address(objReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), + objReg); + Jump overridden = masm.branchTest32(Assembler::NonZero, objReg, + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT)); + masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), objReg); + + Jump outOfBounds; + if (idRemat.isConstant()) { + outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32())); + } else { + outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg()); + } + + masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg); + if (idRemat.isConstant()) { + Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value)); + masm.loadTypeTag(slot, objReg); + } else { + BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE, + offsetof(ArgumentsData, slots)); + masm.loadTypeTag(slot, objReg); + } + Jump holeCheck = masm.branchPtr(Assembler::Equal, objReg, ImmType(JSVAL_TYPE_MAGIC)); + + masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::STACK_FRAME_SLOT)), objReg); + Jump liveArguments = masm.branchPtr(Assembler::NotEqual, objReg, ImmPtr(0)); + + masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg); + + if (idRemat.isConstant()) { + Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value)); + masm.loadValueAsComponents(slot, typeReg, objReg); + } else { + BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE, + offsetof(ArgumentsData, slots)); + masm.loadValueAsComponents(slot, typeReg, objReg); + } + + Jump done = masm.jump(); + + liveArguments.linkTo(masm.label(), &masm); + + masm.move(objReg, typeReg); + + Address fun(typeReg, StackFrame::offsetOfExec()); + masm.loadPtr(fun, objReg); + + Address nargs(objReg, offsetof(JSFunction, nargs)); + masm.load16(nargs, objReg); + + Jump notFormalArg; + if (idRemat.isConstant()) + notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32())); else - *vp = holder->getSlot(shape->slot); + notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg()); + + masm.lshift32(Imm32(3), objReg); /* nargs << 3 == nargs * sizeof(Value) */ + masm.subPtr(objReg, typeReg); /* fp - numFormalArgs => start of formal args */ + + Label loadFromStack = masm.label(); + masm.move(typeReg, objReg); + + if (idRemat.isConstant()) { + Address frameEntry(objReg, v.toInt32() * sizeof(Value)); + masm.loadValueAsComponents(frameEntry, typeReg, objReg); + } else { + BaseIndex frameEntry(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE); + masm.loadValueAsComponents(frameEntry, typeReg, objReg); + } + Jump done2 = masm.jump(); + + notFormalArg.linkTo(masm.label(), &masm); + + masm.push(typeReg); + + Address argsObject(typeReg, StackFrame::offsetOfArgsObj()); + masm.loadPtr(argsObject, typeReg); + + masm.load32(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), + typeReg); + masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), typeReg); + + /* This basically does fp - (numFormalArgs + numActualArgs + 2) */ + + masm.addPtr(typeReg, objReg); + masm.addPtr(Imm32(2), objReg); + masm.lshiftPtr(Imm32(3), objReg); + + masm.pop(typeReg); + masm.subPtr(objReg, typeReg); + + masm.jump(loadFromStack); + + updatePCCounters(f, masm); + + PICLinker buffer(masm, *this); + + if (!buffer.init(cx)) + return error(cx); + + if (!buffer.verifyRange(f.chunk())) + return disable(f, "code memory is out of range"); + + buffer.link(shapeGuard, slowPathStart); + buffer.link(overridden, slowPathStart); + buffer.link(outOfBounds, slowPathStart); + buffer.link(holeCheck, slowPathStart); + buffer.link(done, fastPathRejoin); + buffer.link(done2, fastPathRejoin); + + CodeLocationLabel cs = buffer.finalizeCodeAddendum(); + + JaegerSpew(JSpew_PICs, "generated getelem arguments stub at %p\n", cs.executableAddress()); + + Repatcher repatcher(f.chunk()); + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), cs); + + JS_ASSERT(!shouldPatchUnconditionalShapeGuard()); + JS_ASSERT(!inlineShapeGuardPatched); + + inlineShapeGuardPatched = true; + stubsGenerated++; + + if (stubsGenerated == MAX_GETELEM_IC_STUBS) + disable(f, "max stubs reached"); + + disable(f, "generated arguments stub"); + + if (!obj->getGeneric(cx, id, vp)) + return Lookup_Error; return Lookup_Cacheable; } -#if defined JS_POLYIC_TYPED_ARRAY +#if defined JS_METHODJIT_TYPED_ARRAY LookupStatus -GetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp) +GetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp) { + JSContext *cx = f.cx; + if (!v.isInt32()) - return disable(cx, "typed array with string key"); + return disable(f, "typed array with string key"); if (op == JSOP_CALLELEM) - return disable(cx, "typed array with call"); + return disable(f, "typed array with call"); - // The fast-path guarantees that after the dense clasp guard, the type is + // The fast-path guarantees that after the dense shape guard, the type is // known to be int32, either via type inference or the inline type check. JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32); Assembler masm; - // Guard on this typed array's clasp. - Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass()); - - // Get the internal typed array. - masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg); + // Guard on this typed array's shape/class. + Jump shapeGuard = masm.guardShape(objReg, obj); // Bounds check. Jump outOfBounds; - Address typedArrayLength(objReg, js::TypedArray::lengthOffset()); + Address typedArrayLength = masm.payloadOf(Address(objReg, TypedArray::lengthOffset())); if (idRemat.isConstant()) { JS_ASSERT(idRemat.value().toInt32() == v.toInt32()); outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, Imm32(v.toInt32())); @@ -2243,162 +2536,122 @@ GetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsi } // Load the array's packed data vector. - masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg); + masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg); - js::TypedArray *tarray = js::TypedArray::fromJSObject(obj); - int shift = tarray->slotWidth(); - if (idRemat.isConstant()) { - int32 index = v.toInt32(); - Address addr(objReg, index * shift); - LoadFromTypedArray(masm, tarray, addr, typeReg, objReg); - } else { - Assembler::Scale scale = Assembler::TimesOne; - switch (shift) { - case 2: - scale = Assembler::TimesTwo; - break; - case 4: - scale = Assembler::TimesFour; - break; - case 8: - scale = Assembler::TimesEight; - break; - } - BaseIndex addr(objReg, idRemat.dataReg(), scale); - LoadFromTypedArray(masm, tarray, addr, typeReg, objReg); + Int32Key key = idRemat.isConstant() + ? Int32Key::FromConstant(v.toInt32()) + : Int32Key::FromRegister(idRemat.dataReg()); + + JSObject *tarray = js::TypedArray::getTypedArray(obj); + if (!masm.supportsFloatingPoint() && + (TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32 || + TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT64 || + TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT32)) + { + return disable(f, "fpu not supported"); } + MaybeRegisterID tempReg; + masm.loadFromTypedArray(TypedArray::getType(tarray), objReg, key, typeReg, objReg, tempReg); + Jump done = masm.jump(); + updatePCCounters(f, masm); + PICLinker buffer(masm, *this); if (!buffer.init(cx)) return error(cx); - if (!buffer.verifyRange(cx->fp()->jit())) - return disable(cx, "code memory is out of range"); + if (!buffer.verifyRange(f.chunk())) + return disable(f, "code memory is out of range"); - buffer.link(claspGuard, slowPathStart); + buffer.link(shapeGuard, slowPathStart); buffer.link(outOfBounds, slowPathStart); buffer.link(done, fastPathRejoin); CodeLocationLabel cs = buffer.finalizeCodeAddendum(); JaegerSpew(JSpew_PICs, "generated getelem typed array stub at %p\n", cs.executableAddress()); - // If we can generate a typed array stub, the clasp guard is conditional. + // If we can generate a typed array stub, the shape guard is conditional. // Also, we only support one typed array. - JS_ASSERT(!shouldPatchUnconditionalClaspGuard()); - JS_ASSERT(!inlineClaspGuardPatched); + JS_ASSERT(!shouldPatchUnconditionalShapeGuard()); + JS_ASSERT(!inlineShapeGuardPatched); - Repatcher repatcher(cx->fp()->jit()); - repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), cs); - inlineClaspGuardPatched = true; + Repatcher repatcher(f.chunk()); + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), cs); + inlineShapeGuardPatched = true; stubsGenerated++; // In the future, it might make sense to attach multiple typed array stubs. // For simplicitly, they are currently monomorphic. if (stubsGenerated == MAX_GETELEM_IC_STUBS) - disable(cx, "max stubs reached"); + disable(f, "max stubs reached"); - disable(cx, "generated typed array stub"); + disable(f, "generated typed array stub"); // Fetch the value as expected of Lookup_Cacheable for GetElement. - if (!obj->getProperty(cx, id, vp)) + if (!obj->getGeneric(cx, id, vp)) return Lookup_Error; return Lookup_Cacheable; } -#endif /* JS_POLYIC_TYPED_ARRAY */ +#endif /* JS_METHODJIT_TYPED_ARRAY */ LookupStatus -GetElementIC::update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp) +GetElementIC::update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp) { - if (v.isString()) - return attachGetProp(cx, obj, v, id, vp); - -#if defined JS_POLYIC_TYPED_ARRAY - if (js_IsTypedArray(obj)) - return attachTypedArray(cx, obj, v, id, vp); + /* + * Only treat this as a GETPROP for non-numeric string identifiers. The + * GETPROP IC assumes the id has already gone through filtering for string + * indexes in the emitter, i.e. js_GetProtoIfDenseArray is only valid to + * use when looking up non-integer identifiers. + */ + uint32_t dummy; + if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) + return attachGetProp(f, obj, v, JSID_TO_ATOM(id)->asPropertyName(), vp); + + if (obj->isArguments()) + return attachArguments(f, obj, v, id, vp); + +#if defined JS_METHODJIT_TYPED_ARRAY + /* + * Typed array ICs can make stub calls, and need to know which registers + * are in use and need to be restored after the call. If type inference is + * enabled then we don't necessarily know the full set of such registers + * when generating the IC (loop-carried registers may be allocated later), + * and additionally the push/pop instructions used to save/restore in the + * IC are not compatible with carrying entries in floating point registers. + * Since we can use type information to generate inline paths for typed + * arrays, just don't generate these ICs with inference enabled. + */ + if (!f.cx->typeInferenceEnabled() && js_IsTypedArray(obj)) + return attachTypedArray(f, obj, v, id, vp); #endif - return disable(cx, "unhandled object and key type"); + return disable(f, "unhandled object and key type"); } void JS_FASTCALL -ic::CallElement(VMFrame &f, ic::GetElementIC *ic) +ic::GetElement(VMFrame &f, ic::GetElementIC *ic) { JSContext *cx = f.cx; - // Right now, we don't optimize for strings. + // Right now, we don't optimize for strings or lazy arguments. if (!f.regs.sp[-2].isObject()) { - ic->disable(cx, "non-object"); - stubs::CallElem(f); + ic->disable(f, "non-object"); + stubs::GetElem(f); return; } - Value thisv = f.regs.sp[-2]; - JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); - if (!thisObj) - THROW(); - - jsid id; Value idval = f.regs.sp[-1]; - if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32())) - id = INT_TO_JSID(idval.toInt32()); - else if (!js_InternNonIntElementId(cx, thisObj, idval, &id)) - THROW(); - if (ic->shouldUpdate(cx)) { -#ifdef DEBUG - f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC); -#endif - LookupStatus status = ic->update(cx, thisObj, idval, id, &f.regs.sp[-2]); - if (status != Lookup_Uncacheable) { - if (status == Lookup_Error) - THROW(); + RecompilationMonitor monitor(cx); - // If the result can be cached, the value was already retrieved. - JS_ASSERT(!f.regs.sp[-2].isMagic()); - f.regs.sp[-1].setObject(*thisObj); - return; - } - } - - /* Get or set the element. */ - if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &f.regs.sp[-2])) - THROW(); - -#if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(f.regs.sp[-2].isPrimitive()) && thisv.isObject()) { - f.regs.sp[-2] = f.regs.sp[-1]; - f.regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, f.regs.sp - 2)) - THROW(); - } else -#endif - { - f.regs.sp[-1] = thisv; - } -} - -void JS_FASTCALL -ic::GetElement(VMFrame &f, ic::GetElementIC *ic) -{ - JSContext *cx = f.cx; - - // Right now, we don't optimize for strings. - if (!f.regs.sp[-2].isObject()) { - ic->disable(cx, "non-object"); - stubs::GetElem(f); - return; - } - - JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]); + JSObject *obj = ValueToObject(cx, f.regs.sp[-2]); if (!obj) THROW(); - Value idval = f.regs.sp[-1]; - jsid id; if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32())) { id = INT_TO_JSID(idval.toInt32()); @@ -2407,11 +2660,11 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic) THROW(); } - if (ic->shouldUpdate(cx)) { + if (!monitor.recompiled() && ic->shouldUpdate(cx)) { #ifdef DEBUG f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC); #endif - LookupStatus status = ic->update(cx, obj, idval, id, &f.regs.sp[-2]); + LookupStatus status = ic->update(f, obj, idval, id, &f.regs.sp[-2]); if (status != Lookup_Uncacheable) { if (status == Lookup_Error) THROW(); @@ -2422,7 +2675,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic) } } - if (!obj->getProperty(cx, id, &f.regs.sp[-2])) + if (!obj->getGeneric(cx, id, &f.regs.sp[-2])) THROW(); } @@ -2430,18 +2683,17 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic) (FunctionTemplateConditional(s, f, f)) LookupStatus -SetElementIC::disable(JSContext *cx, const char *reason) +SetElementIC::disable(VMFrame &f, const char *reason) { slowCallPatched = true; VoidStub stub = APPLY_STRICTNESS(stubs::SetElem, strictMode); - BaseIC::disable(cx, reason, JS_FUNC_TO_DATA_PTR(void *, stub)); + BaseIC::disable(f, reason, JS_FUNC_TO_DATA_PTR(void *, stub)); return Lookup_Uncacheable; } LookupStatus SetElementIC::error(JSContext *cx) { - disable(cx, "error"); return Lookup_Error; } @@ -2449,8 +2701,8 @@ void SetElementIC::purge(Repatcher &repatcher) { // Repatch the inline jumps. - if (inlineClaspGuardPatched) - repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart); + if (inlineShapeGuardPatched) + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), slowPathStart); if (inlineHoleGuardPatched) repatcher.relink(fastPathStart.jumpAtOffset(inlineHoleGuard), slowPathStart); @@ -2463,25 +2715,26 @@ SetElementIC::purge(Repatcher &repatcher) } LookupStatus -SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval) +SetElementIC::attachHoleStub(VMFrame &f, JSObject *obj, int32_t keyval) { + JSContext *cx = f.cx; + if (keyval < 0) - return disable(cx, "negative key index"); + return disable(f, "negative key index"); // We may have failed a capacity check instead of a dense array check. // However we should still build the IC in this case, since it could - // be in a loop that is filling in the array. We can assert, however, - // that either we're in capacity or there's a hole - guaranteed by - // the fast path. - JS_ASSERT((jsuint)keyval >= obj->getDenseArrayCapacity() || - obj->getDenseArrayElement(keyval).isMagic(JS_ARRAY_HOLE)); + // be in a loop that is filling in the array. if (js_PrototypeHasIndexedProperties(cx, obj)) - return disable(cx, "prototype has indexed properties"); + return disable(f, "prototype has indexed properties"); Assembler masm; - Vector fails(cx); + Vector fails(cx); + + if (!GeneratePrototypeGuards(cx, fails, masm, obj, NULL, objReg, objReg)) + return error(cx); // Test for indexed properties in Array.prototype. We test each shape // along the proto chain. This affords us two optimizations: @@ -2490,7 +2743,7 @@ SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval) // 2) We only have to test the shape, rather than INDEXED. for (JSObject *pobj = obj->getProto(); pobj; pobj = pobj->getProto()) { if (!pobj->isNative()) - return disable(cx, "non-native array prototype"); + return disable(f, "non-native array prototype"); masm.move(ImmPtr(pobj), objReg); Jump j = masm.guardShape(objReg, pobj); if (!fails.append(j)) @@ -2500,27 +2753,31 @@ SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval) // Restore |obj|. masm.rematPayload(StateRemat::FromInt32(objRemat), objReg); - // Guard against negative indices. - MaybeJump keyGuard; - if (!hasConstantKey) - keyGuard = masm.branch32(Assembler::LessThan, keyReg, Imm32(0)); + // Load the elements. + masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), objReg); - // Update the array length if necessary. - Jump skipUpdate; - Address arrayLength(objReg, offsetof(JSObject, privateData)); - if (hasConstantKey) { - skipUpdate = masm.branch32(Assembler::Above, arrayLength, Imm32(keyValue)); - masm.store32(Imm32(keyValue + 1), arrayLength); - } else { - skipUpdate = masm.branch32(Assembler::Above, arrayLength, keyReg); - masm.add32(Imm32(1), keyReg); - masm.store32(keyReg, arrayLength); - masm.sub32(Imm32(1), keyReg); - } - skipUpdate.linkTo(masm.label(), &masm); + Int32Key key = hasConstantKey ? Int32Key::FromConstant(keyValue) : Int32Key::FromRegister(keyReg); + + // Guard that the initialized length is being updated exactly. + fails.append(masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), + objReg, key, Assembler::NotEqual)); + + // Check the array capacity. + fails.append(masm.guardArrayExtent(ObjectElements::offsetOfCapacity(), + objReg, key, Assembler::BelowOrEqual)); + + masm.bumpKey(key, 1); + + // Update the length and initialized length. + masm.storeKey(key, Address(objReg, ObjectElements::offsetOfInitializedLength())); + Jump lengthGuard = masm.guardArrayExtent(ObjectElements::offsetOfLength(), + objReg, key, Assembler::AboveOrEqual); + masm.storeKey(key, Address(objReg, ObjectElements::offsetOfLength())); + lengthGuard.linkTo(masm.label(), &masm); + + masm.bumpKey(key, -1); // Store the value back. - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); if (hasConstantKey) { Address slot(objReg, keyValue * sizeof(Value)); masm.storeValue(vr, slot); @@ -2534,59 +2791,67 @@ SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval) JS_ASSERT(!execPool); JS_ASSERT(!inlineHoleGuardPatched); - LinkerHelper buffer(masm); + LinkerHelper buffer(masm, JSC::METHOD_CODE); execPool = buffer.init(cx); if (!execPool) return error(cx); - if (!buffer.verifyRange(cx->fp()->jit())) - return disable(cx, "code memory is out of range"); + if (!buffer.verifyRange(f.chunk())) + return disable(f, "code memory is out of range"); // Patch all guards. for (size_t i = 0; i < fails.length(); i++) buffer.link(fails[i], slowPathStart); buffer.link(done, fastPathRejoin); - CodeLocationLabel cs = buffer.finalize(); + CodeLocationLabel cs = buffer.finalize(f); JaegerSpew(JSpew_PICs, "generated dense array hole stub at %p\n", cs.executableAddress()); - Repatcher repatcher(cx->fp()->jit()); + Repatcher repatcher(f.chunk()); repatcher.relink(fastPathStart.jumpAtOffset(inlineHoleGuard), cs); inlineHoleGuardPatched = true; - disable(cx, "generated dense array hole stub"); + disable(f, "generated dense array hole stub"); return Lookup_Cacheable; } -#if defined JS_POLYIC_TYPED_ARRAY +#if defined JS_METHODJIT_TYPED_ARRAY LookupStatus -SetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, int32 key) +SetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, int32_t key) { - // Right now, only one clasp guard extension is supported. - JS_ASSERT(!inlineClaspGuardPatched); + // Right now, only one shape guard extension is supported. + JS_ASSERT(!inlineShapeGuardPatched); Assembler masm; + JSContext *cx = f.cx; - // Guard on this typed array's clasp. - Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass()); + // Restore |obj|. + masm.rematPayload(StateRemat::FromInt32(objRemat), objReg); - // Get the internal typed array. - masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg); + // Guard on this typed array's shape. + Jump shapeGuard = masm.guardShape(objReg, obj); // Bounds check. Jump outOfBounds; - Address typedArrayLength(objReg, js::TypedArray::lengthOffset()); + Address typedArrayLength = masm.payloadOf(Address(objReg, TypedArray::lengthOffset())); if (hasConstantKey) outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, Imm32(keyValue)); else outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, keyReg); // Load the array's packed data vector. - js::TypedArray *tarray = js::TypedArray::fromJSObject(obj); - masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg); + masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg); - int shift = tarray->slotWidth(); + JSObject *tarray = js::TypedArray::getTypedArray(obj); + if (!masm.supportsFloatingPoint() && + (TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32 || + TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT64)) + { + return disable(f, "fpu not supported"); + } + + int shift = js::TypedArray::slotWidth(obj); if (hasConstantKey) { Address addr(objReg, keyValue * shift); if (!StoreToTypedArray(cx, masm, tarray, addr, vr, volatileMask)) @@ -2615,16 +2880,16 @@ SetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, int32 key) // by a GC or shape regenerated GC. We let this stub live for the lifetime // of the script. JS_ASSERT(!execPool); - LinkerHelper buffer(masm); + LinkerHelper buffer(masm, JSC::METHOD_CODE); execPool = buffer.init(cx); if (!execPool) return error(cx); - if (!buffer.verifyRange(cx->fp()->jit())) - return disable(cx, "code memory is out of range"); + if (!buffer.verifyRange(f.chunk())) + return disable(f, "code memory is out of range"); // Note that the out-of-bounds path simply does nothing. - buffer.link(claspGuard, slowPathStart); + buffer.link(shapeGuard, slowPathStart); buffer.link(outOfBounds, fastPathRejoin); buffer.link(done, fastPathRejoin); masm.finalize(buffer); @@ -2632,43 +2897,59 @@ SetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, int32 key) CodeLocationLabel cs = buffer.finalizeCodeAddendum(); JaegerSpew(JSpew_PICs, "generated setelem typed array stub at %p\n", cs.executableAddress()); - Repatcher repatcher(cx->fp()->jit()); - repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), cs); - inlineClaspGuardPatched = true; + Repatcher repatcher(f.chunk()); + repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), cs); + inlineShapeGuardPatched = true; stubsGenerated++; // In the future, it might make sense to attach multiple typed array stubs. // For simplicitly, they are currently monomorphic. if (stubsGenerated == MAX_GETELEM_IC_STUBS) - disable(cx, "max stubs reached"); + disable(f, "max stubs reached"); - disable(cx, "generated typed array stub"); + disable(f, "generated typed array stub"); return Lookup_Cacheable; } -#endif /* JS_POLYIC_TYPED_ARRAY */ +#endif /* JS_METHODJIT_TYPED_ARRAY */ LookupStatus -SetElementIC::update(JSContext *cx, const Value &objval, const Value &idval) +SetElementIC::update(VMFrame &f, const Value &objval, const Value &idval) { if (!objval.isObject()) - return disable(cx, "primitive lval"); + return disable(f, "primitive lval"); if (!idval.isInt32()) - return disable(cx, "non-int32 key"); + return disable(f, "non-int32 key"); JSObject *obj = &objval.toObject(); - int32 key = idval.toInt32(); + int32_t key = idval.toInt32(); if (obj->isDenseArray()) - return attachHoleStub(cx, obj, key); + return attachHoleStub(f, obj, key); -#if defined JS_POLYIC_TYPED_ARRAY - if (js_IsTypedArray(obj)) - return attachTypedArray(cx, obj, key); +#if defined JS_METHODJIT_TYPED_ARRAY + /* Not attaching typed array stubs with linear scan allocator, see GetElementIC. */ + if (!f.cx->typeInferenceEnabled() && js_IsTypedArray(obj)) + return attachTypedArray(f, obj, key); #endif - return disable(cx, "unsupported object type"); + return disable(f, "unsupported object type"); +} + +bool +SetElementIC::shouldUpdate(JSContext *cx) +{ + if (!hit) { + hit = true; + spew(cx, "ignored", "first hit"); + return false; + } +#ifdef JSGC_INCREMENTAL_MJ + JS_ASSERT(!cx->compartment->needsBarrier()); +#endif + JS_ASSERT(stubsGenerated < MAX_PIC_STUBS); + return true; } template @@ -2678,7 +2959,7 @@ ic::SetElement(VMFrame &f, ic::SetElementIC *ic) JSContext *cx = f.cx; if (ic->shouldUpdate(cx)) { - LookupStatus status = ic->update(cx, f.regs.sp[-3], f.regs.sp[-2]); + LookupStatus status = ic->update(f, f.regs.sp[-3], f.regs.sp[-2]); if (status == Lookup_Error) THROW(); } @@ -2689,56 +2970,5 @@ ic::SetElement(VMFrame &f, ic::SetElementIC *ic) template void JS_FASTCALL ic::SetElement(VMFrame &f, SetElementIC *ic); template void JS_FASTCALL ic::SetElement(VMFrame &f, SetElementIC *ic); -void -JITScript::purgePICs() -{ - if (!nPICs && !nGetElems && !nSetElems) - return; - - Repatcher repatcher(this); - - ic::PICInfo *pics_ = pics(); - for (uint32 i = 0; i < nPICs; i++) { - ic::PICInfo &pic = pics_[i]; - switch (pic.kind) { - case ic::PICInfo::SET: - case ic::PICInfo::SETMETHOD: - SetPropCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::NAME: - case ic::PICInfo::XNAME: - ScopeNameCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::BIND: - BindNameCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::CALL: /* fall-through */ - case ic::PICInfo::GET: - GetPropCompiler::reset(repatcher, pic); - break; - default: - JS_NOT_REACHED("Unhandled PIC kind"); - break; - } - pic.reset(); - } - - ic::GetElementIC *getElems_ = getElems(); - ic::SetElementIC *setElems_ = setElems(); - for (uint32 i = 0; i < nGetElems; i++) - getElems_[i].purge(repatcher); - for (uint32 i = 0; i < nSetElems; i++) - setElems_[i].purge(repatcher); -} - -void -ic::PurgePICs(JSContext *cx, JSScript *script) -{ - if (script->jitNormal) - script->jitNormal->purgePICs(); - if (script->jitCtor) - script->jitCtor->purgePICs(); -} - #endif /* JS_POLYIC */ diff --git a/deps/mozjs/js/src/methodjit/PolyIC.h b/deps/mozjs/js/src/methodjit/PolyIC.h index 9fa7bcfb02a..48b99f1380c 100644 --- a/deps/mozjs/js/src/methodjit/PolyIC.h +++ b/deps/mozjs/js/src/methodjit/PolyIC.h @@ -41,10 +41,9 @@ #define jsjaeger_poly_ic_h__ #include "jscntxt.h" -#include "jstl.h" -#include "jsvector.h" #include "assembler/assembler/MacroAssembler.h" #include "assembler/assembler/CodeLocation.h" +#include "js/Vector.h" #include "methodjit/MethodJIT.h" #include "methodjit/ICRepatcher.h" #include "BaseAssembler.h" @@ -58,10 +57,8 @@ namespace mjit { namespace ic { /* Maximum number of stubs for a given callsite. */ -static const uint32 MAX_PIC_STUBS = 16; -static const uint32 MAX_GETELEM_IC_STUBS = 17; - -void PurgePICs(JSContext *cx); +static const uint32_t MAX_PIC_STUBS = 16; +static const uint32_t MAX_GETELEM_IC_STUBS = 17; enum LookupStatus { Lookup_Error = 0, @@ -84,17 +81,23 @@ struct BaseIC : public MacroAssemblerTypedefs { // Slow path stub call. CodeLocationCall slowPathCall; + // Offset from start of stub to jump target of second shape guard as Nitro + // asm data location. This is 0 if there is only one shape guard in the + // last stub. + int32_t secondShapeGuard; + // Whether or not the callsite has been hit at least once. bool hit : 1; bool slowCallPatched : 1; - // Number of stubs generated. - uint32 stubsGenerated : 5; + // Whether getter/setter hooks can be called from IC stubs. + bool canCallHook : 1; - // Offset from start of stub to jump target of second shape guard as Nitro - // asm data location. This is 0 if there is only one shape guard in the - // last stub. - int32 secondShapeGuard : 11; + // Whether a type barrier is in place for the result of the op. + bool forcedTypeBarrier : 1; + + // Number of stubs generated. + uint32_t stubsGenerated : 5; // Opcode this was compiled for. JSOp op : 9; @@ -102,12 +105,14 @@ struct BaseIC : public MacroAssemblerTypedefs { void reset() { hit = false; slowCallPatched = false; + forcedTypeBarrier = false; stubsGenerated = 0; secondShapeGuard = 0; } bool shouldUpdate(JSContext *cx); void spew(JSContext *cx, const char *event, const char *reason); - LookupStatus disable(JSContext *cx, const char *reason, void *stub); + LookupStatus disable(VMFrame &f, const char *reason, void *stub); + void updatePCCounters(VMFrame &f, Assembler &masm); bool isCallOp(); }; @@ -192,7 +197,7 @@ class BasePolyIC : public BaseIC { if (isOnePool()) { JSC::ExecutablePool *oldPool = u.execPool; JS_ASSERT(!isTagged(oldPool)); - ExecPoolVector *execPools = cx->new_(SystemAllocPolicy()); + ExecPoolVector *execPools = OffTheBooks::new_(SystemAllocPolicy()); if (!execPools) return false; if (!execPools->append(oldPool) || !execPools->append(pool)) { @@ -228,22 +233,22 @@ struct GetElementIC : public BasePolyIC { // This is only set if hasInlineTypeCheck() is true. unsigned inlineTypeGuard : 8; - // Offset from the fast path to the inline clasp guard. This is always + // Offset from the fast path to the inline shape guard. This is always // set; if |id| is known to not be int32, then it's an unconditional // jump to the slow path. - unsigned inlineClaspGuard : 8; + unsigned inlineShapeGuard : 8; // This is usable if hasInlineTypeGuard() returns true, which implies // that a dense array fast path exists. The inline type guard serves as // the head of the chain of all string-based element stubs. bool inlineTypeGuardPatched : 1; - // This is always usable, and specifies whether the inline clasp guard + // This is always usable, and specifies whether the inline shape guard // has been patched. If hasInlineTypeGuard() is true, it guards against // a dense array, and guarantees the inline type guard has passed. - // Otherwise, there is no inline type guard, and the clasp guard is just + // Otherwise, there is no inline type guard, and the shape guard is just // an unconditional jump. - bool inlineClaspGuardPatched : 1; + bool inlineShapeGuardPatched : 1; //////////////////////////////////////////// // State for string-based property stubs. // @@ -255,9 +260,9 @@ struct GetElementIC : public BasePolyIC { // These offsets are used for string-key dependent stubs, such as named // property accesses. They are separated from the int-key dependent stubs, // in order to guarantee that the id type needs only one guard per type. - int32 atomGuard : 8; // optional, non-zero if present - int32 firstShapeGuard : 11; // always set - int32 secondShapeGuard : 11; // optional, non-zero if present + int32_t atomGuard : 8; // optional, non-zero if present + int32_t firstShapeGuard : 11; // always set + int32_t secondShapeGuard : 11; // optional, non-zero if present bool hasLastStringStub : 1; JITCode lastStringStub; @@ -277,28 +282,28 @@ struct GetElementIC : public BasePolyIC { bool shouldPatchInlineTypeGuard() { return hasInlineTypeGuard() && !inlineTypeGuardPatched; } - bool shouldPatchUnconditionalClaspGuard() { - // The clasp guard is only unconditional if the type is known to not + bool shouldPatchUnconditionalShapeGuard() { + // The shape guard is only unconditional if the type is known to not // be an int32. if (idRemat.isTypeKnown() && idRemat.knownType() != JSVAL_TYPE_INT32) - return !inlineClaspGuardPatched; + return !inlineShapeGuardPatched; return false; } void reset() { BasePolyIC::reset(); inlineTypeGuardPatched = false; - inlineClaspGuardPatched = false; + inlineShapeGuardPatched = false; typeRegHasBaseShape = false; hasLastStringStub = false; } void purge(Repatcher &repatcher); - LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp); - LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id, + LookupStatus update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp); + LookupStatus attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name, Value *vp); - LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id, - Value *vp); - LookupStatus disable(JSContext *cx, const char *reason); + LookupStatus attachArguments(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp); + LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp); + LookupStatus disable(VMFrame &f, const char *reason); LookupStatus error(JSContext *cx); bool shouldUpdate(JSContext *cx); }; @@ -317,13 +322,13 @@ struct SetElementIC : public BaseIC { RegisterID objReg : 5; // Information on how to rematerialize |objReg|. - int32 objRemat : MIN_STATE_REMAT_BITS; + int32_t objRemat : MIN_STATE_REMAT_BITS; - // Offset from the start of the fast path to the inline clasp guard. - unsigned inlineClaspGuard : 6; + // Offset from the start of the fast path to the inline shape guard. + unsigned inlineShapeGuard : 6; - // True if the clasp guard has been patched; false otherwise. - bool inlineClaspGuardPatched : 1; + // True if the shape guard has been patched; false otherwise. + bool inlineShapeGuardPatched : 1; // Offset from the start of the fast path to the inline hole guard. unsigned inlineHoleGuard : 8; @@ -336,14 +341,14 @@ struct SetElementIC : public BaseIC { // A bitmask of registers that are volatile and must be preserved across // stub calls inside the IC. - uint32 volatileMask; + uint32_t volatileMask; // If true, then keyValue contains a constant index value >= 0. Otherwise, // keyReg contains a dynamic integer index in any range. bool hasConstantKey : 1; union { RegisterID keyReg; - int32 keyValue; + int32_t keyValue; }; // Rematerialize information about the value being stored. @@ -357,15 +362,16 @@ struct SetElementIC : public BaseIC { if (execPool != NULL) execPool->release(); execPool = NULL; - inlineClaspGuardPatched = false; + inlineShapeGuardPatched = false; inlineHoleGuardPatched = false; } void purge(Repatcher &repatcher); - LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, int32 key); - LookupStatus attachHoleStub(JSContext *cx, JSObject *obj, int32 key); - LookupStatus update(JSContext *cx, const Value &objval, const Value &idval); - LookupStatus disable(JSContext *cx, const char *reason); + LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, int32_t key); + LookupStatus attachHoleStub(VMFrame &f, JSObject *obj, int32_t key); + LookupStatus update(VMFrame &f, const Value &objval, const Value &idval); + LookupStatus disable(VMFrame &f, const char *reason); LookupStatus error(JSContext *cx); + bool shouldUpdate(JSContext *cx); }; struct PICInfo : public BasePolyIC { @@ -378,7 +384,6 @@ struct PICInfo : public BasePolyIC { #endif { GET, // JSOP_GETPROP - CALL, // JSOP_CALLPROP SET, // JSOP_SETPROP, JSOP_SETNAME SETMETHOD, // JSOP_SETMETHOD NAME, // JSOP_NAME @@ -392,7 +397,7 @@ struct PICInfo : public BasePolyIC { bool hasTypeCheck : 1; // type check and reg are present // Reverse offset from slowPathStart to the type check slow path. - int32 typeCheckOffset; + int32_t typeCheckOffset; } get; ValueRemat vr; } u; @@ -421,9 +426,9 @@ struct PICInfo : public BasePolyIC { // Return a JITCode block corresponding to the code memory to attach a // new stub to. - JITCode lastCodeBlock(JITScript *jit) { + JITCode lastCodeBlock(JITChunk *chunk) { if (!stubsGenerated) - return JITCode(jit->code.m_code.executableAddress(), jit->code.m_size); + return JITCode(chunk->code.m_code.executableAddress(), chunk->code.m_size); return lastStubStart; } @@ -447,14 +452,20 @@ struct PICInfo : public BasePolyIC { RegisterID shapeReg : 5; // also the out type reg RegisterID objReg : 5; // also the out data reg + // Whether type properties need to be updated to reflect generated stubs. + bool typeMonitored : 1; + // Offset from start of fast path to initial shape guard. - uint32 shapeGuard; + uint32_t shapeGuard; + + // Possible types of the RHS, for monitored SETPROP PICs. + types::TypeSet *rhsTypes; inline bool isSet() const { return kind == SET || kind == SETMETHOD; } inline bool isGet() const { - return kind == GET || kind == CALL; + return kind == GET; } inline bool isBind() const { return kind == BIND; @@ -473,17 +484,7 @@ struct PICInfo : public BasePolyIC { inline bool shapeNeedsRemat() { return !shapeRegHasBaseShape; } - inline bool isFastCall() { - JS_ASSERT(kind == CALL); - return !hasTypeCheck(); - } -#if !defined JS_HAS_IC_LABELS - static GetPropLabels getPropLabels_; - static SetPropLabels setPropLabels_; - static BindNameLabels bindNameLabels_; - static ScopeNameLabels scopeNameLabels_; -#else union { GetPropLabels getPropLabels_; SetPropLabels setPropLabels_; @@ -506,7 +507,6 @@ struct PICInfo : public BasePolyIC { JS_ASSERT(kind == NAME || kind == XNAME); scopeNameLabels_ = labels; } -#endif GetPropLabels &getPropLabels() { JS_ASSERT(isGet()); @@ -529,7 +529,7 @@ struct PICInfo : public BasePolyIC { jsbytecode *pc; // Index into the script's atom table. - JSAtom *atom; + PropertyName *name; // Reset the data members to the state of a fresh PIC before any patching // or stub generation was done. @@ -541,15 +541,13 @@ struct PICInfo : public BasePolyIC { }; #ifdef JS_POLYIC -void PurgePICs(JSContext *cx, JSScript *script); void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *); void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *); void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *); void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *); void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *); void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *); -void JS_FASTCALL CallElement(VMFrame &f, ic::GetElementIC *); template void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *); #endif diff --git a/deps/mozjs/js/src/methodjit/PunboxAssembler.h b/deps/mozjs/js/src/methodjit/PunboxAssembler.h index 5c6da2755e7..db1b42aa445 100644 --- a/deps/mozjs/js/src/methodjit/PunboxAssembler.h +++ b/deps/mozjs/js/src/methodjit/PunboxAssembler.h @@ -50,7 +50,7 @@ namespace mjit { struct Imm64 : JSC::MacroAssembler::ImmPtr { - Imm64(uint64 u) + Imm64(uint64_t u) : ImmPtr((const void *)u) { } }; @@ -67,19 +67,21 @@ struct ImmType : ImmTag { ImmType(JSValueType type) : ImmTag(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))) - { } + { + JS_ASSERT(type > JSVAL_TYPE_DOUBLE); + } }; struct ImmPayload : Imm64 { - ImmPayload(uint64 payload) + ImmPayload(uint64_t payload) : Imm64(payload) { } }; class PunboxAssembler : public JSC::MacroAssembler { - static const uint32 PAYLOAD_OFFSET = 0; + static const uint32_t PAYLOAD_OFFSET = 0; public: static const JSC::MacroAssembler::Scale JSVAL_SCALE = JSC::MacroAssembler::TimesEight; @@ -94,7 +96,7 @@ class PunboxAssembler : public JSC::MacroAssembler return address; } - void loadInlineSlot(RegisterID objReg, uint32 slot, + void loadInlineSlot(RegisterID objReg, uint32_t slot, RegisterID typeReg, RegisterID dataReg) { Address address(objReg, JSObject::getFixedSlotOffset(slot)); loadValueAsComponents(address, typeReg, dataReg); @@ -127,8 +129,13 @@ class PunboxAssembler : public JSC::MacroAssembler } void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { - move(Imm64(val.asRawBits() & JSVAL_TAG_MASK), type); - move(Imm64(val.asRawBits() & JSVAL_PAYLOAD_MASK), payload); + uint64_t bits = JSVAL_TO_IMPL(val).asBits; + move(Imm64(bits & JSVAL_TAG_MASK), type); + move(Imm64(bits & JSVAL_PAYLOAD_MASK), payload); + } + + void loadValuePayload(const Value &val, RegisterID payload) { + move(Imm64(JSVAL_TO_IMPL(val).asBits & JSVAL_PAYLOAD_MASK), payload); } /* @@ -174,15 +181,13 @@ class PunboxAssembler : public JSC::MacroAssembler /* Overload for constant type and constant data. */ DataLabel32 storeValueWithAddressOffsetPatch(const Value &v, Address address) { - jsval_layout jv; - jv.asBits = JSVAL_BITS(Jsvalify(v)); - - move(ImmPtr(reinterpret_cast(jv.asBits)), Registers::ValueReg); + move(ImmPtr(JSVAL_TO_IMPL(v).asPtr), Registers::ValueReg); return storePtrWithAddressOffsetPatch(Registers::ValueReg, valueOf(address)); } /* Overloaded for store with value remat info. */ DataLabel32 storeValueWithAddressOffsetPatch(const ValueRemat &vr, Address address) { + JS_ASSERT(!vr.isFPRegister()); if (vr.isConstant()) { return storeValueWithAddressOffsetPatch(vr.value(), address); } else if (vr.isTypeKnown()) { @@ -244,16 +249,15 @@ class PunboxAssembler : public JSC::MacroAssembler template void storeValue(const Value &v, T address) { - jsval_layout jv; - jv.asBits = JSVAL_BITS(Jsvalify(v)); - - storePtr(Imm64(jv.asBits), valueOf(address)); + storePtr(Imm64(JSVAL_TO_IMPL(v).asBits), valueOf(address)); } template void storeValue(const ValueRemat &vr, T address) { if (vr.isConstant()) storeValue(vr.value(), address); + else if (vr.isFPRegister()) + storeDouble(vr.fpReg(), address); else if (vr.isTypeKnown()) storeValueFromComponents(ImmType(vr.knownType()), vr.dataReg(), address); else @@ -271,8 +275,8 @@ class PunboxAssembler : public JSC::MacroAssembler lshiftPtr(Imm32(1), to); } - void loadObjPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); + void loadObjPrivate(RegisterID base, RegisterID to, uint32_t nfixed) { + Address priv(base, JSObject::getPrivateDataOffset(nfixed)); loadPtr(priv, to); } @@ -335,6 +339,16 @@ class PunboxAssembler : public JSC::MacroAssembler return testObject(cond, Registers::ValueReg); } + Jump testGCThing(RegisterID reg) { + return branchPtr(AboveOrEqual, reg, ImmTag(JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET)); + } + + Jump testGCThing(Address address) { + loadValue(address, Registers::ValueReg); + return branchPtr(AboveOrEqual, Registers::ValueReg, + ImmTag(JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET)); + } + Jump testDouble(Condition cond, RegisterID reg) { cond = (cond == Equal) ? BelowOrEqual : Above; return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_MAX_DOUBLE)); @@ -363,6 +377,12 @@ class PunboxAssembler : public JSC::MacroAssembler return testString(cond, Registers::ValueReg); } + void compareValue(Address one, Address two, RegisterID T0, RegisterID T1, + Vector *mismatches) { + loadValue(one, T0); + mismatches->append(branchPtr(NotEqual, T0, two)); + } + void breakDouble(FPRegisterID srcDest, RegisterID typeReg, RegisterID dataReg) { m_assembler.movq_rr(srcDest, typeReg); move(Registers::PayloadMaskReg, dataReg); @@ -384,9 +404,22 @@ class PunboxAssembler : public JSC::MacroAssembler } template - Jump fastArrayLoadSlot(T address, RegisterID typeReg, RegisterID dataReg) { - loadValueAsComponents(address, typeReg, dataReg); - return branchPtr(Equal, typeReg, ImmType(JSVAL_TYPE_MAGIC)); + Jump fastArrayLoadSlot(T address, bool holeCheck, + MaybeRegisterID typeReg, RegisterID dataReg) + { + Jump notHole; + if (typeReg.isSet()) { + loadValueAsComponents(address, typeReg.reg(), dataReg); + if (holeCheck) + notHole = branchPtr(Equal, typeReg.reg(), ImmType(JSVAL_TYPE_MAGIC)); + } else { + if (holeCheck) { + loadTypeTag(address, Registers::ValueReg); + notHole = branchPtr(Equal, Registers::ValueReg, ImmType(JSVAL_TYPE_MAGIC)); + } + loadPayload(address, dataReg); + } + return notHole; } }; diff --git a/deps/mozjs/js/src/methodjit/RematInfo.h b/deps/mozjs/js/src/methodjit/RematInfo.h index caba92daed0..6855dd8c49a 100644 --- a/deps/mozjs/js/src/methodjit/RematInfo.h +++ b/deps/mozjs/js/src/methodjit/RematInfo.h @@ -43,6 +43,7 @@ #include "jscntxt.h" #include "MachineRegs.h" #include "assembler/assembler/MacroAssembler.h" +#include "vm/Stack.h" namespace js { namespace mjit { @@ -52,7 +53,7 @@ struct StateRemat { typedef JSC::MacroAssembler::RegisterID RegisterID; typedef JSC::MacroAssembler::Address Address; - static const int32 CONSTANT = -int(UINT16_LIMIT * sizeof(Value)); + static const int32_t CONSTANT = -int(UINT16_LIMIT * sizeof(Value)); // This union encodes the fastest rematerialization of a non-constant // value. The |offset| field can be used to recover information @@ -62,10 +63,10 @@ struct StateRemat { // 3) A value in [fp, inf) is a local slot. union { RegisterID reg_; - int32 offset_; + int32_t offset_; }; - static StateRemat FromInt32(int32 i32) { + static StateRemat FromInt32(int32_t i32) { StateRemat sr; sr.offset_ = i32; return sr; @@ -84,7 +85,7 @@ struct StateRemat { return sr; } - // Minimum number of bits needed to compactly store the int32 + // Minimum number of bits needed to compactly store the int32_t // representation in a struct or union. This prevents bloating the IC // structs by an extra 8 bytes in some cases. 16 bits are needed to encode // the largest local: @@ -94,13 +95,13 @@ struct StateRemat { bool isConstant() const { return offset_ == CONSTANT; } bool inRegister() const { return offset_ >= 0 && - offset_ <= int32(JSC::MacroAssembler::TotalRegisters); } + offset_ <= int32_t(JSC::MacroAssembler::TotalRegisters); } bool inMemory() const { - return offset_ >= int32(sizeof(StackFrame)) || + return offset_ >= int32_t(sizeof(StackFrame)) || offset_ < 0; } - int32 toInt32() const { return offset_; } + int32_t toInt32() const { return offset_; } Address address() const { JS_ASSERT(inMemory()); return Address(JSFrameReg, offset_); @@ -114,30 +115,42 @@ struct StateRemat { /* Lightweight version of FrameEntry. */ struct ValueRemat { typedef JSC::MacroAssembler::RegisterID RegisterID; + typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; union { struct { union { - int32 typeRemat_; + int32_t typeRemat_; JSValueType knownType_; } type; - int32 dataRemat_ : MIN_STATE_REMAT_BITS; + int32_t dataRemat_ : MIN_STATE_REMAT_BITS; bool isTypeKnown_ : 1; } s; jsval v_; + FPRegisterID fpreg_; } u; bool isConstant_ : 1; + bool isFPRegister_ : 1; bool isDataSynced : 1; bool isTypeSynced : 1; static ValueRemat FromConstant(const Value &v) { ValueRemat vr; vr.isConstant_ = true; - vr.u.v_ = Jsvalify(v); + vr.isFPRegister_ = false; + vr.u.v_ = v; + return vr; + } + static ValueRemat FromFPRegister(FPRegisterID fpreg) { + ValueRemat vr; + vr.isConstant_ = false; + vr.isFPRegister_ = true; + vr.u.fpreg_ = fpreg; return vr; } static ValueRemat FromKnownType(JSValueType type, RegisterID dataReg) { ValueRemat vr; vr.isConstant_ = false; + vr.isFPRegister_ = false; vr.u.s.type.knownType_ = type; vr.u.s.isTypeKnown_ = true; vr.u.s.dataRemat_ = StateRemat::FromRegister(dataReg).toInt32(); @@ -149,6 +162,7 @@ struct ValueRemat { static ValueRemat FromRegisters(RegisterID typeReg, RegisterID dataReg) { ValueRemat vr; vr.isConstant_ = false; + vr.isFPRegister_ = false; vr.u.s.isTypeKnown_ = false; vr.u.s.type.typeRemat_ = StateRemat::FromRegister(typeReg).toInt32(); vr.u.s.dataRemat_ = StateRemat::FromRegister(dataReg).toInt32(); @@ -159,8 +173,12 @@ struct ValueRemat { return vr; } + FPRegisterID fpReg() const { + JS_ASSERT(isFPRegister()); + return u.fpreg_; + } RegisterID dataReg() const { - JS_ASSERT(!isConstant()); + JS_ASSERT(!isConstant() && !isFPRegister()); return dataRemat().reg(); } RegisterID typeReg() const { @@ -169,7 +187,8 @@ struct ValueRemat { } bool isConstant() const { return isConstant_; } - bool isTypeKnown() const { return isConstant() || u.s.isTypeKnown_; } + bool isFPRegister() const { return isFPRegister_; } + bool isTypeKnown() const { return isConstant() || isFPRegister() || u.s.isTypeKnown_; } StateRemat dataRemat() const { JS_ASSERT(!isConstant()); @@ -181,7 +200,7 @@ struct ValueRemat { } Value value() const { JS_ASSERT(isConstant()); - return Valueify(u.v_); + return u.v_; } JSValueType knownType() const { JS_ASSERT(isTypeKnown()); @@ -191,6 +210,8 @@ struct ValueRemat { return JSVAL_TYPE_DOUBLE; return v.extractNonDoubleType(); } + if (isFPRegister()) + return JSVAL_TYPE_DOUBLE; return u.s.type.knownType_; } bool isType(JSValueType type_) const { @@ -203,6 +224,7 @@ struct ValueRemat { */ struct RematInfo { typedef JSC::MacroAssembler::RegisterID RegisterID; + typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; enum SyncState { SYNCED, @@ -224,9 +246,12 @@ struct RematInfo { /* Backing bits are known at compile time. */ PhysLoc_Constant, - /* Backing bits are in a register. */ + /* Backing bits are in a general purpose register. */ PhysLoc_Register, + /* Backing bits are part of a floating point register. */ + PhysLoc_FPRegister, + /* Backing bits are invalid/unknown. */ PhysLoc_Invalid }; @@ -241,20 +266,51 @@ struct RematInfo { return reg_; } + void setFPRegister(FPRegisterID reg) { + fpreg_ = reg; + location_ = PhysLoc_FPRegister; + } + + FPRegisterID fpreg() const { + JS_ASSERT(inFPRegister()); + return fpreg_; + } + void setMemory() { location_ = PhysLoc_Memory; sync_ = SYNCED; } +#ifdef DEBUG void invalidate() { location_ = PhysLoc_Invalid; } +#else + void invalidate() {} +#endif void setConstant() { location_ = PhysLoc_Constant; } - bool isConstant() const { return location_ == PhysLoc_Constant; } - bool inRegister() const { return location_ == PhysLoc_Register; } - bool inMemory() const { return location_ == PhysLoc_Memory; } + bool isConstant() const { + JS_ASSERT(location_ != PhysLoc_Invalid); + return location_ == PhysLoc_Constant; + } + + bool inRegister() const { + JS_ASSERT(location_ != PhysLoc_Invalid); + return location_ == PhysLoc_Register; + } + + bool inFPRegister() const { + JS_ASSERT(location_ != PhysLoc_Invalid); + return location_ == PhysLoc_FPRegister; + } + + bool inMemory() const { + JS_ASSERT(location_ != PhysLoc_Invalid); + return location_ == PhysLoc_Memory; + } + bool synced() const { return sync_ == SYNCED; } void sync() { JS_ASSERT(!synced()); @@ -265,13 +321,22 @@ struct RematInfo { } void inherit(const RematInfo &other) { + JS_STATIC_ASSERT(sizeof(RegisterID) == sizeof(FPRegisterID)); reg_ = other.reg_; location_ = other.location_; } private: - /* Set if location is PhysLoc_Register. */ - RegisterID reg_; + union { + /* Set if location is PhysLoc_Register. */ + RegisterID reg_; + + /* + * Set if location is PhysLoc_FPRegister. This must be the data for a FE, + * and the known type is JSVAL_TYPE_DOUBLE. + */ + FPRegisterID fpreg_; + }; /* Remat source. */ PhysLoc location_; @@ -280,6 +345,59 @@ struct RematInfo { SyncState sync_; }; +template +class MaybeRegister { + public: + MaybeRegister() + : reg_((T)0), set(false) + { } + + MaybeRegister(T reg) + : reg_(reg), set(true) + { } + + inline T reg() const { JS_ASSERT(set); return reg_; } + inline void setReg(T r) { reg_ = r; set = true; } + inline bool isSet() const { return set; } + + MaybeRegister & operator =(const MaybeRegister &other) { + set = other.set; + reg_ = other.reg_; + return *this; + } + + MaybeRegister & operator =(T r) { + setReg(r); + return *this; + } + + private: + T reg_; + bool set; +}; + +typedef MaybeRegister MaybeRegisterID; +typedef MaybeRegister MaybeFPRegisterID; + +class MaybeJump { + typedef JSC::MacroAssembler::Jump Jump; + public: + MaybeJump() + : set(false) + { } + + inline Jump getJump() const { JS_ASSERT(set); return jump; } + inline Jump get() const { JS_ASSERT(set); return jump; } + inline void setJump(const Jump &j) { jump = j; set = true; } + inline bool isSet() const { return set; } + + inline MaybeJump &operator=(Jump j) { setJump(j); return *this; } + + private: + Jump jump; + bool set; +}; + } /* namespace mjit */ } /* namespace js */ diff --git a/deps/mozjs/js/src/methodjit/Retcon.cpp b/deps/mozjs/js/src/methodjit/Retcon.cpp index 05dd2e1e07e..682c866df37 100644 --- a/deps/mozjs/js/src/methodjit/Retcon.cpp +++ b/deps/mozjs/js/src/methodjit/Retcon.cpp @@ -43,10 +43,14 @@ #include "Retcon.h" #include "MethodJIT.h" #include "Compiler.h" +#include "StubCalls.h" #include "jsdbgapi.h" #include "jsnum.h" +#include "assembler/assembler/LinkBuffer.h" +#include "assembler/assembler/RepatchBuffer.h" #include "jscntxtinlines.h" +#include "jsinterpinlines.h" using namespace js; using namespace js::mjit; @@ -54,168 +58,435 @@ using namespace js::mjit; namespace js { namespace mjit { -AutoScriptRetrapper::~AutoScriptRetrapper() +static inline void +SetRejoinState(StackFrame *fp, const CallSite &site, void **location) { - while (!traps.empty()) { - jsbytecode *pc = traps.back(); - traps.popBack(); - *pc = JSOP_TRAP; + if (site.rejoin == REJOIN_SCRIPTED) { + fp->setRejoin(ScriptedRejoin(site.pcOffset)); + *location = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted); + } else { + fp->setRejoin(StubRejoin(site.rejoin)); + *location = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline); } } -bool -AutoScriptRetrapper::untrap(jsbytecode *pc) +static inline bool +CallsiteMatches(uint8_t *codeStart, const CallSite &site, void *location) { - if (!traps.append(pc)) - return false; - *pc = JS_GetTrapOpcode(traps.allocPolicy().context(), script, pc); - return true; + if (codeStart + site.codeOffset == location) + return true; + +#ifdef JS_CPU_ARM + if (codeStart + site.codeOffset + 4 == location) + return true; +#endif + + return false; } -Recompiler::PatchableAddress -Recompiler::findPatch(JITScript *jit, void **location) -{ - uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); - CallSite *callSites_ = jit->callSites(); - for (uint32 i = 0; i < jit->nCallSites; i++) { - if (callSites_[i].codeOffset + codeStart == *location) { - PatchableAddress result; - result.location = location; - result.callSite = callSites_[i]; - return result; +void +Recompiler::patchCall(JITChunk *chunk, StackFrame *fp, void **location) +{ + uint8_t* codeStart = (uint8_t *)chunk->code.m_code.executableAddress(); + + CallSite *callSites_ = chunk->callSites(); + for (uint32_t i = 0; i < chunk->nCallSites; i++) { + if (CallsiteMatches(codeStart, callSites_[i], *location)) { + JS_ASSERT(callSites_[i].inlineIndex == analyze::CrossScriptSSA::OUTER_FRAME); + SetRejoinState(fp, callSites_[i], location); + return; } } JS_NOT_REACHED("failed to find call site"); - return PatchableAddress(); } void -Recompiler::applyPatch(Compiler& c, PatchableAddress& toPatch) +Recompiler::patchNative(JSCompartment *compartment, JITChunk *chunk, StackFrame *fp, + jsbytecode *pc, RejoinState rejoin) +{ + /* + * There is a native call or getter IC at pc which triggered recompilation. + * The recompilation could have been triggered either by the native call + * itself, or by a SplatApplyArgs preparing for the native call. Either + * way, we don't want to patch up the call, but will instead steal the pool + * for the IC so it doesn't get freed with the JITChunk, and patch up the + * jump at the end to go to the interpoline. + * + * When doing this, we do not reset the the IC itself; the JITChunk must + * be dead and about to be released due to the recompilation (or a GC). + */ + fp->setRejoin(StubRejoin(rejoin)); + + /* :XXX: We might crash later if this fails. */ + compartment->jaegerCompartment()->orphanedNativeFrames.append(fp); + + DebugOnly found = false; + + /* + * Find and patch all native call stubs attached to the given PC. There may + * be multiple ones for getter stubs attached to e.g. a GETELEM. + */ + for (unsigned i = 0; i < chunk->nativeCallStubs.length(); i++) { + NativeCallStub &stub = chunk->nativeCallStubs[i]; + if (stub.pc != pc) + continue; + + found = true; + + /* Check for pools that were already patched. */ + if (!stub.pool) + continue; + + /* Patch the native fallthrough to go to the interpoline. */ + { +#if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) + /* Win64 needs stack adjustment */ + void *interpoline = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolinePatched); +#else + void *interpoline = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline); +#endif + uint8_t *start = (uint8_t *)stub.jump.executableAddress(); + JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64)); +#ifdef JS_CPU_X64 + repatch.repatch(stub.jump, interpoline); +#else + repatch.relink(stub.jump, JSC::CodeLocationLabel(interpoline)); +#endif + } + + /* :XXX: We leak the pool if this fails. Oh well. */ + compartment->jaegerCompartment()->orphanedNativePools.append(stub.pool); + + /* Mark as stolen in case there are multiple calls on the stack. */ + stub.pool = NULL; + } + + JS_ASSERT(found); +} + +void +Recompiler::patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script) { - void *result = c.findCallSite(toPatch.callSite); - JS_ASSERT(result); - *toPatch.location = result; + /* + * Check if the VMFrame returns directly into the script's jitcode. This + * depends on the invariant that f->fp() reflects the frame at the point + * where the call occurred, irregardless of any frames which were pushed + * inside the call. + */ + StackFrame *fp = f->fp(); + void **addr = f->returnAddressLocation(); + RejoinState rejoin = (RejoinState) f->stubRejoin; + if (rejoin == REJOIN_NATIVE || + rejoin == REJOIN_NATIVE_LOWERED || + rejoin == REJOIN_NATIVE_GETTER) { + /* Native call. */ + if (fp->script() == script) { + patchNative(compartment, fp->jit()->chunk(f->regs.pc), fp, f->regs.pc, rejoin); + f->stubRejoin = REJOIN_NATIVE_PATCHED; + } + } else if (rejoin == REJOIN_NATIVE_PATCHED) { + /* Already patched, don't do anything. */ + } else if (rejoin) { + /* Recompilation triggered by CompileFunction. */ + if (fp->script() == script) { + fp->setRejoin(StubRejoin(rejoin)); + *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline); + f->stubRejoin = 0; + } + } else { + if (script->jitCtor) { + JITChunk *chunk = script->jitCtor->findCodeChunk(*addr); + if (chunk) + patchCall(chunk, fp, addr); + } + if (script->jitNormal) { + JITChunk *chunk = script->jitNormal->findCodeChunk(*addr); + if (chunk) + patchCall(chunk, fp, addr); + } + } +} + +StackFrame * +Recompiler::expandInlineFrameChain(StackFrame *outer, InlineFrame *inner) +{ + StackFrame *parent; + if (inner->parent) + parent = expandInlineFrameChain(outer, inner->parent); + else + parent = outer; + + JaegerSpew(JSpew_Recompile, "Expanding inline frame\n"); + + StackFrame *fp = (StackFrame *) ((uint8_t *)outer + sizeof(Value) * inner->depth); + fp->initInlineFrame(inner->fun, parent, inner->parentpc); + uint32_t pcOffset = inner->parentpc - parent->script()->code; + + void **location = fp->addressOfNativeReturnAddress(); + *location = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted); + parent->setRejoin(ScriptedRejoin(pcOffset)); + + return fp; } -Recompiler::Recompiler(JSContext *cx, JSScript *script) - : cx(cx), script(script) -{ +/* + * Whether a given return address for a frame indicates it returns directly + * into JIT code. + */ +static inline bool +JITCodeReturnAddress(void *data) +{ + return data != NULL /* frame is interpreted */ + && data != JS_FUNC_TO_DATA_PTR(void *, JaegerTrampolineReturn) + && data != JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline) +#if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) + && data != JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolinePatched) +#endif + && data != JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted); } /* - * The strategy for this goes as follows: - * - * 1) Scan the stack, looking at all return addresses that could go into JIT - * code. - * 2) If an address corresponds to a call site registered by |callSite| during - * the last compilation, remember it. - * 3) Purge the old compiled state and return if there were no active frames of - * this script on the stack. - * 4) Fix up the stack by replacing all saved addresses with the addresses the - * new compiler gives us for the call sites. + * Expand all inlined frames within fp per 'inlined' and update next and regs + * to refer to the new innermost frame. */ -bool -Recompiler::recompile() +void +Recompiler::expandInlineFrames(JSCompartment *compartment, + StackFrame *fp, mjit::CallSite *inlined, + StackFrame *next, VMFrame *f) { - JS_ASSERT(script->hasJITCode()); + JS_ASSERT_IF(next, next->prev() == fp && next->prevInline() == inlined); + + /* + * Treat any frame expansion as a recompilation event, so that f.jit() is + * stable if no recompilations have occurred. + */ + compartment->types.frameExpansions++; + + jsbytecode *pc = next ? next->prevpc(NULL) : f->regs.pc; + JITChunk *chunk = fp->jit()->chunk(pc); + + /* + * Patch the VMFrame's return address if it is returning at the given inline site. + * Note there is no worry about handling a native or CompileFunction call here, + * as such IC stubs are not generated within inline frames. + */ + void **frameAddr = f->returnAddressLocation(); + uint8_t* codeStart = (uint8_t *)chunk->code.m_code.executableAddress(); + + InlineFrame *inner = &chunk->inlineFrames()[inlined->inlineIndex]; + jsbytecode *innerpc = inner->fun->script()->code + inlined->pcOffset; + + StackFrame *innerfp = expandInlineFrameChain(fp, inner); + + /* Check if the VMFrame returns into the inlined frame. */ + if (f->stubRejoin && f->fp() == fp) { + /* The VMFrame is calling CompileFunction. */ + JS_ASSERT(f->stubRejoin != REJOIN_NATIVE && + f->stubRejoin != REJOIN_NATIVE_LOWERED && + f->stubRejoin != REJOIN_NATIVE_GETTER && + f->stubRejoin != REJOIN_NATIVE_PATCHED); + innerfp->setRejoin(StubRejoin((RejoinState) f->stubRejoin)); + *frameAddr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline); + f->stubRejoin = 0; + } + if (CallsiteMatches(codeStart, *inlined, *frameAddr)) { + /* The VMFrame returns directly into the expanded frame. */ + SetRejoinState(innerfp, *inlined, frameAddr); + } + + if (f->fp() == fp) { + JS_ASSERT(f->regs.inlined() == inlined); + f->regs.expandInline(innerfp, innerpc); + } - Vector normalPatches(cx); - Vector ctorPatches(cx); + /* + * Note: unlike the case for recompilation, during frame expansion we don't + * need to worry about the next VMFrame holding a reference to the inlined + * frame in its entryncode. entryncode is non-NULL only if the next frame's + * code was discarded and has executed via the Interpoline, which can only + * happen after all inline frames have been expanded. + */ + + if (next) { + next->resetInlinePrev(innerfp, innerpc); + void **addr = next->addressOfNativeReturnAddress(); + if (JITCodeReturnAddress(*addr)) { + innerfp->setRejoin(ScriptedRejoin(inlined->pcOffset)); + *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted); + } + } +} - StackFrame *firstCtorFrame = NULL; - StackFrame *firstNormalFrame = NULL; +void +ExpandInlineFrames(JSCompartment *compartment) +{ + if (!compartment || !compartment->hasJaegerCompartment()) + return; - // Find all JIT'd stack frames to account for return addresses that will - // need to be patched after recompilation. - for (VMFrame *f = script->compartment->jaegerCompartment->activeFrame(); + for (VMFrame *f = compartment->jaegerCompartment()->activeFrame(); f != NULL; f = f->previous) { - // Scan all frames owned by this VMFrame. + if (f->regs.inlined()) + mjit::Recompiler::expandInlineFrames(compartment, f->fp(), f->regs.inlined(), NULL, f); + StackFrame *end = f->entryfp->prev(); + StackFrame *next = NULL; for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) { - // Remember the latest frame for each type of JIT'd code, so the - // compiler will have a frame to re-JIT from. - if (!firstCtorFrame && fp->script() == script && fp->isConstructing()) - firstCtorFrame = fp; - else if (!firstNormalFrame && fp->script() == script && !fp->isConstructing()) - firstNormalFrame = fp; - - void **addr = fp->addressOfNativeReturnAddress(); - if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { - if (!ctorPatches.append(findPatch(script->jitCtor, addr))) - return false; - } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { - if (!normalPatches.append(findPatch(script->jitNormal, addr))) - return false; + if (!next) { + next = fp; + continue; } - } - - void **addr = f->returnAddressLocation(); - if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { - if (!ctorPatches.append(findPatch(script->jitCtor, addr))) - return false; - } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { - if (!normalPatches.append(findPatch(script->jitNormal, addr))) - return false; + mjit::CallSite *inlined; + next->prevpc(&inlined); + if (inlined) { + mjit::Recompiler::expandInlineFrames(compartment, fp, inlined, next, f); + fp = next; + next = NULL; + } else { + if (fp->downFramesExpanded()) + break; + next = fp; + } + fp->setDownFramesExpanded(); } } +} - Vector normalSites(cx); - Vector ctorSites(cx); +void +ClearAllFrames(JSCompartment *compartment) +{ + if (!compartment || !compartment->hasJaegerCompartment()) + return; - if (script->jitNormal && !saveTraps(script->jitNormal, &normalSites)) - return false; - if (script->jitCtor && !saveTraps(script->jitCtor, &ctorSites)) - return false; + ExpandInlineFrames(compartment); - ReleaseScriptCode(cx, script); + for (VMFrame *f = compartment->jaegerCompartment()->activeFrame(); + f != NULL; + f = f->previous) { - if (normalPatches.length() && - !recompile(firstNormalFrame, normalPatches, normalSites)) { - return false; - } + Recompiler::patchFrame(compartment, f, f->fp()->script()); - if (ctorPatches.length() && - !recompile(firstCtorFrame, ctorPatches, ctorSites)) { - return false; - } + // Clear ncode values from all frames associated with the VMFrame. + // Patching the VMFrame's return address will cause all its frames to + // finish in the interpreter, unless the interpreter enters one of the + // intermediate frames at a loop boundary (where EnterMethodJIT will + // overwrite ncode). However, leaving stale values for ncode in stack + // frames can confuse the recompiler, which may see the VMFrame before + // it has resumed execution. - return true; + for (StackFrame *fp = f->fp(); fp != f->entryfp; fp = fp->prev()) + fp->setNativeReturnAddress(NULL); + } } -bool -Recompiler::saveTraps(JITScript *jit, Vector *sites) +/* + * Recompilation can be triggered either by the debugger (turning debug mode on for + * a script or setting/clearing a trap), or by dynamic changes in type information + * from type inference. When recompiling we don't immediately recompile the JIT + * code, but destroy the old code and remove all references to the code, including + * those from active stack frames. Things to do: + * + * - Purge scripted call inline caches calling into the script. + * + * - For frames with an ncode return address in the original script, redirect + * to the interpoline. + * + * - For VMFrames with a stub call return address in the original script, + * redirect to the interpoline. + * + * - For VMFrames whose entryncode address (the value of entryfp->ncode before + * being clobbered with JaegerTrampolineReturn) is in the original script, + * redirect that entryncode to the interpoline. + */ +void +Recompiler::clearStackReferences(JSContext *cx, JSScript *script) { - CallSite *callSites_ = jit->callSites(); - for (uint32 i = 0; i < jit->nCallSites; i++) { - CallSite &site = callSites_[i]; - if (site.isTrap() && !sites->append(site)) - return false; + JS_ASSERT(script->hasJITCode()); + + JaegerSpew(JSpew_Recompile, "recompiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", + script->filename, script->lineno, script->length); + + types::AutoEnterTypeInference enter(cx, true); + + /* + * The strategy for this goes as follows: + * + * 1) Scan the stack, looking at all return addresses that could go into JIT + * code. + * 2) If an address corresponds to a call site registered by |callSite| during + * the last compilation, patch it to go to the interpoline. + * 3) Purge the old compiled state. + */ + + // Find all JIT'd stack frames to account for return addresses that will + // need to be patched after recompilation. + for (VMFrame *f = script->compartment()->jaegerCompartment()->activeFrame(); + f != NULL; + f = f->previous) { + + // Scan all frames owned by this VMFrame. + StackFrame *end = f->entryfp->prev(); + StackFrame *next = NULL; + for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) { + if (fp->script() != script) { + next = fp; + continue; + } + + if (next) { + // check for a scripted call returning into the recompiled script. + // this misses scanning the entry fp, which cannot return directly + // into JIT code. + void **addr = next->addressOfNativeReturnAddress(); + + if (JITCodeReturnAddress(*addr)) { + JITChunk *chunk = fp->jit()->findCodeChunk(*addr); + patchCall(chunk, fp, addr); + } + } + + next = fp; + } + + patchFrame(cx->compartment, f, script); } - return true; + + cx->compartment->types.recompilations++; } -bool -Recompiler::recompile(StackFrame *fp, Vector &patches, - Vector &sites) +void +Recompiler::clearStackReferencesAndChunk(JSContext *cx, JSScript *script, + JITScript *jit, size_t chunkIndex, + bool resetUses) { - /* If we get this far, the script is live, and we better be safe to re-jit. */ - JS_ASSERT(cx->compartment->debugMode); - JS_ASSERT(fp); - - Compiler c(cx, fp); - if (!c.loadOldTraps(sites)) - return false; - if (c.compile() != Compile_Okay) - return false; - - /* Perform the earlier scanned patches */ - for (uint32 i = 0; i < patches.length(); i++) - applyPatch(c, patches[i]); + Recompiler::clearStackReferences(cx, script); + + bool releaseChunk = true; + if (jit->nchunks > 1) { + // If we are in the middle of a native call from a native or getter IC, + // we need to make sure all JIT code for the script is purged, as + // otherwise we will have orphaned the native stub but pointers to it + // still exist in the containing chunk. + for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame(); + f != NULL; + f = f->previous) { + if (f->fp()->script() == script) { + JS_ASSERT(f->stubRejoin != REJOIN_NATIVE && + f->stubRejoin != REJOIN_NATIVE_LOWERED && + f->stubRejoin != REJOIN_NATIVE_GETTER); + if (f->stubRejoin == REJOIN_NATIVE_PATCHED) { + mjit::ReleaseScriptCode(cx, script); + releaseChunk = false; + break; + } + } + } + } - return true; + if (releaseChunk) + jit->destroyChunk(cx, chunkIndex, resetUses); } } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/Retcon.h b/deps/mozjs/js/src/methodjit/Retcon.h index 582a8167581..222d1a9db82 100644 --- a/deps/mozjs/js/src/methodjit/Retcon.h +++ b/deps/mozjs/js/src/methodjit/Retcon.h @@ -55,52 +55,42 @@ namespace js { namespace mjit { /* - * A problem often arises where, for one reason or another, a piece of code - * wants to touch the script->code, but isn't expecting JSOP_TRAP. This allows - * one to temporarily remove JSOP_TRAPs from the instruction stream (without - * copying) and automatically re-add them on scope exit. + * This class is responsible for sanely destroying a JITed script while frames + * for it are still on the stack, removing all references in the world to it + * and patching up those existing frames to go into the interpreter. If you + * ever change the code associated with a JSScript, or otherwise would cause + * existing JITed code to be incorrect, you /must/ use this to invalidate the + * JITed code, fixing up the stack in the process. */ -class AutoScriptRetrapper -{ - public: - AutoScriptRetrapper(JSContext *cx, JSScript *script1) : - script(script1), traps(cx) {}; - ~AutoScriptRetrapper(); +class Recompiler { +public: - bool untrap(jsbytecode *pc); + // Clear all uses of compiled code for script on the stack. This must be + // followed by destroying all JIT code for the script. + static void + clearStackReferences(JSContext *cx, JSScript *script); - private: - JSScript *script; - Vector traps; -}; + // Clear all uses of compiled code for script on the stack, along with + // the specified compiled chunk. + static void + clearStackReferencesAndChunk(JSContext *cx, JSScript *script, + JITScript *jit, size_t chunkIndex, + bool resetUses = true); -/* - * This class is responsible for sanely re-JITing a script and fixing up - * the world. If you ever change the code associated with a JSScript, or - * otherwise would cause existing JITed code to be incorrect, you /must/ use - * this to invalidate and potentially re-compile the existing JITed code, - * fixing up the stack in the process. - */ -class Recompiler { - struct PatchableAddress { - void **location; - CallSite callSite; - }; - -public: - Recompiler(JSContext *cx, JSScript *script); - - bool recompile(); + static void + expandInlineFrames(JSCompartment *compartment, StackFrame *fp, mjit::CallSite *inlined, + StackFrame *next, VMFrame *f); + + static void patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script); private: - JSContext *cx; - JSScript *script; - - PatchableAddress findPatch(JITScript *jit, void **location); - void applyPatch(Compiler& c, PatchableAddress& toPatch); - bool recompile(StackFrame *fp, Vector &patches, - Vector &sites); - bool saveTraps(JITScript *jit, Vector *sites); + + static void patchCall(JITChunk *chunk, StackFrame *fp, void **location); + static void patchNative(JSCompartment *compartment, JITChunk *chunk, StackFrame *fp, + jsbytecode *pc, RejoinState rejoin); + + static StackFrame * + expandInlineFrameChain(StackFrame *outer, InlineFrame *inner); }; } /* namespace mjit */ diff --git a/deps/mozjs/js/src/methodjit/StubCalls-inl.h b/deps/mozjs/js/src/methodjit/StubCalls-inl.h index 84ce86153d2..39f9d112386 100644 --- a/deps/mozjs/js/src/methodjit/StubCalls-inl.h +++ b/deps/mozjs/js/src/methodjit/StubCalls-inl.h @@ -38,7 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef jslogic_h_inl__ +#if !defined jslogic_h_inl__ && defined JS_METHODJIT #define jslogic_h_inl__ namespace js { @@ -51,16 +51,8 @@ ThrowException(VMFrame &f) *f.returnAddressLocation() = ptr; } -#define THROW() do { ThrowException(f); return; } while (0) -#define THROWV(v) do { ThrowException(f); return v; } while (0) - -static inline JSObject * -ValueToObject(JSContext *cx, Value *vp) -{ - if (vp->isObject()) - return &vp->toObject(); - return js_ValueToNonNullObject(cx, *vp); -} +#define THROW() do { mjit::ThrowException(f); return; } while (0) +#define THROWV(v) do { mjit::ThrowException(f); return v; } while (0) static inline void ReportAtomNotDefined(JSContext *cx, JSAtom *atom) @@ -73,10 +65,10 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) #define NATIVE_SET(cx,obj,shape,entry,strict,vp) \ JS_BEGIN_MACRO \ if (shape->hasDefaultSetter() && \ - (shape)->slot != SHAPE_INVALID_SLOT && \ - !(obj)->brandedOrHasMethodBarrier()) { \ + (shape)->hasSlot() && \ + !(shape)->isMethod()) { \ /* Fast path for, e.g., plain Object instance properties. */ \ - (obj)->nativeSetSlot((shape)->slot, *vp); \ + (obj)->nativeSetSlotWithType(cx, shape, *vp); \ } else { \ if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \ THROW(); \ @@ -87,10 +79,10 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) JS_BEGIN_MACRO \ if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \ /* Fast path for Object instance properties. */ \ - JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \ + JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT || \ !shape->hasDefaultSetter()); \ - if (((shape)->slot != SHAPE_INVALID_SLOT)) \ - *(vp) = (pobj)->nativeGetSlot((shape)->slot); \ + if (((shape)->slot() != SHAPE_INVALID_SLOT)) \ + *(vp) = (pobj)->nativeGetSlot((shape)->slot()); \ else \ (vp)->setUndefined(); \ } else { \ diff --git a/deps/mozjs/js/src/methodjit/StubCalls.cpp b/deps/mozjs/js/src/methodjit/StubCalls.cpp index c6ce8724649..c9a8d113432 100644 --- a/deps/mozjs/js/src/methodjit/StubCalls.cpp +++ b/deps/mozjs/js/src/methodjit/StubCalls.cpp @@ -43,22 +43,23 @@ #include "jsobj.h" #include "jslibmath.h" #include "jsiter.h" +#include "jsgcmark.h" #include "jsnum.h" #include "jsxml.h" -#include "jsstaticcheck.h" #include "jsbool.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" #include "jsiter.h" #include "jstypes.h" +#include "vm/Debugger.h" +#include "vm/String.h" #include "methodjit/Compiler.h" #include "methodjit/StubCalls.h" +#include "methodjit/Retcon.h" #include "jsinterpinlines.h" -#include "jspropertycache.h" -#include "jspropertycacheinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" -#include "jsstrinlines.h" +#include "jsnuminlines.h" #include "jsobjinlines.h" #include "jscntxtinlines.h" #include "jsatominlines.h" @@ -66,6 +67,9 @@ #include "jsfuninlines.h" #include "jstypedarray.h" +#include "vm/RegExpObject-inl.h" +#include "vm/String-inl.h" + #ifdef XP_WIN # include "jswin.h" #endif @@ -74,35 +78,13 @@ using namespace js; using namespace js::mjit; +using namespace js::types; using namespace JSC; void JS_FASTCALL -stubs::BindName(VMFrame &f) -{ - PropertyCacheEntry *entry; - - /* Fast-path should have caught this. See comment in interpreter. */ - JS_ASSERT(f.fp()->scopeChain().getParent()); - - JSAtom *atom; - JSObject *obj2; - JSContext *cx = f.cx; - JSObject *obj = &f.fp()->scopeChain(); - JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom); - if (atom) { - jsid id = ATOM_TO_JSID(atom); - obj = js_FindIdentifierBase(cx, &f.fp()->scopeChain(), id); - if (!obj) - THROW(); - } - f.regs.sp++; - f.regs.sp[-1].setObject(*obj); -} - -void JS_FASTCALL -stubs::BindNameNoCache(VMFrame &f, JSAtom *atom) +stubs::BindName(VMFrame &f, PropertyName *name) { - JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom)); + JSObject *obj = FindIdentifierBase(f.cx, &f.fp()->scopeChain(), name); if (!obj) THROW(); f.regs.sp[0].setObject(*obj); @@ -111,300 +93,43 @@ stubs::BindNameNoCache(VMFrame &f, JSAtom *atom) JSObject * JS_FASTCALL stubs::BindGlobalName(VMFrame &f) { - return f.fp()->scopeChain().getGlobal(); -} - -template -void JS_FASTCALL -stubs::SetName(VMFrame &f, JSAtom *origAtom) -{ - JSContext *cx = f.cx; - - Value rval = f.regs.sp[-1]; - Value &lref = f.regs.sp[-2]; - JSObject *obj = ValueToObject(cx, &lref); - if (!obj) - THROW(); - - do { - PropertyCache *cache = &JS_PROPERTY_CACHE(cx); - - /* - * Probe the property cache, specializing for two important - * set-property cases. First: - * - * function f(a, b, c) { - * var o = {p:a, q:b, r:c}; - * return o; - * } - * - * or similar real-world cases, which evolve a newborn native - * object predicatably through some bounded number of property - * additions. And second: - * - * o.p = x; - * - * in a frequently executed method or loop body, where p will - * (possibly after the first iteration) always exist in native - * object o. - */ - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) { - /* - * Property cache hit, only partially confirmed by testForSet. We - * know that the entry applies to regs.pc and that obj's shape - * matches. - * - * The entry predicts either a new property to be added directly to - * obj by this set, or on an existing "own" property, or on a - * prototype property that has a setter. - */ - const Shape *shape = entry->vword.toShape(); - JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable()); - JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0); - - /* - * Fastest path: check whether obj already has the cached shape and - * call NATIVE_SET and break to get out of the do-while(0). But we - * can call NATIVE_SET only for a direct or proto-setter hit. - */ - if (!entry->adding()) { - if (entry->vcapTag() == 0 || - ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape())) - { -#ifdef DEBUG - if (entry->directHit()) { - JS_ASSERT(obj->nativeContains(*shape)); - } else { - JS_ASSERT(obj2->nativeContains(*shape)); - JS_ASSERT(entry->vcapTag() == 1); - JS_ASSERT(entry->kshape != entry->vshape()); - JS_ASSERT(!shape->hasSlot()); - } -#endif - - PCMETER(cache->pchits++); - PCMETER(cache->setpchits++); - NATIVE_SET(cx, obj, shape, entry, strict, &rval); - break; - } - } else { - JS_ASSERT(obj->isExtensible()); - - if (obj->nativeEmpty()) { - if (!obj->ensureClassReservedSlotsForEmptyObject(cx)) - THROW(); - } - - uint32 slot; - if (shape->previous() == obj->lastProperty() && - entry->vshape() == cx->runtime->protoHazardShape && - shape->hasDefaultSetter()) { - slot = shape->slot; - JS_ASSERT(slot == obj->slotSpan()); - - /* - * Fast path: adding a plain old property that was once at - * the frontier of the property tree, whose slot is next to - * claim among the already-allocated slots in obj, where - * shape->table has not been created yet. - */ - PCMETER(cache->pchits++); - PCMETER(cache->addpchits++); - - if (slot < obj->numSlots()) { - JS_ASSERT(obj->getSlot(slot).isUndefined()); - } else { - if (!obj->allocSlot(cx, &slot)) - THROW(); - JS_ASSERT(slot == shape->slot); - } - - /* Simply extend obj's property tree path with shape! */ - obj->extend(cx, shape); - - /* - * No method change check here because here we are adding a - * new property, not updating an existing slot's value that - * might contain a method of a branded shape. - */ - obj->setSlot(slot, rval); - - /* - * Purge the property cache of the id we may have just - * shadowed in obj's scope and proto chains. - */ - js_PurgeScopeChain(cx, obj, shape->id); - break; - } - } - PCMETER(cache->setpcmisses++); - - atom = origAtom; - } else { - JS_ASSERT(atom); - } - - jsid id = ATOM_TO_JSID(atom); - if (entry && JS_LIKELY(!obj->getOps()->setProperty)) { - uintN defineHow; - JSOp op = JSOp(*f.regs.pc); - if (op == JSOP_SETMETHOD) - defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD; - else if (op == JSOP_SETNAME) - defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED; - else - defineHow = JSDNP_CACHE_RESULT; - if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, strict)) - THROW(); - } else { - if (!obj->setProperty(cx, id, &rval, strict)) - THROW(); - } - } while (0); - - f.regs.sp[-2] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); -template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); - -template -void JS_FASTCALL -stubs::SetPropNoCache(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - Value rval = f.regs.sp[-1]; - if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict)) - THROW(); - f.regs.sp[-2] = rval; + return &f.fp()->scopeChain().global(); } -template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); -template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); - template void JS_FASTCALL -stubs::SetGlobalNameNoCache(VMFrame &f, JSAtom *atom) +stubs::SetName(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx; + const Value &rval = f.regs.sp[-1]; + const Value &lval = f.regs.sp[-2]; - Value rval = f.regs.sp[-1]; - Value &lref = f.regs.sp[-2]; - JSObject *obj = ValueToObject(cx, &lref); - if (!obj) - THROW(); - jsid id = ATOM_TO_JSID(atom); - if (!obj->setProperty(cx, id, &rval, strict)) + if (!SetPropertyOperation(cx, f.pc(), lval, rval)) THROW(); f.regs.sp[-2] = f.regs.sp[-1]; } -template void JS_FASTCALL stubs::SetGlobalNameNoCache(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::SetGlobalNameNoCache(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL stubs::SetName(VMFrame &f, PropertyName *origName); +template void JS_FASTCALL stubs::SetName(VMFrame &f, PropertyName *origName); template void JS_FASTCALL -stubs::SetGlobalName(VMFrame &f, JSAtom *atom) -{ - SetName(f, atom); -} - -template void JS_FASTCALL stubs::SetGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::SetGlobalName(VMFrame &f, JSAtom *atom); - -static inline void -PushImplicitThis(VMFrame &f, JSObject *obj, Value &rval) +stubs::SetGlobalName(VMFrame &f, PropertyName *name) { - Value thisv; - - if (!ComputeImplicitThis(f.cx, obj, rval, &thisv)) - return; - *f.regs.sp++ = thisv; + SetName(f, name); } -static JSObject * -NameOp(VMFrame &f, JSObject *obj, bool callname = false) -{ - JSContext *cx = f.cx; - - const Shape *shape; - Value rval; - - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom); - if (!atom) { - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uintN slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - shape = entry->vword.toShape(); - NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL); - } - - JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - } else { - jsid id; - id = ATOM_TO_JSID(atom); - JSProperty *prop; - if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) - return NULL; - if (!prop) { - /* Kludge to allow (typeof foo == "undefined") tests. */ - JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH); - if (op2 == JSOP_TYPEOF) { - f.regs.sp++; - f.regs.sp[-1].setUndefined(); - return obj; - } - ReportAtomNotDefined(cx, atom); - return NULL; - } - - /* Take the slow path if prop was not found in a native object. */ - if (!obj->isNative() || !obj2->isNative()) { - if (!obj->getProperty(cx, id, &rval)) - return NULL; - } else { - shape = (Shape *)prop; - JSObject *normalized = obj; - if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter()) - normalized = js_UnwrapWithObject(cx, normalized); - NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL); - } - } - - *f.regs.sp++ = rval; - - if (callname) - PushImplicitThis(f, obj, rval); - - return obj; -} +template void JS_FASTCALL stubs::SetGlobalName(VMFrame &f, PropertyName *name); +template void JS_FASTCALL stubs::SetGlobalName(VMFrame &f, PropertyName *name); void JS_FASTCALL stubs::Name(VMFrame &f) { - if (!NameOp(f, &f.fp()->scopeChain())) + Value rval; + if (!NameOperation(f.cx, f.pc(), &rval)) THROW(); -} - -void JS_FASTCALL -stubs::GetGlobalName(VMFrame &f) -{ - JSObject *globalObj = f.fp()->scopeChain().getGlobal(); - if (!NameOp(f, globalObj)) - THROW(); + f.regs.sp[0] = rval; } void JS_FASTCALL @@ -415,115 +140,74 @@ stubs::GetElem(VMFrame &f) Value &lref = regs.sp[-2]; Value &rref = regs.sp[-1]; + Value &rval = regs.sp[-2]; if (lref.isString() && rref.isInt32()) { JSString *str = lref.toString(); int32_t i = rref.toInt32(); if ((size_t)i < str->length()) { - str = JSAtom::getUnitStringForElement(cx, str, (size_t)i); + str = f.cx->runtime->staticStrings.getUnitStringForElement(cx, str, (size_t)i); if (!str) THROW(); - f.regs.sp[-2].setString(str); + rval.setString(str); + return; + } + } + + if (lref.isMagic(JS_LAZY_ARGUMENTS)) { + if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) { + rval = regs.fp()->canonicalActualArg(rref.toInt32()); return; } + MarkArgumentsCreated(cx, f.script()); + JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS)); } - JSObject *obj = ValueToObject(cx, &lref); + bool isObject = lref.isObject(); + JSObject *obj = ValueToObject(cx, lref); if (!obj) THROW(); - const Value *copyFrom; - Value rval; - jsid id; - if (rref.isInt32()) { - int32_t i = rref.toInt32(); + uint32_t index; + if (IsDefinitelyIndex(rref, &index)) { if (obj->isDenseArray()) { - jsuint idx = jsuint(i); - - if (idx < obj->getArrayLength() && - idx < obj->getDenseArrayCapacity()) { - copyFrom = obj->addressOfDenseArrayElement(idx); - if (!copyFrom->isMagic()) - goto end_getelem; + if (index < obj->getDenseArrayInitializedLength()) { + rval = obj->getDenseArrayElement(index); + if (!rval.isMagic()) + return; } } else if (obj->isArguments()) { - uint32 arg = uint32(i); - - if (arg < obj->getArgsInitialLength()) { - copyFrom = obj->addressOfArgsElement(arg); - if (!copyFrom->isMagic()) { - if (StackFrame *afp = (StackFrame *) obj->getPrivate()) - copyFrom = &afp->canonicalActualArg(arg); - goto end_getelem; - } - } + if (obj->asArguments().getElement(index, &rval)) + return; } - if (JS_LIKELY(INT_FITS_IN_JSID(i))) - id = INT_TO_JSID(i); - else - goto intern_big_int; + if (!obj->getElement(cx, index, &rval)) + THROW(); } else { - int32_t i; - if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) { - id = INT_TO_JSID(i); + SpecialId special; + if (ValueIsSpecial(obj, &rref, &special, cx)) { + if (!obj->getSpecial(cx, obj, special, &rval)) + THROW(); } else { - intern_big_int: - if (!js_InternNonIntElementId(cx, obj, rref, &id)) + JSAtom *name; + if (!js_ValueToAtom(cx, rref, &name)) THROW(); - } - } - - if (!obj->getProperty(cx, id, &rval)) - THROW(); - copyFrom = &rval; - end_getelem: - f.regs.sp[-2] = *copyFrom; -} - -static inline bool -FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp) -{ - int32_t i_; - if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) { - id = INT_TO_JSID(i_); - return true; + if (name->isIndex(&index)) { + if (!obj->getElement(cx, index, &rval)) + THROW(); + } else { + if (!obj->getProperty(cx, name->asPropertyName(), &rval)) + THROW(); + } + } } - return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp); -} - -void JS_FASTCALL -stubs::CallElem(VMFrame &f) -{ - JSContext *cx = f.cx; - FrameRegs ®s = f.regs; - - /* Find the object on which to look for |this|'s properties. */ - Value thisv = regs.sp[-2]; - JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); - if (!thisObj) - THROW(); - - /* Fetch index and convert it to id suitable for use with thisObj. */ - jsid id; - if (!FetchElementId(f, thisObj, regs.sp[-1], id, ®s.sp[-2])) - THROW(); - - /* Get or set the element. */ - if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) - THROW(); #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) { - regs.sp[-2] = regs.sp[-1]; - regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(rval.isPrimitive()) && isObject) { + if (!OnUnknownMethod(cx, obj, rref, &rval)) THROW(); - } else -#endif - { - regs.sp[-1] = thisv; } +#endif } template @@ -540,30 +224,35 @@ stubs::SetElem(VMFrame &f) JSObject *obj; jsid id; - obj = ValueToObject(cx, &objval); + obj = ValueToObject(cx, objval); if (!obj) THROW(); - if (!FetchElementId(f, obj, idval, id, ®s.sp[-2])) + if (!FetchElementId(f.cx, obj, idval, id, ®s.sp[-2])) THROW(); + TypeScript::MonitorAssign(cx, f.script(), f.pc(), obj, id, rval); + do { if (obj->isDenseArray() && JSID_IS_INT(id)) { - jsuint length = obj->getDenseArrayCapacity(); + jsuint length = obj->getDenseArrayInitializedLength(); jsint i = JSID_TO_INT(id); if ((jsuint)i < length) { if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { if (js_PrototypeHasIndexedProperties(cx, obj)) break; if ((jsuint)i >= obj->getArrayLength()) - obj->setArrayLength(i + 1); + obj->setArrayLength(cx, i + 1); } - obj->setDenseArrayElement(i, rval); + obj->setDenseArrayElementWithType(cx, i, rval); goto end_setelem; + } else { + if (f.script()->hasAnalysis()) + f.script()->analysis()->getCode(f.pc()).arrayWriteHole = true; } } } while (0); - if (!obj->setProperty(cx, id, &rval, strict)) + if (!obj->setGeneric(cx, id, &rval, strict)) THROW(); end_setelem: /* :FIXME: Moving the assigned object into the lowest stack slot @@ -577,22 +266,33 @@ template void JS_FASTCALL stubs::SetElem(VMFrame &f); template void JS_FASTCALL stubs::SetElem(VMFrame &f); void JS_FASTCALL -stubs::CallName(VMFrame &f) +stubs::ToId(VMFrame &f) { - JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true); + Value &objval = f.regs.sp[-2]; + Value &idval = f.regs.sp[-1]; + + JSObject *obj = ValueToObject(f.cx, objval); if (!obj) THROW(); + + jsid id; + if (!FetchElementId(f.cx, obj, idval, id, &idval)) + THROW(); + + if (!idval.isInt32()) + TypeScript::MonitorUnknown(f.cx, f.script(), f.pc()); } -/* - * Push the implicit this value, with the assumption that the callee - * (which is on top of the stack) was read as a property from the - * global object. - */ void JS_FASTCALL -stubs::PushImplicitThisForGlobal(VMFrame &f) +stubs::ImplicitThis(VMFrame &f, PropertyName *name) { - return PushImplicitThis(f, f.fp()->scopeChain().getGlobal(), f.regs.sp[-1]); + JSObject *obj, *obj2; + JSProperty *prop; + if (!FindPropertyHelper(f.cx, name, false, f.cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + THROW(); + + if (!ComputeImplicitThis(f.cx, obj, &f.regs.sp[0])) + THROW(); } void JS_FASTCALL @@ -600,10 +300,9 @@ stubs::BitOr(VMFrame &f) { int32_t i, j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) || - !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) { + if (!ToInt32(f.cx, f.regs.sp[-2], &i) || !ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); - } + i = i | j; f.regs.sp[-2].setInt32(i); } @@ -613,10 +312,9 @@ stubs::BitXor(VMFrame &f) { int32_t i, j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) || - !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) { + if (!ToInt32(f.cx, f.regs.sp[-2], &i) || !ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); - } + i = i ^ j; f.regs.sp[-2].setInt32(i); } @@ -626,10 +324,9 @@ stubs::BitAnd(VMFrame &f) { int32_t i, j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) || - !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) { + if (!ToInt32(f.cx, f.regs.sp[-2], &i) || !ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); - } + i = i & j; f.regs.sp[-2].setInt32(i); } @@ -639,7 +336,7 @@ stubs::BitNot(VMFrame &f) { int32_t i; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &i)) + if (!ToInt32(f.cx, f.regs.sp[-1], &i)) THROW(); i = ~i; f.regs.sp[-1].setInt32(i); @@ -649,9 +346,9 @@ void JS_FASTCALL stubs::Lsh(VMFrame &f) { int32_t i, j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i)) + if (!ToInt32(f.cx, f.regs.sp[-2], &i)) THROW(); - if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) + if (!ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); i = i << (j & 31); f.regs.sp[-2].setInt32(i); @@ -661,9 +358,9 @@ void JS_FASTCALL stubs::Rsh(VMFrame &f) { int32_t i, j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i)) + if (!ToInt32(f.cx, f.regs.sp[-2], &i)) THROW(); - if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) + if (!ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); i = i >> (j & 31); f.regs.sp[-2].setInt32(i); @@ -673,15 +370,16 @@ void JS_FASTCALL stubs::Ursh(VMFrame &f) { uint32_t u; - if (!ValueToECMAUint32(f.cx, f.regs.sp[-2], &u)) + if (!ToUint32(f.cx, f.regs.sp[-2], &u)) THROW(); int32_t j; - if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) + if (!ToInt32(f.cx, f.regs.sp[-1], &j)) THROW(); u >>= (j & 31); - f.regs.sp[-2].setNumber(uint32(u)); + if (!f.regs.sp[-2].setNumber(uint32_t(u))) + TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); } template @@ -699,9 +397,9 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - JSObject *obj = FUN_OBJECT(fun); + JSObject *obj = fun; - if (FUN_NULL_CLOSURE(fun)) { + if (fun->isNullClosure()) { /* * Even a null closure needs a parent for principals finding. * FIXME: bug 476950, although debugger users may also demand some kind @@ -711,7 +409,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) } else { JS_ASSERT(!fun->isFlatClosure()); - obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH); + obj2 = GetScopeChain(cx, fp); if (!obj2) THROW(); } @@ -725,10 +423,11 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * windows, and user-defined JS functions precompiled and then shared among * requests in server-side JS. */ - if (obj->getParent() != obj2) { - obj = CloneFunctionObject(cx, fun, obj2); + if (obj->toFunction()->environment() != obj2) { + obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2); if (!obj) THROW(); + JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global()); } /* @@ -744,13 +443,13 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * current scope chain even for the case of function expression statements * and functions defined by eval inside let or with blocks. */ - JSObject *parent = &cx->stack.currentVarObj(); + JSObject *parent = &fp->varObj(); /* ES5 10.5 (NB: with subsequent errata). */ - jsid id = ATOM_TO_JSID(fun->atom); + PropertyName *name = fun->atom->asPropertyName(); JSProperty *prop = NULL; JSObject *pobj; - if (!parent->lookupProperty(cx, id, &pobj, &prop)) + if (!parent->lookupProperty(cx, name, &pobj, &prop)) THROW(); Value rval = ObjectValue(*obj); @@ -758,8 +457,11 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) do { /* Steps 5d, 5f. */ if (!prop || pobj != parent) { - if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) + if (!parent->defineProperty(cx, name, rval, + JS_PropertyStub, JS_StrictPropertyStub, attrs)) + { THROW(); + } break; } @@ -768,16 +470,19 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) Shape *shape = reinterpret_cast(prop); if (parent->isGlobal()) { if (shape->configurable()) { - if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) + if (!parent->defineProperty(cx, name, rval, + JS_PropertyStub, JS_StrictPropertyStub, attrs)) + { THROW(); + } break; } if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) { JSAutoByteString bytes; - if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) { + if (js_AtomToPrintableString(cx, name, &bytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_REDEFINE_PROP, name); + JSMSG_CANT_REDEFINE_PROP, bytes.ptr()); } THROW(); } @@ -791,7 +496,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) */ /* Step 5f. */ - if (!parent->setProperty(cx, id, &rval, strict)) + if (!parent->setProperty(cx, name, &rval, strict)) THROW(); } while (false); } @@ -799,39 +504,28 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) template void JS_FASTCALL stubs::DefFun(VMFrame &f, JSFunction *fun); template void JS_FASTCALL stubs::DefFun(VMFrame &f, JSFunction *fun); -#define DEFAULT_VALUE(cx, n, hint, v) \ - JS_BEGIN_MACRO \ - JS_ASSERT(v.isObject()); \ - JS_ASSERT(v == regs.sp[n]); \ - if (!DefaultValue(cx, &v.toObject(), hint, ®s.sp[n])) \ - THROWV(JS_FALSE); \ - v = regs.sp[n]; \ - JS_END_MACRO - #define RELATIONAL(OP) \ JS_BEGIN_MACRO \ JSContext *cx = f.cx; \ FrameRegs ®s = f.regs; \ - Value rval = regs.sp[-1]; \ - Value lval = regs.sp[-2]; \ + Value &rval = regs.sp[-1]; \ + Value &lval = regs.sp[-2]; \ bool cond; \ - if (lval.isObject()) \ - DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \ - if (rval.isObject()) \ - DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ + if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \ + THROWV(JS_FALSE); \ + if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \ + THROWV(JS_FALSE); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - JSBool cmp; \ + int32_t cmp; \ if (!CompareStrings(cx, l, r, &cmp)) \ THROWV(JS_FALSE); \ cond = cmp OP 0; \ } else { \ double l, r; \ - if (!ValueToNumber(cx, lval, &l) || \ - !ValueToNumber(cx, rval, &r)) { \ + if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) \ THROWV(JS_FALSE); \ - } \ - cond = JSDOUBLE_COMPARE(l, OP, r, false); \ + cond = (l OP r); \ } \ regs.sp[-2].setBoolean(cond); \ return cond; \ @@ -874,7 +568,7 @@ stubs::Not(VMFrame &f) f.regs.sp[-1].setBoolean(b); } -template +template static inline bool StubEqualityOp(VMFrame &f) { @@ -884,23 +578,25 @@ StubEqualityOp(VMFrame &f) Value rval = regs.sp[-1]; Value lval = regs.sp[-2]; - JSBool cond; + bool cond; /* The string==string case is easily the hottest; try it first. */ if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - JSBool equal; + bool equal; if (!EqualStrings(cx, l, r, &equal)) return false; cond = equal == EQ; } else #if JS_HAS_XML_SUPPORT if ((lval.isObject() && lval.toObject().isXML()) || - (rval.isObject() && rval.toObject().isXML())) { - if (!js_TestXMLEquality(cx, lval, rval, &cond)) + (rval.isObject() && rval.toObject().isXML())) + { + JSBool equal; + if (!js_TestXMLEquality(cx, lval, rval, &equal)) return false; - cond = cond == EQ; + cond = !!equal == EQ; } else #endif @@ -910,16 +606,16 @@ StubEqualityOp(VMFrame &f) double l = lval.toDouble(); double r = rval.toDouble(); if (EQ) - cond = JSDOUBLE_COMPARE(l, ==, r, IFNAN); + cond = (l == r); else - cond = JSDOUBLE_COMPARE(l, !=, r, IFNAN); + cond = (l != r); } else if (lval.isObject()) { JSObject *l = &lval.toObject(), *r = &rval.toObject(); - l->assertSpecialEqualitySynced(); - if (EqualityOp eq = l->getClass()->ext.equality) { - if (!eq(cx, l, &rval, &cond)) + if (JSEqualityOp eq = l->getClass()->ext.equality) { + JSBool equal; + if (!eq(cx, l, &rval, &equal)) return false; - cond = cond == EQ; + cond = !!equal == EQ; } else { cond = (l == r) == EQ; } @@ -934,40 +630,31 @@ StubEqualityOp(VMFrame &f) } else if (rval.isNullOrUndefined()) { cond = !EQ; } else { - if (lval.isObject()) { - if (!DefaultValue(cx, &lval.toObject(), JSTYPE_VOID, ®s.sp[-2])) - return false; - lval = regs.sp[-2]; - } - - if (rval.isObject()) { - if (!DefaultValue(cx, &rval.toObject(), JSTYPE_VOID, ®s.sp[-1])) - return false; - rval = regs.sp[-1]; - } + if (!ToPrimitive(cx, &lval)) + return false; + if (!ToPrimitive(cx, &rval)) + return false; /* - * The string==string case is repeated because DefaultValue() can + * The string==string case is repeated because ToPrimitive can * convert lval/rval to strings. */ if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - JSBool equal; + bool equal; if (!EqualStrings(cx, l, r, &equal)) return false; cond = equal == EQ; } else { double l, r; - if (!ValueToNumber(cx, lval, &l) || - !ValueToNumber(cx, rval, &r)) { + if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) return false; - } if (EQ) - cond = JSDOUBLE_COMPARE(l, ==, r, false); + cond = (l == r); else - cond = JSDOUBLE_COMPARE(l, !=, r, true); + cond = (l != r); } } } @@ -979,7 +666,7 @@ StubEqualityOp(VMFrame &f) JSBool JS_FASTCALL stubs::Equal(VMFrame &f) { - if (!StubEqualityOp(f)) + if (!StubEqualityOp(f)) THROWV(JS_FALSE); return f.regs.sp[-2].toBoolean(); } @@ -987,21 +674,11 @@ stubs::Equal(VMFrame &f) JSBool JS_FASTCALL stubs::NotEqual(VMFrame &f) { - if (!StubEqualityOp(f)) + if (!StubEqualityOp(f)) THROWV(JS_FALSE); return f.regs.sp[-2].toBoolean(); } -static inline bool -DefaultValue(VMFrame &f, JSType hint, Value &v, int n) -{ - JS_ASSERT(v.isObject()); - if (!DefaultValue(f.cx, &v.toObject(), hint, &f.regs.sp[n])) - return false; - v = f.regs.sp[n]; - return true; -} - void JS_FASTCALL stubs::Add(VMFrame &f) { @@ -1025,21 +702,22 @@ stubs::Add(VMFrame &f) rval.isObject() && rval.toObject().isXML()) { if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval)) THROW(); + regs.sp[-2] = rval; regs.sp--; - regs.sp[-1] = rval; + TypeScript::MonitorUnknown(cx, f.script(), f.pc()); } else #endif { - /* These can convert lval/rval to strings. */ - if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2)) + bool lIsObject = lval.isObject(), rIsObject = rval.isObject(); + if (!ToPrimitive(f.cx, &lval)) THROW(); - if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1)) + if (!ToPrimitive(f.cx, &rval)) THROW(); if ((lIsString = lval.isString()) || (rIsString = rval.isString())) { if (lIsString) { lstr = lval.toString(); } else { - lstr = js_ValueToString(cx, lval); + lstr = ToString(cx, lval); if (!lstr) THROW(); regs.sp[-2].setString(lstr); @@ -1047,20 +725,24 @@ stubs::Add(VMFrame &f) if (rIsString) { rstr = rval.toString(); } else { - rstr = js_ValueToString(cx, rval); + rstr = ToString(cx, rval); if (!rstr) THROW(); regs.sp[-1].setString(rstr); } + if (lIsObject || rIsObject) + TypeScript::MonitorString(cx, f.script(), f.pc()); goto string_concat; } else { double l, r; - if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r)) + if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) THROW(); l += r; - regs.sp--; - regs.sp[-1].setNumber(l); + if (!regs.sp[-2].setNumber(l) && + (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) { + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); + } } } return; @@ -1069,8 +751,8 @@ stubs::Add(VMFrame &f) JSString *str = js_ConcatStrings(cx, lstr, rstr); if (!str) THROW(); + regs.sp[-2].setString(str); regs.sp--; - regs.sp[-1].setString(str); } @@ -1080,12 +762,11 @@ stubs::Sub(VMFrame &f) JSContext *cx = f.cx; FrameRegs ®s = f.regs; double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { + if (!ToNumber(cx, regs.sp[-2], &d1) || !ToNumber(cx, regs.sp[-1], &d2)) THROW(); - } double d = d1 - d2; - regs.sp[-2].setNumber(d); + if (!regs.sp[-2].setNumber(d)) + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); } void JS_FASTCALL @@ -1094,12 +775,11 @@ stubs::Mul(VMFrame &f) JSContext *cx = f.cx; FrameRegs ®s = f.regs; double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { + if (!ToNumber(cx, regs.sp[-2], &d1) || !ToNumber(cx, regs.sp[-1], &d2)) THROW(); - } double d = d1 * d2; - regs.sp[-2].setNumber(d); + if (!regs.sp[-2].setNumber(d)) + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); } void JS_FASTCALL @@ -1110,10 +790,8 @@ stubs::Div(VMFrame &f) FrameRegs ®s = f.regs; double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { + if (!ToNumber(cx, regs.sp[-2], &d1) || !ToNumber(cx, regs.sp[-1], &d2)) THROW(); - } if (d2 == 0) { const Value *vp; #ifdef XP_WIN @@ -1129,9 +807,11 @@ stubs::Div(VMFrame &f) else vp = &rt->positiveInfinityValue; regs.sp[-2] = *vp; + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); } else { d1 /= d2; - regs.sp[-2].setNumber(d1); + if (!regs.sp[-2].setNumber(d1)) + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); } } @@ -1150,27 +830,31 @@ stubs::Mod(VMFrame &f) regs.sp[-2].setInt32(mod); } else { double d1, d2; - if (!ValueToNumber(cx, regs.sp[-2], &d1) || - !ValueToNumber(cx, regs.sp[-1], &d2)) { + if (!ToNumber(cx, regs.sp[-2], &d1) || !ToNumber(cx, regs.sp[-1], &d2)) THROW(); - } if (d2 == 0) { regs.sp[-2].setDouble(js_NaN); } else { d1 = js_fmod(d1, d2); regs.sp[-2].setDouble(d1); } + TypeScript::MonitorOverflow(cx, f.script(), f.pc()); } } void JS_FASTCALL -stubs::Debugger(VMFrame &f, jsbytecode *pc) +stubs::DebuggerStatement(VMFrame &f, jsbytecode *pc) { JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler; - if (handler) { + if (handler || !f.cx->compartment->getDebuggees().empty()) { + JSTrapStatus st = JSTRAP_CONTINUE; Value rval; - switch (handler(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval), - f.cx->debugHooks->debuggerHandlerData)) { + if (handler) + st = handler(f.cx, f.script(), pc, &rval, f.cx->debugHooks->debuggerHandlerData); + if (st == JSTRAP_CONTINUE) + st = Debugger::onDebuggerStatement(f.cx, &rval); + + switch (st) { case JSTRAP_THROW: f.cx->setPendingException(rval); THROW(); @@ -1194,15 +878,24 @@ stubs::Debugger(VMFrame &f, jsbytecode *pc) void JS_FASTCALL stubs::Interrupt(VMFrame &f, jsbytecode *pc) { + gc::VerifyBarriers(f.cx); + if (!js_HandleExecutionInterrupt(f.cx)) THROW(); } void JS_FASTCALL -stubs::Trap(VMFrame &f, uint32 trapTypes) +stubs::RecompileForInline(VMFrame &f) +{ + ExpandInlineFrames(f.cx->compartment); + Recompiler::clearStackReferencesAndChunk(f.cx, f.script(), f.jit(), f.chunkIndex(), + /* resetUses = */ false); +} + +void JS_FASTCALL +stubs::Trap(VMFrame &f, uint32_t trapTypes) { Value rval; - jsbytecode *pc = f.cx->regs().pc; /* * Trap may be called for a single-step interrupt trap and/or a @@ -1217,12 +910,14 @@ stubs::Trap(VMFrame &f, uint32 trapTypes) */ JSInterruptHook hook = f.cx->debugHooks->interruptHook; if (hook) - result = hook(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval), - f.cx->debugHooks->interruptHookData); + result = hook(f.cx, f.script(), f.pc(), &rval, f.cx->debugHooks->interruptHookData); + + if (result == JSTRAP_CONTINUE) + result = Debugger::onSingleStep(f.cx, &rval); } if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP)) - result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval)); + result = Debugger::onTrap(f.cx, &rval); switch (result) { case JSTRAP_THROW: @@ -1247,6 +942,15 @@ stubs::Trap(VMFrame &f, uint32 trapTypes) void JS_FASTCALL stubs::This(VMFrame &f) { + /* + * We can't yet inline scripts which need to compute their 'this' object + * from a primitive; the frame we are computing 'this' for does not exist yet. + */ + if (f.regs.inlined()) { + f.script()->uninlineable = true; + MarkTypeObjectFlags(f.cx, &f.fp()->callee(), OBJECT_FLAG_UNINLINEABLE); + } + if (!ComputeThis(f.cx, f.fp())) THROW(); f.regs.sp[-1] = f.fp()->thisValue(); @@ -1256,44 +960,54 @@ void JS_FASTCALL stubs::Neg(VMFrame &f) { double d; - if (!ValueToNumber(f.cx, f.regs.sp[-1], &d)) + if (!ToNumber(f.cx, f.regs.sp[-1], &d)) THROW(); d = -d; - f.regs.sp[-1].setNumber(d); + if (!f.regs.sp[-1].setNumber(d)) + TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); } -JSObject * JS_FASTCALL -stubs::NewInitArray(VMFrame &f, uint32 count) +void JS_FASTCALL +stubs::NewInitArray(VMFrame &f, uint32_t count) { JSObject *obj = NewDenseAllocatedArray(f.cx, count); if (!obj) - THROWV(NULL); + THROW(); - return obj; + TypeObject *type = (TypeObject *) f.scratch; + if (type) + obj->setType(type); + + f.regs.sp[0].setObject(*obj); } -JSObject * JS_FASTCALL +void JS_FASTCALL stubs::NewInitObject(VMFrame &f, JSObject *baseobj) { JSContext *cx = f.cx; + TypeObject *type = (TypeObject *) f.scratch; if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(0); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) - THROWV(NULL); - return obj; + THROW(); + if (type) + obj->setType(type); + f.regs.sp[0].setObject(*obj); + return; } - JSObject *obj = CopyInitializerObject(cx, baseobj); + JS_ASSERT(type); + JSObject *obj = CopyInitializerObject(cx, baseobj, type); if (!obj) - THROWV(NULL); - return obj; + THROW(); + f.regs.sp[0].setObject(*obj); } void JS_FASTCALL -stubs::InitElem(VMFrame &f, uint32 last) +stubs::InitElem(VMFrame &f, uint32_t last) { JSContext *cx = f.cx; FrameRegs ®s = f.regs; @@ -1310,7 +1024,7 @@ stubs::InitElem(VMFrame &f, uint32 last) /* Fetch id now that we have obj. */ jsid id; const Value &idval = regs.sp[-2]; - if (!FetchElementId(f, obj, idval, id, ®s.sp[-2])) + if (!FetchElementId(f.cx, obj, idval, id, ®s.sp[-2])) THROW(); /* @@ -1321,20 +1035,20 @@ stubs::InitElem(VMFrame &f, uint32 last) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX); + JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) THROW(); } else { - if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) + if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) THROW(); } } void JS_FASTCALL -stubs::GetUpvar(VMFrame &f, uint32 ck) +stubs::GetUpvar(VMFrame &f, uint32_t ck) { /* :FIXME: We can do better, this stub isn't needed. */ - uint32 staticLevel = f.fp()->script()->staticLevel; + uint32_t staticLevel = f.script()->staticLevel; UpvarCookie cookie; cookie.fromInteger(ck); f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie); @@ -1351,25 +1065,21 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun) * activation. */ JS_ASSERT(fun->isInterpreted()); - JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); - JSObject *obj = FUN_OBJECT(fun); + JS_ASSERT(!fun->isFlatClosure()); - if (FUN_NULL_CLOSURE(fun)) { - obj = CloneFunctionObject(f.cx, fun, &f.fp()->scopeChain()); - if (!obj) - THROWV(NULL); + JSObject *parent; + if (fun->isNullClosure()) { + parent = &f.fp()->scopeChain(); } else { - JSObject *parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN, - JSOP_DEFLOCALFUN_LENGTH); + parent = GetScopeChain(f.cx, f.fp()); if (!parent) THROWV(NULL); - - if (obj->getParent() != parent) { - obj = CloneFunctionObject(f.cx, fun, parent); - if (!obj) - THROWV(NULL); - } } + JSObject *obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent); + if (!obj) + THROWV(NULL); + + JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global()); return obj; } @@ -1377,54 +1087,47 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun) JSObject * JS_FASTCALL stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(f.cx, fun); if (!obj) THROWV(NULL); return obj; } -JSObject * JS_FASTCALL +void JS_FASTCALL stubs::RegExp(VMFrame &f, JSObject *regex) { /* * Push a regexp object cloned from the regexp literal object mapped by the - * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was - * flouted by many browser-based implementations. - * - * We avoid the GetScopeChain call here and pass fp->scopeChain() as - * js_GetClassPrototype uses the latter only to locate the global. + * bytecode at pc. */ - JSObject *proto; - if (!js_GetClassPrototype(f.cx, &f.fp()->scopeChain(), JSProto_RegExp, &proto)) - THROWV(NULL); + JSObject *proto = f.fp()->scopeChain().global().getOrCreateRegExpPrototype(f.cx); + if (!proto) + THROW(); JS_ASSERT(proto); - JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto); + JSObject *obj = CloneRegExpObject(f.cx, regex, proto); if (!obj) - THROWV(NULL); - return obj; + THROW(); + f.regs.sp[0].setObject(*obj); } JSObject * JS_FASTCALL -stubs::LambdaForInit(VMFrame &f, JSFunction *fun) +stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun) { - JSObject *obj = FUN_OBJECT(fun); - if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { - fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc))); - return obj; - } - return Lambda(f, fun); + JS_ASSERT(fun->joinable()); + DebugOnly nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH; + JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc))); + return fun; } JSObject * JS_FASTCALL -stubs::LambdaForSet(VMFrame &f, JSFunction *fun) -{ - JSObject *obj = FUN_OBJECT(fun); - if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { - const Value &lref = f.regs.sp[-1]; - if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { - fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc))); - return obj; - } +stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun) +{ + JS_ASSERT(fun->joinable()); + const Value &lref = f.regs.sp[-1]; + if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { + DebugOnly nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH; + JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc))); + return fun; } return Lambda(f, fun); } @@ -1432,605 +1135,100 @@ stubs::LambdaForSet(VMFrame &f, JSFunction *fun) JSObject * JS_FASTCALL stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun) { - JSObject *obj = FUN_OBJECT(fun); - if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { - /* - * Array.prototype.sort and String.prototype.replace are - * optimized as if they are special form. We know that they - * won't leak the joined function object in obj, therefore - * we don't need to clone that compiler- created function - * object for identity/mutation reasons. - */ - int iargc = GET_ARGC(f.regs.pc); + JS_ASSERT(fun->joinable()); - /* - * Note that we have not yet pushed obj as the final argument, - * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], - * is the callee for this JSOP_CALL. - */ - const Value &cref = f.regs.sp[1 - (iargc + 2)]; - JSObject *callee; - - if (IsFunctionObject(cref, &callee)) { - JSFunction *calleeFun = callee->getFunctionPrivate(); - Native native = calleeFun->maybeNative(); - - if (native) { - if (iargc == 1 && native == array_sort) - return obj; - if (iargc == 2 && native == str_replace) - return obj; - } - } - } - return Lambda(f, fun); + /* + * Array.prototype.sort and String.prototype.replace are optimized as if + * they are special form. We know that they won't leak the joined function + * object fun, therefore we don't need to clone that compiler-created + * function object for identity/mutation reasons. + */ + int iargc = GET_ARGC(f.regs.pc + JSOP_LAMBDA_LENGTH); + + /* + * Note that we have not yet pushed fun as the final argument, so + * regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], is the callee + * for this JSOP_CALL. + */ + const Value &cref = f.regs.sp[1 - (iargc + 2)]; + JSFunction *callee; + + if (IsFunctionObject(cref, &callee)) { + Native native = callee->maybeNative(); + + if (native) { + if (iargc == 1 && native == array_sort) + return fun; + if (iargc == 2 && native == str_replace) + return fun; + } + } + return Lambda(f, fun); } JSObject * JS_FASTCALL stubs::LambdaJoinableForNull(VMFrame &f, JSFunction *fun) { - JSObject *obj = FUN_OBJECT(fun); - if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { - jsbytecode *pc2 = f.regs.pc + JSOP_NULL_LENGTH; - JSOp op2 = JSOp(*pc2); - - if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) - return obj; - } - return Lambda(f, fun); + JS_ASSERT(fun->joinable()); + return fun; } JSObject * JS_FASTCALL stubs::Lambda(VMFrame &f, JSFunction *fun) { - JSObject *obj = FUN_OBJECT(fun); - JSObject *parent; - if (FUN_NULL_CLOSURE(fun)) { + if (fun->isNullClosure()) { parent = &f.fp()->scopeChain(); } else { - parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH); + parent = GetScopeChain(f.cx, f.fp()); if (!parent) THROWV(NULL); } - obj = CloneFunctionObject(f.cx, fun, parent); + JSObject *obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent); if (!obj) THROWV(NULL); + JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global()); return obj; } -/* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */ -static JS_ALWAYS_INLINE bool -CanIncDecWithoutOverflow(int32_t i) -{ - return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX); -} - -template -static inline bool -ObjIncOp(VMFrame &f, JSObject *obj, jsid id) -{ - JSContext *cx = f.cx; - - f.regs.sp[0].setNull(); - f.regs.sp++; - if (!obj->getProperty(cx, id, &f.regs.sp[-1])) - return false; - - uint32 setPropFlags = qualified - ? JSRESOLVE_ASSIGNING - : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED; - - Value &ref = f.regs.sp[-1]; - int32_t tmp; - if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) { - if (POST) - ref.getInt32Ref() = tmp + N; - else - ref.getInt32Ref() = tmp += N; - - { - JSAutoResolveFlags rf(cx, setPropFlags); - if (!obj->setProperty(cx, id, &ref, strict)) - return false; - } - - /* - * We must set regs.sp[-1] to tmp for both post and pre increments - * as the setter overwrites regs.sp[-1]. - */ - ref.setInt32(tmp); - } else { - Value v; - double d; - if (!ValueToNumber(cx, ref, &d)) - return false; - if (POST) { - ref.setDouble(d); - d += N; - } else { - d += N; - ref.setDouble(d); - } - v.setDouble(d); - - { - JSAutoResolveFlags rf(cx, setPropFlags); - if (!obj->setProperty(cx, id, &v, strict)) - return false; - } - } - - return true; -} - -template -static inline bool -NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom) -{ - JSContext *cx = f.cx; - - JSAtom *atom; - JSObject *obj2; - JSProperty *prop; - PropertyCacheEntry *entry; - JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom); - if (!atom) { - if (obj == obj2 && entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - Value &rref = obj->nativeGetSlotRef(slot); - int32_t tmp; - if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) { - int32_t inc = tmp + N; - if (!POST) - tmp = inc; - rref.getInt32Ref() = inc; - f.regs.sp[0].setInt32(tmp); - return true; - } - } - atom = origAtom; - } - - jsid id = ATOM_TO_JSID(atom); - if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) - return false; - if (!prop) { - ReportAtomNotDefined(cx, atom); - return false; - } - return ObjIncOp(f, obj, id); -} - -template -void JS_FASTCALL -stubs::PropInc(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]); - if (!obj) - THROW(); - if (!ObjIncOp<1, true, strict, true>(f, obj, ATOM_TO_JSID(atom))) - THROW(); - f.regs.sp[-2] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::PropInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::PropInc(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::PropDec(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]); - if (!obj) - THROW(); - if (!ObjIncOp<-1, true, strict, true>(f, obj, ATOM_TO_JSID(atom))) - THROW(); - f.regs.sp[-2] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::PropDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::PropDec(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::IncProp(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]); - if (!obj) - THROW(); - if (!ObjIncOp<1, false, strict, true>(f, obj, ATOM_TO_JSID(atom))) - THROW(); - f.regs.sp[-2] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::IncProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::IncProp(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::DecProp(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]); - if (!obj) - THROW(); - if (!ObjIncOp<-1, false, strict, true>(f, obj, ATOM_TO_JSID(atom))) - THROW(); - f.regs.sp[-2] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::DecProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::DecProp(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::ElemInc(VMFrame &f) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1])) - THROW(); - if (!ObjIncOp<1, true, strict, true>(f, obj, id)) - THROW(); - f.regs.sp[-3] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::ElemInc(VMFrame &f); -template void JS_FASTCALL stubs::ElemInc(VMFrame &f); - -template -void JS_FASTCALL -stubs::ElemDec(VMFrame &f) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1])) - THROW(); - if (!ObjIncOp<-1, true, strict, true>(f, obj, id)) - THROW(); - f.regs.sp[-3] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::ElemDec(VMFrame &f); -template void JS_FASTCALL stubs::ElemDec(VMFrame &f); - -template -void JS_FASTCALL -stubs::IncElem(VMFrame &f) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1])) - THROW(); - if (!ObjIncOp<1, false, strict, true>(f, obj, id)) - THROW(); - f.regs.sp[-3] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::IncElem(VMFrame &f); -template void JS_FASTCALL stubs::IncElem(VMFrame &f); - -template void JS_FASTCALL -stubs::DecElem(VMFrame &f) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1])) - THROW(); - if (!ObjIncOp<-1, false, strict, true>(f, obj, id)) - THROW(); - f.regs.sp[-3] = f.regs.sp[-1]; -} - -template void JS_FASTCALL stubs::DecElem(VMFrame &f); -template void JS_FASTCALL stubs::DecElem(VMFrame &f); - -template -void JS_FASTCALL -stubs::NameInc(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = &f.fp()->scopeChain(); - if (!NameIncDec<1, true, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::NameInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::NameInc(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::NameDec(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = &f.fp()->scopeChain(); - if (!NameIncDec<-1, true, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::NameDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::NameDec(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::IncName(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = &f.fp()->scopeChain(); - if (!NameIncDec<1, false, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::IncName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::IncName(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::DecName(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = &f.fp()->scopeChain(); - if (!NameIncDec<-1, false, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::DecName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::DecName(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::GlobalNameInc(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = f.fp()->scopeChain().getGlobal(); - if (!NameIncDec<1, true, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::GlobalNameInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::GlobalNameInc(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::GlobalNameDec(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = f.fp()->scopeChain().getGlobal(); - if (!NameIncDec<-1, true, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::GlobalNameDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::GlobalNameDec(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::IncGlobalName(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = f.fp()->scopeChain().getGlobal(); - if (!NameIncDec<1, false, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::IncGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::IncGlobalName(VMFrame &f, JSAtom *atom); - -template -void JS_FASTCALL -stubs::DecGlobalName(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = f.fp()->scopeChain().getGlobal(); - if (!NameIncDec<-1, false, strict>(f, obj, atom)) - THROW(); -} - -template void JS_FASTCALL stubs::DecGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::DecGlobalName(VMFrame &f, JSAtom *atom); - -static bool JS_FASTCALL -InlineGetProp(VMFrame &f) +stubs::GetProp(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx; FrameRegs ®s = f.regs; - Value *vp = &f.regs.sp[-1]; - JSObject *obj = ValueToObject(f.cx, vp); - if (!obj) - return false; - Value rval; - do { - /* - * We do not impose the method read barrier if in an imacro, - * assuming any property gets it does (e.g., for 'toString' - * from JSOP_NEW) will not be leaked to the calling script. - */ - JSObject *aobj = js_GetProtoIfDenseArray(obj); - - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom); - if (!atom) { - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - const Shape *shape = entry->vword.toShape(); - NATIVE_GET(cx, obj, obj2, shape, - f.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER, - &rval, return false); - } - break; - } - - jsid id = ATOM_TO_JSID(atom); - if (JS_LIKELY(!aobj->getOps()->getProperty) - ? !js_GetPropertyHelper(cx, obj, id, - f.fp()->hasImacropc() - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER, - &rval) - : !obj->getProperty(cx, id, &rval)) { - return false; - } - } while(0); - - regs.sp[-1] = rval; - return true; -} - -void JS_FASTCALL -stubs::GetProp(VMFrame &f) -{ - if (!InlineGetProp(f)) - THROW(); -} - -void JS_FASTCALL -stubs::GetPropNoCache(VMFrame &f, JSAtom *atom) -{ - JSContext *cx = f.cx; - - Value *vp = &f.regs.sp[-1]; - JSObject *obj = ValueToObject(cx, vp); - if (!obj) + if (!GetPropertyOperation(cx, f.pc(), f.regs.sp[-1], &rval)) THROW(); - if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp)) - THROW(); + regs.sp[-1] = rval; } void JS_FASTCALL -stubs::CallProp(VMFrame &f, JSAtom *origAtom) +stubs::GetPropNoCache(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx; FrameRegs ®s = f.regs; - Value lval; - lval = regs.sp[-1]; + const Value &lval = f.regs.sp[-1]; - Value objv; - if (lval.isObject()) { - objv = lval; - } else { - JSProtoKey protoKey; - if (lval.isString()) { - protoKey = JSProto_String; - } else if (lval.isNumber()) { - protoKey = JSProto_Number; - } else if (lval.isBoolean()) { - protoKey = JSProto_Boolean; - } else { - JS_ASSERT(lval.isNull() || lval.isUndefined()); - js_ReportIsNullOrUndefined(cx, -1, lval, NULL); - THROW(); - } - JSObject *pobj; - if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) - THROW(); - objv.setObject(*pobj); - } + // Uncached lookups are only used for .prototype accesses at the start of constructors. + JS_ASSERT(lval.isObject()); + JS_ASSERT(name == cx->runtime->atomState.classPrototypeAtom); - JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject()); - Value rval; + JSObject *obj = &lval.toObject(); - PropertyCacheEntry *entry; - JSObject *obj2; - JSAtom *atom; - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom); - if (!atom) { - if (entry->vword.isFunObj()) { - rval.setObject(entry->vword.toFunObj()); - } else if (entry->vword.isSlot()) { - uint32 slot = entry->vword.toSlot(); - rval = obj2->nativeGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isShape()); - const Shape *shape = entry->vword.toShape(); - NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval, - THROW()); - } - regs.sp++; - regs.sp[-2] = rval; - regs.sp[-1] = lval; - } else { - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(origAtom); - - regs.sp++; - regs.sp[-1].setNull(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!aobj->getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; - } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - } - } -#if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { - regs.sp[-2].setString(origAtom); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) - THROW(); - } -#endif -} - -void JS_FASTCALL -stubs::Length(VMFrame &f) -{ - FrameRegs ®s = f.regs; - Value *vp = ®s.sp[-1]; - - if (vp->isString()) { - vp->setInt32(vp->toString()->length()); - return; - } else if (vp->isObject()) { - JSObject *obj = &vp->toObject(); - if (obj->isArray()) { - jsuint length = obj->getArrayLength(); - regs.sp[-1].setNumber(length); - return; - } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) { - uint32 length = obj->getArgsInitialLength(); - JS_ASSERT(length < INT32_MAX); - regs.sp[-1].setInt32(int32_t(length)); - return; - } - } - - if (!InlineGetProp(f)) + Value rval; + if (!obj->getProperty(cx, name, &rval)) THROW(); + + regs.sp[-1] = rval; } void JS_FASTCALL -stubs::Iter(VMFrame &f, uint32 flags) +stubs::Iter(VMFrame &f, uint32_t flags) { if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1])) THROW(); @@ -2038,10 +1236,9 @@ stubs::Iter(VMFrame &f, uint32 flags) } static void -InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op) +InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op) { JSContext *cx = f.cx; - JSRuntime *rt = cx->runtime; FrameRegs ®s = f.regs; /* Load the property's initial value into rval. */ @@ -2053,85 +1250,37 @@ InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op) JSObject *obj = ®s.sp[-2].toObject(); JS_ASSERT(obj->isNative()); - /* - * Probe the property cache. - * - * We can not assume that the object created by JSOP_NEWINIT is still - * single-threaded as the debugger can access it from other threads. - * So check first. - * - * On a hit, if the cached shape has a non-default setter, it must be - * __proto__. If shape->previous() != obj->lastProperty(), there must be a - * repeated property name. The fast path does not handle these two cases. - */ - PropertyCacheEntry *entry; - const Shape *shape; - if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) && - shape->hasDefaultSetter() && - shape->previous() == obj->lastProperty()) - { - /* Fast path. Property cache hit. */ - uint32 slot = shape->slot; + /* Get the immediate property name into id. */ + jsid id = ATOM_TO_JSID(name); - JS_ASSERT(slot == obj->slotSpan()); - JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass())); - if (slot < obj->numSlots()) { - JS_ASSERT(obj->getSlot(slot).isUndefined()); - } else { - if (!obj->allocSlot(cx, &slot)) - THROW(); - JS_ASSERT(slot == shape->slot); - } - - /* A new object, or one we just extended in a recent initprop op. */ - JS_ASSERT(!obj->lastProperty() || - obj->shape() == obj->lastProperty()->shape); - obj->extend(cx, shape); - - /* - * No method change check here because here we are adding a new - * property, not updating an existing slot's value that might - * contain a method of a branded shape. - */ - obj->nativeSetSlot(slot, rval); - } else { - PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++); - - /* Get the immediate property name into id. */ - jsid id = ATOM_TO_JSID(atom); - - uintN defineHow = (op == JSOP_INITMETHOD) - ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD - : JSDNP_CACHE_RESULT; - if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) - ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false) - : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, - JSPROP_ENUMERATE, 0, 0, NULL, - defineHow))) { - THROW(); - } + uintN defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0; + if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom) + ? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false) + : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL, + JSPROP_ENUMERATE, 0, 0, defineHow)) { + THROW(); } } void JS_FASTCALL -stubs::InitProp(VMFrame &f, JSAtom *atom) +stubs::InitProp(VMFrame &f, PropertyName *name) { - InitPropOrMethod(f, atom, JSOP_INITPROP); + InitPropOrMethod(f, name, JSOP_INITPROP); } void JS_FASTCALL -stubs::InitMethod(VMFrame &f, JSAtom *atom) +stubs::InitMethod(VMFrame &f, PropertyName *name) { - InitPropOrMethod(f, atom, JSOP_INITMETHOD); + InitPropOrMethod(f, name, JSOP_INITMETHOD); } void JS_FASTCALL -stubs::IterNext(VMFrame &f) +stubs::IterNext(VMFrame &f, int32_t offset) { - JS_ASSERT(f.regs.sp - 1 >= f.fp()->base()); - JS_ASSERT(f.regs.sp[-1].isObject()); + JS_ASSERT(f.regs.sp - offset >= f.fp()->base()); + JS_ASSERT(f.regs.sp[-offset].isObject()); - JSObject *iterobj = &f.regs.sp[-1].toObject(); + JSObject *iterobj = &f.regs.sp[-offset].toObject(); f.regs.sp[0].setNull(); f.regs.sp++; if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1])) @@ -2164,9 +1313,8 @@ JSString * JS_FASTCALL stubs::TypeOf(VMFrame &f) { const Value &ref = f.regs.sp[-1]; - JSType type = JS_TypeOfValue(f.cx, Jsvalify(ref)); - JSAtom *atom = f.cx->runtime->atomState.typeAtoms[type]; - return atom; + JSType type = JS_TypeOfValue(f.cx, ref); + return f.cx->runtime->atomState.typeAtoms[type]; } void JS_FASTCALL @@ -2174,7 +1322,7 @@ stubs::StrictEq(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - JSBool equal; + bool equal; if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) THROW(); f.regs.sp--; @@ -2186,7 +1334,7 @@ stubs::StrictNe(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - JSBool equal; + bool equal; if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) THROW(); f.regs.sp--; @@ -2206,7 +1354,7 @@ stubs::Throw(VMFrame &f) JSObject * JS_FASTCALL stubs::FlatLambda(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(f.cx, fun); if (!obj) THROWV(NULL); return obj; @@ -2258,37 +1406,27 @@ stubs::FastInstanceOf(VMFrame &f) f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.toObject(), f.regs.sp[-3])); } -void JS_FASTCALL -stubs::ArgCnt(VMFrame &f) -{ - JSContext *cx = f.cx; - JSRuntime *rt = cx->runtime; - StackFrame *fp = f.fp(); - - jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom); - f.regs.sp++; - if (!js_GetArgsProperty(cx, fp, id, &f.regs.sp[-1])) - THROW(); -} - void JS_FASTCALL stubs::EnterBlock(VMFrame &f, JSObject *obj) { FrameRegs ®s = f.regs; -#ifdef DEBUG StackFrame *fp = f.fp(); -#endif + StaticBlockObject &blockObj = obj->asStaticBlock(); - JS_ASSERT(obj->isStaticBlock()); - JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); - Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(regs.sp < vp); - JS_ASSERT(vp <= fp->slots() + fp->script()->nslots); - SetValueRangeToUndefined(regs.sp, vp); - regs.sp = vp; + JS_ASSERT(!f.regs.inlined()); + + if (*regs.pc == JSOP_ENTERBLOCK) { + JS_ASSERT(fp->base() + blockObj.stackDepth() == regs.sp); + Value *vp = regs.sp + blockObj.slotCount(); + JS_ASSERT(regs.sp < vp); + JS_ASSERT(vp <= fp->slots() + fp->script()->nslots); + SetValueRangeToUndefined(regs.sp, vp); + regs.sp = vp; + } #ifdef DEBUG JSContext *cx = f.cx; + JS_ASSERT(fp->maybeBlockChain() == blockObj.enclosingBlock()); /* * The young end of fp->scopeChain() may omit blocks if we haven't closed @@ -2298,43 +1436,60 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj) * static scope. */ JSObject *obj2 = &fp->scopeChain(); - Class *clasp; - while ((clasp = obj2->getClass()) == &js_WithClass) - obj2 = obj2->getParent(); - if (clasp == &js_BlockClass && + while (obj2->isWith()) + obj2 = &obj2->asWith().enclosingScope(); + if (obj2->isBlock() && obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) { - JSObject *youngestProto = obj2->getProto(); - JS_ASSERT(youngestProto->isStaticBlock()); - JSObject *parent = obj; - while ((parent = parent->getParent()) != youngestProto) + JSObject &youngestProto = obj2->asClonedBlock().staticBlock(); + StaticBlockObject *parent = &blockObj; + while ((parent = parent->enclosingBlock()) != &youngestProto) JS_ASSERT(parent); } #endif + + fp->setBlockChain(&blockObj); } void JS_FASTCALL -stubs::LeaveBlock(VMFrame &f, JSObject *blockChain) +stubs::LeaveBlock(VMFrame &f) { JSContext *cx = f.cx; StackFrame *fp = f.fp(); -#ifdef DEBUG - JS_ASSERT(blockChain->isStaticBlock()); - uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain); + StaticBlockObject &blockObj = fp->blockChain(); + JS_ASSERT(blockObj.stackDepth() <= StackDepth(fp->script())); - JS_ASSERT(blockDepth <= StackDepth(fp->script())); -#endif /* * If we're about to leave the dynamic scope of a block that has been * cloned onto fp->scopeChain(), clear its private data, move its locals from * the stack into the clone, and pop it off the chain. */ - JSObject *obj = &fp->scopeChain(); - if (obj->getProto() == blockChain) { - JS_ASSERT(obj->getClass() == &js_BlockClass); - if (!js_PutBlockObject(cx, JS_TRUE)) - THROW(); + JSObject &obj = fp->scopeChain(); + if (obj.getProto() == &blockObj) + obj.asClonedBlock().put(cx); + + fp->setBlockChain(blockObj.enclosingBlock()); +} + +inline void * +FindNativeCode(VMFrame &f, jsbytecode *target) +{ + void* native = f.fp()->script()->nativeCodeForPC(f.fp()->isConstructing(), target); + if (native) + return native; + + uint32_t sourceOffset = f.pc() - f.script()->code; + uint32_t targetOffset = target - f.script()->code; + + CrossChunkEdge *edges = f.jit()->edges(); + for (size_t i = 0; i < f.jit()->nedges; i++) { + const CrossChunkEdge &edge = edges[i]; + if (edge.source == sourceOffset && edge.target == targetOffset) + return edge.shimLabel; } + + JS_NOT_REACHED("Missing edge"); + return NULL; } void * JS_FASTCALL @@ -2342,21 +1497,17 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); - bool ctor = f.fp()->isConstructing(); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; - if (!lval.isPrimitive()) { - void* native = script->nativeCodeForPC(ctor, pc + GET_JUMP_OFFSET(pc)); - JS_ASSERT(native); - return native; - } + if (!lval.isPrimitive()) + return FindNativeCode(f, pc + GET_JUMP_OFFSET(pc)); JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); pc += JUMP_OFFSET_LEN; - uint32 npairs = GET_UINT16(pc); + uint32_t npairs = GET_UINT16(pc); pc += UINT16_LEN; JS_ASSERT(npairs); @@ -2365,60 +1516,49 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSLinearString *str = lval.toString()->ensureLinear(f.cx); if (!str) THROWV(NULL); - for (uint32 i = 1; i <= npairs; i++) { + for (uint32_t i = 1; i <= npairs; i++) { Value rval = script->getConst(GET_INDEX(pc)); pc += INDEX_LEN; if (rval.isString()) { JSLinearString *rhs = &rval.toString()->asLinear(); - if (rhs == str || EqualStrings(str, rhs)) { - void* native = script->nativeCodeForPC(ctor, - jpc + GET_JUMP_OFFSET(pc)); - JS_ASSERT(native); - return native; - } + if (rhs == str || EqualStrings(str, rhs)) + return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc)); } pc += JUMP_OFFSET_LEN; } } else if (lval.isNumber()) { double d = lval.toNumber(); - for (uint32 i = 1; i <= npairs; i++) { + for (uint32_t i = 1; i <= npairs; i++) { Value rval = script->getConst(GET_INDEX(pc)); pc += INDEX_LEN; - if (rval.isNumber() && d == rval.toNumber()) { - void* native = script->nativeCodeForPC(ctor, - jpc + GET_JUMP_OFFSET(pc)); - JS_ASSERT(native); - return native; - } + if (rval.isNumber() && d == rval.toNumber()) + return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc)); pc += JUMP_OFFSET_LEN; } } else { - for (uint32 i = 1; i <= npairs; i++) { + for (uint32_t i = 1; i <= npairs; i++) { Value rval = script->getConst(GET_INDEX(pc)); pc += INDEX_LEN; - if (lval == rval) { - void* native = script->nativeCodeForPC(ctor, - jpc + GET_JUMP_OFFSET(pc)); - JS_ASSERT(native); - return native; - } + if (lval == rval) + return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc)); pc += JUMP_OFFSET_LEN; } } - void* native = script->nativeCodeForPC(ctor, jpc + GET_JUMP_OFFSET(jpc)); - JS_ASSERT(native); - return native; + return FindNativeCode(f, jpc + GET_JUMP_OFFSET(jpc)); } void * JS_FASTCALL stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) { jsbytecode * const originalPC = origPc; - jsbytecode *pc = originalPC; - uint32 jumpOffset = GET_JUMP_OFFSET(pc); - pc += JUMP_OFFSET_LEN; + DebugOnly op = JSOp(*originalPC); + JS_ASSERT(op == JSOP_TABLESWITCH); + + uint32_t jumpOffset = GET_JUMP_OFFSET(originalPC); + jsbytecode *pc = originalPC + JUMP_OFFSET_LEN; + /* Note: compiler adjusts the stack beforehand. */ Value rval = f.regs.sp[-1]; @@ -2446,89 +1586,64 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) tableIdx -= low; if ((jsuint) tableIdx < (jsuint)(high - low + 1)) { pc += JUMP_OFFSET_LEN * tableIdx; - uint32 candidateOffset = GET_JUMP_OFFSET(pc); - if (candidateOffset) + if (uint32_t candidateOffset = GET_JUMP_OFFSET(pc)) jumpOffset = candidateOffset; } } finally: /* Provide the native address. */ - JSScript* script = f.fp()->script(); - void* native = script->nativeCodeForPC(f.fp()->isConstructing(), - originalPC + jumpOffset); - JS_ASSERT(native); - return native; -} - -void JS_FASTCALL -stubs::Unbrand(VMFrame &f) -{ - const Value &thisv = f.regs.sp[-1]; - if (!thisv.isObject()) - return; - JSObject *obj = &thisv.toObject(); - if (obj->isNative()) - obj->unbrand(f.cx); + return FindNativeCode(f, originalPC + jumpOffset); } void JS_FASTCALL stubs::Pos(VMFrame &f) { - if (!ValueToNumber(f.cx, &f.regs.sp[-1])) + if (!ToNumber(f.cx, &f.regs.sp[-1])) THROW(); + if (!f.regs.sp[-1].isInt32()) + TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); } void JS_FASTCALL -stubs::ArgSub(VMFrame &f, uint32 n) +stubs::DelName(VMFrame &f, PropertyName *name) { - jsid id = INT_TO_JSID(n); - Value rval; - if (!js_GetArgsProperty(f.cx, f.fp(), id, &rval)) - THROW(); - f.regs.sp[0] = rval; -} - -void JS_FASTCALL -stubs::DelName(VMFrame &f, JSAtom *atom) -{ - jsid id = ATOM_TO_JSID(atom); JSObject *obj, *obj2; JSProperty *prop; - if (!js_FindProperty(f.cx, id, &obj, &obj2, &prop)) + if (!FindProperty(f.cx, name, f.cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) THROW(); /* Strict mode code should never contain JSOP_DELNAME opcodes. */ - JS_ASSERT(!f.fp()->script()->strictModeCode); + JS_ASSERT(!f.script()->strictModeCode); /* ECMA says to return true if name is undefined or inherited. */ f.regs.sp++; f.regs.sp[-1] = BooleanValue(true); if (prop) { - if (!obj->deleteProperty(f.cx, id, &f.regs.sp[-1], false)) + if (!obj->deleteProperty(f.cx, name, &f.regs.sp[-1], false)) THROW(); } } template void JS_FASTCALL -stubs::DelProp(VMFrame &f, JSAtom *atom) +stubs::DelProp(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx; - JSObject *obj = ValueToObject(cx, &f.regs.sp[-1]); + JSObject *obj = ValueToObject(cx, f.regs.sp[-1]); if (!obj) THROW(); Value rval; - if (!obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, strict)) + if (!obj->deleteProperty(cx, name, &rval, strict)) THROW(); f.regs.sp[-1] = rval; } -template void JS_FASTCALL stubs::DelProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL stubs::DelProp(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL stubs::DelProp(VMFrame &f, PropertyName *name); +template void JS_FASTCALL stubs::DelProp(VMFrame &f, PropertyName *name); template void JS_FASTCALL @@ -2536,74 +1651,43 @@ stubs::DelElem(VMFrame &f) { JSContext *cx = f.cx; - JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]); + JSObject *obj = ValueToObject(cx, f.regs.sp[-2]); if (!obj) THROW(); - jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1])) - THROW(); + const Value &propval = f.regs.sp[-1]; + Value &rval = f.regs.sp[-2]; - if (!obj->deleteProperty(cx, id, &f.regs.sp[-2], strict)) + if (!obj->deleteByValue(cx, propval, &rval, strict)) THROW(); } void JS_FASTCALL -stubs::DefVarOrConst(VMFrame &f, JSAtom *atom) +stubs::DefVarOrConst(VMFrame &f, PropertyName *dn) { - JSContext *cx = f.cx; - StackFrame *fp = f.fp(); - - JSObject *obj = &cx->stack.currentVarObj(); - JS_ASSERT(!obj->getOps()->defineProperty); uintN attrs = JSPROP_ENUMERATE; - if (!fp->isEvalFrame()) + if (!f.fp()->isEvalFrame()) attrs |= JSPROP_PERMANENT; - - /* Lookup id in order to check for redeclaration problems. */ - jsid id = ATOM_TO_JSID(atom); - bool shouldDefine; - if (JSOp(*f.regs.pc) == JSOP_DEFVAR) { - /* - * Redundant declaration of a |var|, even one for a non-writable - * property like |undefined| in ES5, does nothing. - */ - JSProperty *prop; - JSObject *obj2; - if (!obj->lookupProperty(cx, id, &obj2, &prop)) - THROW(); - shouldDefine = (!prop || obj2 != obj); - } else { - JS_ASSERT(JSOp(*f.regs.pc) == JSOP_DEFCONST); + if (JSOp(*f.regs.pc) == JSOP_DEFCONST) attrs |= JSPROP_READONLY; - if (!CheckRedeclaration(cx, obj, id, attrs)) - THROW(); - /* - * As attrs includes readonly, CheckRedeclaration can succeed only - * if prop does not exist. - */ - shouldDefine = true; - } + JSObject &obj = f.fp()->varObj(); - /* Bind a variable only if it's not yet defined. */ - if (shouldDefine && - !js_DefineNativeProperty(cx, obj, id, UndefinedValue(), PropertyStub, StrictPropertyStub, - attrs, 0, 0, NULL)) { + if (!DefVarOrConstOperation(f.cx, obj, dn, attrs)) THROW(); - } } void JS_FASTCALL -stubs::SetConst(VMFrame &f, JSAtom *atom) +stubs::SetConst(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx; - JSObject *obj = &cx->stack.currentVarObj(); + JSObject *obj = &f.fp()->varObj(); const Value &ref = f.regs.sp[-1]; - if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ref, - PropertyStub, StrictPropertyStub, - JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { + + if (!obj->defineProperty(cx, name, ref, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) + { THROW(); } } @@ -2621,12 +1705,12 @@ stubs::In(VMFrame &f) JSObject *obj = &rref.toObject(); jsid id; - if (!FetchElementId(f, obj, f.regs.sp[-2], id, &f.regs.sp[-2])) + if (!FetchElementId(f.cx, obj, f.regs.sp[-2], id, &f.regs.sp[-2])) THROWV(JS_FALSE); JSObject *obj2; JSProperty *prop; - if (!obj->lookupProperty(cx, id, &obj2, &prop)) + if (!obj->lookupGeneric(cx, id, &obj2, &prop)) THROWV(JS_FALSE); return !!prop; @@ -2635,14 +1719,193 @@ stubs::In(VMFrame &f) template void JS_FASTCALL stubs::DelElem(VMFrame &f); template void JS_FASTCALL stubs::DelElem(VMFrame &f); +void JS_FASTCALL +stubs::TypeBarrierHelper(VMFrame &f, uint32_t which) +{ + JS_ASSERT(which == 0 || which == 1); + + /* The actual pushed value is at sp[0], fix up the stack. See finishBarrier. */ + Value &result = f.regs.sp[-1 - (int)which]; + result = f.regs.sp[0]; + + /* + * Break type barriers at this bytecode if we have added many objects to + * the target already. This isn't needed if inference results for the + * script have been destroyed, as we will reanalyze and prune type barriers + * as they are regenerated. + */ + if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) { + AutoEnterTypeInference enter(f.cx); + f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false); + } + + TypeScript::Monitor(f.cx, f.script(), f.pc(), result); +} + +void JS_FASTCALL +stubs::StubTypeHelper(VMFrame &f, int32_t which) +{ + const Value &result = f.regs.sp[which]; + + if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) { + AutoEnterTypeInference enter(f.cx); + f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false); + } + + TypeScript::Monitor(f.cx, f.script(), f.pc(), result); +} + +/* + * Variant of TypeBarrierHelper for checking types after making a native call. + * The stack is already correct, and no fixup should be performed. + */ +void JS_FASTCALL +stubs::TypeBarrierReturn(VMFrame &f, Value *vp) +{ + TypeScript::Monitor(f.cx, f.script(), f.pc(), vp[0]); +} + +void JS_FASTCALL +stubs::NegZeroHelper(VMFrame &f) +{ + f.regs.sp[-1].setDouble(-0.0); + TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); +} + +void JS_FASTCALL +stubs::CheckArgumentTypes(VMFrame &f) +{ + StackFrame *fp = f.fp(); + JSFunction *fun = fp->fun(); + JSScript *script = fun->script(); + RecompilationMonitor monitor(f.cx); + + { + /* Postpone recompilations until all args have been updated. */ + types::AutoEnterTypeInference enter(f.cx); + + if (!f.fp()->isConstructing()) + TypeScript::SetThis(f.cx, script, fp->thisValue()); + for (unsigned i = 0; i < fun->nargs; i++) + TypeScript::SetArgument(f.cx, script, i, fp->formalArg(i)); + } + + if (monitor.recompiled()) + return; + +#ifdef JS_MONOIC + ic::GenerateArgumentCheckStub(f); +#endif +} + +#ifdef DEBUG +void JS_FASTCALL +stubs::AssertArgumentTypes(VMFrame &f) +{ + StackFrame *fp = f.fp(); + JSFunction *fun = fp->fun(); + JSScript *script = fun->script(); + + /* + * Don't check the type of 'this' for constructor frames, the 'this' value + * has not been constructed yet. + */ + if (!fp->isConstructing()) { + Type type = GetValueType(f.cx, fp->thisValue()); + if (!TypeScript::ThisTypes(script)->hasType(type)) + TypeFailure(f.cx, "Missing type for this: %s", TypeString(type)); + } + + for (unsigned i = 0; i < fun->nargs; i++) { + Type type = GetValueType(f.cx, fp->formalArg(i)); + if (!TypeScript::ArgTypes(script, i)->hasType(type)) + TypeFailure(f.cx, "Missing type for arg %d: %s", i, TypeString(type)); + } +} +#endif + +/* + * These two are never actually called, they just give us a place to rejoin if + * there is an invariant failure when initially entering a loop. + */ +void JS_FASTCALL stubs::MissedBoundsCheckEntry(VMFrame &f) {} +void JS_FASTCALL stubs::MissedBoundsCheckHead(VMFrame &f) {} + +void * JS_FASTCALL +stubs::InvariantFailure(VMFrame &f, void *rval) +{ + /* + * Patch this call to the return site of the call triggering the invariant + * failure (or a MissedBoundsCheck* function if the failure occurred on + * initial loop entry), and trigger a recompilation which will then + * redirect to the rejoin point for that call. We want to make things look + * to the recompiler like we are still inside that call, and that after + * recompilation we will return to the call's rejoin point. + */ + void *repatchCode = f.scratch; + JS_ASSERT(repatchCode); + void **frameAddr = f.returnAddressLocation(); + *frameAddr = repatchCode; + + /* Recompile the outermost script, and don't hoist any bounds checks. */ + JSScript *script = f.fp()->script(); + JS_ASSERT(!script->failedBoundsCheck); + script->failedBoundsCheck = true; + + ExpandInlineFrames(f.cx->compartment); + + mjit::Recompiler::clearStackReferences(f.cx, script); + mjit::ReleaseScriptCode(f.cx, script); + + /* Return the same value (if any) as the call triggering the invariant failure. */ + return rval; +} + void JS_FASTCALL stubs::Exception(VMFrame &f) { + // Check the interrupt flag to allow interrupting deeply nested exception + // handling. + if (f.cx->runtime->interrupt && !js_HandleExecutionInterrupt(f.cx)) + THROW(); + f.regs.sp[0] = f.cx->getPendingException(); f.cx->clearPendingException(); } + +void JS_FASTCALL +stubs::FunctionFramePrologue(VMFrame &f) +{ + if (!f.fp()->functionPrologue(f.cx)) + THROW(); +} + +void JS_FASTCALL +stubs::FunctionFrameEpilogue(VMFrame &f) +{ + f.fp()->functionEpilogue(); +} + +void JS_FASTCALL +stubs::AnyFrameEpilogue(VMFrame &f) +{ + /* + * On the normal execution path, emitReturn calls ScriptDebugEpilogue + * and inlines ScriptEpilogue. This function implements forced early + * returns, so it must have the same effect. + */ + bool ok = true; + if (f.cx->compartment->debugMode()) + ok = js::ScriptDebugEpilogue(f.cx, f.fp(), ok); + ok = ScriptEpilogue(f.cx, f.fp(), ok); + if (!ok) + THROW(); + if (f.fp()->isNonEvalFunctionFrame()) + f.fp()->functionEpilogue(); +} + template -int32 JS_FASTCALL +int32_t JS_FASTCALL stubs::ConvertToTypedInt(JSContext *cx, Value *vp) { JS_ASSERT(!vp->isInt32()); @@ -2661,7 +1924,7 @@ stubs::ConvertToTypedInt(JSContext *cx, Value *vp) JS_ASSERT(vp->isString()); - int32 i32 = 0; + int32_t i32 = 0; #ifdef DEBUG bool success = #endif @@ -2671,8 +1934,8 @@ stubs::ConvertToTypedInt(JSContext *cx, Value *vp) return i32; } -template int32 JS_FASTCALL stubs::ConvertToTypedInt(JSContext *, Value *); -template int32 JS_FASTCALL stubs::ConvertToTypedInt(JSContext *, Value *); +template int32_t JS_FASTCALL stubs::ConvertToTypedInt(JSContext *, Value *); +template int32_t JS_FASTCALL stubs::ConvertToTypedInt(JSContext *, Value *); void JS_FASTCALL stubs::ConvertToTypedFloat(JSContext *cx, Value *vp) @@ -2697,3 +1960,16 @@ stubs::ConvertToTypedFloat(JSContext *cx, Value *vp) } } +void JS_FASTCALL +stubs::WriteBarrier(VMFrame &f, Value *addr) +{ + js::gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), *addr, "write barrier"); +} + +void JS_FASTCALL +stubs::GCThingWriteBarrier(VMFrame &f, Value *addr) +{ + gc::Cell *cell = (gc::Cell *)addr->toGCThing(); + if (cell && !cell->isMarked()) + gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), *addr, "write barrier"); +} diff --git a/deps/mozjs/js/src/methodjit/StubCalls.h b/deps/mozjs/js/src/methodjit/StubCalls.h index 989bfa7c491..d6bbd6684c6 100644 --- a/deps/mozjs/js/src/methodjit/StubCalls.h +++ b/deps/mozjs/js/src/methodjit/StubCalls.h @@ -38,7 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef jslogic_h__ +#if !defined jslogic_h__ && defined JS_METHODJIT #define jslogic_h__ #include "MethodJIT.h" @@ -54,25 +54,29 @@ typedef enum JSTrapType { } JSTrapType; void JS_FASTCALL This(VMFrame &f); -JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count); -JSObject * JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base); -void JS_FASTCALL Trap(VMFrame &f, uint32 trapTypes); -void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc); +void JS_FASTCALL NewInitArray(VMFrame &f, uint32_t count); +void JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base); +void JS_FASTCALL Trap(VMFrame &f, uint32_t trapTypes); +void JS_FASTCALL DebuggerStatement(VMFrame &f, jsbytecode *pc); void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc); -void JS_FASTCALL InitElem(VMFrame &f, uint32 last); -void JS_FASTCALL InitProp(VMFrame &f, JSAtom *atom); -void JS_FASTCALL InitMethod(VMFrame &f, JSAtom *atom); +void JS_FASTCALL RecompileForInline(VMFrame &f); +void JS_FASTCALL InitElem(VMFrame &f, uint32_t last); +void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name); +void JS_FASTCALL InitMethod(VMFrame &f, PropertyName *name); void JS_FASTCALL HitStackQuota(VMFrame &f); -void * JS_FASTCALL FixupArity(VMFrame &f, uint32 argc); -void * JS_FASTCALL CompileFunction(VMFrame &f, uint32 argc); -void JS_FASTCALL SlowNew(VMFrame &f, uint32 argc); -void JS_FASTCALL SlowCall(VMFrame &f, uint32 argc); -void * JS_FASTCALL UncachedNew(VMFrame &f, uint32 argc); -void * JS_FASTCALL UncachedCall(VMFrame &f, uint32 argc); -void JS_FASTCALL Eval(VMFrame &f, uint32 argc); +void * JS_FASTCALL FixupArity(VMFrame &f, uint32_t argc); +void * JS_FASTCALL CompileFunction(VMFrame &f, uint32_t argc); +void JS_FASTCALL SlowNew(VMFrame &f, uint32_t argc); +void JS_FASTCALL SlowCall(VMFrame &f, uint32_t argc); +void * JS_FASTCALL UncachedNew(VMFrame &f, uint32_t argc); +void * JS_FASTCALL UncachedCall(VMFrame &f, uint32_t argc); +void * JS_FASTCALL UncachedLoweredCall(VMFrame &f, uint32_t argc); +void JS_FASTCALL Eval(VMFrame &f, uint32_t argc); void JS_FASTCALL ScriptDebugPrologue(VMFrame &f); void JS_FASTCALL ScriptDebugEpilogue(VMFrame &f); +void JS_FASTCALL ScriptProbeOnlyPrologue(VMFrame &f); +void JS_FASTCALL ScriptProbeOnlyEpilogue(VMFrame &f); /* * Result struct for UncachedXHelper. @@ -86,13 +90,11 @@ void JS_FASTCALL ScriptDebugEpilogue(VMFrame &f); * to JM native code. Then all fields are non-NULL. */ struct UncachedCallResult { - JSObject *callee; // callee object JSFunction *fun; // callee function void *codeAddr; // code address of compiled callee function bool unjittable; // did we try to JIT and fail? void init() { - callee = NULL; fun = NULL; codeAddr = NULL; unjittable = false; @@ -104,79 +106,48 @@ struct UncachedCallResult { * These functions either execute the function, return a native code * pointer that can be used to call the function, or throw. */ -void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); -void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); +void UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallResult *ucr); +void UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult *ucr); void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); -void JS_FASTCALL PutActivationObjects(VMFrame &f); -void JS_FASTCALL CreateFunCallObject(VMFrame &f); -#if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic); -#else -void * JS_FASTCALL InvokeTracer(VMFrame &f); -#endif void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc); void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); -void JS_FASTCALL BindName(VMFrame &f); -void JS_FASTCALL BindNameNoCache(VMFrame &f, JSAtom *atom); +void JS_FASTCALL BindName(VMFrame &f, PropertyName *name); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); -template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL SetGlobalNameNoCache(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL SetName(VMFrame &f, PropertyName *name); +template void JS_FASTCALL SetGlobalName(VMFrame &f, PropertyName *name); void JS_FASTCALL Name(VMFrame &f); -void JS_FASTCALL GetProp(VMFrame &f); -void JS_FASTCALL GetPropNoCache(VMFrame &f, JSAtom *atom); +void JS_FASTCALL GetProp(VMFrame &f, PropertyName *name); +void JS_FASTCALL GetPropNoCache(VMFrame &f, PropertyName *name); void JS_FASTCALL GetElem(VMFrame &f); -void JS_FASTCALL CallElem(VMFrame &f); template void JS_FASTCALL SetElem(VMFrame &f); -void JS_FASTCALL Length(VMFrame &f); -void JS_FASTCALL CallName(VMFrame &f); -void JS_FASTCALL PushImplicitThisForGlobal(VMFrame &f); -void JS_FASTCALL GetUpvar(VMFrame &f, uint32 index); -void JS_FASTCALL GetGlobalName(VMFrame &f); - -template void JS_FASTCALL NameInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL NameDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL IncName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL DecName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL GlobalNameInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL GlobalNameDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL IncGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL DecGlobalName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL PropInc(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL PropDec(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL IncProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL DecProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL ElemInc(VMFrame &f); -template void JS_FASTCALL ElemDec(VMFrame &f); -template void JS_FASTCALL IncElem(VMFrame &f); -template void JS_FASTCALL DecElem(VMFrame &f); -void JS_FASTCALL CallProp(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL DelProp(VMFrame &f, JSAtom *atom); +void JS_FASTCALL ToId(VMFrame &f); +void JS_FASTCALL ImplicitThis(VMFrame &f, PropertyName *name); +void JS_FASTCALL GetUpvar(VMFrame &f, uint32_t index); + +template void JS_FASTCALL DelProp(VMFrame &f, PropertyName *name); template void JS_FASTCALL DelElem(VMFrame &f); -void JS_FASTCALL DelName(VMFrame &f, JSAtom *atom); +void JS_FASTCALL DelName(VMFrame &f, PropertyName *name); JSBool JS_FASTCALL In(VMFrame &f); -void JS_FASTCALL DefVarOrConst(VMFrame &f, JSAtom *atom); -void JS_FASTCALL SetConst(VMFrame &f, JSAtom *atom); +void JS_FASTCALL DefVarOrConst(VMFrame &f, PropertyName *name); +void JS_FASTCALL SetConst(VMFrame &f, PropertyName *name); template void JS_FASTCALL DefFun(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL DefLocalFun(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL DefLocalFun_FC(VMFrame &f, JSFunction *fun); -JSObject * JS_FASTCALL RegExp(VMFrame &f, JSObject *regex); +void JS_FASTCALL RegExp(VMFrame &f, JSObject *regex); JSObject * JS_FASTCALL Lambda(VMFrame &f, JSFunction *fun); -JSObject * JS_FASTCALL LambdaForInit(VMFrame &f, JSFunction *fun); -JSObject * JS_FASTCALL LambdaForSet(VMFrame &f, JSFunction *fun); +JSObject * JS_FASTCALL LambdaJoinableForInit(VMFrame &f, JSFunction *fun); +JSObject * JS_FASTCALL LambdaJoinableForSet(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL LambdaJoinableForCall(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL LambdaJoinableForNull(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL FlatLambda(VMFrame &f, JSFunction *fun); void JS_FASTCALL Arguments(VMFrame &f); -void JS_FASTCALL ArgSub(VMFrame &f, uint32 n); void JS_FASTCALL EnterBlock(VMFrame &f, JSObject *obj); -void JS_FASTCALL LeaveBlock(VMFrame &f, JSObject *blockChain); +void JS_FASTCALL LeaveBlock(VMFrame &f); JSBool JS_FASTCALL LessThan(VMFrame &f); JSBool JS_FASTCALL LessEqual(VMFrame &f); @@ -203,8 +174,8 @@ void JS_FASTCALL Not(VMFrame &f); void JS_FASTCALL StrictEq(VMFrame &f); void JS_FASTCALL StrictNe(VMFrame &f); -void JS_FASTCALL Iter(VMFrame &f, uint32 flags); -void JS_FASTCALL IterNext(VMFrame &f); +void JS_FASTCALL Iter(VMFrame &f, uint32_t flags); +void JS_FASTCALL IterNext(VMFrame &f, int32_t offset); JSBool JS_FASTCALL IterMore(VMFrame &f); void JS_FASTCALL EndIter(VMFrame &f); @@ -212,14 +183,48 @@ JSBool JS_FASTCALL ValueToBoolean(VMFrame &f); JSString * JS_FASTCALL TypeOf(VMFrame &f); JSBool JS_FASTCALL InstanceOf(VMFrame &f); void JS_FASTCALL FastInstanceOf(VMFrame &f); -void JS_FASTCALL ArgCnt(VMFrame &f); -void JS_FASTCALL Unbrand(VMFrame &f); -template int32 JS_FASTCALL ConvertToTypedInt(JSContext *cx, Value *vp); +/* + * Helper for triggering recompilation should a name read miss a type barrier, + * produce undefined or -0. + */ +void JS_FASTCALL TypeBarrierHelper(VMFrame &f, uint32_t which); +void JS_FASTCALL TypeBarrierReturn(VMFrame &f, Value *vp); +void JS_FASTCALL NegZeroHelper(VMFrame &f); + +void JS_FASTCALL StubTypeHelper(VMFrame &f, int32_t which); + +void JS_FASTCALL CheckArgumentTypes(VMFrame &f); + +#ifdef DEBUG +void JS_FASTCALL AssertArgumentTypes(VMFrame &f); +#endif + +void JS_FASTCALL MissedBoundsCheckEntry(VMFrame &f); +void JS_FASTCALL MissedBoundsCheckHead(VMFrame &f); +void * JS_FASTCALL InvariantFailure(VMFrame &f, void *repatchCode); + +template int32_t JS_FASTCALL ConvertToTypedInt(JSContext *cx, Value *vp); void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp); void JS_FASTCALL Exception(VMFrame &f); +void JS_FASTCALL FunctionFramePrologue(VMFrame &f); +void JS_FASTCALL FunctionFrameEpilogue(VMFrame &f); + +void JS_FASTCALL AnyFrameEpilogue(VMFrame &f); + +JSObject * JS_FASTCALL +NewDenseUnallocatedArray(VMFrame &f, uint32_t length); + +void JS_FASTCALL ArrayConcatTwoArrays(VMFrame &f); +void JS_FASTCALL ArrayShift(VMFrame &f); + +void JS_FASTCALL WriteBarrier(VMFrame &f, Value *addr); +void JS_FASTCALL GCThingWriteBarrier(VMFrame &f, Value *addr); + +void JS_FASTCALL CrossChunkShim(VMFrame &f, void *edge); + } /* namespace stubs */ /* @@ -238,5 +243,8 @@ inline FuncPtr FunctionTemplateConditional(bool cond, FuncPtr a, FuncPtr b) { extern "C" void * js_InternalThrow(js::VMFrame &f); +extern "C" void * +js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f); + #endif /* jslogic_h__ */ diff --git a/deps/mozjs/js/src/methodjit/StubCompiler.cpp b/deps/mozjs/js/src/methodjit/StubCompiler.cpp index 1bde46072a6..66bbcfbcf4e 100644 --- a/deps/mozjs/js/src/methodjit/StubCompiler.cpp +++ b/deps/mozjs/js/src/methodjit/StubCompiler.cpp @@ -47,11 +47,10 @@ using namespace js; using namespace mjit; -StubCompiler::StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame, JSScript *script) +StubCompiler::StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame) : cx(cx), cc(cc), frame(frame), - script(script), generation(1), lastGeneration(0), exits(CompilerAllocPolicy(cx, cc)), @@ -152,8 +151,9 @@ StubCompiler::rejoin(Changes changes) frame.merge(masm, changes); - Jump j = masm.jump(); - crossJump(j, cc.getLabel()); + unsigned index = crossJump(masm.jump(), cc.getLabel()); + if (cc.loop) + cc.loop->addJoin(index, false); JaegerSpew(JSpew_Insns, " ---- END SLOW RESTORE CODE ---- \n"); } @@ -167,58 +167,98 @@ StubCompiler::linkRejoin(Jump j) typedef JSC::MacroAssembler::RegisterID RegisterID; typedef JSC::MacroAssembler::ImmPtr ImmPtr; typedef JSC::MacroAssembler::Imm32 Imm32; +typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr; JSC::MacroAssembler::Call -StubCompiler::emitStubCall(void *ptr, uint32 id) +StubCompiler::emitStubCall(void *ptr, RejoinState rejoin, Uses uses) { - return emitStubCall(ptr, frame.stackDepth() + script->nfixed, id); + return emitStubCall(ptr, rejoin, uses, frame.totalDepth()); } JSC::MacroAssembler::Call -StubCompiler::emitStubCall(void *ptr, int32 slots, uint32 id) +StubCompiler::emitStubCall(void *ptr, RejoinState rejoin, Uses uses, int32_t slots) { JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW CALL CODE ---- \n"); - Call cl = masm.fallibleVMCall(ptr, cc.getPC(), slots); + masm.bumpStubCounter(cc.script, cc.PC, Registers::tempCallReg()); + DataLabelPtr inlinePatch; + Call cl = masm.fallibleVMCall(cx->typeInferenceEnabled(), + ptr, cc.outerPC(), &inlinePatch, slots); JaegerSpew(JSpew_Insns, " ---- END SLOW CALL CODE ---- \n"); - if (cc.debugMode()) { - Compiler::InternalCallSite site(masm.callReturnOffset(cl), cc.getPC(), id, true, true); - cc.addCallSite(site); + + /* Add the call site for debugging and recompilation. */ + Compiler::InternalCallSite site(masm.callReturnOffset(cl), + cc.inlineIndex(), cc.inlinePC(), + rejoin, true); + site.inlinePatch = inlinePatch; + + /* Add a hook for restoring loop invariants if necessary. */ + if (cc.loop && cc.loop->generatingInvariants()) { + site.loopJumpLabel = masm.label(); + Jump j = masm.jump(); + Label l = masm.label(); + /* MissedBoundsCheck* are not actually called, so f.regs need to be written before InvariantFailure. */ + bool entry = (ptr == JS_FUNC_TO_DATA_PTR(void *, stubs::MissedBoundsCheckEntry)) + || (ptr == JS_FUNC_TO_DATA_PTR(void *, stubs::MissedBoundsCheckHead)); + cc.loop->addInvariantCall(j, l, true, entry, cc.callSites.length(), uses); } + + cc.addCallSite(site); return cl; } void -StubCompiler::fixCrossJumps(uint8 *ncode, size_t offset, size_t total) +StubCompiler::fixCrossJumps(uint8_t *ncode, size_t offset, size_t total) { - JSC::LinkBuffer fast(ncode, total); - JSC::LinkBuffer slow(ncode + offset, total - offset); + JSC::LinkBuffer fast(ncode, total, JSC::METHOD_CODE); + JSC::LinkBuffer slow(ncode + offset, total - offset, JSC::METHOD_CODE); for (size_t i = 0; i < exits.length(); i++) fast.link(exits[i].from, slow.locationOf(exits[i].to)); for (size_t i = 0; i < scriptJoins.length(); i++) { const CrossJumpInScript &cj = scriptJoins[i]; - slow.link(cj.from, fast.locationOf(cc.labelOf(cj.pc))); + slow.link(cj.from, fast.locationOf(cc.labelOf(cj.pc, cj.inlineIndex))); } for (size_t i = 0; i < joins.length(); i++) slow.link(joins[i].from, fast.locationOf(joins[i].to)); } -void +unsigned StubCompiler::crossJump(Jump j, Label L) { joins.append(CrossPatch(j, L)); + + /* This won't underflow, as joins has space preallocated for some entries. */ + return joins.length() - 1; } bool StubCompiler::jumpInScript(Jump j, jsbytecode *target) { if (cc.knownJump(target)) { - crossJump(j, cc.labelOf(target)); - return true; + unsigned index = crossJump(j, cc.labelOf(target, cc.inlineIndex())); + if (cc.loop) + cc.loop->addJoin(index, false); } else { - return scriptJoins.append(CrossJumpInScript(j, target)); + if (!scriptJoins.append(CrossJumpInScript(j, target, cc.inlineIndex()))) + return false; + if (cc.loop) + cc.loop->addJoin(scriptJoins.length() - 1, true); } + return true; } +void +StubCompiler::patchJoin(unsigned i, bool script, Assembler::Address address, AnyRegisterID reg) +{ + Jump &j = script ? scriptJoins[i].from : joins[i].from; + j.linkTo(masm.label(), &masm); + + if (reg.isReg()) + masm.loadPayload(address, reg.reg()); + else + masm.loadDouble(address, reg.fpreg()); + + j = masm.jump(); +} diff --git a/deps/mozjs/js/src/methodjit/StubCompiler.h b/deps/mozjs/js/src/methodjit/StubCompiler.h index e7a8ccc8806..e036b04f5f3 100644 --- a/deps/mozjs/js/src/methodjit/StubCompiler.h +++ b/deps/mozjs/js/src/methodjit/StubCompiler.h @@ -42,7 +42,6 @@ #define jsstub_compiler_h__ #include "jscntxt.h" -#include "jstl.h" #include "MethodJIT.h" #include "methodjit/FrameState.h" #include "CodeGenIncludes.h" @@ -68,25 +67,25 @@ class StubCompiler }; struct CrossJumpInScript { - CrossJumpInScript(Jump from, jsbytecode *pc) - : from(from), pc(pc) + CrossJumpInScript(Jump from, jsbytecode *pc, uint32_t inlineIndex) + : from(from), pc(pc), inlineIndex(inlineIndex) { } Jump from; jsbytecode *pc; + uint32_t inlineIndex; }; JSContext *cx; Compiler &cc; FrameState &frame; - JSScript *script; public: Assembler masm; private: - uint32 generation; - uint32 lastGeneration; + uint32_t generation; + uint32_t lastGeneration; Vector exits; Vector joins; @@ -94,13 +93,13 @@ class StubCompiler Vector jumpList; public: - StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame, JSScript *script); + StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame); size_t size() { return masm.size(); } - uint8 *buffer() { + uint8_t *buffer() { return masm.buffer(); } @@ -122,7 +121,7 @@ class StubCompiler void linkExitDirect(Jump j, Label L); void leave(); - void leaveWithDepth(uint32 depth); + void leaveWithDepth(uint32_t depth); /* * Rejoins slow-path code back to the fast-path. The invalidation param @@ -133,12 +132,14 @@ class StubCompiler void linkRejoin(Jump j); /* Finish all native code patching. */ - void fixCrossJumps(uint8 *ncode, size_t offset, size_t total); + void fixCrossJumps(uint8_t *ncode, size_t offset, size_t total); bool jumpInScript(Jump j, jsbytecode *target); - void crossJump(Jump j, Label l); + unsigned crossJump(Jump j, Label l); - Call emitStubCall(void *ptr, uint32 id); - Call emitStubCall(void *ptr, int32 slots, uint32 id); + Call emitStubCall(void *ptr, RejoinState rejoin, Uses uses); + Call emitStubCall(void *ptr, RejoinState rejoin, Uses uses, int32_t slots); + + void patchJoin(unsigned i, bool script, Assembler::Address address, AnyRegisterID reg); }; } /* namepsace mjit */ diff --git a/deps/mozjs/js/src/methodjit/TrampolineCompiler.cpp b/deps/mozjs/js/src/methodjit/TrampolineCompiler.cpp index a6ac9d709f0..74583605e0a 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineCompiler.cpp +++ b/deps/mozjs/js/src/methodjit/TrampolineCompiler.cpp @@ -41,6 +41,7 @@ #include "TrampolineCompiler.h" #include "StubCalls.h" #include "assembler/assembler/LinkBuffer.h" +#include "assembler/jit/ExecutableAllocator.h" namespace js { namespace mjit { @@ -93,14 +94,14 @@ TrampolineCompiler::compileTrampoline(Trampolines::TrampolinePtr *where, Label entry = masm.label(); CHECK_RESULT(generator(masm)); - JS_ASSERT(entry.isValid()); + JS_ASSERT(entry.isSet()); bool ok; - JSC::LinkBuffer buffer(&masm, execAlloc, poolp, &ok); + JSC::LinkBuffer buffer(&masm, execAlloc, poolp, &ok, JSC::METHOD_CODE); if (!ok) return false; masm.finalize(buffer); - uint8 *result = (uint8*)buffer.finalizeCodeAddendum().dataLocation(); + uint8_t *result = (uint8_t*)buffer.finalizeCodeAddendum().dataLocation(); *where = JS_DATA_TO_FUNC_PTR(Trampolines::TrampolinePtr, result + masm.distanceOf(entry)); return true; @@ -109,18 +110,19 @@ TrampolineCompiler::compileTrampoline(Trampolines::TrampolinePtr *where, /* * This is shamelessly copied from emitReturn, but with several changes: * - There was always at least one inline call. - * - We don't know if there is a call object, so we always check. + * - We don't know if there are activation objects or a script with nesting + * state whose active frames need adjustment, so we always stub the epilogue. * - We don't know where we came from, so we don't know frame depth or PC. * - There is no stub buffer. */ bool TrampolineCompiler::generateForceReturn(Assembler &masm) { - /* if (hasArgsObj() || hasCallObj()) stubs::PutActivationObjects() */ - Jump noActObjs = masm.branchTest32(Assembler::Zero, FrameFlagsAddress(), - Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ)); - masm.fallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::PutActivationObjects), NULL, 0); - noActObjs.linkTo(masm.label(), &masm); + /* The JSStackFrame register may have been clobbered while returning, reload it. */ + masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); + + /* Perform the frame epilogue. */ + masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::AnyFrameEpilogue), NULL, NULL, 0); /* Store any known return value */ masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); diff --git a/deps/mozjs/js/src/methodjit/TrampolineMIPS.cpp b/deps/mozjs/js/src/methodjit/TrampolineMIPS.cpp new file mode 100644 index 00000000000..fa7a1818ec5 --- /dev/null +++ b/deps/mozjs/js/src/methodjit/TrampolineMIPS.cpp @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Jaegermonkey. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chao-ying Fu + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jstypes.h" + +/* + * The MIPS VMFrame is 112 bytes as follows. + * + * 108 [ unused4 ] For alignment. + * 104 [ ra ] + * 100 [ gp ] If PIC code is generated, we will save gp. + * 96 [ s7 ] + * 92 [ s6 ] + * 88 [ s5 ] + * 84 [ s4 ] + * 80 [ s3 ] + * 76 [ s2 ] + * 72 [ s1 ] + * 68 [ s0 ] + * 64 [ stubRejoin ] + * 60 [ entrycode ] + * 56 [ entryfp ] + * 52 [ stkLimit ] + * 48 [ cx ] + * 44 [ regs.fp_ ] + * 40 [ regs.inlined_] + * 36 [ regs.pc ] + * 32 [ regs.sp ] + * 28 [ scratch ] + * 24 [ previous ] + * 20 [ args.ptr2 ] [ dynamicArgc ] (union) + * 16 [ args.ptr ] [ lazyArgsObj ] (union) + * 12 [ unused3 ] O32 ABI, space for a3 (used in callee) + * 8 [ unused2 ] O32 ABI, space for a2 (used in callee) + * 4 [ unused1 ] O32 ABI, space for a1 (used in callee) + * 0 [ unused0 ] O32 ABI, space for a0 (used in callee) + */ + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerThrowpoline" "\n" + ".ent JaegerThrowpoline" "\n" + ".type JaegerThrowpoline,@function" "\n" +"JaegerThrowpoline:" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" + "la $25,js_InternalThrow" "\n" + ".reloc 1f,R_MIPS_JALR,js_InternalThrow" "\n" +"1: jalr $25" "\n" + "move $4,$29 # set up a0" "\n" +#else + "jal js_InternalThrow" "\n" + "move $4,$29 # set up a0" "\n" +#endif + "beq $2,$0,1f" "\n" + "nop" "\n" + "jr $2 # jump to a scripted handler" "\n" + "nop" "\n" +"1:" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" + "la $25,PopActiveVMFrame" "\n" + ".reloc 1f,R_MIPS_JALR,PopActiveVMFrame" "\n" +"1: jalr $25" "\n" + "move $4,$29 # set up a0" "\n" +#else + "jal PopActiveVMFrame" "\n" + "move $4,$29 # set up a0" "\n" +#endif + "lw $31,104($29)" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" +#endif + "lw $23,96($29)" "\n" + "lw $22,92($29)" "\n" + "lw $21,88($29)" "\n" + "lw $20,84($29)" "\n" + "lw $19,80($29)" "\n" + "lw $18,76($29)" "\n" + "lw $17,72($29)" "\n" + "lw $16,68($29)" "\n" + "li $2,0 # return 0 to represent an unhandled exception." "\n" + "jr $31" "\n" + "addiu $29,$29,112" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerThrowpoline" "\n" + ".size JaegerThrowpoline,.-JaegerThrowpoline" "\n" +); + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerTrampoline" "\n" + ".ent JaegerTrampoline" "\n" + ".type JaegerTrampoline,@function" "\n" +"JaegerTrampoline:" "\n" +#if defined(__PIC__) + "lui $28,%hi(_gp_disp)" "\n" + "addiu $28,$28,%lo(_gp_disp)" "\n" + "addu $28,$28,$25" "\n" +#endif + "addiu $29,$29,-112" "\n" + "sw $31,104($29)" "\n" +#if defined(__PIC__) + "sw $28,100($29)" "\n" +#endif + "sw $23,96($29)" "\n" + "sw $22,92($29)" "\n" + "sw $21,88($29)" "\n" + "sw $20,84($29)" "\n" + "sw $19,80($29)" "\n" + "sw $18,76($29)" "\n" + "sw $17,72($29)" "\n" + "sw $16,68($29)" "\n" + "sw $0,64($29) # stubRejoin" "\n" + "sw $5,60($29) # entrycode" "\n" + "sw $5,56($29) # entryfp" "\n" + "sw $7,52($29) # stackLimit" "\n" + "sw $4,48($29) # cx" "\n" + "sw $5,44($29) # regs.fp" "\n" + "move $16,$5 # preserve fp to s0" "\n" + "move $17,$6 # preserve code to s1" "\n" +#if defined(__PIC__) + "la $25,PushActiveVMFrame" "\n" + ".reloc 1f,R_MIPS_JALR,PushActiveVMFrame" "\n" +"1: jalr $25" "\n" + "move $4,$29 # set up a0" "\n" +#else + "jal PushActiveVMFrame" "\n" + "move $4,$29 # set up a0" "\n" +#endif + + "move $25,$17 # move code to $25" "\n" + "jr $25 # jump to the compiled JavaScript Function" "\n" + "nop" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerTrampoline" "\n" + ".size JaegerTrampoline,.-JaegerTrampoline" "\n" +); + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerTrampolineReturn" "\n" + ".ent JaegerTrampolineReturn" "\n" + ".type JaegerTrampolineReturn,@function" "\n" +"JaegerTrampolineReturn:" "\n" +#if defined(IS_LITTLE_ENDIAN) + "sw $4,28($16) # a0: fp->rval type for LITTLE-ENDIAN" "\n" + "sw $6,24($16) # a2: fp->rval data for LITTLE-ENDIAN" "\n" +#else + "sw $4,24($16) # a0: fp->rval type for BIG-ENDIAN" "\n" + "sw $6,28($16) # a2: fp->rval data for BIG-ENDIAN" "\n" +#endif +#if defined(__PIC__) + "lw $28,100($29)" "\n" + "la $25,PopActiveVMFrame" "\n" + ".reloc 1f,R_MIPS_JALR,PopActiveVMFrame" "\n" +"1: jalr $25" "\n" + "move $4,$29 # set up a0" "\n" +#else + "jal PopActiveVMFrame" "\n" + "move $4,$29 # set up a0" "\n" +#endif + "lw $31,104($29)" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" +#endif + "lw $23,96($29)" "\n" + "lw $22,92($29)" "\n" + "lw $21,88($29)" "\n" + "lw $20,84($29)" "\n" + "lw $19,80($29)" "\n" + "lw $18,76($29)" "\n" + "lw $17,72($29)" "\n" + "lw $16,68($29)" "\n" + "li $2,1 # return ture to indicate successful completion" "\n" + "jr $31" "\n" + "addiu $29,$29,112" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerTrampolineReturn" "\n" + ".size JaegerTrampolineReturn,.-JaegerTrampolineReturn" "\n" +); + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerStubVeneer" "\n" + ".ent JaegerStubVeneer" "\n" + ".type JaegerStubVeneer,@function" "\n" +"JaegerStubVeneer:" "\n" + "addiu $29,$29,-24 # Need 16 (a0-a3) + 4 (align) + 4 ($31) bytes" "\n" + "sw $31,20($29) # Store $31 to 20($29)" "\n" + "move $25,$2 # the target address is passed from $2" "\n" + "jalr $25" "\n" + "nop" "\n" + "lw $31,20($29)" "\n" + "jr $31" "\n" + "addiu $29,$29,24" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerStubVeneer" "\n" + ".size JaegerStubVeneer,.-JaegerStubVeneer" "\n" +); + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerInterpolineScripted" "\n" + ".ent JaegerInterpolineScripted" "\n" + ".type JaegerInterpolineScripted,@function" "\n" +"JaegerInterpolineScripted:" "\n" + "lw $16,16($16) # Load f->prev_" "\n" + "b JaegerInterpoline" "\n" + "sw $16,44($29) # Update f->regs->fp_" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerInterpolineScripted" "\n" + ".size JaegerInterpolineScripted,.-JaegerInterpolineScripted" "\n" +); + +asm ( + ".text" "\n" + ".align 2" "\n" + ".set noreorder" "\n" + ".set nomacro" "\n" + ".set nomips16" "\n" + ".globl JaegerInterpoline" "\n" + ".ent JaegerInterpoline" "\n" + ".type JaegerInterpoline,@function" "\n" +"JaegerInterpoline:" "\n" + "move $5,$4 # returntype" "\n" + "move $4,$6 # returnData" "\n" + "move $6,$2 # returnReg" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" + "la $25,js_InternalInterpret" "\n" + ".reloc 1f,R_MIPS_JALR,js_InternalInterpret" "\n" +"1: jalr $25" "\n" + "move $7,$29 # f" "\n" +#else + "jal js_InternalInterpret" "\n" + "move $7,$29 # f" "\n" +#endif + "lw $16,44($29) # Load f->regs->fp_ to s0" "\n" +#if defined(IS_LITTLE_ENDIAN) + "lw $4,28($16) # a0: fp->rval type for LITTLE-ENDIAN" "\n" + "lw $6,24($16) # a2: fp->rval data for LITTLE-ENDIAN" "\n" +#else + "lw $4,24($16) # a0: fp->rval type for BIG-ENDIAN" "\n" + "lw $6,28($16) # a2: fp->rval data for BIG-ENDIAN" "\n" +#endif + "lw $5,28($29) # Load sctrach -> argc" "\n" + "beq $2,$0,1f" "\n" + "nop" "\n" + "jr $2" "\n" + "nop" "\n" +"1:" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" + "la $25,PopActiveVMFrame" "\n" + ".reloc 1f,R_MIPS_JALR,PopActiveVMFrame" "\n" +"1: jalr $25" "\n" + "move $4,$29 # set up a0" "\n" +#else + "jal PopActiveVMFrame" "\n" + "move $4,$29 # set up a0" "\n" +#endif + "lw $31,104($29)" "\n" +#if defined(__PIC__) + "lw $28,100($29)" "\n" +#endif + "lw $23,96($29)" "\n" + "lw $22,92($29)" "\n" + "lw $21,88($29)" "\n" + "lw $20,84($29)" "\n" + "lw $19,80($29)" "\n" + "lw $18,76($29)" "\n" + "lw $17,72($29)" "\n" + "lw $16,68($29)" "\n" + "li $2,0 # return 0" "\n" + "jr $31" "\n" + "addiu $29,$29,112" "\n" + ".set reorder" "\n" + ".set macro" "\n" + ".end JaegerInterpoline" "\n" + ".size JaegerInterpoline,.-JaegerInterpoline" "\n" +); diff --git a/deps/mozjs/js/src/methodjit/TrampolineMasmX64.asm b/deps/mozjs/js/src/methodjit/TrampolineMasmX64.asm index 054f7a80a8b..57fc6b97529 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineMasmX64.asm +++ b/deps/mozjs/js/src/methodjit/TrampolineMasmX64.asm @@ -37,9 +37,9 @@ extern js_InternalThrow:PROC -extern SetVMFrameRegs:PROC extern PushActiveVMFrame:PROC extern PopActiveVMFrame:PROC +extern js_InternalInterpret:PROC .CODE @@ -75,6 +75,8 @@ JaegerTrampoline PROC FRAME ; rdx = fp ; r9 = inlineCallCount ; fp must go into rbx + push 0 ; stubRejoin + push rdx ; entryncode push rdx ; entryFp push r9 ; inlineCallCount push rcx ; cx @@ -92,8 +94,6 @@ JaegerTrampoline PROC FRAME push r8 mov rcx, rsp sub rsp, 20h - call SetVMFrameRegs - lea rcx, [rsp+20h] call PushActiveVMFrame add rsp, 20h @@ -104,13 +104,13 @@ JaegerTrampoline ENDP ; void JaegerTrampolineReturn(); JaegerTrampolineReturn PROC FRAME .ENDPROLOG - or rcx, rdx - mov qword ptr [rbx + 30h], rcx + or rsi, rdi + mov qword ptr [rbx+30h], rsi sub rsp, 20h lea rcx, [rsp+20h] call PopActiveVMFrame - add rsp, 58h+20h + add rsp, 68h+20h pop rbx pop rsi pop rdi @@ -139,7 +139,7 @@ JaegerThrowpoline PROC FRAME throwpoline_exit: lea rcx, [rsp+20h] call PopActiveVMFrame - add rsp, 58h+20h + add rsp, 68h+20h pop rbx pop rsi pop rdi @@ -152,5 +152,54 @@ throwpoline_exit: ret JaegerThrowpoline ENDP +JaegerInterpoline PROC FRAME + .ENDPROLOG + mov rcx, rdi + mov rdx, rsi + lea r9, [rsp+20h] + mov r8, rax + call js_InternalInterpret + mov rbx, qword ptr [rsp+38h+20h] ; Load Frame + mov rsi, qword ptr [rbx+30h] ; Load rval payload + and rsi, r14 ; Mask rval payload + mov rdi, qword ptr [rbx+30h] ; Load rval type + and rdi, r13 ; Mask rval type + mov rcx, qword ptr [rsp+18h+20h] ; Load scratch -> argc + test rax, rax + je interpoline_exit + add rsp, 20h + jmp rax + +interpoline_exit: + lea rcx, [rsp+20h] + call PopActiveVMFrame + add rsp, 68h+20h + pop rbx + pop rsi + pop rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + xor rax, rax + ret +JaegerInterpoline ENDP + +JaegerInterpolineScripted PROC FRAME + .ENDPROLOG + mov rbx, qword ptr [rbx+20h] ; Load prev + mov qword ptr [rsp+38h], rbx ; fp -> regs.fp + sub rsp, 20h + jmp JaegerInterpoline +JaegerInterpolineScripted ENDP + +JaegerInterpolinePatched PROC FRAME + sub rsp, 20h + .ALLOCSTACK 32 + .ENDPROLOG + jmp JaegerInterpoline +JaegerInterpolinePatched ENDP + END diff --git a/deps/mozjs/js/src/methodjit/TrampolineMingwX64.s b/deps/mozjs/js/src/methodjit/TrampolineMingwX64.s index 95a47ac45db..197d0b1c366 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineMingwX64.s +++ b/deps/mozjs/js/src/methodjit/TrampolineMingwX64.s @@ -37,9 +37,9 @@ .extern js_InternalThrow -.extern SetVMFrameRegs .extern PushActiveVMFrame .extern PopActiveVMFrame +.extern js_InternalInterpret .text .intel_syntax noprefix @@ -81,6 +81,8 @@ JaegerTrampoline: # rdx = fp # r9 = inlineCallCount # fp must go into rbx + push 0 # stubRejoin + push rdx # entryncode push rdx # entryFp push r9 # inlineCallCount push rcx # cx @@ -98,8 +100,6 @@ JaegerTrampoline: push r8 mov rcx, rsp sub rsp, 0x20 - call SetVMFrameRegs - lea rcx, [rsp+0x20] call PushActiveVMFrame add rsp, 0x20 @@ -114,13 +114,13 @@ JaegerTrampoline: .endef JaegerTrampolineReturn: # .ENDPROLOG - or rcx, rdx - mov qword ptr [rbx + 0x30], rcx + or rsi, rdi + mov qword ptr [rbx + 0x30], rsi sub rsp, 0x20 lea rcx, [rsp+0x20] call PopActiveVMFrame - add rsp, 0x58+0x20 + add rsp, 0x68+0x20 pop rbx pop rsi pop rdi @@ -153,7 +153,46 @@ JaegerThrowpoline: throwpoline_exit: lea rcx, [rsp+0x20] call PopActiveVMFrame - add rsp, 0x58+0x20 + add rsp, 0x68+0x20 + pop rbx + pop rsi + pop rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + xor rax, rax + ret + + +.globl JaegerInterpoline +.def JaegerInterpoline + .scl 3 + .type 46 +.endef +JaegerInterpoline: + #.ENDPROLOG + mov rcx, rdi + mov rdx, rsi + lea r9, [rsp+0x20] + mov r8, rax + call js_InternalInterpret + mov rbx, qword ptr [rsp+0x38+0x20] # Load Frame + mov rsi, qword ptr [rbx+0x30] # Load rval payload + and rsi, r14 # Mask rval payload + mov rdi, qword ptr [rbx+0x30] # Load rval type + and rdi, r13 # Mask rval type + mov rcx, qword ptr [rsp+0x18+0x20] # Load scratch -> argc + test rax, rax + je interpoline_exit + add rsp, 0x20 + jmp rax + +interpoline_exit: + lea rcx, [rsp+0x20] + call PopActiveVMFrame + add rsp, 0x68+0x20 pop rbx pop rsi pop rdi @@ -165,4 +204,25 @@ throwpoline_exit: xor rax, rax ret +.globl JaegerInterpolineScripted +.def JaegerInterpolineScripted + .scl 3 + .type 46 +.endef +JaegerInterpolineScripted: + #.ENDPROLOG + mov rbx, qword ptr [rbx+0x20] # Load prev + mov qword ptr [rsp+0x38], rbx # fp -> regs.fp + sub rsp, 0x20 + jmp JaegerInterpoline +.globl JaegerInterpolinePatched +.def JaegerInterpolinePatched + .scl 3 + .type 46 +.endef +JaegerInterpolinePatched: + sub rsp, 0x20 + #.ALLOCSTACK 32 + #.ENDPROLOG + jmp JaegerInterpoline diff --git a/deps/mozjs/js/src/methodjit/TrampolineSUNWX64.s b/deps/mozjs/js/src/methodjit/TrampolineSUNWX64.s index ae415d30be9..3787e396bc5 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineSUNWX64.s +++ b/deps/mozjs/js/src/methodjit/TrampolineSUNWX64.s @@ -38,7 +38,7 @@ .text / JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, -/ FrameRegs *regs, uintptr_t inlineCallCount) +/ Value *stackLimit) .global JaegerTrampoline .type JaegerTrampoline, @function JaegerTrampoline: @@ -62,7 +62,9 @@ JaegerTrampoline: * rcx = inlineCallCount * fp must go into rbx */ - pushq %rsi /* entryFp */ + pushq $0x0 /* stubRejoin */ + pushq %rsi /* entryncode */ + pushq %rsi /* entryfp */ pushq %rcx /* inlineCallCount */ pushq %rdi /* cx */ pushq %rsi /* fp */ @@ -77,8 +79,6 @@ JaegerTrampoline: /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ pushq %rdx movq %rsp, %rdi - call SetVMFrameRegs - movq %rsp, %rdi call PushActiveVMFrame /* Jump into into the JIT'd code. */ @@ -89,12 +89,12 @@ JaegerTrampoline: .global JaegerTrampolineReturn .type JaegerTrampolineReturn, @function JaegerTrampolineReturn: - or %rdx, %rcx - movq %rcx, 0x30(%rbx) + or %rdi, %rsi + movq %rsx, 0x30(%rbx) movq %rsp, %rdi call PopActiveVMFrame - addq $0x58, %rsp + addq $0x68, %rsp popq %rbx popq %r15 popq %r14 @@ -118,7 +118,7 @@ JaegerThrowpoline: throwpoline_exit: movq %rsp, %rdi call PopActiveVMFrame - addq $0x58, %rsp + addq $0x68, %rsp popq %rbx popq %r15 popq %r14 @@ -129,3 +129,41 @@ JaegerThrowpoline: ret .size JaegerThrowpoline, . - JaegerThrowpoline +/ void JaegerInterpoline() +.global JaegerInterpoline +.type JaegerInterpoline, @function +JaegerInterpoline: + movq %rsp, %rcx + movq %rax, %rdx + call js_InternalInterpret + movq 0x38(%rsp), %rbx /* Load frame */ + movq 0x30(%rbx), %rsi /* Load rval payload */ + and %r14, %rsi /* Mask rval payload */ + movq 0x30(%rbx), %rdi /* Load rval type */ + and %r13, %rdi /* Mask rval type */ + movq 0x18(%rsp), %rcx /* Load scratch -> argc */ + testq %rax, %rax + je interpoline_exit + jmp *%rax + interpoline_exit: + movq %rsp, %rdi + call PopActiveVMFrame + addq $0x68, %rsp + popq %rbx + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + xorq %rax,%rax + ret +.size JaegerInterpoline, . - JaegerInterpoline + +/ void JaegerInterpolineScripted() +.global JaegerInterpolineScripted +.type JaegerInterpolineScripted, @function +JaegerInterpolineScripted: + movq 0x20(%rbx), %rbx /* load prev */ + movq %rbx, 0x38(%rsp) + jmp JaegerInterpoline +.size JaegerInterpolineScripted, . - JaegerInterpolineScripted diff --git a/deps/mozjs/js/src/methodjit/TrampolineSUNWX86.s b/deps/mozjs/js/src/methodjit/TrampolineSUNWX86.s index 292046f1839..146525b1250 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineSUNWX86.s +++ b/deps/mozjs/js/src/methodjit/TrampolineSUNWX86.s @@ -38,7 +38,7 @@ .text / JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, -/ FrameRegs *regs, uintptr_t inlineCallCount) +/ Value *stackLimit) .global JaegerTrampoline .type JaegerTrampoline, @function JaegerTrampoline: @@ -52,8 +52,12 @@ JaegerTrampoline: /* Build the JIT frame. Push fields in order, */ /* then align the stack to form esp == VMFrame. */ - movl 12(%ebp), %ebx /* fp */ - pushl %ebx /* entryFp */ + movl 12(%ebp), %ebx /* load fp */ + pushl %ebx /* unused1 */ + pushl %ebx /* unused0 */ + pushl $0x0 /* stubRejoin */ + pushl %ebx /* entryncode */ + pushl %ebx /* entryfp */ pushl 20(%ebp) /* stackLimit */ pushl 8(%ebp) /* cx */ pushl %ebx /* fp */ @@ -62,22 +66,25 @@ JaegerTrampoline: /* Jump into the JIT'd code. */ /* No fastcall for sunstudio. */ pushl %esp - call SetVMFrameRegs call PushActiveVMFrame popl %edx - jmp *16(%ebp) + + movl 28(%esp), %ebp /* load fp for JIT code */ + jmp *88(%esp) .size JaegerTrampoline, . - JaegerTrampoline / void JaegerTrampolineReturn() .global JaegerTrampolineReturn .type JaegerTrampolineReturn, @function JaegerTrampolineReturn: - movl %edx, 0x18(%ebx) - movl %ecx, 0x1C(%ebx) + movl %esi, 0x18(%ebp) + movl %edi, 0x1C(%ebp) + movl %esp, %ebp + addl $0x48, %ebp pushl %esp call PopActiveVMFrame - addl $0x30, %esp + addl $0x40, %esp popl %ebx popl %edi popl %esi @@ -110,8 +117,7 @@ JaegerThrowpoline: throwpoline_exit: pushl %esp call PopActiveVMFrame - popl %ebx - addl $0x2C, %esp + addl $0x40, %esp popl %ebx popl %edi popl %esi @@ -120,3 +126,53 @@ throwpoline_exit: ret .size JaegerThrowpoline, . - JaegerThrowpoline +/ void JaegerInterpoline() +.global JaegerInterpoline +.type JaegerInterpoline, @function +JaegerInterpoline: + /* For Sun Studio there is no fast call. */ + /* We add the stack by 16 before. */ + addl $0x10, %esp + /* Align the stack to 16 bytes. */ + pushl %esp + pushl %eax + pushl %edi + pushl %esi + call js_InternalInterpret + addl $0x10, %esp + movl 0x1C(%esp), %ebp /* Load frame */ + movl 0x18(%ebp), %esi /* Load rval payload */ + movl 0x1C(%ebp), %edi /* Load rval type */ + movl 0xC(%esp), %ecx /* Load scratch -> argc, for any scripted call */ + testl %eax, %eax + je interpoline_exit + jmp *%eax +interpoline_exit: + pushl %esp + call PopActiveVMFrame + addl $0x40, %esp + popl %ebx + popl %edi + popl %esi + popl %ebp + xorl %eax, %eax + ret +.size JaegerInterpoline, . - JaegerInterpoline + +/ void JaegerInterpolineScripted() +.global JaegerInterpolineScripted +.type JaegerInterpolineScripted, @function +JaegerInterpolineScripted: + movl 0x10(%ebp), %ebp + movl %ebp, 0x1C(%esp) + subl $0x10, %esp + jmp JaegerInterpoline +.size JaegerInterpolineScripted, . - JaegerInterpolineScripted + +/ void JaegerInterpolinePatched() +.global JaegerInterpolinePatched +.type JaegerInterpolinePatched, @function +JaegerInterpolinePatched: + subl $0x10, %esp + jmp JaegerInterpoline +.size JaegerInterpolinePatched, . - JaegerInterpolinePatched diff --git a/deps/mozjs/js/src/methodjit/TrampolineSparc.s b/deps/mozjs/js/src/methodjit/TrampolineSparc.s index 7fb929f378d..fb79b5a2abf 100644 --- a/deps/mozjs/js/src/methodjit/TrampolineSparc.s +++ b/deps/mozjs/js/src/methodjit/TrampolineSparc.s @@ -44,16 +44,16 @@ .global JaegerTrampoline .type JaegerTrampoline, #function JaegerTrampoline: - save %sp,-160,%sp - st %i3, [%fp - 20] ! stackLimit - st %i0, [%fp - 24] ! cx - st %i1, [%fp - 16] ! entryFp - st %i1, [%fp - 28] ! regs->fp - mov %i1, %l0 ! fp - call SetVMFrameRegs - mov %sp, %o0 + save %sp,-168,%sp + st %i1, [%fp - 36] ! fp + st %i0, [%fp - 32] ! cx + st %i3, [%fp - 28] ! stackLimit + st %i1, [%fp - 24] ! entryFp + st %i1, [%fp - 20] ! entryncode + st %g0, [%fp - 16] ! stubRejoin call PushActiveVMFrame mov %sp, %o0 + ld [%fp - 36], %l0 ! fp jmp %i2 st %i7, [%fp - 12] ! return address .size JaegerTrampoline, . - JaegerTrampoline @@ -62,8 +62,8 @@ JaegerTrampoline: .global JaegerTrampolineReturn .type JaegerTrampolineReturn, #function JaegerTrampolineReturn: - st %i0, [%l0 + 0x18] /* fp->rval type */ - st %i1, [%l0 + 0x1c] /* fp->rval data */ + st %l2, [%l0 + 0x18] /* fp->rval type */ + st %l3, [%l0 + 0x1c] /* fp->rval data */ call PopActiveVMFrame mov %sp, %o0 ld [%fp - 12], %i7 ! return address @@ -104,14 +104,63 @@ throwpoline_exit: restore .size JaegerThrowpoline, . - JaegerThrowpoline -.global InjectJaegerReturn -.type InjectJaegerReturn, #function -InjectJaegerReturn: - ld [%l0 + 0x18], %i0 /* fp->rval type */ - ld [%l0 + 0x1c], %i1 /* fp->rval data */ - ld [%l0 + 0x14], %i7 /* fp->ncode */ - sub %i7, 8, %i7 - ld [%fp - 28], %l0 +! void JaegerInterpolineScripted() +.global JaegerInterpolineScripted +.type JaegerInterpolineScripted, #function +JaegerInterpolineScripted: + ld [%l0 + 0x10], %l0 /* Load f->prev_ */ + st %l0, [%fp - 36] /* Update f->regs->fp_ */ + ba interpoline_enter + nop +.size JaegerInterpolineScripted, . - JaegerInterpolineScripted + +! void JaegerInterpoline() +.global JaegerInterpoline +.type JaegerInterpoline, #function +JaegerInterpoline: +interpoline_enter: + mov %o0,%o2 + mov %l3,%o0 + mov %l2,%o1 + call js_InternalInterpret + mov %sp,%o3 + ld [%fp - 36], %l0 + ld [%l0 + 0x18], %l2 /* fp->rval type */ + ld [%l0 + 0x1c], %l3 /* fp->rval data */ + ld [%fp - 48], %l4 + tst %o0 + be interpoline_exit + nop + jmp %o0 + nop +interpoline_exit: + ta 3 + mov %sp, %o2 + mov %fp, %o3 + ldd [%o2 + (0*8)], %l0 + ldd [%o2 + (1*8)], %l2 + ldd [%o2 + (2*8)], %l4 + ldd [%o2 + (3*8)], %l6 + ldd [%o2 + (4*8)], %i0 + ldd [%o2 + (5*8)], %i2 + ldd [%o2 + (6*8)], %i4 + ldd [%o2 + (7*8)], %i6 + ld [%o3 - 12], %i7 ! return address + mov %o2, %sp + call PopActiveVMFrame + mov %sp, %o0 + clr %i0 ret + restore +.size JaegerInterpoline, . - JaegerInterpoline + +! void JaegerStubVeneer() +.global JaegerStubVeneer +.type JaegerStubVeneer, #function +JaegerStubVeneer: + call %i0 + nop + ld [%fp - 8], %g2 + jmp %g2 nop -.size InjectJaegerReturn, . - InjectJaegerReturn +.size JaegerStubVeneer, . - JaegerStubVeneer diff --git a/deps/mozjs/js/src/methodjit/TypedArrayIC.h b/deps/mozjs/js/src/methodjit/TypedArrayIC.h index a31e6bcc5ea..132967cb8c0 100644 --- a/deps/mozjs/js/src/methodjit/TypedArrayIC.h +++ b/deps/mozjs/js/src/methodjit/TypedArrayIC.h @@ -42,11 +42,15 @@ #include "jscntxt.h" #include "jstypedarray.h" +#include "jstypedarrayinlines.h" + +#include "jsnuminlines.h" +#include "jstypedarrayinlines.h" namespace js { namespace mjit { -#ifdef JS_POLYIC_TYPED_ARRAY +#ifdef JS_METHODJIT_TYPED_ARRAY typedef JSC::MacroAssembler::RegisterID RegisterID; typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; @@ -54,62 +58,6 @@ typedef JSC::MacroAssembler::Jump Jump; typedef JSC::MacroAssembler::Imm32 Imm32; typedef JSC::MacroAssembler::ImmDouble ImmDouble; -template -static void -LoadFromTypedArray(Assembler &masm, js::TypedArray *tarray, T address, - RegisterID typeReg, RegisterID dataReg) -{ - switch (tarray->type) { - case js::TypedArray::TYPE_INT8: - masm.load8SignExtend(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - break; - case js::TypedArray::TYPE_UINT8: - case js::TypedArray::TYPE_UINT8_CLAMPED: - masm.load8ZeroExtend(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - break; - case js::TypedArray::TYPE_INT16: - masm.load16SignExtend(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - break; - case js::TypedArray::TYPE_UINT16: - masm.load16(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - break; - case js::TypedArray::TYPE_INT32: - masm.load32(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - break; - case js::TypedArray::TYPE_UINT32: - { - masm.load32(address, dataReg); - masm.move(ImmType(JSVAL_TYPE_INT32), typeReg); - Jump safeInt = masm.branch32(Assembler::Below, dataReg, Imm32(0x80000000)); - masm.convertUInt32ToDouble(dataReg, FPRegisters::First); - masm.breakDouble(FPRegisters::First, typeReg, dataReg); - safeInt.linkTo(masm.label(), &masm); - break; - } - case js::TypedArray::TYPE_FLOAT32: - case js::TypedArray::TYPE_FLOAT64: - { - if (tarray->type == js::TypedArray::TYPE_FLOAT32) - masm.loadFloat(address, FPRegisters::First); - else - masm.loadDouble(address, FPRegisters::First); - // Make sure NaN gets canonicalized. - Jump notNaN = masm.branchDouble(Assembler::DoubleEqual, - FPRegisters::First, - FPRegisters::First); - masm.loadStaticDouble(&js_NaN, FPRegisters::First, dataReg); - notNaN.linkTo(masm.label(), &masm); - masm.breakDouble(FPRegisters::First, typeReg, dataReg); - break; - } - } -} - static inline bool ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr) { @@ -150,18 +98,8 @@ ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr) return true; } -static inline int32 -ClampIntForUint8Array(int32 x) -{ - if (x < 0) - return 0; - if (x > 255) - return 255; - return x; -} - static inline bool -ConstantFoldForIntArray(JSContext *cx, js::TypedArray *tarray, ValueRemat *vr) +ConstantFoldForIntArray(JSContext *cx, JSObject *tarray, ValueRemat *vr) { if (!vr->isTypeKnown()) return true; @@ -187,14 +125,14 @@ ConstantFoldForIntArray(JSContext *cx, js::TypedArray *tarray, ValueRemat *vr) v.setNumber(d); } - int32 i32 = 0; + int32_t i32 = 0; if (v.isDouble()) { - i32 = (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) + i32 = (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED) ? js_TypedArray_uint8_clamp_double(v.toDouble()) : js_DoubleToECMAInt32(v.toDouble()); } else if (v.isInt32()) { i32 = v.toInt32(); - if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) + if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED) i32 = ClampIntForUint8Array(i32); } else if (v.isBoolean()) { i32 = v.toBoolean() ? 1 : 0; @@ -207,47 +145,14 @@ ConstantFoldForIntArray(JSContext *cx, js::TypedArray *tarray, ValueRemat *vr) return true; } -template -static void -StoreToIntArray(Assembler &masm, js::TypedArray *tarray, S src, T address) -{ - switch (tarray->type) { - case js::TypedArray::TYPE_INT8: - case js::TypedArray::TYPE_UINT8: - case js::TypedArray::TYPE_UINT8_CLAMPED: - masm.store8(src, address); - break; - case js::TypedArray::TYPE_INT16: - case js::TypedArray::TYPE_UINT16: - masm.store16(src, address); - break; - case js::TypedArray::TYPE_INT32: - case js::TypedArray::TYPE_UINT32: - masm.store32(src, address); - break; - default: - JS_NOT_REACHED("unknown int array type"); - } -} - -template -static void -StoreToFloatArray(Assembler &masm, js::TypedArray *tarray, S src, T address) -{ - if (tarray->type == js::TypedArray::TYPE_FLOAT32) - masm.storeFloat(src, address); - else - masm.storeDouble(src, address); -} - // Generate code that will ensure a dynamically typed value, pinned in |vr|, // can be stored in an integer typed array. If any sort of conversion is // required, |dataReg| will be clobbered by a new value. |saveMask| is // used to ensure that |dataReg| (and volatile registers) are preserved // across any conversion process. static void -GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr, - uint32 saveMask) +GenConversionForIntArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr, + uint32_t saveMask) { if (vr.isConstant()) { // Constants are always folded to ints up-front. @@ -273,9 +178,9 @@ GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRem masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx))); masm.storeArgAddr(1, masm.addressOfExtra(vp)); - typedef int32 (JS_FASTCALL *Int32CxVp)(JSContext *, Value *); + typedef int32_t (JS_FASTCALL *Int32CxVp)(JSContext *, Value *); Int32CxVp stub; - if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) + if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED) stub = stubs::ConvertToTypedInt; else stub = stubs::ConvertToTypedInt; @@ -291,26 +196,8 @@ GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRem } // Performing clamping, if needed. - if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) { - // cmp dr, 0 - // jge _min - // mov dr, 0 - // jump _done - // _min: - // cmp dr, 255 - // jle _done - // mov dr, 255 - // _done: - // - Jump j = masm.branch32(Assembler::GreaterThanOrEqual, vr.dataReg(), Imm32(0)); - masm.move(Imm32(0), vr.dataReg()); - Jump done = masm.jump(); - j.linkTo(masm.label(), &masm); - j = masm.branch32(Assembler::LessThanOrEqual, vr.dataReg(), Imm32(255)); - masm.move(Imm32(255), vr.dataReg()); - j.linkTo(masm.label(), &masm); - done.linkTo(masm.label(), &masm); - } + if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED) + masm.clampInt32ToUint8(vr.dataReg()); } // Generate code that will ensure a dynamically typed value, pinned in |vr|, @@ -318,11 +205,10 @@ GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRem // |dataReg| (and volatile registers) are preserved across any conversion // process. // -// Constants are left untouched. Any other value is placed into -// FPRegisters::First. +// Constants are left untouched. Any other value is placed into destReg. static void -GenConversionForFloatArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr, - FPRegisterID destReg, uint32 saveMask) +GenConversionForFloatArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr, + FPRegisterID destReg, uint32_t saveMask) { if (vr.isConstant()) { // Constants are always folded to doubles up-front. @@ -348,7 +234,7 @@ GenConversionForFloatArray(Assembler &masm, js::TypedArray *tarray, const ValueR } } - // Generate a generic conversion call, if not known to be int32 or double. + // Generate a generic conversion call, if not known to be int32_t or double. if (!vr.isTypeKnown() || (vr.knownType() != JSVAL_TYPE_INT32 && vr.knownType() != JSVAL_TYPE_DOUBLE)) { @@ -386,18 +272,19 @@ GenConversionForFloatArray(Assembler &masm, js::TypedArray *tarray, const ValueR if (skip2.isSet()) skip2.get().linkTo(masm.label(), &masm); - if (tarray->type == js::TypedArray::TYPE_FLOAT32) + if (TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32) masm.convertDoubleToFloat(destReg, destReg); } template static bool -StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T address, - const ValueRemat &vrIn, uint32 saveMask) +StoreToTypedArray(JSContext *cx, Assembler &masm, JSObject *tarray, T address, + const ValueRemat &vrIn, uint32_t saveMask) { ValueRemat vr = vrIn; - switch (tarray->type) { + uint32_t type = TypedArray::getType(tarray); + switch (type) { case js::TypedArray::TYPE_INT8: case js::TypedArray::TYPE_UINT8: case js::TypedArray::TYPE_UINT8_CLAMPED: @@ -426,11 +313,11 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr // for the conversion call. This is because the object and key may be // in temporary registers, and we want to restore those without killing // the mutated RHS. - bool singleByte = (tarray->type == js::TypedArray::TYPE_INT8 || - tarray->type == js::TypedArray::TYPE_UINT8 || - tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED); + bool singleByte = (type == js::TypedArray::TYPE_INT8 || + type == js::TypedArray::TYPE_UINT8 || + type == js::TypedArray::TYPE_UINT8_CLAMPED); bool mayNeedConversion = (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32); - bool mayNeedClamping = !vr.isConstant() && (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED); + bool mayNeedClamping = !vr.isConstant() && (type == js::TypedArray::TYPE_UINT8_CLAMPED); bool needsSingleByteReg = singleByte && !vr.isConstant() && !(Registers::SingleByteRegs & Registers::maskReg(vr.dataReg())); @@ -442,12 +329,12 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr // - won't clobber the key, object, or RHS type regs // - is temporary, but // - is not in saveMask, which contains live volatile registers. - uint32 allowMask = Registers::AvailRegs; + uint32_t allowMask = Registers::AvailRegs; if (singleByte) allowMask &= Registers::SingleByteRegs; // Create a mask of registers we absolutely cannot clobber. - uint32 pinned = Assembler::maskAddress(address); + uint32_t pinned = Assembler::maskAddress(address); if (!vr.isTypeKnown()) pinned |= Registers::maskReg(vr.typeReg()); @@ -455,13 +342,13 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr RegisterID newReg; if (!avail.empty()) { - newReg = avail.takeAnyReg(); + newReg = avail.takeAnyReg().reg(); } else { // If no registers meet the ideal set, relax a constraint and spill. avail = allowMask & ~pinned; if (!avail.empty()) { - newReg = avail.takeAnyReg(); + newReg = avail.takeAnyReg().reg(); saveRHS.preserve(Registers::maskReg(newReg)); } else { // Oh no! *All* single byte registers are pinned. This @@ -470,12 +357,12 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr // If |vr|'s registers are part of the address, swapping is // going to cause problems during the store. - uint32 vrRegs = Registers::mask2Regs(vr.dataReg(), vr.typeReg()); - uint32 lhsMask = vrRegs & Assembler::maskAddress(address); + uint32_t vrRegs = Registers::mask2Regs(vr.dataReg(), vr.typeReg()); + uint32_t lhsMask = vrRegs & Assembler::maskAddress(address); // We'll also need to save any of the registers which won't // be restored via |lhsMask| above. - uint32 rhsMask = vrRegs & ~lhsMask; + uint32_t rhsMask = vrRegs & ~lhsMask; // Push them, but get the order right. We'll pop LHS first. saveRHS.preserve(rhsMask); @@ -512,9 +399,9 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr saveLHS.restore(); if (vr.isConstant()) - StoreToIntArray(masm, tarray, Imm32(vr.value().toInt32()), address); + masm.storeToTypedIntArray(type, Imm32(vr.value().toInt32()), address); else - StoreToIntArray(masm, tarray, vr.dataReg(), address); + masm.storeToTypedIntArray(type, vr.dataReg(), address); // Note that this will finish restoring the damage from the // earlier register swap. @@ -523,21 +410,29 @@ StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T addr } case js::TypedArray::TYPE_FLOAT32: - case js::TypedArray::TYPE_FLOAT64: + case js::TypedArray::TYPE_FLOAT64: { + /* + * Use a temporary for conversion. Inference is disabled, so no FP + * registers are live. + */ + Registers regs(Registers::TempFPRegs); + FPRegisterID temp = regs.takeAnyReg().fpreg(); + if (!ConstantFoldForFloatArray(cx, &vr)) return false; - GenConversionForFloatArray(masm, tarray, vr, FPRegisters::First, saveMask); + GenConversionForFloatArray(masm, tarray, vr, temp, saveMask); if (vr.isConstant()) - StoreToFloatArray(masm, tarray, ImmDouble(vr.value().toDouble()), address); + masm.storeToTypedFloatArray(type, ImmDouble(vr.value().toDouble()), address); else - StoreToFloatArray(masm, tarray, FPRegisters::First, address); + masm.storeToTypedFloatArray(type, temp, address); break; + } } return true; } -#endif // defined(JS_POLYIC) && (defined JS_CPU_X86 || defined JS_CPU_X64) +#endif /* JS_METHODJIT_TYPED_ARRAY */ } /* namespace mjit */ } /* namespace js */ diff --git a/deps/mozjs/js/src/metrics/gc/README.txt b/deps/mozjs/js/src/metrics/gc/README.txt index 23ddbf2f403..f4f37efbaa0 100644 --- a/deps/mozjs/js/src/metrics/gc/README.txt +++ b/deps/mozjs/js/src/metrics/gc/README.txt @@ -3,6 +3,4 @@ Usage: Requirements: 1) The shell has to be compiled with --enable-gctimer -2) The JS_WANT_GC_SUITE_PRINT flag has to be set to true in jsgc.cpp - Tested with python2.6 diff --git a/deps/mozjs/js/src/metrics/gc/gc-test.py b/deps/mozjs/js/src/metrics/gc/gc-test.py index 90ee29af357..3ebe1d3f5e4 100644 --- a/deps/mozjs/js/src/metrics/gc/gc-test.py +++ b/deps/mozjs/js/src/metrics/gc/gc-test.py @@ -40,6 +40,7 @@ def stddev(seq, mean): def run_test(test): env = os.environ.copy() + env['MOZ_GCTIMER'] = 'stderr' cmd = get_test_cmd(test.path) total = [] mark = [] diff --git a/deps/mozjs/js/src/nanojit-import-filemap b/deps/mozjs/js/src/nanojit-import-filemap deleted file mode 100644 index d26ac7af497..00000000000 --- a/deps/mozjs/js/src/nanojit-import-filemap +++ /dev/null @@ -1,13 +0,0 @@ -include nanojit -include vprof -include lirasm - -exclude Makefile.in -exclude configure.in -exclude config.h.in -exclude autoconf -exclude .hgtags - -rename vprof js/src/vprof -rename nanojit js/src/nanojit -rename lirasm js/src/lirasm diff --git a/deps/mozjs/js/src/nanojit-import-rev b/deps/mozjs/js/src/nanojit-import-rev deleted file mode 100644 index 1b3fb0bc93c..00000000000 --- a/deps/mozjs/js/src/nanojit-import-rev +++ /dev/null @@ -1 +0,0 @@ -74e1b2344f722020fb10e11d7b115373c43c37b6 diff --git a/deps/mozjs/js/src/nanojit/Allocator.cpp b/deps/mozjs/js/src/nanojit/Allocator.cpp deleted file mode 100644 index b592ac8ccc1..00000000000 --- a/deps/mozjs/js/src/nanojit/Allocator.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef FEATURE_NANOJIT - -namespace nanojit -{ - Allocator::Allocator() - : current_chunk(NULL) - , current_top(NULL) - , current_limit(NULL) - { } - - Allocator::~Allocator() - { - reset(); - } - - void Allocator::reset() - { - Chunk *c = current_chunk; - while (c) { - Chunk *prev = c->prev; - freeChunk(c); - c = prev; - } - current_chunk = NULL; - current_top = NULL; - current_limit = NULL; - postReset(); - } - - void* Allocator::allocSlow(size_t nbytes, bool fallible) - { - NanoAssert((nbytes & 7) == 0); - if (fill(nbytes, fallible)) { - NanoAssert(current_top + nbytes <= current_limit); - void* p = current_top; - current_top += nbytes; - return p; - } - return NULL; - } - - bool Allocator::fill(size_t nbytes, bool fallible) - { - if (nbytes < MIN_CHUNK_SZB) - nbytes = MIN_CHUNK_SZB; - size_t chunkbytes = sizeof(Chunk) + nbytes - sizeof(int64_t); - void* mem = allocChunk(chunkbytes, fallible); - if (mem) { - Chunk* chunk = (Chunk*) mem; - chunk->prev = current_chunk; - chunk->size = chunkbytes; - current_chunk = chunk; - current_top = (char*)chunk->data; - current_limit = (char*)mem + chunkbytes; - return true; - } else { - NanoAssert(fallible); - return false; - } - } - - size_t Allocator::getBytesAllocated() - { - size_t n = 0; - Chunk *c = current_chunk; - while (c) { - n += c->size; - c = c->prev; - } - return n; - } -} - -#endif // FEATURE_NANOJIT diff --git a/deps/mozjs/js/src/nanojit/Allocator.h b/deps/mozjs/js/src/nanojit/Allocator.h deleted file mode 100644 index 2cf15edc1f1..00000000000 --- a/deps/mozjs/js/src/nanojit/Allocator.h +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_Allocator__ -#define __nanojit_Allocator__ - -namespace nanojit -{ - /** - * Allocator is a bump-pointer allocator with an SPI for getting more - * memory from embedder-implemented allocator, such as malloc()/free(). - * - * alloc() never returns NULL. The implementation of allocChunk() - * is expected to perform a longjmp or exception when an allocation can't - * proceed. fallibleAlloc() (and fallibleAllocChunk()) may return NULL. - * They should be used for large allocations whose failure can be handled - * without too much difficulty. - */ - class Allocator { - public: - Allocator(); - ~Allocator(); - - // Usable space in the minimum chunk size; there are also a few bytes - // used for administration. - static const size_t MIN_CHUNK_SZB = 2000; - - void reset(); - - /** alloc memory, never return null. */ - void* alloc(size_t nbytes) { - void* p; - nbytes = (nbytes + 7) & ~7; // round up - if (current_top + nbytes <= current_limit) { - p = current_top; - current_top += nbytes; - } else { - p = allocSlow(nbytes, /* fallible = */false); - NanoAssert(p); - } - return p; - } - - /** alloc memory, maybe return null. */ - void* fallibleAlloc(size_t nbytes) { - void* p; - nbytes = (nbytes + 7) & ~7; // round up - if (current_top + nbytes <= current_limit) { - p = current_top; - current_top += nbytes; - } else { - p = allocSlow(nbytes, /* fallible = */true); - } - return p; - } - - size_t getBytesAllocated(); - - protected: - void* allocSlow(size_t nbytes, bool fallible = false); - bool fill(size_t minbytes, bool fallible); - - class Chunk { - public: - Chunk* prev; - size_t size; - int64_t data[1]; // int64_t forces 8-byte alignment. - }; - - Chunk* current_chunk; - char* current_top; - char* current_limit; - - // allocator SPI - - /** allocate another block from a host provided allocator */ - void* allocChunk(size_t nbytes, bool fallible); - - /** free back to the same allocator */ - void freeChunk(void*); - - /** hook for post-reset action. */ - void postReset(); - }; -} - -/** global new overload enabling this pattern: new (allocator) T(...) */ -inline void* operator new(size_t size, nanojit::Allocator &a) { - return a.alloc(size); -} - -/** global new overload enabling this pattern: new (allocator) T(...) */ -inline void* operator new(size_t size, nanojit::Allocator *a) { - return a->alloc(size); -} - -/** global new[] overload enabling this pattern: new (allocator) T[] */ -inline void* operator new[](size_t size, nanojit::Allocator& a) { - return a.alloc(size); -} - -/** global new[] overload enabling this pattern: new (allocator) T[] */ -inline void* operator new[](size_t size, nanojit::Allocator* a) { - return a->alloc(size); -} - -#endif // __nanojit_Allocator__ diff --git a/deps/mozjs/js/src/nanojit/Assembler.cpp b/deps/mozjs/js/src/nanojit/Assembler.cpp deleted file mode 100755 index fe37dfe5225..00000000000 --- a/deps/mozjs/js/src/nanojit/Assembler.cpp +++ /dev/null @@ -1,2596 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef FEATURE_NANOJIT - -#ifdef VMCFG_VTUNE -#include "../core/CodegenLIR.h" -#endif - -#ifdef _MSC_VER - // disable some specific warnings which are normally useful, but pervasive in the code-gen macros - #pragma warning(disable:4310) // cast truncates constant value -#endif - -#ifdef VMCFG_VTUNE -namespace vtune { - using namespace nanojit; - void vtuneStart(void*, NIns*); - void vtuneEnd(void*, NIns*); - void vtuneLine(void*, int, NIns*); - void vtuneFile(void*, void*); -} -using namespace vtune; -#endif // VMCFG_VTUNE - - -namespace nanojit -{ - /** - * Need the following: - * - * - merging paths ( build a graph? ), possibly use external rep to drive codegen - */ - Assembler::Assembler(CodeAlloc& codeAlloc, Allocator& dataAlloc, Allocator& alloc, LogControl* logc, const Config& config) - : alloc(alloc) - , _codeAlloc(codeAlloc) - , _dataAlloc(dataAlloc) - , _thisfrag(NULL) - , _branchStateMap(alloc) - , _patches(alloc) - , _labels(alloc) - , _noise(NULL) - #if NJ_USES_IMMD_POOL - , _immDPool(alloc) - #endif - , codeList(NULL) - , _epilogue(NULL) - , _err(None) - #if PEDANTIC - , pedanticTop(NULL) - #endif - #ifdef VMCFG_VTUNE - , vtuneHandle(NULL) - #endif - , _config(config) - { - // Per-opcode register hint table. Defaults to no hints for all - // instructions (it's zeroed in the constructor). Must be zeroed - // before calling nInit(). - for (int i = 0; i < LIR_sentinel+1; i++) - nHints[i] = 0; - nInit(); - (void)logc; - verbose_only( _logc = logc; ) - verbose_only( _outputCache = 0; ) - verbose_only( outline[0] = '\0'; ) - verbose_only( outlineEOL[0] = '\0'; ) - - reset(); - } - -#ifdef _DEBUG - - /*static*/ LIns* const AR::BAD_ENTRY = (LIns*)0xdeadbeef; - - void AR::validateQuick() - { - NanoAssert(_highWaterMark < NJ_MAX_STACK_ENTRY); - NanoAssert(_entries[0] == NULL); - // Only check a few entries around _highWaterMark. - uint32_t const RADIUS = 4; - uint32_t const lo = (_highWaterMark > 1 + RADIUS ? _highWaterMark - RADIUS : 1); - uint32_t const hi = (_highWaterMark + 1 + RADIUS < NJ_MAX_STACK_ENTRY ? _highWaterMark + 1 + RADIUS : NJ_MAX_STACK_ENTRY); - for (uint32_t i = lo; i <= _highWaterMark; ++i) - NanoAssert(_entries[i] != BAD_ENTRY); - for (uint32_t i = _highWaterMark+1; i < hi; ++i) - NanoAssert(_entries[i] == BAD_ENTRY); - } - - void AR::validateFull() - { - NanoAssert(_highWaterMark < NJ_MAX_STACK_ENTRY); - NanoAssert(_entries[0] == NULL); - for (uint32_t i = 1; i <= _highWaterMark; ++i) - NanoAssert(_entries[i] != BAD_ENTRY); - for (uint32_t i = _highWaterMark+1; i < NJ_MAX_STACK_ENTRY; ++i) - NanoAssert(_entries[i] == BAD_ENTRY); - } - - void AR::validate() - { - static uint32_t validateCounter = 0; - if (++validateCounter >= 100) - { - validateFull(); - validateCounter = 0; - } - else - { - validateQuick(); - } - } - -#endif - - inline void AR::clear() - { - _highWaterMark = 0; - NanoAssert(_entries[0] == NULL); - #ifdef _DEBUG - for (uint32_t i = 1; i < NJ_MAX_STACK_ENTRY; ++i) - _entries[i] = BAD_ENTRY; - #endif - } - - bool AR::Iter::next(LIns*& ins, uint32_t& nStackSlots, int32_t& arIndex) - { - while (_i <= _ar._highWaterMark) { - ins = _ar._entries[_i]; - if (ins) { - arIndex = _i; - nStackSlots = nStackSlotsFor(ins); - _i += nStackSlots; - return true; - } - _i++; - } - ins = NULL; - nStackSlots = 0; - arIndex = 0; - return false; - } - - void Assembler::arReset() - { - _activation.clear(); - _branchStateMap.clear(); - _patches.clear(); - _labels.clear(); - #if NJ_USES_IMMD_POOL - _immDPool.clear(); - #endif - } - - void Assembler::registerResetAll() - { - nRegisterResetAll(_allocator); - _allocator.managed = _allocator.free; - - // At start, should have some registers free and none active. - NanoAssert(0 != _allocator.free); - NanoAssert(0 == _allocator.activeMask()); -#ifdef NANOJIT_IA32 - debug_only(_fpuStkDepth = 0; ) -#endif - } - - // Legend for register sets: A = allowed, P = preferred, F = free, S = SavedReg. - // - // Finds a register in 'setA___' to store the result of 'ins' (one from - // 'set_P__' if possible), evicting one if necessary. Doesn't consider - // the prior state of 'ins'. - // - // Nb: 'setA___' comes from the instruction's use, 'set_P__' comes from its def. - // Eg. in 'add(call(...), ...)': - // - the call's use means setA___==GpRegs; - // - the call's def means set_P__==rmask(retRegs[0]). - // - Register Assembler::registerAlloc(LIns* ins, RegisterMask setA___, RegisterMask set_P__) - { - Register r; - RegisterMask set__F_ = _allocator.free; - RegisterMask setA_F_ = setA___ & set__F_; - - if (setA_F_) { - RegisterMask set___S = SavedRegs; - RegisterMask setA_FS = setA_F_ & set___S; - RegisterMask setAPF_ = setA_F_ & set_P__; - RegisterMask setAPFS = setA_FS & set_P__; - RegisterMask set; - - if (setAPFS) set = setAPFS; - else if (setAPF_) set = setAPF_; - else if (setA_FS) set = setA_FS; - else set = setA_F_; - - r = nRegisterAllocFromSet(set); - _allocator.addActive(r, ins); - ins->setReg(r); - } else { - // Nothing free, steal one. - // LSRA says pick the one with the furthest use. - LIns* vic = findVictim(setA___); - NanoAssert(vic->isInReg()); - r = vic->getReg(); - - evict(vic); - - // r ends up staying active, but the LIns defining it changes. - _allocator.removeFree(r); - _allocator.addActive(r, ins); - ins->setReg(r); - } - - return r; - } - - // Finds a register in 'allow' to store a temporary value (one not - // associated with a particular LIns), evicting one if necessary. The - // returned register is marked as being free and so can only be safely - // used for code generation purposes until the regstate is next inspected - // or updated. - Register Assembler::registerAllocTmp(RegisterMask allow) - { - LIns dummyIns; - Register r = registerAlloc(&dummyIns, allow, /*prefer*/0); - - // Mark r as free, ready for use as a temporary value. - _allocator.removeActive(r); - _allocator.addFree(r); - return r; - } - - void Assembler::codeAlloc(NIns *&start, NIns *&end, NIns *&eip - verbose_only(, size_t &nBytes) - , size_t byteLimit) - { - // save the block we just filled - if (start) - CodeAlloc::add(codeList, start, end); - - // CodeAlloc contract: allocations never fail - _codeAlloc.alloc(start, end, byteLimit); - verbose_only( nBytes += (end - start) * sizeof(NIns); ) - NanoAssert(uintptr_t(end) - uintptr_t(start) >= (size_t)LARGEST_UNDERRUN_PROT); - eip = end; - verbose_only( _nInsAfter = eip; ) - } - - void Assembler::clearNInsPtrs() - { - _nIns = 0; - _nExitIns = 0; - codeStart = codeEnd = 0; - exitStart = exitEnd = 0; - codeList = 0; - } - - void Assembler::reset() - { - clearNInsPtrs(); - nativePageReset(); - registerResetAll(); - arReset(); - } - - #ifdef _DEBUG - void Assembler::pageValidate() - { - if (error()) return; - // This may be a normal code chunk or an exit code chunk. - NanoAssertMsg(codeStart <= _nIns && _nIns <= codeEnd, - "Native instruction pointer overstep paging bounds; check overrideProtect for last instruction"); - } - #endif - - #ifdef _DEBUG - - bool AR::isValidEntry(uint32_t idx, LIns* ins) const - { - return idx > 0 && idx <= _highWaterMark && _entries[idx] == ins; - } - - void AR::checkForResourceConsistency(const RegAlloc& regs) - { - validate(); - for (uint32_t i = 1; i <= _highWaterMark; ++i) - { - LIns* ins = _entries[i]; - if (!ins) - continue; - uint32_t arIndex = ins->getArIndex(); - NanoAssert(arIndex != 0); - if (ins->isop(LIR_allocp)) { - int const n = i + (ins->size()>>2); - for (int j=i+1; j < n; j++) { - NanoAssert(_entries[j]==ins); - } - NanoAssert(arIndex == (uint32_t)n-1); - i = n-1; - } - else if (ins->isQorD()) { - NanoAssert(_entries[i + 1]==ins); - i += 1; // skip high word - } - else { - NanoAssertMsg(arIndex == i, "Stack record index mismatch"); - } - NanoAssertMsg(!ins->isInReg() || regs.isConsistent(ins->getReg(), ins), - "Register record mismatch"); - } - } - - void Assembler::resourceConsistencyCheck() - { - NanoAssert(!error()); -#ifdef NANOJIT_IA32 - // Within the expansion of a single LIR instruction, we may use the x87 - // stack for unmanaged temporaries. Otherwise, we do not use the x87 stack - // as such, but use the top element alone as a single allocatable FP register. - // Compensation code must be inserted to keep the stack balanced and avoid - // overflow, and the mechanisms for this are rather fragile and IA32-specific. - // The predicate below should hold between any pair of instructions within - // a basic block, at labels, and just after a conditional branch. Currently, - // we enforce this condition between all pairs of instructions, but this is - // overly restrictive, and would fail if we did not generate unreachable x87 - // stack pops following unconditional branches. - NanoAssert((_allocator.active[REGNUM(FST0)] && _fpuStkDepth == -1) || - (!_allocator.active[REGNUM(FST0)] && _fpuStkDepth == 0)); -#endif - _activation.checkForResourceConsistency(_allocator); - registerConsistencyCheck(); - } - - void Assembler::registerConsistencyCheck() - { - RegisterMask managed = _allocator.managed; - for (Register r = lsReg(managed); managed; r = nextLsReg(managed, r)) { - // A register managed by register allocation must be either - // free or active, but not both. - if (_allocator.isFree(r)) { - NanoAssertMsgf(_allocator.getActive(r)==0, - "register %s is free but assigned to ins", gpn(r)); - } else { - // An LIns defining a register must have that register in - // its reservation. - LIns* ins = _allocator.getActive(r); - NanoAssert(ins); - NanoAssertMsg(r == ins->getReg(), "Register record mismatch"); - } - } - - RegisterMask not_managed = ~_allocator.managed; - for (Register r = lsReg(not_managed); not_managed; r = nextLsReg(not_managed, r)) { - // A register not managed by register allocation must be - // neither free nor active. - if (REGNUM(r) <= LastRegNum) { - NanoAssert(!_allocator.isFree(r)); - NanoAssert(!_allocator.getActive(r)); - } - } - } - #endif /* _DEBUG */ - - void Assembler::findRegFor2(RegisterMask allowa, LIns* ia, Register& ra, - RegisterMask allowb, LIns* ib, Register& rb) - { - // There should be some overlap between 'allowa' and 'allowb', else - // there's no point calling this function. - NanoAssert(allowa & allowb); - - if (ia == ib) { - ra = rb = findRegFor(ia, allowa & allowb); // use intersection(allowa, allowb) - - } else if (ib->isInRegMask(allowb)) { - // 'ib' is already in an allowable reg -- don't let it get evicted - // when finding 'ra'. - rb = ib->getReg(); - ra = findRegFor(ia, allowa & ~rmask(rb)); - - } else { - ra = findRegFor(ia, allowa); - rb = findRegFor(ib, allowb & ~rmask(ra)); - } - } - - Register Assembler::findSpecificRegFor(LIns* i, Register w) - { - return findRegFor(i, rmask(w)); - } - - // Like findRegFor(), but called when the LIns is used as a pointer. It - // doesn't have to be called, findRegFor() can still be used, but it can - // optimize the LIR_allocp case by indexing off FP, thus saving the use of - // a GpReg. - // - Register Assembler::getBaseReg(LIns* base, int &d, RegisterMask allow) - { - #if !PEDANTIC - if (base->isop(LIR_allocp)) { - // The value of a LIR_allocp is a pointer to its stack memory, - // which is always relative to FP. So we can just return FP if we - // also adjust 'd' (and can do so in a valid manner). Or, in the - // PEDANTIC case, we can just assign a register as normal; - // findRegFor() will allocate the stack memory for LIR_allocp if - // necessary. - d += findMemFor(base); - return FP; - } - #else - (void) d; - #endif - return findRegFor(base, allow); - } - - // Like findRegFor2(), but used for stores where the base value has the - // same type as the stored value, eg. in asm_store32() on 32-bit platforms - // and asm_store64() on 64-bit platforms. Similar to getBaseReg(), - // findRegFor2() can be called instead, but this function can optimize the - // case where the base value is a LIR_allocp. - void Assembler::getBaseReg2(RegisterMask allowValue, LIns* value, Register& rv, - RegisterMask allowBase, LIns* base, Register& rb, int &d) - { - #if !PEDANTIC - if (base->isop(LIR_allocp)) { - rb = FP; - d += findMemFor(base); - rv = findRegFor(value, allowValue); - return; - } - #else - (void) d; - #endif - findRegFor2(allowValue, value, rv, allowBase, base, rb); - } - - RegisterMask Assembler::hint(LIns* ins) - { - RegisterMask prefer = nHints[ins->opcode()]; - return (prefer == PREFER_SPECIAL) ? nHint(ins) : prefer; - } - - // Finds a register in 'allow' to hold the result of 'ins'. Used when we - // encounter a use of 'ins'. The actions depend on the prior regstate of - // 'ins': - // - If the result of 'ins' is not in any register, we find an allowed - // one, evicting one if necessary. - // - If the result of 'ins' is already in an allowed register, we use that. - // - If the result of 'ins' is already in a not-allowed register, we find an - // allowed one and move it. - // - Register Assembler::findRegFor(LIns* ins, RegisterMask allow) - { - if (ins->isop(LIR_allocp)) { - // Never allocate a reg for this without stack space too. - findMemFor(ins); - } - - Register r; - - if (!ins->isInReg()) { - // 'ins' isn't in a register (must be in a spill slot or nowhere). - r = registerAlloc(ins, allow, hint(ins)); - - } else if (rmask(r = ins->getReg()) & allow) { - // 'ins' is in an allowed register. - _allocator.useActive(r); - - } else { - // 'ins' is in a register (r) that's not in 'allow'. -#ifdef NANOJIT_IA32 - if (((rmask(r)&XmmRegs) && !(allow&XmmRegs)) || - ((rmask(r)&x87Regs) && !(allow&x87Regs))) - { - // x87 <-> xmm copy required - //_nvprof("fpu-evict",1); - evict(ins); - r = registerAlloc(ins, allow, hint(ins)); - } else -#elif defined(NANOJIT_PPC) || defined(NANOJIT_MIPS) || defined(NANOJIT_SPARC) - if (((rmask(r)&GpRegs) && !(allow&GpRegs)) || - ((rmask(r)&FpRegs) && !(allow&FpRegs))) - { - evict(ins); - r = registerAlloc(ins, allow, hint(ins)); - } else -#endif - { - // The post-state register holding 'ins' is 's', the pre-state - // register holding 'ins' is 'r'. For example, if s=eax and - // r=ecx: - // - // pre-state: ecx(ins) - // instruction: mov eax, ecx - // post-state: eax(ins) - // - Register s = r; - _allocator.retire(r); - r = registerAlloc(ins, allow, hint(ins)); - - // 'ins' is in 'allow', in register r (different to the old r); - // s is the old r. - if ((rmask(s) & GpRegs) && (rmask(r) & GpRegs)) { - MR(s, r); // move 'ins' from its pre-state reg (r) to its post-state reg (s) - } else { - asm_nongp_copy(s, r); - } - } - } - - return r; - } - - // Like findSpecificRegFor(), but only for when 'r' is known to be free - // and 'ins' is known to not already have a register allocated. Updates - // the regstate (maintaining the invariants) but does not generate any - // code. The return value is redundant, always being 'r', but it's - // sometimes useful to have it there for assignments. - Register Assembler::findSpecificRegForUnallocated(LIns* ins, Register r) - { - if (ins->isop(LIR_allocp)) { - // never allocate a reg for this w/out stack space too - findMemFor(ins); - } - - NanoAssert(!ins->isInReg()); - NanoAssert(_allocator.free & rmask(r)); - - ins->setReg(r); - _allocator.removeFree(r); - _allocator.addActive(r, ins); - - return r; - } - -#if NJ_USES_IMMD_POOL - const uint64_t* Assembler::findImmDFromPool(uint64_t q) - { - uint64_t* p = _immDPool.get(q); - if (!p) - { - p = new (_dataAlloc) uint64_t; - *p = q; - _immDPool.put(q, p); - } - return p; - } -#endif - - int Assembler::findMemFor(LIns *ins) - { -#if NJ_USES_IMMD_POOL - NanoAssert(!ins->isImmD()); -#endif - if (!ins->isInAr()) { - uint32_t const arIndex = arReserve(ins); - ins->setArIndex(arIndex); - NanoAssert(_activation.isValidEntry(ins->getArIndex(), ins) == (arIndex != 0)); - } - return arDisp(ins); - } - - // XXX: this function is dangerous and should be phased out; - // See bug 513615. Calls to it should replaced it with a - // prepareResultReg() / generate code / freeResourcesOf() sequence. - Register Assembler::deprecated_prepResultReg(LIns *ins, RegisterMask allow) - { -#ifdef NANOJIT_IA32 - // We used to have to worry about possibly popping the x87 stack here. - // But this function is no longer used on i386, and this assertion - // ensures that. - NanoAssert(0); -#endif - Register r = findRegFor(ins, allow); - deprecated_freeRsrcOf(ins); - return r; - } - - // Finds a register in 'allow' to hold the result of 'ins'. Also - // generates code to spill the result if necessary. Called just prior to - // generating the code for 'ins' (because we generate code backwards). - // - // An example where no spill is necessary. Lines marked '*' are those - // done by this function. - // - // regstate: R - // asm: define res into r - // * regstate: R + r(res) - // ... - // asm: use res in r - // - // An example where a spill is necessary. - // - // regstate: R - // asm: define res into r - // * regstate: R + r(res) - // * asm: spill res from r - // regstate: R - // ... - // asm: restore res into r2 - // regstate: R + r2(res) + other changes from "..." - // asm: use res in r2 - // - Register Assembler::prepareResultReg(LIns *ins, RegisterMask allow) - { - // At this point, we know the result of 'ins' is used later in the - // code, unless it is a call to an impure function that must be - // included for effect even though its result is ignored. It may have - // had to be evicted, in which case the restore will have already been - // generated, so we now generate the spill. QUERY: Is there any attempt - // to elide the spill if we know that all restores can be rematerialized? -#ifdef NANOJIT_IA32 - const bool notInFST0 = (!ins->isInReg() || ins->getReg() != FST0); - Register r = findRegFor(ins, allow); - // If the result register is FST0, but FST0 is not in the post-regstate, - // then we must pop the x87 stack. This may occur because the result is - // unused, or because it has been stored to a spill slot or an XMM register. - const bool needPop = notInFST0 && (r == FST0); - const bool didSpill = asm_maybe_spill(ins, needPop); - if (!didSpill && needPop) { - // If the instruction is spilled, then the pop will have already - // been performed by the store to the stack slot. Otherwise, we - // must pop now. This may occur when the result of a LIR_calld - // to an impure (side-effecting) function is not used. - FSTP(FST0); - } -#else - Register r = findRegFor(ins, allow); - asm_maybe_spill(ins, false); -#endif - return r; - } - - bool Assembler::asm_maybe_spill(LIns* ins, bool pop) - { - if (ins->isInAr()) { - int d = arDisp(ins); - Register r = ins->getReg(); - verbose_only( RefBuf b; - if (_logc->lcbits & LC_Native) { - setOutputForEOL(" <= spill %s", - _thisfrag->lirbuf->printer->formatRef(&b, ins)); } ) -#ifdef NANOJIT_IA32 - asm_spill(r, d, pop); -#else - (void)pop; - asm_spill(r, d, ins->isQorD()); -#endif - return true; - } - return false; - } - - // XXX: This function is error-prone and should be phased out; see bug 513615. - void Assembler::deprecated_freeRsrcOf(LIns *ins) - { - if (ins->isInReg()) { - asm_maybe_spill(ins, /*pop*/false); - _allocator.retire(ins->getReg()); // free any register associated with entry - ins->clearReg(); - } - if (ins->isInAr()) { - arFree(ins); // free any AR space associated with entry - ins->clearArIndex(); - } - } - - // Frees all record of registers and spill slots used by 'ins'. - void Assembler::freeResourcesOf(LIns *ins) - { - if (ins->isInReg()) { - _allocator.retire(ins->getReg()); // free any register associated with entry - ins->clearReg(); - } - if (ins->isInAr()) { - arFree(ins); // free any AR space associated with entry - ins->clearArIndex(); - } - } - - // Frees 'r' in the RegAlloc regstate, if it's not already free. - void Assembler::evictIfActive(Register r) - { - if (LIns* vic = _allocator.getActive(r)) { - NanoAssert(vic->getReg() == r); - evict(vic); - } - } - - // Frees 'r' (which currently holds the result of 'vic') in the regstate. - // An example: - // - // pre-regstate: eax(ld1) - // instruction: mov ebx,-4(ebp) <= restore add1 # %ebx is dest - // post-regstate: eax(ld1) ebx(add1) - // - // At run-time we are *restoring* 'add1' into %ebx, hence the call to - // asm_restore(). But at regalloc-time we are moving backwards through - // the code, so in that sense we are *evicting* 'add1' from %ebx. - // - void Assembler::evict(LIns* vic) - { - // Not free, need to steal. - Register r = vic->getReg(); - - NanoAssert(!_allocator.isFree(r)); - NanoAssert(vic == _allocator.getActive(r)); - - verbose_only( RefBuf b; - if (_logc->lcbits & LC_Native) { - setOutputForEOL(" <= restore %s", - _thisfrag->lirbuf->printer->formatRef(&b, vic)); } ) - asm_restore(vic, r); - - _allocator.retire(r); - vic->clearReg(); - - // At this point 'vic' is unused (if rematerializable), or in a spill - // slot (if not). - } - - // If we have this: - // - // W = ld(addp(B, lshp(I, k)))[d] , where int(1) <= k <= int(3) - // - // then we set base=B, index=I, scale=k. - // - // Otherwise, we must have this: - // - // W = ld(addp(B, I))[d] - // - // and we set base=B, index=I, scale=0. - // - void Assembler::getBaseIndexScale(LIns* addp, LIns** base, LIns** index, int* scale) - { - NanoAssert(addp->isop(LIR_addp)); - - *base = addp->oprnd1(); - LIns* rhs = addp->oprnd2(); - int k; - - if (rhs->opcode() == LIR_lshp && rhs->oprnd2()->isImmI() && - (k = rhs->oprnd2()->immI(), (1 <= k && k <= 3))) - { - *index = rhs->oprnd1(); - *scale = k; - } else { - *index = rhs; - *scale = 0; - } - } - void Assembler::patch(GuardRecord *lr) - { - if (!lr->jmp) // the guard might have been eliminated as redundant - return; - Fragment *frag = lr->exit->target; - NanoAssert(frag->fragEntry != 0); - nPatchBranch((NIns*)lr->jmp, frag->fragEntry); - CodeAlloc::flushICache(lr->jmp, LARGEST_BRANCH_PATCH); - verbose_only(verbose_outputf("patching jump at %p to target %p\n", - lr->jmp, frag->fragEntry);) - } - - void Assembler::patch(SideExit *exit) - { - GuardRecord *rec = exit->guards; - NanoAssert(rec); - while (rec) { - patch(rec); - rec = rec->next; - } - } - - NIns* Assembler::asm_exit(LIns* guard) - { - SideExit *exit = guard->record()->exit; - NIns* at = 0; - if (!_branchStateMap.get(exit)) - { - at = asm_leave_trace(guard); - } - else - { - RegAlloc* captured = _branchStateMap.get(exit); - intersectRegisterState(*captured); - at = exit->target->fragEntry; - NanoAssert(at != 0); - _branchStateMap.remove(exit); - } - return at; - } - - NIns* Assembler::asm_leave_trace(LIns* guard) - { - verbose_only( verbose_outputf("----------------------------------- ## END exit block %p", guard);) - - // This point is unreachable. So free all the registers. If an - // instruction has a stack entry we will leave it alone, otherwise we - // free it entirely. intersectRegisterState() will restore. - RegAlloc capture = _allocator; - releaseRegisters(); - - swapCodeChunks(); - _inExit = true; - verbose_only( _nInsAfter = _nIns; ) - -#ifdef NANOJIT_IA32 - debug_only( _sv_fpuStkDepth = _fpuStkDepth; _fpuStkDepth = 0; ) -#endif - - nFragExit(guard); - - // Restore the callee-saved register and parameters. - assignSavedRegs(); - assignParamRegs(); - - intersectRegisterState(capture); - - // this can be useful for breaking whenever an exit is taken - //INT3(); - //NOP(); - - // we are done producing the exit logic for the guard so demark where our exit block code begins - NIns* jmpTarget = _nIns; // target in exit path for our mainline conditional jump - - // swap back pointers, effectively storing the last location used in the exit path - swapCodeChunks(); - _inExit = false; - verbose_only( _nInsAfter = _nIns; ) - - //verbose_only( verbose_outputf(" LIR_xt/xf swapCodeChunks, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) ); - verbose_only( verbose_outputf("%p:", jmpTarget);) - verbose_only( verbose_outputf("----------------------------------- ## BEGIN exit block (LIR_xt|LIR_xf)") ); - -#ifdef NANOJIT_IA32 - NanoAssertMsgf(_fpuStkDepth == _sv_fpuStkDepth, "LIR_xtf, _fpuStkDepth=%d, expect %d",_fpuStkDepth, _sv_fpuStkDepth); - debug_only( _fpuStkDepth = _sv_fpuStkDepth; _sv_fpuStkDepth = 9999; ) -#endif - - return jmpTarget; - } - - void Assembler::compile(Fragment* frag, Allocator& alloc, bool optimize verbose_only(, LInsPrinter* printer)) - { - verbose_only( - bool anyVerb = (_logc->lcbits & 0xFFFF & ~LC_FragProfile) > 0; - bool liveVerb = (_logc->lcbits & 0xFFFF & LC_Liveness) > 0; - ) - - /* BEGIN decorative preamble */ - verbose_only( - if (anyVerb) { - _logc->printf("========================================" - "========================================\n"); - _logc->printf("=== BEGIN LIR::compile(%p, %p)\n", - (void*)this, (void*)frag); - _logc->printf("===\n"); - }) - /* END decorative preamble */ - - verbose_only( if (liveVerb) { - _logc->printf("\n"); - _logc->printf("=== Results of liveness analysis:\n"); - _logc->printf("===\n"); - LirReader br(frag->lastIns); - LirFilter* lir = &br; - if (optimize) { - StackFilter* sf = new (alloc) StackFilter(lir, alloc, frag->lirbuf->sp); - lir = sf; - } - live(lir, alloc, frag, _logc); - }) - - /* Set up the generic text output cache for the assembler */ - verbose_only( StringList asmOutput(alloc); ) - verbose_only( _outputCache = &asmOutput; ) - - beginAssembly(frag); - if (error()) - return; - - //_logc->printf("recompile trigger %X kind %d\n", (int)frag, frag->kind); - - verbose_only( if (anyVerb) { - _logc->printf("=== Translating LIR fragments into assembly:\n"); - }) - - // now the the main trunk - verbose_only( RefBuf b; ) - verbose_only( if (anyVerb) { - _logc->printf("=== -- Compile trunk %s: begin\n", printer->formatAddr(&b, frag)); - }) - - // Used for debug printing, if needed - debug_only(ValidateReader *validate = NULL;) - verbose_only( - ReverseLister *pp_init = NULL; - ReverseLister *pp_after_sf = NULL; - ) - - // The LIR passes through these filters as listed in this - // function, viz, top to bottom. - - // set up backwards pipeline: assembler <- StackFilter <- LirReader - LirFilter* lir = new (alloc) LirReader(frag->lastIns); - -#ifdef DEBUG - // VALIDATION - validate = new (alloc) ValidateReader(lir); - lir = validate; -#endif - - // INITIAL PRINTING - verbose_only( if (_logc->lcbits & LC_ReadLIR) { - pp_init = new (alloc) ReverseLister(lir, alloc, frag->lirbuf->printer, _logc, - "Initial LIR"); - lir = pp_init; - }) - - // STACKFILTER - if (optimize) { - StackFilter* stackfilter = new (alloc) StackFilter(lir, alloc, frag->lirbuf->sp); - lir = stackfilter; - } - - verbose_only( if (_logc->lcbits & LC_AfterSF) { - pp_after_sf = new (alloc) ReverseLister(lir, alloc, frag->lirbuf->printer, _logc, - "After StackFilter"); - lir = pp_after_sf; - }) - - assemble(frag, lir); - - // If we were accumulating debug info in the various ReverseListers, - // call finish() to emit whatever contents they have accumulated. - verbose_only( - if (pp_init) pp_init->finish(); - if (pp_after_sf) pp_after_sf->finish(); - ) - - verbose_only( if (anyVerb) { - _logc->printf("=== -- Compile trunk %s: end\n", printer->formatAddr(&b, frag)); - }) - - endAssembly(frag); - - // Reverse output so that assembly is displayed low-to-high. - // Up to this point, _outputCache has been non-NULL, and so has been - // accumulating output. Now we set it to NULL, traverse the entire - // list of stored strings, and hand them a second time to output. - // Since _outputCache is now NULL, outputf just hands these strings - // directly onwards to _logc->printf. - verbose_only( if (anyVerb) { - _logc->printf("\n"); - _logc->printf("=== Aggregated assembly output: BEGIN\n"); - _logc->printf("===\n"); - _outputCache = 0; - for (Seq* p = asmOutput.get(); p != NULL; p = p->tail) { - char *str = p->head; - outputf(" %s", str); - } - _logc->printf("===\n"); - _logc->printf("=== Aggregated assembly output: END\n"); - }); - - if (error()) - frag->fragEntry = 0; - - verbose_only( frag->nCodeBytes += codeBytes; ) - verbose_only( frag->nExitBytes += exitBytes; ) - - /* BEGIN decorative postamble */ - verbose_only( if (anyVerb) { - _logc->printf("\n"); - _logc->printf("===\n"); - _logc->printf("=== END LIR::compile(%p, %p)\n", - (void*)this, (void*)frag); - _logc->printf("========================================" - "========================================\n"); - _logc->printf("\n"); - }); - /* END decorative postamble */ - } - - void Assembler::beginAssembly(Fragment *frag) - { - verbose_only( codeBytes = 0; ) - verbose_only( exitBytes = 0; ) - - reset(); - - NanoAssert(codeList == 0); - NanoAssert(codeStart == 0); - NanoAssert(codeEnd == 0); - NanoAssert(exitStart == 0); - NanoAssert(exitEnd == 0); - NanoAssert(_nIns == 0); - NanoAssert(_nExitIns == 0); - - _thisfrag = frag; - _inExit = false; - - setError(None); - - // native code gen buffer setup - nativePageSetup(); - - // make sure we got memory at least one page - if (error()) return; - - _epilogue = NULL; - verbose_only( _nInsAfter = _nIns; ) - - nBeginAssembly(); - } - - void Assembler::assemble(Fragment* frag, LirFilter* reader) - { - if (error()) return; - _thisfrag = frag; - - // check the fragment is starting out with a sane profiling state - verbose_only( NanoAssert(frag->nStaticExits == 0); ) - verbose_only( NanoAssert(frag->nCodeBytes == 0); ) - verbose_only( NanoAssert(frag->nExitBytes == 0); ) - verbose_only( NanoAssert(frag->profCount == 0); ) - verbose_only( if (_logc->lcbits & LC_FragProfile) - NanoAssert(frag->profFragID > 0); - else - NanoAssert(frag->profFragID == 0); ) - - _inExit = false; - - gen(reader); - - if (!error()) { - // patch all branches - NInsMap::Iter iter(_patches); - while (iter.next()) { - NIns* where = iter.key(); - LIns* target = iter.value(); - if (target->isop(LIR_jtbl)) { - // Need to patch up a whole jump table, 'where' is the table. - LIns *jtbl = target; - NIns** native_table = (NIns**) (void *) where; - for (uint32_t i = 0, n = jtbl->getTableSize(); i < n; i++) { - LabelState* lstate = _labels.get(jtbl->getTarget(i)); - NIns* ntarget = lstate->addr; - if (ntarget) { - native_table[i] = ntarget; - } else { - setError(UnknownBranch); - break; - } - } - } else { - // target is a label for a single-target branch - LabelState *lstate = _labels.get(target); - NIns* ntarget = lstate->addr; - if (ntarget) { - nPatchBranch(where, ntarget); - } else { - setError(UnknownBranch); - break; - } - } - } - } - } - - void Assembler::cleanupAfterError() - { - _codeAlloc.freeAll(codeList); - if (_nExitIns) - _codeAlloc.free(exitStart, exitEnd); - _codeAlloc.free(codeStart, codeEnd); - codeList = NULL; - _codeAlloc.markAllExec(); // expensive but safe, we mark all code pages R-X - } - - void Assembler::endAssembly(Fragment* frag) - { - // don't try to patch code if we are in an error state since we might have partially - // overwritten the code cache already - if (error()) { - // something went wrong, release all allocated code memory - cleanupAfterError(); - return; - } - - NIns* fragEntry = genPrologue(); - verbose_only( asm_output("[prologue]"); ) - - debug_only(_activation.checkForResourceLeaks()); - - NanoAssert(!_inExit); - // save used parts of current block on fragment's code list, free the rest -#if defined(NANOJIT_ARM) || defined(NANOJIT_MIPS) - // [codeStart, _nSlot) ... gap ... [_nIns, codeEnd) - if (_nExitIns) { - _codeAlloc.addRemainder(codeList, exitStart, exitEnd, _nExitSlot, _nExitIns); - verbose_only( exitBytes -= (_nExitIns - _nExitSlot) * sizeof(NIns); ) - } - _codeAlloc.addRemainder(codeList, codeStart, codeEnd, _nSlot, _nIns); - verbose_only( codeBytes -= (_nIns - _nSlot) * sizeof(NIns); ) -#else - // [codeStart ... gap ... [_nIns, codeEnd)) - if (_nExitIns) { - _codeAlloc.addRemainder(codeList, exitStart, exitEnd, exitStart, _nExitIns); - verbose_only( exitBytes -= (_nExitIns - exitStart) * sizeof(NIns); ) - } - _codeAlloc.addRemainder(codeList, codeStart, codeEnd, codeStart, _nIns); - verbose_only( codeBytes -= (_nIns - codeStart) * sizeof(NIns); ) -#endif - - // note: the code pages are no longer writable from this point onwards - _codeAlloc.markExec(codeList); - - // at this point all our new code is in the d-cache and not the i-cache, - // so flush the i-cache on cpu's that need it. - CodeAlloc::flushICache(codeList); - - // save entry point pointers - frag->fragEntry = fragEntry; - frag->setCode(_nIns); - -#ifdef VMCFG_VTUNE - if (vtuneHandle) - { - vtuneEnd(vtuneHandle, codeEnd); - vtuneStart(vtuneHandle, _nIns); - } -#endif - - PERFM_NVPROF("code", CodeAlloc::size(codeList)); - -#ifdef NANOJIT_IA32 - NanoAssertMsgf(_fpuStkDepth == 0,"_fpuStkDepth %d\n",_fpuStkDepth); -#endif - - debug_only( pageValidate(); ) - NanoAssert(_branchStateMap.isEmpty()); - } - - void Assembler::releaseRegisters() - { - RegisterMask active = _allocator.activeMask(); - for (Register r = lsReg(active); active; r = nextLsReg(active, r)) - { - LIns *ins = _allocator.getActive(r); - // Clear reg allocation, preserve stack allocation. - _allocator.retire(r); - NanoAssert(r == ins->getReg()); - ins->clearReg(); - } - } - -#ifdef PERFM -#define countlir_live() _nvprof("lir-live",1) -#define countlir_ret() _nvprof("lir-ret",1) -#define countlir_alloc() _nvprof("lir-alloc",1) -#define countlir_var() _nvprof("lir-var",1) -#define countlir_use() _nvprof("lir-use",1) -#define countlir_def() _nvprof("lir-def",1) -#define countlir_imm() _nvprof("lir-imm",1) -#define countlir_param() _nvprof("lir-param",1) -#define countlir_cmov() _nvprof("lir-cmov",1) -#define countlir_ld() _nvprof("lir-ld",1) -#define countlir_ldq() _nvprof("lir-ldq",1) -#define countlir_alu() _nvprof("lir-alu",1) -#define countlir_qjoin() _nvprof("lir-qjoin",1) -#define countlir_qlo() _nvprof("lir-qlo",1) -#define countlir_qhi() _nvprof("lir-qhi",1) -#define countlir_fpu() _nvprof("lir-fpu",1) -#define countlir_st() _nvprof("lir-st",1) -#define countlir_stq() _nvprof("lir-stq",1) -#define countlir_jmp() _nvprof("lir-jmp",1) -#define countlir_jcc() _nvprof("lir-jcc",1) -#define countlir_label() _nvprof("lir-label",1) -#define countlir_xcc() _nvprof("lir-xcc",1) -#define countlir_x() _nvprof("lir-x",1) -#define countlir_call() _nvprof("lir-call",1) -#define countlir_jtbl() _nvprof("lir-jtbl",1) -#else -#define countlir_live() -#define countlir_ret() -#define countlir_alloc() -#define countlir_var() -#define countlir_use() -#define countlir_def() -#define countlir_imm() -#define countlir_param() -#define countlir_cmov() -#define countlir_ld() -#define countlir_ldq() -#define countlir_alu() -#define countlir_qjoin() -#define countlir_qlo() -#define countlir_qhi() -#define countlir_fpu() -#define countlir_st() -#define countlir_stq() -#define countlir_jmp() -#define countlir_jcc() -#define countlir_label() -#define countlir_xcc() -#define countlir_x() -#define countlir_call() -#define countlir_jtbl() -#endif - - void Assembler::asm_jmp(LIns* ins, InsList& pending_lives) - { - NanoAssert((ins->isop(LIR_j) && !ins->oprnd1()) || - (ins->isop(LIR_jf) && ins->oprnd1()->isImmI(0)) || - (ins->isop(LIR_jt) && ins->oprnd1()->isImmI(1))); - - countlir_jmp(); - LIns* to = ins->getTarget(); - LabelState *label = _labels.get(to); - // The jump is always taken so whatever register state we - // have from downstream code, is irrelevant to code before - // this jump. So clear it out. We will pick up register - // state from the jump target, if we have seen that label. - releaseRegisters(); -#ifdef NANOJIT_IA32 - // Unreachable, so assume correct stack depth. - debug_only( _fpuStkDepth = 0; ) -#endif - if (label && label->addr) { - // Forward jump - pick up register state from target. - unionRegisterState(label->regs); -#ifdef NANOJIT_IA32 - // Set stack depth according to the register state we just loaded, - // negating the effect of any unreachable x87 stack pop that might - // have been emitted by unionRegisterState(). - debug_only( _fpuStkDepth = (_allocator.getActive(FST0) ? -1 : 0); ) -#endif - JMP(label->addr); - } - else { - // Backwards jump. - handleLoopCarriedExprs(pending_lives); - if (!label) { - // save empty register state at loop header - _labels.add(to, 0, _allocator); - } - else { - intersectRegisterState(label->regs); -#ifdef NANOJIT_IA32 - debug_only( _fpuStkDepth = (_allocator.getActive(FST0) ? -1 : 0); ) -#endif - } - JMP(0); - _patches.put(_nIns, to); - } - } - - void Assembler::asm_jcc(LIns* ins, InsList& pending_lives) - { - bool branchOnFalse = (ins->opcode() == LIR_jf); - LIns* cond = ins->oprnd1(); - if (cond->isImmI()) { - if ((!branchOnFalse && !cond->immI()) || (branchOnFalse && cond->immI())) { - // jmp never taken, not needed - } else { - asm_jmp(ins, pending_lives); // jmp always taken - } - return; - } - - // Changes to the logic below will likely need to be propagated to Assembler::asm_jov(). - - countlir_jcc(); - LIns* to = ins->getTarget(); - LabelState *label = _labels.get(to); - if (label && label->addr) { - // Forward jump to known label. Need to merge with label's register state. - unionRegisterState(label->regs); - asm_branch(branchOnFalse, cond, label->addr); - } - else { - // Back edge. - handleLoopCarriedExprs(pending_lives); - if (!label) { - // Evict all registers, most conservative approach. - evictAllActiveRegs(); - _labels.add(to, 0, _allocator); - } - else { - // Evict all registers, most conservative approach. - intersectRegisterState(label->regs); - } - Branches branches = asm_branch(branchOnFalse, cond, 0); - if (branches.branch1) { - _patches.put(branches.branch1,to); - } - if (branches.branch2) { - _patches.put(branches.branch2,to); - } - } - } - - void Assembler::asm_jov(LIns* ins, InsList& pending_lives) - { - // The caller is responsible for countlir_* profiling, unlike - // asm_jcc above. The reason for this is that asm_jov may not be - // be called if the instruction is dead, and it is our convention - // to count such instructions anyway. - LOpcode op = ins->opcode(); - LIns* to = ins->getTarget(); - LabelState *label = _labels.get(to); - if (label && label->addr) { - // forward jump to known label. need to merge with label's register state. - unionRegisterState(label->regs); - asm_branch_ov(op, label->addr); - } - else { - // back edge. - handleLoopCarriedExprs(pending_lives); - if (!label) { - // evict all registers, most conservative approach. - evictAllActiveRegs(); - _labels.add(to, 0, _allocator); - } - else { - // evict all registers, most conservative approach. - intersectRegisterState(label->regs); - } - NIns *branch = asm_branch_ov(op, 0); - _patches.put(branch,to); - } - } - - void Assembler::asm_x(LIns* ins) - { - verbose_only( _thisfrag->nStaticExits++; ) - countlir_x(); - // Generate the side exit branch on the main trace. - NIns *exit = asm_exit(ins); - JMP(exit); - } - - void Assembler::asm_xcc(LIns* ins) - { - LIns* cond = ins->oprnd1(); - if (cond->isImmI()) { - if ((ins->isop(LIR_xt) && !cond->immI()) || (ins->isop(LIR_xf) && cond->immI())) { - // guard never taken, not needed - } else { - asm_x(ins); // guard always taken - } - return; - } - - verbose_only( _thisfrag->nStaticExits++; ) - countlir_xcc(); - // We only support cmp with guard right now, also assume it is 'close' - // and only emit the branch. - NIns* exit = asm_exit(ins); // does intersectRegisterState() - asm_branch(ins->opcode() == LIR_xf, cond, exit); - } - - // helper function for nop insertion feature that results in no more - // than 1 no-op instruction insertion every 128-1151 Bytes - static inline uint32_t noiseForNopInsertion(Noise* n) { - return n->getValue(1023) + 128; - } - - void Assembler::gen(LirFilter* reader) - { - NanoAssert(_thisfrag->nStaticExits == 0); - - InsList pending_lives(alloc); - - NanoAssert(!error()); - - // compiler hardening setup - NIns* priorIns = _nIns; - int32_t nopInsertTrigger = hardenNopInsertion(_config) ? noiseForNopInsertion(_noise): 0; - - // What's going on here: we're visiting all the LIR instructions in - // the buffer, working strictly backwards in buffer-order, and - // generating machine instructions for them as we go. - // - // For each LIns, we first check if it's live. If so we mark its - // operands as also live, and then generate code for it *if - // necessary*. It may not be necessary if the instruction is an - // expression and code has already been generated for all its uses in - // combination with previously handled instructions (ins->isExtant() - // will return false if this is so). - - // Note that the backwards code traversal can make register allocation - // confusing. (For example, we restore a value before we spill it!) - // In particular, words like "before" and "after" must be used very - // carefully -- their meaning at regalloc-time is opposite to their - // meaning at run-time. We use the term "pre-regstate" to refer to - // the register allocation state that occurs prior to an instruction's - // execution, and "post-regstate" to refer to the state that occurs - // after an instruction's execution, e.g.: - // - // pre-regstate: ebx(ins) - // instruction: mov eax, ebx // mov dst, src - // post-regstate: eax(ins) - // - // At run-time, the instruction updates the pre-regstate into the - // post-regstate (and these states are the real machine's regstates). - // But when allocating registers, because we go backwards, the - // pre-regstate is constructed from the post-regstate (and these - // regstates are those stored in RegAlloc). - // - // One consequence of generating code backwards is that we tend to - // both spill and restore registers as early (at run-time) as - // possible; this is good for tolerating memory latency. If we - // generated code forwards, we would expect to both spill and restore - // registers as late (at run-time) as possible; this might be better - // for reducing register pressure. - - // The trace must end with one of these opcodes. Mark it as live. - NanoAssert(reader->finalIns()->isop(LIR_x) || - reader->finalIns()->isRet() || - isLiveOpcode(reader->finalIns()->opcode())); - - for (currIns = reader->read(); !currIns->isop(LIR_start); currIns = reader->read()) - { - LIns* ins = currIns; // give it a shorter name for local use - - if (!ins->isLive()) { - NanoAssert(!ins->isExtant()); - continue; - } - -#ifdef NJ_VERBOSE - // Output the post-regstate (registers and/or activation). - // Because asm output comes in reverse order, doing it now means - // it is printed after the LIR and native code, exactly when the - // post-regstate should be shown. - if ((_logc->lcbits & LC_Native) && (_logc->lcbits & LC_Activation)) - printActivationState(); - if ((_logc->lcbits & LC_Native) && (_logc->lcbits & LC_RegAlloc)) - printRegState(); -#endif - - // compiler hardening technique that inserts no-op instructions in the compiled method when nopInsertTrigger < 0 - if (hardenNopInsertion(_config)) - { - size_t delta = (uintptr_t)priorIns - (uintptr_t)_nIns; // # bytes that have been emitted since last go-around - - // if no codeList then we know priorIns and _nIns are on same page, otherwise make sure priorIns was not in the previous code block - if (!codeList || !codeList->isInBlock(priorIns)) { - NanoAssert(delta < VMPI_getVMPageSize()); // sanity check - nopInsertTrigger -= (int32_t) delta; - if (nopInsertTrigger < 0) - { - nopInsertTrigger = noiseForNopInsertion(_noise); - asm_insert_random_nop(); - PERFM_NVPROF("hardening:nop-insert", 1); - } - } - priorIns = _nIns; - } - - LOpcode op = ins->opcode(); - switch (op) - { - default: - NanoAssertMsgf(false, "unsupported LIR instruction: %d\n", op); - break; - - case LIR_regfence: - evictAllActiveRegs(); - break; - - case LIR_livei: - CASE64(LIR_liveq:) - case LIR_lived: { - countlir_live(); - LIns* op1 = ins->oprnd1(); - op1->setResultLive(); - // LIR_allocp's are meant to live until the point of the - // LIR_livep instruction, marking other expressions as - // live ensures that they remain so at loop bottoms. - // LIR_allocp areas require special treatment because they - // are accessed indirectly and the indirect accesses are - // invisible to the assembler, other than via LIR_livep. - // Other expression results are only accessed directly in - // ways that are visible to the assembler, so extending - // those expression's lifetimes past the last loop edge - // isn't necessary. - if (op1->isop(LIR_allocp)) { - findMemFor(op1); - } else { - pending_lives.add(ins); - } - break; - } - - case LIR_reti: - CASE64(LIR_retq:) - case LIR_retd: - countlir_ret(); - ins->oprnd1()->setResultLive(); - asm_ret(ins); - break; - - // Allocate some stack space. The value of this instruction - // is the address of the stack space. - case LIR_allocp: - countlir_alloc(); - if (ins->isExtant()) { - NanoAssert(ins->isInAr()); - if (ins->isInReg()) - evict(ins); - freeResourcesOf(ins); - } - break; - - case LIR_immi: - countlir_imm(); - if (ins->isExtant()) { - asm_immi(ins); - } - break; - -#ifdef NANOJIT_64BIT - case LIR_immq: - countlir_imm(); - if (ins->isExtant()) { - asm_immq(ins); - } - break; -#endif - case LIR_immd: - countlir_imm(); - if (ins->isExtant()) { - asm_immd(ins); - } - break; - - case LIR_paramp: - countlir_param(); - if (ins->isExtant()) { - asm_param(ins); - } - break; - -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_hcalli: { - LIns* op1 = ins->oprnd1(); - op1->setResultLive(); - if (ins->isExtant()) { - // Return result of quad-call in register. - deprecated_prepResultReg(ins, rmask(retRegs[1])); - // If hi half was used, we must use the call to ensure it happens. - findSpecificRegFor(op1, retRegs[0]); - } - break; - } - - case LIR_dlo2i: - countlir_qlo(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_qlo(ins); - } - break; - - case LIR_dhi2i: - countlir_qhi(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_qhi(ins); - } - break; - - case LIR_ii2d: - countlir_qjoin(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_qjoin(ins); - } - break; -#endif - case LIR_cmovi: - CASE64(LIR_cmovq:) - case LIR_cmovd: - countlir_cmov(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - ins->oprnd3()->setResultLive(); - if (ins->isExtant()) { - asm_cmov(ins); - } - break; - - case LIR_lduc2ui: - case LIR_ldus2ui: - case LIR_ldc2i: - case LIR_lds2i: - case LIR_ldi: - countlir_ld(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_load32(ins); - } - break; - - CASE64(LIR_ldq:) - case LIR_ldd: - case LIR_ldf2d: - countlir_ldq(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_load64(ins); - } - break; - - case LIR_negi: - case LIR_noti: - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_neg_not(ins); - } - break; - -#if defined NANOJIT_64BIT - case LIR_addq: - case LIR_subq: - case LIR_andq: - case LIR_lshq: - case LIR_rshuq: - case LIR_rshq: - case LIR_orq: - case LIR_xorq: - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_qbinop(ins); - } - break; -#endif - - case LIR_addi: - case LIR_subi: - case LIR_muli: - case LIR_andi: - case LIR_ori: - case LIR_xori: - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - CASE86(LIR_divi:) - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_arith(ins); - } - break; - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - CASE86(LIR_modi:) - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_arith(ins); - } - break; -#endif - - case LIR_negd: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_fneg(ins); - } - break; - - case LIR_addd: - case LIR_subd: - case LIR_muld: - case LIR_divd: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_fop(ins); - } - break; - - case LIR_i2d: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_i2d(ins); - } - break; - - case LIR_ui2d: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_ui2d(ins); - } - break; - - case LIR_d2i: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_d2i(ins); - } - break; - -#ifdef NANOJIT_64BIT - case LIR_i2q: - case LIR_ui2uq: - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_ui2uq(ins); - } - break; - - case LIR_q2i: - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_q2i(ins); - } - break; - - case LIR_dasq: - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_dasq(ins); - } - break; - - case LIR_qasd: - countlir_alu(); - ins->oprnd1()->setResultLive(); - if (ins->isExtant()) { - asm_qasd(ins); - } - break; -#endif - case LIR_sti2c: - case LIR_sti2s: - case LIR_sti: - countlir_st(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - asm_store32(op, ins->oprnd1(), ins->disp(), ins->oprnd2()); - break; - - CASE64(LIR_stq:) - case LIR_std: - case LIR_std2f: { - countlir_stq(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - LIns* value = ins->oprnd1(); - LIns* base = ins->oprnd2(); - int dr = ins->disp(); -#if NJ_SOFTFLOAT_SUPPORTED - if (value->isop(LIR_ii2d) && op == LIR_std) - { - // This is correct for little-endian only. - asm_store32(LIR_sti, value->oprnd1(), dr, base); - asm_store32(LIR_sti, value->oprnd2(), dr+4, base); - } - else -#endif - { - asm_store64(op, value, dr, base); - } - break; - } - - case LIR_j: - asm_jmp(ins, pending_lives); - break; - - case LIR_jt: - case LIR_jf: - ins->oprnd1()->setResultLive(); - asm_jcc(ins, pending_lives); - break; - - #if NJ_JTBL_SUPPORTED - case LIR_jtbl: { - countlir_jtbl(); - ins->oprnd1()->setResultLive(); - // Multiway jump can contain both forward and backward jumps. - // Out of range indices aren't allowed or checked. - // Code after this jtbl instruction is unreachable. - releaseRegisters(); - NanoAssert(_allocator.activeMask() == 0); - - uint32_t count = ins->getTableSize(); - bool has_back_edges = false; - - // Merge the regstates of labels we have already seen. - for (uint32_t i = count; i-- > 0;) { - LIns* to = ins->getTarget(i); - LabelState *lstate = _labels.get(to); - if (lstate) { - unionRegisterState(lstate->regs); - verbose_only( RefBuf b; ) - asm_output(" %u: [&%s]", i, _thisfrag->lirbuf->printer->formatRef(&b, to)); - } else { - has_back_edges = true; - } - } - asm_output("forward edges"); - - // In a multi-way jump, the register allocator has no ability to deal - // with two existing edges that have conflicting register assignments, unlike - // a conditional branch where code can be inserted on the fall-through path - // to reconcile registers. So, frontends *must* insert LIR_regfence at labels of - // forward jtbl jumps. Check here to make sure no registers were picked up from - // any forward edges. - NanoAssert(_allocator.activeMask() == 0); - - if (has_back_edges) { - handleLoopCarriedExprs(pending_lives); - // save merged (empty) register state at target labels we haven't seen yet - for (uint32_t i = count; i-- > 0;) { - LIns* to = ins->getTarget(i); - LabelState *lstate = _labels.get(to); - if (!lstate) { - _labels.add(to, 0, _allocator); - verbose_only( RefBuf b; ) - asm_output(" %u: [&%s]", i, _thisfrag->lirbuf->printer->formatRef(&b, to)); - } - } - asm_output("backward edges"); - } - - // Emit the jump instruction, which allocates 1 register for the jump index. - NIns** native_table = new (_dataAlloc) NIns*[count]; - asm_output("[%p]:", (void*)native_table); - _patches.put((NIns*)native_table, ins); - asm_jtbl(ins, native_table); - break; - } - #endif - - case LIR_label: { - countlir_label(); - LabelState *label = _labels.get(ins); - // add profiling inc, if necessary. - verbose_only( if (_logc->lcbits & LC_FragProfile) { - if (ins == _thisfrag->loopLabel) - asm_inc_m32(& _thisfrag->profCount); - }) - if (!label) { - // label seen first, normal target of forward jump, save addr & allocator - _labels.add(ins, _nIns, _allocator); - } - else { - // we're at the top of a loop - NanoAssert(label->addr == 0); - //evictAllActiveRegs(); - intersectRegisterState(label->regs); - label->addr = _nIns; - } - verbose_only( - RefBuf b; - if (_logc->lcbits & LC_Native) { - asm_output("[%s]", _thisfrag->lirbuf->printer->formatRef(&b, ins)); - }) - break; - } - - case LIR_xbarrier: - break; - - case LIR_xt: - case LIR_xf: - ins->oprnd1()->setResultLive(); - asm_xcc(ins); - break; - - case LIR_x: - asm_x(ins); - break; - - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - verbose_only( _thisfrag->nStaticExits++; ) - countlir_xcc(); - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - NIns* exit = asm_exit(ins); // does intersectRegisterState() - asm_branch_ov(op, exit); - asm_arith(ins); - } - break; - - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - countlir_jcc(); - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_jov(ins, pending_lives); - asm_arith(ins); - } - break; - -#ifdef NANOJIT_64BIT - case LIR_addjovq: - case LIR_subjovq: - countlir_jcc(); - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_jov(ins, pending_lives); - asm_qbinop(ins); - } - break; -#endif - - case LIR_eqd: - case LIR_led: - case LIR_ltd: - case LIR_gtd: - case LIR_ged: - countlir_fpu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_condd(ins); - } - break; - - case LIR_eqi: - case LIR_lei: - case LIR_lti: - case LIR_gti: - case LIR_gei: - case LIR_ltui: - case LIR_leui: - case LIR_gtui: - case LIR_geui: - CASE64(LIR_eqq:) - CASE64(LIR_leq:) - CASE64(LIR_ltq:) - CASE64(LIR_gtq:) - CASE64(LIR_geq:) - CASE64(LIR_ltuq:) - CASE64(LIR_leuq:) - CASE64(LIR_gtuq:) - CASE64(LIR_geuq:) - countlir_alu(); - ins->oprnd1()->setResultLive(); - ins->oprnd2()->setResultLive(); - if (ins->isExtant()) { - asm_cond(ins); - } - break; - - case LIR_callv: - case LIR_calli: - CASE64(LIR_callq:) - case LIR_calld: - countlir_call(); - for (int i = 0, argc = ins->argc(); i < argc; i++) - ins->arg(i)->setResultLive(); - - // You might think that a call cannot be pure, live, - // and-not-extant, because there's no way the codegen - // for a call can be folded into the codegen of another - // LIR instruction. However, it's possible that a pure - // call, C, has a result that is only be used (directly - // or indirectly) in a section of code that is unreachable, - // e.g. due to an always-taken branch. C is dead, but the - // assembly pass doesn't realize is dead. So C may end - // up non-extant, in which case we don't generate code - // for it. See bug 620406 for an example. - if (!ins->callInfo()->_isPure || ins->isExtant()) { - asm_call(ins); - } - break; - - #ifdef VMCFG_VTUNE - case LIR_file: { - // we traverse backwards so we are now hitting the file - // that is associated with a bunch of LIR_lines we already have seen - if (vtuneHandle) { - void * currentFile = (void *) ins->oprnd1()->immI(); - vtuneFile(vtuneHandle, currentFile); - } - break; - } - case LIR_line: { - // add a new table entry, we don't yet knwo which file it belongs - // to so we need to add it to the update table too - // note the alloc, actual act is delayed; see above - if (vtuneHandle) { - uint32_t currentLine = (uint32_t) ins->oprnd1()->immI(); - vtuneLine(vtuneHandle, currentLine, _nIns); - } - break; - } - #endif // VMCFG_VTUNE - - case LIR_comment: - // Do nothing. - break; - } - -#ifdef NJ_VERBOSE - // We do final LIR printing inside this loop to avoid printing - // dead LIR instructions. We print the LIns after generating the - // code. This ensures that the LIns will appear in debug output - // *before* the native code, because Assembler::outputf() - // prints everything in reverse. - // - if (_logc->lcbits & LC_AfterDCE) { - InsBuf b; - LInsPrinter* printer = _thisfrag->lirbuf->printer; - if (ins->isop(LIR_comment)) - outputf("%s", printer->formatIns(&b, ins)); - else - outputf(" %s", printer->formatIns(&b, ins)); - } -#endif - - if (error()) - return; - - // check that all is well (don't check in exit paths since its more complicated) - debug_only( pageValidate(); ) - debug_only( resourceConsistencyCheck(); ) - } - } - - void Assembler::assignSavedRegs() - { - // Restore saved regsters. - LirBuffer *b = _thisfrag->lirbuf; - for (int i=0, n = NumSavedRegs; i < n; i++) { - LIns *p = b->savedRegs[i]; - if (p) - findSpecificRegForUnallocated(p, savedRegs[p->paramArg()]); - } - } - - void Assembler::reserveSavedRegs() - { - LirBuffer *b = _thisfrag->lirbuf; - for (int i = 0, n = NumSavedRegs; i < n; i++) { - LIns *ins = b->savedRegs[i]; - if (ins) - findMemFor(ins); - } - } - - void Assembler::assignParamRegs() - { - LIns* state = _thisfrag->lirbuf->state; - if (state) - findSpecificRegForUnallocated(state, argRegs[state->paramArg()]); - LIns* param1 = _thisfrag->lirbuf->param1; - if (param1) - findSpecificRegForUnallocated(param1, argRegs[param1->paramArg()]); - } - - void Assembler::handleLoopCarriedExprs(InsList& pending_lives) - { - // ensure that exprs spanning the loop are marked live at the end of the loop - reserveSavedRegs(); - for (Seq *p = pending_lives.get(); p != NULL; p = p->tail) { - LIns *ins = p->head; - NanoAssert(isLiveOpcode(ins->opcode())); - LIns *op1 = ins->oprnd1(); - // Must findMemFor even if we're going to findRegFor; loop-carried - // operands may spill on another edge, and we need them to always - // spill to the same place. -#if NJ_USES_IMMD_POOL - // Exception: if float constants are true constants, we should - // never call findMemFor on those ops. - if (!op1->isImmD()) -#endif - { - findMemFor(op1); - } - if (!op1->isImmAny()) - findRegFor(op1, ins->isop(LIR_lived) ? FpRegs : GpRegs); - } - - // clear this list since we have now dealt with those lifetimes. extending - // their lifetimes again later (earlier in the code) serves no purpose. - pending_lives.clear(); - } - - void AR::freeEntryAt(uint32_t idx) - { - NanoAssert(idx > 0 && idx <= _highWaterMark); - - // NB: this loop relies on using entry[0] being NULL, - // so that we are guaranteed to terminate - // without access negative entries. - LIns* i = _entries[idx]; - NanoAssert(i != NULL); - do { - _entries[idx] = NULL; - idx--; - } while (_entries[idx] == i); - } - -#ifdef NJ_VERBOSE - void Assembler::printRegState() - { - char* s = &outline[0]; - VMPI_memset(s, ' ', 26); s[26] = '\0'; - s += VMPI_strlen(s); - VMPI_sprintf(s, "RR"); - s += VMPI_strlen(s); - - RegisterMask active = _allocator.activeMask(); - for (Register r = lsReg(active); active != 0; r = nextLsReg(active, r)) { - LIns *ins = _allocator.getActive(r); - NanoAssertMsg(!_allocator.isFree(r), - "Coding error; register is both free and active! " ); - RefBuf b; - const char* n = _thisfrag->lirbuf->printer->formatRef(&b, ins); - - if (ins->isop(LIR_paramp) && ins->paramKind()==1 && - r == Assembler::savedRegs[ins->paramArg()]) - { - // dont print callee-saved regs that arent used - continue; - } - - VMPI_sprintf(s, " %s(%s)", gpn(r), n); - s += VMPI_strlen(s); - } - output(); - } - - void Assembler::printActivationState() - { - char* s = &outline[0]; - VMPI_memset(s, ' ', 26); s[26] = '\0'; - s += VMPI_strlen(s); - VMPI_sprintf(s, "AR"); - s += VMPI_strlen(s); - - LIns* ins = 0; - uint32_t nStackSlots = 0; - int32_t arIndex = 0; - for (AR::Iter iter(_activation); iter.next(ins, nStackSlots, arIndex); ) - { - RefBuf b; - const char* n = _thisfrag->lirbuf->printer->formatRef(&b, ins); - if (nStackSlots > 1) { - VMPI_sprintf(s," %d-%d(%s)", 4*arIndex, 4*(arIndex+nStackSlots-1), n); - } - else { - VMPI_sprintf(s," %d(%s)", 4*arIndex, n); - } - s += VMPI_strlen(s); - } - output(); - } -#endif - - inline bool AR::isEmptyRange(uint32_t start, uint32_t nStackSlots) const - { - for (uint32_t i=0; i < nStackSlots; i++) - { - if (_entries[start-i] != NULL) - return false; - } - return true; - } - - uint32_t AR::reserveEntry(LIns* ins) - { - uint32_t const nStackSlots = nStackSlotsFor(ins); - - if (nStackSlots == 1) - { - for (uint32_t i = 1; i <= _highWaterMark; i++) - { - if (_entries[i] == NULL) - { - _entries[i] = ins; - return i; - } - } - if (_highWaterMark < NJ_MAX_STACK_ENTRY - 1) - { - NanoAssert(_entries[_highWaterMark+1] == BAD_ENTRY); - _highWaterMark++; - _entries[_highWaterMark] = ins; - return _highWaterMark; - } - } - else - { - // alloc larger block on 8byte boundary. - uint32_t const start = nStackSlots + (nStackSlots & 1); - for (uint32_t i = start; i <= _highWaterMark; i += 2) - { - if (isEmptyRange(i, nStackSlots)) - { - // place the entry in the table and mark the instruction with it - for (uint32_t j=0; j < nStackSlots; j++) - { - NanoAssert(i-j <= _highWaterMark); - NanoAssert(_entries[i-j] == NULL); - _entries[i-j] = ins; - } - return i; - } - } - - // Be sure to account for any 8-byte-round-up when calculating spaceNeeded. - uint32_t const spaceLeft = NJ_MAX_STACK_ENTRY - _highWaterMark - 1; - uint32_t const spaceNeeded = nStackSlots + (_highWaterMark & 1); - if (spaceLeft >= spaceNeeded) - { - if (_highWaterMark & 1) - { - NanoAssert(_entries[_highWaterMark+1] == BAD_ENTRY); - _entries[_highWaterMark+1] = NULL; - } - _highWaterMark += spaceNeeded; - for (uint32_t j = 0; j < nStackSlots; j++) - { - NanoAssert(_highWaterMark-j < NJ_MAX_STACK_ENTRY); - NanoAssert(_entries[_highWaterMark-j] == BAD_ENTRY); - _entries[_highWaterMark-j] = ins; - } - return _highWaterMark; - } - } - // no space. oh well. - return 0; - } - - #ifdef _DEBUG - void AR::checkForResourceLeaks() const - { - for (uint32_t i = 1; i <= _highWaterMark; i++) { - NanoAssertMsgf(_entries[i] == NULL, "frame entry %d wasn't freed\n",4*i); - } - } - #endif - - uint32_t Assembler::arReserve(LIns* ins) - { - uint32_t i = _activation.reserveEntry(ins); - if (!i) - setError(StackFull); - return i; - } - - void Assembler::arFree(LIns* ins) - { - NanoAssert(ins->isInAr()); - uint32_t arIndex = ins->getArIndex(); - NanoAssert(arIndex); - NanoAssert(_activation.isValidEntry(arIndex, ins)); - _activation.freeEntryAt(arIndex); // free any stack stack space associated with entry - } - - /** - * Move regs around so the SavedRegs contains the highest priority regs. - */ - void Assembler::evictScratchRegsExcept(RegisterMask ignore) - { - // Find the top GpRegs that are candidates to put in SavedRegs. - - // 'tosave' is a binary heap stored in an array. The root is tosave[0], - // left child is at i+1, right child is at i+2. - - Register tosave[LastRegNum - FirstRegNum + 1]; - int len=0; - RegAlloc *regs = &_allocator; - RegisterMask evict_set = regs->activeMask() & GpRegs & ~ignore; - for (Register r = lsReg(evict_set); evict_set; r = nextLsReg(evict_set, r)) { - LIns *ins = regs->getActive(r); - if (canRemat(ins)) { - NanoAssert(ins->getReg() == r); - evict(ins); - } - else { - int32_t pri = regs->getPriority(r); - // add to heap by adding to end and bubbling up - int j = len++; - while (j > 0 && pri > regs->getPriority(tosave[j/2])) { - tosave[j] = tosave[j/2]; - j /= 2; - } - NanoAssert(size_t(j) < sizeof(tosave)/sizeof(tosave[0])); - tosave[j] = r; - } - } - - // Now primap has the live exprs in priority order. - // Allocate each of the top priority exprs to a SavedReg. - - RegisterMask allow = SavedRegs; - while (allow && len > 0) { - // get the highest priority var - Register hi = tosave[0]; - if (!(rmask(hi) & SavedRegs)) { - LIns *ins = regs->getActive(hi); - Register r = findRegFor(ins, allow); - allow &= ~rmask(r); - } - else { - // hi is already in a saved reg, leave it alone. - allow &= ~rmask(hi); - } - - // remove from heap by replacing root with end element and bubbling down. - if (allow && --len > 0) { - Register last = tosave[len]; - int j = 0; - while (j+1 < len) { - int child = j+1; - if (j+2 < len && regs->getPriority(tosave[j+2]) > regs->getPriority(tosave[j+1])) - child++; - if (regs->getPriority(last) > regs->getPriority(tosave[child])) - break; - tosave[j] = tosave[child]; - j = child; - } - tosave[j] = last; - } - } - - // now evict everything else. - evictSomeActiveRegs(~(SavedRegs | ignore)); - } - - // Generate code to restore any registers in 'regs' that are currently active, - void Assembler::evictSomeActiveRegs(RegisterMask regs) - { - RegisterMask evict_set = regs & _allocator.activeMask(); - for (Register r = lsReg(evict_set); evict_set; r = nextLsReg(evict_set, r)) - evict(_allocator.getActive(r)); - } - - /** - * Merge the current regstate with a previously stored version. - * - * Situation Change to _allocator - * --------- -------------------- - * !current & !saved - * !current & saved add saved - * current & !saved evict current (unionRegisterState does nothing) - * current & saved & current==saved - * current & saved & current!=saved evict current, add saved - */ - void Assembler::intersectRegisterState(RegAlloc& saved) - { - Register regsTodo[LastRegNum + 1]; - LIns* insTodo[LastRegNum + 1]; - int nTodo = 0; - - // Do evictions and pops first. - verbose_only(bool shouldMention=false; ) - // The obvious thing to do here is to iterate from FirstRegNum to - // LastRegNum. However, on ARM that causes lower-numbered integer - // registers to be be saved at higher addresses, which inhibits the - // formation of load/store multiple instructions. Hence iterate the - // loop the other way. - RegisterMask reg_set = _allocator.activeMask() | saved.activeMask(); - for (Register r = msReg(reg_set); reg_set; r = nextMsReg(reg_set, r)) - { - LIns* curins = _allocator.getActive(r); - LIns* savedins = saved.getActive(r); - if (curins != savedins) - { - if (savedins) { - regsTodo[nTodo] = r; - insTodo[nTodo] = savedins; - nTodo++; - } - if (curins) { - //_nvprof("intersect-evict",1); - verbose_only( shouldMention=true; ) - NanoAssert(curins->getReg() == r); - evict(curins); - } - - #ifdef NANOJIT_IA32 - if (savedins && r == FST0) { - verbose_only( shouldMention=true; ) - FSTP(FST0); - } - #endif - } - } - // Now reassign mainline registers. - for (int i = 0; i < nTodo; i++) { - findSpecificRegFor(insTodo[i], regsTodo[i]); - } - verbose_only( - if (shouldMention) - verbose_outputf("## merging registers (intersect) with existing edge"); - ) - } - - /** - * Merge the current state of the registers with a previously stored version. - * - * Situation Change to _allocator - * --------- -------------------- - * !current & !saved none - * !current & saved add saved - * current & !saved none (intersectRegisterState evicts current) - * current & saved & current==saved none - * current & saved & current!=saved evict current, add saved - */ - void Assembler::unionRegisterState(RegAlloc& saved) - { - Register regsTodo[LastRegNum + 1]; - LIns* insTodo[LastRegNum + 1]; - int nTodo = 0; - - // Do evictions and pops first. - verbose_only(bool shouldMention=false; ) - RegisterMask reg_set = _allocator.activeMask() | saved.activeMask(); - for (Register r = lsReg(reg_set); reg_set; r = nextLsReg(reg_set, r)) - { - LIns* curins = _allocator.getActive(r); - LIns* savedins = saved.getActive(r); - if (curins != savedins) - { - if (savedins) { - regsTodo[nTodo] = r; - insTodo[nTodo] = savedins; - nTodo++; - } - if (curins && savedins) { - //_nvprof("union-evict",1); - verbose_only( shouldMention=true; ) - NanoAssert(curins->getReg() == r); - evict(curins); - } - - #ifdef NANOJIT_IA32 - if (r == FST0) { - if (savedins) { - // Discard top of x87 stack. - FSTP(FST0); - } - else if (curins) { - // Saved state did not have fpu reg allocated, - // so we must evict here to keep x87 stack balanced. - evict(curins); - } - verbose_only( shouldMention=true; ) - } - #endif - } - } - // Now reassign mainline registers. - for (int i = 0; i < nTodo; i++) { - findSpecificRegFor(insTodo[i], regsTodo[i]); - } - verbose_only( - if (shouldMention) - verbose_outputf("## merging registers (union) with existing edge"); - ) - } - - // Scan table for instruction with the lowest priority, meaning it is used - // furthest in the future. - LIns* Assembler::findVictim(RegisterMask allow) - { - NanoAssert(allow); - LIns *ins, *vic = 0; - int allow_pri = 0x7fffffff; - RegisterMask vic_set = allow & _allocator.activeMask(); - for (Register r = lsReg(vic_set); vic_set; r = nextLsReg(vic_set, r)) - { - ins = _allocator.getActive(r); - int pri = canRemat(ins) ? 0 : _allocator.getPriority(r); - if (!vic || pri < allow_pri) { - vic = ins; - allow_pri = pri; - } - } - NanoAssert(vic != 0); - return vic; - } - -#ifdef NJ_VERBOSE - char Assembler::outline[8192]; - char Assembler::outlineEOL[512]; - - void Assembler::output() - { - // The +1 is for the terminating NUL char. - VMPI_strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1)); - - if (_outputCache) { - char* str = new (alloc) char[VMPI_strlen(outline)+1]; - VMPI_strcpy(str, outline); - _outputCache->insert(str); - } else { - _logc->printf("%s\n", outline); - } - - outline[0] = '\0'; - outlineEOL[0] = '\0'; - } - - void Assembler::outputf(const char* format, ...) - { - va_list args; - va_start(args, format); - - outline[0] = '\0'; - vsprintf(outline, format, args); - output(); - } - - void Assembler::setOutputForEOL(const char* format, ...) - { - va_list args; - va_start(args, format); - - outlineEOL[0] = '\0'; - vsprintf(outlineEOL, format, args); - } -#endif // NJ_VERBOSE - - void LabelStateMap::add(LIns *label, NIns *addr, RegAlloc ®s) { - LabelState *st = new (alloc) LabelState(addr, regs); - labels.put(label, st); - } - - LabelState* LabelStateMap::get(LIns *label) { - return labels.get(label); - } -} -#endif /* FEATURE_NANOJIT */ diff --git a/deps/mozjs/js/src/nanojit/Assembler.h b/deps/mozjs/js/src/nanojit/Assembler.h deleted file mode 100644 index 83f43035a10..00000000000 --- a/deps/mozjs/js/src/nanojit/Assembler.h +++ /dev/null @@ -1,565 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_Assembler__ -#define __nanojit_Assembler__ - - -namespace nanojit -{ - /** - * Some notes on this Assembler (Emitter). - * - * The class RegAlloc is essentially the register allocator from MIR - * - * The Assembler class parses the LIR instructions starting at any point and converts - * them to machine code. It does the translation using expression trees which are simply - * LIR instructions in the stream that have side-effects. Any other instruction in the - * stream is simply ignored. - * This approach is interesting in that dead code elimination occurs for 'free', strength - * reduction occurs fairly naturally, along with some other optimizations. - * - * A negative is that we require state as we 'push' and 'pop' nodes along the tree. - * Also, this is most easily performed using recursion which may not be desirable in - * the mobile environment. - * - */ - - #define STACK_GRANULARITY sizeof(void *) - - // Basics: - // - 'entry' records the state of the native machine stack at particular - // points during assembly. Each entry represents four bytes. - // - // - Parts of the stack can be allocated by LIR_allocp, in which case each - // slot covered by the allocation contains a pointer to the LIR_allocp - // LIns. - // - // - The stack also holds spilled values, in which case each slot holding - // a spilled value (one slot for 32-bit values, two slots for 64-bit - // values) contains a pointer to the instruction defining the spilled - // value. - // - // - Each LIns has a "reservation" which includes a stack index, - // 'arIndex'. Combined with AR, it provides a two-way mapping between - // stack slots and LIR instructions. - // - // - Invariant: the two-way mapping between active stack slots and their - // defining/allocating instructions must hold in both directions and be - // unambiguous. More specifically: - // - // * An LIns can appear in at most one contiguous sequence of slots in - // AR, and the length of that sequence depends on the opcode (1 slot - // for instructions producing 32-bit values, 2 slots for instructions - // producing 64-bit values, N slots for LIR_allocp). - // - // * An LIns named by 'entry[i]' must have an in-use reservation with - // arIndex==i (or an 'i' indexing the start of the same contiguous - // sequence that 'entry[i]' belongs to). - // - // * And vice versa: an LIns with an in-use reservation with arIndex==i - // must be named by 'entry[i]'. - // - // * If an LIns's reservation names has arIndex==0 then LIns should not - // be in 'entry[]'. - // - class AR - { - private: - uint32_t _highWaterMark; /* index of highest entry used since last clear() */ - LIns* _entries[ NJ_MAX_STACK_ENTRY ]; /* maps to 4B contiguous locations relative to the frame pointer. - NB: _entries[0] is always unused */ - - #ifdef _DEBUG - static LIns* const BAD_ENTRY; - #endif - - bool isEmptyRange(uint32_t start, uint32_t nStackSlots) const; - static uint32_t nStackSlotsFor(LIns* ins); - - public: - AR(); - - uint32_t stackSlotsNeeded() const; - - void clear(); - void freeEntryAt(uint32_t i); - uint32_t reserveEntry(LIns* ins); /* return 0 if unable to reserve the entry */ - - #ifdef _DEBUG - void validateQuick(); - void validateFull(); - void validate(); - bool isValidEntry(uint32_t idx, LIns* ins) const; /* return true iff idx and ins are matched */ - void checkForResourceConsistency(const RegAlloc& regs); - void checkForResourceLeaks() const; - #endif - - class Iter - { - private: - const AR& _ar; - // '_i' points to the start of the entries for an LIns, or to the first NULL entry. - uint32_t _i; - public: - inline Iter(const AR& ar) : _ar(ar), _i(1) { } - bool next(LIns*& ins, uint32_t& nStackSlots, int32_t& offset); // get the next one (moves iterator forward) - }; - }; - - inline AR::AR() - { - _entries[0] = NULL; - clear(); - } - - inline /*static*/ uint32_t AR::nStackSlotsFor(LIns* ins) - { - uint32_t n = 0; - if (ins->isop(LIR_allocp)) { - n = ins->size() >> 2; - } else { - switch (ins->retType()) { - case LTy_I: n = 1; break; - CASE64(LTy_Q:) - case LTy_D: n = 2; break; - case LTy_V: NanoAssert(0); break; - default: NanoAssert(0); break; - } - } - return n; - } - - inline uint32_t AR::stackSlotsNeeded() const - { - // NB: _highWaterMark is an index, not a count - return _highWaterMark+1; - } - - #ifndef AVMPLUS_ALIGN16 - #ifdef _MSC_VER - #define AVMPLUS_ALIGN16(type) __declspec(align(16)) type - #else - #define AVMPLUS_ALIGN16(type) type __attribute__ ((aligned (16))) - #endif - #endif - - class Noise - { - public: - virtual ~Noise() {} - - // produce a random number from 0-maxValue for the JIT to use in attack mitigation - virtual uint32_t getValue(uint32_t maxValue) = 0; - }; - - // error codes - enum AssmError - { - None = 0 - ,StackFull - ,UnknownBranch - ,BranchTooFar - }; - - typedef SeqBuilder NInsList; - typedef HashMap NInsMap; -#if NJ_USES_IMMD_POOL - typedef HashMap ImmDPoolMap; -#endif - -#ifdef VMCFG_VTUNE - class avmplus::CodegenLIR; -#endif - - class LabelState - { - public: - RegAlloc regs; - NIns *addr; - LabelState(NIns *a, RegAlloc &r) : regs(r), addr(a) - {} - }; - - class LabelStateMap - { - Allocator& alloc; - HashMap labels; - public: - LabelStateMap(Allocator& alloc) : alloc(alloc), labels(alloc) - {} - - void clear() { labels.clear(); } - void add(LIns *label, NIns *addr, RegAlloc ®s); - LabelState *get(LIns *); - }; - - /** - * Some architectures (i386, X64) can emit two branches that need patching - * in some situations. This is returned by asm_branch() implementations - * with 0, 1 or 2 of these fields set to a non-NULL value. (If only 1 is set, - * it must be patch1, not patch2.) - */ - struct Branches - { - NIns* const branch1; - NIns* const branch2; - inline explicit Branches(NIns* b1 = NULL, NIns* b2 = NULL) - : branch1(b1) - , branch2(b2) - { - } - }; - - /** map tracking the register allocation state at each bailout point - * (represented by SideExit*) in a trace fragment. */ - typedef HashMap RegAllocMap; - - /** - * Information about the activation record for the method is built up - * as we generate machine code. As part of the prologue, we issue - * a stack adjustment instruction and then later patch the adjustment - * value. Temporary values can be placed into the AR as method calls - * are issued. Also LIR_allocp instructions will consume space. - */ - class Assembler - { - friend class VerboseBlockReader; - #ifdef NJ_VERBOSE - public: - // Buffer for holding text as we generate it in reverse order. - StringList* _outputCache; - - // Outputs the format string and 'outlineEOL', and resets - // 'outline' and 'outlineEOL'. - void outputf(const char* format, ...); - - private: - // Log controller object. Contains what-stuff-should-we-print - // bits, and a sink function for debug printing. - LogControl* _logc; - - // Buffer used in most of the output function. It must big enough - // to hold both the output line and the 'outlineEOL' buffer, which - // is concatenated onto 'outline' just before it is printed. - static char outline[8192]; - // Buffer used to hold extra text to be printed at the end of some - // lines. - static char outlineEOL[512]; - - // Outputs 'outline' and 'outlineEOL', and resets them both. - // Output goes to '_outputCache' if it's non-NULL, or is printed - // directly via '_logc'. - void output(); - - // Sets 'outlineEOL'. - void setOutputForEOL(const char* format, ...); - - void printRegState(); - void printActivationState(); - #endif // NJ_VERBOSE - - public: - #ifdef VMCFG_VTUNE - void* vtuneHandle; - #endif - - Assembler(CodeAlloc& codeAlloc, Allocator& dataAlloc, Allocator& alloc, LogControl* logc, const Config& config); - - void compile(Fragment *frag, Allocator& alloc, bool optimize - verbose_only(, LInsPrinter*)); - - void endAssembly(Fragment* frag); - void assemble(Fragment* frag, LirFilter* reader); - void beginAssembly(Fragment *frag); - - void setNoiseGenerator(Noise* noise) { _noise = noise; } // used for attack mitigation; setting to 0 disables all mitigations - - void releaseRegisters(); - void patch(GuardRecord *lr); - void patch(SideExit *exit); - AssmError error() { return _err; } - void setError(AssmError e) { _err = e; } - void cleanupAfterError(); - void clearNInsPtrs(); - void reset(); - - debug_only ( void pageValidate(); ) - - // support calling out from a fragment ; used to debug the jit - debug_only( void resourceConsistencyCheck(); ) - debug_only( void registerConsistencyCheck(); ) - - private: - void gen(LirFilter* toCompile); - NIns* genPrologue(); - NIns* genEpilogue(); - - uint32_t arReserve(LIns* ins); - void arFree(LIns* ins); - void arReset(); - - Register registerAlloc(LIns* ins, RegisterMask allow, RegisterMask prefer); - Register registerAllocTmp(RegisterMask allow); - void registerResetAll(); - void evictAllActiveRegs() { - // The evicted set will be be intersected with activeSet(), - // so use an all-1s mask to avoid an extra load or call. - evictSomeActiveRegs(~RegisterMask(0)); - } - void evictSomeActiveRegs(RegisterMask regs); - void evictScratchRegsExcept(RegisterMask ignore); - void intersectRegisterState(RegAlloc& saved); - void unionRegisterState(RegAlloc& saved); - void assignSaved(RegAlloc &saved, RegisterMask skip); - LIns* findVictim(RegisterMask allow); - - Register getBaseReg(LIns *ins, int &d, RegisterMask allow); - void getBaseReg2(RegisterMask allowValue, LIns* value, Register& rv, - RegisterMask allowBase, LIns* base, Register& rb, int &d); -#if NJ_USES_IMMD_POOL - const uint64_t* - findImmDFromPool(uint64_t q); -#endif - int findMemFor(LIns* ins); - Register findRegFor(LIns* ins, RegisterMask allow); - void findRegFor2(RegisterMask allowa, LIns* ia, Register &ra, - RegisterMask allowb, LIns *ib, Register &rb); - Register findSpecificRegFor(LIns* ins, Register r); - Register findSpecificRegForUnallocated(LIns* ins, Register r); - Register deprecated_prepResultReg(LIns *ins, RegisterMask allow); - Register prepareResultReg(LIns *ins, RegisterMask allow); - void deprecated_freeRsrcOf(LIns *ins); - void freeResourcesOf(LIns *ins); - void evictIfActive(Register r); - void evict(LIns* vic); - RegisterMask hint(LIns* ins); - - void getBaseIndexScale(LIns* addp, LIns** base, LIns** index, int* scale); - - void codeAlloc(NIns *&start, NIns *&end, NIns *&eip - verbose_only(, size_t &nBytes) - , size_t byteLimit=0); - - // These instructions don't have to be saved & reloaded to spill, - // they can just be recalculated cheaply. - // - // WARNING: this function must match asm_restore() -- it should return - // true for the instructions that are handled explicitly without a spill - // in asm_restore(), and false otherwise. - // - // If it doesn't match asm_restore(), the register allocator's decisions - // about which values to evict will be suboptimal. - static bool canRemat(LIns*); - - bool deprecated_isKnownReg(Register r) { - return r != deprecated_UnknownReg; - } - - Allocator& alloc; // for items with same lifetime as this Assembler - CodeAlloc& _codeAlloc; // for code we generate - Allocator& _dataAlloc; // for data used by generated code - Fragment* _thisfrag; - RegAllocMap _branchStateMap; - NInsMap _patches; - LabelStateMap _labels; - Noise* _noise; // object to generate random noise used when hardening enabled. - #if NJ_USES_IMMD_POOL - ImmDPoolMap _immDPool; - #endif - - // We generate code into two places: normal code chunks, and exit - // code chunks (for exit stubs). We use a hack to avoid having to - // parameterise the code that does the generating -- we let that - // code assume that it's always generating into a normal code - // chunk (most of the time it is), and when we instead need to - // generate into an exit code chunk, we set _inExit to true and - // temporarily swap all the code/exit variables below (using - // swapCodeChunks()). Afterwards we swap them all back and set - // _inExit to false again. - CodeList* codeList; // finished blocks of code. - bool _inExit, vpad2[3]; - NIns *codeStart, *codeEnd; // current normal code chunk - NIns *exitStart, *exitEnd; // current exit code chunk - NIns* _nIns; // current instruction in current normal code chunk - NIns* _nExitIns; // current instruction in current exit code chunk - // note: _nExitIns == NULL until the first side exit is seen. - #ifdef NJ_VERBOSE - NIns* _nInsAfter; // next instruction (ascending) in current normal/exit code chunk (for verbose output) - size_t codeBytes; // bytes allocated in normal code chunks - size_t exitBytes; // bytes allocated in exit code chunks - #endif - - #define SWAP(t, a, b) do { t tmp = a; a = b; b = tmp; } while (0) - void swapCodeChunks(); - - NIns* _epilogue; - AssmError _err; // 0 = means assemble() appears ok, otherwise it failed - #if PEDANTIC - NIns* pedanticTop; - #endif - - // Holds the current instruction during gen(). - LIns* currIns; - - AR _activation; - RegAlloc _allocator; - - verbose_only( void asm_inc_m32(uint32_t*); ) - void asm_mmq(Register rd, int dd, Register rs, int ds); - void asm_jmp(LIns* ins, InsList& pending_lives); - void asm_jcc(LIns* ins, InsList& pending_lives); - void asm_jov(LIns* ins, InsList& pending_lives); - void asm_x(LIns* ins); - void asm_xcc(LIns* ins); - NIns* asm_exit(LIns* guard); - NIns* asm_leave_trace(LIns* guard); - void asm_store32(LOpcode op, LIns *val, int d, LIns *base); - void asm_store64(LOpcode op, LIns *val, int d, LIns *base); - - // WARNING: the implementation of asm_restore() should emit fast code - // to rematerialize instructions where canRemat() returns true. - // Otherwise, register allocation decisions will be suboptimal. - void asm_restore(LIns*, Register); - - bool asm_maybe_spill(LIns* ins, bool pop); -#ifdef NANOJIT_IA32 - void asm_spill(Register rr, int d, bool pop); -#else - void asm_spill(Register rr, int d, bool quad); -#endif - void asm_load64(LIns* ins); - void asm_ret(LIns* ins); -#ifdef NANOJIT_64BIT - void asm_immq(LIns* ins); -#endif - void asm_immd(LIns* ins); - void asm_condd(LIns* ins); - void asm_cond(LIns* ins); - void asm_arith(LIns* ins); - void asm_neg_not(LIns* ins); - void asm_load32(LIns* ins); - void asm_cmov(LIns* ins); - void asm_param(LIns* ins); - void asm_immi(LIns* ins); -#if NJ_SOFTFLOAT_SUPPORTED - void asm_qlo(LIns* ins); - void asm_qhi(LIns* ins); - void asm_qjoin(LIns *ins); -#endif - void asm_fneg(LIns* ins); - void asm_fop(LIns* ins); - void asm_i2d(LIns* ins); - void asm_ui2d(LIns* ins); - void asm_d2i(LIns* ins); -#ifdef NANOJIT_64BIT - void asm_q2i(LIns* ins); - void asm_ui2uq(LIns *ins); - void asm_dasq(LIns *ins); - void asm_qasd(LIns *ins); -#endif - void asm_nongp_copy(Register r, Register s); - void asm_call(LIns*); - Register asm_binop_rhs_reg(LIns* ins); - Branches asm_branch(bool branchOnFalse, LIns* cond, NIns* targ); - NIns* asm_branch_ov(LOpcode op, NIns* targ); - void asm_jtbl(LIns* ins, NIns** table); - void asm_insert_random_nop(); - void assignSavedRegs(); - void reserveSavedRegs(); - void assignParamRegs(); - void handleLoopCarriedExprs(InsList& pending_lives); - - // platform specific implementation (see NativeXXX.cpp file) - void nInit(); - void nBeginAssembly(); - Register nRegisterAllocFromSet(RegisterMask set); - void nRegisterResetAll(RegAlloc& a); - void nPatchBranch(NIns* branch, NIns* location); - void nFragExit(LIns* guard); - - RegisterMask nHints[LIR_sentinel+1]; - RegisterMask nHint(LIns* ins); - - // A special entry for hints[]; if an opcode has this value, we call - // nHint() in the back-end. Used for cases where you need to look at more - // than just the opcode to decide. - static const RegisterMask PREFER_SPECIAL = 0xffffffff; - - // platform specific methods - public: - const static Register savedRegs[NumSavedRegs+1]; // Allocate an extra element in case NumSavedRegs == 0 - DECLARE_PLATFORM_ASSEMBLER() - - private: -#ifdef NANOJIT_IA32 - debug_only( int32_t _fpuStkDepth; ) - debug_only( int32_t _sv_fpuStkDepth; ) - - // The FPU stack depth is the number of pushes in excess of the number of pops. - // Since we generate backwards, we track the FPU stack depth as a negative number. - // We use the top of the x87 stack as the single allocatable FP register, FST0. - // Thus, between LIR instructions, the depth of the FPU stack must be either 0 or -1, - // depending on whether FST0 is in use. Within the expansion of a single LIR - // instruction, however, deeper levels of the stack may be used as unmanaged - // temporaries. Hence, we allow for all eight levels in the assertions below. - inline void fpu_push() { - debug_only( ++_fpuStkDepth; NanoAssert(_fpuStkDepth <= 0); ) - } - inline void fpu_pop() { - debug_only( --_fpuStkDepth; NanoAssert(_fpuStkDepth >= -7); ) - } -#endif - const Config& _config; - }; - - inline int32_t arDisp(LIns* ins) - { - // even on 64bit cpu's, we allocate stack area in 4byte chunks - return -4 * int32_t(ins->getArIndex()); - } - // XXX: deprecated, use arDisp() instead. See bug 538924. - inline int32_t deprecated_disp(LIns* ins) - { - // even on 64bit cpu's, we allocate stack area in 4byte chunks - return -4 * int32_t(ins->deprecated_getArIndex()); - } -} -#endif // __nanojit_Assembler__ diff --git a/deps/mozjs/js/src/nanojit/CodeAlloc.cpp b/deps/mozjs/js/src/nanojit/CodeAlloc.cpp deleted file mode 100644 index b28ccabc7e2..00000000000 --- a/deps/mozjs/js/src/nanojit/CodeAlloc.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -//#define DOPROF -#include "../vprof/vprof.h" - -#ifdef FEATURE_NANOJIT - -namespace nanojit -{ - static const bool verbose = false; -#ifdef VMCFG_VTUNE - // vtune jit profiling api can't handle non-contiguous methods, - // so make the allocation size huge to avoid non-contiguous methods - static const int pagesPerAlloc = 128; // 1MB -#elif defined(NANOJIT_ARM) - // ARM requires single-page allocations, due to the constant pool that - // lives on each page that must be reachable by a 4kb pcrel load. - static const int pagesPerAlloc = 1; -#else - static const int pagesPerAlloc = 16; -#endif - - CodeAlloc::CodeAlloc() - : heapblocks(0) - , availblocks(0) - , totalAllocated(0) - , bytesPerPage(VMPI_getVMPageSize()) - , bytesPerAlloc(pagesPerAlloc * bytesPerPage) - { - } - - CodeAlloc::~CodeAlloc() { - reset(); - } - - void CodeAlloc::reset() { - // give all memory back to gcheap. Assumption is that all - // code is done being used by now. - for (CodeList* hb = heapblocks; hb != 0; ) { - _nvprof("free page",1); - CodeList* next = hb->next; - CodeList* fb = firstBlock(hb); - markBlockWrite(fb); - freeCodeChunk(fb, bytesPerAlloc); - totalAllocated -= bytesPerAlloc; - hb = next; - } - NanoAssert(!totalAllocated); - heapblocks = availblocks = 0; - } - - CodeList* CodeAlloc::firstBlock(CodeList* term) { - // use uintptr_t, rather than char*, to avoid "increases required alignment" warning - uintptr_t end = (uintptr_t)alignUp(term, bytesPerPage); - return (CodeList*) (end - (uintptr_t)bytesPerAlloc); - } - - static int round(size_t x) { - return (int)((x + 512) >> 10); - } - - void CodeAlloc::getStats(size_t& total, size_t& frag_size, size_t& free_size) { - total = 0; - frag_size = 0; - free_size = 0; - int free_count = 0; - for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) { - total += bytesPerAlloc; - for (CodeList* b = hb->lower; b != 0; b = b->lower) { - if (b->isFree) { - free_count++; - free_size += b->blockSize(); - if (b->size() < minAllocSize) - frag_size += b->blockSize(); - } - } - } - } - - void CodeAlloc::logStats() { - size_t total, frag_size, free_size; - getStats(total, frag_size, free_size); - avmplus::AvmLog("code-heap: %dk free %dk fragmented %d\n", - round(total), round(free_size), frag_size); - } - - inline void CodeAlloc::markBlockWrite(CodeList* b) { - NanoAssert(b->terminator != NULL); - CodeList* term = b->terminator; - if (term->isExec) { - markCodeChunkWrite(firstBlock(term), bytesPerAlloc); - term->isExec = false; - } - } - - void CodeAlloc::alloc(NIns* &start, NIns* &end, size_t byteLimit) { - if (!availblocks) { - // no free mem, get more - addMem(); - } - - // grab a block - NanoAssert(!byteLimit || byteLimit > blkSpaceFor(2)); // if a limit is imposed it must be bigger than 2x minimum block size (see below) - markBlockWrite(availblocks); - CodeList* b = removeBlock(availblocks); - - // limit imposed (byteLimit > 0) and the block is too big? then break it apart - if (byteLimit > 0 && b->size() > byteLimit) { - - size_t consume; // # bytes to extract from the free block - - // enough space to carve out a perfectly sized blk? (leaving at least a full free blk) - if (b->size() >= byteLimit + headerSpaceFor(1) + blkSpaceFor(1)) { - // yes, then take exactly what we need - consume = byteLimit + headerSpaceFor(1); - } else { - // no, then we should only take the min amount - consume = blkSpaceFor(1); - - // ... and since b->size() > byteLimit && byteLimit > blkSpaceFor(2) - NanoAssert( b->size() > blkSpaceFor(2) ); - NanoAssert( b->size() - consume > blkSpaceFor(1) ); // thus, we know that at least 1 blk left. - } - - // break block into 2 pieces, returning the lower portion to the free list - CodeList* higher = b->higher; - b->end = (NIns*) ( (uintptr_t)b->end - consume ); - CodeList* b1 = b->higher; - higher->lower = b1; - b1->higher = higher; - b1->lower = b; - b1->terminator = b->terminator; - NanoAssert(b->size() > minAllocSize); - addBlock(availblocks, b); // put back the rest of the block - b = b1; - } - NanoAssert(b->size() >= minAllocSize); - b->next = 0; // not technically needed (except for debug builds), but good hygiene. - b->isFree = false; - start = b->start(); - end = b->end; - if (verbose) - avmplus::AvmLog("CodeAlloc(%p).alloc %p-%p %d\n", this, start, end, int(end-start)); - debug_only(sanity_check();) - } - - void CodeAlloc::free(NIns* start, NIns *end) { - NanoAssert(heapblocks); - CodeList *blk = getBlock(start, end); - if (verbose) - avmplus::AvmLog("free %p-%p %d\n", start, end, (int)blk->size()); - - NanoAssert(!blk->isFree); - - // coalesce adjacent blocks. - bool already_on_avail_list; - - if (blk->lower && blk->lower->isFree) { - // combine blk into blk->lower (destroy blk) - CodeList* lower = blk->lower; - CodeList* higher = blk->higher; - already_on_avail_list = lower->size() >= minAllocSize; - lower->higher = higher; - higher->lower = lower; - blk = lower; - } - else - already_on_avail_list = false; - - // the last block in each heapblock is a terminator block, - // which is never free, therefore blk->higher != null - if (blk->higher->isFree) { - CodeList *higher = blk->higher->higher; - CodeList *coalescedBlock = blk->higher; - - if ( coalescedBlock->size() >= minAllocSize ) { - // Unlink coalescedBlock from the available block chain. - if ( availblocks == coalescedBlock ) { - removeBlock(availblocks); - } - else { - CodeList* free_block = availblocks; - while (free_block->next != coalescedBlock) { - NanoAssert(free_block->size() >= minAllocSize); - NanoAssert(free_block->isFree); - NanoAssert(free_block->next); - free_block = free_block->next; - } - NanoAssert(free_block->next == coalescedBlock); - free_block->next = coalescedBlock->next; - } - } - - // combine blk->higher into blk (destroy coalescedBlock) - blk->higher = higher; - higher->lower = blk; - } - blk->isFree = true; - NanoAssert(!blk->lower || !blk->lower->isFree); - NanoAssert(blk->higher && !blk->higher->isFree); - //memset(blk->start(), 0xCC, blk->size()); // INT 3 instruction - if ( !already_on_avail_list && blk->size() >= minAllocSize ) - addBlock(availblocks, blk); - - NanoAssert(heapblocks); - debug_only(sanity_check();) - } - - void CodeAlloc::freeAll(CodeList* &code) { - while (code) { - CodeList *b = removeBlock(code); - free(b->start(), b->end); - } - } - -#if defined NANOJIT_ARM && defined UNDER_CE - // Use a single flush for the whole CodeList, when we have no - // finer-granularity flush support, as on WinCE. - void CodeAlloc::flushICache(CodeList* &/*blocks*/) { - FlushInstructionCache(GetCurrentProcess(), NULL, NULL); - } -#else - void CodeAlloc::flushICache(CodeList* &blocks) { - for (CodeList *b = blocks; b != 0; b = b->next) - flushICache(b->start(), b->size()); - } -#endif - -#if defined(AVMPLUS_UNIX) && defined(NANOJIT_ARM) -#include -extern "C" void __clear_cache(char *BEG, char *END); -#endif - -#if defined(AVMPLUS_UNIX) && defined(NANOJIT_MIPS) -#include -extern "C" int cacheflush(char *addr, int nbytes, int cache); -#endif - -#ifdef AVMPLUS_SPARC -// Note: the linux #define provided by the compiler. -#ifdef linux // bugzilla 502369 -void sync_instruction_memory(caddr_t v, u_int len) -{ - caddr_t end = v + len; - caddr_t p = v; - while (p < end) { - asm("flush %0" : : "r" (p)); - p += 32; - } -} -#else -extern "C" void sync_instruction_memory(caddr_t v, u_int len); -#endif -#endif - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - // intel chips have dcache/icache interlock - void CodeAlloc::flushICache(void *start, size_t len) { - // Tell Valgrind that new code has been generated, and it must flush - // any translations it has for the memory range generated into. - (void)start; - (void)len; - VALGRIND_DISCARD_TRANSLATIONS(start, len); - } - -#elif defined NANOJIT_ARM && defined UNDER_CE - // On arm/winmo, just flush the whole icache. The - // WinCE docs indicate that this function actually ignores its - // 2nd and 3rd arguments, and wants them to be NULL. - void CodeAlloc::flushICache(void *, size_t) { - FlushInstructionCache(GetCurrentProcess(), NULL, NULL); - } - -#elif defined NANOJIT_ARM && defined DARWIN - void CodeAlloc::flushICache(void *, size_t) { - VMPI_debugBreak(); - } - -#elif defined AVMPLUS_MAC && defined NANOJIT_PPC - -# ifdef NANOJIT_64BIT - extern "C" void sys_icache_invalidate(const void*, size_t len); - extern "C" void sys_dcache_flush(const void*, size_t len); - - // mac 64bit requires 10.5 so use that api - void CodeAlloc::flushICache(void *start, size_t len) { - sys_dcache_flush(start, len); - sys_icache_invalidate(start, len); - } -# else - // mac ppc 32 could be 10.0 or later - // uses MakeDataExecutable() from Carbon api, OSUtils.h - // see http://developer.apple.com/documentation/Carbon/Reference/Memory_Manag_nt_Utilities/Reference/reference.html#//apple_ref/c/func/MakeDataExecutable - void CodeAlloc::flushICache(void *start, size_t len) { - MakeDataExecutable(start, len); - } -# endif - -#elif defined NANOJIT_ARM && defined VMCFG_SYMBIAN - void CodeAlloc::flushICache(void *ptr, size_t len) { - uint32_t start = (uint32_t)ptr; - uint32_t rangeEnd = start + len; - User::IMB_Range((TAny*)start, (TAny*)rangeEnd); - } - -#elif defined AVMPLUS_SPARC - // fixme: sync_instruction_memory is a solaris api, test for solaris not sparc - void CodeAlloc::flushICache(void *start, size_t len) { - sync_instruction_memory((char*)start, len); - } - -#elif defined NANOJIT_SH4 -#include /* CACHEFLUSH_*, */ -#include /* __NR_cacheflush, */ - void CodeAlloc::flushICache(void *start, size_t len) { - syscall(__NR_cacheflush, start, len, CACHEFLUSH_D_WB | CACHEFLUSH_I); - } - -#elif defined(AVMPLUS_UNIX) && defined(NANOJIT_MIPS) - void CodeAlloc::flushICache(void *start, size_t len) { - // FIXME Use synci on MIPS32R2 - cacheflush((char *)start, len, BCACHE); - } - -#elif defined AVMPLUS_UNIX - #ifdef ANDROID - void CodeAlloc::flushICache(void *start, size_t len) { - cacheflush((int)start, (int)start + len, 0); - } - #else - // fixme: __clear_cache is a libgcc feature, test for libgcc or gcc - void CodeAlloc::flushICache(void *start, size_t len) { - __clear_cache((char*)start, (char*)start + len); - } - #endif -#endif // AVMPLUS_MAC && NANOJIT_PPC - - void CodeAlloc::addBlock(CodeList* &blocks, CodeList* b) { - NanoAssert(b->terminator != NULL); // should not be mucking with terminator blocks - b->next = blocks; - blocks = b; - } - - void CodeAlloc::addMem() { - void *mem = allocCodeChunk(bytesPerAlloc); // allocations never fail - totalAllocated += bytesPerAlloc; - NanoAssert(mem != NULL); // see allocCodeChunk contract in CodeAlloc.h - _nvprof("alloc page", uintptr_t(mem)>>12); - - CodeList* b = (CodeList*)mem; - b->lower = 0; - b->next = 0; - b->end = (NIns*) (uintptr_t(mem) + bytesPerAlloc - sizeofMinBlock); - b->isFree = true; - - // create a tiny terminator block, add to fragmented list, this way - // all other blocks have a valid block at b->higher - CodeList* terminator = b->higher; - b->terminator = terminator; - terminator->lower = b; - terminator->end = 0; // this is how we identify the terminator - terminator->isFree = false; - terminator->isExec = false; - terminator->terminator = 0; - debug_only(sanity_check();) - - // add terminator to heapblocks list so we can track whole blocks - terminator->next = heapblocks; - heapblocks = terminator; - - addBlock(availblocks, b); // add to free list - } - - CodeList* CodeAlloc::getBlock(NIns* start, NIns* end) { - CodeList* b = (CodeList*) (uintptr_t(start) - offsetof(CodeList, code)); - NanoAssert(b->end == end && b->next == 0); (void) end; - return b; - } - - CodeList* CodeAlloc::removeBlock(CodeList* &blocks) { - CodeList* b = blocks; - NanoAssert(b != NULL); - NanoAssert(b->terminator != NULL); // should not be mucking with terminator blocks - blocks = b->next; - b->next = 0; - return b; - } - - void CodeAlloc::add(CodeList* &blocks, NIns* start, NIns* end) { - addBlock(blocks, getBlock(start, end)); - } - - /** - * split a block by freeing the hole in the middle defined by [holeStart,holeEnd), - * and adding the used prefix and suffix parts to the blocks CodeList. - */ - void CodeAlloc::addRemainder(CodeList* &blocks, NIns* start, NIns* end, NIns* holeStart, NIns* holeEnd) { - NanoAssert(start < end && start <= holeStart && holeStart <= holeEnd && holeEnd <= end); - // shrink the hole by aligning holeStart forward and holeEnd backward - holeStart = (NIns*) ((uintptr_t(holeStart) + sizeof(NIns*)-1) & ~(sizeof(NIns*)-1)); - holeEnd = (NIns*) (uintptr_t(holeEnd) & ~(sizeof(NIns*)-1)); - // hole needs to be big enough for 2 headers + 1 block of free space (subtraction not used in check to avoid wraparound) - size_t minHole = headerSpaceFor(2) + blkSpaceFor(1); - if (uintptr_t(holeEnd) < minHole + uintptr_t(holeStart) ) { - // the hole is too small to make a new free block and a new used block. just keep - // the whole original block and don't free anything. - add(blocks, start, end); - } else if (holeStart == start && holeEnd == end) { - // totally empty block. free whole start-end range - this->free(start, end); - } else if (holeStart == start) { - // hole is lower-aligned with start, so just need one new block - // b1 b2 - CodeList* b1 = getBlock(start, end); - CodeList* b2 = (CodeList*) (uintptr_t(holeEnd) - offsetof(CodeList, code)); - b2->terminator = b1->terminator; - b2->isFree = false; - b2->next = 0; - b2->higher = b1->higher; - b2->lower = b1; - b2->higher->lower = b2; - b1->higher = b2; - debug_only(sanity_check();) - this->free(b1->start(), b1->end); - addBlock(blocks, b2); - } else if (holeEnd == end) { - // hole is right-aligned with end, just need one new block - // todo - NanoAssert(false); - } else { - // there's enough space left to split into three blocks (two new ones) - CodeList* b1 = getBlock(start, end); - CodeList* b2 = (CodeList*) (void*) holeStart; - CodeList* b3 = (CodeList*) (uintptr_t(holeEnd) - offsetof(CodeList, code)); - b1->higher = b2; - b2->lower = b1; - b2->higher = b3; - b2->isFree = false; // redundant, since we're about to free, but good hygiene - b2->terminator = b1->terminator; - b3->lower = b2; - b3->end = end; - b3->isFree = false; - b3->higher->lower = b3; - b3->terminator = b1->terminator; - b2->next = 0; - b3->next = 0; - debug_only(sanity_check();) - this->free(b2->start(), b2->end); - addBlock(blocks, b3); - addBlock(blocks, b1); - } - } - -#ifdef PERFM - // This method is used only for profiling purposes. - // See CodegenLIR::emitMD() in Tamarin for an example. - - size_t CodeAlloc::size(const CodeList* blocks) { - size_t size = 0; - for (const CodeList* b = blocks; b != 0; b = b->next) - size += int((uintptr_t)b->end - (uintptr_t)b); - return size; - } -#endif - - size_t CodeAlloc::size() { - return totalAllocated; - } - - // check that all block neighbors are correct - #ifdef _DEBUG - void CodeAlloc::sanity_check() { - for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) { - NanoAssert(hb->higher == 0); - for (CodeList* b = hb->lower; b != 0; b = b->lower) { - NanoAssert(b->higher->lower == b); - } - } - for (CodeList* avail = this->availblocks; avail; avail = avail->next) { - NanoAssert(avail->isFree && avail->size() >= minAllocSize); - } - - #if CROSS_CHECK_FREE_LIST - for(CodeList* term = heapblocks; term; term = term->next) { - for(CodeList* hb = term->lower; hb; hb = hb->lower) { - if (hb->isFree && hb->size() >= minAllocSize) { - bool found_on_avail = false; - for (CodeList* avail = this->availblocks; !found_on_avail && avail; avail = avail->next) { - found_on_avail = avail == hb; - } - - NanoAssert(found_on_avail); - } - } - } - for (CodeList* avail = this->availblocks; avail; avail = avail->next) { - bool found_in_heapblocks = false; - for(CodeList* term = heapblocks; !found_in_heapblocks && term; term = term->next) { - for(CodeList* hb = term->lower; !found_in_heapblocks && hb; hb = hb->lower) { - found_in_heapblocks = hb == avail; - } - } - NanoAssert(found_in_heapblocks); - } - #endif /* CROSS_CHECK_FREE_LIST */ - } - #endif - - // Loop through a list of blocks marking the chunks executable. If we encounter - // multiple blocks in the same chunk, only the first block will cause the - // chunk to become executable, the other calls will no-op (isExec flag checked) - void CodeAlloc::markExec(CodeList* &blocks) { - for (CodeList *b = blocks; b != 0; b = b->next) { - markChunkExec(b->terminator); - } - } - - // Variant of markExec(CodeList*) that walks all heapblocks (i.e. chunks) marking - // each one executable. On systems where bytesPerAlloc is low (i.e. have lots - // of elements in the list) this can be expensive. - void CodeAlloc::markAllExec() { - for (CodeList* hb = heapblocks; hb != NULL; hb = hb->next) { - markChunkExec(hb); - } - } - - // make an entire chunk executable - void CodeAlloc::markChunkExec(CodeList* term) { - NanoAssert(term->terminator == NULL); - if (!term->isExec) { - term->isExec = true; - markCodeChunkExec(firstBlock(term), bytesPerAlloc); - } - } -} -#endif // FEATURE_NANOJIT diff --git a/deps/mozjs/js/src/nanojit/CodeAlloc.h b/deps/mozjs/js/src/nanojit/CodeAlloc.h deleted file mode 100644 index 077d1d6ae68..00000000000 --- a/deps/mozjs/js/src/nanojit/CodeAlloc.h +++ /dev/null @@ -1,240 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_CodeAlloc__ -#define __nanojit_CodeAlloc__ - -namespace nanojit -{ - /** - * CodeList is a single block of code. The next field is used to - * form linked lists of non-contiguous blocks of code. Clients use CodeList* - * to point to the first block in a list. - */ - class CodeList - { - friend class CodeAlloc; - - /** for making singly linked lists of blocks in any order */ - CodeList* next; - - /** adjacent block at lower address. This field plus higher - form a doubly linked list of blocks in address order, used - for splitting and coalescing blocks. */ - CodeList* lower; - - /** pointer to the heapblock terminal that represents the code chunk containing this block */ - CodeList* terminator; - - /** true if block is free, false otherwise */ - bool isFree; - - /** (only valid for terminator blocks). Set true just before calling - * markCodeChunkExec() and false just after markCodeChunkWrite() */ - bool isExec; - - union { - // this union is used in leu of pointer punning in code - // the end of this block is always the address of the next higher block - CodeList* higher; // adjacent block at higher address - NIns* end; // points just past the end - }; - - /** code holds this block's payload of binary code, from - here to this->end */ - NIns code[1]; // more follows - - /** return the starting address for this block only */ - NIns* start() { return &code[0]; } - - /** return just the usable size of this block */ - size_t size() const { return uintptr_t(end) - uintptr_t(&code[0]); } - - /** return the whole size of this block including overhead */ - size_t blockSize() const { return uintptr_t(end) - uintptr_t(this); } - - public: - /** true is the given NIns is contained within this block */ - bool isInBlock(NIns* n) { return (n >= this->start() && n < this->end); } - }; - - /** - * Code memory allocator is a long lived manager for many code blocks that - * manages interaction with an underlying code memory allocator, - * sets page permissions. CodeAlloc provides APIs for allocating and freeing - * individual blocks of code memory (for methods, stubs, or compiled - * traces), static functions for managing lists of allocated code, and has - * a few pure virtual methods that embedders must implement to provide - * memory to the allocator. - * - * A "chunk" is a region of memory obtained from allocCodeChunk; it must - * be page aligned and be a multiple of the system page size. - * - * A "block" is a region of memory within a chunk. It can be arbitrarily - * sized and aligned, but is always contained within a single chunk. - * class CodeList represents one block; the members of CodeList track the - * extent of the block and support creating lists of blocks. - * - * The allocator coalesces free blocks when it can, in free(), but never - * coalesces chunks. - */ - class CodeAlloc - { - static const size_t sizeofMinBlock = offsetof(CodeList, code); - static const size_t minAllocSize = LARGEST_UNDERRUN_PROT; - - // Return the number of bytes needed for the header of 'n' blocks - static size_t headerSpaceFor(uint32_t nbrBlks) { return nbrBlks * sizeofMinBlock; } - - // Return the number of bytes needed in order to safely construct 'n' blocks - static size_t blkSpaceFor(uint32_t nbrBlks) { return (nbrBlks * minAllocSize) + headerSpaceFor(nbrBlks); } - - /** Terminator blocks. All active and free allocations - are reachable by traversing this chain and each - element's lower chain. */ - CodeList* heapblocks; - - /** Reusable blocks. */ - CodeList* availblocks; - size_t totalAllocated; - - /** Cached value of VMPI_getVMPageSize */ - const size_t bytesPerPage; - - /** Number of bytes to request from VMPI layer, always a multiple of the page size */ - const size_t bytesPerAlloc; - - /** remove one block from a list */ - static CodeList* removeBlock(CodeList* &list); - - /** add one block to a list */ - static void addBlock(CodeList* &blocks, CodeList* b); - - /** compute the CodeList pointer from a [start, end) range */ - static CodeList* getBlock(NIns* start, NIns* end); - - /** add raw memory to the free list */ - void addMem(); - - /** make sure all the higher/lower pointers are correct for every block */ - void sanity_check(); - - /** find the beginning of the heapblock terminated by term */ - CodeList* firstBlock(CodeList* term); - - // - // CodeAlloc's SPI (Service Provider Interface). Implementations must be - // defined by nanojit embedder. Allocation failures should cause an exception - // or longjmp; nanojit intentionally does not check for null. - // - - /** allocate nbytes of memory to hold code. Never return null! */ - void* allocCodeChunk(size_t nbytes); - - /** free a block previously allocated by allocCodeMem. nbytes will - * match the previous allocCodeMem, but is provided here as well - * to mirror the mmap()/munmap() api. markCodeChunkWrite() will have - * been called if necessary, so it is not necessary for freeCodeChunk() - * to do it again. */ - void freeCodeChunk(void* addr, size_t nbytes); - - /** make this specific extent ready to execute (might remove write) */ - void markCodeChunkExec(void* addr, size_t nbytes); - - /** make this extent ready to modify (might remove exec) */ - void markCodeChunkWrite(void* addr, size_t nbytes); - - public: - CodeAlloc(); - ~CodeAlloc(); - - /** return all the memory allocated through this allocator to the gcheap. */ - void reset(); - - /** allocate some memory (up to 'byteLimit' bytes) for code returning pointers to the region. A zero 'byteLimit' means no limit */ - void alloc(NIns* &start, NIns* &end, size_t byteLimit); - - /** free a block of memory previously returned by alloc() */ - void free(NIns* start, NIns* end); - - /** free several blocks */ - void freeAll(CodeList* &code); - - /** flush the icache for all code in the list, before executing */ - static void flushICache(CodeList* &blocks); - - /** flush the icache for a specific extent */ - static void flushICache(void *start, size_t len); - - /** add the ranges [start, holeStart) and [holeEnd, end) to code, and - free [holeStart, holeEnd) if the hole is >= minsize */ - void addRemainder(CodeList* &code, NIns* start, NIns* end, NIns* holeStart, NIns* holeEnd); - - /** add a block previously returned by alloc(), to code */ - static void add(CodeList* &code, NIns* start, NIns* end); - - /** return the number of bytes in all the code blocks in "code", including block overhead */ -#ifdef PERFM - static size_t size(const CodeList* code); -#endif - - /** return the total number of bytes held by this CodeAlloc. */ - size_t size(); - - /** get stats about heap usage */ - void getStats(size_t& total, size_t& frag_size, size_t& free_size); - - /** print out stats about heap usage */ - void logStats(); - - /** protect all code managed by this CodeAlloc */ - void markAllExec(); - - /** protect all mem in the block list */ - void markExec(CodeList* &blocks); - - /** protect an entire chunk */ - void markChunkExec(CodeList* term); - - /** unprotect the code chunk containing just this one block */ - void markBlockWrite(CodeList* b); - }; -} - -#endif // __nanojit_CodeAlloc__ diff --git a/deps/mozjs/js/src/nanojit/Containers.cpp b/deps/mozjs/js/src/nanojit/Containers.cpp deleted file mode 100644 index ac7c32004f7..00000000000 --- a/deps/mozjs/js/src/nanojit/Containers.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef FEATURE_NANOJIT - -namespace nanojit -{ - BitSet::BitSet(Allocator& allocator, int nbits) - : allocator(allocator) - , cap((nbits+63)>>6) - , bits((int64_t*)allocator.alloc(cap * sizeof(int64_t))) - { - reset(); - } - - void BitSet::reset() - { - for (int i=0, n=cap; i < n; i++) - bits[i] = 0; - } - - bool BitSet::setFrom(BitSet& other) - { - int c = other.cap; - if (c > cap) - grow(c); - int64_t *bits = this->bits; - int64_t *otherbits = other.bits; - int64_t newbits = 0; - for (int i=0; i < c; i++) { - int64_t b = bits[i]; - int64_t b2 = otherbits[i]; - newbits |= b2 & ~b; // bits in b2 that are not in b - bits[i] = b|b2; - } - return newbits != 0; - } - - /** keep doubling the bitset length until w fits */ - void BitSet::grow(int w) - { - int cap2 = cap; - do { - cap2 <<= 1; - } while (w >= cap2); - int64_t *bits2 = (int64_t*) allocator.alloc(cap2 * sizeof(int64_t)); - int j=0; - for (; j < cap; j++) - bits2[j] = bits[j]; - for (; j < cap2; j++) - bits2[j] = 0; - cap = cap2; - bits = bits2; - } -} - -#endif // FEATURE_NANOJIT diff --git a/deps/mozjs/js/src/nanojit/Containers.h b/deps/mozjs/js/src/nanojit/Containers.h deleted file mode 100644 index d3439664b76..00000000000 --- a/deps/mozjs/js/src/nanojit/Containers.h +++ /dev/null @@ -1,466 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_Containers__ -#define __nanojit_Containers__ - -namespace nanojit -{ - /** simple linear bit array, memory taken from Allocator - * warning: when bit array grows, old memory is wasted since it - * was allocated from Allocator. pre-size the bitmap when possible - * by passing nbits to the constructor. */ - class BitSet { - Allocator &allocator; - int cap; - int64_t *bits; - static const int64_t ONE = 1; - static const int SHIFT = 6; - - inline int bitnum2word(int i) { - return i >> 6; - } - inline int64_t bitnum2mask(int i) { - return ONE << (i & 63); - } - - /** keep doubling array to fit at least w words */ - void grow(int w); - - public: - BitSet(Allocator& allocator, int nbits=128); - - /** clear all bits */ - void reset(); - - /** perform a bitwise or with BitSet other, return true if - * this bitset was modified */ - bool setFrom(BitSet& other); - - /** return bit i as a bool */ - bool get(int i) { - NanoAssert(i >= 0); - int w = bitnum2word(i); - if (w < cap) - return (bits[w] & bitnum2mask(i)) != 0; - return false; - } - - /** set bit i */ - void set(int i) { - NanoAssert(i >= 0); - int w = bitnum2word(i); - if (w >= cap) - grow(w); - NanoAssert(w < cap); - bits[w] |= bitnum2mask(i); - } - - /** clear bit i */ - void clear(int i) { - NanoAssert(i >= 0); - int w = bitnum2word(i); - if (w < cap) - bits[w] &= ~bitnum2mask(i); - } - }; - - /** Seq is a single node in a linked list */ - template class Seq { - public: - Seq(T head, Seq* tail=NULL) : head(head), tail(tail) {} - T head; - Seq* tail; - }; - - /** SeqBuilder is used to create a linked list of Seq by inserting - * nodes either at the beginning, with insert(), or at the end, with - * add(). Once built, the actual list can be retained while this - * SeqBuilder can be discarded. */ - template class SeqBuilder { - public: - SeqBuilder(Allocator& allocator) - : allocator(allocator) - , items(NULL) - , last(NULL) - { } - - /** add item to beginning of list */ - void insert(T item) { - Seq* e = new (allocator) Seq(item, items); - if (last == NULL) - last = e; - items = e; - } - - /** add item to end of list */ - void add(T item) { - Seq* e = new (allocator) Seq(item); - if (last == NULL) - items = e; - else - last->tail = e; - last = e; - } - - /** return first item in sequence */ - Seq* get() const { - return items; - } - - /** self explanitory */ - bool isEmpty() const { - return items == NULL; - } - - /** de-reference all items */ - void clear() { - items = last = NULL; - } - - private: - Allocator& allocator; - Seq* items; - Seq* last; - }; - -#ifdef NANOJIT_64BIT - static inline size_t murmurhash(const void *key, size_t len) { - const uint64_t m = 0xc6a4a7935bd1e995; - const int r = 47; - uint64_t h = 0; - - const uint64_t *data = (const uint64_t*)key; - const uint64_t *end = data + (len/8); - - while(data != end) - { - uint64_t k = *data++; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - - const unsigned char *data2 = (const unsigned char*)data; - - switch(len & 7) { - case 7: h ^= uint64_t(data2[6]) << 48; - case 6: h ^= uint64_t(data2[5]) << 40; - case 5: h ^= uint64_t(data2[4]) << 32; - case 4: h ^= uint64_t(data2[3]) << 24; - case 3: h ^= uint64_t(data2[2]) << 16; - case 2: h ^= uint64_t(data2[1]) << 8; - case 1: h ^= uint64_t(data2[0]); - h *= m; - }; - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return (size_t)h; - } -#else - static inline size_t murmurhash(const void * key, size_t len) { - const uint32_t m = 0x5bd1e995; - const int r = 24; - uint32_t h = 0; - - const unsigned char * data = (const unsigned char *)key; - while(len >= 4) { - uint32_t k = *(size_t *)(void*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - switch(len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return (size_t)h; - } -#endif - - template struct DefaultHash { - static size_t hash(const K &k) { - // (const void*) cast is required by ARM RVCT 2.2 - return murmurhash((const void*) &k, sizeof(K)); - } - }; - - template struct DefaultHash { - static size_t hash(K* k) { - uintptr_t h = (uintptr_t) k; - // move the low 3 bits higher up since they're often 0 - h = (h>>3) ^ (h<<((sizeof(uintptr_t) * 8) - 3)); - return (size_t) h; - } - }; - - /** Bucket hashtable with a fixed # of buckets (never rehash) - * Intended for use when a reasonable # of buckets can be estimated ahead of time. - * Note that operator== is used to compare keys. - */ - template > class HashMap { - Allocator& allocator; - size_t nbuckets; - class Node { - public: - K key; - T value; - Node(K k, T v) : key(k), value(v) { } - }; - Seq** buckets; - - /** return the node containing K, and the bucket index, or NULL if not found */ - Node* find(K k, size_t &i) { - i = H::hash(k) % nbuckets; - for (Seq* p = buckets[i]; p != NULL; p = p->tail) { - if (p->head.key == k) - return &p->head; - } - return NULL; - } - public: - HashMap(Allocator& a, size_t nbuckets = 16) - : allocator(a) - , nbuckets(nbuckets) - , buckets(new (a) Seq*[nbuckets]) - { - NanoAssert(nbuckets > 0); - clear(); - } - - /** clear all buckets. Since we allocate all memory from Allocator, - * nothing needs to be freed. */ - void clear() { - VMPI_memset(buckets, 0, sizeof(Seq*) * nbuckets); - } - - /** add (k,v) to the map. If k is already in the map, replace the value */ - void put(const K& k, const T& v) { - size_t i; - Node* n = find(k, i); - if (n) { - n->value = v; - return; - } - buckets[i] = new (allocator) Seq(Node(k,v), buckets[i]); - } - - /** return v for element k, or T(0) if k is not present */ - T get(const K& k) { - size_t i; - Node* n = find(k, i); - return n ? n->value : 0; - } - - /** returns true if k is in the map. */ - bool containsKey(const K& k) { - size_t i; - return find(k, i) != 0; - } - - /** remove k from the map, if it is present. if not, remove() - * silently returns */ - void remove(const K& k) { - size_t i = H::hash(k) % nbuckets; - Seq** prev = &buckets[i]; - for (Seq* p = buckets[i]; p != NULL; p = p->tail) { - if (p->head.key == k) { - (*prev) = p->tail; - return; - } - prev = &p->tail; - } - } - - /** Iter is an iterator for HashMap, intended to be instantiated on - * the stack. Iteration order is undefined. Mutating the hashmap - * while iteration is in progress gives undefined results. All iteration - * state is in class Iter, so multiple iterations can be in progress - * at the same time. for example: - * - * HashMap::Iter iter(map); - * while (iter.next()) { - * K *k = iter.key(); - * T *t = iter.value(); - * } - */ - class Iter { - friend class HashMap; - const HashMap ↦ - int bucket; - const Seq* current; - - public: - Iter(HashMap& map) : map(map), bucket((int)map.nbuckets-1), current(NULL) - { } - - /** return true if more (k,v) remain to be visited */ - bool next() { - if (current) - current = current->tail; - while (bucket >= 0 && !current) - current = map.buckets[bucket--]; - return current != NULL; - } - - /** return the current key */ - const K& key() const { - NanoAssert(current != NULL); - return current->head.key; - } - - /** return the current value */ - const T& value() const { - NanoAssert(current != NULL); - return current->head.value; - } - }; - - /** return true if the hashmap has no elements */ - bool isEmpty() { - Iter iter(*this); - return !iter.next(); - } - }; - - /** - * Simple binary tree. No balancing is performed under the assumption - * that the only users of this structure are not performance critical. - */ - template class TreeMap { - Allocator& alloc; - class Node { - public: - Node* left; - Node* right; - K key; - T value; - Node(K k, T v) : left(NULL), right(NULL), key(k), value(v) - { } - }; - Node* root; - - /** - * helper method to recursively insert (k,v) below Node n or a child - * of n so that the binary search tree remains well formed. - */ - void insert(Node* &n, K k, T v) { - if (!n) - n = new (alloc) Node(k, v); - else if (k == n->key) - n->value = v; - else if (k < n->key) - insert(n->left, k, v); - else - insert(n->right, k, v); - } - - /** - * search for key k below Node n and return n if found, or the - * closest parent n where k should be inserted. - */ - Node* find(Node* n, K k) { - if (!n) - return NULL; - if (k == n->key) - return n; - if (k < n->key) - return find(n->left, k); - if (n->right) - return find(n->right, k); - return n; - } - - public: - TreeMap(Allocator& alloc) : alloc(alloc), root(NULL) - { } - - /** set k = v in the map. if k already exists, replace its value */ - void put(K k, T v) { - insert(root, k, v); - } - - /** return the closest key that is <= k, or NULL if k - is smaller than every key in the Map. */ - K findNear(K k) { - Node* n = find(root, k); - return n ? n->key : 0; - } - - /** returns the value for k or NULL */ - T get(K k) { - Node* n = find(root, k); - return (n && n->key == k) ? n->value : 0; - } - - /** returns true iff k is in the Map. */ - bool containsKey(K k) { - Node* n = find(root, k); - return n && n->key == k; - } - - /** make the tree empty. trivial since we dont manage elements */ - void clear() { - root = NULL; - } - }; -} -#endif // __nanojit_Containers__ diff --git a/deps/mozjs/js/src/nanojit/Fragmento.cpp b/deps/mozjs/js/src/nanojit/Fragmento.cpp deleted file mode 100644 index 6bf35cfcfd7..00000000000 --- a/deps/mozjs/js/src/nanojit/Fragmento.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * Mozilla TraceMonkey Team - * Asko Tontti - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -namespace nanojit -{ - #ifdef FEATURE_NANOJIT - - using namespace avmplus; - - // - // Fragment - // - Fragment::Fragment(const void* _ip - verbose_only(, uint32_t profFragID)) - : - lirbuf(NULL), - lastIns(NULL), - ip(_ip), - recordAttempts(0), - fragEntry(NULL), - verbose_only( loopLabel(NULL), ) - verbose_only( profFragID(profFragID), ) - verbose_only( profCount(0), ) - verbose_only( nStaticExits(0), ) - verbose_only( nCodeBytes(0), ) - verbose_only( nExitBytes(0), ) - verbose_only( guardNumberer(1), ) - verbose_only( guardsForFrag(NULL), ) - _code(NULL), - _hits(0) - { - // when frag profiling is enabled, profFragID should be >= 1, - // else it should be zero. However, there's no way to assert - // that here since there's no way to determine whether frag - // profiling is enabled. - } - #endif /* FEATURE_NANOJIT */ -} - - diff --git a/deps/mozjs/js/src/nanojit/Fragmento.h b/deps/mozjs/js/src/nanojit/Fragmento.h deleted file mode 100644 index f4bbe7a3772..00000000000 --- a/deps/mozjs/js/src/nanojit/Fragmento.h +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * Mozilla TraceMonkey Team - * Asko Tontti - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_Fragmento__ -#define __nanojit_Fragmento__ - -namespace nanojit -{ - struct GuardRecord; - - /** - * Fragments are linear sequences of native code that have a single entry - * point at the start of the fragment and may have one or more exit points - * - * It may turn out that that this arrangement causes too much traffic - * between d and i-caches and that we need to carve up the structure differently. - */ - class Fragment - { - public: - Fragment(const void* - verbose_only(, uint32_t profFragID)); - - NIns* code() { return _code; } - void setCode(NIns* codee) { _code = codee; } - int32_t& hits() { return _hits; } - - LirBuffer* lirbuf; - LIns* lastIns; - - const void* ip; - uint32_t recordAttempts; - NIns* fragEntry; - - // for fragment entry and exit profiling. See detailed - // how-to-use comment below. - verbose_only( LIns* loopLabel; ) // where's the loop top? - verbose_only( uint32_t profFragID; ) - verbose_only( uint32_t profCount; ) - verbose_only( uint32_t nStaticExits; ) - verbose_only( size_t nCodeBytes; ) - verbose_only( size_t nExitBytes; ) - verbose_only( uint32_t guardNumberer; ) - verbose_only( GuardRecord* guardsForFrag; ) - - private: - NIns* _code; // ptr to start of code - int32_t _hits; - }; -} - -/* - * How to use fragment profiling - * - * Fragprofiling adds code to count how many times each fragment is - * entered, and how many times each guard (exit) is taken. Using this - * it's possible to easily find which fragments are hot, which ones - * typically exit early, etc. The fragprofiler also gathers some - * simple static info: for each fragment, the number of code bytes, - * number of exit-block bytes, and number of guards (exits). - * - * Fragments and guards are given unique IDs (FragID, GuardID) which - * are shown in debug printouts, so as to facilitate navigating from - * the accumulated statistics to the associated bits of code. - * GuardIDs are issued automatically, but FragIDs you must supply when - * calling Fragment::Fragment. Supply values >= 1, and supply a - * different value for each new fragment (doesn't matter what, they - * just have to be unique and >= 1); else - * js_FragProfiling_FragFinalizer will assert. - * - * How to use/embed: - * - * - use a debug build (one with NJ_VERBOSE). Without it, none of - * this code is compiled in. - * - * - set LC_FragProfile in the lcbits of the LogControl* object handed - * to Nanojit - * - * When enabled, Fragment::profCount is incremented every time the - * fragment is entered, and GuardRecord::profCount is incremented - * every time that guard exits. However, NJ has no way to know where - * the fragment entry/loopback point is. So you must set - * Fragment::loopLabel before running the assembler, so as to indicate - * where the fragment-entry counter increment should be placed. If - * the fragment does not naturally have a loop label then you will - * need to artificially add one. - * - * It is the embedder's problem to fish out, collate and present the - * accumulated stats at the end of the Fragment's lifetime. A - * Fragment contains stats indicating its entry count and static code - * sizes. It also has a ::guardsForFrag field, which is a linked list - * of GuardRecords, and by traversing them you can get hold of the - * exit counts. - */ - -#endif // __nanojit_Fragmento__ diff --git a/deps/mozjs/js/src/nanojit/LIR.cpp b/deps/mozjs/js/src/nanojit/LIR.cpp deleted file mode 100644 index 3a2a43f5522..00000000000 --- a/deps/mozjs/js/src/nanojit/LIR.cpp +++ /dev/null @@ -1,3841 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -namespace nanojit -{ - using namespace avmplus; - #ifdef FEATURE_NANOJIT - - const uint8_t repKinds[] = { -#define OP___(op, number, repKind, retType, isCse) \ - LRK_##repKind, -#include "LIRopcode.tbl" -#undef OP___ - 0 - }; - - const LTy retTypes[] = { -#define OP___(op, number, repKind, retType, isCse) \ - LTy_##retType, -#include "LIRopcode.tbl" -#undef OP___ - LTy_V - }; - - const uint8_t insSizes[] = { -#define OP___(op, number, repKind, retType, isCse) \ - sizeof(LIns##repKind), -#include "LIRopcode.tbl" -#undef OP___ - 0 - }; - - const int8_t isCses[] = { -#define OP___(op, number, repKind, retType, isCse) \ - isCse, -#include "LIRopcode.tbl" -#undef OP___ - 0 - }; - - // LIR verbose specific - #ifdef NJ_VERBOSE - - const char* lirNames[] = { -#define OP___(op, number, repKind, retType, isCse) \ - #op, -#include "LIRopcode.tbl" -#undef OP___ - NULL - }; - - #endif /* NANOJIT_VERBOSE */ - - uint32_t CallInfo::count_args() const - { - uint32_t argc = 0; - uint32_t argt = _typesig; - argt >>= TYPESIG_FIELDSZB; // remove retType - while (argt) { - argc++; - argt >>= TYPESIG_FIELDSZB; - } - return argc; - } - - uint32_t CallInfo::count_int32_args() const - { - uint32_t argc = 0; - uint32_t argt = _typesig; - argt >>= TYPESIG_FIELDSZB; // remove retType - while (argt) { - ArgType a = ArgType(argt & TYPESIG_FIELDMASK); - if (a == ARGTYPE_I || a == ARGTYPE_UI) - argc++; - argt >>= TYPESIG_FIELDSZB; - } - return argc; - } - - uint32_t CallInfo::getArgTypes(ArgType* argTypes) const - { - uint32_t argc = 0; - uint32_t argt = _typesig; - argt >>= TYPESIG_FIELDSZB; // remove retType - while (argt) { - ArgType a = ArgType(argt & TYPESIG_FIELDMASK); - argTypes[argc] = a; - argc++; - argt >>= TYPESIG_FIELDSZB; - } - return argc; - } - - // implementation -#ifdef NJ_VERBOSE - void ReverseLister::finish() - { - _logc->printf("\n"); - _logc->printf("=== BEGIN %s ===\n", _title); - int j = 0; - for (Seq* p = _strs.get(); p != NULL; p = p->tail) - _logc->printf(" %02d: %s\n", j++, p->head); - _logc->printf("=== END %s ===\n", _title); - _logc->printf("\n"); - } - - LIns* ReverseLister::read() - { - // This check is necessary to avoid printing the LIR_start multiple - // times due to lookahead in Assembler::gen(). - if (_prevIns && _prevIns->isop(LIR_start)) - return _prevIns; - LIns* ins = in->read(); - InsBuf b; - const char* str = _printer->formatIns(&b, ins); - char* cpy = new (_alloc) char[strlen(str)+1]; - VMPI_strcpy(cpy, str); - _strs.insert(cpy); - _prevIns = ins; - return ins; - } -#endif - - // LCompressedBuffer - LirBuffer::LirBuffer(Allocator& alloc) : -#ifdef NJ_VERBOSE - printer(NULL), -#endif - abi(ABI_FASTCALL), state(NULL), param1(NULL), sp(NULL), rp(NULL), - _allocator(alloc) - { - clear(); - } - - void LirBuffer::clear() - { - // clear the stats, etc - _unused = 0; - _limit = 0; - _stats.lir = 0; - for (int i = 0; i < NumSavedRegs; ++i) - savedRegs[i] = NULL; - chunkAlloc(); - } - - void LirBuffer::chunkAlloc() - { - _unused = (uintptr_t) _allocator.alloc(CHUNK_SZB); - NanoAssert(_unused != 0); // Allocator.alloc() never returns null. See Allocator.h - _limit = _unused + CHUNK_SZB; - } - - int32_t LirBuffer::insCount() - { - return _stats.lir; - } - - // Allocate a new page, and write the first instruction to it -- a skip - // linking to last instruction of the previous page. - void LirBuffer::moveToNewChunk(uintptr_t addrOfLastLInsOnCurrentChunk) - { - chunkAlloc(); - // Link LIR stream back to prior instruction. - // Unlike all the ins*() functions, we don't call makeRoom() here - // because we know we have enough space, having just started a new - // page. - LInsSk* insSk = (LInsSk*)_unused; - LIns* ins = insSk->getLIns(); - ins->initLInsSk((LIns*)addrOfLastLInsOnCurrentChunk); - _unused += sizeof(LInsSk); - verbose_only(_stats.lir++); - } - - // Make room for a single instruction. - uintptr_t LirBuffer::makeRoom(size_t szB) - { - // Make sure the size is ok - NanoAssert(0 == szB % sizeof(void*)); - NanoAssert(sizeof(LIns) <= szB && szB <= sizeof(LInsSt)); // LInsSt is the biggest one - NanoAssert(_unused < _limit); - - debug_only( bool moved = false; ) - - // If the instruction won't fit on the current chunk, get a new chunk - if (_unused + szB > _limit) { - uintptr_t addrOfLastLInsOnChunk = _unused - sizeof(LIns); - moveToNewChunk(addrOfLastLInsOnChunk); - debug_only( moved = true; ) - } - - // We now know that we are on a chunk that has the requested amount of - // room: record the starting address of the requested space and bump - // the pointer. - uintptr_t startOfRoom = _unused; - _unused += szB; - verbose_only(_stats.lir++); // count the instruction - - // If there's no more space on this chunk, move to a new one. - // (This will only occur if the asked-for size filled up exactly to - // the end of the chunk.) This ensures that next time we enter this - // function, _unused won't be pointing one byte past the end of - // the chunk, which would break everything. - if (_unused >= _limit) { - // Check we used exactly the remaining space - NanoAssert(_unused == _limit); - NanoAssert(!moved); // shouldn't need to moveToNewChunk twice - uintptr_t addrOfLastLInsOnChunk = _unused - sizeof(LIns); - moveToNewChunk(addrOfLastLInsOnChunk); - } - - // Make sure it's word-aligned. - NanoAssert(0 == startOfRoom % sizeof(void*)); - return startOfRoom; - } - - LIns* LirBufWriter::insStore(LOpcode op, LIns* val, LIns* base, int32_t d, AccSet accSet) - { - if (isS16(d)) { - LInsSt* insSt = (LInsSt*)_buf->makeRoom(sizeof(LInsSt)); - LIns* ins = insSt->getLIns(); - ins->initLInsSt(op, val, base, d, accSet); - return ins; - } else { - // If the displacement is more than 16 bits, put it in a separate instruction. - return insStore(op, val, ins2(LIR_addp, base, insImmWord(d)), 0, accSet); - } - } - - LIns* LirBufWriter::ins0(LOpcode op) - { - LInsOp0* insOp0 = (LInsOp0*)_buf->makeRoom(sizeof(LInsOp0)); - LIns* ins = insOp0->getLIns(); - ins->initLInsOp0(op); - return ins; - } - - LIns* LirBufWriter::ins1(LOpcode op, LIns* o1) - { - LInsOp1* insOp1 = (LInsOp1*)_buf->makeRoom(sizeof(LInsOp1)); - LIns* ins = insOp1->getLIns(); - ins->initLInsOp1(op, o1); - return ins; - } - - LIns* LirBufWriter::ins2(LOpcode op, LIns* o1, LIns* o2) - { - LInsOp2* insOp2 = (LInsOp2*)_buf->makeRoom(sizeof(LInsOp2)); - LIns* ins = insOp2->getLIns(); - ins->initLInsOp2(op, o1, o2); - return ins; - } - - LIns* LirBufWriter::ins3(LOpcode op, LIns* o1, LIns* o2, LIns* o3) - { - LInsOp3* insOp3 = (LInsOp3*)_buf->makeRoom(sizeof(LInsOp3)); - LIns* ins = insOp3->getLIns(); - ins->initLInsOp3(op, o1, o2, o3); - return ins; - } - - LIns* LirBufWriter::insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet, LoadQual loadQual) - { - if (isS16(d)) { - LInsLd* insLd = (LInsLd*)_buf->makeRoom(sizeof(LInsLd)); - LIns* ins = insLd->getLIns(); - ins->initLInsLd(op, base, d, accSet, loadQual); - return ins; - } else { - // If the displacement is more than 16 bits, put it in a separate instruction. - // Note that CseFilter::insLoad() also does this, so this will - // only occur if CseFilter has been removed from the pipeline. - return insLoad(op, ins2(LIR_addp, base, insImmWord(d)), 0, accSet, loadQual); - } - } - - LIns* LirBufWriter::insGuard(LOpcode op, LIns* c, GuardRecord *gr) - { - debug_only( if (LIR_x == op || LIR_xbarrier == op) NanoAssert(!c); ) - return ins2(op, c, (LIns*)gr); - } - - LIns* LirBufWriter::insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord *gr) - { - return ins3(op, a, b, (LIns*)gr); - } - - LIns* LirBufWriter::insBranch(LOpcode op, LIns* condition, LIns* toLabel) - { - NanoAssert((op == LIR_j && !condition) || - ((op == LIR_jf || op == LIR_jt) && condition)); - return ins2(op, condition, toLabel); - } - - LIns* LirBufWriter::insBranchJov(LOpcode op, LIns* a, LIns* b, LIns* toLabel) - { - return ins3(op, a, b, toLabel); - } - - LIns* LirBufWriter::insJtbl(LIns* index, uint32_t size) - { - LInsJtbl* insJtbl = (LInsJtbl*) _buf->makeRoom(sizeof(LInsJtbl)); - LIns** table = new (_buf->_allocator) LIns*[size]; - LIns* ins = insJtbl->getLIns(); - VMPI_memset(table, 0, size * sizeof(LIns*)); - ins->initLInsJtbl(index, size, table); - return ins; - } - - LIns* LirBufWriter::insAlloc(int32_t size) - { - size = (size+3)>>2; // # of required 32bit words - LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); - LIns* ins = insI->getLIns(); - ins->initLInsI(LIR_allocp, size); - return ins; - } - - LIns* LirBufWriter::insParam(int32_t arg, int32_t kind) - { - LInsP* insP = (LInsP*)_buf->makeRoom(sizeof(LInsP)); - LIns* ins = insP->getLIns(); - ins->initLInsP(arg, kind); - if (kind) { - NanoAssert(arg < NumSavedRegs); - _buf->savedRegs[arg] = ins; - } - return ins; - } - - LIns* LirBufWriter::insImmI(int32_t imm) - { - LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); - LIns* ins = insI->getLIns(); - ins->initLInsI(LIR_immi, imm); - return ins; - } - -#ifdef NANOJIT_64BIT - LIns* LirBufWriter::insImmQ(uint64_t imm) - { - LInsQorD* insQorD = (LInsQorD*)_buf->makeRoom(sizeof(LInsQorD)); - LIns* ins = insQorD->getLIns(); - ins->initLInsQorD(LIR_immq, imm); - return ins; - } -#endif - - LIns* LirBufWriter::insComment(const char* str) - { - // Allocate space for and copy the string. We use the same allocator - // as the normal LIR buffers so it has the same lifetime. - char* str2 = (char*)_buf->_allocator.alloc(VMPI_strlen(str) + 1); - VMPI_strcpy(str2, str); - return ins1(LIR_comment, (LIns*)str); - } - - LIns* LirBufWriter::insSkip(LIns* skipTo) - { - LInsSk* insSk = (LInsSk*)_buf->makeRoom(sizeof(LInsSk)); - LIns* ins = insSk->getLIns(); - ins->initLInsSk(skipTo); - return ins; - } - - LIns* LirBufWriter::insImmD(double d) - { - LInsQorD* insQorD = (LInsQorD*)_buf->makeRoom(sizeof(LInsQorD)); - LIns* ins = insQorD->getLIns(); - union { - double d; - uint64_t q; - } u; - u.d = d; - ins->initLInsQorD(LIR_immd, u.q); - return ins; - } - - LOpcode arithOpcodeD2I(LOpcode op) - { - switch (op) { - case LIR_negd: return LIR_negi; - case LIR_addd: return LIR_addi; - case LIR_subd: return LIR_subi; - case LIR_muld: return LIR_muli; - default: NanoAssert(0); return LIR_skip; - } - } - -#ifdef NANOJIT_64BIT - LOpcode cmpOpcodeI2Q(LOpcode op) - { - switch (op) { - case LIR_eqi: return LIR_eqq; - case LIR_lti: return LIR_ltq; - case LIR_gti: return LIR_gtq; - case LIR_lei: return LIR_leq; - case LIR_gei: return LIR_geq; - case LIR_ltui: return LIR_ltuq; - case LIR_gtui: return LIR_gtuq; - case LIR_leui: return LIR_leuq; - case LIR_geui: return LIR_geuq; - default: NanoAssert(0); return LIR_skip; - } - } -#endif - - LOpcode cmpOpcodeD2I(LOpcode op) - { - switch (op) { - case LIR_eqd: return LIR_eqi; - case LIR_ltd: return LIR_lti; - case LIR_gtd: return LIR_gti; - case LIR_led: return LIR_lei; - case LIR_ged: return LIR_gei; - default: NanoAssert(0); return LIR_skip; - } - } - - LOpcode cmpOpcodeD2UI(LOpcode op) - { - switch (op) { - case LIR_eqd: return LIR_eqi; - case LIR_ltd: return LIR_ltui; - case LIR_gtd: return LIR_gtui; - case LIR_led: return LIR_leui; - case LIR_ged: return LIR_geui; - default: NanoAssert(0); return LIR_skip; - } - } - - // This is never called, but that's ok because it contains only static - // assertions. - void LIns::staticSanityCheck() - { - // LIns must be word-sized. - NanoStaticAssert(sizeof(LIns) == 1*sizeof(void*)); - - // LInsXYZ have expected sizes too. - NanoStaticAssert(sizeof(LInsOp0) == 1*sizeof(void*)); - NanoStaticAssert(sizeof(LInsOp1) == 2*sizeof(void*)); - NanoStaticAssert(sizeof(LInsOp2) == 3*sizeof(void*)); - NanoStaticAssert(sizeof(LInsOp3) == 4*sizeof(void*)); - NanoStaticAssert(sizeof(LInsLd) == 3*sizeof(void*)); - NanoStaticAssert(sizeof(LInsSt) == 4*sizeof(void*)); - NanoStaticAssert(sizeof(LInsSk) == 2*sizeof(void*)); - NanoStaticAssert(sizeof(LInsC) == 3*sizeof(void*)); - NanoStaticAssert(sizeof(LInsP) == 2*sizeof(void*)); - NanoStaticAssert(sizeof(LInsI) == 2*sizeof(void*)); - #if defined NANOJIT_64BIT - NanoStaticAssert(sizeof(LInsQorD) == 2*sizeof(void*)); - #else - NanoStaticAssert(sizeof(LInsQorD) == 3*sizeof(void*)); - #endif - NanoStaticAssert(sizeof(LInsJtbl) == 4*sizeof(void*)); - - // oprnd_1 must be in the same position in LIns{Op1,Op2,Op3,Ld,St,Jtbl} - // because oprnd1() is used for all of them. - #define OP1OFFSET (offsetof(LInsOp1, ins) - offsetof(LInsOp1, oprnd_1)) - NanoStaticAssert( OP1OFFSET == (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_1)) ); - NanoStaticAssert( OP1OFFSET == (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_1)) ); - NanoStaticAssert( OP1OFFSET == (offsetof(LInsLd, ins) - offsetof(LInsLd, oprnd_1)) ); - NanoStaticAssert( OP1OFFSET == (offsetof(LInsSt, ins) - offsetof(LInsSt, oprnd_1)) ); - NanoStaticAssert( OP1OFFSET == (offsetof(LInsJtbl, ins) - offsetof(LInsJtbl, oprnd_1)) ); - - // oprnd_2 must be in the same position in LIns{Op2,Op3,St} - // because oprnd2() is used for all of them. - #define OP2OFFSET (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_2)) - NanoStaticAssert( OP2OFFSET == (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_2)) ); - NanoStaticAssert( OP2OFFSET == (offsetof(LInsSt, ins) - offsetof(LInsSt, oprnd_2)) ); - } - - void LIns::overwriteWithSkip(LIns* skipTo) - { - // Ensure the instruction is at least as big as a LIR_skip. - NanoAssert(insSizes[opcode()] >= insSizes[LIR_skip]); - initLInsSk(skipTo); - } - - bool insIsS16(LIns* i) - { - if (i->isImmI()) { - int c = i->immI(); - return isS16(c); - } - if (i->isCmov()) { - return insIsS16(i->oprnd2()) && insIsS16(i->oprnd3()); - } - if (i->isCmp()) - return true; - // many other possibilities too. - return false; - } - - LIns* ExprFilter::ins1(LOpcode v, LIns* oprnd) - { - switch (v) { -#ifdef NANOJIT_64BIT - case LIR_q2i: - if (oprnd->isImmQ()) - return insImmI(oprnd->immQlo()); - break; - case LIR_i2q: - if (oprnd->isImmI()) - return insImmQ(int64_t(int32_t(oprnd->immI()))); - break; - case LIR_ui2uq: - if (oprnd->isImmI()) - return insImmQ(uint64_t(uint32_t(oprnd->immI()))); - break; - case LIR_dasq: - if (oprnd->isop(LIR_qasd)) - return oprnd->oprnd1(); - break; - case LIR_qasd: - if (oprnd->isop(LIR_dasq)) - return oprnd->oprnd1(); - break; -#endif -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_dlo2i: - if (oprnd->isImmD()) - return insImmI(oprnd->immDlo()); - if (oprnd->isop(LIR_ii2d)) - return oprnd->oprnd1(); - break; - case LIR_dhi2i: - if (oprnd->isImmD()) - return insImmI(oprnd->immDhi()); - if (oprnd->isop(LIR_ii2d)) - return oprnd->oprnd2(); - break; -#endif - case LIR_noti: - if (oprnd->isImmI()) - return insImmI(~oprnd->immI()); - involution: - if (v == oprnd->opcode()) - return oprnd->oprnd1(); - break; - case LIR_negi: - if (oprnd->isImmI()) - return insImmI(-oprnd->immI()); - if (oprnd->isop(LIR_subi)) // -(a-b) = b-a - return out->ins2(LIR_subi, oprnd->oprnd2(), oprnd->oprnd1()); - goto involution; - case LIR_negd: - if (oprnd->isImmD()) - return insImmD(-oprnd->immD()); - if (oprnd->isop(LIR_subd)) - return out->ins2(LIR_subd, oprnd->oprnd2(), oprnd->oprnd1()); - goto involution; - case LIR_i2d: - if (oprnd->isImmI()) - return insImmD(oprnd->immI()); - // Nb: i2d(d2i(x)) != x - break; - case LIR_d2i: - if (oprnd->isImmD()) - return insImmI(int32_t(oprnd->immD())); - if (oprnd->isop(LIR_i2d)) - return oprnd->oprnd1(); - break; - case LIR_ui2d: - if (oprnd->isImmI()) - return insImmD(uint32_t(oprnd->immI())); - break; - default: - ; - } - - return out->ins1(v, oprnd); - } - - // This is an ugly workaround for an apparent compiler - // bug; in VC2008, compiling with optimization on - // will produce spurious errors if this code is inlined - // into ExprFilter::ins2(). See https://bugzilla.mozilla.org/show_bug.cgi?id=538504 - inline double do_join(int32_t c1, int32_t c2) - { - union { - double d; - uint64_t u64; - } u; - u.u64 = uint32_t(c1) | uint64_t(c2)<<32; - return u.d; - } - - LIns* ExprFilter::ins2(LOpcode v, LIns* oprnd1, LIns* oprnd2) - { - NanoAssert(oprnd1 && oprnd2); - - //------------------------------------------------------------------- - // Folding where the two operands are equal - //------------------------------------------------------------------- - if (oprnd1 == oprnd2) { - // The operands are equal. - switch (v) { - case LIR_xori: - case LIR_subi: - case LIR_ltui: - case LIR_gtui: - case LIR_gti: - case LIR_lti: - return insImmI(0); - - case LIR_ori: - case LIR_andi: - return oprnd1; - - case LIR_lei: - case LIR_leui: - case LIR_gei: - case LIR_geui: - return insImmI(1); // (x <= x) == 1; (x >= x) == 1 - - default: - break; - } - } - - //------------------------------------------------------------------- - // Folding where both operands are immediates, grouped by type - //------------------------------------------------------------------- - if (oprnd1->isImmI() && oprnd2->isImmI()) { - // The operands are both int immediates. - int32_t c1 = oprnd1->immI(); - int32_t c2 = oprnd2->immI(); - double d; - int32_t r; - - switch (v) { -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_ii2d: return insImmD(do_join(c1, c2)); -#endif - case LIR_eqi: return insImmI(c1 == c2); - case LIR_lti: return insImmI(c1 < c2); - case LIR_gti: return insImmI(c1 > c2); - case LIR_lei: return insImmI(c1 <= c2); - case LIR_gei: return insImmI(c1 >= c2); - case LIR_ltui: return insImmI(uint32_t(c1) < uint32_t(c2)); - case LIR_gtui: return insImmI(uint32_t(c1) > uint32_t(c2)); - case LIR_leui: return insImmI(uint32_t(c1) <= uint32_t(c2)); - case LIR_geui: return insImmI(uint32_t(c1) >= uint32_t(c2)); - - case LIR_lshi: return insImmI(c1 << (c2 & 0x1f)); - case LIR_rshi: return insImmI(c1 >> (c2 & 0x1f)); - case LIR_rshui: return insImmI(uint32_t(c1) >> (c2 & 0x1f)); - - case LIR_ori: return insImmI(c1 | c2); - case LIR_andi: return insImmI(c1 & c2); - case LIR_xori: return insImmI(c1 ^ c2); - - case LIR_addi: d = double(c1) + double(c2); goto fold; - case LIR_subi: d = double(c1) - double(c2); goto fold; - case LIR_muli: d = double(c1) * double(c2); goto fold; - fold: - // Make sure the constant expression doesn't overflow. This - // probably isn't necessary, because the C++ overflow - // behaviour is very likely to be the same as the machine code - // overflow behaviour, but we do it just to be safe. - r = int32_t(d); - if (r == d) - return insImmI(r); - break; - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - case LIR_divi: - case LIR_modi: - // We can't easily fold div and mod, since folding div makes it - // impossible to calculate the mod that refers to it. The - // frontend shouldn't emit div and mod with constant operands. - NanoAssert(0); -#endif - default: - break; - } - -#ifdef NANOJIT_64BIT - } else if (oprnd1->isImmQ() && oprnd2->isImmQ()) { - // The operands are both quad immediates. - int64_t c1 = oprnd1->immQ(); - int64_t c2 = oprnd2->immQ(); - static const int64_t MIN_INT64 = int64_t(0x8000000000000000LL); - static const int64_t MAX_INT64 = int64_t(0x7FFFFFFFFFFFFFFFLL); - - switch (v) { - case LIR_eqq: return insImmI(c1 == c2); - case LIR_ltq: return insImmI(c1 < c2); - case LIR_gtq: return insImmI(c1 > c2); - case LIR_leq: return insImmI(c1 <= c2); - case LIR_geq: return insImmI(c1 >= c2); - case LIR_ltuq: return insImmI(uint64_t(c1) < uint64_t(c2)); - case LIR_gtuq: return insImmI(uint64_t(c1) > uint64_t(c2)); - case LIR_leuq: return insImmI(uint64_t(c1) <= uint64_t(c2)); - case LIR_geuq: return insImmI(uint64_t(c1) >= uint64_t(c2)); - - case LIR_orq: return insImmQ(c1 | c2); - case LIR_andq: return insImmQ(c1 & c2); - case LIR_xorq: return insImmQ(c1 ^ c2); - - // Nb: LIR_rshq, LIR_lshq and LIR_rshuq aren't here because their - // RHS is an int. They are below. - - case LIR_addq: - // Overflow is only possible if both values are positive or - // both negative. Just like the 32-bit case, this check - // probably isn't necessary, because the C++ overflow - // behaviour is very likely to be the same as the machine code - // overflow behaviour, but we do it just to be safe. - if (c1 > 0 && c2 > 0) { - // Overflows if: c1 + c2 > MAX_INT64 - // Re-express to avoid overflow in the check: c1 > MAX_INT64 - c2 - if (c1 > MAX_INT64 - c2) - break; // overflow - } else if (c1 < 0 && c2 < 0) { - // Overflows if: c1 + c2 < MIN_INT64 - // Re-express to avoid overflow in the check: c1 < MIN_INT64 - c2 - if (c1 < MIN_INT64 - c2) - break; // overflow - } - return insImmQ(c1 + c2); - - case LIR_subq: - // Overflow is only possible if one value is positive and one - // negative. - if (c1 > 0 && c2 < 0) { - // Overflows if: c1 - c2 > MAX_INT64 - // Re-express to avoid overflow in the check: c1 > MAX_INT64 + c2 - if (c1 > MAX_INT64 + c2) - break; // overflow - } else if (c1 < 0 && c2 > 0) { - // Overflows if: c1 - c2 < MIN_INT64 - // Re-express to avoid overflow in the check: c1 < MIN_INT64 + c2 - if (c1 < MIN_INT64 + c2) - break; // overflow - } - return insImmQ(c1 - c2); - - default: - break; - } - - } else if (oprnd1->isImmQ() && oprnd2->isImmI()) { - // The first operand is a quad immediate, the second is an int - // immediate. - int64_t c1 = oprnd1->immQ(); - int32_t c2 = oprnd2->immI(); - - switch (v) { - case LIR_lshq: return insImmQ(c1 << (c2 & 0x3f)); - case LIR_rshq: return insImmQ(c1 >> (c2 & 0x3f)); - case LIR_rshuq: return insImmQ(uint64_t(c1) >> (c2 & 0x3f)); - - default: break; - } -#endif // NANOJIT_64BIT - - } else if (oprnd1->isImmD() && oprnd2->isImmD()) { - // The operands are both double immediates. - double c1 = oprnd1->immD(); - double c2 = oprnd2->immD(); - switch (v) { - case LIR_eqd: return insImmI(c1 == c2); - case LIR_ltd: return insImmI(c1 < c2); - case LIR_gtd: return insImmI(c1 > c2); - case LIR_led: return insImmI(c1 <= c2); - case LIR_ged: return insImmI(c1 >= c2); - - case LIR_addd: return insImmD(c1 + c2); - case LIR_subd: return insImmD(c1 - c2); - case LIR_muld: return insImmD(c1 * c2); - case LIR_divd: return insImmD(c1 / c2); - - default: break; - } - } - - //------------------------------------------------------------------- - // If only one operand is an immediate, make sure it's on the RHS, if possible - //------------------------------------------------------------------- - if (oprnd1->isImmAny() && !oprnd2->isImmAny()) { - switch (v) { - case LIR_eqi: - CASE64(LIR_eqq:) - case LIR_eqd: - case LIR_addi: - CASE64(LIR_addq:) - case LIR_addd: - case LIR_muli: - case LIR_muld: - case LIR_andi: - CASE64(LIR_andq:) - case LIR_ori: - CASE64(LIR_orq:) - case LIR_xori: - CASE64(LIR_xorq:) { - // move immediate to RHS - LIns* t = oprnd2; - oprnd2 = oprnd1; - oprnd1 = t; - break; - } - default: - if (isCmpOpcode(v)) { - // move immediate to RHS, swap the operator - LIns *t = oprnd2; - oprnd2 = oprnd1; - oprnd1 = t; - v = invertCmpOpcode(v); - } - break; - } - } - - //------------------------------------------------------------------- - // Folding where the RHS is an immediate - //------------------------------------------------------------------- - if (oprnd2->isImmI()) { - // The second operand is an int immediate. - int c = oprnd2->immI(); - switch (v) { - case LIR_addi: - if (oprnd1->isop(LIR_addi) && oprnd1->oprnd2()->isImmI()) { - // add(add(x,c1),c2) => add(x,c1+c2) - c += oprnd1->oprnd2()->immI(); - oprnd2 = insImmI(c); - oprnd1 = oprnd1->oprnd1(); - } - break; - - case LIR_subi: - if (oprnd1->isop(LIR_addi) && oprnd1->oprnd2()->isImmI()) { - // sub(add(x,c1),c2) => add(x,c1-c2) - c = oprnd1->oprnd2()->immI() - c; - oprnd2 = insImmI(c); - oprnd1 = oprnd1->oprnd1(); - v = LIR_addi; - } - break; - - case LIR_rshi: - if (c == 16 && oprnd1->isop(LIR_lshi) && - oprnd1->oprnd2()->isImmI(16) && - insIsS16(oprnd1->oprnd1())) - { - // rsh(lhs(x,16),16) == x, if x is S16 - return oprnd1->oprnd1(); - } - break; - - default: - break; - } - - if (c == 0) { - switch (v) { - case LIR_addi: - case LIR_ori: - case LIR_xori: - case LIR_subi: - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - CASE64(LIR_lshq:) // These are here because their RHS is an int - CASE64(LIR_rshq:) - CASE64(LIR_rshuq:) - return oprnd1; - - case LIR_andi: - case LIR_muli: - case LIR_ltui: // unsigned < 0 -> always false - return oprnd2; - - case LIR_geui: // unsigned >= 0 -> always true - return insImmI(1); - - case LIR_eqi: - if (oprnd1->isop(LIR_ori) && - oprnd1->oprnd2()->isImmI() && - oprnd1->oprnd2()->immI() != 0) - { - // (x or c) != 0 if c != 0 - return insImmI(0); - } - - default: - break; - } - - } else if (c == -1) { - switch (v) { - case LIR_ori: return oprnd2; // x | -1 = -1 - case LIR_andi: return oprnd1; // x & -1 = x - case LIR_gtui: return insImmI(0); // u32 > 0xffffffff -> always false - case LIR_leui: return insImmI(1); // u32 <= 0xffffffff -> always true - default: break; - } - - } else if (c == 1) { - if (oprnd1->isCmp()) { - switch (v) { - case LIR_ori: return oprnd2; // 0or1 | 1 = 1 (and oprnd2 == 1) - case LIR_andi: return oprnd1; // 0or1 & 1 = 0or1 - case LIR_gtui: return insImmI(0); // 0or1 > 1 -> always false - default: break; - } - } else if (v == LIR_muli) { - return oprnd1; // x * 1 = x - } - } - -#ifdef NANOJIT_64BIT - } else if (oprnd2->isImmQ()) { - // The second operand is a quad immediate. - int64_t c = oprnd2->immQ(); - if (c == 0) { - switch (v) { - case LIR_addq: - case LIR_orq: - case LIR_xorq: - case LIR_subq: - return oprnd1; - - case LIR_andq: - return oprnd2; - - case LIR_ltuq: // unsigned < 0 -> always false - return insImmI(0); - - case LIR_geuq: // unsigned >= 0 -> always true - return insImmI(1); - - default: - break; - } - - } else if (c == -1) { - switch (v) { - case LIR_orq: return oprnd2; // x | -1 = -1 - case LIR_andq: return oprnd1; // x & -1 = x - case LIR_gtuq: return insImmI(0); // u64 > 0xffffffffffffffff -> always false - case LIR_leuq: return insImmI(1); // u64 <= 0xffffffffffffffff -> always true - default: break; - } - - } else if (c == 1) { - if (oprnd1->isCmp()) { - switch (v) { - case LIR_orq: return oprnd2; // 0or1 | 1 = 1 (and oprnd2 == 1) - case LIR_andq: return oprnd1; // 0or1 & 1 = 0or1 - case LIR_gtuq: return insImmI(0); // 0or1 > 1 -> always false - default: break; - } - } - } -#endif // NANOJIT_64BIT - } - -#if NJ_SOFTFLOAT_SUPPORTED - //------------------------------------------------------------------- - // SoftFloat-specific folding - //------------------------------------------------------------------- - LIns* ins; - if (v == LIR_ii2d && oprnd1->isop(LIR_dlo2i) && oprnd2->isop(LIR_dhi2i) && - (ins = oprnd1->oprnd1()) == oprnd2->oprnd1()) - { - // qjoin(qlo(x),qhi(x)) == x - return ins; - } -#endif - - //------------------------------------------------------------------- - // No folding possible - //------------------------------------------------------------------- - return out->ins2(v, oprnd1, oprnd2); - } - - LIns* ExprFilter::ins3(LOpcode v, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3) - { - NanoAssert(oprnd1 && oprnd2 && oprnd3); - NanoAssert(isCmovOpcode(v)); - if (oprnd2 == oprnd3) { - // c ? a : a => a - return oprnd2; - } - if (oprnd1->isImmI()) { - // immediate ? x : y => return x or y depending on immediate - return oprnd1->immI() ? oprnd2 : oprnd3; - } - if (oprnd1->isop(LIR_eqi) && - ((oprnd1->oprnd2() == oprnd2 && oprnd1->oprnd1() == oprnd3) || - (oprnd1->oprnd1() == oprnd2 && oprnd1->oprnd2() == oprnd3))) { - // (y == x) ? x : y => y - // (x == y) ? x : y => y - return oprnd3; - } - - return out->ins3(v, oprnd1, oprnd2, oprnd3); - } - - LIns* ExprFilter::insGuard(LOpcode v, LIns* c, GuardRecord *gr) - { - if (v == LIR_xt || v == LIR_xf) { - if (c->isImmI()) { - if ((v == LIR_xt && !c->immI()) || (v == LIR_xf && c->immI())) { - return 0; // no guard needed - } else { -#ifdef JS_TRACER - // We're emitting a guard that will always fail. Any code - // emitted after this guard is dead code. But it won't be - // optimized away, and it could indicate a performance - // problem or other bug, so assert in debug builds. - NanoAssertMsg(0, "Constantly false guard detected"); -#endif - return out->insGuard(LIR_x, NULL, gr); - } - } else { - while (c->isop(LIR_eqi) && c->oprnd1()->isCmp() && c->oprnd2()->isImmI(0)) { - // xt(eq(cmp,0)) => xf(cmp) or xf(eq(cmp,0)) => xt(cmp) - v = invertCondGuardOpcode(v); - c = c->oprnd1(); - } - } - } - return out->insGuard(v, c, gr); - } - - // Simplify operator if possible. Always return NULL if overflow is possible. - - LIns* ExprFilter::simplifyOverflowArith(LOpcode op, LIns** opnd1, LIns** opnd2) - { - LIns* oprnd1 = *opnd1; - LIns* oprnd2 = *opnd2; - - if (oprnd1->isImmI() && oprnd2->isImmI()) { - int32_t c1 = oprnd1->immI(); - int32_t c2 = oprnd2->immI(); - double d = 0.0; - - // The code below attempts to perform the operation while - // detecting overflow. For multiplication, we may unnecessarily - // infer a possible overflow due to the insufficient integer - // range of the double type. - - switch (op) { - case LIR_addjovi: - case LIR_addxovi: d = double(c1) + double(c2); break; - case LIR_subjovi: - case LIR_subxovi: d = double(c1) - double(c2); break; - case LIR_muljovi: - case LIR_mulxovi: d = double(c1) * double(c2); break; - default: NanoAssert(0); break; - } - int32_t r = int32_t(d); - if (r == d) - return insImmI(r); - - } else if (oprnd1->isImmI() && !oprnd2->isImmI()) { - switch (op) { - case LIR_addjovi: - case LIR_addxovi: - case LIR_muljovi: - case LIR_mulxovi: { - // swap operands, moving immediate to RHS - LIns* t = oprnd2; - oprnd2 = oprnd1; - oprnd1 = t; - // swap actual arguments in caller as well - *opnd1 = oprnd1; - *opnd2 = oprnd2; - break; - } - case LIR_subjovi: - case LIR_subxovi: - break; - default: - NanoAssert(0); - } - } - - if (oprnd2->isImmI()) { - int c = oprnd2->immI(); - if (c == 0) { - switch (op) { - case LIR_addjovi: - case LIR_addxovi: - case LIR_subjovi: - case LIR_subxovi: - return oprnd1; - case LIR_muljovi: - case LIR_mulxovi: - return oprnd2; - default: - ; - } - } else if (c == 1 && (op == LIR_muljovi || op == LIR_mulxovi)) { - return oprnd1; - } - } - - return NULL; - } - - LIns* ExprFilter::insGuardXov(LOpcode op, LIns* oprnd1, LIns* oprnd2, GuardRecord *gr) - { - LIns* simplified = simplifyOverflowArith(op, &oprnd1, &oprnd2); - if (simplified) - return simplified; - - return out->insGuardXov(op, oprnd1, oprnd2, gr); - } - - LIns* ExprFilter::insBranch(LOpcode v, LIns *c, LIns *t) - { - if (v == LIR_jt || v == LIR_jf) { - if (c->isImmI()) { - if ((v == LIR_jt && !c->immI()) || (v == LIR_jf && c->immI())) { - return 0; // no jump needed - } else { -#ifdef JS_TRACER - // We're emitting a branch that will always be taken. This may - // result in dead code that will not be optimized away, and - // could indicate a performance problem or other bug, so assert - // in debug builds. - NanoAssertMsg(0, "Constantly taken branch detected"); -#endif - return out->insBranch(LIR_j, NULL, t); - } - } else { - while (c->isop(LIR_eqi) && c->oprnd1()->isCmp() && c->oprnd2()->isImmI(0)) { - // jt(eq(cmp,0)) => jf(cmp) or jf(eq(cmp,0)) => jt(cmp) - v = invertCondJmpOpcode(v); - c = c->oprnd1(); - } - } - } - return out->insBranch(v, c, t); - } - - LIns* ExprFilter::insBranchJov(LOpcode op, LIns* oprnd1, LIns* oprnd2, LIns* target) - { - LIns* simplified = simplifyOverflowArith(op, &oprnd1, &oprnd2); - if (simplified) - return simplified; - - return out->insBranchJov(op, oprnd1, oprnd2, target); - } - - LIns* ExprFilter::insLoad(LOpcode op, LIns* base, int32_t off, AccSet accSet, LoadQual loadQual) { - if (base->isImmP() && !isS8(off)) { - // if the effective address is constant, then transform: - // ld const[bigconst] => ld (const+bigconst)[0] - // note: we don't do this optimization for <8bit field offsets, - // under the assumption that we're more likely to CSE-match the - // constant base address if we dont const-fold small offsets. - uintptr_t p = (uintptr_t)base->immP() + off; - return out->insLoad(op, insImmP((void*)p), 0, accSet, loadQual); - } - return out->insLoad(op, base, off, accSet, loadQual); - } - - LIns* LirWriter::insStore(LIns* value, LIns* base, int32_t d, AccSet accSet) - { - // Determine which kind of store should be used for 'value' based on - // its type. - LOpcode op = LOpcode(0); - switch (value->retType()) { - case LTy_I: op = LIR_sti; break; -#ifdef NANOJIT_64BIT - case LTy_Q: op = LIR_stq; break; -#endif - case LTy_D: op = LIR_std; break; - case LTy_V: NanoAssert(0); break; - default: NanoAssert(0); break; - } - return insStore(op, value, base, d, accSet); - } - - LIns* LirWriter::insChoose(LIns* cond, LIns* iftrue, LIns* iffalse, bool use_cmov) - { - // 'cond' must be a conditional, unless it has been optimized to 0 or - // 1. In that case make it an ==0 test and flip the branches. It'll - // get constant-folded by ExprFilter subsequently. - if (!cond->isCmp()) { - NanoAssert(cond->isImmI()); - cond = insEqI_0(cond); - LIns* tmp = iftrue; - iftrue = iffalse; - iffalse = tmp; - } - - if (use_cmov) { - LOpcode op = LIR_cmovi; - if (iftrue->isI() && iffalse->isI()) { - op = LIR_cmovi; -#ifdef NANOJIT_64BIT - } else if (iftrue->isQ() && iffalse->isQ()) { - op = LIR_cmovq; -#endif - } else if (iftrue->isD() && iffalse->isD()) { - op = LIR_cmovd; - } else { - NanoAssert(0); // type error - } - return ins3(op, cond, iftrue, iffalse); - } - - LIns* ncond = ins1(LIR_negi, cond); // cond ? -1 : 0 - return ins2(LIR_ori, - ins2(LIR_andi, iftrue, ncond), - ins2(LIR_andi, iffalse, ins1(LIR_noti, ncond))); - } - - LIns* LirBufWriter::insCall(const CallInfo *ci, LIns* args[]) - { - LOpcode op = getCallOpcode(ci); -#if NJ_SOFTFLOAT_SUPPORTED - // SoftFloat: convert LIR_calld to LIR_calli. - if (_config.soft_float && op == LIR_calld) - op = LIR_calli; -#endif - - int32_t argc = ci->count_args(); - NanoAssert(argc <= (int)MAXARGS); - - // Allocate space for and copy the arguments. We use the same - // allocator as the normal LIR buffers so it has the same lifetime. - // Nb: this must be kept in sync with arg(). - LIns** args2 = (LIns**)_buf->_allocator.alloc(argc * sizeof(LIns*)); - memcpy(args2, args, argc * sizeof(LIns*)); - - // Allocate and write the call instruction. - LInsC* insC = (LInsC*)_buf->makeRoom(sizeof(LInsC)); - LIns* ins = insC->getLIns(); - ins->initLInsC(op, args2, ci); - return ins; - } - - using namespace avmplus; - - StackFilter::StackFilter(LirFilter *in, Allocator& alloc, LIns* sp) - : LirFilter(in), sp(sp), stk(alloc), top(0) - {} - - // If we see a sequence like this: - // - // sti sp[0] - // ... - // sti sp[0] - // - // where '...' contains no guards, we can remove the first store. Also, - // because stack entries are eight bytes each (we check this), if we have - // this: - // - // stfi sp[0] - // ... - // sti sp[0] - // - // we can again remove the first store -- even though the second store - // doesn't clobber the high four bytes -- because we know the entire value - // stored by the first store is dead. - // - LIns* StackFilter::read() - { - for (;;) { - LIns* ins = in->read(); - - if (ins->isStore()) { - LIns* base = ins->oprnd2(); - if (base == sp) { - // 'disp' must be eight-aligned because each stack entry is 8 bytes. - NanoAssert((ins->disp() & 0x7) == 0); - - int d = ins->disp() >> 3; - if (d >= top) { - continue; - } else { - d = top - d; - if (stk.get(d)) { - continue; - } else { - stk.set(d); - } - } - } - } - /* - * NB: If there is a backward branch other than the loop-restart branch, this is - * going to be wrong. Unfortunately there doesn't seem to be an easy way to detect - * such branches. Just do not create any. - * - * The isLive() call is valid because liveness will have been - * computed by Assembler::gen() for every instruction following - * this guard. - */ - else if (ins->isGuard() && ins->isLive()) { - stk.reset(); - top = getTop(ins); - top >>= 3; - } - - return ins; - } - } - -#ifdef NJ_VERBOSE - class RetiredEntry - { - public: - Seq* live; - LIns* i; - RetiredEntry(): live(NULL), i(NULL) {} - }; - - class LiveTable - { - Allocator& alloc; - public: - HashMap live; - SeqBuilder retired; - int retiredCount; - int maxlive; - LiveTable(Allocator& alloc) - : alloc(alloc) - , live(alloc) - , retired(alloc) - , retiredCount(0) - , maxlive(0) - { } - - void add(LIns* ins, LIns* use) { - if (!ins->isImmAny() && !live.containsKey(ins)) { - NanoAssert(size_t(ins->opcode()) < sizeof(lirNames) / sizeof(lirNames[0])); - live.put(ins,use); - } - } - - void retire(LIns* i) { - RetiredEntry *e = new (alloc) RetiredEntry(); - e->i = i; - SeqBuilder livelist(alloc); - HashMap::Iter iter(live); - int live_count = 0; - while (iter.next()) { - LIns* ins = iter.key(); - if (!ins->isV()) { - live_count++; - livelist.insert(ins); - } - } - e->live = livelist.get(); - if (live_count > maxlive) - maxlive = live_count; - - live.remove(i); - retired.insert(e); - retiredCount++; - } - - bool contains(LIns* i) { - return live.containsKey(i); - } - }; - - /* - * traverse the LIR buffer and discover which instructions are live - * by starting from instructions with side effects (stores, calls, branches) - * and marking instructions used by them. Works bottom-up, in one pass. - * if showLiveRefs == true, also print the set of live expressions next to - * each instruction - */ - void live(LirFilter* in, Allocator& alloc, Fragment *frag, LogControl *logc) - { - // traverse backwards to find live exprs and a few other stats. - - LiveTable live(alloc); - uint32_t exits = 0; - int total = 0; - if (frag->lirbuf->state) - live.add(frag->lirbuf->state, 0); - for (LIns* ins = in->read(); !ins->isop(LIR_start); ins = in->read()) - { - total++; - - // First handle instructions that are always live (ie. those that - // don't require being marked as live), eg. those with - // side-effects. We ignore LIR_paramp. - if (ins->isLive() && !ins->isop(LIR_paramp)) - { - live.add(ins, 0); - if (ins->isGuard()) - exits++; - } - - // now propagate liveness - if (live.contains(ins)) - { - live.retire(ins); - - switch (ins->opcode()) { - case LIR_skip: - NanoAssertMsg(0, "Shouldn't see LIR_skip"); - break; - - case LIR_start: - case LIR_regfence: - case LIR_paramp: - case LIR_x: - case LIR_xbarrier: - case LIR_j: - case LIR_label: - case LIR_immi: - CASE64(LIR_immq:) - case LIR_immd: - case LIR_allocp: - case LIR_comment: - // No operands, do nothing. - break; - - case LIR_ldi: - CASE64(LIR_ldq:) - case LIR_ldd: - case LIR_lduc2ui: - case LIR_ldus2ui: - case LIR_ldc2i: - case LIR_lds2i: - case LIR_ldf2d: - case LIR_reti: - CASE64(LIR_retq:) - case LIR_retd: - case LIR_livei: - CASE64(LIR_liveq:) - case LIR_lived: - case LIR_xt: - case LIR_xf: - case LIR_jt: - case LIR_jf: - case LIR_jtbl: - case LIR_negi: - case LIR_negd: - case LIR_noti: - CASESF(LIR_dlo2i:) - CASESF(LIR_dhi2i:) - CASESF(LIR_hcalli:) - CASE64(LIR_i2q:) - CASE64(LIR_ui2uq:) - case LIR_i2d: - case LIR_ui2d: - CASE64(LIR_q2i:) - case LIR_d2i: - CASE64(LIR_dasq:) - CASE64(LIR_qasd:) - CASE86(LIR_modi:) - live.add(ins->oprnd1(), 0); - break; - - case LIR_sti: - CASE64(LIR_stq:) - case LIR_std: - case LIR_sti2c: - case LIR_sti2s: - case LIR_std2f: - case LIR_eqi: - case LIR_lti: - case LIR_gti: - case LIR_lei: - case LIR_gei: - case LIR_ltui: - case LIR_gtui: - case LIR_leui: - case LIR_geui: - case LIR_eqd: - case LIR_ltd: - case LIR_gtd: - case LIR_led: - case LIR_ged: - CASE64(LIR_eqq:) - CASE64(LIR_ltq:) - CASE64(LIR_gtq:) - CASE64(LIR_leq:) - CASE64(LIR_geq:) - CASE64(LIR_ltuq:) - CASE64(LIR_gtuq:) - CASE64(LIR_leuq:) - CASE64(LIR_geuq:) - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - CASE64(LIR_lshq:) - CASE64(LIR_rshq:) - CASE64(LIR_rshuq:) - case LIR_addi: - case LIR_subi: - case LIR_muli: - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - CASE86(LIR_divi:) - case LIR_addd: - case LIR_subd: - case LIR_muld: - case LIR_divd: - CASE64(LIR_addq:) - CASE64(LIR_subq:) - CASE64(LIR_addjovq:) - CASE64(LIR_subjovq:) - case LIR_andi: - case LIR_ori: - case LIR_xori: - CASE64(LIR_andq:) - CASE64(LIR_orq:) - CASE64(LIR_xorq:) - CASESF(LIR_ii2d:) - case LIR_file: - case LIR_line: - live.add(ins->oprnd1(), 0); - live.add(ins->oprnd2(), 0); - break; - - case LIR_cmovi: - CASE64(LIR_cmovq:) - case LIR_cmovd: - live.add(ins->oprnd1(), 0); - live.add(ins->oprnd2(), 0); - live.add(ins->oprnd3(), 0); - break; - - case LIR_callv: - case LIR_calli: - CASE64(LIR_callq:) - case LIR_calld: - for (int i = 0, argc = ins->argc(); i < argc; i++) - live.add(ins->arg(i), 0); - break; - - default: - NanoAssertMsgf(0, "unhandled opcode: %d", ins->opcode()); - break; - } - } - } - - logc->printf(" Live instruction count %d, total %u, max pressure %d\n", - live.retiredCount, total, live.maxlive); - if (exits > 0) - logc->printf(" Side exits %u\n", exits); - logc->printf(" Showing LIR instructions with live-after variables\n"); - logc->printf("\n"); - - // print live exprs, going forwards - LInsPrinter *printer = frag->lirbuf->printer; - bool newblock = true; - for (Seq* p = live.retired.get(); p != NULL; p = p->tail) { - RetiredEntry* e = p->head; - InsBuf ib; - RefBuf rb; - char livebuf[4000], *s=livebuf; - *s = 0; - if (!newblock && e->i->isop(LIR_label)) { - logc->printf("\n"); - } - newblock = false; - for (Seq* p = e->live; p != NULL; p = p->tail) { - VMPI_strcpy(s, printer->formatRef(&rb, p->head)); - s += VMPI_strlen(s); - *s++ = ' '; *s = 0; - NanoAssert(s < livebuf+sizeof(livebuf)); - } - /* If the LIR insn is pretty short, print it and its - live-after set on the same line. If not, put - live-after set on a new line, suitably indented. */ - const char* insn_text = printer->formatIns(&ib, e->i); - if (VMPI_strlen(insn_text) >= 30-2) { - logc->printf(" %-30s\n %-30s %s\n", insn_text, "", livebuf); - } else { - logc->printf(" %-30s %s\n", insn_text, livebuf); - } - - if (e->i->isGuard() || e->i->isBranch() || e->i->isRet()) { - logc->printf("\n"); - newblock = true; - } - } - } - - void LirNameMap::addNameWithSuffix(LIns* ins, const char *name, int suffix, - bool ignoreOneSuffix) { - NanoAssert(!names.containsKey(ins)); - const int N = 100; - char name2[N]; - if (suffix == 1 && ignoreOneSuffix) { - VMPI_snprintf(name2, N, "%s", name); // don't add '1' suffix - } else if (VMPI_isdigit(name[VMPI_strlen(name)-1])) { - VMPI_snprintf(name2, N, "%s_%d", name, suffix); // use '_' to avoid confusion - } else { - VMPI_snprintf(name2, N, "%s%d", name, suffix); // normal case - } - - char *copy = new (alloc) char[VMPI_strlen(name2)+1]; - VMPI_strcpy(copy, name2); - Entry *e = new (alloc) Entry(copy); - names.put(ins, e); - } - - void LirNameMap::addName(LIns* ins, const char* name) { - // The lookup may succeed, ie. we may already have a name for this - // instruction. This can happen because of CSE. Eg. if we have this: - // - // ins = addName("foo", insImmI(0)) - // - // that assigns the name "foo1" to 'ins'. If we later do this: - // - // ins2 = addName("foo", insImmI(0)) - // - // then CSE will cause 'ins' and 'ins2' to be equal. So 'ins2' - // already has a name ("foo1") and there's no need to generate a new - // name "foo2". - // - if (!names.containsKey(ins)) { - Str* str = new (alloc) Str(alloc, name); - int suffix = namecounts.add(*str); - addNameWithSuffix(ins, name, suffix, /*ignoreOneSuffix*/true); - } - } - - const char* LirNameMap::createName(LIns* ins) { - if (ins->isCall()) { -#if NJ_SOFTFLOAT_SUPPORTED - if (ins->isop(LIR_hcalli)) { - ins = ins->oprnd1(); // we've presumably seen the other half already - } else -#endif - { - if (!names.containsKey(ins)) - addNameWithSuffix(ins, ins->callInfo()->_name, funccounts.add(ins->callInfo()), - /*ignoreOneSuffix*/false); - } - } else { - if (!names.containsKey(ins)) - addNameWithSuffix(ins, lirNames[ins->opcode()], lircounts.add(ins->opcode()), - /*ignoreOneSuffix*/false); - - } - return names.get(ins)->name; - } - - const char* LirNameMap::lookupName(LIns* ins) - { - Entry* e = names.get(ins); - return e ? e->name : NULL; - } - - char* LInsPrinter::formatAccSet(RefBuf* buf, AccSet accSet) { - if (accSet == ACCSET_NONE) { - VMPI_sprintf(buf->buf, ".none"); - } else if (accSet == ACCSET_ALL) { - VMPI_sprintf(buf->buf, ".all"); - } else { - char* b = buf->buf; - b[0] = 0; - // The AccSet may contain bits set for regions not used by the - // embedding, if any have been specified via - // (ACCSET_ALL & ~ACCSET_XYZ). So only print those that are - // relevant. - for (int i = 0; i < EMB_NUM_USED_ACCS; i++) { - if (accSet & (1 << i)) { - VMPI_strcat(b, "."); - VMPI_strcat(b, accNames[i]); - accSet &= ~(1 << i); - } - } - NanoAssert(VMPI_strlen(b) < buf->len); - } - return buf->buf; - } - - char* LInsPrinter::formatImmI(RefBuf* buf, int32_t c) { - if (-10000 < c && c < 10000) { - VMPI_snprintf(buf->buf, buf->len, "%d", c); - } else { -#if !defined NANOJIT_64BIT - formatAddr(buf, (void*)c); -#else - VMPI_snprintf(buf->buf, buf->len, "0x%x", (unsigned int)c); -#endif - } - return buf->buf; - } - -#if defined NANOJIT_64BIT - char* LInsPrinter::formatImmQ(RefBuf* buf, uint64_t c) { - if (-10000 < (int64_t)c && c < 10000) { - VMPI_snprintf(buf->buf, buf->len, "%dLL", (int)c); - } else { - formatAddr(buf, (void*)c); - } - return buf->buf; - } -#endif - - char* LInsPrinter::formatImmD(RefBuf* buf, double c) { - VMPI_snprintf(buf->buf, buf->len, "%g", c); - return buf->buf; - } - - char* LInsPrinter::formatAddr(RefBuf* buf, void* p) - { - char* name; - int32_t offset; - addrNameMap->lookupAddr(p, name, offset); - - if (name) { - if (offset != 0) { - VMPI_snprintf(buf->buf, buf->len, "%p %s+%d", p, name, offset); - } else { - VMPI_snprintf(buf->buf, buf->len, "%p %s", p, name); - } - } else { - VMPI_snprintf(buf->buf, buf->len, "%p", p); - } - - return buf->buf; - } - - char* LInsPrinter::formatRef(RefBuf* buf, LIns *ref, bool showImmValue) - { - // Give 'ref' a name if it doesn't have one. - const char* name = lirNameMap->lookupName(ref); - if (!name) { - name = lirNameMap->createName(ref); - } - - // Put it in the buffer. If it's an immediate, show the value if - // showImmValue==true. (This facility allows us to print immediate - // values when they're used but not when they're def'd, ie. we don't - // want "immi1/*1*/ = immi 1".) - RefBuf buf2; - if (ref->isImmI() && showImmValue) { - VMPI_snprintf(buf->buf, buf->len, "%s/*%s*/", name, formatImmI(&buf2, ref->immI())); - } -#ifdef NANOJIT_64BIT - else if (ref->isImmQ() && showImmValue) { - VMPI_snprintf(buf->buf, buf->len, "%s/*%s*/", name, formatImmQ(&buf2, ref->immQ())); - } -#endif - else if (ref->isImmD() && showImmValue) { - VMPI_snprintf(buf->buf, buf->len, "%s/*%s*/", name, formatImmD(&buf2, ref->immD())); - } - else { - VMPI_snprintf(buf->buf, buf->len, "%s", name); - } - - return buf->buf; - } - - char* LInsPrinter::formatIns(InsBuf* buf, LIns* i) - { - char *s = buf->buf; - size_t n = buf->len; - RefBuf b1, b2, b3, b4; - LOpcode op = i->opcode(); - switch (op) - { - case LIR_immi: - VMPI_snprintf(s, n, "%s = %s %s", formatRef(&b1, i, /*showImmValue*/false), - lirNames[op], formatImmI(&b2, i->immI())); - break; - -#ifdef NANOJIT_64BIT - case LIR_immq: - VMPI_snprintf(s, n, "%s = %s %s", formatRef(&b1, i, /*showImmValue*/false), - lirNames[op], formatImmQ(&b2, i->immQ())); - break; -#endif - - case LIR_immd: - VMPI_snprintf(s, n, "%s = %s %s", formatRef(&b1, i, /*showImmValue*/false), - lirNames[op], formatImmD(&b2, i->immD())); - break; - - case LIR_allocp: - VMPI_snprintf(s, n, "%s = %s %d", formatRef(&b1, i), lirNames[op], i->size()); - break; - - case LIR_start: - case LIR_regfence: - VMPI_snprintf(s, n, "%s", lirNames[op]); - break; - - case LIR_callv: - case LIR_calli: - CASE64(LIR_callq:) - case LIR_calld: { - const CallInfo* call = i->callInfo(); - int32_t argc = i->argc(); - int32_t m = int32_t(n); // Windows doesn't have 'ssize_t' - if (call->isIndirect()) - m -= VMPI_snprintf(s, m, "%s = %s%s [%s] ( ", formatRef(&b1, i), lirNames[op], - formatAccSet(&b2, call->_storeAccSet), - formatRef(&b3, i->arg(--argc))); - else - m -= VMPI_snprintf(s, m, "%s = %s%s #%s ( ", formatRef(&b1, i), lirNames[op], - formatAccSet(&b2, call->_storeAccSet), call->_name); - if (m < 0) break; - for (int32_t j = argc - 1; j >= 0; j--) { - s += VMPI_strlen(s); - m -= VMPI_snprintf(s, m, "%s ",formatRef(&b2, i->arg(j))); - if (m < 0) break; - } - s += VMPI_strlen(s); - m -= VMPI_snprintf(s, m, ")"); - break; - } - - case LIR_jtbl: { - int32_t m = int32_t(n); // Windows doesn't have 'ssize_t' - m -= VMPI_snprintf(s, m, "%s %s [ ", lirNames[op], formatRef(&b1, i->oprnd1())); - if (m < 0) break; - for (uint32_t j = 0, sz = i->getTableSize(); j < sz; j++) { - LIns* target = i->getTarget(j); - s += VMPI_strlen(s); - m -= VMPI_snprintf(s, m, "%s ", target ? formatRef(&b2, target) : "unpatched"); - if (m < 0) break; - } - s += VMPI_strlen(s); - m -= VMPI_snprintf(s, m, "]"); - break; - } - - case LIR_paramp: { - uint32_t arg = i->paramArg(); - if (!i->paramKind()) { - if (arg < sizeof(Assembler::argRegs)/sizeof(Assembler::argRegs[0])) { - VMPI_snprintf(s, n, "%s = %s %d %s", formatRef(&b1, i), lirNames[op], - arg, gpn(Assembler::argRegs[arg])); - } else { - VMPI_snprintf(s, n, "%s = %s %d", formatRef(&b1, i), lirNames[op], arg); - } - } else { - VMPI_snprintf(s, n, "%s = %s %d %s", formatRef(&b1, i), lirNames[op], - arg, gpn(Assembler::savedRegs[arg])); - } - break; - } - - case LIR_label: - VMPI_snprintf(s, n, "%s:", formatRef(&b1, i)); - break; - - case LIR_jt: - case LIR_jf: - VMPI_snprintf(s, n, "%s %s -> %s", lirNames[op], formatRef(&b1, i->oprnd1()), - i->oprnd2() ? formatRef(&b2, i->oprnd2()) : "unpatched"); - break; - - case LIR_j: - VMPI_snprintf(s, n, "%s -> %s", lirNames[op], - i->oprnd2() ? formatRef(&b1, i->oprnd2()) : "unpatched"); - break; - - case LIR_livei: - case LIR_lived: - CASE64(LIR_liveq:) - case LIR_reti: - CASE64(LIR_retq:) - case LIR_retd: - VMPI_snprintf(s, n, "%s %s", lirNames[op], formatRef(&b1, i->oprnd1())); - break; - - CASESF(LIR_hcalli:) - case LIR_negi: - case LIR_negd: - case LIR_i2d: - case LIR_ui2d: - CASESF(LIR_dlo2i:) - CASESF(LIR_dhi2i:) - case LIR_noti: - CASE86(LIR_modi:) - CASE64(LIR_i2q:) - CASE64(LIR_ui2uq:) - CASE64(LIR_q2i:) - case LIR_d2i: - CASE64(LIR_dasq:) - CASE64(LIR_qasd:) - VMPI_snprintf(s, n, "%s = %s %s", formatRef(&b1, i), lirNames[op], - formatRef(&b2, i->oprnd1())); - break; - - case LIR_x: - case LIR_xt: - case LIR_xf: - case LIR_xbarrier: - formatGuard(buf, i); - break; - - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - formatGuardXov(buf, i); - break; - - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - CASE64(LIR_addjovq:) - CASE64(LIR_subjovq:) - VMPI_snprintf(s, n, "%s = %s %s, %s ; ovf -> %s", formatRef(&b1, i), lirNames[op], - formatRef(&b2, i->oprnd1()), - formatRef(&b3, i->oprnd2()), - i->oprnd3() ? formatRef(&b4, i->oprnd3()) : "unpatched"); - break; - - case LIR_addi: CASE64(LIR_addq:) - case LIR_subi: CASE64(LIR_subq:) - case LIR_muli: - CASE86(LIR_divi:) - case LIR_addd: - case LIR_subd: - case LIR_muld: - case LIR_divd: - case LIR_andi: CASE64(LIR_andq:) - case LIR_ori: CASE64(LIR_orq:) - case LIR_xori: CASE64(LIR_xorq:) - case LIR_lshi: CASE64(LIR_lshq:) - case LIR_rshi: CASE64(LIR_rshq:) - case LIR_rshui: CASE64(LIR_rshuq:) - case LIR_eqi: CASE64(LIR_eqq:) - case LIR_lti: CASE64(LIR_ltq:) - case LIR_lei: CASE64(LIR_leq:) - case LIR_gti: CASE64(LIR_gtq:) - case LIR_gei: CASE64(LIR_geq:) - case LIR_ltui: CASE64(LIR_ltuq:) - case LIR_leui: CASE64(LIR_leuq:) - case LIR_gtui: CASE64(LIR_gtuq:) - case LIR_geui: CASE64(LIR_geuq:) - case LIR_eqd: - case LIR_ltd: - case LIR_led: - case LIR_gtd: - case LIR_ged: -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_ii2d: -#endif - VMPI_snprintf(s, n, "%s = %s %s, %s", formatRef(&b1, i), lirNames[op], - formatRef(&b2, i->oprnd1()), - formatRef(&b3, i->oprnd2())); - break; - - CASE64(LIR_cmovq:) - case LIR_cmovi: - case LIR_cmovd: - VMPI_snprintf(s, n, "%s = %s %s ? %s : %s", formatRef(&b1, i), lirNames[op], - formatRef(&b2, i->oprnd1()), - formatRef(&b3, i->oprnd2()), - formatRef(&b4, i->oprnd3())); - break; - - case LIR_ldi: - CASE64(LIR_ldq:) - case LIR_ldd: - case LIR_lduc2ui: - case LIR_ldus2ui: - case LIR_ldc2i: - case LIR_lds2i: - case LIR_ldf2d: { - const char* qualStr; - switch (i->loadQual()) { - case LOAD_CONST: qualStr = "/c"; break; - case LOAD_NORMAL: qualStr = ""; break; - case LOAD_VOLATILE: qualStr = "/v"; break; - default: NanoAssert(0); qualStr = "/?"; break; - } - VMPI_snprintf(s, n, "%s = %s%s%s %s[%d]", formatRef(&b1, i), lirNames[op], - formatAccSet(&b2, i->accSet()), qualStr, formatRef(&b3, i->oprnd1()), - i->disp()); - break; - } - - case LIR_sti: - CASE64(LIR_stq:) - case LIR_std: - case LIR_sti2c: - case LIR_sti2s: - case LIR_std2f: - VMPI_snprintf(s, n, "%s%s %s[%d] = %s", lirNames[op], - formatAccSet(&b1, i->accSet()), - formatRef(&b2, i->oprnd2()), - i->disp(), - formatRef(&b3, i->oprnd1())); - break; - - case LIR_comment: - VMPI_snprintf(s, n, "------------------------------ # %s", (char*)i->oprnd1()); - break; - - default: - NanoAssertMsgf(0, "Can't handle opcode %s\n", lirNames[op]); - break; - } - return buf->buf; - } -#endif - - CseFilter::CseFilter(LirWriter *out, uint8_t embNumUsedAccs, Allocator& alloc) - : LirWriter(out), - EMB_NUM_USED_ACCS(embNumUsedAccs), - CSE_NUM_USED_ACCS(EMB_NUM_USED_ACCS + 2), - CSE_ACC_CONST( EMB_NUM_USED_ACCS + 0), - CSE_ACC_MULTIPLE( EMB_NUM_USED_ACCS + 1), - storesSinceLastLoad(ACCSET_NONE), - alloc(alloc), - knownCmpValues(alloc), - suspended(false), - initOOM(false) - { - m_findNL[NLImmISmall] = &CseFilter::findImmISmall; - m_findNL[NLImmILarge] = &CseFilter::findImmILarge; - m_findNL[NLImmQ] = PTR_SIZE(NULL, &CseFilter::findImmQ); - m_findNL[NLImmD] = &CseFilter::findImmD; - m_findNL[NL1] = &CseFilter::find1; - m_findNL[NL2] = &CseFilter::find2; - m_findNL[NL3] = &CseFilter::find3; - m_findNL[NLCall] = &CseFilter::findCall; - - m_capNL[NLImmISmall] = 17; // covers 0..16, which is over half the cases for TraceMonkey - m_capNL[NLImmILarge] = 64; - m_capNL[NLImmQ] = PTR_SIZE(0, 16); - m_capNL[NLImmD] = 16; - m_capNL[NL1] = 256; - m_capNL[NL2] = 512; - m_capNL[NL3] = 16; - m_capNL[NLCall] = 64; - - // The largish allocations are fallible, the small ones are - // infallible. See the comment on initOOM's declaration for why. - - for (NLKind nlkind = NLFirst; nlkind <= NLLast; nlkind = nextNLKind(nlkind)) { - m_listNL[nlkind] = (LIns**)alloc.fallibleAlloc(sizeof(LIns*) * m_capNL[nlkind]); - if (!m_listNL[nlkind]) { - initOOM = true; - return; - } - m_usedNL[nlkind] = 1; // Force memset in clearAll(). - } - - // Note that this allocates the CONST and MULTIPLE tables as well. - for (CseAcc a = 0; a < CSE_NUM_USED_ACCS; a++) { - m_capL[a] = 16; - m_listL[a] = (LIns**)alloc.fallibleAlloc(sizeof(LIns*) * m_capL[a]); - if (!m_listL[a]) { - initOOM = true; - return; - } - m_usedL[a] = 1; // Force memset(0) in first clearAll(). - } - - clearAll(); - } - - // Inlined/separated version of SuperFastHash. - // This content is copyrighted by Paul Hsieh. - // For reference see: http://www.azillionmonkeys.com/qed/hash.html - // - inline uint32_t CseFilter::hash8(uint32_t hash, const uint8_t data) - { - hash += data; - hash ^= hash << 10; - hash += hash >> 1; - return hash; - } - - inline uint32_t CseFilter::hash32(uint32_t hash, const uint32_t data) - { - const uint32_t dlo = data & 0xffff; - const uint32_t dhi = data >> 16; - hash += dlo; - const uint32_t tmp = (dhi << 11) ^ hash; - hash = (hash << 16) ^ tmp; - hash += hash >> 11; - return hash; - } - - inline uint32_t CseFilter::hashptr(uint32_t hash, const void* data) - { -#ifdef NANOJIT_64BIT - hash = hash32(hash, uint32_t(uintptr_t(data) >> 32)); - hash = hash32(hash, uint32_t(uintptr_t(data))); - return hash; -#else - return hash32(hash, uint32_t(data)); -#endif - } - - inline uint32_t CseFilter::hashfinish(uint32_t hash) - { - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - return hash; - } - - void CseFilter::clearNL(NLKind nlkind) { - if (m_usedNL[nlkind] > 0) { - VMPI_memset(m_listNL[nlkind], 0, sizeof(LIns*)*m_capNL[nlkind]); - m_usedNL[nlkind] = 0; - } - } - - void CseFilter::clearL(CseAcc a) { - if (m_usedL[a] > 0) { - VMPI_memset(m_listL[a], 0, sizeof(LIns*)*m_capL[a]); - m_usedL[a] = 0; - } - } - - void CseFilter::clearAll() { - for (NLKind nlkind = NLFirst; nlkind <= NLLast; nlkind = nextNLKind(nlkind)) - clearNL(nlkind); - - // Note that this clears the CONST and MULTIPLE load tables as well. - for (CseAcc a = 0; a < CSE_NUM_USED_ACCS; a++) - clearL(a); - - knownCmpValues.clear(); - } - - inline uint32_t CseFilter::hashImmI(int32_t a) { - return hashfinish(hash32(0, a)); - } - - inline uint32_t CseFilter::hashImmQorD(uint64_t a) { - uint32_t hash = hash32(0, uint32_t(a >> 32)); - return hashfinish(hash32(hash, uint32_t(a))); - } - - inline uint32_t CseFilter::hash1(LOpcode op, LIns* a) { - uint32_t hash = hash8(0, uint8_t(op)); - return hashfinish(hashptr(hash, a)); - } - - inline uint32_t CseFilter::hash2(LOpcode op, LIns* a, LIns* b) { - uint32_t hash = hash8(0, uint8_t(op)); - hash = hashptr(hash, a); - return hashfinish(hashptr(hash, b)); - } - - inline uint32_t CseFilter::hash3(LOpcode op, LIns* a, LIns* b, LIns* c) { - uint32_t hash = hash8(0, uint8_t(op)); - hash = hashptr(hash, a); - hash = hashptr(hash, b); - return hashfinish(hashptr(hash, c)); - } - - // Nb: no need to hash the load's MiniAccSet because each every load goes - // into a table where all the loads have the same MiniAccSet. - inline uint32_t CseFilter::hashLoad(LOpcode op, LIns* a, int32_t d) { - uint32_t hash = hash8(0, uint8_t(op)); - hash = hashptr(hash, a); - return hashfinish(hash32(hash, d)); - } - - inline uint32_t CseFilter::hashCall(const CallInfo *ci, uint32_t argc, LIns* args[]) { - uint32_t hash = hashptr(0, ci); - for (int32_t j=argc-1; j >= 0; j--) - hash = hashptr(hash,args[j]); - return hashfinish(hash); - } - - bool CseFilter::growNL(NLKind nlkind) - { - NanoAssert(nlkind != NLImmISmall); - const uint32_t oldcap = m_capNL[nlkind]; - m_capNL[nlkind] <<= 1; - // We make this allocation fallible because it's potentially large and - // easy to recover from. If it fails, we won't add any more - // instructions to the table and some CSE opportunities may be missed. - LIns** tmp = (LIns**)alloc.fallibleAlloc(sizeof(LIns*) * m_capNL[nlkind]); - if (tmp) { - LIns** oldlist = m_listNL[nlkind]; - m_listNL[nlkind] = tmp; - VMPI_memset(m_listNL[nlkind], 0, m_capNL[nlkind] * sizeof(LIns*)); - find_t find = m_findNL[nlkind]; - for (uint32_t i = 0; i < oldcap; i++) { - LIns* ins = oldlist[i]; - if (!ins) continue; - uint32_t j = (this->*find)(ins); - NanoAssert(!m_listNL[nlkind][j]); - m_listNL[nlkind][j] = ins; - } - return true; - } else { - m_capNL[nlkind] = oldcap; - return false; - } - } - - bool CseFilter::growL(CseAcc cseAcc) - { - const uint32_t oldcap = m_capL[cseAcc]; - m_capL[cseAcc] <<= 1; - LIns** tmp = (LIns**)alloc.fallibleAlloc(sizeof(LIns*) * m_capL[cseAcc]); - if (tmp) { - LIns** oldlist = m_listL[cseAcc]; - m_listL[cseAcc] = tmp; - VMPI_memset(m_listL[cseAcc], 0, m_capL[cseAcc] * sizeof(LIns*)); - find_t find = &CseFilter::findLoad; - for (uint32_t i = 0; i < oldcap; i++) { - LIns* ins = oldlist[i]; - if (!ins) continue; - uint32_t j = (this->*find)(ins); - NanoAssert(!m_listL[cseAcc][j]); - m_listL[cseAcc][j] = ins; - } - return true; - } else { - m_capL[cseAcc] = oldcap; - return false; - } - } - - void CseFilter::addNLImmISmall(LIns* ins, uint32_t k) - { - NanoAssert(!initOOM); - if (suspended) return; - NLKind nlkind = NLImmISmall; - NanoAssert(k < m_capNL[nlkind]); - NanoAssert(!m_listNL[nlkind][k]); - m_usedNL[nlkind]++; - m_listNL[nlkind][k] = ins; - } - - void CseFilter::addNL(NLKind nlkind, LIns* ins, uint32_t k) - { - NanoAssert(!initOOM); - if (suspended) return; - NanoAssert(!m_listNL[nlkind][k]); - m_usedNL[nlkind]++; - m_listNL[nlkind][k] = ins; - if ((m_usedNL[nlkind] * 4) >= (m_capNL[nlkind] * 3)) { // load factor of 0.75 - bool ok = growNL(nlkind); - if (!ok) { - // OOM: undo the insertion. - m_usedNL[nlkind]--; - m_listNL[nlkind][k] = NULL; - } - } - } - - void CseFilter::addL(LIns* ins, uint32_t k) - { - NanoAssert(!initOOM); - if (suspended) return; - CseAcc cseAcc = miniAccSetToCseAcc(ins->miniAccSet(), ins->loadQual()); - NanoAssert(!m_listL[cseAcc][k]); - m_usedL[cseAcc]++; - m_listL[cseAcc][k] = ins; - if ((m_usedL[cseAcc] * 4) >= (m_capL[cseAcc] * 3)) { // load factor of 0.75 - bool ok = growL(cseAcc); - if (!ok) { - // OOM: undo the insertion. - m_usedL[cseAcc]--; - m_listL[cseAcc][k] = NULL; - } - } - } - - inline LIns* CseFilter::findImmISmall(int32_t a, uint32_t &k) - { - // This one is a direct array lookup rather than a hashtable lookup. - NLKind nlkind = NLImmISmall; - k = a; - LIns* ins = m_listNL[nlkind][k]; - NanoAssert(!ins || ins->isImmI(a)); - return ins; - } - - uint32_t CseFilter::findImmISmall(LIns* ins) - { - uint32_t k; - findImmISmall(ins->immI(), k); - return k; - } - - inline LIns* CseFilter::findImmILarge(int32_t a, uint32_t &k) - { - NLKind nlkind = NLImmILarge; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hashImmI(a) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - NanoAssert(ins->isImmI()); - if (ins->immI() == a) - return ins; - // Quadratic probe: h(k,i) = h(k) + 0.5i + 0.5i^2, which gives the - // sequence h(k), h(k)+1, h(k)+3, h(k)+6, h+10, ... This is a - // good sequence for 2^n-sized tables as the values h(k,i) for i - // in [0,m - 1] are all distinct so termination is guaranteed. - // See http://portal.acm.org/citation.cfm?id=360737 and - // http://en.wikipedia.org/wiki/Quadratic_probing (fetched - // 06-Nov-2009) for more details. - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::findImmILarge(LIns* ins) - { - uint32_t k; - findImmILarge(ins->immI(), k); - return k; - } - -#ifdef NANOJIT_64BIT - inline LIns* CseFilter::findImmQ(uint64_t a, uint32_t &k) - { - NLKind nlkind = NLImmQ; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hashImmQorD(a) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - NanoAssert(ins->isImmQ()); - if (ins->immQ() == a) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::findImmQ(LIns* ins) - { - uint32_t k; - findImmQ(ins->immQ(), k); - return k; - } -#endif - - inline LIns* CseFilter::findImmD(uint64_t a, uint32_t &k) - { - NLKind nlkind = NLImmD; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hashImmQorD(a) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - NanoAssert(ins->isImmD()); - if (ins->immDasQ() == a) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::findImmD(LIns* ins) - { - uint32_t k; - findImmD(ins->immDasQ(), k); - return k; - } - - inline LIns* CseFilter::find1(LOpcode op, LIns* a, uint32_t &k) - { - NLKind nlkind = NL1; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hash1(op, a) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - if (ins->isop(op) && ins->oprnd1() == a) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::find1(LIns* ins) - { - uint32_t k; - find1(ins->opcode(), ins->oprnd1(), k); - return k; - } - - inline LIns* CseFilter::find2(LOpcode op, LIns* a, LIns* b, uint32_t &k) - { - NLKind nlkind = NL2; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hash2(op, a, b) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - if (ins->isop(op) && ins->oprnd1() == a && ins->oprnd2() == b) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::find2(LIns* ins) - { - uint32_t k; - find2(ins->opcode(), ins->oprnd1(), ins->oprnd2(), k); - return k; - } - - inline LIns* CseFilter::find3(LOpcode op, LIns* a, LIns* b, LIns* c, uint32_t &k) - { - NLKind nlkind = NL3; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hash3(op, a, b, c) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - if (ins->isop(op) && ins->oprnd1() == a && ins->oprnd2() == b && ins->oprnd3() == c) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::find3(LIns* ins) - { - uint32_t k; - find3(ins->opcode(), ins->oprnd1(), ins->oprnd2(), ins->oprnd3(), k); - return k; - } - - inline LIns* CseFilter::findLoad(LOpcode op, LIns* a, int32_t d, MiniAccSet miniAccSet, - LoadQual loadQual, uint32_t &k) - { - CseAcc cseAcc = miniAccSetToCseAcc(miniAccSet, loadQual); - const uint32_t bitmask = m_capL[cseAcc] - 1; - k = hashLoad(op, a, d) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listL[cseAcc][k]; - if (!ins) - return NULL; - // All the loads in this table should have the same miniAccSet and - // loadQual. - NanoAssert(miniAccSetToCseAcc(ins->miniAccSet(), ins->loadQual()) == cseAcc && - ins->loadQual() == loadQual); - if (ins->isop(op) && ins->oprnd1() == a && ins->disp() == d) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::findLoad(LIns* ins) - { - uint32_t k; - findLoad(ins->opcode(), ins->oprnd1(), ins->disp(), ins->miniAccSet(), ins->loadQual(), k); - return k; - } - - bool argsmatch(LIns* ins, uint32_t argc, LIns* args[]) - { - for (uint32_t j=0; j < argc; j++) - if (ins->arg(j) != args[j]) - return false; - return true; - } - - inline LIns* CseFilter::findCall(const CallInfo *ci, uint32_t argc, LIns* args[], uint32_t &k) - { - NLKind nlkind = NLCall; - const uint32_t bitmask = m_capNL[nlkind] - 1; - k = hashCall(ci, argc, args) & bitmask; - uint32_t n = 1; - while (true) { - LIns* ins = m_listNL[nlkind][k]; - if (!ins) - return NULL; - if (ins->isCall() && ins->callInfo() == ci && argsmatch(ins, argc, args)) - return ins; - k = (k + n) & bitmask; - n += 1; - } - } - - uint32_t CseFilter::findCall(LIns* ins) - { - LIns* args[MAXARGS]; - uint32_t argc = ins->argc(); - NanoAssert(argc < MAXARGS); - for (uint32_t j=0; j < argc; j++) - args[j] = ins->arg(j); - uint32_t k; - findCall(ins->callInfo(), argc, args, k); - return k; - } - - LIns* CseFilter::insImmI(int32_t imm) - { - uint32_t k; - LIns* ins; - if (0 <= imm && imm < int32_t(m_capNL[NLImmISmall])) { - ins = findImmISmall(imm, k); - if (!ins) { - ins = out->insImmI(imm); - addNLImmISmall(ins, k); - } - } else { - ins = findImmILarge(imm, k); - if (!ins) { - ins = out->insImmI(imm); - addNL(NLImmILarge, ins, k); - } - } - // We assume that downstream stages do not modify the instruction, so - // that we can insert 'ins' into slot 'k'. Check this. - NanoAssert(ins->isop(LIR_immi) && ins->immI() == imm); - return ins; - } - -#ifdef NANOJIT_64BIT - LIns* CseFilter::insImmQ(uint64_t q) - { - uint32_t k; - LIns* ins = findImmQ(q, k); - if (!ins) { - ins = out->insImmQ(q); - addNL(NLImmQ, ins, k); - } - NanoAssert(ins->isop(LIR_immq) && ins->immQ() == q); - return ins; - } -#endif - - LIns* CseFilter::insImmD(double d) - { - uint32_t k; - // We must pun 'd' as a uint64_t otherwise 0 and -0 will be treated as - // equal, which breaks things (see bug 527288). - union { - double d; - uint64_t u64; - } u; - u.d = d; - LIns* ins = findImmD(u.u64, k); - if (!ins) { - ins = out->insImmD(d); - addNL(NLImmD, ins, k); - } - NanoAssert(ins->isop(LIR_immd) && ins->immDasQ() == u.u64); - return ins; - } - - LIns* CseFilter::ins0(LOpcode op) - { - if (op == LIR_label && !suspended) - clearAll(); - return out->ins0(op); - } - - LIns* CseFilter::ins1(LOpcode op, LIns* a) - { - LIns* ins; - if (isCseOpcode(op)) { - uint32_t k; - ins = find1(op, a, k); - if (!ins) { - ins = out->ins1(op, a); - addNL(NL1, ins, k); - } - } else { - ins = out->ins1(op, a); - } - NanoAssert(ins->isop(op) && ins->oprnd1() == a); - return ins; - } - - LIns* CseFilter::ins2(LOpcode op, LIns* a, LIns* b) - { - LIns* ins; - NanoAssert(isCseOpcode(op)); - uint32_t k; - ins = find2(op, a, b, k); - if (!ins) { - ins = out->ins2(op, a, b); - addNL(NL2, ins, k); - } else if (ins->isCmp()) { - if (knownCmpValues.containsKey(ins)) { - // We've seen this comparison before, and it was previously - // used in a guard, so we know what its value must be at this - // point. Replace it with a constant. - NanoAssert(ins->isCmp()); - bool cmpValue = knownCmpValues.get(ins); - return insImmI(cmpValue ? 1 : 0); - } - } - NanoAssert(ins->isop(op) && ins->oprnd1() == a && ins->oprnd2() == b); - return ins; - } - - LIns* CseFilter::ins3(LOpcode op, LIns* a, LIns* b, LIns* c) - { - NanoAssert(isCseOpcode(op)); - uint32_t k; - LIns* ins = find3(op, a, b, c, k); - if (!ins) { - ins = out->ins3(op, a, b, c); - addNL(NL3, ins, k); - } - NanoAssert(ins->isop(op) && ins->oprnd1() == a && ins->oprnd2() == b && ins->oprnd3() == c); - return ins; - } - - LIns* CseFilter::insLoad(LOpcode op, LIns* base, int32_t disp, AccSet accSet, LoadQual loadQual) - { - LIns* ins; - if (isS16(disp)) { - if (storesSinceLastLoad != ACCSET_NONE) { - // Clear all normal (excludes CONST and MULTIPLE) loads - // aliased by stores and calls since the last time we were in - // this function. Aliased loads must be cleared even when CSE - // is suspended. - AccSet a = storesSinceLastLoad & ((1 << EMB_NUM_USED_ACCS) - 1); - while (a) { - int acc = msbSet32(a); - clearL((CseAcc)acc); - a &= ~(1 << acc); - } - - // No need to clear CONST loads (those in the CSE_ACC_CONST table). - - // Multi-region loads must be treated conservatively -- we - // always clear all of them. - clearL(CSE_ACC_MULTIPLE); - - storesSinceLastLoad = ACCSET_NONE; - } - - if (loadQual == LOAD_VOLATILE) { - // Volatile loads are never CSE'd, don't bother looking for - // them or inserting them in the table. - ins = out->insLoad(op, base, disp, accSet, loadQual); - } else { - uint32_t k; - ins = findLoad(op, base, disp, compressAccSet(accSet), loadQual, k); - if (!ins) { - ins = out->insLoad(op, base, disp, accSet, loadQual); - addL(ins, k); - } - } - // Nb: must compare miniAccSets, not AccSets, because the AccSet - // stored in the load may have lost info if it's multi-region. - NanoAssert(ins->isop(op) && ins->oprnd1() == base && ins->disp() == disp && - ins->miniAccSet().val == compressAccSet(accSet).val && - ins->loadQual() == loadQual); - } else { - // If the displacement is more than 16 bits, put it in a separate - // instruction. Nb: LirBufWriter also does this, we do it here - // too because CseFilter relies on LirBufWriter not changing code. - ins = insLoad(op, ins2(LIR_addp, base, insImmWord(disp)), 0, accSet, loadQual); - } - return ins; - } - - LIns* CseFilter::insStore(LOpcode op, LIns* value, LIns* base, int32_t disp, AccSet accSet) - { - LIns* ins; - if (isS16(disp)) { - storesSinceLastLoad |= accSet; - ins = out->insStore(op, value, base, disp, accSet); - NanoAssert(ins->isop(op) && ins->oprnd1() == value && ins->oprnd2() == base && - ins->disp() == disp && ins->accSet() == accSet); - } else { - // If the displacement is more than 16 bits, put it in a separate - // instruction. Nb: LirBufWriter also does this, we do it here - // too because CseFilter relies on LirBufWriter not changing code. - ins = insStore(op, value, ins2(LIR_addp, base, insImmWord(disp)), 0, accSet); - } - return ins; - } - - LIns* CseFilter::insGuard(LOpcode op, LIns* c, GuardRecord *gr) - { - // LIR_xt and LIR_xf guards are CSEable. Note that we compare the - // opcode and condition when determining if two guards are equivalent - // -- in find1() and hash1() -- but we do *not* compare the - // GuardRecord. This works because: - // - If guard 1 is taken (exits) then guard 2 is never reached, so - // guard 2 can be removed. - // - If guard 1 is not taken then neither is guard 2, so guard 2 can - // be removed. - // - // The underlying assumptions that are required for this to be safe: - // - There's never a path from the side exit of guard 1 back to guard - // 2; for tree-shaped fragments this should be true. - // - GuardRecords do not contain information other than what is needed - // to execute a successful exit. That is currently true. - // - The CSE algorithm will always keep guard 1 and remove guard 2 - // (not vice versa). The current algorithm does this. - // - LIns* ins; - if (isCseOpcode(op)) { - // conditional guard - uint32_t k; - ins = find1(op, c, k); - if (!ins) { - ins = out->insGuard(op, c, gr); - addNL(NL1, ins, k); - } - // After this guard, we know that 'c's result was true (if - // op==LIR_xf) or false (if op==LIR_xt), else we would have - // exited. Record this fact in case 'c' occurs again. - if (!suspended) { - bool c_value = (op == LIR_xt ? false : true); - knownCmpValues.put(c, c_value); - } - } else { - ins = out->insGuard(op, c, gr); - } - NanoAssert(ins->isop(op) && ins->oprnd1() == c); - return ins; - } - - LIns* CseFilter::insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord *gr) - { - // LIR_*xov are CSEable. See CseFilter::insGuard() for details. - NanoAssert(isCseOpcode(op)); - // conditional guard - uint32_t k; - LIns* ins = find2(op, a, b, k); - if (!ins) { - ins = out->insGuardXov(op, a, b, gr); - addNL(NL2, ins, k); - } - NanoAssert(ins->isop(op) && ins->oprnd1() == a && ins->oprnd2() == b); - return ins; - } - - // There is no CseFilter::insBranchJov(), as LIR_*jov* are not CSEable. - - LIns* CseFilter::insCall(const CallInfo *ci, LIns* args[]) - { - LIns* ins; - uint32_t argc = ci->count_args(); - if (ci->_isPure) { - NanoAssert(ci->_storeAccSet == ACCSET_NONE); - uint32_t k; - ins = findCall(ci, argc, args, k); - if (!ins) { - ins = out->insCall(ci, args); - addNL(NLCall, ins, k); - } - } else { - // We only need to worry about aliasing if !ci->_isPure. - storesSinceLastLoad |= ci->_storeAccSet; - ins = out->insCall(ci, args); - } - NanoAssert(ins->isCall() && ins->callInfo() == ci && argsmatch(ins, argc, args)); - return ins; - } - - // Interval analysis can be done much more accurately than we do here. - // For speed and simplicity in a number of cases (eg. LIR_andi, LIR_rshi) - // we just look for easy-to-handle (but common!) cases such as when the - // RHS is a constant; in practice this gives good results. It also cuts - // down the amount of backwards traversals we have to do, which is good. - // - // 'lim' also limits the number of backwards traversals; it's decremented - // on each recursive call and we give up when it reaches zero. This - // prevents possible time blow-ups in long expression chains. We don't - // check 'lim' at the top of this function, as you might expect, because - // the behaviour when the limit is reached depends on the opcode. - // - Interval Interval::of(LIns* ins, int lim) - { - switch (ins->opcode()) { - case LIR_immi: { - int32_t i = ins->immI(); - return Interval(i, i); - } - - case LIR_ldc2i: return Interval( -128, 127); - case LIR_lduc2ui: return Interval( 0, 255); - case LIR_lds2i: return Interval(-32768, 32767); - case LIR_ldus2ui: return Interval( 0, 65535); - - case LIR_addi: - case LIR_addxovi: - case LIR_addjovi: - if (lim > 0) - return add(of(ins->oprnd1(), lim-1), of(ins->oprnd2(), lim-1)); - goto overflow; - - case LIR_subi: - case LIR_subxovi: - case LIR_subjovi: - if (lim > 0) - return sub(of(ins->oprnd1(), lim-1), of(ins->oprnd2(), lim-1)); - goto overflow; - - case LIR_negi: - if (lim > 0) - return sub(Interval(0, 0), of(ins->oprnd1(), lim-1)); - goto overflow; - - case LIR_muli: - case LIR_mulxovi: - case LIR_muljovi: - if (lim > 0) - return mul(of(ins->oprnd1(), lim), of(ins->oprnd2(), lim)); - goto overflow; - - case LIR_andi: { - // Only handle one common case accurately, for speed and simplicity. - if (ins->oprnd2()->isImmI() && ins->oprnd2()->immI() > 0) { - // Example: andi [lo,hi], 0xffff --> [0, 0xffff] - return Interval(0, ins->oprnd2()->immI()); - } - goto worst_non_overflow; - } - - case LIR_rshui: { - // Only handle one common case accurately, for speed and simplicity. - if (ins->oprnd2()->isImmI() && lim > 0) { - Interval x = of(ins->oprnd1(), lim-1); - int32_t y = ins->oprnd2()->immI() & 0x1f; // we only use the bottom 5 bits - NanoAssert(x.isSane()); - if (!x.hasOverflowed && (x.lo >= 0 || y > 0)) { - // If LHS is non-negative or RHS is positive, the result is - // non-negative because the top bit must be zero. - // Example: rshui [0,hi], 16 --> [0, hi>>16] - return Interval(0, x.hi >> y); - } - } - goto worst_non_overflow; - } - - case LIR_rshi: { - // Only handle one common case accurately, for speed and simplicity. - if (ins->oprnd2()->isImmI()) { - // Example: rshi [lo,hi], 16 --> [32768, 32767] - int32_t y = ins->oprnd2()->immI() & 0x1f; // we only use the bottom 5 bits - return Interval(-(1 << (31 - y)), - (1 << (31 - y)) - 1); - } - goto worst_non_overflow; - } - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - case LIR_modi: { - NanoAssert(ins->oprnd1()->isop(LIR_divi)); - LIns* op2 = ins->oprnd1()->oprnd2(); - // Only handle one common case accurately, for speed and simplicity. - if (op2->isImmI() && op2->immI() != 0) { - int32_t y = op2->immI(); - int32_t absy = (y >= 0) ? y : -y; - // The result must smaller in magnitude than 'y'. - // Example: modi [lo,hi], 5 --> [-4, 4] - return Interval(-absy + 1, absy - 1); - } - goto worst_non_overflow; - } -#endif - - case LIR_cmovi: { - if (lim > 0) { - Interval x = of(ins->oprnd2(), lim-1); - Interval y = of(ins->oprnd3(), lim-1); - NanoAssert(x.isSane() && y.isSane()); - if (!x.hasOverflowed && !y.hasOverflowed) - return Interval(NJ_MIN(x.lo, y.lo), NJ_MAX(x.hi, y.hi)); - } - goto overflow; - } - - case LIR_eqi: CASE64(LIR_eqq:) - case LIR_lti: CASE64(LIR_ltq:) - case LIR_lei: CASE64(LIR_leq:) - case LIR_gti: CASE64(LIR_gtq:) - case LIR_gei: CASE64(LIR_geq:) - case LIR_ltui: CASE64(LIR_ltuq:) - case LIR_leui: CASE64(LIR_leuq:) - case LIR_gtui: CASE64(LIR_gtuq:) - case LIR_geui: CASE64(LIR_geuq:) - case LIR_eqd: - case LIR_ltd: - case LIR_led: - case LIR_gtd: - case LIR_ged: - return Interval(0, 1); - - CASE32(LIR_paramp:) - case LIR_ldi: - case LIR_noti: - case LIR_ori: - case LIR_xori: - case LIR_lshi: - CASE86(LIR_divi:) - case LIR_calli: - case LIR_reti: - CASE64(LIR_q2i:) - case LIR_d2i: - CASESF(LIR_dlo2i:) - CASESF(LIR_dhi2i:) - CASESF(LIR_hcalli:) - goto worst_non_overflow; - - default: - NanoAssertMsgf(0, "%s", lirNames[ins->opcode()]); - } - - overflow: - return OverflowInterval(); - - worst_non_overflow: - // Only cases that cannot overflow should reach here, ie. not add/sub/mul. - return Interval(I32_MIN, I32_MAX); - } - - Interval Interval::add(Interval x, Interval y) { - NanoAssert(x.isSane() && y.isSane()); - - if (x.hasOverflowed || y.hasOverflowed) - return OverflowInterval(); - - // Nb: the bounds in x and y are known to fit in 32 bits (isSane() - // checks that) so x.lo+y.lo and x.hi+y.hi are guaranteed to fit - // in 64 bits. This also holds for the other cases below such as - // sub() and mul(). - return Interval(x.lo + y.lo, x.hi + y.hi); - } - - Interval Interval::sub(Interval x, Interval y) { - NanoAssert(x.isSane() && y.isSane()); - - if (x.hasOverflowed || y.hasOverflowed) - return OverflowInterval(); - - return Interval(x.lo - y.hi, x.hi - y.lo); - } - - Interval Interval::mul(Interval x, Interval y) { - NanoAssert(x.isSane() && y.isSane()); - - if (x.hasOverflowed || y.hasOverflowed) - return OverflowInterval(); - - int64_t a = x.lo * y.lo; - int64_t b = x.lo * y.hi; - int64_t c = x.hi * y.lo; - int64_t d = x.hi * y.hi; - return Interval(NJ_MIN(NJ_MIN(a, b), NJ_MIN(c, d)), - NJ_MAX(NJ_MAX(a, b), NJ_MAX(c, d))); - } - -#if NJ_SOFTFLOAT_SUPPORTED - static int32_t FASTCALL d2i(double d) { return (int32_t) d; } - static double FASTCALL i2d(int32_t i) { return i; } - static double FASTCALL ui2d(uint32_t u) { return u; } - static double FASTCALL negd(double a) { return -a; } - static double FASTCALL addd(double a, double b) { return a + b; } - static double FASTCALL subd(double a, double b) { return a - b; } - static double FASTCALL muld(double a, double b) { return a * b; } - static double FASTCALL divd(double a, double b) { return a / b; } - static int32_t FASTCALL eqd(double a, double b) { return a == b; } - static int32_t FASTCALL ltd(double a, double b) { return a < b; } - static int32_t FASTCALL gtd(double a, double b) { return a > b; } - static int32_t FASTCALL led(double a, double b) { return a <= b; } - static int32_t FASTCALL ged(double a, double b) { return a >= b; } - - #define SIG_I_D CallInfo::typeSig1(ARGTYPE_I, ARGTYPE_D) - #define SIG_D_I CallInfo::typeSig1(ARGTYPE_D, ARGTYPE_I) - #define SIG_D_UI CallInfo::typeSig1(ARGTYPE_D, ARGTYPE_UI) - #define SIG_D_D CallInfo::typeSig1(ARGTYPE_D, ARGTYPE_D) - #define SIG_D_DD CallInfo::typeSig2(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D) - #define SIG_B_DD CallInfo::typeSig2(ARGTYPE_B, ARGTYPE_D, ARGTYPE_D) - - #define SF_CALLINFO(name, typesig) \ - static const CallInfo name##_ci = \ - { (intptr_t)&name, typesig, ABI_FASTCALL, /*isPure*/1, ACCSET_NONE verbose_only(, #name) } - - SF_CALLINFO(d2i, SIG_I_D); - SF_CALLINFO(i2d, SIG_D_I); - SF_CALLINFO(ui2d, SIG_D_UI); - SF_CALLINFO(negd, SIG_D_D); - SF_CALLINFO(addd, SIG_D_DD); - SF_CALLINFO(subd, SIG_D_DD); - SF_CALLINFO(muld, SIG_D_DD); - SF_CALLINFO(divd, SIG_D_DD); - SF_CALLINFO(eqd, SIG_B_DD); - SF_CALLINFO(ltd, SIG_B_DD); - SF_CALLINFO(gtd, SIG_B_DD); - SF_CALLINFO(led, SIG_B_DD); - SF_CALLINFO(ged, SIG_B_DD); - - SoftFloatOps::SoftFloatOps() - { - memset(opmap, 0, sizeof(opmap)); - opmap[LIR_d2i] = &d2i_ci; - opmap[LIR_i2d] = &i2d_ci; - opmap[LIR_ui2d] = &ui2d_ci; - opmap[LIR_negd] = &negd_ci; - opmap[LIR_addd] = &addd_ci; - opmap[LIR_subd] = &subd_ci; - opmap[LIR_muld] = &muld_ci; - opmap[LIR_divd] = &divd_ci; - opmap[LIR_eqd] = &eqd_ci; - opmap[LIR_ltd] = <d_ci; - opmap[LIR_gtd] = >d_ci; - opmap[LIR_led] = &led_ci; - opmap[LIR_ged] = &ged_ci; - } - - const SoftFloatOps softFloatOps; - - SoftFloatFilter::SoftFloatFilter(LirWriter *out) : LirWriter(out) - {} - - LIns* SoftFloatFilter::split(LIns *a) { - if (a->isD() && !a->isop(LIR_ii2d)) { - // all F64 args must be qjoin's for soft-float - a = ins2(LIR_ii2d, ins1(LIR_dlo2i, a), ins1(LIR_dhi2i, a)); - } - return a; - } - - LIns* SoftFloatFilter::split(const CallInfo *call, LIns* args[]) { - LIns *lo = out->insCall(call, args); - LIns *hi = out->ins1(LIR_hcalli, lo); - return out->ins2(LIR_ii2d, lo, hi); - } - - LIns* SoftFloatFilter::callD1(const CallInfo *call, LIns *a) { - LIns *args[] = { split(a) }; - return split(call, args); - } - - LIns* SoftFloatFilter::callI1(const CallInfo *call, LIns *a) { - LIns *args[] = { split(a) }; - return out->insCall(call, args); - } - - LIns* SoftFloatFilter::callD2(const CallInfo *call, LIns *a, LIns *b) { - LIns *args[] = { split(b), split(a) }; - return split(call, args); - } - - LIns* SoftFloatFilter::cmpD(const CallInfo *call, LIns *a, LIns *b) { - LIns *args[] = { split(b), split(a) }; - return out->ins2(LIR_eqi, out->insCall(call, args), out->insImmI(1)); - } - - LIns* SoftFloatFilter::ins1(LOpcode op, LIns *a) { - const CallInfo *ci = softFloatOps.opmap[op]; - if (ci) { - if (ci->returnType() == ARGTYPE_D) - return callD1(ci, a); - else - return callI1(ci, a); - } - if (op == LIR_retd) - return out->ins1(op, split(a)); - return out->ins1(op, a); - } - - LIns* SoftFloatFilter::ins2(LOpcode op, LIns *a, LIns *b) { - const CallInfo *ci = softFloatOps.opmap[op]; - if (ci) { - if (isCmpDOpcode(op)) - return cmpD(ci, a, b); - return callD2(ci, a, b); - } - return out->ins2(op, a, b); - } - - LIns* SoftFloatFilter::insCall(const CallInfo *ci, LIns* args[]) { - uint32_t nArgs = ci->count_args(); - for (uint32_t i = 0; i < nArgs; i++) - args[i] = split(args[i]); - - if (ci->returnType() == ARGTYPE_D) { - // This function returns a double as two 32bit values, so replace - // call with qjoin(qhi(call), call). - return split(ci, args); - } - return out->insCall(ci, args); - } -#endif // NJ_SOFTFLOAT_SUPPORTED - - - #endif /* FEATURE_NANOJIT */ - -#if defined(NJ_VERBOSE) - AddrNameMap::AddrNameMap(Allocator& a) - : allocator(a), names(a) - {} - - void AddrNameMap::addAddrRange(const void *p, size_t size, size_t align, const char *name) - { - if (!this || names.containsKey(p)) - return; - char* copy = new (allocator) char[VMPI_strlen(name)+1]; - VMPI_strcpy(copy, name); - Entry *e = new (allocator) Entry(copy, size << align, align); - names.put(p, e); - } - - void AddrNameMap::lookupAddr(void *p, char*& name, int32_t& offset) - { - const void *start = names.findNear(p); - if (start) { - Entry *e = names.get(start); - const void *end = (const char*)start + e->size; - if (p == start) { - name = e->name; - offset = 0; - } - else if (p > start && p < end) { - name = e->name; - offset = int32_t(intptr_t(p)-intptr_t(start)) >> e->align; - } - else { - name = NULL; - offset = 0; - } - } else { - name = NULL; - offset = 0; - } - } - - // --------------------------------------------------------------- - // START debug-logging definitions - // --------------------------------------------------------------- - - void LogControl::printf( const char* format, ... ) - { - va_list vargs; - va_start(vargs, format); - vfprintf(stdout, format, vargs); - va_end(vargs); - // Flush every line immediately so that if crashes occur in generated - // code we won't lose any output. - fflush(stdout); - } - -#endif // NJ_VERBOSE - - -#ifdef FEATURE_NANOJIT -#ifdef DEBUG - const char* ValidateWriter::type2string(LTy type) - { - switch (type) { - case LTy_V: return "void"; - case LTy_I: return "int"; -#ifdef NANOJIT_64BIT - case LTy_Q: return "quad"; -#endif - case LTy_D: return "double"; - default: NanoAssert(0); return "???"; - } - } - - void ValidateWriter::typeCheckArgs(LOpcode op, int nArgs, LTy formals[], LIns* args[]) - { - NanoAssert(nArgs >= 0); - - // Type-check the arguments. - for (int i = 0; i < nArgs; i++) { - LTy formal = formals[i]; - LTy actual = args[i]->retType(); - if (formal != actual) { - // Assert on a type error. The disadvantage of doing this (as - // opposed to printing a message and continuing) is that at - // most one type error will be detected per run. But type - // errors should be rare, and assertion failures are certain - // to be caught by test suites whereas error messages may not - // be. - NanoAssertMsgf(0, - "LIR type error (%s): arg %d of '%s' is '%s' " - "which has type %s (expected %s)", - whereInPipeline, i+1, lirNames[op], - lirNames[args[i]->opcode()], - type2string(actual), type2string(formal)); - } - } - } - - void ValidateWriter::errorStructureShouldBe(LOpcode op, const char* argDesc, int argN, - LIns* arg, const char* shouldBeDesc) - { - NanoAssertMsgf(0, - "LIR structure error (%s): %s %d of '%s' is '%s' (expected %s)", - whereInPipeline, argDesc, argN, - lirNames[op], lirNames[arg->opcode()], shouldBeDesc); - } - - void ValidateWriter::errorAccSet(const char* what, AccSet accSet, const char* shouldDesc) - { - RefBuf b; - NanoAssertMsgf(0, - "LIR AccSet error (%s): '%s' AccSet is '%s'; %s", - whereInPipeline, what, printer->formatAccSet(&b, accSet), shouldDesc); - } - - void ValidateWriter::errorLoadQual(const char* what, LoadQual loadQual) - { - NanoAssertMsgf(0, - "LIR LoadQual error (%s): '%s' loadQual is '%d'", - whereInPipeline, what, loadQual); - } - - void ValidateWriter::checkLInsIsACondOrConst(LOpcode op, int argN, LIns* ins) - { - // We could introduce a LTy_B32 type in the type system but that's a - // bit weird because its representation is identical to LTy_I. It's - // easier to just do this check structurally. Also, optimization can - // cause the condition to become a LIR_immi. - if (!ins->isCmp() && !ins->isImmI()) - errorStructureShouldBe(op, "argument", argN, ins, "a condition or 32-bit constant"); - } - - void ValidateWriter::checkLInsIsNull(LOpcode op, int argN, LIns* ins) - { - if (ins) - errorStructureShouldBe(op, "argument", argN, ins, NULL); - } - - void ValidateWriter::checkLInsHasOpcode(LOpcode op, int argN, LIns* ins, LOpcode op2) - { - if (!ins->isop(op2)) - errorStructureShouldBe(op, "argument", argN, ins, lirNames[op2]); - } - - ValidateWriter::ValidateWriter(LirWriter *out, LInsPrinter* printer, const char* where) - : LirWriter(out), printer(printer), whereInPipeline(where), - checkAccSetExtras(0) - {} - - LIns* ValidateWriter::insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet, - LoadQual loadQual) - { - checkAccSet(op, base, d, accSet); - - switch (loadQual) { - case LOAD_CONST: - case LOAD_NORMAL: - case LOAD_VOLATILE: - break; - default: - errorLoadQual(lirNames[op], loadQual); - break; - } - - - int nArgs = 1; - LTy formals[1] = { LTy_P }; - LIns* args[1] = { base }; - - switch (op) { - case LIR_ldi: - case LIR_ldd: - case LIR_lduc2ui: - case LIR_ldus2ui: - case LIR_ldc2i: - case LIR_lds2i: - case LIR_ldf2d: - CASE64(LIR_ldq:) - break; - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->insLoad(op, base, d, accSet, loadQual); - } - - LIns* ValidateWriter::insStore(LOpcode op, LIns* value, LIns* base, int32_t d, AccSet accSet) - { - checkAccSet(op, base, d, accSet); - - int nArgs = 2; - LTy formals[2] = { LTy_V, LTy_P }; // LTy_V is overwritten shortly - LIns* args[2] = { value, base }; - - switch (op) { - case LIR_sti2c: - case LIR_sti2s: - case LIR_sti: - formals[0] = LTy_I; - break; - -#ifdef NANOJIT_64BIT - case LIR_stq: - formals[0] = LTy_Q; - break; -#endif - - case LIR_std: - case LIR_std2f: - formals[0] = LTy_D; - break; - - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->insStore(op, value, base, d, accSet); - } - - LIns* ValidateWriter::ins0(LOpcode op) - { - switch (op) { - case LIR_start: - case LIR_regfence: - case LIR_label: - break; - default: - NanoAssert(0); - } - - // No args to type-check. - - return out->ins0(op); - } - - LIns* ValidateWriter::ins1(LOpcode op, LIns* a) - { - int nArgs = 1; - LTy formals[1]; - LIns* args[1] = { a }; - - switch (op) { - case LIR_negi: - case LIR_noti: - case LIR_i2d: - case LIR_ui2d: - case LIR_livei: - case LIR_reti: - formals[0] = LTy_I; - break; - -#ifdef NANOJIT_64BIT - case LIR_i2q: - case LIR_ui2uq: - formals[0] = LTy_I; - break; - - case LIR_q2i: - case LIR_qasd: - case LIR_retq: - case LIR_liveq: - formals[0] = LTy_Q; - break; -#endif - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 - case LIR_modi: // see LIRopcode.tbl for why 'mod' is unary - checkLInsHasOpcode(op, 1, a, LIR_divi); - formals[0] = LTy_I; - break; -#endif - -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_dlo2i: - case LIR_dhi2i: - formals[0] = LTy_D; - break; - - case LIR_hcalli: - // The operand of a LIR_hcalli is LIR_calli, even though the - // function being called has a return type of LTy_D. - checkLInsHasOpcode(op, 1, a, LIR_calli); - formals[0] = LTy_I; - break; -#endif - - case LIR_negd: - case LIR_retd: - case LIR_lived: - case LIR_d2i: - CASE64(LIR_dasq:) - formals[0] = LTy_D; - break; - - case LIR_file: - case LIR_line: - // These will never get hit since VTUNE implies !DEBUG. Ignore for the moment. - nArgs = 0; - break; - - default: - NanoAssertMsgf(0, "%s\n", lirNames[op]); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->ins1(op, a); - } - - LIns* ValidateWriter::ins2(LOpcode op, LIns* a, LIns* b) - { - int nArgs = 2; - LTy formals[2]; - LIns* args[2] = { a, b }; - - switch (op) { - case LIR_addi: - case LIR_subi: - case LIR_muli: - CASE86(LIR_divi:) - case LIR_andi: - case LIR_ori: - case LIR_xori: - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - case LIR_eqi: - case LIR_lti: - case LIR_gti: - case LIR_lei: - case LIR_gei: - case LIR_ltui: - case LIR_gtui: - case LIR_leui: - case LIR_geui: - formals[0] = LTy_I; - formals[1] = LTy_I; - break; - -#if NJ_SOFTFLOAT_SUPPORTED - case LIR_ii2d: - formals[0] = LTy_I; - formals[1] = LTy_I; - break; -#endif - -#ifdef NANOJIT_64BIT - case LIR_andq: - case LIR_orq: - case LIR_xorq: - case LIR_addq: - case LIR_subq: - case LIR_eqq: - case LIR_ltq: - case LIR_gtq: - case LIR_leq: - case LIR_geq: - case LIR_ltuq: - case LIR_gtuq: - case LIR_leuq: - case LIR_geuq: - formals[0] = LTy_Q; - formals[1] = LTy_Q; - break; - - case LIR_lshq: - case LIR_rshq: - case LIR_rshuq: - formals[0] = LTy_Q; - formals[1] = LTy_I; - break; -#endif - - case LIR_addd: - case LIR_subd: - case LIR_muld: - case LIR_divd: - case LIR_eqd: - case LIR_gtd: - case LIR_ltd: - case LIR_led: - case LIR_ged: - formals[0] = LTy_D; - formals[1] = LTy_D; - break; - - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->ins2(op, a, b); - } - - LIns* ValidateWriter::ins3(LOpcode op, LIns* a, LIns* b, LIns* c) - { - int nArgs = 3; - LTy formals[3] = { LTy_I, LTy_V, LTy_V }; // LTy_V gets overwritten - LIns* args[3] = { a, b, c }; - - switch (op) { - case LIR_cmovi: - checkLInsIsACondOrConst(op, 1, a); - formals[1] = LTy_I; - formals[2] = LTy_I; - break; - -#ifdef NANOJIT_64BIT - case LIR_cmovq: - checkLInsIsACondOrConst(op, 1, a); - formals[1] = LTy_Q; - formals[2] = LTy_Q; - break; -#endif - - case LIR_cmovd: - checkLInsIsACondOrConst(op, 1, a); - formals[1] = LTy_D; - formals[2] = LTy_D; - break; - - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->ins3(op, a, b, c); - } - - LIns* ValidateWriter::insParam(int32_t arg, int32_t kind) - { - return out->insParam(arg, kind); - } - - LIns* ValidateWriter::insImmI(int32_t imm) - { - return out->insImmI(imm); - } - -#ifdef NANOJIT_64BIT - LIns* ValidateWriter::insImmQ(uint64_t imm) - { - return out->insImmQ(imm); - } -#endif - - LIns* ValidateWriter::insImmD(double d) - { - return out->insImmD(d); - } - - static const char* argtypeNames[] = { - "void", // ARGTYPE_V = 0 - "int32_t", // ARGTYPE_I = 1 - "uint32_t", // ARGTYPE_UI = 2 - "uint64_t", // ARGTYPE_Q = 3 - "double" // ARGTYPE_D = 4 - }; - - LIns* ValidateWriter::insCall(const CallInfo *ci, LIns* args0[]) - { - ArgType argTypes[MAXARGS]; - uint32_t nArgs = ci->getArgTypes(argTypes); - LTy formals[MAXARGS]; - LIns* args[MAXARGS]; // in left-to-right order, unlike args0[] - - LOpcode op = getCallOpcode(ci); - ArgType retType = ci->returnType(); - - if ((op == LIR_callv) != (retType == ARGTYPE_V) || - (op == LIR_calli) != (retType == ARGTYPE_UI || - retType == ARGTYPE_I) || -#ifdef NANOJIT_64BIT - (op == LIR_callq) != (retType == ARGTYPE_Q) || -#endif - (op == LIR_calld) != (retType == ARGTYPE_D)) { - NanoAssertMsgf(0, - "LIR structure error (%s): return type mismatch: opcode %s with %s return type", - whereInPipeline, lirNames[op], argtypeNames[retType]); - } - - if (op == LIR_callv && ci->_isPure) { - // Since nobody can use the result of a void call, any pure call - // would just be dead. This is probably a mistake. - NanoAssertMsgf(0, - "LIR structure error (%s): LIR_callv must only be used with nonpure functions.", - whereInPipeline); - } - - if (ci->_isPure && ci->_storeAccSet != ACCSET_NONE) - errorAccSet(ci->_name, ci->_storeAccSet, "it should be ACCSET_NONE for pure functions"); - - // This loop iterates over the args from right-to-left (because arg() - // and getArgTypes() use right-to-left order), but puts the results - // into formals[] and args[] in left-to-right order so that arg - // numbers in error messages make sense to the user. - for (uint32_t i = 0; i < nArgs; i++) { - uint32_t i2 = nArgs - i - 1; // converts right-to-left to left-to-right - switch (argTypes[i]) { - case ARGTYPE_I: - case ARGTYPE_UI: formals[i2] = LTy_I; break; -#ifdef NANOJIT_64BIT - case ARGTYPE_Q: formals[i2] = LTy_Q; break; -#endif - case ARGTYPE_D: formals[i2] = LTy_D; break; - default: NanoAssertMsgf(0, "%d %s\n", argTypes[i],ci->_name); formals[i2] = LTy_V; break; - } - args[i2] = args0[i]; - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->insCall(ci, args0); - } - - LIns* ValidateWriter::insGuard(LOpcode op, LIns *cond, GuardRecord *gr) - { - int nArgs = -1; // init to shut compilers up - LTy formals[1]; - LIns* args[1]; - - switch (op) { - case LIR_x: - case LIR_xbarrier: - checkLInsIsNull(op, 1, cond); - nArgs = 0; - break; - - case LIR_xt: - case LIR_xf: - checkLInsIsACondOrConst(op, 1, cond); - nArgs = 1; - formals[0] = LTy_I; - args[0] = cond; - break; - - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->insGuard(op, cond, gr); - } - - LIns* ValidateWriter::insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord* gr) - { - int nArgs = 2; - LTy formals[2] = { LTy_I, LTy_I }; - LIns* args[2] = { a, b }; - - switch (op) { - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - break; - - default: - NanoAssert(0); - } - - typeCheckArgs(op, nArgs, formals, args); - - return out->insGuardXov(op, a, b, gr); - } - - LIns* ValidateWriter::insBranch(LOpcode op, LIns* cond, LIns* to) - { - int nArgs = -1; // init to shut compilers up - LTy formals[1]; - LIns* args[1]; - - switch (op) { - case LIR_j: - checkLInsIsNull(op, 1, cond); - nArgs = 0; - break; - - case LIR_jt: - case LIR_jf: - checkLInsIsACondOrConst(op, 1, cond); - nArgs = 1; - formals[0] = LTy_I; - args[0] = cond; - break; - - default: - NanoAssert(0); - } - - // We check that target is a label in ValidateReader because it may - // not have been set here. - - typeCheckArgs(op, nArgs, formals, args); - - return out->insBranch(op, cond, to); - } - - LIns* ValidateWriter::insBranchJov(LOpcode op, LIns* a, LIns* b, LIns* to) - { - int nArgs = 2; - LTy formals[2]; - LIns* args[2] = { a, b }; - - switch (op) { - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - formals[0] = LTy_I; - formals[1] = LTy_I; - break; - -#ifdef NANOJIT_64BIT - case LIR_addjovq: - case LIR_subjovq: - formals[0] = LTy_Q; - formals[1] = LTy_Q; - break; -#endif - default: - NanoAssert(0); - } - - // We check that target is a label in ValidateReader because it may - // not have been set here. - - typeCheckArgs(op, nArgs, formals, args); - - return out->insBranchJov(op, a, b, to); - } - - LIns* ValidateWriter::insAlloc(int32_t size) - { - return out->insAlloc(size); - } - - LIns* ValidateWriter::insJtbl(LIns* index, uint32_t size) - { - int nArgs = 1; - LTy formals[1] = { LTy_I }; - LIns* args[1] = { index }; - - typeCheckArgs(LIR_jtbl, nArgs, formals, args); - - // We check that all jump table entries are labels in ValidateReader - // because they won't have been set here. - - return out->insJtbl(index, size); - } - - ValidateReader::ValidateReader(LirFilter* in) : LirFilter(in) - {} - - LIns* ValidateReader::read() - { - LIns *ins = in->read(); - switch (ins->opcode()) { - case LIR_jt: - case LIR_jf: - case LIR_j: - NanoAssert(ins->getTarget() && ins->oprnd2()->isop(LIR_label)); - break; - - case LIR_addjovi: - case LIR_subjovi: - case LIR_muljovi: - CASE64(LIR_addjovq:) - CASE64(LIR_subjovq:) - NanoAssert(ins->getTarget() && ins->oprnd3()->isop(LIR_label)); - break; - - case LIR_jtbl: { - uint32_t tableSize = ins->getTableSize(); - NanoAssert(tableSize > 0); - for (uint32_t i = 0; i < tableSize; i++) { - LIns* target = ins->getTarget(i); - NanoAssert(target); - NanoAssert(target->isop(LIR_label)); - } - break; - } - default: - ; - } - return ins; - } - -#endif -#endif - -} diff --git a/deps/mozjs/js/src/nanojit/LIR.h b/deps/mozjs/js/src/nanojit/LIR.h deleted file mode 100644 index 95562452577..00000000000 --- a/deps/mozjs/js/src/nanojit/LIR.h +++ /dev/null @@ -1,2475 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_LIR__ -#define __nanojit_LIR__ - -namespace nanojit -{ - enum LOpcode -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum - : unsigned -#endif - { -#define OP___(op, number, repKind, retType, isCse) \ - LIR_##op = (number), -#include "LIRopcode.tbl" - LIR_sentinel, -#undef OP___ - -#ifdef NANOJIT_64BIT -# define PTR_SIZE(a,b) b -#else -# define PTR_SIZE(a,b) a -#endif - - // Pointer-sized synonyms. - - LIR_paramp = PTR_SIZE(LIR_parami, LIR_paramq), - - LIR_retp = PTR_SIZE(LIR_reti, LIR_retq), - - LIR_livep = PTR_SIZE(LIR_livei, LIR_liveq), - - LIR_ldp = PTR_SIZE(LIR_ldi, LIR_ldq), - - LIR_stp = PTR_SIZE(LIR_sti, LIR_stq), - - LIR_callp = PTR_SIZE(LIR_calli, LIR_callq), - - LIR_eqp = PTR_SIZE(LIR_eqi, LIR_eqq), - LIR_ltp = PTR_SIZE(LIR_lti, LIR_ltq), - LIR_gtp = PTR_SIZE(LIR_gti, LIR_gtq), - LIR_lep = PTR_SIZE(LIR_lei, LIR_leq), - LIR_gep = PTR_SIZE(LIR_gei, LIR_geq), - LIR_ltup = PTR_SIZE(LIR_ltui, LIR_ltuq), - LIR_gtup = PTR_SIZE(LIR_gtui, LIR_gtuq), - LIR_leup = PTR_SIZE(LIR_leui, LIR_leuq), - LIR_geup = PTR_SIZE(LIR_geui, LIR_geuq), - - LIR_addp = PTR_SIZE(LIR_addi, LIR_addq), - LIR_subp = PTR_SIZE(LIR_subi, LIR_subq), - LIR_addjovp = PTR_SIZE(LIR_addjovi, LIR_addjovq), - - LIR_andp = PTR_SIZE(LIR_andi, LIR_andq), - LIR_orp = PTR_SIZE(LIR_ori, LIR_orq), - LIR_xorp = PTR_SIZE(LIR_xori, LIR_xorq), - - LIR_lshp = PTR_SIZE(LIR_lshi, LIR_lshq), - LIR_rshp = PTR_SIZE(LIR_rshi, LIR_rshq), - LIR_rshup = PTR_SIZE(LIR_rshui, LIR_rshuq), - - LIR_cmovp = PTR_SIZE(LIR_cmovi, LIR_cmovq) - }; - - // 32-bit integer comparisons must be contiguous, as must 64-bit integer - // comparisons and 64-bit float comparisons. - NanoStaticAssert(LIR_eqi + 1 == LIR_lti && - LIR_eqi + 2 == LIR_gti && - LIR_eqi + 3 == LIR_lei && - LIR_eqi + 4 == LIR_gei && - LIR_eqi + 5 == LIR_ltui && - LIR_eqi + 6 == LIR_gtui && - LIR_eqi + 7 == LIR_leui && - LIR_eqi + 8 == LIR_geui); -#ifdef NANOJIT_64BIT - NanoStaticAssert(LIR_eqq + 1 == LIR_ltq && - LIR_eqq + 2 == LIR_gtq && - LIR_eqq + 3 == LIR_leq && - LIR_eqq + 4 == LIR_geq && - LIR_eqq + 5 == LIR_ltuq && - LIR_eqq + 6 == LIR_gtuq && - LIR_eqq + 7 == LIR_leuq && - LIR_eqq + 8 == LIR_geuq); -#endif - NanoStaticAssert(LIR_eqd + 1 == LIR_ltd && - LIR_eqd + 2 == LIR_gtd && - LIR_eqd + 3 == LIR_led && - LIR_eqd + 4 == LIR_ged); - - // Various opcodes must be changeable to their opposite with op^1 - // (although we use invertXyz() when possible, ie. outside static - // assertions). - NanoStaticAssert((LIR_jt^1) == LIR_jf && (LIR_jf^1) == LIR_jt); - - NanoStaticAssert((LIR_xt^1) == LIR_xf && (LIR_xf^1) == LIR_xt); - - NanoStaticAssert((LIR_lti^1) == LIR_gti && (LIR_gti^1) == LIR_lti); - NanoStaticAssert((LIR_lei^1) == LIR_gei && (LIR_gei^1) == LIR_lei); - NanoStaticAssert((LIR_ltui^1) == LIR_gtui && (LIR_gtui^1) == LIR_ltui); - NanoStaticAssert((LIR_leui^1) == LIR_geui && (LIR_geui^1) == LIR_leui); - -#ifdef NANOJIT_64BIT - NanoStaticAssert((LIR_ltq^1) == LIR_gtq && (LIR_gtq^1) == LIR_ltq); - NanoStaticAssert((LIR_leq^1) == LIR_geq && (LIR_geq^1) == LIR_leq); - NanoStaticAssert((LIR_ltuq^1) == LIR_gtuq && (LIR_gtuq^1) == LIR_ltuq); - NanoStaticAssert((LIR_leuq^1) == LIR_geuq && (LIR_geuq^1) == LIR_leuq); -#endif - - NanoStaticAssert((LIR_ltd^1) == LIR_gtd && (LIR_gtd^1) == LIR_ltd); - NanoStaticAssert((LIR_led^1) == LIR_ged && (LIR_ged^1) == LIR_led); - - - struct GuardRecord; - struct SideExit; - - enum AbiKind { - ABI_FASTCALL, - ABI_THISCALL, - ABI_STDCALL, - ABI_CDECL - }; - - // This is much the same as LTy, but we need to distinguish signed and - // unsigned 32-bit ints so that they will be extended to 64-bits correctly - // on 64-bit platforms. - // - // All values must fit into three bits. See CallInfo for details. - enum ArgType { - ARGTYPE_V = 0, // void - ARGTYPE_I = 1, // int32_t - ARGTYPE_UI = 2, // uint32_t -#ifdef NANOJIT_64BIT - ARGTYPE_Q = 3, // uint64_t -#endif - ARGTYPE_D = 4, // double - - // aliases - ARGTYPE_P = PTR_SIZE(ARGTYPE_I, ARGTYPE_Q), // pointer - ARGTYPE_B = ARGTYPE_I // bool - }; - - enum IndirectCall { - CALL_INDIRECT = 0 - }; - - //----------------------------------------------------------------------- - // Aliasing - // -------- - // *Aliasing* occurs when a single memory location can be accessed through - // multiple names. For example, consider this code: - // - // ld a[0] - // sti b[0] - // ld a[0] - // - // In general, it's possible that a[0] and b[0] may refer to the same - // memory location. This means, for example, that you cannot safely - // perform CSE on the two loads. However, if you know that 'a' cannot be - // an alias of 'b' (ie. the two loads do not alias with the store) then - // you can safely perform CSE. - // - // Access regions - // -------------- - // Doing alias analysis precisely is difficult. But it turns out that - // keeping track of aliasing at a coarse level is enough to help with many - // optimisations. So we conceptually divide the memory that is accessible - // from LIR into a small number of "access regions" (aka. "Acc"). An - // access region may be non-contiguous. No two access regions can - // overlap. The union of all access regions covers all memory accessible - // from LIR. - // - // In general a (static) load or store may be executed more than once, and - // thus may access multiple regions; however, in practice almost all - // loads and stores will obviously access only a single region. A - // function called from LIR may load and/or store multiple access regions - // (even if executed only once). - // - // If two loads/stores/calls are known to not access the same region(s), - // then they do not alias. - // - // All regions are defined by the embedding. It makes sense to add new - // embedding-specific access regions when doing so will help with one or - // more optimisations. - // - // Access region sets and instruction markings - // ------------------------------------------- - // Each load/store is marked with an "access region set" (aka. "AccSet"), - // which is a set of one or more access regions. This indicates which - // parts of LIR-accessible memory the load/store may touch. - // - // Each function called from LIR is also marked with an access region set - // for memory stored to by the function. (We could also have a marking - // for memory loads done by the function, but there's no need at the - // moment.) These markings apply to the function itself, not the call - // site, ie. they're not context-sensitive. - // - // These load/store/call markings MUST BE ACCURATE -- if not then invalid - // optimisations might occur that change the meaning of the code. - // However, they can safely be imprecise (ie. conservative), ie. a - // load/store/call can be marked with an access region set that is a - // superset of the actual access region set. Such imprecision is safe but - // may reduce optimisation opportunities. - // - // Optimisations that use access region info - // ----------------------------------------- - // Currently only CseFilter uses this, and only for determining whether - // loads can be CSE'd. Note that CseFilter treats loads that are marked - // with a single access region precisely, but all loads marked with - // multiple access regions get lumped together. So if you can't mark a - // load with a single access region, you might as well use ACC_LOAD_ANY. - //----------------------------------------------------------------------- - - // An access region set is represented as a bitset. Using a uint32_t - // restricts us to at most 32 alias regions for the moment. This could be - // expanded to a uint64_t easily if needed. - typedef uint32_t AccSet; - static const int NUM_ACCS = sizeof(AccSet) * 8; - - // Some common (non-singleton) access region sets. ACCSET_NONE does not make - // sense for loads or stores (which must access at least one region), it - // only makes sense for calls. - // - static const AccSet ACCSET_NONE = 0x0; - static const AccSet ACCSET_ALL = 0xffffffff; - static const AccSet ACCSET_LOAD_ANY = ACCSET_ALL; // synonym - static const AccSet ACCSET_STORE_ANY = ACCSET_ALL; // synonym - - inline bool isSingletonAccSet(AccSet accSet) { - // This is a neat way of testing if a value has only one bit set. - return (accSet & (accSet - 1)) == 0; - } - - // Full AccSets don't fit into load and store instructions. But - // load/store AccSets almost always contain a single access region. We - // take advantage of this to create a compressed AccSet, MiniAccSet, that - // does fit. - // - // The 32 single-region AccSets get compressed into a number in the range - // 0..31 (according to the position of the set bit), and all other - // (multi-region) AccSets get converted into MINI_ACCSET_MULTIPLE. So the - // representation is lossy in the latter case, but that case is rare for - // loads/stores. We use a full AccSet for the storeAccSets of calls, for - // which multi-region AccSets are common. - // - // We wrap the uint8_t inside a struct to avoid the possiblity of subtle - // bugs caused by mixing up AccSet and MiniAccSet, which is easy to do. - // However, the struct gets padded inside LInsLd in an inconsistent way on - // Windows, so we actually store a MiniAccSetVal inside LInsLd. Sigh. - // But we use MiniAccSet everywhere else. - // - typedef uint8_t MiniAccSetVal; - struct MiniAccSet { MiniAccSetVal val; }; - static const MiniAccSet MINI_ACCSET_MULTIPLE = { 99 }; - - static MiniAccSet compressAccSet(AccSet accSet) { - if (isSingletonAccSet(accSet)) { - MiniAccSet ret = { uint8_t(msbSet32(accSet)) }; - return ret; - } - - // If we got here, it must be a multi-region AccSet. - return MINI_ACCSET_MULTIPLE; - } - - static AccSet decompressMiniAccSet(MiniAccSet miniAccSet) { - return (miniAccSet.val == MINI_ACCSET_MULTIPLE.val) ? ACCSET_ALL : (1 << miniAccSet.val); - } - - // The LoadQual affects how a load can be optimised: - // - // - CONST: These loads are guaranteed to always return the same value - // during a single execution of a fragment (but the value is allowed to - // change between executions of the fragment). This means that the - // location is never stored to by the LIR, and is never modified by an - // external entity while the fragment is running. - // - // - NORMAL: These loads may be stored to by the LIR, but are never - // modified by an external entity while the fragment is running. - // - // - VOLATILE: These loads may be stored to by the LIR, and may be - // modified by an external entity while the fragment is running. - // - // This gives a lattice with the ordering: CONST < NORMAL < VOLATILE. - // As usual, it's safe to mark a load with a value higher (less precise) - // that actual, but it may result in fewer optimisations occurring. - // - // Generally CONST loads are highly amenable to optimisation (eg. CSE), - // VOLATILE loads are entirely unoptimisable, and NORMAL loads are in - // between and require some alias analysis to optimise. - // - // Note that CONST has a stronger meaning to "const" in C and C++; in C - // and C++ a "const" variable may be modified by an external entity, such - // as hardware. Hence "const volatile" makes sense in C and C++, but - // CONST+VOLATILE doesn't make sense in LIR. - // - // Note also that a 2-bit bitfield in LInsLd is used to hold LoadQual - // values, so you can one add one more value without expanding it. - // - enum LoadQual { - LOAD_CONST = 0, - LOAD_NORMAL = 1, - LOAD_VOLATILE = 2 - }; - - struct CallInfo - { - private: - // In CallInfo::_typesig, each entry is three bits. - static const int TYPESIG_FIELDSZB = 3; - static const int TYPESIG_FIELDMASK = 7; - - public: - uintptr_t _address; - uint32_t _typesig:27; // 9 3-bit fields indicating arg type, by ARGTYPE above (including ret type): a1 a2 a3 a4 a5 ret - AbiKind _abi:3; - uint32_t _isPure:1; // _isPure=1 means no side-effects, result only depends on args - AccSet _storeAccSet; // access regions stored by the function - verbose_only ( const char* _name; ) - - // The following encode 'r func()' through to 'r func(a1, a2, a3, a4, a5, a6, a7, a8)'. - static inline uint32_t typeSig0(ArgType r) { - return r; - } - static inline uint32_t typeSig1(ArgType r, ArgType a1) { - return a1 << TYPESIG_FIELDSZB*1 | typeSig0(r); - } - static inline uint32_t typeSig2(ArgType r, ArgType a1, ArgType a2) { - return a1 << TYPESIG_FIELDSZB*2 | typeSig1(r, a2); - } - static inline uint32_t typeSig3(ArgType r, ArgType a1, ArgType a2, ArgType a3) { - return a1 << TYPESIG_FIELDSZB*3 | typeSig2(r, a2, a3); - } - static inline uint32_t typeSig4(ArgType r, ArgType a1, ArgType a2, ArgType a3, ArgType a4) { - return a1 << TYPESIG_FIELDSZB*4 | typeSig3(r, a2, a3, a4); - } - static inline uint32_t typeSig5(ArgType r, ArgType a1, ArgType a2, ArgType a3, - ArgType a4, ArgType a5) { - return a1 << TYPESIG_FIELDSZB*5 | typeSig4(r, a2, a3, a4, a5); - } - static inline uint32_t typeSig6(ArgType r, ArgType a1, ArgType a2, ArgType a3, - ArgType a4, ArgType a5, ArgType a6) { - return a1 << TYPESIG_FIELDSZB*6 | typeSig5(r, a2, a3, a4, a5, a6); - } - static inline uint32_t typeSig7(ArgType r, ArgType a1, ArgType a2, ArgType a3, - ArgType a4, ArgType a5, ArgType a6, ArgType a7) { - return a1 << TYPESIG_FIELDSZB*7 | typeSig6(r, a2, a3, a4, a5, a6, a7); - } - static inline uint32_t typeSig8(ArgType r, ArgType a1, ArgType a2, ArgType a3, ArgType a4, - ArgType a5, ArgType a6, ArgType a7, ArgType a8) { - return a1 << TYPESIG_FIELDSZB*8 | typeSig7(r, a2, a3, a4, a5, a6, a7, a8); - } - // Encode 'r func(a1, ..., aN))' - static inline uint32_t typeSigN(ArgType r, int N, ArgType a[]) { - uint32_t typesig = r; - for (int i = 0; i < N; i++) { - typesig |= a[i] << TYPESIG_FIELDSZB*(N-i); - } - return typesig; - } - - uint32_t count_args() const; - uint32_t count_int32_args() const; - // Nb: uses right-to-left order, eg. sizes[0] is the size of the right-most arg. - // XXX: See bug 525815 for fixing this. - uint32_t getArgTypes(ArgType* types) const; - - inline ArgType returnType() const { - return ArgType(_typesig & TYPESIG_FIELDMASK); - } - - inline bool isIndirect() const { - return _address < 256; - } - }; - - // Array holding the 'isCse' field from LIRopcode.tbl. - extern const int8_t isCses[]; // cannot be uint8_t, some values are negative - - inline bool isCseOpcode(LOpcode op) { - NanoAssert(isCses[op] != -1); // see LIRopcode.tbl to understand this - return isCses[op] == 1; - } - inline bool isLiveOpcode(LOpcode op) { - return -#if defined NANOJIT_64BIT - op == LIR_liveq || -#endif - op == LIR_livei || op == LIR_lived; - } - inline bool isRetOpcode(LOpcode op) { - return -#if defined NANOJIT_64BIT - op == LIR_retq || -#endif - op == LIR_reti || op == LIR_retd; - } - inline bool isCmovOpcode(LOpcode op) { - return -#if defined NANOJIT_64BIT - op == LIR_cmovq || -#endif - op == LIR_cmovi || - op == LIR_cmovd; - } - inline bool isCmpIOpcode(LOpcode op) { - return LIR_eqi <= op && op <= LIR_geui; - } - inline bool isCmpSIOpcode(LOpcode op) { - return LIR_eqi <= op && op <= LIR_gei; - } - inline bool isCmpUIOpcode(LOpcode op) { - return LIR_eqi == op || (LIR_ltui <= op && op <= LIR_geui); - } -#ifdef NANOJIT_64BIT - inline bool isCmpQOpcode(LOpcode op) { - return LIR_eqq <= op && op <= LIR_geuq; - } - inline bool isCmpSQOpcode(LOpcode op) { - return LIR_eqq <= op && op <= LIR_geq; - } - inline bool isCmpUQOpcode(LOpcode op) { - return LIR_eqq == op || (LIR_ltuq <= op && op <= LIR_geuq); - } -#endif - inline bool isCmpDOpcode(LOpcode op) { - return LIR_eqd <= op && op <= LIR_ged; - } - inline bool isCmpOpcode(LOpcode op) { - return isCmpIOpcode(op) || -#if defined NANOJIT_64BIT - isCmpQOpcode(op) || -#endif - isCmpDOpcode(op); - } - - inline LOpcode invertCondJmpOpcode(LOpcode op) { - NanoAssert(op == LIR_jt || op == LIR_jf); - return LOpcode(op ^ 1); - } - inline LOpcode invertCondGuardOpcode(LOpcode op) { - NanoAssert(op == LIR_xt || op == LIR_xf); - return LOpcode(op ^ 1); - } - inline LOpcode invertCmpOpcode(LOpcode op) { - NanoAssert(isCmpOpcode(op)); - return LOpcode(op ^ 1); - } - - inline LOpcode getCallOpcode(const CallInfo* ci) { - LOpcode op = LIR_callp; - switch (ci->returnType()) { - case ARGTYPE_V: op = LIR_callv; break; - case ARGTYPE_I: - case ARGTYPE_UI: op = LIR_calli; break; -#ifdef NANOJIT_64BIT - case ARGTYPE_Q: op = LIR_callq; break; -#endif - case ARGTYPE_D: op = LIR_calld; break; - default: NanoAssert(0); break; - } - return op; - } - - LOpcode arithOpcodeD2I(LOpcode op); -#ifdef NANOJIT_64BIT - LOpcode cmpOpcodeI2Q(LOpcode op); -#endif - LOpcode cmpOpcodeD2I(LOpcode op); - LOpcode cmpOpcodeD2UI(LOpcode op); - - // Array holding the 'repKind' field from LIRopcode.tbl. - extern const uint8_t repKinds[]; - - enum LTy { - LTy_V, // void: no value/no type - LTy_I, // int: 32-bit integer -#ifdef NANOJIT_64BIT - LTy_Q, // quad: 64-bit integer -#endif - LTy_D, // double: 64-bit float - - LTy_P = PTR_SIZE(LTy_I, LTy_Q) // word-sized integer - }; - - // Array holding the 'retType' field from LIRopcode.tbl. - extern const LTy retTypes[]; - - // Array holding the size in bytes of each LIns from LIRopcode.tbl. - extern const uint8_t insSizes[]; - - inline RegisterMask rmask(Register r) - { - return RegisterMask(1) << REGNUM(r); - } - - //----------------------------------------------------------------------- - // Low-level instructions. This is a bit complicated, because we have a - // variable-width representation to minimise space usage. - // - // - Instruction size is always an integral multiple of word size. - // - // - Every instruction has at least one word, holding the opcode and the - // reservation info ("SharedFields"). That word is in class LIns. - // - // - Beyond that, most instructions have 1, 2 or 3 extra words. These - // extra words are in classes LInsOp1, LInsOp2, etc (collectively called - // "LInsXYZ" in what follows). Each LInsXYZ class also contains an LIns, - // accessible by the 'ins' member, which holds the LIns data. - // - // - LIR is written forward, but read backwards. When reading backwards, - // in order to find the opcode, it must be in a predictable place in the - // LInsXYZ isn't affected by instruction width. Therefore, the LIns - // word (which contains the opcode) is always the *last* word in an - // instruction. - // - // - Each instruction is created by casting pre-allocated bytes from a - // LirBuffer to the LInsXYZ type. Therefore there are no constructors - // for LIns or LInsXYZ. - // - // - The standard handle for an instruction is a LIns*. This actually - // points to the LIns word, ie. to the final word in the instruction. - // This is a bit odd, but it allows the instruction's opcode to be - // easily accessed. Once you've looked at the opcode and know what kind - // of instruction it is, if you want to access any of the other words, - // you need to use toLInsXYZ(), which takes the LIns* and gives you an - // LInsXYZ*, ie. the pointer to the actual start of the instruction's - // bytes. From there you can access the instruction-specific extra - // words. - // - // - However, from outside class LIns, LInsXYZ isn't visible, nor is - // toLInsXYZ() -- from outside LIns, all LIR instructions are handled - // via LIns pointers and get/set methods are used for all LIns/LInsXYZ - // accesses. In fact, all data members in LInsXYZ are private and can - // only be accessed by LIns, which is a friend class. The only thing - // anyone outside LIns can do with a LInsXYZ is call getLIns(). - // - // - An example Op2 instruction and the likely pointers to it (each line - // represents a word, and pointers to a line point to the start of the - // word on that line): - // - // [ oprnd_2 <-- LInsOp2* insOp2 == toLInsOp2(ins) - // oprnd_1 - // opcode + resv ] <-- LIns* ins - // - // - LIR_skip instructions are used to link code chunks. If the first - // instruction on a chunk isn't a LIR_start, it will be a skip, and the - // skip's operand will point to the last LIns on the preceding chunk. - // LInsSk has the same layout as LInsOp1, but we represent it as a - // different class because there are some places where we treat - // skips specially and so having it separate seems like a good idea. - // - // - Various things about the size and layout of LIns and LInsXYZ are - // statically checked in staticSanityCheck(). In particular, this is - // worthwhile because there's nothing that guarantees that all the - // LInsXYZ classes have a size that is a multiple of word size (but in - // practice all sane compilers use a layout that results in this). We - // also check that every LInsXYZ is word-aligned in - // LirBuffer::makeRoom(); this seems sensible to avoid potential - // slowdowns due to misalignment. It relies on chunks themselves being - // word-aligned, which is extremely likely. - // - // - There is an enum, LInsRepKind, with one member for each of the - // LInsXYZ kinds. Each opcode is categorised with its LInsRepKind value - // in LIRopcode.tbl, and this is used in various places. - //----------------------------------------------------------------------- - - enum LInsRepKind { - // LRK_XYZ corresponds to class LInsXYZ. - LRK_Op0, - LRK_Op1, - LRK_Op2, - LRK_Op3, - LRK_Ld, - LRK_St, - LRK_Sk, - LRK_C, - LRK_P, - LRK_I, - LRK_QorD, - LRK_Jtbl, - LRK_None // this one is used for unused opcode numbers - }; - - class LInsOp0; - class LInsOp1; - class LInsOp2; - class LInsOp3; - class LInsLd; - class LInsSt; - class LInsSk; - class LInsC; - class LInsP; - class LInsI; - class LInsQorD; - class LInsJtbl; - - class LIns - { - private: - // SharedFields: fields shared by all LIns kinds. - // - // The .inReg, .regnum, .inAr and .arIndex fields form a "reservation" - // that is used temporarily during assembly to record information - // relating to register allocation. See class RegAlloc for more - // details. Note: all combinations of .inReg/.inAr are possible, ie. - // 0/0, 0/1, 1/0, 1/1. - // - // The .isResultLive field is only used for instructions that return - // results. It indicates if the result is live. It's set (if - // appropriate) and used only during the codegen pass. - // - struct SharedFields { - uint32_t inReg:1; // if 1, 'reg' is active - uint32_t regnum:7; - uint32_t inAr:1; // if 1, 'arIndex' is active - uint32_t isResultLive:1; // if 1, the instruction's result is live - - uint32_t arIndex:14; // index into stack frame; displ is -4*arIndex - - LOpcode opcode:8; // instruction's opcode - }; - - union { - SharedFields sharedFields; - // Force sizeof(LIns)==8 and 8-byte alignment on 64-bit machines. - // This is necessary because sizeof(SharedFields)==4 and we want all - // instances of LIns to be pointer-aligned. - void* wholeWord; - }; - - inline void initSharedFields(LOpcode opcode) - { - // We must zero .inReg, .inAR and .isResultLive, but zeroing the - // whole word is easier. Then we set the opcode. - wholeWord = 0; - sharedFields.opcode = opcode; - } - - // LIns-to-LInsXYZ converters. - inline LInsOp0* toLInsOp0() const; - inline LInsOp1* toLInsOp1() const; - inline LInsOp2* toLInsOp2() const; - inline LInsOp3* toLInsOp3() const; - inline LInsLd* toLInsLd() const; - inline LInsSt* toLInsSt() const; - inline LInsSk* toLInsSk() const; - inline LInsC* toLInsC() const; - inline LInsP* toLInsP() const; - inline LInsI* toLInsI() const; - inline LInsQorD* toLInsQorD() const; - inline LInsJtbl*toLInsJtbl()const; - - void staticSanityCheck(); - - public: - // LIns initializers. - inline void initLInsOp0(LOpcode opcode); - inline void initLInsOp1(LOpcode opcode, LIns* oprnd1); - inline void initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2); - inline void initLInsOp3(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3); - inline void initLInsLd(LOpcode opcode, LIns* val, int32_t d, AccSet accSet, LoadQual loadQual); - inline void initLInsSt(LOpcode opcode, LIns* val, LIns* base, int32_t d, AccSet accSet); - inline void initLInsSk(LIns* prevLIns); - // Nb: args[] must be allocated and initialised before being passed in; - // initLInsC() just copies the pointer into the LInsC. - inline void initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci); - inline void initLInsP(int32_t arg, int32_t kind); - inline void initLInsI(LOpcode opcode, int32_t immI); - inline void initLInsQorD(LOpcode opcode, uint64_t immQorD); - inline void initLInsJtbl(LIns* index, uint32_t size, LIns** table); - - LOpcode opcode() const { return sharedFields.opcode; } - - // Generally, void instructions (statements) are always live and - // non-void instructions (expressions) are live if used by another - // live instruction. But there are some trickier cases. - // Any non-void instruction can be marked isResultLive=1 even - // when it is unreachable, e.g. due to an always-taken branch. - // The assembler marks it live if it sees any uses, regardless of - // whether those uses are in reachable code or not. - bool isLive() const { - return isV() || - sharedFields.isResultLive || - (isCall() && !callInfo()->_isPure) || // impure calls are always live - isop(LIR_paramp); // LIR_paramp is always live - } - void setResultLive() { - NanoAssert(!isV()); - sharedFields.isResultLive = 1; - } - - // XXX: old reservation manipulating functions. See bug 538924. - // Replacement strategy: - // - deprecated_markAsClear() --> clearReg() and/or clearArIndex() - // - deprecated_hasKnownReg() --> isInReg() - // - deprecated_getReg() --> getReg() after checking isInReg() - // - void deprecated_markAsClear() { - sharedFields.inReg = 0; - sharedFields.inAr = 0; - } - bool deprecated_hasKnownReg() { - NanoAssert(isExtant()); - return isInReg(); - } - Register deprecated_getReg() { - NanoAssert(isExtant()); - if (isInReg()) { - Register r = { sharedFields.regnum }; - return r; - } else { - return deprecated_UnknownReg; - } - } - uint32_t deprecated_getArIndex() { - NanoAssert(isExtant()); - return ( isInAr() ? sharedFields.arIndex : 0 ); - } - - // Reservation manipulation. - // - // "Extant" mean "in existence, still existing, surviving". In other - // words, has the value been computed explicitly (not folded into - // something else) and is it still available (in a register or spill - // slot) for use? - bool isExtant() { - return isInReg() || isInAr(); - } - bool isInReg() { - return sharedFields.inReg; - } - bool isInRegMask(RegisterMask allow) { - return isInReg() && (rmask(getReg()) & allow); - } - Register getReg() { - NanoAssert(isInReg()); - Register r = { sharedFields.regnum }; - return r; - } - void setReg(Register r) { - sharedFields.inReg = 1; - sharedFields.regnum = REGNUM(r); - } - void clearReg() { - sharedFields.inReg = 0; - } - bool isInAr() { - return sharedFields.inAr; - } - uint32_t getArIndex() { - NanoAssert(isInAr()); - return sharedFields.arIndex; - } - void setArIndex(uint32_t arIndex) { - sharedFields.inAr = 1; - sharedFields.arIndex = arIndex; - } - void clearArIndex() { - sharedFields.inAr = 0; - } - - // For various instruction kinds. - inline LIns* oprnd1() const; - inline LIns* oprnd2() const; - inline LIns* oprnd3() const; - - // For branches. - inline LIns* getTarget() const; - inline void setTarget(LIns* label); - - // For guards. - inline GuardRecord* record() const; - - // For loads. - inline LoadQual loadQual() const; - - // For loads/stores. - inline int32_t disp() const; - inline MiniAccSet miniAccSet() const; - inline AccSet accSet() const; - - // For LInsSk. - inline LIns* prevLIns() const; - - // For LInsP. - inline uint8_t paramArg() const; - inline uint8_t paramKind() const; - - // For LInsI. - inline int32_t immI() const; - - // For LInsQorD. -#ifdef NANOJIT_64BIT - inline int32_t immQlo() const; - inline uint64_t immQ() const; -#endif - inline int32_t immDlo() const; - inline int32_t immDhi() const; - inline double immD() const; - inline uint64_t immDasQ() const; - - // For LIR_allocp. - inline int32_t size() const; - inline void setSize(int32_t nbytes); - - // For LInsC. - inline LIns* arg(uint32_t i) const; // right-to-left-order: arg(0) is rightmost - inline uint32_t argc() const; - inline LIns* callArgN(uint32_t n) const; - inline const CallInfo* callInfo() const; - - // For LIR_jtbl - inline uint32_t getTableSize() const; - inline LIns* getTarget(uint32_t index) const; - inline void setTarget(uint32_t index, LIns* label) const; - - // isLInsXYZ() returns true if the instruction has the LInsXYZ form. - // Note that there is some overlap with other predicates, eg. - // isStore()==isLInsSt(), isCall()==isLInsC(), but that's ok; these - // ones are used mostly to check that opcodes are appropriate for - // instruction layouts, the others are used for non-debugging - // purposes. - bool isLInsOp0() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op0 == repKinds[opcode()]; - } - bool isLInsOp1() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op1 == repKinds[opcode()]; - } - bool isLInsOp2() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op2 == repKinds[opcode()]; - } - bool isLInsOp3() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op3 == repKinds[opcode()]; - } - bool isLInsLd() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Ld == repKinds[opcode()]; - } - bool isLInsSt() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_St == repKinds[opcode()]; - } - bool isLInsSk() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Sk == repKinds[opcode()]; - } - bool isLInsC() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_C == repKinds[opcode()]; - } - bool isLInsP() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_P == repKinds[opcode()]; - } - bool isLInsI() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_I == repKinds[opcode()]; - } - bool isLInsQorD() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_QorD == repKinds[opcode()]; - } - bool isLInsJtbl() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Jtbl == repKinds[opcode()]; - } - - // LIns predicates. - bool isop(LOpcode o) const { - return opcode() == o; - } - bool isRet() const { - return isRetOpcode(opcode()); - } - bool isCmp() const { - return isCmpOpcode(opcode()); - } - bool isCall() const { - return isop(LIR_callv) || - isop(LIR_calli) || -#if defined NANOJIT_64BIT - isop(LIR_callq) || -#endif - isop(LIR_calld); - } - bool isCmov() const { - return isCmovOpcode(opcode()); - } - bool isStore() const { - return isLInsSt(); - } - bool isLoad() const { - return isLInsLd(); - } - bool isGuard() const { - return isop(LIR_x) || isop(LIR_xf) || isop(LIR_xt) || isop(LIR_xbarrier) || - isop(LIR_addxovi) || isop(LIR_subxovi) || isop(LIR_mulxovi); - } - bool isJov() const { - return -#ifdef NANOJIT_64BIT - isop(LIR_addjovq) || isop(LIR_subjovq) || -#endif - isop(LIR_addjovi) || isop(LIR_subjovi) || isop(LIR_muljovi); - } - // True if the instruction is a 32-bit integer immediate. - bool isImmI() const { - return isop(LIR_immi); - } - // True if the instruction is a 32-bit integer immediate and - // has the value 'val' when treated as a 32-bit signed integer. - bool isImmI(int32_t val) const { - return isImmI() && immI()==val; - } -#ifdef NANOJIT_64BIT - // True if the instruction is a 64-bit integer immediate. - bool isImmQ() const { - return isop(LIR_immq); - } -#endif - // True if the instruction is a pointer-sized integer immediate. - bool isImmP() const - { -#ifdef NANOJIT_64BIT - return isImmQ(); -#else - return isImmI(); -#endif - } - // True if the instruction is a 64-bit float immediate. - bool isImmD() const { - return isop(LIR_immd); - } - // True if the instruction is a 64-bit integer or float immediate. - bool isImmQorD() const { - return -#ifdef NANOJIT_64BIT - isImmQ() || -#endif - isImmD(); - } - // True if the instruction an any type of immediate. - bool isImmAny() const { - return isImmI() || isImmQorD(); - } - - bool isBranch() const { - return isop(LIR_jt) || isop(LIR_jf) || isop(LIR_j) || isop(LIR_jtbl) || isJov(); - } - - LTy retType() const { - return retTypes[opcode()]; - } - bool isV() const { - return retType() == LTy_V; - } - bool isI() const { - return retType() == LTy_I; - } -#ifdef NANOJIT_64BIT - bool isQ() const { - return retType() == LTy_Q; - } -#endif - bool isD() const { - return retType() == LTy_D; - } - bool isQorD() const { - return -#ifdef NANOJIT_64BIT - isQ() || -#endif - isD(); - } - bool isP() const { -#ifdef NANOJIT_64BIT - return isQ(); -#else - return isI(); -#endif - } - - inline void* immP() const - { - #ifdef NANOJIT_64BIT - return (void*)immQ(); - #else - return (void*)immI(); - #endif - } - - void overwriteWithSkip(LIns* skipTo); - }; - - typedef SeqBuilder InsList; - typedef SeqBuilder StringList; - - - // 0-operand form. Used for LIR_start and LIR_label. - class LInsOp0 - { - private: - friend class LIns; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // 1-operand form. Used for LIR_reti, unary arithmetic/logic ops, etc. - class LInsOp1 - { - private: - friend class LIns; - - LIns* oprnd_1; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // 2-operand form. Used for guards, branches, comparisons, binary - // arithmetic/logic ops, etc. - class LInsOp2 - { - private: - friend class LIns; - - LIns* oprnd_2; - - LIns* oprnd_1; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // 3-operand form. Used for conditional moves, jov branches, and xov guards. - class LInsOp3 - { - private: - friend class LIns; - - LIns* oprnd_3; - - LIns* oprnd_2; - - LIns* oprnd_1; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for all loads. - class LInsLd - { - private: - friend class LIns; - - // Nb: the LIR writer pipeline handles things if a displacement - // exceeds 16 bits. This is rare, but does happen occasionally. We - // could go to 24 bits but then it would happen so rarely that the - // handler code would be difficult to test and thus untrustworthy. - // - // Nb: the types of these bitfields are all 32-bit integers to ensure - // they are fully packed on Windows, sigh. Also, 'loadQual' is - // unsigned to ensure the values 0, 1, and 2 all fit in 2 bits. - // - // Nb: explicit signed keyword for bitfield types is required, - // some compilers may treat them as unsigned without it. - // See Bugzilla 584219 comment #18 - signed int disp:16; - signed int miniAccSetVal:8; - uint32_t loadQual:2; - - LIns* oprnd_1; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for all stores. - class LInsSt - { - private: - friend class LIns; - - int16_t disp; - MiniAccSetVal miniAccSetVal; - - LIns* oprnd_2; - - LIns* oprnd_1; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for LIR_skip. - class LInsSk - { - private: - friend class LIns; - - LIns* prevLIns; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for all variants of LIR_call. - class LInsC - { - private: - friend class LIns; - - // Arguments in reverse order, just like insCall() (ie. args[0] holds - // the rightmost arg). The array should be allocated by the same - // allocator as the LIR buffers, because it has the same lifetime. - LIns** args; - - const CallInfo* ci; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for LIR_paramp. - class LInsP - { - private: - friend class LIns; - - uintptr_t arg:8; - uintptr_t kind:8; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for LIR_immi and LIR_allocp. - class LInsI - { - private: - friend class LIns; - - int32_t immI; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for LIR_immq and LIR_immd. - class LInsQorD - { - private: - friend class LIns; - - int32_t immQorDlo; - - int32_t immQorDhi; - - LIns ins; - - public: - LIns* getLIns() { return &ins; }; - }; - - // Used for LIR_jtbl. 'oprnd_1' must be a uint32_t index in - // the range 0 <= index < size; no range check is performed. - // 'table' is an array of labels. - class LInsJtbl - { - private: - friend class LIns; - - uint32_t size; // number of entries in table - LIns** table; // pointer to table[size] with same lifetime as this LInsJtbl - LIns* oprnd_1; // uint32_t index expression - - LIns ins; - - public: - LIns* getLIns() { return &ins; } - }; - - // Used only as a placeholder for OP___ macros for unused opcodes in - // LIRopcode.tbl. - class LInsNone - { - }; - - LInsOp0* LIns::toLInsOp0() const { return (LInsOp0* )(uintptr_t(this+1) - sizeof(LInsOp0 )); } - LInsOp1* LIns::toLInsOp1() const { return (LInsOp1* )(uintptr_t(this+1) - sizeof(LInsOp1 )); } - LInsOp2* LIns::toLInsOp2() const { return (LInsOp2* )(uintptr_t(this+1) - sizeof(LInsOp2 )); } - LInsOp3* LIns::toLInsOp3() const { return (LInsOp3* )(uintptr_t(this+1) - sizeof(LInsOp3 )); } - LInsLd* LIns::toLInsLd() const { return (LInsLd* )(uintptr_t(this+1) - sizeof(LInsLd )); } - LInsSt* LIns::toLInsSt() const { return (LInsSt* )(uintptr_t(this+1) - sizeof(LInsSt )); } - LInsSk* LIns::toLInsSk() const { return (LInsSk* )(uintptr_t(this+1) - sizeof(LInsSk )); } - LInsC* LIns::toLInsC() const { return (LInsC* )(uintptr_t(this+1) - sizeof(LInsC )); } - LInsP* LIns::toLInsP() const { return (LInsP* )(uintptr_t(this+1) - sizeof(LInsP )); } - LInsI* LIns::toLInsI() const { return (LInsI* )(uintptr_t(this+1) - sizeof(LInsI )); } - LInsQorD* LIns::toLInsQorD() const { return (LInsQorD*)(uintptr_t(this+1) - sizeof(LInsQorD)); } - LInsJtbl* LIns::toLInsJtbl() const { return (LInsJtbl*)(uintptr_t(this+1) - sizeof(LInsJtbl)); } - - void LIns::initLInsOp0(LOpcode opcode) { - initSharedFields(opcode); - NanoAssert(isLInsOp0()); - } - void LIns::initLInsOp1(LOpcode opcode, LIns* oprnd1) { - initSharedFields(opcode); - toLInsOp1()->oprnd_1 = oprnd1; - NanoAssert(isLInsOp1()); - } - void LIns::initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2) { - initSharedFields(opcode); - toLInsOp2()->oprnd_1 = oprnd1; - toLInsOp2()->oprnd_2 = oprnd2; - NanoAssert(isLInsOp2()); - } - void LIns::initLInsOp3(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3) { - initSharedFields(opcode); - toLInsOp3()->oprnd_1 = oprnd1; - toLInsOp3()->oprnd_2 = oprnd2; - toLInsOp3()->oprnd_3 = oprnd3; - NanoAssert(isLInsOp3()); - } - void LIns::initLInsLd(LOpcode opcode, LIns* val, int32_t d, AccSet accSet, LoadQual loadQual) { - initSharedFields(opcode); - toLInsLd()->oprnd_1 = val; - NanoAssert(d == int16_t(d)); - toLInsLd()->disp = int16_t(d); - toLInsLd()->miniAccSetVal = compressAccSet(accSet).val; - toLInsLd()->loadQual = loadQual; - NanoAssert(isLInsLd()); - } - void LIns::initLInsSt(LOpcode opcode, LIns* val, LIns* base, int32_t d, AccSet accSet) { - initSharedFields(opcode); - toLInsSt()->oprnd_1 = val; - toLInsSt()->oprnd_2 = base; - NanoAssert(d == int16_t(d)); - toLInsSt()->disp = int16_t(d); - toLInsSt()->miniAccSetVal = compressAccSet(accSet).val; - NanoAssert(isLInsSt()); - } - void LIns::initLInsSk(LIns* prevLIns) { - initSharedFields(LIR_skip); - toLInsSk()->prevLIns = prevLIns; - NanoAssert(isLInsSk()); - } - void LIns::initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci) { - initSharedFields(opcode); - toLInsC()->args = args; - toLInsC()->ci = ci; - NanoAssert(isLInsC()); - } - void LIns::initLInsP(int32_t arg, int32_t kind) { - initSharedFields(LIR_paramp); - NanoAssert(isU8(arg) && isU8(kind)); - toLInsP()->arg = arg; - toLInsP()->kind = kind; - NanoAssert(isLInsP()); - } - void LIns::initLInsI(LOpcode opcode, int32_t immI) { - initSharedFields(opcode); - toLInsI()->immI = immI; - NanoAssert(isLInsI()); - } - void LIns::initLInsQorD(LOpcode opcode, uint64_t immQorD) { - initSharedFields(opcode); - toLInsQorD()->immQorDlo = int32_t(immQorD); - toLInsQorD()->immQorDhi = int32_t(immQorD >> 32); - NanoAssert(isLInsQorD()); - } - void LIns::initLInsJtbl(LIns* index, uint32_t size, LIns** table) { - initSharedFields(LIR_jtbl); - toLInsJtbl()->oprnd_1 = index; - toLInsJtbl()->table = table; - toLInsJtbl()->size = size; - NanoAssert(isLInsJtbl()); - } - - LIns* LIns::oprnd1() const { - NanoAssert(isLInsOp1() || isLInsOp2() || isLInsOp3() || isLInsLd() || isLInsSt() || isLInsJtbl()); - return toLInsOp2()->oprnd_1; - } - LIns* LIns::oprnd2() const { - NanoAssert(isLInsOp2() || isLInsOp3() || isLInsSt()); - return toLInsOp2()->oprnd_2; - } - LIns* LIns::oprnd3() const { - NanoAssert(isLInsOp3()); - return toLInsOp3()->oprnd_3; - } - - LIns* LIns::getTarget() const { - NanoAssert(isBranch() && !isop(LIR_jtbl)); - if (isJov()) - return oprnd3(); - else - return oprnd2(); - } - - void LIns::setTarget(LIns* label) { - NanoAssert(label && label->isop(LIR_label)); - NanoAssert(isBranch() && !isop(LIR_jtbl)); - if (isJov()) - toLInsOp3()->oprnd_3 = label; - else - toLInsOp2()->oprnd_2 = label; - } - - LIns* LIns::getTarget(uint32_t index) const { - NanoAssert(isop(LIR_jtbl)); - NanoAssert(index < toLInsJtbl()->size); - return toLInsJtbl()->table[index]; - } - - void LIns::setTarget(uint32_t index, LIns* label) const { - NanoAssert(label && label->isop(LIR_label)); - NanoAssert(isop(LIR_jtbl)); - NanoAssert(index < toLInsJtbl()->size); - toLInsJtbl()->table[index] = label; - } - - GuardRecord *LIns::record() const { - NanoAssert(isGuard()); - switch (opcode()) { - case LIR_x: - case LIR_xt: - case LIR_xf: - case LIR_xbarrier: - return (GuardRecord*)oprnd2(); - - case LIR_addxovi: - case LIR_subxovi: - case LIR_mulxovi: - return (GuardRecord*)oprnd3(); - - default: - NanoAssert(0); - return NULL; - } - } - - LoadQual LIns::loadQual() const { - NanoAssert(isLInsLd()); - return (LoadQual)toLInsLd()->loadQual; - } - - int32_t LIns::disp() const { - if (isLInsSt()) { - return toLInsSt()->disp; - } else { - NanoAssert(isLInsLd()); - return toLInsLd()->disp; - } - } - - MiniAccSet LIns::miniAccSet() const { - MiniAccSet miniAccSet; - if (isLInsSt()) { - miniAccSet.val = toLInsSt()->miniAccSetVal; - } else { - NanoAssert(isLInsLd()); - miniAccSet.val = toLInsLd()->miniAccSetVal; - } - return miniAccSet; - } - - AccSet LIns::accSet() const { - return decompressMiniAccSet(miniAccSet()); - } - - LIns* LIns::prevLIns() const { - NanoAssert(isLInsSk()); - return toLInsSk()->prevLIns; - } - - inline uint8_t LIns::paramArg() const { NanoAssert(isop(LIR_paramp)); return toLInsP()->arg; } - inline uint8_t LIns::paramKind() const { NanoAssert(isop(LIR_paramp)); return toLInsP()->kind; } - - inline int32_t LIns::immI() const { NanoAssert(isImmI()); return toLInsI()->immI; } - -#ifdef NANOJIT_64BIT - inline int32_t LIns::immQlo() const { NanoAssert(isImmQ()); return toLInsQorD()->immQorDlo; } - uint64_t LIns::immQ() const { - NanoAssert(isImmQ()); - return (uint64_t(toLInsQorD()->immQorDhi) << 32) | uint32_t(toLInsQorD()->immQorDlo); - } -#endif - inline int32_t LIns::immDlo() const { NanoAssert(isImmD()); return toLInsQorD()->immQorDlo; } - inline int32_t LIns::immDhi() const { NanoAssert(isImmD()); return toLInsQorD()->immQorDhi; } - double LIns::immD() const { - NanoAssert(isImmD()); - union { - double f; - uint64_t q; - } u; - u.q = immDasQ(); - return u.f; - } - uint64_t LIns::immDasQ() const { - NanoAssert(isImmD()); - return (uint64_t(toLInsQorD()->immQorDhi) << 32) | uint32_t(toLInsQorD()->immQorDlo); - } - - int32_t LIns::size() const { - NanoAssert(isop(LIR_allocp)); - return toLInsI()->immI << 2; - } - - void LIns::setSize(int32_t nbytes) { - NanoAssert(isop(LIR_allocp)); - NanoAssert(nbytes > 0); - toLInsI()->immI = (nbytes+3)>>2; // # of required 32bit words - } - - // Index args in reverse order, i.e. arg(0) returns the rightmost arg. - // Nb: this must be kept in sync with insCall(). - LIns* LIns::arg(uint32_t i) const - { - NanoAssert(isCall()); - NanoAssert(i < callInfo()->count_args()); - return toLInsC()->args[i]; // args[] is in right-to-left order as well - } - - uint32_t LIns::argc() const { - return callInfo()->count_args(); - } - - LIns* LIns::callArgN(uint32_t n) const - { - return arg(argc()-n-1); - } - - const CallInfo* LIns::callInfo() const - { - NanoAssert(isCall()); - return toLInsC()->ci; - } - - uint32_t LIns::getTableSize() const - { - NanoAssert(isLInsJtbl()); - return toLInsJtbl()->size; - } - - class LirWriter - { - public: - LirWriter *out; - - LirWriter(LirWriter* out) - : out(out) {} - virtual ~LirWriter() {} - - virtual LIns* ins0(LOpcode v) { - return out->ins0(v); - } - virtual LIns* ins1(LOpcode v, LIns* a) { - return out->ins1(v, a); - } - virtual LIns* ins2(LOpcode v, LIns* a, LIns* b) { - return out->ins2(v, a, b); - } - virtual LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c) { - return out->ins3(v, a, b, c); - } - virtual LIns* insGuard(LOpcode v, LIns *c, GuardRecord *gr) { - return out->insGuard(v, c, gr); - } - virtual LIns* insGuardXov(LOpcode v, LIns *a, LIns* b, GuardRecord *gr) { - return out->insGuardXov(v, a, b, gr); - } - virtual LIns* insBranch(LOpcode v, LIns* condition, LIns* to) { - return out->insBranch(v, condition, to); - } - virtual LIns* insBranchJov(LOpcode v, LIns* a, LIns* b, LIns* to) { - return out->insBranchJov(v, a, b, to); - } - // arg: 0=first, 1=second, ... - // kind: 0=arg 1=saved-reg - virtual LIns* insParam(int32_t arg, int32_t kind) { - return out->insParam(arg, kind); - } - virtual LIns* insImmI(int32_t imm) { - return out->insImmI(imm); - } -#ifdef NANOJIT_64BIT - virtual LIns* insImmQ(uint64_t imm) { - return out->insImmQ(imm); - } -#endif - virtual LIns* insImmD(double d) { - return out->insImmD(d); - } - virtual LIns* insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet, LoadQual loadQual) { - return out->insLoad(op, base, d, accSet, loadQual); - } - virtual LIns* insStore(LOpcode op, LIns* value, LIns* base, int32_t d, AccSet accSet) { - return out->insStore(op, value, base, d, accSet); - } - // args[] is in reverse order, ie. args[0] holds the rightmost arg. - virtual LIns* insCall(const CallInfo *call, LIns* args[]) { - return out->insCall(call, args); - } - virtual LIns* insAlloc(int32_t size) { - NanoAssert(size != 0); - return out->insAlloc(size); - } - virtual LIns* insJtbl(LIns* index, uint32_t size) { - return out->insJtbl(index, size); - } - virtual LIns* insComment(const char* str) { - return out->insComment(str); - } - virtual LIns* insSkip(LIns* skipTo) { - return out->insSkip(skipTo); - } - - // convenience functions - - // Inserts a conditional to execute and branches to execute if - // the condition is true and false respectively. - LIns* insChoose(LIns* cond, LIns* iftrue, LIns* iffalse, bool use_cmov); - - // Inserts an integer comparison to 0 - LIns* insEqI_0(LIns* oprnd1) { - return ins2ImmI(LIR_eqi, oprnd1, 0); - } - - // Inserts a pointer comparison to 0 - LIns* insEqP_0(LIns* oprnd1) { - return ins2(LIR_eqp, oprnd1, insImmWord(0)); - } - - // Inserts a binary operation where the second operand is an - // integer immediate. - LIns* ins2ImmI(LOpcode v, LIns* oprnd1, int32_t imm) { - return ins2(v, oprnd1, insImmI(imm)); - } - - LIns* insImmP(const void *ptr) { -#ifdef NANOJIT_64BIT - return insImmQ((uint64_t)ptr); -#else - return insImmI((int32_t)ptr); -#endif - } - - LIns* insImmWord(intptr_t value) { -#ifdef NANOJIT_64BIT - return insImmQ(value); -#else - return insImmI(value); -#endif - } - - // Sign-extend integers to native integers. On 32-bit this is a no-op. - LIns* insI2P(LIns* intIns) { -#ifdef NANOJIT_64BIT - return ins1(LIR_i2q, intIns); -#else - return intIns; -#endif - } - - // Zero-extend integers to native integers. On 32-bit this is a no-op. - LIns* insUI2P(LIns* uintIns) { - #ifdef NANOJIT_64BIT - return ins1(LIR_ui2uq, uintIns); - #else - return uintIns; - #endif - } - - // Do a load with LoadQual==LOAD_NORMAL. - LIns* insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet) { - return insLoad(op, base, d, accSet, LOAD_NORMAL); - } - - // Chooses LIR_sti, LIR_stq or LIR_std according to the type of 'value'. - LIns* insStore(LIns* value, LIns* base, int32_t d, AccSet accSet); - }; - - -#ifdef NJ_VERBOSE - extern const char* lirNames[]; - - // Maps address ranges to meaningful names. - class AddrNameMap - { - Allocator& allocator; - class Entry - { - public: - Entry(int) : name(0), size(0), align(0) {} - Entry(char *n, size_t s, size_t a) : name(n), size(s), align(a) {} - char* name; - size_t size:29, align:3; - }; - TreeMap names; // maps code regions to names - public: - AddrNameMap(Allocator& allocator); - void addAddrRange(const void *p, size_t size, size_t align, const char *name); - void lookupAddr(void *p, char*& name, int32_t& offset); - }; - - // Maps LIR instructions to meaningful names. - class LirNameMap - { - private: - Allocator& alloc; - - // A small string-wrapper class, required because we need '==' to - // compare string contents, not string pointers, when strings are used - // as keys in CountMap. - struct Str { - Allocator& alloc; - char* s; - - Str(Allocator& alloc_, const char* s_) : alloc(alloc_) { - s = new (alloc) char[1+strlen(s_)]; - strcpy(s, s_); - } - - bool operator==(const Str& str) const { - return (0 == strcmp(this->s, str.s)); - } - }; - - // Similar to 'struct Str' -- we need to hash the string's contents, - // not its pointer. - template struct StrHash { - static size_t hash(const Str &k) { - // (const void*) cast is required by ARM RVCT 2.2 - return murmurhash((const void*)k.s, strlen(k.s)); - } - }; - - template > - class CountMap: public HashMap { - public: - CountMap(Allocator& alloc) : HashMap(alloc, 128) {} - int add(Key k) { - int c = 1; - if (this->containsKey(k)) { - c = 1+this->get(k); - } - this->put(k,c); - return c; - } - }; - - CountMap lircounts; - CountMap funccounts; - CountMap > namecounts; - - void addNameWithSuffix(LIns* i, const char *s, int suffix, bool ignoreOneSuffix); - - class Entry - { - public: - Entry(int) : name(0) {} - Entry(char* n) : name(n) {} - char* name; - }; - - HashMap names; - - public: - LirNameMap(Allocator& alloc) - : alloc(alloc), - lircounts(alloc), - funccounts(alloc), - namecounts(alloc), - names(alloc) - {} - - void addName(LIns* ins, const char *s); // gives 'ins' a special name - const char* createName(LIns* ins); // gives 'ins' a generic name - const char* lookupName(LIns* ins); - }; - - // We use big buffers for cases where we need to fit a whole instruction, - // and smaller buffers for all the others. These should easily be long - // enough, but for safety the formatXyz() functions check and won't exceed - // those limits. - class InsBuf { - public: - static const size_t len = 1000; - char buf[len]; - }; - class RefBuf { - public: - static const size_t len = 200; - char buf[len]; - }; - - class LInsPrinter - { - private: - Allocator& alloc; - const int EMB_NUM_USED_ACCS; - - char *formatImmI(RefBuf* buf, int32_t c); -#ifdef NANOJIT_64BIT - char *formatImmQ(RefBuf* buf, uint64_t c); -#endif - char *formatImmD(RefBuf* buf, double c); - void formatGuard(InsBuf* buf, LIns* ins); // defined by the embedder - void formatGuardXov(InsBuf* buf, LIns* ins); // defined by the embedder - - public: - static const char* accNames[]; // defined by the embedder - - LInsPrinter(Allocator& alloc, int embNumUsedAccs) - : alloc(alloc), EMB_NUM_USED_ACCS(embNumUsedAccs) - { - addrNameMap = new (alloc) AddrNameMap(alloc); - lirNameMap = new (alloc) LirNameMap(alloc); - } - - char *formatAddr(RefBuf* buf, void* p); - char *formatRef(RefBuf* buf, LIns* ref, bool showImmValue = true); - char *formatIns(InsBuf* buf, LIns* ins); - char *formatAccSet(RefBuf* buf, AccSet accSet); - - AddrNameMap* addrNameMap; - LirNameMap* lirNameMap; - }; - - - class VerboseWriter : public LirWriter - { - InsList code; - LInsPrinter* printer; - LogControl* logc; - const char* const prefix; - bool const always_flush; - public: - VerboseWriter(Allocator& alloc, LirWriter *out, LInsPrinter* printer, LogControl* logc, - const char* prefix = "", bool always_flush = false) - : LirWriter(out), code(alloc), printer(printer), logc(logc), prefix(prefix), always_flush(always_flush) - {} - - LIns* add(LIns* i) { - if (i) { - code.add(i); - if (always_flush) - flush(); - } - return i; - } - - LIns* add_flush(LIns* i) { - if ((i = add(i)) != 0) - flush(); - return i; - } - - void flush() - { - if (!code.isEmpty()) { - InsBuf b; - for (Seq* p = code.get(); p != NULL; p = p->tail) - logc->printf("%s %s\n", prefix, printer->formatIns(&b, p->head)); - code.clear(); - } - } - - LIns* insGuard(LOpcode op, LIns* cond, GuardRecord *gr) { - return add_flush(out->insGuard(op,cond,gr)); - } - - LIns* insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord *gr) { - return add(out->insGuardXov(op,a,b,gr)); - } - - LIns* insBranch(LOpcode v, LIns* condition, LIns* to) { - return add_flush(out->insBranch(v, condition, to)); - } - - LIns* insBranchJov(LOpcode v, LIns* a, LIns* b, LIns* to) { - return add(out->insBranchJov(v, a, b, to)); - } - - LIns* insJtbl(LIns* index, uint32_t size) { - return add_flush(out->insJtbl(index, size)); - } - - LIns* ins0(LOpcode v) { - if (v == LIR_label || v == LIR_start) { - flush(); - } - return add(out->ins0(v)); - } - - LIns* ins1(LOpcode v, LIns* a) { - return isRetOpcode(v) ? add_flush(out->ins1(v, a)) : add(out->ins1(v, a)); - } - LIns* ins2(LOpcode v, LIns* a, LIns* b) { - return add(out->ins2(v, a, b)); - } - LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c) { - return add(out->ins3(v, a, b, c)); - } - LIns* insCall(const CallInfo *call, LIns* args[]) { - return add_flush(out->insCall(call, args)); - } - LIns* insParam(int32_t i, int32_t kind) { - return add(out->insParam(i, kind)); - } - LIns* insLoad(LOpcode v, LIns* base, int32_t disp, AccSet accSet, LoadQual loadQual) { - return add(out->insLoad(v, base, disp, accSet, loadQual)); - } - LIns* insStore(LOpcode op, LIns* v, LIns* b, int32_t d, AccSet accSet) { - return add_flush(out->insStore(op, v, b, d, accSet)); - } - LIns* insAlloc(int32_t size) { - return add(out->insAlloc(size)); - } - LIns* insImmI(int32_t imm) { - return add(out->insImmI(imm)); - } -#ifdef NANOJIT_64BIT - LIns* insImmQ(uint64_t imm) { - return add(out->insImmQ(imm)); - } -#endif - LIns* insImmD(double d) { - return add(out->insImmD(d)); - } - - LIns* insComment(const char* str) { - return add_flush(out->insComment(str)); - } - }; - -#endif - - class ExprFilter: public LirWriter - { - public: - ExprFilter(LirWriter *out) : LirWriter(out) {} - LIns* ins1(LOpcode v, LIns* a); - LIns* ins2(LOpcode v, LIns* a, LIns* b); - LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c); - LIns* insGuard(LOpcode, LIns* cond, GuardRecord *); - LIns* insGuardXov(LOpcode, LIns* a, LIns* b, GuardRecord *); - LIns* insBranch(LOpcode, LIns* cond, LIns* target); - LIns* insBranchJov(LOpcode, LIns* a, LIns* b, LIns* target); - LIns* insLoad(LOpcode op, LIns* base, int32_t off, AccSet accSet, LoadQual loadQual); - private: - LIns* simplifyOverflowArith(LOpcode op, LIns** opnd1, LIns** opnd2); - }; - - class CseFilter: public LirWriter - { - enum NLKind { - // We divide instruction kinds into groups. LIns0 isn't present - // because we don't need to record any 0-ary instructions. Loads - // aren't here, they're handled separately. - NLImmISmall = 0, - NLImmILarge = 1, - NLImmQ = 2, // only occurs on 64-bit platforms - NLImmD = 3, - NL1 = 4, - NL2 = 5, - NL3 = 6, - NLCall = 7, - - NLFirst = 0, - NLLast = 7, - // Need a value after "last" to outsmart compilers that insist last+1 is impossible. - NLInvalid = 8 - }; - #define nextNLKind(kind) NLKind(kind+1) - - // There is one table for each NLKind. This lets us size the lists - // appropriately (some instruction kinds are more common than others). - // It also lets us have NLKind-specific find/add/grow functions, which - // are faster than generic versions. - // - // Nb: m_listNL and m_capNL sizes must be a power of 2. - // Don't start m_capNL too small, or we'll waste time growing and rehashing. - // Don't start m_capNL too large, will waste memory. - // - LIns** m_listNL[NLLast + 1]; - uint32_t m_capNL[ NLLast + 1]; - uint32_t m_usedNL[NLLast + 1]; - typedef uint32_t (CseFilter::*find_t)(LIns*); - find_t m_findNL[NLLast + 1]; - - // Similarly, for loads, there is one table for each CseAcc. A CseAcc - // is like a normal access region, but there are two extra possible - // values: CSE_ACC_CONST, which is where we put all CONST-qualified - // loads, and CSE_ACC_MULTIPLE, where we put all multi-region loads. - // All remaining loads are single-region and go in the table entry for - // their region. - // - // This arrangement makes the removal of invalidated loads fast -- we - // can invalidate all loads from a single region by clearing that - // region's table. - // - typedef uint8_t CseAcc; // same type as MiniAccSet - - static const uint8_t CSE_NUM_ACCS = NUM_ACCS + 2; - - // These values would be 'static const' except they are defined in - // terms of EMB_NUM_USED_ACCS which is itself not 'static const' - // because it's passed in by the embedding. - const uint8_t EMB_NUM_USED_ACCS; // number of access regions used by the embedding - const uint8_t CSE_NUM_USED_ACCS; // EMB_NUM_USED_ACCS + 2 - const CseAcc CSE_ACC_CONST; // EMB_NUM_USED_ACCS + 0 - const CseAcc CSE_ACC_MULTIPLE; // EMB_NUM_USED_ACCS + 1 - - // We will only use CSE_NUM_USED_ACCS of these entries, ie. the - // number of lists allocated depends on the number of access regions - // in use by the embedding. - LIns** m_listL[CSE_NUM_ACCS]; - uint32_t m_capL[ CSE_NUM_ACCS]; - uint32_t m_usedL[CSE_NUM_ACCS]; - - AccSet storesSinceLastLoad; // regions stored to since the last load - - Allocator& alloc; - - // After a conditional guard such as "xf cmp", we know that 'cmp' must - // be true, else we would have side-exited. So if we see 'cmp' again - // we can treat it like a constant. This table records such - // comparisons. - HashMap knownCmpValues; - - // If true, we will not add new instructions to the CSE tables, but we - // will continue to CSE instructions that match existing table - // entries. Load instructions will still be removed if aliasing - // stores are encountered. - bool suspended; - - CseAcc miniAccSetToCseAcc(MiniAccSet miniAccSet, LoadQual loadQual) { - NanoAssert(miniAccSet.val < NUM_ACCS || miniAccSet.val == MINI_ACCSET_MULTIPLE.val); - return (loadQual == LOAD_CONST) ? CSE_ACC_CONST : - (miniAccSet.val == MINI_ACCSET_MULTIPLE.val) ? CSE_ACC_MULTIPLE : - miniAccSet.val; - } - - static uint32_t hash8(uint32_t hash, const uint8_t data); - static uint32_t hash32(uint32_t hash, const uint32_t data); - static uint32_t hashptr(uint32_t hash, const void* data); - static uint32_t hashfinish(uint32_t hash); - - static uint32_t hashImmI(int32_t); - static uint32_t hashImmQorD(uint64_t); // not NANOJIT_64BIT-only -- used by findImmD() - static uint32_t hash1(LOpcode op, LIns*); - static uint32_t hash2(LOpcode op, LIns*, LIns*); - static uint32_t hash3(LOpcode op, LIns*, LIns*, LIns*); - static uint32_t hashLoad(LOpcode op, LIns*, int32_t); - static uint32_t hashCall(const CallInfo *call, uint32_t argc, LIns* args[]); - - // These versions are used before an LIns has been created. - LIns* findImmISmall(int32_t a, uint32_t &k); - LIns* findImmILarge(int32_t a, uint32_t &k); -#ifdef NANOJIT_64BIT - LIns* findImmQ(uint64_t a, uint32_t &k); -#endif - LIns* findImmD(uint64_t d, uint32_t &k); - LIns* find1(LOpcode v, LIns* a, uint32_t &k); - LIns* find2(LOpcode v, LIns* a, LIns* b, uint32_t &k); - LIns* find3(LOpcode v, LIns* a, LIns* b, LIns* c, uint32_t &k); - LIns* findLoad(LOpcode v, LIns* a, int32_t b, MiniAccSet miniAccSet, LoadQual loadQual, - uint32_t &k); - LIns* findCall(const CallInfo *call, uint32_t argc, LIns* args[], uint32_t &k); - - // These versions are used after an LIns has been created; they are - // used for rehashing after growing. They just call onto the - // multi-arg versions above. - uint32_t findImmISmall(LIns* ins); - uint32_t findImmILarge(LIns* ins); -#ifdef NANOJIT_64BIT - uint32_t findImmQ(LIns* ins); -#endif - uint32_t findImmD(LIns* ins); - uint32_t find1(LIns* ins); - uint32_t find2(LIns* ins); - uint32_t find3(LIns* ins); - uint32_t findCall(LIns* ins); - uint32_t findLoad(LIns* ins); - - // These return false if they failed to grow due to OOM. - bool growNL(NLKind kind); - bool growL(CseAcc cseAcc); - - void addNLImmISmall(LIns* ins, uint32_t k); - // 'k' is the index found by findXYZ(). - void addNL(NLKind kind, LIns* ins, uint32_t k); - void addL(LIns* ins, uint32_t k); - - void clearAll(); // clears all tables - void clearNL(NLKind); // clears one non-load table - void clearL(CseAcc); // clears one load table - - public: - CseFilter(LirWriter *out, uint8_t embNumUsedAccs, Allocator&); - - // CseFilter does some largish fallible allocations at start-up. If - // they fail, the constructor sets this field to 'true'. It should be - // checked after creation, and if set the CseFilter cannot be used. - // (But the check can be skipped if allocChunk() always succeeds.) - // - // FIXME: This fallibility is a sop to TraceMonkey's implementation of - // infallible malloc -- by avoiding some largish infallible - // allocations, it reduces the size of the reserve space needed. - // Bug 624590 is open to fix this. - bool initOOM; - - LIns* insImmI(int32_t imm); -#ifdef NANOJIT_64BIT - LIns* insImmQ(uint64_t q); -#endif - LIns* insImmD(double d); - LIns* ins0(LOpcode v); - LIns* ins1(LOpcode v, LIns*); - LIns* ins2(LOpcode v, LIns*, LIns*); - LIns* ins3(LOpcode v, LIns*, LIns*, LIns*); - LIns* insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet, LoadQual loadQual); - LIns* insStore(LOpcode op, LIns* value, LIns* base, int32_t d, AccSet accSet); - LIns* insCall(const CallInfo *call, LIns* args[]); - LIns* insGuard(LOpcode op, LIns* cond, GuardRecord *gr); - LIns* insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord *gr); - - // These functions provide control over CSE in the face of control - // flow. A suspend()/resume() pair may be put around a synthetic - // control flow diamond, preventing the inserted label from resetting - // the CSE state. A suspend() call must be dominated by a resume() - // call, else incorrect code could result. - void suspend() { suspended = true; } - void resume() { suspended = false; } - }; - - class LirBuffer - { - public: - LirBuffer(Allocator& alloc); - void clear(); - uintptr_t makeRoom(size_t szB); // make room for an instruction - - debug_only (void validate() const;) - verbose_only(LInsPrinter* printer;) - - int32_t insCount(); - - // stats - struct - { - uint32_t lir; // # instructions - } - _stats; - - AbiKind abi; - LIns *state, *param1, *sp, *rp; - LIns* savedRegs[NumSavedRegs+1]; // Allocate an extra element in case NumSavedRegs == 0 - - /** Each chunk is just a raw area of LIns instances, with no header - and no more than 8-byte alignment. The chunk size is somewhat arbitrary. */ - static const size_t CHUNK_SZB = 8000; - - protected: - friend class LirBufWriter; - - /** Get CHUNK_SZB more memory for LIR instructions. */ - void chunkAlloc(); - void moveToNewChunk(uintptr_t addrOfLastLInsOnCurrentChunk); - - Allocator& _allocator; - uintptr_t _unused; // next unused instruction slot in the current LIR chunk - uintptr_t _limit; // one past the last usable byte of the current LIR chunk - }; - - class LirBufWriter : public LirWriter - { - LirBuffer* _buf; // underlying buffer housing the instructions - const Config& _config; - - public: - LirBufWriter(LirBuffer* buf, const Config& config) - : LirWriter(0), _buf(buf), _config(config) { - } - - // LirWriter interface - LIns* insLoad(LOpcode op, LIns* base, int32_t disp, AccSet accSet, LoadQual loadQual); - LIns* insStore(LOpcode op, LIns* o1, LIns* o2, int32_t disp, AccSet accSet); - LIns* ins0(LOpcode op); - LIns* ins1(LOpcode op, LIns* o1); - LIns* ins2(LOpcode op, LIns* o1, LIns* o2); - LIns* ins3(LOpcode op, LIns* o1, LIns* o2, LIns* o3); - LIns* insParam(int32_t i, int32_t kind); - LIns* insImmI(int32_t imm); -#ifdef NANOJIT_64BIT - LIns* insImmQ(uint64_t imm); -#endif - LIns* insImmD(double d); - LIns* insCall(const CallInfo *call, LIns* args[]); - LIns* insGuard(LOpcode op, LIns* cond, GuardRecord *gr); - LIns* insGuardXov(LOpcode op, LIns* a, LIns* b, GuardRecord *gr); - LIns* insBranch(LOpcode v, LIns* condition, LIns* to); - LIns* insBranchJov(LOpcode v, LIns* a, LIns* b, LIns* to); - LIns* insAlloc(int32_t size); - LIns* insJtbl(LIns* index, uint32_t size); - LIns* insComment(const char* str); - LIns* insSkip(LIns* skipTo); - }; - - class LirFilter - { - public: - LirFilter *in; - LirFilter(LirFilter *in) : in(in) {} - virtual ~LirFilter(){} - - // It's crucial that once this reaches the LIR_start at the beginning - // of the buffer, that it just keeps returning that LIR_start LIns on - // any subsequent calls. - virtual LIns* read() { - return in->read(); - } - virtual LIns* finalIns() { - return in->finalIns(); - } - }; - - // concrete - class LirReader : public LirFilter - { - LIns* _ins; // next instruction to be read; invariant: is never a skip - LIns* _finalIns; // final instruction in the stream; ie. the first one to be read - - public: - LirReader(LIns* ins) : LirFilter(0), _ins(ins), _finalIns(ins) - { - // The last instruction for a fragment shouldn't be a skip. - // (Actually, if the last *inserted* instruction exactly fills up - // a chunk, a new chunk will be created, and thus the last *written* - // instruction will be a skip -- the one needed for the - // cross-chunk link. But the last *inserted* instruction is what - // is recorded and used to initialise each LirReader, and that is - // what is seen here, and therefore this assertion holds.) - NanoAssert(ins && !ins->isop(LIR_skip)); - } - virtual ~LirReader() {} - - // Returns next instruction and advances to the prior instruction. - // Invariant: never returns a skip. - LIns* read() - { - const uint8_t insReadSizes[] = { - // LIR_start is treated specially -- see below. We intentionally - // do not use the global insSizes[] because of this customization. - #define OP___(op, number, repKind, retType, isCse) \ - ((number) == LIR_start ? 0 : sizeof(LIns##repKind)), - #include "LIRopcode.tbl" - #undef OP___ - 0 - }; - - // Check the invariant: _ins never points to a skip. - NanoAssert(_ins && !_ins->isop(LIR_skip)); - - // Step back one instruction. Use a table lookup rather than a switch - // to avoid branch mispredictions. LIR_start is given a special size - // of zero so that we don't step back past the start of the block. - // (Callers of this function should stop once they see a LIR_start.) - LIns* ret = _ins; - _ins = (LIns*)(uintptr_t(_ins) - insReadSizes[_ins->opcode()]); - - // Ensure _ins doesn't end up pointing to a skip. - while (_ins->isop(LIR_skip)) { - NanoAssert(_ins->prevLIns() != _ins); - _ins = _ins->prevLIns(); - } - - return ret; - } - - // Returns the instruction that read() will return on the next call. - // Invariant: never returns a skip. - LIns* peek() - { - // Check the invariant: _ins never points to a skip. - NanoAssert(_ins && !_ins->isop(LIR_skip)); - return _ins; - } - - LIns* finalIns() { - return _finalIns; - } - }; - - verbose_only(void live(LirFilter* in, Allocator& alloc, Fragment* frag, LogControl*);) - - // WARNING: StackFilter assumes that all stack entries are eight bytes. - // Some of its optimisations aren't valid if that isn't true. See - // StackFilter::read() for more details. - class StackFilter: public LirFilter - { - LIns* sp; - BitSet stk; - int top; - int getTop(LIns* br); - - public: - StackFilter(LirFilter *in, Allocator& alloc, LIns* sp); - LIns* read(); - }; - - // This type is used to perform a simple interval analysis of 32-bit - // add/sub/mul. It lets us avoid overflow checks in some cases. - struct Interval - { - // The bounds are 64-bit integers so that any overflow from a 32-bit - // operation can be safely detected. - // - // If 'hasOverflowed' is false, 'lo' and 'hi' must be in the range - // I32_MIN..I32_MAX. If 'hasOverflowed' is true, 'lo' and 'hi' should - // not be trusted (and in debug builds we set them both to a special - // value UNTRUSTWORTHY that is outside the I32_MIN..I32_MAX range to - // facilitate sanity checking). - // - int64_t lo; - int64_t hi; - bool hasOverflowed; - - static const int64_t I32_MIN = int64_t(int32_t(0x80000000)); - static const int64_t I32_MAX = int64_t(int32_t(0x7fffffff)); - -#ifdef DEBUG - static const int64_t UNTRUSTWORTHY = int64_t(0xdeafdeadbeeffeedLL); - - bool isSane() { - return (hasOverflowed && lo == UNTRUSTWORTHY && hi == UNTRUSTWORTHY) || - (!hasOverflowed && lo <= hi && I32_MIN <= lo && hi <= I32_MAX); - } -#endif - - Interval(int64_t lo_, int64_t hi_) { - if (lo_ < I32_MIN || I32_MAX < hi_) { - hasOverflowed = true; -#ifdef DEBUG - lo = UNTRUSTWORTHY; - hi = UNTRUSTWORTHY; -#endif - } else { - hasOverflowed = false; - lo = lo_; - hi = hi_; - } - NanoAssert(isSane()); - } - - static Interval OverflowInterval() { - Interval interval(0, 0); -#ifdef DEBUG - interval.lo = UNTRUSTWORTHY; - interval.hi = UNTRUSTWORTHY; -#endif - interval.hasOverflowed = true; - return interval; - } - - static Interval of(LIns* ins, int32_t lim); - - static Interval add(Interval x, Interval y); - static Interval sub(Interval x, Interval y); - static Interval mul(Interval x, Interval y); - - bool canBeZero() { - NanoAssert(isSane()); - return hasOverflowed || (lo <= 0 && 0 <= hi); - } - - bool canBeNegative() { - NanoAssert(isSane()); - return hasOverflowed || (lo < 0); - } - }; - -#if NJ_SOFTFLOAT_SUPPORTED - struct SoftFloatOps - { - const CallInfo* opmap[LIR_sentinel]; - SoftFloatOps(); - }; - - extern const SoftFloatOps softFloatOps; - - // Replaces fpu ops with function calls, for platforms lacking float - // hardware (eg. some ARM machines). - class SoftFloatFilter: public LirWriter - { - public: - static const CallInfo* opmap[LIR_sentinel]; - - SoftFloatFilter(LirWriter *out); - LIns *split(LIns *a); - LIns *split(const CallInfo *call, LIns* args[]); - LIns *callD1(const CallInfo *call, LIns *a); - LIns *callD2(const CallInfo *call, LIns *a, LIns *b); - LIns *callI1(const CallInfo *call, LIns *a); - LIns *cmpD(const CallInfo *call, LIns *a, LIns *b); - LIns *ins1(LOpcode op, LIns *a); - LIns *ins2(LOpcode op, LIns *a, LIns *b); - LIns *insCall(const CallInfo *ci, LIns* args[]); - }; -#endif - -#ifdef DEBUG - // This class does thorough checking of LIR. It checks *implicit* LIR - // instructions, ie. LIR instructions specified via arguments -- to - // methods like insLoad() -- that have not yet been converted into - // *explicit* LIns objects in a LirBuffer. The reason for this is that if - // we wait until the LIR instructions are explicit, they will have gone - // through the entire writer pipeline and been optimised. By checking - // implicit LIR instructions we can check the LIR code at the start of the - // writer pipeline, exactly as it is generated by the compiler front-end. - // - // A general note about the errors produced by this class: for - // TraceMonkey, they won't include special names for instructions that - // have them unless TMFLAGS is specified. - class ValidateWriter : public LirWriter - { - private: - LInsPrinter* printer; - const char* whereInPipeline; - - const char* type2string(LTy type); - void typeCheckArgs(LOpcode op, int nArgs, LTy formals[], LIns* args[]); - void errorStructureShouldBe(LOpcode op, const char* argDesc, int argN, LIns* arg, - const char* shouldBeDesc); - void errorAccSet(const char* what, AccSet accSet, const char* shouldDesc); - void errorLoadQual(const char* what, LoadQual loadQual); - void checkLInsHasOpcode(LOpcode op, int argN, LIns* ins, LOpcode op2); - void checkLInsIsACondOrConst(LOpcode op, int argN, LIns* ins); - void checkLInsIsNull(LOpcode op, int argN, LIns* ins); - void checkAccSet(LOpcode op, LIns* base, int32_t disp, AccSet accSet); // defined by the embedder - - // These can be set by the embedder and used in checkAccSet(). - void** checkAccSetExtras; - - public: - ValidateWriter(LirWriter* out, LInsPrinter* printer, const char* where); - void setCheckAccSetExtras(void** extras) { checkAccSetExtras = extras; } - - LIns* insLoad(LOpcode op, LIns* base, int32_t d, AccSet accSet, LoadQual loadQual); - LIns* insStore(LOpcode op, LIns* value, LIns* base, int32_t d, AccSet accSet); - LIns* ins0(LOpcode v); - LIns* ins1(LOpcode v, LIns* a); - LIns* ins2(LOpcode v, LIns* a, LIns* b); - LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c); - LIns* insParam(int32_t arg, int32_t kind); - LIns* insImmI(int32_t imm); -#ifdef NANOJIT_64BIT - LIns* insImmQ(uint64_t imm); -#endif - LIns* insImmD(double d); - LIns* insCall(const CallInfo *call, LIns* args[]); - LIns* insGuard(LOpcode v, LIns *c, GuardRecord *gr); - LIns* insGuardXov(LOpcode v, LIns* a, LIns* b, GuardRecord* gr); - LIns* insBranch(LOpcode v, LIns* condition, LIns* to); - LIns* insBranchJov(LOpcode v, LIns* a, LIns* b, LIns* to); - LIns* insAlloc(int32_t size); - LIns* insJtbl(LIns* index, uint32_t size); - }; - - // This just checks things that aren't possible to check in - // ValidateWriter, eg. whether all branch targets are set and are labels. - class ValidateReader: public LirFilter { - public: - ValidateReader(LirFilter* in); - LIns* read(); - }; -#endif - -#ifdef NJ_VERBOSE - /* A listing filter for LIR, going through backwards. It merely - passes its input to its output, but notes it down too. When - finish() is called, prints out what went through. Is intended to be - used to print arbitrary intermediate transformation stages of - LIR. */ - class ReverseLister : public LirFilter - { - Allocator& _alloc; - LInsPrinter* _printer; - const char* _title; - StringList _strs; - LogControl* _logc; - LIns* _prevIns; - public: - ReverseLister(LirFilter* in, Allocator& alloc, - LInsPrinter* printer, LogControl* logc, const char* title) - : LirFilter(in) - , _alloc(alloc) - , _printer(printer) - , _title(title) - , _strs(alloc) - , _logc(logc) - , _prevIns(NULL) - { } - - void finish(); - LIns* read(); - }; -#endif - -} -#endif // __nanojit_LIR__ diff --git a/deps/mozjs/js/src/nanojit/LIRopcode.tbl b/deps/mozjs/js/src/nanojit/LIRopcode.tbl deleted file mode 100644 index a5315191b8b..00000000000 --- a/deps/mozjs/js/src/nanojit/LIRopcode.tbl +++ /dev/null @@ -1,367 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=0 ft=c: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey nanojit. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jeff Walden - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This file is best viewed with 128 columns: -12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 - * - * Definitions of LIR opcodes. If you need to allocate an opcode, look - * for one defined using OP_UN() and claim it. - * - * Includers must define an OPxyz macro of the following form: - * - * #define OPxyz(op, number, repKind, retType) ... - * - * Selected arguments can then be used within the macro expansions. - * - op Opcode name, token-pasted after "LIR_" to form an LOpcode. - * - number Opcode number, used as the LOpcode enum value. - * - repKind Indicates how the instruction is represented in memory; XYZ - * corresponds to LInsXYZ and LRK_XYZ. - * - retType Type (LTy) of the value returned by the instruction. - * - isCse 0 if the opcode can never be CSE'd safely, 1 if it always - * can, -1 if things are more complicated -- in which case - * isCseOpcode() shouldn't be called on this opcode. - * - * Opcodes use type-indicators suffixes that are loosly based on C type names: - * - 'c': "char", ie. 8-bit integer - * - 's': "short", ie. 16-bit integer - * - 'i': "int", ie. 32-bit integer - * - 'q': "quad", ie. 64-bit integer - * - 'u': "unsigned", is used as a prefix on integer type-indicators when necessary - * - 'f': "float", ie. 32-bit floating point value - * - 'd': "double", ie. 64-bit floating point value - * - 'p': "pointer", ie. a int on 32-bit machines, a quad on 64-bit machines - * - * 'p' opcodes are all aliases of int and quad opcodes, they're given in LIR.h - * and chosen according to the platform pointer size. - * - * Certain opcodes aren't supported on all platforms, so OPxyz must be one of - * the following: - * - * OP___: for opcodes supported on all platforms. - * OP_UN: for opcodes not yet used on any platform. - * OP_32: for opcodes supported only on 32-bit platforms. - * OP_64: for opcodes supported only on 64-bit platforms. - * OP_SF: for opcodes supported only on SoftFloat platforms. - * OP_86: for opcodes supported only on i386/X64. - */ - -#define OP_UN(n) OP___(__##n, n, None, V, -1) - -#ifdef NANOJIT_64BIT -# define OP_32(a, b, c, d, e) OP_UN(b) -# define OP_64 OP___ -#else -# define OP_32 OP___ -# define OP_64(a, b, c, d, e) OP_UN(b) -#endif - -#if NJ_SOFTFLOAT_SUPPORTED -# define OP_SF OP___ -#else -# define OP_SF(a, b, c, d, e) OP_UN(b) -#endif - -#if defined NANOJIT_IA32 || defined NANOJIT_X64 -# define OP_86 OP___ -#else -# define OP_86(a, b, c, d, e) OP_UN(b) -#endif - -//--------------------------------------------------------------------------- -// Miscellaneous operations -//--------------------------------------------------------------------------- -OP___(start, 0, Op0, V, 0) // start of a fragment - -// A register fence causes no code to be generated, but it affects register -// allocation so that no registers are live when it is reached. -OP___(regfence, 1, Op0, V, 0) - -OP___(skip, 2, Sk, V, 0) // links code chunks - -OP_32(parami, 3, P, I, 0) // load an int parameter (register or stack location) -OP_64(paramq, 4, P, Q, 0) // load a quad parameter (register or stack location) - -OP___(allocp, 5, I, P, 0) // allocate stack space (result is an address) - -OP___(reti, 6, Op1, V, 0) // return an int -OP_64(retq, 7, Op1, V, 0) // return a quad -OP___(retd, 8, Op1, V, 0) // return a double - -OP___(livei, 9, Op1, V, 0) // extend live range of an int -OP_64(liveq, 10, Op1, V, 0) // extend live range of a quad -OP___(lived, 11, Op1, V, 0) // extend live range of a double - -OP___(file, 12, Op1, V, 0) // source filename for debug symbols -OP___(line, 13, Op1, V, 0) // source line number for debug symbols - -OP___(comment, 14, Op1, V, 0) // a comment shown, on its own line, in LIR dumps -OP_UN(15) -OP_UN(16) - -//--------------------------------------------------------------------------- -// Loads and stores -//--------------------------------------------------------------------------- -OP___(ldc2i, 17, Ld, I, -1) // load char and sign-extend to an int -OP___(lds2i, 18, Ld, I, -1) // load short and sign-extend to an int -OP___(lduc2ui, 19, Ld, I, -1) // load unsigned char and zero-extend to an unsigned int -OP___(ldus2ui, 20, Ld, I, -1) // load unsigned short and zero-extend to an unsigned int -OP___(ldi, 21, Ld, I, -1) // load int -OP_64(ldq, 22, Ld, Q, -1) // load quad -OP___(ldd, 23, Ld, D, -1) // load double -OP___(ldf2d, 24, Ld, D, -1) // load float and extend to a double - -OP___(sti2c, 25, St, V, 0) // store int truncated to char -OP___(sti2s, 26, St, V, 0) // store int truncated to short -OP___(sti, 27, St, V, 0) // store int -OP_64(stq, 28, St, V, 0) // store quad -OP___(std, 29, St, V, 0) // store double -OP___(std2f, 30, St, V, 0) // store double as a float (losing precision) - -OP_UN(31) -OP_UN(32) - -//--------------------------------------------------------------------------- -// Calls -//--------------------------------------------------------------------------- -OP___(callv, 33, C, V, -1) // call subroutine that returns void -OP___(calli, 34, C, I, -1) // call subroutine that returns an int -OP_64(callq, 35, C, Q, -1) // call subroutine that returns a quad -OP___(calld, 36, C, D, -1) // call subroutine that returns a double - -//--------------------------------------------------------------------------- -// Branches and labels -//--------------------------------------------------------------------------- -// 'jt' and 'jf' must be adjacent so that (op ^ 1) gives the opposite one. -// Static assertions in LIR.h check this requirement. -OP___(j, 37, Op2, V, 0) // jump always -OP___(jt, 38, Op2, V, 0) // jump if true -OP___(jf, 39, Op2, V, 0) // jump if false -OP___(jtbl, 40, Jtbl, V, 0) // jump to address in table - -OP___(label, 41, Op0, V, 0) // a jump target (no machine code is emitted for this) - -OP_UN(42) - -//--------------------------------------------------------------------------- -// Guards -//--------------------------------------------------------------------------- -// 'xt' and 'xf' must be adjacent so that (op ^ 1) gives the opposite one. -// Static assertions in LIR.h check this requirement. -OP___(x, 43, Op2, V, 0) // exit always -OP___(xt, 44, Op2, V, 1) // exit if true -OP___(xf, 45, Op2, V, 1) // exit if false -// A LIR_xbarrier cause no code to be generated, but it acts like a never-taken -// guard in that it inhibits certain optimisations, such as dead stack store -// elimination. -OP___(xbarrier, 46, Op2, V, 0) - -OP_UN(47) -OP_UN(48) - -//--------------------------------------------------------------------------- -// Immediates -//--------------------------------------------------------------------------- -OP___(immi, 49, I, I, 1) // int immediate -OP_64(immq, 50, QorD, Q, 1) // quad immediate -OP___(immd, 51, QorD, D, 1) // double immediate - -OP_UN(52) - -//--------------------------------------------------------------------------- -// Comparisons -//--------------------------------------------------------------------------- - -// All comparisons return an int: 0 on failure and 1 on success. -// -// Within each type group, order must be preserved so that, except for eq*, (op -// ^ 1) gives the opposite one (eg. lt ^ 1 == gt). eq* must have odd numbers -// for this to work. They must also remain contiguous so that opcode range -// checking works correctly. Static assertions in LIR.h check these -// requirements. -OP___(eqi, 53, Op2, I, 1) // int equality -OP___(lti, 54, Op2, I, 1) // signed int less-than -OP___(gti, 55, Op2, I, 1) // signed int greater-than -OP___(lei, 56, Op2, I, 1) // signed int less-than-or-equal -OP___(gei, 57, Op2, I, 1) // signed int greater-than-or-equal -OP___(ltui, 58, Op2, I, 1) // unsigned int less-than -OP___(gtui, 59, Op2, I, 1) // unsigned int greater-than -OP___(leui, 60, Op2, I, 1) // unsigned int less-than-or-equal -OP___(geui, 61, Op2, I, 1) // unsigned int greater-than-or-equal - -OP_UN(62) - -OP_64(eqq, 63, Op2, I, 1) // quad equality -OP_64(ltq, 64, Op2, I, 1) // signed quad less-than -OP_64(gtq, 65, Op2, I, 1) // signed quad greater-than -OP_64(leq, 66, Op2, I, 1) // signed quad less-than-or-equal -OP_64(geq, 67, Op2, I, 1) // signed quad greater-than-or-equal -OP_64(ltuq, 68, Op2, I, 1) // unsigned quad less-than -OP_64(gtuq, 69, Op2, I, 1) // unsigned quad greater-than -OP_64(leuq, 70, Op2, I, 1) // unsigned quad less-than-or-equal -OP_64(geuq, 71, Op2, I, 1) // unsigned quad greater-than-or-equal - -OP_UN(72) - -OP___(eqd, 73, Op2, I, 1) // double equality -OP___(ltd, 74, Op2, I, 1) // double less-than -OP___(gtd, 75, Op2, I, 1) // double greater-than -OP___(led, 76, Op2, I, 1) // double less-than-or-equal -OP___(ged, 77, Op2, I, 1) // double greater-than-or-equal - -//--------------------------------------------------------------------------- -// Arithmetic -//--------------------------------------------------------------------------- -OP___(negi, 78, Op1, I, 1) // negate int -OP___(addi, 79, Op2, I, 1) // add int -OP___(subi, 80, Op2, I, 1) // subtract int -OP___(muli, 81, Op2, I, 1) // multiply int -OP_86(divi, 82, Op2, I, 1) // divide int -// LIR_modi is a hack. It's only used on i386/X64. The operand is the result -// of a LIR_divi because on i386/X64 div and mod results are computed by the -// same instruction. -OP_86(modi, 83, Op1, I, 1) // modulo int - -OP___(noti, 84, Op1, I, 1) // bitwise-NOT int -OP___(andi, 85, Op2, I, 1) // bitwise-AND int -OP___(ori, 86, Op2, I, 1) // bitwise-OR int -OP___(xori, 87, Op2, I, 1) // bitwise-XOR int - -// For all three integer shift operations, only the bottom five bits of the -// second operand are used, and they are treated as unsigned. This matches -// x86 semantics. -OP___(lshi, 88, Op2, I, 1) // left shift int -OP___(rshi, 89, Op2, I, 1) // right shift int (>>) -OP___(rshui, 90, Op2, I, 1) // right shift unsigned int (>>>) - -OP_64(addq, 91, Op2, Q, 1) // add quad -OP_64(subq, 92, Op2, Q, 1) // subtract quad - -OP_64(andq, 93, Op2, Q, 1) // bitwise-AND quad -OP_64(orq, 94, Op2, Q, 1) // bitwise-OR quad -OP_64(xorq, 95, Op2, Q, 1) // bitwise-XOR quad - -// For all three quad shift operations, only the bottom six bits of the -// second operand are used, and they are treated as unsigned. This matches -// x86-64 semantics. -OP_64(lshq, 96, Op2, Q, 1) // left shift quad; 2nd operand is an int -OP_64(rshq, 97, Op2, Q, 1) // right shift quad; 2nd operand is an int -OP_64(rshuq, 98, Op2, Q, 1) // right shift unsigned quad; 2nd operand is an int - -OP___(negd, 99, Op1, D, 1) // negate double -OP___(addd, 100, Op2, D, 1) // add double -OP___(subd, 101, Op2, D, 1) // subtract double -OP___(muld, 102, Op2, D, 1) // multiply double -OP___(divd, 103, Op2, D, 1) // divide double -// LIR_modd is just a place-holder opcode, ie. the back-ends cannot generate -// code for it. It's used in TraceMonkey briefly but is always demoted to a -// LIR_modl or converted to a function call before Nanojit has to do anything -// serious with it. -OP___(modd, 104, Op2, D, 1) // modulo double - -OP___(cmovi, 105, Op3, I, 1) // conditional move int -OP_64(cmovq, 106, Op3, Q, 1) // conditional move quad -OP___(cmovd, 107, Op3, D, 1) // conditional move double - -//--------------------------------------------------------------------------- -// Conversions -//--------------------------------------------------------------------------- -OP_64(i2q, 108, Op1, Q, 1) // sign-extend int to quad -OP_64(ui2uq, 109, Op1, Q, 1) // zero-extend unsigned int to unsigned quad -OP_64(q2i, 110, Op1, I, 1) // truncate quad to int (removes the high 32 bits) - -OP___(i2d, 111, Op1, D, 1) // convert int to double -OP___(ui2d, 112, Op1, D, 1) // convert unsigned int to double - -// The rounding behavior of LIR_d2i is platform specific. -// -// Platform Asm code Behavior -// -------- -------- -------- -// x86 w/ x87 fist uses current FP control word (default is rounding) -// x86 w/ SSE cvttsd2si performs round to zero (truncate) -// x64 (SSE) cvttsd2si performs round to zero (truncate) -// PowerPC unsupported -// ARM ftosid round to nearest -// MIPS trunc.w.d performs round to zero (truncate) -// SH4 frtc performs round to zero (truncate) -// SPARC fdtoi performs round to zero (truncate) -// -// round to zero examples: 1.9 -> 1, 1.1 -> 1, -1.1 -> -1, -1.9 -> -1 -// round to nearest examples: 1.9 -> 2, 1.1 -> 1, -1.1 -> -1, -1.9 -> -2 -OP___(d2i, 113, Op1, I, 1) // convert double to int (no exceptions raised) - -OP_64(dasq, 114, Op1, Q, 1) // interpret the bits of a double as a quad -OP_64(qasd, 115, Op1, D, 1) // interpret the bits of a quad as a double - -//--------------------------------------------------------------------------- -// Overflow arithmetic -//--------------------------------------------------------------------------- -// These all exit if overflow occurred. The result is valid on either path. -OP___(addxovi, 116, Op3, I, 1) // add int and exit on overflow -OP___(subxovi, 117, Op3, I, 1) // subtract int and exit on overflow -OP___(mulxovi, 118, Op3, I, 1) // multiply int and exit on overflow - -// These all branch if overflow occurred. The result is valid on either path. -OP___(addjovi, 119, Op3, I, 1) // add int and branch on overflow -OP___(subjovi, 120, Op3, I, 1) // subtract int and branch on overflow -OP___(muljovi, 121, Op3, I, 1) // multiply int and branch on overflow - -OP_64(addjovq, 122, Op3, Q, 1) // add quad and branch on overflow -OP_64(subjovq, 123, Op3, Q, 1) // subtract quad and branch on overflow - -//--------------------------------------------------------------------------- -// SoftFloat -//--------------------------------------------------------------------------- -OP_SF(dlo2i, 124, Op1, I, 1) // get the low 32 bits of a double as an int -OP_SF(dhi2i, 125, Op1, I, 1) // get the high 32 bits of a double as an int -OP_SF(ii2d, 126, Op2, D, 1) // join two ints (1st arg is low bits, 2nd is high) - -// LIR_hcalli is a hack that's only used on 32-bit platforms that use -// SoftFloat. Its operand is always a LIR_calli, but one that specifies a -// function that returns a double. It indicates that the double result is -// returned via two 32-bit integer registers. The result is always used as the -// second operand of a LIR_ii2d. -OP_SF(hcalli, 127, Op1, I, 1) - -#undef OP_UN -#undef OP_32 -#undef OP_64 -#undef OP_SF -#undef OP_86 diff --git a/deps/mozjs/js/src/nanojit/Native.h b/deps/mozjs/js/src/nanojit/Native.h deleted file mode 100644 index 7c2a8da95fa..00000000000 --- a/deps/mozjs/js/src/nanojit/Native.h +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_Native__ -#define __nanojit_Native__ - -// define PEDANTIC=1 to ignore specialized forms, force general forms -// for everything, far branches, extra page-linking, etc. This will -// flush out many corner cases. - -#define PEDANTIC 0 -#if PEDANTIC -# define UNLESS_PEDANTIC(...) -# define IF_PEDANTIC(...) __VA_ARGS__ -#else -# define UNLESS_PEDANTIC(...) __VA_ARGS__ -# define IF_PEDANTIC(...) -#endif - -#ifdef NANOJIT_IA32 -#include "Nativei386.h" -#elif defined(NANOJIT_ARM) -#include "NativeARM.h" -#elif defined(NANOJIT_PPC) -#include "NativePPC.h" -#elif defined(NANOJIT_SPARC) -#include "NativeSparc.h" -#elif defined(NANOJIT_X64) -#include "NativeX64.h" -#elif defined(NANOJIT_SH4) -#include "NativeSH4.h" -#elif defined(NANOJIT_MIPS) -#include "NativeMIPS.h" -#else -#error "unknown nanojit architecture" -#endif - -#ifndef NJ_USES_IMMD_POOL -# define NJ_USES_IMMD_POOL 0 -#endif - -#ifndef NJ_JTBL_SUPPORTED -# define NJ_JTBL_SUPPORTED 0 -#endif - -#ifndef NJ_EXPANDED_LOADSTORE_SUPPORTED -# define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 -#endif - -#ifndef NJ_F2I_SUPPORTED -# define NJ_F2I_SUPPORTED 0 -#endif - -#ifndef NJ_SOFTFLOAT_SUPPORTED -# define NJ_SOFTFLOAT_SUPPORTED 0 -#endif - -#ifndef NJ_DIVI_SUPPORTED -# define NJ_DIVI_SUPPORTED 0 -#endif - -#if NJ_SOFTFLOAT_SUPPORTED - #define CASESF(x) case x -#else - #define CASESF(x) -#endif - -namespace nanojit { - - class Fragment; - struct SideExit; - - struct GuardRecord - { - void* jmp; - GuardRecord* next; - SideExit* exit; - // profiling stuff - verbose_only( uint32_t profCount; ) - verbose_only( uint32_t profGuardID; ) - verbose_only( GuardRecord* nextInFrag; ) - }; - - struct SideExit - { - GuardRecord* guards; - Fragment* from; - Fragment* target; - - void addGuard(GuardRecord* gr) - { - NanoAssert(gr->next == NULL); - NanoAssert(guards != gr); - gr->next = guards; - guards = gr; - } - }; -} - - #define isSPorFP(r) ( (r)==SP || (r)==FP ) - - #if defined(NJ_VERBOSE) - inline char cvaltoa(unsigned char u) { - return u<10 ? u+'0' : u+'a'-10; - } - - inline char* appendHexVals(char* str, char* valFrom, char* valTo) { - NanoAssert(valFrom <= valTo); - str += VMPI_strlen(str); - for(char* ch = valFrom; ch < valTo; ch++) { - unsigned char u = (unsigned char)*ch; - *str++ = cvaltoa(u >> 4); - *str++ = cvaltoa(u & 0xf); - *str++ = ' '; - } - *str = '\0'; - return str; - } - - inline char* padTo(char* str, int n, char c=' ') { - char* start = str + VMPI_strlen(str); - char* end = &str[n]; - while(start < end) - *start++ = c; - *end = '\0'; - return end; - } - - // Used for printing native instructions. Like Assembler::outputf(), - // but only outputs if LC_Native is set. Also prepends the output - // with the address of the current native instruction. - #define asm_output(...) do { \ - if (_logc->lcbits & LC_Native) { \ - outline[0]='\0'; \ - VMPI_sprintf(outline, "%p ", _nIns); \ - if (_logc->lcbits & LC_Bytes) { \ - appendHexVals(outline, (char*)_nIns, (char*)_nInsAfter); \ - padTo(outline, 3*15); \ - } \ - VMPI_sprintf(outline + VMPI_strlen(outline), ##__VA_ARGS__); \ - output(); \ - _nInsAfter = _nIns; \ - } \ - } while (0) /* no semi */ - #define gpn(r) regNames[(REGNUM(r))] - #else - #define asm_output(...) - #define gpn(r) - #endif /* NJ_VERBOSE */ - -#endif // __nanojit_Native__ diff --git a/deps/mozjs/js/src/nanojit/NativeARM.cpp b/deps/mozjs/js/src/nanojit/NativeARM.cpp deleted file mode 100644 index 8559e567734..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeARM.cpp +++ /dev/null @@ -1,2928 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * Vladimir Vukicevic - * Jacob Bramley - * Tero Koskinen - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef UNDER_CE -#include -#endif - -#if defined(FEATURE_NANOJIT) && defined(NANOJIT_ARM) - -namespace nanojit -{ - -#ifdef NJ_VERBOSE -const char* regNames[] = {"r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","fp","ip","sp","lr","pc", - "d0","d1","d2","d3","d4","d5","d6","d7","s0"}; -const char* condNames[] = {"eq","ne","cs","cc","mi","pl","vs","vc","hi","ls","ge","lt","gt","le",""/*al*/,"nv"}; -const char* shiftNames[] = { "lsl", "lsl", "lsr", "lsr", "asr", "asr", "ror", "ror" }; -#endif - -const Register Assembler::argRegs[] = { R0, R1, R2, R3 }; -const Register Assembler::retRegs[] = { R0, R1 }; -const Register Assembler::savedRegs[] = { R4, R5, R6, R7, R8, R9, R10 }; - -// -------------------------------- -// ARM-specific utility functions. -// -------------------------------- - -#ifdef DEBUG -// Return true if enc is a valid Operand 2 encoding and thus can be used as-is -// in an ARM arithmetic operation that accepts such encoding. -// -// This utility does not know (or determine) the actual value that the encoded -// value represents, and thus cannot be used to ensure the correct operation of -// encOp2Imm, but it does ensure that the encoded value can be used to encode a -// valid ARM instruction. decOp2Imm can be used if you also need to check that -// a literal is correctly encoded (and thus that encOp2Imm is working -// correctly). -inline bool -Assembler::isOp2Imm(uint32_t enc) -{ - return ((enc & 0xfff) == enc); -} - -// Decodes operand 2 immediate values (for debug output and assertions). -inline uint32_t -Assembler::decOp2Imm(uint32_t enc) -{ - NanoAssert(isOp2Imm(enc)); - - uint32_t imm8 = enc & 0xff; - uint32_t rot = 32 - ((enc >> 7) & 0x1e); - - return imm8 << (rot & 0x1f); -} -#endif - -// Calculate the number of leading zeroes in data. -static inline uint32_t -CountLeadingZeroesSlow(uint32_t data) -{ - // Other platforms must fall back to a C routine. This won't be as - // efficient as the CLZ instruction, but it is functional. - uint32_t try_shift; - - uint32_t leading_zeroes = 0; - - // This loop does a bisection search rather than the obvious rotation loop. - // This should be faster, though it will still be no match for CLZ. - for (try_shift = 16; try_shift != 0; try_shift /= 2) { - uint32_t shift = leading_zeroes + try_shift; - if (((data << shift) >> shift) == data) { - leading_zeroes = shift; - } - } - - return leading_zeroes; -} - -inline uint32_t -Assembler::CountLeadingZeroes(uint32_t data) -{ - uint32_t leading_zeroes; - -#if defined(__ARMCC__) - // ARMCC can do this with an intrinsic. - leading_zeroes = __clz(data); -#elif defined(__GNUC__) - // GCC can use inline assembler to insert a CLZ instruction. - if (ARM_ARCH_AT_LEAST(5)) { - __asm ( -#if defined(ANDROID) && (NJ_COMPILER_ARM_ARCH < 7) - // On Android gcc compiler, the clz instruction is not supported with a - // target smaller than armv7, despite it being legal for armv5+. - " .arch armv7-a\n" -#elif (NJ_COMPILER_ARM_ARCH < 5) - // Targetting armv5t allows a toolchain with armv4t target to still build - // with clz, and clz to be used when appropriate at runtime. - " .arch armv5t\n" -#endif - " clz %0, %1 \n" - : "=r" (leading_zeroes) - : "r" (data) - ); - } else { - leading_zeroes = CountLeadingZeroesSlow(data); - } -#elif defined(UNDER_CE) - // WinCE can do this with an intrinsic. - leading_zeroes = _CountLeadingZeros(data); -#else - leading_zeroes = CountLeadingZeroesSlow(data); -#endif - - // Assert that the operation worked! - NanoAssert(((0xffffffff >> leading_zeroes) & data) == data); - - return leading_zeroes; -} - -// The ARM instruction set allows some flexibility to the second operand of -// most arithmetic operations. When operand 2 is an immediate value, it takes -// the form of an 8-bit value rotated by an even value in the range 0-30. -// -// Some values that can be encoded this scheme — such as 0xf000000f — are -// probably fairly rare in practice and require extra code to detect, so this -// function implements a fast CLZ-based heuristic to detect any value that can -// be encoded using just a shift, and not a full rotation. For example, -// 0xff000000 and 0x000000ff are both detected, but 0xf000000f is not. -// -// This function will return true to indicate that the encoding was successful, -// or false to indicate that the literal could not be encoded as an operand 2 -// immediate. If successful, the encoded value will be written to *enc. -inline bool -Assembler::encOp2Imm(uint32_t literal, uint32_t * enc) -{ - // The number of leading zeroes in the literal. This is used to calculate - // the rotation component of the encoding. - uint32_t leading_zeroes; - - // Components of the operand 2 encoding. - int32_t rot; - uint32_t imm8; - - // Check the literal to see if it is a simple 8-bit value. I suspect that - // most literals are in fact small values, so doing this check early should - // give a decent speed-up. - if (literal < 256) - { - *enc = literal; - return true; - } - - // Determine the number of leading zeroes in the literal. This is used to - // calculate the required rotation. - leading_zeroes = CountLeadingZeroes(literal); - - // We've already done a check to see if the literal is an 8-bit value, so - // leading_zeroes must be less than (and not equal to) (32-8)=24. However, - // if it is greater than 24, this algorithm will break, so debug code - // should use an assertion here to check that we have a value that we - // expect. - NanoAssert(leading_zeroes < 24); - - // Assuming that we have a field of no more than 8 bits for a valid - // literal, we can calculate the required rotation by subtracting - // leading_zeroes from (32-8): - // - // Example: - // 0: Known to be zero. - // 1: Known to be one. - // X: Either zero or one. - // .: Zero in a valid operand 2 literal. - // - // Literal: [ 1XXXXXXX ........ ........ ........ ] - // leading_zeroes = 0 - // Therefore rot (left) = 24. - // Encoded 8-bit literal: [ 1XXXXXXX ] - // - // Literal: [ ........ ..1XXXXX XX...... ........ ] - // leading_zeroes = 10 - // Therefore rot (left) = 14. - // Encoded 8-bit literal: [ 1XXXXXXX ] - // - // Note, however, that we can only encode even shifts, and so - // "rot=24-leading_zeroes" is not sufficient by itself. By ignoring - // zero-bits in odd bit positions, we can ensure that we get a valid - // encoding. - // - // Example: - // Literal: [ 01XXXXXX ........ ........ ........ ] - // leading_zeroes = 1 - // Therefore rot (left) = round_up(23) = 24. - // Encoded 8-bit literal: [ 01XXXXXX ] - rot = 24 - (leading_zeroes & ~1); - - // The imm8 component of the operand 2 encoding can be calculated from the - // rot value. - imm8 = literal >> rot; - - // The validity of the literal can be checked by reversing the - // calculation. It is much easier to decode the immediate than it is to - // encode it! - if (literal != (imm8 << rot)) { - // The encoding is not valid, so report the failure. Calling code - // should use some other method of loading the value (such as LDR). - return false; - } - - // The operand is valid, so encode it. - // Note that the ARM encoding is actually described by a rotate to the - // _right_, so rot must be negated here. Calculating a left shift (rather - // than calculating a right rotation) simplifies the above code. - *enc = ((-rot << 7) & 0xf00) | imm8; - - // Assert that the operand was properly encoded. - NanoAssert(decOp2Imm(*enc) == literal); - - return true; -} - -// Encode "rd = rn + imm" using an appropriate instruction sequence. -// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. -// (The declaration in NativeARM.h defines the default value of stat as 0.) -// -// It is not valid to call this function if: -// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm) -// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the -// encOp2Imm method. -void -Assembler::asm_add_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) -{ - // Operand 2 encoding of the immediate. - uint32_t op2imm; - - NanoAssert(IsGpReg(rd)); - NanoAssert(IsGpReg(rn)); - NanoAssert((stat & 1) == stat); - - // As a special case to simplify code elsewhere, emit nothing where we - // don't want to update the flags (stat == 0), the second operand is 0 and - // (rd == rn). Such instructions are effectively NOPs. - if ((imm == 0) && (stat == 0) && (rd == rn)) { - return; - } - - // Try to encode the value directly as an operand 2 immediate value, then - // fall back to loading the value into a register. - if (encOp2Imm(imm, &op2imm)) { - ADDis(rd, rn, op2imm, stat); - } else if (encOp2Imm(-imm, &op2imm)) { - // We could not encode the value for ADD, so try to encode it for SUB. - // Note that this is valid even if stat is set, _unless_ imm is 0, but - // that case is caught above. - NanoAssert(imm != 0); - SUBis(rd, rn, op2imm, stat); - } else { - // We couldn't encode the value directly, so use an intermediate - // register to encode the value. We will use IP to do this unless rn is - // IP; in that case we can reuse rd. This allows every case other than - // "ADD IP, IP, =#imm". - Register rm = (rn == IP) ? (rd) : (IP); - NanoAssert(rn != rm); - - ADDs(rd, rn, rm, stat); - asm_ld_imm(rm, imm); - } -} - -// Encode "rd = rn - imm" using an appropriate instruction sequence. -// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. -// (The declaration in NativeARM.h defines the default value of stat as 0.) -// -// It is not valid to call this function if: -// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm) -// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the -// encOp2Imm method. -void -Assembler::asm_sub_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) -{ - // Operand 2 encoding of the immediate. - uint32_t op2imm; - - NanoAssert(IsGpReg(rd)); - NanoAssert(IsGpReg(rn)); - NanoAssert((stat & 1) == stat); - - // As a special case to simplify code elsewhere, emit nothing where we - // don't want to update the flags (stat == 0), the second operand is 0 and - // (rd == rn). Such instructions are effectively NOPs. - if ((imm == 0) && (stat == 0) && (rd == rn)) { - return; - } - - // Try to encode the value directly as an operand 2 immediate value, then - // fall back to loading the value into a register. - if (encOp2Imm(imm, &op2imm)) { - SUBis(rd, rn, op2imm, stat); - } else if (encOp2Imm(-imm, &op2imm)) { - // We could not encode the value for SUB, so try to encode it for ADD. - // Note that this is valid even if stat is set, _unless_ imm is 0, but - // that case is caught above. - NanoAssert(imm != 0); - ADDis(rd, rn, op2imm, stat); - } else { - // We couldn't encode the value directly, so use an intermediate - // register to encode the value. We will use IP to do this unless rn is - // IP; in that case we can reuse rd. This allows every case other than - // "SUB IP, IP, =#imm". - Register rm = (rn == IP) ? (rd) : (IP); - NanoAssert(rn != rm); - - SUBs(rd, rn, rm, stat); - asm_ld_imm(rm, imm); - } -} - -// Encode "rd = rn & imm" using an appropriate instruction sequence. -// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. -// (The declaration in NativeARM.h defines the default value of stat as 0.) -// -// It is not valid to call this function if: -// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(~imm) -// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the -// encOp2Imm method. -void -Assembler::asm_and_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) -{ - // Operand 2 encoding of the immediate. - uint32_t op2imm; - - NanoAssert(IsGpReg(rd)); - NanoAssert(IsGpReg(rn)); - NanoAssert((stat & 1) == stat); - - // Try to encode the value directly as an operand 2 immediate value, then - // fall back to loading the value into a register. - if (encOp2Imm(imm, &op2imm)) { - ANDis(rd, rn, op2imm, stat); - } else if (encOp2Imm(~imm, &op2imm)) { - // Use BIC with the inverted immediate. - BICis(rd, rn, op2imm, stat); - } else { - // We couldn't encode the value directly, so use an intermediate - // register to encode the value. We will use IP to do this unless rn is - // IP; in that case we can reuse rd. This allows every case other than - // "AND IP, IP, =#imm". - Register rm = (rn == IP) ? (rd) : (IP); - NanoAssert(rn != rm); - - ANDs(rd, rn, rm, stat); - asm_ld_imm(rm, imm); - } -} - -// Encode "rd = rn | imm" using an appropriate instruction sequence. -// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. -// (The declaration in NativeARM.h defines the default value of stat as 0.) -// -// It is not valid to call this function if: -// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) -// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the -// encOp2Imm method. -void -Assembler::asm_orr_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) -{ - // Operand 2 encoding of the immediate. - uint32_t op2imm; - - NanoAssert(IsGpReg(rd)); - NanoAssert(IsGpReg(rn)); - NanoAssert((stat & 1) == stat); - - // Try to encode the value directly as an operand 2 immediate value, then - // fall back to loading the value into a register. - if (encOp2Imm(imm, &op2imm)) { - ORRis(rd, rn, op2imm, stat); - } else { - // We couldn't encode the value directly, so use an intermediate - // register to encode the value. We will use IP to do this unless rn is - // IP; in that case we can reuse rd. This allows every case other than - // "ORR IP, IP, =#imm". - Register rm = (rn == IP) ? (rd) : (IP); - NanoAssert(rn != rm); - - ORRs(rd, rn, rm, stat); - asm_ld_imm(rm, imm); - } -} - -// Encode "rd = rn ^ imm" using an appropriate instruction sequence. -// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. -// (The declaration in NativeARM.h defines the default value of stat as 0.) -// -// It is not valid to call this function if: -// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) -// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the -// encOp2Imm method. -void -Assembler::asm_eor_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) -{ - // Operand 2 encoding of the immediate. - uint32_t op2imm; - - NanoAssert(IsGpReg(rd)); - NanoAssert(IsGpReg(rn)); - NanoAssert((stat & 1) == stat); - - // Try to encode the value directly as an operand 2 immediate value, then - // fall back to loading the value into a register. - if (encOp2Imm(imm, &op2imm)) { - EORis(rd, rn, op2imm, stat); - } else { - // We couldn't encoder the value directly, so use an intermediate - // register to encode the value. We will use IP to do this unless rn is - // IP; in that case we can reuse rd. This allows every case other than - // "EOR IP, IP, =#imm". - Register rm = (rn == IP) ? (rd) : (IP); - NanoAssert(rn != rm); - - EORs(rd, rn, rm, stat); - asm_ld_imm(rm, imm); - } -} - -// -------------------------------- -// Assembler functions. -// -------------------------------- - -void -Assembler::nInit() -{ - nHints[LIR_calli] = rmask(retRegs[0]); - nHints[LIR_hcalli] = rmask(retRegs[1]); - nHints[LIR_paramp] = PREFER_SPECIAL; -} - -void Assembler::nBeginAssembly() -{ - max_out_args = 0; -} - -NIns* -Assembler::genPrologue() -{ - /** - * Prologue - */ - - // NJ_RESV_OFFSET is space at the top of the stack for us - // to use for parameter passing (8 bytes at the moment) - uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.stackSlotsNeeded(); - uint32_t savingCount = 2; - - uint32_t savingMask = rmask(FP) | rmask(LR); - - // so for alignment purposes we've pushed return addr and fp - uint32_t stackPushed = STACK_GRANULARITY * savingCount; - uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); - int32_t amt = aligned - stackPushed; - - // Make room on stack for what we are doing - if (amt) - asm_sub_imm(SP, SP, amt); - - verbose_only( asm_output("## %p:",(void*)_nIns); ) - verbose_only( asm_output("## patch entry"); ) - NIns *patchEntry = _nIns; - - MOV(FP, SP); - PUSH_mask(savingMask); - return patchEntry; -} - -void -Assembler::nFragExit(LIns* guard) -{ - SideExit * exit = guard->record()->exit; - Fragment * frag = exit->target; - - bool target_is_known = frag && frag->fragEntry; - - if (target_is_known) { - // The target exists so we can simply emit a branch to its location. - JMP_far(frag->fragEntry); - } else { - // The target doesn't exit yet, so emit a jump to the epilogue. If the - // target is created later on, the jump will be patched. - - GuardRecord *gr = guard->record(); - - if (!_epilogue) - _epilogue = genEpilogue(); - - // Jump to the epilogue. This may get patched later, but JMP_far always - // emits two instructions even when only one is required, so patching - // will work correctly. - JMP_far(_epilogue); - - // In the future you may want to move this further down so that we can - // overwrite the r0 guard record load during a patch to a different - // fragment with some assumed input-register state. Not today though. - gr->jmp = _nIns; - - // NB: this is a workaround for the fact that, by patching a - // fragment-exit jump, we could be changing the *meaning* of the R0 - // register we're passing to the jump target. If we jump to the - // epilogue, ideally R0 means "return value when exiting fragment". - // If we patch this to jump to another fragment however, R0 means - // "incoming 0th parameter". This is just a quirk of ARM ABI. So - // we compromise by passing "return value" to the epilogue in IP, - // not R0, and have the epilogue MOV(R0, IP) first thing. - - asm_ld_imm(IP, int(gr)); - } - -#ifdef NJ_VERBOSE - if (_config.arm_show_stats) { - // load R1 with Fragment *fromFrag, target fragment - // will make use of this when calling fragenter(). - int fromfrag = int((Fragment*)_thisfrag); - asm_ld_imm(argRegs[1], fromfrag); - } -#endif - - // profiling for the exit - verbose_only( - if (_logc->lcbits & LC_FragProfile) { - asm_inc_m32( &guard->record()->profCount ); - } - ) - - // Pop the stack frame. - MOV(SP, FP); -} - -NIns* -Assembler::genEpilogue() -{ - RegisterMask savingMask; - - if (ARM_ARCH_AT_LEAST(5)) { - // On ARMv5+, loading directly to PC correctly handles interworking. - savingMask = rmask(FP) | rmask(PC); - - } else { - // On ARMv4T, interworking is not handled properly, therefore, we pop - // lr and use bx lr to avoid that. - savingMask = rmask(FP) | rmask(LR); - BX(LR); - } - POP_mask(savingMask); // regs - - // NB: this is the later half of the dual-nature patchable exit branch - // workaround noted above in nFragExit. IP has the "return value" - // incoming, we need to move it to R0. - MOV(R0, IP); - - return _nIns; -} - -/* - * asm_arg will encode the specified argument according to the current ABI, and - * will update r and stkd as appropriate so that the next argument can be - * encoded. - * - * Linux has used ARM's EABI for some time. Windows CE uses the legacy ABI. - * - * Under EABI: - * - doubles are 64-bit aligned both in registers and on the stack. - * If the next available argument register is R1, it is skipped - * and the double is placed in R2:R3. If R0:R1 or R2:R3 are not - * available, the double is placed on the stack, 64-bit aligned. - * - 32-bit arguments are placed in registers and 32-bit aligned - * on the stack. - * - * Under EABI with hardware floating-point procedure-call variant: - * - Same as EABI, but doubles are passed in D0..D7 registers. - * - * Under legacy ABI: - * - doubles are placed in subsequent arg registers; if the next - * available register is r3, the low order word goes into r3 - * and the high order goes on the stack. - * - 32-bit arguments are placed in the next available arg register, - * - both doubles and 32-bit arguments are placed on stack with 32-bit - * alignment. - */ -void -Assembler::asm_arg(ArgType ty, LIns* arg, ParameterRegisters& params) -{ - // The stack pointer must always be at least aligned to 4 bytes. - NanoAssert((params.stkd & 3) == 0); - - if (ty == ARGTYPE_D) { - // This task is fairly complex and so is delegated to asm_arg_64. - asm_arg_64(arg, params); - } else { - NanoAssert(ty == ARGTYPE_I || ty == ARGTYPE_UI); - // pre-assign registers R0-R3 for arguments (if they fit) - if (params.r < R4) { - asm_regarg(ty, arg, params.r); - params.r = Register(params.r + 1); - } else { - asm_stkarg(arg, params.stkd); - params.stkd += 4; - } - } -} - -// Encode a 64-bit floating-point argument using the appropriate ABI. -// This function operates in the same way as asm_arg, except that it will only -// handle arguments where (ArgType)ty == ARGTYPE_D. - -#ifdef NJ_ARM_EABI_HARD_FLOAT -void -Assembler::asm_arg_64(LIns* arg, ParameterRegisters& params) -{ - NanoAssert(IsFpReg(params.float_r)); - if (params.float_r <= D7) { - findSpecificRegFor(arg, params.float_r); - params.float_r = Register(params.float_r + 1); - } else { - NanoAssertMsg(0, "Only 8 floating point arguments supported"); - } -} - -#else -void -Assembler::asm_arg_64(LIns* arg, ParameterRegisters& params) -{ - // The stack pointer must always be at least aligned to 4 bytes. - NanoAssert((params.stkd & 3) == 0); - // The only use for this function when we are using soft floating-point - // is for LIR_ii2d. - NanoAssert(ARM_VFP || arg->isop(LIR_ii2d)); - -#ifdef NJ_ARM_EABI - // EABI requires that 64-bit arguments are aligned on even-numbered - // registers, as R0:R1 or R2:R3. If the register base is at an - // odd-numbered register, advance it. Note that this will push r past - // R3 if r is R3 to start with, and will force the argument to go on - // the stack. - if ((params.r == R1) || (params.r == R3)) { - params.r = Register(params.r + 1); - } -#endif - - if (params.r < R3) { - Register ra = params.r; - Register rb = Register(params.r + 1); - params.r = Register(rb + 1); - -#ifdef NJ_ARM_EABI - // EABI requires that 64-bit arguments are aligned on even-numbered - // registers, as R0:R1 or R2:R3. - NanoAssert( ((ra == R0) && (rb == R1)) || ((ra == R2) && (rb == R3)) ); -#endif - - // Put the argument in ra and rb. If the argument is in a VFP register, - // use FMRRD to move it to ra and rb. Otherwise, let asm_regarg deal - // with the argument as if it were two 32-bit arguments. - if (ARM_VFP) { - Register dm = findRegFor(arg, FpRegs); - FMRRD(ra, rb, dm); - } else { - asm_regarg(ARGTYPE_I, arg->oprnd1(), ra); - asm_regarg(ARGTYPE_I, arg->oprnd2(), rb); - } - -#ifndef NJ_ARM_EABI - } else if (params.r == R3) { - // We only have one register left, but the legacy ABI requires that we - // put 32 bits of the argument in the register (R3) and the remaining - // 32 bits on the stack. - Register ra = params.r; // R3 - params.r = R4; - - // We're splitting the argument between registers and the stack. This - // must be the first time that the stack is used, so stkd must be at 0. - NanoAssert(params.stkd == 0); - - if (ARM_VFP) { - Register dm = findRegFor(arg, FpRegs); - // TODO: We could optimize the this to store directly from - // the VFP register to memory using "FMRRD ra, fp_reg[31:0]" and - // "STR fp_reg[63:32], [SP, #stkd]". - - // Load from the floating-point register as usual, but use IP - // as a swap register. - STR(IP, SP, 0); - FMRRD(ra, IP, dm); - } else { - // Without VFP, we can simply use asm_regarg and asm_stkarg to - // encode the two 32-bit words as we don't need to load from a VFP - // register. - asm_regarg(ARGTYPE_I, arg->oprnd1(), ra); - asm_stkarg(arg->oprnd2(), 0); - } - params.stkd += 4; -#endif - } else { - // The argument won't fit in registers, so pass on to asm_stkarg. -#ifdef NJ_ARM_EABI - // EABI requires that 64-bit arguments are 64-bit aligned. - if ((params.stkd & 7) != 0) { - // stkd will always be aligned to at least 4 bytes; this was - // asserted on entry to this function. - params.stkd += 4; - } -#endif - if (ARM_VFP) { - asm_stkarg(arg, params.stkd); - } else { - asm_stkarg(arg->oprnd1(), params.stkd); - asm_stkarg(arg->oprnd2(), params.stkd+4); - } - params.stkd += 8; - } -} -#endif // NJ_ARM_EABI_HARD_FLOAT - -void -Assembler::asm_regarg(ArgType ty, LIns* p, Register rd) -{ - // Note that we don't have to prepareResultReg here because it is already - // done by the caller, and the target register is passed as 'rd'. - // Similarly, we don't have to freeResourcesOf(p). - - if (ty == ARGTYPE_I || ty == ARGTYPE_UI) - { - // Put the argument in register rd. - if (p->isImmI()) { - asm_ld_imm(rd, p->immI()); - } else { - if (p->isInReg()) { - MOV(rd, p->getReg()); - } else { - // Re-use the target register if the source is no longer - // required. This saves a MOV instruction. - findSpecificRegForUnallocated(p, rd); - } - } - } else { - NanoAssert(ty == ARGTYPE_D); - // Floating-point arguments are handled as two integer arguments. - NanoAssert(false); - } -} - -void -Assembler::asm_stkarg(LIns* arg, int stkd) -{ - // The ABI doesn't allow accesses below the SP. - NanoAssert(stkd >= 0); - // The argument resides somewhere in registers, so we simply need to - // push it onto the stack. - if (arg->isI()) { - Register rt = findRegFor(arg, GpRegs); - asm_str(rt, SP, stkd); - } else { - // According to the comments in asm_arg_64, LIR_ii2d - // can have a 64-bit argument even if VFP is disabled. However, - // asm_arg_64 will split the argument and issue two 32-bit - // arguments to asm_stkarg so we can ignore that case here. - NanoAssert(arg->isD()); - NanoAssert(ARM_VFP); - Register dt = findRegFor(arg, FpRegs); -#ifdef NJ_ARM_EABI - // EABI requires that 64-bit arguments are 64-bit aligned. - NanoAssert((stkd % 8) == 0); -#endif - FSTD(dt, SP, stkd); - } -} - -void -Assembler::asm_call(LIns* ins) -{ - if (ARM_VFP && ins->isop(LIR_calld)) { - /* Because ARM actually returns the result in (R0,R1), and not in a - * floating point register, the code to move the result into a correct - * register is below. We do nothing here. - * - * The reason being that if we did something here, the final code - * sequence we'd get would be something like: - * MOV {R0-R3},params [from below] - * BL function [from below] - * MOV {R0-R3},spilled data [from evictScratchRegsExcept()] - * MOV Dx,{R0,R1} [from here] - * which is clearly broken. - * - * This is not a problem for non-floating point calls, because the - * restoring of spilled data into R0 is done via a call to - * prepareResultReg(R0) in the other branch of this if-then-else, - * meaning that evictScratchRegsExcept() will not modify R0. However, - * prepareResultReg is not aware of the concept of using a register - * pair (R0,R1) for the result of a single operation, so it can only be - * used here with the ultimate VFP register, and not R0/R1, which - * potentially allows for R0/R1 to get corrupted as described. - */ -#ifdef NJ_ARM_EABI_HARD_FLOAT - /* With ARM hardware floating point ABI, D0 is used to return the double - * from the function. We need to prepare it like we do for R0 in the else - * branch. - */ - prepareResultReg(ins, rmask(D0)); - freeResourcesOf(ins); -#endif - } else if (!ins->isop(LIR_callv)) { - prepareResultReg(ins, rmask(retRegs[0])); - // Immediately free the resources as we need to re-use the register for - // the arguments. - freeResourcesOf(ins); - } - - // Do this after we've handled the call result, so we don't - // force the call result to be spilled unnecessarily. - - evictScratchRegsExcept(0); - - const CallInfo* ci = ins->callInfo(); - ArgType argTypes[MAXARGS]; - uint32_t argc = ci->getArgTypes(argTypes); - bool indirect = ci->isIndirect(); - - // If we aren't using VFP, assert that the LIR operation is an integer - // function call. - NanoAssert(ARM_VFP || ins->isop(LIR_callv) || ins->isop(LIR_calli)); - - // If we're using VFP, but not hardware floating point ABI, and - // the return type is a double, it'll come back in R0/R1. - // We need to either place it in the result fp reg, or store it. - // See comments above for more details as to why this is necessary here - // for floating point calls, but not for integer calls. - if (!ARM_EABI_HARD && ARM_VFP && ins->isExtant()) { - // If the result size is a floating-point value, treat the result - // specially, as described previously. - if (ci->returnType() == ARGTYPE_D) { - NanoAssert(ins->isop(LIR_calld)); - - if (ins->isInReg()) { - Register dd = ins->getReg(); - // Copy the result to the (VFP) result register. - FMDRR(dd, R0, R1); - } else { - int d = findMemFor(ins); - // Immediately free the resources so the arguments can re-use - // the slot. - freeResourcesOf(ins); - - // The result doesn't have a register allocated, so store the - // result (in R0,R1) directly to its stack slot. - asm_str(R0, FP, d+0); - asm_str(R1, FP, d+4); - } - } - } - - // Emit the branch. - if (!indirect) { - verbose_only(if (_logc->lcbits & LC_Native) - outputf(" %p:", _nIns); - ) - - BranchWithLink((NIns*)ci->_address); - } else { - // Indirect call: we assign the address arg to LR - if (ARM_ARCH_AT_LEAST(5)) { -#ifndef UNDER_CE - // workaround for msft device emulator bug (blx lr emulated as no-op) - underrunProtect(8); - BLX(IP); - MOV(IP, LR); -#else - BLX(LR); -#endif - } else { - underrunProtect(12); - BX(IP); - MOV(LR, PC); - MOV(IP, LR); - } - asm_regarg(ARGTYPE_I, ins->arg(--argc), LR); - } - - // Encode the arguments, starting at R0 and with an empty argument stack (0). - // With hardware fp ABI, floating point arguments start from D0. - ParameterRegisters params = init_params(0, R0, D0); - - // Iterate through the argument list and encode each argument according to - // the ABI. - // Note that we loop through the arguments backwards as LIR specifies them - // in reverse order. - uint32_t i = argc; - while(i--) { - asm_arg(argTypes[i], ins->arg(i), params); - } - - if (params.stkd > max_out_args) { - max_out_args = params.stkd; - } -} - -Register -Assembler::nRegisterAllocFromSet(RegisterMask set) -{ - NanoAssert(set != 0); - - // The CountLeadingZeroes function will use the CLZ instruction where - // available. In other cases, it will fall back to a (slower) C - // implementation. - Register r = (Register)(31-CountLeadingZeroes(set)); - _allocator.free &= ~rmask(r); - - NanoAssert(IsGpReg(r) || IsFpReg(r)); - NanoAssert((rmask(r) & set) == rmask(r)); - - return r; -} - -void -Assembler::nRegisterResetAll(RegAlloc& a) -{ - // add scratch registers to our free list for the allocator - a.clear(); - a.free = - rmask(R0) | rmask(R1) | rmask(R2) | rmask(R3) | rmask(R4) | - rmask(R5) | rmask(R6) | rmask(R7) | rmask(R8) | rmask(R9) | - rmask(R10) | rmask(LR); - if (ARM_VFP) { - a.free |= - rmask(D0) | rmask(D1) | rmask(D2) | rmask(D3) | - rmask(D4) | rmask(D5) | rmask(D6) | rmask(D7); - } -} - -static inline ConditionCode -get_cc(NIns *ins) -{ - return ConditionCode((*ins >> 28) & 0xF); -} - -static inline bool -branch_is_B(NIns* branch) -{ - return (*branch & 0x0E000000) == 0x0A000000; -} - -static inline bool -branch_is_LDR_PC(NIns* branch) -{ - return (*branch & 0x0F7FF000) == 0x051FF000; -} - -// Is this an instruction of the form ldr/str reg, [fp, #-imm] ? -static inline bool -is_ldstr_reg_fp_minus_imm(/*OUT*/uint32_t* isLoad, /*OUT*/uint32_t* rX, - /*OUT*/uint32_t* immX, NIns i1) -{ - if ((i1 & 0xFFEF0000) != 0xE50B0000) - return false; - *isLoad = (i1 >> 20) & 1; - *rX = (i1 >> 12) & 0xF; - *immX = i1 & 0xFFF; - return true; -} - -// Is this an instruction of the form ldmdb/stmdb fp, regset ? -static inline bool -is_ldstmdb_fp(/*OUT*/uint32_t* isLoad, /*OUT*/uint32_t* regSet, NIns i1) -{ - if ((i1 & 0xFFEF0000) != 0xE90B0000) - return false; - *isLoad = (i1 >> 20) & 1; - *regSet = i1 & 0xFFFF; - return true; -} - -// Make an instruction of the form ldmdb/stmdb fp, regset -static inline NIns -mk_ldstmdb_fp(uint32_t isLoad, uint32_t regSet) -{ - return 0xE90B0000 | (regSet & 0xFFFF) | ((isLoad & 1) << 20); -} - -// Compute the number of 1 bits in the lowest 16 bits of regSet -static inline uint32_t -size_of_regSet(uint32_t regSet) -{ - uint32_t x = regSet; - x = (x & 0x5555) + ((x >> 1) & 0x5555); - x = (x & 0x3333) + ((x >> 2) & 0x3333); - x = (x & 0x0F0F) + ((x >> 4) & 0x0F0F); - x = (x & 0x00FF) + ((x >> 8) & 0x00FF); - return x; -} - -// See if two ARM instructions, i1 and i2, can be combined into one -static bool -do_peep_2_1(/*OUT*/NIns* merged, NIns i1, NIns i2) -{ - uint32_t rX, rY, immX, immY, isLoadX, isLoadY, regSet; - /* ld/str rX, [fp, #-8] - ld/str rY, [fp, #-4] - ==> - ld/stmdb fp, {rX, rY} - when - X < Y and X != fp and Y != fp and X != 15 and Y != 15 - */ - if (is_ldstr_reg_fp_minus_imm(&isLoadX, &rX, &immX, i1) && - is_ldstr_reg_fp_minus_imm(&isLoadY, &rY, &immY, i2) && - immX == 8 && immY == 4 && rX < rY && - isLoadX == isLoadY && - rX != FP && rY != FP && - rX != 15 && rY != 15) { - *merged = mk_ldstmdb_fp(isLoadX, (1 << rX) | (1< - ld/stmdb fp, union(regset,{rX}) - when - regset is nonempty - X < all elements of regset - N == 4 * (1 + card(regset)) - X != fp and X != 15 - */ - if (is_ldstr_reg_fp_minus_imm(&isLoadX, &rX, &immX, i1) && - is_ldstmdb_fp(&isLoadY, ®Set, i2) && - regSet != 0 && - (regSet & ((1 << (rX + 1)) - 1)) == 0 && - immX == 4 * (1 + size_of_regSet(regSet)) && - isLoadX == isLoadY && - rX != FP && rX != 15) { - *merged = mk_ldstmdb_fp(isLoadX, regSet | (1 << rX)); - return true; - } - return false; -} - -// Determine whether or not it's safe to look at _nIns[1]. -// Necessary condition for safe peepholing with do_peep_2_1. -static inline bool -does_next_instruction_exist(NIns* _nIns, NIns* codeStart, NIns* codeEnd, - NIns* exitStart, NIns* exitEnd) -{ - return (exitStart <= _nIns && _nIns+1 < exitEnd) || - (codeStart <= _nIns && _nIns+1 < codeEnd); -} - -void -Assembler::nPatchBranch(NIns* branch, NIns* target) -{ - // Patch the jump in a loop - - // - // There are two feasible cases here, the first of which has 2 sub-cases: - // - // (1) We are patching a patchable unconditional jump emitted by - // JMP_far. All possible encodings we may be looking at with - // involve 2 words, though we *may* have to change from 1 word to - // 2 or vice verse. - // - // 1a: B ±32MB ; BKPT - // 1b: LDR PC [PC, #-4] ; $imm - // - // (2) We are patching a patchable conditional jump emitted by - // B_cond_chk. Short conditional jumps are non-patchable, so we - // won't have one here; will only ever have an instruction of the - // following form: - // - // LDRcc PC [PC, #lit] ... - // - // We don't actually know whether the lit-address is in the - // constant pool or in-line of the instruction stream, following - // the insn (with a jump over it) and we don't need to. For our - // purposes here, cases 2, 3 and 4 all look the same. - // - // For purposes of handling our patching task, we group cases 1b and 2 - // together, and handle case 1a on its own as it might require expanding - // from a short-jump to a long-jump. - // - // We do not handle contracting from a long-jump to a short-jump, though - // this is a possible future optimisation for case 1b. For now it seems - // not worth the trouble. - // - - if (branch_is_B(branch)) { - // Case 1a - // A short B branch, must be unconditional. - NanoAssert(get_cc(branch) == AL); - - int32_t offset = PC_OFFSET_FROM(target, branch); - if (isS24(offset>>2)) { - // We can preserve the existing form, just rewrite its offset. - NIns cond = *branch & 0xF0000000; - *branch = (NIns)( cond | (0xA<<24) | ((offset>>2) & 0xFFFFFF) ); - } else { - // We need to expand the existing branch to a long jump. - // make sure the next instruction is a dummy BKPT - NanoAssert(*(branch+1) == BKPT_insn); - - // Set the branch instruction to LDRcc pc, [pc, #-4] - NIns cond = *branch & 0xF0000000; - *branch++ = (NIns)( cond | (0x51<<20) | (PC<<16) | (PC<<12) | (4)); - *branch++ = (NIns)target; - } - } else { - // Case 1b & 2 - // Not a B branch, must be LDR, might be any kind of condition. - NanoAssert(branch_is_LDR_PC(branch)); - - NIns *addr = branch+2; - int offset = (*branch & 0xFFF) / sizeof(NIns); - if (*branch & (1<<23)) { - addr += offset; - } else { - addr -= offset; - } - - // Just redirect the jump target, leave the insn alone. - *addr = (NIns) target; - } -} - -RegisterMask -Assembler::nHint(LIns* ins) -{ - NanoAssert(ins->isop(LIR_paramp)); - RegisterMask prefer = 0; - if (ins->paramKind() == 0) - if (ins->paramArg() < 4) - prefer = rmask(argRegs[ins->paramArg()]); - return prefer; -} - -void -Assembler::asm_qjoin(LIns *ins) -{ - int d = findMemFor(ins); - NanoAssert(d); - LIns* lo = ins->oprnd1(); - LIns* hi = ins->oprnd2(); - - Register rlo; - Register rhi; - - findRegFor2(GpRegs, lo, rlo, GpRegs, hi, rhi); - - asm_str(rhi, FP, d+4); - asm_str(rlo, FP, d); - - freeResourcesOf(ins); -} - -void -Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base) -{ - Register ra, rb; - getBaseReg2(GpRegs, value, ra, GpRegs, base, rb, dr); - - switch (op) { - case LIR_sti: - if (isU12(-dr) || isU12(dr)) { - STR(ra, rb, dr); - } else { - STR(ra, IP, 0); - asm_add_imm(IP, rb, dr); - } - return; - case LIR_sti2c: - if (isU12(-dr) || isU12(dr)) { - STRB(ra, rb, dr); - } else { - STRB(ra, IP, 0); - asm_add_imm(IP, rb, dr); - } - return; - case LIR_sti2s: - // Similar to the sti/stb case, but the max offset is smaller. - if (isU8(-dr) || isU8(dr)) { - STRH(ra, rb, dr); - } else { - STRH(ra, IP, 0); - asm_add_imm(IP, rb, dr); - } - return; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - return; - } -} - -bool -canRematALU(LIns *ins) -{ - // Return true if we can generate code for this instruction that neither - // sets CCs, clobbers an input register, nor requires allocating a register. - switch (ins->opcode()) { - case LIR_addi: - case LIR_subi: - case LIR_andi: - case LIR_ori: - case LIR_xori: - return ins->oprnd1()->isInReg() && ins->oprnd2()->isImmI(); - default: - ; - } - return false; -} - -bool -Assembler::canRemat(LIns* ins) -{ - return ins->isImmI() || ins->isop(LIR_allocp) || canRematALU(ins); -} - -void -Assembler::asm_restore(LIns* i, Register r) -{ - // The following registers should never be restored: - NanoAssert(r != PC); - NanoAssert(r != IP); - NanoAssert(r != SP); - - if (i->isop(LIR_allocp)) { - int d = findMemFor(i); - asm_add_imm(r, FP, d); - } else if (i->isImmI()) { - asm_ld_imm(r, i->immI()); - } else if (canRematALU(i)) { - Register rn = i->oprnd1()->getReg(); - int32_t imm = i->oprnd2()->immI(); - switch (i->opcode()) { - case LIR_addi: asm_add_imm(r, rn, imm, /*stat=*/ 0); break; - case LIR_subi: asm_sub_imm(r, rn, imm, /*stat=*/ 0); break; - case LIR_andi: asm_and_imm(r, rn, imm, /*stat=*/ 0); break; - case LIR_ori: asm_orr_imm(r, rn, imm, /*stat=*/ 0); break; - case LIR_xori: asm_eor_imm(r, rn, imm, /*stat=*/ 0); break; - default: NanoAssert(0); break; - } - } else { - // We can't easily load immediate values directly into FP registers, so - // ensure that memory is allocated for the constant and load it from - // memory. - int d = findMemFor(i); - if (ARM_VFP && IsFpReg(r)) { - if (isU8(d/4) || isU8(-d/4)) { - FLDD(r, FP, d); - } else { - FLDD(r, IP, d%1024); - asm_add_imm(IP, FP, d-(d%1024)); - } - } else { - NIns merged; - LDR(r, FP, d); - // See if we can merge this load into an immediately following - // one, by creating or extending an LDM instruction. - if (/* is it safe to poke _nIns[1] ? */ - does_next_instruction_exist(_nIns, codeStart, codeEnd, - exitStart, exitEnd) - && /* can we merge _nIns[0] into _nIns[1] ? */ - do_peep_2_1(&merged, _nIns[0], _nIns[1])) { - _nIns[1] = merged; - _nIns++; - verbose_only( asm_output("merge next into LDMDB"); ) - } - } - } -} - -void -Assembler::asm_spill(Register rr, int d, bool quad) -{ - (void) quad; - NanoAssert(d); - // The following registers should never be spilled: - NanoAssert(rr != PC); - NanoAssert(rr != IP); - NanoAssert(rr != SP); - if (ARM_VFP && IsFpReg(rr)) { - if (isU8(d/4) || isU8(-d/4)) { - FSTD(rr, FP, d); - } else { - FSTD(rr, IP, d%1024); - asm_add_imm(IP, FP, d-(d%1024)); - } - } else { - NIns merged; - // asm_str always succeeds, but returns '1' to indicate that it emitted - // a simple, easy-to-merge STR. - if (asm_str(rr, FP, d)) { - // See if we can merge this store into an immediately following one, - // one, by creating or extending a STM instruction. - if (/* is it safe to poke _nIns[1] ? */ - does_next_instruction_exist(_nIns, codeStart, codeEnd, - exitStart, exitEnd) - && /* can we merge _nIns[0] into _nIns[1] ? */ - do_peep_2_1(&merged, _nIns[0], _nIns[1])) { - _nIns[1] = merged; - _nIns++; - verbose_only( asm_output("merge next into STMDB"); ) - } - } - } -} - -void -Assembler::asm_load64(LIns* ins) -{ - NanoAssert(ins->isD()); - - if (ARM_VFP) { - Register dd; - LIns* base = ins->oprnd1(); - Register rn = findRegFor(base, GpRegs); - int offset = ins->disp(); - - if (ins->isInReg()) { - dd = prepareResultReg(ins, FpRegs & ~rmask(D0)); - } else { - // If the result isn't already in a register, use the VFP scratch - // register for the result and store it directly into memory. - NanoAssert(ins->isInAr()); - int d = arDisp(ins); - evictIfActive(D0); - dd = D0; - // VFP can only do loads and stores with a range of ±1020, so we - // might need to do some arithmetic to extend its range. - if (isU8(d/4) || isU8(-d/4)) { - FSTD(dd, FP, d); - } else { - FSTD(dd, IP, d%1024); - asm_add_imm(IP, FP, d-(d%1024)); - } - } - - switch (ins->opcode()) { - case LIR_ldd: - if (isU8(offset/4) || isU8(-offset/4)) { - FLDD(dd, rn, offset); - } else { - FLDD(dd, IP, offset%1024); - asm_add_imm(IP, rn, offset-(offset%1024)); - } - break; - case LIR_ldf2d: - evictIfActive(D0); - FCVTDS(dd, S0); - if (isU8(offset/4) || isU8(-offset/4)) { - FLDS(S0, rn, offset); - } else { - FLDS(S0, IP, offset%1024); - asm_add_imm(IP, rn, offset-(offset%1024)); - } - break; - default: - NanoAssertMsg(0, "LIR opcode unsupported by asm_load64."); - break; - } - } else { - NanoAssert(ins->isInAr()); - int d = arDisp(ins); - - LIns* base = ins->oprnd1(); - Register rn = findRegFor(base, GpRegs); - int offset = ins->disp(); - - switch (ins->opcode()) { - case LIR_ldd: - asm_mmq(FP, d, rn, offset); - break; - case LIR_ldf2d: - NanoAssertMsg(0, "LIR_ldf2d is not yet implemented for soft-float."); - break; - default: - NanoAssertMsg(0, "LIR opcode unsupported by asm_load64."); - break; - } - } - - freeResourcesOf(ins); -} - -void -Assembler::asm_store64(LOpcode op, LIns* value, int dr, LIns* base) -{ - NanoAssert(value->isD()); - - if (ARM_VFP) { - Register dd = findRegFor(value, FpRegs & ~rmask(D0)); - Register rn = findRegFor(base, GpRegs); - - switch (op) { - case LIR_std: - // VFP can only do stores with a range of ±1020, so we might - // need to do some arithmetic to extend its range. - if (isU8(dr/4) || isU8(-dr/4)) { - FSTD(dd, rn, dr); - } else { - FSTD(dd, IP, dr%1024); - asm_add_imm(IP, rn, dr-(dr%1024)); - } - - break; - case LIR_std2f: - // VFP can only do stores with a range of ±1020, so we might - // need to do some arithmetic to extend its range. - evictIfActive(D0); - if (isU8(dr/4) || isU8(-dr/4)) { - FSTS(S0, rn, dr); - } else { - FSTS(S0, IP, dr%1024); - asm_add_imm(IP, rn, dr-(dr%1024)); - } - - FCVTSD(S0, dd); - - break; - default: - NanoAssertMsg(0, "LIR opcode unsupported by asm_store64."); - break; - } - } else { - int d = findMemFor(value); - Register rn = findRegFor(base, GpRegs); - - switch (op) { - case LIR_std: - // Doubles in soft-float never get registers allocated, so this - // is always a simple two-word memcpy. - // *(uint64_t*)(rb+dr) = *(uint64_t*)(FP+da) - asm_mmq(rn, dr, FP, d); - break; - case LIR_std2f: - NanoAssertMsg(0, "TODO: Soft-float implementation of LIR_std2f."); - break; - default: - NanoAssertMsg(0, "LIR opcode unsupported by asm_store64."); - break; - } - } -} - -// Load the float64 specified by immDhi:immDlo into VFP register dd. -void -Assembler::asm_immd_nochk(Register dd, int32_t immDlo, int32_t immDhi) -{ - // We're not going to use a slot, because it might be too far - // away. Instead, we're going to stick a branch in the stream to - // jump over the constants, and then load from a short PC relative - // offset. - - // stream should look like: - // branch A - // immDlo - // immDhi - // A: FLDD PC-16 - - FLDD(dd, PC, -16); - - *(--_nIns) = (NIns) immDhi; - *(--_nIns) = (NIns) immDlo; - - B_nochk(_nIns+2); -} - -void -Assembler::asm_immd(LIns* ins) -{ - // If the value isn't in a register, it's simplest to use integer - // instructions to put the value in its stack slot. Otherwise, use a VFP - // load to get the value from a literal pool. - if (ARM_VFP && ins->isInReg()) { - Register dd = prepareResultReg(ins, FpRegs); - underrunProtect(4*4); - asm_immd_nochk(dd, ins->immDlo(), ins->immDhi()); - } else { - NanoAssert(ins->isInAr()); - int d = arDisp(ins); - asm_str(IP, FP, d+4); - asm_ld_imm(IP, ins->immDhi()); - asm_str(IP, FP, d); - asm_ld_imm(IP, ins->immDlo()); - } - - freeResourcesOf(ins); -} - -void -Assembler::asm_nongp_copy(Register r, Register s) -{ - if (ARM_VFP && IsFpReg(r) && IsFpReg(s)) { - // fp->fp - FCPYD(r, s); - } else { - // We can't move a double-precision FP register into a 32-bit GP - // register, so assert that no calling code is trying to do that. - NanoAssert(0); - } -} - -/** - * copy 64 bits: (rd+dd) <- (rs+ds) - */ -void -Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) -{ - // The value is either a 64bit struct or maybe a float that isn't live in - // an FPU reg. Either way, don't put it in an FPU reg just to load & store - // it. - // This operation becomes a simple 64-bit memcpy. - - // In order to make the operation optimal, we will require two GP - // registers. We can't allocate a register here because the caller may have - // called deprecated_freeRsrcOf, and allocating a register here may cause something - // else to spill onto the stack which has just be conveniently freed by - // deprecated_freeRsrcOf (resulting in stack corruption). - // - // Falling back to a single-register implementation of asm_mmq is better - // than adjusting the callers' behaviour (to allow us to allocate another - // register here) because spilling a register will end up being slower than - // just using the same register twice anyway. - // - // Thus, if there is a free register which we can borrow, we will emit the - // following code: - // LDR rr, [rs, #ds] - // LDR ip, [rs, #(ds+4)] - // STR rr, [rd, #dd] - // STR ip, [rd, #(dd+4)] - // (Where rr is the borrowed register.) - // - // If there is no free register, don't spill an existing allocation. Just - // do the following: - // LDR ip, [rs, #ds] - // STR ip, [rd, #dd] - // LDR ip, [rs, #(ds+4)] - // STR ip, [rd, #(dd+4)] - // - // Note that if rs+4 or rd+4 is outside the LDR or STR range, extra - // instructions will be emitted as required to make the code work. - - // Ensure that the PC is not used as either base register. The instruction - // generation macros call underrunProtect, and a side effect of this is - // that we may be pushed onto another page, so the PC is not a reliable - // base register. - NanoAssert(rs != PC); - NanoAssert(rd != PC); - - // We use IP as a swap register, so check that it isn't used for something - // else by the caller. - NanoAssert(rs != IP); - NanoAssert(rd != IP); - - // Find the list of free registers from the allocator's free list and the - // GpRegs mask. This excludes any floating-point registers that may be on - // the free list. - RegisterMask free = _allocator.free & AllowableFlagRegs; - - // Ensure that ds and dd are within the +/-4095 offset range of STR and - // LDR. If either is out of range, adjust and modify rd or rs so that the - // load works correctly. - // The modification here is performed after the LDR/STR block (because code - // is emitted backwards), so this one is the reverse operation. - - int32_t dd_adj = 0; - int32_t ds_adj = 0; - - if ((dd+4) >= 0x1000) { - dd_adj = ((dd+4) & ~0xfff); - } else if (dd <= -0x1000) { - dd_adj = -((-dd) & ~0xfff); - } - if ((ds+4) >= 0x1000) { - ds_adj = ((ds+4) & ~0xfff); - } else if (ds <= -0x1000) { - ds_adj = -((-ds) & ~0xfff); - } - - // These will emit no code if d*_adj is 0. - asm_sub_imm(rd, rd, dd_adj); - asm_sub_imm(rs, rs, ds_adj); - - ds -= ds_adj; - dd -= dd_adj; - - if (free) { - // There is at least one register on the free list, so grab one for - // temporary use. There is no need to allocate it explicitly because - // we won't need it after this function returns. - - // The CountLeadingZeroes utility can be used to quickly find a set bit - // in the free mask. - Register rr = (Register)(31-CountLeadingZeroes(free)); - - // Note: Not every register in GpRegs is usable here. However, these - // registers will never appear on the free list. - NanoAssert((free & rmask(PC)) == 0); - NanoAssert((free & rmask(LR)) == 0); - NanoAssert((free & rmask(SP)) == 0); - NanoAssert((free & rmask(IP)) == 0); - NanoAssert((free & rmask(FP)) == 0); - - // Emit the actual instruction sequence. - STR(IP, rd, dd+4); - STR(rr, rd, dd); - LDR(IP, rs, ds+4); - LDR(rr, rs, ds); - } else { - // There are no free registers, so fall back to using IP twice. - STR(IP, rd, dd+4); - LDR(IP, rs, ds+4); - STR(IP, rd, dd); - LDR(IP, rs, ds); - } - - // Re-adjust the base registers. (These will emit no code if d*_adj is 0. - asm_add_imm(rd, rd, dd_adj); - asm_add_imm(rs, rs, ds_adj); -} - -// Increment the 32-bit profiling counter at pCtr, without -// changing any registers. -verbose_only( -void Assembler::asm_inc_m32(uint32_t* pCtr) -{ - // We need to temporarily free up two registers to do this, so - // just push r0 and r1 on the stack. This assumes that the area - // at r13 - 8 .. r13 - 1 isn't being used for anything else at - // this point. This guaranteed us by the EABI; although the - // situation with the legacy ABI I'm not sure of. - // - // Plan: emit the following bit of code. It's not efficient, but - // this is for profiling debug builds only, and is self contained, - // except for above comment re stack use. - // - // E92D0003 push {r0,r1} - // E59F0000 ldr r0, [r15] ; pCtr - // EA000000 b .+8 ; jump over imm - // 12345678 .word 0x12345678 ; pCtr - // E5901000 ldr r1, [r0] - // E2811001 add r1, r1, #1 - // E5801000 str r1, [r0] - // E8BD0003 pop {r0,r1} - - // We need keep the 4 words beginning at "ldr r0, [r15]" - // together. Simplest to underrunProtect the whole thing. - underrunProtect(8*4); - IMM32(0xE8BD0003); // pop {r0,r1} - IMM32(0xE5801000); // str r1, [r0] - IMM32(0xE2811001); // add r1, r1, #1 - IMM32(0xE5901000); // ldr r1, [r0] - IMM32((uint32_t)pCtr); // .word pCtr - IMM32(0xEA000000); // b .+8 - IMM32(0xE59F0000); // ldr r0, [r15] - IMM32(0xE92D0003); // push {r0,r1} -} -) - -void -Assembler::nativePageReset() -{ - _nSlot = 0; - _nExitSlot = 0; -} - -void -Assembler::nativePageSetup() -{ - NanoAssert(!_inExit); - if (!_nIns) - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes), NJ_MAX_CPOOL_OFFSET); - - // constpool starts at top of page and goes down, - // code starts at bottom of page and moves up - if (!_nSlot) - _nSlot = codeStart; -} - - -void -Assembler::underrunProtect(int bytes) -{ - NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - NanoAssert(_nSlot != 0 && int(_nIns)-int(_nSlot) <= 4096); - uintptr_t top = uintptr_t(_nSlot); - uintptr_t pc = uintptr_t(_nIns); - if (pc - bytes < top) - { - verbose_only(verbose_outputf(" %p:", _nIns);) - NIns* target = _nIns; - // This may be in a normal code chunk or an exit code chunk. - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes), NJ_MAX_CPOOL_OFFSET); - - _nSlot = codeStart; - - // _nSlot points to the first empty position in the new code block - // _nIns points just past the last empty position. - // Assume B_nochk won't ever try to write to _nSlot. See B_cond_chk macro. - B_nochk(target); - } -} - -void -Assembler::JMP_far(NIns* addr) -{ - // Even if a simple branch is all that is required, this function must emit - // two words so that the branch can be arbitrarily patched later on. - underrunProtect(8); - - intptr_t offs = PC_OFFSET_FROM(addr,_nIns-2); - - if (isS24(offs>>2)) { - // Emit a BKPT to ensure that we reserve enough space for a full 32-bit - // branch patch later on. The BKPT should never be executed. - BKPT_nochk(); - - asm_output("bkpt"); - - // B [PC+offs] - *(--_nIns) = (NIns)( COND_AL | (0xA<<24) | ((offs>>2) & 0xFFFFFF) ); - - asm_output("b %p", (void*)addr); - } else { - // Insert the target address as a constant in the instruction stream. - *(--_nIns) = (NIns)((addr)); - // ldr pc, [pc, #-4] // load the address into pc, reading it from [pc-4] (e.g., - // the next instruction) - *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (PC<<16) | (PC<<12) | (4)); - - asm_output("ldr pc, =%p", (void*)addr); - } -} - -// Perform a branch with link, and ARM/Thumb exchange if necessary. The actual -// BLX instruction is only available from ARMv5 onwards, but as we don't -// support anything older than that this function will not attempt to output -// pre-ARMv5 sequences. -// -// Note: This function is not designed to be used with branches which will be -// patched later, though it will work if the patcher knows how to patch the -// generated instruction sequence. -void -Assembler::BranchWithLink(NIns* addr) -{ - // Most branches emitted by TM are loaded through a register, so always - // reserve enough space for the LDR sequence. This should give us a slight - // net gain over reserving the exact amount required for shorter branches. - // This _must_ be called before PC_OFFSET_FROM as it can move _nIns! - underrunProtect(8+LD32_size); - - // Calculate the offset from the instruction that is about to be - // written (at _nIns-1) to the target. - intptr_t offs = PC_OFFSET_FROM(addr,_nIns-1); - - // ARMv5 and above can use BLX for branches within ±32MB of the - // PC and BLX Rm for long branches. - if (isS24(offs>>2)) { - // the value we need to stick in the instruction; masked, - // because it will be sign-extended back to 32 bits. - intptr_t offs2 = (offs>>2) & 0xffffff; - - if (((intptr_t)addr & 1) == 0) { - // The target is ARM, so just emit a BL. - - // BL target - *(--_nIns) = (NIns)( (COND_AL) | (0xB<<24) | (offs2) ); - asm_output("bl %p", (void*)addr); - return; - } else if (ARM_ARCH_AT_LEAST(5)) { - // The target is Thumb, so emit a BLX (ARMv5+) - // The (pre-shifted) value of the "H" bit in the BLX encoding. - uint32_t H = (offs & 0x2) << 23; - - // BLX addr - *(--_nIns) = (NIns)( (0xF << 28) | (0x5<<25) | (H) | (offs2) ); - asm_output("blx %p", (void*)addr); - return; - } - /* If we get here, it means we are on ARMv4T, and the target is Thumb, - in which case we want to emit a branch with a register */ - } - if (ARM_ARCH_AT_LEAST(5)) { - // Load the target address into IP and branch to that. We've already - // done underrunProtect, so we can skip that here. - BLX(IP, false); - } else { - BX(IP); - MOV(LR, PC); - } - // LDR IP, =addr - asm_ld_imm(IP, (int32_t)addr, false); -} - -// This is identical to BranchWithLink(NIns*) but emits a branch to an address -// held in a register rather than a literal address. -inline void -Assembler::BLX(Register addr, bool chk /* = true */) -{ - // We need to emit an ARMv5+ instruction, so assert that we have a suitable - // processor. Note that we don't support ARMv4(T), but this serves as a - // useful sanity check. - NanoAssert(ARM_ARCH_AT_LEAST(5)); - - NanoAssert(IsGpReg(addr)); -#ifdef UNDER_CE - // There is a bug in the WinCE device emulator which stops "BLX LR" from - // working as expected. Assert that we never do that! - NanoAssert(addr != LR); -#endif - - if (chk) { - underrunProtect(4); - } - - // BLX reg - *(--_nIns) = (NIns)( (COND_AL) | (0x12<<20) | (0xFFF<<8) | (0x3<<4) | (addr) ); - asm_output("blx %s", gpn(addr)); -} - -// Emit the code required to load a memory address into a register as follows: -// d = *(b+off) -// underrunProtect calls from this function can be disabled by setting chk to -// false. However, this function can use more than LD32_size bytes of space if -// the offset is out of the range of a LDR instruction; the maximum space this -// function requires for underrunProtect is 4+LD32_size. -void -Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk) -{ - if (ARM_VFP && IsFpReg(d)) { - FLDD_chk(d,b,off,chk); - return; - } - - NanoAssert(IsGpReg(d)); - NanoAssert(IsGpReg(b)); - - // We can't use underrunProtect if the base register is the PC because - // underrunProtect might move the PC if there isn't enough space on the - // current page. - NanoAssert((b != PC) || (!chk)); - - if (isU12(off)) { - // LDR d, b, #+off - if (chk) underrunProtect(4); - *(--_nIns) = (NIns)( COND_AL | (0x59<<20) | (b<<16) | (d<<12) | off ); - } else if (isU12(-off)) { - // LDR d, b, #-off - if (chk) underrunProtect(4); - *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (b<<16) | (d<<12) | -off ); - } else { - // The offset is over 4096 (and outside the range of LDR), so we need - // to add a level of indirection to get the address into IP. - - // Because of that, we can't do a PC-relative load unless it fits within - // the single-instruction forms above. - - NanoAssert(b != PC); - NanoAssert(b != IP); - - if (chk) underrunProtect(4+LD32_size); - - *(--_nIns) = (NIns)( COND_AL | (0x79<<20) | (b<<16) | (d<<12) | IP ); - asm_ld_imm(IP, off, false); - } - - asm_output("ldr %s, [%s, #%d]",gpn(d),gpn(b),(off)); -} - -// Emit a store, using a register base and an arbitrary immediate offset. This -// behaves like a STR instruction, but doesn't care about the offset range, and -// emits one of the following instruction sequences: -// -// ---- -// STR rt, [rr, #offset] -// ---- -// asm_add_imm ip, rr, #(offset & ~0xfff) -// STR rt, [ip, #(offset & 0xfff)] -// ---- -// # This one's fairly horrible, but should be rare. -// asm_add_imm rr, rr, #(offset & ~0xfff) -// STR rt, [ip, #(offset & 0xfff)] -// asm_sub_imm rr, rr, #(offset & ~0xfff) -// ---- -// SUB-based variants (for negative offsets) are also supported. -// ---- -// -// The return value is 1 if a simple STR could be emitted, or 0 if the required -// sequence was more complex. -int32_t -Assembler::asm_str(Register rt, Register rr, int32_t offset) -{ - // We can't do PC-relative stores, and we can't store the PC value, because - // we use macros (such as STR) which call underrunProtect, and this can - // push _nIns to a new page, thus making any PC value impractical to - // predict. - NanoAssert(rr != PC); - NanoAssert(rt != PC); - if (offset >= 0) { - // The offset is positive, so use ADD (and variants). - if (isU12(offset)) { - STR(rt, rr, offset); - return 1; - } - - if (rt != IP) { - STR(rt, IP, offset & 0xfff); - asm_add_imm(IP, rr, offset & ~0xfff); - } else { - int32_t adj = offset & ~0xfff; - asm_sub_imm(rr, rr, adj); - STR(rt, rr, offset-adj); - asm_add_imm(rr, rr, adj); - } - } else { - // The offset is negative, so use SUB (and variants). - if (isU12(-offset)) { - STR(rt, rr, offset); - return 1; - } - - if (rt != IP) { - STR(rt, IP, -((-offset) & 0xfff)); - asm_sub_imm(IP, rr, (-offset) & ~0xfff); - } else { - int32_t adj = ((-offset) & ~0xfff); - asm_add_imm(rr, rr, adj); - STR(rt, rr, offset+adj); - asm_sub_imm(rr, rr, adj); - } - } - - return 0; -} - -// Emit the code required to load an immediate value (imm) into general-purpose -// register d. Optimal (MOV-based) mechanisms are used if the immediate can be -// encoded using ARM's operand 2 encoding. Otherwise, a slot is used on the -// literal pool and LDR is used to load the value. -// -// chk can be explicitly set to false in order to disable underrunProtect calls -// from this function; this allows the caller to perform the check manually. -// This function guarantees not to use more than LD32_size bytes of space. -void -Assembler::asm_ld_imm(Register d, int32_t imm, bool chk /* = true */) -{ - uint32_t op2imm; - - NanoAssert(IsGpReg(d)); - - // Attempt to encode the immediate using the second operand of MOV or MVN. - // This is the simplest solution and generates the shortest and fastest - // code, but can only encode a limited set of values. - - if (encOp2Imm(imm, &op2imm)) { - // Use MOV to encode the literal. - MOVis(d, op2imm, 0); - return; - } - - if (encOp2Imm(~imm, &op2imm)) { - // Use MVN to encode the inverted literal. - MVNis(d, op2imm, 0); - return; - } - - // Try to use simple MOV, MVN or MOV(W|T) instructions to load the - // immediate. If this isn't possible, load it from memory. - // - We cannot use MOV(W|T) on cores older than the introduction of - // Thumb-2 or if the target register is the PC. - // - // (Note that we use Thumb-2 if arm_arch is ARMv7 or later; the only earlier - // ARM core that provided Thumb-2 is ARMv6T2/ARM1156, which is a real-time - // core that nanojit is unlikely to ever target.) - if (ARM_ARCH_AT_LEAST(7) && (d != PC)) { - // ARMv6T2 and above have MOVW and MOVT. - uint32_t high_h = (uint32_t)imm >> 16; - uint32_t low_h = imm & 0xffff; - - if (high_h != 0) { - // Load the high half-word (if necessary). - MOVTi_chk(d, high_h, chk); - } - // Load the low half-word. This also zeroes the high half-word, and - // thus must execute _before_ MOVT, and is necessary even if low_h is 0 - // because MOVT will not change the existing low half-word. - MOVWi_chk(d, low_h, chk); - - return; - } - - // We couldn't encode the literal in the instruction stream, so load it - // from memory. - - // Because the literal pool is on the same page as the generated code, it - // will almost always be within the ±4096 range of a LDR. However, this may - // not be the case if _nSlot is at the start of the page and _nIns is at - // the end because the PC is 8 bytes ahead of _nIns. This is unlikely to - // happen, but if it does occur we can simply waste a word or two of - // literal space. - - // We must do the underrunProtect before PC_OFFSET_FROM as underrunProtect - // can move the PC if there isn't enough space on the current page! - if (chk) { - underrunProtect(LD32_size); - } - - int offset = PC_OFFSET_FROM(_nSlot, _nIns-1); - // If the offset is out of range, waste literal space until it is in range. - while (offset <= -4096) { - ++_nSlot; - offset += sizeof(_nSlot); - } - NanoAssert((isU12(-offset) || isU12(offset)) && (offset <= -8)); - - // Write the literal. - *(_nSlot++) = imm; - asm_output("## imm= 0x%x", imm); - - // Load the literal. - LDR_nochk(d,PC,offset); - NanoAssert(uintptr_t(_nIns) + 8 + offset == uintptr_t(_nSlot-1)); - NanoAssert(*((int32_t*)_nSlot-1) == imm); -} - -// Branch to target address _t with condition _c, doing underrun -// checks (_chk == 1) or skipping them (_chk == 0). -// -// Set the target address (_t) to 0 if the target is not yet known and the -// branch will be patched up later. -// -// If the jump is to a known address (with _t != 0) and it fits in a relative -// jump (±32MB), emit that. -// If the jump is unconditional, emit the dest address inline in -// the instruction stream and load it into pc. -// If the jump has a condition, but noone's mucked with _nIns and our _nSlot -// pointer is valid, stick the constant in the slot and emit a conditional -// load into pc. -// Otherwise, emit the conditional load into pc from a nearby constant, -// and emit a jump to jump over it it in case the condition fails. -// -// NB: B_nochk depends on this not calling samepage() when _c == AL -void -Assembler::B_cond_chk(ConditionCode _c, NIns* _t, bool _chk) -{ - int32_t offs = PC_OFFSET_FROM(_t,_nIns-1); - //nj_dprintf("B_cond_chk target: 0x%08x offset: %d @0x%08x\n", _t, offs, _nIns-1); - - // optimistically check if this will fit in 24 bits - if (_chk && isS24(offs>>2) && (_t != 0)) { - underrunProtect(4); - // recalculate the offset, because underrunProtect may have - // moved _nIns to a new page - offs = PC_OFFSET_FROM(_t,_nIns-1); - } - - // Emit one of the following patterns: - // - // --- Short branch. This can never be emitted if the branch target is not - // known. - // B(cc) ±32MB - // - // --- Long unconditional branch. - // LDR PC, #lit - // lit: #target - // - // --- Long conditional branch. Note that conditional branches will never - // be patched, so the nPatchBranch function doesn't need to know where - // the literal pool is located. - // LDRcc PC, #lit - // ; #lit is in the literal pool at _nSlot - // - // --- Long conditional branch (if the slot isn't on the same page as the instruction). - // LDRcc PC, #lit - // B skip ; Jump over the literal data. - // lit: #target - // skip: [...] - - if (isS24(offs>>2) && (_t != 0)) { - // The underrunProtect for this was done above (if required by _chk). - *(--_nIns) = (NIns)( ((_c)<<28) | (0xA<<24) | (((offs)>>2) & 0xFFFFFF) ); - asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); - } else if (_c == AL) { - if(_chk) underrunProtect(8); - *(--_nIns) = (NIns)(_t); - *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (PC<<16) | (PC<<12) | 0x4 ); - asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); - } else if (PC_OFFSET_FROM(_nSlot, _nIns-1) > -0x1000) { - if(_chk) underrunProtect(8); - *(_nSlot++) = (NIns)(_t); - offs = PC_OFFSET_FROM(_nSlot-1,_nIns-1); - NanoAssert(offs < 0); - *(--_nIns) = (NIns)( ((_c)<<28) | (0x51<<20) | (PC<<16) | (PC<<12) | ((-offs) & 0xFFF) ); - asm_output("ldr%s %s, [%s, #-%d]", condNames[_c], gpn(PC), gpn(PC), -offs); - NanoAssert(uintptr_t(_nIns)+8+offs == uintptr_t(_nSlot-1)); - } else { - if(_chk) underrunProtect(12); - // Emit a pointer to the target as a literal in the instruction stream. - *(--_nIns) = (NIns)(_t); - // Emit a branch to skip over the literal. The PC value is 8 bytes - // ahead of the executing instruction, so to branch two instructions - // forward this must branch 8-8=0 bytes. - *(--_nIns) = (NIns)( COND_AL | (0xA<<24) | 0x0 ); - // Emit the conditional branch. - *(--_nIns) = (NIns)( ((_c)<<28) | (0x51<<20) | (PC<<16) | (PC<<12) | 0x0 ); - asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); - } -} - -/* - * VFP - */ - -void -Assembler::asm_i2d(LIns* ins) -{ - Register dd = prepareResultReg(ins, FpRegs & ~rmask(D0)); - Register rt = findRegFor(ins->oprnd1(), GpRegs); - - evictIfActive(D0); - FSITOD(dd, S0); - FMSR(S0, rt); - - freeResourcesOf(ins); -} - -void -Assembler::asm_ui2d(LIns* ins) -{ - Register dd = prepareResultReg(ins, FpRegs & ~rmask(D0)); - Register rt = findRegFor(ins->oprnd1(), GpRegs); - - evictIfActive(D0); - FUITOD(dd, S0); - FMSR(S0, rt); - - freeResourcesOf(ins); -} - -void Assembler::asm_d2i(LIns* ins) -{ - evictIfActive(D0); - if (ins->isInReg()) { - Register rt = ins->getReg(); - FMRS(rt, S0); - } else { - // There's no active result register, so store the result directly into - // memory to avoid the FP->GP transfer cost on Cortex-A8. - int32_t d = arDisp(ins); - // VFP can only do stores with a range of ±1020, so we might need to do - // some arithmetic to extend its range. - if (isU8(d/4) || isU8(-d/4)) { - FSTS(S0, FP, d); - } else { - FSTS(S0, IP, d%1024); - asm_add_imm(IP, FP, d-(d%1024)); - } - } - - Register dm = findRegFor(ins->oprnd1(), FpRegs & ~rmask(D0)); - - FTOSID(S0, dm); - - freeResourcesOf(ins); -} - -void -Assembler::asm_fneg(LIns* ins) -{ - LIns* lhs = ins->oprnd1(); - - Register dd = prepareResultReg(ins, FpRegs); - // If the argument doesn't have a register assigned, re-use dd. - Register dm = lhs->isInReg() ? lhs->getReg() : dd; - - FNEGD(dd, dm); - - freeResourcesOf(ins); - if (dd == dm) { - NanoAssert(!lhs->isInReg()); - findSpecificRegForUnallocated(lhs, dd); - } -} - -void -Assembler::asm_fop(LIns* ins) -{ - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - - Register dd = prepareResultReg(ins, FpRegs); - // Try to re-use the result register for one of the arguments. - Register dn = lhs->isInReg() ? lhs->getReg() : dd; - Register dm = rhs->isInReg() ? rhs->getReg() : dd; - if ((dn == dm) && (lhs != rhs)) { - // We can't re-use the result register for both arguments, so force one - // into its own register. - dm = findRegFor(rhs, FpRegs & ~rmask(dd)); - NanoAssert(rhs->isInReg()); - } - - // TODO: Special cases for simple constants. - - switch(ins->opcode()) { - case LIR_addd: FADDD(dd,dn,dm); break; - case LIR_subd: FSUBD(dd,dn,dm); break; - case LIR_muld: FMULD(dd,dn,dm); break; - case LIR_divd: FDIVD(dd,dn,dm); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - - // If we re-used the result register, mark it as active. - if (dn == dd) { - NanoAssert(!lhs->isInReg()); - findSpecificRegForUnallocated(lhs, dd); - } else if (dm == dd) { - NanoAssert(!rhs->isInReg()); - findSpecificRegForUnallocated(rhs, dd); - } else { - NanoAssert(lhs->isInReg()); - NanoAssert(rhs->isInReg()); - } -} - -void -Assembler::asm_cmpd(LIns* ins) -{ - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - LOpcode op = ins->opcode(); - - NanoAssert(ARM_VFP); - NanoAssert(isCmpDOpcode(op)); - NanoAssert(lhs->isD() && rhs->isD()); - - Register ra, rb; - findRegFor2(FpRegs, lhs, ra, FpRegs, rhs, rb); - - int e_bit = (op != LIR_eqd); - - // Do the comparison and get results loaded in ARM status register. - // TODO: For asm_condd, we should put the results directly into an ARM - // machine register, then use bit operations to get the result. - FMSTAT(); - FCMPD(ra, rb, e_bit); -} - -/* Call this with targ set to 0 if the target is not yet known and the branch - * will be patched up later. - */ -Branches -Assembler::asm_branch(bool branchOnFalse, LIns* cond, NIns* targ) -{ - LOpcode condop = cond->opcode(); - NanoAssert(cond->isCmp()); - NanoAssert(ARM_VFP || !isCmpDOpcode(condop)); - - // The old "never" condition code has special meaning on newer ARM cores, - // so use "always" as a sensible default code. - ConditionCode cc = AL; - - // Detect whether or not this is a floating-point comparison. - bool fp_cond; - - // Select the appropriate ARM condition code to match the LIR instruction. - switch (condop) - { - // Floating-point conditions. Note that the VFP LT/LE conditions - // require use of the unsigned condition codes, even though - // float-point comparisons are always signed. - case LIR_eqd: cc = EQ; fp_cond = true; break; - case LIR_ltd: cc = LO; fp_cond = true; break; - case LIR_led: cc = LS; fp_cond = true; break; - case LIR_ged: cc = GE; fp_cond = true; break; - case LIR_gtd: cc = GT; fp_cond = true; break; - - // Standard signed and unsigned integer comparisons. - case LIR_eqi: cc = EQ; fp_cond = false; break; - case LIR_lti: cc = LT; fp_cond = false; break; - case LIR_lei: cc = LE; fp_cond = false; break; - case LIR_gti: cc = GT; fp_cond = false; break; - case LIR_gei: cc = GE; fp_cond = false; break; - case LIR_ltui: cc = LO; fp_cond = false; break; - case LIR_leui: cc = LS; fp_cond = false; break; - case LIR_gtui: cc = HI; fp_cond = false; break; - case LIR_geui: cc = HS; fp_cond = false; break; - - // Default case for invalid or unexpected LIR instructions. - default: cc = AL; fp_cond = false; break; - } - - // Invert the condition if required. - if (branchOnFalse) - cc = OppositeCond(cc); - - // Ensure that we got a sensible condition code. - NanoAssert((cc != AL) && (cc != NV)); - - // Ensure that we don't hit floating-point LIR codes if VFP is disabled. - NanoAssert(ARM_VFP || !fp_cond); - - // Emit a suitable branch instruction. - B_cond(cc, targ); - - // Store the address of the branch instruction so that we can return it. - // asm_[f]cmp will move _nIns so we must do this now. - NIns *at = _nIns; - - asm_cmp(cond); - - return Branches(at); -} - -NIns* Assembler::asm_branch_ov(LOpcode op, NIns* target) -{ - // Because MUL can't set the V flag, we use SMULL and CMP to set the Z flag - // to detect overflow on multiply. Thus, if we have a LIR_mulxovi, we must - // be conditional on !Z, not V. - ConditionCode cc = ( (op == LIR_mulxovi) || (op == LIR_muljovi) ? NE : VS ); - - // Emit a suitable branch instruction. - B_cond(cc, target); - return _nIns; -} - -void -Assembler::asm_cmp(LIns *cond) -{ - LIns* lhs = cond->oprnd1(); - LIns* rhs = cond->oprnd2(); - - // Forward floating-point comparisons directly to asm_cmpd to simplify - // logic in other methods which need to issue an implicit comparison, but - // don't care about the details of comparison itself. - if (lhs->isD()) { - NanoAssert(rhs->isD()); - asm_cmpd(cond); - return; - } - - NanoAssert(lhs->isI() && rhs->isI()); - - // ready to issue the compare - if (rhs->isImmI()) { - int c = rhs->immI(); - Register r = findRegFor(lhs, GpRegs); - asm_cmpi(r, c); - } else { - Register ra, rb; - findRegFor2(GpRegs, lhs, ra, GpRegs, rhs, rb); - CMP(ra, rb); - } -} - -void -Assembler::asm_cmpi(Register r, int32_t imm) -{ - if (imm < 0) { - if (imm > -256) { - ALUi(AL, cmn, 1, 0, r, -imm); - } else { - underrunProtect(4 + LD32_size); - CMP(r, IP); - asm_ld_imm(IP, imm); - } - } else { - if (imm < 256) { - ALUi(AL, cmp, 1, 0, r, imm); - } else { - underrunProtect(4 + LD32_size); - CMP(r, IP); - asm_ld_imm(IP, imm); - } - } -} - -void -Assembler::asm_condd(LIns* ins) -{ - Register rd = prepareResultReg(ins, GpRegs); - - // TODO: Modify cmpd to allow the FP flags to move directly to an ARM - // machine register, then use simple bit operations here rather than - // conditional moves. - - switch (ins->opcode()) { - case LIR_eqd: SETEQ(rd); break; - case LIR_ltd: SETLO(rd); break; // Note: VFP LT/LE operations require - case LIR_led: SETLS(rd); break; // unsigned LO/LS condition codes! - case LIR_ged: SETGE(rd); break; - case LIR_gtd: SETGT(rd); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - - asm_cmpd(ins); -} - -void -Assembler::asm_cond(LIns* ins) -{ - Register rd = prepareResultReg(ins, GpRegs); - LOpcode op = ins->opcode(); - - switch(op) - { - case LIR_eqi: SETEQ(rd); break; - case LIR_lti: SETLT(rd); break; - case LIR_lei: SETLE(rd); break; - case LIR_gti: SETGT(rd); break; - case LIR_gei: SETGE(rd); break; - case LIR_ltui: SETLO(rd); break; - case LIR_leui: SETLS(rd); break; - case LIR_gtui: SETHI(rd); break; - case LIR_geui: SETHS(rd); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - - asm_cmp(ins); -} - -void -Assembler::asm_arith(LIns* ins) -{ - LOpcode op = ins->opcode(); - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - - // We always need the result register and the first operand register, so - // find them up-front. (If the second operand is constant it is encoded - // differently.) - Register rd = prepareResultReg(ins, GpRegs); - - // Try to re-use the result register for operand 1. - Register rn = lhs->isInReg() ? lhs->getReg() : rd; - - // If the rhs is constant, we can use the instruction-specific code to - // determine if the value can be encoded in an ARM instruction. If the - // value cannot be encoded, it will be loaded into a register. - // - // Note that the MUL instruction can never take an immediate argument so - // even if the argument is constant, we must allocate a register for it. - if (rhs->isImmI() && (op != LIR_muli) && (op != LIR_mulxovi) && (op != LIR_muljovi)) - { - int32_t immI = rhs->immI(); - - switch (op) - { - case LIR_addi: asm_add_imm(rd, rn, immI); break; - case LIR_addjovi: - case LIR_addxovi: asm_add_imm(rd, rn, immI, 1); break; - case LIR_subi: asm_sub_imm(rd, rn, immI); break; - case LIR_subjovi: - case LIR_subxovi: asm_sub_imm(rd, rn, immI, 1); break; - case LIR_andi: asm_and_imm(rd, rn, immI); break; - case LIR_ori: asm_orr_imm(rd, rn, immI); break; - case LIR_xori: asm_eor_imm(rd, rn, immI); break; - case LIR_lshi: LSLi(rd, rn, immI); break; - case LIR_rshi: ASRi(rd, rn, immI); break; - case LIR_rshui: LSRi(rd, rn, immI); break; - - default: - NanoAssertMsg(0, "Unsupported"); - break; - } - - freeResourcesOf(ins); - if (rd == rn) { - // Mark the re-used register as active. - NanoAssert(!lhs->isInReg()); - findSpecificRegForUnallocated(lhs, rd); - } - return; - } - - // The rhs is either already in a register or cannot be encoded as an - // Operand 2 constant for this operation. - - Register rm = rhs->isInReg() ? rhs->getReg() : rd; - if ((rm == rn) && (lhs != rhs)) { - // We can't re-use the result register for both arguments, so force one - // into its own register. We favour re-use for operand 2 (rm) here as - // it is more likely to take a fast path for LIR_mul on ARMv5. - rn = findRegFor(lhs, GpRegs & ~rmask(rd)); - NanoAssert(lhs->isInReg()); - } - - switch (op) - { - case LIR_addi: ADDs(rd, rn, rm, 0); break; - case LIR_addjovi: - case LIR_addxovi: ADDs(rd, rn, rm, 1); break; - case LIR_subi: SUBs(rd, rn, rm, 0); break; - case LIR_subjovi: - case LIR_subxovi: SUBs(rd, rn, rm, 1); break; - case LIR_andi: ANDs(rd, rn, rm, 0); break; - case LIR_ori: ORRs(rd, rn, rm, 0); break; - case LIR_xori: EORs(rd, rn, rm, 0); break; - - case LIR_muli: - if (!ARM_ARCH_AT_LEAST(6) && (rd == rn)) { - // ARMv4 and ARMv5 cannot handle a MUL where rd == rn, so - // explicitly assign a new register to rn. - NanoAssert(!lhs->isInReg()); - rn = findRegFor(lhs, GpRegs & ~rmask(rd) & ~rmask(rm)); - if (lhs == rhs) { - rm = rn; - } - } - MUL(rd, rn, rm); - break; - case LIR_muljovi: - case LIR_mulxovi: - if (!ARM_ARCH_AT_LEAST(6) && (rd == rn)) { - // ARMv5 (and earlier) cannot handle a MUL where rd == rn, so - // if that is the case, explicitly assign a new register to rn. - NanoAssert(!lhs->isInReg()); - rn = findRegFor(lhs, GpRegs & ~rmask(rd) & ~rmask(rm)); - if (lhs == rhs) { - rm = rn; - } - } - // ARM cannot automatically detect overflow from a MUL operation, - // so we have to perform some other arithmetic: - // SMULL rr, ip, ra, rb - // CMP ip, rr, ASR #31 - // An explanation can be found in bug 521161. This sets Z if we did - // _not_ overflow, and clears it if we did. - ALUr_shi(AL, cmp, 1, SBZ, IP, rd, ASR_imm, 31); - SMULL(rd, IP, rn, rm); - break; - - // The shift operations need a mask to match the JavaScript - // specification because the ARM architecture allows a greater shift - // range than JavaScript. - case LIR_lshi: - LSL(rd, rn, IP); - ANDi(IP, rm, 0x1f); - break; - case LIR_rshi: - ASR(rd, rn, IP); - ANDi(IP, rm, 0x1f); - break; - case LIR_rshui: - LSR(rd, rn, IP); - ANDi(IP, rm, 0x1f); - break; - default: - NanoAssertMsg(0, "Unsupported"); - break; - } - - freeResourcesOf(ins); - // If we re-used the result register, mark it as active. - if (rn == rd) { - NanoAssert(!lhs->isInReg()); - findSpecificRegForUnallocated(lhs, rd); - } else if (rm == rd) { - NanoAssert(!rhs->isInReg()); - findSpecificRegForUnallocated(rhs, rd); - } else { - NanoAssert(lhs->isInReg()); - NanoAssert(rhs->isInReg()); - } -} - -void -Assembler::asm_neg_not(LIns* ins) -{ - LIns* lhs = ins->oprnd1(); - Register rr = prepareResultReg(ins, GpRegs); - - // If 'lhs' isn't in a register, we can give it the result register. - Register ra = lhs->isInReg() ? lhs->getReg() : rr; - - if (ins->isop(LIR_noti)) { - MVN(rr, ra); - } else { - NanoAssert(ins->isop(LIR_negi)); - RSBS(rr, ra); - } - - freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - // Update the register state to indicate that we've claimed ra for lhs. - findSpecificRegForUnallocated(lhs, ra); - } -} - -void -Assembler::asm_load32(LIns* ins) -{ - LOpcode op = ins->opcode(); - LIns* base = ins->oprnd1(); - int d = ins->disp(); - - Register rt = prepareResultReg(ins, GpRegs); - // Try to re-use the result register for the base pointer. - Register rn = base->isInReg() ? base->getReg() : rt; - - // TODO: The x86 back-end has a special case where the base address is - // given by LIR_addp. The same technique may be useful here to take - // advantage of ARM's register+register addressing mode. - - switch (op) { - case LIR_lduc2ui: - if (isU12(-d) || isU12(d)) { - LDRB(rt, rn, d); - } else { - LDRB(rt, IP, d%4096); - asm_add_imm(IP, rn, d-(d%4096)); - } - break; - case LIR_ldus2ui: - // Some ARM machines require 2-byte alignment here. - // Similar to the lduc2ui case, but the max offset is smaller. - if (isU8(-d) || isU8(d)) { - LDRH(rt, rn, d); - } else { - LDRH(rt, IP, d%256); - asm_add_imm(IP, rn, d-(d%256)); - } - break; - case LIR_ldi: - // Some ARM machines require 4-byte alignment here. - if (isU12(-d) || isU12(d)) { - LDR(rt, rn, d); - } else { - LDR(rt, IP, d%4096); - asm_add_imm(IP, rn, d-(d%4096)); - } - break; - case LIR_ldc2i: - // Like LIR_lduc2ui, but sign-extend. - // Some ARM machines require 2-byte alignment here. - if (isU8(-d) || isU8(d)) { - LDRSB(rt, rn, d); - } else { - LDRSB(rn, IP, d%256); - asm_add_imm(IP, rn, d-(d%256)); - } - break; - case LIR_lds2i: - // Like LIR_ldus2ui, but sign-extend. - if (isU8(-d) || isU8(d)) { - LDRSH(rt, rn, d); - } else { - LDRSH(rt, IP, d%256); - asm_add_imm(IP, rn, d-(d%256)); - } - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - break; - } - - freeResourcesOf(ins); - - if (rn == rt) { - NanoAssert(!base->isInReg()); - findSpecificRegForUnallocated(base, rn); - } -} - -void -Assembler::asm_cmov(LIns* ins) -{ - LIns* condval = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - RegisterMask allow = ins->isD() ? FpRegs : GpRegs; - ConditionCode cc; - - NanoAssert(condval->isCmp()); - NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) || - (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD())); - - Register rd = prepareResultReg(ins, allow); - - // Try to re-use the result register for one of the arguments. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rd; - Register rf = iffalse->isInReg() ? iffalse->getReg() : rd; - // Note that iftrue and iffalse may actually be the same, though it - // shouldn't happen with the LIR optimizers turned on. - if ((rt == rf) && (iftrue != iffalse)) { - // We can't re-use the result register for both arguments, so force one - // into its own register. - rf = findRegFor(iffalse, allow & ~rmask(rd)); - NanoAssert(iffalse->isInReg()); - } - - switch(condval->opcode()) { - default: NanoAssert(0); - // Integer comparisons. - case LIR_eqi: cc = EQ; break; - case LIR_lti: cc = LT; break; - case LIR_lei: cc = LE; break; - case LIR_gti: cc = GT; break; - case LIR_gei: cc = GE; break; - case LIR_ltui: cc = LO; break; - case LIR_leui: cc = LS; break; - case LIR_gtui: cc = HI; break; - case LIR_geui: cc = HS; break; - // VFP comparisons. - case LIR_eqd: cc = EQ; break; - case LIR_ltd: cc = LO; break; - case LIR_led: cc = LS; break; - case LIR_ged: cc = GE; break; - case LIR_gtd: cc = GT; break; - } - - // Emit something like this: - // CMP [...] - // MOV(CC) rd, rf - // MOV(!CC) rd, rt - // If the destination was re-used for an input, the corresponding MOV will - // be omitted as it will be redundant. - if (ins->isI()) { - if (rd != rf) { - MOV_cond(OppositeCond(cc), rd, rf); - } - if (rd != rt) { - MOV_cond(cc, rd, rt); - } - } else if (ins->isD()) { - // The VFP sequence is similar to the integer sequence, but uses a - // VFP instruction in place of MOV. - NanoAssert(ARM_VFP); - if (rd != rf) { - FCPYD_cond(OppositeCond(cc), rd, rf); - } - if (rd != rt) { - FCPYD_cond(cc, rd, rt); - } - } else { - NanoAssert(0); - } - - freeResourcesOf(ins); - - // If we re-used the result register, mark it as active for either iftrue - // or iffalse (or both in the corner-case where they're the same). - if (rt == rd) { - NanoAssert(!iftrue->isInReg()); - findSpecificRegForUnallocated(iftrue, rd); - } else if (rf == rd) { - NanoAssert(!iffalse->isInReg()); - findSpecificRegForUnallocated(iffalse, rd); - } else { - NanoAssert(iffalse->isInReg()); - NanoAssert(iftrue->isInReg()); - } - - asm_cmp(condval); -} - -void -Assembler::asm_qhi(LIns* ins) -{ - Register rd = prepareResultReg(ins, GpRegs); - LIns *lhs = ins->oprnd1(); - int d = findMemFor(lhs); - - LDR(rd, FP, d+4); - - freeResourcesOf(ins); -} - -void -Assembler::asm_qlo(LIns* ins) -{ - Register rd = prepareResultReg(ins, GpRegs); - LIns *lhs = ins->oprnd1(); - int d = findMemFor(lhs); - - LDR(rd, FP, d); - - freeResourcesOf(ins); -} - -void -Assembler::asm_param(LIns* ins) -{ - uint32_t a = ins->paramArg(); - uint32_t kind = ins->paramKind(); - if (kind == 0) { - // Ordinary parameter. These are always (32-bit-)word-sized, and will - // be in the first four registers (argRegs) and then on the stack. - if (a < 4) { - // Register argument. - prepareResultReg(ins, rmask(argRegs[a])); - } else { - // Stack argument. - Register r = prepareResultReg(ins, GpRegs); - int d = (a - 4) * sizeof(intptr_t) + 8; - LDR(r, FP, d); - } - } else { - // Saved parameter. - NanoAssert(a < (sizeof(savedRegs)/sizeof(savedRegs[0]))); - prepareResultReg(ins, rmask(savedRegs[a])); - } - freeResourcesOf(ins); -} - -void -Assembler::asm_immi(LIns* ins) -{ - Register rd = prepareResultReg(ins, GpRegs); - asm_ld_imm(rd, ins->immI()); - freeResourcesOf(ins); -} - -void -Assembler::asm_ret(LIns *ins) -{ - genEpilogue(); - - // NB: our contract with genEpilogue is actually that the return value - // we are intending for R0 is currently IP, not R0. This has to do with - // the strange dual-nature of the patchable jump in a side-exit. See - // nPatchBranch. - // - // With hardware floating point ABI we can skip this for retd. - if (!(ARM_EABI_HARD && ins->isop(LIR_retd))) { - MOV(IP, R0); - } - - // Pop the stack frame. - MOV(SP,FP); - - releaseRegisters(); - assignSavedRegs(); - LIns *value = ins->oprnd1(); - if (ins->isop(LIR_reti)) { - findSpecificRegFor(value, R0); - } - else { - NanoAssert(ins->isop(LIR_retd)); - if (ARM_VFP) { -#ifdef NJ_ARM_EABI_HARD_FLOAT - findSpecificRegFor(value, D0); -#else - Register reg = findRegFor(value, FpRegs); - FMRRD(R0, R1, reg); -#endif - } else { - NanoAssert(value->isop(LIR_ii2d)); - findSpecificRegFor(value->oprnd1(), R0); // lo - findSpecificRegFor(value->oprnd2(), R1); // hi - } - } -} - -void -Assembler::asm_jtbl(LIns* ins, NIns** table) -{ - Register indexreg = findRegFor(ins->oprnd1(), GpRegs); - Register tmp = registerAllocTmp(GpRegs & ~rmask(indexreg)); - LDR_scaled(PC, tmp, indexreg, 2); // LDR PC, [tmp + index*4] - asm_ld_imm(tmp, (int32_t)table); // tmp = #table -} - -void Assembler::swapCodeChunks() { - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes), NJ_MAX_CPOOL_OFFSET); - if (!_nExitSlot) - _nExitSlot = exitStart; - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, _nSlot, _nExitSlot); // this one is ARM-specific - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) -} - -void Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported -} - -} -#endif /* FEATURE_NANOJIT */ diff --git a/deps/mozjs/js/src/nanojit/NativeARM.h b/deps/mozjs/js/src/nanojit/NativeARM.h deleted file mode 100644 index 4b7a940f7ef..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeARM.h +++ /dev/null @@ -1,1117 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * Vladimir Vukicevic - * Jacob Bramley - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_NativeARM__ -#define __nanojit_NativeARM__ - - -#ifdef PERFM -#include "../vprof/vprof.h" -#define count_instr() _nvprof("arm",1) -#define count_prolog() _nvprof("arm-prolog",1); count_instr(); -#define count_imt() _nvprof("arm-imt",1) count_instr() -#else -#define count_instr() -#define count_prolog() -#define count_imt() -#endif - -#ifdef DEBUG -#define ARM_ARCH_AT_LEAST(wanted) (_config.arm_arch >= (wanted)) -#define ARM_VFP (_config.arm_vfp) -#else -/* Note: Non DEBUG builds will ignore arm_arch if it is lower than - NJ_COMPILER_ARM_ARCH, and will ignore arm_vfp if NJ_COMPILER_ARM_ARCH - is greater or equal to 7. */ -#define ARM_ARCH_AT_LEAST(wanted) \ - ((NJ_COMPILER_ARM_ARCH >= (wanted)) || (_config.arm_arch >= (wanted))) -#define ARM_VFP ((NJ_COMPILER_ARM_ARCH >= 7) || (_config.arm_vfp)) -#endif - -namespace nanojit -{ -#if defined VMCFG_DOUBLE_MSW_FIRST || defined _MSC_VER -# undef NJ_ARM_EABI -#else -# define NJ_ARM_EABI 1 -#endif - -// GCC defines __ARM_PCS_VFP if it uses hardware floating point ABI -// See http://gcc.gnu.org/viewcvs?view=revision&revision=162637 -#ifdef __ARM_PCS_VFP -# define NJ_ARM_EABI_HARD_FLOAT 1 -#endif - -#ifdef NJ_ARM_EABI_HARD_FLOAT -# define ARM_EABI_HARD true -#else -# define ARM_EABI_HARD false -#endif - -// only d0-d7 are used; in addition, we'll use d0 as s0-s1 for i2d/u2f/etc. -#define NJ_VFP_MAX_REGISTERS 8 -#define NJ_MAX_REGISTERS (11 + NJ_VFP_MAX_REGISTERS) -#define NJ_MAX_STACK_ENTRY 4096 -#define NJ_MAX_PARAMETERS 16 -#define NJ_ALIGN_STACK 8 - -#define NJ_JTBL_SUPPORTED 1 -#define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 -#define NJ_F2I_SUPPORTED 1 -#define NJ_SOFTFLOAT_SUPPORTED 1 -#define NJ_DIVI_SUPPORTED 0 - -#define NJ_CONSTANT_POOLS -const int NJ_MAX_CPOOL_OFFSET = 4096; -const int NJ_CPOOL_SIZE = 16; - -const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect - -typedef int NIns; - -// Bytes of icache to flush after Assembler::patch -const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); - -/* ARM registers */ -typedef uint32_t Register; -static const Register - R0 = { 0 }, - R1 = { 1 }, - R2 = { 2 }, - R3 = { 3 }, - R4 = { 4 }, - R5 = { 5 }, - R6 = { 6 }, - R7 = { 7 }, - R8 = { 8 }, - R9 = { 9 }, - R10 = { 10 }, - FP = { 11 }, - IP = { 12 }, - SP = { 13 }, - LR = { 14 }, - PC = { 15 }, - - // VFP regs (we currently only use D0-D7 and S0) - D0 = { 16 }, - D1 = { 17 }, - D2 = { 18 }, - D3 = { 19 }, - D4 = { 20 }, - D5 = { 21 }, - D6 = { 22 }, - D7 = { 23 }, - // D8-D15 are caller-saved registers that we don't currently handle. - - FirstFloatReg = D0, - LastFloatReg = D7, - - deprecated_UnknownReg = { 32 }, // XXX: remove eventually, see bug 538924 - - // S0 overlaps with D0 and is hard-coded into i2d and u2f operations - S0 = { 24 }, - - SBZ = { 0 } ; // Used for 'should-be-zero' fields in instructions with - // unused register fields. - -/* winmo builds error with C2057 and C2229 on usage of First/LastRegNum as R0/D7 */ -static const uint32_t FirstRegNum = 0; /* R0 */ -static const uint32_t LastRegNum = 23; /* D7 */ -} - -#define NJ_USE_UINT32_REGISTER 1 -#include "NativeCommon.h" - -namespace nanojit -{ - -/* ARM condition codes */ -typedef enum { - EQ = 0x0, // Equal - NE = 0x1, // Not Equal - CS = 0x2, // Carry Set (or HS) - HS = 0x2, - CC = 0x3, // Carry Clear (or LO) - LO = 0x3, - MI = 0x4, // MInus - PL = 0x5, // PLus - VS = 0x6, // oVerflow Set - VC = 0x7, // oVerflow Clear - HI = 0x8, // HIgher - LS = 0x9, // Lower or Same - GE = 0xA, // Greater or Equal - LT = 0xB, // Less Than - GT = 0xC, // Greater Than - LE = 0xD, // Less or Equal - AL = 0xE, // ALways - - // Note that condition code NV is unpredictable on ARMv3 and ARMv4, and has - // special meaning for ARMv5 onwards. As such, it should never be used in - // an instruction encoding unless the special (ARMv5+) meaning is required. - NV = 0xF // NeVer -} ConditionCode; -#define IsCond(cc) (((cc) >= EQ) && ((cc) <= AL)) - -// Bit 0 of the condition code can be flipped to obtain the opposite condition. -// However, this won't work for AL because its opposite — NV — has special -// meaning. -#define OppositeCond(cc) ((ConditionCode)((unsigned int)(cc)^0x1)) - -typedef int RegisterMask; -typedef struct _FragInfo { - RegisterMask needRestoring; - NIns* epilogue; -} FragInfo; - -typedef struct _ParameterRegisters { - int stkd; - Register r; -#ifdef NJ_ARM_EABI_HARD_FLOAT - Register float_r; -#endif -} ParameterRegisters; - -#ifdef NJ_ARM_EABI_HARD_FLOAT -#define init_params(a,b,c) { (a), (b), (c) } -#else -#define init_params(a,b,c) { (a), (b) } -#endif - -// D0-D7 are not saved; D8-D15 are, but we don't use those, -// so we don't have to worry about saving/restoring them -static const RegisterMask SavedFpRegs = 0; -static const RegisterMask SavedRegs = 1<= LSL_imm) && ((sh) <= ROR_reg)) - -#define LD32_size 8 - -#define BEGIN_NATIVE_CODE(x) \ - { DWORD* _nIns = (uint8_t*)x - -#define END_NATIVE_CODE(x) \ - (x) = (dictwordp*)_nIns; } - -// BX -#define BX(_r) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_r)); \ - *(--_nIns) = (NIns)( COND_AL | (0x12<<20) | (0xFFF<<8) | (1<<4) | (_r)); \ - asm_output("bx %s", gpn(_r)); } while(0) - -/* - * ALU operations - */ - -enum { - ARM_and = 0, - ARM_eor = 1, - ARM_sub = 2, - ARM_rsb = 3, - ARM_add = 4, - ARM_adc = 5, - ARM_sbc = 6, - ARM_rsc = 7, - ARM_tst = 8, - ARM_teq = 9, - ARM_cmp = 10, - ARM_cmn = 11, - ARM_orr = 12, - ARM_mov = 13, - ARM_bic = 14, - ARM_mvn = 15 -}; -#define IsOp(op) (((ARM_##op) >= ARM_and) && ((ARM_##op) <= ARM_mvn)) - -// ALU operation with register and 8-bit immediate arguments -// S - bit, 0 or 1, whether the CPSR register is updated -// rd - destination register -// rl - first (left) operand register -// op2imm - operand 2 immediate. Use encOp2Imm (from NativeARM.cpp) to calculate this. -#define ALUi(cond, op, S, rd, rl, op2imm) ALUi_chk(cond, op, S, rd, rl, op2imm, 1) -#define ALUi_chk(cond, op, S, rd, rl, op2imm, chk) do {\ - if (chk) underrunProtect(4);\ - NanoAssert(IsCond(cond));\ - NanoAssert(IsOp(op));\ - NanoAssert(((S)==0) || ((S)==1));\ - NanoAssert(IsGpReg(rd) && IsGpReg(rl));\ - NanoAssert(isOp2Imm(op2imm));\ - *(--_nIns) = (NIns) ((cond)<<28 | OP_IMM | (ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (op2imm));\ - if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ - asm_output("%s%s%s %s, #0x%X", #op, condNames[cond], (S)?"s":"", gpn(rd), decOp2Imm(op2imm));\ - } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ - NanoAssert(S==1);\ - asm_output("%s%s %s, #0x%X", #op, condNames[cond], gpn(rl), decOp2Imm(op2imm));\ - } else { \ - asm_output("%s%s%s %s, %s, #0x%X", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), decOp2Imm(op2imm));\ - }\ - } while (0) - -// ALU operation with two register arguments -// S - bit, 0 or 1, whether the CPSR register is updated -// rd - destination register -// rl - first (left) operand register -// rr - first (left) operand register -#define ALUr(cond, op, S, rd, rl, rr) ALUr_chk(cond, op, S, rd, rl, rr, 1) -#define ALUr_chk(cond, op, S, rd, rl, rr, chk) do {\ - if (chk) underrunProtect(4);\ - NanoAssert(IsCond(cond));\ - NanoAssert(IsOp(op));\ - NanoAssert(((S)==0) || ((S)==1));\ - NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\ - *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rr));\ - if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ - asm_output("%s%s%s %s, %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr));\ - } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ - NanoAssert(S==1);\ - asm_output("%s%s %s, %s", #op, condNames[cond], gpn(rl), gpn(rr));\ - } else { \ - asm_output("%s%s%s %s, %s, %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr));\ - }\ - } while (0) - -// ALU operation with two register arguments, with rr operated on by a shift and shift immediate -// S - bit, 0 or 1, whether the CPSR register is updated -// rd - destination register -// rl - first (left) operand register -// rr - second (right) operand register -// sh - a ShiftOperator -// imm - immediate argument to shift operator, 5 bits (0..31) -#define ALUr_shi(cond, op, S, rd, rl, rr, sh, imm) do {\ - underrunProtect(4);\ - NanoAssert(IsCond(cond));\ - NanoAssert(IsOp(op));\ - NanoAssert(((S)==0) || ((S)==1));\ - NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\ - NanoAssert(IsShift(sh));\ - NanoAssert((imm)>=0 && (imm)<32);\ - *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (imm)<<7 | (sh)<<4 | (rr));\ - if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ - NanoAssert(rl==0); \ - asm_output("%s%s%s %s, %s, %s #%d", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr), shiftNames[sh], (imm));\ - } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ - NanoAssert(S==1);\ - NanoAssert(rd==0);\ - asm_output("%s%s %s, %s, %s #%d", #op, condNames[cond], gpn(rl), gpn(rr), shiftNames[sh], (imm));\ - } else { \ - asm_output("%s%s%s %s, %s, %s, %s #%d", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr), shiftNames[sh], (imm));\ - }\ - } while (0) - -// ALU operation with two register arguments, with rr operated on by a shift and shift register -// S - bit, 0 or 1, whether the CPSR register is updated -// rd - destination register -// rl - first (left) operand register -// rr - first (left) operand register -// sh - a ShiftOperator -// rs - shift operand register -#define ALUr_shr(cond, op, S, rd, rl, rr, sh, rs) do {\ - underrunProtect(4);\ - NanoAssert(IsCond(cond));\ - NanoAssert(IsOp(op));\ - NanoAssert(((S)==0) || ((S)==1));\ - NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr) && IsGpReg(rs));\ - NanoAssert(IsShift(sh));\ - *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rs)<<8 | (sh)<<4 | (rr));\ - if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ - asm_output("%s%s%s %s, %s, %s %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr), shiftNames[sh], gpn(rs));\ - } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ - NanoAssert(S==1);\ - asm_output("%s%s %s, %s, %s %s", #op, condNames[cond], gpn(rl), gpn(rr), shiftNames[sh], gpn(rs));\ - } else { \ - asm_output("%s%s%s %s, %s, %s, %s %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr), shiftNames[sh], gpn(rs));\ - }\ - } while (0) - -// -------- -// Basic arithmetic operations. -// -------- -// Argument naming conventions for these macros: -// _d Destination register. -// _l First (left) operand. -// _r Second (right) operand. -// _op2imm An operand 2 immediate value. Use encOp2Imm to calculate this. -// _s Set to 1 to update the status flags (for subsequent conditional -// tests). Otherwise, set to 0. - -// _d = _l + decOp2Imm(_op2imm) -#define ADDis(_d,_l,_op2imm,_s) ALUi(AL, add, _s, _d, _l, _op2imm) -#define ADDi(_d,_l,_op2imm) ALUi(AL, add, 0, _d, _l, _op2imm) - -// _d = _l & ~decOp2Imm(_op2imm) -#define BICis(_d,_l,_op2imm,_s) ALUi(AL, bic, _s, _d, _l, _op2imm) -#define BICi(_d,_l,_op2imm) ALUi(AL, bic, 0, _d, _l, _op2imm) - -// _d = _l - decOp2Imm(_op2imm) -#define SUBis(_d,_l,_op2imm,_s) ALUi(AL, sub, _s, _d, _l, _op2imm) -#define SUBi(_d,_l,_op2imm) ALUi(AL, sub, 0, _d, _l, _op2imm) - -// _d = _l & decOp2Imm(_op2imm) -#define ANDis(_d,_l,_op2imm,_s) ALUi(AL, and, _s, _d, _l, _op2imm) -#define ANDi(_d,_l,_op2imm) ALUi(AL, and, 0, _d, _l, _op2imm) - -// _d = _l | decOp2Imm(_op2imm) -#define ORRis(_d,_l,_op2imm,_s) ALUi(AL, orr, _s, _d, _l, _op2imm) -#define ORRi(_d,_l,_op2imm) ALUi(AL, orr, 0, _d, _l, _op2imm) - -// _d = _l ^ decOp2Imm(_op2imm) -#define EORis(_d,_l,_op2imm,_s) ALUi(AL, eor, _s, _d, _l, _op2imm) -#define EORi(_d,_l,_op2imm) ALUi(AL, eor, 0, _d, _l, _op2imm) - -// _d = _l | _r -#define ORRs(_d,_l,_r,_s) ALUr(AL, orr, _s, _d, _l, _r) -#define ORR(_d,_l,_r) ALUr(AL, orr, 0, _d, _l, _r) - -// _d = _l & _r -#define ANDs(_d,_l,_r,_s) ALUr(AL, and, _s, _d, _l, _r) -#define AND(_d,_l,_r) ALUr(AL, and, 0, _d, _l, _r) - -// _d = _l ^ _r -#define EORs(_d,_l,_r,_s) ALUr(AL, eor, _s, _d, _l, _r) -#define EOR(_d,_l,_r) ALUr(AL, eor, 0, _d, _l, _r) - -// _d = _l + _r -#define ADDs(_d,_l,_r,_s) ALUr(AL, add, _s, _d, _l, _r) -#define ADD(_d,_l,_r) ALUr(AL, add, 0, _d, _l, _r) - -// _d = _l - _r -#define SUBs(_d,_l,_r,_s) ALUr(AL, sub, _s, _d, _l, _r) -#define SUB(_d,_l,_r) ALUr(AL, sub, 0, _d, _l, _r) - -// -------- -// Other operations. -// -------- - -// [_d_hi,_d] = _l * _r -#define SMULL(_d, _d_hi, _l, _r) do { \ - underrunProtect(4); \ - NanoAssert(ARM_ARCH_AT_LEAST(6) || ((_d ) != (_l))); \ - NanoAssert(ARM_ARCH_AT_LEAST(6) || ((_d_hi) != (_l))); \ - NanoAssert(IsGpReg(_d) && IsGpReg(_d_hi) && IsGpReg(_l) && IsGpReg(_r)); \ - NanoAssert(((_d) != PC) && ((_d_hi) != PC) && ((_l) != PC) && ((_r) != PC)); \ - *(--_nIns) = (NIns)( COND_AL | 0xc00090 | (_d_hi)<<16 | (_d)<<12 | (_r)<<8 | (_l) ); \ - asm_output("smull %s, %s, %s, %s",gpn(_d),gpn(_d_hi),gpn(_l),gpn(_r)); \ -} while(0) - -// _d = _l * _r -#define MUL(_d, _l, _r) do { \ - underrunProtect(4); \ - NanoAssert(ARM_ARCH_AT_LEAST(6) || ((_d) != (_l))); \ - NanoAssert(IsGpReg(_d) && IsGpReg(_l) && IsGpReg(_r)); \ - NanoAssert(((_d) != PC) && ((_l) != PC) && ((_r) != PC)); \ - *(--_nIns) = (NIns)( COND_AL | (_d)<<16 | (_r)<<8 | 0x90 | (_l) ); \ - asm_output("mul %s, %s, %s",gpn(_d),gpn(_l),gpn(_r)); \ -} while(0) - -// RSBS _d, _r -// _d = 0 - _r -#define RSBS(_d,_r) ALUi(AL, rsb, 1, _d, _r, 0) - -// MVN -// _d = ~_r (one's compliment) -#define MVN(_d,_r) ALUr(AL, mvn, 0, _d, 0, _r) -#define MVNis_chk(_d,_op2imm,_stat,_chk) ALUi_chk(AL, mvn, _stat, _d, 0, op2imm, _chk) -#define MVNis(_d,_op2imm,_stat) MVNis_chk(_d,_op2imm,_stat,1); - -// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions. -// MOVS _d, _r, LSR <_s> -// _d = _r >> _s -#define LSR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSR_reg, _s) - -// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions. -// MOVS _d, _r, LSR #(_imm & 0x1f) -// _d = _r >> (_imm & 0x1f) -#define LSRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSR_imm, (_imm & 0x1f)) - -// Arithmetic Shift Right (ASR) maintains the sign extension. -// MOVS _d, _r, ASR <_s> -// _d = _r >> _s -#define ASR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, ASR_reg, _s) - -// Arithmetic Shift Right (ASR) maintains the sign extension. -// MOVS _r, _r, ASR #(_imm & 0x1f) -// _d = _r >> (_imm & 0x1f) -#define ASRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, ASR_imm, (_imm & 0x1f)) - -// Logical Shift Left (LSL). -// MOVS _d, _r, LSL <_s> -// _d = _r << _s -#define LSL(_d, _r, _s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSL_reg, _s) - -// Logical Shift Left (LSL). -// MOVS _d, _r, LSL #(_imm & 0x1f) -// _d = _r << (_imm & 0x1f) -#define LSLi(_d, _r, _imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSL_imm, (_imm & 0x1f)) - -// TST -#define TST(_l,_r) ALUr(AL, tst, 1, 0, _l, _r) -#define TSTi(_d,_imm) ALUi(AL, tst, 1, 0, _d, _imm) - -// CMP -#define CMP(_l,_r) ALUr(AL, cmp, 1, 0, _l, _r) -#define CMN(_l,_r) ALUr(AL, cmn, 1, 0, _l, _r) - -// MOV -#define MOVis_chk(_d,_op2imm,_stat,_chk) ALUi_chk(AL, mov, _stat, _d, 0, op2imm, _chk) -#define MOVis(_d,_op2imm,_stat) MOVis_chk(_d,_op2imm,_stat,1) -#define MOVi(_d,_op2imm) MOVis(_d,_op2imm,0); - -#define MOV_cond(_cond,_d,_s) ALUr(_cond, mov, 0, _d, 0, _s) - -#define MOV(dr,sr) MOV_cond(AL, dr, sr) - -// _d = [_b+off] -#define LDR(_d,_b,_off) asm_ldr_chk(_d,_b,_off,1) -#define LDR_nochk(_d,_b,_off) asm_ldr_chk(_d,_b,_off,0) - -// _d = [_b + _x<<_s] -#define LDR_scaled(_d, _b, _x, _s) do { \ - NanoAssert(((_s)&31) == _s);\ - NanoAssert(IsGpReg(_d) && IsGpReg(_b) && IsGpReg(_x));\ - underrunProtect(4);\ - *(--_nIns) = (NIns)(COND_AL | (0x79<<20) | ((_b)<<16) | ((_d)<<12) | ((_s)<<7) | (_x));\ - asm_output("ldr %s, [%s, +%s, LSL #%d]", gpn(_d), gpn(_b), gpn(_x), (_s));\ - } while (0) - -// _d = #_imm -#define LDi(_d,_imm) asm_ld_imm(_d,_imm) - -// MOVW and MOVT are ARMv6T2 or newer only - -// MOVW -- writes _imm into _d, zero-extends. -#define MOVWi_cond_chk(_cond,_d,_imm,_chk) do { \ - NanoAssert(isU16(_imm)); \ - NanoAssert(IsGpReg(_d)); \ - NanoAssert(IsCond(_cond)); \ - if (_chk) underrunProtect(4); \ - *(--_nIns) = (NIns)( (_cond)<<28 | 3<<24 | 0<<20 | (((_imm)>>12)&0xf)<<16 | (_d)<<12 | ((_imm)&0xfff) ); \ - asm_output("movw%s %s, #0x%x", condNames[_cond], gpn(_d), (_imm)); \ - } while (0) - -#define MOVWi(_d,_imm) MOVWi_cond_chk(AL, _d, _imm, 1) -#define MOVWi_chk(_d,_imm,_chk) MOVWi_cond_chk(AL, _d, _imm, _chk) -#define MOVWi_cond(_cond,_d,_imm) MOVWi_cond_chk(_cond, _d, _imm, 1) - -// MOVT -- writes _imm into top halfword of _d, does not affect bottom halfword -#define MOVTi_cond_chk(_cond,_d,_imm,_chk) do { \ - NanoAssert(isU16(_imm)); \ - NanoAssert(IsGpReg(_d)); \ - NanoAssert(IsCond(_cond)); \ - if (_chk) underrunProtect(4); \ - *(--_nIns) = (NIns)( (_cond)<<28 | 3<<24 | 4<<20 | (((_imm)>>12)&0xf)<<16 | (_d)<<12 | ((_imm)&0xfff) ); \ - asm_output("movt%s %s, #0x%x", condNames[_cond], gpn(_d), (_imm)); \ - } while (0) - -#define MOVTi(_d,_imm) MOVTi_cond_chk(AL, _d, _imm, 1) -#define MOVTi_chk(_d,_imm,_chk) MOVTi_cond_chk(AL, _d, _imm, _chk) -#define MOVTi_cond(_cond,_d,_imm) MOVTi_cond_chk(_cond, _d, _imm, 1) - -// i386 compat, for Assembler.cpp -#define MR(d,s) MOV(d,s) -#define ST(base,offset,reg) STR(reg,base,offset) - -// Load a byte (8 bits). The offset range is ±4095. -#define LDRB(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - underrunProtect(4); \ - if (_off < 0) { \ - NanoAssert(isU12(-(_off))); \ - *(--_nIns) = (NIns)( COND_AL | (0x55<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xfff) ); \ - } else { \ - NanoAssert(isU12(_off)); \ - *(--_nIns) = (NIns)( COND_AL | (0x5D<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xfff) ); \ - } \ - asm_output("ldrb %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ - } while(0) - -// Load a byte (8 bits), sign-extend to 32 bits. The offset range is -// ±255 (different from LDRB, same as LDRH/LDRSH) -#define LDRSB(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - underrunProtect(4); \ - if (_off < 0) { \ - NanoAssert(isU8(-(_off))); \ - *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_n)<<16) | ((_d)<<12) | ((0xD)<<4) | (((-(_off))&0xf0)<<4) | ((-(_off))&0xf) ); \ - } else { \ - NanoAssert(isU8(_off)); \ - *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_n)<<16) | ((_d)<<12) | ((0xD)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \ - } \ - asm_output("ldrsb %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ - } while(0) - -// Load and sign-extend a half word (16 bits). The offset range is ±255, and -// must be aligned to two bytes on some architectures (the caller is responsible -// for ensuring appropriate alignment) -#define LDRH(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - underrunProtect(4); \ - if (_off < 0) { \ - NanoAssert(isU8(-(_off))); \ - *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((-(_off))&0xf0)<<4) | ((-(_off))&0xf) ); \ - } else { \ - NanoAssert(isU8(_off)); \ - *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \ - } \ - asm_output("ldrh %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ - } while(0) - -// Load and sign-extend a half word (16 bits). The offset range is ±255, and -// must be aligned to two bytes on some architectures (the caller is responsible -// for ensuring appropriate alignment) -#define LDRSH(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - underrunProtect(4); \ - if (_off < 0) { \ - NanoAssert(isU8(-(_off))); \ - *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_n)<<16) | ((_d)<<12) | ((0xF)<<4) | (((-(_off))&0xf0)<<4) | ((-(_off))&0xf) ); \ - } else { \ - NanoAssert(isU8(_off)); \ - *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_n)<<16) | ((_d)<<12) | ((0xF)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \ - } \ - asm_output("ldrsh %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ - } while(0) - -// Valid offset for STR and STRB is +/- 4095, STRH only has +/- 255 -#define STR(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - NanoAssert(isU12(_off) || isU12(-(_off))); \ - underrunProtect(4); \ - if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x50<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \ - else *(--_nIns) = (NIns)( COND_AL | (0x58<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \ - asm_output("str %s, [%s, #%d]", gpn(_d), gpn(_n), (_off)); \ - } while(0) - -#define STRB(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - NanoAssert(isU12(_off) || isU12(-(_off))); \ - underrunProtect(4); \ - if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x54<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \ - else *(--_nIns) = (NIns)( COND_AL | (0x5C<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \ - asm_output("strb %s, [%s, #%d]", gpn(_d), gpn(_n), (_off)); \ - } while(0) - -// Only +/- 255 range, unlike STRB/STR -#define STRH(_d,_n,_off) do { \ - NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ - underrunProtect(4); \ - if ((_off)<0) { \ - NanoAssert(isU8(-(_off))); \ - *(--_nIns) = (NIns)( COND_AL | (0x14<<20) | ((_n)<<16) | ((_d)<<12) | (((-(_off))&0xF0)<<4) | (0xB<<4) | ((-(_off))&0xF) ); \ - } else { \ - NanoAssert(isU8(_off)); \ - *(--_nIns) = (NIns)( COND_AL | (0x1C<<20) | ((_n)<<16) | ((_d)<<12) | (((_off)&0xF0)<<4) | (0xB<<4) | ((_off)&0xF) ); \ - } \ - asm_output("strh %s, [%s, #%d]", gpn(_d), gpn(_n), (_off)); \ - } while(0) - -// Encode a breakpoint. The ID is not important and is ignored by the -// processor, but it can be useful as a marker when debugging emitted code. -#define BKPT_insn ((NIns)( COND_AL | (0x12<<20) | (0x7<<4) )) -#define BKPTi_insn(id) ((NIns)(BKPT_insn | ((id << 4) & 0xfff00) | (id & 0xf))); - -#define BKPT_nochk() BKPTi_nochk(0) -#define BKPTi_nochk(id) do { \ - NanoAssert((id & 0xffff) == id); \ - *(--_nIns) = BKPTi_insn(id); \ - } while (0) - -// STMFD SP!, {reg} -#define PUSHr(_r) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_r)); \ - *(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | rmask(_r) ); \ - asm_output("push %s",gpn(_r)); } while (0) - -// STMFD SP!,{reglist} -#define PUSH_mask(_mask) do { \ - underrunProtect(4); \ - NanoAssert(isU16(_mask)); \ - *(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | (_mask) ); \ - asm_output("push %x", (_mask));} while (0) - -// LDMFD SP!,{reg} -#define POPr(_r) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_r)); \ - *(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | rmask(_r) ); \ - asm_output("pop %s",gpn(_r));} while (0) - -// LDMFD SP!,{reglist} -#define POP_mask(_mask) do { \ - underrunProtect(4); \ - NanoAssert(isU16(_mask)); \ - *(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | (_mask) ); \ - asm_output("pop %x", (_mask));} while (0) - -// PC always points to current instruction + 8, so when calculating pc-relative -// offsets, use PC+8. -#define PC_OFFSET_FROM(target,frompc) ((intptr_t)(target) - ((intptr_t)(frompc) + 8)) - -#define B_cond(_c,_t) \ - B_cond_chk(_c,_t,1) - -#define B_nochk(_t) \ - B_cond_chk(AL,_t,0) - -#define B(t) B_cond(AL,t) -#define BHI(t) B_cond(HI,t) -#define BLS(t) B_cond(LS,t) -#define BHS(t) B_cond(HS,t) -#define BLO(t) B_cond(LO,t) -#define BEQ(t) B_cond(EQ,t) -#define BNE(t) B_cond(NE,t) -#define BLT(t) B_cond(LT,t) -#define BGE(t) B_cond(GE,t) -#define BLE(t) B_cond(LE,t) -#define BGT(t) B_cond(GT,t) -#define BVS(t) B_cond(VS,t) -#define BVC(t) B_cond(VC,t) -#define BCC(t) B_cond(CC,t) -#define BCS(t) B_cond(CS,t) - -#define JMP(t) B(t) -#define JMP_nochk(t) B_nochk(t) - -// MOV(cond) _r, #1 -// MOV(!cond) _r, #0 -#define SET(_r,_cond) do { \ - ConditionCode _opp = OppositeCond(_cond); \ - underrunProtect(8); \ - *(--_nIns) = (NIns)( ( _opp<<28) | (0x3A<<20) | ((_r)<<12) | (0) ); \ - *(--_nIns) = (NIns)( (_cond<<28) | (0x3A<<20) | ((_r)<<12) | (1) ); \ - asm_output("mov%s %s, #1", condNames[_cond], gpn(_r)); \ - asm_output("mov%s %s, #0", condNames[_opp], gpn(_r)); \ - } while (0) - -#define SETEQ(r) SET(r,EQ) -#define SETNE(r) SET(r,NE) -#define SETLT(r) SET(r,LT) -#define SETLE(r) SET(r,LE) -#define SETGT(r) SET(r,GT) -#define SETGE(r) SET(r,GE) -#define SETLO(r) SET(r,LO) -#define SETLS(r) SET(r,LS) -#define SETHI(r) SET(r,HI) -#define SETHS(r) SET(r,HS) -#define SETVS(r) SET(r,VS) -#define SETCS(r) SET(r,CS) - -// Load and sign extend a 16-bit value into a reg -#define MOVSX(_d,_off,_b) do { \ - if ((_off)>=0) { \ - if ((_off)<256) { \ - underrunProtect(4); \ - *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_b)<<16) | ((_d)<<12) | ((((_off)>>4)&0xF)<<8) | (0xF<<4) | ((_off)&0xF) ); \ - } else if ((_off)<=510) { \ - underrunProtect(8); \ - int rem = (_off) - 255; \ - NanoAssert(rem<256); \ - *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_d)<<16) | ((_d)<<12) | ((((rem)>>4)&0xF)<<8) | (0xF<<4) | ((rem)&0xF) ); \ - *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<23) | ((_b)<<16) | ((_d)<<12) | (0xFF) ); \ - } else { \ - underrunProtect(16); \ - int rem = (_off) & 3; \ - *(--_nIns) = (NIns)( COND_AL | (0x19<<20) | ((_b)<<16) | ((_d)<<12) | (0xF<<4) | (_d) ); \ - asm_output("ldrsh %s,[%s, #%d]",gpn(_d), gpn(_b), (_off)); \ - *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<23) | ((_d)<<16) | ((_d)<<12) | rem ); \ - *(--_nIns) = (NIns)( COND_AL | (0x1A<<20) | ((_d)<<12) | (2<<7)| (_d) ); \ - *(--_nIns) = (NIns)( COND_AL | (0x3B<<20) | ((_d)<<12) | (((_off)>>2)&0xFF) ); \ - asm_output("mov %s,%d",gpn(_d),(_off)); \ - } \ - } else { \ - if ((_off)>-256) { \ - underrunProtect(4); \ - *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_b)<<16) | ((_d)<<12) | ((((-(_off))>>4)&0xF)<<8) | (0xF<<4) | ((-(_off))&0xF) ); \ - asm_output("ldrsh %s,[%s, #%d]",gpn(_d), gpn(_b), (_off)); \ - } else if ((_off)>=-510){ \ - underrunProtect(8); \ - int rem = -(_off) - 255; \ - NanoAssert(rem<256); \ - *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_d)<<16) | ((_d)<<12) | ((((rem)>>4)&0xF)<<8) | (0xF<<4) | ((rem)&0xF) ); \ - *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<22) | ((_b)<<16) | ((_d)<<12) | (0xFF) ); \ - } else NanoAssert(0); \ - } \ - } while(0) - -/* - * VFP - */ - -#define FMDRR(_Dm,_Rd,_Rn) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dm) && IsGpReg(_Rd) && IsGpReg(_Rn)); \ - *(--_nIns) = (NIns)( COND_AL | (0xC4<<20) | ((_Rn)<<16) | ((_Rd)<<12) | (0xB1<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fmdrr %s,%s,%s", gpn(_Dm), gpn(_Rd), gpn(_Rn)); \ - } while (0) - -#define FMRRD(_Rd,_Rn,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsGpReg(_Rd) && IsGpReg(_Rn) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xC5<<20) | ((_Rn)<<16) | ((_Rd)<<12) | (0xB1<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fmrrd %s,%s,%s", gpn(_Rd), gpn(_Rn), gpn(_Dm)); \ - } while (0) - -#define FMRDH(_Rd,_Dn) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsGpReg(_Rd) && IsFpReg(_Dn)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | ((_Rd)<<12) | (0xB<<8) | (1<<4) ); \ - asm_output("fmrdh %s,%s", gpn(_Rd), gpn(_Dn)); \ - } while (0) - -#define FMRDL(_Rd,_Dn) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsGpReg(_Rd) && IsFpReg(_Dn)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE1<<20) | (FpRegNum(_Dn)<<16) | ((_Rd)<<12) | (0xB<<8) | (1<<4) ); \ - asm_output("fmrdh %s,%s", gpn(_Rd), gpn(_Dn)); \ - } while (0) - -#define FSTD_allowD7(_Dd,_Rn,_offs,_allowD7) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_offs)%4) == 0); \ - NanoAssert(isU8((_offs)/4) || isU8(-(_offs)/4)); \ - NanoAssert(IsFpReg(_Dd) && IsGpReg(_Rn)); \ - int negflag = 1<<23; \ - intptr_t offs = (_offs); \ - if (_offs < 0) { \ - negflag = 0<<23; \ - offs = -(offs); \ - } \ - *(--_nIns) = (NIns)( COND_AL | (0xD0<<20) | ((_Rn)<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | negflag | ((offs>>2)&0xff) ); \ - asm_output("fstd %s,%s(%d)", gpn(_Dd), gpn(_Rn), _offs); \ - } while (0) - -#define FSTD(_Dd,_Rn,_offs) \ - FSTD_allowD7(_Dd,_Rn,_offs,0) - -#define FLDD_chk(_Dd,_Rn,_offs,_chk) do { \ - if(_chk) underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_offs)%4) == 0); \ - NanoAssert(isU8((_offs)/4) || isU8(-(_offs)/4)); \ - NanoAssert(IsFpReg(_Dd) && !IsFpReg(_Rn)); \ - int negflag = 1<<23; \ - intptr_t offs = (_offs); \ - if (_offs < 0) { \ - negflag = 0<<23; \ - offs = -(offs); \ - } \ - *(--_nIns) = (NIns)( COND_AL | (0xD1<<20) | ((_Rn)<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | negflag | ((offs>>2)&0xff) ); \ - asm_output("fldd %s,%s(%d)", gpn(_Dd), gpn(_Rn), _offs); \ - } while (0) -#define FLDD(_Dd,_Rn,_offs) FLDD_chk(_Dd,_Rn,_offs,1) - -#define FUITOD(_Dd,_Sm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && ((_Sm) == S0)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB8<<16) | (FpRegNum(_Dd)<<12) | (0x2D<<6) | (0<<5) | (0x0) ); \ - asm_output("fuitod %s,%s", gpn(_Dd), gpn(_Sm)); \ - } while (0) - -#define FNEGD(_Dd,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB1<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fnegd %s,%s", gpn(_Dd), gpn(_Dm)); \ - } while (0) - -#define FADDD(_Dd,_Dn,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ - asm_output("faddd %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ - } while (0) - -#define FSUBD(_Dd,_Dn,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fsubd %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ - } while (0) - -#define FMULD(_Dd,_Dn,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE2<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fmuld %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ - } while (0) - -#define FDIVD(_Dd,_Dn,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE8<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fdivd %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ - } while (0) - -#define FMSTAT() do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - *(--_nIns) = (NIns)( COND_AL | 0x0EF1FA10); \ - asm_output("fmstat"); \ - } while (0) - -#define FCMPD(_Dd,_Dm,_E) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ - NanoAssert(((_E)==0) || ((_E)==1)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB4<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | ((_E)<<7) | (0x4<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fcmp%sd %s,%s", (((_E)==1)?"e":""), gpn(_Dd), gpn(_Dm)); \ - } while (0) - -#define FCPYD_cond(_cond,_Dd,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ - NanoAssert(IsCond(_cond)); \ - *(--_nIns) = (NIns)( ((_cond)<<28) | (0xEB0<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ - asm_output("fcpyd%s %s,%s", condNames[_cond], gpn(_Dd), gpn(_Dm)); \ - } while (0) -#define FCPYD(_Dd,_Dm) FCPYD_cond(AL,_Dd,_Dm) - -#define FMRS(_Rd,_Sn) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sn) == S0) && IsGpReg(_Rd)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE1<<20) | (0x0<<16) | ((_Rd)<<12) | (0xA<<8) | (0<<7) | (0x1<<4) ); \ - asm_output("fmrs %s,%s", gpn(_Rd), gpn(_Sn)); \ - } while (0) - -/* - * The following instructions can only be used with S0 as the - * single-precision register; that limitation can be removed if - * needed, but we'd have to teach NJ about all the single precision - * regs, and their encoding is strange (top 4 bits usually in a block, - * low bit elsewhere). - */ - -#define FSITOD(_Dd,_Sm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(IsFpReg(_Dd) && ((_Sm) == S0)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB8<<16) | (FpRegNum(_Dd)<<12) | (0x2F<<6) | (0<<5) | (0x0) ); \ - asm_output("fsitod %s,%s", gpn(_Dd), gpn(_Sm)); \ - } while (0) - -#define FMSR(_Sn,_Rd) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sn) == S0) && IsGpReg(_Rd)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE0<<20) | (0x0<<16) | ((_Rd)<<12) | (0xA<<8) | (0<<7) | (0x1<<4) ); \ - asm_output("fmsr %s,%s", gpn(_Sn), gpn(_Rd)); \ - } while (0) - -#define FMRS(_Rd,_Sn) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sn) == S0) && IsGpReg(_Rd)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE1<<20) | (0x0<<16) | ((_Rd)<<12) | (0xA<<8) | (0<<7) | (0x1<<4) ); \ - asm_output("fmrs %s,%s", gpn(_Rd), gpn(_Sn)); \ - } while (0) - -#define FMSR(_Sn,_Rd) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sn) == S0) && IsGpReg(_Rd)); \ - *(--_nIns) = (NIns)( COND_AL | (0xE0<<20) | (0x0<<16) | ((_Rd)<<12) | (0xA<<8) | (0<<7) | (0x1<<4) ); \ - asm_output("fmsr %s,%s", gpn(_Sn), gpn(_Rd)); \ - } while (0) - -#define FCVTSD(_Sd,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sd) == S0) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB7<<16) | (0x0<<12) | (0xBC<<4) | (FpRegNum(_Dm)) ); \ - asm_output("[0x%08x] fcvtsd s0,%s", *_nIns, gpn(_Dm)); \ - } while (0) - -#define FCVTDS(_Dd,_Sm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sm) == S0) && IsFpReg(_Dd)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEB7<<16) | (FpRegNum(_Dd)<<12) | (0xAC<<4) | (0x0) ); \ - asm_output("fcvtds %s,s0", gpn(_Dd)); \ - } while(0) - -#define FLDS(_Sd,_Rn,_offs) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sd) == S0) && !IsFpReg(_Rn)); \ - NanoAssert(((_offs)%4) == 0); \ - NanoAssert((isU8((_offs)/4)) || isU8(-(_offs)/4)); \ - int addflag = 1<<23; \ - intptr_t offs = (_offs); \ - if (offs < 0) { \ - addflag = 0; \ - offs = -offs; \ - } \ - *(--_nIns) = (NIns)( COND_AL | (0xD1<<20) | ((_Rn)<<16) | (0x0<<12) | (0xA << 8) | addflag | ((offs>>2)&0xff) ); \ - asm_output("flds s0, [%s, #%d]", gpn(_Rn), (_offs)); \ - } while (0) - -#define FSTS(_Sd,_Rn,_offs) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sd) == S0) && !IsFpReg(_Rn)); \ - NanoAssert(((_offs)%4) == 0); \ - NanoAssert((isU8((_offs)/4)) || isU8(-(_offs)/4)); \ - int addflag = 1<<23; \ - intptr_t offs = (_offs); \ - if (offs < 0) { \ - addflag = 0; \ - offs = -offs; \ - } \ - *(--_nIns) = (NIns)( COND_AL | (0xD0<<20) | ((_Rn)<<16) | (0x0<<12) | (0xA << 8) | addflag | ((offs>>2)&0xff) ); \ - asm_output("fsts s0, [%s, #%d]", gpn(_Rn), (_offs)); \ - } while (0) - -#define FTOSID(_Sd,_Dm) do { \ - underrunProtect(4); \ - NanoAssert(ARM_VFP); \ - NanoAssert(((_Sd) == S0) && IsFpReg(_Dm)); \ - *(--_nIns) = (NIns)( COND_AL | (0xEBD<<16) | (0x0<<12) | (0xB4<<4) | FpRegNum(_Dm) ); \ - asm_output("ftosid s0, %s", gpn(_Dm)); \ - } while (0) - -} // namespace nanojit -#endif // __nanojit_NativeARM__ diff --git a/deps/mozjs/js/src/nanojit/NativeCommon.h b/deps/mozjs/js/src/nanojit/NativeCommon.h deleted file mode 100644 index aed175be833..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeCommon.h +++ /dev/null @@ -1,122 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_NativeCommon__ -#define __nanojit_NativeCommon__ - -namespace nanojit -{ - // In debug builds, Register is defined as a non-integer type to avoid - // possible mix-ups with RegisterMask and integer offsets. In non-debug - // builds, it's defined as an integer just in case some compilers fail to - // optimize single-element structs in the obvious way. - // - // Note that in either case, a Register can be initialized like so: - // - // Register r = { 0 }; - // - // In the debug case it's a struct initializer, in the non-debug case it's - // a scalar initializer. - // - // XXX: The exception to all the above is that if NJ_USE_UINT32_REGISTER - // is defined, the back-end is responsible for defining its own Register - // type, which will probably be an enum. This is just to give all the - // back-ends a chance to transition smoothly. -#if defined(NJ_USE_UINT32_REGISTER) - #define REGNUM(r) (r) - -#elif defined(DEBUG) || defined(__SUNPRO_CC) - // Always use struct declaration for 'Register' with - // Solaris Studio C++ compiler, because it has a bug: - // Scalar type can not be initialized by '{1}'. - // See bug 603560. - - struct Register { - uint32_t n; // the register number - }; - - static inline uint32_t REGNUM(Register r) { - return r.n; - } - - static inline Register operator+(Register r, int c) - { - r.n += c; - return r; - } - - static inline bool operator==(Register r1, Register r2) - { - return r1.n == r2.n; - } - - static inline bool operator!=(Register r1, Register r2) - { - return r1.n != r2.n; - } - - static inline bool operator<=(Register r1, Register r2) - { - return r1.n <= r2.n; - } - - static inline bool operator<(Register r1, Register r2) - { - return r1.n < r2.n; - } - - static inline bool operator>=(Register r1, Register r2) - { - return r1.n >= r2.n; - } - - static inline bool operator>(Register r1, Register r2) - { - return r1.n > r2.n; - } -#else - typedef uint32_t Register; - - static inline uint32_t REGNUM(Register r) { - return r; - } -#endif -} // namespace nanojit - -#endif // __nanojit_NativeCommon__ diff --git a/deps/mozjs/js/src/nanojit/NativeMIPS.cpp b/deps/mozjs/js/src/nanojit/NativeMIPS.cpp deleted file mode 100644 index 08274dc3d7d..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeMIPS.cpp +++ /dev/null @@ -1,2073 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * MIPS Technologies Inc - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Chris Dearman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#if defined FEATURE_NANOJIT && defined NANOJIT_MIPS - -namespace nanojit -{ -#ifdef NJ_VERBOSE - const char *regNames[] = { - "$zr", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", - "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", - "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", - "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", - - "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", - "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", - "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", - "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" - }; - - const char *cname[16] = { - "f", "un", "eq", "ueq", - "olt", "ult", "ole", "ule", - "sf", "ngle", "seq", "ngl", - "lt", "nge", "le", "ngt" - }; - - const char *fname[32] = { - "resv", "resv", "resv", "resv", - "resv", "resv", "resv", "resv", - "resv", "resv", "resv", "resv", - "resv", "resv", "resv", "resv", - "s", "d", "resv", "resv", - "w", "l", "ps", "resv", - "resv", "resv", "resv", "resv", - "resv", "resv", "resv", "resv", - }; - - const char *oname[64] = { - "special", "regimm", "j", "jal", "beq", "bne", "blez", "bgtz", - "addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui", - "cop0", "cop1", "cop2", "cop1x", "beql", "bnel", "blezl", "bgtzl", - "resv", "resv", "resv", "resv", "special2", "jalx", "resv", "special3", - "lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr", "resv", - "sb", "sh", "swl", "sw", "resv", "resv", "swr", "cache", - "ll", "lwc1", "lwc2", "pref", "resv", "ldc1", "ldc2", "resv", - "sc", "swc1", "swc2", "resv", "resv", "sdc1", "sdc2", "resv", - }; -#endif - - const Register Assembler::argRegs[] = { A0, A1, A2, A3 }; - const Register Assembler::retRegs[] = { V0, V1 }; - const Register Assembler::savedRegs[] = { - S0, S1, S2, S3, S4, S5, S6, S7, -#ifdef FPCALLEESAVED - FS0, FS1, FS2, FS3, FS4, FS5 -#endif - }; - -#define USE(x) (void)x -#define BADOPCODE(op) NanoAssertMsgf(false, "unexpected opcode %s", lirNames[op]) - - // This function will get will get optimised by the compiler into a known value - static inline bool isLittleEndian(void) - { - const union { - uint32_t ival; - unsigned char cval[4]; - } u = { 1 }; - return u.cval[0] == 1; - } - - // offsets to most/least significant parts of 64bit data in memory - // These functions will get optimised by the compiler into a known value - static inline int mswoff(void) { - return isLittleEndian() ? 4 : 0; - } - - static inline int lswoff(void) { - return isLittleEndian() ? 0 : 4; - } - - static inline Register mswregpair(Register r) { - return Register(r + (isLittleEndian() ? 1 : 0)); - } - - static inline Register lswregpair(Register r) { - return Register(r + (isLittleEndian() ? 0 : 1)); - } - -// These variables affect the code generator -// They can be defined as constants and the compiler will remove -// the unused paths through dead code elimination -// Alternatively they can be defined as variables which will allow -// the exact code generated to be determined at runtime -// -// cpu_has_fpu CPU has fpu -// cpu_has_movn CPU has movn -// cpu_has_cmov CPU has movf/movn instructions -// cpu_has_lsdc1 CPU has ldc1/sdc1 instructions -// cpu_has_lsxdc1 CPU has ldxc1/sdxc1 instructions -// cpu_has_fpuhazard hazard between c.xx.xx & bc1[tf] -// -// Currently the values are initialised bases on preprocessor definitions - -#ifdef DEBUG - // Don't allow the compiler to eliminate dead code for debug builds - #define _CONST -#else - #define _CONST const -#endif - -#if NJ_SOFTFLOAT_SUPPORTED - _CONST bool cpu_has_fpu = false; -#else - _CONST bool cpu_has_fpu = true; -#endif - -#if (__mips==4 || __mips==32 || __mips==64) - _CONST bool cpu_has_cmov = true; -#else - _CONST bool cpu_has_cmov = false; -#endif - -#if __mips != 1 - _CONST bool cpu_has_lsdc1 = true; -#else - _CONST bool cpu_has_lsdc1 = false; -#endif - -#if (__mips==32 || __mips==64) && __mips_isa_rev>=2 - _CONST bool cpu_has_lsdxc1 = true; -#else - _CONST bool cpu_has_lsdxc1 = false; -#endif - -#if (__mips==1 || __mips==2 || __mips==3) - _CONST bool cpu_has_fpuhazard = true; -#else - _CONST bool cpu_has_fpuhazard = false; -#endif -#undef _CONST - - /* Support routines */ - - debug_only ( - // break to debugger when generating code to this address - static NIns *breakAddr; - static void codegenBreak(NIns *genAddr) - { - NanoAssert (breakAddr != genAddr); - } - ) - - // Equivalent to assembler %hi(), %lo() - uint16_t hi(uint32_t v) - { - uint16_t r = v >> 16; - if ((int16_t)(v) < 0) - r += 1; - return r; - } - - int16_t lo(uint32_t v) - { - int16_t r = v; - return r; - } - - void Assembler::asm_li32(Register r, int32_t imm) - { - // general case generating a full 32-bit load - ADDIU(r, r, lo(imm)); - LUI(r, hi(imm)); - } - - void Assembler::asm_li(Register r, int32_t imm) - { -#if !PEDANTIC - if (isU16(imm)) { - ORI(r, ZERO, imm); - return; - } - if (isS16(imm)) { - ADDIU(r, ZERO, imm); - return; - } - if ((imm & 0xffff) == 0) { - LUI(r, uint32_t(imm) >> 16); - return; - } -#endif - asm_li32(r, imm); - } - - // 64 bit immediate load to a register pair - void Assembler::asm_li_d(Register r, int32_t msw, int32_t lsw) - { - if (IsFpReg(r)) { - NanoAssert(cpu_has_fpu); - // li $at,lsw # iff lsw != 0 - // mtc1 $at,$r # may use $0 instead of $at - // li $at,msw # iff (msw != 0) && (msw != lsw) - // mtc1 $at,$(r+1) # may use $0 instead of $at - if (msw == 0) - MTC1(ZERO, r+1); - else { - MTC1(AT, r+1); - // If the MSW & LSW values are different, reload AT - if (msw != lsw) - asm_li(AT, msw); - } - if (lsw == 0) - MTC1(ZERO, r); - else { - MTC1(AT, r); - asm_li(AT, lsw); - } - } - else { - /* - * li $r.lo, lsw - * li $r.hi, msw # will be converted to move $f.hi,$f.lo if (msw==lsw) - */ - if (msw == lsw) - MOVE(mswregpair(r), lswregpair(r)); - else - asm_li(mswregpair(r), msw); - asm_li(lswregpair(r), lsw); - } - } - - void Assembler::asm_move(Register d, Register s) - { - MOVE(d, s); - } - - // General load/store operation - void Assembler::asm_ldst(int op, Register rt, int dr, Register rbase) - { -#if !PEDANTIC - if (isS16(dr)) { - LDST(op, rt, dr, rbase); - return; - } -#endif - - // lui AT,hi(d) - // addu AT,rbase - // ldst rt,lo(d)(AT) - LDST(op, rt, lo(dr), AT); - ADDU(AT, AT, rbase); - LUI(AT, hi(dr)); - } - - void Assembler::asm_ldst64(bool store, Register r, int dr, Register rbase) - { -#if !PEDANTIC - if (isS16(dr) && isS16(dr+4)) { - if (IsGpReg(r)) { - LDST(store ? OP_SW : OP_LW, r+1, dr+4, rbase); - LDST(store ? OP_SW : OP_LW, r, dr, rbase); - } - else { - NanoAssert(cpu_has_fpu); - // NanoAssert((dr & 7) == 0); - if (cpu_has_lsdc1 && ((dr & 7) == 0)) { - // lsdc1 $fr,dr($rbase) - LDST(store ? OP_SDC1 : OP_LDC1, r, dr, rbase); - } - else { - // lswc1 $fr, dr+LSWOFF($rbase) - // lswc1 $fr+1,dr+MSWOFF($rbase) - LDST(store ? OP_SWC1 : OP_LWC1, r+1, dr+mswoff(), rbase); - LDST(store ? OP_SWC1 : OP_LWC1, r, dr+lswoff(), rbase); - } - return; - } - } -#endif - - if (IsGpReg(r)) { - // lui $at,%hi(d) - // addu $at,$rbase - // ldsw $r, %lo(d)($at) - // ldst $r+1,%lo(d+4)($at) - LDST(store ? OP_SW : OP_LW, r+1, lo(dr+4), AT); - LDST(store ? OP_SW : OP_LW, r, lo(dr), AT); - ADDU(AT, AT, rbase); - LUI(AT, hi(dr)); - } - else { - NanoAssert(cpu_has_fpu); - if (cpu_has_lsdxc1) { - // li $at,dr - // lsdcx1 $r,$at($rbase) - if (store) - SDXC1(r, AT, rbase); - else - LDXC1(r, AT, rbase); - asm_li(AT, dr); - } - else if (cpu_has_lsdc1) { - // lui $at,%hi(dr) - // addu $at,$rbase - // lsdc1 $r,%lo(dr)($at) - LDST(store ? OP_SDC1 : OP_LDC1, r, lo(dr), AT); - ADDU(AT, AT, rbase); - LUI(AT, hi(dr)); - } - else { - // lui $at,%hi(d) - // addu $at,$rbase - // lswc1 $r, %lo(d+LSWOFF)($at) - // lswc1 $r+1,%lo(d+MSWOFF)($at) - LDST(store ? OP_SWC1 : OP_LWC1, r+1, lo(dr+mswoff()), AT); - LDST(store ? OP_SWC1 : OP_LWC1, r, lo(dr+lswoff()), AT); - ADDU(AT, AT, rbase); - LUI(AT, hi(dr)); - } - } - } - - void Assembler::asm_store_imm64(LIns *value, int dr, Register rbase) - { - NanoAssert(value->isImmD()); - int32_t msw = value->immDhi(); - int32_t lsw = value->immDlo(); - - // li $at,lsw # iff lsw != 0 - // sw $at,off+LSWOFF($rbase) # may use $0 instead of $at - // li $at,msw # iff (msw != 0) && (msw != lsw) - // sw $at,off+MSWOFF($rbase) # may use $0 instead of $at - - NanoAssert(isS16(dr) && isS16(dr+4)); - - if (lsw == 0) - SW(ZERO, dr+lswoff(), rbase); - else { - SW(AT, dr+lswoff(), rbase); - if (msw != lsw) - asm_li(AT, lsw); - } - if (msw == 0) - SW(ZERO, dr+mswoff(), rbase); - else { - SW(AT, dr+mswoff(), rbase); - // If the MSW & LSW values are different, reload AT - if (msw != lsw) - asm_li(AT, msw); - } - } - - void Assembler::asm_regarg(ArgType ty, LIns* p, Register r) - { - NanoAssert(deprecated_isKnownReg(r)); - if (ty == ARGTYPE_I || ty == ARGTYPE_UI) { - // arg goes in specific register - if (p->isImmI()) - asm_li(r, p->immI()); - else { - if (p->isExtant()) { - if (!p->deprecated_hasKnownReg()) { - // load it into the arg reg - int d = findMemFor(p); - if (p->isop(LIR_allocp)) - ADDIU(r, FP, d); - else - asm_ldst(OP_LW, r, d, FP); - } - else - // it must be in a saved reg - MOVE(r, p->deprecated_getReg()); - } - else { - // this is the last use, so fine to assign it - // to the scratch reg, it's dead after this point. - findSpecificRegFor(p, r); - } - } - } - else { - // Other argument types unsupported - NanoAssert(false); - } - } - - void Assembler::asm_stkarg(LIns* arg, int stkd) - { - bool isF64 = arg->isD(); - Register rr; - if (arg->isExtant() && (rr = arg->deprecated_getReg(), deprecated_isKnownReg(rr))) { - // The argument resides somewhere in registers, so we simply need to - // push it onto the stack. - if (!cpu_has_fpu || !isF64) { - NanoAssert(IsGpReg(rr)); - SW(rr, stkd, SP); - } - else { - NanoAssert(cpu_has_fpu); - NanoAssert(IsFpReg(rr)); - NanoAssert((stkd & 7) == 0); - asm_ldst64(true, rr, stkd, SP); - } - } - else { - // The argument does not reside in registers, so we need to get some - // memory for it and then copy it onto the stack. - int d = findMemFor(arg); - if (!isF64) { - SW(AT, stkd, SP); - if (arg->isop(LIR_allocp)) - ADDIU(AT, FP, d); - else - LW(AT, d, FP); - } - else { - NanoAssert((stkd & 7) == 0); - SW(AT, stkd+4, SP); - LW(AT, d+4, FP); - SW(AT, stkd, SP); - LW(AT, d, FP); - } - } - } - - // Encode a 64-bit floating-point argument using the appropriate ABI. - // This function operates in the same way as asm_arg, except that it will only - // handle arguments where (ArgType)ty == ARGTYPE_D. - void - Assembler::asm_arg_64(LIns* arg, Register& r, Register& fr, int& stkd) - { - // The stack offset always be at least aligned to 4 bytes. - NanoAssert((stkd & 3) == 0); -#if NJ_SOFTFLOAT_SUPPORTED - NanoAssert(arg->isop(LIR_ii2d)); -#else - NanoAssert(cpu_has_fpu); -#endif - - // O32 ABI requires that 64-bit arguments are aligned on even-numbered - // registers, as A0:A1/FA0 or A2:A3/FA1. Use the stack offset to keep track - // where we are - if (stkd & 4) { - if (stkd < 16) { - r = Register(r + 1); - fr = Register(fr + 1); - } - stkd += 4; - } - - if (stkd < 16) { - NanoAssert(fr == FA0 || fr == FA1 || fr == A2); - if (fr == FA0 || fr == FA1) - findSpecificRegFor(arg, fr); - else { - findSpecificRegFor(arg, FA1); - // Move it to the integer pair - Register fpupair = arg->getReg(); - Register intpair = fr; - MFC1(mswregpair(intpair), Register(fpupair + 1)); // Odd fpu register contains sign,expt,manthi - MFC1(lswregpair(intpair), fpupair); // Even fpu register contains mantlo - } - r = Register(r + 2); - fr = Register(fr + 2); - } - else - asm_stkarg(arg, stkd); - - stkd += 8; - } - - /* Required functions */ - -#define FRAMESIZE 8 -#define RA_OFFSET 4 -#define FP_OFFSET 0 - - void Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base) - { - Register rt, rbase; - getBaseReg2(GpRegs, value, rt, GpRegs, base, rbase, dr); - - switch (op) { - case LIR_sti: - asm_ldst(OP_SW, rt, dr, rbase); - break; - case LIR_sti2s: - asm_ldst(OP_SH, rt, dr, rbase); - break; - case LIR_sti2c: - asm_ldst(OP_SB, rt, dr, rbase); - break; - default: - BADOPCODE(op); - } - - TAG("asm_store32(value=%p{%s}, dr=%d, base=%p{%s})", - value, lirNames[value->opcode()], dr, base, lirNames[base->opcode()]); - } - - void Assembler::asm_ui2d(LIns *ins) - { - Register fr = deprecated_prepResultReg(ins, FpRegs); - Register v = findRegFor(ins->oprnd1(), GpRegs); - Register ft = registerAllocTmp(FpRegs & ~(rmask(fr))); // allocate temporary register for constant - - // todo: support int value in memory, as per x86 - NanoAssert(deprecated_isKnownReg(v)); - - // mtc1 $v,$ft - // bgez $v,1f - // cvt.d.w $fr,$ft - // lui $at,0x41f0 # (double)0x10000000LL = 0x41f0000000000000 - // mtc1 $0,$ft - // mtc1 $at,$ft+1 - // add.d $fr,$fr,$ft - // 1: - - underrunProtect(6*4); // keep branch and destination together - NIns *here = _nIns; - ADD_D(fr,fr,ft); - MTC1(AT,ft+1); - MTC1(ZERO,ft); - LUI(AT,0x41f0); - CVT_D_W(fr,ft); // branch delay slot - BGEZ(v,here); - MTC1(v,ft); - - TAG("asm_ui2d(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_d2i(LIns* ins) - { - NanoAssert(cpu_has_fpu); - - Register rr = deprecated_prepResultReg(ins, GpRegs); - Register sr = findRegFor(ins->oprnd1(), FpRegs); - // trunc.w.d $sr,$sr - // mfc1 $rr,$sr - MFC1(rr,sr); - TRUNC_W_D(sr,sr); - TAG("asm_d2i(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_fop(LIns *ins) - { - NanoAssert(cpu_has_fpu); - if (cpu_has_fpu) { - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - LOpcode op = ins->opcode(); - - // rr = ra OP rb - - Register rr = deprecated_prepResultReg(ins, FpRegs); - Register ra = findRegFor(lhs, FpRegs); - Register rb = (rhs == lhs) ? ra : findRegFor(rhs, FpRegs & ~rmask(ra)); - - switch (op) { - case LIR_addd: ADD_D(rr, ra, rb); break; - case LIR_subd: SUB_D(rr, ra, rb); break; - case LIR_muld: MUL_D(rr, ra, rb); break; - case LIR_divd: DIV_D(rr, ra, rb); break; - default: - BADOPCODE(op); - } - } - TAG("asm_fop(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_fneg(LIns *ins) - { - NanoAssert(cpu_has_fpu); - if (cpu_has_fpu) { - LIns* lhs = ins->oprnd1(); - Register rr = deprecated_prepResultReg(ins, FpRegs); - Register sr = ( !lhs->isInReg() - ? findRegFor(lhs, FpRegs) - : lhs->deprecated_getReg() ); - NEG_D(rr, sr); - } - TAG("asm_fneg(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_immd(LIns *ins) - { - int d = deprecated_disp(ins); - Register rr = ins->deprecated_getReg(); - - deprecated_freeRsrcOf(ins); - - if (cpu_has_fpu && deprecated_isKnownReg(rr)) { - if (d) - asm_spill(rr, d, true); - asm_li_d(rr, ins->immDhi(), ins->immDlo()); - } - else { - NanoAssert(d); - asm_store_imm64(ins, d, FP); - } - TAG("asm_immd(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - -#ifdef NANOJIT_64BIT - void - Assembler::asm_q2i(LIns *) - { - NanoAssert(0); // q2i shouldn't occur on 32-bit platforms - } - - void Assembler::asm_ui2uq(LIns *ins) - { - USE(ins); - TODO(asm_ui2uq); - TAG("asm_ui2uq(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } -#endif - - void Assembler::asm_load64(LIns *ins) - { - NanoAssert(ins->isD()); - - LIns* base = ins->oprnd1(); - int dr = ins->disp(); - - Register rd = ins->deprecated_getReg(); - int ds = deprecated_disp(ins); - - Register rbase = findRegFor(base, GpRegs); - NanoAssert(IsGpReg(rbase)); - deprecated_freeRsrcOf(ins); - - if (cpu_has_fpu && deprecated_isKnownReg(rd)) { - NanoAssert(IsFpReg(rd)); - asm_ldst64 (false, rd, dr, rbase); - } - else { - // Either FPU is not available or the result needs to go into memory; - // in either case, FPU instructions are not required. Note that the - // result will never be loaded into registers if FPU is not available. - NanoAssert(!deprecated_isKnownReg(rd)); - NanoAssert(ds != 0); - - NanoAssert(isS16(dr) && isS16(dr+4)); - NanoAssert(isS16(ds) && isS16(ds+4)); - - // Check that the offset is 8-byte (64-bit) aligned. - NanoAssert((ds & 0x7) == 0); - - // FIXME: allocate a temporary to use for the copy - // to avoid load to use delay - // lw $at,dr($rbase) - // sw $at,ds($fp) - // lw $at,dr+4($rbase) - // sw $at,ds+4($fp) - - SW(AT, ds+4, FP); - LW(AT, dr+4, rbase); - SW(AT, ds, FP); - LW(AT, dr, rbase); - } - - TAG("asm_load64(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_cond(LIns *ins) - { - Register r = deprecated_prepResultReg(ins, GpRegs); - LOpcode op = ins->opcode(); - LIns *a = ins->oprnd1(); - LIns *b = ins->oprnd2(); - - asm_cmp(op, a, b, r); - - TAG("asm_cond(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - -#if NJ_SOFTFLOAT_SUPPORTED - void Assembler::asm_qhi(LIns *ins) - { - Register rr = deprecated_prepResultReg(ins, GpRegs); - LIns *q = ins->oprnd1(); - int d = findMemFor(q); - LW(rr, d+mswoff(), FP); - TAG("asm_qhi(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_qlo(LIns *ins) - { - Register rr = deprecated_prepResultReg(ins, GpRegs); - LIns *q = ins->oprnd1(); - int d = findMemFor(q); - LW(rr, d+lswoff(), FP); - TAG("asm_qlo(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_qjoin(LIns *ins) - { - int d = findMemFor(ins); - NanoAssert(d && isS16(d)); - LIns* lo = ins->oprnd1(); - LIns* hi = ins->oprnd2(); - - Register r = findRegFor(hi, GpRegs); - SW(r, d+mswoff(), FP); - r = findRegFor(lo, GpRegs); // okay if r gets recycled. - SW(r, d+lswoff(), FP); - deprecated_freeRsrcOf(ins); // if we had a reg in use, flush it to mem - - TAG("asm_qjoin(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - -#endif - - void Assembler::asm_neg_not(LIns *ins) - { - LOpcode op = ins->opcode(); - Register rr = deprecated_prepResultReg(ins, GpRegs); - - LIns* lhs = ins->oprnd1(); - // If this is the last use of lhs in reg, we can re-use result reg. - // Else, lhs already has a register assigned. - Register ra = !lhs->isInReg() ? findSpecificRegFor(lhs, rr) : lhs->deprecated_getReg(); - if (op == LIR_noti) - NOT(rr, ra); - else - NEGU(rr, ra); - TAG("asm_neg_not(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_immi(LIns *ins) - { - Register rr = deprecated_prepResultReg(ins, GpRegs); - asm_li(rr, ins->immI()); - TAG("asm_immi(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_cmov(LIns *ins) - { - LIns* condval = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - - NanoAssert(condval->isCmp()); - NanoAssert(ins->opcode() == LIR_cmovi && iftrue->isI() && iffalse->isI()); - - const Register rr = deprecated_prepResultReg(ins, GpRegs); - - const Register iftruereg = findRegFor(iftrue, GpRegs & ~rmask(rr)); - MOVN(rr, iftruereg, AT); - /*const Register iffalsereg =*/ findSpecificRegFor(iffalse, rr); - asm_cmp(condval->opcode(), condval->oprnd1(), condval->oprnd2(), AT); - TAG("asm_cmov(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_condd(LIns *ins) - { - NanoAssert(cpu_has_fpu); - if (cpu_has_fpu) { - Register r = deprecated_prepResultReg(ins, GpRegs); - LOpcode op = ins->opcode(); - LIns *a = ins->oprnd1(); - LIns *b = ins->oprnd2(); - - if (cpu_has_cmov) { - // c.xx.d $a,$b - // li $r,1 - // movf $r,$0,$fcc0 - MOVF(r, ZERO, 0); - ORI(r, ZERO, 1); - } - else { - // c.xx.d $a,$b - // [nop] - // bc1t 1f - // li $r,1 - // move $r,$0 - // 1: - NIns *here = _nIns; - verbose_only(verbose_outputf("%p:", here);) - underrunProtect(3*4); - MOVE(r, ZERO); - ORI(r, ZERO, 1); // branch delay slot - BC1T(here); - if (cpu_has_fpuhazard) - NOP(); - } - asm_cmp(op, a, b, r); - } - TAG("asm_condd(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_i2d(LIns *ins) - { - NanoAssert(cpu_has_fpu); - if (cpu_has_fpu) { - Register fr = deprecated_prepResultReg(ins, FpRegs); - Register v = findRegFor(ins->oprnd1(), GpRegs); - - // mtc1 $v,$fr - // cvt.d.w $fr,$fr - CVT_D_W(fr,fr); - MTC1(v,fr); - } - TAG("asm_i2d(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_ret(LIns *ins) - { - genEpilogue(); - - releaseRegisters(); - assignSavedRegs(); - - LIns *value = ins->oprnd1(); - if (ins->isop(LIR_reti)) { - findSpecificRegFor(value, V0); - } - else { - NanoAssert(ins->isop(LIR_retd)); -#if NJ_SOFTFLOAT_SUPPORTED - NanoAssert(value->isop(LIR_ii2d)); - findSpecificRegFor(value->oprnd1(), V0); // lo - findSpecificRegFor(value->oprnd2(), V1); // hi -#else - findSpecificRegFor(value, FV0); -#endif - } - TAG("asm_ret(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_load32(LIns *ins) - { - LOpcode op = ins->opcode(); - LIns* base = ins->oprnd1(); - int d = ins->disp(); - - Register rres = deprecated_prepResultReg(ins, GpRegs); - Register rbase = getBaseReg(base, d, GpRegs); - - switch (op) { - case LIR_lduc2ui: // 8-bit integer load, zero-extend to 32-bit - asm_ldst(OP_LBU, rres, d, rbase); - break; - case LIR_ldus2ui: // 16-bit integer load, zero-extend to 32-bit - asm_ldst(OP_LHU, rres, d, rbase); - break; - case LIR_ldc2i: // 8-bit integer load, sign-extend to 32-bit - asm_ldst(OP_LB, rres, d, rbase); - break; - case LIR_lds2i: // 16-bit integer load, sign-extend to 32-bit - asm_ldst(OP_LH, rres, d, rbase); - break; - case LIR_ldi: // 32-bit integer load - asm_ldst(OP_LW, rres, d, rbase); - break; - default: - BADOPCODE(op); - } - - TAG("asm_load32(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_param(LIns *ins) - { - uint32_t a = ins->paramArg(); - uint32_t kind = ins->paramKind(); - - if (kind == 0) { - // ordinary param - // first 4 args A0..A3 - if (a < 4) { - // incoming arg in register - deprecated_prepResultReg(ins, rmask(argRegs[a])); - } else { - // incoming arg is on stack - Register r = deprecated_prepResultReg(ins, GpRegs); - TODO(Check stack offset); - int d = FRAMESIZE + a * sizeof(intptr_t); - LW(r, d, FP); - } - } - else { - // saved param - deprecated_prepResultReg(ins, rmask(savedRegs[a])); - } - TAG("asm_param(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_arith(LIns *ins) - { - LOpcode op = ins->opcode(); - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - - RegisterMask allow = GpRegs; - - // We always need the result register and the first operand register. - Register rr = deprecated_prepResultReg(ins, allow); - - // If this is the last use of lhs in reg, we can re-use the result reg. - // Else, lhs already has a register assigned. - Register ra = !lhs->isInReg() ? findSpecificRegFor(lhs, rr) : lhs->deprecated_getReg(); - Register rb, t; - - // Don't re-use the registers we've already allocated. - NanoAssert(deprecated_isKnownReg(rr)); - NanoAssert(deprecated_isKnownReg(ra)); - allow &= ~rmask(rr); - allow &= ~rmask(ra); - - if (rhs->isImmI()) { - int32_t rhsc = rhs->immI(); - if (isS16(rhsc)) { - // MIPS arith immediate ops sign-extend the imm16 value - switch (op) { - case LIR_addxovi: - case LIR_addjovi: - // add with overflow result into $at - // overflow is indicated by ((sign(rr)^sign(ra)) & (sign(rr)^sign(rhsc)) - - // [move $t,$ra] if (rr==ra) - // addiu $rr,$ra,rhsc - // [xor $at,$rr,$ra] if (rr!=ra) - // [xor $at,$rr,$t] if (rr==ra) - // [not $t,$rr] if (rhsc < 0) - // [and $at,$at,$t] if (rhsc < 0) - // [and $at,$at,$rr] if (rhsc >= 0) - // srl $at,$at,31 - - t = registerAllocTmp(allow); - SRL(AT, AT, 31); - if (rhsc < 0) { - AND(AT, AT, t); - NOT(t, rr); - } - else - AND(AT, AT, rr); - if (rr == ra) - XOR(AT, rr, t); - else - XOR(AT, rr, ra); - ADDIU(rr, ra, rhsc); - if (rr == ra) - MOVE(t, ra); - goto done; - case LIR_addi: - ADDIU(rr, ra, rhsc); - goto done; - case LIR_subxovi: - case LIR_subjovi: - // subtract with overflow result into $at - // overflow is indicated by (sign(ra)^sign(rhsc)) & (sign(rr)^sign(ra)) - - // [move $t,$ra] if (rr==ra) - // addiu $rr,$ra,-rhsc - // [xor $at,$rr,$ra] if (rr!=ra) - // [xor $at,$rr,$t] if (rr==ra) - // [and $at,$at,$ra] if (rhsc >= 0 && rr!=ra) - // [and $at,$at,$t] if (rhsc >= 0 && rr==ra) - // [not $t,$ra] if (rhsc < 0 && rr!=ra) - // [not $t,$t] if (rhsc < 0 && rr==ra) - // [and $at,$at,$t] if (rhsc < 0) - // srl $at,$at,31 - if (isS16(-rhsc)) { - t = registerAllocTmp(allow); - SRL(AT,AT,31); - if (rhsc < 0) { - AND(AT, AT, t); - if (rr == ra) - NOT(t, t); - else - NOT(t, ra); - } - else { - if (rr == ra) - AND(AT, AT, t); - else - AND(AT, AT, ra); - } - if (rr == ra) - XOR(AT, rr, t); - else - XOR(AT, rr, ra); - ADDIU(rr, ra, -rhsc); - if (rr == ra) - MOVE(t, ra); - goto done; - } - break; - case LIR_subi: - if (isS16(-rhsc)) { - ADDIU(rr, ra, -rhsc); - goto done; - } - break; - case LIR_mulxovi: - case LIR_muljovi: - case LIR_muli: - // FIXME: optimise constant multiply by 2^n - // if ((rhsc & (rhsc-1)) == 0) - // SLL(rr, ra, ffs(rhsc)-1); - //goto done; - break; - default: - break; - } - } - if (isU16(rhsc)) { - // MIPS logical immediate zero-extend the imm16 value - switch (op) { - case LIR_ori: - ORI(rr, ra, rhsc); - goto done; - case LIR_andi: - ANDI(rr, ra, rhsc); - goto done; - case LIR_xori: - XORI(rr, ra, rhsc); - goto done; - default: - break; - } - } - - // LIR shift ops only use last 5bits of shift const - switch (op) { - case LIR_lshi: - SLL(rr, ra, rhsc&31); - goto done; - case LIR_rshui: - SRL(rr, ra, rhsc&31); - goto done; - case LIR_rshi: - SRA(rr, ra, rhsc&31); - goto done; - default: - break; - } - } - - // general case, put rhs in register - rb = (rhs == lhs) ? ra : findRegFor(rhs, allow); - NanoAssert(deprecated_isKnownReg(rb)); - allow &= ~rmask(rb); - - // The register allocator will have set up one of these 4 cases - // rr==ra && ra==rb r0 = r0 op r0 - // rr==ra && ra!=rb r0 = r0 op r1 - // rr!=ra && ra==rb r0 = r1 op r1 - // rr!=ra && ra!=rb && rr!=rb r0 = r1 op r2 - NanoAssert(ra == rb || rr != rb); - - switch (op) { - case LIR_addxovi: - case LIR_addjovi: - // add with overflow result into $at - // overflow is indicated by (sign(rr)^sign(ra)) & (sign(rr)^sign(rb)) - - // [move $t,$ra] if (rr==ra) - // addu $rr,$ra,$rb - // ; Generate sign($rr)^sign($ra) - // [xor $at,$rr,$t] sign($at)=sign($rr)^sign($t) if (rr==ra) - // [xor $at,$rr,$ra] sign($at)=sign($rr)^sign($ra) if (rr!=ra) - // ; Generate sign($rr)^sign($rb) if $ra!=$rb - // [xor $t,$rr,$rb] if (ra!=rb) - // [and $at,$t] if (ra!=rb) - // srl $at,31 - - t = ZERO; - if (rr == ra || ra != rb) - t = registerAllocTmp(allow); - SRL(AT, AT, 31); - if (ra != rb) { - AND(AT, AT, t); - XOR(t, rr, rb); - } - if (rr == ra) - XOR(AT, rr, t); - else - XOR(AT, rr, ra); - ADDU(rr, ra, rb); - if (rr == ra) - MOVE(t, ra); - break; - case LIR_addi: - ADDU(rr, ra, rb); - break; - case LIR_andi: - AND(rr, ra, rb); - break; - case LIR_ori: - OR(rr, ra, rb); - break; - case LIR_xori: - XOR(rr, ra, rb); - break; - case LIR_subxovi: - case LIR_subjovi: - // subtract with overflow result into $at - // overflow is indicated by (sign(ra)^sign(rb)) & (sign(rr)^sign(ra)) - - // [move $t,$ra] if (rr==ra) - // ; Generate sign($at)=sign($ra)^sign($rb) - // xor $at,$ra,$rb - // subu $rr,$ra,$rb - // ; Generate sign($t)=sign($rr)^sign($ra) - // [xor $t,$rr,$ra] if (rr!=ra) - // [xor $t,$rr,$t] if (rr==ra) - // and $at,$at,$t - // srl $at,$at,31 - - if (ra == rb) { - // special case for (ra == rb) which can't overflow - MOVE(AT, ZERO); - SUBU(rr, ra, rb); - } - else { - t = registerAllocTmp(allow); - SRL(AT, AT, 31); - AND(AT, AT, t); - if (rr == ra) - XOR(t, rr, t); - else - XOR(t, rr, ra); - SUBU(rr, ra, rb); - XOR(AT, ra, rb); - if (rr == ra) - MOVE(t, ra); - } - break; - case LIR_subi: - SUBU(rr, ra, rb); - break; - case LIR_lshi: - // SLLV uses the low-order 5 bits of rb for the shift amount so no masking required - SLLV(rr, ra, rb); - break; - case LIR_rshi: - // SRAV uses the low-order 5 bits of rb for the shift amount so no masking required - SRAV(rr, ra, rb); - break; - case LIR_rshui: - // SRLV uses the low-order 5 bits of rb for the shift amount so no masking required - SRLV(rr, ra, rb); - break; - case LIR_mulxovi: - case LIR_muljovi: - t = registerAllocTmp(allow); - // Overflow indication required - // Do a 32x32 signed multiply generating a 64 bit result - // Compare bit31 of the result with the high order bits - // mult $ra,$rb - // mflo $rr # result to $rr - // sra $t,$rr,31 # $t = 0x00000000 or 0xffffffff - // mfhi $at - // xor $at,$at,$t # sets $at to nonzero if overflow - XOR(AT, AT, t); - MFHI(AT); - SRA(t, rr, 31); - MFLO(rr); - MULT(ra, rb); - break; - case LIR_muli: - MUL(rr, ra, rb); - break; - default: - BADOPCODE(op); - } - done: - TAG("asm_arith(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - void Assembler::asm_store64(LOpcode op, LIns *value, int dr, LIns *base) - { - // NanoAssert((dr & 7) == 0); -#if NANOJIT_64BIT - NanoAssert (op == LIR_stq || op == LIR_std2f || op == LIR_std); -#else - NanoAssert (op == LIR_std2f || op == LIR_std); -#endif - - switch (op) { - case LIR_std: - if (cpu_has_fpu) { - Register rbase = findRegFor(base, GpRegs); - - if (value->isImmD()) - asm_store_imm64(value, dr, rbase); - else { - Register fr = findRegFor(value, FpRegs); - asm_ldst64(true, fr, dr, rbase); - } - } - else { - Register rbase = findRegFor(base, GpRegs); - // *(uint64_t*)(rb+dr) = *(uint64_t*)(FP+da) - - int ds = findMemFor(value); - - // lw $at,ds(FP) - // sw $at,dr($rbase) - // lw $at,ds+4(FP) - // sw $at,dr+4($rbase) - SW(AT, dr+4, rbase); - LW(AT, ds+4, FP); - SW(AT, dr, rbase); - LW(AT, ds, FP); - } - - break; - case LIR_std2f: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - BADOPCODE(op); - return; - } - - TAG("asm_store64(value=%p{%s}, dr=%d, base=%p{%s})", - value, lirNames[value->opcode()], dr, base, lirNames[base->opcode()]); - } - - bool Assembler::canRemat(LIns* ins) - { - return ins->isImmI() || ins->isop(LIR_allocp); - } - - void Assembler::asm_restore(LIns *i, Register r) - { - int d; - if (i->isop(LIR_allocp)) { - d = deprecated_disp(i); - if (isS16(d)) - ADDIU(r, FP, d); - else { - ADDU(r, FP, AT); - asm_li(AT, d); - } - } - else if (i->isImmI()) { - asm_li(r, i->immI()); - } - else { - d = findMemFor(i); - if (IsFpReg(r)) { - asm_ldst64(false, r, d, FP); - } - else { - asm_ldst(OP_LW, r, d, FP); - } - } - TAG("asm_restore(i=%p{%s}, r=%d)", i, lirNames[i->opcode()], r); - } - - void Assembler::asm_cmp(LOpcode condop, LIns *a, LIns *b, Register cr) - { - RegisterMask allow = isCmpDOpcode(condop) ? FpRegs : GpRegs; - Register ra = findRegFor(a, allow); - Register rb = (b==a) ? ra : findRegFor(b, allow & ~rmask(ra)); - - // FIXME: Use slti if b is small constant - - /* Generate the condition code */ - switch (condop) { - case LIR_eqi: - SLTIU(cr,cr,1); - XOR(cr,ra,rb); - break; - case LIR_lti: - SLT(cr,ra,rb); - break; - case LIR_gti: - SLT(cr,rb,ra); - break; - case LIR_lei: - XORI(cr,cr,1); - SLT(cr,rb,ra); - break; - case LIR_gei: - XORI(cr,cr,1); - SLT(cr,ra,rb); - break; - case LIR_ltui: - SLTU(cr,ra,rb); - break; - case LIR_gtui: - SLTU(cr,rb,ra); - break; - case LIR_leui: - XORI(cr,cr,1); - SLTU(cr,rb,ra); - break; - case LIR_geui: - XORI(cr,cr,1); - SLTU(cr,ra,rb); - break; - case LIR_eqd: - C_EQ_D(ra,rb); - break; - case LIR_ltd: - C_LT_D(ra,rb); - break; - case LIR_gtd: - C_LT_D(rb,ra); - break; - case LIR_led: - C_LE_D(ra,rb); - break; - case LIR_ged: - C_LE_D(rb,ra); - break; - default: - debug_only(outputf("%s",lirNames[condop]);) - TODO(asm_cond); - } - } - -#define SEG(addr) (uint32_t(addr) & 0xf0000000) -#define SEGOFFS(addr) (uint32_t(addr) & 0x0fffffff) - - - // Check that the branch target is in range - // Generate a trampoline if it isn't - // Emits the branch delay slot instruction - NIns* Assembler::asm_branchtarget(NIns * const targ) - { - bool inrange; - NIns *btarg = targ; - - // do initial underrun check here to ensure that inrange test is correct - // allow - if (targ) - underrunProtect(2 * 4); // branch + delay slot - - // MIPS offsets are based on the address of the branch delay slot - // which is the next instruction that will be generated - ptrdiff_t bd = BOFFSET(targ-1); - -#if PEDANTIC - inrange = false; -#else - inrange = (targ && isS16(bd)); -#endif - - // If the branch target is known and in range we can just generate a branch - // Otherwise generate a branch to a trampoline that will be stored in the - // literal area - if (inrange) - NOP(); - else { - NIns *tramp = _nSlot; - if (targ) { - // Can the target be reached by a jump instruction? - if (SEG(targ) == SEG(tramp)) { - // [linkedinstructions] - // bxxx trampoline - // nop - // ... - // trampoline: - // j targ - // nop - - underrunProtect(4 * 4); // keep bxx and trampoline together - - NOP(); // delay slot - - // NB trampoline code is emitted in the correct order - trampJ(targ); - trampNOP(); // trampoline delay slot - - } - else { - // [linkedinstructions] - // bxxx trampoline - // lui $at,%hi(targ) - // ... - // trampoline: - // addiu $at,%lo(targ) - // jr $at - // nop - - underrunProtect(5 * 4); // keep bxx and trampoline together - - LUI(AT,hi(uint32_t(targ))); // delay slot - - // NB trampoline code is emitted in the correct order - trampADDIU(AT, AT, lo(uint32_t(targ))); - trampJR(AT); - trampNOP(); // trampoline delay slot - - } - } - else { - // Worst case is bxxx,lui addiu;jr;nop as above - // Best case is branch to trampoline can be replaced - // with branch to target in which case the trampoline will be abandoned - // Fixup handled in nPatchBranch - - underrunProtect(5 * 4); // keep bxx and trampoline together - - NOP(); // delay slot - - trampNOP(); - trampNOP(); - trampNOP(); - - } - btarg = tramp; - } - - return btarg; - } - - - NIns* Assembler::asm_bxx(bool branchOnFalse, LOpcode condop, Register ra, Register rb, NIns * const targ) - { - NIns *patch = NULL; - NIns *btarg = asm_branchtarget(targ); - - if (cpu_has_fpu && isCmpDOpcode(condop)) { - // c.xx.d $ra,$rb - // bc1x btarg - switch (condop) { - case LIR_eqd: - if (branchOnFalse) - BC1F(btarg); - else - BC1T(btarg); - patch = _nIns; - if (cpu_has_fpuhazard) - NOP(); - C_EQ_D(ra, rb); - break; - case LIR_ltd: - if (branchOnFalse) - BC1F(btarg); - else - BC1T(btarg); - patch = _nIns; - if (cpu_has_fpuhazard) - NOP(); - C_LT_D(ra, rb); - break; - case LIR_gtd: - if (branchOnFalse) - BC1F(btarg); - else - BC1T(btarg); - patch = _nIns; - if (cpu_has_fpuhazard) - NOP(); - C_LT_D(rb, ra); - break; - case LIR_led: - if (branchOnFalse) - BC1F(btarg); - else - BC1T(btarg); - patch = _nIns; - if (cpu_has_fpuhazard) - NOP(); - C_LE_D(ra, rb); - break; - case LIR_ged: - if (branchOnFalse) - BC1F(btarg); - else - BC1T(btarg); - patch = _nIns; - if (cpu_has_fpuhazard) - NOP(); - C_LE_D(rb, ra); - break; - default: - BADOPCODE(condop); - break; - } - } - else { - // general case - // s[lg]tu? $at,($ra,$rb|$rb,$ra) - // b(ne|eq)z $at,btarg - switch (condop) { - case LIR_eqi: - // special case - // b(ne|eq) $ra,$rb,btarg - if (branchOnFalse) - BNE(ra, rb, btarg); - else { - if (ra == rb) - B(btarg); - else - BEQ(ra, rb, btarg); - } - patch = _nIns; - break; - case LIR_lti: - if (branchOnFalse) - BEQ(AT, ZERO, btarg); - else - BNE(AT, ZERO, btarg); - patch = _nIns; - SLT(AT, ra, rb); - break; - case LIR_gti: - if (branchOnFalse) - BEQ(AT, ZERO, btarg); - else - BNE(AT, ZERO, btarg); - patch = _nIns; - SLT(AT, rb, ra); - break; - case LIR_lei: - if (branchOnFalse) - BNE(AT, ZERO, btarg); - else - BEQ(AT, ZERO, btarg); - patch = _nIns; - SLT(AT, rb, ra); - break; - case LIR_gei: - if (branchOnFalse) - BNE(AT, ZERO, btarg); - else - BEQ(AT, ZERO, btarg); - patch = _nIns; - SLT(AT, ra, rb); - break; - case LIR_ltui: - if (branchOnFalse) - BEQ(AT, ZERO, btarg); - else - BNE(AT, ZERO, btarg); - patch = _nIns; - SLTU(AT, ra, rb); - break; - case LIR_gtui: - if (branchOnFalse) - BEQ(AT, ZERO, btarg); - else - BNE(AT, ZERO, btarg); - patch = _nIns; - SLTU(AT, rb, ra); - break; - case LIR_leui: - if (branchOnFalse) - BNE(AT, ZERO, btarg); - else - BEQ(AT, ZERO, btarg); - patch = _nIns; - SLT(AT, rb, ra); - break; - case LIR_geui: - if (branchOnFalse) - BNE(AT, ZERO, btarg); - else - BEQ(AT, ZERO, btarg); - patch = _nIns; - SLTU(AT, ra, rb); - break; - default: - BADOPCODE(condop); - } - } - TAG("asm_bxx(branchOnFalse=%d, condop=%s, ra=%s rb=%s targ=%p)", - branchOnFalse, lirNames[condop], gpn(ra), gpn(rb), targ); - return patch; - } - - NIns* Assembler::asm_branch_ov(LOpcode op, NIns* target) - { - USE(op); - NanoAssert(target != NULL); - - NIns* patch = asm_bxx(true, LIR_eqi, AT, ZERO, target); - - TAG("asm_branch_ov(op=%s, target=%p)", lirNames[op], target); - return patch; - } - - Branches Assembler::asm_branch(bool branchOnFalse, LIns *cond, NIns * const targ) - { - NanoAssert(cond->isCmp()); - LOpcode condop = cond->opcode(); - RegisterMask allow = isCmpDOpcode(condop) ? FpRegs : GpRegs; - LIns *a = cond->oprnd1(); - LIns *b = cond->oprnd2(); - Register ra = findRegFor(a, allow); - Register rb = (b==a) ? ra : findRegFor(b, allow & ~rmask(ra)); - - return Branches(asm_bxx(branchOnFalse, condop, ra, rb, targ)); - } - - void Assembler::asm_j(NIns * const targ, bool bdelay) - { - if (targ == NULL) { - NanoAssert(bdelay); - (void) asm_bxx(false, LIR_eqi, ZERO, ZERO, targ); - } - else { - NanoAssert(SEG(targ) == SEG(_nIns)); - if (bdelay) { - underrunProtect(2*4); // j + delay - NOP(); - } - J(targ); - } - TAG("asm_j(targ=%p) bdelay=%d", targ); - } - - void - Assembler::asm_spill(Register rr, int d, bool quad) - { - USE(quad); - NanoAssert(d); - if (IsFpReg(rr)) { - NanoAssert(quad); - asm_ldst64(true, rr, d, FP); - } - else { - NanoAssert(!quad); - asm_ldst(OP_SW, rr, d, FP); - } - TAG("asm_spill(rr=%d, d=%d, quad=%d)", rr, d, quad); - } - - void - Assembler::asm_nongp_copy(Register dst, Register src) - { - NanoAssert ((rmask(dst) & FpRegs) && (rmask(src) & FpRegs)); - MOV_D(dst, src); - TAG("asm_nongp_copy(dst=%d src=%d)", dst, src); - } - - /* - * asm_arg will encode the specified argument according to the current ABI, and - * will update r and stkd as appropriate so that the next argument can be - * encoded. - * - * - doubles are 64-bit aligned. both in registers and on the stack. - * If the next available argument register is A1, it is skipped - * and the double is placed in A2:A3. If A0:A1 or A2:A3 are not - * available, the double is placed on the stack, 64-bit aligned. - * - 32-bit arguments are placed in registers and 32-bit aligned - * on the stack. - */ - void - Assembler::asm_arg(ArgType ty, LIns* arg, Register& r, Register& fr, int& stkd) - { - // The stack offset must always be at least aligned to 4 bytes. - NanoAssert((stkd & 3) == 0); - - if (ty == ARGTYPE_D) { - // This task is fairly complex and so is delegated to asm_arg_64. - asm_arg_64(arg, r, fr, stkd); - } else { - NanoAssert(ty == ARGTYPE_I || ty == ARGTYPE_UI); - if (stkd < 16) { - asm_regarg(ty, arg, r); - fr = Register(fr + 1); - r = Register(r + 1); - } - else - asm_stkarg(arg, stkd); - // The o32 ABI calling convention is that if the first arguments - // is not a double, subsequent double values are passed in integer registers - fr = r; - stkd += 4; - } - } - - void - Assembler::asm_call(LIns* ins) - { - if (!ins->isop(LIR_callv)) { - Register rr; - LOpcode op = ins->opcode(); - - switch (op) { - case LIR_calli: - rr = retRegs[0]; - break; - case LIR_calld: - NanoAssert(cpu_has_fpu); - rr = FV0; - break; - default: - BADOPCODE(op); - return; - } - - deprecated_prepResultReg(ins, rmask(rr)); - } - - // Do this after we've handled the call result, so we don't - // force the call result to be spilled unnecessarily. - evictScratchRegsExcept(0); - - const CallInfo* ci = ins->callInfo(); - ArgType argTypes[MAXARGS]; - uint32_t argc = ci->getArgTypes(argTypes); - bool indirect = ci->isIndirect(); - - // FIXME: Put one of the argument moves into the BDS slot - - underrunProtect(2*4); // jalr+delay - NOP(); - JALR(T9); - - if (!indirect) - // FIXME: If we can tell that we are calling non-PIC - // (ie JIT) code, we could call direct instead of using t9 - asm_li(T9, ci->_address); - else - // Indirect call: we assign the address arg to t9 - // which matches the o32 ABI for calling functions - asm_regarg(ARGTYPE_P, ins->arg(--argc), T9); - - // Encode the arguments, starting at A0 and with an empty argument stack. - Register r = A0, fr = FA0; - int stkd = 0; - - // Iterate through the argument list and encode each argument according to - // the ABI. - // Note that we loop through the arguments backwards as LIR specifies them - // in reverse order. - while(argc--) - asm_arg(argTypes[argc], ins->arg(argc), r, fr, stkd); - - if (stkd > max_out_args) - max_out_args = stkd; - TAG("asm_call(ins=%p{%s})", ins, lirNames[ins->opcode()]); - } - - Register - Assembler::nRegisterAllocFromSet(RegisterMask set) - { - Register i; - int n; - - // note, deliberate truncation of 64->32 bits - if (set & 0xffffffff) { - // gp reg - n = ffs(int(set)); - NanoAssert(n != 0); - i = Register(n - 1); - } - else { - // fp reg - NanoAssert(cpu_has_fpu); - n = ffs(int(set >> 32)); - NanoAssert(n != 0); - i = Register(32 + n - 1); - } - _allocator.free &= ~rmask(i); - TAG("nRegisterAllocFromSet(set=%016llx) => %s", set, gpn(i)); - return i; - } - - void - Assembler::nRegisterResetAll(RegAlloc& regs) - { - regs.clear(); - regs.free = GpRegs; - if (cpu_has_fpu) - regs.free |= FpRegs; - } - -#define signextend16(s) ((int32_t(s)<<16)>>16) - - void - Assembler::nPatchBranch(NIns* branch, NIns* target) - { - uint32_t op = (branch[0] >> 26) & 0x3f; - uint32_t bdoffset = target-(branch+1); - - if (op == OP_BEQ || op == OP_BNE || - ((branch[0] & 0xfffe0000) == ((OP_COP1 << 26) | (COP1_BC << 21)))) { - if (isS16(bdoffset)) { - // The branch is in range, so just replace the offset in the instruction - // The trampoline that was allocated is redundant and will remain unused - branch[0] = (branch[0] & 0xffff0000) | (bdoffset & 0xffff); - } - else { - // The branch is pointing to a trampoline. Find out where that is - NIns *tramp = branch + 1 + (signextend16(branch[0] & 0xffff)); - if (SEG(branch) == SEG(target)) { - *tramp = J_FORMAT(OP_J,JINDEX(target)); - } - else { - // Full 32-bit jump - // bxx tramp - // lui $at,(target>>16)>0xffff - // .. - // tramp: - // ori $at,target & 0xffff - // jr $at - // nop - branch[1] = U_FORMAT(OP_LUI,0,AT,hi(uint32_t(target))); - tramp[0] = U_FORMAT(OP_ADDIU,AT,AT,lo(uint32_t(target))); - tramp[1] = R_FORMAT(OP_SPECIAL,AT,0,0,0,SPECIAL_JR); - } - } - } - else if (op == OP_J) { - NanoAssert (SEG(branch) == SEG(target)); - branch[0] = J_FORMAT(OP_J,JINDEX(target)); - } - else - TODO(unknown_patch); - // TAG("nPatchBranch(branch=%p target=%p)", branch, target); - } - - void - Assembler::nFragExit(LIns *guard) - { - SideExit *exit = guard->record()->exit; - Fragment *frag = exit->target; - bool destKnown = (frag && frag->fragEntry); - - // Generate jump to epilogue and initialize lr. - - // If the guard already exists, use a simple jump. - if (destKnown) { - // j _fragEntry - // move $v0,$zero - underrunProtect(2 * 4); // j + branch delay - MOVE(V0, ZERO); - asm_j(frag->fragEntry, false); - } - else { - // Target doesn't exist. Jump to an epilogue for now. - // This can be patched later. - if (!_epilogue) - _epilogue = genEpilogue(); - GuardRecord *lr = guard->record(); - // FIXME: _epilogue may be in another segment - // lui $v0,%hi(lr) - // j _epilogue - // addiu $v0,%lo(lr) - underrunProtect(2 * 4); // j + branch delay - ADDIU(V0, V0, lo(int32_t(lr))); - asm_j(_epilogue, false); - LUI(V0, hi(int32_t(lr))); - lr->jmp = _nIns; - } - - // profiling for the exit - verbose_only( - if (_logc->lcbits & LC_FragProfile) { - // lui $fp,%hi(profCount) - // lw $at,%lo(profCount)(fp) - // addiu $at,1 - // sw $at,%lo(profCount)(fp) - uint32_t profCount = uint32_t(&guard->record()->profCount); - SW(AT, lo(profCount), FP); - ADDIU(AT, AT, 1); - LW(AT, lo(profCount), FP); - LUI(FP, hi(profCount)); - } - ) - - // Pop the stack frame. - MOVE(SP, FP); - - // return value is GuardRecord* - TAG("nFragExit(guard=%p{%s})", guard, lirNames[guard->opcode()]); - } - - void - Assembler::nInit() - { - nHints[LIR_calli] = rmask(V0); -#if NJ_SOFTFLOAT_SUPPORTED - nHints[LIR_hcalli] = rmask(V1); -#endif - nHints[LIR_calld] = rmask(FV0); - nHints[LIR_paramp] = PREFER_SPECIAL; - } - - void Assembler::nBeginAssembly() - { - max_out_args = 16; // Always reserve space for a0-a3 - } - - // Increment the 32-bit profiling counter at pCtr, without - // changing any registers. - verbose_only( - void Assembler::asm_inc_m32(uint32_t* /*pCtr*/) - { - // TODO: implement this - } - ) - - void - Assembler::nativePageReset(void) - { - _nSlot = 0; - _nExitSlot = 0; - TAG("nativePageReset()"); - } - - void - Assembler::nativePageSetup(void) - { - NanoAssert(!_inExit); - if (!_nIns) - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - - // constpool starts at bottom of page and moves up - // code starts at top of page and goes down, - - if (!_nSlot) - _nSlot = codeStart; - if (!_nExitSlot) - _nExitSlot = exitStart; - - TAG("nativePageSetup()"); - } - - - NIns* - Assembler::genPrologue(void) - { - /* - * Use a non standard fp because we don't know the final framesize until now - * addiu $sp,-FRAMESIZE - * sw $ra,RA_OFFSET($sp) - * sw $fp,FP_OFFSET($sp) - * move $fp,$sp - * addu $sp,-stackNeeded - */ - - uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.stackSlotsNeeded(); - uint32_t amt = alignUp(stackNeeded, NJ_ALIGN_STACK); - - if (amt) { - if (isS16(-amt)) - ADDIU(SP, SP, -amt); - else { - ADDU(SP, SP, AT); - asm_li(AT, -amt); - } - } - - NIns *patchEntry = _nIns; // FIXME: who uses this value and where should it point? - - MOVE(FP, SP); - SW(FP, FP_OFFSET, SP); - SW(RA, RA_OFFSET, SP); // No need to save for leaf functions - ADDIU(SP, SP, -FRAMESIZE); - - TAG("genPrologue()"); - - return patchEntry; - } - - NIns* - Assembler::genEpilogue(void) - { - /* - * move $sp,$fp - * lw $ra,RA_OFFSET($sp) - * lw $fp,FP_OFFSET($sp) - * j $ra - * addiu $sp,FRAMESIZE - */ - ADDIU(SP, SP, FRAMESIZE); - JR(RA); - LW(FP, FP_OFFSET, SP); - LW(RA, RA_OFFSET, SP); - MOVE(SP, FP); - - TAG("genEpilogue()"); - - return _nIns; - } - - RegisterMask - Assembler::nHint(LIns* ins) - { - NanoAssert(ins->isop(LIR_paramp)); - RegisterMask prefer = 0; - // FIXME: FLOAT parameters? - if (ins->paramKind() == 0) - if (ins->paramArg() < 4) - prefer = rmask(argRegs[ins->paramArg()]); - return prefer; - } - - void - Assembler::underrunProtect(int bytes) - { - NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - NanoAssert(_nSlot != 0); - uintptr_t top = uintptr_t(_nSlot); - uintptr_t pc = uintptr_t(_nIns); - if (pc - bytes < top) { - verbose_only(verbose_outputf(" %p:", _nIns);) - NIns* target = _nIns; - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - - _nSlot = codeStart; - - // _nSlot points to the first empty position in the new code block - // _nIns points just past the last empty position. - asm_j(target, true); - } - } - - void - Assembler::swapCodeChunks() { - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - if (!_nExitSlot) - _nExitSlot = exitStart; - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, _nSlot, _nExitSlot); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - void - Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported - } - -} - -#endif // FEATURE_NANOJIT && NANOJIT_MIPS diff --git a/deps/mozjs/js/src/nanojit/NativeMIPS.h b/deps/mozjs/js/src/nanojit/NativeMIPS.h deleted file mode 100644 index df158b82a96..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeMIPS.h +++ /dev/null @@ -1,715 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * MIPS Technologies Inc - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Chris Dearman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_NativeMIPS__ -#define __nanojit_NativeMIPS__ - -#include "../vprof/vprof.h" -#ifdef PERFM -#define DOPROF -#endif -#define count_instr() _nvprof("mips", 1) -#define count_mov() do { _nvprof("mips-mov", 1); count_instr(); } while (0) -#define count_jmp() do { _nvprof("mips-jmp", 1); count_instr(); } while (0) -#define count_prolog() do { _nvprof("mips-prolog", 1); count_instr(); } while (0) -#define count_alu() do { _nvprof("mips-alu", 1); count_instr(); } while (0) -#define count_misc() do { _nvprof("mips-misc", 1); count_instr(); } while (0) -#define count_fpu() do { _nvprof("mips-fpu", 1); count_instr(); } while (0) -#define count_br() do { _nvprof("mips-br", 1); count_instr(); } while (0) - -namespace nanojit -{ - // Req: NJ_MAX_STACK_ENTRY is number of instructions to hold in LIR stack -#if 0 - // FIXME: Inconsistent use in signed/unsigned expressions makes this generate errors - static const uint32_t NJ_MAX_STACK_ENTRY = 4096; -#else -#define NJ_MAX_STACK_ENTRY 4096 -#endif - static const int NJ_ALIGN_STACK = 8; - - typedef uint32_t NIns; // REQ: Instruction count - typedef uint64_t RegisterMask; // REQ: Large enough to hold LastRegNum-FirstRegNum bits -#define _rmask_(r) (1LL<<(r)) - - typedef uint32_t Register; // REQ: Register identifiers - // Register numbers for Native code generator - static const Register - ZERO = { 0 }, - AT = { 1 }, - V0 = { 2 }, - V1 = { 3 }, - A0 = { 4 }, - A1 = { 5 }, - A2 = { 6 }, - A3 = { 7 }, - - T0 = { 8 }, - T1 = { 9 }, - T2 = { 10 }, - T3 = { 11 }, - T4 = { 12 }, - T5 = { 13 }, - T6 = { 14 }, - T7 = { 15 }, - - S0 = { 16 }, - S1 = { 17 }, - S2 = { 18 }, - S3 = { 19 }, - S4 = { 20 }, - S5 = { 21 }, - S6 = { 22 }, - S7 = { 23 }, - - T8 = { 24 }, - T9 = { 25 }, - K0 = { 26 }, - K1 = { 27 }, - GP = { 28 }, - SP = { 29 }, - FP = { 30 }, - RA = { 31 }, - - F0 = { 32 }, - F1 = { 33 }, - F2 = { 34 }, - F3 = { 35 }, - F4 = { 36 }, - F5 = { 37 }, - F6 = { 38 }, - F7 = { 39 }, - - F8 = { 40 }, - F9 = { 41 }, - F10 = { 42 }, - F11 = { 43 }, - F12 = { 44 }, - F13 = { 45 }, - F14 = { 46 }, - F15 = { 47 }, - - F16 = { 48 }, - F17 = { 49 }, - F18 = { 50 }, - F19 = { 51 }, - F20 = { 52 }, - F21 = { 53 }, - F22 = { 54 }, - F23 = { 55 }, - - F24 = { 56 }, - F25 = { 57 }, - F26 = { 58 }, - F27 = { 59 }, - F28 = { 60 }, - F29 = { 61 }, - F30 = { 62 }, - F31 = { 63 }, - - // FP register aliases - FV0 = F0, - FV1 = F2, - FA0 = F12, - FA1 = F14, - FT0 = F4, - FT1 = F6, - FT2 = F8, - FT3 = F10, - FT4 = F16, - FT5 = F18, - FS0 = F20, - FS1 = F22, - FS2 = F24, - FS3 = F26, - FS4 = F28, - FS5 = F30, - - deprecated_UnknownReg = { 127 }; // XXX: remove eventually, see bug 538924 - - static const uint32_t FirstRegNum = ZERO; - static const uint32_t LastRegNum = F31; -} - -#define NJ_USE_UINT32_REGISTER 1 -#include "NativeCommon.h" - -namespace nanojit { - // REQ: register names - verbose_only(extern const char* regNames[];) - - // REQ: Bytes of icache to flush after Assembler::patch - const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); - - // REQ: largest value passed to underrunProtect - static const int LARGEST_UNDERRUN_PROT = 32; - - // REQ: Number of callee saved registers -#ifdef FPCALLEESAVED - static const int NumSavedRegs = 14; -#else - static const int NumSavedRegs = 8; -#endif - - // REQ: Callee saved registers - const RegisterMask SavedRegs = -#ifdef FPCALLEESAVED - _rmask_(FS0) | _rmask_(FS1) | _rmask_(FS2) | - _rmask_(FS3) | _rmask_(FS4) | _rmask_(FS5) | -#endif - _rmask_(S0) | _rmask_(S1) | _rmask_(S2) | _rmask_(S3) | - _rmask_(S4) | _rmask_(S5) | _rmask_(S6) | _rmask_(S7); - - // REQ: General purpose registers - static const RegisterMask GpRegs = - _rmask_(V0) | _rmask_(V1) | - _rmask_(A0) | _rmask_(A1) | _rmask_(A2) | _rmask_(A3) | - _rmask_(S0) | _rmask_(S1) | _rmask_(S2) | _rmask_(S3) | - _rmask_(S4) | _rmask_(S5) | _rmask_(S6) | _rmask_(S7) | - _rmask_(T0) | _rmask_(T1) | _rmask_(T2) | _rmask_(T3) | - _rmask_(T4) | _rmask_(T5) | _rmask_(T6) | _rmask_(T7) | - _rmask_(T8) | _rmask_(T9); - - // REQ: Floating point registers - static const RegisterMask FpRegs = -#ifdef FPCALLEESAVED - _rmask_(FS0) | _rmask_(FS1) | _rmask_(FS2) | - _rmask_(FS3) | _rmask_(FS4) | _rmask_(FS5) | -#endif - _rmask_(FV0) | _rmask_(FV1) | - _rmask_(FA0) | _rmask_(FA1) | - _rmask_(FT0) | _rmask_(FT1) | _rmask_(FT2) | - _rmask_(FT3) | _rmask_(FT4) | _rmask_(FT5); - - static const RegisterMask AllowableFlagRegs = GpRegs; // REQ: Registers that can hold flag results FIXME - - static inline bool IsFpReg(Register r) - { - return (_rmask_(r) & FpRegs) != 0; - } - - static inline bool IsGpReg(Register r) - { - return (_rmask_(r) & GpRegs) != 0; - } - -#define GPR(r) ((r)&31) -#define FPR(r) ((r)&31) - -// REQ: Platform specific declarations to include in Stats structure -#define DECLARE_PLATFORM_STATS() - -// REQ: Platform specific declarations to include in Assembler class -#define DECLARE_PLATFORM_ASSEMBLER() \ - const static Register argRegs[4]; \ - const static Register retRegs[2]; \ - void nativePageSetup(void); \ - void nativePageReset(void); \ - void underrunProtect(int bytes); \ - bool hardenNopInsertion(const Config& /*c*/) { return false; } \ - NIns *_nSlot; \ - NIns *_nExitSlot; \ - int max_out_args; \ - Register ovreg; \ - \ - void asm_ldst(int op, Register r, int offset, Register b); \ - void asm_ldst64(bool store, Register fr, int offset, Register b); \ - void asm_store_imm64(LIns *value, int dr, Register rbase); \ - void asm_li32(Register r, int32_t imm); \ - void asm_li_d(Register fr, int32_t msw, int32_t lsw); \ - void asm_li(Register r, int32_t imm); \ - void asm_j(NIns*, bool bdelay); \ - void asm_cmp(LOpcode condop, LIns *a, LIns *b, Register cr); \ - void asm_move(Register d, Register s); \ - void asm_regarg(ArgType ty, LIns* p, Register r); \ - void asm_stkarg(LIns* arg, int stkd); \ - void asm_arg(ArgType ty, LIns* arg, Register& r, Register& fr, int& stkd); \ - void asm_arg_64(LIns* arg, Register& r, Register& fr, int& stkd); \ - NIns *asm_branchtarget(NIns*); \ - NIns *asm_bxx(bool, LOpcode, Register, Register, NIns*); - -// REQ: Platform specific declarations to include in RegAlloc class -#define DECLARE_PLATFORM_REGALLOC() - -// REQ: -#define swapptrs() do { \ - NIns* _tins = _nIns; _nIns = _nExitIns; _nExitIns = _tins; \ - NIns* _nslot = _nSlot; _nSlot = _nExitSlot; _nExitSlot = _nslot; \ - } while (0) - -#define TODO(x) do { verbose_only(avmplus::AvmLog(#x);) NanoAssertMsgf(false, "%s", #x); } while (0) -#ifdef MIPSDEBUG -#define TAG(fmt, ...) do { debug_only(verbose_outputf(" # MIPS: " fmt, ##__VA_ARGS__);) } while (0) -#else -#define TAG(fmt, ...) do { } while (0) -#endif - -#define EMIT(ins, fmt, ...) do { \ - underrunProtect(4); \ - *(--_nIns) = (NIns) (ins); \ - debug_only(codegenBreak(_nIns);) \ - asm_output(fmt, ##__VA_ARGS__); \ - } while (0) - -// Emit code in trampoline/literal area -// Assume that underrunProtect has already been called -// This is a bit hacky... -#define TRAMP(ins, fmt, ...) do { \ - verbose_only( \ - NIns *save_nIns = _nIns; _nIns = _nSlot; \ - ) \ - *_nSlot = (NIns)ins; \ - debug_only(codegenBreak(_nSlot);) \ - _nSlot++; \ - verbose_only(setOutputForEOL("<= trampoline");) \ - asm_output(fmt, ##__VA_ARGS__); \ - verbose_only( \ - _nIns = save_nIns; \ - ) \ - } while (0) - -#define MR(d, s) asm_move(d, s) - -// underrun guarantees that there is always room to insert a jump and branch delay slot -#define JMP(t) asm_j(t, true) - -// Opcodes: bits 31..26 -#define OP_SPECIAL 0x00 -#define OP_REGIMM 0x01 -#define OP_J 0x02 -#define OP_JAL 0x03 -#define OP_BEQ 0x04 -#define OP_BNE 0x05 -#define OP_ADDIU 0x09 -#define OP_SLTIU 0x0b -#define OP_ANDI 0x0c -#define OP_ORI 0x0d -#define OP_XORI 0x0e -#define OP_LUI 0x0f -#define OP_COP1 0x11 -#define OP_COP1X 0x13 -#define OP_SPECIAL2 0x1c -#define OP_LB 0x20 -#define OP_LH 0x21 -#define OP_LW 0x23 -#define OP_LBU 0x24 -#define OP_LHU 0x25 -#define OP_SB 0x28 -#define OP_SH 0x29 -#define OP_SW 0x2b -#define OP_LWC1 0x31 -#define OP_LDC1 0x35 -#define OP_SWC1 0x39 -#define OP_SDC1 0x3d - -// REGIMM: bits 20..16 -#define REGIMM_BLTZ 0x00 -#define REGIMM_BGEZ 0x01 - -// COP1: bits 25..21 -#define COP1_ADD 0x00 -#define COP1_SUB 0x01 -#define COP1_MUL 0x02 -#define COP1_DIV 0x03 -#define COP1_MOV 0x06 -#define COP1_NEG 0x07 -#define COP1_BC 0x08 -#define COP1_TRUNCW 0x0d -#define COP1_CVTD 0x21 - -// COP1X: bits 5..0 -#define COP1X_LDXC1 0x01 -#define COP1X_SDXC1 0x09 - -// SPECIAL: bits 5..0 -#define SPECIAL_SLL 0x00 -#define SPECIAL_MOVCI 0x01 -#define SPECIAL_SRL 0x02 -#define SPECIAL_SRA 0x03 -#define SPECIAL_SLLV 0x04 -#define SPECIAL_SRLV 0x06 -#define SPECIAL_SRAV 0x07 -#define SPECIAL_JR 0x08 -#define SPECIAL_JALR 0x09 -#define SPECIAL_MOVN 0x0b -#define SPECIAL_MFHI 0x10 -#define SPECIAL_MFLO 0x12 -#define SPECIAL_MULT 0x18 -#define SPECIAL_ADDU 0x21 -#define SPECIAL_SUBU 0x23 -#define SPECIAL_AND 0x24 -#define SPECIAL_OR 0x25 -#define SPECIAL_XOR 0x26 -#define SPECIAL_NOR 0x27 -#define SPECIAL_SLT 0x2a -#define SPECIAL_SLTU 0x2b - -// SPECIAL2: bits 5..0 -#define SPECIAL2_MUL 0x02 - -// FORMAT: bits 25..21 -#define FMT_S 0x10 -#define FMT_D 0x11 -#define FMT_W 0x14 -#define FMT_L 0x15 -#define FMT_PS 0x16 - -// CONDITION: bits 4..0 -#define COND_F 0x0 -#define COND_UN 0x1 -#define COND_EQ 0x2 -#define COND_UEQ 0x3 -#define COND_OLT 0x4 -#define COND_ULT 0x5 -#define COND_OLE 0x6 -#define COND_ULE 0x7 -#define COND_SF 0x8 -#define COND_NGLE 0x9 -#define COND_SEQ 0xa -#define COND_NGL 0xb -#define COND_LT 0xc -#define COND_NGE 0xd -#define COND_LE 0xe -#define COND_NGT 0xf - -// Helper definitions to encode different classes of MIPS instructions -// Parameters are in instruction order - -#define R_FORMAT(op, rs, rt, rd, re, func) \ - (((op)<<26)|(GPR(rs)<<21)|(GPR(rt)<<16)|(GPR(rd)<<11)|((re)<<6)|(func)) - -#define I_FORMAT(op, rs, rt, simm) \ - (((op)<<26)|(GPR(rs)<<21)|(GPR(rt)<<16)|((simm)&0xffff)) - -#define J_FORMAT(op, index) \ - (((op)<<26)|(index)) - -#define U_FORMAT(op, rs, rt, uimm) \ - (((op)<<26)|(GPR(rs)<<21)|(GPR(rt)<<16)|((uimm)&0xffff)) - -#define F_FORMAT(op, ffmt, ft, fs, fd, func) \ - (((op)<<26)|((ffmt)<<21)|(FPR(ft)<<16)|(FPR(fs)<<11)|(FPR(fd)<<6)|(func)) - -#define oname(op) Assembler::oname[op] -#define cname(cond) Assembler::cname[cond] -#define fname(ffmt) Assembler::fname[ffmt] -#define fpn(fr) gpn(fr) - -#define BOFFSET(targ) (uint32_t(targ - (_nIns+1))) - -#define LDST(op, rt, offset, base) \ - do { count_misc(); EMIT(I_FORMAT(op, base, rt, offset), \ - "%s %s, %d(%s)", oname[op], gpn(rt), offset, gpn(base)); } while (0) - -#define BX(op, rs, rt, targ) \ - do { count_br(); EMIT(I_FORMAT(op, rs, rt, BOFFSET(targ)), \ - "%s %s, %s, %p", oname[op], gpn(rt), gpn(rs), targ); } while (0) - -// MIPS instructions -// Parameters are in "assembler" order -#define ADDIU(rt, rs, simm) \ - do { count_alu(); EMIT(I_FORMAT(OP_ADDIU, rs, rt, simm), \ - "addiu %s, %s, %d", gpn(rt), gpn(rs), simm); } while (0) - -#define trampADDIU(rt, rs, simm) \ - do { count_alu(); TRAMP(I_FORMAT(OP_ADDIU, rs, rt, simm), \ - "addiu %s, %s, %d", gpn(rt), gpn(rs), simm); } while (0) - -#define ADDU(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_ADDU), \ - "addu %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define AND(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_AND), \ - "and %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define ANDI(rt, rs, uimm) \ - do { count_alu(); EMIT(U_FORMAT(OP_ANDI, rs, rt, uimm), \ - "andi %s, %s, 0x%x", gpn(rt), gpn(rs), ((uimm)&0xffff)); } while (0) - -#define BC1F(targ) \ - do { count_br(); EMIT(I_FORMAT(OP_COP1, COP1_BC, 0, BOFFSET(targ)), \ - "bc1f %p", targ); } while (0) - -#define BC1T(targ) \ - do { count_br(); EMIT(I_FORMAT(OP_COP1, COP1_BC, 1, BOFFSET(targ)), \ - "bc1t %p", targ); } while (0) - -#define B(targ) BX(OP_BEQ, ZERO, ZERO, targ) -#define BEQ(rs, rt, targ) BX(OP_BEQ, rs, rt, targ) -#define BNE(rs, rt, targ) BX(OP_BNE, rs, rt, targ) -#define BLEZ(rs, targ) BX(OP_BLEZ, rs, ZERO, targ) -#define BGTZ(rs, targ) BX(OP_BGTZ, rs, ZERO, targ) -#define BGEZ(rs, targ) BX(OP_REGIMM, rs, REGIMM_BGEZ, targ) -#define BLTZ(rs, targ) BX(OP_REGIMM, rs, REGIMM_BLTZ, targ) - -#define JINDEX(dest) ((uint32_t(dest)>>2)&0x03ffffff) - -#define J(dest) \ - do { count_jmp(); EMIT(J_FORMAT(OP_J, JINDEX(dest)), \ - "j %p", dest); } while (0) - -#define trampJ(dest) \ - do { count_jmp(); TRAMP(J_FORMAT(OP_J, JINDEX(dest)), \ - "j %p", dest); } while (0) - -#define JAL(dest) \ - do { count_jmp(); EMIT(J_FORMAT(OP_JAL, JINDEX(dest)), \ - "jal %p", dest); } while (0) - -#define JALR(rs) \ - do { count_jmp(); EMIT(R_FORMAT(OP_SPECIAL, rs, 0, RA, 0, SPECIAL_JALR), \ - "jalr %s", gpn(rs)); } while (0) - -#define JR(rs) \ - do { count_jmp(); EMIT(R_FORMAT(OP_SPECIAL, rs, 0, 0, 0, SPECIAL_JR), \ - "jr %s", gpn(rs)); } while (0) -#define trampJR(rs) \ - do { count_jmp(); TRAMP(R_FORMAT(OP_SPECIAL, rs, 0, 0, 0, SPECIAL_JR), \ - "jr %s", gpn(rs)); } while (0) - -#define LB(rt, offset, base) \ - LDST(OP_LB, rt, offset, base) - -#define LH(rt, offset, base) \ - LDST(OP_LH, rt, offset, base) - -#define LUI(rt, uimm) \ - do { count_alu(); EMIT(U_FORMAT(OP_LUI, 0, rt, uimm), \ - "lui %s, 0x%x", gpn(rt), ((uimm)&0xffff)); } while (0) - -#define LW(rt, offset, base) \ - LDST(OP_LW, rt, offset, base) - -#define MFHI(rd) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, 0, 0, rd, 0, SPECIAL_MFHI), \ - "mfhi %s", gpn(rd)); } while (0) - -#define MFLO(rd) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, 0, 0, rd, 0, SPECIAL_MFLO), \ - "mflo %s", gpn(rd)); } while (0) - -#define MUL(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL2, rs, rt, rd, 0, SPECIAL2_MUL), \ - "mul %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define MULT(rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, 0, 0, SPECIAL_MULT), \ - "mult %s, %s", gpn(rs), gpn(rt)); } while (0) - -#define MOVE(rd, rs) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, ZERO, rd, 0, SPECIAL_ADDU), \ - "move %s, %s", gpn(rd), gpn(rs)); } while (0) - -#define MOVN(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_MOVN), \ - "movn %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define NEGU(rd, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, ZERO, rt, rd, 0, SPECIAL_SUBU), \ - "negu %s, %s", gpn(rd), gpn(rt)); } while (0) - -#define NOP() \ - do { count_misc(); EMIT(R_FORMAT(OP_SPECIAL, 0, 0, 0, 0, SPECIAL_SLL), \ - "nop"); } while (0) - -#define trampNOP() \ - do { count_misc(); TRAMP(R_FORMAT(OP_SPECIAL, 0, 0, 0, 0, SPECIAL_SLL), \ - "nop"); } while (0) - -#define NOR(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_NOR), \ - "nor %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define NOT(rd, rs) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, ZERO, rd, 0, SPECIAL_NOR), \ - "not %s, %s", gpn(rd), gpn(rs)); } while (0) - -#define OR(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_OR), \ - "or %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define ORI(rt, rs, uimm) \ - do { count_alu(); EMIT(U_FORMAT(OP_ORI, rs, rt, uimm), \ - "ori %s, %s, 0x%x", gpn(rt), gpn(rs), ((uimm)&0xffff)); } while (0) - -#define SLTIU(rt, rs, simm) \ - do { count_alu(); EMIT(I_FORMAT(OP_SLTIU, rs, rt, simm), \ - "sltiu %s, %s, %d", gpn(rt), gpn(rs), simm); } while (0) - -#define SLT(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SLT), \ - "slt %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define SLTU(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SLTU), \ - "sltu %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define SLL(rd, rt, sa) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, 0, rt, rd, sa, SPECIAL_SLL), \ - "sll %s, %s, %d", gpn(rd), gpn(rt), sa); } while (0) - -#define SLLV(rd, rt, rs) \ - do { count_misc(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SLLV), \ - "sllv %s, %s, %s", gpn(rd), gpn(rt), gpn(rs)); } while (0) - -#define SRA(rd, rt, sa) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, 0, rt, rd, sa, SPECIAL_SRA), \ - "sra %s, %s, %d", gpn(rd), gpn(rt), sa); } while (0) - -#define SRAV(rd, rt, rs) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SRAV), \ - "srav %s, %s, %s", gpn(rd), gpn(rt), gpn(rs)); } while (0) - -#define SRL(rd, rt, sa) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, 0, rt, rd, sa, SPECIAL_SRL), \ - "srl %s, %s, %d", gpn(rd), gpn(rt), sa); } while (0) - -#define SRLV(rd, rt, rs) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SRLV), \ - "srlv %s, %s, %s", gpn(rd), gpn(rt), gpn(rs)); } while (0) - -#define SUBU(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_SUBU), \ - "subu %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define SW(rt, offset, base) \ - LDST(OP_SW, rt, offset, base) - -#define XOR(rd, rs, rt) \ - do { count_alu(); EMIT(R_FORMAT(OP_SPECIAL, rs, rt, rd, 0, SPECIAL_XOR), \ - "xor %s, %s, %s", gpn(rd), gpn(rs), gpn(rt)); } while (0) - -#define XORI(rt, rs, uimm) \ - do { count_alu(); EMIT(U_FORMAT(OP_XORI, rs, rt, uimm), \ - "xori %s, %s, 0x%x", gpn(rt), gpn(rs), ((uimm)&0xffff)); } while (0) - - -/* FPU instructions */ -#ifdef NJ_SOFTFLOAT_SUPPORTED - -#if !defined(__mips_soft_float) || __mips_soft_float != 1 -#error NJ_SOFTFLOAT_SUPPORTED defined but not compiled with -msoft-float -#endif - -#define LWC1(ft, offset, base) NanoAssertMsg(0, "softfloat LWC1") -#define SWC1(ft, offset, base) NanoAssertMsg(0, "softfloat SWC1") -#define LDC1(ft, offset, base) NanoAssertMsg(0, "softfloat LDC1") -#define SDC1(ft, offset, base) NanoAssertMsg(0, "softfloat SDC1") -#define LDXC1(fd, index, base) NanoAssertMsg(0, "softfloat LDXC1") -#define SDXC1(fs, index, base) NanoAssertMsg(0, "softfloat SDXC1") - -#define MFC1(rt, fs) NanoAssertMsg(0, "softfloat MFC1") -#define MTC1(rt, fs) NanoAssertMsg(0, "softfloat MTC1") -#define MOVF(rt, fs, cc) NanoAssertMsg(0, "softfloat MOVF") -#define CVT_D_W(fd, fs) NanoAssertMsg(0, "softfloat CVT_D_W") -#define C_EQ_D(fs, ft) NanoAssertMsg(0, "softfloat C_EQ_D") -#define C_LE_D(fs, ft) NanoAssertMsg(0, "softfloat C_LE_D") -#define C_LT_D(fs, ft) NanoAssertMsg(0, "softfloat C_LT_D") -#define ADD_D(fd, fs, ft) NanoAssertMsg(0, "softfloat ADD_D") -#define DIV_D(fd, fs, ft) NanoAssertMsg(0, "softfloat DIV_D") -#define MOV_D(fd, fs) NanoAssertMsg(0, "softfloat MOV_D") -#define MUL_D(fd, fs, ft) NanoAssertMsg(0, "softfloat MUL_D") -#define NEG_D(fd, fs) NanoAssertMsg(0, "softfloat NEG_D") -#define SUB_D(fd, fs, ft) NanoAssertMsg(0, "softfloat SUB_D") -#define TRUNC_W_D(fd,fs) NanoAssertMsg(0, "softfloat TRUNC_W_D") - -#else - -#if defined(__mips_soft_float) && __mips_soft_float != 0 -#error compiled with -msoft-float but NJ_SOFTFLOAT_SUPPORTED not defined -#endif - -#define FOP_FMT2(ffmt, fd, fs, func, name) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, ffmt, 0, fs, fd, func), \ - "%s.%s %s, %s", name, fname[ffmt], fpn(fd), fpn(fs)); } while (0) - -#define FOP_FMT3(ffmt, fd, fs, ft, func, name) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, ffmt, ft, fs, fd, func), \ - "%s.%s %s, %s, %s", name, fname[ffmt], fpn(fd), fpn(fs), fpn(ft)); } while (0) - -#define C_COND_FMT(cond, ffmt, fs, ft) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, ffmt, ft, fs, 0, 0x30|(cond)), \ - "c.%s.%s %s, %s", cname[cond], fname[ffmt], fpn(fs), fpn(ft)); } while (0) - -#define MFC1(rt, fs) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, 0, rt, fs, 0, 0), \ - "mfc1 %s, %s", gpn(rt), fpn(fs)); } while (0) - -#define MTC1(rt, fs) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, 4, rt, fs, 0, 0), \ - "mtc1 %s, %s", gpn(rt), fpn(fs)); } while (0) - -#define MOVF(rd, rs, cc) \ - do { count_fpu(); EMIT(R_FORMAT(OP_SPECIAL, rs, (cc)<<2, rd, 0, SPECIAL_MOVCI), \ - "movf %s, %s, $fcc%d", gpn(rd), gpn(rs), cc); } while (0) - -#define CVT_D_W(fd, fs) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, FMT_W, 0, fs, fd, COP1_CVTD), \ - "cvt.d.w %s, %s", fpn(fd), fpn(fs)); } while (0) - -#define TRUNC_W_D(fd, fs) \ - do { count_fpu(); EMIT(F_FORMAT(OP_COP1, FMT_D, 0, fs, fd, COP1_TRUNCW), \ - "trunc.w.d %s, %s", fpn(fd), fpn(fs)); } while (0) - - -#define LWC1(ft, offset, base) LDST(OP_LWC1, ft, offset, base) -#define SWC1(ft, offset, base) LDST(OP_SWC1, ft, offset, base) -#define LDC1(ft, offset, base) LDST(OP_LDC1, ft, offset, base) -#define SDC1(ft, offset, base) LDST(OP_SDC1, ft, offset, base) -#define LDXC1(fd, index, base) \ - do { count_fpu(); EMIT(R_FORMAT(OP_COP1X, base, index, 0, fd, COP1X_LDXC1), \ - "ldxc1 %s, %s(%s)", fpn(fd), gpn(index), gpn(base)); } while (0) -#define SDXC1(fs, index, base) \ - do { count_fpu(); EMIT(R_FORMAT(OP_COP1X, base, index, fs, 0, COP1X_SDXC1), \ - "sdxc1 %s, %s(%s)", fpn(fs), gpn(index), gpn(base)); } while (0) - -#define C_EQ_D(fs, ft) C_COND_FMT(COND_EQ, FMT_D, fs, ft) -#define C_LE_D(fs, ft) C_COND_FMT(COND_LE, FMT_D, fs, ft) -#define C_LT_D(fs, ft) C_COND_FMT(COND_LT, FMT_D, fs, ft) -#define ADD_D(fd, fs, ft) FOP_FMT3(FMT_D, fd, fs, ft, COP1_ADD, "add") -#define DIV_D(fd, fs, ft) FOP_FMT3(FMT_D, fd, fs, ft, COP1_DIV, "div") -#define MOV_D(fd, fs) FOP_FMT2(FMT_D, fd, fs, COP1_MOV, "mov") -#define MUL_D(fd, fs, ft) FOP_FMT3(FMT_D, fd, fs, ft, COP1_MUL, "mul") -#define NEG_D(fd, fs) FOP_FMT2(FMT_D, fd, fs, COP1_NEG, "neg") -#define SUB_D(fd, fs, ft) FOP_FMT3(FMT_D, fd, fs, ft, COP1_SUB, "sub") -#endif - -} -#endif // __nanojit_NativeMIPS__ diff --git a/deps/mozjs/js/src/nanojit/NativePPC.cpp b/deps/mozjs/js/src/nanojit/NativePPC.cpp deleted file mode 100644 index 5bf8b38f233..00000000000 --- a/deps/mozjs/js/src/nanojit/NativePPC.cpp +++ /dev/null @@ -1,1459 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#if defined FEATURE_NANOJIT && defined NANOJIT_PPC - -namespace nanojit -{ - const Register Assembler::retRegs[] = { R3, R4 }; // high=R3, low=R4 - const Register Assembler::argRegs[] = { R3, R4, R5, R6, R7, R8, R9, R10 }; - - const Register Assembler::savedRegs[] = { - #if !defined NANOJIT_64BIT - R13, - #endif - R14, R15, R16, R17, R18, R19, R20, R21, R22, - R23, R24, R25, R26, R27, R28, R29, R30 - }; - - const char *regNames[] = { - "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", - "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", - "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", - "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", - "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" - }; - - const char *bitNames[] = { "lt", "gt", "eq", "so" }; - - #define TODO(x) do{ avmplus::AvmLog(#x); NanoAssertMsgf(false, "%s", #x); } while(0) - - /* - * see http://developer.apple.com/documentation/developertools/Conceptual/LowLevelABI/index.html - * stack layout (higher address going down) - * sp -> out linkage area - * out parameter area - * local variables - * saved registers - * sp' -> in linkage area - * in parameter area - * - * linkage area layout: - * PPC32 PPC64 - * sp+0 sp+0 saved sp - * sp+4 sp+8 saved cr - * sp+8 sp+16 saved lr - * sp+12 sp+24 reserved - */ - - const int min_param_area_size = 8*sizeof(void*); // r3-r10 - const int linkage_size = 6*sizeof(void*); - const int lr_offset = 2*sizeof(void*); // linkage.lr - const int cr_offset = 1*sizeof(void*); // linkage.cr - - NIns* Assembler::genPrologue() { - // mflr r0 - // stw r0, lr_offset(sp) - // stwu sp, -framesize(sp) - - // param_area must be at least large enough for r3-r10 to be saved, - // regardless of whether we think the callee needs less: e.g., the callee - // might tail-call to a function that uses varargs, which could flush - // r3-r10 to the parameter area. - uint32_t param_area = (max_param_size > min_param_area_size) ? max_param_size : min_param_area_size; - // activation frame is 4 bytes per entry even on 64bit machines - uint32_t stackNeeded = param_area + linkage_size + _activation.stackSlotsNeeded() * 4; - uint32_t aligned = alignUp(stackNeeded, NJ_ALIGN_STACK); - - UNLESS_PEDANTIC( if (isS16(aligned)) { - STPU(SP, -aligned, SP); // *(sp-aligned) = sp; sp -= aligned - } else ) { - STPUX(SP, SP, R0); - asm_li(R0, -aligned); - } - - NIns *patchEntry = _nIns; - MR(FP,SP); // save SP to use as a FP - STP(FP, cr_offset, SP); // cheat and save our FP in linkage.cr - STP(R0, lr_offset, SP); // save LR in linkage.lr - MFLR(R0); - - return patchEntry; - } - - NIns* Assembler::genEpilogue() { - BLR(); - MTLR(R0); - LP(R0, lr_offset, SP); - LP(FP, cr_offset, SP); // restore FP from linkage.cr - MR(SP,FP); - return _nIns; - } - - void Assembler::asm_load32(LIns *ins) { - LIns* base = ins->oprnd1(); - int d = ins->disp(); - Register rr = deprecated_prepResultReg(ins, GpRegs); - Register ra = getBaseReg(base, d, GpRegs); - - switch(ins->opcode()) { - case LIR_lduc2ui: - if (isS16(d)) { - LBZ(rr, d, ra); - } else { - LBZX(rr, ra, R0); // rr = [ra+R0] - asm_li(R0,d); - } - return; - case LIR_ldus2ui: - // these are expected to be 2 or 4-byte aligned - if (isS16(d)) { - LHZ(rr, d, ra); - } else { - LHZX(rr, ra, R0); // rr = [ra+R0] - asm_li(R0,d); - } - return; - case LIR_ldi: - // these are expected to be 4-byte aligned - if (isS16(d)) { - LWZ(rr, d, ra); - } else { - LWZX(rr, ra, R0); // rr = [ra+R0] - asm_li(R0,d); - } - return; - case LIR_ldc2i: - case LIR_lds2i: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - return; - } - } - - void Assembler::asm_store32(LOpcode op, LIns *value, int32_t dr, LIns *base) { - - switch (op) { - case LIR_sti: - case LIR_sti2c: - // handled by mainline code below for now - break; - case LIR_sti2s: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - return; - } - - Register rs = findRegFor(value, GpRegs); - Register ra = value == base ? rs : getBaseReg(base, dr, GpRegs & ~rmask(rs)); - - #if !PEDANTIC - if (isS16(dr)) { - switch (op) { - case LIR_sti: - STW(rs, dr, ra); - break; - case LIR_sti2c: - STB(rs, dr, ra); - break; - } - return; - } - #endif - - // general case store, any offset size - switch (op) { - case LIR_sti: - STWX(rs, ra, R0); - break; - case LIR_sti2c: - STBX(rs, ra, R0); - break; - } - asm_li(R0, dr); - } - - void Assembler::asm_load64(LIns *ins) { - - switch (ins->opcode()) { - case LIR_ldd: - CASE64(LIR_ldq:) - // handled by mainline code below for now - break; - case LIR_ldf2d: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); - return; - } - - LIns* base = ins->oprnd1(); - #ifdef NANOJIT_64BIT - Register rr = ins->deprecated_getReg(); - if (deprecated_isKnownReg(rr) && (rmask(rr) & FpRegs)) { - // FPR already assigned, fine, use it - deprecated_freeRsrcOf(ins); - } else { - // use a GPR register; its okay to copy doubles with GPR's - // but *not* okay to copy non-doubles with FPR's - rr = deprecated_prepResultReg(ins, GpRegs); - } - #else - Register rr = deprecated_prepResultReg(ins, FpRegs); - #endif - - int dr = ins->disp(); - Register ra = getBaseReg(base, dr, GpRegs); - - #ifdef NANOJIT_64BIT - if (rmask(rr) & GpRegs) { - #if !PEDANTIC - if (isS16(dr)) { - LD(rr, dr, ra); - return; - } - #endif - // general case 64bit GPR load - LDX(rr, ra, R0); - asm_li(R0, dr); - return; - } - #endif - - // FPR - #if !PEDANTIC - if (isS16(dr)) { - LFD(rr, dr, ra); - return; - } - #endif - - // general case FPR load - LFDX(rr, ra, R0); - asm_li(R0, dr); - } - - void Assembler::asm_li(Register r, int32_t imm) { - #if !PEDANTIC - if (isS16(imm)) { - LI(r, imm); - return; - } - if ((imm & 0xffff) == 0) { - imm = uint32_t(imm) >> 16; - LIS(r, imm); - return; - } - #endif - asm_li32(r, imm); - } - - void Assembler::asm_li32(Register r, int32_t imm) { - // general case - // TODO use ADDI instead of ORI if r != r0, impl might have 3way adder - ORI(r, r, imm); - LIS(r, imm>>16); // on ppc64, this sign extends - } - - void Assembler::asm_li64(Register r, uint64_t imm) { - underrunProtect(5*sizeof(NIns)); // must be contiguous to be patchable - ORI(r,r,uint16_t(imm)); // r[0:15] = imm[0:15] - ORIS(r,r,uint16_t(imm>>16)); // r[16:31] = imm[16:31] - SLDI(r,r,32); // r[32:63] = r[0:31], r[0:31] = 0 - asm_li32(r, int32_t(imm>>32)); // r[0:31] = imm[32:63] - } - - void Assembler::asm_store64(LOpcode op, LIns *value, int32_t dr, LIns *base) { - NanoAssert(value->isQorD()); - - switch (op) { - case LIR_std: - CASE64(LIR_stq:) - // handled by mainline code below for now - break; - case LIR_std2f: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); - return; - } - - Register ra = getBaseReg(base, dr, GpRegs); - - // general case for any value - #if !defined NANOJIT_64BIT - // on 32bit cpu's, we only use store64 for doubles - Register rs = findRegFor(value, FpRegs); - #else - // if we have to choose a register, use a GPR - Register rs = ( !value->isInReg() - ? findRegFor(value, GpRegs & ~rmask(ra)) - : value->deprecated_getReg() ); - - if (rmask(rs) & GpRegs) { - #if !PEDANTIC - if (isS16(dr)) { - // short offset - STD(rs, dr, ra); - return; - } - #endif - // general case store 64bit GPR - STDX(rs, ra, R0); - asm_li(R0, dr); - return; - } - #endif // NANOJIT_64BIT - - #if !PEDANTIC - if (isS16(dr)) { - // short offset - STFD(rs, dr, ra); - return; - } - #endif - - // general case for any offset - STFDX(rs, ra, R0); - asm_li(R0, dr); - } - - void Assembler::asm_cond(LIns *ins) { - LOpcode op = ins->opcode(); - LIns *a = ins->oprnd1(); - LIns *b = ins->oprnd2(); - ConditionRegister cr = CR7; - Register r = deprecated_prepResultReg(ins, GpRegs); - switch (op) { - case LIR_eqi: case LIR_eqd: - CASE64(LIR_eqq:) - EXTRWI(r, r, 1, 4*cr+COND_eq); // extract CR7.eq - MFCR(r); - break; - case LIR_lti: case LIR_ltui: - case LIR_ltd: case LIR_led: - CASE64(LIR_ltq:) CASE64(LIR_ltuq:) - EXTRWI(r, r, 1, 4*cr+COND_lt); // extract CR7.lt - MFCR(r); - break; - case LIR_gti: case LIR_gtui: - case LIR_gtd: case LIR_ged: - CASE64(LIR_gtq:) CASE64(LIR_gtuq:) - EXTRWI(r, r, 1, 4*cr+COND_gt); // extract CR7.gt - MFCR(r); - break; - case LIR_lei: case LIR_leui: - CASE64(LIR_leq:) CASE64(LIR_leuq:) - EXTRWI(r, r, 1, 4*cr+COND_eq); // extract CR7.eq - MFCR(r); - CROR(CR7, eq, lt, eq); - break; - case LIR_gei: case LIR_geui: - CASE64(LIR_geq:) CASE64(LIR_geuq:) - EXTRWI(r, r, 1, 4*cr+COND_eq); // select CR7.eq - MFCR(r); - CROR(CR7, eq, gt, eq); - break; - default: - debug_only(outputf("%s",lirNames[ins->opcode()]);) - TODO(asm_cond); - break; - } - asm_cmp(op, a, b, cr); - } - - void Assembler::asm_condd(LIns *ins) { - asm_cond(ins); - } - - // cause sign extension to test bits. ptrdiff_t is a signed, - // pointer-sized int - static inline bool isS14(ptrdiff_t d) { - const int shift = sizeof(ptrdiff_t) * 8 - 14; // 18 or 50 - return ((d << shift) >> shift) == d; - } - - Branches Assembler::asm_branch(bool onfalse, LIns *cond, NIns * const targ) { - LOpcode condop = cond->opcode(); - NanoAssert(cond->isCmp()); - - // powerpc offsets are based on the address of the branch instruction - NIns *patch; - #if !PEDANTIC - ptrdiff_t bd = targ - (_nIns-1); - if (targ && isS24(bd)) - patch = asm_branch_near(onfalse, cond, targ); - else - #endif - patch = asm_branch_far(onfalse, cond, targ); - asm_cmp(condop, cond->oprnd1(), cond->oprnd2(), CR7); - return Branches(patch); - } - - NIns* Assembler::asm_branch_near(bool onfalse, LIns *cond, NIns * const targ) { - NanoAssert(targ != 0); - underrunProtect(4); - ptrdiff_t bd = targ - (_nIns-1); - NIns *patch = 0; - if (!isS14(bd)) { - underrunProtect(8); - bd = targ - (_nIns-1); - if (isS24(bd)) { - // can't fit conditional branch offset into 14 bits, but - // we can fit in 24, so invert the condition and branch - // around an unconditional jump - verbose_only(verbose_outputf("%p:", _nIns);) - NIns *skip = _nIns; - B(bd); - patch = _nIns; // this is the patchable branch to the given target - onfalse = !onfalse; - bd = skip - (_nIns-1); - NanoAssert(isS14(bd)); - verbose_only(verbose_outputf("branch24");) - } - else { - // known far target - return asm_branch_far(onfalse, cond, targ); - } - } - ConditionRegister cr = CR7; - switch (cond->opcode()) { - case LIR_eqi: - case LIR_eqd: - CASE64(LIR_eqq:) - if (onfalse) BNE(cr,bd); else BEQ(cr,bd); - break; - case LIR_lti: case LIR_ltui: - case LIR_ltd: case LIR_led: - CASE64(LIR_ltq:) CASE64(LIR_ltuq:) - if (onfalse) BNL(cr,bd); else BLT(cr,bd); - break; - case LIR_lei: case LIR_leui: - CASE64(LIR_leq:) CASE64(LIR_leuq:) - if (onfalse) BGT(cr,bd); else BLE(cr,bd); - break; - case LIR_gti: case LIR_gtui: - case LIR_gtd: case LIR_ged: - CASE64(LIR_gtq:) CASE64(LIR_gtuq:) - if (onfalse) BNG(cr,bd); else BGT(cr,bd); - break; - case LIR_gei: case LIR_geui: - CASE64(LIR_geq:) CASE64(LIR_geuq:) - if (onfalse) BLT(cr,bd); else BGE(cr,bd); - break; - default: - debug_only(outputf("%s",lirNames[cond->opcode()]);) - TODO(unknown_cond); - } - if (!patch) - patch = _nIns; - return patch; - } - - // general case branch to any address (using CTR) - NIns *Assembler::asm_branch_far(bool onfalse, LIns *cond, NIns * const targ) { - LOpcode condop = cond->opcode(); - ConditionRegister cr = CR7; - underrunProtect(16); - switch (condop) { - case LIR_eqi: - case LIR_eqd: - CASE64(LIR_eqq:) - if (onfalse) BNECTR(cr); else BEQCTR(cr); - break; - case LIR_lti: case LIR_ltui: - CASE64(LIR_ltq:) CASE64(LIR_ltuq:) - case LIR_ltd: case LIR_led: - if (onfalse) BNLCTR(cr); else BLTCTR(cr); - break; - case LIR_lei: case LIR_leui: - CASE64(LIR_leq:) CASE64(LIR_leuq:) - if (onfalse) BGTCTR(cr); else BLECTR(cr); - break; - case LIR_gti: case LIR_gtui: - CASE64(LIR_gtq:) CASE64(LIR_gtuq:) - case LIR_gtd: case LIR_ged: - if (onfalse) BNGCTR(cr); else BGTCTR(cr); - break; - case LIR_gei: case LIR_geui: - CASE64(LIR_geq:) CASE64(LIR_geuq:) - if (onfalse) BLTCTR(cr); else BGECTR(cr); - break; - default: - debug_only(outputf("%s",lirNames[condop]);) - TODO(unknown_cond); - } - - #if !defined NANOJIT_64BIT - MTCTR(R0); - asm_li32(R0, (int)targ); - #else - MTCTR(R0); - if (!targ || !isU32(uintptr_t(targ))) { - asm_li64(R0, uint64_t(targ)); - } else { - asm_li32(R0, uint32_t(uintptr_t(targ))); - } - #endif - return _nIns; - } - - NIns* Assembler::asm_branch_ov(LOpcode, NIns*) { - TODO(asm_branch_ov); - return _nIns; - } - - void Assembler::asm_cmp(LOpcode condop, LIns *a, LIns *b, ConditionRegister cr) { - RegisterMask allow = isCmpDOpcode(condop) ? FpRegs : GpRegs; - Register ra = findRegFor(a, allow); - - #if !PEDANTIC - if (b->isImmI()) { - int32_t d = b->immI(); - if (isS16(d)) { - if (isCmpSIOpcode(condop)) { - CMPWI(cr, ra, d); - return; - } - #if defined NANOJIT_64BIT - if (isCmpSQOpcode(condop)) { - CMPDI(cr, ra, d); - TODO(cmpdi); - return; - } - #endif - } - if (isU16(d)) { - if (isCmpUIOpcode(condop)) { - CMPLWI(cr, ra, d); - return; - } - #if defined NANOJIT_64BIT - if (isCmpUQOpcode(condop)) { - CMPLDI(cr, ra, d); - TODO(cmpldi); - return; - } - #endif - } - } - #endif - - // general case - Register rb = b==a ? ra : findRegFor(b, allow & ~rmask(ra)); - if (isCmpSIOpcode(condop)) { - CMPW(cr, ra, rb); - } - else if (isCmpUIOpcode(condop)) { - CMPLW(cr, ra, rb); - } - #if defined NANOJIT_64BIT - else if (isCmpSQOpcode(condop)) { - CMPD(cr, ra, rb); - } - else if (isCmpUQOpcode(condop)) { - CMPLD(cr, ra, rb); - } - #endif - else if (isCmpDOpcode(condop)) { - // set the lt/gt bit for fle/fge. We don't do this for - // int/uint because in those cases we can invert the branch condition. - // for float, we can't because of unordered comparisons - if (condop == LIR_led) - CROR(cr, lt, lt, eq); // lt = lt|eq - else if (condop == LIR_ged) - CROR(cr, gt, gt, eq); // gt = gt|eq - FCMPU(cr, ra, rb); - } - else { - TODO(asm_cmp); - } - } - - void Assembler::asm_ret(LIns *ins) { - genEpilogue(); - releaseRegisters(); - assignSavedRegs(); - LIns *value = ins->oprnd1(); - Register r = ins->isop(LIR_retd) ? F1 : R3; - findSpecificRegFor(value, r); - } - - void Assembler::asm_nongp_copy(Register r, Register s) { - // PPC doesn't support any GPR<->FPR moves - NanoAssert((rmask(r) & FpRegs) && (rmask(s) & FpRegs)); - FMR(r, s); - } - - bool Assembler::canRemat(LIns* ins) - { - return ins->isImmI() || ins->isop(LIR_allocp); - } - - void Assembler::asm_restore(LIns *i, Register r) { - int d; - if (i->isop(LIR_allocp)) { - d = deprecated_disp(i); - ADDI(r, FP, d); - } - else if (i->isImmI()) { - asm_li(r, i->immI()); - } - else { - d = findMemFor(i); - if (IsFpReg(r)) { - NanoAssert(i->isQorD()); - LFD(r, d, FP); - } else if (i->isQorD()) { - NanoAssert(IsGpReg(r)); - LD(r, d, FP); - } else { - NanoAssert(i->isI()); - NanoAssert(IsGpReg(r)); - LWZ(r, d, FP); - } - } - } - - void Assembler::asm_immi(LIns *ins) { - Register rr = deprecated_prepResultReg(ins, GpRegs); - asm_li(rr, ins->immI()); - } - - void Assembler::asm_fneg(LIns *ins) { - Register rr = deprecated_prepResultReg(ins, FpRegs); - Register ra = findRegFor(ins->oprnd1(), FpRegs); - FNEG(rr,ra); - } - - void Assembler::asm_param(LIns *ins) { - uint32_t a = ins->paramArg(); - uint32_t kind = ins->paramKind(); - if (kind == 0) { - // ordinary param - // first eight args always in R3..R10 for PPC - if (a < 8) { - // incoming arg in register - deprecated_prepResultReg(ins, rmask(argRegs[a])); - } else { - // todo: support stack based args, arg 0 is at [FP+off] where off - // is the # of regs to be pushed in genProlog() - TODO(asm_param_stk); - } - } - else { - // saved param - deprecated_prepResultReg(ins, rmask(savedRegs[a])); - } - } - - void Assembler::asm_call(LIns *ins) { - if (!ins->isop(LIR_callv)) { - Register retReg = ( ins->isop(LIR_calld) ? F1 : retRegs[0] ); - deprecated_prepResultReg(ins, rmask(retReg)); - } - - // Do this after we've handled the call result, so we don't - // force the call result to be spilled unnecessarily. - evictScratchRegsExcept(0); - - const CallInfo* call = ins->callInfo(); - ArgType argTypes[MAXARGS]; - uint32_t argc = call->getArgTypes(argTypes); - - bool indirect; - if (!(indirect = call->isIndirect())) { - verbose_only(if (_logc->lcbits & LC_Native) - outputf(" %p:", _nIns); - ) - br((NIns*)call->_address, 1); - } else { - // Indirect call: we assign the address arg to R11 since it's not - // used for regular arguments, and is otherwise scratch since it's - // clobberred by the call. - underrunProtect(8); // underrunProtect might clobber CTR - BCTRL(); - MTCTR(R11); - asm_regarg(ARGTYPE_P, ins->arg(--argc), R11); - } - - int param_size = 0; - - Register r = R3; - Register fr = F1; - for(uint32_t i = 0; i < argc; i++) { - uint32_t j = argc - i - 1; - ArgType ty = argTypes[j]; - LIns* arg = ins->arg(j); - NanoAssert(ty != ARGTYPE_V); - if (ty != ARGTYPE_D) { - // GP arg - if (r <= R10) { - asm_regarg(ty, arg, r); - r = r + 1; - param_size += sizeof(void*); - } else { - // put arg on stack - TODO(stack_int32); - } - } else { - // double - if (fr <= F13) { - asm_regarg(ty, arg, fr); - fr = fr + 1; - #ifdef NANOJIT_64BIT - r = r + 1; - #else - r = r + 2; // Skip 2 GPRs. - #endif - param_size += sizeof(double); - } else { - // put arg on stack - TODO(stack_double); - } - } - } - if (param_size > max_param_size) - max_param_size = param_size; - } - - void Assembler::asm_regarg(ArgType ty, LIns* p, Register r) - { - NanoAssert(r != deprecated_UnknownReg); - NanoAssert(ty != ARGTYPE_V); - if (ty != ARGTYPE_D) - { - #ifdef NANOJIT_64BIT - if (ty == ARGTYPE_I) { - // sign extend 32->64 - EXTSW(r, r); - } else if (ty == ARGTYPE_UI) { - // zero extend 32->64 - CLRLDI(r, r, 32); - } - #endif - // arg goes in specific register - if (p->isImmI()) { - asm_li(r, p->immI()); - } else { - if (p->isExtant()) { - if (!p->deprecated_hasKnownReg()) { - // load it into the arg reg - int d = findMemFor(p); - if (p->isop(LIR_allocp)) { - NanoAssert(isS16(d)); - ADDI(r, FP, d); - } else if (p->isQorD()) { - LD(r, d, FP); - } else { - LWZ(r, d, FP); - } - } else { - // it must be in a saved reg - MR(r, p->deprecated_getReg()); - } - } - else { - // this is the last use, so fine to assign it - // to the scratch reg, it's dead after this point. - findSpecificRegFor(p, r); - } - } - } - else { - if (p->isExtant()) { - Register rp = p->deprecated_getReg(); - if (!deprecated_isKnownReg(rp) || !IsFpReg(rp)) { - // load it into the arg reg - int d = findMemFor(p); - LFD(r, d, FP); - } else { - // it must be in a saved reg - NanoAssert(IsFpReg(r) && IsFpReg(rp)); - FMR(r, rp); - } - } - else { - // this is the last use, so fine to assign it - // to the scratch reg, it's dead after this point. - findSpecificRegFor(p, r); - } - } - } - - void Assembler::asm_spill(Register rr, int d, bool quad) { - (void)quad; - NanoAssert(d); - if (IsFpReg(rr)) { - NanoAssert(quad); - STFD(rr, d, FP); - } - #ifdef NANOJIT_64BIT - else if (quad) { - STD(rr, d, FP); - } - #endif - else { - NanoAssert(!quad); - STW(rr, d, FP); - } - } - - void Assembler::asm_arith(LIns *ins) { - LOpcode op = ins->opcode(); - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - RegisterMask allow = GpRegs; - Register rr = deprecated_prepResultReg(ins, allow); - Register ra = findRegFor(lhs, GpRegs); - - if (rhs->isImmI()) { - int32_t rhsc = rhs->immI(); - if (isS16(rhsc)) { - // ppc arith immediate ops sign-exted the imm16 value - switch (op) { - case LIR_addi: - CASE64(LIR_addq:) - ADDI(rr, ra, rhsc); - return; - case LIR_subi: - SUBI(rr, ra, rhsc); - return; - case LIR_muli: - MULLI(rr, ra, rhsc); - return; - } - } - if (isU16(rhsc)) { - // ppc logical immediate zero-extend the imm16 value - switch (op) { - CASE64(LIR_orq:) - case LIR_ori: - ORI(rr, ra, rhsc); - return; - CASE64(LIR_andq:) - case LIR_andi: - ANDI(rr, ra, rhsc); - return; - CASE64(LIR_xorq:) - case LIR_xori: - XORI(rr, ra, rhsc); - return; - } - } - - // LIR shift ops only use last 5bits of shift const - switch (op) { - case LIR_lshi: - SLWI(rr, ra, rhsc&31); - return; - case LIR_rshui: - SRWI(rr, ra, rhsc&31); - return; - case LIR_rshi: - SRAWI(rr, ra, rhsc&31); - return; - } - } - - // general case, put rhs in register - Register rb = rhs==lhs ? ra : findRegFor(rhs, GpRegs&~rmask(ra)); - switch (op) { - CASE64(LIR_addq:) - case LIR_addi: - ADD(rr, ra, rb); - break; - CASE64(LIR_andq:) - case LIR_andi: - AND(rr, ra, rb); - break; - CASE64(LIR_orq:) - case LIR_ori: - OR(rr, ra, rb); - break; - CASE64(LIR_xorq:) - case LIR_xori: - XOR(rr, ra, rb); - break; - case LIR_subi: SUBF(rr, rb, ra); break; - case LIR_lshi: SLW(rr, ra, R0); ANDI(R0, rb, 31); break; - case LIR_rshi: SRAW(rr, ra, R0); ANDI(R0, rb, 31); break; - case LIR_rshui: SRW(rr, ra, R0); ANDI(R0, rb, 31); break; - case LIR_muli: MULLW(rr, ra, rb); break; - #ifdef NANOJIT_64BIT - case LIR_lshq: - SLD(rr, ra, R0); - ANDI(R0, rb, 63); - break; - case LIR_rshuq: - SRD(rr, ra, R0); - ANDI(R0, rb, 63); - break; - case LIR_rshq: - SRAD(rr, ra, R0); - ANDI(R0, rb, 63); - TODO(qirsh); - break; - #endif - default: - debug_only(outputf("%s",lirNames[op]);) - TODO(asm_arith); - } - } - - void Assembler::asm_fop(LIns *ins) { - LOpcode op = ins->opcode(); - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - RegisterMask allow = FpRegs; - Register rr = deprecated_prepResultReg(ins, allow); - Register ra, rb; - findRegFor2(allow, lhs, ra, allow, rhs, rb); - switch (op) { - case LIR_addd: FADD(rr, ra, rb); break; - case LIR_subd: FSUB(rr, ra, rb); break; - case LIR_muld: FMUL(rr, ra, rb); break; - case LIR_divd: FDIV(rr, ra, rb); break; - default: - debug_only(outputf("%s",lirNames[op]);) - TODO(asm_fop); - } - } - - void Assembler::asm_i2d(LIns *ins) { - Register r = deprecated_prepResultReg(ins, FpRegs); - Register v = findRegFor(ins->oprnd1(), GpRegs); - const int d = 16; // natural aligned - - #if defined NANOJIT_64BIT && !PEDANTIC - FCFID(r, r); // convert to double - LFD(r, d, SP); // load into fpu register - STD(v, d, SP); // save int64 - EXTSW(v, v); // extend sign destructively, ok since oprnd1 only is 32bit - #else - FSUB(r, r, F0); - LFD(r, d, SP); // scratch area in outgoing linkage area - STW(R0, d+4, SP); - XORIS(R0, v, 0x8000); - LFD(F0, d, SP); - STW(R0, d+4, SP); - LIS(R0, 0x8000); - STW(R0, d, SP); - LIS(R0, 0x4330); - #endif - } - - void Assembler::asm_ui2d(LIns *ins) { - Register r = deprecated_prepResultReg(ins, FpRegs); - Register v = findRegFor(ins->oprnd1(), GpRegs); - const int d = 16; - - #if defined NANOJIT_64BIT && !PEDANTIC - FCFID(r, r); // convert to double - LFD(r, d, SP); // load into fpu register - STD(v, d, SP); // save int64 - CLRLDI(v, v, 32); // zero-extend destructively - #else - FSUB(r, r, F0); - LFD(F0, d, SP); - STW(R0, d+4, SP); - LI(R0, 0); - LFD(r, d, SP); - STW(v, d+4, SP); - STW(R0, d, SP); - LIS(R0, 0x4330); - #endif - } - - void Assembler::asm_d2i(LIns*) { - NanoAssertMsg(0, "NJ_F2I_SUPPORTED not yet supported for this architecture"); - } - - #if defined NANOJIT_64BIT - // XXX: this is sub-optimal, see https://bugzilla.mozilla.org/show_bug.cgi?id=540368#c7. - void Assembler::asm_q2i(LIns *ins) { - Register rr = deprecated_prepResultReg(ins, GpRegs); - int d = findMemFor(ins->oprnd1()); - LWZ(rr, d+4, FP); - } - - void Assembler::asm_ui2uq(LIns *ins) { - LOpcode op = ins->opcode(); - Register r = deprecated_prepResultReg(ins, GpRegs); - Register v = findRegFor(ins->oprnd1(), GpRegs); - switch (op) { - default: - debug_only(outputf("%s",lirNames[op])); - TODO(asm_ui2uq); - case LIR_ui2uq: - CLRLDI(r, v, 32); // clears the top 32 bits - break; - case LIR_i2q: - EXTSW(r, v); - break; - } - } - - void Assembler::asm_dasq(LIns*) { - TODO(asm_dasq); - } - - void Assembler::asm_qasd(LIns*) { - TODO(asm_qasd); - } - - #endif - -#ifdef NANOJIT_64BIT - void Assembler::asm_immq(LIns *ins) { - Register r = ins->deprecated_getReg(); - if (deprecated_isKnownReg(r) && (rmask(r) & FpRegs)) { - // FPR already assigned, fine, use it - deprecated_freeRsrcOf(ins); - } else { - // use a GPR register; its okay to copy doubles with GPR's - // but *not* okay to copy non-doubles with FPR's - r = deprecated_prepResultReg(ins, GpRegs); - } - - if (rmask(r) & FpRegs) { - union { - double d; - struct { - int32_t hi, lo; // Always assuming big-endian in NativePPC.cpp - } w; - }; - d = ins->immD(); - LFD(r, 8, SP); - STW(R0, 12, SP); - asm_li(R0, w.lo); - STW(R0, 8, SP); - asm_li(R0, w.hi); - } - else { - int64_t q = ins->immQ(); - if (isS32(q)) { - asm_li(r, int32_t(q)); - return; - } - RLDIMI(r,R0,32,0); // or 32,32? - asm_li(R0, int32_t(q>>32)); // hi bits into R0 - asm_li(r, int32_t(q)); // lo bits into dest reg - } - } -#endif - - void Assembler::asm_immd(LIns *ins) { - #ifdef NANOJIT_64BIT - Register r = ins->deprecated_getReg(); - if (deprecated_isKnownReg(r) && (rmask(r) & FpRegs)) { - // FPR already assigned, fine, use it - deprecated_freeRsrcOf(ins); - } else { - // use a GPR register; its okay to copy doubles with GPR's - // but *not* okay to copy non-doubles with FPR's - r = deprecated_prepResultReg(ins, GpRegs); - } - #else - Register r = deprecated_prepResultReg(ins, FpRegs); - #endif - - if (rmask(r) & FpRegs) { - union { - double d; - struct { - int32_t hi, lo; // Always assuming big-endian in NativePPC.cpp - } w; - }; - d = ins->immD(); - LFD(r, 8, SP); - STW(R0, 12, SP); - asm_li(R0, w.lo); - STW(R0, 8, SP); - asm_li(R0, w.hi); - } - else { - int64_t q = ins->immDasQ(); - if (isS32(q)) { - asm_li(r, int32_t(q)); - return; - } - RLDIMI(r,R0,32,0); // or 32,32? - asm_li(R0, int32_t(q>>32)); // hi bits into R0 - asm_li(r, int32_t(q)); // lo bits into dest reg - } - } - - void Assembler::br(NIns* addr, int link) { - // destination unknown, then use maximum branch possible - if (!addr) { - br_far(addr,link); - return; - } - - // powerpc offsets are based on the address of the branch instruction - underrunProtect(4); // ensure _nIns is addr of Bx - ptrdiff_t offset = addr - (_nIns-1); // we want ptr diff's implicit >>2 here - - #if !PEDANTIC - if (isS24(offset)) { - Bx(offset, 0, link); // b addr or bl addr - return; - } - ptrdiff_t absaddr = addr - (NIns*)0; // ptr diff implies >>2 - if (isS24(absaddr)) { - Bx(absaddr, 1, link); // ba addr or bla addr - return; - } - #endif // !PEDANTIC - - br_far(addr,link); - } - - void Assembler::br_far(NIns* addr, int link) { - // far jump. - // can't have a page break in this sequence, because the break - // would also clobber ctr and r2. We use R2 here because it's not available - // to the register allocator, and we use R0 everywhere else as scratch, so using - // R2 here avoids clobbering anything else besides CTR. - #ifdef NANOJIT_64BIT - if (addr==0 || !isU32(uintptr_t(addr))) { - // really far jump to 64bit abs addr - underrunProtect(28); // 7 instructions - BCTR(link); - MTCTR(R2); - asm_li64(R2, uintptr_t(addr)); // 5 instructions - return; - } - #endif - underrunProtect(16); - BCTR(link); - MTCTR(R2); - asm_li32(R2, uint32_t(uintptr_t(addr))); // 2 instructions - } - - void Assembler::underrunProtect(int bytes) { - NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - int instr = (bytes + sizeof(NIns) - 1) / sizeof(NIns); - NIns *pc = _nIns; - NIns *top = codeStart; // this may be in a normal code chunk or an exit code chunk - - #if PEDANTIC - // pedanticTop is based on the last call to underrunProtect; any time we call - // underrunProtect and would use more than what's already protected, then insert - // a page break jump. Sometimes, this will be to a new page, usually it's just - // the next instruction and the only effect is to clobber R2 & CTR - - NanoAssert(pedanticTop >= top); - if (pc - instr < pedanticTop) { - // no page break required, but insert a far branch anyway just to be difficult - #ifdef NANOJIT_64BIT - const int br_size = 7; - #else - const int br_size = 4; - #endif - if (pc - instr - br_size < top) { - // really do need a page break - verbose_only(if (_logc->lcbits & LC_Native) outputf("newpage %p:", pc);) - codeAlloc(); - } - // now emit the jump, but make sure we won't need another page break. - // we're pedantic, but not *that* pedantic. - pedanticTop = _nIns - br_size; - br(pc, 0); - pedanticTop = _nIns - instr; - } - #else - if (pc - instr < top) { - verbose_only(if (_logc->lcbits & LC_Native) outputf("newpage %p:", pc);) - // This may be in a normal code chunk or an exit code chunk. - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - // This jump will call underrunProtect again, but since we're on a new - // page, nothing will happen. - br(pc, 0); - } - #endif - } - - void Assembler::asm_cmov(LIns* ins) - { - LIns* condval = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - - #ifdef NANOJIT_64BIT - NanoAssert((ins->opcode() == LIR_cmovi && iftrue->isI() && iffalse->isI()) || - (ins->opcode() == LIR_cmovq && iftrue->isQ() && iffalse->isQ())); - #else - NanoAssert((ins->opcode() == LIR_cmovi && iftrue->isI() && iffalse->isI())); - #endif - - Register rr = prepareResultReg(ins, GpRegs); - Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr)); - - // If 'iftrue' isn't in a register, it can be clobbered by 'ins'. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rr; - - underrunProtect(16); // make sure branch target and branch are on same page and thus near - NIns *after = _nIns; - verbose_only(if (_logc->lcbits & LC_Native) outputf("%p:",after);) - MR(rr,rf); - - NanoAssert(isS24(after - (_nIns-1))); - asm_branch_near(false, condval, after); - - if (rr != rt) - MR(rr, rt); - - freeResourcesOf(ins); - if (!iftrue->isInReg()) { - NanoAssert(rt == rr); - findSpecificRegForUnallocated(iftrue, rr); - } - - asm_cmp(condval->opcode(), condval->oprnd1(), condval->oprnd2(), CR7); - } - - RegisterMask Assembler::nHint(LIns* ins) { - NanoAssert(ins->isop(LIR_paramp)); - RegisterMask prefer = 0; - if (ins->paramKind() == 0) - if (ins->paramArg() < 8) - prefer = rmask(argRegs[ins->paramArg()]); - return prefer; - } - - void Assembler::asm_neg_not(LIns *ins) { - Register rr = deprecated_prepResultReg(ins, GpRegs); - Register ra = findRegFor(ins->oprnd1(), GpRegs); - if (ins->isop(LIR_negi)) { - NEG(rr, ra); - } else { - NOT(rr, ra); - } - } - - void Assembler::nInit() { - nHints[LIR_calli] = rmask(R3); - #ifdef NANOJIT_64BIT - nHints[LIR_callq] = rmask(R3); - #endif - nHints[LIR_calld] = rmask(F1); - nHints[LIR_paramp] = PREFER_SPECIAL; - } - - void Assembler::nBeginAssembly() { - max_param_size = 0; - } - - void Assembler::nativePageSetup() { - NanoAssert(!_inExit); - if (!_nIns) { - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - IF_PEDANTIC( pedanticTop = _nIns; ) - } - } - - void Assembler::nativePageReset() - {} - - // Increment the 32-bit profiling counter at pCtr, without - // changing any registers. - verbose_only( - void Assembler::asm_inc_m32(uint32_t* /*pCtr*/) - { - } - ) - - void Assembler::nPatchBranch(NIns *branch, NIns *target) { - // ppc relative offsets are based on the addr of the branch instruction - ptrdiff_t bd = target - branch; - if (branch[0] == PPC_b) { - // unconditional, 24bit offset. Whoever generated the unpatched jump - // must have known the final size would fit in 24bits! otherwise the - // jump would be (lis,ori,mtctr,bctr) and we'd be patching the lis,ori. - NanoAssert(isS24(bd)); - branch[0] |= (bd & 0xffffff) << 2; - } - else if ((branch[0] & PPC_bc) == PPC_bc) { - // conditional, 14bit offset. Whoever generated the unpatched jump - // must have known the final size would fit in 14bits! otherwise the - // jump would be (lis,ori,mtctr,bcctr) and we'd be patching the lis,ori below. - NanoAssert(isS14(bd)); - NanoAssert(((branch[0] & 0x3fff)<<2) == 0); - branch[0] |= (bd & 0x3fff) << 2; - TODO(patch_bc); - } - #ifdef NANOJIT_64BIT - // patch 64bit branch - else if ((branch[0] & ~(31<<21)) == PPC_addis) { - // general branch, using lis,ori,sldi,oris,ori to load the const 64bit addr. - Register rd = { (branch[0] >> 21) & 31 }; - NanoAssert(branch[1] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); - NanoAssert(branch[3] == PPC_oris | GPR(rd)<<21 | GPR(rd)<<16); - NanoAssert(branch[4] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); - uint64_t imm = uintptr_t(target); - uint32_t lo = uint32_t(imm); - uint32_t hi = uint32_t(imm>>32); - branch[0] = PPC_addis | GPR(rd)<<21 | uint16_t(hi>>16); - branch[1] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(hi); - branch[3] = PPC_oris | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(lo>>16); - branch[4] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(lo); - } - #else // NANOJIT_64BIT - // patch 32bit branch - else if ((branch[0] & ~(31<<21)) == PPC_addis) { - // general branch, using lis,ori to load the const addr. - // patch a lis,ori sequence with a 32bit value - Register rd = { (branch[0] >> 21) & 31 }; - NanoAssert(branch[1] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); - uint32_t imm = uint32_t(target); - branch[0] = PPC_addis | GPR(rd)<<21 | uint16_t(imm >> 16); // lis rd, imm >> 16 - branch[1] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(imm); // ori rd, rd, imm & 0xffff - } - #endif // !NANOJIT_64BIT - else { - TODO(unknown_patch); - } - } - - static int cntzlw(int set) { - // On PowerPC, prefer higher registers, to minimize - // size of nonvolatile area that must be saved. - register uint32_t i; - #ifdef __GNUC__ - asm ("cntlzw %0,%1" : "=r" (i) : "r" (set)); - #else // __GNUC__ - # error("unsupported compiler") - #endif // __GNUC__ - return 31-i; - } - - Register Assembler::nRegisterAllocFromSet(RegisterMask set) { - uint32_t i; - // note, deliberate truncation of 64->32 bits - if (set & 0xffffffff) { - i = cntzlw(int(set)); // gp reg - } else { - i = 32 + cntzlw(int(set>>32)); // fp reg - } - Register r = { i }; - _allocator.free &= ~rmask(r); - return r; - } - - void Assembler::nRegisterResetAll(RegAlloc ®s) { - regs.clear(); - regs.free = SavedRegs | 0x1ff8 /* R3-12 */ | 0x3ffe00000000LL /* F1-13 */; - } - -#ifdef NANOJIT_64BIT - void Assembler::asm_qbinop(LIns *ins) { - LOpcode op = ins->opcode(); - switch (op) { - case LIR_orq: - case LIR_andq: - case LIR_rshuq: - case LIR_rshq: - case LIR_lshq: - case LIR_xorq: - case LIR_addq: - asm_arith(ins); - break; - default: - debug_only(outputf("%s",lirNames[op])); - TODO(asm_qbinop); - } - } -#endif // NANOJIT_64BIT - - void Assembler::nFragExit(LIns*) { - TODO(nFragExit); - } - - void Assembler::asm_jtbl(LIns* ins, NIns** native_table) - { - // R0 = index*4, R2 = table, CTR = computed address to jump to. - // must ensure no page breaks in here because R2 & CTR can get clobbered. - Register indexreg = findRegFor(ins->oprnd1(), GpRegs); -#ifdef NANOJIT_64BIT - underrunProtect(9*4); - BCTR(0); // jump to address in CTR - MTCTR(R2); // CTR = R2 - LDX(R2, R2, R0); // R2 = [table + index*8] - SLDI(R0, indexreg, 3); // R0 = index*8 - asm_li64(R2, uint64_t(native_table)); // R2 = table (5 instr) -#else // 64bit - underrunProtect(6*4); - BCTR(0); // jump to address in CTR - MTCTR(R2); // CTR = R2 - LWZX(R2, R2, R0); // R2 = [table + index*4] - SLWI(R0, indexreg, 2); // R0 = index*4 - asm_li(R2, int32_t(native_table)); // R2 = table (up to 2 instructions) -#endif // 64bit - } - - void Assembler::swapCodeChunks() { - if (!_nExitIns) { - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - } - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - void Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported - } - -} // namespace nanojit - -#endif // FEATURE_NANOJIT && NANOJIT_PPC diff --git a/deps/mozjs/js/src/nanojit/NativePPC.h b/deps/mozjs/js/src/nanojit/NativePPC.h deleted file mode 100644 index 70cd5a0f3ed..00000000000 --- a/deps/mozjs/js/src/nanojit/NativePPC.h +++ /dev/null @@ -1,604 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_NativePPC__ -#define __nanojit_NativePPC__ - -#ifdef PERFM -#define DOPROF -#include "../vprof/vprof.h" -#define count_instr() _nvprof("ppc",1) -#define count_prolog() _nvprof("ppc-prolog",1); count_instr(); -#define count_imt() _nvprof("ppc-imt",1) count_instr() -#else -#define count_instr() -#define count_prolog() -#define count_imt() -#endif - -#include "NativeCommon.h" - -namespace nanojit -{ -#define NJ_MAX_STACK_ENTRY 4096 -#define NJ_ALIGN_STACK 16 - -#define NJ_JTBL_SUPPORTED 1 -#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 -#define NJ_F2I_SUPPORTED 0 -#define NJ_SOFTFLOAT_SUPPORTED 0 -#define NJ_DIVI_SUPPORTED 0 - - enum ConditionRegister { - CR0 = 0, - CR1 = 1, - CR2 = 2, - CR3 = 3, - CR4 = 4, - CR5 = 5, - CR6 = 6, - CR7 = 7, - }; - - enum ConditionBit { - COND_lt = 0, // msb of CR - COND_gt = 1, - COND_eq = 2, - COND_so = 3, // lsb of CR - COND_un = 3, - }; - - // this is the BO field in condition instructions - enum ConditionOption { - BO_true = 12, // branch if true - BO_false = 4, // branch if false - }; - - static const Register - // general purpose 32bit regs - R0 = { 0 }, // scratch or the value 0, excluded from regalloc - SP = { 1 }, // stack pointer, excluded from regalloc - R2 = { 2 }, // scratch on MacOSX, rtoc pointer elsewhere - R3 = { 3 }, // this, return value, MSW of int64 return - R4 = { 4 }, // param, LSW of int64 return - R5 = { 5 }, // param - R6 = { 6 }, // param - R7 = { 7 }, // param - R8 = { 8 }, // param - R9 = { 9 }, // param - R10 = { 10 }, // param - R11 = { 11 }, // scratch in leaf funcs, outgoing arg ptr otherwise - R12 = { 12 }, // scratch - R13 = { 13 }, // ppc32: saved, ppc64: thread-specific storage - R14 = { 14 }, // saved - R15 = { 15 }, - R16 = { 16 }, - R17 = { 17 }, - R18 = { 18 }, - R19 = { 19 }, - R20 = { 20 }, - R21 = { 21 }, - R22 = { 22 }, - R23 = { 23 }, - R24 = { 24 }, - R25 = { 25 }, - R26 = { 26 }, - R27 = { 27 }, - R28 = { 28 }, - R29 = { 29 }, - R30 = { 30 }, - R31 = { 31 }, // excluded from regalloc since we use it as FP - FP = R31, - - // FP regs - F0 = { 32 }, // scratch, excluded from reg alloc - F1 = { 33 }, // param, double return value - F2 = { 34 }, // param - F3 = { 35 }, // param - F4 = { 36 }, // param - F5 = { 37 }, // param - F6 = { 38 }, // param - F7 = { 39 }, // param - F8 = { 40 }, // param - F9 = { 41 }, // param - F10 = { 42 }, // param - F11 = { 43 }, // param - F12 = { 44 }, // param - F13 = { 45 }, // param - F14 = { 46 }, // F14-31 saved - F15 = { 47 }, - F16 = { 48 }, - F17 = { 49 }, - F18 = { 50 }, - F19 = { 51 }, - F20 = { 52 }, - F21 = { 53 }, - F22 = { 54 }, - F23 = { 55 }, - F24 = { 56 }, - F25 = { 57 }, - F26 = { 58 }, - F27 = { 59 }, - F28 = { 60 }, - F29 = { 61 }, - F30 = { 62 }, - F31 = { 63 }, - - // special purpose registers (SPR) - Rxer = { 1 }, - Rlr = { 8 }, - Rctr = { 9 }, - - deprecated_UnknownReg = { 127 }; // XXX: remove eventually, see bug 538924 - - static const uint32_t FirstRegNum = 0; // R0 - static const uint32_t LastRegNum = 64; // F31 - - enum PpcOpcode { - // opcodes - PPC_add = 0x7C000214, // add - PPC_addo = 0x7C000614, // add & OE=1 (can set OV) - PPC_addi = 0x38000000, // add immediate - PPC_addis = 0x3C000000, // add immediate shifted - PPC_and = 0x7C000038, // and - PPC_andc = 0x7C000078, // and with compliment - PPC_andi = 0x70000000, // and immediate - PPC_andis = 0x74000000, // and immediate shifted - PPC_b = 0x48000000, // branch - PPC_bc = 0x40000000, // branch conditional - PPC_bcctr = 0x4C000420, // branch conditional to count register - PPC_cmp = 0x7C000000, // compare - PPC_cmpi = 0x2C000000, // compare immediate - PPC_cmpl = 0x7C000040, // compare logical - PPC_cmpli = 0x28000000, // compare logical immediate - PPC_cror = 0x4C000382, // condition register or - PPC_extsw = 0x7C0007B4, // extend sign word - PPC_fadd = 0xFC00002A, // floating add (double precision) - PPC_fcfid = 0xFC00069C, // floating convert from integer doubleword - PPC_fcmpu = 0xFC000000, // floating compare unordered - PPC_fdiv = 0xFC000024, // floating divide (double precision) - PPC_fmr = 0xFC000090, // floating move register (double precision) - PPC_fmul = 0xFC000032, // floating multiply (double precision) - PPC_fneg = 0xFC000050, // floating negate - PPC_fsub = 0xFC000028, // floating subtract (double precision) - PPC_lbz = 0x88000000, // load byte and zero - PPC_lbzx = 0x7C0000AE, // load byte and zero indexed - PPC_ld = 0xE8000000, // load doubleword - PPC_ldx = 0x7C00002A, // load doubleword indexed - PPC_lfd = 0xC8000000, // load floating point double - PPC_lfdx = 0x7C0004AE, // load floating-point double indexed - PPC_lhz = 0xA0000000, // load halfword and zero - PPC_lhzx = 0x7C00022E, // load halfword and zero indexed - PPC_lwz = 0x80000000, // load word and zero - PPC_lwzx = 0x7C00002E, // load word and zero indexed - PPC_mfcr = 0x7C000026, // move from condition register - PPC_mfspr = 0x7C0002A6, // move from spr (special purpose register) - PPC_mtspr = 0x7C0003A6, // move to spr - PPC_mulli = 0x1C000000, // multiply low immediate - PPC_mullw = 0x7C0001D6, // multiply low word - PPC_neg = 0x7C0000D0, // negate - PPC_nor = 0x7C0000F8, // nor - PPC_or = 0x7C000378, // or - PPC_ori = 0x60000000, // or immediate - PPC_oris = 0x64000000, // or immediate shifted - PPC_rlwinm = 0x54000000, // rotate left word then and with mask - PPC_rldicl = 0x78000000, // rotate left doubleword immediate then clear left - PPC_rldicr = 0x78000004, // rotate left doubleword immediate then clear right - PPC_rldimi = 0x7800000C, // rotate left doubleword immediate then mask insert - PPC_sld = 0x7C000036, // shift left doubleword - PPC_slw = 0x7C000030, // shift left word - PPC_srad = 0x7C000634, // shift right algebraic doubleword (sign ext) - PPC_sradi = 0x7C000674, // shift right algebraic doubleword immediate - PPC_sraw = 0x7C000630, // shift right algebraic word (sign ext) - PPC_srawi = 0x7C000670, // shift right algebraic word immediate - PPC_srd = 0x7C000436, // shift right doubleword (zero ext) - PPC_srw = 0x7C000430, // shift right word (zero ext) - PPC_stb = 0x98000000, // store byte - PPC_stbx = 0x7C0001AE, // store byte indexed - PPC_std = 0xF8000000, // store doubleword - PPC_stdu = 0xF8000001, // store doubleword with update - PPC_stdux = 0x7C00016A, // store doubleword with update indexed - PPC_stdx = 0x7C00012A, // store doubleword indexed - PPC_stfd = 0xD8000000, // store floating-point double - PPC_stfdx = 0x7C0005AE, // store floating-point double indexed - PPC_stw = 0x90000000, // store word - PPC_stwu = 0x94000000, // store word with update - PPC_stwux = 0x7C00016E, // store word with update indexed - PPC_stwx = 0x7C00012E, // store word indexed - PPC_subf = 0x7C000050, // subtract from - PPC_xor = 0x7C000278, // xor - PPC_xori = 0x68000000, // xor immediate - PPC_xoris = 0x6C000000, // xor immediate shifted - - // simplified mnemonics - PPC_mr = PPC_or, - PPC_not = PPC_nor, - PPC_nop = PPC_ori, - }; - - typedef uint64_t RegisterMask; - - static const RegisterMask GpRegs = 0xffffffff; - static const RegisterMask FpRegs = 0xffffffff00000000LL; - // R31 is a saved reg too, but we use it as our Frame ptr FP -#ifdef NANOJIT_64BIT - // R13 reserved for thread-specific storage on ppc64-darwin - static const RegisterMask SavedRegs = 0x7fffc000; // R14-R30 saved - static const int NumSavedRegs = 17; // R14-R30 -#else - static const RegisterMask SavedRegs = 0x7fffe000; // R13-R30 saved - static const int NumSavedRegs = 18; // R13-R30 -#endif - - static inline bool IsGpReg(Register r) { - return r <= R31; - } - static inline bool IsFpReg(Register r) { - return r >= F0; - } - - verbose_only( extern const char* regNames[]; ) - - #define DECLARE_PLATFORM_STATS() - #define DECLARE_PLATFORM_REGALLOC() - -#ifdef NANOJIT_64BIT - #define DECL_PPC64()\ - void asm_qbinop(LIns*); -#else - #define DECL_PPC64() -#endif - - #define DECLARE_PLATFORM_ASSEMBLER() \ - const static Register argRegs[8], retRegs[2]; \ - void underrunProtect(int bytes); \ - void nativePageReset(); \ - void nativePageSetup(); \ - bool hardenNopInsertion(const Config& /*c*/) { return false; } \ - void br(NIns *addr, int link); \ - void br_far(NIns *addr, int link); \ - void asm_regarg(ArgType, LIns*, Register); \ - void asm_li(Register r, int32_t imm); \ - void asm_li32(Register r, int32_t imm); \ - void asm_li64(Register r, uint64_t imm); \ - void asm_cmp(LOpcode op, LIns *a, LIns *b, ConditionRegister); \ - NIns* asm_branch_far(bool onfalse, LIns *cond, NIns * const targ); \ - NIns* asm_branch_near(bool onfalse, LIns *cond, NIns * const targ); \ - int max_param_size; /* bytes */ \ - DECL_PPC64() - - const int LARGEST_UNDERRUN_PROT = 9*4; // largest value passed to underrunProtect - - typedef uint32_t NIns; - - // Bytes of icache to flush after Assembler::patch - const size_t LARGEST_BRANCH_PATCH = 4 * sizeof(NIns); - - #define EMIT1(ins, fmt, ...) do {\ - underrunProtect(4);\ - *(--_nIns) = (NIns) (ins);\ - asm_output(fmt, ##__VA_ARGS__);\ - } while (0) /* no semi */ - - #define GPR(r) REGNUM(r) - #define FPR(r) (REGNUM(r) & 31) - - #define Bx(li,aa,lk) EMIT1(PPC_b | ((li)&0xffffff)<<2 | (aa)<<1 | (lk),\ - "b%s%s %p", (lk)?"l":"", (aa)?"a":"", _nIns+(li)) - - #define B(li) Bx(li,0,0) - #define BA(li) Bx(li,1,0) - #define BL(li) Bx(li,0,1) - #define BLA(li) Bx(li,1,1) - - #define BCx(op,bo,bit,cr,bd,aa,lk) EMIT1(PPC_bc | (bo)<<21 | (4*(cr)+COND_##bit)<<16 |\ - ((bd)&0x3fff)<<2 | (aa)<<1 | (lk),\ - "%s%s%s cr%d,%p", #op, (lk)?"l":"", (aa)?"a":"", (cr), _nIns+(bd)) - - #define BLT(cr,bd) BCx(blt, BO_true, lt, cr, bd, 0, 0) - #define BGT(cr,bd) BCx(bgt, BO_true, gt, cr, bd, 0, 0) - #define BEQ(cr,bd) BCx(beq, BO_true, eq, cr, bd, 0, 0) - #define BGE(cr,bd) BCx(bge, BO_false, lt, cr, bd, 0, 0) - #define BLE(cr,bd) BCx(ble, BO_false, gt, cr, bd, 0, 0) - #define BNE(cr,bd) BCx(bne, BO_false, eq, cr, bd, 0, 0) - #define BNG(cr,bd) BCx(bng, BO_false, gt, cr, bd, 0, 0) - #define BNL(cr,bd) BCx(bnl, BO_false, lt, cr, bd, 0, 0) - - #define BCCTRx(op, bo, bit, cr, lk) EMIT1(PPC_bcctr | (bo)<<21 | (4*(cr)+COND_##bit)<<16 | (lk)&1,\ - "%sctr%s cr%d", #op, (lk)?"l":"", (cr)) - - #define BLTCTR(cr) BCCTRx(blt, BO_true, lt, cr, 0) - #define BGTCTR(cr) BCCTRx(bgt, BO_true, gt, cr, 0) - #define BEQCTR(cr) BCCTRx(beq, BO_true, eq, cr, 0) - #define BGECTR(cr) BCCTRx(bge, BO_false, lt, cr, 0) - #define BLECTR(cr) BCCTRx(ble, BO_false, gt, cr, 0) - #define BNECTR(cr) BCCTRx(bne, BO_false, eq, cr, 0) - #define BNGCTR(cr) BCCTRx(bng, BO_false, gt, cr, 0) - #define BNLCTR(cr) BCCTRx(bnl, BO_false, lt, cr, 0) - - #define Simple(asm,op) EMIT1(op, "%s", #asm) - - #define BCTR(link) EMIT1(0x4E800420 | (link), "bctr%s", (link) ? "l" : "") - #define BCTRL() BCTR(1) - - #define BLR() EMIT1(0x4E800020, "blr") - #define NOP() EMIT1(PPC_nop, "nop") /* ori 0,0,0 */ - - #define ALU2(op, rd, ra, rb, rc) EMIT1(PPC_##op | GPR(rd)<<21 | GPR(ra)<<16 | GPR(rb)<<11 | (rc),\ - "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(rd), gpn(ra), gpn(rb)) - #define BITALU2(op, ra, rs, rb, rc) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rb)<<11 | (rc),\ - "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(ra), gpn(rs), gpn(rb)) - #define FPUAB(op, d, a, b, rc) EMIT1(PPC_##op | FPR(d)<<21 | FPR(a)<<16 | FPR(b)<<11 | (rc),\ - "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(d), gpn(a), gpn(b)) - #define FPUAC(op, d, a, c, rc) EMIT1(PPC_##op | FPR(d)<<21 | FPR(a)<<16 | FPR(c)<<6 | (rc),\ - "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(d), gpn(a), gpn(c)) - - #define ADD(rd,ra,rb) ALU2(add, rd, ra, rb, 0) - #define ADD_(rd,ra,rb) ALU2(add, rd, ra, rb, 1) - #define ADDO(rd,ra,rb) ALU2(addo, rd, ra, rb, 0) - #define ADDO_(rd,ra,rb) ALU2(addo, rd, ra, rb, 1) - #define SUBF(rd,ra,rb) ALU2(subf, rd, ra, rb, 0) - #define SUBF_(rd,ra,rb) ALU2(subf, rd, ra, rb, 1) - - #define AND(rd,rs,rb) BITALU2(and, rd, rs, rb, 0) - #define AND_(rd,rs,rb) BITALU2(and, rd, rs, rb, 1) - #define OR(rd,rs,rb) BITALU2(or, rd, rs, rb, 0) - #define OR_(rd,rs,rb) BITALU2(or, rd, rs, rb, 1) - #define NOR(rd,rs,rb) BITALU2(nor, rd, rs, rb, 0) - #define NOR_(rd,rs,rb) BITALU2(nor, rd, rs, rb, 1) - #define SLW(rd,rs,rb) BITALU2(slw, rd, rs, rb, 0) - #define SLW_(rd,rs,rb) BITALU2(slw, rd, rs, rb, 1) - #define SRW(rd,rs,rb) BITALU2(srw, rd, rs, rb, 0) - #define SRW_(rd,rs,rb) BITALU2(srw, rd, rs, rb, 1) - #define SRAW(rd,rs,rb) BITALU2(sraw, rd, rs, rb, 0) - #define SRAW_(rd,rs,rb) BITALU2(sraw, rd, rs, rb, 1) - #define XOR(rd,rs,rb) BITALU2(xor, rd, rs, rb, 0) - #define XOR_(rd,rs,rb) BITALU2(xor, rd, rs, rb, 1) - - #define SLD(rd,rs,rb) BITALU2(sld, rd, rs, rb, 0) - #define SRD(rd,rs,rb) BITALU2(srd, rd, rs, rb, 0) - #define SRAD(rd,rs,rb) BITALU2(srad, rd, rs, rb, 0) - - #define FADD(rd,ra,rb) FPUAB(fadd, rd, ra, rb, 0) - #define FADD_(rd,ra,rb) FPUAB(fadd, rd, ra, rb, 1) - #define FDIV(rd,ra,rb) FPUAB(fdiv, rd, ra, rb, 0) - #define FDIV_(rd,ra,rb) FPUAB(fdiv, rd, ra, rb, 1) - #define FMUL(rd,ra,rb) FPUAC(fmul, rd, ra, rb, 0) - #define FMUL_(rd,ra,rb) FPUAC(fmul, rd, ra, rb, 1) - #define FSUB(rd,ra,rb) FPUAB(fsub, rd, ra, rb, 0) - #define FSUB_(rd,ra,rb) FPUAB(fsub, rd, ra, rb, 1) - - #define MULLI(rd,ra,simm) EMIT1(PPC_mulli | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(simm),\ - "mulli %s,%s,%d", gpn(rd), gpn(ra), int16_t(simm)) - #define MULLW(rd,ra,rb) EMIT1(PPC_mullw | GPR(rd)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ - "mullw %s,%s,%s", gpn(rd), gpn(ra), gpn(rb)) - - // same as ALU2 with rs=rb, for simplified mnemonics - #define ALU1(op, ra, rs, rc) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rs)<<11 | (rc),\ - "%s%s %s,%s", #op, (rc)?".":"", gpn(ra), gpn(rs)) - - #define MR(rd, rs) ALU1(mr, rd, rs, 0) // or rd,rs,rs - #define MR_(rd, rs) ALU1(mr, rd, rs, 1) // or. rd,rs,rs - #define NOT(rd, rs) ALU1(not, rd, rs, 0) // nor rd,rs,rs - #define NOT_(rd, rs) ALU1(not, rd, rs, 0) // nor. rd,rs,rs - - #define EXTSW(rd, rs) EMIT1(PPC_extsw | GPR(rs)<<21 | GPR(rd)<<16,\ - "extsw %s,%s", gpn(rd), gpn(rs)) - - #define NEG(rd, rs) EMIT1(PPC_neg | GPR(rd)<<21 | GPR(rs)<<16, "neg %s,%s", gpn(rd), gpn(rs)) - #define FNEG(rd,rs) EMIT1(PPC_fneg | FPR(rd)<<21 | FPR(rs)<<11, "fneg %s,%s", gpn(rd), gpn(rs)) - #define FMR(rd,rb) EMIT1(PPC_fmr | FPR(rd)<<21 | FPR(rb)<<11, "fmr %s,%s", gpn(rd), gpn(rb)) - #define FCFID(rd,rs) EMIT1(PPC_fcfid | FPR(rd)<<21 | FPR(rs)<<11, "fcfid %s,%s", gpn(rd), gpn(rs)) - - #define JMP(addr) br(addr, 0) - - #define SPR(spr) (REGNUM(R##spr)>>5|(REGNUM(R##spr)&31)<<5) - #define MTSPR(spr,rs) EMIT1(PPC_mtspr | GPR(rs)<<21 | SPR(spr)<<11,\ - "mt%s %s", #spr, gpn(rs)) - #define MFSPR(rd,spr) EMIT1(PPC_mfspr | GPR(rd)<<21 | SPR(spr)<<11,\ - "mf%s %s", #spr, gpn(rd)) - - #define MTXER(r) MTSPR(xer, r) - #define MTLR(r) MTSPR(lr, r) - #define MTCTR(r) MTSPR(ctr, r) - - #define MFXER(r) MFSPR(r, xer) - #define MFLR(r) MFSPR(r, lr) - #define MFCTR(r) MFSPR(r, ctr) - - #define MEMd(op, r, d, a) do {\ - NanoAssert(isS16(d));\ - EMIT1(PPC_##op | GPR(r)<<21 | GPR(a)<<16 | uint16_t(d), "%s %s,%d(%s)", #op, gpn(r), int16_t(d), gpn(a));\ - } while(0) /* no addr */ - - #define FMEMd(op, r, d, b) do {\ - NanoAssert(isS16(d));\ - EMIT1(PPC_##op | FPR(r)<<21 | GPR(b)<<16 | uint16_t(d), "%s %s,%d(%s)", #op, gpn(r), int16_t(d), gpn(b));\ - } while(0) /* no addr */ - - #define MEMx(op, r, a, b) EMIT1(PPC_##op | GPR(r)<<21 | GPR(a)<<16 | GPR(b)<<11,\ - "%s %s,%s,%s", #op, gpn(r), gpn(a), gpn(b)) - #define FMEMx(op, r, a, b) EMIT1(PPC_##op | FPR(r)<<21 | GPR(a)<<16 | GPR(b)<<11,\ - "%s %s,%s,%s", #op, gpn(r), gpn(a), gpn(b)) - - #define MEMux(op, rs, ra, rb) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ - "%s %s,%s,%s", #op, gpn(rs), gpn(ra), gpn(rb)) - - #define LBZ(r, d, b) MEMd(lbz, r, d, b) - #define LHZ(r, d, b) MEMd(lhz, r, d, b) - #define LWZ(r, d, b) MEMd(lwz, r, d, b) - #define LD(r, d, b) MEMd(ld, r, d, b) - #define LBZX(r, a, b) MEMx(lbzx, r, a, b) - #define LHZX(r, a, b) MEMx(lhzx, r, a, b) - #define LWZX(r, a, b) MEMx(lwzx, r, a, b) - #define LDX(r, a, b) MEMx(ldx, r, a, b) - - // store word (32-bit integer) - #define STW(r, d, b) MEMd(stw, r, d, b) - #define STWU(r, d, b) MEMd(stwu, r, d, b) - #define STWX(s, a, b) MEMx(stwx, s, a, b) - #define STWUX(s, a, b) MEMux(stwux, s, a, b) - - // store byte - #define STB(r, d, b) MEMd(stb, r, d, b) - #define STBX(s, a, b) MEMx(stbx, s, a, b) - - // store double (64-bit float) - #define STD(r, d, b) MEMd(std, r, d, b) - #define STDU(r, d, b) MEMd(stdu, r, d, b) - #define STDX(s, a, b) MEMx(stdx, s, a, b) - #define STDUX(s, a, b) MEMux(stdux, s, a, b) - -#ifdef NANOJIT_64BIT - #define LP(r, d, b) LD(r, d, b) - #define STP(r, d, b) STD(r, d, b) - #define STPU(r, d, b) STDU(r, d, b) - #define STPX(s, a, b) STDX(s, a, b) - #define STPUX(s, a, b) STDUX(s, a, b) -#else - #define LP(r, d, b) LWZ(r, d, b) - #define STP(r, d, b) STW(r, d, b) - #define STPU(r, d, b) STWU(r, d, b) - #define STPX(s, a, b) STWX(s, a, b) - #define STPUX(s, a, b) STWUX(s, a, b) -#endif - - #define LFD(r, d, b) FMEMd(lfd, r, d, b) - #define LFDX(r, a, b) FMEMx(lfdx, r, a, b) - #define STFD(r, d, b) FMEMd(stfd, r, d, b) - #define STFDX(s, a, b) FMEMx(stfdx, s, a, b) - - #define ALUI(op,rd,ra,d) EMIT1(PPC_##op | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(d),\ - "%s %s,%s,%d (0x%x)", #op, gpn(rd), gpn(ra), int16_t(d), int16_t(d)) - - #define ADDI(rd,ra,d) ALUI(addi, rd, ra, d) - #define ADDIS(rd,ra,d) ALUI(addis, rd, ra, d) - - // bitwise operators have different src/dest registers - #define BITALUI(op,rd,ra,d) EMIT1(PPC_##op | GPR(ra)<<21 | GPR(rd)<<16 | uint16_t(d),\ - "%s %s,%s,%u (0x%x)", #op, gpn(rd), gpn(ra), uint16_t(d), uint16_t(d)) - - #define ANDI(rd,ra,d) BITALUI(andi, rd, ra, d) - #define ORI(rd,ra,d) BITALUI(ori, rd, ra, d) - #define ORIS(rd,ra,d) BITALUI(oris, rd, ra, d) - #define XORI(rd,ra,d) BITALUI(xori, rd, ra, d) - #define XORIS(rd,ra,d) BITALUI(xoris, rd, ra, d) - - #define SUBI(rd,ra,d) EMIT1(PPC_addi | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(-(d)),\ - "subi %s,%s,%d", gpn(rd), gpn(ra), (d)) - - #define LI(rd,v) EMIT1(PPC_addi | GPR(rd)<<21 | uint16_t(v),\ - "li %s,%d (0x%x)", gpn(rd), int16_t(v), int16_t(v)) /* addi rd,0,v */ - - #define LIS(rd,v) EMIT1(PPC_addis | GPR(rd)<<21 | uint16_t(v),\ - "lis %s,%d (0x%x)", gpn(rd), int16_t(v), int16_t(v)<<16) /* addis, rd,0,v */ - - #define MTCR(rs) /* mtcrf 0xff,rs */ - #define MFCR(rd) EMIT1(PPC_mfcr | GPR(rd)<<21, "mfcr %s", gpn(rd)) - - #define CMPx(op, crfd, ra, rb, l) EMIT1(PPC_##op | (crfd)<<23 | (l)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ - "%s%c cr%d,%s,%s", #op, (l)?'d':'w', (crfd), gpn(ra), gpn(rb)) - - #define CMPW(cr, ra, rb) CMPx(cmp, cr, ra, rb, 0) - #define CMPLW(cr, ra, rb) CMPx(cmpl, cr, ra, rb, 0) - #define CMPD(cr, ra, rb) CMPx(cmp, cr, ra, rb, 1) - #define CMPLD(cr, ra, rb) CMPx(cmpl, cr, ra, rb, 1) - - #define CMPxI(cr, ra, simm, l) EMIT1(PPC_cmpi | (cr)<<23 | (l)<<21 | GPR(ra)<<16 | uint16_t(simm),\ - "cmp%ci cr%d,%s,%d (0x%x)", (l)?'d':'w', (cr), gpn(ra), int16_t(simm), int16_t(simm)) - - #define CMPWI(cr, ra, simm) CMPxI(cr, ra, simm, 0) - #define CMPDI(cr, ra, simm) CMPxI(cr, ra, simm, 1) - - #define CMPLxI(cr, ra, uimm, l) EMIT1(PPC_cmpli | (cr)<<23 | (l)<<21 | GPR(ra)<<16 | uint16_t(uimm),\ - "cmp%ci cr%d,%s,%d (0x%x)", (l)?'d':'w', (cr), gpn(ra), uint16_t(uimm), uint16_t(uimm)) - - #define CMPLWI(cr, ra, uimm) CMPLxI(cr, ra, uimm, 0) - #define CMPLDI(cr, ra, uimm) CMPLxI(cr, ra, uimm, 1) - - #define FCMPx(op, crfd, ra, rb) EMIT1(PPC_##op | (crfd)<<23 | FPR(ra)<<16 | FPR(rb)<<11,\ - "%s cr%d,%s,%s", #op, (crfd), gpn(ra), gpn(rb)) - - #define FCMPU(cr, ra, rb) FCMPx(fcmpu, cr, ra, rb) - - #define CROR(cr,d,a,b) EMIT1(PPC_cror | (4*(cr)+COND_##d)<<21 | (4*(cr)+COND_##a)<<16 | (4*(cr)+COND_##b)<<11,\ - "cror %d,%d,%d", 4*(cr)+COND_##d, 4*(cr)+COND_##a, 4*(cr)+COND_##b) - - #define RLWINM(rd,rs,sh,mb,me) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (sh)<<11 | (mb)<<6 | (me)<<1,\ - "rlwinm %s,%s,%d,%d,%d", gpn(rd), gpn(rs), (sh), (mb), (me)) - - #define LO5(sh) ((sh) & 31) - #define BIT6(sh) (((sh) >> 5) & 1) - #define SPLITMB(mb) (LO5(mb)<<1 | BIT6(mb)) - - #define RLDICL(rd,rs,sh,mb) \ - EMIT1(PPC_rldicl | GPR(rs)<<21 | GPR(rd)<<16 | LO5(sh)<<11 | SPLITMB(mb)<<5 | BIT6(sh)<<1,\ - "rldicl %s,%s,%d,%d", gpn(rd), gpn(rs), (sh), (mb)) - - // clrldi d,s,n => rldicl d,s,0,n - #define CLRLDI(rd,rs,n) \ - EMIT1(PPC_rldicl | GPR(rs)<<21 | GPR(rd)<<16 | SPLITMB(n)<<5,\ - "clrldi %s,%s,%d", gpn(rd), gpn(rs), (n)) - - #define RLDIMI(rd,rs,sh,mb) \ - EMIT1(PPC_rldimi | GPR(rs)<<21 | GPR(rd)<<16 | LO5(sh)<<11 | SPLITMB(mb)<<5 | BIT6(sh)<<1,\ - "rldimi %s,%s,%d,%d", gpn(rd), gpn(rs), (sh), (mb)) - - // insrdi rD,rS,n,b => rldimi rD,rS,64-(b+n),b: insert n bit value into rD starting at b - #define INSRDI(rd,rs,n,b) \ - EMIT1(PPC_rldimi | GPR(rs)<<21 | GPR(rd)<<16 | LO5(64-((b)+(n)))<<11 | SPLITMB(b)<<5 | BIT6(64-((b)+(n)))<<1,\ - "insrdi %s,%s,%d,%d", gpn(rd), gpn(rs), (n), (b)) - - #define EXTRWI(rd,rs,n,b) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | ((n)+(b))<<11 | (32-(n))<<6 | 31<<1,\ - "extrwi %s,%s,%d,%d", gpn(rd), gpn(rs), (n), (b)) - - // sldi rd,rs,n (n<64) => rldicr rd,rs,n,63-n - #define SLDI(rd,rs,n) EMIT1(PPC_rldicr | GPR(rs)<<21 | GPR(rd)<<16 | LO5(n)<<11 | SPLITMB(63-(n))<<5 | BIT6(n)<<1,\ - "sldi %s,%s,%d", gpn(rd), gpn(rs), (n)) - - #define SLWI(rd,rs,n) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (n)<<11 | 0<<6 | (31-(n))<<1,\ - "slwi %s,%s,%d", gpn(rd), gpn(rs), (n)) - #define SRWI(rd,rs,n) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (32-(n))<<11 | (n)<<6 | 31<<1,\ - "slwi %s,%s,%d", gpn(rd), gpn(rs), (n)) - #define SRAWI(rd,rs,n) EMIT1(PPC_srawi | GPR(rs)<<21 | GPR(rd)<<16 | (n)<<11,\ - "srawi %s,%s,%d", gpn(rd), gpn(rs), (n)) - -} // namespace nanojit - -#endif // __nanojit_NativePPC__ diff --git a/deps/mozjs/js/src/nanojit/NativeSH4-auto-generated.h b/deps/mozjs/js/src/nanojit/NativeSH4-auto-generated.h deleted file mode 100644 index 8d8d8b2fd6f..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeSH4-auto-generated.h +++ /dev/null @@ -1,1726 +0,0 @@ -/* THIS FILE IS AUTO-GENERATED */ - -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * STMicroelectronics. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Cédric VINCENT for STMicroelectronics - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#define SH4_CHECK_RANGE_add_imm(imm) ((imm) >= -128 && (imm) <= 127) - -#define FITS_SH4_add_imm(imm) (SH4_CHECK_RANGE_add_imm(imm)) - - inline void Assembler::SH4_add_imm(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_add_imm(imm)); - SH4_emit16((0x7 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0xFF) << 0)); - asm_output("add_imm %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_add(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xC << 0)); - asm_output("add R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_addc(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xE << 0)); - asm_output("addc R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_addv(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("addv R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_and_imm_R0(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_and_imm_R0(imm) (SH4_CHECK_RANGE_and_imm_R0(imm)) - - inline void Assembler::SH4_and_imm_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_and_imm_R0(imm)); - SH4_emit16((0xC << 12) | (0x9 << 8) | ((imm & 0xFF) << 0)); - asm_output("and_imm_R0 %d", imm); - } - - inline void Assembler::SH4_and(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("and R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_andb_imm_dispR0GBR(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_andb_imm_dispR0GBR(imm) (SH4_CHECK_RANGE_andb_imm_dispR0GBR(imm)) - - inline void Assembler::SH4_andb_imm_dispR0GBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_andb_imm_dispR0GBR(imm)); - SH4_emit16((0xC << 12) | (0xD << 8) | ((imm & 0xFF) << 0)); - asm_output("andb_imm_dispR0GBR %d", imm); - } - -#define SH4_CHECK_RANGE_bra(imm) ((imm) >= -4096 && (imm) <= 4094) - -#define SH4_CHECK_ALIGN_bra(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bra(imm) (SH4_CHECK_RANGE_bra(imm) && SH4_CHECK_ALIGN_bra(imm)) - - inline void Assembler::SH4_bra(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bra(imm) && SH4_CHECK_ALIGN_bra(imm)); - SH4_emit16((0xA << 12) | (((imm & 0x1FFE) >> 1) << 0)); - asm_output("bra %d", imm); - } - -#define SH4_CHECK_RANGE_bsr(imm) ((imm) >= -4096 && (imm) <= 4094) - -#define SH4_CHECK_ALIGN_bsr(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bsr(imm) (SH4_CHECK_RANGE_bsr(imm) && SH4_CHECK_ALIGN_bsr(imm)) - - inline void Assembler::SH4_bsr(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bsr(imm) && SH4_CHECK_ALIGN_bsr(imm)); - SH4_emit16((0xB << 12) | (((imm & 0x1FFE) >> 1) << 0)); - asm_output("bsr %d", imm); - } - -#define SH4_CHECK_RANGE_bt(imm) ((imm) >= -256 && (imm) <= 254) - -#define SH4_CHECK_ALIGN_bt(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bt(imm) (SH4_CHECK_RANGE_bt(imm) && SH4_CHECK_ALIGN_bt(imm)) - - inline void Assembler::SH4_bt(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bt(imm) && SH4_CHECK_ALIGN_bt(imm)); - SH4_emit16((0x8 << 12) | (0x9 << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("bt %d", imm); - } - -#define SH4_CHECK_RANGE_bf(imm) ((imm) >= -256 && (imm) <= 254) - -#define SH4_CHECK_ALIGN_bf(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bf(imm) (SH4_CHECK_RANGE_bf(imm) && SH4_CHECK_ALIGN_bf(imm)) - - inline void Assembler::SH4_bf(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bf(imm) && SH4_CHECK_ALIGN_bf(imm)); - SH4_emit16((0x8 << 12) | (0xB << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("bf %d", imm); - } - -#define SH4_CHECK_RANGE_bts(imm) ((imm) >= -256 && (imm) <= 254) - -#define SH4_CHECK_ALIGN_bts(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bts(imm) (SH4_CHECK_RANGE_bts(imm) && SH4_CHECK_ALIGN_bts(imm)) - - inline void Assembler::SH4_bts(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bts(imm) && SH4_CHECK_ALIGN_bts(imm)); - SH4_emit16((0x8 << 12) | (0xD << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("bts %d", imm); - } - -#define SH4_CHECK_RANGE_bfs(imm) ((imm) >= -256 && (imm) <= 254) - -#define SH4_CHECK_ALIGN_bfs(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_bfs(imm) (SH4_CHECK_RANGE_bfs(imm) && SH4_CHECK_ALIGN_bfs(imm)) - - inline void Assembler::SH4_bfs(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_bfs(imm) && SH4_CHECK_ALIGN_bfs(imm)); - SH4_emit16((0x8 << 12) | (0xF << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("bfs %d", imm); - } - - inline void Assembler::SH4_clrmac() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x2 << 4) | (0x8 << 0)); - asm_output("clrmac"); - } - - inline void Assembler::SH4_clrs() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x4 << 4) | (0x8 << 0)); - asm_output("clrs"); - } - - inline void Assembler::SH4_clrt() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x0 << 4) | (0x8 << 0)); - asm_output("clrt"); - } - -#define SH4_CHECK_RANGE_cmpeq_imm_R0(imm) ((imm) >= -128 && (imm) <= 127) - -#define FITS_SH4_cmpeq_imm_R0(imm) (SH4_CHECK_RANGE_cmpeq_imm_R0(imm)) - - inline void Assembler::SH4_cmpeq_imm_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_cmpeq_imm_R0(imm)); - SH4_emit16((0x8 << 12) | (0x8 << 8) | ((imm & 0xFF) << 0)); - asm_output("cmpeq_imm_R0 %d", imm); - } - - inline void Assembler::SH4_cmpeq(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x0 << 0)); - asm_output("cmpeq R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_cmpge(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x3 << 0)); - asm_output("cmpge R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_cmpgt(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x7 << 0)); - asm_output("cmpgt R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_cmphi(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("cmphi R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_cmphs(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x2 << 0)); - asm_output("cmphs R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_cmppl(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x5 << 0)); - asm_output("cmppl R%d", Rx.n); - } - - inline void Assembler::SH4_cmppz(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x1 << 0)); - asm_output("cmppz R%d", Rx.n); - } - - inline void Assembler::SH4_cmpstr(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xC << 0)); - asm_output("cmpstr R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_div0s(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x7 << 0)); - asm_output("div0s R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_div0u() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x1 << 4) | (0x9 << 0)); - asm_output("div0u"); - } - - inline void Assembler::SH4_div1(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x4 << 0)); - asm_output("div1 R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_extsb(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xE << 0)); - asm_output("extsb R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_extsw(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("extsw R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_extub(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xC << 0)); - asm_output("extub R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_extuw(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xD << 0)); - asm_output("extuw R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_icbi_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xE << 4) | (0x3 << 0)); - asm_output("icbi_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_jmp_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0xB << 0)); - asm_output("jmp_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_jsr_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0xB << 0)); - asm_output("jsr_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_SR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0xE << 0)); - asm_output("ldc_SR R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_GBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0xE << 0)); - asm_output("ldc_GBR R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_SGR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0xA << 0)); - asm_output("ldc_SGR R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_VBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0xE << 0)); - asm_output("ldc_VBR R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_SSR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0xE << 0)); - asm_output("ldc_SSR R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_SPC(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x4 << 4) | (0xE << 0)); - asm_output("ldc_SPC R%d", Rx.n); - } - - inline void Assembler::SH4_ldc_DBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xF << 4) | (0xA << 0)); - asm_output("ldc_DBR R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_ldc_bank(imm) ((imm) >= 0 && (imm) <= 7) - -#define FITS_SH4_ldc_bank(imm) (SH4_CHECK_RANGE_ldc_bank(imm)) - - inline void Assembler::SH4_ldc_bank(Register Rx, int imm) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_ldc_bank(imm)); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0x7) << 4) | (0xE << 0)); - asm_output("ldc_bank R%d, %d", Rx.n, imm); - } - - inline void Assembler::SH4_ldcl_incRx_SR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_SR R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_GBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_GBR R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_VBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_VBR R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_SGR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0x6 << 0)); - asm_output("ldcl_incRx_SGR R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_SSR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_SSR R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_SPC(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x4 << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_SPC R%d", Rx.n); - } - - inline void Assembler::SH4_ldcl_incRx_DBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xF << 4) | (0x6 << 0)); - asm_output("ldcl_incRx_DBR R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_ldcl_incRx_bank(imm) ((imm) >= 0 && (imm) <= 7) - -#define FITS_SH4_ldcl_incRx_bank(imm) (SH4_CHECK_RANGE_ldcl_incRx_bank(imm)) - - inline void Assembler::SH4_ldcl_incRx_bank(Register Rx, int imm) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_ldcl_incRx_bank(imm)); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0x7) << 4) | (0x7 << 0)); - asm_output("ldcl_incRx_bank R%d, %d", Rx.n, imm); - } - - inline void Assembler::SH4_lds_MACH(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0xA << 0)); - asm_output("lds_MACH R%d", Rx.n); - } - - inline void Assembler::SH4_lds_MACL(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0xA << 0)); - asm_output("lds_MACL R%d", Rx.n); - } - - inline void Assembler::SH4_lds_PR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0xA << 0)); - asm_output("lds_PR R%d", Rx.n); - } - - inline void Assembler::SH4_lds_FPUL(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0x5 << 4) | (0xA << 0)); - asm_output("lds_FPUL R%d", Ry.n); - } - - inline void Assembler::SH4_lds_FPSCR(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0x6 << 4) | (0xA << 0)); - asm_output("lds_FPSCR R%d", Ry.n); - } - - inline void Assembler::SH4_ldsl_incRx_MACH(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x6 << 0)); - asm_output("ldsl_incRx_MACH R%d", Rx.n); - } - - inline void Assembler::SH4_ldsl_incRx_MACL(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x6 << 0)); - asm_output("ldsl_incRx_MACL R%d", Rx.n); - } - - inline void Assembler::SH4_ldsl_incRx_PR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x6 << 0)); - asm_output("ldsl_incRx_PR R%d", Rx.n); - } - - inline void Assembler::SH4_ldsl_incRy_FPUL(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0x5 << 4) | (0x6 << 0)); - asm_output("ldsl_incRy_FPUL R%d", Ry.n); - } - - inline void Assembler::SH4_ldsl_incRy_FPSCR(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0x6 << 4) | (0x6 << 0)); - asm_output("ldsl_incRy_FPSCR R%d", Ry.n); - } - - inline void Assembler::SH4_ldtlb() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x3 << 4) | (0x8 << 0)); - asm_output("ldtlb"); - } - - inline void Assembler::SH4_macw_incRy_incRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("macw_incRy_incRx R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_mov_imm(imm) ((imm) >= -128 && (imm) <= 127) - -#define FITS_SH4_mov_imm(imm) (SH4_CHECK_RANGE_mov_imm(imm)) - - inline void Assembler::SH4_mov_imm(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_mov_imm(imm)); - SH4_emit16((0xE << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0xFF) << 0)); - asm_output("mov_imm %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_mov(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x3 << 0)); - asm_output("mov R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movb_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x4 << 0)); - asm_output("movb_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movb_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x4 << 0)); - asm_output("movb_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movb_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x0 << 0)); - asm_output("movb_indRx R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movb_dispRy_R0(imm) ((imm) >= 0 && (imm) <= 15) - -#define FITS_SH4_movb_dispRy_R0(imm) (SH4_CHECK_RANGE_movb_dispRy_R0(imm)) - - inline void Assembler::SH4_movb_dispRy_R0(int imm, Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15 && SH4_CHECK_RANGE_movb_dispRy_R0(imm)); - SH4_emit16((0x8 << 12) | (0x4 << 8) | ((REGNUM(Ry) & 0xF) << 4) | ((imm & 0xF) << 0)); - asm_output("movb_dispRy_R0 %d, R%d", imm, Ry.n); - } - -#define SH4_CHECK_RANGE_movb_dispGBR_R0(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_movb_dispGBR_R0(imm) (SH4_CHECK_RANGE_movb_dispGBR_R0(imm)) - - inline void Assembler::SH4_movb_dispGBR_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movb_dispGBR_R0(imm)); - SH4_emit16((0xC << 12) | (0x4 << 8) | ((imm & 0xFF) << 0)); - asm_output("movb_dispGBR_R0 %d", imm); - } - - inline void Assembler::SH4_movb_dispR0Ry(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xC << 0)); - asm_output("movb_dispR0Ry R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movb_incRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x4 << 0)); - asm_output("movb_incRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movb_indRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x0 << 0)); - asm_output("movb_indRy R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movb_R0_dispRx(imm) ((imm) >= 0 && (imm) <= 15) - -#define FITS_SH4_movb_R0_dispRx(imm) (SH4_CHECK_RANGE_movb_R0_dispRx(imm)) - - inline void Assembler::SH4_movb_R0_dispRx(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_movb_R0_dispRx(imm)); - SH4_emit16((0x8 << 12) | (0x0 << 8) | ((REGNUM(Rx) & 0xF) << 4) | ((imm & 0xF) << 0)); - asm_output("movb_R0_dispRx %d, R%d", imm, Rx.n); - } - -#define SH4_CHECK_RANGE_movb_R0_dispGBR(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_movb_R0_dispGBR(imm) (SH4_CHECK_RANGE_movb_R0_dispGBR(imm)) - - inline void Assembler::SH4_movb_R0_dispGBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movb_R0_dispGBR(imm)); - SH4_emit16((0xC << 12) | (0x0 << 8) | ((imm & 0xFF) << 0)); - asm_output("movb_R0_dispGBR %d", imm); - } - -#define SH4_CHECK_RANGE_movl_dispRx(imm) ((imm) >= 0 && (imm) <= 60) - -#define SH4_CHECK_ALIGN_movl_dispRx(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_movl_dispRx(imm) (SH4_CHECK_RANGE_movl_dispRx(imm) && SH4_CHECK_ALIGN_movl_dispRx(imm)) - - inline void Assembler::SH4_movl_dispRx(Register Ry, int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15 && SH4_CHECK_RANGE_movl_dispRx(imm) && SH4_CHECK_ALIGN_movl_dispRx(imm)); - SH4_emit16((0x1 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (((imm & 0x3C) >> 2) << 0)); - asm_output("movl_dispRx R%d, %d, R%d", Ry.n, imm, Rx.n); - } - - inline void Assembler::SH4_movl_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("movl_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movl_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("movl_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movl_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x2 << 0)); - asm_output("movl_indRx R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movl_dispRy(imm) ((imm) >= 0 && (imm) <= 60) - -#define SH4_CHECK_ALIGN_movl_dispRy(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_movl_dispRy(imm) (SH4_CHECK_RANGE_movl_dispRy(imm) && SH4_CHECK_ALIGN_movl_dispRy(imm)) - - inline void Assembler::SH4_movl_dispRy(int imm, Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15 && SH4_CHECK_RANGE_movl_dispRy(imm) && SH4_CHECK_ALIGN_movl_dispRy(imm)); - SH4_emit16((0x5 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (((imm & 0x3C) >> 2) << 0)); - asm_output("movl_dispRy %d, R%d, R%d", imm, Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movl_dispGBR_R0(imm) ((imm) >= 0 && (imm) <= 1020) - -#define SH4_CHECK_ALIGN_movl_dispGBR_R0(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_movl_dispGBR_R0(imm) (SH4_CHECK_RANGE_movl_dispGBR_R0(imm) && SH4_CHECK_ALIGN_movl_dispGBR_R0(imm)) - - inline void Assembler::SH4_movl_dispGBR_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movl_dispGBR_R0(imm) && SH4_CHECK_ALIGN_movl_dispGBR_R0(imm)); - SH4_emit16((0xC << 12) | (0x6 << 8) | (((imm & 0x3FC) >> 2) << 0)); - asm_output("movl_dispGBR_R0 %d", imm); - } - -#define SH4_CHECK_RANGE_movl_dispPC(imm) ((imm) >= 0 && (imm) <= 1020) - -#define SH4_CHECK_ALIGN_movl_dispPC(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_movl_dispPC(imm) (SH4_CHECK_RANGE_movl_dispPC(imm) && SH4_CHECK_ALIGN_movl_dispPC(imm)) - - inline void Assembler::SH4_movl_dispPC(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_movl_dispPC(imm) && SH4_CHECK_ALIGN_movl_dispPC(imm)); - SH4_emit16((0xD << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((imm & 0x3FC) >> 2) << 0)); - asm_output("movl_dispPC %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_movl_dispR0Ry(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xE << 0)); - asm_output("movl_dispR0Ry R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movl_incRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("movl_incRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movl_indRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x2 << 0)); - asm_output("movl_indRy R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movl_R0_dispGBR(imm) ((imm) >= 0 && (imm) <= 1020) - -#define SH4_CHECK_ALIGN_movl_R0_dispGBR(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_movl_R0_dispGBR(imm) (SH4_CHECK_RANGE_movl_R0_dispGBR(imm) && SH4_CHECK_ALIGN_movl_R0_dispGBR(imm)) - - inline void Assembler::SH4_movl_R0_dispGBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movl_R0_dispGBR(imm) && SH4_CHECK_ALIGN_movl_R0_dispGBR(imm)); - SH4_emit16((0xC << 12) | (0x2 << 8) | (((imm & 0x3FC) >> 2) << 0)); - asm_output("movl_R0_dispGBR %d", imm); - } - - inline void Assembler::SH4_movw_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x5 << 0)); - asm_output("movw_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movw_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x5 << 0)); - asm_output("movw_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movw_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x1 << 0)); - asm_output("movw_indRx R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movw_dispRy_R0(imm) ((imm) >= 0 && (imm) <= 30) - -#define SH4_CHECK_ALIGN_movw_dispRy_R0(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_movw_dispRy_R0(imm) (SH4_CHECK_RANGE_movw_dispRy_R0(imm) && SH4_CHECK_ALIGN_movw_dispRy_R0(imm)) - - inline void Assembler::SH4_movw_dispRy_R0(int imm, Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15 && SH4_CHECK_RANGE_movw_dispRy_R0(imm) && SH4_CHECK_ALIGN_movw_dispRy_R0(imm)); - SH4_emit16((0x8 << 12) | (0x5 << 8) | ((REGNUM(Ry) & 0xF) << 4) | (((imm & 0x1E) >> 1) << 0)); - asm_output("movw_dispRy_R0 %d, R%d", imm, Ry.n); - } - -#define SH4_CHECK_RANGE_movw_dispGBR_R0(imm) ((imm) >= 0 && (imm) <= 510) - -#define SH4_CHECK_ALIGN_movw_dispGBR_R0(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_movw_dispGBR_R0(imm) (SH4_CHECK_RANGE_movw_dispGBR_R0(imm) && SH4_CHECK_ALIGN_movw_dispGBR_R0(imm)) - - inline void Assembler::SH4_movw_dispGBR_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movw_dispGBR_R0(imm) && SH4_CHECK_ALIGN_movw_dispGBR_R0(imm)); - SH4_emit16((0xC << 12) | (0x5 << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("movw_dispGBR_R0 %d", imm); - } - -#define SH4_CHECK_RANGE_movw_dispPC(imm) ((imm) >= 0 && (imm) <= 510) - -#define SH4_CHECK_ALIGN_movw_dispPC(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_movw_dispPC(imm) (SH4_CHECK_RANGE_movw_dispPC(imm) && SH4_CHECK_ALIGN_movw_dispPC(imm)) - - inline void Assembler::SH4_movw_dispPC(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_movw_dispPC(imm) && SH4_CHECK_ALIGN_movw_dispPC(imm)); - SH4_emit16((0x9 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("movw_dispPC %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_movw_dispR0Ry(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xD << 0)); - asm_output("movw_dispR0Ry R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movw_incRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x5 << 0)); - asm_output("movw_incRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_movw_indRy(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x1 << 0)); - asm_output("movw_indRy R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_movw_R0_dispRx(imm) ((imm) >= 0 && (imm) <= 30) - -#define SH4_CHECK_ALIGN_movw_R0_dispRx(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_movw_R0_dispRx(imm) (SH4_CHECK_RANGE_movw_R0_dispRx(imm) && SH4_CHECK_ALIGN_movw_R0_dispRx(imm)) - - inline void Assembler::SH4_movw_R0_dispRx(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_movw_R0_dispRx(imm) && SH4_CHECK_ALIGN_movw_R0_dispRx(imm)); - SH4_emit16((0x8 << 12) | (0x1 << 8) | ((REGNUM(Rx) & 0xF) << 4) | (((imm & 0x1E) >> 1) << 0)); - asm_output("movw_R0_dispRx %d, R%d", imm, Rx.n); - } - -#define SH4_CHECK_RANGE_movw_R0_dispGBR(imm) ((imm) >= 0 && (imm) <= 510) - -#define SH4_CHECK_ALIGN_movw_R0_dispGBR(imm) (((imm) & 0x1) == 0) - -#define FITS_SH4_movw_R0_dispGBR(imm) (SH4_CHECK_RANGE_movw_R0_dispGBR(imm) && SH4_CHECK_ALIGN_movw_R0_dispGBR(imm)) - - inline void Assembler::SH4_movw_R0_dispGBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_movw_R0_dispGBR(imm) && SH4_CHECK_ALIGN_movw_R0_dispGBR(imm)); - SH4_emit16((0xC << 12) | (0x1 << 8) | (((imm & 0x1FE) >> 1) << 0)); - asm_output("movw_R0_dispGBR %d", imm); - } - -#define SH4_CHECK_RANGE_mova_dispPC_R0(imm) ((imm) >= 0 && (imm) <= 1020) - -#define SH4_CHECK_ALIGN_mova_dispPC_R0(imm) (((imm) & 0x3) == 0) - -#define FITS_SH4_mova_dispPC_R0(imm) (SH4_CHECK_RANGE_mova_dispPC_R0(imm) && SH4_CHECK_ALIGN_mova_dispPC_R0(imm)) - - inline void Assembler::SH4_mova_dispPC_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_mova_dispPC_R0(imm) && SH4_CHECK_ALIGN_mova_dispPC_R0(imm)); - SH4_emit16((0xC << 12) | (0x7 << 8) | (((imm & 0x3FC) >> 2) << 0)); - asm_output("mova_dispPC_R0 %d", imm); - } - - inline void Assembler::SH4_movcal_R0_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xC << 4) | (0x3 << 0)); - asm_output("movcal_R0_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_movcol_R0_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x7 << 4) | (0x3 << 0)); - asm_output("movcol_R0_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_movlil_indRy_R0(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0x6 << 4) | (0x3 << 0)); - asm_output("movlil_indRy_R0 R%d", Ry.n); - } - - inline void Assembler::SH4_movt(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x9 << 0)); - asm_output("movt R%d", Rx.n); - } - - inline void Assembler::SH4_movual_indRy_R0(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0xA << 4) | (0x9 << 0)); - asm_output("movual_indRy_R0 R%d", Ry.n); - } - - inline void Assembler::SH4_movual_incRy_R0(Register Ry) { - NanoAssert(1 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Ry) & 0xF) << 8) | (0xE << 4) | (0x9 << 0)); - asm_output("movual_incRy_R0 R%d", Ry.n); - } - - inline void Assembler::SH4_mulsw(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("mulsw R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_muls(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("muls R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_mull(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x7 << 0)); - asm_output("mull R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_muluw(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xE << 0)); - asm_output("muluw R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_mulu(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xE << 0)); - asm_output("mulu R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_neg(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xB << 0)); - asm_output("neg R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_negc(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xA << 0)); - asm_output("negc R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_nop() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x0 << 4) | (0x9 << 0)); - asm_output("nop"); - } - - inline void Assembler::SH4_not(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x7 << 0)); - asm_output("not R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_ocbi_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x9 << 4) | (0x3 << 0)); - asm_output("ocbi_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_ocbp_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xA << 4) | (0x3 << 0)); - asm_output("ocbp_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_ocbwb_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xB << 4) | (0x3 << 0)); - asm_output("ocbwb_indRx R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_or_imm_R0(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_or_imm_R0(imm) (SH4_CHECK_RANGE_or_imm_R0(imm)) - - inline void Assembler::SH4_or_imm_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_or_imm_R0(imm)); - SH4_emit16((0xC << 12) | (0xB << 8) | ((imm & 0xFF) << 0)); - asm_output("or_imm_R0 %d", imm); - } - - inline void Assembler::SH4_or(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xB << 0)); - asm_output("or R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_orb_imm_dispR0GBR(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_orb_imm_dispR0GBR(imm) (SH4_CHECK_RANGE_orb_imm_dispR0GBR(imm)) - - inline void Assembler::SH4_orb_imm_dispR0GBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_orb_imm_dispR0GBR(imm)); - SH4_emit16((0xC << 12) | (0xF << 8) | ((imm & 0xFF) << 0)); - asm_output("orb_imm_dispR0GBR %d", imm); - } - - inline void Assembler::SH4_pref_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x8 << 4) | (0x3 << 0)); - asm_output("pref_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_prefi_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xD << 4) | (0x3 << 0)); - asm_output("prefi_indRx R%d", Rx.n); - } - - inline void Assembler::SH4_rotcl(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x4 << 0)); - asm_output("rotcl R%d", Rx.n); - } - - inline void Assembler::SH4_rotcr(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x5 << 0)); - asm_output("rotcr R%d", Rx.n); - } - - inline void Assembler::SH4_rotl(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x4 << 0)); - asm_output("rotl R%d", Rx.n); - } - - inline void Assembler::SH4_rotr(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x5 << 0)); - asm_output("rotr R%d", Rx.n); - } - - inline void Assembler::SH4_rte() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x2 << 4) | (0xB << 0)); - asm_output("rte"); - } - - inline void Assembler::SH4_rts() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x0 << 4) | (0xB << 0)); - asm_output("rts"); - } - - inline void Assembler::SH4_sets() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x5 << 4) | (0x8 << 0)); - asm_output("sets"); - } - - inline void Assembler::SH4_sett() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x1 << 4) | (0x8 << 0)); - asm_output("sett"); - } - - inline void Assembler::SH4_shad(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xC << 0)); - asm_output("shad R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_shld(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xD << 0)); - asm_output("shld R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_shal(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x0 << 0)); - asm_output("shal R%d", Rx.n); - } - - inline void Assembler::SH4_shar(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x1 << 0)); - asm_output("shar R%d", Rx.n); - } - - inline void Assembler::SH4_shll(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x0 << 0)); - asm_output("shll R%d", Rx.n); - } - - inline void Assembler::SH4_shll16(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x8 << 0)); - asm_output("shll16 R%d", Rx.n); - } - - inline void Assembler::SH4_shll2(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x8 << 0)); - asm_output("shll2 R%d", Rx.n); - } - - inline void Assembler::SH4_shll8(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x8 << 0)); - asm_output("shll8 R%d", Rx.n); - } - - inline void Assembler::SH4_shlr(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x1 << 0)); - asm_output("shlr R%d", Rx.n); - } - - inline void Assembler::SH4_shlr16(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x9 << 0)); - asm_output("shlr16 R%d", Rx.n); - } - - inline void Assembler::SH4_shlr2(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x9 << 0)); - asm_output("shlr2 R%d", Rx.n); - } - - inline void Assembler::SH4_shlr8(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x9 << 0)); - asm_output("shlr8 R%d", Rx.n); - } - - inline void Assembler::SH4_sleep() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0x1 << 4) | (0xB << 0)); - asm_output("sleep"); - } - - inline void Assembler::SH4_stc_SR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x2 << 0)); - asm_output("stc_SR R%d", Rx.n); - } - - inline void Assembler::SH4_stc_GBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x2 << 0)); - asm_output("stc_GBR R%d", Rx.n); - } - - inline void Assembler::SH4_stc_VBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x2 << 0)); - asm_output("stc_VBR R%d", Rx.n); - } - - inline void Assembler::SH4_stc_SSR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0x2 << 0)); - asm_output("stc_SSR R%d", Rx.n); - } - - inline void Assembler::SH4_stc_SPC(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x4 << 4) | (0x2 << 0)); - asm_output("stc_SPC R%d", Rx.n); - } - - inline void Assembler::SH4_stc_SGR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0xA << 0)); - asm_output("stc_SGR R%d", Rx.n); - } - - inline void Assembler::SH4_stc_DBR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xF << 4) | (0xA << 0)); - asm_output("stc_DBR R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_stc_bank(imm) ((imm) >= 0 && (imm) <= 7) - -#define FITS_SH4_stc_bank(imm) (SH4_CHECK_RANGE_stc_bank(imm)) - - inline void Assembler::SH4_stc_bank(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_stc_bank(imm)); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0x7) << 4) | (0x2 << 0)); - asm_output("stc_bank %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_stcl_SR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x3 << 0)); - asm_output("stcl_SR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_VBR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x3 << 0)); - asm_output("stcl_VBR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_SSR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0x3 << 0)); - asm_output("stcl_SSR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_SPC_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x4 << 4) | (0x3 << 0)); - asm_output("stcl_SPC_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_GBR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x3 << 0)); - asm_output("stcl_GBR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_SGR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x3 << 4) | (0x2 << 0)); - asm_output("stcl_SGR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stcl_DBR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0xF << 4) | (0x2 << 0)); - asm_output("stcl_DBR_decRx R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_stcl_bank_decRx(imm) ((imm) >= 0 && (imm) <= 7) - -#define FITS_SH4_stcl_bank_decRx(imm) (SH4_CHECK_RANGE_stcl_bank_decRx(imm)) - - inline void Assembler::SH4_stcl_bank_decRx(int imm, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && SH4_CHECK_RANGE_stcl_bank_decRx(imm)); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((imm & 0x7) << 4) | (0x3 << 0)); - asm_output("stcl_bank_decRx %d, R%d", imm, Rx.n); - } - - inline void Assembler::SH4_sts_MACH(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0xA << 0)); - asm_output("sts_MACH R%d", Rx.n); - } - - inline void Assembler::SH4_sts_MACL(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0xA << 0)); - asm_output("sts_MACL R%d", Rx.n); - } - - inline void Assembler::SH4_sts_PR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0xA << 0)); - asm_output("sts_PR R%d", Rx.n); - } - - inline void Assembler::SH4_sts_FPUL(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x5 << 4) | (0xA << 0)); - asm_output("sts_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_sts_FPSCR(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x6 << 4) | (0xA << 0)); - asm_output("sts_FPSCR R%d", Rx.n); - } - - inline void Assembler::SH4_stsl_MACH_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x2 << 0)); - asm_output("stsl_MACH_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stsl_MACL_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x2 << 0)); - asm_output("stsl_MACL_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stsl_PR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x2 << 0)); - asm_output("stsl_PR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stsl_FPUL_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x5 << 4) | (0x2 << 0)); - asm_output("stsl_FPUL_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_stsl_FPSCR_decRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x6 << 4) | (0x2 << 0)); - asm_output("stsl_FPSCR_decRx R%d", Rx.n); - } - - inline void Assembler::SH4_sub(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("sub R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_subc(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xA << 0)); - asm_output("subc R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_subv(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xB << 0)); - asm_output("subv R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_swapb(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("swapb R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_swapw(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x6 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("swapw R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_synco() { - NanoAssert(1); - SH4_emit16((0x0 << 12) | (0x0 << 8) | (0xA << 4) | (0xB << 0)); - asm_output("synco"); - } - - inline void Assembler::SH4_tasb_indRx(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0xB << 0)); - asm_output("tasb_indRx R%d", Rx.n); - } - -#define SH4_CHECK_RANGE_trapa_imm(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_trapa_imm(imm) (SH4_CHECK_RANGE_trapa_imm(imm)) - - inline void Assembler::SH4_trapa_imm(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_trapa_imm(imm)); - SH4_emit16((0xC << 12) | (0x3 << 8) | ((imm & 0xFF) << 0)); - asm_output("trapa_imm %d", imm); - } - -#define SH4_CHECK_RANGE_tst_imm_R0(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_tst_imm_R0(imm) (SH4_CHECK_RANGE_tst_imm_R0(imm)) - - inline void Assembler::SH4_tst_imm_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_tst_imm_R0(imm)); - SH4_emit16((0xC << 12) | (0x8 << 8) | ((imm & 0xFF) << 0)); - asm_output("tst_imm_R0 %d", imm); - } - - inline void Assembler::SH4_tst(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("tst R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_tstb_imm_dispR0GBR(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_tstb_imm_dispR0GBR(imm) (SH4_CHECK_RANGE_tstb_imm_dispR0GBR(imm)) - - inline void Assembler::SH4_tstb_imm_dispR0GBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_tstb_imm_dispR0GBR(imm)); - SH4_emit16((0xC << 12) | (0xC << 8) | ((imm & 0xFF) << 0)); - asm_output("tstb_imm_dispR0GBR %d", imm); - } - -#define SH4_CHECK_RANGE_xor_imm_R0(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_xor_imm_R0(imm) (SH4_CHECK_RANGE_xor_imm_R0(imm)) - - inline void Assembler::SH4_xor_imm_R0(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_xor_imm_R0(imm)); - SH4_emit16((0xC << 12) | (0xA << 8) | ((imm & 0xFF) << 0)); - asm_output("xor_imm_R0 %d", imm); - } - - inline void Assembler::SH4_xor(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xA << 0)); - asm_output("xor R%d, R%d", Ry.n, Rx.n); - } - -#define SH4_CHECK_RANGE_xorb_imm_dispR0GBR(imm) ((imm) >= 0 && (imm) <= 255) - -#define FITS_SH4_xorb_imm_dispR0GBR(imm) (SH4_CHECK_RANGE_xorb_imm_dispR0GBR(imm)) - - inline void Assembler::SH4_xorb_imm_dispR0GBR(int imm) { - NanoAssert(1 && SH4_CHECK_RANGE_xorb_imm_dispR0GBR(imm)); - SH4_emit16((0xC << 12) | (0xE << 8) | ((imm & 0xFF) << 0)); - asm_output("xorb_imm_dispR0GBR %d", imm); - } - - inline void Assembler::SH4_xtrct(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x2 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xD << 0)); - asm_output("xtrct R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_dt(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x4 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x1 << 4) | (0x0 << 0)); - asm_output("dt R%d", Rx.n); - } - - inline void Assembler::SH4_dmulsl(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xD << 0)); - asm_output("dmulsl R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_dmulul(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x3 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x5 << 0)); - asm_output("dmulul R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_macl_incRy_incRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0xF << 0)); - asm_output("macl_incRy_incRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_braf(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x2 << 4) | (0x3 << 0)); - asm_output("braf R%d", Rx.n); - } - - inline void Assembler::SH4_bsrf(Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15); - SH4_emit16((0x0 << 12) | ((REGNUM(Rx) & 0xF) << 8) | (0x0 << 4) | (0x3 << 0)); - asm_output("bsrf R%d", Rx.n); - } - - inline void Assembler::SH4_fabs(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x5 << 4) | (0xD << 0)); - asm_output("fabs R%d", Rx.n); - } - - inline void Assembler::SH4_fabs_double(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x5 << 4) | (0xD << 0)); - asm_output("fabs_double R%d", Rx.n); - } - - inline void Assembler::SH4_fadd(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x0 << 0)); - asm_output("fadd R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fadd_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x0 << 0)); - asm_output("fadd_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fcmpeq(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x4 << 0)); - asm_output("fcmpeq R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fcmpeq_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x4 << 0)); - asm_output("fcmpeq_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fcmpgt(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x5 << 0)); - asm_output("fcmpgt R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fcmpgt_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x5 << 0)); - asm_output("fcmpgt_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fcnvds_double_FPUL(Register Rx) { - NanoAssert(1 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0xB << 4) | (0xD << 0)); - asm_output("fcnvds_double_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_fcnvsd_FPUL_double(Register Rx) { - NanoAssert(1 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0xA << 4) | (0xD << 0)); - asm_output("fcnvsd_FPUL_double R%d", Rx.n); - } - - inline void Assembler::SH4_fdiv(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x3 << 0)); - asm_output("fdiv R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fdiv_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x3 << 0)); - asm_output("fdiv_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fipr(Register Ry, Register Rx) { - NanoAssert(1 && !(((REGNUM(Rx) - 16) & 0x3) || ((REGNUM(Ry) - 16) & 0x3))); - SH4_emit16((0xF << 12) | (((((REGNUM(Rx) - 16) & 0xF) << 2) | (((REGNUM(Ry) - 16) & 0xF) >> 2)) << 8) | (0xE << 4) | (0xD << 0)); - asm_output("fipr R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fldi0(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x8 << 4) | (0xD << 0)); - asm_output("fldi0 R%d", Rx.n); - } - - inline void Assembler::SH4_fldi1(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x9 << 4) | (0xD << 0)); - asm_output("fldi1 R%d", Rx.n); - } - - inline void Assembler::SH4_flds_FPUL(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x1 << 4) | (0xD << 0)); - asm_output("flds_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_float_FPUL(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x2 << 4) | (0xD << 0)); - asm_output("float_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_float_FPUL_double(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x2 << 4) | (0xD << 0)); - asm_output("float_FPUL_double R%d", Rx.n); - } - - inline void Assembler::SH4_fmac(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xE << 0)); - asm_output("fmac R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xC << 0)); - asm_output("fmov R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_Xdouble_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xC << 0)); - asm_output("fmov_Xdouble_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_indRy(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("fmov_indRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_indRy_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("fmov_indRy_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xA << 0)); - asm_output("fmov_indRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_Xdouble_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xA << 0)); - asm_output("fmov_Xdouble_indRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_incRy(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("fmov_incRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_incRy_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("fmov_incRy_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xB << 0)); - asm_output("fmov_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_Xdouble_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xB << 0)); - asm_output("fmov_Xdouble_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_dispR0Ry(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("fmov_dispR0Ry R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_dispR0Ry_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("fmov_dispR0Ry_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x7 << 0)); - asm_output("fmov_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmov_Xdouble_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x7 << 0)); - asm_output("fmov_Xdouble_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_indRy_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("fmovd_indRy_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_Xdouble_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xA << 0)); - asm_output("fmovd_Xdouble_indRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_incRy_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("fmovd_incRy_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_Xdouble_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xB << 0)); - asm_output("fmovd_Xdouble_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_dispR0Ry_Xdouble(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("fmovd_dispR0Ry_Xdouble R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovd_Xdouble_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x7 << 0)); - asm_output("fmovd_Xdouble_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_indRy(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x8 << 0)); - asm_output("fmovs_indRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_indRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xA << 0)); - asm_output("fmovs_indRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_incRy(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x9 << 0)); - asm_output("fmovs_incRy R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_decRx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0xB << 0)); - asm_output("fmovs_decRx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_dispR0Ry(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && REGNUM(Ry) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | ((REGNUM(Ry) & 0xF) << 4) | (0x6 << 0)); - asm_output("fmovs_dispR0Ry R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmovs_dispR0Rx(Register Ry, Register Rx) { - NanoAssert(1 && REGNUM(Rx) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | ((REGNUM(Rx) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x7 << 0)); - asm_output("fmovs_dispR0Rx R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmul(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x2 << 0)); - asm_output("fmul R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fmul_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x2 << 0)); - asm_output("fmul_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fneg(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x4 << 4) | (0xD << 0)); - asm_output("fneg R%d", Rx.n); - } - - inline void Assembler::SH4_fneg_double(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x4 << 4) | (0xD << 0)); - asm_output("fneg_double R%d", Rx.n); - } - - inline void Assembler::SH4_fpchg() { - NanoAssert(1); - SH4_emit16((0xF << 12) | (0x7 << 8) | (0xF << 4) | (0xD << 0)); - asm_output("fpchg"); - } - - inline void Assembler::SH4_frchg() { - NanoAssert(1); - SH4_emit16((0xF << 12) | (0xB << 8) | (0xF << 4) | (0xD << 0)); - asm_output("frchg"); - } - - inline void Assembler::SH4_fsca_FPUL_double(Register Rx) { - NanoAssert(1 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0xF << 4) | (0xD << 0)); - asm_output("fsca_FPUL_double R%d", Rx.n); - } - - inline void Assembler::SH4_fschg() { - NanoAssert(1); - SH4_emit16((0xF << 12) | (0x3 << 8) | (0xF << 4) | (0xD << 0)); - asm_output("fschg"); - } - - inline void Assembler::SH4_fsqrt(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x6 << 4) | (0xD << 0)); - asm_output("fsqrt R%d", Rx.n); - } - - inline void Assembler::SH4_fsqrt_double(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x6 << 4) | (0xD << 0)); - asm_output("fsqrt_double R%d", Rx.n); - } - - inline void Assembler::SH4_fsrra(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x7 << 4) | (0xD << 0)); - asm_output("fsrra R%d", Rx.n); - } - - inline void Assembler::SH4_fsts_FPUL(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x0 << 4) | (0xD << 0)); - asm_output("fsts_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_fsub(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && (REGNUM(Ry) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x1 << 0)); - asm_output("fsub R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_fsub_double(Register Ry, Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1) && (REGNUM(Ry) - 16) <= 15 && !((REGNUM(Ry) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (((REGNUM(Ry) - 16) & 0xF) << 4) | (0x1 << 0)); - asm_output("fsub_double R%d, R%d", Ry.n, Rx.n); - } - - inline void Assembler::SH4_ftrc_FPUL(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x3 << 4) | (0xD << 0)); - asm_output("ftrc_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_ftrc_double_FPUL(Register Rx) { - NanoAssert(1 && (REGNUM(Rx) - 16) <= 15 && !((REGNUM(Rx) - 16) & 0x1)); - SH4_emit16((0xF << 12) | (((REGNUM(Rx) - 16) & 0xF) << 8) | (0x3 << 4) | (0xD << 0)); - asm_output("ftrc_double_FPUL R%d", Rx.n); - } - - inline void Assembler::SH4_ftrv(Register Rx) { - NanoAssert(1 && !((REGNUM(Rx) - 16) & 0x3)); - SH4_emit16((0xF << 12) | (((((REGNUM(Rx) - 16) & 0xF) << 2) | 0x1) << 8) | (0xF << 4) | (0xD << 0)); - asm_output("ftrv R%d", Rx.n); - } diff --git a/deps/mozjs/js/src/nanojit/NativeSH4.cpp b/deps/mozjs/js/src/nanojit/NativeSH4.cpp deleted file mode 100644 index 539f8f86d09..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeSH4.cpp +++ /dev/null @@ -1,1814 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * STMicroelectronics. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Cédric VINCENT for STMicroelectronics - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#if defined FEATURE_NANOJIT && defined NANOJIT_SH4 - -namespace nanojit -{ - const int Assembler::NumArgRegs = 4; - const Register Assembler::argRegs[] = { R4, R5, R6, R7 }; - const Register Assembler::retRegs[] = { R0, R1 }; - const Register Assembler::savedRegs[] = { R8, R9, R10, R11, R12, R13 }; - - const int Assembler::NumArgDregs = 4; - const Register Assembler::argDregs[] = { _D2, _D3, _D4, _D5 }; - const Register Assembler::retDregs[] = { _D0 }; - - const char *regNames[] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", "r12", "r13", "fp", "sp", - "d0", "d0~", "d1", "d1~", "d2", "d2~", "d3", "d3~", - "d4", "d4~", "d5", "d5~", "d6", "d6~", "d7", "d7~" - }; - - struct pool Assembler::current_pool; - - inline void Assembler::SH4_emit16(uint16_t value) { - _nIns--; - NanoAssert((uintptr_t)_nIns >= (uintptr_t)codeStart); - *((uint16_t*)_nIns) = value; - } - - inline void Assembler::SH4_emit32(uint32_t value) { - _nIns -= 2; - NanoAssert((uintptr_t)_nIns >= (uintptr_t)codeStart); - *((uint32_t *)(void *)_nIns) = value; - } - -#include "NativeSH4-auto-generated.h" - -#define SH4_movl_PCrel(address, reg) \ - SH4_movl_dispPC(((uint32_t)(address) - (((uint32_t)(_nIns + 1)) & ~0x3)), reg) - -#define SH4_LABEL(target) (int32_t)((uint32_t)(target) - (uint32_t)(_nIns) - 2) - -#define FP_RELATIVE(offset) (STATIC_FP + (offset)) - - void Assembler::nativePageReset() { } - - void Assembler::nativePageSetup() { - NanoAssert(!_inExit); - if (!_nIns) - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - current_pool.nb_slots = 0; - current_pool.slots = NULL; - } - - void Assembler::MR(Register dest, Register src) { - underrunProtect(1 * sizeof(NIns)); - SH4_mov(src, dest); - } - - void Assembler::JMP(NIns *target, bool from_underrunProtect) { - // Take the opportunity of this unconditional branch to - // pre-allocate space for constants right after. - asm_generate_pool(0); - - underrunProtect(7 * sizeof(NIns)); - - if (target != NULL && FITS_SH4_bra(SH4_LABEL(target + 1 * sizeof(NIns)))) { - // We can reach the target with a "bra". - SH4_nop(); - SH4_bra(SH4_LABEL(target)); - } - else { - // Don't use asm_immi() here since the address will likely - // land in a constant slot, this allows to store the - // constant right after the unconditional jump. - - NIns *target_addr; - - // 5. align the constant with nop - if (((uint32_t)_nIns & 0x3) != 0) - SH4_nop(); - - // 4. constant slot - SH4_emit32((uint32_t)target); - target_addr = _nIns; - - // 3. restore Rtemp from the stack during the delay slot - // (if needed, see the last comment of this function). - if (from_underrunProtect) - SH4_movl_incRy(SP, Rtemp); - else - SH4_nop(); - - // 3. Jump to the target. - SH4_jmp_indRx(Rtemp); - - // 2. @(PC + offset) -> reg - SH4_movl_PCrel(target_addr, Rtemp); - - // When compiling the following code, where XXX is "large": - // - // array[XXX] = YYY - // - // The SH4 backend will produce the native code in two steps: - // - // 1. mov.l @(pc, ZZZ), Rtemp - // 2a. add FP, Rtemp - // 2b. mov.l Rsrc, @Rtemp - // - // If the code buffer is full right after the first step, - // it will add the [in]famous glue JMP: - // - // 1. mov.l @(pc, ZZZ), Rtemp - // mov.l @(pc, TARGET), Rtemp # Technically Rtemp is now the address of 2a. - // jmp @Rtemp # Jump to 2a. - // 2a. add FP, Rtemp - // 2b. mov.l Rsrc, @Rtemp - // - // As you can see, we have to keep/restore the value of - // Rtemp when called from underrunProtect(). - if (from_underrunProtect) - SH4_movl_decRx(Rtemp, SP); - } - } - - void Assembler::asm_arith(LIns *inst) { - LOpcode opcode = inst->opcode(); - LIns *operand1 = inst->oprnd1(); - LIns *operand2 = inst->oprnd2(); - - RegisterMask allow = GpRegs; - - Register result_reg = prepareResultReg(inst, allow); - allow &= ~rmask(result_reg); - - Register operand1_reg = findRegFor(operand1, allow); - allow &= ~rmask(operand1_reg); - - underrunProtect(7 * sizeof(NIns)); - - // Special case on SH4: the instruction "add" is the only - // arithmetic operation that can use an [small] immediate. - if (operand2->isImmI() && - (inst->opcode() == LIR_addi || inst->opcode() == LIR_subi)) { - int immI = operand2->immI(); - - if (inst->opcode() == LIR_subi) - immI = -immI; - - if (FITS_SH4_add_imm(immI)) { - // 2. operand2 + result -> result - SH4_add_imm(immI, result_reg); - - // 1. operand1 -> result - if (result_reg != operand1_reg) - SH4_mov(operand1_reg, result_reg); - - freeResourcesOf(inst); - return; - } - } - - Register operand2_reg = inst->oprnd1() == inst->oprnd2() - ? operand1_reg - : findRegFor(operand2, allow); - - // 2. operand2 OP result -> result - NanoAssert(result_reg != Rtemp && operand1_reg != Rtemp && operand2_reg != Rtemp); - switch (opcode) { - case LIR_rshi: - SH4_shad(Rtemp, result_reg); - SH4_neg(Rtemp, Rtemp); - SH4_and(operand2_reg, Rtemp); - SH4_mov_imm(0x1f, Rtemp); - break; - - case LIR_rshui: - SH4_shld(Rtemp, result_reg); - SH4_neg(Rtemp, Rtemp); - SH4_and(operand2_reg, Rtemp); - SH4_mov_imm(0x1f, Rtemp); - break; - - case LIR_muli: - SH4_sts_MACL(result_reg); - SH4_mull(operand2_reg, result_reg); - break; - - case LIR_muljovi: - case LIR_mulxovi: - // 3. Now the T-bit is equal to "sign_of(result_reg) == sign_of(operand1) XOR sign_of(operand2)". - SH4_shll(Rtemp); - SH4_xor(result_reg, Rtemp); - - // 2. Perform the actual multiplication. - SH4_sts_MACL(result_reg); - SH4_dmulsl(operand2_reg, result_reg); - - // 1. Now Rtemp[31] is equal to "sign_of(operand1) XOR sign_of(operand2)". - SH4_xor(operand2_reg, Rtemp); - SH4_mov(result_reg, Rtemp); - break; - - case LIR_lshi: SH4_shad(operand2_reg, result_reg); break; - case LIR_ori: SH4_or(operand2_reg, result_reg); break; - case LIR_subi: SH4_sub(operand2_reg, result_reg); break; - case LIR_addi: SH4_add(operand2_reg, result_reg); break; - case LIR_andi: SH4_and(operand2_reg, result_reg); break; - case LIR_xori: SH4_xor(operand2_reg, result_reg); break; - - case LIR_subjovi: - case LIR_subxovi: SH4_subv(operand2_reg, result_reg); break; - - case LIR_addjovi: - case LIR_addxovi: SH4_addv(operand2_reg, result_reg); break; - default: - NanoAssertMsg(0, "asm_arith does not support this LIR opcode yet"); - } - - // 1. operand1 -> result - if (result_reg != operand1_reg) - SH4_mov(operand1_reg, result_reg); - - freeResourcesOf(inst); - } - - void Assembler::asm_arg_regi(LIns *arg, Register reg) { - if (arg->isExtant()) { - if (arg->isInReg()) { - // This argument is assigned to a register, thus we - // just have to copy it to the right register. - underrunProtect(1 * sizeof(NIns)); - SH4_mov(arg->getReg(), reg); - } - else { - // This argument is not assigned to a register, thus - // we have to get its frame address first and then - // copy it to the right register. - int offset = findMemFor(arg); - - if (arg->isop(LIR_allocp)) { - // Load the address of the argument, not its value. - underrunProtect(1 * sizeof(NIns)); - offset = FP_RELATIVE(offset); - SH4_add(FP, reg); // 2. arg_reg + FP -> arg_reg - asm_immi(offset, reg); // 1. offset -> arg_reg - } - else { - // Load the value of the argument. - asm_load32i(offset, FP, reg); - } - } - } - else { - // This is the last use of "arg", so fine to - // assign it to the right register immediately. - findSpecificRegFor(arg, reg); - } - } - - void Assembler::asm_arg_regd(LIns *arg, Register reg) { - if (arg->isExtant()) { - if (arg->isInReg()) { - // This argument is assigned to a register, thus we - // just have to copy it to the right register. - underrunProtect(2 * sizeof(NIns)); - SH4_fmov(arg->getReg(), reg); - SH4_fmov(arg->getReg() + 1, reg + 1); - } - else { - // This argument is not assigned to a register, thus - // we have to get its frame address first and then - // copy it to the right register. - int offset = findMemFor(arg); - asm_load64d(offset, FP, reg); - } - } - else { - // This is the last use of "arg", so fine to - // assign it to the right register immediately. - findSpecificRegFor(arg, reg); - } - } - - void Assembler::asm_arg_stacki(LIns *arg, int used_stack) { - if (arg->isExtant() && arg->isInReg()) { - Register result_reg = arg->getReg(); - // This argument is assigned to a register, thus we just - // have to copy it to the right stack slot. - asm_store32i(result_reg, used_stack, SP); - } - else { - // This argument is not assigned to a register, thus we - // have to get its frame address first and then copy it to - // the right stack slot. - int offset = findMemFor(arg); - Register reg = findRegFor(arg, GpRegs); - - asm_store32i(reg, used_stack, SP); - - if (arg->isop(LIR_allocp)) { - // Load the address of the argument, not its value. - underrunProtect(1 * sizeof(NIns)); - offset = FP_RELATIVE(offset); - SH4_add(FP, reg); // 2. arg_reg + FP -> arg_reg - asm_immi(offset, reg); // 1. offset -> arg_reg - } - else { - // Load the value of the argument. - asm_load32i(offset, FP, reg); - } - } - } - - void Assembler::asm_arg_stackd(LIns *arg, int used_stack) { - if (arg->isExtant() && arg->isInReg()) { - Register result_reg = arg->getReg(); - // This argument is assigned to a register, thus we just - // have to copy it to the right stack slot. - asm_store64d(result_reg, used_stack, SP); - } - else { - // This argument is not assigned to a register, thus we - // have to get its frame address first and then copy it to - // the right stack slot. - int offset = findMemFor(arg); - Register reg = findRegFor(arg, FpRegs); - - asm_store64d(reg, used_stack, SP); - asm_load64d(offset, FP, reg); - } - } - - void Assembler::asm_call(LIns *inst) { - if (!inst->isop(LIR_callv)) { - Register result_reg = inst->isop(LIR_calld) ? retDregs[0] : retRegs[0]; - prepareResultReg(inst, rmask(result_reg)); - - // Do this after we've handled the call result, so we don't - // force the call result to be spilled unnecessarily. - evictScratchRegsExcept(rmask(result_reg)); - } else { - evictScratchRegsExcept(0); - } - ArgType types[MAXARGS]; - const CallInfo* call = inst->callInfo(); - uint32_t argc = call->getArgTypes(types); - bool indirect = call->isIndirect(); - - // Emit the branch. - if (!indirect) { - NIns *target = (NIns*)call->_address; - - underrunProtect(2 * sizeof(NIns)); - if (FITS_SH4_bsr(SH4_LABEL(target + 1 * sizeof(NIns)))) { - // We can reach the target with a "bsr". - SH4_nop(); - SH4_bsr(SH4_LABEL(target)); - } - else { - // General case. - SH4_nop(); - SH4_jsr_indRx(Rtemp); // 2. jump to target - asm_immi((int)target, Rtemp); // 1. load target - } - // Call this now so that the argument setup can involve 'result_reg'. - freeResourcesOf(inst); - } - else { - // Indirect call: we assign the address arg to R0 since it's not - // used for regular arguments, and is otherwise scratch since it's - // clobberred by the call. - underrunProtect(2 * sizeof(NIns)); - - SH4_nop(); - SH4_jsr_indRx(R0); - - // Call this now so that the argument setup can involve 'result_reg'. - freeResourcesOf(inst); - - argc--; - asm_arg_regi(inst->arg(argc), R0); - } - - // Emit the arguments. - int ireg_index = 0; - int dreg_index = 0; - int used_stack = 0; - - for (int i = argc - 1; i >= 0; i--) { - switch(types[i]) { - case ARGTYPE_I: // int32_t - case ARGTYPE_UI: // uint32_t - if (ireg_index < NumArgRegs) { - // Arguments are [still] stored into registers. - asm_arg_regi(inst->arg(i), argRegs[ireg_index]); - ireg_index++; - } - else { - // Arguments are [now] stored into the stack. - asm_arg_stacki(inst->arg(i), used_stack); - used_stack += sizeof(int); - } - break; - - case ARGTYPE_D: // double (64bit) - if (dreg_index < NumArgDregs) { - // Arguments are [still] stored into registers. - asm_arg_regd(inst->arg(i), argDregs[dreg_index]); - dreg_index++; - } - else { - // Arguments are [now] stored into the stack. - asm_arg_stackd(inst->arg(i), used_stack); - used_stack += sizeof(double); - } - break; - - default: - NanoAssertMsg(0, "asm_call does not support this size of argument yet"); - } - } - - if (used_stack > max_stack_args) - max_stack_args = used_stack; - } - - void Assembler::asm_cmov(LIns *inst) { - LIns* condition = inst->oprnd1(); - LIns* if_true = inst->oprnd2(); - LIns* if_false = inst->oprnd3(); - - NanoAssert(condition->isCmp()); - NanoAssert( (inst->isop(LIR_cmovi) && if_true->isI() && if_false->isI()) - || (inst->isop(LIR_cmovd) && if_true->isD() && if_false->isD())); - - RegisterMask allow = inst->isD() ? FpRegs : GpRegs; - - Register dest_reg = prepareResultReg(inst, allow); - - // Try to re-use the result register for one of the arguments. - Register src_true_reg = if_true->isInReg() ? if_true->getReg() : dest_reg; - Register src_false_reg = if_false->isInReg() ? if_false->getReg() : dest_reg; - - // Note that iftrue and iffalse may actually be the same, though - // it shouldn't happen with the LIR optimizers turned on. - if (src_true_reg == src_false_reg && if_true != if_false) { - // We can't re-use the result register for both arguments, - // so force one into its own register. - src_false_reg = findRegFor(if_false, allow & ~rmask(dest_reg)); - NanoAssert(if_false->isInReg()); - } - - underrunProtect(6 * sizeof(NIns)); - // 3. If the test is "true", copy the "true" source register. - NIns *after_mov_true = _nIns; - if (inst->isop(LIR_cmovd)) { - SH4_fmov(src_true_reg, dest_reg); - SH4_fmov(src_true_reg + 1, dest_reg + 1); - } - else { - SH4_mov(src_true_reg, dest_reg); - } - - // 2. If the test is "false", copy the "false" source register - // then jump over the "mov if true". - NIns *after_mov_false = _nIns; - - // Take the opportunity of this unconditional branch to - // pre-allocate space for constants right after. The size is - // limited to 50 words because of the limitation of the - // instruction bf/t: branch at most 256 bytes far away. - // - // pool size = 50 * sizeof(word) = 200 bytes - // + pool alignement = ~1 * sizeof(half) = 2 bytes - // + instructions below = ~5 * sizeof(NIns) = 10 bytes - // + instructions in asm_branch = ~10 * sizeof(NIns) = 20 bytes - // total = 232 bytes - // - // Note asm_generate_pool() prevents any code buffer - // allocation so there is no need for underrunProtect(), - // however we still have to reserve space for the instructions - // below. - asm_generate_pool(4 * sizeof(sizeof(NIns)), 50); - - SH4_nop(); - SH4_bra(SH4_LABEL(after_mov_true)); - - if (inst->isop(LIR_cmovd)) { - SH4_fmov(src_false_reg, dest_reg); - SH4_fmov(src_false_reg + 1, dest_reg + 1); - } - else { - SH4_mov(src_false_reg, dest_reg); - } - - freeResourcesOf(inst); - - // If we re-used the result register, mark it as active for either if_true - // or if_false (or both in the corner-case where they're the same). - if (src_true_reg == dest_reg) { - NanoAssert(!if_true->isInReg()); - findSpecificRegForUnallocated(if_true, dest_reg); - } else if (src_false_reg == dest_reg) { - NanoAssert(!if_false->isInReg()); - findSpecificRegForUnallocated(if_false, dest_reg); - } else { - NanoAssert(if_false->isInReg()); - NanoAssert(if_true->isInReg()); - } - - // 1. Branch [or not] according to the condition. - asm_branch(false, condition, after_mov_false); - } - - void Assembler::asm_cond(LIns *inst) { - Register result_reg = prepareResultReg(inst, GpRegs); - LOpcode opcode = inst->opcode(); - - // 2. Get the T-bit. - underrunProtect(3 * sizeof(NIns)); - SH4_movt(result_reg); - - // Inverse the T-bit with a small trick. - bool negated = simplifyOpcode(opcode); - if (negated) { - SH4_tst(result_reg, result_reg); - SH4_movt(result_reg); - } - - freeResourcesOf(inst); - - // 1. Do the comparison - asm_cmp(opcode, inst); - } - - void Assembler::asm_condd(LIns *inst) { - asm_cond(inst); - } - - void Assembler::asm_fneg(LIns *inst) { - Register result_reg = prepareResultReg(inst, FpRegs); - Register operand_reg = findRegFor(inst->oprnd1(), FpRegs); - - // 2. -result -> result - underrunProtect(1 * sizeof(NIns)); - SH4_fneg_double(result_reg); - - // 1. operand -> result - if (result_reg != operand_reg) { - underrunProtect(2 * sizeof(NIns)); - SH4_fmov(operand_reg, result_reg); - SH4_fmov(operand_reg + 1, result_reg + 1); - } - - freeResourcesOf(inst); - } - - void Assembler::asm_fop(LIns *inst) { - LOpcode opcode = inst->opcode(); - LIns *operand1 = inst->oprnd1(); - LIns *operand2 = inst->oprnd2(); - - RegisterMask allow = FpRegs; - - Register result_reg = prepareResultReg(inst, allow); - allow &= ~rmask(result_reg); - - Register operand1_reg = findRegFor(operand1, allow); - allow &= ~rmask(operand1_reg); - - Register operand2_reg = inst->oprnd1() == inst->oprnd2() - ? operand1_reg - : findRegFor(operand2, allow); - - underrunProtect(3 * sizeof(NIns)); - - // 2. operand2 OP result -> result - switch (opcode) { - case LIR_addd: SH4_fadd_double(operand2_reg, result_reg); break; - case LIR_subd: SH4_fsub_double(operand2_reg, result_reg); break; - case LIR_muld: SH4_fmul_double(operand2_reg, result_reg); break; - case LIR_divd: SH4_fdiv_double(operand2_reg, result_reg); break; - default: - NanoAssertMsg(0, "asm_fop does not support this LIR opcode yet"); - } - - // 1. operand1 -> result - if (result_reg != operand1_reg) { - SH4_fmov(operand1_reg, result_reg); - SH4_fmov(operand1_reg + 1, result_reg + 1); - } - - freeResourcesOf(inst); - } - - void Assembler::asm_d2i(LIns *inst) { - Register result_reg = prepareResultReg(inst, GpRegs); - Register operand1_reg = findRegFor(inst->oprnd1(), FpRegs); - - underrunProtect(2 * sizeof(NIns)); - SH4_sts_FPUL(result_reg); - SH4_ftrc_double_FPUL(operand1_reg); - - freeResourcesOf(inst); - } - - void Assembler::asm_i2d(LIns *inst) { - Register result_reg = prepareResultReg(inst, FpRegs); - Register operand1_reg = findRegFor(inst->oprnd1(), GpRegs); - - underrunProtect(2 * sizeof(NIns)); - SH4_float_FPUL_double(result_reg); - SH4_lds_FPUL(operand1_reg); - - freeResourcesOf(inst); - } - - void Assembler::asm_ui2d(LIns *inst) { - Register result_reg = prepareResultReg(inst, FpRegs); - Register operand1_reg = findRegFor(inst->oprnd1(), GpRegs); - - underrunProtect(SH4_IMMD_NOCHK_SIZE + 5 * sizeof(NIns)); - NIns *over_conversion = _nIns; - - // 3. Otherwise adjust the result. - SH4_fadd(Dtemp, result_reg); - asm_immd_nochk(0x41F0000000000000LLU, Dtemp); // It does the trickâ„¢. - - // 2. Done if it was a positif integer. - SH4_bt(SH4_LABEL(over_conversion)); - SH4_cmppz(operand1_reg); - - // 1. Convert the *signed* integer. - SH4_float_FPUL_double(result_reg); - SH4_lds_FPUL(operand1_reg); - - freeResourcesOf(inst); - } - - void Assembler::asm_immi(LIns *inst) { - Register result_reg = prepareResultReg(inst, GpRegs); - asm_immi(inst->immI(), result_reg); - freeResourcesOf(inst); - } - - void Assembler::asm_base_offset(int offset, Register base_reg) { - underrunProtect(2 * sizeof(NIns)); - - if (base_reg == FP) - offset = FP_RELATIVE(offset); - - if (offset != 0) { - if (FITS_SH4_add_imm(offset)) { - SH4_add_imm(offset, Rtemp); - if (base_reg != Rtemp) - SH4_mov(base_reg, Rtemp); - } - else { - NanoAssert(base_reg != Rtemp); - SH4_add(base_reg, Rtemp); // b. base + temp -> temp - asm_immi(offset, Rtemp); // a. offset -> temp - } - } - else { - if (base_reg != Rtemp) - SH4_mov(base_reg, Rtemp); - } - } - - void Assembler::asm_load64d(int offset, Register base_reg, Register result_reg) { - underrunProtect(2 * sizeof(NIns)); - - // 2. Load the "double" from @Rtemp. - SH4_fmovs_indRy(Rtemp, result_reg); - SH4_fmovs_incRy(Rtemp, result_reg + 1); - - // 1. base + offset -> Rtemp. - asm_base_offset(offset, base_reg); - } - - void Assembler::asm_load32d(int offset, Register base_reg, Register result_reg) { - underrunProtect(3 * sizeof(NIns)); - - // 3. Convert to double precision. - SH4_fcnvsd_FPUL_double(result_reg); - SH4_flds_FPUL(Dtemp); - - // 2. Load the "float" from @Rtemp. - SH4_fmovs_indRy(Rtemp, Dtemp); - - // 1. base + offset -> Rtemp. - asm_base_offset(offset, base_reg); - } - - void Assembler::asm_load32i(int offset, Register base_reg, Register result_reg) { - // Rtemp is mandatory to handle the "worst" case. - NanoAssert(result_reg != Rtemp); - - if (base_reg == FP) - offset = FP_RELATIVE(offset); - - if (offset == 0) { - // No displacement. - underrunProtect(1 * sizeof(NIns)); - SH4_movl_indRy(base_reg, result_reg); - } - else if (FITS_SH4_movl_dispRy(offset)) { - // The displacement fits the constraints of this instruction. - underrunProtect(1 * sizeof(NIns)); - SH4_movl_dispRy(offset, base_reg, result_reg); - } - else { - // General case. - if ( result_reg == R0 - && base_reg != R0) { - // Optimize when R0 is clobbered and not used as the base. - underrunProtect(1 * sizeof(NIns)); - SH4_movl_dispR0Ry(base_reg, result_reg); // 2. @(R0 + base) -> R0 - asm_immi(offset, R0); // 1. offset -> R0 - } - else { - // Worst case. - underrunProtect(2 * sizeof(NIns)); - SH4_movl_indRy(Rtemp, result_reg); // 3. @(temp) -> result - SH4_add(base_reg, Rtemp); // 2. base + temp -> temp - asm_immi(offset, Rtemp); // 1. offset -> temp - } - } - } - -#define gen_asm_loadXXi(size, length) \ - void Assembler::asm_load ## size ## i(int offset, Register base_reg, Register result_reg, bool sign_extend) { \ - underrunProtect(5 * sizeof(NIns)); \ - \ - if (!sign_extend) { \ - SH4_extu ## length(result_reg, result_reg); \ - } \ - \ - if (base_reg == FP) \ - offset = FP_RELATIVE(offset); \ - \ - if (offset == 0) { \ - /* No displacement. */ \ - SH4_mov ## length ## _indRy(base_reg, result_reg); \ - } \ - else if (FITS_SH4_mov ## length ## _dispRy_R0(offset)) { \ - /* The displacement is small enough to fit into the */ \ - /* special #size bits load "@(base + offset) -> R0". */ \ - if (result_reg == R0) { \ - /* We are lucky, the result is R0. */ \ - SH4_mov ## length ## _dispRy_R0(offset, base_reg); /* @(base + offset) -> R0 */ \ - } \ - else { \ - /* We have to save and restore R0. */ \ - SH4_mov(Rtemp, R0); /* 4. Rtemp -> R0 */ \ - SH4_mov(R0, result_reg); /* 3. R0 -> result */ \ - SH4_mov ## length ## _dispRy_R0(offset, base_reg); /* 2. @(base + offset) -> R0 */ \ - SH4_mov(R0, Rtemp); /* 1. R0 -> Rtemp */ \ - } \ - } \ - else { \ - /* Worst case. */ \ - SH4_mov ## length ## _indRy(Rtemp, result_reg); /* 3. @(temp) -> result */ \ - SH4_add(base_reg, Rtemp); /* 2. base + temp -> temp */ \ - asm_immi(offset, Rtemp); /* 1. offset -> temp */ \ - } \ - } - - gen_asm_loadXXi(16, w) - gen_asm_loadXXi(8, b) - - void Assembler::asm_load32(LIns *inst) { - LIns* base = inst->oprnd1(); - int offset = inst->disp(); - - Register result_reg = prepareResultReg(inst, GpRegs); - Register base_reg = getBaseReg(base, offset, GpRegs); - - switch (inst->opcode()) { - case LIR_lduc2ui: - asm_load8i(offset, base_reg, result_reg, false); - break; - case LIR_ldc2i: - asm_load8i(offset, base_reg, result_reg, true); - break; - case LIR_ldus2ui: - asm_load16i(offset, base_reg, result_reg, false); - break; - case LIR_lds2i: - asm_load16i(offset, base_reg, result_reg, true); - break; - case LIR_ldi: - asm_load32i(offset, base_reg, result_reg); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - } - freeResourcesOf(inst); - } - - void Assembler::asm_load64(LIns *inst) { - LIns* base = inst->oprnd1(); - int offset = inst->disp(); - - Register result_reg = prepareResultReg(inst, FpRegs); - Register base_reg = getBaseReg(base, offset, GpRegs); - - switch (inst->opcode()) { - case LIR_ldd: - asm_load64d(offset, base_reg, result_reg); - break; - case LIR_ldf2d: - asm_load32d(offset, base_reg, result_reg); - break; - default: - NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); - } - - freeResourcesOf(inst); - } - - void Assembler::asm_neg_not(LIns *inst) { - // TODO: Try to use the same register. - Register result_reg = prepareResultReg(inst, GpRegs); - Register operand_reg = findRegFor(inst->oprnd1(), GpRegs); - - underrunProtect(1 * sizeof(NIns)); - if (inst->isop(LIR_negi)) - SH4_neg(operand_reg, result_reg); - else - SH4_not(operand_reg, result_reg); - - freeResourcesOf(inst); - } - - void Assembler::asm_nongp_copy(Register dest_reg, Register src_reg) { - NanoAssert(IsFpReg(dest_reg) && IsFpReg(src_reg)); - - underrunProtect(2 * sizeof(NIns)); - SH4_fmov(src_reg, dest_reg); - SH4_fmov(src_reg + 1, dest_reg + 1); - } - - void Assembler::asm_param(LIns *inst) { - int arg_number = inst->paramArg(); - int kind = inst->paramKind(); - - if (kind == 0) { - // Ordinary parameter. - if (arg_number < NumArgRegs) { - // The incoming parameter is in register. - prepareResultReg(inst, rmask(argRegs[arg_number])); - } - else { - // The incoming parameter is on stack, and FP points - // nearby. Keep in sync' with genPrologue(). - Register reg = prepareResultReg(inst, GpRegs); - int offset = (arg_number - NumArgRegs) * sizeof(intptr_t) - + 2 * sizeof(int); /* saved PR and saved SP. */ - asm_load32i(offset, FP, reg); - } - } - else { - // Saved parameter. - prepareResultReg(inst, rmask(savedRegs[arg_number])); - // No code to generate. - } - freeResourcesOf(inst); - } - - // Keep in sync' with SH4_IMMD_NOCHK_SIZE. - void Assembler::asm_immd_nochk(uint64_t value, Register result_reg) { - NIns *over_constant = NULL; - - // Sanity check. - NanoAssert((uintptr_t)_nIns - SH4_IMMD_NOCHK_SIZE >= (uintptr_t)codeStart); - NanoAssert(result_reg != Rtemp); - - // 8. Load the "double" constant from @Rtemp. - SH4_fmovs_indRy(Rtemp, result_reg); - SH4_fmovs_incRy(Rtemp, result_reg + 1); - - // 7. Restore R0 from the stack. - SH4_movl_incRy(SP, R0); - - // 6. Get the address of the constant - SH4_mov(R0, Rtemp); - over_constant = _nIns; - - // 5. align the constant with nop - if (((uint32_t)_nIns & 0x3) != 0) - SH4_nop(); - - // 4. constant64 - SH4_emit32((uint32_t)(value >> 32)); - SH4_emit32((uint32_t)(value & 0x00000000FFFFFFFFLLU)); - - // 3. branch over the constant - SH4_nop(); - SH4_bra(SH4_LABEL(over_constant)); - - // 2. Compute the address of the constant. - SH4_mova_dispPC_R0(2 * sizeof(NIns)); - - // 1. Save R0 onto the stack since we need it soon. - SH4_movl_decRx(R0, SP); - } - - void Assembler::asm_immd(uint64_t value, Register result_reg) { - underrunProtect(SH4_IMMD_NOCHK_SIZE); - asm_immd_nochk(value, result_reg); - } - - void Assembler::asm_immd(LIns *inst) { - Register result_reg = prepareResultReg(inst, FpRegs); - asm_immd(inst->immDasQ(), result_reg); - freeResourcesOf(inst); - } - - bool Assembler::canRemat(LIns* inst) { - return inst->isImmI() || inst->isop(LIR_allocp); - } - - void Assembler::asm_restore(LIns *inst, Register reg) { - if (inst->isop(LIR_allocp)) { - int offset = FP_RELATIVE(arDisp(inst)); - - underrunProtect(2 * sizeof(NIns)); - if (FITS_SH4_add_imm(offset)){ - SH4_add_imm(offset, reg); // 2. reg + offset -> reg - SH4_mov(FP, reg); // 1. FP -> reg - } - else { - SH4_add(FP, reg); // 2. FP + reg -> reg - asm_immi(offset, reg); // 1. offset -> reg - } - } - else if (inst->isImmI()) { - asm_immi(inst->immI(), reg); - } - else { - int offset = findMemFor(inst); - if (IsGpReg(reg)) { - asm_load32i(offset, FP, reg); - } - else { - asm_load64d(offset, FP, reg); - } - } - } - - NIns* Assembler::genEpilogue() { - // Take the opportunity of this epilogue to pre-allocate space - // for constants right after. - asm_generate_pool(0); - - underrunProtect(6 * sizeof(NIns)); - - // 3. Perform a return to the caller using the return address. - SH4_nop(); - SH4_rts(); - - // 2. Restore the previous frame pointer. - SH4_movl_incRy(SP, FP); - - // 1. Restore the return address. - SH4_ldsl_incRx_PR(SP); - - // 0. Restore the stack pointer. - SH4_add_imm(STATIC_FP, SP); - SH4_mov(FP, SP); - - return _nIns; - } - - void Assembler::asm_ret(LIns* inst) { - genEpilogue(); - releaseRegisters(); - assignSavedRegs(); - - LIns *value = inst->oprnd1(); - - Register reg = inst->isop(LIR_retd) ? retDregs[0] : retRegs[0]; - findSpecificRegFor(value, reg); - } - - void Assembler::asm_spill(Register reg, int offset, bool quad) { - (void)quad; - - if (offset == 0) - return; // Nothing to spill. - - if (IsGpReg(reg)) { - NanoAssert(!quad); - asm_store32i(reg, offset, FP); - } - else { - NanoAssert(quad); - asm_store64d(reg, offset, FP); - } - } - -#define MIN_IMM -128 -#define MAX_IMM 127 - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - - // Keep in sync' with SH4_IMMI_NOCHK_SIZE. - void Assembler::asm_immi_nochk(int constant, Register reg, bool force) { - // Build the constant by adding several smaller constants that - // fit into the [very small] immediate field of SH4 instructions. - if (!force - && constant >= 2 * MIN_IMM - && constant <= 2 * MAX_IMM) { - int tmp; - - while (constant < MIN_IMM || constant > MAX_IMM) { - if (constant < 0) - tmp = MAX(constant, MIN_IMM); - else - tmp = MIN(constant, MAX_IMM); - - SH4_add_imm(tmp, reg); // 2. tmp + Rx -> Rx - constant -= tmp; - } - SH4_mov_imm(constant, reg); // 1. tmp -> Rx - } - // Otherwise use a constant stored in the instruction stream. - else { - NIns *over_constant = _nIns; - NIns *constant_addr = NULL; - - // Try to [re-]use a slot from the current pool. - if (!force && asm_load_constant(constant, reg)) - return; - - // 4. align the constant with nop - if (((uint32_t)_nIns & 0x3) != 0) - SH4_nop(); - - // 3. constant32 - SH4_emit32(constant); - constant_addr = _nIns; - - // 2. branch over the constant - SH4_nop(); - SH4_bra(SH4_LABEL(over_constant)); - - // 1. @(PC + offset) -> reg - SH4_movl_PCrel(constant_addr, reg); - } - } - -#undef MIN_IMM -#undef MAX_IMM - - void Assembler::asm_immi(int constant, Register reg, bool force) { - underrunProtect(SH4_IMMI_NOCHK_SIZE); - asm_immi_nochk(constant, reg, force); - } - - void Assembler::asm_store32i(Register value_reg, int offset, Register base_reg) { - // Rtemp is mandatory to handle the "worst" case. - NanoAssert(value_reg != Rtemp && base_reg != Rtemp); - - if (base_reg == FP) - offset = FP_RELATIVE(offset); - - if (offset == 0) { - // No displacement. - underrunProtect(1 * sizeof(NIns)); - SH4_movl_indRx(value_reg, base_reg); - } - else if (FITS_SH4_movl_dispRx(offset)) { - // The displacement fits the constraints of this instruction. - underrunProtect(1 * sizeof(NIns)); - SH4_movl_dispRx(value_reg, offset, base_reg); - } - else { - // General & worst case. - underrunProtect(2 * sizeof(NIns)); - SH4_movl_indRx(value_reg, Rtemp); // 3. value -> @(temp) - SH4_add(base_reg, Rtemp); // 2. base + temp -> temp - asm_immi(offset, Rtemp); // 1. offset -> temp - } - } - -#define gen_asm_storeXXi(size, length) \ - void Assembler::asm_store ## size ## i(Register value_reg, int offset, Register base_reg) { \ - if (base_reg == FP) \ - offset = FP_RELATIVE(offset); \ - \ - if (offset == 0) { \ - /* No displacement. */ \ - underrunProtect(1 * sizeof(NIns)); \ - SH4_mov ## length ## _indRx(value_reg, base_reg); \ - } \ - else if (FITS_SH4_mov ## length ## _R0_dispRx(offset)) { \ - /* The displacement is small enough to fit into the */ \ - /* special #size bits store "R0 -> @(base + offset)". */ \ - if (value_reg == R0) { \ - /* We are lucky, the value is R0. */ \ - underrunProtect(1 * sizeof(NIns)); \ - SH4_mov ## length ## _R0_dispRx(offset, base_reg); /* R0 -> @(base + offset) */ \ - } else { \ - /* We have to save and restore R0. */ \ - underrunProtect(4 * sizeof(NIns)); \ - SH4_mov(Rtemp, R0); /* 4. Rtemp -> R0 */ \ - SH4_mov ## length ## _R0_dispRx(offset, base_reg); /* 3. R0 -> @(base + offset) */ \ - SH4_mov(value_reg, R0); /* 2. value -> R0 */ \ - SH4_mov(R0, Rtemp); /* 1. R0 -> Rtemp */ \ - } \ - } \ - else { \ - /* Worst case. */ \ - underrunProtect(2 * sizeof(NIns)); \ - SH4_mov ## length ## _indRx(value_reg, Rtemp); /* 3. value -> @(temp) */ \ - SH4_add(base_reg, Rtemp); /* 2. base + temp -> temp */ \ - asm_immi(offset, Rtemp); /* 1. offset -> temp */ \ - } \ - } - - gen_asm_storeXXi(16, w) - gen_asm_storeXXi(8, b) - - void Assembler::asm_store32(LOpcode opcode, LIns *value, int offset, LIns *base) { - Register value_reg; - Register base_reg; - - getBaseReg2(GpRegs, value, value_reg, GpRegs, base, base_reg, offset); - - switch (opcode) { - case LIR_sti: - asm_store32i(value_reg, offset, base_reg); - break; - case LIR_sti2s: - asm_store16i(value_reg, offset, base_reg); - break; - case LIR_sti2c: - asm_store8i(value_reg, offset, base_reg); - break; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - return; - } - } - - void Assembler::asm_store64d(Register value_reg, int offset, Register base_reg) { - underrunProtect(2 * sizeof(NIns)); - - // 2. Store the "double" to @Rtemp. - SH4_fmovs_decRx(value_reg + 1, Rtemp); - SH4_fmovs_decRx(value_reg, Rtemp); - - // Adjust the offset since we are using pre-decrementd (by 4) indirect loads. - offset += 8; - - // 1. base + offset -> Rtemp. - asm_base_offset(offset, base_reg); - } - - void Assembler::asm_store32d(Register value_reg, int offset, Register base_reg) { - underrunProtect(3 * sizeof(NIns)); - - // 3. Store the "float" to @Rtemp. - SH4_fmovs_indRx(Dtemp, Rtemp); - - // 2. Convert back to simple precision. - SH4_fsts_FPUL(Dtemp); - SH4_fcnvds_double_FPUL(value_reg); - - // 1. base + offset -> Rtemp. - asm_base_offset(offset, base_reg); - } - - void Assembler::asm_store64(LOpcode opcode, LIns *value, int offset, LIns *base) { - Register value_reg; - Register base_reg; - - // If "value" is already in a register, use that one. - value_reg = ( value->isInReg() ? value->getReg() : findRegFor(value, FpRegs) ); - base_reg = getBaseReg(base, offset, GpRegs); - - switch (opcode) { - case LIR_std: - asm_store64d(value_reg, offset, base_reg); - break; - case LIR_std2f: - asm_store32d(value_reg, offset, base_reg); - break; - default: - NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); - } - } - -#ifdef NJ_VERBOSE - void Assembler::asm_inc_m32(uint32_t *counter) { - NIns *over_constant = NULL; - NIns *constant_addr = NULL; - - // Increment the 32-bit profiling counter without changing any registers. - - underrunProtect(11 * sizeof(NIns)); - - // 6. restore the register used as a pointer to the counter - SH4_movl_incRy(SP, R0); - - // 5. (*counter)++ - SH4_movl_indRx(Rtemp, R0); - SH4_add_imm(1, Rtemp); - SH4_movl_indRy(R0, Rtemp); - - over_constant = _nIns; - // 4. align the constant with nop - if (((uint32_t)_nIns & 0x3) != 0) - SH4_nop(); - - // 3. constant (== counter address) - SH4_emit32((uint32_t)counter); - constant_addr = _nIns; - - // 2. branch over the constant - SH4_nop(); - SH4_bra(SH4_LABEL(over_constant)); - - // 1. @(PC + offset) -> R0 - SH4_movl_PCrel(constant_addr, R0); - - // 0. R0 will be used as a pointer to the counter - SH4_movl_decRx(R0, SP); - } -#endif - - void Assembler::asm_cmp(LOpcode simplified_opcode, LIns *condition) { - LIns *operand1 = condition->oprnd1(); - LIns *operand2 = condition->oprnd2(); - - Register operand1_reg; - Register operand2_reg; - - if (isCmpDOpcode(simplified_opcode)) - findRegFor2(FpRegs, operand1, operand1_reg, FpRegs, operand2, operand2_reg); - else - findRegFor2(GpRegs, operand1, operand1_reg, GpRegs, operand2, operand2_reg); - - underrunProtect(3 * sizeof(NIns)); - - switch(simplified_opcode) { - case LIR_eqi : SH4_cmpeq(operand2_reg, operand1_reg); break; - case LIR_gti : SH4_cmpgt(operand2_reg, operand1_reg); break; - case LIR_gei : SH4_cmpge(operand2_reg, operand1_reg); break; - case LIR_gtui : SH4_cmphi(operand2_reg, operand1_reg); break; - case LIR_geui : SH4_cmphs(operand2_reg, operand1_reg); break; - - case LIR_eqd : SH4_fcmpeq_double(operand2_reg, operand1_reg); break; - case LIR_gtd : SH4_fcmpgt_double(operand2_reg, operand1_reg); break; - case LIR_ltd : SH4_fcmpgt_double(operand1_reg, operand2_reg); break; - - case LIR_ged : { /* (A >= B) <==> ((A == B) || (A > B)) */ - NIns *end_of_test = _nIns; - SH4_fcmpeq_double(operand2_reg, operand1_reg); // 3. A == B ? - SH4_bt(SH4_LABEL(end_of_test)); // 2. skip to preserve T-bit. - SH4_fcmpgt_double(operand2_reg, operand1_reg); // 1. A > B ? - } - break; - - case LIR_led : { /* (A <= B) <==> ((A == B) || (B > A)) */ - NIns *end_of_test = _nIns; - SH4_fcmpeq_double(operand2_reg, operand1_reg); // 2. A == B ? - SH4_bt(SH4_LABEL(end_of_test)); // 2. skip to preserve T-bit. - SH4_fcmpgt_double(operand1_reg, operand2_reg); // 1. B > A ? - } - break; - - default: - NanoAssertMsg(0, "asm_cmp should never receive this LIR opcode"); - } - } - - NIns* Assembler::asm_branch_ov(LOpcode opcode, NIns* target) { - NIns *patch_target = NULL; - - if (opcode == LIR_mulxovi || opcode == LIR_muljovi) { - patch_target = asm_branch(false, target); - - underrunProtect(3 * sizeof(NIns)); - - // Remember the T-bit is equal to : - // sign_of(result_reg) == sign_of(operand1) XOR sign_of(operand2) - - SH4_tst(Rtemp, Rtemp); // 3. test Rtemp == 0, hence asm_branch(false) - SH4_rotcl(Rtemp); // 2. Rtemp << 1 + T-Bit -> Rtemp - SH4_sts_MACH(Rtemp); // 1. Hi32(MAC) -> Rtemp - } - else - patch_target = asm_branch(true, target); - - return patch_target; - } - - bool Assembler::simplifyOpcode(LOpcode &opcode) { - bool negated= false; - - // Those following opcodes do not exist on SH4, so we have - // to reverse the test: - switch(opcode) { - case LIR_lti: - opcode = LIR_gei; - negated = true; - break; - - case LIR_lei: - opcode = LIR_gti; - negated = true; - break; - - case LIR_ltui: - opcode = LIR_geui; - negated = true; - break; - - case LIR_leui: - opcode = LIR_gtui; - negated = true; - break; - - default: - break; - } - - return negated; - } - - NIns *Assembler::asm_branch(bool test, NIns *target) { - NIns *patch_target = NULL; - NIns *over_pool = NULL; - - underrunProtect(3 * sizeof(NIns) + SH4_IMMI_NOCHK_SIZE); - - // Try to select the optimal branch code sequence. - if (target != NULL && FITS_SH4_bt(SH4_LABEL(target))) { - // The target is close enough to be reached thanks to one - // instruction only. Note: FITS_SH4_bf <==> FITS_SH4_bt - if (test == true) - SH4_bt(SH4_LABEL(target)); - else - SH4_bf(SH4_LABEL(target)); - - return NULL; - } - - over_pool = _nIns; - - // Take the opportunity of this unconditional branch to - // pre-allocate space for constants right after. The size is - // limited to 50 words because of the limitation of the - // instruction bf/t: branch at most 256 bytes far away. - // - // pool size = 50 * sizeof(word) = 200 bytes - // + pool alignement = ~1 * sizeof(half) = 2 bytes - // + instructions below = ~10 * sizeof(NIns) = 20 bytes - // total = 222 bytes - // - // Note asm_generate_pool() prevents any code buffer - // allocation so there is no need for underrunProtect(), - // however we still have to reserve space for the instructions - // below. - asm_generate_pool(3 * sizeof(NIns) + SH4_IMMI_NOCHK_SIZE, 50); - - // Check if the target is not so far we can reach it with an - // unconditional branch wrapped around by a reversed branch. - if (target != NULL && FITS_SH4_bra(SH4_LABEL(target + 1 * sizeof(NIns)))) { - // 2. branch #target - SH4_nop(); - SH4_bra(SH4_LABEL(target)); - - // 1. branch over 2. if true (negated test) - if (test == true) - SH4_bf(SH4_LABEL(over_pool)); - else - SH4_bt(SH4_LABEL(over_pool)); - - return NULL; - } - - // Otherwise it's the worst case. - - // 3. jump @target_reg - SH4_nop(); - SH4_jmp_indRx(Rtemp); - - // 2. target -> target_reg - asm_immi_nochk((int)target, Rtemp, true); - patch_target = _nIns; - - // 1. branch over 3. if true (negated test) - if (test == true) - SH4_bf(SH4_LABEL(over_pool)); - else - SH4_bt(SH4_LABEL(over_pool)); - - return patch_target; - } - - Branches Assembler::asm_branch(bool branchOnFalse, LIns *condition, NIns *target) { - NanoAssert(condition->isCmp()); - - LOpcode opcode = condition->opcode(); - bool negated = simplifyOpcode(opcode); - - NIns *patch_target = asm_branch(negated ? branchOnFalse : !branchOnFalse, target); - - asm_cmp(opcode, condition); - - return Branches(patch_target); - } - - void Assembler::nBeginAssembly() { - max_stack_args = 0; - } - - void Assembler::nFragExit(LIns *guard) { - Fragment *target = guard->record()->exit->target; - GuardRecord *guard_record = NULL; - - // 3. Jump to the target fragment. - if (target && target->fragEntry) - JMP(target->fragEntry); - else { - // The target fragment doesn't exist yet, so emit a jump to the epilogue. - // If the target is created later on, the jump will be patched. - if (!_epilogue) - _epilogue = genEpilogue(); - - // Take the opportunity of this unconditional branch to create - // a new pool right after the code sequence. - asm_generate_pool(0); - - underrunProtect(2 * sizeof(NIns)); - - // Use a branch code sequence patchable by nPatchBranch(), - // that is, with a constant pool instead of an immediate. - SH4_nop(); - SH4_jmp_indRx(Rtemp); - asm_immi((int)_epilogue, Rtemp, true); - - guard_record = guard->record(); - guard_record->jmp = _nIns; - } - - // Profiling for the exit. - verbose_only( - if (_logc->lcbits & LC_FragProfile) - asm_inc_m32(&guard->record()->profCount); - ) - - // 2. Restore the stack pointer. - MR(SP, FP); - - // 1. Set the return value. - asm_immi((int)guard_record, R0); - } - - void Assembler::nInit() { - int fpscr = 0; - - __asm__ __volatile__ ("sts fpscr, %0": "=r" (fpscr)); - - // Only the 'double' precision mode is currently supported within Nano/SH4. - NanoAssert((fpscr & (1 << 19)) != 0); - - // Only the 'simple' transfer size mode is currently supported within Nano/SH4. - NanoAssert((fpscr & (1 << 20)) == 0); - - // Declare prefered registers for some specific opcodes. - nHints[LIR_calli] = rmask(retRegs[0]); - nHints[LIR_calld] = rmask(retDregs[0]); - nHints[LIR_paramp] = PREFER_SPECIAL; - nHints[LIR_sti2s] = rmask(R0); - nHints[LIR_sti2c] = rmask(R0); - nHints[LIR_lduc2ui] = rmask(R0); - nHints[LIR_ldc2i] = rmask(R0); - nHints[LIR_ldus2ui] = rmask(R0); - nHints[LIR_lds2i] = rmask(R0); - nHints[LIR_ldi] = rmask(R0); - - current_pool.nb_slots = 0; - current_pool.slots = NULL; - } - - // Keep in sync' with LARGEST_BRANCH_PATCH. - void Assembler::nPatchBranch(NIns *branch, NIns *target) { - // Adjust the address to point to the constant (three instructions below). - branch += 3; - - // Patch the constant. - *((uint32_t *)(void *)branch) = (uint32_t) target; - } - - Register Assembler::nRegisterAllocFromSet(RegisterMask set) { - Register reg; - - // Find the first register in this set. - reg = lsReg(set); - - _allocator.free &= ~rmask(reg); - - // Sanity check. - NanoAssert((rmask(reg) & set) == rmask(reg)); - - return reg; - } - - void Assembler::nRegisterResetAll(RegAlloc& regs) { - regs.clear(); - regs.free = GpRegs | FpRegs; - } - - // Keep in sync' with Assembler::asm_param(). - NIns* Assembler::genPrologue() { - int adjusted_stack_size = max_stack_args + _activation.stackSlotsNeeded() * STACK_GRANULARITY - STATIC_FP; - adjusted_stack_size = alignUp(adjusted_stack_size, NJ_ALIGN_STACK); - - underrunProtect(SH4_IMMI_NOCHK_SIZE + 5 * sizeof(NIns)); - - // 4. Adjust the SP to the expected value. - if (adjusted_stack_size != 0) { - adjusted_stack_size = -adjusted_stack_size; - - if (FITS_SH4_add_imm(adjusted_stack_size)) - SH4_add_imm(adjusted_stack_size, SP); - else { - // We use R0 since it's not used for regular arguments, - // and is otherwise clobberred by the call. - SH4_add(R0, SP); - asm_immi_nochk(adjusted_stack_size, R0); - } - } - - // 3. Move the FP to a "static" point, this allows - // positive addressing, more efficient on SH4. - SH4_mov(SP, FP); - SH4_add_imm(-STATIC_FP, SP); - - NIns *patchEntry = _nIns; - - // 2. Save the return address. - SH4_stsl_PR_decRx(SP); - - // 1. Save the previous frame pointer. - SH4_movl_decRx(FP, SP); - - return patchEntry; - } - - RegisterMask Assembler::nHint(LIns* inst) { - RegisterMask prefer = 0; - - NanoAssert(inst->isop(LIR_paramp)); - if (inst->paramKind() == 0) - if (inst->paramArg() < 4) - prefer = rmask(argRegs[inst->paramArg()]); - - return prefer; - } - - void Assembler::swapCodeChunks() { - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - void Assembler::underrunProtect(int nb_bytes) { - NanoAssertMsg(nb_bytes <= LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - - if ((uintptr_t)_nIns - nb_bytes < (uintptr_t)codeStart) { - NIns* target = _nIns; - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - - // This jump will call underrunProtect again, but since we're on - // a new page large enough to host its code, nothing will happen. - JMP(target, true); - } - } - - void Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported - } - -#define MOVL_PCREL_RANGE 1020 - - // Note: reserved_bytes can be 0 only if the call to - // asm_generate_pool() is followed by a call to - // underrunProtect() since this former does not call this - // latter. - bool Assembler::asm_generate_pool(int reserved_bytes, int nb_slots) { - const int instructions_per_constant = 10; - int i; - - nb_slots = MIN(MAX_NB_SLOTS, nb_slots); - - // Don't pre-allocate a new pool if the number of instructions - // that can reach the first free slot is lesser than the - // number of reachable slots multiplied by the density of the - // constant/instructions: - // - // scalar ]x, y] < scalar [m, n] * instructions_per_constant - // - // where x, y, m ,n are defined as below: - // - // start of code buffer ----> (low addresses) - // ... - // instruction y <----+ (not yet emitted) - // ... : - // current _nIns +----> instruction x | - // : ... : - // | instruction 2 | maximum range - // first instruction +----> instruction 1 | for movl_dispPC - // | +-------------+ | - // | | free slot n | | - // : : ... : : - // first free slot +----> | free slot m | <----+ - // : ... : - // | used slot 2 | - // | used slot 1 | - // +-------------+ - // ... - // end of code buffer ----> (high addresses) - // - // - // The idea is to not allocate a new pool each time to avoid - // the waste of memory. - // - // Note I aligned instruction_x on a 4-word boundary since it - // is used as the start address of the pool. - - int scalar_mn = 0; - int scalar_xy = 0; - uintptr_t instruction_x = (uintptr_t)_nIns & ~0x3; - uintptr_t instruction_y = instruction_x; - uintptr_t code_start = (uintptr_t)codeStart + reserved_bytes; - uintptr_t code_end = (uintptr_t)codeEnd; - - // Ensure we don't use a pool from another code buffer. - if ( (uintptr_t)current_pool.slots < code_start - || (uintptr_t)current_pool.slots > code_end) - current_pool.nb_slots = 0; - - // Compute the expression "scalar [m, n]" - for (i = 0; i < current_pool.nb_slots; i++) { - if (current_pool.slots[i] == 0) { - scalar_mn = current_pool.nb_slots - i; - instruction_y = (uintptr_t)¤t_pool.slots[i] - MOVL_PCREL_RANGE; - break; - } - } - - // Ensure we stay within the current code buffer. - if ( instruction_y < code_start - || instruction_y > code_end) - instruction_y = code_start - 1 * sizeof(NIns); - - // Compute the expression "scalar ]x, y]" - scalar_xy = (ptrdiff_t)(instruction_x - instruction_y) / (int)sizeof(NIns); - scalar_xy--; - - if (scalar_xy > 0 && scalar_xy <= scalar_mn * instructions_per_constant) { - verbose_only(verbose_outputf("pool not emitted: 0 < scalar_xy [%d] <= %d * scalar_mn [%d]", - (int)scalar_xy, instructions_per_constant, scalar_mn)); - return false; - } - - // Shrink the size of the new pool to ensure it will not - // require an new code buffer. - while (instruction_x - nb_slots * (sizeof(int) + instructions_per_constant * sizeof(NIns)) < code_start) - nb_slots--; - - if (nb_slots <= 2) { - verbose_only(verbose_outputf("pool not emitted: not enough space in the current code buffer.")); - return false; - } - - // Fill the pool with dummy values. - current_pool.slots = (int *)(instruction_x - nb_slots * sizeof(int)); - current_pool.nb_slots = nb_slots; - memset(current_pool.slots, 0, nb_slots * sizeof(int)); - - _nIns = (NIns *)current_pool.slots; - - verbose_only(verbose_outputf("pool emitted: 0 >= scalar_xy [%d] > %d * scalar_mn [%d]; address = %p, size = %d", - (int)scalar_xy, instructions_per_constant, scalar_mn, current_pool.slots, current_pool.nb_slots)); - return true; - } - - bool Assembler::asm_load_constant(int constant, Register reg) { - bool has_generated_pool = false; - int *slot; - NIns *over_pool; - - // Generate a new pool if there is no current pool. - if (current_pool.slots == NULL) { - over_pool = _nIns; - - // reserved_bytes was set to ``2 * sizeof(NIns)`` to - // ensure there's still enough space after the pool - // allocation for the instructions below. - has_generated_pool = asm_generate_pool(2 * sizeof(NIns)); - if (!has_generated_pool) - return false; - - // No need for underrunProtect() here since it was done by - // the caller of asm_immi_nochk(). - SH4_nop(); - SH4_bra(SH4_LABEL(over_pool)); - } - - try_again: - - // Search the farther reachable slot in the current pool ... - uintptr_t farther_slot = MIN((((uintptr_t) _nIns) & ~0x3) + MOVL_PCREL_RANGE, - (uintptr_t) ¤t_pool.slots[current_pool.nb_slots - 1]); - - verbose_only(verbose_outputf("pool load: _nIns = %p, constant = %d, farther_slot = 0x%x", _nIns, constant, farther_slot)); - - // ... search from that point ... - for (slot = (int *)farther_slot; slot >= current_pool.slots; slot--) { - // ... if this constant was already stored in the current pool ... - if (*slot == constant) { - verbose_only(verbose_outputf("pool load: found same constant in slot @ %p", slot)); - SH4_movl_PCrel(slot, reg); - return true; - } - // ... otherwise, use the first free slot. - if (*slot == 0) { - verbose_only(verbose_outputf("pool load: found a free slot @ %p", slot)); - *slot = constant; - SH4_movl_PCrel(slot, reg); - return true; - } - } - - NanoAssert(!has_generated_pool); - - over_pool = _nIns; - - // reserved_bytes was set to ``1 * sizeof(NIns)`` to ensure - // there's still enough space after the pool allocation for - // the SH4_movl_PCrel above (backward goto). - has_generated_pool = asm_generate_pool(1 * sizeof(NIns)); - if (!has_generated_pool) { - verbose_only(verbose_outputf("pool load: no slot found", slot)); - return false; - } - else { - SH4_nop(); - SH4_bra(SH4_LABEL(over_pool)); - goto try_again; - } - } -} - -#endif // FEATURE_NANOJIT && FEATURE_SH4 diff --git a/deps/mozjs/js/src/nanojit/NativeSH4.h b/deps/mozjs/js/src/nanojit/NativeSH4.h deleted file mode 100644 index b90482a2c8d..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeSH4.h +++ /dev/null @@ -1,481 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * STMicroelectronics. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Cédric VINCENT for STMicroelectronics - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_NativeSH4__ -#define __nanojit_NativeSH4__ - -#include "NativeCommon.h" - -namespace nanojit -{ - /*********************************************************************** - * Definitions for the register allocation. - */ - - // General purpose and ABI registers. - - // Scratch registers (a.k.a caller-saved, a.k.a local). - static const Register R0 = { 0 }; - static const Register R1 = { 1 }; - static const Register R2 = { 2 }; - static const Register R3 = { 3 }; // Excluded from the regalloc because of its use as a hyper-scratch. - static const Register R4 = { 4 }; - static const Register R5 = { 5 }; - static const Register R6 = { 6 }; - static const Register R7 = { 7 }; - - // Saved registers (a.k.a callee-saved, a.k.a global). - static const Register R8 = { 8 }; - static const Register R9 = { 9 }; - static const Register R10 = { 10 }; - static const Register R11 = { 11 }; - static const Register R12 = { 12 }; - static const Register R13 = { 13 }; - - // ABI registers, excluded from the register allocation. - static const Register FP = { 14 }; - static const Register SP = { 15 }; - - // Floatting-point registers. - static const Register _D0 = { 16 }; - static const Register _F0 = _D0; - static const Register _F1 = { 17 }; - static const Register _D1 = { 18 }; - static const Register _F2 = _D1; - static const Register _F3 = { 19 }; - static const Register _D2 = { 20 }; - static const Register _F4 = _D2; - static const Register _F5 = { 21 }; - static const Register _D3 = { 22 }; - static const Register _F6 = _D3; - static const Register _F7 = { 23 }; - static const Register _D4 = { 24 }; - static const Register _F8 = _D4; - static const Register _F9 = { 25 }; - static const Register _D5 = { 26 }; - static const Register _F10 = _D5; - static const Register _F11 = { 27 }; - static const Register _D6 = { 28 }; - static const Register _F12 = _D6; - static const Register _F13 = { 29 }; - static const Register _D7 = { 30 }; - static const Register _F14 = _D7; // Excluded from the regalloc because of its use as a hyper-scratch. - static const Register _F15 = { 31 }; - - // Helpers. - static const Register deprecated_UnknownReg = { 32 }; - static const Register UnspecifiedReg = { 32 }; - static const Register Rtemp = R3; - static const Register Dtemp = _D7; - - static const uint32_t FirstRegNum = 0; - static const uint32_t LastRegNum = 30; -} - -#define NJ_USE_UINT32_REGISTER 1 -#include "NativeCommon.h" - -namespace nanojit -{ - // There's 16 integer registers + 8 double registers on SH4. - typedef uint32_t RegisterMask; - - static const int NumSavedRegs = 6; - static const RegisterMask SavedRegs = ((1<int conversion. -#define NJ_F2I_SUPPORTED 1 - -} - -#endif /* __nanojit_NativeSH4__ */ diff --git a/deps/mozjs/js/src/nanojit/NativeSparc.cpp b/deps/mozjs/js/src/nanojit/NativeSparc.cpp deleted file mode 100644 index 8142082d688..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeSparc.cpp +++ /dev/null @@ -1,1645 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * leon.sha@oracle.com - * ginn.chen@oracle.com - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include -#include -#include -#include "nanojit.h" - -namespace nanojit -{ -#ifdef FEATURE_NANOJIT - -#ifdef NJ_VERBOSE - const char *regNames[] = { - "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", - "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", - "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", - "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", - "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", - "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", - "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", - "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31" - }; -#endif - - const Register Assembler::argRegs[] = { I0, I1, I2, I3, I4, I5 }; - const Register Assembler::retRegs[] = { O0 }; - const Register Assembler::savedRegs[] = { L1 }; // Dummy element not used, as NumSavedRegs == 0 - - static const int kLinkageAreaSize = 68; - static const int kcalleeAreaSize = 80; // The max size. - -#define BIT_ROUND_UP(v,q) ( (((uintptr_t)v)+(q)-1) & ~((q)-1) ) -#define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0) - - inline void Assembler::CALL(const CallInfo* ci) { - int32_t offset = (ci->_address) - ((int32_t)_nIns) + 4; - int32_t i = 0x40000000 | ((offset >> 2) & 0x3FFFFFFF); - IMM32(i); - asm_output("call %s",(ci->_name)); - } - - inline void Assembler::IntegerOperation - (Register rs1, Register rs2, Register rd, int32_t op3, const char *opcode) { - Format_3_1(2, rd, op3, rs1, 0, rs2); - asm_output("%s %s, %s, %s", opcode, gpn(rs1), gpn(rs2), gpn(rd)); - } - - inline void Assembler::IntegerOperationI - (Register rs1, int32_t simm13, Register rd, int32_t op3, const char *opcode) { - Format_3_1I(2, rd, op3, rs1, simm13); - asm_output("%s %s, %d, %s", opcode, gpn(rs1), simm13, gpn(rd)); - } - - inline void Assembler::ADD(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0, "add"); - } - inline void Assembler::ADDCC(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x10, "addcc"); - } - inline void Assembler::AND(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x1, "and"); - } - inline void Assembler::ANDCC(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x11, "andcc"); - } - inline void Assembler::OR(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x2, "or"); - } - inline void Assembler::ORI(Register rs1, int32_t simm13, Register rd) { - IntegerOperationI(rs1, simm13, rd, 0x2, "or"); - } - inline void Assembler::ORN(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x6, "orn"); - } - inline void Assembler::SMULCC(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x1b, "smulcc"); - } - inline void Assembler::SUB(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x4, "sub"); - } - inline void Assembler::SUBCC(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x14, "subcc"); - } - inline void Assembler::SUBI(Register rs1, int32_t simm13, Register rd) { - IntegerOperationI(rs1, simm13, rd, 0x4, "sub"); - } - inline void Assembler::XOR(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x3, "xor"); - } - - inline void Assembler::Bicc(int32_t a, int32_t dsp22, int32_t cond, const char *opcode) { - Format_2_2(a, cond, 0x2, dsp22); - asm_output("%s 0x%x", opcode, _nIns + dsp22 - 1); - } - - inline void Assembler::BA (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x8, "ba"); } - inline void Assembler::BE (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x1, "be"); } - inline void Assembler::BNE (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x9, "bne"); } - inline void Assembler::BG (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0xa, "bg"); } - inline void Assembler::BGU (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0xc, "bgu"); } - inline void Assembler::BGE (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0xb, "bge"); } - inline void Assembler::BL (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x3, "bl"); } - inline void Assembler::BLE (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x2, "ble"); } - inline void Assembler::BLEU(int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x4, "bleu"); } - inline void Assembler::BCC (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0xd, "bcc"); } - inline void Assembler::BCS (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x5, "bcs"); } - inline void Assembler::BVC (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0xf, "bvc"); } - inline void Assembler::BVS (int32_t a, int32_t dsp22) { Bicc(a, dsp22, 0x7, "bvs"); } - - inline void Assembler::FABSS(Register rs2, Register rd) { - Format_3_8(2, rd, 0x34, G0, 0x9, rs2); - asm_output("fabs %s, %s", gpn(rs2), gpn(rd)); - } - - inline void Assembler::FADDD(Register rs1, Register rs2, Register rd) { - Format_3_8(2, rd, 0x34, rs1, 0x42, rs2); - asm_output("faddd %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); - } - - inline void Assembler::FBfcc(int32_t a, int32_t dsp22, int32_t cond, const char *opcode) { - Format_2_2(a, cond, 0x6, dsp22); - asm_output("%s 0x%x", opcode, _nIns + dsp22 - 1); - } - - inline void Assembler::FBE (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x9, "fbe"); } - inline void Assembler::FBNE (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x1, "fbne"); } - inline void Assembler::FBUE (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0xa, "fbue"); } - inline void Assembler::FBG (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x6, "fbg"); } - inline void Assembler::FBUG (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x5, "fbug"); } - inline void Assembler::FBGE (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0xb, "fbge"); } - inline void Assembler::FBUGE(int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0xc, "fbuge"); } - inline void Assembler::FBL (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x4, "fbl"); } - inline void Assembler::FBUL (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0x3, "fbul"); } - inline void Assembler::FBLE (int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0xd, "fble"); } - inline void Assembler::FBULE(int32_t a, int32_t dsp22) { FBfcc(a, dsp22, 0xe, "fbule"); } - - inline void Assembler::FCMPD(Register rs1, Register rs2) { - Format_3_9(2, 0, 0, 0x35, rs1, 0x52, rs2); - asm_output("fcmpd %s, %s", gpn(rs1), gpn(rs2)); - } - - inline void Assembler::FloatOperation - (Register rs1, Register rs2, Register rd, int32_t opf, const char *opcode) { - Format_3_8(2, rd, 0x34, rs1, opf, rs2); - if (rs1 != G0) { - asm_output("%s %s, %s, %s", opcode, gpn(rs1), gpn(rs2), gpn(rd)); - } else { - asm_output("%s %s, %s", opcode, gpn(rs2), gpn(rd)); - } - } - - inline void Assembler::FSUBD(Register rs1, Register rs2, Register rd) { - FloatOperation(rs1, rs2, rd, 0x46, "fsubd"); - } - inline void Assembler::FMULD(Register rs1, Register rs2, Register rd) { - FloatOperation(rs1, rs2, rd, 0x4a, "fsubd"); - } - inline void Assembler::FDTOI(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0xd2, "fdtoi"); - } - inline void Assembler::FDIVD(Register rs1, Register rs2, Register rd) { - FloatOperation(rs1, rs2, rd, 0x4e, "fdivd"); - } - inline void Assembler::FMOVD(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0x2, "fmovd"); - } - inline void Assembler::FNEGD(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0x6, "fnegd"); - } - inline void Assembler::FITOD(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0xc8, "fitod"); - } - inline void Assembler::FDTOS(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0xc6, "fdtos"); - } - inline void Assembler::FSTOD(Register rs2, Register rd) { - FloatOperation(G0, rs2, rd, 0xc9, "fstod"); - } - - inline void Assembler::JMPL(Register rs1, Register rs2, Register rd) { - Format_3_1(2, rd, 0x38, rs1, 0, rs2); - asm_output("jmpl [%s + %s]", gpn(rs1), gpn(rs2)); - } - - inline void Assembler::JMPLI(Register rs1, int32_t simm13, Register rd) { - Format_3_1I(2, rd, 0x38, rs1, simm13); - asm_output("jmpl [%s + 0x%x]", gpn(rs1), simm13); - } - - inline void Assembler::LoadOperation - (Register rs1, Register rs2, Register rd, int32_t op3, const char* opcode) { - Format_3_1(3, rd, op3, rs1, 0, rs2); - asm_output("%s [%s + %s], %s", opcode, gpn(rs1), gpn(rs2), gpn(rd)); - } - - inline void Assembler::LoadOperationI - (Register rs1, int32_t simm13, Register rd, int32_t op3, const char* opcode) { - Format_3_1I(3, rd, op3, rs1, simm13); - asm_output("%s [%s + 0x%x], %s", opcode, gpn(rs1), simm13, gpn(rd)); - } - - inline void Assembler::LDF(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0x20, "ldf"); - } - inline void Assembler::LDFI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0x20, "ldf"); - } - - inline void Assembler::LDF32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDFI(rs1, immI, rd); - } else { - LDF(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDDF32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI+4)) { - LDFI(rs1, immI+4, rd + 1); - LDFI(rs1, immI, rd); - } else { - LDF(rs1, L0, rd + 1); - SET32(immI+4, L0); - LDF(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDUB(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0x1, "ldub"); - } - inline void Assembler::LDUBI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0x1, "ldub"); - } - - inline void Assembler::LDUB32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDUBI(rs1, immI, rd); - } else { - LDUB(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDSB(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0x9, "ldsb"); - } - inline void Assembler::LDSBI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0x9, "ldsb"); - } - - inline void Assembler::LDSB32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDSBI(rs1, immI, rd); - } else { - LDSB(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDUH(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0x2, "lduh"); - } - inline void Assembler::LDUHI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0x2, "lduh"); - } - - inline void Assembler::LDUH32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDUHI(rs1, immI, rd); - } else { - LDUH(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDSH(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0xa, "ldsh"); - } - inline void Assembler::LDSHI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0xa, "ldsh"); - } - - inline void Assembler::LDSH32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDSHI(rs1, immI, rd); - } else { - LDSH(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::LDSW(Register rs1, Register rs2, Register rd) { - LoadOperation(rs1, rs2, rd, 0x8, "ldsw"); - } - inline void Assembler::LDSWI(Register rs1, int32_t simm13, Register rd) { - LoadOperationI(rs1, simm13, rd, 0x8, "ldsw"); - } - - inline void Assembler::LDSW32(Register rs1, int32_t immI, Register rd) { - if (isIMM13(immI)) { - LDSWI(rs1, immI, rd); - } else { - LDSW(rs1, L0, rd); - SET32(immI, L0); - } - } - - inline void Assembler::MOVcc - (Register rs, int32_t cc2, int32_t cc1, int32_t cc0, Register rd, int32_t cond, const char *opcode) { - Format_4_2(rd, 0x2c, cc2, cond, cc1, cc0, rs); - asm_output("%s %s, %s", opcode, gpn(rs), gpn(rd)); - } - - inline void Assembler::MOVccI - (int32_t simm11, int32_t cc2, int32_t cc1, int32_t cc0, Register rd, int32_t cond, const char *opcode) { - Format_4_2I(rd, 0x2c, cc2, cond, cc1, cc0, simm11); - asm_output("%s 0x%x, %s", opcode, simm11, gpn(rd)); - } - - inline void Assembler::MOVE (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x1, "move"); } - inline void Assembler::MOVNE (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x9, "movne"); } - inline void Assembler::MOVL (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x3, "movl"); } - inline void Assembler::MOVLE (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x2, "movle"); } - inline void Assembler::MOVG (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0xa, "movg"); } - inline void Assembler::MOVGE (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0xb, "movge"); } - inline void Assembler::MOVLEU(Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x4, "movleu"); } - inline void Assembler::MOVGU (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0xc, "movgu"); } - inline void Assembler::MOVCC (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0xd, "movcc"); } - inline void Assembler::MOVCS (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0x5, "movcs"); } - inline void Assembler::MOVVC (Register rs, Register rd) { MOVcc(rs, 1, 0, 0, rd, 0xf, "movvc"); } - inline void Assembler::MOVEI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x1, "move"); } - inline void Assembler::MOVNEI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x9, "movne"); } - inline void Assembler::MOVLI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x3, "movl"); } - inline void Assembler::MOVLEI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x2, "movle"); } - inline void Assembler::MOVGI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0xa, "movg"); } - inline void Assembler::MOVGEI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0xb, "movge"); } - inline void Assembler::MOVLEUI(int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x4, "movleu"); } - inline void Assembler::MOVGUI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0xc, "movgu"); } - inline void Assembler::MOVCCI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0xd, "movcc"); } - inline void Assembler::MOVCSI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x5, "movcs"); } - inline void Assembler::MOVVSI (int32_t simm11, Register rd) { MOVccI(simm11, 1, 0, 0, rd, 0x7, "movvs"); } - inline void Assembler::MOVFEI (int32_t simm11, Register rd) { MOVccI(simm11, 0, 0, 0, rd, 0x9, "movfe"); } - inline void Assembler::MOVFLI (int32_t simm11, Register rd) { MOVccI(simm11, 0, 0, 0, rd, 0x4, "movfl"); } - inline void Assembler::MOVFLEI(int32_t simm11, Register rd) { MOVccI(simm11, 0, 0, 0, rd, 0xd, "movfle"); } - inline void Assembler::MOVFGI (int32_t simm11, Register rd) { MOVccI(simm11, 0, 0, 0, rd, 0x6, "movfg"); } - inline void Assembler::MOVFGEI(int32_t simm11, Register rd) { MOVccI(simm11, 0, 0, 0, rd, 0xb, "movfge"); } - - inline void Assembler::FMOVDcc(Register rs, int32_t opt_cc, Register rd, int32_t cond, const char *opcode) { - Format_4_5(rd, 0x35, cond, opt_cc, 0x2, rs); - asm_output("%s %s, %s", opcode, gpn(rs), gpn(rd)); - } - - inline void Assembler::FMOVDNE (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0x9, "fmovdne"); } - inline void Assembler::FMOVDL (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0x3, "fmovdl"); } - inline void Assembler::FMOVDLE (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0x2, "fmovdle"); } - inline void Assembler::FMOVDLEU (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0x4, "fmovdleu");} - inline void Assembler::FMOVDG (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0xa, "fmovdg"); } - inline void Assembler::FMOVDGU (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0xc, "fmovdgu"); } - inline void Assembler::FMOVDGE (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0xb, "fmovdfge");} - inline void Assembler::FMOVDCC (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0xd, "fmovdcc"); } - inline void Assembler::FMOVDCS (Register rs, Register rd) { FMOVDcc(rs, 0x4, rd, 0x5, "fmovdcs"); } - inline void Assembler::FMOVDFNE (Register rs, Register rd) { FMOVDcc(rs, 0x0, rd, 0x1, "fmovdfne"); } - inline void Assembler::FMOVDFUG (Register rs, Register rd) { FMOVDcc(rs, 0x0, rd, 0x5, "fmovdfug"); } - inline void Assembler::FMOVDFUGE(Register rs, Register rd) { FMOVDcc(rs, 0x0, rd, 0xc, "fmovdfuge");} - inline void Assembler::FMOVDFUL (Register rs, Register rd) { FMOVDcc(rs, 0x0, rd, 0x3, "fmovdful"); } - inline void Assembler::FMOVDFULE(Register rs, Register rd) { FMOVDcc(rs, 0x0, rd, 0xe, "fmovdfule");} - - inline void Assembler::NOP() { - Format_2(0, 0x4, 0); - asm_output("nop"); - } - - inline void Assembler::RDY(Register rd) { - Format_3_1(2, rd, 0x28, G0, 0, G0); - asm_output("rdy %s", gpn(rd)); - } - - inline void Assembler::RESTORE(Register rs1, Register rs2, Register rd) { - Format_3_1(2, rd, 0x3d, rs1, 0, rs2); - asm_output("restore"); - } - - inline void Assembler::SAVE(Register rs1, Register rs2, Register rd) { - IntegerOperation(rs1, rs2, rd, 0x3c, "save"); - } - inline void Assembler::SAVEI(Register rs1, int32_t simm13, Register rd) { - IntegerOperationI(rs1, simm13, rd, 0x3c, "save"); - } - - inline void Assembler::SETHI(int32_t immI, Register rd) { - Format_2A(rd, 0x4, immI >> 10); - asm_output("sethi 0x%x, %s ! 0x%x", immI >> 10, gpn(rd), immI); - } - - inline void Assembler::SET32(int32_t immI, Register rd) { - if (isIMM13(immI)) { - ORI(G0, immI, rd); - } else { - ORI(rd, immI & 0x3FF, rd); - SETHI(immI, rd); - } - } - - inline void Assembler::ShiftOperation - (Register rs1, Register rs2, Register rd, int32_t op3, const char* opcode) { - Format_3_5(2, rd, op3, rs1, 0, rs2); - asm_output("%s %s, %s, %s", opcode, gpn(rs1), gpn(rs2), gpn(rd)); - } - - inline void Assembler::ShiftOperationI - (Register rs1, int32_t shcnt32, Register rd, int32_t op3, const char* opcode) { - Format_3_6(2, rd, op3, rs1, shcnt32); - asm_output("%s %s, %d, %s", opcode, gpn(rs1), shcnt32, gpn(rd)); - } - - inline void Assembler::SLL(Register rs1, Register rs2, Register rd) { - ShiftOperation(rs1, rs2, rd, 0x25, "sll"); - } - inline void Assembler::SRA(Register rs1, Register rs2, Register rd) { - ShiftOperation(rs1, rs2, rd, 0x27, "sra"); - } - inline void Assembler::SRAI(Register rs1, int32_t shcnt32, Register rd) { - ShiftOperationI(rs1, shcnt32, rd, 0x27, "sra"); - } - inline void Assembler::SRL(Register rs1, Register rs2, Register rd) { - ShiftOperation(rs1, rs2, rd, 0x26, "srl"); - } - - inline void Assembler::Store - (Register rd, Register rs1, Register rs2, int32_t op3, const char* opcode) { - Format_3_1(3, rd, op3, rs1, 0, rs2); - asm_output("%s %s, [%s + %s]", opcode, gpn(rd), gpn(rs1), gpn(rs2)); - } - - inline void Assembler::StoreI - (Register rd, int32_t simm13, Register rs1, int32_t op3, const char* opcode) { - Format_3_1I(3, rd, op3, rs1, simm13); - asm_output("%s %s, [%s + 0x%x]", opcode, gpn(rd), gpn(rs1), simm13); - } - - inline void Assembler::STF(Register rd, Register rs1, Register rs2) { - Store(rd, rs1, rs2, 0x24, "stf"); - } - inline void Assembler::STFI(Register rd, int32_t simm13, Register rs1) { - StoreI(rd, simm13, rs1, 0x24, "stf"); - } - - inline void Assembler::STF32(Register rd, int32_t immI, Register rs1) { - if (isIMM13(immI)) { - STFI(rd, immI, rs1); - } else { - STF(rd, L0, rs1); - SET32(immI, L0); - } - } - - inline void Assembler::STDF32(Register rd, int32_t immI, Register rs1) { - if (isIMM13(immI+4)) { - STFI(rd + 1, immI+4, rs1); - STFI(rd, immI, rs1); - } else { - STF(rd + 1, L0, rs1); - SET32(immI+4, L0); - STF(rd, L0, rs1); - SET32(immI, L0); - } - } - - inline void Assembler::STW(Register rd, Register rs1, Register rs2) { - Store(rd, rs1, rs2, 0x4, "st"); - } - inline void Assembler::STWI(Register rd, int32_t simm13, Register rs1) { - StoreI(rd, simm13, rs1, 0x4, "st"); - } - - inline void Assembler::STW32(Register rd, int32_t immI, Register rs1) { - if (isIMM13(immI)) { - STWI(rd, immI, rs1); - } else { - STW(rd, L0, rs1); - SET32(immI, L0); - } - } - - inline void Assembler::STH(Register rd, Register rs1, Register rs2) { - Store(rd, rs1, rs2, 0x6, "sth"); - } - inline void Assembler::STHI(Register rd, int32_t simm13, Register rs1) { - StoreI(rd, simm13, rs1, 0x6, "sth"); - } - - inline void Assembler::STH32(Register rd, int32_t immI, Register rs1) { - if (isIMM13(immI)) { - STHI(rd, immI, rs1); - } else { - STH(rd, L0, rs1); - SET32(immI, L0); - } - } - - inline void Assembler::STB(Register rd, Register rs1, Register rs2) { - Store(rd, rs1, rs2, 0x5, "stb"); - } - inline void Assembler::STBI(Register rd, int32_t simm13, Register rs1) { - StoreI(rd, simm13, rs1, 0x5, "stb"); - } - - inline void Assembler::STB32(Register rd, int32_t immI, Register rs1) { - if (isIMM13(immI)) { - STBI(rd, immI, rs1); - } else { - STB(rd, L0, rs1); - SET32(immI, L0); - } - } - - // general Assemble - inline void Assembler::JMP_long_nocheck(int32_t t) { - NOP(); - JMPL(G0, G2, G0); - ORI(G2, t & 0x3FF, G2); - SETHI(t, G2); - } - - inline void Assembler::JMP_long(int32_t t) { - underrunProtect(16); - JMP_long_nocheck(t); - } - - inline void Assembler::JMP_long_placeholder() { - JMP_long(0); - } - - inline int32_t Assembler::JCC(void *t) { - underrunProtect(32); - int32_t tt = ((intptr_t)t - (intptr_t)_nIns + 8) >> 2; - if( !(isIMM22(tt)) ) { - NOP(); - JMPL(G0, G2, G0); - SET32((intptr_t)t, G2); - NOP(); - BA(0, 5); - tt = 4; - } - NOP(); - return tt; - } - - void Assembler::JMP(void *t) { - if (!t) { - JMP_long_placeholder(); - } else { - int32_t tt = JCC(t); - BA(0, tt); - } - } - - void Assembler::MR(Register rd, Register rs) { - underrunProtect(4); - ORI(rs, 0, rd); - } - - void Assembler::nInit() { - } - - void Assembler::nBeginAssembly() { - } - - NIns* Assembler::genPrologue() - { - /** - * Prologue - */ - underrunProtect(16); - uint32_t stackNeeded = STACK_GRANULARITY * _activation.stackSlotsNeeded(); - uint32_t frameSize = stackNeeded + kcalleeAreaSize + kLinkageAreaSize; - frameSize = BIT_ROUND_UP(frameSize, 8); - - if (frameSize <= 4096) - SUBI(FP, frameSize, SP); - else { - SUB(FP, G1, SP); - ORI(G1, frameSize & 0x3FF, G1); - SETHI(frameSize, G1); - } - - verbose_only( - if (_logc->lcbits & LC_Native) { - outputf(" 0x%x:",_nIns); - outputf(" patch entry:"); - }) - NIns *patchEntry = _nIns; - - // The frame size in SAVE is faked. We will still re-caculate SP later. - // We can use 0 here but it is not good for debuggers. - SAVEI(SP, -148, SP); - - // align the entry point - asm_align_code(); - - return patchEntry; - } - - void Assembler::asm_align_code() { - while(uintptr_t(_nIns) & 15) { - NOP(); - } - } - - void Assembler::nFragExit(LIns* guard) - { - SideExit* exit = guard->record()->exit; - Fragment *frag = exit->target; - GuardRecord *lr; - if (frag && frag->fragEntry) - { - JMP(frag->fragEntry); - lr = 0; - } - else - { - // Target doesn't exit yet. Emit jump to epilog, and set up to patch later. - if (!_epilogue) - _epilogue = genEpilogue(); - lr = guard->record(); - JMP_long((intptr_t)_epilogue); - lr->jmp = _nIns; - } - - // return value is GuardRecord* - SET32(int(lr), O0); - } - - NIns *Assembler::genEpilogue() - { - underrunProtect(12); - RESTORE(G0, G0, G0); //restore - JMPLI(I7, 8, G0); //ret - ORI(O0, 0, I0); - return _nIns; - } - - void Assembler::asm_call(LIns* ins) - { - if (!ins->isop(LIR_callv)) { - Register retReg = ( ins->isop(LIR_calld) ? F0 : retRegs[0] ); - deprecated_prepResultReg(ins, rmask(retReg)); - } - - // Do this after we've handled the call result, so we don't - // force the call result to be spilled unnecessarily. - evictScratchRegsExcept(0); - - const CallInfo* ci = ins->callInfo(); - - underrunProtect(8); - NOP(); - - ArgType argTypes[MAXARGS]; - uint32_t argc = ci->getArgTypes(argTypes); - - NanoAssert(ins->isop(LIR_callv) || ins->isop(LIR_callp) || - ins->isop(LIR_calld)); - verbose_only(if (_logc->lcbits & LC_Native) - outputf(" 0x%x:", _nIns); - ) - bool indirect = ci->isIndirect(); - if (!indirect) { - CALL(ci); - } - else { - argc--; - Register r = findSpecificRegFor(ins->arg(argc), I0); - JMPL(G0, I0, O7); - } - - Register GPRIndex = O0; - uint32_t offset = kLinkageAreaSize; // start of parameters stack postion. - - for(int i=0; iarg(j), FpRegs); - - underrunProtect(48); - // We might be calling a varargs function. - // So, make sure the GPR's are also loaded with - // the value, or the stack contains it. - if (GPRIndex <= O5) { - LDSW32(SP, offset, GPRIndex); - } - GPRIndex = GPRIndex + 1; - if (GPRIndex <= O5) { - LDSW32(SP, offset+4, GPRIndex); - } - GPRIndex = GPRIndex + 1; - STDF32(r, offset, SP); - offset += 8; - } else { - if (GPRIndex > O5) { - underrunProtect(12); - Register r = findRegFor(ins->arg(j), GpRegs); - STW32(r, offset, SP); - } else { - Register r = findSpecificRegFor(ins->arg(j), GPRIndex); - } - GPRIndex = GPRIndex + 1; - offset += 4; - } - } - } - - Register Assembler::nRegisterAllocFromSet(RegisterMask set) - { - // need to implement faster way - Register i = G0; - while (!(set & rmask(i))) - i = i + 1; - _allocator.free &= ~rmask(i); - return i; - } - - void Assembler::nRegisterResetAll(RegAlloc& a) - { - a.clear(); - a.free = GpRegs | FpRegs; - } - - void Assembler::nPatchBranch(NIns* branch, NIns* location) - { - *(uint32_t*)&branch[0] &= 0xFFC00000; - *(uint32_t*)&branch[0] |= ((intptr_t)location >> 10) & 0x3FFFFF; - *(uint32_t*)&branch[1] &= 0xFFFFFC00; - *(uint32_t*)&branch[1] |= (intptr_t)location & 0x3FF; - } - - RegisterMask Assembler::nHint(LIns* ins) - { - // Never called, because no entries in nHints[] == PREFER_SPECIAL. - NanoAssert(0); - return 0; - } - - bool Assembler::canRemat(LIns* ins) - { - return ins->isImmI() || ins->isop(LIR_allocp); - } - - void Assembler::asm_restore(LIns* i, Register r) - { - underrunProtect(24); - if (i->isop(LIR_allocp)) { - ADD(FP, L2, r); - int32_t d = deprecated_disp(i); - SET32(d, L2); - } - else if (i->isImmI()) { - int v = i->immI(); - SET32(v, r); - } else { - int d = findMemFor(i); - if (rmask(r) & FpRegs) { - LDDF32(FP, d, r); - } else { - LDSW32(FP, d, r); - } - } - } - - void Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base) - { - switch (op) { - case LIR_sti: - case LIR_sti2c: - case LIR_sti2s: - // handled by mainline code below for now - break; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - return; - } - - underrunProtect(20); - if (value->isImmI()) - { - Register rb = getBaseReg(base, dr, GpRegs); - int c = value->immI(); - switch (op) { - case LIR_sti: - STW32(L2, dr, rb); - break; - case LIR_sti2c: - STB32(L2, dr, rb); - break; - case LIR_sti2s: - STH32(L2, dr, rb); - break; - } - SET32(c, L2); - } - else - { - // make sure what is in a register - Register ra, rb; - if (base->isImmI()) { - // absolute address - dr += base->immI(); - ra = findRegFor(value, GpRegs); - rb = G0; - } else { - getBaseReg2(GpRegs, value, ra, GpRegs, base, rb, dr); - } - switch (op) { - case LIR_sti: - STW32(ra, dr, rb); - break; - case LIR_sti2c: - STB32(ra, dr, rb); - break; - case LIR_sti2s: - STH32(ra, dr, rb); - break; - } - } - } - - void Assembler::asm_spill(Register rr, int d, bool quad) - { - underrunProtect(24); - (void)quad; - NanoAssert(d); - if (rmask(rr) & FpRegs) { - STDF32(rr, d, FP); - } else { - STW32(rr, d, FP); - } - } - - void Assembler::asm_load64(LIns* ins) - { - switch (ins->opcode()) { - case LIR_ldd: - case LIR_ldf2d: - // handled by mainline code below for now - break; - default: - NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); - return; - } - - underrunProtect(48); - LIns* base = ins->oprnd1(); - int db = ins->disp(); - Register rb = getBaseReg(base, db, GpRegs); - - if (ins->isInReg()) { - Register rr = ins->getReg(); - asm_maybe_spill(ins, false); - NanoAssert(rmask(rr) & FpRegs); - - if (ins->opcode() == LIR_ldd) { - LDDF32(rb, db, rr); - } else { - FSTOD(F28, rr); - LDF32(rb, db, F28); - } - } else { - NanoAssert(ins->isInAr()); - int dr = arDisp(ins); - - if (ins->opcode() == LIR_ldd) { - // don't use an fpu reg to simply load & store the value. - asm_mmq(FP, dr, rb, db); - } else { - STDF32(F28, dr, FP); - FSTOD(F28, F28); - LDF32(rb, db, F28); - } - } - - freeResourcesOf(ins); - } - - void Assembler::asm_store64(LOpcode op, LIns* value, int dr, LIns* base) - { - switch (op) { - case LIR_std: - case LIR_std2f: - // handled by mainline code below for now - break; - default: - NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); - return; - } - - underrunProtect(48); - Register rb = getBaseReg(base, dr, GpRegs); - if (op == LIR_std2f) { - Register rv = ( !value->isInReg() - ? findRegFor(value, FpRegs) - : value->getReg() ); - NanoAssert(rmask(rv) & FpRegs); - STF32(F28, dr, rb); - FDTOS(rv, F28); - return; - } - - if (value->isImmD()) - { - // if a constant 64-bit value just store it now rather than - // generating a pointless store/load/store sequence - STW32(L2, dr+4, rb); - SET32(value->immDlo(), L2); - STW32(L2, dr, rb); - SET32(value->immDhi(), L2); - return; - } - - if (value->isop(LIR_ldd)) - { - // value is 64bit struct or int64_t, or maybe a double. - // it may be live in an FPU reg. Either way, don't - // put it in an FPU reg just to load & store it. - - // a) if we know it's not a double, this is right. - // b) if we guarded that its a double, this store could be on - // the side exit, copying a non-double. - // c) maybe its a double just being stored. oh well. - - int da = findMemFor(value); - asm_mmq(rb, dr, FP, da); - return; - } - - // if value already in a reg, use that, otherwise - // get it into FPU regs. - Register rv = ( !value->isInReg() - ? findRegFor(value, FpRegs) - : value->getReg() ); - - STDF32(rv, dr, rb); - } - - /** - * copy 64 bits: (rd+dd) <- (rs+ds) - */ - void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) - { - // value is either a 64bit struct or maybe a float - // that isn't live in an FPU reg. Either way, don't - // put it in an FPU reg just to load & store it. - Register t = registerAllocTmp(GpRegs & ~(rmask(rd)|rmask(rs))); - STW32(t, dd+4, rd); - LDSW32(rs, ds+4, t); - STW32(t, dd, rd); - LDSW32(rs, ds, t); - } - - Branches Assembler::asm_branch(bool branchOnFalse, LIns* cond, NIns* targ) - { - NIns* at = 0; - LOpcode condop = cond->opcode(); - NanoAssert(cond->isCmp()); - if (isCmpDOpcode(condop)) - { - return Branches(asm_branchd(branchOnFalse, cond, targ)); - } - - underrunProtect(32); - intptr_t tt = ((intptr_t)targ - (intptr_t)_nIns + 8) >> 2; - // !targ means that it needs patch. - if( !(isIMM22((int32_t)tt)) || !targ ) { - JMP_long_nocheck((intptr_t)targ); - at = _nIns; - NOP(); - BA(0, 5); - tt = 4; - } - NOP(); - - // produce the branch - if (branchOnFalse) - { - if (condop == LIR_eqi) - BNE(0, tt); - else if (condop == LIR_lti) - BGE(0, tt); - else if (condop == LIR_lei) - BG(0, tt); - else if (condop == LIR_gti) - BLE(0, tt); - else if (condop == LIR_gei) - BL(0, tt); - else if (condop == LIR_ltui) - BCC(0, tt); - else if (condop == LIR_leui) - BGU(0, tt); - else if (condop == LIR_gtui) - BLEU(0, tt); - else //if (condop == LIR_geui) - BCS(0, tt); - } - else // op == LIR_xt - { - if (condop == LIR_eqi) - BE(0, tt); - else if (condop == LIR_lti) - BL(0, tt); - else if (condop == LIR_lei) - BLE(0, tt); - else if (condop == LIR_gti) - BG(0, tt); - else if (condop == LIR_gei) - BGE(0, tt); - else if (condop == LIR_ltui) - BCS(0, tt); - else if (condop == LIR_leui) - BLEU(0, tt); - else if (condop == LIR_gtui) - BGU(0, tt); - else //if (condop == LIR_geui) - BCC(0, tt); - } - asm_cmp(cond); - return Branches(at); - } - - NIns* Assembler::asm_branch_ov(LOpcode op, NIns* targ) - { - NIns* at = 0; - underrunProtect(32); - intptr_t tt = ((intptr_t)targ - (intptr_t)_nIns + 8) >> 2; - // !targ means that it needs patch. - if( !(isIMM22((int32_t)tt)) || !targ ) { - JMP_long_nocheck((intptr_t)targ); - at = _nIns; - NOP(); - BA(0, 5); - tt = 4; - } - NOP(); - - if( op == LIR_mulxovi || op == LIR_muljovi ) - BNE(0, tt); - else - BVS(0, tt); - return at; - } - - void Assembler::asm_cmp(LIns *cond) - { - underrunProtect(12); - - LIns* lhs = cond->oprnd1(); - LIns* rhs = cond->oprnd2(); - - NanoAssert(lhs->isI() && rhs->isI()); - - // ready to issue the compare - if (rhs->isImmI()) - { - int c = rhs->immI(); - Register r = findRegFor(lhs, GpRegs); - if (c == 0 && cond->isop(LIR_eqi)) { - ANDCC(r, r, G0); - } - else { - SUBCC(r, L2, G0); - SET32(c, L2); - } - } - else - { - Register ra, rb; - findRegFor2(GpRegs, lhs, ra, GpRegs, rhs, rb); - SUBCC(ra, rb, G0); - } - } - - void Assembler::asm_condd(LIns* ins) - { - // only want certain regs - Register r = deprecated_prepResultReg(ins, AllowableFlagRegs); - underrunProtect(8); - LOpcode condop = ins->opcode(); - NanoAssert(isCmpDOpcode(condop)); - if (condop == LIR_eqd) - MOVFEI(1, r); - else if (condop == LIR_led) - MOVFLEI(1, r); - else if (condop == LIR_ltd) - MOVFLI(1, r); - else if (condop == LIR_ged) - MOVFGEI(1, r); - else // if (condop == LIR_gtd) - MOVFGI(1, r); - ORI(G0, 0, r); - asm_cmpd(ins); - } - - void Assembler::asm_cond(LIns* ins) - { - underrunProtect(8); - // only want certain regs - LOpcode op = ins->opcode(); - Register r = deprecated_prepResultReg(ins, AllowableFlagRegs); - - if (op == LIR_eqi) - MOVEI(1, r); - else if (op == LIR_lti) - MOVLI(1, r); - else if (op == LIR_lei) - MOVLEI(1, r); - else if (op == LIR_gti) - MOVGI(1, r); - else if (op == LIR_gei) - MOVGEI(1, r); - else if (op == LIR_ltui) - MOVCSI(1, r); - else if (op == LIR_leui) - MOVLEUI(1, r); - else if (op == LIR_gtui) - MOVGUI(1, r); - else // if (op == LIR_geui) - MOVCCI(1, r); - ORI(G0, 0, r); - asm_cmp(ins); - } - - void Assembler::asm_arith(LIns* ins) - { - underrunProtect(28); - LOpcode op = ins->opcode(); - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - - Register rb = deprecated_UnknownReg; - RegisterMask allow = GpRegs; - bool forceReg = (op == LIR_muli || op == LIR_mulxovi || op == LIR_muljovi || !rhs->isImmI()); - - if (lhs != rhs && forceReg) - { - if ((rb = asm_binop_rhs_reg(ins)) == deprecated_UnknownReg) { - rb = findRegFor(rhs, allow); - } - allow &= ~rmask(rb); - } - else if ((op == LIR_addi || op == LIR_addxovi || op == LIR_addjovi) && lhs->isop(LIR_allocp) && rhs->isImmI()) { - // add alloc+const, use lea - Register rr = deprecated_prepResultReg(ins, allow); - int d = findMemFor(lhs) + rhs->immI(); - ADD(FP, L2, rr); - SET32(d, L2); - return; - } - - Register rr = deprecated_prepResultReg(ins, allow); - // if this is last use of lhs in reg, we can re-use result reg - // else, lhs already has a register assigned. - Register ra = ( !lhs->isInReg() - ? findSpecificRegFor(lhs, rr) - : lhs->deprecated_getReg() ); - - if (forceReg) - { - if (lhs == rhs) - rb = ra; - - if (op == LIR_addi || op == LIR_addxovi || op == LIR_addjovi) - ADDCC(rr, rb, rr); - else if (op == LIR_subi || op == LIR_subxovi || op == LIR_subjovi) - SUBCC(rr, rb, rr); - else if (op == LIR_muli) - SMULCC(rr, rb, rr); - else if (op == LIR_mulxovi || op == LIR_muljovi) { - SUBCC(L4, L6, L4); - SRAI(rr, 31, L6); - RDY(L4); - SMULCC(rr, rb, rr); - } - else if (op == LIR_andi) - AND(rr, rb, rr); - else if (op == LIR_ori) - OR(rr, rb, rr); - else if (op == LIR_xori) - XOR(rr, rb, rr); - else if (op == LIR_lshi) - SLL(rr, rb, rr); - else if (op == LIR_rshi) - SRA(rr, rb, rr); - else if (op == LIR_rshui) - SRL(rr, rb, rr); - else - NanoAssertMsg(0, "Unsupported"); - } - else - { - int c = rhs->immI(); - if (op == LIR_addi || op == LIR_addxovi || op == LIR_addjovi) - ADDCC(rr, L2, rr); - else if (op == LIR_subi || op == LIR_subxovi || op == LIR_subjovi) - SUBCC(rr, L2, rr); - else if (op == LIR_andi) - AND(rr, L2, rr); - else if (op == LIR_ori) - OR(rr, L2, rr); - else if (op == LIR_xori) - XOR(rr, L2, rr); - else if (op == LIR_lshi) - SLL(rr, L2, rr); - else if (op == LIR_rshi) - SRA(rr, L2, rr); - else if (op == LIR_rshui) - SRL(rr, L2, rr); - else - NanoAssertMsg(0, "Unsupported"); - SET32(c, L2); - } - - if ( rr != ra ) - ORI(ra, 0, rr); - } - - void Assembler::asm_neg_not(LIns* ins) - { - underrunProtect(8); - LOpcode op = ins->opcode(); - Register rr = deprecated_prepResultReg(ins, GpRegs); - - LIns* lhs = ins->oprnd1(); - // if this is last use of lhs in reg, we can re-use result reg - // else, lhs already has a register assigned. - Register ra = ( !lhs->isInReg() - ? findSpecificRegFor(lhs, rr) - : lhs->deprecated_getReg() ); - - if (op == LIR_noti) - ORN(G0, rr, rr); - else - SUB(G0, rr, rr); - - if ( rr != ra ) - ORI(ra, 0, rr); - } - - void Assembler::asm_load32(LIns* ins) - { - underrunProtect(12); - LOpcode op = ins->opcode(); - LIns* base = ins->oprnd1(); - int d = ins->disp(); - Register rr = deprecated_prepResultReg(ins, GpRegs); - Register ra = getBaseReg(base, d, GpRegs); - switch(op) { - case LIR_lduc2ui: - LDUB32(ra, d, rr); - break; - case LIR_ldus2ui: - LDUH32(ra, d, rr); - break; - case LIR_ldi: - LDSW32(ra, d, rr); - break; - case LIR_ldc2i: - LDSB32(ra, d, rr); - break; - case LIR_lds2i: - LDSH32(ra, d, rr); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - return; - } - } - - void Assembler::asm_cmov(LIns* ins) - { - underrunProtect(4); - LOpcode op = ins->opcode(); - LIns* condval = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - - NanoAssert(condval->isCmp()); - NanoAssert(op == LIR_cmovi && iftrue->isI() && iffalse->isI() || - (op == LIR_cmovd && iftrue->isD() && iffalse->isD())); - - RegisterMask rm = (op == LIR_cmovi) ? GpRegs : FpRegs; - const Register rr = deprecated_prepResultReg(ins, rm); - const Register iffalsereg = findRegFor(iffalse, rm & ~rmask(rr)); - bool isIcc = true; - - if (op == LIR_cmovi) { - switch (condval->opcode()) { - // note that these are all opposites... - case LIR_eqi: MOVNE (iffalsereg, rr); break; - case LIR_lti: MOVGE (iffalsereg, rr); break; - case LIR_lei: MOVG (iffalsereg, rr); break; - case LIR_gti: MOVLE (iffalsereg, rr); break; - case LIR_gei: MOVL (iffalsereg, rr); break; - case LIR_ltui: MOVCC (iffalsereg, rr); break; - case LIR_leui: MOVGU (iffalsereg, rr); break; - case LIR_gtui: MOVLEU(iffalsereg, rr); break; - case LIR_geui: MOVCS (iffalsereg, rr); break; - debug_only( default: NanoAssert(0); break; ) - } - } else { - switch (condval->opcode()) { - // note that these are all opposites... - case LIR_eqi: FMOVDNE (iffalsereg, rr); break; - case LIR_lti: FMOVDGE (iffalsereg, rr); break; - case LIR_lei: FMOVDG (iffalsereg, rr); break; - case LIR_gti: FMOVDLE (iffalsereg, rr); break; - case LIR_gei: FMOVDL (iffalsereg, rr); break; - case LIR_ltui: FMOVDCC (iffalsereg, rr); break; - case LIR_leui: FMOVDGU (iffalsereg, rr); break; - case LIR_gtui: FMOVDLEU (iffalsereg, rr); break; - case LIR_geui: FMOVDCS (iffalsereg, rr); break; - case LIR_eqd: FMOVDFNE (iffalsereg, rr); isIcc = false; break; - case LIR_led: FMOVDFUG (iffalsereg, rr); isIcc = false; break; - case LIR_ltd: FMOVDFUGE(iffalsereg, rr); isIcc = false; break; - case LIR_ged: FMOVDFUL (iffalsereg, rr); isIcc = false; break; - case LIR_gtd: FMOVDFULE(iffalsereg, rr); isIcc = false; break; - debug_only( default: NanoAssert(0); break; ) - } - } - - /*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr); - if (isIcc) - asm_cmp(condval); - else - asm_cmpd(condval); - - } - - void Assembler::asm_param(LIns* ins) - { - underrunProtect(12); - uint32_t a = ins->paramArg(); - NanoAssertMsg(ins->paramKind() == 0, "savedRegs are not used on SPARC"); - - if (a < sizeof(argRegs)/sizeof(argRegs[0])) { // i0 - i5 - prepareResultReg(ins, rmask(argRegs[a])); - } else { - // Incoming arg is on stack - Register r = prepareResultReg(ins, GpRegs); - int32_t d = a * sizeof (intptr_t) + kLinkageAreaSize; - LDSW32(FP, d, r); - } - freeResourcesOf(ins); - } - - void Assembler::asm_immi(LIns* ins) - { - underrunProtect(8); - Register rr = deprecated_prepResultReg(ins, GpRegs); - int32_t val = ins->immI(); - if (val == 0) - XOR(rr, rr, rr); - else - SET32(val, rr); - } - - void Assembler::asm_immd(LIns* ins) - { - underrunProtect(64); - Register rr = ins->deprecated_getReg(); - if (rr != deprecated_UnknownReg) - { - // @todo -- add special-cases for 0 and 1 - _allocator.retire(rr); - ins->clearReg(); - NanoAssert((rmask(rr) & FpRegs) != 0); - findMemFor(ins); - int d = deprecated_disp(ins); - LDDF32(FP, d, rr); - } - - // @todo, if we used xor, ldsd, fldz, etc above, we don't need mem here - int d = deprecated_disp(ins); - deprecated_freeRsrcOf(ins); - if (d) - { - STW32(L2, d+4, FP); - SET32(ins->immDlo(), L2); - STW32(L2, d, FP); - SET32(ins->immDhi(), L2); - } - } - - void Assembler::asm_fneg(LIns* ins) - { - underrunProtect(4); - Register rr = deprecated_prepResultReg(ins, FpRegs); - LIns* lhs = ins->oprnd1(); - - // lhs into reg, prefer same reg as result - // if this is last use of lhs in reg, we can re-use result reg - // else, lhs already has a different reg assigned - Register ra = ( !lhs->isInReg() - ? findSpecificRegFor(lhs, rr) - : findRegFor(lhs, FpRegs) ); - - FNEGD(ra, rr); - } - - void Assembler::asm_fop(LIns* ins) - { - underrunProtect(4); - LOpcode op = ins->opcode(); - LIns *lhs = ins->oprnd1(); - LIns *rhs = ins->oprnd2(); - - RegisterMask allow = FpRegs; - Register ra, rb; - findRegFor2(allow, lhs, ra, allow, rhs, rb); - Register rr = deprecated_prepResultReg(ins, allow); - - if (op == LIR_addd) - FADDD(ra, rb, rr); - else if (op == LIR_subd) - FSUBD(ra, rb, rr); - else if (op == LIR_muld) - FMULD(ra, rb, rr); - else //if (op == LIR_divd) - FDIVD(ra, rb, rr); - - } - - void Assembler::asm_i2d(LIns* ins) - { - underrunProtect(32); - // where our result goes - Register rr = deprecated_prepResultReg(ins, FpRegs); - int d = findMemFor(ins->oprnd1()); - FITOD(rr, rr); - LDDF32(FP, d, rr); - } - - void Assembler::asm_ui2d(LIns* ins) - { - underrunProtect(72); - // where our result goes - Register rr = deprecated_prepResultReg(ins, FpRegs); - Register rt = registerAllocTmp(FpRegs & ~(rmask(rr))); - Register gr = findRegFor(ins->oprnd1(), GpRegs); - int disp = -8; - - FABSS(rr, rr); - FSUBD(rt, rr, rr); - LDDF32(SP, disp, rr); - STWI(G0, disp+4, SP); - LDDF32(SP, disp, rt); - STWI(gr, disp+4, SP); - STWI(G1, disp, SP); - SETHI(0x43300000, G1); - } - - void Assembler::asm_d2i(LIns* ins) { - underrunProtect(28); - LIns *lhs = ins->oprnd1(); - Register rr = prepareResultReg(ins, GpRegs); - Register ra = findRegFor(lhs, FpRegs); - int d = findMemFor(ins); - LDSW32(FP, d, rr); - STF32(ra, d, FP); - FDTOI(ra, ra); - freeResourcesOf(ins); - } - - void Assembler::asm_nongp_copy(Register r, Register s) - { - underrunProtect(4); - NanoAssert((rmask(r) & FpRegs) && (rmask(s) & FpRegs)); - FMOVD(s, r); - } - - NIns * Assembler::asm_branchd(bool branchOnFalse, LIns *cond, NIns *targ) - { - NIns *at = 0; - LOpcode condop = cond->opcode(); - NanoAssert(isCmpDOpcode(condop)); - underrunProtect(32); - intptr_t tt = ((intptr_t)targ - (intptr_t)_nIns + 8) >> 2; - // !targ means that it needs patch. - if( !(isIMM22((int32_t)tt)) || !targ ) { - JMP_long_nocheck((intptr_t)targ); - at = _nIns; - NOP(); - BA(0, 5); - tt = 4; - } - NOP(); - - // produce the branch - if (branchOnFalse) - { - if (condop == LIR_eqd) - FBNE(0, tt); - else if (condop == LIR_led) - FBUG(0, tt); - else if (condop == LIR_ltd) - FBUGE(0, tt); - else if (condop == LIR_ged) - FBUL(0, tt); - else //if (condop == LIR_gtd) - FBULE(0, tt); - } - else // op == LIR_xt - { - if (condop == LIR_eqd) - FBE(0, tt); - else if (condop == LIR_led) - FBLE(0, tt); - else if (condop == LIR_ltd) - FBL(0, tt); - else if (condop == LIR_ged) - FBGE(0, tt); - else //if (condop == LIR_gtd) - FBG(0, tt); - } - asm_cmpd(cond); - return at; - } - - void Assembler::asm_cmpd(LIns *cond) - { - underrunProtect(4); - LIns* lhs = cond->oprnd1(); - LIns* rhs = cond->oprnd2(); - - Register rLhs = findRegFor(lhs, FpRegs); - Register rRhs = findRegFor(rhs, FpRegs); - - FCMPD(rLhs, rRhs); - } - - void Assembler::nativePageReset() - { - } - - Register Assembler::asm_binop_rhs_reg(LIns* ins) - { - return deprecated_UnknownReg; - } - - void Assembler::nativePageSetup() - { - NanoAssert(!_inExit); - if (!_nIns) - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - } - - // Increment the 32-bit profiling counter at pCtr, without - // changing any registers. - verbose_only( - void Assembler::asm_inc_m32(uint32_t*) - { - // todo: implement this - } - ) - - void - Assembler::underrunProtect(int n) - { - NIns *eip = _nIns; - // This may be in a normal code chunk or an exit code chunk. - if (eip - n < codeStart) { - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - JMP_long_nocheck((intptr_t)eip); - } - } - - void Assembler::asm_ret(LIns* ins) - { - genEpilogue(); - releaseRegisters(); - assignSavedRegs(); - LIns *val = ins->oprnd1(); - if (ins->isop(LIR_reti)) { - findSpecificRegFor(val, retRegs[0]); - } else { - NanoAssert(ins->isop(LIR_retd)); - findSpecificRegFor(val, F0); - } - } - - void Assembler::swapCodeChunks() { - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - void Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported - } - -#endif /* FEATURE_NANOJIT */ -} diff --git a/deps/mozjs/js/src/nanojit/NativeSparc.h b/deps/mozjs/js/src/nanojit/NativeSparc.h deleted file mode 100644 index 2cf7f277fd9..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeSparc.h +++ /dev/null @@ -1,455 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * leon.sha@oracle.com - * ginn.chen@oracle.com - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_NativeSparc__ -#define __nanojit_NativeSparc__ - -#include "NativeCommon.h" - -#define count_instr() -#define count_ret() -#define count_push() -#define count_pop() -#define count_st() -#define count_stq() -#define count_ld() -#define count_ldq() -#define count_call() -#define count_calli() -#define count_prolog() -#define count_alu() -#define count_mov() -#define count_fpu() -#define count_jmp() -#define count_jcc() -#define count_fpuld() -#define count_aluld() -#define count_alust() -#define count_pushld() -#define count_imt() - -namespace nanojit -{ - const int NJ_MAX_REGISTERS = 30; // L0 - L7, I0 - I5, F2 - F14 - - const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect - -#define NJ_MAX_STACK_ENTRY 8192 -#define NJ_MAX_PARAMETERS 1 - -#define NJ_JTBL_SUPPORTED 0 -#define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 -#define NJ_F2I_SUPPORTED 1 -#define NJ_SOFTFLOAT_SUPPORTED 0 -#define NJ_DIVI_SUPPORTED 0 - - const int NJ_ALIGN_STACK = 16; - - typedef uint32_t NIns; - - // Bytes of icache to flush after Assembler::patch - const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); - - // These are used as register numbers in various parts of the code - static const Register - G0 = { 0 }, - G1 = { 1 }, - G2 = { 2 }, - G3 = { 3 }, - G4 = { 4 }, - G5 = { 5 }, // Reserved for system - G6 = { 6 }, // Reserved for system - G7 = { 7 }, // Reserved for system - - O0 = { 8 }, - O1 = { 9 }, - O2 = { 10 }, - O3 = { 11 }, - O4 = { 12 }, - O5 = { 13 }, - O6 = { 14 }, // SP - O7 = { 15 }, // Not used. - - L0 = { 16 }, - L1 = { 17 }, - L2 = { 18 }, - L3 = { 19 }, - L4 = { 20 }, - L5 = { 21 }, - L6 = { 22 }, - L7 = { 23 }, - - I0 = { 24 }, - I1 = { 25 }, - I2 = { 26 }, - I3 = { 27 }, - I4 = { 28 }, - I5 = { 29 }, - I6 = { 30 }, // FP - I7 = { 31 }, // Not used - - SP = O6, - FP = I6, - - F0 = { 32 }, - F1 = { 33 }, - F2 = { 34 }, - F3 = { 35 }, - F4 = { 36 }, - F5 = { 37 }, - F6 = { 38 }, - F7 = { 39 }, - F8 = { 40 }, - F9 = { 41 }, - F10 = { 42 }, - F11 = { 43 }, - F12 = { 44 }, - F13 = { 45 }, - F14 = { 46 }, - F15 = { 47 }, - F16 = { 48 }, - F17 = { 49 }, - F18 = { 50 }, - F19 = { 51 }, - F20 = { 52 }, - F21 = { 53 }, - F22 = { 54 }, - F23 = { 55 }, - F24 = { 56 }, - F25 = { 57 }, - F26 = { 58 }, - F27 = { 59 }, - F28 = { 60 }, - F29 = { 61 }, - F30 = { 62 }, - F31 = { 63 }, - - // helpers - FRAME_PTR = G4, - - deprecated_UnknownReg = { 64 }; // XXX: remove eventually, see bug 538924 - - static const uint32_t FirstRegNum = 0; - static const uint32_t LastRegNum = 63; -} - -namespace nanojit -{ - // We only use 32 registers to be managed. - // So we choose some of them. - // And other unmanaged registers we can use them directly. - // The registers that can be used directly are G0-G4, L0, L2, L4, L6 - // SP, FP, F8-F12, F24-F28 - typedef uint64_t RegisterMask; -#define _rmask_(r) ((RegisterMask)1<<(REGNUM(r))) -#define _reg_(r) (REGNUM(r) & 0x1F) - // Assembler::savedRegs[] is not needed for sparc because the - // registers are already saved automatically by "save" instruction. - static const int NumSavedRegs = 0; - - static const RegisterMask SavedRegs = _rmask_(L1) | _rmask_(L3) | _rmask_(L5) | _rmask_(L7) | - _rmask_(I0) | _rmask_(I1) | _rmask_(I2) | _rmask_(I3) | - _rmask_(I4) | _rmask_(I5); - static const RegisterMask GpRegs = SavedRegs | _rmask_(O0) | _rmask_(O1) | _rmask_(O2) | - _rmask_(O3) | _rmask_(O4) | _rmask_(O5); - static const RegisterMask FpRegs = _rmask_(F0) | _rmask_(F2) | _rmask_(F4) | - _rmask_(F6) | _rmask_(F14) | _rmask_(F16) | - _rmask_(F18) | _rmask_(F20) | _rmask_(F22); - static const RegisterMask AllowableFlagRegs = GpRegs; - - verbose_only( extern const char* regNames[]; ) - -#define DECLARE_PLATFORM_STATS() - -#define DECLARE_PLATFORM_REGALLOC() - -#define DECLARE_PLATFORM_ASSEMBLER() \ - const static Register argRegs[6], retRegs[1]; \ - void nativePageReset(); \ - void nativePageSetup(); \ - void underrunProtect(int bytes); \ - bool hardenNopInsertion(const Config& /*c*/) { return false; } \ - void asm_align_code(); \ - void asm_cmp(LIns *cond); \ - void asm_cmpd(LIns *cond); \ - NIns* asm_branchd(bool, LIns*, NIns*); \ - void IMM32(int32_t i) { \ - --_nIns; \ - *((int32_t*)_nIns) = i; \ - } \ - void CALL(const CallInfo* ci); \ - void Format_2(int32_t x, int32_t op2, int32_t imm22) { \ - int32_t i = x << 25 | op2 << 22 | imm22 & 0x3FFFFF; \ - IMM32(i); \ - } \ - void Format_2A(Register rd, int32_t op2, int32_t imm22) { \ - Format_2(_reg_(rd), op2, imm22); \ - } \ - void Format_2_2(int32_t a, int32_t cond, int32_t op2, int32_t disp22) { \ - Format_2((a & 0x1) << 4 | cond & 0xF, op2, disp22); \ - } \ - void Format_2_3(int32_t a, int32_t cond, int32_t op2, int32_t cc1, int32_t cc0, int32_t p, int32_t disp19) { \ - Format_2_2(a, cond, op2, (cc1 & 0x1) << 21 | (cc0 & 0x1) << 20 | (p & 0x1) << 19 | disp19 & 0x7FFFF); \ - } \ - void Format_2_4(int32_t a, int32_t rcond, int32_t op2, int32_t d16hi, int32_t p, int32_t rs1, int32_t d16lo) { \ - Format_2_2(a, rcond & 0x7, op2, (d16hi & 0x3) << 20 | (p & 0x1) << 19 | rs1 << 14 | d16lo & 0x3FFF); \ - } \ - void Format_3(int32_t op1, int32_t x, int32_t op3, int32_t bits19) { \ - int32_t i = op1 << 30 | x << 25 | op3 << 19 | bits19 & 0x7FFFF; \ - IMM32(i); \ - } \ - void Format_3A(int32_t op1, Register rd, int32_t op3, int32_t bits19) { \ - Format_3(op1, _reg_(rd), op3, bits19); \ - } \ - void Format_3_1(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t bit8, Register rs2) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | (bit8 & 0xFF) << 5 | _reg_(rs2)); \ - } \ - void Format_3_1I(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t simm13) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | 1 << 13 | simm13 & 0x1FFF); \ - } \ - void Format_3_2(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t rcond, Register rs2) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | (rcond & 0x3) << 10 | _reg_(rs2)); \ - } \ - void Format_3_2I(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t rcond, int32_t simm10) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | 1 << 13 | (rcond & 0x3) << 10 | simm10 & 0x1FFF); \ - } \ - void Format_3_3(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t cmask, int32_t mmask) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | 1 << 13 | (cmask & 0x7) << 5 | mmask & 0xF); \ - } \ - void Format_3_4(int32_t op1, Register rd, int32_t op3, int32_t bits19) { \ - Format_3A(op1, rd, op3, bits19); \ - } \ - void Format_3_5(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t x, Register rs2) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | (x & 0x1) << 12 | _reg_(rs2)); \ - } \ - void Format_3_6(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t shcnt32) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | 1 << 13 | (shcnt32 & 0x3F)); \ - } \ - void Format_3_7(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t shcnt64) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | 1 << 13 | 1 << 12 | (shcnt64 & 0x7F)); \ - } \ - void Format_3_8(int32_t op1, Register rd, int32_t op3, Register rs1, int32_t bits9, Register rs2) { \ - Format_3A(op1, rd, op3, _reg_(rs1) << 14 | (bits9 & 0x1FF) << 5 | _reg_(rs2)); \ - } \ - void Format_3_9(int32_t op1, int32_t cc1, int32_t cc0, int32_t op3, Register rs1, int32_t bits9, Register rs2) { \ - Format_3(op1, (cc1 & 0x1) << 1 | (cc0 & 0x1), op3, _reg_(rs1) << 14 | (bits9 & 0x1FF) << 5 | _reg_(rs2)); \ - } \ - void Format_4_1(Register rd, int32_t op3, Register rs1, int32_t cc1, int32_t cc0, Register rs2) { \ - Format_3A(2, rd, op3, _reg_(rs1) << 14 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | _reg_(rs2)); \ - } \ - void Format_4_1I(Register rd, int32_t op3, Register rs1, int32_t cc1, int32_t cc0, int32_t simm11) { \ - Format_3A(2, rd, op3, _reg_(rs1) << 14 | (cc1 & 0x1) << 12 | 1 << 13 |(cc0 & 0x1) << 11 | simm11 & 0x7FF); \ - } \ - void Format_4_2(Register rd, int32_t op3, int32_t cc2, int32_t cond, int32_t cc1, int32_t cc0, Register rs2) { \ - Format_3A(2, rd, op3, (cc2 & 0x1) << 18 | (cond & 0xF) << 14 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | _reg_(rs2)); \ - } \ - void Format_4_2I(Register rd, int32_t op3, int32_t cc2, int32_t cond, int32_t cc1, int32_t cc0, int32_t simm11) { \ - Format_3A(2, rd, op3, (cc2 & 0x1) << 18 | (cond & 0xF) << 14 | 1 << 13 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | (simm11 & 0x7FF)); \ - } \ - void Format_4_3(Register rd, int32_t op3, Register rs1, int32_t cc1, int32_t cc0, int32_t swap_trap) { \ - Format_3A(2, rd, op3, _reg_(rs1) << 14 | 1 << 13 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | swap_trap & 0x7F); \ - } \ - void Format_4_4(Register rd, int32_t op3, Register rs1, int32_t rcond, int32_t opf_low, Register rs2) { \ - Format_3A(2, rd, op3, _reg_(rs1) << 14 | (rcond & 0x7) << 10 | (opf_low & 0x1F) << 5 | _reg_(rs2)); \ - } \ - void Format_4_5(Register rd, int32_t op3, int32_t cond, int32_t opf_cc, int32_t opf_low, Register rs2) { \ - Format_3A(2, rd, op3, (cond & 0xF) << 14 | (opf_cc & 0x7) << 11 | (opf_low & 0x3F) << 5 | _reg_(rs2)); \ - } \ - void IntegerOperation(Register rs1, Register rs2, Register rd, int32_t op3, const char *opcode); \ - void IntegerOperationI(Register rs1, int32_t simm13, Register rd, int32_t op3, const char *opcode); \ - void FloatOperation(Register rs1, Register rs2, Register rd, int32_t op3, const char *opcode); \ - void Bicc(int32_t a, int32_t dsp22, int32_t cond, const char *opcode); \ - void FBfcc(int32_t a, int32_t dsp22, int32_t cond, const char *opcode); \ - void LoadOperation(Register rs1, Register rs2, Register rd, int32_t op3, const char* opcode); \ - void LoadOperationI(Register rs1, int32_t simm13, Register rd, int32_t op3, const char* opcode); \ - void MOVcc(Register rs, int32_t cc2, int32_t cc1, int32_t cc0, Register rd, int32_t cond, const char *opcode); \ - void MOVccI(int32_t simm11, int32_t cc2, int32_t cc1, int32_t cc0, Register rd, int32_t cond, const char *opcode); \ - void FMOVDcc(Register rs, int32_t opt_cc, Register rd, int32_t cond, const char *opcode); \ - void ShiftOperation(Register rs1, Register rs2, Register rd, int32_t op3, const char* opcode); \ - void ShiftOperationI(Register rs1, int32_t shcnt32, Register rd, int32_t op3, const char* opcode); \ - void Store(Register rd, Register rs1, Register rs2, int32_t op3, const char* opcode); \ - void StoreI(Register rd, int32_t simm13, Register rs1, int32_t op3, const char* opcode); \ - void ADD(Register rs1, Register rs2, Register rd); \ - void ADDCC(Register rs1, Register rs2, Register rd); \ - void AND(Register rs1, Register rs2, Register rd); \ - void ANDCC(Register rs1, Register rs2, Register rd); \ - void OR(Register rs1, Register rs2, Register rd); \ - void ORI(Register rs1, int32_t simm13, Register rd); \ - void ORN(Register rs1, Register rs2, Register rd); \ - void SMULCC(Register rs1, Register rs2, Register rd); \ - void SUB(Register rs1, Register rs2, Register rd); \ - void SUBCC(Register rs1, Register rs2, Register rd); \ - void SUBI(Register rs1, int32_t simm13, Register rd); \ - void XOR(Register rs1, Register rs2, Register rd); \ - void BA(int32_t a, int32_t dsp22); \ - void BE(int32_t a, int32_t dsp22); \ - void BNE(int32_t a, int32_t dsp22); \ - void BG(int32_t a, int32_t dsp22); \ - void BGU(int32_t a, int32_t dsp22); \ - void BGE(int32_t a, int32_t dsp22); \ - void BL(int32_t a, int32_t dsp22); \ - void BLE(int32_t a, int32_t dsp22); \ - void BLEU(int32_t a, int32_t dsp22); \ - void BCC(int32_t a, int32_t dsp22); \ - void BCS(int32_t a, int32_t dsp22); \ - void BVC(int32_t a, int32_t dsp22); \ - void BVS(int32_t a, int32_t dsp22); \ - void FABSS(Register rs2, Register rd); \ - void FADDD(Register rs1, Register rs2, Register rd); \ - void FBE(int32_t a, int32_t dsp22); \ - void FBNE(int32_t a, int32_t dsp22); \ - void FBUE(int32_t a, int32_t dsp22); \ - void FBG(int32_t a, int32_t dsp22); \ - void FBUG(int32_t a, int32_t dsp22); \ - void FBGE(int32_t a, int32_t dsp22); \ - void FBUGE(int32_t a, int32_t dsp22); \ - void FBL(int32_t a, int32_t dsp22); \ - void FBUL(int32_t a, int32_t dsp22); \ - void FBLE(int32_t a, int32_t dsp22); \ - void FBULE(int32_t a, int32_t dsp22); \ - void FCMPD(Register rs1, Register rs2); \ - void FSUBD(Register rs1, Register rs2, Register rd); \ - void FMULD(Register rs1, Register rs2, Register rd); \ - void FDTOI(Register rs2, Register rd); \ - void FDIVD(Register rs1, Register rs2, Register rd); \ - void FMOVD(Register rs2, Register rd); \ - void FNEGD(Register rs2, Register rd); \ - void FITOD(Register rs2, Register rd); \ - void FDTOS(Register rs2, Register rd); \ - void FSTOD(Register rs2, Register rd); \ - void JMPL(Register rs1, Register rs2, Register rd); \ - void JMPLI(Register rs1, int32_t simm13, Register rd); \ - void LDF(Register rs1, Register rs2, Register rd); \ - void LDFI(Register rs1, int32_t simm13, Register rd); \ - void LDF32(Register rs1, int32_t immI, Register rd); \ - void LDDF32(Register rs1, int32_t immI, Register rd); \ - void LDUB(Register rs1, Register rs2, Register rd); \ - void LDUBI(Register rs1, int32_t simm13, Register rd); \ - void LDUB32(Register rs1, int32_t immI, Register rd); \ - void LDSB(Register rs1, Register rs2, Register rd); \ - void LDSBI(Register rs1, int32_t simm13, Register rd); \ - void LDSB32(Register rs1, int32_t immI, Register rd); \ - void LDUH(Register rs1, Register rs2, Register rd); \ - void LDUHI(Register rs1, int32_t simm13, Register rd); \ - void LDUH32(Register rs1, int32_t immI, Register rd); \ - void LDSH(Register rs1, Register rs2, Register rd); \ - void LDSHI(Register rs1, int32_t simm13, Register rd); \ - void LDSH32(Register rs1, int32_t immI, Register rd); \ - void LDSW(Register rs1, Register rs2, Register rd); \ - void LDSWI(Register rs1, int32_t simm13, Register rd); \ - void LDSW32(Register rs1, int32_t immI, Register rd); \ - void MOVE(Register rs, Register rd); \ - void MOVNE(Register rs, Register rd); \ - void MOVL(Register rs, Register rd); \ - void MOVLE(Register rs, Register rd); \ - void MOVG(Register rs, Register rd); \ - void MOVGE(Register rs, Register rd); \ - void MOVLEU(Register rs, Register rd); \ - void MOVGU(Register rs, Register rd); \ - void MOVCC(Register rs, Register rd); \ - void MOVCS(Register rs, Register rd); \ - void MOVVC(Register rs, Register rd); \ - void MOVEI(int32_t simm11, Register rd); \ - void MOVNEI(int32_t simm11, Register rd); \ - void MOVLI(int32_t simm11, Register rd); \ - void MOVLEI(int32_t simm11, Register rd); \ - void MOVGI(int32_t simm11, Register rd); \ - void MOVGEI(int32_t simm11, Register rd); \ - void MOVLEUI(int32_t simm11, Register rd); \ - void MOVGUI(int32_t simm11, Register rd); \ - void MOVCCI(int32_t simm11, Register rd); \ - void MOVCSI(int32_t simm11, Register rd); \ - void MOVVSI(int32_t simm11, Register rd); \ - void MOVFEI(int32_t simm11, Register rd); \ - void MOVFLI(int32_t simm11, Register rd); \ - void MOVFLEI(int32_t simm11, Register rd); \ - void MOVFGI(int32_t simm11, Register rd); \ - void MOVFGEI(int32_t simm11, Register rd); \ - void FMOVDNE(Register rs, Register rd); \ - void FMOVDL(Register rs, Register rd); \ - void FMOVDLE(Register rs, Register rd); \ - void FMOVDLEU(Register rs, Register rd); \ - void FMOVDG(Register rs, Register rd); \ - void FMOVDGU(Register rs, Register rd); \ - void FMOVDGE(Register rs, Register rd); \ - void FMOVDCC(Register rs, Register rd); \ - void FMOVDCS(Register rs, Register rd); \ - void FMOVDFNE(Register rs, Register rd); \ - void FMOVDFUG(Register rs, Register rd); \ - void FMOVDFUGE(Register rs, Register rd); \ - void FMOVDFUL(Register rs, Register rd); \ - void FMOVDFULE(Register rs, Register rd); \ - void NOP(); \ - void RDY(Register rd); \ - void RESTORE(Register rs1, Register rs2, Register rd); \ - void SAVE(Register rs1, Register rs2, Register rd); \ - void SAVEI(Register rs1, int32_t simm13, Register rd); \ - void SETHI(int32_t immI, Register rd); \ - void SET32(int32_t immI, Register rd); \ - void SLL(Register rs1, Register rs2, Register rd); \ - void SRA(Register rs1, Register rs2, Register rd); \ - void SRAI(Register rs1, int32_t shcnt32, Register rd); \ - void SRL(Register rs1, Register rs2, Register rd); \ - void STF(Register rd, Register rs1, Register rs2); \ - void STFI(Register rd, int32_t simm13, Register rs1); \ - void STF32(Register rd, int32_t immI, Register rs1); \ - void STDF32(Register rd, int32_t immI, Register rs1); \ - void STW(Register rd, Register rs1, Register rs2); \ - void STWI(Register rd, int32_t simm13, Register rs1); \ - void STW32(Register rd, int32_t immI, Register rs1); \ - void STH(Register rd, Register rs1, Register rs2); \ - void STHI(Register rd, int32_t simm13, Register rs1); \ - void STH32(Register rd, int32_t immI, Register rs1); \ - void STB(Register rd, Register rs1, Register rs2); \ - void STBI(Register rd, int32_t simm13, Register rs1); \ - void STB32(Register rd, int32_t immI, Register rs1); \ - bool isIMM13(int32_t imm) { return (imm) <= 0xfff && (imm) >= -0x1000; } \ - bool isIMM19(int32_t imm) { return (imm) <= 0x3ffff && (imm) >= -0x40000; } \ - bool isIMM22(int32_t imm) { return (imm) <= 0x1fffff && (imm) >= -0x200000; } \ - void JMP_long_nocheck(int32_t t); \ - void JMP_long(int32_t t); \ - void JMP_long_placeholder(); \ - int32_t JCC(void *t); \ - void JMP(void *t); \ - void MR(Register rd, Register rs); -} -#endif // __nanojit_NativeSparc__ diff --git a/deps/mozjs/js/src/nanojit/NativeX64.cpp b/deps/mozjs/js/src/nanojit/NativeX64.cpp deleted file mode 100644 index 343e67bd5f4..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeX64.cpp +++ /dev/null @@ -1,2219 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -// uncomment this to enable _vprof/_nvprof macros -//#define DOPROF -#include "../vprof/vprof.h" - -#if defined FEATURE_NANOJIT && defined NANOJIT_X64 - -/* -completion -- 64bit branch offsets -- finish cmov/qcmov with other conditions -- validate asm_cond with other conditions - -better code -- put R12 back in play as a base register -- no-disp addr modes (except RBP/R13) -- disp64 branch/call -- spill gp values to xmm registers? -- prefer xmm registers for copies since gprs are in higher demand? -- stack arg doubles -- stack based LIR_paramp - -tracing -- nFragExit - -*/ - -namespace nanojit -{ - const Register Assembler::retRegs[] = { RAX }; -#ifdef _WIN64 - const Register Assembler::argRegs[] = { RCX, RDX, R8, R9 }; - const static int maxArgRegs = 4; - const Register Assembler::savedRegs[] = { RBX, RSI, RDI, R12, R13, R14, R15 }; -#else - const Register Assembler::argRegs[] = { RDI, RSI, RDX, RCX, R8, R9 }; - const static int maxArgRegs = 6; - const Register Assembler::savedRegs[] = { RBX, R12, R13, R14, R15 }; -#endif - - const char *regNames[] = { - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" - }; - - const char *gpRegNames32[] = { - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", - "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" - }; - - const char *gpRegNames8[] = { - "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", - "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l" - }; - - const char *gpRegNames8hi[] = { - "ah", "ch", "dh", "bh" - }; - - const char *gpRegNames16[] = { - "ax", "cx", "dx", "bx", "spx", "bpx", "six", "dix", - "r8x", "r9x", "r10x", "r11x", "r12x", "r13x", "r14x", "r15x" - }; - -#ifdef _DEBUG - #define TODO(x) todo(#x) - static void todo(const char *s) { - verbose_only( avmplus::AvmLog("%s",s); ) - NanoAssertMsgf(false, "%s", s); - } -#else - #define TODO(x) -#endif - - // MODRM and SIB restrictions: - // memory access modes != 11 require SIB if base&7 == 4 (RSP or R12) - // mode 00 with base == x101 means RIP+disp32 (RBP or R13), use mode 01 disp8=0 instead - // mode 01 or 11 with base = x101 means disp32 + EBP or R13, not RIP relative - // base == x100 means SIB byte is present, so using ESP|R12 as base requires SIB - // rex prefix required to use RSP-R15 as 8bit registers in mod/rm8 modes. - - // take R12 out of play as a base register because using ESP or R12 as base requires the SIB byte - const RegisterMask BaseRegs = GpRegs & ~rmask(R12); - - static inline int oplen(uint64_t op) { - return op & 255; - } - - // encode 2-register rex prefix. dropped if none of its bits are set. - static inline uint64_t rexrb(uint64_t op, Register r, Register b) { - int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); - return rex != 0x40 ? op | rex << shift : op - 1; - } - - // encode 3-register rex prefix. dropped if none of its bits are set. - static inline uint64_t rexrxb(uint64_t op, Register r, Register x, Register b) { - int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(x)&8)>>2) | ((REGNUM(b)&8)>>3); - return rex != 0x40 ? op | rex << shift : op - 1; - } - - // encode 2-register rex prefix. dropped if none of its bits are set, but - // keep REX if b >= rsp, to allow uniform use of all 16 8bit registers - static inline uint64_t rexrb8(uint64_t op, Register r, Register b) { - int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); - return ((rex | (REGNUM(b) & ~3)) != 0x40) ? (op | (rex << shift)) : op - 1; - } - - // encode 2-register rex prefix that follows a manditory prefix (66,F2,F3) - // [prefix][rex][opcode] - static inline uint64_t rexprb(uint64_t op, Register r, Register b) { - int shift = 64 - 8*oplen(op) + 8; - uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); - // to drop rex, we replace rex with manditory prefix, and decrement length - return rex != 0x40 ? op | rex << shift : - ((op & ~(255LL<>(shift-8)&255) << shift) - 1; - } - - // [rex][opcode][mod-rr] - static inline uint64_t mod_rr(uint64_t op, Register r, Register b) { - return op | uint64_t((REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<56; - } - - // [rex][opcode][modrm=r][sib=xb] - static inline uint64_t mod_rxb(uint64_t op, Register r, Register x, Register b) { - return op | /*modrm*/uint64_t((REGNUM(r)&7)<<3)<<48 | /*sib*/uint64_t((REGNUM(x)&7)<<3|(REGNUM(b)&7))<<56; - } - - static inline uint64_t mod_disp32(uint64_t op, Register r, Register b, int32_t d) { - NanoAssert(IsGpReg(r) && IsGpReg(b)); - NanoAssert((REGNUM(b) & 7) != 4); // using RSP or R12 as base requires SIB - uint64_t mod = (((op>>24)&255)>>6); // mod bits in addressing mode: 0,1,2, or 3 - if (mod == 2 && isS8(d)) { - // op is: 0x[disp32=0][mod=2:r:b][op][rex][len] - int len = oplen(op); - op = (op & ~0xff000000LL) | (0x40 | (REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<24; // replace mod - return op<<24 | int64_t(d)<<56 | (len-3); // shrink disp, add disp8 - } else { - // op is: 0x[disp32][mod][op][rex][len] - return op | int64_t(d)<<32 | uint64_t((REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<24; - } - } - - // All the emit() functions should only be called from within codegen - // functions PUSHR(), SHR(), etc. - - void Assembler::emit(uint64_t op) { - int len = oplen(op); - // we will only move nIns by -len bytes, but we write 8 - // bytes. so need to protect 8 so we dont stomp the page - // header or the end of the preceding page (might segf) - underrunProtect(8); - ((int64_t*)_nIns)[-1] = op; - _nIns -= len; // move pointer by length encoded in opcode - _nvprof("x64-bytes", len); - } - - void Assembler::emit8(uint64_t op, int64_t v) { - NanoAssert(isS8(v)); - emit(op | uint64_t(v)<<56); - } - - void Assembler::emit_target8(size_t underrun, uint64_t op, NIns* target) { - underrunProtect(underrun); // must do this before calculating offset - // Nb: see emit_target32() for why we use _nIns here. - int64_t offset = target - _nIns; - NanoAssert(isS8(offset)); - emit(op | uint64_t(offset)<<56); - } - - void Assembler::emit_target32(size_t underrun, uint64_t op, NIns* target) { - underrunProtect(underrun); // must do this before calculating offset - // Nb: at this point in time, _nIns points to the most recently - // written instruction, ie. the jump's successor. So why do we use it - // to compute the offset, rather than the jump's address? Because in - // x86/x64-64 the offset in a relative jump is not from the jmp itself - // but from the following instruction. Eg. 'jmp $0' will jump to the - // next instruction. - int64_t offset = target ? target - _nIns : 0; - if (!isS32(offset)) - setError(BranchTooFar); - emit(op | uint64_t(uint32_t(offset))<<32); - } - - void Assembler::emit_target64(size_t underrun, uint64_t op, NIns* target) { - NanoAssert(underrun >= 16); - underrunProtect(underrun); // must do this before calculating offset - // Nb: at this point in time, _nIns points to the most recently - // written instruction, ie. the jump's successor. - ((uint64_t*)_nIns)[-1] = (uint64_t) target; - _nIns -= 8; - emit(op); - } - - // 3-register modrm32+sib form - void Assembler::emitrxb(uint64_t op, Register r, Register x, Register b) { - emit(rexrxb(mod_rxb(op, r, x, b), r, x, b)); - } - - // 2-register modrm32 form - void Assembler::emitrr(uint64_t op, Register r, Register b) { - emit(rexrb(mod_rr(op, r, b), r, b)); - } - - // 2-register modrm8 form (8 bit operand size) - void Assembler::emitrr8(uint64_t op, Register r, Register b) { - emit(rexrb8(mod_rr(op, r, b), r, b)); - } - - // same as emitrr, but with a prefix byte - void Assembler::emitprr(uint64_t op, Register r, Register b) { - emit(rexprb(mod_rr(op, r, b), r, b)); - } - - // disp32 modrm8 form, when the disp fits in the instruction (opcode is 1-3 bytes) - void Assembler::emitrm8(uint64_t op, Register r, int32_t d, Register b) { - emit(rexrb8(mod_disp32(op, r, b, d), r, b)); - } - - // disp32 modrm form, when the disp fits in the instruction (opcode is 1-3 bytes) - void Assembler::emitrm(uint64_t op, Register r, int32_t d, Register b) { - emit(rexrb(mod_disp32(op, r, b, d), r, b)); - } - - // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) - uint64_t Assembler::emit_disp32(uint64_t op, int32_t d) { - if (isS8(d)) { - NanoAssert(((op>>56)&0xC0) == 0x80); // make sure mod bits == 2 == disp32 mode - underrunProtect(1+8); - *(--_nIns) = (NIns) d; - _nvprof("x64-bytes", 1); - op ^= 0xC000000000000000LL; // change mod bits to 1 == disp8 mode - } else { - underrunProtect(4+8); // room for displ plus fullsize op - *((int32_t*)(_nIns -= 4)) = d; - _nvprof("x64-bytes", 4); - } - return op; - } - - // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) - void Assembler::emitrm_wide(uint64_t op, Register r, int32_t d, Register b) { - op = emit_disp32(op, d); - emitrr(op, r, b); - } - - // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) - // p = prefix -- opcode must have a 66, F2, or F3 prefix - void Assembler::emitprm(uint64_t op, Register r, int32_t d, Register b) { - op = emit_disp32(op, d); - emitprr(op, r, b); - } - - // disp32 modrm form with 32-bit immediate value - void Assembler::emitrm_imm32(uint64_t op, Register b, int32_t d, int32_t imm) { - NanoAssert(IsGpReg(b)); - NanoAssert((REGNUM(b) & 7) != 4); // using RSP or R12 as base requires SIB - underrunProtect(4+4+8); // room for imm plus disp plus fullsize op - *((int32_t*)(_nIns -= 4)) = imm; - _nvprof("x86-bytes", 4); - emitrm_wide(op, RZero, d, b); - } - - // disp32 modrm form with 16-bit immediate value - // p = prefix -- opcode must have a 66, F2, or F3 prefix - void Assembler::emitprm_imm16(uint64_t op, Register b, int32_t d, int32_t imm) { - NanoAssert(IsGpReg(b)); - NanoAssert((REGNUM(b) & 7) != 4); // using RSP or R12 as base requires SIB - underrunProtect(2+4+8); // room for imm plus disp plus fullsize op - *((int16_t*)(_nIns -= 2)) = (int16_t) imm; - _nvprof("x86-bytes", 2); - emitprm(op, RZero, d, b); - } - - // disp32 modrm form with 8-bit immediate value - void Assembler::emitrm_imm8(uint64_t op, Register b, int32_t d, int32_t imm) { - NanoAssert(IsGpReg(b)); - NanoAssert((REGNUM(b) & 7) != 4); // using RSP or R12 as base requires SIB - underrunProtect(1+4+8); // room for imm plus disp plus fullsize op - *((int8_t*)(_nIns -= 1)) = (int8_t) imm; - _nvprof("x86-bytes", 1); - emitrm_wide(op, RZero, d, b); - } - - void Assembler::emitrr_imm(uint64_t op, Register r, Register b, int32_t imm) { - NanoAssert(IsGpReg(r) && IsGpReg(b)); - underrunProtect(4+8); // room for imm plus fullsize op - *((int32_t*)(_nIns -= 4)) = imm; - _nvprof("x86-bytes", 4); - emitrr(op, r, b); - } - - void Assembler::emitr_imm64(uint64_t op, Register r, uint64_t imm64) { - underrunProtect(8+8); // imm64 + worst case instr len - *((uint64_t*)(_nIns -= 8)) = imm64; - _nvprof("x64-bytes", 8); - emitr(op, r); - } - - void Assembler::emitrxb_imm(uint64_t op, Register r, Register x, Register b, int32_t imm) { - NanoAssert(IsGpReg(r) && IsGpReg(x) && IsGpReg(b)); - underrunProtect(4+8); // room for imm plus fullsize op - *((int32_t*)(_nIns -= 4)) = imm; - _nvprof("x86-bytes", 4); - emitrxb(op, r, x, b); - } - - // op = [rex][opcode][modrm][imm8] - void Assembler::emitr_imm8(uint64_t op, Register b, int32_t imm8) { - NanoAssert(IsGpReg(b) && isS8(imm8)); - op |= uint64_t(imm8)<<56 | uint64_t(REGNUM(b)&7)<<48; // modrm is 2nd to last byte - emit(rexrb(op, RZero, b)); - } - - void Assembler::emitxm_abs(uint64_t op, Register r, int32_t addr32) - { - underrunProtect(4+8); - *((int32_t*)(_nIns -= 4)) = addr32; - _nvprof("x64-bytes", 4); - op = op | uint64_t((REGNUM(r)&7)<<3)<<48; // put rr[0:2] into mod/rm byte - op = rexrb(op, r, RZero); // put rr[3] into rex byte - emit(op); - } - - void Assembler::emitxm_rel(uint64_t op, Register r, NIns* addr64) - { - underrunProtect(4+8); - int32_t d = (int32_t)(addr64 - _nIns); - *((int32_t*)(_nIns -= 4)) = d; - _nvprof("x64-bytes", 4); - emitrr(op, r, RZero); - } - - // Succeeds if 'target' is within a signed 8-bit offset from the current - // instruction's address. - bool Assembler::isTargetWithinS8(NIns* target) - { - NanoAssert(target); - // First call underrunProtect(). Without it, we might compute the - // difference just before starting a new code chunk. - underrunProtect(8); - return isS8(target - _nIns); - } - - // Like isTargetWithinS8(), but for signed 32-bit offsets. - bool Assembler::isTargetWithinS32(NIns* target) - { - NanoAssert(target); - underrunProtect(8); - return isS32(target - _nIns); - } - -#define RB(r) gpRegNames8[(REGNUM(r))] -#define RS(r) gpRegNames16[(REGNUM(r))] -#define RBhi(r) gpRegNames8hi[(REGNUM(r))] -#define RL(r) gpRegNames32[(REGNUM(r))] -#define RQ(r) gpn(r) - - typedef Register R; - typedef int I; - typedef int32_t I32; - typedef uint64_t U64; - typedef size_t S; - - void Assembler::PUSHR(R r) { emitr(X64_pushr,r); asm_output("push %s", RQ(r)); } - void Assembler::POPR( R r) { emitr(X64_popr, r); asm_output("pop %s", RQ(r)); } - void Assembler::NOT( R r) { emitr(X64_not, r); asm_output("notl %s", RL(r)); } - void Assembler::NEG( R r) { emitr(X64_neg, r); asm_output("negl %s", RL(r)); } - void Assembler::IDIV( R r) { emitr(X64_idiv, r); asm_output("idivl edx:eax, %s",RL(r)); } - - void Assembler::SHR( R r) { emitr(X64_shr, r); asm_output("shrl %s, ecx", RL(r)); } - void Assembler::SAR( R r) { emitr(X64_sar, r); asm_output("sarl %s, ecx", RL(r)); } - void Assembler::SHL( R r) { emitr(X64_shl, r); asm_output("shll %s, ecx", RL(r)); } - void Assembler::SHRQ(R r) { emitr(X64_shrq, r); asm_output("shrq %s, ecx", RQ(r)); } - void Assembler::SARQ(R r) { emitr(X64_sarq, r); asm_output("sarq %s, ecx", RQ(r)); } - void Assembler::SHLQ(R r) { emitr(X64_shlq, r); asm_output("shlq %s, ecx", RQ(r)); } - - void Assembler::SHRI( R r, I i) { emit8(rexrb(X64_shri | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shrl %s, %d", RL(r), i); } - void Assembler::SARI( R r, I i) { emit8(rexrb(X64_sari | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("sarl %s, %d", RL(r), i); } - void Assembler::SHLI( R r, I i) { emit8(rexrb(X64_shli | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shll %s, %d", RL(r), i); } - void Assembler::SHRQI(R r, I i) { emit8(rexrb(X64_shrqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shrq %s, %d", RQ(r), i); } - void Assembler::SARQI(R r, I i) { emit8(rexrb(X64_sarqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("sarq %s, %d", RQ(r), i); } - void Assembler::SHLQI(R r, I i) { emit8(rexrb(X64_shlqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shlq %s, %d", RQ(r), i); } - - void Assembler::SETE( R r) { emitr8(X64_sete, r); asm_output("sete %s", RB(r)); } - void Assembler::SETL( R r) { emitr8(X64_setl, r); asm_output("setl %s", RB(r)); } - void Assembler::SETLE(R r) { emitr8(X64_setle,r); asm_output("setle %s",RB(r)); } - void Assembler::SETG( R r) { emitr8(X64_setg, r); asm_output("setg %s", RB(r)); } - void Assembler::SETGE(R r) { emitr8(X64_setge,r); asm_output("setge %s",RB(r)); } - void Assembler::SETB( R r) { emitr8(X64_setb, r); asm_output("setb %s", RB(r)); } - void Assembler::SETBE(R r) { emitr8(X64_setbe,r); asm_output("setbe %s",RB(r)); } - void Assembler::SETA( R r) { emitr8(X64_seta, r); asm_output("seta %s", RB(r)); } - void Assembler::SETAE(R r) { emitr8(X64_setae,r); asm_output("setae %s",RB(r)); } - void Assembler::SETO( R r) { emitr8(X64_seto, r); asm_output("seto %s", RB(r)); } - - void Assembler::ADDRR(R l, R r) { emitrr(X64_addrr,l,r); asm_output("addl %s, %s", RL(l),RL(r)); } - void Assembler::SUBRR(R l, R r) { emitrr(X64_subrr,l,r); asm_output("subl %s, %s", RL(l),RL(r)); } - void Assembler::ANDRR(R l, R r) { emitrr(X64_andrr,l,r); asm_output("andl %s, %s", RL(l),RL(r)); } - void Assembler::ORLRR(R l, R r) { emitrr(X64_orlrr,l,r); asm_output("orl %s, %s", RL(l),RL(r)); } - void Assembler::XORRR(R l, R r) { emitrr(X64_xorrr,l,r); asm_output("xorl %s, %s", RL(l),RL(r)); } - void Assembler::IMUL( R l, R r) { emitrr(X64_imul, l,r); asm_output("imull %s, %s",RL(l),RL(r)); } - void Assembler::CMPLR(R l, R r) { emitrr(X64_cmplr,l,r); asm_output("cmpl %s, %s", RL(l),RL(r)); } - void Assembler::MOVLR(R l, R r) { emitrr(X64_movlr,l,r); asm_output("movl %s, %s", RL(l),RL(r)); } - - void Assembler::ADDQRR( R l, R r) { emitrr(X64_addqrr, l,r); asm_output("addq %s, %s", RQ(l),RQ(r)); } - void Assembler::SUBQRR( R l, R r) { emitrr(X64_subqrr, l,r); asm_output("subq %s, %s", RQ(l),RQ(r)); } - void Assembler::ANDQRR( R l, R r) { emitrr(X64_andqrr, l,r); asm_output("andq %s, %s", RQ(l),RQ(r)); } - void Assembler::ORQRR( R l, R r) { emitrr(X64_orqrr, l,r); asm_output("orq %s, %s", RQ(l),RQ(r)); } - void Assembler::XORQRR( R l, R r) { emitrr(X64_xorqrr, l,r); asm_output("xorq %s, %s", RQ(l),RQ(r)); } - void Assembler::CMPQR( R l, R r) { emitrr(X64_cmpqr, l,r); asm_output("cmpq %s, %s", RQ(l),RQ(r)); } - void Assembler::MOVQR( R l, R r) { emitrr(X64_movqr, l,r); asm_output("movq %s, %s", RQ(l),RQ(r)); } - void Assembler::MOVAPSR(R l, R r) { emitrr(X64_movapsr,l,r); asm_output("movaps %s, %s",RQ(l),RQ(r)); } - - void Assembler::CMOVNO( R l, R r) { emitrr(X64_cmovno, l,r); asm_output("cmovlno %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNE( R l, R r) { emitrr(X64_cmovne, l,r); asm_output("cmovlne %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNL( R l, R r) { emitrr(X64_cmovnl, l,r); asm_output("cmovlnl %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNLE(R l, R r) { emitrr(X64_cmovnle,l,r); asm_output("cmovlnle %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNG( R l, R r) { emitrr(X64_cmovng, l,r); asm_output("cmovlng %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNGE(R l, R r) { emitrr(X64_cmovnge,l,r); asm_output("cmovlnge %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNB( R l, R r) { emitrr(X64_cmovnb, l,r); asm_output("cmovlnb %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNBE(R l, R r) { emitrr(X64_cmovnbe,l,r); asm_output("cmovlnbe %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNA( R l, R r) { emitrr(X64_cmovna, l,r); asm_output("cmovlna %s, %s", RL(l),RL(r)); } - void Assembler::CMOVNAE(R l, R r) { emitrr(X64_cmovnae,l,r); asm_output("cmovlnae %s, %s", RL(l),RL(r)); } - - void Assembler::CMOVQNO( R l, R r) { emitrr(X64_cmovqno, l,r); asm_output("cmovqno %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNE( R l, R r) { emitrr(X64_cmovqne, l,r); asm_output("cmovqne %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNL( R l, R r) { emitrr(X64_cmovqnl, l,r); asm_output("cmovqnl %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNLE(R l, R r) { emitrr(X64_cmovqnle,l,r); asm_output("cmovqnle %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNG( R l, R r) { emitrr(X64_cmovqng, l,r); asm_output("cmovqng %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNGE(R l, R r) { emitrr(X64_cmovqnge,l,r); asm_output("cmovqnge %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNB( R l, R r) { emitrr(X64_cmovqnb, l,r); asm_output("cmovqnb %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNBE(R l, R r) { emitrr(X64_cmovqnbe,l,r); asm_output("cmovqnbe %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNA( R l, R r) { emitrr(X64_cmovqna, l,r); asm_output("cmovqna %s, %s", RQ(l),RQ(r)); } - void Assembler::CMOVQNAE(R l, R r) { emitrr(X64_cmovqnae,l,r); asm_output("cmovqnae %s, %s", RQ(l),RQ(r)); } - - void Assembler::MOVSXDR(R l, R r) { emitrr(X64_movsxdr,l,r); asm_output("movsxd %s, %s",RQ(l),RL(r)); } - - void Assembler::MOVZX8(R l, R r) { emitrr8(X64_movzx8,l,r); asm_output("movzx %s, %s",RQ(l),RB(r)); } - -// XORPS is a 4x32f vector operation, we use it instead of the more obvious -// XORPD because it's one byte shorter. This is ok because it's only used for -// zeroing an XMM register; hence the single argument. -// Also note that (unlike most SSE2 instructions) XORPS does not have a prefix, thus emitrr() should be used. - void Assembler::XORPS( R r) { emitrr(X64_xorps, r,r); asm_output("xorps %s, %s", RQ(r),RQ(r)); } - void Assembler::XORPS( R l, R r) { emitrr(X64_xorps, l,r); asm_output("xorps %s, %s", RQ(l),RQ(r)); } - void Assembler::DIVSD( R l, R r) { emitprr(X64_divsd, l,r); asm_output("divsd %s, %s", RQ(l),RQ(r)); } - void Assembler::MULSD( R l, R r) { emitprr(X64_mulsd, l,r); asm_output("mulsd %s, %s", RQ(l),RQ(r)); } - void Assembler::ADDSD( R l, R r) { emitprr(X64_addsd, l,r); asm_output("addsd %s, %s", RQ(l),RQ(r)); } - void Assembler::SUBSD( R l, R r) { emitprr(X64_subsd, l,r); asm_output("subsd %s, %s", RQ(l),RQ(r)); } - void Assembler::CVTSQ2SD(R l, R r) { emitprr(X64_cvtsq2sd,l,r); asm_output("cvtsq2sd %s, %s",RQ(l),RQ(r)); } - void Assembler::CVTSI2SD(R l, R r) { emitprr(X64_cvtsi2sd,l,r); asm_output("cvtsi2sd %s, %s",RQ(l),RL(r)); } - void Assembler::CVTSS2SD(R l, R r) { emitprr(X64_cvtss2sd,l,r); asm_output("cvtss2sd %s, %s",RQ(l),RL(r)); } - void Assembler::CVTSD2SS(R l, R r) { emitprr(X64_cvtsd2ss,l,r); asm_output("cvtsd2ss %s, %s",RL(l),RQ(r)); } - void Assembler::CVTSD2SI(R l, R r) { emitprr(X64_cvtsd2si,l,r); asm_output("cvtsd2si %s, %s",RL(l),RQ(r)); } - void Assembler::CVTTSD2SI(R l, R r) { emitprr(X64_cvttsd2si,l,r);asm_output("cvttsd2si %s, %s",RL(l),RQ(r));} - void Assembler::UCOMISD( R l, R r) { emitprr(X64_ucomisd, l,r); asm_output("ucomisd %s, %s", RQ(l),RQ(r)); } - void Assembler::MOVQRX( R l, R r) { emitprr(X64_movqrx, r,l); asm_output("movq %s, %s", RQ(l),RQ(r)); } // Nb: r and l are deliberately reversed within the emitprr() call. - void Assembler::MOVQXR( R l, R r) { emitprr(X64_movqxr, l,r); asm_output("movq %s, %s", RQ(l),RQ(r)); } - - // MOVI must not affect condition codes! - void Assembler::MOVI( R r, I32 i32) { emitr_imm(X64_movi, r,i32); asm_output("movl %s, %d",RL(r),i32); } - void Assembler::ADDLRI(R r, I32 i32) { emitr_imm(X64_addlri,r,i32); asm_output("addl %s, %d",RL(r),i32); } - void Assembler::SUBLRI(R r, I32 i32) { emitr_imm(X64_sublri,r,i32); asm_output("subl %s, %d",RL(r),i32); } - void Assembler::ANDLRI(R r, I32 i32) { emitr_imm(X64_andlri,r,i32); asm_output("andl %s, %d",RL(r),i32); } - void Assembler::ORLRI( R r, I32 i32) { emitr_imm(X64_orlri, r,i32); asm_output("orl %s, %d", RL(r),i32); } - void Assembler::XORLRI(R r, I32 i32) { emitr_imm(X64_xorlri,r,i32); asm_output("xorl %s, %d",RL(r),i32); } - void Assembler::CMPLRI(R r, I32 i32) { emitr_imm(X64_cmplri,r,i32); asm_output("cmpl %s, %d",RL(r),i32); } - - void Assembler::ADDQRI( R r, I32 i32) { emitr_imm(X64_addqri, r,i32); asm_output("addq %s, %d", RQ(r),i32); } - void Assembler::SUBQRI( R r, I32 i32) { emitr_imm(X64_subqri, r,i32); asm_output("subq %s, %d", RQ(r),i32); } - void Assembler::ANDQRI( R r, I32 i32) { emitr_imm(X64_andqri, r,i32); asm_output("andq %s, %d", RQ(r),i32); } - void Assembler::ORQRI( R r, I32 i32) { emitr_imm(X64_orqri, r,i32); asm_output("orq %s, %d", RQ(r),i32); } - void Assembler::XORQRI( R r, I32 i32) { emitr_imm(X64_xorqri, r,i32); asm_output("xorq %s, %d", RQ(r),i32); } - void Assembler::CMPQRI( R r, I32 i32) { emitr_imm(X64_cmpqri, r,i32); asm_output("cmpq %s, %d", RQ(r),i32); } - void Assembler::MOVQI32(R r, I32 i32) { emitr_imm(X64_movqi32,r,i32); asm_output("movqi32 %s, %d",RQ(r),i32); } - - void Assembler::ADDLR8(R r, I32 i8) { emitr_imm8(X64_addlr8,r,i8); asm_output("addl %s, %d", RL(r),i8); } - void Assembler::SUBLR8(R r, I32 i8) { emitr_imm8(X64_sublr8,r,i8); asm_output("subl %s, %d", RL(r),i8); } - void Assembler::ANDLR8(R r, I32 i8) { emitr_imm8(X64_andlr8,r,i8); asm_output("andl %s, %d", RL(r),i8); } - void Assembler::ORLR8( R r, I32 i8) { emitr_imm8(X64_orlr8, r,i8); asm_output("orl %s, %d", RL(r),i8); } - void Assembler::XORLR8(R r, I32 i8) { emitr_imm8(X64_xorlr8,r,i8); asm_output("xorl %s, %d", RL(r),i8); } - void Assembler::CMPLR8(R r, I32 i8) { emitr_imm8(X64_cmplr8,r,i8); asm_output("cmpl %s, %d", RL(r),i8); } - - void Assembler::ADDQR8(R r, I32 i8) { emitr_imm8(X64_addqr8,r,i8); asm_output("addq %s, %d",RQ(r),i8); } - void Assembler::SUBQR8(R r, I32 i8) { emitr_imm8(X64_subqr8,r,i8); asm_output("subq %s, %d",RQ(r),i8); } - void Assembler::ANDQR8(R r, I32 i8) { emitr_imm8(X64_andqr8,r,i8); asm_output("andq %s, %d",RQ(r),i8); } - void Assembler::ORQR8( R r, I32 i8) { emitr_imm8(X64_orqr8, r,i8); asm_output("orq %s, %d", RQ(r),i8); } - void Assembler::XORQR8(R r, I32 i8) { emitr_imm8(X64_xorqr8,r,i8); asm_output("xorq %s, %d",RQ(r),i8); } - void Assembler::CMPQR8(R r, I32 i8) { emitr_imm8(X64_cmpqr8,r,i8); asm_output("cmpq %s, %d",RQ(r),i8); } - - void Assembler::IMULI(R l, R r, I32 i32) { emitrr_imm(X64_imuli,l,r,i32); asm_output("imuli %s, %s, %d",RL(l),RL(r),i32); } - - void Assembler::MOVQI(R r, U64 u64) { emitr_imm64(X64_movqi,r,u64); asm_output("movq %s, %p",RQ(r),(void*)u64); } - - void Assembler::LEARIP(R r, I32 d) { emitrm(X64_learip,r,d,RZero); asm_output("lea %s, %d(rip)",RQ(r),d); } - - void Assembler::LEALRM(R r, I d, R b) { emitrm(X64_lealrm,r,d,b); asm_output("leal %s, %d(%s)",RL(r),d,RL(b)); } - void Assembler::LEAQRM(R r, I d, R b) { emitrm(X64_leaqrm,r,d,b); asm_output("leaq %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVLRM(R r, I d, R b) { emitrm(X64_movlrm,r,d,b); asm_output("movl %s, %d(%s)",RL(r),d,RQ(b)); } - void Assembler::MOVQRM(R r, I d, R b) { emitrm(X64_movqrm,r,d,b); asm_output("movq %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVBMR(R r, I d, R b) { emitrm8(X64_movbmr,r,d,b); asm_output("movb %d(%s), %s",d,RQ(b),RB(r)); } - void Assembler::MOVSMR(R r, I d, R b) { emitprm(X64_movsmr,r,d,b); asm_output("movs %d(%s), %s",d,RQ(b),RS(r)); } - void Assembler::MOVLMR(R r, I d, R b) { emitrm(X64_movlmr,r,d,b); asm_output("movl %d(%s), %s",d,RQ(b),RL(r)); } - void Assembler::MOVQMR(R r, I d, R b) { emitrm(X64_movqmr,r,d,b); asm_output("movq %d(%s), %s",d,RQ(b),RQ(r)); } - - void Assembler::MOVZX8M( R r, I d, R b) { emitrm_wide(X64_movzx8m, r,d,b); asm_output("movzxb %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVZX16M(R r, I d, R b) { emitrm_wide(X64_movzx16m,r,d,b); asm_output("movzxs %s, %d(%s)",RQ(r),d,RQ(b)); } - - void Assembler::MOVSX8M( R r, I d, R b) { emitrm_wide(X64_movsx8m, r,d,b); asm_output("movsxb %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVSX16M(R r, I d, R b) { emitrm_wide(X64_movsx16m,r,d,b); asm_output("movsxs %s, %d(%s)",RQ(r),d,RQ(b)); } - - void Assembler::MOVSDRM(R r, I d, R b) { emitprm(X64_movsdrm,r,d,b); asm_output("movsd %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVSDMR(R r, I d, R b) { emitprm(X64_movsdmr,r,d,b); asm_output("movsd %d(%s), %s",d,RQ(b),RQ(r)); } - void Assembler::MOVSSRM(R r, I d, R b) { emitprm(X64_movssrm,r,d,b); asm_output("movss %s, %d(%s)",RQ(r),d,RQ(b)); } - void Assembler::MOVSSMR(R r, I d, R b) { emitprm(X64_movssmr,r,d,b); asm_output("movss %d(%s), %s",d,RQ(b),RQ(r)); } - - void Assembler::JMP8( S n, NIns* t) { emit_target8(n, X64_jmp8,t); asm_output("jmp %p", t); } - - void Assembler::JMP32(S n, NIns* t) { emit_target32(n,X64_jmp, t); asm_output("jmp %p", t); } - void Assembler::JMP64(S n, NIns* t) { emit_target64(n,X64_jmpi, t); asm_output("jmp %p", t); } - - void Assembler::JMPX(R indexreg, NIns** table) - { - Register R5 = { 5 }; - emitrxb_imm(X64_jmpx, RZero, indexreg, R5, (int32_t)(uintptr_t)table); - asm_output("jmpq [%s*8 + %p]", RQ(indexreg), (void*)table); - } - - void Assembler::JMPXB(R indexreg, R tablereg) { emitxb(X64_jmpxb, indexreg, tablereg); asm_output("jmp [%s*8 + %s]", RQ(indexreg), RQ(tablereg)); } - - void Assembler::JO( S n, NIns* t) { emit_target32(n,X64_jo, t); asm_output("jo %p", t); } - void Assembler::JE( S n, NIns* t) { emit_target32(n,X64_je, t); asm_output("je %p", t); } - void Assembler::JL( S n, NIns* t) { emit_target32(n,X64_jl, t); asm_output("jl %p", t); } - void Assembler::JLE(S n, NIns* t) { emit_target32(n,X64_jle, t); asm_output("jle %p",t); } - void Assembler::JG( S n, NIns* t) { emit_target32(n,X64_jg, t); asm_output("jg %p", t); } - void Assembler::JGE(S n, NIns* t) { emit_target32(n,X64_jge, t); asm_output("jge %p",t); } - void Assembler::JB( S n, NIns* t) { emit_target32(n,X64_jb, t); asm_output("jb %p", t); } - void Assembler::JBE(S n, NIns* t) { emit_target32(n,X64_jbe, t); asm_output("jbe %p",t); } - void Assembler::JA( S n, NIns* t) { emit_target32(n,X64_ja, t); asm_output("ja %p", t); } - void Assembler::JAE(S n, NIns* t) { emit_target32(n,X64_jae, t); asm_output("jae %p",t); } - void Assembler::JP( S n, NIns* t) { emit_target32(n,X64_jp, t); asm_output("jp %p",t); } - - void Assembler::JNO( S n, NIns* t) { emit_target32(n,X64_jo ^X64_jneg, t); asm_output("jno %p", t); } - void Assembler::JNE( S n, NIns* t) { emit_target32(n,X64_je ^X64_jneg, t); asm_output("jne %p", t); } - void Assembler::JNL( S n, NIns* t) { emit_target32(n,X64_jl ^X64_jneg, t); asm_output("jnl %p", t); } - void Assembler::JNLE(S n, NIns* t) { emit_target32(n,X64_jle^X64_jneg, t); asm_output("jnle %p",t); } - void Assembler::JNG( S n, NIns* t) { emit_target32(n,X64_jg ^X64_jneg, t); asm_output("jng %p", t); } - void Assembler::JNGE(S n, NIns* t) { emit_target32(n,X64_jge^X64_jneg, t); asm_output("jnge %p",t); } - void Assembler::JNB( S n, NIns* t) { emit_target32(n,X64_jb ^X64_jneg, t); asm_output("jnb %p", t); } - void Assembler::JNBE(S n, NIns* t) { emit_target32(n,X64_jbe^X64_jneg, t); asm_output("jnbe %p",t); } - void Assembler::JNA( S n, NIns* t) { emit_target32(n,X64_ja ^X64_jneg, t); asm_output("jna %p", t); } - void Assembler::JNAE(S n, NIns* t) { emit_target32(n,X64_jae^X64_jneg, t); asm_output("jnae %p",t); } - - void Assembler::JO8( S n, NIns* t) { emit_target8(n,X64_jo8, t); asm_output("jo %p", t); } - void Assembler::JE8( S n, NIns* t) { emit_target8(n,X64_je8, t); asm_output("je %p", t); } - void Assembler::JL8( S n, NIns* t) { emit_target8(n,X64_jl8, t); asm_output("jl %p", t); } - void Assembler::JLE8(S n, NIns* t) { emit_target8(n,X64_jle8, t); asm_output("jle %p",t); } - void Assembler::JG8( S n, NIns* t) { emit_target8(n,X64_jg8, t); asm_output("jg %p", t); } - void Assembler::JGE8(S n, NIns* t) { emit_target8(n,X64_jge8, t); asm_output("jge %p",t); } - void Assembler::JB8( S n, NIns* t) { emit_target8(n,X64_jb8, t); asm_output("jb %p", t); } - void Assembler::JBE8(S n, NIns* t) { emit_target8(n,X64_jbe8, t); asm_output("jbe %p",t); } - void Assembler::JA8( S n, NIns* t) { emit_target8(n,X64_ja8, t); asm_output("ja %p", t); } - void Assembler::JAE8(S n, NIns* t) { emit_target8(n,X64_jae8, t); asm_output("jae %p",t); } - void Assembler::JP8( S n, NIns* t) { emit_target8(n,X64_jp8, t); asm_output("jp %p",t); } - - void Assembler::JNO8( S n, NIns* t) { emit_target8(n,X64_jo8 ^X64_jneg8, t); asm_output("jno %p", t); } - void Assembler::JNE8( S n, NIns* t) { emit_target8(n,X64_je8 ^X64_jneg8, t); asm_output("jne %p", t); } - void Assembler::JNL8( S n, NIns* t) { emit_target8(n,X64_jl8 ^X64_jneg8, t); asm_output("jnl %p", t); } - void Assembler::JNLE8(S n, NIns* t) { emit_target8(n,X64_jle8^X64_jneg8, t); asm_output("jnle %p",t); } - void Assembler::JNG8( S n, NIns* t) { emit_target8(n,X64_jg8 ^X64_jneg8, t); asm_output("jng %p", t); } - void Assembler::JNGE8(S n, NIns* t) { emit_target8(n,X64_jge8^X64_jneg8, t); asm_output("jnge %p",t); } - void Assembler::JNB8( S n, NIns* t) { emit_target8(n,X64_jb8 ^X64_jneg8, t); asm_output("jnb %p", t); } - void Assembler::JNBE8(S n, NIns* t) { emit_target8(n,X64_jbe8^X64_jneg8, t); asm_output("jnbe %p",t); } - void Assembler::JNA8( S n, NIns* t) { emit_target8(n,X64_ja8 ^X64_jneg8, t); asm_output("jna %p", t); } - void Assembler::JNAE8(S n, NIns* t) { emit_target8(n,X64_jae8^X64_jneg8, t); asm_output("jnae %p",t); } - - void Assembler::CALL( S n, NIns* t) { emit_target32(n,X64_call,t); asm_output("call %p",t); } - - void Assembler::CALLRAX() { emit(X64_callrax); asm_output("call (rax)"); } - void Assembler::RET() { emit(X64_ret); asm_output("ret"); } - - void Assembler::MOVQMI(R r, I d, I32 imm) { emitrm_imm32(X64_movqmi,r,d,imm); asm_output("movq %d(%s), %d",d,RQ(r),imm); } - void Assembler::MOVLMI(R r, I d, I32 imm) { emitrm_imm32(X64_movlmi,r,d,imm); asm_output("movl %d(%s), %d",d,RQ(r),imm); } - void Assembler::MOVSMI(R r, I d, I32 imm) { emitprm_imm16(X64_movsmi,r,d,imm); asm_output("movs %d(%s), %d",d,RQ(r),imm); } - void Assembler::MOVBMI(R r, I d, I32 imm) { emitrm_imm8(X64_movbmi,r,d,imm); asm_output("movb %d(%s), %d",d,RQ(r),imm); } - - void Assembler::MOVQSPR(I d, R r) { emit(X64_movqspr | U64(d) << 56 | U64((REGNUM(r)&7)<<3) << 40 | U64((REGNUM(r)&8)>>1) << 24); asm_output("movq %d(rsp), %s", d, RQ(r)); } // insert r into mod/rm and rex bytes - - void Assembler::XORPSA(R r, I32 i32) { emitxm_abs(X64_xorpsa, r, i32); asm_output("xorps %s, (0x%x)",RQ(r), i32); } - void Assembler::XORPSM(R r, NIns* a64) { emitxm_rel(X64_xorpsm, r, a64); asm_output("xorps %s, (%p)", RQ(r), a64); } - - void Assembler::X86_AND8R(R r) { emit(X86_and8r | U64(REGNUM(r)<<3|(REGNUM(r)|4))<<56); asm_output("andb %s, %s", RB(r), RBhi(r)); } - void Assembler::X86_SETNP(R r) { emit(X86_setnp | U64(REGNUM(r)|4)<<56); asm_output("setnp %s", RBhi(r)); } - void Assembler::X86_SETE(R r) { emit(X86_sete | U64(REGNUM(r))<<56); asm_output("sete %s", RB(r)); } - -#undef R -#undef I -#undef I32 -#undef U64 -#undef S - - void Assembler::MR(Register d, Register s) { - NanoAssert(IsGpReg(d) && IsGpReg(s)); - MOVQR(d, s); - } - - // This is needed for guards; we must be able to patch the jmp later and - // we cannot do that if an 8-bit relative jump is used, so we can't use - // JMP(). - void Assembler::JMPl(NIns* target) { - if (!target || isTargetWithinS32(target)) { - JMP32(8, target); - } else { - JMP64(16, target); - } - } - - void Assembler::JMP(NIns *target) { - if (!target || isTargetWithinS32(target)) { - if (target && isTargetWithinS8(target)) { - JMP8(8, target); - } else { - JMP32(8, target); - } - } else { - JMP64(16, target); - } - } - - void Assembler::asm_qbinop(LIns *ins) { - asm_arith(ins); - } - - void Assembler::asm_shift(LIns *ins) { - // Shift requires rcx for shift count. - LIns *a = ins->oprnd1(); - LIns *b = ins->oprnd2(); - if (b->isImmI()) { - asm_shift_imm(ins); - return; - } - - Register rr, ra; - if (a != b) { - findSpecificRegFor(b, RCX); - beginOp1Regs(ins, GpRegs & ~rmask(RCX), rr, ra); - } else { - // Nb: this is just like beginOp1Regs() except that it asserts - // that ra is in GpRegs instead of rmask(RCX)) -- this is - // necessary for the a==b case because 'a' might not be in RCX - // (which is ok, the MR(rr, ra) below will move it into RCX). - rr = prepareResultReg(ins, rmask(RCX)); - - // If 'a' isn't in a register, it can be clobbered by 'ins'. - ra = a->isInReg() ? a->getReg() : rr; - NanoAssert(rmask(ra) & GpRegs); - } - - switch (ins->opcode()) { - default: - TODO(asm_shift); - case LIR_rshuq: SHRQ(rr); break; - case LIR_rshq: SARQ(rr); break; - case LIR_lshq: SHLQ(rr); break; - case LIR_rshui: SHR( rr); break; - case LIR_rshi: SAR( rr); break; - case LIR_lshi: SHL( rr); break; - } - if (rr != ra) - MR(rr, ra); - - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_shift_imm(LIns *ins) { - Register rr, ra; - beginOp1Regs(ins, GpRegs, rr, ra); - - int shift = ins->oprnd2()->immI() & 63; - switch (ins->opcode()) { - default: TODO(shiftimm); - case LIR_rshuq: SHRQI(rr, shift); break; - case LIR_rshq: SARQI(rr, shift); break; - case LIR_lshq: SHLQI(rr, shift); break; - case LIR_rshui: SHRI( rr, shift); break; - case LIR_rshi: SARI( rr, shift); break; - case LIR_lshi: SHLI( rr, shift); break; - } - if (rr != ra) - MR(rr, ra); - - endOpRegs(ins, rr, ra); - } - - static bool isImm32(LIns *ins) { - return ins->isImmI() || (ins->isImmQ() && isS32(ins->immQ())); - } - static int32_t getImm32(LIns *ins) { - return ins->isImmI() ? ins->immI() : int32_t(ins->immQ()); - } - - // Binary op, integer regs, rhs is int32 constant. - void Assembler::asm_arith_imm(LIns *ins) { - LIns *b = ins->oprnd2(); - int32_t imm = getImm32(b); - LOpcode op = ins->opcode(); - Register rr, ra; - if (op == LIR_muli || op == LIR_muljovi || op == LIR_mulxovi) { - // Special case: imul-by-imm has true 3-addr form. So we don't - // need the MR(rr, ra) after the IMULI. - beginOp1Regs(ins, GpRegs, rr, ra); - IMULI(rr, ra, imm); - endOpRegs(ins, rr, ra); - return; - } - - beginOp1Regs(ins, GpRegs, rr, ra); - if (isS8(imm)) { - switch (ins->opcode()) { - default: TODO(arith_imm8); - case LIR_addi: - case LIR_addjovi: - case LIR_addxovi: ADDLR8(rr, imm); break; // XXX: bug 547125: could use LEA for LIR_addi - case LIR_andi: ANDLR8(rr, imm); break; - case LIR_ori: ORLR8( rr, imm); break; - case LIR_subi: - case LIR_subjovi: - case LIR_subxovi: SUBLR8(rr, imm); break; - case LIR_xori: XORLR8(rr, imm); break; - case LIR_addq: - case LIR_addjovq: ADDQR8(rr, imm); break; - case LIR_subq: - case LIR_subjovq: SUBQR8(rr, imm); break; - case LIR_andq: ANDQR8(rr, imm); break; - case LIR_orq: ORQR8( rr, imm); break; - case LIR_xorq: XORQR8(rr, imm); break; - } - } else { - switch (ins->opcode()) { - default: TODO(arith_imm); - case LIR_addi: - case LIR_addjovi: - case LIR_addxovi: ADDLRI(rr, imm); break; // XXX: bug 547125: could use LEA for LIR_addi - case LIR_andi: ANDLRI(rr, imm); break; - case LIR_ori: ORLRI( rr, imm); break; - case LIR_subi: - case LIR_subjovi: - case LIR_subxovi: SUBLRI(rr, imm); break; - case LIR_xori: XORLRI(rr, imm); break; - case LIR_addq: - case LIR_addjovq: ADDQRI(rr, imm); break; - case LIR_subq: - case LIR_subjovq: SUBQRI(rr, imm); break; - case LIR_andq: ANDQRI(rr, imm); break; - case LIR_orq: ORQRI( rr, imm); break; - case LIR_xorq: XORQRI(rr, imm); break; - } - } - if (rr != ra) - MR(rr, ra); - - endOpRegs(ins, rr, ra); - } - - // Generates code for a LIR_divi that doesn't have a subsequent LIR_modi. - void Assembler::asm_div(LIns *div) { - NanoAssert(div->isop(LIR_divi)); - LIns *a = div->oprnd1(); - LIns *b = div->oprnd2(); - - evictIfActive(RDX); - prepareResultReg(div, rmask(RAX)); - - Register rb = findRegFor(b, GpRegs & ~(rmask(RAX)|rmask(RDX))); - Register ra = a->isInReg() ? a->getReg() : RAX; - - IDIV(rb); - SARI(RDX, 31); - MR(RDX, RAX); - if (RAX != ra) - MR(RAX, ra); - - freeResourcesOf(div); - if (!a->isInReg()) { - NanoAssert(ra == RAX); - findSpecificRegForUnallocated(a, RAX); - } - } - - // Generates code for a LIR_modi(LIR_divi(divL, divR)) sequence. - void Assembler::asm_div_mod(LIns *mod) { - LIns *div = mod->oprnd1(); - - NanoAssert(mod->isop(LIR_modi)); - NanoAssert(div->isop(LIR_divi)); - - LIns *divL = div->oprnd1(); - LIns *divR = div->oprnd2(); - - prepareResultReg(mod, rmask(RDX)); - prepareResultReg(div, rmask(RAX)); - - Register rDivR = findRegFor(divR, GpRegs & ~(rmask(RAX)|rmask(RDX))); - Register rDivL = divL->isInReg() ? divL->getReg() : RAX; - - IDIV(rDivR); - SARI(RDX, 31); - MR(RDX, RAX); - if (RAX != rDivL) - MR(RAX, rDivL); - - freeResourcesOf(mod); - freeResourcesOf(div); - if (!divL->isInReg()) { - NanoAssert(rDivL == RAX); - findSpecificRegForUnallocated(divL, RAX); - } - } - - // binary op with integer registers - void Assembler::asm_arith(LIns *ins) { - Register rr, ra, rb = UnspecifiedReg; // init to shut GCC up - - switch (ins->opcode()) { - case LIR_lshi: case LIR_lshq: - case LIR_rshi: case LIR_rshq: - case LIR_rshui: case LIR_rshuq: - asm_shift(ins); - return; - case LIR_modi: - asm_div_mod(ins); - return; - case LIR_divi: - // Nb: if the div feeds into a mod it will be handled by - // asm_div_mod() rather than here. - asm_div(ins); - return; - default: - break; - } - - LIns *b = ins->oprnd2(); - if (isImm32(b)) { - asm_arith_imm(ins); - return; - } - beginOp2Regs(ins, GpRegs, rr, ra, rb); - switch (ins->opcode()) { - default: TODO(asm_arith); - case LIR_ori: ORLRR(rr, rb); break; - case LIR_subi: - case LIR_subjovi: - case LIR_subxovi: SUBRR(rr, rb); break; - case LIR_addi: - case LIR_addjovi: - case LIR_addxovi: ADDRR(rr, rb); break; // XXX: bug 547125: could use LEA for LIR_addi - case LIR_andi: ANDRR(rr, rb); break; - case LIR_xori: XORRR(rr, rb); break; - case LIR_muli: - case LIR_muljovi: - case LIR_mulxovi: IMUL(rr, rb); break; - case LIR_xorq: XORQRR(rr, rb); break; - case LIR_orq: ORQRR(rr, rb); break; - case LIR_andq: ANDQRR(rr, rb); break; - case LIR_addq: - case LIR_addjovq: ADDQRR(rr, rb); break; - case LIR_subq: - case LIR_subjovq: SUBQRR(rr, rb); break; - } - if (rr != ra) - MR(rr, ra); - - endOpRegs(ins, rr, ra); - } - - // Binary op with fp registers. - void Assembler::asm_fop(LIns *ins) { - Register rr, ra, rb = UnspecifiedReg; // init to shut GCC up - beginOp2Regs(ins, FpRegs, rr, ra, rb); - switch (ins->opcode()) { - default: TODO(asm_fop); - case LIR_divd: DIVSD(rr, rb); break; - case LIR_muld: MULSD(rr, rb); break; - case LIR_addd: ADDSD(rr, rb); break; - case LIR_subd: SUBSD(rr, rb); break; - } - if (rr != ra) { - asm_nongp_copy(rr, ra); - } - - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_neg_not(LIns *ins) { - Register rr, ra; - beginOp1Regs(ins, GpRegs, rr, ra); - - if (ins->isop(LIR_noti)) - NOT(rr); - else - NEG(rr); - if (rr != ra) - MR(rr, ra); - - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_call(LIns *ins) { - if (!ins->isop(LIR_callv)) { - Register rr = ( ins->isop(LIR_calld) ? XMM0 : retRegs[0] ); - prepareResultReg(ins, rmask(rr)); - evictScratchRegsExcept(rmask(rr)); - } else { - evictScratchRegsExcept(0); - } - - const CallInfo *call = ins->callInfo(); - ArgType argTypes[MAXARGS]; - int argc = call->getArgTypes(argTypes); - - if (!call->isIndirect()) { - verbose_only(if (_logc->lcbits & LC_Native) - outputf(" %p:", _nIns); - ) - NIns *target = (NIns*)call->_address; - if (isTargetWithinS32(target)) { - CALL(8, target); - } else { - // can't reach target from here, load imm64 and do an indirect jump - CALLRAX(); - asm_immq(RAX, (uint64_t)target, /*canClobberCCs*/true); - } - // Call this now so that the arg setup can involve 'rr'. - freeResourcesOf(ins); - } else { - // Indirect call: we assign the address arg to RAX since it's not - // used for regular arguments, and is otherwise scratch since it's - // clobberred by the call. - CALLRAX(); - - // Call this now so that the arg setup can involve 'rr'. - freeResourcesOf(ins); - - // Assign the call address to RAX. Must happen after freeResourcesOf() - // since RAX is usually the return value and will be allocated until that point. - asm_regarg(ARGTYPE_P, ins->arg(--argc), RAX); - } - - #ifdef _WIN64 - int stk_used = 32; // always reserve 32byte shadow area - #else - int stk_used = 0; - Register fr = XMM0; - #endif - int arg_index = 0; - for (int i = 0; i < argc; i++) { - int j = argc - i - 1; - ArgType ty = argTypes[j]; - LIns* arg = ins->arg(j); - if ((ty == ARGTYPE_I || ty == ARGTYPE_UI || ty == ARGTYPE_Q) && arg_index < NumArgRegs) { - // gp arg - asm_regarg(ty, arg, argRegs[arg_index]); - arg_index++; - } - #ifdef _WIN64 - else if (ty == ARGTYPE_D && arg_index < NumArgRegs) { - // double goes in XMM reg # based on overall arg_index - Register rxi = XMM0 + arg_index; - asm_regarg(ty, arg, rxi); - arg_index++; - } - #else - else if (ty == ARGTYPE_D && fr < XMM8) { - // double goes in next available XMM register - asm_regarg(ty, arg, fr); - fr = fr + 1; - } - #endif - else { - asm_stkarg(ty, arg, stk_used); - stk_used += sizeof(void*); - } - } - - if (stk_used > max_stk_used) - max_stk_used = stk_used; - } - - void Assembler::asm_regarg(ArgType ty, LIns *p, Register r) { - if (ty == ARGTYPE_I) { - NanoAssert(p->isI()); - if (p->isImmI()) { - asm_immq(r, int64_t(p->immI()), /*canClobberCCs*/true); - return; - } - // sign extend int32 to int64 - MOVSXDR(r, r); - } else if (ty == ARGTYPE_UI) { - NanoAssert(p->isI()); - if (p->isImmI()) { - asm_immq(r, uint64_t(uint32_t(p->immI())), /*canClobberCCs*/true); - return; - } - // zero extend with 32bit mov, auto-zeros upper 32bits - MOVLR(r, r); - } else { - // Do nothing. - } - /* there is no point in folding an immediate here, because - * the argument register must be a scratch register and we're - * just before a call. Just reserving the register will cause - * the constant to be rematerialized nearby in asm_restore(), - * which is the same instruction we would otherwise emit right - * here, and moving it earlier in the stream provides more scheduling - * freedom to the cpu. */ - findSpecificRegFor(p, r); - } - - void Assembler::asm_stkarg(ArgType ty, LIns *p, int stk_off) { - NanoAssert(isS8(stk_off)); - if (ty == ARGTYPE_I || ty == ARGTYPE_UI || ty == ARGTYPE_Q) { - Register r = findRegFor(p, GpRegs); - MOVQSPR(stk_off, r); // movq [rsp+d8], r - if (ty == ARGTYPE_I) { - // sign extend int32 to int64 - NanoAssert(p->isI()); - MOVSXDR(r, r); - } else if (ty == ARGTYPE_UI) { - // zero extend uint32 to uint64 - NanoAssert(p->isI()); - MOVLR(r, r); - } else { - NanoAssert(ty == ARGTYPE_Q); - // Do nothing. - } - } else { - TODO(asm_stkarg_non_int); - } - } - - void Assembler::asm_q2i(LIns *ins) { - Register rr, ra; - beginOp1Regs(ins, GpRegs, rr, ra); - NanoAssert(IsGpReg(ra)); - // If ra==rr we do nothing. This is valid because we don't assume the - // upper 32-bits of a 64-bit GPR are zero when doing a 32-bit - // operation. More specifically, we widen 32-bit to 64-bit in three - // places, all of which explicitly sign- or zero-extend: asm_ui2uq(), - // asm_regarg() and asm_stkarg(). For the first this is required, for - // the latter two it's unclear if this is required, but it can't hurt. - if (ra != rr) - MOVLR(rr, ra); - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_ui2uq(LIns *ins) { - Register rr, ra; - beginOp1Regs(ins, GpRegs, rr, ra); - NanoAssert(IsGpReg(ra)); - if (ins->isop(LIR_ui2uq)) { - MOVLR(rr, ra); // 32bit mov zeros the upper 32bits of the target - } else { - NanoAssert(ins->isop(LIR_i2q)); - MOVSXDR(rr, ra); // sign extend 32->64 - } - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_dasq(LIns *ins) { - Register rr = prepareResultReg(ins, GpRegs); - Register ra = findRegFor(ins->oprnd1(), FpRegs); - asm_nongp_copy(rr, ra); - freeResourcesOf(ins); - } - - void Assembler::asm_qasd(LIns *ins) { - Register rr = prepareResultReg(ins, FpRegs); - Register ra = findRegFor(ins->oprnd1(), GpRegs); - asm_nongp_copy(rr, ra); - freeResourcesOf(ins); - } - - // The CVTSI2SD instruction only writes to the low 64bits of the target - // XMM register, which hinders register renaming and makes dependence - // chains longer. So we precede with XORPS to clear the target register. - - void Assembler::asm_i2d(LIns *ins) { - LIns *a = ins->oprnd1(); - NanoAssert(ins->isD() && a->isI()); - - Register rr = prepareResultReg(ins, FpRegs); - Register ra = findRegFor(a, GpRegs); - CVTSI2SD(rr, ra); // cvtsi2sd xmmr, b only writes xmm:0:64 - XORPS(rr); // xorps xmmr,xmmr to break dependency chains - freeResourcesOf(ins); - } - - void Assembler::asm_ui2d(LIns *ins) { - LIns *a = ins->oprnd1(); - NanoAssert(ins->isD() && a->isI()); - - Register rr = prepareResultReg(ins, FpRegs); - Register ra = findRegFor(a, GpRegs); - // Because oprnd1 is 32bit, it's ok to zero-extend it without worrying about clobbering. - CVTSQ2SD(rr, ra); // convert int64 to double - XORPS(rr); // xorps xmmr,xmmr to break dependency chains - MOVLR(ra, ra); // zero extend u32 to int64 - freeResourcesOf(ins); - } - - void Assembler::asm_d2i(LIns *ins) { - LIns *a = ins->oprnd1(); - NanoAssert(ins->isI() && a->isD()); - - Register rr = prepareResultReg(ins, GpRegs); - Register rb = findRegFor(a, FpRegs); - CVTTSD2SI(rr, rb); - freeResourcesOf(ins); - } - - void Assembler::asm_cmov(LIns *ins) { - LIns* cond = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - NanoAssert(cond->isCmp()); - NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) || - (ins->isop(LIR_cmovq) && iftrue->isQ() && iffalse->isQ()) || - (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD())); - - RegisterMask allow = ins->isD() ? FpRegs : GpRegs; - - Register rr = prepareResultReg(ins, allow); - - Register rf = findRegFor(iffalse, allow & ~rmask(rr)); - - if (ins->isop(LIR_cmovd)) { - // See Nativei386.cpp:asm_cmov() for an explanation of the subtleties here. - NIns* target = _nIns; - asm_nongp_copy(rr, rf); - asm_branch_helper(false, cond, target); - - // If 'iftrue' isn't in a register, it can be clobbered by 'ins'. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rr; - - if (rr != rt) - asm_nongp_copy(rr, rt); - - freeResourcesOf(ins); - if (!iftrue->isInReg()) { - NanoAssert(rt == rr); - findSpecificRegForUnallocated(iftrue, rr); - } - - asm_cmp(cond); - return; - } - - // If 'iftrue' isn't in a register, it can be clobbered by 'ins'. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rr; - - // WARNING: We cannot generate any code that affects the condition - // codes between the MRcc generation here and the asm_cmpi() call - // below. See asm_cmpi() for more details. - LOpcode condop = cond->opcode(); - if (ins->isop(LIR_cmovi)) { - switch (condop) { - case LIR_eqi: case LIR_eqq: CMOVNE( rr, rf); break; - case LIR_lti: case LIR_ltq: CMOVNL( rr, rf); break; - case LIR_gti: case LIR_gtq: CMOVNG( rr, rf); break; - case LIR_lei: case LIR_leq: CMOVNLE(rr, rf); break; - case LIR_gei: case LIR_geq: CMOVNGE(rr, rf); break; - case LIR_ltui: case LIR_ltuq: CMOVNB( rr, rf); break; - case LIR_gtui: case LIR_gtuq: CMOVNA( rr, rf); break; - case LIR_leui: case LIR_leuq: CMOVNBE(rr, rf); break; - case LIR_geui: case LIR_geuq: CMOVNAE(rr, rf); break; - default: NanoAssert(0); break; - } - } else { - NanoAssert(ins->isop(LIR_cmovq)); - switch (condop) { - case LIR_eqi: case LIR_eqq: CMOVQNE( rr, rf); break; - case LIR_lti: case LIR_ltq: CMOVQNL( rr, rf); break; - case LIR_gti: case LIR_gtq: CMOVQNG( rr, rf); break; - case LIR_lei: case LIR_leq: CMOVQNLE(rr, rf); break; - case LIR_gei: case LIR_geq: CMOVQNGE(rr, rf); break; - case LIR_ltui: case LIR_ltuq: CMOVQNB( rr, rf); break; - case LIR_gtui: case LIR_gtuq: CMOVQNA( rr, rf); break; - case LIR_leui: case LIR_leuq: CMOVQNBE(rr, rf); break; - case LIR_geui: case LIR_geuq: CMOVQNAE(rr, rf); break; - default: NanoAssert(0); break; - } - } - if (rr != rt) - MR(rr, rt); - - freeResourcesOf(ins); - if (!iftrue->isInReg()) { - NanoAssert(rt == rr); - findSpecificRegForUnallocated(iftrue, rr); - } - - asm_cmpi(cond); - } - - Branches Assembler::asm_branch(bool onFalse, LIns* cond, NIns* target) { - Branches branches = asm_branch_helper(onFalse, cond, target); - asm_cmp(cond); - return branches; - } - - Branches Assembler::asm_branch_helper(bool onFalse, LIns *cond, NIns *target) { - if (target && !isTargetWithinS32(target)) { - // A conditional jump beyond 32-bit range, so invert the - // branch/compare and emit an unconditional jump to the target: - // j(inverted) B1 - // jmp target - // B1: - NIns* shortTarget = _nIns; - JMP(target); - target = shortTarget; - onFalse = !onFalse; - } - return isCmpDOpcode(cond->opcode()) - ? asm_branchd_helper(onFalse, cond, target) - : asm_branchi_helper(onFalse, cond, target); - } - - Branches Assembler::asm_branchi_helper(bool onFalse, LIns *cond, NIns *target) { - // We must ensure there's room for the instruction before calculating - // the offset. And the offset determines the opcode (8bit or 32bit). - LOpcode condop = cond->opcode(); - if (target && isTargetWithinS8(target)) { - if (onFalse) { - switch (condop) { - case LIR_eqi: case LIR_eqq: JNE8( 8, target); break; - case LIR_lti: case LIR_ltq: JNL8( 8, target); break; - case LIR_gti: case LIR_gtq: JNG8( 8, target); break; - case LIR_lei: case LIR_leq: JNLE8(8, target); break; - case LIR_gei: case LIR_geq: JNGE8(8, target); break; - case LIR_ltui: case LIR_ltuq: JNB8( 8, target); break; - case LIR_gtui: case LIR_gtuq: JNA8( 8, target); break; - case LIR_leui: case LIR_leuq: JNBE8(8, target); break; - case LIR_geui: case LIR_geuq: JNAE8(8, target); break; - default: NanoAssert(0); break; - } - } else { - switch (condop) { - case LIR_eqi: case LIR_eqq: JE8( 8, target); break; - case LIR_lti: case LIR_ltq: JL8( 8, target); break; - case LIR_gti: case LIR_gtq: JG8( 8, target); break; - case LIR_lei: case LIR_leq: JLE8(8, target); break; - case LIR_gei: case LIR_geq: JGE8(8, target); break; - case LIR_ltui: case LIR_ltuq: JB8( 8, target); break; - case LIR_gtui: case LIR_gtuq: JA8( 8, target); break; - case LIR_leui: case LIR_leuq: JBE8(8, target); break; - case LIR_geui: case LIR_geuq: JAE8(8, target); break; - default: NanoAssert(0); break; - } - } - } else { - if (onFalse) { - switch (condop) { - case LIR_eqi: case LIR_eqq: JNE( 8, target); break; - case LIR_lti: case LIR_ltq: JNL( 8, target); break; - case LIR_gti: case LIR_gtq: JNG( 8, target); break; - case LIR_lei: case LIR_leq: JNLE(8, target); break; - case LIR_gei: case LIR_geq: JNGE(8, target); break; - case LIR_ltui: case LIR_ltuq: JNB( 8, target); break; - case LIR_gtui: case LIR_gtuq: JNA( 8, target); break; - case LIR_leui: case LIR_leuq: JNBE(8, target); break; - case LIR_geui: case LIR_geuq: JNAE(8, target); break; - default: NanoAssert(0); break; - } - } else { - switch (condop) { - case LIR_eqi: case LIR_eqq: JE( 8, target); break; - case LIR_lti: case LIR_ltq: JL( 8, target); break; - case LIR_gti: case LIR_gtq: JG( 8, target); break; - case LIR_lei: case LIR_leq: JLE(8, target); break; - case LIR_gei: case LIR_geq: JGE(8, target); break; - case LIR_ltui: case LIR_ltuq: JB( 8, target); break; - case LIR_gtui: case LIR_gtuq: JA( 8, target); break; - case LIR_leui: case LIR_leuq: JBE(8, target); break; - case LIR_geui: case LIR_geuq: JAE(8, target); break; - default: NanoAssert(0); break; - } - } - } - return Branches(_nIns); // address of instruction to patch - } - - NIns* Assembler::asm_branch_ov(LOpcode, NIns* target) { - // We must ensure there's room for the instr before calculating - // the offset. And the offset determines the opcode (8bit or 32bit). - if (target && isTargetWithinS8(target)) - JO8(8, target); - else - JO( 8, target); - return _nIns; - } - - void Assembler::asm_cmp(LIns *cond) { - isCmpDOpcode(cond->opcode()) ? asm_cmpd(cond) : asm_cmpi(cond); - } - - // WARNING: this function cannot generate code that will affect the - // condition codes prior to the generation of the test/cmp. See - // Nativei386.cpp:asm_cmpi() for details. - void Assembler::asm_cmpi(LIns *cond) { - LIns *b = cond->oprnd2(); - if (isImm32(b)) { - asm_cmpi_imm(cond); - return; - } - LIns *a = cond->oprnd1(); - Register ra, rb; - if (a != b) { - findRegFor2(GpRegs, a, ra, GpRegs, b, rb); - } else { - // optimize-me: this will produce a const result! - ra = rb = findRegFor(a, GpRegs); - } - - LOpcode condop = cond->opcode(); - if (isCmpQOpcode(condop)) { - CMPQR(ra, rb); - } else { - NanoAssert(isCmpIOpcode(condop)); - CMPLR(ra, rb); - } - } - - void Assembler::asm_cmpi_imm(LIns *cond) { - LOpcode condop = cond->opcode(); - LIns *a = cond->oprnd1(); - LIns *b = cond->oprnd2(); - Register ra = findRegFor(a, GpRegs); - int32_t imm = getImm32(b); - if (isCmpQOpcode(condop)) { - if (isS8(imm)) - CMPQR8(ra, imm); - else - CMPQRI(ra, imm); - } else { - NanoAssert(isCmpIOpcode(condop)); - if (isS8(imm)) - CMPLR8(ra, imm); - else - CMPLRI(ra, imm); - } - } - - // Compiling floating point branches. - // Discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=443886. - // - // fucom/p/pp: c3 c2 c0 jae ja jbe jb je jne - // ucomisd: Z P C !C !C&!Z C|Z C Z !Z - // -- -- -- -- ----- --- -- -- -- - // unordered 1 1 1 T T T - // greater > 0 0 0 T T T - // less < 0 0 1 T T T - // equal = 1 0 0 T T T - // - // Here are the cases, using conditionals: - // - // branch >= > <= < = - // ------ --- --- --- --- --- - // LIR_jt jae ja swap+jae swap+ja jp over je - // LIR_jf jb jbe swap+jb swap+jbe jne+jp - - Branches Assembler::asm_branchd_helper(bool onFalse, LIns *cond, NIns *target) { - LOpcode condop = cond->opcode(); - NIns *patch1 = NULL; - NIns *patch2 = NULL; - if (condop == LIR_eqd) { - if (onFalse) { - // branch if unordered or != - JP(16, target); // underrun of 12 needed, round up for overhang --> 16 - patch1 = _nIns; - JNE(0, target); // no underrun needed, previous was enough - patch2 = _nIns; - } else { - // jp skip (2byte) - // jeq target - // skip: ... - underrunProtect(16); // underrun of 7 needed but we write 2 instr --> 16 - NIns *skip = _nIns; - JE(0, target); // no underrun needed, previous was enough - patch1 = _nIns; - JP8(0, skip); // ditto - } - } - else { - // LIR_ltd and LIR_gtd are handled by the same case because - // asm_cmpd() converts LIR_ltd(a,b) to LIR_gtd(b,a). Likewise for - // LIR_led/LIR_ged. - switch (condop) { - case LIR_ltd: - case LIR_gtd: if (onFalse) JBE(8, target); else JA(8, target); break; - case LIR_led: - case LIR_ged: if (onFalse) JB(8, target); else JAE(8, target); break; - default: NanoAssert(0); break; - } - patch1 = _nIns; - } - return Branches(patch1, patch2); - } - - void Assembler::asm_condd(LIns *ins) { - LOpcode op = ins->opcode(); - if (op == LIR_eqd) { - // result = ZF & !PF, must do logic on flags - // r = al|bl|cl|dl, can only use rh without rex prefix - Register r = prepareResultReg(ins, 1<opcode(); - LIns* a = cond->oprnd1(); - LIns* b = cond->oprnd2(); - // First, we convert (a < b) into (b > a), and (a <= b) into (b >= a). - if (opcode == LIR_ltd) { - opcode = LIR_gtd; - LIns* t = a; a = b; b = t; - } else if (opcode == LIR_led) { - opcode = LIR_ged; - LIns* t = a; a = b; b = t; - } - Register ra, rb; - findRegFor2(FpRegs, a, ra, FpRegs, b, rb); - UCOMISD(ra, rb); - } - - // Return true if we can generate code for this instruction that neither - // sets CCs nor clobbers any input register. - // LEA is the only native instruction that fits those requirements. - bool canRematLEA(LIns* ins) - { - switch (ins->opcode()) { - case LIR_addi: - return ins->oprnd1()->isInRegMask(BaseRegs) && ins->oprnd2()->isImmI(); - case LIR_addq: { - LIns* rhs; - return ins->oprnd1()->isInRegMask(BaseRegs) && - (rhs = ins->oprnd2())->isImmQ() && - isS32(rhs->immQ()); - } - // Subtract and some left-shifts could be rematerialized using LEA, - // but it hasn't shown to help in real code yet. Noting them anyway: - // maybe sub? R = subl/q rL, const => leal/q R, [rL + -const] - // maybe lsh? R = lshl/q rL, 1/2/3 => leal/q R, [rL * 2/4/8] - default: - ; - } - return false; - } - - bool Assembler::canRemat(LIns* ins) { - return ins->isImmAny() || ins->isop(LIR_allocp) || canRematLEA(ins); - } - - // WARNING: the code generated by this function must not affect the - // condition codes. See asm_cmpi() for details. - void Assembler::asm_restore(LIns *ins, Register r) { - if (ins->isop(LIR_allocp)) { - int d = arDisp(ins); - LEAQRM(r, d, FP); - } - else if (ins->isImmI()) { - asm_immi(r, ins->immI(), /*canClobberCCs*/false); - } - else if (ins->isImmQ()) { - asm_immq(r, ins->immQ(), /*canClobberCCs*/false); - } - else if (ins->isImmD()) { - asm_immd(r, ins->immDasQ(), /*canClobberCCs*/false); - } - else if (canRematLEA(ins)) { - Register lhsReg = ins->oprnd1()->getReg(); - if (ins->isop(LIR_addq)) - LEAQRM(r, (int32_t)ins->oprnd2()->immQ(), lhsReg); - else // LIR_addi - LEALRM(r, ins->oprnd2()->immI(), lhsReg); - } - else { - int d = findMemFor(ins); - if (ins->isD()) { - NanoAssert(IsFpReg(r)); - MOVSDRM(r, d, FP); - } else if (ins->isQ()) { - NanoAssert(IsGpReg(r)); - MOVQRM(r, d, FP); - } else { - NanoAssert(ins->isI()); - MOVLRM(r, d, FP); - } - } - } - - void Assembler::asm_cond(LIns *ins) { - LOpcode op = ins->opcode(); - - // unlike x86-32, with a rex prefix we can use any GP register as an 8bit target - Register r = prepareResultReg(ins, GpRegs); - - // SETcc only sets low 8 bits, so extend - MOVZX8(r, r); - switch (op) { - default: - TODO(cond); - case LIR_eqq: - case LIR_eqi: SETE(r); break; - case LIR_ltq: - case LIR_lti: SETL(r); break; - case LIR_leq: - case LIR_lei: SETLE(r); break; - case LIR_gtq: - case LIR_gti: SETG(r); break; - case LIR_geq: - case LIR_gei: SETGE(r); break; - case LIR_ltuq: - case LIR_ltui: SETB(r); break; - case LIR_leuq: - case LIR_leui: SETBE(r); break; - case LIR_gtuq: - case LIR_gtui: SETA(r); break; - case LIR_geuq: - case LIR_geui: SETAE(r); break; - } - freeResourcesOf(ins); - - asm_cmpi(ins); - } - - void Assembler::asm_ret(LIns *ins) { - genEpilogue(); - - // Restore RSP from RBP, undoing SUB(RSP,amt) in the prologue - MR(RSP,FP); - - releaseRegisters(); - assignSavedRegs(); - LIns *value = ins->oprnd1(); - Register r = ins->isop(LIR_retd) ? XMM0 : RAX; - findSpecificRegFor(value, r); - } - - void Assembler::asm_nongp_copy(Register d, Register s) { - if (!IsFpReg(d) && IsFpReg(s)) { - // gpr <- xmm: use movq r/m64, xmm (66 REX.W 0F 7E /r) - MOVQRX(d, s); - } else if (IsFpReg(d) && IsFpReg(s)) { - // xmm <- xmm: use movaps. movsd r,r causes partial register stall - MOVAPSR(d, s); - } else { - NanoAssert(IsFpReg(d) && !IsFpReg(s)); - // xmm <- gpr: use movq xmm, r/m64 (66 REX.W 0F 6E /r) - MOVQXR(d, s); - } - } - - // Register setup for load ops. Pairs with endLoadRegs(). - void Assembler::beginLoadRegs(LIns *ins, RegisterMask allow, Register &rr, int32_t &dr, Register &rb) { - dr = ins->disp(); - LIns *base = ins->oprnd1(); - rb = getBaseReg(base, dr, BaseRegs); - rr = prepareResultReg(ins, allow & ~rmask(rb)); - } - - // Register clean-up for load ops. Pairs with beginLoadRegs(). - void Assembler::endLoadRegs(LIns* ins) { - freeResourcesOf(ins); - } - - void Assembler::asm_load64(LIns *ins) { - Register rr, rb; - int32_t dr; - switch (ins->opcode()) { - case LIR_ldq: - beginLoadRegs(ins, GpRegs, rr, dr, rb); - NanoAssert(IsGpReg(rr)); - MOVQRM(rr, dr, rb); // general 64bit load, 32bit const displacement - break; - case LIR_ldd: - beginLoadRegs(ins, FpRegs, rr, dr, rb); - NanoAssert(IsFpReg(rr)); - MOVSDRM(rr, dr, rb); // load 64bits into XMM - break; - case LIR_ldf2d: - beginLoadRegs(ins, FpRegs, rr, dr, rb); - NanoAssert(IsFpReg(rr)); - CVTSS2SD(rr, rr); - MOVSSRM(rr, dr, rb); - break; - default: - NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); - break; - } - endLoadRegs(ins); - } - - void Assembler::asm_load32(LIns *ins) { - NanoAssert(ins->isI()); - Register r, b; - int32_t d; - beginLoadRegs(ins, GpRegs, r, d, b); - LOpcode op = ins->opcode(); - switch (op) { - case LIR_lduc2ui: - MOVZX8M( r, d, b); - break; - case LIR_ldus2ui: - MOVZX16M(r, d, b); - break; - case LIR_ldi: - MOVLRM( r, d, b); - break; - case LIR_ldc2i: - MOVSX8M( r, d, b); - break; - case LIR_lds2i: - MOVSX16M( r, d, b); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - break; - } - endLoadRegs(ins); - } - - void Assembler::asm_store64(LOpcode op, LIns *value, int d, LIns *base) { - NanoAssert(value->isQorD()); - - switch (op) { - case LIR_stq: { - uint64_t c; - if (value->isImmQ() && (c = value->immQ(), isS32(c))) { - uint64_t c = value->immQ(); - Register rb = getBaseReg(base, d, BaseRegs); - // MOVQMI takes a 32-bit integer that gets signed extended to a 64-bit value. - MOVQMI(rb, d, int32_t(c)); - } else { - Register rr, rb; - getBaseReg2(GpRegs, value, rr, BaseRegs, base, rb, d); - MOVQMR(rr, d, rb); // gpr store - } - break; - } - case LIR_std: { - Register b = getBaseReg(base, d, BaseRegs); - Register r = findRegFor(value, FpRegs); - MOVSDMR(r, d, b); // xmm store - break; - } - case LIR_std2f: { - Register b = getBaseReg(base, d, BaseRegs); - Register r = findRegFor(value, FpRegs); - Register t = registerAllocTmp(FpRegs & ~rmask(r)); - - MOVSSMR(t, d, b); // store - CVTSD2SS(t, r); // cvt to single-precision - XORPS(t); // break dependency chains - break; - } - default: - NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); - break; - } - } - - void Assembler::asm_store32(LOpcode op, LIns *value, int d, LIns *base) { - if (value->isImmI()) { - Register rb = getBaseReg(base, d, BaseRegs); - int c = value->immI(); - switch (op) { - case LIR_sti2c: MOVBMI(rb, d, c); break; - case LIR_sti2s: MOVSMI(rb, d, c); break; - case LIR_sti: MOVLMI(rb, d, c); break; - default: NanoAssert(0); break; - } - - } else { - // Quirk of x86-64: reg cannot appear to be ah/bh/ch/dh for - // single-byte stores with REX prefix. - const RegisterMask SrcRegs = (op == LIR_sti2c) ? SingleByteStoreRegs : GpRegs; - - NanoAssert(value->isI()); - Register b = getBaseReg(base, d, BaseRegs); - Register r = findRegFor(value, SrcRegs & ~rmask(b)); - - switch (op) { - case LIR_sti2c: MOVBMR(r, d, b); break; - case LIR_sti2s: MOVSMR(r, d, b); break; - case LIR_sti: MOVLMR(r, d, b); break; - default: NanoAssert(0); break; - } - } - } - - void Assembler::asm_immi(LIns *ins) { - Register rr = prepareResultReg(ins, GpRegs); - asm_immi(rr, ins->immI(), /*canClobberCCs*/true); - freeResourcesOf(ins); - } - - void Assembler::asm_immq(LIns *ins) { - Register rr = prepareResultReg(ins, GpRegs); - asm_immq(rr, ins->immQ(), /*canClobberCCs*/true); - freeResourcesOf(ins); - } - - void Assembler::asm_immd(LIns *ins) { - Register r = prepareResultReg(ins, FpRegs); - asm_immd(r, ins->immDasQ(), /*canClobberCCs*/true); - freeResourcesOf(ins); - } - - void Assembler::asm_immi(Register r, int32_t v, bool canClobberCCs) { - NanoAssert(IsGpReg(r)); - if (v == 0 && canClobberCCs) { - XORRR(r, r); - } else { - MOVI(r, v); - } - } - - void Assembler::asm_immq(Register r, uint64_t v, bool canClobberCCs) { - NanoAssert(IsGpReg(r)); - if (isU32(v)) { - asm_immi(r, int32_t(v), canClobberCCs); - } else if (isS32(v)) { - // safe for sign-extension 32->64 - MOVQI32(r, int32_t(v)); - } else if (isTargetWithinS32((NIns*)v)) { - // value is with +/- 2GB from RIP, can use LEA with RIP-relative disp32 - int32_t d = int32_t(int64_t(v)-int64_t(_nIns)); - LEARIP(r, d); - } else { - MOVQI(r, v); - } - } - - void Assembler::asm_immd(Register r, uint64_t v, bool canClobberCCs) { - NanoAssert(IsFpReg(r)); - if (v == 0 && canClobberCCs) { - XORPS(r); - } else { - // There's no general way to load an immediate into an XMM reg. - // For non-zero floats the best thing is to put the equivalent - // 64-bit integer into a scratch GpReg and then move it into the - // appropriate FpReg. - Register rt = registerAllocTmp(GpRegs); - MOVQXR(r, rt); - asm_immq(rt, v, canClobberCCs); - } - } - - void Assembler::asm_param(LIns *ins) { - uint32_t a = ins->paramArg(); - uint32_t kind = ins->paramKind(); - if (kind == 0) { - // Ordinary param. First four or six args always in registers for x86_64 ABI. - if (a < (uint32_t)NumArgRegs) { - // incoming arg in register - prepareResultReg(ins, rmask(argRegs[a])); - // No code to generate. - } else { - // todo: support stack based args, arg 0 is at [FP+off] where off - // is the # of regs to be pushed in genProlog() - TODO(asm_param_stk); - } - } - else { - // Saved param. - prepareResultReg(ins, rmask(savedRegs[a])); - // No code to generate. - } - freeResourcesOf(ins); - } - - // Register setup for 2-address style unary ops of the form R = (op) R. - // Pairs with endOpRegs(). - void Assembler::beginOp1Regs(LIns* ins, RegisterMask allow, Register &rr, Register &ra) { - LIns* a = ins->oprnd1(); - - rr = prepareResultReg(ins, allow); - - // If 'a' isn't in a register, it can be clobbered by 'ins'. - ra = a->isInReg() ? a->getReg() : rr; - NanoAssert(rmask(ra) & allow); - } - - // Register setup for 2-address style binary ops of the form R = R (op) B. - // Pairs with endOpRegs(). - void Assembler::beginOp2Regs(LIns *ins, RegisterMask allow, Register &rr, Register &ra, - Register &rb) { - LIns *a = ins->oprnd1(); - LIns *b = ins->oprnd2(); - if (a != b) { - rb = findRegFor(b, allow); - allow &= ~rmask(rb); - } - rr = prepareResultReg(ins, allow); - - // If 'a' isn't in a register, it can be clobbered by 'ins'. - ra = a->isInReg() ? a->getReg() : rr; - NanoAssert(rmask(ra) & allow); - - if (a == b) { - rb = ra; - } - } - - // Register clean-up for 2-address style unary ops of the form R = (op) R. - // Pairs with beginOp1Regs() and beginOp2Regs(). - void Assembler::endOpRegs(LIns* ins, Register rr, Register ra) { - (void) rr; // quell warnings when NanoAssert is compiled out. - - LIns* a = ins->oprnd1(); - - // We're finished with 'ins'. - NanoAssert(ins->getReg() == rr); - freeResourcesOf(ins); - - // If 'a' isn't in a register yet, that means it's clobbered by 'ins'. - if (!a->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(a, ra); - } - } - - static const AVMPLUS_ALIGN16(int64_t) negateMask[] = {0x8000000000000000LL,0}; - - void Assembler::asm_fneg(LIns *ins) { - Register rr, ra; - beginOp1Regs(ins, FpRegs, rr, ra); - if (isS32((uintptr_t)negateMask)) { - // builtin code is in bottom or top 2GB addr space, use absolute addressing - XORPSA(rr, (int32_t)(uintptr_t)negateMask); - } else if (isTargetWithinS32((NIns*)negateMask)) { - // jit code is within +/-2GB of builtin code, use rip-relative - XORPSM(rr, (NIns*)negateMask); - } else { - // This is just hideous - can't use RIP-relative load, can't use - // absolute-address load, and cant move imm64 const to XMM. - // Solution: move negateMask into a temp GP register, then copy to - // a temp XMM register. - // Nb: we don't want any F64 values to end up in a GpReg, nor any - // I64 values to end up in an FpReg. - // - // # 'gt' and 'ga' are temporary GpRegs. - // # ins->oprnd1() is in 'rr' (FpRegs) - // mov gt, 0x8000000000000000 - // mov rt, gt - // xorps rr, rt - Register rt = registerAllocTmp(FpRegs & ~(rmask(ra)|rmask(rr))); - Register gt = registerAllocTmp(GpRegs); - XORPS(rr, rt); - MOVQXR(rt, gt); - asm_immq(gt, negateMask[0], /*canClobberCCs*/true); - } - if (ra != rr) - asm_nongp_copy(rr,ra); - endOpRegs(ins, rr, ra); - } - - void Assembler::asm_spill(Register rr, int d, bool quad) { - NanoAssert(d); - if (!IsFpReg(rr)) { - if (quad) - MOVQMR(rr, d, FP); - else - MOVLMR(rr, d, FP); - } else { - // store 64bits from XMM to memory - NanoAssert(quad); - MOVSDMR(rr, d, FP); - } - } - - NIns* Assembler::genPrologue() { - // activation frame is 4 bytes per entry even on 64bit machines - uint32_t stackNeeded = max_stk_used + _activation.stackSlotsNeeded() * 4; - - uint32_t stackPushed = - sizeof(void*) + // returnaddr - sizeof(void*); // ebp - uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); - uint32_t amt = aligned - stackPushed; - -#ifdef _WIN64 - // Windows uses a single guard page for extending the stack, so - // new stack pages must be first touched in stack-growth order. - // We touch each whole page that will be allocated to the frame - // (following the saved FP) to cause the OS to commit the page if - // necessary. Since we don't calculate page boundaries, but just - // probe at intervals of the pagesize, it is possible that the - // last page of the frame will be touched unnecessarily. Note that - // we must generate the probes in the reverse order of their execution. - // We require that the page size be a power of 2. - uint32_t pageSize = uint32_t(VMPI_getVMPageSize()); - NanoAssert((pageSize & (pageSize-1)) == 0); - uint32_t pageRounded = amt & ~(pageSize-1); - for (int32_t d = pageRounded; d > 0; d -= pageSize) { - MOVLMI(RBP, -d, 0); - } -#endif - - // Reserve stackNeeded bytes, padded - // to preserve NJ_ALIGN_STACK-byte alignment. - if (amt) { - if (isS8(amt)) - SUBQR8(RSP, amt); - else - SUBQRI(RSP, amt); - } - - verbose_only( asm_output("[patch entry]"); ) - NIns *patchEntry = _nIns; - MR(FP, RSP); // Establish our own FP. - PUSHR(FP); // Save caller's FP. - - return patchEntry; - } - - NIns* Assembler::genEpilogue() { - // pop rbp - // ret - RET(); - POPR(RBP); - return _nIns; - } - - void Assembler::nRegisterResetAll(RegAlloc &a) { - // add scratch registers to our free list for the allocator - a.clear(); -#ifdef _WIN64 - a.free = 0x001fffcf; // rax-rbx, rsi, rdi, r8-r15, xmm0-xmm5 -#else - a.free = 0xffffffff & ~(1<record()->exit; - Fragment *frag = exit->target; - GuardRecord *lr = 0; - bool destKnown = (frag && frag->fragEntry); - // Generate jump to epilog and initialize lr. - // If the guard already exists, use a simple jump. - if (destKnown) { - JMP(frag->fragEntry); - lr = 0; - } else { // target doesn't exist. Use 0 jump offset and patch later - if (!_epilogue) - _epilogue = genEpilogue(); - lr = guard->record(); - JMPl(_epilogue); - lr->jmp = _nIns; - } - - // profiling for the exit - verbose_only( - if (_logc->lcbits & LC_FragProfile) { - asm_inc_m32( &guard->record()->profCount ); - } - ) - - MR(RSP, RBP); - - // return value is GuardRecord* - asm_immq(RAX, uintptr_t(lr), /*canClobberCCs*/true); - } - - void Assembler::nInit() { - nHints[LIR_calli] = rmask(retRegs[0]); - nHints[LIR_calld] = rmask(XMM0); - nHints[LIR_paramp] = PREFER_SPECIAL; - } - - void Assembler::nBeginAssembly() { - max_stk_used = 0; - } - - // This should only be called from within emit() et al. - void Assembler::underrunProtect(ptrdiff_t bytes) { - NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - NIns *pc = _nIns; - NIns *top = codeStart; // this may be in a normal code chunk or an exit code chunk - - #if PEDANTIC - // pedanticTop is based on the last call to underrunProtect; any time we call - // underrunProtect and would use more than what's already protected, then insert - // a page break jump. Sometimes, this will be to a new page, usually it's just - // the next instruction - - NanoAssert(pedanticTop >= top); - if (pc - bytes < pedanticTop) { - // no page break required, but insert a far branch anyway just to be difficult - const int br_size = 8; // opcode + 32bit addr - if (pc - bytes - br_size < top) { - // really do need a page break - verbose_only(if (_logc->lcbits & LC_Native) outputf("newpage %p:", pc);) - // This may be in a normal code chunk or an exit code chunk. - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - } - // now emit the jump, but make sure we won't need another page break. - // we're pedantic, but not *that* pedantic. - pedanticTop = _nIns - br_size; - JMP(pc); - pedanticTop = _nIns - bytes; - } - #else - if (pc - bytes < top) { - verbose_only(if (_logc->lcbits & LC_Native) outputf("newpage %p:", pc);) - // This may be in a normal code chunk or an exit code chunk. - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - // This jump will call underrunProtect again, but since we're on a new - // page, nothing will happen. - JMP(pc); - } - #endif - } - - RegisterMask Assembler::nHint(LIns* ins) - { - NanoAssert(ins->isop(LIR_paramp)); - RegisterMask prefer = 0; - uint8_t arg = ins->paramArg(); - if (ins->paramKind() == 0) { - if (arg < maxArgRegs) - prefer = rmask(argRegs[arg]); - } else { - if (arg < NumSavedRegs) - prefer = rmask(savedRegs[arg]); - } - return prefer; - } - - void Assembler::nativePageSetup() { - NanoAssert(!_inExit); - if (!_nIns) { - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - IF_PEDANTIC( pedanticTop = _nIns; ) - } - } - - void Assembler::nativePageReset() - {} - - // Increment the 32-bit profiling counter at pCtr, without - // changing any registers. - verbose_only( - void Assembler::asm_inc_m32(uint32_t* pCtr) - { - // Not as simple as on x86. We need to temporarily free up a - // register into which to generate the address, so just push - // it on the stack. This assumes that the scratch area at - // -8(%rsp) .. -1(%esp) isn't being used for anything else - // at this point. - emitr(X64_popr, RAX); // popq %rax - emit(X64_inclmRAX); // incl (%rax) - asm_immq(RAX, (uint64_t)pCtr, /*canClobberCCs*/true); // movabsq $pCtr, %rax - emitr(X64_pushr, RAX); // pushq %rax - } - ) - - void Assembler::asm_jtbl(LIns* ins, NIns** table) - { - // exclude R12 because ESP and R12 cannot be used as an index - // (index=100 in SIB means "none") - Register indexreg = findRegFor(ins->oprnd1(), GpRegs & ~rmask(R12)); - if (isS32((intptr_t)table)) { - // table is in low 2GB or high 2GB, can use absolute addressing - // jmpq [indexreg*8 + table] - JMPX(indexreg, table); - } else { - // don't use R13 for base because we want to use mod=00, i.e. [index*8+base + 0] - Register tablereg = registerAllocTmp(GpRegs & ~(rmask(indexreg)|rmask(R13))); - // jmp [indexreg*8 + tablereg] - JMPXB(indexreg, tablereg); - // tablereg <- #table - asm_immq(tablereg, (uint64_t)table, /*canClobberCCs*/true); - } - } - - void Assembler::swapCodeChunks() { - if (!_nExitIns) { - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - } - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - void Assembler::asm_insert_random_nop() { - NanoAssert(0); // not supported - } - -} // namespace nanojit - -#endif // FEATURE_NANOJIT && NANOJIT_X64 diff --git a/deps/mozjs/js/src/nanojit/NativeX64.h b/deps/mozjs/js/src/nanojit/NativeX64.h deleted file mode 100644 index fcf4205f7ca..00000000000 --- a/deps/mozjs/js/src/nanojit/NativeX64.h +++ /dev/null @@ -1,630 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __nanojit_NativeX64__ -#define __nanojit_NativeX64__ - -#include "NativeCommon.h" - -#ifndef NANOJIT_64BIT -#error "NANOJIT_64BIT must be defined for X64 backend" -#endif - -#ifdef PERFM -#define DOPROF -#include "../vprof/vprof.h" -#define count_instr() _nvprof("x64",1) -#define count_prolog() _nvprof("x64-prolog",1); count_instr(); -#define count_imt() _nvprof("x64-imt",1) count_instr() -#else -#define count_instr() -#define count_prolog() -#define count_imt() -#endif - -namespace nanojit -{ -#define NJ_MAX_STACK_ENTRY 4096 -#define NJ_ALIGN_STACK 16 - -#define NJ_JTBL_SUPPORTED 1 -#define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 -#define NJ_F2I_SUPPORTED 1 -#define NJ_SOFTFLOAT_SUPPORTED 0 -#define NJ_DIVI_SUPPORTED 1 - - static const Register RAX = { 0 }; // 1st int return, # of sse varargs - static const Register RCX = { 1 }; // 4th int arg - static const Register RDX = { 2 }; // 3rd int arg 2nd return - static const Register RBX = { 3 }; // saved - static const Register RSP = { 4 }; // stack ptr - static const Register RBP = { 5 }; // frame ptr, saved, sib reqd - static const Register RSI = { 6 }; // 2nd int arg - static const Register RDI = { 7 }; // 1st int arg - static const Register R8 = { 8 }; // 5th int arg - static const Register R9 = { 9 }; // 6th int arg - static const Register R10 = { 10 }; // scratch - static const Register R11 = { 11 }; // scratch - static const Register R12 = { 12 }; // saved - static const Register R13 = { 13 }; // saved, sib reqd like rbp - static const Register R14 = { 14 }; // saved - static const Register R15 = { 15 }; // saved - - static const Register XMM0 = { 16 }; // 1st double arg, return - static const Register XMM1 = { 17 }; // 2nd double arg, return - static const Register XMM2 = { 18 }; // 3rd double arg - static const Register XMM3 = { 19 }; // 4th double arg - static const Register XMM4 = { 20 }; // 5th double arg - static const Register XMM5 = { 21 }; // 6th double arg - static const Register XMM6 = { 22 }; // 7th double arg - static const Register XMM7 = { 23 }; // 8th double arg - static const Register XMM8 = { 24 }; // scratch - static const Register XMM9 = { 25 }; // scratch - static const Register XMM10 = { 26 }; // scratch - static const Register XMM11 = { 27 }; // scratch - static const Register XMM12 = { 28 }; // scratch - static const Register XMM13 = { 29 }; // scratch - static const Register XMM14 = { 30 }; // scratch - static const Register XMM15 = { 31 }; // scratch - - static const Register FP = RBP; - static const Register RZero = { 0 }; // useful in a few places in codegen - - static const uint32_t FirstRegNum = 0; - static const uint32_t LastRegNum = 31; - - static const Register deprecated_UnknownReg = { 32 }; // XXX: remove eventually, see bug 538924 - static const Register UnspecifiedReg = { 32 }; - -/* - * Micro-templating variable-length opcodes, idea first - * describe by Mike Pall of Luajit. - * - * X86-64 opcode encodings: LSB encodes the length of the - * opcode in bytes, remaining bytes are encoded as 1-7 bytes - * in a single uint64_t value. The value is written as a single - * store into the code stream, and the code pointer is decremented - * by the length. each successive instruction partially overlaps - * the previous one. - * - * emit methods below are able to encode mod/rm, sib, rex, and - * register and small immediate values into these opcode values - * without much branchy code. - * - * these opcodes encapsulate all the const parts of the instruction. - * for example, the alu-immediate opcodes (add, sub, etc) encode - * part of their opcode in the R field of the mod/rm byte; this - * hardcoded value is in the constant below, and the R argument - * to emitrr() is 0. In a few cases, a whole instruction is encoded - * this way (eg callrax). - * - * when a disp32, immI, or imm64 suffix can't fit in an 8-byte - * opcode, then it is written into the code separately and not counted - * in the opcode length. - */ - - enum X64Opcode -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum - : uint64_t -#endif - { - // 64bit opcode constants - // msb lsb len - X64_addqrr = 0xC003480000000003LL, // 64bit add r += b - X64_addqri = 0xC081480000000003LL, // 64bit add r += int64(immI) - X64_addqr8 = 0x00C0834800000004LL, // 64bit add r += int64(imm8) - X64_andqri = 0xE081480000000003LL, // 64bit and r &= int64(immI) - X64_andqr8 = 0x00E0834800000004LL, // 64bit and r &= int64(imm8) - X64_orqri = 0xC881480000000003LL, // 64bit or r |= int64(immI) - X64_orqr8 = 0x00C8834800000004LL, // 64bit or r |= int64(imm8) - X64_xorqri = 0xF081480000000003LL, // 64bit xor r ^= int64(immI) - X64_xorqr8 = 0x00F0834800000004LL, // 64bit xor r ^= int64(imm8) - X64_addlri = 0xC081400000000003LL, // 32bit add r += immI - X64_addlr8 = 0x00C0834000000004LL, // 32bit add r += imm8 - X64_andlri = 0xE081400000000003LL, // 32bit and r &= immI - X64_andlr8 = 0x00E0834000000004LL, // 32bit and r &= imm8 - X64_orlri = 0xC881400000000003LL, // 32bit or r |= immI - X64_orlr8 = 0x00C8834000000004LL, // 32bit or r |= imm8 - X64_sublri = 0xE881400000000003LL, // 32bit sub r -= immI - X64_sublr8 = 0x00E8834000000004LL, // 32bit sub r -= imm8 - X64_xorlri = 0xF081400000000003LL, // 32bit xor r ^= immI - X64_xorlr8 = 0x00F0834000000004LL, // 32bit xor r ^= imm8 - X64_addrr = 0xC003400000000003LL, // 32bit add r += b - X64_andqrr = 0xC023480000000003LL, // 64bit and r &= b - X64_andrr = 0xC023400000000003LL, // 32bit and r &= b - X64_call = 0x00000000E8000005LL, // near call - X64_callrax = 0xD0FF000000000002LL, // indirect call to addr in rax (no REX) - X64_cmovqno = 0xC0410F4800000004LL, // 64bit conditional mov if (no overflow) r = b - X64_cmovqnae= 0xC0420F4800000004LL, // 64bit conditional mov if (uint <) r = b - X64_cmovqnb = 0xC0430F4800000004LL, // 64bit conditional mov if (uint >=) r = b - X64_cmovqne = 0xC0450F4800000004LL, // 64bit conditional mov if (c) r = b - X64_cmovqna = 0xC0460F4800000004LL, // 64bit conditional mov if (uint <=) r = b - X64_cmovqnbe= 0xC0470F4800000004LL, // 64bit conditional mov if (uint >) r = b - X64_cmovqnge= 0xC04C0F4800000004LL, // 64bit conditional mov if (int <) r = b - X64_cmovqnl = 0xC04D0F4800000004LL, // 64bit conditional mov if (int >=) r = b - X64_cmovqng = 0xC04E0F4800000004LL, // 64bit conditional mov if (int <=) r = b - X64_cmovqnle= 0xC04F0F4800000004LL, // 64bit conditional mov if (int >) r = b - X64_cmovno = 0xC0410F4000000004LL, // 32bit conditional mov if (no overflow) r = b - X64_cmovnae = 0xC0420F4000000004LL, // 32bit conditional mov if (uint <) r = b - X64_cmovnb = 0xC0430F4000000004LL, // 32bit conditional mov if (uint >=) r = b - X64_cmovne = 0xC0450F4000000004LL, // 32bit conditional mov if (c) r = b - X64_cmovna = 0xC0460F4000000004LL, // 32bit conditional mov if (uint <=) r = b - X64_cmovnbe = 0xC0470F4000000004LL, // 32bit conditional mov if (uint >) r = b - X64_cmovnge = 0xC04C0F4000000004LL, // 32bit conditional mov if (int <) r = b - X64_cmovnl = 0xC04D0F4000000004LL, // 32bit conditional mov if (int >=) r = b - X64_cmovng = 0xC04E0F4000000004LL, // 32bit conditional mov if (int <=) r = b - X64_cmovnle = 0xC04F0F4000000004LL, // 32bit conditional mov if (int >) r = b - X64_cmplr = 0xC03B400000000003LL, // 32bit compare r,b - X64_cmpqr = 0xC03B480000000003LL, // 64bit compare r,b - X64_cmplri = 0xF881400000000003LL, // 32bit compare r,immI - X64_cmpqri = 0xF881480000000003LL, // 64bit compare r,int64(immI) - X64_cmplr8 = 0x00F8834000000004LL, // 32bit compare r,imm8 - X64_cmpqr8 = 0x00F8834800000004LL, // 64bit compare r,int64(imm8) - X64_cvtsi2sd= 0xC02A0F40F2000005LL, // convert int32 to double r = (double) b - X64_cvtsq2sd= 0xC02A0F48F2000005LL, // convert int64 to double r = (double) b - X64_cvtss2sd= 0xC05A0F40F3000005LL, // convert float to double r = (double) b - X64_cvtsd2ss= 0xC05A0F40F2000005LL, // convert double to float r = (float) b - X64_cvtsd2si= 0xC02D0F40F2000005LL, // convert double to int32 with rounding r = (int32) b - X64_cvttsd2si=0xC02C0F40F2000005LL, // convert double to int32 r = (int32) b - X64_divsd = 0xC05E0F40F2000005LL, // divide scalar double r /= b - X64_mulsd = 0xC0590F40F2000005LL, // multiply scalar double r *= b - X64_addsd = 0xC0580F40F2000005LL, // add scalar double r += b - X64_idiv = 0xF8F7400000000003LL, // 32bit signed div (rax = rdx:rax/r, rdx=rdx:rax%r) - X64_imul = 0xC0AF0F4000000004LL, // 32bit signed mul r *= b - X64_imuli = 0xC069400000000003LL, // 32bit signed mul r = b * immI - X64_imul8 = 0x00C06B4000000004LL, // 32bit signed mul r = b * imm8 - X64_jmpi = 0x0000000025FF0006LL, // jump *0(rip) - X64_jmp = 0x00000000E9000005LL, // jump near rel32 - X64_jmp8 = 0x00EB000000000002LL, // jump near rel8 - X64_jo = 0x00000000800F0006LL, // jump near if overflow - X64_jb = 0x00000000820F0006LL, // jump near if below (uint <) - X64_jae = 0x00000000830F0006LL, // jump near if above or equal (uint >=) - X64_ja = 0x00000000870F0006LL, // jump near if above (uint >) - X64_jbe = 0x00000000860F0006LL, // jump near if below or equal (uint <=) - X64_je = 0x00000000840F0006LL, // near jump if equal - X64_jl = 0x000000008C0F0006LL, // jump near if less (int <) - X64_jge = 0x000000008D0F0006LL, // jump near if greater or equal (int >=) - X64_jg = 0x000000008F0F0006LL, // jump near if greater (int >) - X64_jle = 0x000000008E0F0006LL, // jump near if less or equal (int <=) - X64_jp = 0x000000008A0F0006LL, // jump near if parity (PF == 1) - X64_jneg = 0x0000000001000000LL, // xor with this mask to negate the condition - X64_jo8 = 0x0070000000000002LL, // jump near if overflow - X64_jb8 = 0x0072000000000002LL, // jump near if below (uint <) - X64_jae8 = 0x0073000000000002LL, // jump near if above or equal (uint >=) - X64_ja8 = 0x0077000000000002LL, // jump near if above (uint >) - X64_jbe8 = 0x0076000000000002LL, // jump near if below or equal (uint <=) - X64_je8 = 0x0074000000000002LL, // near jump if equal - X64_jne8 = 0x0075000000000002LL, // jump near if not equal - X64_jl8 = 0x007C000000000002LL, // jump near if less (int <) - X64_jge8 = 0x007D000000000002LL, // jump near if greater or equal (int >=) - X64_jg8 = 0x007F000000000002LL, // jump near if greater (int >) - X64_jle8 = 0x007E000000000002LL, // jump near if less or equal (int <=) - X64_jp8 = 0x007A000000000002LL, // jump near if parity (PF == 1) - X64_jnp8 = 0x007B000000000002LL, // jump near if not parity (PF == 0) - X64_jneg8 = 0x0001000000000000LL, // xor with this mask to negate the condition - X64_leaqrm = 0x00000000808D4807LL, // 64bit load effective addr reg <- disp32+base - X64_lealrm = 0x00000000808D4007LL, // 32bit load effective addr reg <- disp32+base - X64_learip = 0x00000000058D4807LL, // 64bit RIP-relative lea. reg <- disp32+rip (modrm = 00rrr101 = 05) - X64_movlr = 0xC08B400000000003LL, // 32bit mov r <- b - X64_movbmr = 0x0000000080884007LL, // 8bit store r -> [b+d32] - X64_movsmr = 0x8089406600000004LL, // 16bit store r -> [b+d32] - X64_movlmr = 0x0000000080894007LL, // 32bit store r -> [b+d32] - X64_movlrm = 0x00000000808B4007LL, // 32bit load r <- [b+d32] - X64_movqmr = 0x0000000080894807LL, // 64bit store gpr -> [b+d32] - X64_movqspr = 0x0024448948000005LL, // 64bit store gpr -> [rsp+d32] (sib required) - X64_movqr = 0xC08B480000000003LL, // 64bit mov r <- b - X64_movqi = 0xB848000000000002LL, // 64bit mov r <- imm64 - X64_movi = 0xB840000000000002LL, // 32bit mov r <- immI - X64_movqi32 = 0xC0C7480000000003LL, // 64bit mov r <- int64(immI) - X64_movapsr = 0xC0280F4000000004LL, // 128bit mov xmm <- xmm - X64_movqrx = 0xC07E0F4866000005LL, // 64bit mov b <- xmm-r (reverses the usual r/b order) - X64_movqxr = 0xC06E0F4866000005LL, // 64bit mov b -> xmm-r - X64_movqrm = 0x00000000808B4807LL, // 64bit load r <- [b+d32] - X64_movsdrr = 0xC0100F40F2000005LL, // 64bit mov xmm-r <- xmm-b (upper 64bits unchanged) - X64_movsdrm = 0x80100F40F2000005LL, // 64bit load xmm-r <- [b+d32] (upper 64 cleared) - X64_movsdmr = 0x80110F40F2000005LL, // 64bit store xmm-r -> [b+d32] - X64_movssrm = 0x80100F40F3000005LL, // 32bit load xmm-r <- [b+d32] (upper 96 cleared) - X64_movssmr = 0x80110F40F3000005LL, // 32bit store xmm-r -> [b+d32] - X64_movsxdr = 0xC063480000000003LL, // sign extend i32 to i64 r = (int64)(int32) b - X64_movzx8 = 0xC0B60F4000000004LL, // zero extend i8 to i64 r = (uint64)(uint8) b - X64_movzx8m = 0x80B60F4000000004LL, // zero extend i8 load to i32 r <- [b+d32] - X64_movzx16m= 0x80B70F4000000004LL, // zero extend i16 load to i32 r <- [b+d32] - X64_movsx8m = 0x80BE0F4000000004LL, // sign extend i8 load to i32 r <- [b+d32] - X64_movsx16m= 0x80BF0F4000000004LL, // sign extend i16 load to i32 r <- [b+d32] - X64_neg = 0xD8F7400000000003LL, // 32bit two's compliment b = -b - X64_nop1 = 0x9000000000000001LL, // one byte NOP - X64_nop2 = 0x9066000000000002LL, // two byte NOP - X64_nop3 = 0x001F0F0000000003LL, // three byte NOP - X64_nop4 = 0x00401F0F00000004LL, // four byte NOP - X64_nop5 = 0x0000441F0F000005LL, // five byte NOP - X64_nop6 = 0x0000441F0F660006LL, // six byte NOP - X64_nop7 = 0x00000000801F0F07LL, // seven byte NOP - X64_not = 0xD0F7400000000003LL, // 32bit ones compliment b = ~b - X64_orlrr = 0xC00B400000000003LL, // 32bit or r |= b - X64_orqrr = 0xC00B480000000003LL, // 64bit or r |= b - X64_popr = 0x5840000000000002LL, // 64bit pop r <- [rsp++] - X64_pushr = 0x5040000000000002LL, // 64bit push r -> [--rsp] - X64_pxor = 0xC0EF0F4066000005LL, // 128bit xor xmm-r ^= xmm-b - X64_ret = 0xC300000000000001LL, // near return from called procedure - X64_sete = 0xC0940F4000000004LL, // set byte if equal (ZF == 1) - X64_seto = 0xC0900F4000000004LL, // set byte if overflow (OF == 1) - X64_setc = 0xC0920F4000000004LL, // set byte if carry (CF == 1) - X64_setl = 0xC09C0F4000000004LL, // set byte if less (int <) (SF != OF) - X64_setle = 0xC09E0F4000000004LL, // set byte if less or equal (int <=) (ZF == 1 || SF != OF) - X64_setg = 0xC09F0F4000000004LL, // set byte if greater (int >) (ZF == 0 && SF == OF) - X64_setge = 0xC09D0F4000000004LL, // set byte if greater or equal (int >=) (SF == OF) - X64_seta = 0xC0970F4000000004LL, // set byte if above (uint >) (CF == 0 && ZF == 0) - X64_setae = 0xC0930F4000000004LL, // set byte if above or equal (uint >=) (CF == 0) - X64_setb = 0xC0920F4000000004LL, // set byte if below (uint <) (CF == 1) - X64_setbe = 0xC0960F4000000004LL, // set byte if below or equal (uint <=) (ZF == 1 || CF == 1) - X64_subsd = 0xC05C0F40F2000005LL, // subtract scalar double r -= b - X64_shl = 0xE0D3400000000003LL, // 32bit left shift r <<= rcx - X64_shlq = 0xE0D3480000000003LL, // 64bit left shift r <<= rcx - X64_shr = 0xE8D3400000000003LL, // 32bit uint right shift r >>= rcx - X64_shrq = 0xE8D3480000000003LL, // 64bit uint right shift r >>= rcx - X64_sar = 0xF8D3400000000003LL, // 32bit int right shift r >>= rcx - X64_sarq = 0xF8D3480000000003LL, // 64bit int right shift r >>= rcx - X64_shli = 0x00E0C14000000004LL, // 32bit left shift r <<= imm8 - X64_shlqi = 0x00E0C14800000004LL, // 64bit left shift r <<= imm8 - X64_sari = 0x00F8C14000000004LL, // 32bit int right shift r >>= imm8 - X64_sarqi = 0x00F8C14800000004LL, // 64bit int right shift r >>= imm8 - X64_shri = 0x00E8C14000000004LL, // 32bit uint right shift r >>= imm8 - X64_shrqi = 0x00E8C14800000004LL, // 64bit uint right shift r >>= imm8 - X64_subqrr = 0xC02B480000000003LL, // 64bit sub r -= b - X64_subrr = 0xC02B400000000003LL, // 32bit sub r -= b - X64_subqri = 0xE881480000000003LL, // 64bit sub r -= int64(immI) - X64_subqr8 = 0x00E8834800000004LL, // 64bit sub r -= int64(imm8) - X64_ucomisd = 0xC02E0F4066000005LL, // unordered compare scalar double - X64_xorqrr = 0xC033480000000003LL, // 64bit xor r &= b - X64_xorrr = 0xC033400000000003LL, // 32bit xor r &= b - X64_xorpd = 0xC0570F4066000005LL, // 128bit xor xmm (two packed doubles) - X64_xorps = 0xC0570F4000000004LL, // 128bit xor xmm (four packed singles), one byte shorter - X64_xorpsm = 0x05570F4000000004LL, // 128bit xor xmm, [rip+disp32] - X64_xorpsa = 0x2504570F40000005LL, // 128bit xor xmm, [disp32] - X64_inclmRAX= 0x00FF000000000002LL, // incl (%rax) - X64_jmpx = 0xC524ff4000000004LL, // jmp [d32+x*8] - X64_jmpxb = 0xC024ff4000000004LL, // jmp [b+x*8] - - X64_movqmi = 0x80C7480000000003LL, // 32bit signed extended to 64-bit store imm -> qword ptr[b+disp32] - X64_movlmi = 0x80C7400000000003LL, // 32bit store imm -> dword ptr[b+disp32] - X64_movsmi = 0x80C7406600000004LL, // 16bit store imm -> word ptr[b+disp32] - X64_movbmi = 0x80C6400000000003LL, // 8bit store imm -> byte ptr[b+disp32] - - X86_and8r = 0xC022000000000002LL, // and rl,rh - X86_sete = 0xC0940F0000000003LL, // no-rex version of X64_sete - X86_setnp = 0xC09B0F0000000003LL // no-rex set byte if odd parity (ordered fcmp result) (PF == 0) - }; - - typedef uint32_t RegisterMask; - - static const RegisterMask GpRegs = 0xffff; - static const RegisterMask FpRegs = 0xffff0000; -#ifdef _WIN64 - static const RegisterMask SavedRegs = 1< - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ -#include "nanojit.h" - -#ifdef _MSC_VER - // disable some specific warnings which are normally useful, but pervasive in the code-gen macros - #pragma warning(disable:4310) // cast truncates constant value -#endif - -namespace nanojit -{ - #if defined FEATURE_NANOJIT && defined NANOJIT_IA32 - - #ifdef NJ_VERBOSE - const char *regNames[] = { - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", - "f0" - }; - - const char *gpRegNames8lo[] = { "al", "cl", "dl", "bl" }; - const char *gpRegNames8hi[] = { "ah", "ch", "dh", "bh" }; - #endif - - #define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0) - - const Register Assembler::argRegs[] = { rECX, rEDX }; - const Register Assembler::retRegs[] = { rEAX, rEDX }; - const Register Assembler::savedRegs[] = { rEBX, rESI, rEDI }; - - const static uint8_t max_abi_regs[] = { - 2, /* ABI_FASTCALL */ - 1, /* ABI_THISCALL */ - 0, /* ABI_STDCALL */ - 0 /* ABI_CDECL */ - }; - - #define RB(r) gpRegNames8lo[REGNUM(r)] - #define RBhi(r) gpRegNames8hi[REGNUM(r)] - - typedef Register R; - typedef int32_t I32; - - // Length: 2--6 bytes. - inline void Assembler::MODRMsib(I32 r, R b, R i, I32 s, I32 d) { - if (d == 0 && b != rEBP) { - SIB(s, REGNUM(i), REGNUM(b)); - MODRM(0, r, 4); // amode == (b + i<> 3, REGNUM(r)); - OPCODE(0x83); - } else { - IMM32(i); - if ( r == rEAX) { - OPCODE(opc); - } else { - MODRMr(opc >> 3, REGNUM(r)); - OPCODE(0x81); - } - } - } - - inline void Assembler::ALUmi(I32 opc, I32 d, Register b, I32 i) { - underrunProtect(10); - NanoAssert(REGNUM(b) < 8); - if (isS8(i)) { - IMM8(i); - MODRMm(opc >> 3, d, b); - OPCODE(0x83); - } else { - IMM32(i); - MODRMm(opc >> 3, d, b); - OPCODE(0x81); - } - } - - inline void Assembler::ALU2(I32 opc2, R d, R s) { - underrunProtect(3); - MODRMr(REGNUM(d), REGNUM(s)); - OPCODE2(opc2); - } - - inline Register Assembler::AL2AHReg(R r) { - NanoAssert(REGNUM(r) < 4); // one of: al, cl, dl, bl - Register r2 = { REGNUM(r) | 4 }; // convert to one of: ah, ch, dh, bh - return r2; - } - - inline void Assembler::OR(R l, R r) { count_alu(); ALU(0x0b, REGNUM(l), r); asm_output("or %s,%s", gpn(l), gpn(r)); } - inline void Assembler::AND(R l, R r) { count_alu(); ALU(0x23, REGNUM(l), r); asm_output("and %s,%s", gpn(l), gpn(r)); } - inline void Assembler::XOR(R l, R r) { count_alu(); ALU(0x33, REGNUM(l), r); asm_output("xor %s,%s", gpn(l), gpn(r)); } - inline void Assembler::ADD(R l, R r) { count_alu(); ALU(0x03, REGNUM(l), r); asm_output("add %s,%s", gpn(l), gpn(r)); } - inline void Assembler::SUB(R l, R r) { count_alu(); ALU(0x2b, REGNUM(l), r); asm_output("sub %s,%s", gpn(l), gpn(r)); } - inline void Assembler::IMUL(R l, R r){ count_alu(); ALU2(0x0faf, l, r); asm_output("imul %s,%s", gpn(l), gpn(r)); } - inline void Assembler::DIV(R r) { count_alu(); ALU(0xf7, 7, r); asm_output("idiv edx:eax, %s", gpn(r)); } - inline void Assembler::NOT(R r) { count_alu(); ALU(0xf7, 2, r); asm_output("not %s", gpn(r)); } - inline void Assembler::NEG(R r) { count_alu(); ALU(0xf7, 3, r); asm_output("neg %s", gpn(r)); } - inline void Assembler::AND8R(R r) { count_alu(); ALU(0x22, REGNUM(r), AL2AHReg(r)); asm_output("andb %s, %s", RB(r), RBhi(r)); } - - inline void Assembler::SHR(R r, R s) { - count_alu(); - NanoAssert(s == rECX); (void)s; - ALU(0xd3, 5, r); - asm_output("shr %s,%s", gpn(r), gpn(s)); - } - - inline void Assembler::SAR(R r, R s) { - count_alu(); - NanoAssert(s == rECX); (void)s; - ALU(0xd3, 7, r); - asm_output("sar %s,%s", gpn(r), gpn(s)); - } - - inline void Assembler::SHL(R r, R s) { - count_alu(); - NanoAssert(s == rECX); (void)s; - ALU(0xd3, 4, r); - asm_output("shl %s,%s", gpn(r), gpn(s)); - } - - inline void Assembler::SHIFTi(I32 c, R r, I32 i) { - underrunProtect(3); - IMM8(i); - MODRMr(c, REGNUM(r)); - OPCODE(0xc1); - } - - inline void Assembler::SHLi(R r, I32 i) { count_alu(); SHIFTi(4, r, i); asm_output("shl %s,%d", gpn(r), i); } - inline void Assembler::SHRi(R r, I32 i) { count_alu(); SHIFTi(5, r, i); asm_output("shr %s,%d", gpn(r), i); } - inline void Assembler::SARi(R r, I32 i) { count_alu(); SHIFTi(7, r, i); asm_output("sar %s,%d", gpn(r), i); } - - inline void Assembler::MOVZX8(R d, R s) { count_alu(); ALU2(0x0fb6, d, s); asm_output("movzx %s,%s", gpn(d), gpn(s)); } - - inline void Assembler::SUBi(R r, I32 i) { count_alu(); ALUi(0x2d, r, i); asm_output("sub %s,%d", gpn(r), i); } - inline void Assembler::ADDi(R r, I32 i) { count_alu(); ALUi(0x05, r, i); asm_output("add %s,%d", gpn(r), i); } - inline void Assembler::ANDi(R r, I32 i) { count_alu(); ALUi(0x25, r, i); asm_output("and %s,%d", gpn(r), i); } - inline void Assembler::ORi(R r, I32 i) { count_alu(); ALUi(0x0d, r, i); asm_output("or %s,%d", gpn(r), i); } - inline void Assembler::XORi(R r, I32 i) { count_alu(); ALUi(0x35, r, i); asm_output("xor %s,%d", gpn(r), i); } - - inline void Assembler::ADDmi(I32 d, R b, I32 i) { count_alust(); ALUmi(0x05, d, b, i); asm_output("add %d(%s), %d", d, gpn(b), i); } - - inline void Assembler::TEST(R d, R s) { count_alu(); ALU(0x85, REGNUM(d), s); asm_output("test %s,%s", gpn(d), gpn(s)); } - inline void Assembler::CMP(R l, R r) { count_alu(); ALU(0x3b, REGNUM(l), r); asm_output("cmp %s,%s", gpn(l), gpn(r)); } - inline void Assembler::CMPi(R r, I32 i) { count_alu(); ALUi(0x3d, r, i); asm_output("cmp %s,%d", gpn(r), i); } - - inline void Assembler::LEA(R r, I32 d, R b) { count_alu(); ALUm(0x8d, REGNUM(r), d, b); asm_output("lea %s,%d(%s)", gpn(r), d, gpn(b)); } - - inline void Assembler::CDQ() { SARi(rEDX, 31); MR(rEDX, rEAX); } - - inline void Assembler::INCLi(I32 p) { - count_alu(); - underrunProtect(6); - IMM32((uint32_t)(ptrdiff_t)p); - OPCODE(0x05); - OPCODE(0xFF); - asm_output("incl (%p)", (void*)p); - } - - inline void Assembler::SETE( R r) { count_alu(); ALU2(0x0f94, r, r); asm_output("sete %s", gpn(r)); } - inline void Assembler::SETNP(R r) { count_alu(); ALU2(0x0f9B, r, r); asm_output("setnp %s", gpn(r)); } - inline void Assembler::SETNPH(R r) { count_alu(); ALU2(0x0f9B, AL2AHReg(r), AL2AHReg(r)); asm_output("setnp %s", RBhi(r)); } - inline void Assembler::SETL( R r) { count_alu(); ALU2(0x0f9C, r, r); asm_output("setl %s", gpn(r)); } - inline void Assembler::SETLE(R r) { count_alu(); ALU2(0x0f9E, r, r); asm_output("setle %s", gpn(r)); } - inline void Assembler::SETG( R r) { count_alu(); ALU2(0x0f9F, r, r); asm_output("setg %s", gpn(r)); } - inline void Assembler::SETGE(R r) { count_alu(); ALU2(0x0f9D, r, r); asm_output("setge %s", gpn(r)); } - inline void Assembler::SETB( R r) { count_alu(); ALU2(0x0f92, r, r); asm_output("setb %s", gpn(r)); } - inline void Assembler::SETBE(R r) { count_alu(); ALU2(0x0f96, r, r); asm_output("setbe %s", gpn(r)); } - inline void Assembler::SETA( R r) { count_alu(); ALU2(0x0f97, r, r); asm_output("seta %s", gpn(r)); } - inline void Assembler::SETAE(R r) { count_alu(); ALU2(0x0f93, r, r); asm_output("setae %s", gpn(r)); } - inline void Assembler::SETO( R r) { count_alu(); ALU2(0x0f92, r, r); asm_output("seto %s", gpn(r)); } - - inline void Assembler::MREQ(R d, R s) { count_alu(); ALU2(0x0f44, d, s); asm_output("cmove %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRNE(R d, R s) { count_alu(); ALU2(0x0f45, d, s); asm_output("cmovne %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRL( R d, R s) { count_alu(); ALU2(0x0f4C, d, s); asm_output("cmovl %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRLE(R d, R s) { count_alu(); ALU2(0x0f4E, d, s); asm_output("cmovle %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRG( R d, R s) { count_alu(); ALU2(0x0f4F, d, s); asm_output("cmovg %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRGE(R d, R s) { count_alu(); ALU2(0x0f4D, d, s); asm_output("cmovge %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRB( R d, R s) { count_alu(); ALU2(0x0f42, d, s); asm_output("cmovb %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRBE(R d, R s) { count_alu(); ALU2(0x0f46, d, s); asm_output("cmovbe %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRA( R d, R s) { count_alu(); ALU2(0x0f47, d, s); asm_output("cmova %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRAE(R d, R s) { count_alu(); ALU2(0x0f43, d, s); asm_output("cmovae %s,%s", gpn(d), gpn(s)); } - inline void Assembler::MRNO(R d, R s) { count_alu(); ALU2(0x0f41, d, s); asm_output("cmovno %s,%s", gpn(d), gpn(s)); } - - // these aren't currently used but left in for reference - //#define LDEQ(r,d,b) do { ALU2m(0x0f44,r,d,b); asm_output("cmove %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) - //#define LDNEQ(r,d,b) do { ALU2m(0x0f45,r,d,b); asm_output("cmovne %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) - - inline void Assembler::LD(R reg, I32 disp, R base) { - count_ld(); - ALUm(0x8b, REGNUM(reg), disp, base); - asm_output("mov %s,%d(%s)", gpn(reg), disp, gpn(base)); - } - - inline void Assembler::LDdm(R reg, I32 addr) { - count_ld(); - ALUdm(0x8b, reg, addr); - asm_output("mov %s,0(%p)", gpn(reg), (void*)addr); - } - -#define SIBIDX(n) "1248"[n] - - inline void Assembler::LDsib(R reg, I32 disp, R base, R index, I32 scale) { - count_ld(); - ALUsib(0x8b, reg, base, index, scale, disp); - asm_output("mov %s,%d(%s+%s*%c)", gpn(reg), disp, gpn(base), gpn(index), SIBIDX(scale)); - } - - // note: movzx/movsx are being output with an 8/16 suffix to indicate the - // size being loaded. This doesn't really match standard intel format - // (though is arguably terser and more obvious in this case) and would - // probably be nice to fix. (Likewise, the 8/16 bit stores being output - // as "mov8" and "mov16" respectively.) - - // Load 16-bit, sign extend. - inline void Assembler::LD16S(R r, I32 d, R b) { - count_ld(); - ALU2m(0x0fbf, r, d, b); - asm_output("movsx16 %s,%d(%s)", gpn(r), d, gpn(b)); - } - - inline void Assembler::LD16Sdm(R r, I32 addr) { - count_ld(); - ALU2dm(0x0fbf, r, addr); - asm_output("movsx16 %s,0(%lx)", gpn(r), (unsigned long)addr); - } - - inline void Assembler::LD16Ssib(R r, I32 disp, R base, R index, I32 scale) { - count_ld(); - ALU2sib(0x0fbf, r, base, index, scale, disp); - asm_output("movsx16 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); - } - - // Load 16-bit, zero extend. - inline void Assembler::LD16Z(R r, I32 d, R b) { - count_ld(); - ALU2m(0x0fb7, r, d, b); - asm_output("movzx16 %s,%d(%s)", gpn(r), d, gpn(b)); - } - - inline void Assembler::LD16Zdm(R r, I32 addr) { - count_ld(); - ALU2dm(0x0fb7, r, addr); - asm_output("movzx16 %s,0(%lx)", gpn(r), (unsigned long)addr); - } - - inline void Assembler::LD16Zsib(R r, I32 disp, R base, R index, I32 scale) { - count_ld(); - ALU2sib(0x0fb7, r, base, index, scale, disp); - asm_output("movzx16 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); - } - - // Load 8-bit, zero extend. - inline void Assembler::LD8Z(R r, I32 d, R b) { - count_ld(); - ALU2m(0x0fb6, r, d, b); - asm_output("movzx8 %s,%d(%s)", gpn(r), d, gpn(b)); - } - - inline void Assembler::LD8Zdm(R r, I32 addr) { - count_ld(); - ALU2dm(0x0fb6, r, addr); - asm_output("movzx8 %s,0(%lx)", gpn(r), (long unsigned)addr); - } - - inline void Assembler::LD8Zsib(R r, I32 disp, R base, R index, I32 scale) { - count_ld(); - ALU2sib(0x0fb6, r, base, index, scale, disp); - asm_output("movzx8 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); - } - - // Load 8-bit, sign extend. - inline void Assembler::LD8S(R r, I32 d, R b) { - count_ld(); - ALU2m(0x0fbe, r, d, b); - asm_output("movsx8 %s,%d(%s)", gpn(r), d, gpn(b)); - } - - inline void Assembler::LD8Sdm(R r, I32 addr) { - count_ld(); - ALU2dm(0x0fbe, r, addr); - asm_output("movsx8 %s,0(%lx)", gpn(r), (long unsigned)addr); - } - - inline void Assembler::LD8Ssib(R r, I32 disp, R base, R index, I32 scale) { - count_ld(); - ALU2sib(0x0fbe, r, base, index, scale, disp); - asm_output("movsx8 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); - } - - inline void Assembler::LDi(R r, I32 i) { - count_ld(); - underrunProtect(5); - IMM32(i); - NanoAssert(REGNUM(r) < 8); - OPCODE(0xb8 | REGNUM(r)); - asm_output("mov %s,%d", gpn(r), i); - } - - // Quirk of x86-32: reg must be a/b/c/d for byte stores here. - inline void Assembler::ST8(R base, I32 disp, R reg) { - count_st(); - NanoAssert(REGNUM(reg) < 4); - ALUm(0x88, REGNUM(reg), disp, base); - asm_output("mov8 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); - } - - // Quirk of x86-32: reg must be a/b/c/d for byte stores here. - inline void Assembler::ST8sib(I32 disp, R base, R index, I32 scale, R reg) { - count_st(); - NanoAssert(REGNUM(reg) < 4); - ALUsib(0x88, reg, base, index, scale, disp); - asm_output("mov8 %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), - gpn(index), SIBIDX(scale), gpn(reg)); - } - - inline void Assembler::ST16(R base, I32 disp, R reg) { - count_st(); - ALUm16(0x89, REGNUM(reg), disp, base); - asm_output("mov16 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); - } - - inline void Assembler::ST16sib(I32 disp, R base, R index, I32 scale, R reg) { - count_st(); - ALUsib16(0x89, reg, base, index, scale, disp); - asm_output("mov16 %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), - gpn(index), SIBIDX(scale), gpn(reg)); - } - - inline void Assembler::ST(R base, I32 disp, R reg) { - count_st(); - ALUm(0x89, REGNUM(reg), disp, base); - asm_output("mov %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); - } - - inline void Assembler::STsib(I32 disp, R base, R index, I32 scale, R reg) { - count_st(); - ALUsib(0x89, reg, base, index, scale, disp); - asm_output("mov %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), - gpn(index), SIBIDX(scale), gpn(reg)); - } - - inline void Assembler::ST8i(R base, I32 disp, I32 imm) { - count_st(); - underrunProtect(8); - IMM8(imm); - MODRMm(0, disp, base); - OPCODE(0xc6); - asm_output("mov8 %d(%s),%d", disp, gpn(base), imm); - } - - inline void Assembler::ST8isib(I32 disp, R base, R index, I32 scale, I32 imm) { - count_st(); - underrunProtect(8); - IMM8(imm); - MODRMsib(0, base, index, scale, disp); - OPCODE(0xc6); - asm_output("mov8 %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); - } - - inline void Assembler::ST16i(R base, I32 disp, I32 imm) { - count_st(); - underrunProtect(10); - IMM16(imm); - MODRMm(0, disp, base); - OPCODE(0xc7); - OPCODE(0x66); - asm_output("mov16 %d(%s),%d", disp, gpn(base), imm); - } - - inline void Assembler::ST16isib(I32 disp, R base, R index, I32 scale, I32 imm) { - count_st(); - underrunProtect(10); - IMM16(imm); - MODRMsib(0, base, index, scale, disp); - OPCODE(0xc7); - OPCODE(0x66); - asm_output("mov16 %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); - } - - inline void Assembler::STi(R base, I32 disp, I32 imm) { - count_st(); - underrunProtect(11); - IMM32(imm); - MODRMm(0, disp, base); - OPCODE(0xc7); - asm_output("mov %d(%s),%d", disp, gpn(base), imm); - } - - inline void Assembler::STisib(I32 disp, R base, R index, I32 scale, I32 imm) { - count_st(); - underrunProtect(11); - IMM32(imm); - MODRMsib(0, base, index, scale, disp); - OPCODE(0xc7); - asm_output("mov %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); - } - - const uint8_t INT3_OP = 0xcc; - - inline void Assembler::RET() { count_ret(); ALU0(0xc3); asm_output("ret"); } - inline void Assembler::NOP() { count_alu(); ALU0(0x90); asm_output("nop"); } - inline void Assembler::INT3() { ALU0(INT3_OP); asm_output("int3"); } - - inline void Assembler::PUSHi(I32 i) { - count_push(); - if (isS8(i)) { - underrunProtect(2); - IMM8(i); - OPCODE(0x6a); - asm_output("push %d", i); - } else { - underrunProtect(5); - IMM32(i); - OPCODE(0x68); - asm_output("push %d", i); - } - } - - inline void Assembler::PUSHr(R r) { - count_push(); - underrunProtect(1); - NanoAssert(REGNUM(r) < 8); - OPCODE(0x50 | REGNUM(r)); - asm_output("push %s", gpn(r)); - } - - inline void Assembler::PUSHm(I32 d, R b) { - count_pushld(); - ALUm(0xff, 6, d, b); - asm_output("push %d(%s)", d, gpn(b)); - } - - inline void Assembler::POPr(R r) { - count_pop(); - underrunProtect(1); - NanoAssert(REGNUM(r) < 8); - OPCODE(0x58 | REGNUM(r)); - asm_output("pop %s", gpn(r)); - } - - inline void Assembler::JCC(I32 o, NIns* t, const char* n) { - count_jcc(); - underrunProtect(6); - intptr_t tt = (intptr_t)t - (intptr_t)_nIns; - if (t && isS8(tt)) { - IMM8(tt); - OPCODE(0x70 | o); - } else { - IMM32(tt); - OPCODE(0x80 | o); - OPCODE(JCC32); - } - asm_output("%-5s %p", n, t); - (void) n; - } - - inline void Assembler::JMP_long(NIns* t) { - count_jmp(); - underrunProtect(5); - NanoAssert(t); - intptr_t tt = (intptr_t)t - (intptr_t)_nIns; - IMM32(tt); - OPCODE(JMP32); - asm_output("jmp %p", t); - verbose_only( verbose_outputf("%p:", (void*)_nIns); ) - } - - inline void Assembler::JMP_indexed(Register x, I32 ss, NIns** addr) { - underrunProtect(7); - IMM32(int32_t(addr)); - SIB(ss, REGNUM(x), 5); - MODRM(0, 4, 4); // amode == addr(table + x<> 8); - } - - inline void Assembler::FPUdm(I32 o, const double* const m) { - underrunProtect(6); - MODRMdm(uint8_t(o), int32_t(m)); - OPCODE(o >> 8); - } - - inline void Assembler::TEST_AH(I32 i) { - count_alu(); - underrunProtect(3); - OPCODE(i); - OPCODE(0xc4); - OPCODE(0xf6); - asm_output("test ah, %d", i); - } - - inline void Assembler::FNSTSW_AX() { count_fpu(); FPUc(0xdfe0); asm_output("fnstsw_ax"); } - inline void Assembler::FCHS() { count_fpu(); FPUc(0xd9e0); asm_output("fchs"); } - inline void Assembler::FLD1() { count_fpu(); FPUc(0xd9e8); asm_output("fld1"); fpu_push(); } - inline void Assembler::FLDZ() { count_fpu(); FPUc(0xd9ee); asm_output("fldz"); fpu_push(); } - - inline void Assembler::FST32(bool p, I32 d, R b){ count_stq(); FPUm(0xd902|(p?1:0), d, b); asm_output("fst%s32 %d(%s)", (p?"p":""), d, gpn(b)); if (p) fpu_pop(); } - inline void Assembler::FSTQ(bool p, I32 d, R b) { count_stq(); FPUm(0xdd02|(p?1:0), d, b); asm_output("fst%sq %d(%s)", (p?"p":""), d, gpn(b)); if (p) fpu_pop(); } - - inline void Assembler::FSTPQ(I32 d, R b) { FSTQ(1, d, b); } - - inline void Assembler::FCOM(bool p, I32 d, R b) { count_fpuld(); FPUm(0xdc02|(p?1:0), d, b); asm_output("fcom%s %d(%s)", (p?"p":""), d, gpn(b)); if (p) fpu_pop(); } - inline void Assembler::FCOMdm(bool p, const double* dm) { - count_fpuld(); - FPUdm(0xdc02|(p?1:0), dm); - asm_output("fcom%s (%p)", (p?"p":""), (void*)dm); - if (p) fpu_pop(); - } - - inline void Assembler::FLD32(I32 d, R b) { count_ldq(); FPUm(0xd900, d, b); asm_output("fld32 %d(%s)", d, gpn(b)); fpu_push();} - inline void Assembler::FLDQ(I32 d, R b) { count_ldq(); FPUm(0xdd00, d, b); asm_output("fldq %d(%s)", d, gpn(b)); fpu_push();} - inline void Assembler::FLDQdm(const double* dm) { count_ldq(); FPUdm(0xdd00, dm); asm_output("fldq (%p)", (void*)dm); fpu_push();} - inline void Assembler::FILDQ(I32 d, R b) { count_fpuld(); FPUm(0xdf05, d, b); asm_output("fildq %d(%s)", d, gpn(b)); fpu_push(); } - inline void Assembler::FILD(I32 d, R b) { count_fpuld(); FPUm(0xdb00, d, b); asm_output("fild %d(%s)", d, gpn(b)); fpu_push(); } - - inline void Assembler::FIST(bool p, I32 d, R b) { - count_fpu(); - FPUm(0xdb02 | (p?1:0), d, b); - asm_output("fist%s %d(%s)", (p?"p":""), d, gpn(b)); - if (p) fpu_pop(); - } - - inline void Assembler::FADD( I32 d, R b) { count_fpu(); FPUm(0xdc00, d, b); asm_output("fadd %d(%s)", d, gpn(b)); } - inline void Assembler::FSUB( I32 d, R b) { count_fpu(); FPUm(0xdc04, d, b); asm_output("fsub %d(%s)", d, gpn(b)); } - inline void Assembler::FSUBR(I32 d, R b) { count_fpu(); FPUm(0xdc05, d, b); asm_output("fsubr %d(%s)", d, gpn(b)); } - inline void Assembler::FMUL( I32 d, R b) { count_fpu(); FPUm(0xdc01, d, b); asm_output("fmul %d(%s)", d, gpn(b)); } - inline void Assembler::FDIV( I32 d, R b) { count_fpu(); FPUm(0xdc06, d, b); asm_output("fdiv %d(%s)", d, gpn(b)); } - inline void Assembler::FDIVR(I32 d, R b) { count_fpu(); FPUm(0xdc07, d, b); asm_output("fdivr %d(%s)", d, gpn(b)); } - - inline void Assembler::FADDdm( const double *dm) { count_ldq(); FPUdm(0xdc00, dm); asm_output("fadd (%p)", (void*)dm); } - inline void Assembler::FSUBRdm(const double* dm) { count_ldq(); FPUdm(0xdc05, dm); asm_output("fsubr (%p)", (void*)dm); } - inline void Assembler::FMULdm( const double* dm) { count_ldq(); FPUdm(0xdc01, dm); asm_output("fmul (%p)", (void*)dm); } - inline void Assembler::FDIVRdm(const double* dm) { count_ldq(); FPUdm(0xdc07, dm); asm_output("fdivr (%p)", (void*)dm); } - - inline void Assembler::FCOMP() { count_fpu(); FPUc(0xD8D9); asm_output("fcomp"); fpu_pop();} - inline void Assembler::FCOMPP() { count_fpu(); FPUc(0xDED9); asm_output("fcompp"); fpu_pop();fpu_pop();} - inline void Assembler::FLDr(R r) { count_ldq(); FPU(0xd9c0, r); asm_output("fld %s", gpn(r)); fpu_push(); } - inline void Assembler::EMMS() { count_fpu(); FPUc(0x0f77); asm_output("emms"); } - - // standard direct call - inline void Assembler::CALL(const CallInfo* ci) { - count_call(); - underrunProtect(5); - int offset = (ci->_address) - ((int)_nIns); - IMM32((uint32_t)offset); - OPCODE(0xE8); - verbose_only(asm_output("call %s", (ci->_name));) - debug_only(if (ci->returnType()==ARGTYPE_D) fpu_push();) - } - - // indirect call thru register - inline void Assembler::CALLr(const CallInfo* ci, Register r) { - count_calli(); - underrunProtect(2); - ALU(0xff, 2, r); - verbose_only(asm_output("call %s", gpn(r));) - debug_only(if (ci->returnType()==ARGTYPE_D) fpu_push();) (void)ci; - } - - void Assembler::nInit() - { - nHints[LIR_calli] = rmask(retRegs[0]); - nHints[LIR_calld] = rmask(FST0); - nHints[LIR_paramp] = PREFER_SPECIAL; - nHints[LIR_immi] = ScratchRegs; - // Nb: Doing this with a loop future-proofs against the possibilty of - // new comparison operations being added. - for (LOpcode op = LOpcode(0); op < LIR_sentinel; op = LOpcode(op+1)) - if (isCmpOpcode(op)) - nHints[op] = AllowableByteRegs; - } - - void Assembler::nBeginAssembly() { - max_stk_args = 0; - } - - NIns* Assembler::genPrologue() - { - // Prologue - uint32_t stackNeeded = max_stk_args + STACK_GRANULARITY * _activation.stackSlotsNeeded(); - - uint32_t stackPushed = - STACK_GRANULARITY + // returnaddr - STACK_GRANULARITY; // ebp - - uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); - uint32_t amt = aligned - stackPushed; - -#ifdef _WIN32 - // Windows uses a single guard page for extending the stack, so - // new stack pages must be first touched in stack-growth order. - // We touch each whole page that will be allocated to the frame - // (following the saved FP) to cause the OS to commit the page if - // necessary. Since we don't calculate page boundaries, but just - // probe at intervals of the pagesize, it is possible that the - // last page of the frame will be touched unnecessarily. Note that - // we must generate the probes in the reverse order of their execution. - // We require that the page size be a power of 2. - size_t pageSize = VMPI_getVMPageSize(); - NanoAssert((pageSize & (pageSize-1)) == 0); - size_t pageRounded = amt & ~(pageSize-1); - for (int32_t d = pageRounded; d > 0; d -= pageSize) { - STi(rEBP, -d, 0); - } -#endif - - // Reserve stackNeeded bytes, padded - // to preserve NJ_ALIGN_STACK-byte alignment. - if (amt) { - SUBi(SP, amt); - } - - verbose_only( asm_output("[frag entry]"); ) - NIns *fragEntry = _nIns; - MR(FP, SP); // Establish our own FP. - PUSHr(FP); // Save caller's FP. - - return fragEntry; - } - - void Assembler::nFragExit(LIns* guard) - { - SideExit *exit = guard->record()->exit; - Fragment *frag = exit->target; - GuardRecord *lr = 0; - bool destKnown = (frag && frag->fragEntry); - - // Generate jump to epilog and initialize lr. - // If the guard already exists, use a simple jump. - if (destKnown) { - JMP(frag->fragEntry); - lr = 0; - } else { // Target doesn't exist. Jump to an epilogue for now. This can be patched later. - if (!_epilogue) - _epilogue = genEpilogue(); - lr = guard->record(); - JMP_long(_epilogue); - lr->jmp = _nIns; - } - - // profiling for the exit - verbose_only( - if (_logc->lcbits & LC_FragProfile) { - INCLi(int32_t(&guard->record()->profCount)); - } - ) - - // Restore rESP from rEBP, undoing SUBi(SP,amt) in the prologue - MR(SP,FP); - - // return value is GuardRecord* - asm_immi(rEAX, int(lr), /*canClobberCCs*/true); - } - - NIns *Assembler::genEpilogue() - { - RET(); - POPr(FP); // Restore caller's FP. - - return _nIns; - } - - void Assembler::asm_call(LIns* ins) - { - if (!ins->isop(LIR_callv)) { - Register rr = ( ins->isop(LIR_calld) ? FST0 : retRegs[0] ); - prepareResultReg(ins, rmask(rr)); - evictScratchRegsExcept(rmask(rr)); - } else { - evictScratchRegsExcept(0); - } - const CallInfo* call = ins->callInfo(); - // must be signed, not unsigned - uint32_t iargs = call->count_int32_args(); - int32_t fargs = call->count_args() - iargs; - - bool indirect = call->isIndirect(); - if (indirect) { - // target arg isn't pushed, its consumed in the call - iargs --; - } - - AbiKind abi = call->_abi; - uint32_t max_regs = max_abi_regs[abi]; - if (max_regs > iargs) - max_regs = iargs; - - int32_t istack = iargs-max_regs; // first 2 4B args are in registers - int32_t extra = 0; - const int32_t pushsize = 4*istack + 8*fargs; // actual stack space used - -#if _MSC_VER - // msc only provides 4-byte alignment but we have 8 byte stack adjustment - // logic so maintain our 8 byte alignment. - uint32_t align = 8; -#else - uint32_t align = NJ_ALIGN_STACK; -#endif - - if (pushsize) { - if (_config.i386_fixed_esp) { - // In case of fastcall, stdcall and thiscall the callee cleans up the stack, - // and since we reserve max_stk_args words in the prolog to call functions - // and don't adjust the stack pointer individually for each call we have - // to undo here any changes the callee just did to the stack. - if (abi != ABI_CDECL) - SUBi(SP, pushsize); - } else { - // stack re-alignment - // only pop our adjustment amount since callee pops args in FASTCALL mode - extra = alignUp(pushsize, align) - pushsize; - if (call->_abi == ABI_CDECL) { - // with CDECL only, caller pops args - ADDi(SP, extra+pushsize); - } else if (extra > 0) { - ADDi(SP, extra); - } - } - } - - NanoAssert(ins->isop(LIR_callv) || ins->isop(LIR_callp) || ins->isop(LIR_calld)); - if (!indirect) { - CALL(call); - } - else { - // Indirect call. x86 Calling conventions don't use rEAX as an - // argument, and do use rEAX as a return value. We need a register - // for the address to call, so we use rEAX since it will always be - // available. - CALLr(call, rEAX); - } - - // Call this now so that the arg setup can involve 'rr'. - freeResourcesOf(ins); - - // Make sure fpu stack is empty before call. - NanoAssert(_allocator.isFree(FST0)); - - // Pre-assign registers to the first N 4B args based on the calling convention. - uint32_t n = 0; - - ArgType argTypes[MAXARGS]; - uint32_t argc = call->getArgTypes(argTypes); - int32_t stkd = 0; - - if (indirect) { - argc--; - asm_arg(ARGTYPE_P, ins->arg(argc), rEAX, stkd); - if (!_config.i386_fixed_esp) - stkd = 0; - } - - for (uint32_t i = 0; i < argc; i++) - { - uint32_t j = argc-i-1; - ArgType ty = argTypes[j]; - Register r = UnspecifiedReg; - if (n < max_regs && ty != ARGTYPE_D) { - r = argRegs[n++]; // tell asm_arg what reg to use - } - asm_arg(ty, ins->arg(j), r, stkd); - if (!_config.i386_fixed_esp) - stkd = 0; - } - - if (_config.i386_fixed_esp) { - if (pushsize > max_stk_args) - max_stk_args = pushsize; - } else if (extra > 0) { - SUBi(SP, extra); - } - } - - Register Assembler::nRegisterAllocFromSet(RegisterMask set) - { - Register r; - RegAlloc ®s = _allocator; - #ifdef _MSC_VER - _asm - { - mov ecx, regs - bsf eax, set // i = first bit set - btr RegAlloc::free[ecx], eax // free &= ~rmask(i) - mov r, eax - } - #else - asm( - "bsf %1, %%eax\n\t" - "btr %%eax, %2\n\t" - "movl %%eax, %0\n\t" - : "=m"(r) : "m"(set), "m"(regs.free) : "%eax", "memory" ); - #endif /* _MSC_VER */ - return r; - } - - void Assembler::nRegisterResetAll(RegAlloc& a) - { - // add scratch registers to our free list for the allocator - a.clear(); - a.free = SavedRegs | ScratchRegs; - if (!_config.i386_sse2) - a.free &= ~XmmRegs; - } - - void Assembler::nPatchBranch(NIns* branch, NIns* targ) - { - intptr_t offset = intptr_t(targ) - intptr_t(branch); - if (branch[0] == JMP32) { - *(int32_t*)&branch[1] = offset - 5; - } else if (branch[0] == JCC32) { - *(int32_t*)&branch[2] = offset - 6; - } else - NanoAssertMsg(0, "Unknown branch type in nPatchBranch"); - } - - RegisterMask Assembler::nHint(LIns* ins) - { - NanoAssert(ins->isop(LIR_paramp)); - RegisterMask prefer = 0; - uint8_t arg = ins->paramArg(); - if (ins->paramKind() == 0) { - uint32_t max_regs = max_abi_regs[_thisfrag->lirbuf->abi]; - if (arg < max_regs) - prefer = rmask(argRegs[arg]); - } else { - if (arg < NumSavedRegs) - prefer = rmask(savedRegs[arg]); - } - return prefer; - } - - // Return true if we can generate code for this instruction that neither - // sets CCs nor clobbers any input register. - // LEA is the only native instruction that fits those requirements. - bool canRematLEA(LIns* ins) - { - if (ins->isop(LIR_addi)) - return ins->oprnd1()->isInReg() && ins->oprnd2()->isImmI(); - // Subtract and some left-shifts could be rematerialized using LEA, - // but it hasn't shown to help in real code yet. Noting them anyway: - // maybe sub? R = subl rL, const => leal R, [rL + -const] - // maybe lsh? R = lshl rL, 1/2/3 => leal R, [rL * 2/4/8] - return false; - } - - bool Assembler::canRemat(LIns* ins) - { - return ins->isImmAny() || ins->isop(LIR_allocp) || canRematLEA(ins); - } - - // WARNING: the code generated by this function must not affect the - // condition codes. See asm_cmpi(). - void Assembler::asm_restore(LIns* ins, Register r) - { - NanoAssert(ins->getReg() == r); - - uint32_t arg; - uint32_t abi_regcount; - if (ins->isop(LIR_allocp)) { - // The value of a LIR_allocp instruction is the address of the - // stack allocation. We can rematerialize that from the record we - // have of where the allocation lies in the stack. - NanoAssert(ins->isInAr()); // must have stack slots allocated - LEA(r, arDisp(ins), FP); - - } else if (ins->isImmI()) { - asm_immi(r, ins->immI(), /*canClobberCCs*/false); - - } else if (ins->isImmD()) { - asm_immd(r, ins->immDasQ(), ins->immD(), /*canClobberCCs*/false); - - } else if (ins->isop(LIR_paramp) && ins->paramKind() == 0 && - (arg = ins->paramArg()) >= (abi_regcount = max_abi_regs[_thisfrag->lirbuf->abi])) { - // Incoming arg is on stack, can restore it from there instead of spilling. - - // this case is intentionally not detected in canRemat(), because we still - // emit a load instead of a fast ALU operation. We don't want parameter - // spills to have precedence over immediates & ALU ops, but if one does - // spill, we want to load it directly from its stack area, saving a store - // in the prolog. - - // Compute position of argument relative to ebp. Higher argument - // numbers are at higher positive offsets. The first abi_regcount - // arguments are in registers, rest on stack. +8 accomodates the - // return address and saved ebp value. Assuming abi_regcount == 0: - // - // low-addr ebp - // [frame...][saved-ebp][return-addr][arg0][arg1]... - // - int d = (arg - abi_regcount) * sizeof(intptr_t) + 8; - LD(r, d, FP); - - } else if (canRematLEA(ins)) { - LEA(r, ins->oprnd2()->immI(), ins->oprnd1()->getReg()); - - } else { - int d = findMemFor(ins); - if (ins->isI()) { - NanoAssert(rmask(r) & GpRegs); - LD(r, d, FP); - } else { - NanoAssert(ins->isD()); - if (rmask(r) & XmmRegs) { - SSE_LDQ(r, d, FP); - } else { - NanoAssert(r == FST0); - FLDQ(d, FP); - } - } - } - } - - void Assembler::asm_store32(LOpcode op, LIns* value, int dr, LIns* base) - { - if (value->isImmI()) { - if (base->opcode() == LIR_addp) { - LIns* index; - int scale; - getBaseIndexScale(base, &base, &index, &scale); - - Register rb, ri; - getBaseReg2(GpRegs, index, ri, GpRegs, base, rb, dr); - - int c = value->immI(); - switch (op) { - case LIR_sti2c: ST8isib( dr, rb, ri, scale, c); break; - case LIR_sti2s: ST16isib(dr, rb, ri, scale, c); break; - case LIR_sti: STisib( dr, rb, ri, scale, c); break; - default: NanoAssert(0); break; - } - } else { - Register rb = getBaseReg(base, dr, GpRegs); - int c = value->immI(); - switch (op) { - case LIR_sti2c: ST8i( rb, dr, c); break; - case LIR_sti2s: ST16i(rb, dr, c); break; - case LIR_sti: STi( rb, dr, c); break; - default: NanoAssert(0); break; - } - } - - } else { - // Quirk of x86-32: reg must be a/b/c/d for single-byte stores. - const RegisterMask SrcRegs = (op == LIR_sti2c) ? AllowableByteRegs : GpRegs; - - Register rv, rb; - if (base->opcode() == LIR_addp) { - LIns* index; - int scale; - getBaseIndexScale(base, &base, &index, &scale); - - Register rb, ri, rv; - getBaseReg2(SrcRegs, value, rv, GpRegs, base, rb, dr); - ri = (index == value) ? rv - : (index == base) ? rb - : findRegFor(index, GpRegs & ~(rmask(rb)|rmask(rv))); - - switch (op) { - case LIR_sti2c: ST8sib( dr, rb, ri, scale, rv); break; - case LIR_sti2s: ST16sib(dr, rb, ri, scale, rv); break; - case LIR_sti: STsib( dr, rb, ri, scale, rv); break; - default: NanoAssert(0); break; - } - - } else { - if (base->isImmI()) { - // absolute address - rb = UnspecifiedReg; - dr += base->immI(); - rv = findRegFor(value, SrcRegs); - } else { - getBaseReg2(SrcRegs, value, rv, GpRegs, base, rb, dr); - } - switch (op) { - case LIR_sti2c: ST8( rb, dr, rv); break; - case LIR_sti2s: ST16(rb, dr, rv); break; - case LIR_sti: ST( rb, dr, rv); break; - default: NanoAssert(0); break; - } - } - } - } - - void Assembler::asm_spill(Register rr, int d, bool pop) - { - NanoAssert(d); - if (rmask(rr) & GpRegs) { - ST(FP, d, rr); - } else if (rmask(rr) & XmmRegs) { - SSE_STQ(d, FP, rr); - } else { - NanoAssert(rr == FST0); - FSTQ(pop, d, FP); - } - } - - void Assembler::asm_load64(LIns* ins) - { - LIns* base = ins->oprnd1(); - int d = ins->disp(); - - // There are two cases: - // - 'ins' is in FpRegs: load it. - // - otherwise: there's no point loading the value into a register - // because its only use will be to immediately spill it. Instead we - // do a memory-to-memory move from the load address directly to the - // spill slot. (There must be a spill slot assigned.) This is why - // we don't use prepareResultReg() here unlike most other places -- - // because it mandates bringing the value into a register. - // - if (ins->isInReg()) { - Register rr = prepareResultReg(ins, rmask(ins->getReg())); - - if (base->opcode() == LIR_addp && rmask(rr) & XmmRegs) { - LIns* index; - int scale; - getBaseIndexScale(base, &base, &index, &scale); - - // (**) We don't have the usual opportunity to clobber 'base' - // or 'ins' with the result because it has a different type. - Register rb, ri; - RegisterMask allow = GpRegs & ~rmask(rr); - getBaseReg2(allow, index, ri, allow, base, rb, d); - - switch (ins->opcode()) { - case LIR_ldd: SSE_LDQsib(rr, d, rb, ri, scale); break; - case LIR_ldf2d: SSE_CVTSS2SD(rr, rr); - SSE_LDSSsib(rr, d, rb, ri, scale); - SSE_XORPDr(rr, rr); break; - default: NanoAssert(0); break; - } - - } else { - // (**) We don't have the usual opportunity to clobber 'base' - // or 'ins' with the result because it has a different type. - Register rb = getBaseReg(base, d, GpRegs); - if (rmask(rr) & XmmRegs) { - switch (ins->opcode()) { - case LIR_ldd: SSE_LDQ(rr, d, rb); break; - case LIR_ldf2d: SSE_CVTSS2SD(rr, rr); - SSE_LDSS(rr, d, rb); - SSE_XORPDr(rr, rr); break; - default: NanoAssert(0); break; - } - } else { - NanoAssert(rr == FST0); - switch (ins->opcode()) { - case LIR_ldd: FLDQ(d, rb); break; - case LIR_ldf2d: FLD32(d, rb); break; - default: NanoAssert(0); break; - } - } - } - - } else { - Register rb = getBaseReg(base, d, GpRegs); - - NanoAssert(ins->isInAr()); - int dr = arDisp(ins); - - switch (ins->opcode()) { - case LIR_ldd: - // Don't use an fpu reg to simply load & store the value. - asm_mmq(FP, dr, rb, d); - break; - - case LIR_ldf2d: - // Need to use fpu to expand 32->64. - FSTPQ(dr, FP); - FLD32(d, rb); - break; - - default: - NanoAssert(0); - break; - } - } - - freeResourcesOf(ins); - // Nb: no need for a possible findSpecificRegForUnallocated() call - // here because of (**) above. - } - - void Assembler::asm_store64(LOpcode op, LIns* value, int d, LIns* base) - { - if (op == LIR_std2f) { - Register rb = getBaseReg(base, d, GpRegs); - bool pop = !value->isInReg(); - Register rv = ( pop - ? findRegFor(value, _config.i386_sse2 ? XmmRegs : FpRegs) - : value->getReg() ); - - if (rmask(rv) & XmmRegs) { - // need a scratch reg - Register rt = registerAllocTmp(XmmRegs); - - // cvt to single-precision and store - SSE_STSS(d, rb, rt); - SSE_CVTSD2SS(rt, rv); - SSE_XORPDr(rt, rt); // zero dest to ensure no dependency stalls - - } else { - FST32(pop, d, rb); - } - - } else if (value->isImmD()) { - Register rb = getBaseReg(base, d, GpRegs); - STi(rb, d+4, value->immDhi()); - STi(rb, d, value->immDlo()); - - } else if (base->opcode() == LIR_addp && _config.i386_sse2) { - LIns* index; - int scale; - getBaseIndexScale(base, &base, &index, &scale); - - Register rb, ri; - getBaseReg2(GpRegs, index, ri, GpRegs, base, rb, d); - - Register rv = value->isInReg() ? value->getReg() : findRegFor(value, XmmRegs); - NanoAssert(rmask(rv) & XmmRegs); - SSE_STQsib(d, rb, ri, scale, rv); - - } else if (value->isop(LIR_ldd) && !_config.i386_sse2) { - // 'value' may be live in an FPU reg. Either way, don't put it on - // the FPU stack just to load & store it. - Register rb = getBaseReg(base, d, GpRegs); - int da = findMemFor(value); - asm_mmq(rb, d, FP, da); - - } else { - Register rb = getBaseReg(base, d, GpRegs); - bool pop = !value->isInReg(); - Register rv = ( pop - ? findRegFor(value, _config.i386_sse2 ? XmmRegs : FpRegs) - : value->getReg() ); - if (rmask(rv) & XmmRegs) - SSE_STQ(d, rb, rv); - else - FSTQ(pop, d, rb); - } - } - - // Copy 64 bits: (rd+dd) <- (rs+ds). - // - void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) - { - // Value is either a 64-bit struct or maybe a float that isn't live in - // an FPU reg. Either way, avoid allocating an FPU reg just to load - // and store it. - if (_config.i386_sse2) { - Register t = registerAllocTmp(XmmRegs); - SSE_STQ(dd, rd, t); - SSE_LDQ(t, ds, rs); - } else { - // We avoid copying via the FP stack because it's slow and likely - // to cause spills. - Register t = registerAllocTmp(GpRegs & ~(rmask(rd)|rmask(rs))); - ST(rd, dd+4, t); - LD(t, ds+4, rs); - ST(rd, dd, t); - LD(t, ds, rs); - } - } - - Branches Assembler::asm_branch_helper(bool branchOnFalse, LIns* cond, NIns* targ) - { - return isCmpDOpcode(cond->opcode()) - ? asm_branchd_helper(branchOnFalse, cond, targ) - : asm_branchi_helper(branchOnFalse, cond, targ); - } - - Branches Assembler::asm_branchi_helper(bool branchOnFalse, LIns* cond, NIns* targ) - { - if (branchOnFalse) { - // op == LIR_xf/LIR_jf - switch (cond->opcode()) { - case LIR_eqi: JNE(targ); break; - case LIR_lti: JNL(targ); break; - case LIR_lei: JNLE(targ); break; - case LIR_gti: JNG(targ); break; - case LIR_gei: JNGE(targ); break; - case LIR_ltui: JNB(targ); break; - case LIR_leui: JNBE(targ); break; - case LIR_gtui: JNA(targ); break; - case LIR_geui: JNAE(targ); break; - default: NanoAssert(0); break; - } - } else { - // op == LIR_xt/LIR_jt - switch (cond->opcode()) { - case LIR_eqi: JE(targ); break; - case LIR_lti: JL(targ); break; - case LIR_lei: JLE(targ); break; - case LIR_gti: JG(targ); break; - case LIR_gei: JGE(targ); break; - case LIR_ltui: JB(targ); break; - case LIR_leui: JBE(targ); break; - case LIR_gtui: JA(targ); break; - case LIR_geui: JAE(targ); break; - default: NanoAssert(0); break; - } - } - return Branches(_nIns); - } - - Branches Assembler::asm_branch(bool branchOnFalse, LIns* cond, NIns* targ) - { - Branches branches = asm_branch_helper(branchOnFalse, cond, targ); - asm_cmp(cond); - return branches; - } - - NIns* Assembler::asm_branch_ov(LOpcode, NIns* target) - { - JO(target); - return _nIns; - } - - void Assembler::asm_jtbl(LIns* ins, NIns** table) - { - Register indexreg = findRegFor(ins->oprnd1(), GpRegs); - JMP_indexed(indexreg, 2, table); - } - - void Assembler::asm_cmp(LIns *cond) - { - isCmpDOpcode(cond->opcode()) ? asm_cmpd(cond) : asm_cmpi(cond); - } - - // This generates a 'test' or 'cmp' instruction for a condition, which - // causes the condition codes to be set appropriately. It's used with - // conditional branches, conditional moves, and when generating - // conditional values. For example: - // - // LIR: eq1 = eq a, 0 - // LIR: xf1: xf eq1 -> ... - // asm: test edx, edx # generated by this function - // asm: je ... - // - // If this is the only use of eq1, then on entry 'cond' is *not* marked as - // used, and we do not allocate a register for it. That's because its - // result ends up in the condition codes rather than a normal register. - // This doesn't get recorded in the regstate and so the asm code that - // consumes the result (eg. a conditional branch like 'je') must follow - // shortly after. - // - // If eq1 is instead used again later, we will also generate code - // (eg. in asm_cond()) to compute it into a normal register, something - // like this: - // - // LIR: eq1 = eq a, 0 - // LIR: test edx, edx - // asm: sete ebx - // asm: movzx ebx, ebx - // - // In this case we end up computing the condition twice, but that's ok, as - // it's just as short as testing eq1's value in the code generated for the - // guard. - // - // WARNING: Because the condition code update is not recorded in the - // regstate, this function cannot generate any code that will affect the - // condition codes prior to the generation of the test/cmp, because any - // such code will be run after the test/cmp but before the instruction - // that consumes the condition code. And because this function calls - // findRegFor() before the test/cmp is generated, and findRegFor() calls - // asm_restore(), that means that asm_restore() cannot generate code which - // affects the condition codes. - // - void Assembler::asm_cmpi(LIns *cond) - { - LIns* lhs = cond->oprnd1(); - LIns* rhs = cond->oprnd2(); - - NanoAssert(lhs->isI() && rhs->isI()); - - // Ready to issue the compare. - if (rhs->isImmI()) { - int c = rhs->immI(); - // findRegFor() can call asm_restore() -- asm_restore() better not - // disturb the CCs! - Register r = findRegFor(lhs, GpRegs); - if (c == 0 && cond->isop(LIR_eqi)) { - bool canSkipTest = lhs->isop(LIR_andi) || lhs->isop(LIR_ori); - if (canSkipTest) { - // Setup a short-lived reader to do lookahead; does no - // optimisations but that should be good enough for this - // simple case, something like this: - // - // a = andi x, y # lhs - // eq1 = eq a, 0 # cond - // xt eq1 # currIns - // - // Note that we don't have to worry about lookahead - // hitting the start of the buffer, because read() will - // just return LIR_start repeatedly in that case. - // - LirReader lookahead(currIns); - canSkipTest = currIns == lookahead.read() && - cond == lookahead.read() && - lhs == lookahead.read(); - } - if (canSkipTest) { - // Do nothing. At run-time, 'lhs' will have just computed - // by an i386 instruction that sets ZF for us ('and' or - // 'or'), so we don't have to do it ourselves. - } else { - TEST(r, r); // sets ZF according to the value of 'lhs' - } - } else { - CMPi(r, c); - } - } else { - Register ra, rb; - findRegFor2(GpRegs, lhs, ra, GpRegs, rhs, rb); - CMP(ra, rb); - } - } - - void Assembler::asm_condd(LIns* ins) - { - LOpcode opcode = ins->opcode(); - Register r = prepareResultReg(ins, AllowableByteRegs); - - // SETcc only sets low 8 bits, so extend - MOVZX8(r,r); - - if (_config.i386_sse2) { - // LIR_ltd and LIR_gtd are handled by the same case because - // asm_cmpd() converts LIR_ltd(a,b) to LIR_gtd(b,a). Likewise - // for LIR_led/LIR_ged. - switch (opcode) { - case LIR_eqd: - if (ins->oprnd1() == ins->oprnd2()) { - SETNP(r); - } else { - // result = ZF & !PF, must do logic on flags - AND8R(r); // and rl,rh rl &= rh - SETNPH(r); // setnp rh rh = !PF - SETE(r); // sete rl rl = ZF - } - break; - case LIR_ltd: - case LIR_gtd: SETA(r); break; - case LIR_led: - case LIR_ged: SETAE(r); break; - default: NanoAssert(0); break; - } - } else { - SETNP(r); - } - - freeResourcesOf(ins); - - asm_cmpd(ins); - } - - void Assembler::asm_cond(LIns* ins) - { - LOpcode op = ins->opcode(); - - Register r = prepareResultReg(ins, AllowableByteRegs); - - // SETcc only sets low 8 bits, so extend - MOVZX8(r,r); - switch (op) { - case LIR_eqi: SETE(r); break; - case LIR_lti: SETL(r); break; - case LIR_lei: SETLE(r); break; - case LIR_gti: SETG(r); break; - case LIR_gei: SETGE(r); break; - case LIR_ltui: SETB(r); break; - case LIR_leui: SETBE(r); break; - case LIR_gtui: SETA(r); break; - case LIR_geui: SETAE(r); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - - asm_cmpi(ins); - } - - // Two example cases for "ins = add lhs, rhs". '*' lines are those - // generated in this function. - // - // asm: define lhs into rr - // asm: define rhs into rb - // ... - // * asm: add rr, rb - // * asm: spill rr if necessary - // ... no more uses of lhs in rr... - // - // asm: define lhs into ra - // asm: define rhs into rb - // ... - // * asm: mov rr, ra - // * asm: add rr, rb - // * asm: spill rr if necessary - // ... some uses of lhs in ra... - // - void Assembler::asm_arith(LIns* ins) - { - LOpcode op = ins->opcode(); - - // First special case. - if (op == LIR_modi) { - asm_div_mod(ins); - return; - } - - LIns* lhs = ins->oprnd1(); - LIns* rhs = ins->oprnd2(); - - // Second special case. - // XXX: bug 547125: don't need this once LEA is used for LIR_addi in all cases below - if (op == LIR_addi && lhs->isop(LIR_allocp) && rhs->isImmI()) { - // LIR_addi(LIR_allocp, LIR_immi) -- use lea. - Register rr = prepareResultReg(ins, GpRegs); - int d = findMemFor(lhs) + rhs->immI(); - - LEA(rr, d, FP); - - freeResourcesOf(ins); - - return; - } - - bool isConstRhs; - RegisterMask allow = GpRegs; - Register rb = UnspecifiedReg; - - switch (op) { - case LIR_divi: - // Nb: if the div feeds into a mod it will be handled by - // asm_div_mod() rather than here. - isConstRhs = false; - rb = findRegFor(rhs, (GpRegs & ~(rmask(rEAX)|rmask(rEDX)))); - allow = rmask(rEAX); - evictIfActive(rEDX); - break; - case LIR_muli: - case LIR_muljovi: - case LIR_mulxovi: - isConstRhs = false; - if (lhs != rhs) { - rb = findRegFor(rhs, allow); - allow &= ~rmask(rb); - } - break; - case LIR_lshi: - case LIR_rshi: - case LIR_rshui: - isConstRhs = rhs->isImmI(); - if (!isConstRhs) { - rb = findSpecificRegFor(rhs, rECX); - allow &= ~rmask(rb); - } - break; - default: - isConstRhs = rhs->isImmI(); - if (!isConstRhs && lhs != rhs) { - rb = findRegFor(rhs, allow); - allow &= ~rmask(rb); - } - break; - } - - // Somewhere for the result of 'ins'. - Register rr = prepareResultReg(ins, allow); - - // If 'lhs' isn't in a register, it can be clobbered by 'ins'. - Register ra = lhs->isInReg() ? lhs->getReg() : rr; - - if (!isConstRhs) { - if (lhs == rhs) - rb = ra; - - switch (op) { - case LIR_addi: - case LIR_addjovi: - case LIR_addxovi: ADD(rr, rb); break; // XXX: bug 547125: could use LEA for LIR_addi - case LIR_subi: - case LIR_subjovi: - case LIR_subxovi: SUB(rr, rb); break; - case LIR_muli: - case LIR_muljovi: - case LIR_mulxovi: IMUL(rr, rb); break; - case LIR_andi: AND(rr, rb); break; - case LIR_ori: OR( rr, rb); break; - case LIR_xori: XOR(rr, rb); break; - case LIR_lshi: SHL(rr, rb); break; - case LIR_rshi: SAR(rr, rb); break; - case LIR_rshui: SHR(rr, rb); break; - case LIR_divi: - DIV(rb); - CDQ(); // sign-extend rEAX into rEDX:rEAX - break; - default: NanoAssert(0); break; - } - - } else { - int c = rhs->immI(); - switch (op) { - case LIR_addi: - // this doesn't set cc's, only use it when cc's not required. - LEA(rr, c, ra); - ra = rr; // suppress mov - break; - case LIR_addjovi: - case LIR_addxovi: ADDi(rr, c); break; - case LIR_subi: - case LIR_subjovi: - case LIR_subxovi: SUBi(rr, c); break; - case LIR_andi: ANDi(rr, c); break; - case LIR_ori: ORi( rr, c); break; - case LIR_xori: XORi(rr, c); break; - case LIR_lshi: SHLi(rr, c); break; - case LIR_rshi: SARi(rr, c); break; - case LIR_rshui: SHRi(rr, c); break; - default: NanoAssert(0); break; - } - } - - if (rr != ra) - MR(rr, ra); - - freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(lhs, ra); - } - } - - // Generates code for a LIR_modi(LIR_divi(divL, divR)) sequence. - void Assembler::asm_div_mod(LIns* mod) - { - LIns* div = mod->oprnd1(); - - // LIR_modi expects the LIR_divi to be near (no interference from the register allocator). - NanoAssert(mod->isop(LIR_modi)); - NanoAssert(div->isop(LIR_divi)); - - LIns* divL = div->oprnd1(); - LIns* divR = div->oprnd2(); - - prepareResultReg(mod, rmask(rEDX)); - prepareResultReg(div, rmask(rEAX)); - - Register rDivR = findRegFor(divR, (GpRegs & ~(rmask(rEAX)|rmask(rEDX)))); - Register rDivL = divL->isInReg() ? divL->getReg() : rEAX; - - DIV(rDivR); - CDQ(); // sign-extend rEAX into rEDX:rEAX - if (rEAX != rDivL) - MR(rEAX, rDivL); - - freeResourcesOf(mod); - freeResourcesOf(div); - if (!divL->isInReg()) { - NanoAssert(rDivL == rEAX); - findSpecificRegForUnallocated(divL, rEAX); - } - } - - // Two example cases for "ins = neg lhs". Lines marked with '*' are - // generated in this function. - // - // asm: define lhs into rr - // ... - // * asm: neg rr - // * asm: spill rr if necessary - // ... no more uses of lhs in rr... - // - // - // asm: define lhs into ra - // ... - // * asm: mov rr, ra - // * asm: neg rr - // * asm: spill rr if necessary - // ... more uses of lhs in ra... - // - void Assembler::asm_neg_not(LIns* ins) - { - LIns* lhs = ins->oprnd1(); - - Register rr = prepareResultReg(ins, GpRegs); - - // If 'lhs' isn't in a register, it can be clobbered by 'ins'. - Register ra = lhs->isInReg() ? lhs->getReg() : rr; - - if (ins->isop(LIR_noti)) { - NOT(rr); - } else { - NanoAssert(ins->isop(LIR_negi)); - NEG(rr); - } - if (rr != ra) - MR(rr, ra); - - freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(lhs, ra); - } - } - - void Assembler::asm_load32(LIns* ins) - { - LOpcode op = ins->opcode(); - LIns* base = ins->oprnd1(); - int32_t d = ins->disp(); - - Register rr = prepareResultReg(ins, GpRegs); - - if (base->isImmI()) { - intptr_t addr = base->immI(); - addr += d; - switch (op) { - case LIR_lduc2ui: LD8Zdm( rr, addr); break; - case LIR_ldc2i: LD8Sdm( rr, addr); break; - case LIR_ldus2ui: LD16Zdm(rr, addr); break; - case LIR_lds2i: LD16Sdm(rr, addr); break; - case LIR_ldi: LDdm( rr, addr); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - - } else if (base->opcode() == LIR_addp) { - LIns* index; - int scale; - getBaseIndexScale(base, &base, &index, &scale); - - // If 'base' isn't in a register, it can be clobbered by 'ins'. - // Likewise for 'rhs', but we try it with 'base' first. - Register rb, ri; - // @todo -- If base and/or index is const, we could eliminate a register use. - if (!base->isInReg()) { - rb = rr; - ri = findRegFor(index, GpRegs & ~(rmask(rb))); - - } else { - rb = base->getReg(); - NanoAssert(rb != rr); - ri = index->isInReg() ? findRegFor(index, GpRegs & ~(rmask(rb))) : rr; - } - - switch (op) { - case LIR_lduc2ui: LD8Zsib( rr, d, rb, ri, scale); break; - case LIR_ldc2i: LD8Ssib( rr, d, rb, ri, scale); break; - case LIR_ldus2ui: LD16Zsib(rr, d, rb, ri, scale); break; - case LIR_lds2i: LD16Ssib(rr, d, rb, ri, scale); break; - case LIR_ldi: LDsib( rr, d, rb, ri, scale); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - if (!base->isInReg()) { - NanoAssert(rb == rr); - findSpecificRegForUnallocated(base, rb); - } else if (!index->isInReg()) { - NanoAssert(ri == rr); - findSpecificRegForUnallocated(index, ri); - } - - } else { - Register ra = getBaseReg(base, d, GpRegs); - - switch (op) { - case LIR_lduc2ui: LD8Z( rr, d, ra); break; - case LIR_ldc2i: LD8S( rr, d, ra); break; - case LIR_ldus2ui: LD16Z(rr, d, ra); break; - case LIR_lds2i: LD16S(rr, d, ra); break; - case LIR_ldi: LD( rr, d, ra); break; - default: NanoAssert(0); break; - } - - freeResourcesOf(ins); - if (!base->isop(LIR_allocp) && !base->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(base, ra); - } - } - } - - void Assembler::asm_cmov(LIns* ins) - { - LIns* condval = ins->oprnd1(); - LIns* iftrue = ins->oprnd2(); - LIns* iffalse = ins->oprnd3(); - - NanoAssert(condval->isCmp()); - NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) || - (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD())); - - if (!_config.i386_sse2 && ins->isop(LIR_cmovd)) { - // See the SSE2 case below for an explanation of the subtleties here. - debug_only( Register rr = ) prepareResultReg(ins, x87Regs); - NanoAssert(FST0 == rr); - NanoAssert(!iftrue->isInReg() && !iffalse->isInReg()); - - NIns* target = _nIns; - - if (iffalse->isImmD()) { - asm_immd(FST0, iffalse->immDasQ(), iffalse->immD(), /*canClobberCCs*/false); - } else { - int df = findMemFor(iffalse); - FLDQ(df, FP); - } - FSTP(FST0); // pop the stack - asm_branch_helper(false, condval, target); - - NanoAssert(ins->getReg() == rr); - freeResourcesOf(ins); - if (!iftrue->isInReg()) - findSpecificRegForUnallocated(iftrue, FST0); - - asm_cmp(condval); - - return; - } - - RegisterMask allow = ins->isD() ? XmmRegs : GpRegs; - Register rr = prepareResultReg(ins, allow); - Register rf = findRegFor(iffalse, allow & ~rmask(rr)); - - if (ins->isop(LIR_cmovd)) { - // The obvious way to handle this is as follows: - // - // mov rr, rt # only needed if rt is live afterwards - // do comparison - // jt end - // mov rr, rf - // end: - // - // The problem with this is that doing the comparison can cause - // registers to be evicted, possibly including 'rr', which holds - // 'ins'. And that screws things up. So instead we do this: - // - // do comparison - // mov rr, rt # only needed if rt is live afterwards - // jt end - // mov rr, rf - // end: - // - // Putting the 'mov' between the comparison and the jump is ok - // because move instructions don't modify the condition codes. - // - NIns* target = _nIns; - asm_nongp_copy(rr, rf); - asm_branch_helper(false, condval, target); - - // If 'iftrue' isn't in a register, it can be clobbered by 'ins'. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rr; - - if (rr != rt) - asm_nongp_copy(rr, rt); - - NanoAssert(ins->getReg() == rr); - freeResourcesOf(ins); - if (!iftrue->isInReg()) { - NanoAssert(rt == rr); - findSpecificRegForUnallocated(iftrue, rr); - } - - asm_cmp(condval); - return; - } - - // If 'iftrue' isn't in a register, it can be clobbered by 'ins'. - Register rt = iftrue->isInReg() ? iftrue->getReg() : rr; - NanoAssert(ins->isop(LIR_cmovi)); - - // WARNING: We cannot generate any code that affects the condition - // codes between the MRcc generation here and the asm_cmpi() call - // below. See asm_cmpi() for more details. - switch (condval->opcode()) { - // Note that these are all opposites... - case LIR_eqi: MRNE(rr, rf); break; - case LIR_lti: MRGE(rr, rf); break; - case LIR_lei: MRG( rr, rf); break; - case LIR_gti: MRLE(rr, rf); break; - case LIR_gei: MRL( rr, rf); break; - case LIR_ltui: MRAE(rr, rf); break; - case LIR_leui: MRA( rr, rf); break; - case LIR_gtui: MRBE(rr, rf); break; - case LIR_geui: MRB( rr, rf); break; - default: NanoAssert(0); break; - } - - if (rr != rt) - MR(rr, rt); - - NanoAssert(ins->getReg() == rr); - freeResourcesOf(ins); - if (!iftrue->isInReg()) { - NanoAssert(rt == rr); - findSpecificRegForUnallocated(iftrue, rr); - } - - asm_cmp(condval); - } - - void Assembler::asm_param(LIns* ins) - { - uint32_t arg = ins->paramArg(); - uint32_t kind = ins->paramKind(); - if (kind == 0) { - // ordinary param - AbiKind abi = _thisfrag->lirbuf->abi; - uint32_t abi_regcount = max_abi_regs[abi]; - // argRegs must have as many elements as the largest argument register - // requirement of an abi. Currently, this is 2, for ABI_FASTCALL. See - // the definition of max_abi_regs earlier in this file. The following - // assertion reflects this invariant: - NanoAssert(abi_regcount <= sizeof(argRegs)/sizeof(argRegs[0])); - if (arg < abi_regcount) { - // Incoming arg in register. - prepareResultReg(ins, rmask(argRegs[arg])); - // No code to generate. - } else { - // Incoming arg is on stack, and rEBP points nearby (see genPrologue()). - Register r = prepareResultReg(ins, GpRegs); - int d = (arg - abi_regcount) * sizeof(intptr_t) + 8; - LD(r, d, FP); - } - } else { - // Saved param. - prepareResultReg(ins, rmask(savedRegs[arg])); - // No code to generate. - } - freeResourcesOf(ins); - } - - void Assembler::asm_immi(LIns* ins) - { - Register rr = prepareResultReg(ins, GpRegs); - - asm_immi(rr, ins->immI(), /*canClobberCCs*/true); - - freeResourcesOf(ins); - } - - void Assembler::asm_immi(Register r, int32_t val, bool canClobberCCs) - { - if (val == 0 && canClobberCCs) - XOR(r, r); - else - LDi(r, val); - } - - void Assembler::asm_immd(Register r, uint64_t q, double d, bool canClobberCCs) - { - // Floats require non-standard handling. There is no load-64-bit-immediate - // instruction on i386, so in the general case, we must load it from memory. - // This is unlike most other LIR operations which can be computed directly - // in a register. We can special-case 0.0 and various other small ints - // (1.0 on x87, any int32_t value on SSE2), but for all other values, we - // allocate an 8-byte chunk via dataAlloc and load from there. Note that - // this implies that floats never require spill area, since they will always - // be rematerialized from const data (or inline instructions in the special cases). - - if (rmask(r) & XmmRegs) { - if (q == 0) { - // test (int64)0 since -0.0 == 0.0 - SSE_XORPDr(r, r); - } else if (d && d == (int)d && canClobberCCs) { - // can fit in 32bits? then use cvt which is faster - Register tr = registerAllocTmp(GpRegs); - SSE_CVTSI2SD(r, tr); - SSE_XORPDr(r, r); // zero r to ensure no dependency stalls - asm_immi(tr, (int)d, canClobberCCs); - } else { - const uint64_t* p = findImmDFromPool(q); - LDSDm(r, (const double*)p); - } - } else { - NanoAssert(r == FST0); - if (q == 0) { - // test (int64)0 since -0.0 == 0.0 - FLDZ(); - } else if (d == 1.0) { - FLD1(); - } else { - const uint64_t* p = findImmDFromPool(q); - FLDQdm((const double*)p); - } - } - } - - void Assembler::asm_immd(LIns* ins) - { - NanoAssert(ins->isImmD()); - if (ins->isInReg()) { - Register rr = ins->getReg(); - NanoAssert(rmask(rr) & FpRegs); - asm_immd(rr, ins->immDasQ(), ins->immD(), /*canClobberCCs*/true); - } else { - // Do nothing, will be rematerialized when necessary. - } - - freeResourcesOf(ins); - } - - // negateMask is used by asm_fneg. -#if defined __SUNPRO_CC - // From Sun Studio C++ Readme: #pragma align inside namespace requires mangled names. - // Initialize here to avoid multithreading contention issues during initialization. - static uint32_t negateMask_temp[] = {0, 0, 0, 0, 0, 0, 0}; - - static uint32_t* negateMaskInit() - { - uint32_t* negateMask = (uint32_t*)alignUp(negateMask_temp, 16); - negateMask[1] = 0x80000000; - return negateMask; - } - - static uint32_t *negateMask = negateMaskInit(); -#else - static const AVMPLUS_ALIGN16(uint32_t) negateMask[] = {0,0x80000000,0,0}; -#endif - - void Assembler::asm_fneg(LIns* ins) - { - LIns *lhs = ins->oprnd1(); - - if (_config.i386_sse2) { - Register rr = prepareResultReg(ins, XmmRegs); - - // If 'lhs' isn't in a register, it can be clobbered by 'ins'. - Register ra; - if (!lhs->isInReg()) { - ra = rr; - } else if (!(rmask(lhs->getReg()) & XmmRegs)) { - // We need to evict lhs from x87Regs, which then puts us in - // the same situation as the !isInReg() case. - evict(lhs); - ra = rr; - } else { - ra = lhs->getReg(); - } - - SSE_XORPD(rr, negateMask); - - if (rr != ra) - SSE_MOVSD(rr, ra); - - freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(lhs, ra); - } - - } else { - debug_only( Register rr = ) prepareResultReg(ins, x87Regs); - NanoAssert(FST0 == rr); - - NanoAssert(!lhs->isInReg() || FST0 == lhs->getReg()); - - FCHS(); - - freeResourcesOf(ins); - if (!lhs->isInReg()) - findSpecificRegForUnallocated(lhs, FST0); - } - } - - void Assembler::asm_arg(ArgType ty, LIns* ins, Register r, int32_t& stkd) - { - // If 'r' is known, then that's the register we have to put 'ins' - // into. - - if (ty == ARGTYPE_I || ty == ARGTYPE_UI) { - if (r != UnspecifiedReg) { - if (ins->isImmI()) { - // Rematerialize the constant. - asm_immi(r, ins->immI(), /*canClobberCCs*/true); - } else if (ins->isInReg()) { - if (r != ins->getReg()) - MR(r, ins->getReg()); - } else if (ins->isInAr()) { - int d = arDisp(ins); - NanoAssert(d != 0); - if (ins->isop(LIR_allocp)) { - LEA(r, d, FP); - } else { - LD(r, d, FP); - } - - } else { - // This is the last use, so fine to assign it - // to the scratch reg, it's dead after this point. - findSpecificRegForUnallocated(ins, r); - } - } - else { - if (_config.i386_fixed_esp) - asm_stkarg(ins, stkd); - else - asm_pusharg(ins); - } - - } else { - NanoAssert(ty == ARGTYPE_D); - asm_farg(ins, stkd); - } - } - - void Assembler::asm_pusharg(LIns* ins) - { - // arg goes on stack - if (!ins->isExtant() && ins->isImmI()) { - PUSHi(ins->immI()); // small const we push directly - } else if (!ins->isExtant() || ins->isop(LIR_allocp)) { - Register ra = findRegFor(ins, GpRegs); - PUSHr(ra); - } else if (ins->isInReg()) { - PUSHr(ins->getReg()); - } else { - NanoAssert(ins->isInAr()); - PUSHm(arDisp(ins), FP); - } - } - - void Assembler::asm_stkarg(LIns* ins, int32_t& stkd) - { - // arg goes on stack - if (!ins->isExtant() && ins->isImmI()) - { - // small const we push directly - STi(SP, stkd, ins->immI()); - } - else { - Register ra; - if (!ins->isInReg() || ins->isop(LIR_allocp)) - ra = findRegFor(ins, GpRegs & (~SavedRegs)); - else - ra = ins->getReg(); - ST(SP, stkd, ra); - } - - stkd += sizeof(int32_t); - } - - void Assembler::asm_farg(LIns* ins, int32_t& stkd) - { - NanoAssert(ins->isD()); - Register r = findRegFor(ins, FpRegs); - if (rmask(r) & XmmRegs) { - SSE_STQ(stkd, SP, r); - } else { - FSTPQ(stkd, SP); - - // 22Jul09 rickr - Enabling the evict causes a 10% slowdown on primes - // - // evict() triggers a very expensive fstpq/fldq pair around the store. - // We need to resolve the bug some other way. - // - // see https://bugzilla.mozilla.org/show_bug.cgi?id=491084 - - // It's possible that the same LIns* with r=FST0 will appear in the argument list more - // than once. In this case FST0 will not have been evicted and the multiple pop - // actions will unbalance the FPU stack. A quick fix is to always evict FST0 manually. - NanoAssert(r == FST0); - NanoAssert(ins == _allocator.getActive(r)); - evict(ins); - } - if (!_config.i386_fixed_esp) - SUBi(rESP, 8); - - stkd += sizeof(double); - } - - void Assembler::asm_fop(LIns* ins) - { - LOpcode op = ins->opcode(); - if (_config.i386_sse2) - { - LIns *lhs = ins->oprnd1(); - LIns *rhs = ins->oprnd2(); - - RegisterMask allow = XmmRegs; - Register rb = UnspecifiedReg; - if (lhs != rhs) { - rb = findRegFor(rhs, allow); - allow &= ~rmask(rb); - } - - Register rr = prepareResultReg(ins, allow); - - // If 'lhs' isn't in a register, it can be clobbered by 'ins'. - Register ra; - if (!lhs->isInReg()) { - ra = rr; - - } else if (!(rmask(lhs->getReg()) & XmmRegs)) { - NanoAssert(lhs->getReg() == FST0); - - // We need to evict lhs from x87Regs, which then puts us in - // the same situation as the !isInReg() case. - evict(lhs); - ra = rr; - - } else { - ra = lhs->getReg(); - NanoAssert(rmask(ra) & XmmRegs); - } - - if (lhs == rhs) - rb = ra; - - switch (op) { - case LIR_addd: SSE_ADDSD(rr, rb); break; - case LIR_subd: SSE_SUBSD(rr, rb); break; - case LIR_muld: SSE_MULSD(rr, rb); break; - case LIR_divd: SSE_DIVSD(rr, rb); break; - default: NanoAssert(0); - } - - if (rr != ra) - SSE_MOVSD(rr, ra); - - freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(lhs, ra); - } - } - else - { - // We swap lhs/rhs on purpose here, it works out better with - // only one fpu reg -- we can use divr/subr. - LIns* rhs = ins->oprnd1(); - LIns* lhs = ins->oprnd2(); - debug_only( Register rr = ) prepareResultReg(ins, rmask(FST0)); - NanoAssert(FST0 == rr); - NanoAssert(!lhs->isInReg() || FST0 == lhs->getReg()); - - if (rhs->isImmD()) { - const uint64_t* p = findImmDFromPool(rhs->immDasQ()); - - switch (op) { - case LIR_addd: FADDdm( (const double*)p); break; - case LIR_subd: FSUBRdm((const double*)p); break; - case LIR_muld: FMULdm( (const double*)p); break; - case LIR_divd: FDIVRdm((const double*)p); break; - default: NanoAssert(0); - } - - } else { - int db = findMemFor(rhs); - - switch (op) { - case LIR_addd: FADD( db, FP); break; - case LIR_subd: FSUBR(db, FP); break; - case LIR_muld: FMUL( db, FP); break; - case LIR_divd: FDIVR(db, FP); break; - default: NanoAssert(0); - } - } - freeResourcesOf(ins); - if (!lhs->isInReg()) { - findSpecificRegForUnallocated(lhs, FST0); - } - } - } - - void Assembler::asm_i2d(LIns* ins) - { - LIns* lhs = ins->oprnd1(); - - Register rr = prepareResultReg(ins, FpRegs); - if (rmask(rr) & XmmRegs) { - // todo support int value in memory - Register ra = findRegFor(lhs, GpRegs); - SSE_CVTSI2SD(rr, ra); - SSE_XORPDr(rr, rr); // zero rr to ensure no dependency stalls - } else { - int d = findMemFor(lhs); - FILD(d, FP); - } - - freeResourcesOf(ins); - } - - void Assembler::asm_ui2d(LIns* ins) - { - LIns* lhs = ins->oprnd1(); - - Register rr = prepareResultReg(ins, FpRegs); - if (rmask(rr) & XmmRegs) { - Register rt = registerAllocTmp(GpRegs); - - // Technique inspired by gcc disassembly. Edwin explains it: - // - // rt is 0..2^32-1 - // - // sub rt,0x80000000 - // - // Now rt is -2^31..2^31-1, i.e. the range of int, but not the same value - // as before. - // - // cvtsi2sd rr,rt - // - // rr is now a double with the int value range. - // - // addsd rr, 2147483648.0 - // - // Adding back double(0x80000000) makes the range 0..2^32-1. - - static const double k_NEGONE = 2147483648.0; - SSE_ADDSDm(rr, &k_NEGONE); - - SSE_CVTSI2SD(rr, rt); - SSE_XORPDr(rr, rr); // zero rr to ensure no dependency stalls - - if (lhs->isInRegMask(GpRegs)) { - Register ra = lhs->getReg(); - LEA(rt, 0x80000000, ra); - - } else { - const int d = findMemFor(lhs); - SUBi(rt, 0x80000000); - LD(rt, d, FP); - } - - } else { - // Use space just below rESP and use PUSH to avoid writing - // past the end of the stack, see bug 590553. - Register ra = findRegFor(lhs, GpRegs); - NanoAssert(rr == FST0); - ADDi(SP, 8); // fix up the stack - FILDQ(0, SP); // convert int64 to double - PUSHr(ra); // low 32 bits = unsigned value - PUSHi(0); // high 32 bits = 0 - } - - freeResourcesOf(ins); - } - - void Assembler::asm_d2i(LIns* ins) - { - LIns *lhs = ins->oprnd1(); - - if (_config.i386_sse2) { - Register rr = prepareResultReg(ins, GpRegs); - Register ra = findRegFor(lhs, XmmRegs); - SSE_CVTTSD2SI(rr, ra); - } else { - bool pop = !lhs->isInReg(); - findSpecificRegFor(lhs, FST0); - if (ins->isInReg()) - evict(ins); - int d = findMemFor(ins); - FIST(pop, d, FP); - } - - freeResourcesOf(ins); - } - - void Assembler::asm_nongp_copy(Register rd, Register rs) - { - if ((rmask(rd) & XmmRegs) && (rmask(rs) & XmmRegs)) { - // xmm -> xmm - SSE_MOVSD(rd, rs); - } else if ((rmask(rd) & GpRegs) && (rmask(rs) & XmmRegs)) { - // xmm -> gp - SSE_MOVD(rd, rs); - } else { - NanoAssertMsgf(false, "bad asm_nongp_copy(%s, %s)", gpn(rd), gpn(rs)); - } - } - - Branches Assembler::asm_branchd_helper(bool branchOnFalse, LIns* cond, NIns *targ) - { - NIns* patch1 = NULL; - NIns* patch2 = NULL; - LOpcode opcode = cond->opcode(); - - if (_config.i386_sse2) { - // LIR_ltd and LIR_gtd are handled by the same case because - // asm_cmpd() converts LIR_ltd(a,b) to LIR_gtd(b,a). Likewise - // for LIR_led/LIR_ged. - if (branchOnFalse) { - // op == LIR_xf - switch (opcode) { - case LIR_eqd: - if (cond->oprnd1() == cond->oprnd2()) { - JP(targ); - } else { - JP(targ); // unordered - patch1 = _nIns; - JNE(targ); - patch2 = _nIns; - } - break; - case LIR_ltd: - case LIR_gtd: JNA(targ); break; - case LIR_led: - case LIR_ged: JNAE(targ); break; - default: NanoAssert(0); break; - } - } else { - // op == LIR_xt - switch (opcode) { - case LIR_eqd: - if (cond->oprnd1() == cond->oprnd2()) { - JNP(targ); - } else { - // jp skip (2byte) - // je target - // skip: ... - underrunProtect(16); // underrun of 7 needed but we write 2 instr --> 16 - NIns *skip = _nIns; - JE(targ); - patch1 = _nIns; - JP(skip); // unordered - } - break; - case LIR_ltd: - case LIR_gtd: JA(targ); break; - case LIR_led: - case LIR_ged: JAE(targ); break; - default: NanoAssert(0); break; - } - } - } else { - if (branchOnFalse) - JP(targ); - else - JNP(targ); - } - - if (!patch1) - patch1 = _nIns; - - return Branches(patch1, patch2); - } - - // WARNING: This function cannot generate any code that will affect the - // condition codes prior to the generation of the - // ucomisd/fcompp/fcmop/fcom. See asm_cmpi() for more details. - void Assembler::asm_cmpd(LIns *cond) - { - LOpcode condop = cond->opcode(); - NanoAssert(isCmpDOpcode(condop)); - LIns* lhs = cond->oprnd1(); - LIns* rhs = cond->oprnd2(); - NanoAssert(lhs->isD() && rhs->isD()); - - if (_config.i386_sse2) { - // First, we convert (a < b) into (b > a), and (a <= b) into (b >= a). - if (condop == LIR_ltd) { - condop = LIR_gtd; - LIns* t = lhs; lhs = rhs; rhs = t; - } else if (condop == LIR_led) { - condop = LIR_ged; - LIns* t = lhs; lhs = rhs; rhs = t; - } - - // LIR_eqd, if lhs == rhs: - // ucomisd ZPC outcome (SETNP/JNP succeeds if P==0) - // ------- --- ------- - // UNORDERED 111 SETNP/JNP fails - // EQUAL 100 SETNP/JNP succeeds - // - // LIR_eqd, if lhs != rhs; - // ucomisd ZPC outcome (SETP/JP succeeds if P==0, - // SETE/JE succeeds if Z==0) - // ------- --- ------- - // UNORDERED 111 SETP/JP succeeds (and skips to fail target) - // EQUAL 100 SETP/JP fails, SETE/JE succeeds - // GREATER_THAN 000 SETP/JP fails, SETE/JE fails - // LESS_THAN 001 SETP/JP fails, SETE/JE fails - // - // LIR_gtd: - // ucomisd ZPC outcome (SETA/JA succeeds if CZ==00) - // ------- --- ------- - // UNORDERED 111 SETA/JA fails - // EQUAL 100 SETA/JA fails - // GREATER_THAN 000 SETA/JA succeeds - // LESS_THAN 001 SETA/JA fails - // - // LIR_ged: - // ucomisd ZPC outcome (SETAE/JAE succeeds if C==0) - // ------- --- ------- - // UNORDERED 111 SETAE/JAE fails - // EQUAL 100 SETAE/JAE succeeds - // GREATER_THAN 000 SETAE/JAE succeeds - // LESS_THAN 001 SETAE/JAE fails - - Register ra, rb; - findRegFor2(XmmRegs, lhs, ra, XmmRegs, rhs, rb); - SSE_UCOMISD(ra, rb); - - } else { - // First, we convert (a > b) into (b < a), and (a >= b) into (b <= a). - // Note that this is the opposite of the sse2 conversion above. - if (condop == LIR_gtd) { - condop = LIR_ltd; - LIns* t = lhs; lhs = rhs; rhs = t; - } else if (condop == LIR_ged) { - condop = LIR_led; - LIns* t = lhs; lhs = rhs; rhs = t; - } - - // FNSTSW_AX puts the flags into AH like so: B:C3:TOP3:TOP2:TOP1:C2:C1:C0. - // Furthermore, fcom/fcomp/fcompp sets C3:C2:C0 the same values - // that Z:P:C are set by ucomisd, and the relative positions in AH - // line up. (Someone at Intel has a sense of humour.) Therefore - // we can use the same lahf/test(mask) technique as used in the - // sse2 case above. We could use fcomi/fcomip/fcomipp which set - // ZPC directly and then use LAHF instead of FNSTSW_AX and make - // this code generally more like the sse2 code, but we don't - // because fcomi/fcomip/fcomipp/lahf aren't available on earlier - // x86 machines. - // - // The masks are as follows: - // - LIR_eqd: mask == 0x44 == 0100_0100b, which extracts 0Z00_0P00 from AH. - // - LIR_ltd: mask == 0x05 == 0000_0101b, which extracts 0000_0P0C from AH. - // - LIR_led: mask == 0x41 == 0100_0001b, which extracts 0Z00_000C from AH. - // - // LIR_eqd: - // ucomisd C3:C2:C0 lahf/test(0x44) SZP outcome - // ------- -------- --------- --- ------- - // UNORDERED 111 0100_0100 001 SETNP fails - // EQUAL 100 0100_0000 000 SETNP succeeds - // GREATER_THAN 000 0000_0000 011 SETNP fails - // LESS_THAN 001 0000_0000 011 SETNP fails - // - // LIR_ltd: - // fcom C3:C2:C0 lahf/test(0x05) SZP outcome - // ------- -------- --------- --- ------- - // UNORDERED 111 0000_0101 001 SETNP fails - // EQUAL 100 0000_0000 011 SETNP fails - // GREATER_THAN 000 0000_0000 011 SETNP fails - // LESS_THAN 001 0000_0001 000 SETNP succeeds - // - // LIR_led: - // fcom C3:C2:C0 lahf/test(0x41) SZP outcome - // ------- --- --------- --- ------- - // UNORDERED 111 0100_0001 001 SETNP fails - // EQUAL 100 0100_0000 000 SETNP succeeds - // GREATER_THAN 000 0000_0000 011 SETNP fails - // LESS_THAN 001 0000_0001 010 SETNP succeeds - - int mask = 0; // init to avoid MSVC compile warnings - switch (condop) { - case LIR_eqd: mask = 0x44; break; - case LIR_ltd: mask = 0x05; break; - case LIR_led: mask = 0x41; break; - default: NanoAssert(0); break; - } - - evictIfActive(rEAX); - bool pop = !lhs->isInReg(); - findSpecificRegFor(lhs, FST0); - - if (lhs == rhs) { - // NaN test. - TEST_AH(mask); - FNSTSW_AX(); // requires rEAX to be free - if (pop) - FCOMPP(); - else - FCOMP(); - FLDr(FST0); // DUP - } else { - TEST_AH(mask); - FNSTSW_AX(); // requires rEAX to be free - if (rhs->isImmD()) { - const uint64_t* p = findImmDFromPool(rhs->immDasQ()); - FCOMdm(pop, (const double*)p); - } else { - int d = findMemFor(rhs); - FCOM(pop, d, FP); - } - } - } - } - - // Increment the 32-bit profiling counter at pCtr, without - // changing any registers. - verbose_only( - void Assembler::asm_inc_m32(uint32_t* pCtr) - { - INCLi(int32_t(pCtr)); - } - ) - - void Assembler::nativePageReset() - {} - - void Assembler::nativePageSetup() - { - NanoAssert(!_inExit); - if (!_nIns) - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - - // add some random padding, so functions aren't predictably placed. - if (_config.harden_function_alignment) - { - int32_t pad = _noise->getValue(LARGEST_UNDERRUN_PROT); - underrunProtect(pad); - _nIns -= pad; - VMPI_memset(_nIns, INT3_OP, pad); - PERFM_NVPROF("hardening:func-align", pad); - } - } - - // enough room for n bytes - void Assembler::underrunProtect(int n) - { - NIns *eip = _nIns; - NanoAssertMsg(n<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); - // This may be in a normal code chunk or an exit code chunk. - if (eip - n < codeStart) { - codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); - JMP(eip); - } - } - - void Assembler::asm_insert_random_nop() - { - // one of a random nop instructions - uint32_t r = _noise->getValue(5); - switch(r) - { - case 0: MR(rEAX,rEAX); break; - case 1: MR(rEDI,rEDI); break; - case 2: MR(rECX,rECX); break; - case 3: LEA(rECX,0,rECX); break; - case 4: LEA(rESP,0,rESP); break; - } - } - - void Assembler::asm_ret(LIns* ins) - { - genEpilogue(); - - // Restore rESP from rEBP, undoing SUBi(SP,amt) in the prologue - MR(SP,FP); - - releaseRegisters(); - assignSavedRegs(); - - LIns *val = ins->oprnd1(); - if (ins->isop(LIR_reti)) { - findSpecificRegFor(val, retRegs[0]); - } else { - NanoAssert(ins->isop(LIR_retd)); - findSpecificRegFor(val, FST0); - fpu_pop(); - } - } - - void Assembler::swapCodeChunks() { - if (!_nExitIns) - codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); - SWAP(NIns*, _nIns, _nExitIns); - SWAP(NIns*, codeStart, exitStart); - SWAP(NIns*, codeEnd, exitEnd); - verbose_only( SWAP(size_t, codeBytes, exitBytes); ) - } - - #endif /* FEATURE_NANOJIT */ -} diff --git a/deps/mozjs/js/src/nanojit/Nativei386.h b/deps/mozjs/js/src/nanojit/Nativei386.h deleted file mode 100644 index 4a85f5d92f1..00000000000 --- a/deps/mozjs/js/src/nanojit/Nativei386.h +++ /dev/null @@ -1,486 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_Nativei386__ -#define __nanojit_Nativei386__ - -#include "NativeCommon.h" - -#ifdef PERFM -#define DOPROF -#include "../vprof/vprof.h" -#define count_instr() _nvprof("x86",1) -#define count_ret() _nvprof("x86-ret",1); count_instr(); -#define count_push() _nvprof("x86-push",1); count_instr(); -#define count_pop() _nvprof("x86-pop",1); count_instr(); -#define count_st() _nvprof("x86-st",1); count_instr(); -#define count_stq() _nvprof("x86-stq",1); count_instr(); -#define count_ld() _nvprof("x86-ld",1); count_instr(); -#define count_ldq() _nvprof("x86-ldq",1); count_instr(); -#define count_call() _nvprof("x86-call",1); count_instr(); -#define count_calli() _nvprof("x86-calli",1); count_instr(); -#define count_prolog() _nvprof("x86-prolog",1); count_instr(); -#define count_alu() _nvprof("x86-alu",1); count_instr(); -#define count_mov() _nvprof("x86-mov",1); count_instr(); -#define count_fpu() _nvprof("x86-fpu",1); count_instr(); -#define count_jmp() _nvprof("x86-jmp",1); count_instr(); -#define count_jcc() _nvprof("x86-jcc",1); count_instr(); -#define count_fpuld() _nvprof("x86-ldq",1); _nvprof("x86-fpu",1); count_instr() -#define count_aluld() _nvprof("x86-ld",1); _nvprof("x86-alu",1); count_instr() -#define count_alust() _nvprof("x86-ld",1); _nvprof("x86-alu",1); _nvprof("x86-st",1); count_instr() -#define count_pushld() _nvprof("x86-ld",1); _nvprof("x86-push",1); count_instr() -#define count_imt() _nvprof("x86-imt",1) count_instr() -#else -#define count_instr() -#define count_ret() -#define count_push() -#define count_pop() -#define count_st() -#define count_stq() -#define count_ld() -#define count_ldq() -#define count_call() -#define count_calli() -#define count_prolog() -#define count_alu() -#define count_mov() -#define count_fpu() -#define count_jmp() -#define count_jcc() -#define count_fpuld() -#define count_aluld() -#define count_alust() -#define count_pushld() -#define count_imt() -#endif - -namespace nanojit -{ - const int NJ_MAX_REGISTERS = 24; // gpregs, x87 regs, xmm regs - - #define NJ_MAX_STACK_ENTRY 4096 - #define NJ_MAX_PARAMETERS 1 - - #define NJ_USES_IMMD_POOL 1 - - #define NJ_JTBL_SUPPORTED 1 - #define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 - #define NJ_F2I_SUPPORTED 1 - #define NJ_SOFTFLOAT_SUPPORTED 0 - #define NJ_DIVI_SUPPORTED 1 - - // Preserve a 16-byte stack alignment, to support the use of - // SSE instructions like MOVDQA (if not by Tamarin itself, - // then by the C functions it calls). - const int NJ_ALIGN_STACK = 16; - - const int32_t LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect - - typedef uint8_t NIns; - - // Bytes of icache to flush after patch - const size_t LARGEST_BRANCH_PATCH = 16 * sizeof(NIns); - - static const Register - // General purpose 32 bit registers. The names are rEAX, rEBX, etc, - // because EAX, EBX, et al clash with on Solaris (sigh). - // See bug 570726 for details. - rEAX = { 0 }, // return value, scratch - rECX = { 1 }, // this/arg0, scratch - rEDX = { 2 }, // arg1, return-msw, scratch - rEBX = { 3 }, - rESP = { 4 }, // stack pointer - rEBP = { 5 }, // frame pointer - rESI = { 6 }, - rEDI = { 7 }, - - SP = rESP, // alias SP to ESP for convenience - FP = rEBP, // alias FP to EBP for convenience - - // SSE regs come before X87 so we prefer them - XMM0 = { 8 }, - XMM1 = { 9 }, - XMM2 = { 10 }, - XMM3 = { 11 }, - XMM4 = { 12 }, - XMM5 = { 13 }, - XMM6 = { 14 }, - XMM7 = { 15 }, - - // X87 regs - FST0 = { 16 }, - - deprecated_UnknownReg = { 17 }, // XXX: remove eventually, see bug 538924 - UnspecifiedReg = { 17 }; - - static const uint32_t FirstRegNum = 0; - static const uint32_t LastRegNum = 16; - - typedef int RegisterMask; - - static const int NumSavedRegs = 3; - static const RegisterMask SavedRegs = 1<> 8); \ - } \ - void OPCODE3(int32_t opc3) { /* Length: 3 bytes. */ \ - NanoAssert(unsigned(opc3) <= 0xffffff); \ - *(--_nIns) = uint8_t(opc3); \ - *(--_nIns) = uint8_t(opc3 >> 8); \ - *(--_nIns) = uint8_t(opc3 >> 16); \ - } \ - void MODRM(int32_t mod, int32_t ro, int32_t rm) { /* Length: 1 byte. */ \ - NanoAssert(unsigned(mod) < 4 && unsigned(ro) < 8 && unsigned(rm) < 8); \ - *(--_nIns) = uint8_t(mod << 6 | ro << 3 | rm); \ - } \ - void SIB(int32_t s, int32_t i, int32_t b) { /* Length: 1 byte. */ \ - NanoAssert(unsigned(s) < 4 && unsigned(i) < 8 && unsigned(b) < 8); \ - *(--_nIns) = uint8_t(s << 6 | i << 3 | b); \ - } \ - void MODRMr(int32_t d, int32_t s) { /* Length: 1 byte. */ \ - NanoAssert(unsigned(d) < 8 && unsigned(s) < 8); \ - MODRM(3, d, s); \ - }; \ - void MODRMm(int32_t r, int32_t d, Register b); \ - void MODRMsib(int32_t r, Register b, Register i, int32_t s, int32_t d); \ - void MODRMdm(int32_t r, int32_t addr); \ - \ - /* These functions generate entire instructions. */ \ - void ALU0(int32_t o); \ - void ALUm(int32_t c, int32_t r, int32_t d, Register b); \ - void ALUdm(int32_t c, Register r, int32_t addr); \ - void ALUsib(int32_t c, Register r, Register base, Register index, int32_t scale, int32_t disp); \ - void ALUsib16(int32_t c, Register r, Register base, Register index, int32_t scale, int32_t disp); \ - void ALUm16(int32_t c, int32_t r, int32_t d, Register b); \ - void ALU2dm(int32_t c, Register r, int32_t addr); \ - void ALU2m(int32_t c, Register r, int32_t d, Register b); \ - void ALU2sib(int32_t c, Register r, Register base, Register index, int32_t scale, int32_t disp); \ - void ALU(int32_t opc, int32_t d, Register s) { \ - underrunProtect(2); \ - MODRMr(d, REGNUM(s)); \ - OPCODE(opc); \ - }; \ - void ALUi(int32_t c, Register r, int32_t i); \ - void ALUmi(int32_t c, int32_t d, Register b, int32_t i); \ - void ALU2(int32_t c, Register d, Register s); \ - Register AL2AHReg(Register r); \ - void OR(Register l, Register r); \ - void AND(Register l, Register r); \ - void AND8R(Register r); \ - void XOR(Register l, Register r); \ - void ADD(Register l, Register r); \ - void SUB(Register l, Register r); \ - void IMUL(Register l, Register r); \ - void DIV(Register r); \ - void NOT(Register r); \ - void NEG(Register r); \ - void SHR(Register r, Register s); \ - void SAR(Register r, Register s); \ - void SHL(Register r, Register s); \ - void SHIFTi(int32_t c, Register r, int32_t i); \ - void SHLi(Register r, int32_t i); \ - void SHRi(Register r, int32_t i); \ - void SARi(Register r, int32_t i); \ - void MOVZX8(Register d, Register s); \ - void SUBi(Register r, int32_t i); \ - void ADDi(Register r, int32_t i); \ - void ANDi(Register r, int32_t i); \ - void ORi(Register r, int32_t i); \ - void XORi(Register r, int32_t i); \ - void ADDmi(int32_t d, Register b, int32_t i); \ - void TEST(Register d, Register s); \ - void CMP(Register l, Register r); \ - void CMPi(Register r, int32_t i); \ - void MR(Register d, Register s) { \ - count_mov(); \ - ALU(0x8b, REGNUM(d), s); \ - asm_output("mov %s,%s", gpn(d), gpn(s)); \ - }; \ - void LEA(Register r, int32_t d, Register b); \ - void LEAmi4(Register r, int32_t d, Register i); \ - void CDQ(); \ - void INCLi(int32_t p); \ - void SETE( Register r); \ - void SETNP(Register r); \ - void SETNPH(Register r); \ - void SETL( Register r); \ - void SETLE(Register r); \ - void SETG( Register r); \ - void SETGE(Register r); \ - void SETB( Register r); \ - void SETBE(Register r); \ - void SETA( Register r); \ - void SETAE(Register r); \ - void SETO( Register r); \ - void MREQ(Register d, Register s); \ - void MRNE(Register d, Register s); \ - void MRL( Register d, Register s); \ - void MRLE(Register d, Register s); \ - void MRG( Register d, Register s); \ - void MRGE(Register d, Register s); \ - void MRB( Register d, Register s); \ - void MRBE(Register d, Register s); \ - void MRA( Register d, Register s); \ - void MRAE(Register d, Register s); \ - void MRNO(Register d, Register s); \ - void LD(Register reg, int32_t disp, Register base); \ - void LDdm(Register reg, int32_t addr); \ - void LDsib(Register reg, int32_t disp, Register base, Register index, int32_t scale); \ - void LD16S(Register r, int32_t d, Register b); \ - void LD16Sdm(Register r, int32_t addr); \ - void LD16Ssib(Register r, int32_t disp, Register base, Register index, int32_t scale); \ - void LD16Z(Register r, int32_t d, Register b); \ - void LD16Zdm(Register r, int32_t addr); \ - void LD16Zsib(Register r, int32_t disp, Register base, Register index, int32_t scale); \ - void LD8Z(Register r, int32_t d, Register b); \ - void LD8Zdm(Register r, int32_t addr); \ - void LD8Zsib(Register r, int32_t disp, Register base, Register index, int32_t scale); \ - void LD8S(Register r, int32_t d, Register b); \ - void LD8Sdm(Register r, int32_t addr); \ - void LD8Ssib(Register r, int32_t disp, Register base, Register index, int32_t scale); \ - void LDi(Register r, int32_t i); \ - void ST8(Register base, int32_t disp, Register reg); \ - void ST8sib(int32_t disp, Register base, Register index, int32_t scale, Register reg); \ - void ST16(Register base, int32_t disp, Register reg); \ - void ST16sib(int32_t disp, Register base, Register index, int32_t scale, Register reg); \ - void ST(Register base, int32_t disp, Register reg); \ - void STsib(int32_t disp, Register base, Register index, int32_t scale, Register reg); \ - void ST8i(Register base, int32_t disp, int32_t imm); \ - void ST8isib(int32_t disp, Register base, Register index, int32_t scale, int32_t imm); \ - void ST16i(Register base, int32_t disp, int32_t imm); \ - void ST16isib(int32_t disp, Register base, Register index, int32_t scale, int32_t imm); \ - void STi(Register base, int32_t disp, int32_t imm); \ - void STisib(int32_t disp, Register base, Register index, int32_t scale, int32_t imm); \ - void RET(); \ - void NOP(); \ - void INT3(); \ - void PUSHi(int32_t i); \ - void PUSHr(Register r); \ - void PUSHm(int32_t d, Register b); \ - void POPr(Register r); \ - void JCC(int32_t o, NIns* t, const char* n); \ - void JMP_long(NIns* t); \ - void JMP(NIns* t) { \ - count_jmp(); \ - underrunProtect(5); \ - intptr_t tt = t ? (intptr_t)t - (intptr_t)_nIns : 0; \ - if (t && isS8(tt)) { \ - *(--_nIns) = uint8_t(tt & 0xff); \ - *(--_nIns) = JMP8; \ - } else { \ - IMM32(tt); \ - *(--_nIns) = JMP32; \ - } \ - asm_output("jmp %p", t); \ - }; \ - void JMP_indirect(Register r); \ - void JMP_indexed(Register x, int32_t ss, NIns** addr); \ - void JE(NIns* t); \ - void JNE(NIns* t); \ - void JP(NIns* t); \ - void JNP(NIns* t); \ - void JB(NIns* t); \ - void JNB(NIns* t); \ - void JBE(NIns* t); \ - void JNBE(NIns* t); \ - void JA(NIns* t); \ - void JNA(NIns* t); \ - void JAE(NIns* t); \ - void JNAE(NIns* t); \ - void JL(NIns* t); \ - void JNL(NIns* t); \ - void JLE(NIns* t); \ - void JNLE(NIns* t); \ - void JG(NIns* t); \ - void JNG(NIns* t); \ - void JGE(NIns* t); \ - void JNGE(NIns* t); \ - void JO(NIns* t); \ - void JNO(NIns* t); \ - void SSE(int32_t c, Register d, Register s); \ - void SSEm(int32_t c, Register r, int32_t d, Register b); \ - void SSEsib(int32_t c, Register rr, int32_t d, Register rb, Register ri, int32_t scale); \ - void LDSDm(Register r, const double* addr); \ - void SSE_LDQ( Register r, int32_t d, Register b); \ - void SSE_LDSS(Register r, int32_t d, Register b); \ - void SSE_LDQsib(Register r, int32_t d, Register rb, Register ri, int32_t scale); \ - void SSE_LDSSsib(Register r, int32_t d, Register rb, Register ri, int32_t scale); \ - void SSE_STSD(int32_t d, Register b, Register r); \ - void SSE_STQ( int32_t d, Register b, Register r); \ - void SSE_STSS(int32_t d, Register b, Register r); \ - void SSE_STQsib(int32_t d, Register rb, Register ri, int32_t scale, Register rv); \ - void SSE_CVTSI2SD(Register xr, Register gr); \ - void SSE_CVTSD2SI(Register gr, Register xr); \ - void SSE_CVTTSD2SI(Register gr, Register xr); \ - void SSE_CVTSD2SS(Register xr, Register gr); \ - void SSE_CVTSS2SD(Register xr, Register gr); \ - void SSE_CVTDQ2PD(Register d, Register r); \ - void SSE_MOVD(Register d, Register s); \ - void SSE_MOVSD(Register rd, Register rs); \ - void SSE_ADDSD(Register rd, Register rs); \ - void SSE_ADDSDm(Register r, const double* addr); \ - void SSE_SUBSD(Register rd, Register rs); \ - void SSE_MULSD(Register rd, Register rs); \ - void SSE_DIVSD(Register rd, Register rs); \ - void SSE_UCOMISD(Register rl, Register rr); \ - void SSE_XORPD(Register r, const uint32_t* maskaddr); \ - void SSE_XORPDr(Register rd, Register rs); \ - void FPUc(int32_t o); \ - void FPU(int32_t o, Register r) { \ - underrunProtect(2); \ - *(--_nIns) = uint8_t((uint8_t(o) & 0xff) | (REGNUM(r) & 7)); \ - *(--_nIns) = uint8_t((o >> 8) & 0xff); \ - }; \ - void FPUm(int32_t o, int32_t d, Register b); \ - void FPUdm(int32_t o, const double* const m); \ - void TEST_AH(int32_t i); \ - void FNSTSW_AX(); \ - void FCHS(); \ - void FLD1(); \ - void FLDZ(); \ - void FST32(bool p, int32_t d, Register b); \ - void FSTQ(bool p, int32_t d, Register b); \ - void FSTPQ(int32_t d, Register b); \ - void FCOM(bool p, int32_t d, Register b); \ - void FCOMdm(bool p, const double* dm); \ - void FLD32(int32_t d, Register b); \ - void FLDQ(int32_t d, Register b); \ - void FLDQdm(const double* dm); \ - void FILDQ(int32_t d, Register b); \ - void FILD(int32_t d, Register b); \ - void FIST(bool p, int32_t d, Register b); \ - void FADD( int32_t d, Register b); \ - void FSUB( int32_t d, Register b); \ - void FSUBR(int32_t d, Register b); \ - void FMUL( int32_t d, Register b); \ - void FDIV( int32_t d, Register b); \ - void FDIVR(int32_t d, Register b); \ - void FADDdm( const double *dm); \ - void FSUBRdm(const double* dm); \ - void FMULdm( const double* dm); \ - void FDIVRdm(const double* dm); \ - void FSTP(Register r) { \ - count_fpu(); \ - FPU(0xddd8, r); \ - asm_output("fstp %s", gpn(r)); \ - fpu_pop(); \ - }; \ - void FCOMP(); \ - void FCOMPP(); \ - void FLDr(Register r); \ - void EMMS(); \ - void CALL(const CallInfo* ci); \ - void CALLr(const CallInfo* ci, Register r); -} - - - -#endif // __nanojit_Nativei386__ diff --git a/deps/mozjs/js/src/nanojit/RegAlloc.cpp b/deps/mozjs/js/src/nanojit/RegAlloc.cpp deleted file mode 100644 index 81b733af331..00000000000 --- a/deps/mozjs/js/src/nanojit/RegAlloc.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -namespace nanojit -{ - #ifdef FEATURE_NANOJIT - - #ifdef _DEBUG - - bool RegAlloc::isConsistent(Register r, LIns* i) const - { - return (isFree(r) && !getActive(r) && !i) || - (!isFree(r) && getActive(r)== i && i ); - } - - #endif /*DEBUG*/ - #endif /* FEATURE_NANOJIT */ -} diff --git a/deps/mozjs/js/src/nanojit/RegAlloc.h b/deps/mozjs/js/src/nanojit/RegAlloc.h deleted file mode 100644 index 65d0e3e247c..00000000000 --- a/deps/mozjs/js/src/nanojit/RegAlloc.h +++ /dev/null @@ -1,205 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#ifndef __nanojit_RegAlloc__ -#define __nanojit_RegAlloc__ - - -namespace nanojit -{ - class RegAlloc - { - public: - RegAlloc() - { - clear(); - } - - void clear() - { - VMPI_memset(this, 0, sizeof(*this)); - } - - bool isFree(Register r) const - { - return (free & rmask(r)) != 0; - } - - void addFree(Register r) - { - NanoAssert(!isFree(r)); - free |= rmask(r); - } - - void removeFree(Register r) - { - NanoAssert(isFree(r)); - free &= ~rmask(r); - } - - void addActive(Register r, LIns* v) - { - // Count++; - NanoAssert(v); - NanoAssert(active[REGNUM(r)] == NULL); - active[REGNUM(r)] = v; - useActive(r); - } - - void useActive(Register r) - { - NanoAssert(active[REGNUM(r)] != NULL); - usepri[REGNUM(r)] = priority++; - } - - void removeActive(Register r) - { - //registerReleaseCount++; - NanoAssert(active[REGNUM(r)] != NULL); - - // remove the given register from the active list - active[REGNUM(r)] = NULL; - } - - void retire(Register r) - { - NanoAssert(active[REGNUM(r)] != NULL); - active[REGNUM(r)] = NULL; - free |= rmask(r); - } - - int32_t getPriority(Register r) { - NanoAssert(active[REGNUM(r)]); - return usepri[REGNUM(r)]; - } - - LIns* getActive(Register r) const { - return active[REGNUM(r)]; - } - - // Return a mask containing the active registers. For each register - // in this set, getActive(register) will be a nonzero LIns pointer. - RegisterMask activeMask() const { - return ~free & managed; - } - - debug_only( bool isConsistent(Register r, LIns* v) const; ) - - // Some basics: - // - // - 'active' indicates which registers are active at a particular - // point, and for each active register, which instruction - // defines the value it holds. At the start of register - // allocation no registers are active. - // - // - 'free' indicates which registers are free at a particular point - // and thus available for use. At the start of register - // allocation most registers are free; those that are not - // aren't available for general use, e.g. the stack pointer and - // frame pointer registers. - // - // - 'managed' is exactly this list of initially free registers, - // ie. the registers managed by the register allocator. - // - // - Each LIns has a "reservation" which includes a register value, - // 'reg'. Combined with 'active', this provides a two-way - // mapping between registers and LIR instructions. - // - // - Invariant 1: each register must be in exactly one of the - // following states at all times: unmanaged, free, or active. - // In terms of the relevant fields: - // - // * A register in 'managed' must be in 'active' or 'free' but - // not both. - // - // * A register not in 'managed' must be in neither 'active' nor - // 'free'. - // - // - Invariant 2: the two-way mapping between active registers and - // their defining instructions must always hold in both - // directions and be unambiguous. More specifically: - // - // * An LIns can appear at most once in 'active'. - // - // * An LIns named by 'active[R]' must have an in-use - // reservation that names R. - // - // * And vice versa: an LIns with an in-use reservation that - // names R must be named by 'active[R]'. - // - // * If an LIns's reservation names 'deprecated_UnknownReg' then LIns - // should not be in 'active'. - // - LIns* active[LastRegNum + 1]; // active[REGNUM(r)] = LIns that defines r - int32_t usepri[LastRegNum + 1]; // used priority. lower = more likely to spill. - RegisterMask free; // Registers currently free. - RegisterMask managed; // Registers under management (invariant). - int32_t priority; - - DECLARE_PLATFORM_REGALLOC() - }; - - // Return the lowest numbered Register in mask. - inline Register lsReg(RegisterMask mask) { - // This is faster than it looks; we rely on the C++ optimizer - // to strip the dead branch and inline just one alternative. - Register r = { (sizeof(RegisterMask) == 4) ? lsbSet32(mask) : lsbSet64(mask) }; - return r; - } - - // Return the highest numbered Register in mask. - inline Register msReg(RegisterMask mask) { - // This is faster than it looks; we rely on the C++ optimizer - // to strip the dead branch and inline just one alternative. - Register r = { (sizeof(RegisterMask) == 4) ? msbSet32(mask) : msbSet64(mask) }; - return r; - } - - // Clear bit r in mask, then return lsReg(mask). - inline Register nextLsReg(RegisterMask& mask, Register r) { - return lsReg(mask &= ~rmask(r)); - } - - // Clear bit r in mask, then return msReg(mask). - inline Register nextMsReg(RegisterMask& mask, Register r) { - return msReg(mask &= ~rmask(r)); - } -} -#endif // __nanojit_RegAlloc__ diff --git a/deps/mozjs/js/src/nanojit/VMPI.cpp b/deps/mozjs/js/src/nanojit/VMPI.cpp deleted file mode 100644 index 68aa8828b8b..00000000000 --- a/deps/mozjs/js/src/nanojit/VMPI.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 (the - * "License"); you may not use this file except in compliance with the License. You may obtain - * a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT - * WARRANTY OF ANY KIND, either express or implied. See the License for the specific - * language governing rights and limitations under the License. - * - * The Original Code is [Open Source Virtual Machine.] - * - * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created - * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights - * Reserved. - * - * Contributor(s): Adobe AS3 Team - * Andreas Gal - * - * Alternatively, the contents of this file may be used under the terms of either the GNU - * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public - * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the - * LGPL are applicable instead of those above. If you wish to allow use of your version of this - * file only under the terms of either the GPL or the LGPL, and not to allow others to use your - * version of this file under the terms of the MPL, indicate your decision by deleting provisions - * above and replace them with the notice and other provisions required by the GPL or the - * LGPL. If you do not delete the provisions above, a recipient may use your version of this file - * under the terms of any one of the MPL, the GPL or the LGPL. - * - ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef SOLARIS - typedef caddr_t maddr_ptr; -#else - typedef void *maddr_ptr; -#endif - -using namespace avmplus; - -size_t -VMPI_getVMPageSize() -{ - return 4096; -} - -#ifdef WIN32 -void -VMPI_setPageProtection(void *address, - size_t size, - bool executableFlag, - bool writeableFlag) -{ - DWORD oldProtectFlags = 0; - DWORD newProtectFlags = 0; - if ( executableFlag && writeableFlag ) { - newProtectFlags = PAGE_EXECUTE_READWRITE; - } else if ( executableFlag ) { - newProtectFlags = PAGE_EXECUTE_READ; - } else if ( writeableFlag ) { - newProtectFlags = PAGE_READWRITE; - } else { - newProtectFlags = PAGE_READONLY; - } - - BOOL retval; - MEMORY_BASIC_INFORMATION mbi; - do { - VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); - size_t markSize = size > mbi.RegionSize ? mbi.RegionSize : size; - - retval = VirtualProtect(address, markSize, newProtectFlags, &oldProtectFlags); - NanoAssert(retval); - - address = (char*) address + markSize; - size -= markSize; - } while(size > 0 && retval); - - // We should not be clobbering PAGE_GUARD protections - NanoAssert((oldProtectFlags & PAGE_GUARD) == 0); -} - -#elif defined(AVMPLUS_OS2) - -void -VMPI_setPageProtection(void *address, - size_t size, - bool executableFlag, - bool writeableFlag) -{ - ULONG flags = PAG_READ; - if (executableFlag) { - flags |= PAG_EXECUTE; - } - if (writeableFlag) { - flags |= PAG_WRITE; - } - address = (void*)((size_t)address & ~(0xfff)); - size = (size + 0xfff) & ~(0xfff); - - ULONG attribFlags = PAG_FREE; - while (size) { - ULONG attrib; - ULONG range = size; - ULONG retval = DosQueryMem(address, &range, &attrib); - NanoAssert(retval == 0); - - // exit if this is the start of the next memory object - if (attrib & attribFlags) { - break; - } - attribFlags |= PAG_BASE; - - range = size > range ? range : size; - retval = DosSetMem(address, range, flags); - NanoAssert(retval == 0); - - address = (char*)address + range; - size -= range; - } -} - -#else // !WIN32 && !AVMPLUS_OS2 - -void VMPI_setPageProtection(void *address, - size_t size, - bool executableFlag, - bool writeableFlag) -{ - int bitmask = sysconf(_SC_PAGESIZE) - 1; - // mprotect requires that the addresses be aligned on page boundaries - void *endAddress = (void*) ((char*)address + size); - void *beginPage = (void*) ((size_t)address & ~bitmask); - void *endPage = (void*) (((size_t)endAddress + bitmask) & ~bitmask); - size_t sizePaged = (size_t)endPage - (size_t)beginPage; - - int flags = PROT_READ; - if (executableFlag) { - flags |= PROT_EXEC; - } - if (writeableFlag) { - flags |= PROT_WRITE; - } - int retval = mprotect((maddr_ptr)beginPage, (unsigned int)sizePaged, flags); - NanoAssert(retval == 0); - (void)retval; -} - -#endif // WIN32 diff --git a/deps/mozjs/js/src/nanojit/VMPI.h b/deps/mozjs/js/src/nanojit/VMPI.h deleted file mode 100644 index 429ed11e160..00000000000 --- a/deps/mozjs/js/src/nanojit/VMPI.h +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Stub VMPI implementation to support standalone nanojit repository. - * - * Really only works if you *don't* have a busted-up C library. - */ - -#ifndef __VMPI_h__ -#define __VMPI_h__ - -#if defined(HAVE_CONFIG_H) && defined(NANOJIT_CENTRAL) -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include - -#if defined(AVMPLUS_UNIX) || defined(AVMPLUS_OS2) -#include -#include -#endif - -#ifdef AVMPLUS_WIN32 -#if ! defined(_STDINT_H) -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef signed __int64 int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned __int64 uint64_t; -#endif -#else -#include -#include -#endif - -#define VMPI_strlen strlen -#define VMPI_strcat strcat -#define VMPI_strcmp strcmp -#define VMPI_strncat strncat -#define VMPI_strcpy strcpy -#define VMPI_sprintf sprintf -#ifdef _MSC_VER -# define VMPI_snprintf sprintf_s -#else -# define VMPI_snprintf snprintf -#endif -#define VMPI_vfprintf vfprintf -#define VMPI_memset memset -#define VMPI_isdigit isdigit -#define VMPI_getDate() - -extern size_t VMPI_getVMPageSize(); - -extern void VMPI_setPageProtection(void *address, - size_t size, - bool executableFlag, - bool writeableFlag); - -// Keep this warning-set relatively in sync with platform/win32/win32-platform.h in tamarin. - -#ifdef _MSC_VER - #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union - #pragma warning(disable:4512) // assignment operator could not be generated - #pragma warning(disable:4511) // can't generate copy ctor - #pragma warning(disable:4127) // conditional expression is constant - appears to be compiler noise primarily - #pragma warning(disable:4611) // interaction between _setjmp and destruct - #pragma warning(disable:4725) // instruction may be inaccurate on some Pentiums - #pragma warning(disable:4611) // interaction between '_setjmp' and C++ object destruction is non-portable - #pragma warning(disable:4251) // X needs to have dll-interface to be used by clients of class Y - - // enable some that are off even in /W4 mode, but are still handy - #pragma warning(default:4265) // 'class' : class has virtual functions, but destructor is not virtual - #pragma warning(default:4905) // wide string literal cast to 'LPSTR' - #pragma warning(default:4906) // string literal cast to 'LPWSTR' - #pragma warning(default:4263) // 'function' : member function does not override any base class virtual member function - #pragma warning(default:4264) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden - #pragma warning(default:4266) // 'function' : no override available for virtual member function from base 'type'; function is hidden - #pragma warning(default:4242) // 'identifier' : conversion from 'type1' to 'type2', possible loss of data - #pragma warning(default:4263) // member function does not override any base class virtual member function - #pragma warning(default:4296) // expression is always true (false) (Generally, an unsigned variable was used in a comparison operation with zero.) -#endif - -// This part defined in avmshell.h but similarly required for a warning-free nanojit experience. -#ifdef _MSC_VER -#pragma warning(disable:4996) // 'scanf' was declared deprecated -#endif - -// This part is inhibited manually by the CFLAGS in the tamarin configury. -#ifdef _MSC_VER -#pragma warning(disable:4291) // presence of a 'new' operator in nanojit/Allocator.h without matching 'delete' -#endif - -#endif diff --git a/deps/mozjs/js/src/nanojit/avmplus.cpp b/deps/mozjs/js/src/nanojit/avmplus.cpp deleted file mode 100644 index b969ed44b84..00000000000 --- a/deps/mozjs/js/src/nanojit/avmplus.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 (the - * "License"); you may not use this file except in compliance with the License. You may obtain - * a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT - * WARRANTY OF ANY KIND, either express or implied. See the License for the specific - * language governing rights and limitations under the License. - * - * The Original Code is [Open Source Virtual Machine.] - * - * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created - * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights - * Reserved. - * - * Contributor(s): Adobe AS3 Team - * Andreas Gal - * - * Alternatively, the contents of this file may be used under the terms of either the GNU - * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public - * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the - * LGPL are applicable instead of those above. If you wish to allow use of your version of this - * file only under the terms of either the GPL or the LGPL, and not to allow others to use your - * version of this file under the terms of the MPL, indicate your decision by deleting provisions - * above and replace them with the notice and other provisions required by the GPL or the - * LGPL. If you do not delete the provisions above, a recipient may use your version of this file - * under the terms of any one of the MPL, the GPL or the LGPL. - * - ***** END LICENSE BLOCK ***** */ - -#include -#include "nanojit.h" - -#ifdef SOLARIS - typedef caddr_t maddr_ptr; -#else - typedef void *maddr_ptr; -#endif - -using namespace avmplus; - -void -avmplus::AvmLog(char const *msg, ...) { - va_list ap; - va_start(ap, msg); - VMPI_vfprintf(stderr, msg, ap); - va_end(ap); -} - -#ifdef _DEBUG -namespace avmplus { - void AvmAssertFail(const char* /* msg */) { - fflush(stderr); -#if defined(WIN32) - DebugBreak(); - exit(3); -#elif defined(__APPLE__) - /* - * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are - * trapped. - */ - *((int *) NULL) = 0; /* To continue from here in GDB: "return" then "continue". */ - raise(SIGABRT); /* In case above statement gets nixed by the optimizer. */ -#else - raise(SIGABRT); /* To continue from here in GDB: "signal 0". */ -#endif - } -} -#endif - -#ifdef WINCE - -// Due to the per-process heap slots on Windows Mobile, we can often run in to OOM -// situations. jemalloc has worked around this problem, and so we use it here. -// Using posix_memalign (or other malloc)functions) here only works because the OS -// and hardware doesn't check for the execute bit being set. - -#ifndef MOZ_MEMORY -#error MOZ_MEMORY required for building on WINCE -#endif - -void* -nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { - void * buffer; - posix_memalign(&buffer, 4096, nbytes); - VMPI_setPageProtection(buffer, nbytes, true /* exec */, true /* write */); - return buffer; -} - -void -nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { - VMPI_setPageProtection(p, nbytes, false /* exec */, true /* write */); - ::free(p); -} - -#elif defined(WIN32) - -void* -nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { - return VirtualAlloc(NULL, - nbytes, - MEM_COMMIT | MEM_RESERVE, - PAGE_EXECUTE_READWRITE); -} - -void -nanojit::CodeAlloc::freeCodeChunk(void *p, size_t) { - VirtualFree(p, 0, MEM_RELEASE); -} - -#elif defined(AVMPLUS_OS2) - -void* -nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { - - // alloc from high memory, fallback to low memory if that fails - void * addr; - if (DosAllocMem(&addr, nbytes, OBJ_ANY | - PAG_COMMIT | PAG_READ | PAG_WRITE | PAG_EXECUTE)) { - if (DosAllocMem(&addr, nbytes, - PAG_COMMIT | PAG_READ | PAG_WRITE | PAG_EXECUTE)) { - return 0; - } - } - return addr; -} - -void -nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { - DosFreeMem(p); -} - -#elif defined(AVMPLUS_UNIX) - -void* -nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { - return mmap(NULL, - nbytes, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0); -} - -void -nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { - munmap((maddr_ptr)p, nbytes); -} - -#else // !WIN32 && !AVMPLUS_OS2 && !AVMPLUS_UNIX - -void* -nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { - void* mem = valloc(nbytes); - VMPI_setPageProtection(mem, nbytes, true /* exec */, true /* write */); - return mem; -} - -void -nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { - VMPI_setPageProtection(p, nbytes, false /* exec */, true /* write */); - ::free(p); -} - -#endif // WIN32 - -// All of the allocCodeChunk/freeCodeChunk implementations above allocate -// code memory as RWX and then free it, so the explicit page protection api's -// below are no-ops. - -void -nanojit::CodeAlloc::markCodeChunkWrite(void*, size_t) -{} - -void -nanojit::CodeAlloc::markCodeChunkExec(void*, size_t) -{} - diff --git a/deps/mozjs/js/src/nanojit/avmplus.h b/deps/mozjs/js/src/nanojit/avmplus.h deleted file mode 100644 index 68d2f3eba13..00000000000 --- a/deps/mozjs/js/src/nanojit/avmplus.h +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 (the - * "License"); you may not use this file except in compliance with the License. You may obtain - * a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT - * WARRANTY OF ANY KIND, either express or implied. See the License for the specific - * language governing rights and limitations under the License. - * - * The Original Code is [Open Source Virtual Machine.] - * - * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created - * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights - * Reserved. - * - * Contributor(s): Adobe AS3 Team - * Andreas Gal - * Asko Tontti - * - * Alternatively, the contents of this file may be used under the terms of either the GNU - * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public - * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the - * LGPL are applicable instead of those above. If you wish to allow use of your version of this - * file only under the terms of either the GPL or the LGPL, and not to allow others to use your - * version of this file under the terms of the MPL, indicate your decision by deleting provisions - * above and replace them with the notice and other provisions required by the GPL or the - * LGPL. If you do not delete the provisions above, a recipient may use your version of this file - * under the terms of any one of the MPL, the GPL or the LGPL. - * - ***** END LICENSE BLOCK ***** */ - -#ifndef avm_h___ -#define avm_h___ - -#include "VMPI.h" -#include "njcpudetect.h" -#include "njconfig.h" - -#if !defined(AVMPLUS_LITTLE_ENDIAN) && !defined(AVMPLUS_BIG_ENDIAN) -#ifdef IS_BIG_ENDIAN -#define AVMPLUS_BIG_ENDIAN -#else -#define AVMPLUS_LITTLE_ENDIAN -#endif -#endif - -#if defined(_MSC_VER) && defined(_M_IX86) -#define FASTCALL __fastcall -#elif defined(__GNUC__) && defined(__i386__) && \ - ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -#define FASTCALL __attribute__((fastcall)) -#else -#define FASTCALL -#define NO_FASTCALL -#endif - -#if defined(NO_FASTCALL) -#if defined(AVMPLUS_IA32) -#define SIMULATE_FASTCALL(lr, state_ptr, frag_ptr, func_addr) \ - asm volatile( \ - "call *%%esi" \ - : "=a" (lr) \ - : "c" (state_ptr), "d" (frag_ptr), "S" (func_addr) \ - : "memory", "cc" \ - ); -#endif /* defined(AVMPLUS_IA32) */ -#endif /* defined(NO_FASTCALL) */ - -#ifdef WIN32 -#include -#elif defined(AVMPLUS_OS2) -#define INCL_DOSMEMMGR -#include -#endif - -#if defined(__SUNPRO_CC) -#define __asm__ asm -#define __volatile__ volatile -#define __inline__ inline -#endif - -#if defined(DEBUG) -#if !defined _DEBUG -#define _DEBUG -#endif -#define NJ_VERBOSE 1 -#include -#endif - -#ifdef _DEBUG -namespace avmplus { - void AvmAssertFail(const char* msg); -} -#endif - -#if defined(AVMPLUS_IA32) -#if defined(_MSC_VER) - -# define AVMPLUS_HAS_RDTSC 1 - -__declspec(naked) static inline __int64 rdtsc() -{ - __asm - { - rdtsc; - ret; - } -} - -#elif defined(__i386__) || defined(__i386) - -# define AVMPLUS_HAS_RDTSC 1 - -static __inline__ unsigned long long rdtsc(void) -{ - unsigned long long int x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; -} - -#endif /* compilers */ - -#elif defined(__x86_64__) - -# define AVMPLUS_HAS_RDTSC 1 - -static __inline__ uint64_t rdtsc(void) -{ - unsigned hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 ); -} - -#elif defined(_MSC_VER) && defined(_M_AMD64) - -# define AVMPLUS_HAS_RDTSC 1 - -#include -#pragma intrinsic(__rdtsc) - -static inline unsigned __int64 rdtsc(void) -{ - return __rdtsc(); -} - -#elif defined(__GNUC__) && defined(__powerpc__) - -# define AVMPLUS_HAS_RDTSC 1 - -typedef unsigned long long int unsigned long long; - -static __inline__ unsigned long long rdtsc(void) -{ - unsigned long long int result=0; - unsigned long int upper, lower,tmp; - __asm__ volatile( - "0: \n" - "\tmftbu %0 \n" - "\tmftb %1 \n" - "\tmftbu %2 \n" - "\tcmpw %2,%0 \n" - "\tbne 0b \n" - : "=r"(upper),"=r"(lower),"=r"(tmp) - ); - result = upper; - result = result<<32; - result = result|lower; - - return(result); -} - -#endif /* architecture */ - -#ifndef AVMPLUS_HAS_RDTSC -# define AVMPLUS_HAS_RDTSC 0 -#endif - -#ifdef PERFM -# define PERFM_NVPROF(n,v) _nvprof(n,v) -# define PERFM_NTPROF(n) _ntprof(n) -# define PERFM_TPROF_END() _tprof_end() -#else -# define PERFM_NVPROF(n,v) -# define PERFM_NTPROF(n) -# define PERFM_TPROF_END() -#endif - -namespace avmplus { - - extern void AvmLog(char const *msg, ...); - - /** - * Bit vectors are an efficent method of keeping True/False information - * on a set of items or conditions. Class BitSet provides functions - * to manipulate individual bits in the vector. - * - * This object is not optimized for a fixed sized bit vector - * it instead allows for dynamically growing the bit vector. - */ - class BitSet - { - public: - enum { kUnit = 8*sizeof(long), - kDefaultCapacity = 4 }; - - BitSet() - { - capacity = kDefaultCapacity; - ar = (long*)calloc(capacity, sizeof(long)); - reset(); - } - - ~BitSet() - { - free(ar); - } - - void reset() - { - for (int i = 0; i < capacity; i++) - ar[i] = 0; - } - - void set(int bitNbr) - { - int index = bitNbr / kUnit; - int bit = bitNbr % kUnit; - if (index >= capacity) - grow(index+1); - - ar[index] |= (1< -#elif !defined(VALGRIND_DISCARD_TRANSLATIONS) -# define VALGRIND_DISCARD_TRANSLATIONS(addr, szB) -#endif - -namespace nanojit -{ - /** - * ------------------------------------------- - * START AVM bridging definitions - * ------------------------------------------- - */ - const uint32_t MAXARGS = 8; - - #if defined(_DEBUG) - - #define __NanoAssertMsgf(a, file_, line_, f, ...) \ - if (!(a)) { \ - avmplus::AvmLog("Assertion failure: " f "%s (%s:%d)\n", __VA_ARGS__, #a, file_, line_); \ - avmplus::AvmAssertFail(""); \ - } - - #define _NanoAssertMsgf(a, file_, line_, f, ...) __NanoAssertMsgf(a, file_, line_, f, __VA_ARGS__) - - #define NanoAssertMsgf(a,f,...) do { __NanoAssertMsgf(a, __FILE__, __LINE__, f ": ", __VA_ARGS__); } while (0) - #define NanoAssertMsg(a,m) do { __NanoAssertMsgf(a, __FILE__, __LINE__, "\"%s\": ", m); } while (0) - #define NanoAssert(a) do { __NanoAssertMsgf(a, __FILE__, __LINE__, "%s", ""); } while (0) - #else - #define NanoAssertMsgf(a,f,...) do { } while (0) /* no semi */ - #define NanoAssertMsg(a,m) do { } while (0) /* no semi */ - #define NanoAssert(a) do { } while (0) /* no semi */ - #endif - - /* - * Sun Studio C++ compiler has a bug - * "sizeof expression not accepted as size of array parameter" - * The bug number is 6688515. It is not public yet. - * Turn off this assert for Sun Studio until this bug is fixed. - */ - #ifdef __SUNPRO_CC - #define NanoStaticAssert(condition) - #else - #define NanoStaticAssert(condition) \ - extern void nano_static_assert(int arg[(condition) ? 1 : -1]) - #endif - - - /** - * ------------------------------------------- - * END AVM bridging definitions - * ------------------------------------------- - */ -} - -#ifdef AVMPLUS_VERBOSE - #define NJ_VERBOSE 1 -#endif - -#if defined(NJ_VERBOSE) - #include - #define verbose_outputf if (_logc->lcbits & LC_Native) \ - Assembler::outputf - #define verbose_only(...) __VA_ARGS__ -#else - #define verbose_outputf - #define verbose_only(...) -#endif /*NJ_VERBOSE*/ - -#ifdef _DEBUG - #define debug_only(x) x -#else - #define debug_only(x) -#endif /* DEBUG */ - -#define isS8(i) ( int32_t(i) == int8_t(i) ) -#define isU8(i) ( int32_t(i) == uint8_t(i) ) -#define isS16(i) ( int32_t(i) == int16_t(i) ) -#define isU16(i) ( int32_t(i) == uint16_t(i) ) -#define isS24(i) ( (int32_t((i)<<8)>>8) == (i) ) - -static inline bool isS32(intptr_t i) { - return int32_t(i) == i; -} - -static inline bool isU32(uintptr_t i) { - return uint32_t(i) == i; -} - -#define alignTo(x,s) ((((uintptr_t)(x)))&~(((uintptr_t)s)-1)) -#define alignUp(x,s) ((((uintptr_t)(x))+(((uintptr_t)s)-1))&~(((uintptr_t)s)-1)) - -#define NJ_MIN(x, y) ((x) < (y) ? (x) : (y)) -#define NJ_MAX(x, y) ((x) > (y) ? (x) : (y)) - -namespace nanojit -{ -// Define msbSet32(), lsbSet32(), msbSet64(), and lsbSet64() functions using -// fast find-first-bit instructions intrinsics when available. -// The fall-back implementations use iteration. -#if defined(_WIN32) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) - - extern "C" unsigned char _BitScanForward(unsigned long * Index, unsigned long Mask); - extern "C" unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); - # pragma intrinsic(_BitScanForward) - # pragma intrinsic(_BitScanReverse) - - // Returns the index of the most significant bit that is set. - static inline int msbSet32(uint32_t x) { - unsigned long idx; - _BitScanReverse(&idx, (unsigned long)(x | 1)); // the '| 1' ensures a 0 result when x==0 - return idx; - } - - // Returns the index of the least significant bit that is set. - static inline int lsbSet32(uint32_t x) { - unsigned long idx; - _BitScanForward(&idx, (unsigned long)(x | 0x80000000)); // the '| 0x80000000' ensures a 0 result when x==0 - return idx; - } - -#if defined(_M_AMD64) || defined(_M_X64) - extern "C" unsigned char _BitScanForward64(unsigned long * Index, unsigned __int64 Mask); - extern "C" unsigned char _BitScanReverse64(unsigned long * Index, unsigned __int64 Mask); - # pragma intrinsic(_BitScanForward64) - # pragma intrinsic(_BitScanReverse64) - - // Returns the index of the most significant bit that is set. - static inline int msbSet64(uint64_t x) { - unsigned long idx; - _BitScanReverse64(&idx, (unsigned __int64)(x | 1)); // the '| 1' ensures a 0 result when x==0 - return idx; - } - - // Returns the index of the least significant bit that is set. - static inline int lsbSet64(uint64_t x) { - unsigned long idx; - _BitScanForward64(&idx, (unsigned __int64)(x | 0x8000000000000000LL)); // the '| 0x80000000' ensures a 0 result when x==0 - return idx; - } -#else - // Returns the index of the most significant bit that is set. - static int msbSet64(uint64_t x) { - return (x & 0xffffffff00000000LL) ? msbSet32(uint32_t(x >> 32)) + 32 : msbSet32(uint32_t(x)); - } - // Returns the index of the least significant bit that is set. - static int lsbSet64(uint64_t x) { - return (x & 0x00000000ffffffffLL) ? lsbSet32(uint32_t(x)) : lsbSet32(uint32_t(x >> 32)) + 32; - } -#endif - -#elif (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - - // Returns the index of the most significant bit that is set. - static inline int msbSet32(uint32_t x) { - return 31 - __builtin_clz(x | 1); - } - - // Returns the index of the least significant bit that is set. - static inline int lsbSet32(uint32_t x) { - return __builtin_ctz(x | 0x80000000); - } - - // Returns the index of the most significant bit that is set. - static inline int msbSet64(uint64_t x) { - return 63 - __builtin_clzll(x | 1); - } - - // Returns the index of the least significant bit that is set. - static inline int lsbSet64(uint64_t x) { - return __builtin_ctzll(x | 0x8000000000000000LL); - } - -#else - - // Slow fall-back: return most significant bit set by searching iteratively. - static int msbSet32(uint32_t x) { - for (int i = 31; i >= 0; i--) - if ((1 << i) & x) - return i; - return 0; - } - - // Slow fall-back: return least significant bit set by searching iteratively. - static int lsbSet32(uint32_t x) { - for (int i = 0; i < 32; i++) - if ((1 << i) & x) - return i; - return 31; - } - - // Slow fall-back: return most significant bit set by searching iteratively. - static int msbSet64(uint64_t x) { - for (int i = 63; i >= 0; i--) - if ((1LL << i) & x) - return i; - return 0; - } - - // Slow fall-back: return least significant bit set by searching iteratively. - static int lsbSet64(uint64_t x) { - for (int i = 0; i < 64; i++) - if ((1LL << i) & x) - return i; - return 63; - } - -#endif // select compiler -} // namespace nanojit - -// ------------------------------------------------------------------- -// START debug-logging definitions -// ------------------------------------------------------------------- - -/* Debug printing stuff. All Nanojit and jstracer debug printing - should be routed through LogControl::printf. Don't use - ad-hoc calls to printf, fprintf(stderr, ...) etc. - - Similarly, don't use ad-hoc getenvs etc to decide whether or not to - print debug output. Instead consult the relevant control bit in - LogControl::lcbits in the LogControl object you are supplied with. -*/ - -# if defined(__GNUC__) -# define PRINTF_CHECK(x, y) __attribute__((format(__printf__, x, y))) -# else -# define PRINTF_CHECK(x, y) -# endif - -namespace nanojit { - - // LogControl, a class for controlling and routing debug output - - enum LC_Bits { - /* Output control bits for Nanojit code. Only use bits 15 - and below, so that callers can use bits 16 and above for - themselves. */ - // TODO: add entries for the writer pipeline - LC_FragProfile = 1<<8, // collect per-frag usage counts - LC_Liveness = 1<<7, // show LIR liveness analysis - LC_ReadLIR = 1<<6, // as read from LirBuffer - LC_AfterSF = 1<<5, // after StackFilter - LC_AfterDCE = 1<<4, // after dead code elimination - LC_Bytes = 1<<3, // byte values of native instruction - LC_Native = 1<<2, // final native code - LC_RegAlloc = 1<<1, // stuff to do with reg alloc - LC_Activation = 1<<0 // enable printActivationState - }; - - class LogControl - { - public: - // All Nanojit and jstracer printing should be routed through - // this function. - virtual ~LogControl() {} - #ifdef NJ_VERBOSE - virtual void printf( const char* format, ... ) PRINTF_CHECK(2,3); - #endif - - // An OR of LC_Bits values, indicating what should be output - uint32_t lcbits; - }; -} - -// ------------------------------------------------------------------- -// END debug-logging definitions -// ------------------------------------------------------------------- - - -#include "njconfig.h" -#include "Allocator.h" -#include "Containers.h" -#include "Native.h" -#include "CodeAlloc.h" -#include "LIR.h" -#include "RegAlloc.h" -#include "Fragmento.h" -#include "Assembler.h" - -#endif // FEATURE_NANOJIT -#endif // __nanojit_h__ diff --git a/deps/mozjs/js/src/nanojit/njconfig.cpp b/deps/mozjs/js/src/nanojit/njconfig.cpp deleted file mode 100644 index 54ab9428f28..00000000000 --- a/deps/mozjs/js/src/nanojit/njconfig.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nanojit.h" - -#ifdef FEATURE_NANOJIT - -namespace nanojit -{ -#ifdef NANOJIT_IA32 - static int getCpuFeatures() - { - int features = 0; - #if defined _MSC_VER - __asm - { - pushad - mov eax, 1 - cpuid - mov features, edx - popad - } - #elif defined __GNUC__ - asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ - "mov $0x01, %%eax\n" - "cpuid\n" - "mov %%edx, %0\n" - "xchg %%esi, %%ebx\n" - : "=m" (features) - : /* We have no inputs */ - : "%eax", "%esi", "%ecx", "%edx" - ); - #elif defined __SUNPRO_C || defined __SUNPRO_CC - asm("push %%ebx\n" - "mov $0x01, %%eax\n" - "cpuid\n" - "pop %%ebx\n" - : "=d" (features) - : /* We have no inputs */ - : "%eax", "%ecx" - ); - #endif - return features; - } -#endif - - Config::Config() - { - VMPI_memset(this, 0, sizeof(*this)); - - cseopt = true; - -#ifdef NANOJIT_IA32 - int const features = getCpuFeatures(); - i386_sse2 = (features & (1<<26)) != 0; - i386_use_cmov = (features & (1<<15)) != 0; - i386_fixed_esp = false; -#endif - harden_function_alignment = false; - harden_nop_insertion = false; - -#if defined(NANOJIT_ARM) - NanoStaticAssert(NJ_COMPILER_ARM_ARCH >= 5 && NJ_COMPILER_ARM_ARCH <= 7); - arm_arch = NJ_COMPILER_ARM_ARCH; - arm_vfp = (arm_arch >= 7); - - #if defined(DEBUG) || defined(_DEBUG) - arm_show_stats = true; - #else - arm_show_stats = false; - #endif - - soft_float = !arm_vfp; - -#endif // NANOJIT_ARM - } -} -#endif /* FEATURE_NANOJIT */ diff --git a/deps/mozjs/js/src/nanojit/njconfig.h b/deps/mozjs/js/src/nanojit/njconfig.h deleted file mode 100644 index ebddf2aa25b..00000000000 --- a/deps/mozjs/js/src/nanojit/njconfig.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __njconfig_h__ -#define __njconfig_h__ - -#include "avmplus.h" - -// Do not include nanojit.h here; this file should be usable without it. - -#ifdef FEATURE_NANOJIT - -namespace nanojit -{ - /*** - * A struct used to configure the assumptions that Assembler can make when - * generating code. The ctor will fill in all fields with the most reasonable - * values it can derive from compiler flags and/or runtime detection, but - * the embedder is free to override any or all of them as it sees fit. - * Using the ctor-provided default setup is guaranteed to provide a safe - * runtime environment (though perhaps suboptimal in some cases), so an embedder - * should replace these values with great care. - * - * Note that although many fields are used on only specific architecture(s), - * this struct is deliberately declared without ifdef's for them, so (say) ARM-specific - * fields are declared everywhere. This reduces build dependencies (so that this - * files does not require nanojit.h to be included beforehand) and also reduces - * clutter in this file; the extra storage space required is trivial since most - * fields are single bits. - */ - struct Config - { - public: - // fills in reasonable default values for all fields. - Config(); - - // ARM architecture to assume when generate instructions for (currently, 4 <= arm_arch <= 7) - uint8_t arm_arch; - - // If true, use CSE. - uint32_t cseopt:1; - - // Can we use SSE2 instructions? (x86-only) - uint32_t i386_sse2:1; - - // Can we use cmov instructions? (x86-only) - uint32_t i386_use_cmov:1; - - // Should we use a virtual stack pointer? (x86-only) - uint32_t i386_fixed_esp:1; - - // Whether or not to generate VFP instructions. (ARM only) - uint32_t arm_vfp:1; - - // @todo, document me - uint32_t arm_show_stats:1; - - // If true, use softfloat for all floating point operations, - // whether or not an FPU is present. (ARM only for now, but might also includes MIPS in the future) - uint32_t soft_float:1; - - // If true, compiler will insert a random amount of space in between functions (x86-32 only) - uint32_t harden_function_alignment:1; - - // If true, compiler will insert randomly choosen no-op instructions at random locations within a compiled method (x86-32 only) - uint32_t harden_nop_insertion:1; - - inline bool - use_cmov() - { -#ifdef AVMPLUS_IA32 - return i386_use_cmov; -#else - return true; -#endif - } - }; -} - -#endif // FEATURE_NANOJIT -#endif // __njconfig_h__ diff --git a/deps/mozjs/js/src/nanojit/njcpudetect.h b/deps/mozjs/js/src/nanojit/njcpudetect.h deleted file mode 100644 index db2313f0378..00000000000 --- a/deps/mozjs/js/src/nanojit/njcpudetect.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is [Open Source Virtual Machine]. - * - * The Initial Developer of the Original Code is - * Adobe System Incorporated. - * Portions created by the Initial Developer are Copyright (C) 2004-2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Adobe AS3 Team - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef __njcpudetect__ -#define __njcpudetect__ - -/*** - * Note: this file should not include *any* other files, nor should it wrap - * itself in ifdef FEATURE_NANOJIT, nor should it do anything other than - * define preprocessor symbols. - */ - -/*** - * NJ_COMPILER_ARM_ARCH attempts to specify the minimum ARM architecture - * that the C++ compiler has specified. Note that although Config::arm_arch - * is initialized to this value by default, there is no requirement that they - * be in sync. - * - * Note, this is done via #define so that downstream preprocessor usage can - * examine it, but please don't attempt to redefine it. - * - * Note, this is deliberately not encased in "ifdef NANOJIT_ARM", as this file - * may be included before that is defined. On non-ARM platforms we will hit the - * "Unable to determine" case. - */ - -// GCC and RealView usually define __ARM_ARCH__ -#if defined(__ARM_ARCH__) - - #define NJ_COMPILER_ARM_ARCH __ARM_ARCH__ - -// ok, try well-known GCC flags ( see http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html ) -#elif defined(__ARM_ARCH_7__) || \ - defined(__ARM_ARCH_7A__) || \ - defined(__ARM_ARCH_7M__) || \ - defined(__ARM_ARCH_7R__) || \ - defined(_ARM_ARCH_7) - - #define NJ_COMPILER_ARM_ARCH 7 - -#elif defined(__ARM_ARCH_6__) || \ - defined(__ARM_ARCH_6J__) || \ - defined(__ARM_ARCH_6T2__) || \ - defined(__ARM_ARCH_6Z__) || \ - defined(__ARM_ARCH_6ZK__) || \ - defined(__ARM_ARCH_6M__) || \ - defined(_ARM_ARCH_6) - - #define NJ_COMPILER_ARM_ARCH 6 - -#elif defined(__ARM_ARCH_5__) || \ - defined(__ARM_ARCH_5T__) || \ - defined(__ARM_ARCH_5E__) || \ - defined(__ARM_ARCH_5TE__) - - #define NJ_COMPILER_ARM_ARCH 5 - -#elif defined(__ARM_ARCH_4T__) - - #define NJ_COMPILER_ARM_ARCH 4 - -// Visual C has its own mojo -#elif defined(_MSC_VER) && defined(_M_ARM) - - #define NJ_COMPILER_ARM_ARCH _M_ARM - -// RVCT -#elif defined(__TARGET_ARCH_ARM) - - #define NJ_COMPILER_ARM_ARCH __TARGET_ARCH_ARM - -#else - - // non-numeric value - #define NJ_COMPILER_ARM_ARCH "Unable to determine valid NJ_COMPILER_ARM_ARCH (nanojit only supports ARMv4T or later)" - -#endif - -#endif // __njcpudetect__ diff --git a/deps/mozjs/js/src/perf/jsperf.cpp b/deps/mozjs/js/src/perf/jsperf.cpp index 2d573bde340..9982fca5802 100644 --- a/deps/mozjs/js/src/perf/jsperf.cpp +++ b/deps/mozjs/js/src/perf/jsperf.cpp @@ -40,6 +40,8 @@ #include "jscntxt.h" /* for error messages */ #include "jsobj.h" /* for unwrapping without a context */ +#include "jsobjinlines.h" + using JS::PerfMeasurement; // You cannot forward-declare a static object in C++, so instead @@ -52,7 +54,7 @@ static PerfMeasurement* GetPMFromThis(JSContext* cx, jsval* vp); static JSBool pm_construct(JSContext* cx, uintN argc, jsval* vp) { - uint32 mask; + uint32_t mask; if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "u", &mask)) return JS_FALSE; @@ -69,7 +71,7 @@ pm_construct(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; } - JS_SetPrivate(cx, obj, p); + JS_SetPrivate(obj, p); *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } @@ -77,7 +79,7 @@ pm_construct(JSContext* cx, uintN argc, jsval* vp) static void pm_finalize(JSContext* cx, JSObject* obj) { - cx->delete_((PerfMeasurement*) JS_GetPrivate(cx, obj)); + cx->delete_((PerfMeasurement*) JS_GetPrivate(obj)); } // Property access @@ -153,7 +155,7 @@ pm_canMeasureSomething(JSContext* cx, uintN /*unused*/, jsval* vp) return JS_TRUE; } -const uint8 PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED; +const uint8_t PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED; static JSFunctionSpec pm_fns[] = { JS_FN("start", pm_start, 0, PM_FATTRS), JS_FN("stop", pm_stop, 0, PM_FATTRS), @@ -162,7 +164,7 @@ static JSFunctionSpec pm_fns[] = { JS_FS_END }; -const uint8 PM_PATTRS = +const uint8_t PM_PATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED; #define GETTER(name) \ @@ -188,7 +190,7 @@ static JSPropertySpec pm_props[] = { // If this were C++ these would be "static const" members. -const uint8 PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; +const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; #define CONSTANT(name) { #name, PerfMeasurement::name } @@ -234,7 +236,7 @@ GetPM(JSContext* cx, JSObject* obj, const char* fname) // JS_GetInstancePrivate only sets an exception if its last argument // is nonzero, so we have to do it by hand. JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO, - pm_class.name, fname, JS_GET_CLASS(cx, obj)->name); + pm_class.name, fname, JS_GetClass(obj)->name); return 0; } @@ -253,13 +255,15 @@ namespace JS { JSObject* RegisterPerfMeasurement(JSContext *cx, JSObject *global) { - JSObject *prototype = JS_InitClass(cx, global, 0 /* parent */, - &pm_class, pm_construct, 1, - pm_props, pm_fns, 0, 0); + js::RootedVarObject prototype(cx); + prototype = JS_InitClass(cx, global, 0 /* parent */, + &pm_class, pm_construct, 1, + pm_props, pm_fns, 0, 0); if (!prototype) return 0; - JSObject *ctor = JS_GetConstructor(cx, prototype); + js::RootedVarObject ctor(cx); + ctor = JS_GetConstructor(cx, prototype); if (!ctor) return 0; diff --git a/deps/mozjs/js/src/perf/jsperf.h b/deps/mozjs/js/src/perf/jsperf.h index aed019a6c5b..4321cc75881 100644 --- a/deps/mozjs/js/src/perf/jsperf.h +++ b/deps/mozjs/js/src/perf/jsperf.h @@ -98,19 +98,19 @@ class JS_FRIEND_API(PerfMeasurement) * Counters for each measurable event. * Immediately after one of these objects is created, all of the * counters for enabled events will be zero, and all of the - * counters for disabled events will be uint64(-1). + * counters for disabled events will be uint64_t(-1). */ - uint64 cpu_cycles; - uint64 instructions; - uint64 cache_references; - uint64 cache_misses; - uint64 branch_instructions; - uint64 branch_misses; - uint64 bus_cycles; - uint64 page_faults; - uint64 major_page_faults; - uint64 context_switches; - uint64 cpu_migrations; + uint64_t cpu_cycles; + uint64_t instructions; + uint64_t cache_references; + uint64_t cache_misses; + uint64_t branch_instructions; + uint64_t branch_misses; + uint64_t bus_cycles; + uint64_t page_faults; + uint64_t major_page_faults; + uint64_t context_switches; + uint64_t cpu_migrations; /* * Prepare to measure the indicated set of events. If not all of diff --git a/deps/mozjs/js/src/perf/pm_linux.cpp b/deps/mozjs/js/src/perf/pm_linux.cpp index d1fea2c6bd8..7ae92308b62 100644 --- a/deps/mozjs/js/src/perf/pm_linux.cpp +++ b/deps/mozjs/js/src/perf/pm_linux.cpp @@ -106,9 +106,9 @@ struct Impl static const struct { EventMask bit; - uint32 type; - uint32 config; - uint64 PerfMeasurement::* counter; + uint32_t type; + uint32_t config; + uint64_t PerfMeasurement::* counter; int Impl::* fd; } kSlots[PerfMeasurement::NUM_MEASURABLE_EVENTS] = { #define HW(mask, constant, fieldname) \ @@ -247,9 +247,9 @@ Impl::stop(PerfMeasurement* counters) if (fd == -1) continue; - if (read(fd, buf, sizeof(buf)) == sizeof(uint64)) { - uint64 cur; - memcpy(&cur, buf, sizeof(uint64)); + if (read(fd, buf, sizeof(buf)) == sizeof(uint64_t)) { + uint64_t cur; + memcpy(&cur, buf, sizeof(uint64_t)); counters->*(kSlots[i].counter) += cur; } diff --git a/deps/mozjs/js/src/plify_jsdhash.sed b/deps/mozjs/js/src/plify_jsdhash.sed deleted file mode 100644 index 90b36923dc4..00000000000 --- a/deps/mozjs/js/src/plify_jsdhash.sed +++ /dev/null @@ -1,39 +0,0 @@ -/ * Double hashing implementation./a\ -\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! -/ * Double hashing, a la Knuth 6./a\ -\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! -s/jsdhash_h___/pldhash_h___/ -s/jsdhash\.bigdump/pldhash.bigdump/ -s/jstypes\.h/nscore.h/ -s/jsbit\.h/prbit.h/ -s/jsdhash\.h/pldhash.h/ -s/jsdhash\.c/pldhash.c/ -s/jsdhash:/pldhash:/ -s/jsutil\.h/nsDebug.h/ -s/JS_DHASH/PL_DHASH/g -s/JS_DHash/PL_DHash/g -s/JSDHash/PLDHash/g -s/JSHash/PLHash/g -s/uint8 /PRUint8/g -s/uint16 /PRUint16/g -s/uint32 /PRUint32/g -s/\([^U]\)int8 /\1PRInt8/g -s/\([^U]\)int16 /\1PRInt16/g -s/\([^U]\)int32 /\1PRInt32/g -s/uint8/PRUint8/g -s/uint16/PRUint16/g -s/uint32/PRUint32/g -s/\([^U]\)int8/\1PRInt8/g -s/\([^U]\)int16/\1PRInt16/g -s/\([^U]\)int32/\1PRInt32/g -s/JSBool/PRBool/g -s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ -s/JS_PUBLIC_API(\([^()]*\))/\1/ -s/JS_NewDHashTable/PL_NewDHashTable/ -s/JS_ASSERT(0)/NS_NOTREACHED("0")/ -s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ -s/JSDHASH_ONELINE_ASSERT(\(.*\));/NS_ASSERTION(\1, "\1");/ -s/JS_UNLIKELY/NS_UNLIKELY/g -s/JS_LIKELY/NS_LIKELY/g -s/JS_/PR_/g -s/fprintf(stderr,/printf_stderr(/ diff --git a/deps/mozjs/js/src/prmjtime.cpp b/deps/mozjs/js/src/prmjtime.cpp index ae49d4d19bd..cded6dac5e1 100644 --- a/deps/mozjs/js/src/prmjtime.cpp +++ b/deps/mozjs/js/src/prmjtime.cpp @@ -46,7 +46,6 @@ #include #include -#include "jsstdint.h" #include "jstypes.h" #include "jsutil.h" @@ -116,7 +115,7 @@ ComputeLocalTime(time_t local, struct tm *ptm) /* * get the difference in seconds between this time zone and UTC (GMT) */ -JSInt32 +int32_t PRMJ_LocalGMTDifference() { #if defined(XP_WIN) @@ -162,9 +161,9 @@ PRMJ_LocalGMTDifference() #ifdef HAVE_SYSTEMTIMETOFILETIME -static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000); +static const int64_t win2un = 0x19DB1DED53E8000; -#define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime) +#define FILETIME2INT64(ft) (((int64_t)ft.dwHighDateTime) << 32LL | (int64_t)ft.dwLowDateTime) #endif @@ -192,7 +191,7 @@ typedef struct CalibrationData { long double timer_offset; /* The high res 'epoch' */ /* The last high res time that we returned since recalibrating */ - JSInt64 last; + int64_t last; JSBool calibrated; @@ -219,7 +218,7 @@ NowCalibrate() } } if (calibration.freq > 0.0) { - JSInt64 calibrationDelta = 0; + int64_t calibrationDelta = 0; /* By wrapping a timeBegin/EndPeriod pair of calls around this loop, the loop seems to take much less time (1 ms vs 15ms) on Vista. */ @@ -293,41 +292,27 @@ static PRCallOnceType calibrationOnce = { 0 }; #if defined(XP_OS2) -JSInt64 +int64_t PRMJ_Now(void) { - JSInt64 s, us, ms2us, s2us; struct timeb b; - ftime(&b); - JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, b.time); - JSLL_UI2L(us, b.millitm); - JSLL_MUL(us, us, ms2us); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; + return (int64_t(b.time) * PRMJ_USEC_PER_SEC) + (int64_t(b.millitm) * PRMJ_USEC_PER_MSEC); } #elif defined(XP_UNIX) -JSInt64 +int64_t PRMJ_Now(void) { struct timeval tv; - JSInt64 s, us, s2us; #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ gettimeofday(&tv); #else gettimeofday(&tv, 0); #endif /* _SVID_GETTOD */ - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, tv.tv_sec); - JSLL_UI2L(us, tv.tv_usec); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; + + return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec); } #else @@ -399,7 +384,7 @@ def PRMJ_Now(): // 10 seems to be the number of calls to load with a blank homepage. int CALIBRATION_DELAY_COUNT = 10; -JSInt64 +int64_t PRMJ_Now(void) { static int nCalls = 0; @@ -408,7 +393,7 @@ PRMJ_Now(void) LARGE_INTEGER now; JSBool calibrated = JS_FALSE; JSBool needsCalibration = JS_FALSE; - JSInt64 returnedTime; + int64_t returnedTime; long double cachedOffset = 0.0; /* To avoid regressing startup time (where high resolution is likely @@ -471,7 +456,7 @@ PRMJ_Now(void) /* On some dual processor/core systems, we might get an earlier time so we cache the last time that we returned */ - calibration.last = JS_MAX(calibration.last,(JSInt64)highresTime); + calibration.last = JS_MAX(calibration.last, int64_t(highresTime)); returnedTime = calibration.last; MUTEX_UNLOCK(&calibration.data_lock); @@ -509,7 +494,7 @@ PRMJ_Now(void) behavior for this call. It's possible that in the future, the user will want the high resolution timer, so we don't disable it entirely. */ - returnedTime = (JSInt64)lowresTime; + returnedTime = int64_t(lowresTime); needsCalibration = JS_FALSE; } else { /* It is possible that when we recalibrate, we will return a @@ -525,12 +510,12 @@ PRMJ_Now(void) } } else { /* No detectable clock skew */ - returnedTime = (JSInt64)highresTime; + returnedTime = int64_t(highresTime); needsCalibration = JS_FALSE; } } else { /* No high resolution timer is available, so fall back */ - returnedTime = (JSInt64)lowresTime; + returnedTime = int64_t(lowresTime); } } while (needsCalibration); @@ -672,8 +657,8 @@ PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm) return result; } -JSInt64 -DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds) +int64_t +DSTOffsetCache::computeDSTOffsetMilliseconds(int64_t localTimeSeconds) { JS_ASSERT(localTimeSeconds >= 0); JS_ASSERT(localTimeSeconds <= MAX_UNIX_TIMET); @@ -691,13 +676,13 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds) if (!ComputeLocalTime(static_cast(localTimeSeconds), &tm)) return 0; - JSInt32 base = PRMJ_LocalGMTDifference(); + int32_t base = PRMJ_LocalGMTDifference(); - int32 dayoff = int32((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24)); - int32 tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) + + int32_t dayoff = int32_t((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24)); + int32_t tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) + (tm.tm_hour * SECONDS_PER_HOUR); - JSInt32 diff = tmoff - dayoff; + int32_t diff = tmoff - dayoff; if (diff < 0) diff += SECONDS_PER_DAY; @@ -705,13 +690,12 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds) return diff * MILLISECONDS_PER_SECOND; } -JSInt64 -DSTOffsetCache::getDSTOffsetMilliseconds(JSInt64 localTimeMilliseconds, JSContext *cx) +int64_t +DSTOffsetCache::getDSTOffsetMilliseconds(int64_t localTimeMilliseconds, JSContext *cx) { sanityCheck(); - noteOffsetCalculation(); - JSInt64 localTimeSeconds = localTimeMilliseconds / MILLISECONDS_PER_SECOND; + int64_t localTimeSeconds = localTimeMilliseconds / MILLISECONDS_PER_SECOND; if (localTimeSeconds > MAX_UNIX_TIMET) { localTimeSeconds = MAX_UNIX_TIMET; @@ -728,13 +712,11 @@ DSTOffsetCache::getDSTOffsetMilliseconds(JSInt64 localTimeMilliseconds, JSContex if (rangeStartSeconds <= localTimeSeconds && localTimeSeconds <= rangeEndSeconds) { - noteCacheHit(); return offsetMilliseconds; } if (oldRangeStartSeconds <= localTimeSeconds && localTimeSeconds <= oldRangeEndSeconds) { - noteCacheHit(); return oldOffsetMilliseconds; } @@ -743,55 +725,47 @@ DSTOffsetCache::getDSTOffsetMilliseconds(JSInt64 localTimeMilliseconds, JSContex oldRangeEndSeconds = rangeEndSeconds; if (rangeStartSeconds <= localTimeSeconds) { - JSInt64 newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET); + int64_t newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET); if (newEndSeconds >= localTimeSeconds) { - JSInt64 endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds); + int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds); if (endOffsetMilliseconds == offsetMilliseconds) { - noteCacheMissIncrease(); rangeEndSeconds = newEndSeconds; return offsetMilliseconds; } offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds); if (offsetMilliseconds == endOffsetMilliseconds) { - noteCacheMissIncreasingOffsetChangeUpper(); rangeStartSeconds = localTimeSeconds; rangeEndSeconds = newEndSeconds; } else { - noteCacheMissIncreasingOffsetChangeExpand(); rangeEndSeconds = localTimeSeconds; } return offsetMilliseconds; } - noteCacheMissLargeIncrease(); offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds); rangeStartSeconds = rangeEndSeconds = localTimeSeconds; return offsetMilliseconds; } - JSInt64 newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0); + int64_t newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0); if (newStartSeconds <= localTimeSeconds) { - JSInt64 startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds); + int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds); if (startOffsetMilliseconds == offsetMilliseconds) { - noteCacheMissDecrease(); rangeStartSeconds = newStartSeconds; return offsetMilliseconds; } offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds); if (offsetMilliseconds == startOffsetMilliseconds) { - noteCacheMissDecreasingOffsetChangeLower(); rangeStartSeconds = newStartSeconds; rangeEndSeconds = localTimeSeconds; } else { - noteCacheMissDecreasingOffsetChangeExpand(); rangeStartSeconds = localTimeSeconds; } return offsetMilliseconds; } - noteCacheMissLargeDecrease(); rangeStartSeconds = rangeEndSeconds = localTimeSeconds; offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds); return offsetMilliseconds; @@ -807,46 +781,4 @@ DSTOffsetCache::sanityCheck() rangeStartSeconds >= 0 && rangeEndSeconds >= 0); JS_ASSERT_IF(rangeStartSeconds != INT64_MIN, rangeStartSeconds <= MAX_UNIX_TIMET && rangeEndSeconds <= MAX_UNIX_TIMET); - -#ifdef JS_METER_DST_OFFSET_CACHING - JS_ASSERT(totalCalculations == - hit + - missIncreasing + missDecreasing + - missIncreasingOffsetChangeExpand + missIncreasingOffsetChangeUpper + - missDecreasingOffsetChangeExpand + missDecreasingOffsetChangeLower + - missLargeIncrease + missLargeDecrease); -#endif } - -#ifdef JS_METER_DST_OFFSET_CACHING -void -DSTOffsetCache::dumpStats() -{ - if (!getenv("JS_METER_DST_OFFSET_CACHING")) - return; - FILE *fp = fopen("/tmp/dst-offset-cache.stats", "a"); - if (!fp) - return; - typedef unsigned long UL; - fprintf(fp, - "hit:\n" - " in range: %lu\n" - "misses:\n" - " increase range end: %lu\n" - " decrease range start: %lu\n" - " increase, offset change, expand: %lu\n" - " increase, offset change, new range: %lu\n" - " decrease, offset change, expand: %lu\n" - " decrease, offset change, new range: %lu\n" - " large increase: %lu\n" - " large decrease: %lu\n" - "total: %lu\n\n", - UL(hit), - UL(missIncreasing), UL(missDecreasing), - UL(missIncreasingOffsetChangeExpand), UL(missIncreasingOffsetChangeUpper), - UL(missDecreasingOffsetChangeExpand), UL(missDecreasingOffsetChangeLower), - UL(missLargeIncrease), UL(missLargeDecrease), - UL(totalCalculations)); - fclose(fp); -} -#endif diff --git a/deps/mozjs/js/src/prmjtime.h b/deps/mozjs/js/src/prmjtime.h index 44bc2b91fd9..b4318aca791 100644 --- a/deps/mozjs/js/src/prmjtime.h +++ b/deps/mozjs/js/src/prmjtime.h @@ -39,16 +39,8 @@ #ifndef prmjtime_h___ #define prmjtime_h___ -/* - * PR date stuff for mocha and java. Placed here temporarily not to break - * Navigator and localize changes to mocha. - */ -#include -#include "jslong.h" -#ifdef MOZILLA_CLIENT -#include "jscompat.h" -#endif +#include struct JSContext; @@ -99,82 +91,29 @@ struct JSContext; class DSTOffsetCache { public: inline DSTOffsetCache(); - JSInt64 getDSTOffsetMilliseconds(int64 localTimeMilliseconds, JSContext *cx); + int64_t getDSTOffsetMilliseconds(int64_t localTimeMilliseconds, JSContext *cx); inline void purge(); -#ifdef JS_METER_DST_OFFSET_CACHING - void dumpStats(); -#endif private: - JSInt64 computeDSTOffsetMilliseconds(int64 localTimeSeconds); - - JSInt64 offsetMilliseconds; - JSInt64 rangeStartSeconds, rangeEndSeconds; - - JSInt64 oldOffsetMilliseconds; - JSInt64 oldRangeStartSeconds, oldRangeEndSeconds; - -#ifdef JS_METER_DST_OFFSET_CACHING - size_t totalCalculations; - size_t hit; - size_t missIncreasing; - size_t missDecreasing; - size_t missIncreasingOffsetChangeUpper; - size_t missIncreasingOffsetChangeExpand; - size_t missLargeIncrease; - size_t missDecreasingOffsetChangeLower; - size_t missDecreasingOffsetChangeExpand; - size_t missLargeDecrease; -#endif + int64_t computeDSTOffsetMilliseconds(int64_t localTimeSeconds); + + int64_t offsetMilliseconds; + int64_t rangeStartSeconds, rangeEndSeconds; + + int64_t oldOffsetMilliseconds; + int64_t oldRangeStartSeconds, oldRangeEndSeconds; - static const JSInt64 MAX_UNIX_TIMET = 2145859200; /* time_t 12/31/2037 */ - static const JSInt64 MILLISECONDS_PER_SECOND = 1000; - static const JSInt64 SECONDS_PER_MINUTE = 60; - static const JSInt64 SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; - static const JSInt64 SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; + static const int64_t MAX_UNIX_TIMET = 2145859200; /* time_t 12/31/2037 */ + static const int64_t MILLISECONDS_PER_SECOND = 1000; + static const int64_t SECONDS_PER_MINUTE = 60; + static const int64_t SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; + static const int64_t SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; - static const JSInt64 RANGE_EXPANSION_AMOUNT = 30 * SECONDS_PER_DAY; + static const int64_t RANGE_EXPANSION_AMOUNT = 30 * SECONDS_PER_DAY; private: void sanityCheck(); - -#ifdef JS_METER_DST_OFFSET_CACHING -#define NOTE_GENERIC(member) this->member++ -#else -#define NOTE_GENERIC(member) ((void)0) -#endif - void noteOffsetCalculation() { - NOTE_GENERIC(totalCalculations); - } - void noteCacheHit() { - NOTE_GENERIC(hit); - } - void noteCacheMissIncrease() { - NOTE_GENERIC(missIncreasing); - } - void noteCacheMissDecrease() { - NOTE_GENERIC(missDecreasing); - } - void noteCacheMissIncreasingOffsetChangeUpper() { - NOTE_GENERIC(missIncreasingOffsetChangeUpper); - } - void noteCacheMissIncreasingOffsetChangeExpand() { - NOTE_GENERIC(missIncreasingOffsetChangeExpand); - } - void noteCacheMissLargeIncrease() { - NOTE_GENERIC(missLargeIncrease); - } - void noteCacheMissDecreasingOffsetChangeLower() { - NOTE_GENERIC(missDecreasingOffsetChangeLower); - } - void noteCacheMissDecreasingOffsetChangeExpand() { - NOTE_GENERIC(missDecreasingOffsetChangeExpand); - } - void noteCacheMissLargeDecrease() { - NOTE_GENERIC(missLargeDecrease); - } -#undef NOTE_GENERIC }; JS_BEGIN_EXTERN_C @@ -185,16 +124,16 @@ typedef struct PRMJTime PRMJTime; * Broken down form of 64 bit time value. */ struct PRMJTime { - JSInt32 tm_usec; /* microseconds of second (0-999999) */ - JSInt8 tm_sec; /* seconds of minute (0-59) */ - JSInt8 tm_min; /* minutes of hour (0-59) */ - JSInt8 tm_hour; /* hour of day (0-23) */ - JSInt8 tm_mday; /* day of month (1-31) */ - JSInt8 tm_mon; /* month of year (0-11) */ - JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ - JSInt32 tm_year; /* absolute year, AD */ - JSInt16 tm_yday; /* day of year (0 to 365) */ - JSInt8 tm_isdst; /* non-zero if DST in effect */ + int32_t tm_usec; /* microseconds of second (0-999999) */ + int8_t tm_sec; /* seconds of minute (0-59) */ + int8_t tm_min; /* minutes of hour (0-59) */ + int8_t tm_hour; /* hour of day (0-23) */ + int8_t tm_mday; /* day of month (1-31) */ + int8_t tm_mon; /* month of year (0-11) */ + int8_t tm_wday; /* 0=sunday, 1=monday, ... */ + int32_t tm_year; /* absolute year, AD */ + int16_t tm_yday; /* day of year (0 to 365) */ + int8_t tm_isdst; /* non-zero if DST in effect */ }; /* Some handy constants */ @@ -202,7 +141,7 @@ struct PRMJTime { #define PRMJ_USEC_PER_MSEC 1000L /* Return the current local time in micro-seconds */ -extern JSInt64 +extern int64_t PRMJ_Now(void); /* Release the resources associated with PRMJ_Now; don't call PRMJ_Now again */ @@ -214,7 +153,7 @@ PRMJ_NowShutdown(void); #endif /* get the difference between this time zone and gmt timezone in seconds */ -extern JSInt32 +extern int32_t PRMJ_LocalGMTDifference(void); /* Format a time value into a buffer. Same semantics as strftime() */ diff --git a/deps/mozjs/js/src/ref-config/AIX4.1.mk b/deps/mozjs/js/src/ref-config/AIX4.1.mk deleted file mode 100644 index 09c7cb94cf3..00000000000 --- a/deps/mozjs/js/src/ref-config/AIX4.1.mk +++ /dev/null @@ -1,65 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for AIX -# - -CC = xlC_r -CCC = xlC_r - -RANLIB = ranlib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< -ARCH := aix -CPU_ARCH = rs6000 -GFX_ARCH = x -INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 - -OS_CFLAGS = -qarch=com -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R -OS_LIBS = -lbsd -lsvld -lm -#-lpthreads -lc_r - -MKSHLIB = $(LD) -bM:SRE -bh:4 -bnoentry -berok -XLDFLAGS += -lc - -ifdef JS_THREADSAFE -XLDFLAGS += -lsvld -endif diff --git a/deps/mozjs/js/src/ref-config/AIX4.2.mk b/deps/mozjs/js/src/ref-config/AIX4.2.mk deleted file mode 100644 index 1e3f1f183d1..00000000000 --- a/deps/mozjs/js/src/ref-config/AIX4.2.mk +++ /dev/null @@ -1,64 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for AIX -# - -CC = xlC_r -CCC = xlC_r -CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R - -RANLIB = ranlib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< -ARCH := aix -CPU_ARCH = rs6000 -GFX_ARCH = x -INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 - -#-lpthreads -lc_r - -MKSHLIB = /usr/lpp/xlC/bin/makeC++SharedLib_r -p 0 -G -berok - -ifdef JS_THREADSAFE -XLDFLAGS += -ldl -endif - diff --git a/deps/mozjs/js/src/ref-config/AIX4.3.mk b/deps/mozjs/js/src/ref-config/AIX4.3.mk deleted file mode 100644 index df05d8c9256..00000000000 --- a/deps/mozjs/js/src/ref-config/AIX4.3.mk +++ /dev/null @@ -1,65 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for AIX -# - -CC = xlC_r -CCC = xlC_r -CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DAIX4_3 -DHAVE_LOCALTIME_R - -RANLIB = ranlib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< -ARCH := aix -CPU_ARCH = rs6000 -GFX_ARCH = x -INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 - -#-lpthreads -lc_r - -MKSHLIB_BIN = /usr/ibmcxx/bin/makeC++SharedLib_r -MKSHLIB = $(MKSHLIB_BIN) -p 0 -G -berok -bM:UR - -ifdef JS_THREADSAFE -XLDFLAGS += -ldl -endif - diff --git a/deps/mozjs/js/src/ref-config/Darwin.mk b/deps/mozjs/js/src/ref-config/Darwin.mk deleted file mode 100644 index 86c4b9d042d..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin.mk +++ /dev/null @@ -1,85 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = gcc -CCC = g++ -CFLAGS += -Wall -Wno-format -MMD -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN - -RANLIB = ranlib -MKSHLIB = $(CCC) -dynamiclib $(XMKSHLIBOPTS) -framework System - -SO_SUFFIX = dylib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS+= -DX86_LINUX -OS_CFLAGS += -DAVMPLUS_IA32 -DAVMPLUS_UNIX -NANOJIT_ARCH = i386 -endif -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/Darwin1.3.mk b/deps/mozjs/js/src/ref-config/Darwin1.3.mk deleted file mode 100644 index 05d3767a0d2..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin1.3.mk +++ /dev/null @@ -1,81 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = cc -CCC = g++ -CFLAGS += -Wall -Wno-format -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DRHAPSODY - -RANLIB = ranlib -MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS+= -DX86_LINUX -endif -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/Darwin1.4.mk b/deps/mozjs/js/src/ref-config/Darwin1.4.mk deleted file mode 100644 index f7b6af8ec0a..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin1.4.mk +++ /dev/null @@ -1,41 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mike McCabe -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -include $(DEPTH)/config/Darwin1.3.mk diff --git a/deps/mozjs/js/src/ref-config/Darwin5.2.mk b/deps/mozjs/js/src/ref-config/Darwin5.2.mk deleted file mode 100644 index 9b9b6ff0977..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin5.2.mk +++ /dev/null @@ -1,81 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = cc -CCC = g++ -CFLAGS += -Wall -Wno-format -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN - -RANLIB = ranlib -MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS+= -DX86_LINUX -endif -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/Darwin5.3.mk b/deps/mozjs/js/src/ref-config/Darwin5.3.mk deleted file mode 100644 index 9b9b6ff0977..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin5.3.mk +++ /dev/null @@ -1,81 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = cc -CCC = g++ -CFLAGS += -Wall -Wno-format -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN - -RANLIB = ranlib -MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS+= -DX86_LINUX -endif -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/Darwin64.mk b/deps/mozjs/js/src/ref-config/Darwin64.mk deleted file mode 100644 index db195b56690..00000000000 --- a/deps/mozjs/js/src/ref-config/Darwin64.mk +++ /dev/null @@ -1,72 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = cc -CCC = g++ -CFLAGS += -Wall -Wno-format -MMD -OS_LDFLAGS += -m64 -OS_CFLAGS = -m64 -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN - -RANLIB = ranlib -MKSHLIB = $(CCC) -dynamiclib $(XMKSHLIBOPTS) -framework System - -SO_SUFFIX = dylib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86_64 -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/HP-UXB.10.10.mk b/deps/mozjs/js/src/ref-config/HP-UXB.10.10.mk deleted file mode 100644 index 8cd9d20671b..00000000000 --- a/deps/mozjs/js/src/ref-config/HP-UXB.10.10.mk +++ /dev/null @@ -1,77 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for HPUX -# - -# CC = gcc -# CCC = g++ -# CFLAGS += -Wall -Wno-format -fPIC - -CC = cc -Ae +Z -CCC = CC -Ae +a1 +eh +Z - -RANLIB = echo -MKSHLIB = $(LD) -b - -SO_SUFFIX = sl - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = hppa -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R -OS_LIBS = -ldld - -ifeq ($(OS_RELEASE),B.10) -PLATFORM_FLAGS += -DHPUX10 -Dhpux10 -PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H -ifeq ($(OS_VERSION),.10) -PLATFORM_FLAGS += -DHPUX10_10 -endif -ifeq ($(OS_VERSION),.20) -PLATFORM_FLAGS += -DHPUX10_20 -endif -ifeq ($(OS_VERSION),.30) -PLATFORM_FLAGS += -DHPUX10_30 -endif -endif diff --git a/deps/mozjs/js/src/ref-config/HP-UXB.10.20.mk b/deps/mozjs/js/src/ref-config/HP-UXB.10.20.mk deleted file mode 100644 index 8cd9d20671b..00000000000 --- a/deps/mozjs/js/src/ref-config/HP-UXB.10.20.mk +++ /dev/null @@ -1,77 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for HPUX -# - -# CC = gcc -# CCC = g++ -# CFLAGS += -Wall -Wno-format -fPIC - -CC = cc -Ae +Z -CCC = CC -Ae +a1 +eh +Z - -RANLIB = echo -MKSHLIB = $(LD) -b - -SO_SUFFIX = sl - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = hppa -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R -OS_LIBS = -ldld - -ifeq ($(OS_RELEASE),B.10) -PLATFORM_FLAGS += -DHPUX10 -Dhpux10 -PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H -ifeq ($(OS_VERSION),.10) -PLATFORM_FLAGS += -DHPUX10_10 -endif -ifeq ($(OS_VERSION),.20) -PLATFORM_FLAGS += -DHPUX10_20 -endif -ifeq ($(OS_VERSION),.30) -PLATFORM_FLAGS += -DHPUX10_30 -endif -endif diff --git a/deps/mozjs/js/src/ref-config/HP-UXB.11.00.mk b/deps/mozjs/js/src/ref-config/HP-UXB.11.00.mk deleted file mode 100644 index 239188d6011..00000000000 --- a/deps/mozjs/js/src/ref-config/HP-UXB.11.00.mk +++ /dev/null @@ -1,80 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for HPUX -# - -ifdef NS_USE_NATIVE - CC = cc +Z +DAportable +DS2.0 +u4 -# LD = aCC +Z -b -Wl,+s -Wl,-B,symbolic -else - CC = gcc -Wall -Wno-format -fPIC - CCC = g++ -Wall -Wno-format -fPIC -endif - -RANLIB = echo -MKSHLIB = $(LD) -b - -SO_SUFFIX = sl - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = hppa -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -D_HPUX -DNATIVE -D_POSIX_C_SOURCE=199506L -DHAVE_LOCALTIME_R -OS_LIBS = -ldld - -XLDFLAGS = -lpthread - -ifeq ($(OS_RELEASE),B.10) -PLATFORM_FLAGS += -DHPUX10 -Dhpux10 -PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H -ifeq ($(OS_VERSION),.10) -PLATFORM_FLAGS += -DHPUX10_10 -endif -ifeq ($(OS_VERSION),.20) -PLATFORM_FLAGS += -DHPUX10_20 -endif -ifeq ($(OS_VERSION),.30) -PLATFORM_FLAGS += -DHPUX10_30 -endif -endif diff --git a/deps/mozjs/js/src/ref-config/HP-UXB.11.31.mk b/deps/mozjs/js/src/ref-config/HP-UXB.11.31.mk deleted file mode 100644 index 8c1cc40ad1d..00000000000 --- a/deps/mozjs/js/src/ref-config/HP-UXB.11.31.mk +++ /dev/null @@ -1,65 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for HPUX -# -ifdef NS_USE_NATIVE - CC = cc +Z +u4 -else - CC = gcc -Wall -Wno-format -fPIC - CCC = g++ -Wall -Wno-format -fPIC -endif - -RANLIB = echo -MKSHLIB = $(LD) -b - -SO_SUFFIX = sl - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = ia64 -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -D_HPUX \ - -DNATIVE -D_POSIX_C_SOURCE=199506L -DHAVE_LOCALTIME_R -OS_LIBS = -ldld - -XLDFLAGS = -lpthread diff --git a/deps/mozjs/js/src/ref-config/IRIX.mk b/deps/mozjs/js/src/ref-config/IRIX.mk deleted file mode 100644 index 88b162f2271..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX.mk +++ /dev/null @@ -1,87 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX -# - -CPU_ARCH = mips -GFX_ARCH = x - -RANLIB = /bin/true - -#NS_USE_GCC = 1 - -ifndef NS_USE_NATIVE -CC = gcc -CCC = g++ -AS = $(CC) -x assembler-with-cpp -ODD_CFLAGS = -Wall -Wno-format -ifdef BUILD_OPT -OPTIMIZER = -O6 -endif -else -ifeq ($(OS_RELEASE),6.2) -CC = cc -n32 -DIRIX6_2 -endif -ifeq ($(OS_RELEASE),6.3) -CC = cc -n32 -DIRIX6_3 -endif -ifeq ($(OS_RELEASE),6.5) -CC = cc -n32 -DIRIX6_5 -endif -CCC = CC -# LD = CC -ODD_CFLAGS = -fullwarn -xansi -ifdef BUILD_OPT -OPTIMIZER += -Olimit 4000 -endif -endif - -# For purify -HAVE_PURIFY = 1 -PURE_OS_CFLAGS = $(ODD_CFLAGS) -DXP_UNIX -DSVR4 -DSW_THREADS -DIRIX -DHAVE_LOCALTIME_R - -OS_CFLAGS = $(PURE_OS_CFLAGS) -MDupdate $(DEPENDENCIES) - -BSDECHO = echo -MKSHLIB = $(LD) -n32 -shared - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 diff --git a/deps/mozjs/js/src/ref-config/IRIX5.3.mk b/deps/mozjs/js/src/ref-config/IRIX5.3.mk deleted file mode 100644 index f38cc94874b..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX5.3.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX5.3 -# - -include $(DEPTH)/config/IRIX.mk diff --git a/deps/mozjs/js/src/ref-config/IRIX6.1.mk b/deps/mozjs/js/src/ref-config/IRIX6.1.mk deleted file mode 100644 index 354f1d119d9..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX6.1.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX6.3 -# - -include $(DEPTH)/config/IRIX.mk diff --git a/deps/mozjs/js/src/ref-config/IRIX6.2.mk b/deps/mozjs/js/src/ref-config/IRIX6.2.mk deleted file mode 100644 index 354f1d119d9..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX6.2.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX6.3 -# - -include $(DEPTH)/config/IRIX.mk diff --git a/deps/mozjs/js/src/ref-config/IRIX6.3.mk b/deps/mozjs/js/src/ref-config/IRIX6.3.mk deleted file mode 100644 index 354f1d119d9..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX6.3.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX6.3 -# - -include $(DEPTH)/config/IRIX.mk diff --git a/deps/mozjs/js/src/ref-config/IRIX6.5.mk b/deps/mozjs/js/src/ref-config/IRIX6.5.mk deleted file mode 100644 index 354f1d119d9..00000000000 --- a/deps/mozjs/js/src/ref-config/IRIX6.5.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for IRIX6.3 -# - -include $(DEPTH)/config/IRIX.mk diff --git a/deps/mozjs/js/src/ref-config/Linux_All.mk b/deps/mozjs/js/src/ref-config/Linux_All.mk deleted file mode 100644 index 6c289b4045b..00000000000 --- a/deps/mozjs/js/src/ref-config/Linux_All.mk +++ /dev/null @@ -1,105 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for all versions of Linux -# - -CC = gcc -CCC = g++ -LD = g++ -CFLAGS += -Wall -Wno-format -MMD -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DLINUX - -RANLIB = echo -MKSHLIB = $(LD) -shared $(XMKSHLIBOPTS) - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -# don't filter in x86-64 architecture -ifneq (x86_64,$(CPU_ARCH)) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS += -DX86_LINUX -DAVMPLUS_IA32 -DAVMPLUS_UNIX -DAVMPLUS_LINUX -NANOJIT_ARCH = i386 -endif # 86 -endif # !x86_64 - -#JIT disabled until x64 port is cleaned up -#ifeq ($(CPU_ARCH),x86_64) -#OS_CFLAGS += -DAVMPLUS_AMD64 -DAVMPLUS_64BIT -DAVMPLUS_UNIX -DAVMPLUS_LINUX -#NANOJIT_ARCH = i386 -#endif - -ifeq ($(CPU_ARCH),arm) -OS_CFLAGS += -DAVMPLUS_ARM -DAVMPLUS_UNIX -DAVMPLUS_LINUX -NANOJIT_ARCH = ARM -endif - -GFX_ARCH = x - -OS_LIBS = -lm -lc - -ASFLAGS += -x assembler-with-cpp - - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -ifeq ($(CPU_ARCH),x86_64) -# Use VA_COPY() standard macro on x86-64 -# FIXME: better use it everywhere -OS_CFLAGS += -DHAVE_VA_COPY -DVA_COPY=va_copy -endif - -ifeq ($(CPU_ARCH),x86_64) -# We need PIC code for shared libraries -# FIXME: better patch rules.mk & fdlibm/Makefile* -OS_CFLAGS += -DPIC -fPIC -endif diff --git a/deps/mozjs/js/src/ref-config/Mac_OS10.0.mk b/deps/mozjs/js/src/ref-config/Mac_OS10.0.mk deleted file mode 100644 index 74ba151e3b5..00000000000 --- a/deps/mozjs/js/src/ref-config/Mac_OS10.0.mk +++ /dev/null @@ -1,82 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Steve Zellers (zellers@apple.com) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Mac OS X as of PR3 -# Just ripped from Linux config -# - -CC = cc -CCC = g++ -CFLAGS += -Wall -Wno-format -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE --DRHAPSODY - -RANLIB = ranlib -MKSHLIB = libtool -dynamic $(XMKSHLIBOPTS) -framework System - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = $(shell uname -m) -ifeq (86,$(findstring 86,$(CPU_ARCH))) -CPU_ARCH = x86 -OS_CFLAGS+= -DX86_LINUX -endif -GFX_ARCH = x - -OS_LIBS = -lc -framework System - -ASFLAGS += -x assembler-with-cpp - -ifeq ($(CPU_ARCH),alpha) - -# Ask the C compiler on alpha linux to let us work with denormalized -# double values, which are required by the ECMA spec. - -OS_CFLAGS += -mieee -endif - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 - -# Don't allow Makefile.ref to use libmath -NO_LIBM = 1 - diff --git a/deps/mozjs/js/src/ref-config/OSF1V4.0.mk b/deps/mozjs/js/src/ref-config/OSF1V4.0.mk deleted file mode 100644 index 337ca745926..00000000000 --- a/deps/mozjs/js/src/ref-config/OSF1V4.0.mk +++ /dev/null @@ -1,72 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for Data General DG/UX -# - -# -# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) -# - -ifndef NS_USE_NATIVE -CC = gcc -CCC = g++ -CFLAGS += -mieee -Wall -Wno-format -else -CC = cc -CCC = cxx -CFLAGS += -ieee -std -# LD = cxx -endif - -RANLIB = echo -MKSHLIB = $(LD) -shared -taso -all -expect_unresolved "*" - -# -# _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if -# you're not using DG's compiler. It shouldn't hurt if you are. -# -# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in -# prtime.c -# -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl - -NOSUCHFILE = /no-such-file diff --git a/deps/mozjs/js/src/ref-config/OSF1V5.0.mk b/deps/mozjs/js/src/ref-config/OSF1V5.0.mk deleted file mode 100644 index b65738c4e43..00000000000 --- a/deps/mozjs/js/src/ref-config/OSF1V5.0.mk +++ /dev/null @@ -1,69 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for Tru64 Unix 5.0 -# - -# -# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) -# - -ifndef NS_USE_NATIVE -CC = gcc -CCC = g++ -CFLAGS += -mieee -Wall -Wno-format -else -CC = cc -CCC = cxx -CFLAGS += -ieee -std -pthread -# LD = cxx -endif - -RANLIB = echo -MKSHLIB = $(LD) -shared -all -expect_unresolved "*" - -# -# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in -# prtime.c -# -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl - -NOSUCHFILE = /no-such-file diff --git a/deps/mozjs/js/src/ref-config/SunOS4.1.4.mk b/deps/mozjs/js/src/ref-config/SunOS4.1.4.mk deleted file mode 100644 index 62f4815b462..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS4.1.4.mk +++ /dev/null @@ -1,101 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS4.1 -# - -CC = gcc -CCC = g++ -RANLIB = ranlib - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = sparc -GFX_ARCH = x - -# A pile of -D's to build xfe on sunos -MOZ_CFLAGS = -DSTRINGS_ALIGNED -DNO_REGEX -DNO_ISDIR -DUSE_RE_COMP \ - -DNO_REGCOMP -DUSE_GETWD -DNO_MEMMOVE -DNO_ALLOCA \ - -DBOGUS_MB_MAX -DNO_CONST - -# Purify doesn't like -MDupdate -NOMD_OS_CFLAGS = -DXP_UNIX -Wall -Wno-format -DSW_THREADS -DSUNOS4 -DNEED_SYSCALL \ - $(MOZ_CFLAGS) - -OS_CFLAGS = $(NOMD_OS_CFLAGS) -MDupdate $(DEPENDENCIES) -OS_LIBS = -ldl -lm - -MKSHLIB = $(LD) -L$(MOTIF)/lib - -HAVE_PURIFY = 1 -MOTIF = /home/motif/usr -MOTIFLIB = -L$(MOTIF)/lib -lXm -INCLUDES += -I/usr/X11R5/include -I$(MOTIF)/include - -NOSUCHFILE = /solaris-rm-f-sucks - -LOCALE_MAP = $(DEPTH)/cmd/xfe/intl/sunos.lm - -EN_LOCALE = en_US -DE_LOCALE = de -FR_LOCALE = fr -JP_LOCALE = ja -SJIS_LOCALE = ja_JP.SJIS -KR_LOCALE = ko -CN_LOCALE = zh -TW_LOCALE = zh_TW -I2_LOCALE = i2 -IT_LOCALE = it -SV_LOCALE = sv -ES_LOCALE = es -NL_LOCALE = nl -PT_LOCALE = pt - -LOC_LIB_DIR = /usr/openwin/lib/locale - -BSDECHO = echo - -# -# These defines are for building unix plugins -# -BUILD_UNIX_PLUGINS = 1 -DSO_LDOPTS = -DSO_LDFLAGS = diff --git a/deps/mozjs/js/src/ref-config/SunOS5.10.mk b/deps/mozjs/js/src/ref-config/SunOS5.10.mk deleted file mode 100644 index dc0b0a09189..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.10.mk +++ /dev/null @@ -1,50 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.10, using vendor gcc and NSPR -# - -include $(DEPTH)/config/SunOS5.5.mk - -INCLUDES += -I/usr/sfw/include/mozilla/nspr -OTHER_LIBS += -L/usr/sfw/lib/mozilla -R/usr/sfw/lib/mozilla - -CC=gcc - diff --git a/deps/mozjs/js/src/ref-config/SunOS5.3.mk b/deps/mozjs/js/src/ref-config/SunOS5.3.mk deleted file mode 100644 index bd615dee404..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.3.mk +++ /dev/null @@ -1,91 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.3 -# - -CC = gcc -CCC = g++ -CFLAGS += -Wall -Wno-format - -#CC = /opt/SUNWspro/SC3.0.1/bin/cc -RANLIB = echo - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = sparc -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl -ldl - -ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 - -HAVE_PURIFY = 1 - -NOSUCHFILE = /solaris-rm-f-sucks - -ifndef JS_NO_ULTRA -ULTRA_OPTIONS := -xarch=v8plus -ULTRA_OPTIONSD := -DULTRA_SPARC -else -ULTRA_OPTIONS := -xarch=v8 -ULTRA_OPTIONSD := -endif - -ifeq ($(OS_CPUARCH),sun4u) -DEFINES += $(ULTRA_OPTIONSD) -ifeq ($(findstring gcc,$(CC)),gcc) -DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) -else -ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) -endif -endif - -ifeq ($(OS_CPUARCH),sun4m) -ifeq ($(findstring gcc,$(CC)),gcc) -DEFINES += -Wa,-xarch=v8 -else -ASFLAGS += -xarch=v8 -endif -endif - -MKSHLIB = $(LD) -G diff --git a/deps/mozjs/js/src/ref-config/SunOS5.4.mk b/deps/mozjs/js/src/ref-config/SunOS5.4.mk deleted file mode 100644 index de019247a2d..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.4.mk +++ /dev/null @@ -1,92 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.4 -# - -ifdef NS_USE_NATIVE -CC = cc -CCC = CC -else -CC = gcc -CCC = g++ -CFLAGS += -Wall -Wno-format -endif - -RANLIB = echo - -CPU_ARCH = sparc -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D__svr4 -DSOLARIS -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl -ldl - -ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 - -HAVE_PURIFY = 1 - -NOSUCHFILE = /solaris-rm-f-sucks - -ifndef JS_NO_ULTRA -ULTRA_OPTIONS := -xarch=v8plus -ULTRA_OPTIONSD := -DULTRA_SPARC -else -ULTRA_OPTIONS := -xarch=v8 -ULTRA_OPTIONSD := -endif - -ifeq ($(OS_CPUARCH),sun4u) -DEFINES += $(ULTRA_OPTIONSD) -ifeq ($(findstring gcc,$(CC)),gcc) -DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) -else -ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) -endif -endif - -ifeq ($(OS_CPUARCH),sun4m) -ifeq ($(findstring gcc,$(CC)),gcc) -DEFINES += -Wa,-xarch=v8 -else -ASFLAGS += -xarch=v8 -endif -endif - -MKSHLIB = $(LD) -G diff --git a/deps/mozjs/js/src/ref-config/SunOS5.5.1.mk b/deps/mozjs/js/src/ref-config/SunOS5.5.1.mk deleted file mode 100644 index 648f72ffa0d..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.5.1.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.5.1 -# - -include $(DEPTH)/config/SunOS5.5.mk diff --git a/deps/mozjs/js/src/ref-config/SunOS5.5.mk b/deps/mozjs/js/src/ref-config/SunOS5.5.mk deleted file mode 100644 index e26b3a3e0ec..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.5.mk +++ /dev/null @@ -1,87 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.5 -# - -AS = /usr/ccs/bin/as -ifndef NS_USE_NATIVE -CC = gcc -CCC = g++ -CFLAGS += -Wall -Wno-format -else -CC = cc -CCC = CC -endif - -RANLIB = echo - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = sparc -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl -ldl - -ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 - -HAVE_PURIFY = 1 - -NOSUCHFILE = /solaris-rm-f-sucks - -ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? -ifeq ($(CC),gcc) # using gcc? -ifndef JS_NO_ULTRA # do we want ultra? -ifdef JS_THREADSAFE # only in thread-safe mode -DEFINES += -DULTRA_SPARC -DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC -else -ASFLAGS += -xarch=v8plus -DULTRA_SPARC -endif -endif -endif -endif - -MKSHLIB = $(LD) -G - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 diff --git a/deps/mozjs/js/src/ref-config/SunOS5.6.mk b/deps/mozjs/js/src/ref-config/SunOS5.6.mk deleted file mode 100644 index efe11528435..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.6.mk +++ /dev/null @@ -1,89 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.5 -# - -AS = /usr/ccs/bin/as -ifndef NS_USE_NATIVE - CC = gcc - CCC = g++ - CFLAGS += -Wall -Wno-format -else - CC = cc - CCC = CC - CFLAGS += -mt -KPIC -# LD = CC -endif - -RANLIB = echo - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = sparc -GFX_ARCH = x - -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl -ldl - -ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 - -HAVE_PURIFY = 1 - -NOSUCHFILE = /solaris-rm-f-sucks - -ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? -ifeq ($(CC),gcc) # using gcc? -ifndef JS_NO_ULTRA # do we want ultra? -ifdef JS_THREADSAFE # only in thread-safe mode -DEFINES += -DULTRA_SPARC -DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC -else -ASFLAGS += -xarch=v8plus -DULTRA_SPARC -endif -endif -endif -endif - -MKSHLIB = $(LD) -G - -# Use the editline library to provide line-editing support. -JS_EDITLINE = 1 diff --git a/deps/mozjs/js/src/ref-config/SunOS5.7.mk b/deps/mozjs/js/src/ref-config/SunOS5.7.mk deleted file mode 100644 index 2cb02f29590..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.7.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.7 -# - -include $(DEPTH)/config/SunOS5.5.mk diff --git a/deps/mozjs/js/src/ref-config/SunOS5.8.mk b/deps/mozjs/js/src/ref-config/SunOS5.8.mk deleted file mode 100644 index dd8a32d47b1..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.8.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.8 -# - -include $(DEPTH)/config/SunOS5.5.mk diff --git a/deps/mozjs/js/src/ref-config/SunOS5.9.mk b/deps/mozjs/js/src/ref-config/SunOS5.9.mk deleted file mode 100644 index b01ec9c26fc..00000000000 --- a/deps/mozjs/js/src/ref-config/SunOS5.9.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for SunOS5.9 -# - -include $(DEPTH)/config/SunOS5.5.mk diff --git a/deps/mozjs/js/src/ref-config/WINNT4.0.mk b/deps/mozjs/js/src/ref-config/WINNT4.0.mk deleted file mode 100644 index 1d36f91b84f..00000000000 --- a/deps/mozjs/js/src/ref-config/WINNT4.0.mk +++ /dev/null @@ -1,118 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Windows NT using MS Visual C++ (version?) -# - -CC = cl -CXX = cl - -RANLIB = echo - -PDBFILE = $(basename $(@F)).pdb - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86 # XXX fixme -GFX_ARCH = win32 - -# MSVC compiler options for both debug/optimize -# -nologo - suppress copyright message -# -W3 - Warning level 3 -# -Gm - enable minimal rebuild -# -Z7 - put debug info into the executable, not in .pdb file -# -Zi - put debug info into .pdb file -# -YX - automatic precompiled headers -# -GX - enable C++ exception support -WIN_CFLAGS = -nologo -W3 - -# MSVC compiler options for debug builds linked to MSVCRTD.DLL -# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_IDG_CFLAGS = -MDd -Od -Z7 - -# MSVC compiler options for debug builds linked to MSVCRT.DLL -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) - -# MSVC compiler options for release (optimized) builds -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) -# -O2 - Optimize for speed -# -G5 - Optimize for Pentium -WIN_OPT_CFLAGS = -MD -O2 - -ifdef BUILD_OPT -OPTIMIZER = $(WIN_OPT_CFLAGS) -else -ifdef BUILD_IDG -OPTIMIZER = $(WIN_IDG_CFLAGS) -else -OPTIMIZER = $(WIN_DEBUG_CFLAGS) -endif -endif - -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) -JSDLL_CFLAGS = -DEXPORT_JS_API -OS_LIBS = -lm -lc - -PREBUILT_CPUCFG = 1 -USE_MSVC = 1 - -LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ - -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ - -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -# CAFEDIR = t:/cafe -# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip -# JAVAC = $(CAFEDIR)/Bin/sj.exe -# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe -# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/deps/mozjs/js/src/ref-config/WINNT5.0.mk b/deps/mozjs/js/src/ref-config/WINNT5.0.mk deleted file mode 100644 index 7681e016d6a..00000000000 --- a/deps/mozjs/js/src/ref-config/WINNT5.0.mk +++ /dev/null @@ -1,118 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Windows NT using MS Visual C++ (version?) -# - -CC = cl -CXX = cl - -RANLIB = echo - -PDBFILE = $(basename $(@F)).pdb - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86 # XXX fixme -GFX_ARCH = win32 - -# MSVC compiler options for both debug/optimize -# -nologo - suppress copyright message -# -W3 - Warning level 3 -# -Gm - enable minimal rebuild -# -Z7 - put debug info into the executable, not in .pdb file -# -Zi - put debug info into .pdb file -# -YX - automatic precompiled headers -# -GX - enable C++ exception support -WIN_CFLAGS = -nologo -W3 - -# MSVC compiler options for debug builds linked to MSVCRTD.DLL -# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_IDG_CFLAGS = -MDd -Od -Z7 - -# MSVC compiler options for debug builds linked to MSVCRT.DLL -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) - -# MSVC compiler options for release (optimized) builds -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) -# -O2 - Optimize for speed -# -G5 - Optimize for Pentium -WIN_OPT_CFLAGS = -MD -O2 - -ifdef BUILD_OPT -OPTIMIZER = $(WIN_OPT_CFLAGS) -else -ifdef BUILD_IDG -OPTIMIZER = $(WIN_IDG_CFLAGS) -else -OPTIMIZER = $(WIN_DEBUG_CFLAGS) -endif -endif - -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) -JSDLL_CFLAGS = -DEXPORT_JS_API -OS_LIBS = -lm -lc - -PREBUILT_CPUCFG = 1 -USE_MSVC = 1 - -LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ - -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ - -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -# CAFEDIR = t:/cafe -# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip -# JAVAC = $(CAFEDIR)/Bin/sj.exe -# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe -# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/deps/mozjs/js/src/ref-config/WINNT5.1.mk b/deps/mozjs/js/src/ref-config/WINNT5.1.mk deleted file mode 100644 index 7681e016d6a..00000000000 --- a/deps/mozjs/js/src/ref-config/WINNT5.1.mk +++ /dev/null @@ -1,118 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Windows NT using MS Visual C++ (version?) -# - -CC = cl -CXX = cl - -RANLIB = echo - -PDBFILE = $(basename $(@F)).pdb - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86 # XXX fixme -GFX_ARCH = win32 - -# MSVC compiler options for both debug/optimize -# -nologo - suppress copyright message -# -W3 - Warning level 3 -# -Gm - enable minimal rebuild -# -Z7 - put debug info into the executable, not in .pdb file -# -Zi - put debug info into .pdb file -# -YX - automatic precompiled headers -# -GX - enable C++ exception support -WIN_CFLAGS = -nologo -W3 - -# MSVC compiler options for debug builds linked to MSVCRTD.DLL -# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_IDG_CFLAGS = -MDd -Od -Z7 - -# MSVC compiler options for debug builds linked to MSVCRT.DLL -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) - -# MSVC compiler options for release (optimized) builds -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) -# -O2 - Optimize for speed -# -G5 - Optimize for Pentium -WIN_OPT_CFLAGS = -MD -O2 - -ifdef BUILD_OPT -OPTIMIZER = $(WIN_OPT_CFLAGS) -else -ifdef BUILD_IDG -OPTIMIZER = $(WIN_IDG_CFLAGS) -else -OPTIMIZER = $(WIN_DEBUG_CFLAGS) -endif -endif - -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) -JSDLL_CFLAGS = -DEXPORT_JS_API -OS_LIBS = -lm -lc - -PREBUILT_CPUCFG = 1 -USE_MSVC = 1 - -LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ - -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ - -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -# CAFEDIR = t:/cafe -# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip -# JAVAC = $(CAFEDIR)/Bin/sj.exe -# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe -# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/deps/mozjs/js/src/ref-config/WINNT5.2.mk b/deps/mozjs/js/src/ref-config/WINNT5.2.mk deleted file mode 100644 index 5fbcbfe09f3..00000000000 --- a/deps/mozjs/js/src/ref-config/WINNT5.2.mk +++ /dev/null @@ -1,118 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Windows NT using MS Visual C++ (version?) -# - -CC = cl -CXX = cl - -RANLIB = echo - -PDBFILE = $(basename $(@F)).pdb - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86 # XXX fixme -GFX_ARCH = win32 - -# MSVC compiler options for both debug/optimize -# -nologo - suppress copyright message -# -W3 - Warning level 3 -# -Gm - enable minimal rebuild -# -Z7 - put debug info into the executable, not in .pdb file -# -Zi - put debug info into .pdb file -# -YX - automatic precompiled headers -# -GX - enable C++ exception support -WIN_CFLAGS = -nologo -W3 - -# MSVC compiler options for debug builds linked to MSVCRTD.DLL -# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_IDG_CFLAGS = -MDd -Od -Z7 - -# MSVC compiler options for debug builds linked to MSVCRT.DLL -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) - -# MSVC compiler options for release (optimized) builds -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) -# -O2 - Optimize for speed -# -G5 - Optimize for Pentium -WIN_OPT_CFLAGS = -MD -O2 - -ifdef BUILD_OPT -OPTIMIZER = $(WIN_OPT_CFLAGS) -else -ifdef BUILD_IDG -OPTIMIZER = $(WIN_IDG_CFLAGS) -else -OPTIMIZER = $(WIN_DEBUG_CFLAGS) -endif -endif - -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) -DAVMPLUS_WIN32 -DAVMPLUS_IA32 -JSDLL_CFLAGS = -DEXPORT_JS_API -OS_LIBS = -lm -lc - -PREBUILT_CPUCFG = 1 -USE_MSVC = 1 - -LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ - -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ - -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -# CAFEDIR = t:/cafe -# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip -# JAVAC = $(CAFEDIR)/Bin/sj.exe -# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe -# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/deps/mozjs/js/src/ref-config/WINNT6.0.mk b/deps/mozjs/js/src/ref-config/WINNT6.0.mk deleted file mode 100644 index 7681e016d6a..00000000000 --- a/deps/mozjs/js/src/ref-config/WINNT6.0.mk +++ /dev/null @@ -1,118 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config for Windows NT using MS Visual C++ (version?) -# - -CC = cl -CXX = cl - -RANLIB = echo - -PDBFILE = $(basename $(@F)).pdb - -#.c.o: -# $(CC) -c -MD $*.d $(CFLAGS) $< - -CPU_ARCH = x86 # XXX fixme -GFX_ARCH = win32 - -# MSVC compiler options for both debug/optimize -# -nologo - suppress copyright message -# -W3 - Warning level 3 -# -Gm - enable minimal rebuild -# -Z7 - put debug info into the executable, not in .pdb file -# -Zi - put debug info into .pdb file -# -YX - automatic precompiled headers -# -GX - enable C++ exception support -WIN_CFLAGS = -nologo -W3 - -# MSVC compiler options for debug builds linked to MSVCRTD.DLL -# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_IDG_CFLAGS = -MDd -Od -Z7 - -# MSVC compiler options for debug builds linked to MSVCRT.DLL -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) -# -Od - minimal optimization -WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) - -# MSVC compiler options for release (optimized) builds -# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) -# -O2 - Optimize for speed -# -G5 - Optimize for Pentium -WIN_OPT_CFLAGS = -MD -O2 - -ifdef BUILD_OPT -OPTIMIZER = $(WIN_OPT_CFLAGS) -else -ifdef BUILD_IDG -OPTIMIZER = $(WIN_IDG_CFLAGS) -else -OPTIMIZER = $(WIN_DEBUG_CFLAGS) -endif -endif - -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) -JSDLL_CFLAGS = -DEXPORT_JS_API -OS_LIBS = -lm -lc - -PREBUILT_CPUCFG = 1 -USE_MSVC = 1 - -LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ - -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ - -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ - -machine:I386\ - -opt:ref -opt:noicf - -# CAFEDIR = t:/cafe -# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip -# JAVAC = $(CAFEDIR)/Bin/sj.exe -# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe -# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/deps/mozjs/js/src/ref-config/dgux.mk b/deps/mozjs/js/src/ref-config/dgux.mk deleted file mode 100644 index 3b5967e3d1f..00000000000 --- a/deps/mozjs/js/src/ref-config/dgux.mk +++ /dev/null @@ -1,64 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# Config stuff for Data General DG/UX -# - -# -# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) -# - -AS = as -CC = gcc -CCC = g++ - -RANLIB = echo - -# -# _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if -# you're not using DG's compiler. It shouldn't hurt if you are. -# -# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in -# prtime.c -# -OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DHAVE_LOCALTIME_R -OS_LIBS = -lsocket -lnsl - -NOSUCHFILE = /no-such-file diff --git a/deps/mozjs/js/src/resource.h b/deps/mozjs/js/src/resource.h deleted file mode 100644 index 59dbde3775e..00000000000 --- a/deps/mozjs/js/src/resource.h +++ /dev/null @@ -1,15 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by js3240.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/deps/mozjs/js/src/rules.mk b/deps/mozjs/js/src/rules.mk deleted file mode 100644 index 5a1e0533f54..00000000000 --- a/deps/mozjs/js/src/rules.mk +++ /dev/null @@ -1,206 +0,0 @@ -# -*- Mode: makefile -*- -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998-1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Michael Ang -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -# -# JSRef GNUmake makefile rules -# - -ifdef USE_MSVC -LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CPPFILES:.cpp=.obj)) -PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CPPFILES:.cpp=.obj)) -else -LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CPPFILES:.cpp=.o)) -LIB_OBJS += $(addprefix $(OBJDIR)/, $(LIB_ASFILES:.s=.o)) -PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CPPFILES:.cpp=.o)) -endif - -CPPFILES = $(LIB_CPPFILES) $(PROG_CPPFILES) -OBJS = $(LIB_OBJS) $(PROG_OBJS) - -ifdef USE_MSVC -# TARGETS = $(LIBRARY) # $(PROGRAM) not supported for MSVC yet -TARGETS += $(SHARED_LIBRARY) $(PROGRAM) # it is now -else -TARGETS += $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) -endif - -all: - +$(LOOP_OVER_PREDIRS) -ifneq "$(strip $(TARGETS))" "" - $(MAKE) -f Makefile.ref $(TARGETS) -endif - +$(LOOP_OVER_DIRS) - -$(OBJDIR)/%: %.cpp - @$(MAKE_OBJDIR) - $(CXX) -o $@ $(CFLAGS) $(OPTIMIZER) $< $(LDFLAGS) - -# This rule must come before the rule with no dep on header -$(OBJDIR)/%.o: %.cpp %.h - @$(MAKE_OBJDIR) - $(CXX) -o $@ -c $(CFLAGS) $(OPTIMIZER) $< - -$(OBJDIR)/jsinterp.o: jsinterp.cpp jsinterp.h - @$(MAKE_OBJDIR) - $(CXX) -o $@ -c $(CFLAGS) $(INTERP_OPTIMIZER) jsinterp.cpp - -$(OBJDIR)/jsbuiltins.o: jsbuiltins.cpp jsinterp.h - @$(MAKE_OBJDIR) - $(CXX) -o $@ -c $(CFLAGS) $(BUILTINS_OPTIMIZER) jsbuiltins.cpp - -$(OBJDIR)/%.o: %.cpp - @$(MAKE_OBJDIR) - $(CXX) -o $@ -c $(CFLAGS) $(OPTIMIZER) $< - -$(OBJDIR)/%.o: %.s - @$(MAKE_OBJDIR) - $(AS) -o $@ $(ASFLAGS) $< - -# This rule must come before rule with no dep on header -$(OBJDIR)/%.obj: %.cpp %.h - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(OPTIMIZER) $< - -$(OBJDIR)/jsinterp.obj: jsinterp.cpp jsinterp.h - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(INTERP_OPTIMIZER) jsinterp.cpp - -$(OBJDIR)/jsbuiltins.obj: jsbuiltins.cpp jsinterp.h - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(BUILTINS_OPTIMIZER) jsbuiltins.cpp - -$(OBJDIR)/%.obj: %.cpp - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(OPTIMIZER) $< - -$(OBJDIR)/js.obj: js.cpp - @$(MAKE_OBJDIR) - $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(OPTIMIZER) $< - -ifeq ($(OS_ARCH),OS2) -$(LIBRARY): $(LIB_OBJS) - $(AR) $@ $? $(AR_OS2_SUFFIX) - $(RANLIB) $@ -else -ifdef USE_MSVC -$(SHARED_LIBRARY): $(LIB_OBJS) - link.exe $(LIB_LINK_FLAGS) /base:0x61000000 $(OTHER_LIBS) \ - /out:"$@" /pdb:none\ - /implib:"$(OBJDIR)/$(@F:.dll=.lib)" $^ -else -$(LIBRARY): $(LIB_OBJS) - $(AR) rv $@ $? - $(RANLIB) $@ - -$(SHARED_LIBRARY): $(LIB_OBJS) - $(MKSHLIB) -o $@ $(LIB_OBJS) $(LDFLAGS) $(OTHER_LIBS) -endif -endif - -# Java stuff -$(CLASSDIR)/$(OBJDIR)/$(JARPATH)/%.class: %.java - mkdir -p $(@D) - $(JAVAC) $(JAVAC_FLAGS) $< - -define MAKE_OBJDIR -if test ! -d $(@D); then rm -rf $(@D); mkdir -p $(@D); fi -endef - -ifdef DIRS -LOOP_OVER_DIRS = \ - @for d in $(DIRS); do \ - if test -d $$d; then \ - set -e; \ - echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ - cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ - set +e; \ - else \ - echo "Skipping non-directory $$d..."; \ - fi; \ - done -endif - -ifdef PREDIRS -LOOP_OVER_PREDIRS = \ - @for d in $(PREDIRS); do \ - if test -d $$d; then \ - set -e; \ - echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ - cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ - set +e; \ - else \ - echo "Skipping non-directory $$d..."; \ - fi; \ - done -endif - -export: - +$(LOOP_OVER_PREDIRS) - mkdir -p $(DIST)/include $(DIST)/$(LIBDIR) $(DIST)/bin -ifneq "$(strip $(HFILES))" "" - $(CP) $(HFILES) $(DIST)/include -endif -ifneq "$(strip $(LIBRARY))" "" - $(CP) $(LIBRARY) $(DIST)/$(LIBDIR) -endif -ifneq "$(strip $(JARS))" "" - $(CP) $(JARS) $(DIST)/$(LIBDIR) -endif -ifneq "$(strip $(SHARED_LIBRARY))" "" - $(CP) $(SHARED_LIBRARY) $(DIST)/$(LIBDIR) -endif -ifneq "$(strip $(PROGRAM))" "" - $(CP) $(PROGRAM) $(DIST)/bin -endif - +$(LOOP_OVER_DIRS) - -clean: - +$(LOOP_OVER_PREDIRS) - rm -rf $(OBJS) $(GARBAGE) - -clobber: - +$(LOOP_OVER_PREDIRS) - rm -rf $(OBJS) $(TARGETS) $(DEPENDENCIES) $(GARBAGE) - if test -d $(OBJDIR); then rmdir $(OBJDIR); fi - -tar: - tar cvf $(TARNAME) $(TARFILES) - gzip $(TARNAME) - diff --git a/deps/mozjs/js/src/shell/Makefile.in b/deps/mozjs/js/src/shell/Makefile.in index 8a4f8cfde9a..cf27c4dc2af 100644 --- a/deps/mozjs/js/src/shell/Makefile.in +++ b/deps/mozjs/js/src/shell/Makefile.in @@ -48,11 +48,19 @@ PROGRAM = js$(BIN_SUFFIX) CPPSRCS = \ js.cpp \ jsworkers.cpp \ + jsoptparse.cpp \ + jsheaptools.cpp \ $(NULL) DEFINES += -DEXPORT_JS_API +# Building against js_static requires that we declare mfbt sybols "exported" +# on its behalf. +DEFINES += -DIMPL_MFBT LIBS = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) +ifdef MOZ_NATIVE_FFI +EXTRA_LIBS += $(MOZ_FFI_LIBS) +endif LOCAL_INCLUDES += -I$(topsrcdir) -I.. diff --git a/deps/mozjs/js/src/shell/js.cpp b/deps/mozjs/js/src/shell/js.cpp index 83aed3f13ba..214c4c12774 100644 --- a/deps/mozjs/js/src/shell/js.cpp +++ b/deps/mozjs/js/src/shell/js.cpp @@ -38,8 +38,6 @@ * * ***** END LICENSE BLOCK ***** */ -#define __STDC_LIMIT_MACROS - /* * JS shell. */ @@ -50,20 +48,19 @@ #include #include #include + +#include "mozilla/Util.h" + #include "jstypes.h" -#include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jswrapper.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" -#include "jsbuiltins.h" #include "jscntxt.h" #include "jsdate.h" #include "jsdbgapi.h" -#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsiter.h" @@ -71,14 +68,18 @@ #include "jsnum.h" #include "jsobj.h" #include "json.h" -#include "jsparse.h" #include "jsreflect.h" #include "jsscope.h" #include "jsscript.h" #include "jstypedarray.h" +#include "jstypedarrayinlines.h" #include "jsxml.h" #include "jsperf.h" +#include "frontend/BytecodeEmitter.h" +#include "frontend/Parser.h" +#include "methodjit/MethodJIT.h" + #include "prmjtime.h" #ifdef JSDEBUGGER @@ -91,8 +92,11 @@ #endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ +#include "jsoptparse.h" #include "jsworkers.h" +#include "jsheaptools.h" +#include "jsinferinlines.h" #include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" @@ -111,7 +115,9 @@ #include "jswin.h" #endif +using namespace mozilla; using namespace js; +using namespace js::cli; typedef enum JSShellExitCode { EXITCODE_RUNTIME_ERROR = 3, @@ -137,11 +143,9 @@ size_t gMaxStackSize = DEFAULT_MAX_STACK_SIZE; #ifdef JS_THREADSAFE static PRUintn gStackBaseThreadIndex; #else -static jsuword gStackBase; +static uintptr_t gStackBase; #endif -static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; - /* * Limit the timeout to 30 minutes to prevent an overflow on platfoms * that represent the time internally in microseconds using 32-bit int. @@ -150,9 +154,9 @@ static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0; static jsdouble gTimeoutInterval = -1.0; static volatile bool gCanceled = false; -static bool enableTraceJit = false; static bool enableMethodJit = false; -static bool enableProfiling = false; +static bool enableTypeInference = false; +static bool enableDisassemblyDumps = false; static bool printTiming = false; @@ -212,7 +216,6 @@ typedef enum JSShellErrNum { #include "jsshell.msg" #undef MSG_DEF JSShellErr_Limit -#undef MSGDEF } JSShellErrNum; static JSContext * @@ -224,13 +227,10 @@ DestroyContext(JSContext *cx, bool withGC); static const JSErrorFormatString * my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); -static JSObject * -split_setup(JSContext *cx, JSBool evalcx); - #ifdef EDITLINE JS_BEGIN_EXTERN_C -JS_EXTERN_API(char) *readline(const char *prompt); -JS_EXTERN_API(void) add_history(char *line); +extern JS_EXPORT_API(char *) readline(const char *prompt); +extern JS_EXPORT_API(void) add_history(char *line); JS_END_EXTERN_C #endif @@ -243,9 +243,9 @@ ReportException(JSContext *cx) } } -class ToString { +class ToStringHelper { public: - ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE) + ToStringHelper(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE) : cx(aCx), mThrow(aThrow) { mStr = JS_ValueToString(cx, v); @@ -253,7 +253,7 @@ class ToString { ReportException(cx); JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper"); } - ~ToString() { + ~ToStringHelper() { JS_RemoveStringRoot(cx, &mStr); } JSBool threw() { return !mStr; } @@ -270,10 +270,10 @@ class ToString { JSAutoByteString mBytes; }; -class IdStringifier : public ToString { +class IdStringifier : public ToStringHelper { public: IdStringifier(JSContext *cx, jsid id, JSBool aThrow = JS_FALSE) - : ToString(cx, IdToJsval(id), aThrow) + : ToStringHelper(cx, IdToJsval(id), aThrow) { } }; @@ -388,16 +388,42 @@ ShellOperationCallback(JSContext *cx) static void SetContextOptions(JSContext *cx) { - JS_SetNativeStackQuota(cx, gMaxStackSize); - JS_SetScriptStackQuota(cx, gScriptStackQuota); JS_SetOperationCallback(cx, ShellOperationCallback); } +/* + * Some UTF-8 files, notably those written using Notepad, have a Unicode + * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order + * is meaningless for UTF-8) but causes a syntax error unless we skip it. + */ +static void +SkipUTF8BOM(FILE* file) +{ + if (!js_CStringsAreUTF8) + return; + + int ch1 = fgetc(file); + int ch2 = fgetc(file); + int ch3 = fgetc(file); + + // Skip the BOM + if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) + return; + + // No BOM - revert + if (ch3 != EOF) + ungetc(ch3, file); + if (ch2 != EOF) + ungetc(ch2, file); + if (ch1 != EOF) + ungetc(ch1, file); +} + static void -Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) +Process(JSContext *cx, JSObject *obj, const char *filename, bool forceTTY) { JSBool ok, hitEOF; - JSObject *scriptObj; + JSScript *script; jsval result; JSString *str; char *buffer; @@ -407,7 +433,9 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) int lineno; int startline; FILE *file; - uint32 oldopts; + uint32_t oldopts; + + RootObject root(cx, &obj); if (forceTTY || !filename || strcmp(filename, "-") == 0) { file = stdin; @@ -425,13 +453,11 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) if (!forceTTY && !isatty(fileno(file))) { + SkipUTF8BOM(file); + /* - * It's not interactive - just execute it. - * - * Support the UNIX #! shell hack; gobble the first line if it starts - * with '#'. TODO - this isn't quite compatible with sharp variables, - * as a legal js program (using sharp variables) might start with '#'. - * But that would require multi-character lookahead. + * It's not interactive - just execute it. Support the UNIX #! shell + * hack, and gobble the first line if it starts with '#'. */ int ch = fgetc(file); if (ch == '#') { @@ -442,14 +468,17 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) } ungetc(ch, file); - int64 t1 = PRMJ_Now(); + int64_t t1 = PRMJ_Now(); oldopts = JS_GetOptions(cx); JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL); - scriptObj = JS_CompileFileHandle(cx, obj, filename, file); + script = JS_CompileUTF8FileHandle(cx, obj, filename, file); JS_SetOptions(cx, oldopts); - if (scriptObj && !compileOnly) { - (void) JS_ExecuteScript(cx, obj, scriptObj, NULL); - int64 t2 = PRMJ_Now() - t1; + if (script && !compileOnly) { + if (!JS_ExecuteScript(cx, obj, script, NULL)) { + if (!gQuitting && !gCanceled) + gExitCode = EXITCODE_RUNTIME_ERROR; + } + int64_t t2 = PRMJ_Now() - t1; if (printTiming) printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC); } @@ -543,13 +572,12 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) oldopts = JS_GetOptions(cx); if (!compileOnly) JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); - scriptObj = JS_CompileUCScript(cx, obj, uc_buffer, uc_len, "typein", - startline); + script = JS_CompileUCScript(cx, obj, uc_buffer, uc_len, "typein", startline); if (!compileOnly) JS_SetOptions(cx, oldopts); - if (scriptObj && !compileOnly) { - ok = JS_ExecuteScript(cx, obj, scriptObj, &result); + if (script && !compileOnly) { + ok = JS_ExecuteScript(cx, obj, script, &result); if (ok && !JSVAL_IS_VOID(result)) { str = JS_ValueToSource(cx, result); ok = !!str; @@ -573,99 +601,28 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) return; } -static int -usage(void) -{ - fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); - fprintf(gErrFile, "usage: js [options] [scriptfile] [scriptarg...]\n" - "Options:\n" - " -h Display this information\n" - " -z Create a split global object\n" - " Warning: this option is probably not useful\n" - " -P Deeply freeze the global object prototype\n" - " -s Toggle JSOPTION_STRICT flag\n" - " -w Report strict warnings\n" - " -W Do not report strict warnings\n" - " -x Toggle JSOPTION_XML flag\n" - " -U Enable UTF-8 C-strings; also affects scripts loaded via\n" - " run, snarf, read, or entered into the REPL\n" - " -C Compile-only; do not execute\n" - " -i Enable interactive read-eval-print loop\n" - " -j Enable the TraceMonkey tracing JIT\n" - " -m Enable the JaegerMonkey method JIT\n" - " -a Always method JIT, ignore internal tuning\n" - " This only has effect with -m\n" - " -p Enable loop profiling for TraceMonkey\n" - " -d Enable debug mode\n" - " -b Print timing statistics\n" - " -t Interrupt long-running execution after seconds, where\n" - " <= 1800.0. Negative values indicate no timeout (default).\n" - " -c Suggest stack chunk size of bytes. Default is 8192.\n" - " Warning: this option is currently ignored.\n" - " -o